Updated VGMStream to r1050-1726-g5b99d40c.
This commit is contained in:
parent
46e5b89a8b
commit
2a5221da25
234 changed files with 16718 additions and 10938 deletions
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
912
Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_decode.c
Normal file
912
Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_decode.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
278
Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_util.c
Normal file
278
Frameworks/vgmstream/vgmstream/src/coding/acm_decoder_util.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,6 +171,34 @@ 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;
|
||||||
|
|
||||||
|
@ -196,6 +206,9 @@ void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||||
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) {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
@ -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
|
||||||
|
|
259
Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c.orig
Normal file
259
Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c.orig
Normal 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
|
231
Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c
Normal file
231
Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c
Normal 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
|
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
43
Frameworks/vgmstream/vgmstream/src/coding/derf_decoder.c
Normal file
43
Frameworks/vgmstream/vgmstream/src/coding/derf_decoder.c
Normal 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;
|
||||||
|
}
|
|
@ -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;
|
ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets) {
|
||||||
}
|
|
||||||
|
|
||||||
static int16_t utk_read_i16(UTKContext *ctx)
|
|
||||||
{
|
|
||||||
int x = utk_read_byte(ctx);
|
|
||||||
x = (x << 8) | utk_read_byte(ctx);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int utk_read_bits(UTKContext *ctx, int count)
|
|
||||||
{
|
|
||||||
int ret = ctx->bits_value & ((1 << count) - 1);
|
|
||||||
ctx->bits_value >>= count;
|
|
||||||
ctx->bits_count -= count;
|
|
||||||
|
|
||||||
if (ctx->bits_count < 8) {
|
|
||||||
/* read another byte */
|
|
||||||
ctx->bits_value |= utk_read_byte(ctx) << ctx->bits_count;
|
|
||||||
ctx->bits_count += 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void utk_parse_header(UTKContext *ctx)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
float multiplier;
|
|
||||||
|
|
||||||
ctx->reduced_bw = utk_read_bits(ctx, 1);
|
|
||||||
ctx->multipulse_thresh = 32 - utk_read_bits(ctx, 4);
|
|
||||||
ctx->fixed_gains[0] = 8.0f * (1 + utk_read_bits(ctx, 4));
|
|
||||||
multiplier = 1.04f + utk_read_bits(ctx, 6)*0.001f;
|
|
||||||
|
|
||||||
for (i = 1; i < 64; i++)
|
|
||||||
ctx->fixed_gains[i] = ctx->fixed_gains[i-1] * multiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void utk_decode_excitation(UTKContext *ctx, int use_multipulse, float *out, int stride)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (use_multipulse) {
|
|
||||||
/* multi-pulse model: n pulses are coded explicitly; the rest are zero */
|
|
||||||
int model, cmd;
|
|
||||||
model = 0;
|
|
||||||
i = 0;
|
|
||||||
while (i < 108) {
|
|
||||||
cmd = utk_codebooks[model][ctx->bits_value & 0xff];
|
|
||||||
model = utk_commands[cmd].next_model;
|
|
||||||
utk_read_bits(ctx, utk_commands[cmd].code_size);
|
|
||||||
|
|
||||||
if (cmd > 3) {
|
|
||||||
/* insert a pulse with magnitude <= 6.0f */
|
|
||||||
out[i] = utk_commands[cmd].pulse_value;
|
|
||||||
i += stride;
|
|
||||||
} else if (cmd > 1) {
|
|
||||||
/* insert between 7 and 70 zeros */
|
|
||||||
int count = 7 + utk_read_bits(ctx, 6);
|
|
||||||
if (i + count * stride > 108)
|
|
||||||
count = (108 - i)/stride;
|
|
||||||
|
|
||||||
while (count > 0) {
|
|
||||||
out[i] = 0.0f;
|
|
||||||
i += stride;
|
|
||||||
count--;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* insert a pulse with magnitude >= 7.0f */
|
|
||||||
int x = 7;
|
|
||||||
|
|
||||||
while (utk_read_bits(ctx, 1))
|
|
||||||
x++;
|
|
||||||
|
|
||||||
if (!utk_read_bits(ctx, 1))
|
|
||||||
x *= -1;
|
|
||||||
|
|
||||||
out[i] = (float)x;
|
|
||||||
i += stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* RELP model: entire residual (excitation) signal is coded explicitly */
|
|
||||||
i = 0;
|
|
||||||
while (i < 108) {
|
|
||||||
if (!utk_read_bits(ctx, 1))
|
|
||||||
out[i] = 0.0f;
|
|
||||||
else if (!utk_read_bits(ctx, 1))
|
|
||||||
out[i] = -2.0f;
|
|
||||||
else
|
|
||||||
out[i] = 2.0f;
|
|
||||||
|
|
||||||
i += stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rc_to_lpc(const float *rc, float *lpc)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
float tmp1[12];
|
|
||||||
float tmp2[12];
|
|
||||||
|
|
||||||
for (i = 10; i >= 0; i--)
|
|
||||||
tmp2[1+i] = rc[i];
|
|
||||||
|
|
||||||
tmp2[0] = 1.0f;
|
|
||||||
|
|
||||||
for (i = 0; i < 12; i++) {
|
|
||||||
float x = -tmp2[11] * rc[11];
|
|
||||||
|
|
||||||
for (j = 10; j >= 0; j--) {
|
|
||||||
x -= tmp2[j] * rc[j];
|
|
||||||
tmp2[j+1] = x * rc[j] + tmp2[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp1[i] = tmp2[0] = x;
|
|
||||||
|
|
||||||
for (j = 0; j < i; j++)
|
|
||||||
x -= tmp1[i-1-j] * lpc[j];
|
|
||||||
|
|
||||||
lpc[i] = x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void utk_lp_synthesis_filter(UTKContext *ctx, int offset, int num_blocks)
|
|
||||||
{
|
|
||||||
int i, j, k;
|
|
||||||
float lpc[12];
|
|
||||||
float *ptr = &ctx->decompressed_frame[offset];
|
|
||||||
|
|
||||||
rc_to_lpc(ctx->rc, lpc);
|
|
||||||
|
|
||||||
for (i = 0; i < num_blocks; i++) {
|
|
||||||
for (j = 0; j < 12; j++) {
|
|
||||||
float x = *ptr;
|
|
||||||
|
|
||||||
for (k = 0; k < j; k++)
|
|
||||||
x += lpc[k] * ctx->synth_history[k-j+12];
|
|
||||||
for (; k < 12; k++)
|
|
||||||
x += lpc[k] * ctx->synth_history[k-j];
|
|
||||||
|
|
||||||
ctx->synth_history[11-j] = x;
|
|
||||||
*ptr++ = x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Public functions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void utk_decode_frame(UTKContext *ctx)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
int use_multipulse = 0;
|
|
||||||
float excitation[5+108+5];
|
|
||||||
float rc_delta[12];
|
|
||||||
|
|
||||||
if (!ctx->bits_count) {
|
|
||||||
ctx->bits_value = utk_read_byte(ctx);
|
|
||||||
ctx->bits_count = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ctx->parsed_header) {
|
|
||||||
utk_parse_header(ctx);
|
|
||||||
ctx->parsed_header = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&excitation[0], 0, 5*sizeof(float));
|
|
||||||
memset(&excitation[5+108], 0, 5*sizeof(float));
|
|
||||||
|
|
||||||
/* read the reflection coefficients */
|
|
||||||
for (i = 0; i < 12; i++) {
|
|
||||||
int idx;
|
|
||||||
if (i == 0) {
|
|
||||||
idx = utk_read_bits(ctx, 6);
|
|
||||||
if (idx < ctx->multipulse_thresh)
|
|
||||||
use_multipulse = 1;
|
|
||||||
} else if (i < 4) {
|
|
||||||
idx = utk_read_bits(ctx, 6);
|
|
||||||
} else {
|
|
||||||
idx = 16 + utk_read_bits(ctx, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
rc_delta[i] = (utk_rc_table[idx] - ctx->rc[i])*0.25f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* decode four subframes */
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
int pitch_lag = utk_read_bits(ctx, 8);
|
|
||||||
float pitch_gain = (float)utk_read_bits(ctx, 4)/15.0f;
|
|
||||||
float fixed_gain = ctx->fixed_gains[utk_read_bits(ctx, 6)];
|
|
||||||
|
|
||||||
if (!ctx->reduced_bw) {
|
|
||||||
utk_decode_excitation(ctx, use_multipulse, &excitation[5], 1);
|
|
||||||
} else {
|
|
||||||
/* residual (excitation) signal is encoded at reduced bandwidth */
|
|
||||||
int align = utk_read_bits(ctx, 1);
|
|
||||||
int zero = utk_read_bits(ctx, 1);
|
|
||||||
|
|
||||||
utk_decode_excitation(ctx, use_multipulse, &excitation[5+align], 2);
|
|
||||||
|
|
||||||
if (zero) {
|
|
||||||
/* fill the remaining samples with zero
|
|
||||||
** (spectrum is duplicated into high frequencies) */
|
|
||||||
for (j = 0; j < 54; j++)
|
|
||||||
excitation[5+(1-align)+2*j] = 0.0f;
|
|
||||||
} else {
|
|
||||||
/* interpolate the remaining samples
|
|
||||||
** (spectrum is low-pass filtered) */
|
|
||||||
float *ptr = &excitation[5+(1-align)];
|
|
||||||
for (j = 0; j < 108; j += 2)
|
|
||||||
ptr[j] = ptr[j-5] * 0.01803267933428287506103515625f
|
|
||||||
- ptr[j-3] * 0.114591561257839202880859375f
|
|
||||||
+ ptr[j-1] * 0.597385942935943603515625f
|
|
||||||
+ ptr[j+1] * 0.597385942935943603515625f
|
|
||||||
- ptr[j+3] * 0.114591561257839202880859375f
|
|
||||||
+ ptr[j+5] * 0.01803267933428287506103515625f;
|
|
||||||
|
|
||||||
/* scale by 0.5f to give the sinc impulse response unit energy */
|
|
||||||
fixed_gain *= 0.5f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 108; j++)
|
|
||||||
ctx->decompressed_frame[108*i+j] = fixed_gain * excitation[5+j]
|
|
||||||
+ pitch_gain * ctx->adapt_cb[108*i+216-pitch_lag+j];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 324; i++)
|
|
||||||
ctx->adapt_cb[i] = ctx->decompressed_frame[108+i];
|
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
for (j = 0; j < 12; j++)
|
|
||||||
ctx->rc[j] += rc_delta[j];
|
|
||||||
|
|
||||||
utk_lp_synthesis_filter(ctx, 12*i, i < 3 ? 1 : 33);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void utk_init(UTKContext *ctx)
|
|
||||||
{
|
|
||||||
memset(ctx, 0, sizeof(*ctx));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0 //vgmstream extra: see flush_ea_mt
|
|
||||||
static void utk_set_fp(UTKContext *ctx, FILE *fp)
|
|
||||||
{
|
|
||||||
ctx->fp = fp;
|
|
||||||
|
|
||||||
/* reset the bit reader */
|
|
||||||
ctx->bits_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void utk_set_ptr(UTKContext *ctx, const uint8_t *ptr, const uint8_t *end)
|
|
||||||
{
|
|
||||||
ctx->ptr = ptr;
|
|
||||||
ctx->end = end;
|
|
||||||
|
|
||||||
/* reset the bit reader */
|
|
||||||
ctx->bits_count = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
** MicroTalk Revision 3 decoding function.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void utk_rev3_decode_frame(UTKContext *ctx)
|
|
||||||
{
|
|
||||||
int pcm_data_present = (utk_read_byte(ctx) == 0xee);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
utk_decode_frame(ctx);
|
|
||||||
|
|
||||||
/* unread the last 8 bits and reset the bit reader */
|
|
||||||
ctx->ptr--;
|
|
||||||
ctx->bits_count = 0;
|
|
||||||
|
|
||||||
if (pcm_data_present) {
|
|
||||||
/* Overwrite n samples at a given offset in the decoded frame with
|
|
||||||
** raw PCM data. */
|
|
||||||
int offset = utk_read_i16(ctx);
|
|
||||||
int count = utk_read_i16(ctx);
|
|
||||||
|
|
||||||
/* sx.exe does not do any bounds checking or clamping of these two
|
|
||||||
** fields (see 004274D1 in sx.exe v3.01.01), which means a specially
|
|
||||||
** crafted MT5:1 file can crash sx.exe.
|
|
||||||
** We will throw an error instead. */
|
|
||||||
if (offset < 0 || offset > 432) {
|
|
||||||
//fprintf(stderr, "error: invalid PCM offset %d\n", offset);
|
|
||||||
//exit(EXIT_FAILURE);
|
|
||||||
return; //vgmstream extra
|
|
||||||
}
|
|
||||||
if (count < 0 || count > 432 - offset) {
|
|
||||||
//fprintf(stderr, "error: invalid PCM count %d\n", count);
|
|
||||||
//exit(EXIT_FAILURE);
|
|
||||||
return; //vgmstream extra
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++)
|
|
||||||
ctx->decompressed_frame[offset+i] = (float)utk_read_i16(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************************************* */
|
|
||||||
|
|
||||||
ea_mt_codec_data *init_ea_mt(int channel_count, int pcm_blocks) {
|
|
||||||
ea_mt_codec_data *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;
|
|
||||||
|
ch_data->samples_used = 0;
|
||||||
|
ch_data->samples_filled = 432;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copy samples */
|
static void flush_ea_mt_offsets(VGMSTREAM *vgmstream, int is_start, int samples_discard) {
|
||||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
|
||||||
int x = UTK_ROUND(ctx->decompressed_frame[i]);
|
|
||||||
outbuf[sample_count] = (int16_t)UTK_CLAMP(x, -32768, 32767);
|
|
||||||
sample_count += channelspacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == frame_samples)
|
|
||||||
ctx->samples_filled = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void flush_ea_mt_internal(VGMSTREAM *vgmstream, int is_start) {
|
|
||||||
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
469
Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder_utk.h
Normal file
469
Frameworks/vgmstream/vgmstream/src/coding/ea_mt_decoder_utk.h
Normal 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_ */
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
|
@ -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_*/
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
const unsigned int blockSize = data->info.blockSize;
|
||||||
|
|
||||||
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) {
|
while (samples_done < samples_to_do) {
|
||||||
const unsigned int blockSize = data->info.blockSize;
|
|
||||||
const unsigned int channelCount = data->info.channelCount;
|
|
||||||
const unsigned int address = data->info.dataOffset + data->curblock * blockSize;
|
|
||||||
|
|
||||||
if (data->curblock >= data->info.blockCount) {
|
if (data->samples_filled) {
|
||||||
memset(outbuf, 0, (samples_to_do - samples_done) * channelCount * sizeof(sample));
|
int samples_to_get = data->samples_filled;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( read_streamfile((uint8_t*) hca_data, data->start + address, blockSize, data->streamfile) != blockSize )
|
if (data->samples_to_discard) {
|
||||||
break;
|
/* discard samples for looping */
|
||||||
|
if (samples_to_get > data->samples_to_discard)
|
||||||
if ( clHCA_Decode( hca, hca_data, blockSize, address ) < 0 )
|
samples_to_get = data->samples_to_discard;
|
||||||
break;
|
data->samples_to_discard -= samples_to_get;
|
||||||
|
|
||||||
++data->curblock;
|
|
||||||
|
|
||||||
clHCA_DecodeSamples16( hca, data->sample_buffer );
|
|
||||||
|
|
||||||
samples_remain = clHCA_samplesPerBlock;
|
|
||||||
data->sample_ptr = 0;
|
|
||||||
if ( data->samples_discard ) {
|
|
||||||
if ( samples_remain <= data->samples_discard ) {
|
|
||||||
data->samples_discard -= samples_remain;
|
|
||||||
samples_remain = 0;
|
|
||||||
}
|
}
|
||||||
else {
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mark consumed samples */
|
||||||
|
data->samples_consumed += samples_to_get;
|
||||||
|
data->samples_filled -= samples_to_get;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( samples_remain > samples_to_do - samples_done ) samples_remain = samples_to_do - samples_done;
|
void reset_hca(hca_codec_data * data) {
|
||||||
memcpy( outbuf, data->sample_buffer, samples_remain * channelCount * sizeof(sample) );
|
|
||||||
samples_done += samples_remain;
|
|
||||||
outbuf += samples_remain * channelCount;
|
|
||||||
data->sample_ptr = samples_remain;
|
|
||||||
}
|
|
||||||
|
|
||||||
free( hca_data );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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' */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,34 +21,41 @@ 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) {
|
if (first_sample == 0) {
|
||||||
ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0];
|
ch1->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][0];
|
||||||
ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1];
|
ch1->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][1];
|
||||||
ch2->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][0];
|
ch2->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][0];
|
||||||
ch2->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][1];
|
ch2->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][1];
|
||||||
ch1->adpcm_scale = read_16bitLE(offset+2,streamfile);
|
ch1->adpcm_scale = read_16bitLE(frame_offset+0x02,streamfile);
|
||||||
ch2->adpcm_scale = read_16bitLE(offset+4,streamfile);
|
ch2->adpcm_scale = read_16bitLE(frame_offset+0x04,streamfile);
|
||||||
ch1->adpcm_history1_16 = read_16bitLE(offset+6,streamfile);
|
ch1->adpcm_history1_16 = read_16bitLE(frame_offset+0x06,streamfile);
|
||||||
ch2->adpcm_history1_16 = read_16bitLE(offset+8,streamfile);
|
ch2->adpcm_history1_16 = read_16bitLE(frame_offset+0x08,streamfile);
|
||||||
ch1->adpcm_history2_16 = read_16bitLE(offset+10,streamfile);
|
ch1->adpcm_history2_16 = read_16bitLE(frame_offset+0x0a,streamfile);
|
||||||
ch2->adpcm_history2_16 = read_16bitLE(offset+12,streamfile);
|
ch2->adpcm_history2_16 = read_16bitLE(frame_offset+0x0c,streamfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write header samples (needed) */
|
||||||
|
if (first_sample==0) {
|
||||||
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--;
|
||||||
|
@ -58,104 +63,162 @@ void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first
|
||||||
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--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* decode nibbles */
|
||||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||||
int j;
|
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);
|
|
||||||
|
|
||||||
|
/* parse frame header */
|
||||||
if (first_sample == 0) {
|
if (first_sample == 0) {
|
||||||
ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0];
|
stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0];
|
||||||
ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1];
|
stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1];
|
||||||
ch1->adpcm_scale = read_16bitLE(offset+1,streamfile);
|
stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile);
|
||||||
ch1->adpcm_history1_16 = read_16bitLE(offset+3,streamfile);
|
stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x03,stream->streamfile);
|
||||||
ch1->adpcm_history2_16 = read_16bitLE(offset+5,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--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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 hist1,hist2, predicted;
|
||||||
VGMSTREAMCHANNEL *ch = &vgmstream->ch[0];
|
int sample_nibble = (i & 1) ? /* high nibble first */
|
||||||
int sample_nibble =
|
get_low_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) :
|
||||||
(i & 1 ?
|
get_high_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(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long msadpcm_bytes_to_samples(long bytes, int block_size, int channels) {
|
long msadpcm_bytes_to_samples(long bytes, int block_size, int channels) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
size_t bytes_per_frame, samples_per_frame;
|
||||||
|
uint8_t coef_index, shift_factor, flag;
|
||||||
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;
|
||||||
|
|
||||||
short scale;
|
/* external interleave (fixed size), mono */
|
||||||
int i;
|
bytes_per_frame = 0x10;
|
||||||
int32_t sample_count;
|
samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 28 */
|
||||||
uint8_t flag;
|
frames_in = first_sample / samples_per_frame;
|
||||||
|
|
||||||
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 hist2 = stream->adpcm_history2_32;
|
|
||||||
|
|
||||||
int i, sample_count, bytes_per_frame, samples_per_frame;
|
|
||||||
const int header_size = 1;
|
|
||||||
int framesin;
|
|
||||||
|
|
||||||
bytes_per_frame = frame_size - header_size;
|
|
||||||
samples_per_frame = bytes_per_frame * 2;
|
|
||||||
|
|
||||||
framesin = first_sample / samples_per_frame;
|
|
||||||
|
|
||||||
/* 1 byte header: predictor = 1st, shift = 2nd */
|
|
||||||
byte = (uint8_t)read_8bit(stream->offset+framesin*frame_size+0,stream->streamfile);
|
|
||||||
predict_nr = byte >> 4;
|
|
||||||
shift = byte & 0x0f;
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
63
Frameworks/vgmstream/vgmstream/src/coding/xmd_decoder.c
Normal file
63
Frameworks/vgmstream/vgmstream/src/coding/xmd_decoder.c
Normal 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;
|
||||||
|
}
|
|
@ -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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -66,14 +68,12 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
size_t block_data, header_size;
|
||||||
|
|
||||||
|
/* 0x00: "BLCK", rest: null */
|
||||||
|
block_data = read_32bitBE(block_offset+0x04,streamFile);
|
||||||
|
header_size = 0x20;
|
||||||
|
|
||||||
vgmstream->current_block_offset = block_offset;
|
vgmstream->current_block_offset = block_offset;
|
||||||
vgmstream->current_block_size = read_32bitBE(
|
vgmstream->current_block_size = block_data;
|
||||||
vgmstream->current_block_offset+4,
|
vgmstream->next_block_offset = block_offset + block_data*vgmstream->channels + header_size;
|
||||||
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++) {
|
for (i = 0; i < vgmstream->channels; i++) {
|
||||||
vgmstream->ch[i].offset = vgmstream->current_block_offset +
|
vgmstream->ch[i].offset = block_offset + header_size + block_data*i;
|
||||||
0x20 + vgmstream->current_block_size*i;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ???
|
|
||||||
// i set up 0 to current_block_size to make vgmstream not playing bad samples
|
|
||||||
// another way to do it ???
|
|
||||||
// (as the number of samples can be false in cd-xa due to multi-channels)
|
|
||||||
vgmstream->current_block_size = (currentChannel==-1 ? 0 : 0x70);
|
|
||||||
|
|
||||||
vgmstream->next_block_offset = vgmstream->current_block_offset + 0x80;
|
|
||||||
for (i = 0; i < vgmstream->channels; i++) {
|
for (i = 0; i < vgmstream->channels; i++) {
|
||||||
vgmstream->ch[i].offset = vgmstream->current_block_offset;
|
vgmstream->ch[i].offset = block_offset + 0x18;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
Frameworks/vgmstream/vgmstream/src/layout/blocked_xa_aiff.c
Normal file
19
Frameworks/vgmstream/vgmstream/src/layout/blocked_xa_aiff.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
39
Frameworks/vgmstream/vgmstream/src/layout/flat.c
Normal file
39
Frameworks/vgmstream/vgmstream/src/layout/flat.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,63 +1,91 @@
|
||||||
#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);
|
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 = vgmstream->interleave_block_size / frame_size * samples_per_frame;
|
samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame;
|
||||||
|
|
||||||
if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 &&
|
if (has_interleave_last &&
|
||||||
vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block > vgmstream->num_samples) {
|
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);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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) {
|
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)) {
|
||||||
/* we assume that the loop is not back into a short block */
|
/* handle looping, restore standard interleave sizes */
|
||||||
if (vgmstream->interleave_last_block_size && vgmstream->channels > 1) {
|
if (has_interleave_last) { /* assumes that won't loop back into a interleave_last */
|
||||||
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 (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)
|
||||||
|
|
||||||
if (samples_written+samples_to_do > sample_count)
|
|
||||||
samples_to_do = sample_count - samples_written;
|
samples_to_do = sample_count - samples_written;
|
||||||
|
|
||||||
|
if (samples_to_do == 0) { /* happens when interleave is not set */
|
||||||
|
VGM_LOG("layout_interleave: wrong samples_to_do found\n");
|
||||||
|
memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer);
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
/* move to next interleaved block when all samples are consumed */
|
||||||
if (vgmstream->samples_into_block == samples_this_block) {
|
if (vgmstream->samples_into_block == samples_this_block) {
|
||||||
int chan;
|
int ch;
|
||||||
if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 &&
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
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;
|
vgmstream->samples_into_block = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
45
Frameworks/vgmstream/vgmstream/src/meta/a2m.c
Normal file
45
Frameworks/vgmstream/vgmstream/src/meta/a2m.c
Normal 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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
49
Frameworks/vgmstream/vgmstream/src/meta/adpcm_capcom.c
Normal file
49
Frameworks/vgmstream/vgmstream/src/meta/adpcm_capcom.c
Normal 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;
|
||||||
|
}
|
|
@ -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<bruteframecount;i++) {
|
for (i = 0; i < bruteframe_count; 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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_*/
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
48
Frameworks/vgmstream/vgmstream/src/meta/ahv.c
Normal file
48
Frameworks/vgmstream/vgmstream/src/meta/ahv.c
Normal 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;
|
||||||
|
}
|
51
Frameworks/vgmstream/vgmstream/src/meta/aif_asobo.c
Normal file
51
Frameworks/vgmstream/vgmstream/src/meta/aif_asobo.c
Normal 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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,71 +1,60 @@
|
||||||
#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))
|
||||||
|
@ -73,95 +62,89 @@ VGMSTREAM * init_vgmstream_aix(STREAMFILE *streamFile) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
36
Frameworks/vgmstream/vgmstream/src/meta/ao.c
Normal file
36
Frameworks/vgmstream/vgmstream/src/meta/ao.c
Normal 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;
|
||||||
|
}
|
48
Frameworks/vgmstream/vgmstream/src/meta/apc.c
Normal file
48
Frameworks/vgmstream/vgmstream/src/meta/apc.c
Normal 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;
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
393
Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c
Normal file
393
Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c
Normal 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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
177
Frameworks/vgmstream/vgmstream/src/meta/ck.c
Normal file
177
Frameworks/vgmstream/vgmstream/src/meta/ck.c
Normal 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;
|
||||||
|
}
|
55
Frameworks/vgmstream/vgmstream/src/meta/csmp.c
Normal file
55
Frameworks/vgmstream/vgmstream/src/meta/csmp.c
Normal 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;
|
||||||
|
}
|
|
@ -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=
|
||||||
|
|
|
@ -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:
|
||||||
|
|
48
Frameworks/vgmstream/vgmstream/src/meta/derf.c
Normal file
48
Frameworks/vgmstream/vgmstream/src/meta/derf.c
Normal 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;
|
||||||
|
}
|
|
@ -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=
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
182
Frameworks/vgmstream/vgmstream/src/meta/ea_schl_streamfile.h
Normal file
182
Frameworks/vgmstream/vgmstream/src/meta/ea_schl_streamfile.h
Normal 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
Loading…
Reference in a new issue