diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index a7154d759..2dd6dd7b1 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -218,7 +218,6 @@ 834FE111215C79ED000A5D3D /* ck.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E8215C79EC000A5D3D /* ck.c */; }; 835027131ED119E000C25929 /* mta2_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 835027121ED119E000C25929 /* mta2_decoder.c */; }; 8350C0551E071881009E0A93 /* xma.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350C0541E071881009E0A93 /* xma.c */; }; - 8350C05A1E071990009E0A93 /* ps2_svag_snk.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350C0591E071990009E0A93 /* ps2_svag_snk.c */; }; 8351F3292212B53400A606E4 /* dsa_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F3252212B53300A606E4 /* dsa_decoder.c */; }; 8351F32D2212B57000A606E4 /* 208.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32A2212B57000A606E4 /* 208.c */; }; 8351F32E2212B57000A606E4 /* ubi_bao_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */; }; @@ -375,7 +374,6 @@ 836F6FFA18BDC2190095E648 /* ps2_spm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBE18BDC2190095E648 /* ps2_spm.c */; }; 836F6FFB18BDC2190095E648 /* ps2_sps.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBF18BDC2190095E648 /* ps2_sps.c */; }; 836F6FFC18BDC2190095E648 /* ps2_ster.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC018BDC2190095E648 /* ps2_ster.c */; }; - 836F700018BDC2190095E648 /* ps2_svag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC418BDC2190095E648 /* ps2_svag.c */; }; 836F700118BDC2190095E648 /* ps2_tec.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC518BDC2190095E648 /* ps2_tec.c */; }; 836F700218BDC2190095E648 /* ps2_tk5.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC618BDC2190095E648 /* ps2_tk5.c */; }; 836F700418BDC2190095E648 /* ps2_vas.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC818BDC2190095E648 /* ps2_vas.c */; }; @@ -544,6 +542,21 @@ 83AA5D241F6E2F9C0020821C /* awc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D201F6E2F9B0020821C /* awc.c */; }; 83AA5D251F6E2F9C0020821C /* hca_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA5D211F6E2F9C0020821C /* hca_keys.h */; }; 83AA5D271F6E2F9C0020821C /* stm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D231F6E2F9C0020821C /* stm.c */; }; + 83AA7F722519BFEA004C5298 /* vorbis_bitreader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7F6D2519BFEA004C5298 /* vorbis_bitreader.h */; }; + 83AA7F732519BFEA004C5298 /* mpeg_bitreader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7F712519BFEA004C5298 /* mpeg_bitreader.h */; }; + 83AA7F7D2519C042004C5298 /* dsb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F742519C041004C5298 /* dsb.c */; }; + 83AA7F7E2519C042004C5298 /* svag_kcet.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F752519C041004C5298 /* svag_kcet.c */; }; + 83AA7F7F2519C042004C5298 /* bsf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F762519C042004C5298 /* bsf.c */; }; + 83AA7F802519C042004C5298 /* sab_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7F772519C042004C5298 /* sab_streamfile.h */; }; + 83AA7F812519C042004C5298 /* silence.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F782519C042004C5298 /* silence.c */; }; + 83AA7F822519C042004C5298 /* ktsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F792519C042004C5298 /* ktsc.c */; }; + 83AA7F832519C042004C5298 /* adp_konami.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F7A2519C042004C5298 /* adp_konami.c */; }; + 83AA7F842519C042004C5298 /* svag_snk.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F7B2519C042004C5298 /* svag_snk.c */; }; + 83AA7F852519C042004C5298 /* zwv.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F7C2519C042004C5298 /* zwv.c */; }; + 83AA7F8A2519C076004C5298 /* decode.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7F862519C076004C5298 /* decode.h */; }; + 83AA7F8B2519C076004C5298 /* render.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7F872519C076004C5298 /* render.h */; }; + 83AA7F8C2519C076004C5298 /* render.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F882519C076004C5298 /* render.c */; }; + 83AA7F8D2519C076004C5298 /* decode.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F892519C076004C5298 /* decode.c */; }; 83AB8C761E8072A100086084 /* x360_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AB8C741E8072A100086084 /* x360_ast.c */; }; 83AFABBC23795202002F3947 /* xssb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AFABB923795201002F3947 /* xssb.c */; }; 83AFABBD23795202002F3947 /* ea_eaac_opus_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */; }; @@ -947,7 +960,6 @@ 834FE0E8215C79EC000A5D3D /* ck.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ck.c; sourceTree = ""; }; 835027121ED119E000C25929 /* mta2_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mta2_decoder.c; sourceTree = ""; }; 8350C0541E071881009E0A93 /* xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xma.c; sourceTree = ""; }; - 8350C0591E071990009E0A93 /* ps2_svag_snk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag_snk.c; sourceTree = ""; }; 8351F3252212B53300A606E4 /* dsa_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsa_decoder.c; sourceTree = ""; }; 8351F32A2212B57000A606E4 /* 208.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 208.c; sourceTree = ""; }; 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_bao_streamfile.h; sourceTree = ""; }; @@ -1105,7 +1117,6 @@ 836F6EBE18BDC2190095E648 /* ps2_spm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_spm.c; sourceTree = ""; }; 836F6EBF18BDC2190095E648 /* ps2_sps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sps.c; sourceTree = ""; }; 836F6EC018BDC2190095E648 /* ps2_ster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ster.c; sourceTree = ""; }; - 836F6EC418BDC2190095E648 /* ps2_svag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag.c; sourceTree = ""; }; 836F6EC518BDC2190095E648 /* ps2_tec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tec.c; sourceTree = ""; }; 836F6EC618BDC2190095E648 /* ps2_tk5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tk5.c; sourceTree = ""; }; 836F6EC818BDC2190095E648 /* ps2_vas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vas.c; sourceTree = ""; }; @@ -1273,6 +1284,21 @@ 83AA5D201F6E2F9B0020821C /* awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = awc.c; sourceTree = ""; }; 83AA5D211F6E2F9C0020821C /* hca_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys.h; sourceTree = ""; }; 83AA5D231F6E2F9C0020821C /* stm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stm.c; sourceTree = ""; }; + 83AA7F6D2519BFEA004C5298 /* vorbis_bitreader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vorbis_bitreader.h; sourceTree = ""; }; + 83AA7F712519BFEA004C5298 /* mpeg_bitreader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpeg_bitreader.h; sourceTree = ""; }; + 83AA7F742519C041004C5298 /* dsb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsb.c; sourceTree = ""; }; + 83AA7F752519C041004C5298 /* svag_kcet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svag_kcet.c; sourceTree = ""; }; + 83AA7F762519C042004C5298 /* bsf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bsf.c; sourceTree = ""; }; + 83AA7F772519C042004C5298 /* sab_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sab_streamfile.h; sourceTree = ""; }; + 83AA7F782519C042004C5298 /* silence.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = silence.c; sourceTree = ""; }; + 83AA7F792519C042004C5298 /* ktsc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ktsc.c; sourceTree = ""; }; + 83AA7F7A2519C042004C5298 /* adp_konami.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adp_konami.c; sourceTree = ""; }; + 83AA7F7B2519C042004C5298 /* svag_snk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svag_snk.c; sourceTree = ""; }; + 83AA7F7C2519C042004C5298 /* zwv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = zwv.c; sourceTree = ""; }; + 83AA7F862519C076004C5298 /* decode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decode.h; sourceTree = ""; }; + 83AA7F872519C076004C5298 /* render.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = render.h; sourceTree = ""; }; + 83AA7F882519C076004C5298 /* render.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = render.c; sourceTree = ""; }; + 83AA7F892519C076004C5298 /* decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = decode.c; sourceTree = ""; }; 83AB8C741E8072A100086084 /* x360_ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_ast.c; sourceTree = ""; }; 83AFABB923795201002F3947 /* xssb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xssb.c; sourceTree = ""; }; 83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_eaac_opus_streamfile.h; sourceTree = ""; }; @@ -1469,11 +1495,15 @@ 836F6DDF18BDC2180095E648 /* coding */, 836F6DFF18BDC2180095E648 /* layout */, 836F6E2718BDC2180095E648 /* meta */, + 83AA7F892519C076004C5298 /* decode.c */, + 83AA7F862519C076004C5298 /* decode.h */, 83A3F0711E3AD8B900D6A794 /* formats.c */, 83C7282522BC8C1400678B4A /* mixing.c */, 83C7282422BC8C1400678B4A /* mixing.h */, 83C7282622BC8C1400678B4A /* plugins.c */, 83C7282322BC8C1300678B4A /* plugins.h */, + 83AA7F882519C076004C5298 /* render.c */, + 83AA7F872519C076004C5298 /* render.h */, 83A3F0731E3AD8B900D6A794 /* stack_alloc.h */, 836F6F1718BDC2190095E648 /* streamfile.c */, 836F6F1818BDC2190095E648 /* streamfile.h */, @@ -1533,6 +1563,7 @@ 836F6DEC18BDC2180095E648 /* lsf_decoder.c */, 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */, 836F6DEE18BDC2180095E648 /* mp4_aac_decoder.c */, + 83AA7F712519BFEA004C5298 /* mpeg_bitreader.h */, 839E21D91F2EDAF000EE54D7 /* mpeg_custom_utils_ahx.c */, 83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */, 83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */, @@ -1562,6 +1593,7 @@ 8373341023F60C7A00DE14DC /* tgcadpcm_decoder.c */, 837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */, 83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */, + 83AA7F6D2519BFEA004C5298 /* vorbis_bitreader.h */, 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */, 839E21DC1F2EDAF000EE54D7 /* vorbis_custom_data_wwise.h */, 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */, @@ -1643,6 +1675,7 @@ 836F6E2A18BDC2180095E648 /* aax.c */, 837CEAD623487E8300E62A4A /* acb.c */, 836F6E2B18BDC2180095E648 /* acm.c */, + 83AA7F7A2519C042004C5298 /* adp_konami.c */, 834FE0CF215C79E8000A5D3D /* adpcm_capcom.c */, 836F6E2C18BDC2180095E648 /* ads.c */, 8349A9021FE6258100E26435 /* adx_keys.h */, @@ -1682,6 +1715,7 @@ 8373342523F60CDC00DE14DC /* bnsf_keys.h */, 836F6E3918BDC2180095E648 /* bnsf.c */, 836F6E3A18BDC2180095E648 /* brstm.c */, + 83AA7F762519C042004C5298 /* bsf.c */, 83EDE5D71A70951A005F5D84 /* btsnd.c */, 835C883122CC17BD001B4B3F /* bwav.c */, 8306B0CF2098458F000302D4 /* caf.c */, @@ -1703,6 +1737,7 @@ 834FE0CD215C79E8000A5D3D /* derf.c */, 8385D4E2245174C600FF8E67 /* diva.c */, 836F6E4318BDC2180095E648 /* dmsg_segh.c */, + 83AA7F742519C041004C5298 /* dsb.c */, 8351F32C2212B57000A606E4 /* dsf.c */, 83299FCF1E7660C7003A3242 /* dsp_adx.c */, 836F6E4418BDC2180095E648 /* dsp_bdsp.c */, @@ -1766,6 +1801,7 @@ 834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */, 83A21F83201D8981000F04B9 /* kma9.c */, 836F6E5918BDC2180095E648 /* kraw.c */, + 83AA7F792519C042004C5298 /* ktsc.c */, 83D20074248DDB760048BD24 /* ktsr.c */, 830EBE122004656E0023AA10 /* ktss.c */, 8373342423F60CDB00DE14DC /* kwb.c */, @@ -1898,8 +1934,6 @@ 836F6EBE18BDC2190095E648 /* ps2_spm.c */, 836F6EBF18BDC2190095E648 /* ps2_sps.c */, 836F6EC018BDC2190095E648 /* ps2_ster.c */, - 8350C0591E071990009E0A93 /* ps2_svag_snk.c */, - 836F6EC418BDC2190095E648 /* ps2_svag.c */, 836F6EC518BDC2190095E648 /* ps2_tec.c */, 836F6EC618BDC2190095E648 /* ps2_tk5.c */, 832BF80D21E05148006F50F1 /* ps2_va3.c */, @@ -1937,6 +1971,7 @@ 836F6EE818BDC2190095E648 /* rwsd.c */, 836F6EE918BDC2190095E648 /* rwx.c */, 836F6EEA18BDC2190095E648 /* s14_sss.c */, + 83AA7F772519C042004C5298 /* sab_streamfile.h */, 8349A8F11FE6257D00E26435 /* sab.c */, 83D20078248DDB770048BD24 /* sadf.c */, 83D20079248DDB770048BD24 /* sadl.c */, @@ -1953,6 +1988,7 @@ 83C7280822BC893C00678B4A /* sfh.c */, 836F6EF118BDC2190095E648 /* sfl.c */, 831BA6111EAC61A500CF89B0 /* sgxd.c */, + 83AA7F782519C042004C5298 /* silence.c */, 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */, 836F6EF218BDC2190095E648 /* sli.c */, 8306B0D32098458F000302D4 /* smc_smh.c */, @@ -1972,6 +2008,8 @@ 836F6EF718BDC2190095E648 /* str_snds.c */, 834FE0C2215C79E6000A5D3D /* str_wav.c */, 83C7280722BC893B00678B4A /* strm_abylight.c */, + 83AA7F752519C041004C5298 /* svag_kcet.c */, + 83AA7F7B2519C042004C5298 /* svag_snk.c */, 834FE0D7215C79EA000A5D3D /* svg.c */, 836F6EF918BDC2190095E648 /* svs.c */, 83D0381724A4129A004CF90F /* swav.c */, @@ -2065,6 +2103,7 @@ 832BF80E21E05149006F50F1 /* zsnd_streamfile.h */, 832BF80F21E05149006F50F1 /* zsnd.c */, 836F6F1618BDC2190095E648 /* zwdsp.c */, + 83AA7F7C2519C042004C5298 /* zwv.c */, ); path = meta; sourceTree = ""; @@ -2099,6 +2138,7 @@ 836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */, 83031EC9243C50A800C3F3E0 /* circus_decoder_lzxpcm.h in Headers */, 83C7282222BC893D00678B4A /* mta2_streamfile.h in Headers */, + 83AA7F802519C042004C5298 /* sab_streamfile.h in Headers */, 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */, 834FE0EC215C79ED000A5D3D /* kma9_streamfile.h in Headers */, 8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */, @@ -2113,6 +2153,7 @@ 83C7282822BC8C1500678B4A /* mixing.h in Headers */, 832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */, 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */, + 83AA7F722519BFEA004C5298 /* vorbis_bitreader.h in Headers */, 834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */, 835C883722CC17BE001B4B3F /* ogg_vorbis_streamfile.h in Headers */, 837CEAFE23487F2C00E62A4A /* jstm_streamfile.h in Headers */, @@ -2126,6 +2167,7 @@ 8323894B1D22419B00482226 /* clHCA.h in Headers */, 83C7281122BC893D00678B4A /* 9tav_streamfile.h in Headers */, 83C7280F22BC893D00678B4A /* xwb_xsb.h in Headers */, + 83AA7F732519BFEA004C5298 /* mpeg_bitreader.h in Headers */, 83C7281622BC893D00678B4A /* xwma_konami_streamfile.h in Headers */, 839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */, 839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */, @@ -2138,6 +2180,7 @@ 83031EC5243C50A800C3F3E0 /* circus_decoder_lib_data.h in Headers */, 834FE0B5215C798C000A5D3D /* acm_decoder_libacm.h in Headers */, 839E21E61F2EDAF100EE54D7 /* vorbis_custom_data_wwise.h in Headers */, + 83AA7F8A2519C076004C5298 /* decode.h in Headers */, 8373342D23F60CDC00DE14DC /* bnsf_keys.h in Headers */, 834FE103215C79ED000A5D3D /* ea_schl_streamfile.h in Headers */, 83F0AA6021E2028C004BBC04 /* vsv_streamfile.h in Headers */, @@ -2151,6 +2194,7 @@ 8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */, 83031EDD243C510500C3F3E0 /* xnb_streamfile.h in Headers */, 83D2007B248DDB770048BD24 /* mups_streamfile.h in Headers */, + 83AA7F8B2519C076004C5298 /* render.h in Headers */, 8373341623F60C7B00DE14DC /* g7221_decoder_aes.h in Headers */, 8373341923F60C7B00DE14DC /* g7221_decoder_lib_data.h in Headers */, 83AFABBD23795202002F3947 /* ea_eaac_opus_streamfile.h in Headers */, @@ -2382,7 +2426,6 @@ 836F6FD718BDC2190095E648 /* ps2_filp.c in Sources */, 83FF0EBC1E93282100C58054 /* wwise.c in Sources */, 836F6F7018BDC2190095E648 /* apple_caff.c in Sources */, - 836F700018BDC2190095E648 /* ps2_svag.c in Sources */, 836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */, 836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */, 837CEB072348809400E62A4A /* seb.c in Sources */, @@ -2445,6 +2488,7 @@ 836F705618BDC2190095E648 /* util.c in Sources */, 8306B0A820984552000302D4 /* blocked_ps2_iab.c in Sources */, 8306B0EA20984590000302D4 /* caf.c in Sources */, + 83AA7F842519C042004C5298 /* svag_snk.c in Sources */, 834FE107215C79ED000A5D3D /* mib_mih.c in Sources */, 836F703618BDC2190095E648 /* thp.c in Sources */, 836F6F7818BDC2190095E648 /* Cstr.c in Sources */, @@ -2481,6 +2525,7 @@ 836F6F6618BDC2190095E648 /* aax.c in Sources */, 836F6FD618BDC2190095E648 /* ps2_exst.c in Sources */, 8306B0BC20984552000302D4 /* blocked_vs.c in Sources */, + 83AA7F812519C042004C5298 /* silence.c in Sources */, 834FE0B3215C798C000A5D3D /* acm_decoder_util.c in Sources */, 837CEA7A23487E2500E62A4A /* ffmpeg_decoder_utils.c in Sources */, 837CEAF823487F2C00E62A4A /* xmv_valve.c in Sources */, @@ -2497,7 +2542,9 @@ 836F6F6F18BDC2190095E648 /* akb.c in Sources */, 83EED5D6203A8BD7008BEB45 /* blocked_ea_swvr.c in Sources */, 8349A9181FE6258200E26435 /* ea_1snh.c in Sources */, + 83AA7F7E2519C042004C5298 /* svag_kcet.c in Sources */, 8373342C23F60CDC00DE14DC /* kwb.c in Sources */, + 83AA7F852519C042004C5298 /* zwv.c in Sources */, 83EED5D4203A8BC7008BEB45 /* aus.c in Sources */, 836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */, 832BF81D21E0514B006F50F1 /* msf_tamasoft.c in Sources */, @@ -2570,6 +2617,7 @@ 836F705218BDC2190095E648 /* zwdsp.c in Sources */, 836F6FFB18BDC2190095E648 /* ps2_sps.c in Sources */, 836F6F2018BDC2190095E648 /* adx_decoder.c in Sources */, + 83AA7F8D2519C076004C5298 /* decode.c in Sources */, 8349A8EC1FE6253900E26435 /* blocked_vawx.c in Sources */, 834FE0EA215C79ED000A5D3D /* aif_asobo.c in Sources */, 836F700418BDC2190095E648 /* ps2_vas.c in Sources */, @@ -2591,6 +2639,7 @@ 836F6FAF18BDC2190095E648 /* ngc_dsp_std.c in Sources */, 836F6F7118BDC2190095E648 /* ast.c in Sources */, 834FE0BF215C79A9000A5D3D /* flat.c in Sources */, + 83AA7F7D2519C042004C5298 /* dsb.c in Sources */, 836F6F6B18BDC2190095E648 /* agsc.c in Sources */, 836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */, 837CEB0023487F2C00E62A4A /* smk.c in Sources */, @@ -2624,6 +2673,7 @@ 837CEAFF23487F2C00E62A4A /* raw_snds.c in Sources */, 83031EDC243C510500C3F3E0 /* vid1.c in Sources */, 836F700218BDC2190095E648 /* ps2_tk5.c in Sources */, + 83AA7F8C2519C076004C5298 /* render.c in Sources */, 83AA5D271F6E2F9C0020821C /* stm.c in Sources */, 831BA61D1EAC61A500CF89B0 /* ubi_raki.c in Sources */, 8306B0A520984552000302D4 /* blocked_ea_wve_au00.c in Sources */, @@ -2672,7 +2722,6 @@ 8306B0AB20984552000302D4 /* layered.c in Sources */, 8306B0EC20984590000302D4 /* pcm_sre.c in Sources */, 836F6FC818BDC2190095E648 /* pos.c in Sources */, - 8350C05A1E071990009E0A93 /* ps2_svag_snk.c in Sources */, 836F6F8918BDC2190095E648 /* gca.c in Sources */, 836F6FA418BDC2190095E648 /* nds_hwas.c in Sources */, 83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */, @@ -2747,6 +2796,7 @@ 83AFABBC23795202002F3947 /* xssb.c in Sources */, 836F704818BDC2190095E648 /* xbox_ims.c in Sources */, 837CEAF623487F2C00E62A4A /* mzrt.c in Sources */, + 83AA7F832519C042004C5298 /* adp_konami.c in Sources */, 836F6F7518BDC2190095E648 /* bnsf.c in Sources */, 836F704318BDC2190095E648 /* wpd.c in Sources */, 8349A9081FE6258200E26435 /* ezw.c in Sources */, @@ -2760,6 +2810,7 @@ 83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */, 8323894A1D22419B00482226 /* clHCA.c in Sources */, 836F702E18BDC2190095E648 /* sli.c in Sources */, + 83AA7F822519C042004C5298 /* ktsc.c in Sources */, 836F6FDE18BDC2190095E648 /* ps2_ild.c in Sources */, 836F703E18BDC2190095E648 /* wii_ras.c in Sources */, 834FE0EE215C79ED000A5D3D /* ue4opus.c in Sources */, @@ -2797,6 +2848,7 @@ 83D2007C248DDB770048BD24 /* ktsr.c in Sources */, 836F704E18BDC2190095E648 /* xss.c in Sources */, 836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */, + 83AA7F7F2519C042004C5298 /* bsf.c in Sources */, 836F6F4118BDC2190095E648 /* blocked.c in Sources */, 836F6F3B18BDC2190095E648 /* ws_decoder.c in Sources */, 838BDB6E1D3B043C0022CA6F /* ffmpeg.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c index 9814752ab..d87fe6fa1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c @@ -5,17 +5,17 @@ /* libacm 1.2 (despite what libacm.h says) from: https://github.com/markokr/libacm */ typedef struct { - STREAMFILE *streamfile; + STREAMFILE* streamfile; /* reference */ int offset; } acm_io_config; -static int acm_read_streamfile(void *ptr, int size, int n, void *arg); -static int acm_seek_streamfile(void *arg, int offset, int whence); -static int acm_get_length_streamfile(void *arg); -acm_codec_data *init_acm(STREAMFILE *streamFile, int force_channel_number) { +static int acm_read_streamfile(void* ptr, int size, int n, void* arg); +static int acm_seek_streamfile(void* arg, int offset, int whence); +static int acm_get_length_streamfile(void* arg); + +acm_codec_data* init_acm(STREAMFILE* sf, int force_channel_number) { acm_codec_data* data = NULL; - char filename[PATH_LIMIT]; data = calloc(1,sizeof(acm_codec_data)); @@ -24,15 +24,14 @@ acm_codec_data *init_acm(STREAMFILE *streamFile, int force_channel_number) { data->io_config = calloc(1,sizeof(acm_io_config)); if (!data->io_config) goto fail; - streamFile->get_name(streamFile,filename,sizeof(filename)); - data->streamfile = open_streamfile(streamFile,filename); + data->streamfile = reopen_streamfile(sf, 0); if (!data->streamfile) goto fail; /* Setup libacm decoder, needs read callbacks and a parameter for said callbacks */ { - ACMStream *handle = NULL; + ACMStream* handle = NULL; int res; - acm_io_config *io_config = data->io_config; + acm_io_config* io_config = data->io_config; acm_io_callbacks io_callbacks = {0}; io_config->offset = 0; @@ -60,8 +59,8 @@ fail: return NULL; } -void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, int channelspacing) { - ACMStream * acm = data->handle; +void decode_acm(acm_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channelspacing) { + ACMStream* acm = data->handle; int32_t samples_read = 0; while (samples_read < samples_to_do) { @@ -79,14 +78,14 @@ void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, in } } -void reset_acm(acm_codec_data *data) { +void reset_acm(acm_codec_data* data) { if (!data || !data->handle) return; acm_seek_pcm(data->handle, 0); } -void free_acm(acm_codec_data *data) { +void free_acm(acm_codec_data* data) { if (!data) return; @@ -96,6 +95,11 @@ void free_acm(acm_codec_data *data) { free(data); } +STREAMFILE* acm_get_streamfile(acm_codec_data* data) { + if (!data) return NULL; + return data->streamfile; +} + /* ******************************* */ static int acm_read_streamfile(void *ptr, int size, int n, void *arg) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/adx_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/adx_decoder.c index f906ad0f8..36e2099ec 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/adx_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/adx_decoder.c @@ -1,7 +1,7 @@ #include "coding.h" #include "../util.h" -void decode_adx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_size, coding_t coding_type) { +void decode_adx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_size, coding_t coding_type) { uint8_t frame[0x12] = {0}; off_t frame_offset; int i, frames_in, sample_count = 0; @@ -21,12 +21,22 @@ void decode_adx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing frame_offset = stream->offset + bytes_per_frame * frames_in; read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ - scale = get_16bitBE(frame+0x00); + + scale = get_s16be(frame+0x00); switch(coding_type) { case coding_CRI_ADX: scale = scale + 1; coef1 = stream->adpcm_coef[0]; coef2 = stream->adpcm_coef[1]; + + /* Detect EOF scale (0x8001) found in some ADX of any type, signals "stop decoding" (without this frame?). + * Normally num_samples stops right before it, but ADXPLAY will honor it even in the middle on a file + * (may repeat last sample buffer). Some Baroque (SAT) videos set it on file end, but num_samples goes beyond. + * Just the upper bit triggers it even in encrypted ADX (max is 0x7FFF), but the check only here just in case. */ + if (frame[0] == 0x80 && frame[1] == 0x01) { + scale = 0; /* fix scaled click, maybe should just exit */ + VGM_LOG("ADX: reached EOF scale\n"); + } break; case coding_CRI_ADX_exp: scale = 1 << (12 - scale); @@ -79,6 +89,6 @@ void decode_adx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing } } -void adx_next_key(VGMSTREAMCHANNEL * stream) { +void adx_next_key(VGMSTREAMCHANNEL* stream) { stream->adx_xor = (stream->adx_xor * stream->adx_mult + stream->adx_add) & 0x7fff; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/at3plus_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/at3plus_decoder.c index ff00f8bbf..9347d8a73 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/at3plus_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/at3plus_decoder.c @@ -4,6 +4,14 @@ #ifdef VGM_USE_MAIATRAC3PLUS #include "maiatrac3plus.h" +struct maiatrac3plus_codec_data { + sample_t* buffer; + int channels; + int samples_discard; + void* handle; +}; + + maiatrac3plus_codec_data *init_at3plus() { maiatrac3plus_codec_data *data = malloc(sizeof(maiatrac3plus_codec_data)); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c index 045f33011..d609f12cc 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c @@ -1,19 +1,15 @@ #include "coding.h" #ifdef VGM_USE_ATRAC9 -#ifdef __MACOSX__ -#include -#else -#include "libatrac9.h" -#endif +#include "libatrac9/libatrac9.h" /* opaque struct */ struct atrac9_codec_data { - uint8_t *data_buffer; + uint8_t* data_buffer; size_t data_buffer_size; - sample_t *sample_buffer; + sample_t* sample_buffer; size_t samples_filled; /* number of samples in the buffer */ size_t samples_used; /* number of samples extracted from the buffer */ @@ -21,15 +17,15 @@ struct atrac9_codec_data { atrac9_config config; - void *handle; /* decoder handle */ + 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; uint8_t config_data[4]; - atrac9_codec_data *data = NULL; + atrac9_codec_data* data = NULL; data = calloc(1, sizeof(atrac9_codec_data)); if (!data) goto fail; @@ -69,9 +65,9 @@ fail: return NULL; } -void decode_atrac9(VGMSTREAM *vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels) { - VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; - atrac9_codec_data * data = vgmstream->codec_data; +void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { + VGMSTREAMCHANNEL* stream = &vgmstream->ch[0]; + atrac9_codec_data* data = vgmstream->codec_data; int samples_done = 0; @@ -138,8 +134,7 @@ decode_fail: 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; +void reset_atrac9(atrac9_codec_data* data) { if (!data) return; if (!data->handle) @@ -171,11 +166,11 @@ fail: return; /* decode calls should fail... */ } -void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) { - atrac9_codec_data *data = vgmstream->codec_data; +void seek_atrac9(VGMSTREAM* vgmstream, int32_t num_sample) { + atrac9_codec_data* data = vgmstream->codec_data; if (!data) return; - reset_atrac9(vgmstream); + reset_atrac9(data); /* find closest offset to desired sample, and samples to discard after that offset to reach loop */ { @@ -216,7 +211,7 @@ void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) { } -void free_atrac9(atrac9_codec_data *data) { +void free_atrac9(atrac9_codec_data* data) { if (!data) return; if (data->handle) Atrac9ReleaseHandle(data->handle); @@ -269,7 +264,7 @@ fail: return 0; } -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) { return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c index b2805ccce..402c9b1d6 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c @@ -1,231 +1,230 @@ -#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 %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done)); - memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); -} - -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 +#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.11.0 */ +#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_t* buffer; + + sample_t* 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_t* 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 %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done)); + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); +} + +void reset_celt_fsb(celt_codec_data* 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(data); + + data->samples_to_discard = num_sample; + + /* loop offsets are set during decode; force them to stream start so discard works */ + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; +} + +void free_celt_fsb(celt_codec_data* data) { + if (!data) return; + + switch(data->version) { + case CELT_0_06_1: + if (data->decoder_handle) celt_0061_decoder_destroy(data->decoder_handle); + if (data->mode_handle) celt_0061_mode_destroy(data->mode_handle); + break; + + case CELT_0_11_0: + if (data->decoder_handle) celt_0110_decoder_destroy(data->decoder_handle); + if (data->mode_handle) celt_0110_mode_destroy(data->mode_handle); + break; + + default: + break; + } + + free(data->sample_buffer); + free(data); +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index e09a06385..7a2410304 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -4,188 +4,229 @@ #include "../vgmstream.h" /* adx_decoder */ -void decode_adx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes, coding_t coding_type); -void adx_next_key(VGMSTREAMCHANNEL * stream); +void decode_adx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes, coding_t coding_type); +void adx_next_key(VGMSTREAMCHANNEL* stream); + /* g721_decoder */ -void decode_g721(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void g72x_init_state(struct g72x_state *state_ptr); +void decode_g721(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void g72x_init_state(struct g72x_state* state_ptr); + /* ima_decoder */ -void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample_t * 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_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_wv6_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_alp_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ffta2_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_blitz_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_mtf_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); +void decode_standard_ima(VGMSTREAMCHANNEL* stream, sample_t* 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_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_snds_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_otns_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_wv6_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_alp_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ffta2_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_blitz_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_mtf_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); -void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_ms_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_ref_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_xbox_ima(VGMSTREAMCHANNEL * stream, sample_t * 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_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config); -void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format); +void decode_xbox_ima(VGMSTREAMCHANNEL* stream, sample_t* 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_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_nds_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_dat4_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_rad_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_rad_ima_mono(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_apple_ima4(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_fsb_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_wwise_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_awc_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config); +void decode_h4m_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format); +void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); size_t ima_bytes_to_samples(size_t bytes, int channels); size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels); size_t xbox_ima_bytes_to_samples(size_t bytes, int channels); size_t dat4_ima_bytes_to_samples(size_t bytes, int channels); size_t apple_ima4_bytes_to_samples(size_t bytes, int channels); + /* ngc_dsp_decoder */ -void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ngc_dsp_subint(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int interleave); +void decode_ngc_dsp(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ngc_dsp_subint(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int interleave); size_t dsp_bytes_to_samples(size_t bytes, int channels); int32_t dsp_nibbles_to_samples(int32_t nibbles); -void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); -void dsp_read_coefs_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); -void dsp_read_coefs(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be); -void dsp_read_hist_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); -void dsp_read_hist_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); -void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be); +void dsp_read_coefs_be(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing); +void dsp_read_coefs_le(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing); +void dsp_read_coefs(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing, int be); +void dsp_read_hist_be(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing); +void dsp_read_hist_le(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing); +void dsp_read_hist(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing, int be); + /* ngc_dtk_decoder */ -void decode_ngc_dtk(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ngc_dtk(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); + /* ngc_afc_decoder */ -void decode_ngc_afc(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ngc_afc(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* vadpcm_decoder */ -void decode_vadpcm(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int order); +void decode_vadpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int order); //int32_t vadpcm_bytes_to_samples(size_t bytes, int channels); void vadpcm_read_coefs_be(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, int order, int entries, int ch); + /* pcm_decoder */ -void decode_pcm16le(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm16be(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); -void decode_pcm8(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm4(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_pcm4_unsigned(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_ulaw(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_alaw(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); +void decode_pcm16le(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm16be(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm16_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); +void decode_pcm8(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_unsigned(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_sb(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm4(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_pcm4_unsigned(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ulaw(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ulaw_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_alaw(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcmfloat(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample); + /* psx_decoder */ void decode_psx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags, int config); void decode_psx_configurable(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size, int config); -void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); -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_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty); +void decode_psx_pivotal(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); +int ps_find_loop_offsets(STREAMFILE* sf, 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* sf, 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_find_padding(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty); 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); -int ps_check_format(STREAMFILE *streamFile, off_t offset, size_t max); +int ps_check_format(STREAMFILE* sf, off_t offset, size_t max); + /* psv_decoder */ -void decode_hevag(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* xa_decoder */ -void decode_xa(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* 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, int is_form2); + /* ea_xa_decoder */ -void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_ea_xa_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xa(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xa_int(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xa_v2(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_maxis_xa(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); int32_t ea_xa_bytes_to_samples(size_t bytes, int channels); + /* ea_xas_decoder */ -void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xas_v0(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xas_v1(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); + /* sdx2_decoder */ -void decode_sdx2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_sdx2_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_cbd2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_cbd2_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_sdx2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_sdx2_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_cbd2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_cbd2_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* ws_decoder */ -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_codec_data *init_acm(STREAMFILE *streamFile, int force_channel_number); -void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, int channelspacing); -void reset_acm(acm_codec_data *data); -void free_acm(acm_codec_data *data); +acm_codec_data* init_acm(STREAMFILE* sf, int force_channel_number); +void decode_acm(acm_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channelspacing); +void reset_acm(acm_codec_data* data); +void free_acm(acm_codec_data* data); +STREAMFILE* acm_get_streamfile(acm_codec_data* data); + /* nwa_decoder */ -void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do); +typedef struct nwa_codec_data nwa_codec_data; + +nwa_codec_data* init_nwa(STREAMFILE* sf); +void decode_nwa(nwa_codec_data* data, sample_t* outbuf, int32_t samples_to_do); +void seek_nwa(nwa_codec_data *data, int32_t sample); +void reset_nwa(nwa_codec_data *data); +void free_nwa(nwa_codec_data* data); +STREAMFILE* nwa_get_streamfile(nwa_codec_data* data); /* msadpcm_decoder */ -void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t first_sample, int32_t samples_to_do); -void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first_sample, int32_t samples_to_do); +void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_msadpcm_ck(VGMSTREAM* vgmstream, sample_t* 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); -int msadpcm_check_coefs(STREAMFILE *sf, off_t offset); +int msadpcm_check_coefs(STREAMFILE* sf, off_t offset); + /* yamaha_decoder */ -void decode_aica(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); -void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_aica(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); +void decode_aska(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_nxap(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t yamaha_bytes_to_samples(size_t bytes, int channels); size_t aska_bytes_to_samples(size_t bytes, int channels); + /* tgcadpcm_decoder */ -void decode_tgc(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int32_t first_sample, int32_t samples_to_do); +void decode_tgc(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int32_t first_sample, int32_t samples_to_do); + /* nds_procyon_decoder */ -void decode_nds_procyon(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_nds_procyon(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* 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 */ -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 */ -void decode_lsf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_lsf(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* mtaf_decoder */ -void decode_mtaf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_mtaf(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); + /* mta2_decoder */ -void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_mta2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); + /* mc3_decoder */ -void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_mc3(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); + /* fadpcm_decoder */ -void decode_fadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_fadpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* asf_decoder */ -void decode_asf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_asf(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); int32_t asf_bytes_to_samples(size_t bytes, int channels); + /* dsa_decoder */ -void decode_dsa(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_dsa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* xmd_decoder */ -void decode_xmd(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); +void decode_xmd(VGMSTREAMCHANNEL* stream, sample_t* 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); +void decode_derf(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* circus_decoder */ typedef struct circus_codec_data circus_codec_data; + circus_codec_data* init_circus_vq(STREAMFILE* sf, off_t start, uint8_t codec, uint8_t flags); void decode_circus_vq(circus_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels); void reset_circus_vq(circus_codec_data* data); @@ -193,93 +234,205 @@ void seek_circus_vq(circus_codec_data* data, int32_t num_sample); void free_circus_vq(circus_codec_data* data); void decode_circus_adpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* oki_decoder */ -void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode); -void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_oki4s(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_pcfx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode); +void decode_oki16(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_oki4s(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); size_t oki_bytes_to_samples(size_t bytes, int channels); + /* ptadpcm_decoder */ -void decode_ptadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); +void decode_ptadpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size); + /* ubi_adpcm_decoder */ -ubi_adpcm_codec_data *init_ubi_adpcm(STREAMFILE *streamFile, off_t offset, int channels); -void decode_ubi_adpcm(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do); -void reset_ubi_adpcm(ubi_adpcm_codec_data *data); -void seek_ubi_adpcm(ubi_adpcm_codec_data *data, int32_t num_sample); -void free_ubi_adpcm(ubi_adpcm_codec_data *data); -int ubi_adpcm_get_samples(ubi_adpcm_codec_data *data); +typedef struct ubi_adpcm_codec_data ubi_adpcm_codec_data; + +ubi_adpcm_codec_data* init_ubi_adpcm(STREAMFILE* sf, off_t offset, int channels); +void decode_ubi_adpcm(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do); +void reset_ubi_adpcm(ubi_adpcm_codec_data* data); +void seek_ubi_adpcm(ubi_adpcm_codec_data* data, int32_t num_sample); +void free_ubi_adpcm(ubi_adpcm_codec_data* data); +int ubi_adpcm_get_samples(ubi_adpcm_codec_data* data); + /* imuse_decoder */ typedef struct imuse_codec_data imuse_codec_data; -imuse_codec_data *init_imuse(STREAMFILE* sf, int channels); + +imuse_codec_data* init_imuse(STREAMFILE* sf, int channels); void decode_imuse(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do); void reset_imuse(imuse_codec_data* data); void seek_imuse(imuse_codec_data* data, int32_t num_sample); void free_imuse(imuse_codec_data* data); + /* ea_mt_decoder*/ -ea_mt_codec_data *init_ea_mt(int channels, int type); -ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets); -void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); -void reset_ea_mt(VGMSTREAM * vgmstream); -void flush_ea_mt(VGMSTREAM *vgmstream); -void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample); -void free_ea_mt(ea_mt_codec_data *data, int channels); +typedef struct ea_mt_codec_data ea_mt_codec_data; + +ea_mt_codec_data* init_ea_mt(int channels, int type); +ea_mt_codec_data* init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t* loop_offsets); +void decode_ea_mt(VGMSTREAM* vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); +void reset_ea_mt(VGMSTREAM* vgmstream); +void flush_ea_mt(VGMSTREAM* vgmstream); +void seek_ea_mt(VGMSTREAM* vgmstream, int32_t num_sample); +void free_ea_mt(ea_mt_codec_data* data, int channels); + /* relic_decoder */ +typedef struct relic_codec_data relic_codec_data; + relic_codec_data* init_relic(int channels, int bitrate, int codec_rate); void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* outbuf, int32_t samples_to_do); void reset_relic(relic_codec_data* data); void seek_relic(relic_codec_data* data, int32_t num_sample); void free_relic(relic_codec_data* data); + /* hca_decoder */ -hca_codec_data *init_hca(STREAMFILE *streamFile); -void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do); -void reset_hca(hca_codec_data * data); -void loop_hca(hca_codec_data * data, int32_t num_sample); -void free_hca(hca_codec_data * data); -int test_hca_key(hca_codec_data * data, unsigned long long keycode); +typedef struct hca_codec_data hca_codec_data; + +hca_codec_data* init_hca(STREAMFILE* sf); +void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do); +void reset_hca(hca_codec_data* data); +void loop_hca(hca_codec_data* data, int32_t num_sample); +void free_hca(hca_codec_data* data); +int test_hca_key(hca_codec_data* data, unsigned long long keycode); +void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode); +clHCA_stInfo* hca_get_info(hca_codec_data* data); +STREAMFILE* hca_get_streamfile(hca_codec_data* data); + #ifdef VGM_USE_VORBIS /* ogg_vorbis_decoder */ -ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE *sf, off_t start, off_t size, ogg_vorbis_io *io); -void decode_ogg_vorbis(ogg_vorbis_codec_data *data, sample_t *outbuf, int32_t samples_to_do, int channels); -void reset_ogg_vorbis(VGMSTREAM *vgmstream); -void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample); -void free_ogg_vorbis(ogg_vorbis_codec_data *data); +typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data; +typedef struct { //todo simplify + STREAMFILE *streamfile; + ogg_int64_t start; /* file offset where the Ogg starts */ + ogg_int64_t offset; /* virtual offset, from 0 to size */ + ogg_int64_t size; /* virtual size of the Ogg */ + + /* decryption setup */ + void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource); + uint8_t scd_xor; + off_t scd_xor_length; + uint32_t xor_value; +} ogg_vorbis_io; + +ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE* sf, off_t start, off_t size, ogg_vorbis_io* io); +void decode_ogg_vorbis(ogg_vorbis_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels); +void reset_ogg_vorbis(VGMSTREAM* vgmstream); +void seek_ogg_vorbis(ogg_vorbis_codec_data* data, int32_t num_sample); +void free_ogg_vorbis(ogg_vorbis_codec_data* data); + +int ogg_vorbis_get_comment(ogg_vorbis_codec_data* data, const char** comment); +void ogg_vorbis_get_info(ogg_vorbis_codec_data* data, int* p_channels, int* p_sample_rate); +void ogg_vorbis_get_samples(ogg_vorbis_codec_data* data, int* p_samples); +void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data* data, int set); +STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data* data); -int ogg_vorbis_get_comment(ogg_vorbis_codec_data *data, const char **comment); -void ogg_vorbis_get_info(ogg_vorbis_codec_data *data, int *p_channels, int *p_sample_rate); -void ogg_vorbis_get_samples(ogg_vorbis_codec_data *data, int *p_samples); -void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data *data, int set); -STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data *data); /* vorbis_custom_decoder */ -vorbis_custom_codec_data *init_vorbis_custom(STREAMFILE *streamfile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config); -void decode_vorbis_custom(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels); -void reset_vorbis_custom(VGMSTREAM *vgmstream); -void seek_vorbis_custom(VGMSTREAM *vgmstream, int32_t num_sample); -void free_vorbis_custom(vorbis_custom_codec_data *data); +typedef struct vorbis_custom_codec_data vorbis_custom_codec_data; + +typedef enum { + VORBIS_FSB, /* FMOD FSB: simplified/external setup packets, custom packet headers */ + VORBIS_WWISE, /* Wwise WEM: many variations (custom setup, headers and data) */ + VORBIS_OGL, /* Shin'en OGL: custom packet headers */ + VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */ + VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */ +} vorbis_custom_t; + +/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */ +typedef enum { WWV_HEADER_TRIAD, WWV_FULL_SETUP, WWV_INLINE_CODEBOOKS, WWV_EXTERNAL_CODEBOOKS, WWV_AOTUV603_CODEBOOKS } wwise_setup_t; +typedef enum { WWV_TYPE_8, WWV_TYPE_6, WWV_TYPE_2 } wwise_header_t; +typedef enum { WWV_STANDARD, WWV_MODIFIED } wwise_packet_t; + +typedef struct { + /* to reconstruct init packets */ + int channels; + int sample_rate; + int blocksize_0_exp; + int blocksize_1_exp; + + uint32_t setup_id; /* external setup */ + int big_endian; /* flag */ + + /* Wwise Vorbis config */ + wwise_setup_t setup_type; + wwise_header_t header_type; + wwise_packet_t packet_type; + + /* output (kinda ugly here but to simplify) */ + off_t data_start_offset; + +} vorbis_custom_config; + +vorbis_custom_codec_data* init_vorbis_custom(STREAMFILE* sf, off_t start_offset, vorbis_custom_t type, vorbis_custom_config* config); +void decode_vorbis_custom(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); +void reset_vorbis_custom(VGMSTREAM* vgmstream); +void seek_vorbis_custom(VGMSTREAM* vgmstream, int32_t num_sample); +void free_vorbis_custom(vorbis_custom_codec_data* data); #endif + #ifdef VGM_USE_MPEG /* mpeg_decoder */ -mpeg_codec_data *init_mpeg(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels); -mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config *config); -void decode_mpeg(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels); -void reset_mpeg(VGMSTREAM *vgmstream); -void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample); -void free_mpeg(mpeg_codec_data *data); -void flush_mpeg(mpeg_codec_data * data); +typedef struct mpeg_codec_data mpeg_codec_data; -long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data); +/* Custom MPEG modes, mostly differing in the data layout */ +typedef enum { + MPEG_STANDARD, /* 1 stream */ + MPEG_AHX, /* 1 stream with false frame headers */ + MPEG_XVAG, /* N streams of fixed interleave (frame-aligned, several data-frames of fixed size) */ + MPEG_FSB, /* N streams of 1 data-frame+padding (=interleave) */ + MPEG_P3D, /* N streams of fixed interleave (not frame-aligned) */ + MPEG_SCD, /* N streams of fixed interleave (not frame-aligned) */ + MPEG_EA, /* 1 stream (maybe N streams in absolute offsets?) */ + MPEG_EAL31, /* EALayer3 v1 (SCHl), custom frames with v1 header */ + MPEG_EAL31b, /* EALayer3 v1 (SNS), custom frames with v1 header + minor changes */ + MPEG_EAL32P, /* EALayer3 v2 "PCM", custom frames with v2 header + bigger PCM blocks? */ + MPEG_EAL32S, /* EALayer3 v2 "Spike", custom frames with v2 header + smaller PCM blocks? */ + MPEG_LYN, /* N streams of fixed interleave */ + MPEG_AWC, /* N streams in block layout (music) or absolute offsets (sfx) */ + MPEG_EAMP3 /* custom frame header + MPEG frame + PCM blocks */ +} mpeg_custom_t; + +/* config for the above modes */ +typedef struct { + int channels; /* max channels */ + int fsb_padding; /* fsb padding mode */ + int chunk_size; /* size of a data portion */ + int data_size; /* playable size */ + int interleave; /* size of stream interleave */ + int encryption; /* encryption mode */ + int big_endian; + int skip_samples; + /* for AHX */ + int cri_type; + uint16_t cri_key1; + uint16_t cri_key2; + uint16_t cri_key3; +} mpeg_custom_config; + +mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t *coding_type, int channels); +mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t* coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config* config); +void decode_mpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); +void reset_mpeg(mpeg_codec_data* data); +void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample); +void free_mpeg(mpeg_codec_data* data); +void flush_mpeg(mpeg_codec_data* data); + +int mpeg_get_sample_rate(mpeg_codec_data* data); +long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data); #endif + #ifdef VGM_USE_G7221 /* g7221_decoder */ +typedef struct g7221_codec_data g7221_codec_data; + g7221_codec_data* init_g7221(int channel_count, int frame_size); void decode_g7221(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t samples_to_do, int channel); void reset_g7221(g7221_codec_data* data); @@ -288,73 +441,93 @@ void set_key_g7221(g7221_codec_data* data, const uint8_t* key); int test_key_g7221(g7221_codec_data* data, off_t start, STREAMFILE* sf); #endif + #ifdef VGM_USE_G719 /* g719_decoder */ -g719_codec_data *init_g719(int channel_count, int frame_size); -void decode_g719(VGMSTREAM *vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); -void reset_g719(g719_codec_data * data, int channels); -void free_g719(g719_codec_data * data, int channels); +typedef struct g719_codec_data g719_codec_data; + +g719_codec_data* init_g719(int channel_count, int frame_size); +void decode_g719(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t samples_to_do, int channel); +void reset_g719(g719_codec_data* data, int channels); +void free_g719(g719_codec_data* data, int channels); #endif + #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) /* mp4_aac_decoder */ -void decode_mp4_aac(mp4_aac_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); -void reset_mp4_aac(VGMSTREAM *vgmstream); -void seek_mp4_aac(VGMSTREAM *vgmstream, int32_t num_sample); -void free_mp4_aac(mp4_aac_codec_data * data); +void decode_mp4_aac(mp4_aac_codec_data* data, sample * outbuf, int32_t samples_to_do, int channels); +void reset_mp4_aac(VGMSTREAM* vgmstream); +void seek_mp4_aac(VGMSTREAM* vgmstream, int32_t num_sample); +void free_mp4_aac(mp4_aac_codec_data* data); #endif + #ifdef VGM_USE_MAIATRAC3PLUS /* at3plus_decoder */ -maiatrac3plus_codec_data *init_at3plus(); -void decode_at3plus(VGMSTREAM *vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); -void reset_at3plus(VGMSTREAM *vgmstream); -void seek_at3plus(VGMSTREAM *vgmstream, int32_t num_sample); -void free_at3plus(maiatrac3plus_codec_data *data); +typedef struct maiatrac3plus_codec_data maiatrac3plus_codec_data; + +maiatrac3plus_codec_data* init_at3plus(); +void decode_at3plus(VGMSTREAM* vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); +void reset_at3plus(VGMSTREAM* vgmstream); +void seek_at3plus(VGMSTREAM* vgmstream, int32_t num_sample); +void free_at3plus(maiatrac3plus_codec_data* data); #endif + #ifdef VGM_USE_ATRAC9 /* atrac9_decoder */ -atrac9_codec_data *init_atrac9(atrac9_config *cfg); -void decode_atrac9(VGMSTREAM *vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels); -void reset_atrac9(VGMSTREAM *vgmstream); -void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample); -void free_atrac9(atrac9_codec_data *data); -size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data); +typedef struct { + int channels; /* to detect weird multichannel */ + uint32_t config_data; /* ATRAC9 config header */ + int encoder_delay; /* initial samples to discard */ +} atrac9_config; +typedef struct atrac9_codec_data atrac9_codec_data; + +atrac9_codec_data* init_atrac9(atrac9_config* cfg); +void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); +void reset_atrac9(atrac9_codec_data* data); +void seek_atrac9(VGMSTREAM* vgmstream, int32_t num_sample); +void free_atrac9(atrac9_codec_data* data); +size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data* data); size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t atrac9_config); #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); +typedef enum { CELT_0_06_1,CELT_0_11_0} celt_lib_t; +typedef struct celt_codec_data celt_codec_data; + +celt_codec_data* init_celt_fsb(int channels, celt_lib_t version); +void decode_celt_fsb(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); +void reset_celt_fsb(celt_codec_data* data); +void seek_celt_fsb(VGMSTREAM* vgmstream, int32_t num_sample); +void free_celt_fsb(celt_codec_data* data); #endif + #ifdef VGM_USE_FFMPEG /* ffmpeg_decoder */ -ffmpeg_codec_data *init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); -ffmpeg_codec_data *init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size); -ffmpeg_codec_data *init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong); +ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t size); +ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size); +ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong); -void decode_ffmpeg(VGMSTREAM *stream, sample_t * outbuf, int32_t samples_to_do, int channels); -void reset_ffmpeg(VGMSTREAM *vgmstream); -void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample); -void free_ffmpeg(ffmpeg_codec_data *data); +void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); +void reset_ffmpeg(ffmpeg_codec_data* data); +void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample); +void free_ffmpeg(ffmpeg_codec_data* data); -void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples); -uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data); -void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channels_remap); -const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data); -void ffmpeg_set_force_seek(ffmpeg_codec_data * data); +void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples); +uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data); +void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channels_remap); +const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data); +void ffmpeg_set_force_seek(ffmpeg_codec_data* data); const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key); - +STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data); /* ffmpeg_decoder_utils.c (helper-things) */ -ffmpeg_codec_data * init_ffmpeg_atrac3_raw(STREAMFILE *sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay); -ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* out_samples); +ffmpeg_codec_data* init_ffmpeg_atrac3_raw(STREAMFILE* sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay); +ffmpeg_codec_data* init_ffmpeg_atrac3_riff(STREAMFILE* sf, off_t offset, int* out_samples); /* ffmpeg_decoder_custom_opus.c (helper-things) */ @@ -368,28 +541,28 @@ typedef struct { int channel_mapping[8]; } opus_config; -ffmpeg_codec_data * init_ffmpeg_switch_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config* cfg); -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); -ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); -ffmpeg_codec_data * init_ffmpeg_x_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config* cfg); +ffmpeg_codec_data* init_ffmpeg_switch_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data* init_ffmpeg_ue4_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); -size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *streamFile); +size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE* sf); -size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); -size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); -size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); +size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE* sf); +size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE* sf); +size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE* sf); #endif /* coding_utils */ -int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, size_t chunk_size, uint16_t codec); -int ffmpeg_make_riff_atrac3plus(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int encoder_delay); -int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int stream_mode); -int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size); -int ffmpeg_make_riff_xma_from_fmt_chunk(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian); -int ffmpeg_make_riff_xma2_from_xma2_chunk(uint8_t * buf, size_t buf_size, off_t xma2_offset, size_t xma2_size, size_t data_size, STREAMFILE *streamFile); -int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align); +int ffmpeg_fmt_chunk_swap_endian(uint8_t* chunk, size_t chunk_size, uint16_t codec); +int ffmpeg_make_riff_atrac3plus(uint8_t* buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int encoder_delay); +int ffmpeg_make_riff_xma1(uint8_t* buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int stream_mode); +int ffmpeg_make_riff_xma2(uint8_t* buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size); +int ffmpeg_make_riff_xma_from_fmt_chunk(uint8_t* buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE* sf, int big_endian); +int ffmpeg_make_riff_xma2_from_xma2_chunk(uint8_t* buf, size_t buf_size, off_t xma2_offset, size_t xma2_size, size_t data_size, STREAMFILE* sf); +int ffmpeg_make_riff_xwma(uint8_t* buf, size_t buf_size, int codec, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align); /* MS audio format's sample info (struct to avoid passing so much stuff, separate for reusing) */ typedef struct { @@ -413,39 +586,26 @@ typedef struct { int32_t loop_start_sample; int32_t loop_end_sample; } ms_sample_data; -void xma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile); -void wmapro_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_align, int sample_rate, uint32_t decode_flags); -void wma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_align, int sample_rate, uint32_t decode_flags); +void xma_get_samples(ms_sample_data* msd, STREAMFILE* sf); +void wmapro_get_samples(ms_sample_data* msd, STREAMFILE* sf, int block_align, int sample_rate, uint32_t decode_flags); +void wma_get_samples(ms_sample_data* msd, STREAMFILE* sf, int block_align, int sample_rate, uint32_t decode_flags); -void xma1_parse_fmt_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * loop_start_b, int32_t * loop_end_b, int32_t * loop_subframe, int be); -void xma2_parse_fmt_chunk_extra(STREAMFILE *streamFile, off_t chunk_offset, int * loop_flag, int32_t * out_num_samples, int32_t * out_loop_start_sample, int32_t * out_loop_end_sample, int be); -void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * num_samples, int32_t * loop_start_sample, int32_t * loop_end_sample); +void xma1_parse_fmt_chunk(STREAMFILE* sf, 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* sf, 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* sf, off_t chunk_offset, int* channels, int* sample_rate, int* loop_flag, int32_t* num_samples, int32_t* loop_start_sample, int32_t* loop_end_sample); -void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples); -void xma_fix_raw_samples_hb(VGMSTREAM *vgmstream, STREAMFILE *headerFile, STREAMFILE *bodyFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples); -void xma_fix_raw_samples_ch(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, int channel_per_stream, int fix_num_samples, int fix_loop_samples); +void xma_fix_raw_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples); +void xma_fix_raw_samples_hb(VGMSTREAM* vgmstream, STREAMFILE* sf_head, STREAMFILE* sf_body, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples); +void xma_fix_raw_samples_ch(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t stream_offset, size_t stream_size, int channel_per_stream, int fix_num_samples, int fix_loop_samples); 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 ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels); -size_t aac_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes); -size_t mpeg_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes); - - -/* An internal struct to pass around and simulate a bitstream. */ -typedef enum { BITSTREAM_MSF, BITSTREAM_VORBIS } vgm_bitstream_t; -typedef struct { - uint8_t * buf; /* buffer to read/write*/ - size_t bufsize; /* max size of the buffer */ - off_t b_off; /* current offset in bits inside the buffer */ - vgm_bitstream_t mode; /* read/write modes */ -} vgm_bitstream; - -int r_bits(vgm_bitstream * ib, int num_bits, uint32_t * value); -int w_bits(vgm_bitstream * ob, int num_bits, uint32_t value); +size_t aac_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); +size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); /* helper to pass a wrapped, clamped, fake extension-ed, SF to another meta */ -STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* extension); +STREAMFILE* setup_subfile_streamfile(STREAMFILE* sf, off_t subfile_offset, size_t subfile_size, const char* extension); #endif /*_CODING_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c index 4a28b718f..faddd023e 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c @@ -1012,152 +1012,6 @@ size_t aac_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes) return frames * samples_per_frame; } - -/* ******************************************** */ -/* BITSTREAM */ -/* ******************************************** */ - - -/* Read bits (max 32) from buf and update the bit offset. Vorbis packs values in LSB order and byte by byte. - * (ex. from 2 bytes 00100111 00000001 we can could read 4b=0111 and 6b=010010, 6b=remainder (second value is split into the 2nd byte) */ -static int r_bits_vorbis(vgm_bitstream * ib, int num_bits, uint32_t * value) { - off_t off, pos; - int i, bit_buf, bit_val; - if (num_bits == 0) return 1; - if (num_bits > 32 || num_bits < 0 || ib->b_off + num_bits > ib->bufsize*8) goto fail; - - *value = 0; /* set all bits to 0 */ - off = ib->b_off / 8; /* byte offset */ - pos = ib->b_off % 8; /* bit sub-offset */ - for (i = 0; i < num_bits; i++) { - bit_buf = (1U << pos) & 0xFF; /* bit check for buf */ - bit_val = (1U << i); /* bit to set in value */ - - if (ib->buf[off] & bit_buf) /* is bit in buf set? */ - *value |= bit_val; /* set bit */ - - pos++; /* new byte starts */ - if (pos%8 == 0) { - pos = 0; - off++; - } - } - - ib->b_off += num_bits; - return 1; -fail: - return 0; -} - -/* Write bits (max 32) to buf and update the bit offset. Vorbis packs values in LSB order and byte by byte. - * (ex. writing 1101011010 from b_off 2 we get 01101011 00001101 (value split, and 11 in the first byte skipped)*/ -static int w_bits_vorbis(vgm_bitstream * ob, int num_bits, uint32_t value) { - off_t off, pos; - int i, bit_val, bit_buf; - if (num_bits == 0) return 1; - if (num_bits > 32 || num_bits < 0 || ob->b_off + num_bits > ob->bufsize*8) goto fail; - - - off = ob->b_off / 8; /* byte offset */ - pos = ob->b_off % 8; /* bit sub-offset */ - for (i = 0; i < num_bits; i++) { - bit_val = (1U << i); /* bit check for value */ - bit_buf = (1U << pos) & 0xFF; /* bit to set in buf */ - - if (value & bit_val) /* is bit in val set? */ - ob->buf[off] |= bit_buf; /* set bit */ - else - ob->buf[off] &= ~bit_buf; /* unset bit */ - - pos++; /* new byte starts */ - if (pos%8 == 0) { - pos = 0; - off++; - } - } - - ob->b_off += num_bits; - return 1; -fail: - return 0; -} - - -/* Read bits (max 32) from buf and update the bit offset. Order is BE (MSF). */ -static int r_bits_msf(vgm_bitstream * ib, int num_bits, uint32_t * value) { - off_t off, pos; - int i, bit_buf, bit_val; - if (num_bits == 0) return 1; - if (num_bits > 32 || num_bits < 0 || ib->b_off + num_bits > ib->bufsize*8) goto fail; - - *value = 0; /* set all bits to 0 */ - off = ib->b_off / 8; /* byte offset */ - pos = ib->b_off % 8; /* bit sub-offset */ - for (i = 0; i < num_bits; i++) { - bit_buf = (1U << (8-1-pos)) & 0xFF; /* bit check for buf */ - bit_val = (1U << (num_bits-1-i)); /* bit to set in value */ - - if (ib->buf[off] & bit_buf) /* is bit in buf set? */ - *value |= bit_val; /* set bit */ - - pos++; - if (pos%8 == 0) { /* new byte starts */ - pos = 0; - off++; - } - } - - ib->b_off += num_bits; - return 1; -fail: - return 0; -} - -/* Write bits (max 32) to buf and update the bit offset. Order is BE (MSF). */ -static int w_bits_msf(vgm_bitstream * ob, int num_bits, uint32_t value) { - off_t off, pos; - int i, bit_val, bit_buf; - if (num_bits == 0) return 1; - if (num_bits > 32 || num_bits < 0 || ob->b_off + num_bits > ob->bufsize*8) goto fail; - - - off = ob->b_off / 8; /* byte offset */ - pos = ob->b_off % 8; /* bit sub-offset */ - for (i = 0; i < num_bits; i++) { - bit_val = (1U << (num_bits-1-i)); /* bit check for value */ - bit_buf = (1U << (8-1-pos)) & 0xFF; /* bit to set in buf */ - - if (value & bit_val) /* is bit in val set? */ - ob->buf[off] |= bit_buf; /* set bit */ - else - ob->buf[off] &= ~bit_buf; /* unset bit */ - - pos++; - if (pos%8 == 0) { /* new byte starts */ - pos = 0; - off++; - } - } - - ob->b_off += num_bits; - return 1; -fail: - return 0; -} - -int r_bits(vgm_bitstream * ib, int num_bits, uint32_t * value) { - if (ib->mode == BITSTREAM_VORBIS) - return r_bits_vorbis(ib,num_bits,value); - else - return r_bits_msf(ib,num_bits,value); -} -int w_bits(vgm_bitstream * ob, int num_bits, uint32_t value) { - if (ob->mode == BITSTREAM_VORBIS) - return w_bits_vorbis(ob,num_bits,value); - else - return w_bits_msf(ob,num_bits,value); -} - /* ******************************************** */ /* CUSTOM STREAMFILES */ /* ******************************************** */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index 1070921fe..65385b133 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -8,11 +8,8 @@ static volatile int g_ffmpeg_initialized = 0; -static void free_ffmpeg_config(ffmpeg_codec_data *data); -static int init_ffmpeg_config(ffmpeg_codec_data * data, int target_subsong, int reset); - -static void reset_ffmpeg_internal(ffmpeg_codec_data *data); -static void seek_ffmpeg_internal(ffmpeg_codec_data *data, int32_t num_sample); +static void free_ffmpeg_config(ffmpeg_codec_data* data); +static int init_ffmpeg_config(ffmpeg_codec_data* data, int target_subsong, int reset); /* ******************************************** */ /* INTERNAL UTILS */ @@ -34,7 +31,7 @@ static void g_init_ffmpeg() { } } -static void remap_audio(sample_t *outbuf, int sample_count, int channels, int *channel_mappings) { +static void remap_audio(sample_t* outbuf, int sample_count, int channels, int* channel_mappings) { int ch_from,ch_to,s; sample_t temp; for (s = 0; s < sample_count; s++) { @@ -65,7 +62,7 @@ static void remap_audio(sample_t *outbuf, int sample_count, int channels, int *c * first frame/packet and sets up index to timestamp 0. This ensures faulty demuxers will seek to 0 correctly. * Some formats may not seek to 0 even with this, though. */ -static int init_seek(ffmpeg_codec_data * data) { +static int init_seek(ffmpeg_codec_data* data) { int ret, ts_index, packet_count = 0; int64_t ts = 0; /* seek timestamp */ int64_t pos = 0; /* data offset */ @@ -161,8 +158,8 @@ fail: /* ******************************************** */ /* AVIO callback: read stream, handling custom data */ -static int ffmpeg_read(void *opaque, uint8_t *buf, int read_size) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; +static int ffmpeg_read(void* opaque, uint8_t* buf, int read_size) { + ffmpeg_codec_data* data = opaque; int bytes = 0; int max_to_copy = 0; @@ -196,8 +193,8 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int read_size) { } /* AVIO callback: seek stream, handling custom data */ -static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; +static int64_t ffmpeg_seek(void* opaque, int64_t offset, int whence) { + ffmpeg_codec_data* data = opaque; int ret = 0; /* get cache'd size */ @@ -243,12 +240,12 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { /* MAIN INIT/DECODER */ /* ******************************************** */ -ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { - return init_ffmpeg_header_offset(streamFile, NULL,0, start,size); +ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t size) { + return init_ffmpeg_header_offset(sf, NULL,0, start,size); } -ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) { - return init_ffmpeg_header_offset_subsong(streamFile, header, header_size, start, size, 0); +ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) { + return init_ffmpeg_header_offset_subsong(sf, header, header_size, start, size, 0); } @@ -261,8 +258,8 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * * NULL header can used given if the stream has internal data recognized by FFmpeg at offset. * Stream index can be passed if the file has multiple audio streams that FFmpeg can demux (1=first). */ -ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong) { - ffmpeg_codec_data * data = NULL; +ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong) { + ffmpeg_codec_data* data = NULL; int errcode; @@ -270,9 +267,9 @@ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, ui if ((header && !header_size) || (!header && header_size)) goto fail; - if (size == 0 || start + size > get_streamfile_size(streamFile)) { - VGM_LOG("FFMPEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, get_streamfile_size(streamFile)); - size = get_streamfile_size(streamFile) - start; + if (size == 0 || start + size > get_streamfile_size(sf)) { + VGM_LOG("FFMPEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, get_streamfile_size(sf)); + size = get_streamfile_size(sf) - start; } @@ -284,7 +281,7 @@ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, ui data = calloc(1, sizeof(ffmpeg_codec_data)); if (!data) return NULL; - data->streamfile = reopen_streamfile(streamFile, 0); + data->streamfile = reopen_streamfile(sf, 0); if (!data->streamfile) goto fail; /* fake header to trick FFmpeg into demuxing/decoding the stream */ @@ -366,7 +363,7 @@ fail: return NULL; } -static int init_ffmpeg_config(ffmpeg_codec_data * data, int target_subsong, int reset) { +static int init_ffmpeg_config(ffmpeg_codec_data* data, int target_subsong, int reset) { int errcode = 0; /* basic IO/format setup */ @@ -455,7 +452,7 @@ fail: } /* decodes a new frame to internal data */ -static int decode_ffmpeg_frame(ffmpeg_codec_data *data) { +static int decode_ffmpeg_frame(ffmpeg_codec_data* data) { int errcode; int frame_error = 0; @@ -672,7 +669,7 @@ static void samples_dblp_to_s16(sample_t* obuf, double** inbuf, int ichs, int sa } } -static void copy_samples(ffmpeg_codec_data *data, sample_t *outbuf, int samples_to_do) { +static void copy_samples(ffmpeg_codec_data* data, sample_t* outbuf, int samples_to_do) { int channels = data->codecCtx->channels; int is_planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt) && (channels > 1); void* ibuf; @@ -709,8 +706,8 @@ static void copy_samples(ffmpeg_codec_data *data, sample_t *outbuf, int samples_ } /* decode samples of any kind of FFmpeg format */ -void decode_ffmpeg(VGMSTREAM *vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels) { - ffmpeg_codec_data *data = vgmstream->codec_data; +void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { + ffmpeg_codec_data* data = vgmstream->codec_data; while (samples_to_do > 0) { @@ -757,14 +754,11 @@ decode_fail: /* UTILS */ /* ******************************************** */ -void reset_ffmpeg_internal(ffmpeg_codec_data *data) { - seek_ffmpeg_internal(data, 0); -} -void reset_ffmpeg(VGMSTREAM *vgmstream) { - reset_ffmpeg_internal(vgmstream->codec_data); +void reset_ffmpeg(ffmpeg_codec_data* data) { + seek_ffmpeg(data, 0); } -void seek_ffmpeg_internal(ffmpeg_codec_data *data, int32_t num_sample) { +void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) { if (!data) return; /* Start from 0 and discard samples until sample (slower but not too noticeable). @@ -813,12 +807,8 @@ fail: data->bad_init = 1; /* internals were probably free'd */ } -void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample) { - seek_ffmpeg_internal(vgmstream->codec_data, num_sample); -} - -static void free_ffmpeg_config(ffmpeg_codec_data *data) { +static void free_ffmpeg_config(ffmpeg_codec_data* data) { if (data == NULL) return; @@ -858,7 +848,7 @@ static void free_ffmpeg_config(ffmpeg_codec_data *data) { //todo avformat_find_stream_info may cause some Win Handle leaks? related to certain option } -void free_ffmpeg(ffmpeg_codec_data *data) { +void free_ffmpeg(ffmpeg_codec_data* data) { if (data == NULL) return; @@ -883,7 +873,7 @@ void free_ffmpeg(ffmpeg_codec_data *data) { * This could be added per format in FFmpeg directly, but it's here for flexibility and due to bugs * (FFmpeg's stream->(start_)skip_samples causes glitches in XMA). */ -void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples) { +void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) { AVStream *stream = NULL; if (!data || !data->formatCtx) return; @@ -902,7 +892,7 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples) { } /* returns channel layout if set */ -uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data) { +uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data) { if (!data || !data->codecCtx) return 0; return (uint32_t)data->codecCtx->channel_layout; /* uint64 but there ain't so many speaker mappings */ } @@ -910,7 +900,7 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data) { /* yet another hack to fix codecs that encode channels in different order and reorder on decoder * but FFmpeg doesn't do it automatically * (maybe should be done via mixing, but could clash with other stuff?) */ -void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channel_remap) { +void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) { int i; if (data->channels > 32) @@ -922,7 +912,7 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channel_remap) data->channel_remap_set = 1; } -const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data) { +const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data) { if (!data || !data->codec) return NULL; if (data->codec->long_name) @@ -932,12 +922,12 @@ const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data) { return NULL; } -void ffmpeg_set_force_seek(ffmpeg_codec_data * data) { +void ffmpeg_set_force_seek(ffmpeg_codec_data* data) { /* some formats like Smacker are so buggy that any seeking is impossible (even on video players), * or MPC with an incorrectly parsed seek table (using as 0 some non-0 seek offset). * whatever, we'll just kill and reconstruct FFmpeg's config every time */ data->force_seek = 1; - reset_ffmpeg_internal(data); /* reset state from trying to seek */ + reset_ffmpeg(data); /* reset state from trying to seek */ //stream = data->formatCtx->streams[data->streamIndex]; } @@ -959,4 +949,8 @@ const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) return avde->value; } +STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data) { + if (!data) return NULL; + return data->streamfile; +} #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c index c2466630c..627926ca0 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c @@ -1,18 +1,17 @@ #include "coding.h" +#include #ifdef VGM_USE_G719 -#ifdef __MACOSX__ -#include -#else -#include -#endif - #define G719_MAX_CODES ((1280/8)) /* in int16, so max frame size is (value/8)*2 (0xF0=common, 0x140=decoder max 2560b, rare) */ +struct g719_codec_data { + sample_t buffer[960]; + void *handle; +}; -g719_codec_data *init_g719(int channel_count, int frame_size) { +g719_codec_data* init_g719(int channel_count, int frame_size) { int i; - g719_codec_data *data = NULL; + g719_codec_data* data = NULL; if (frame_size / sizeof(int16_t) > G719_MAX_CODES) goto fail; @@ -39,10 +38,10 @@ fail: } -void decode_g719(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel) { - VGMSTREAMCHANNEL *ch = &vgmstream->ch[channel]; - g719_codec_data *data = vgmstream->codec_data; - g719_codec_data *ch_data = &data[channel]; +void decode_g719(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t samples_to_do, int channel) { + VGMSTREAMCHANNEL* ch = &vgmstream->ch[channel]; + g719_codec_data* data = vgmstream->codec_data; + g719_codec_data* ch_data = &data[channel]; int i; if (0 == vgmstream->samples_into_block) { @@ -58,7 +57,7 @@ void decode_g719(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int } -void reset_g719(g719_codec_data * data, int channels) { +void reset_g719(g719_codec_data* data, int channels) { int i; if (!data) return; @@ -67,7 +66,7 @@ void reset_g719(g719_codec_data * data, int channels) { } } -void free_g719(g719_codec_data * data, int channels) { +void free_g719(g719_codec_data* data, int channels) { int i; if (!data) return; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c index 64b6cd477..48205b405 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c @@ -1,21 +1,36 @@ #include "coding.h" +struct hca_codec_data { + STREAMFILE* streamfile; + clHCA_stInfo info; + + signed short* sample_buffer; + size_t samples_filled; + size_t samples_consumed; + size_t samples_to_discard; + + void* data_buffer; + + unsigned int current_block; + + void* handle; +}; + /* init a HCA stream; STREAMFILE will be duplicated for internal use. */ -hca_codec_data * init_hca(STREAMFILE *streamFile) { - char filename[PATH_LIMIT]; +hca_codec_data* init_hca(STREAMFILE* sf) { uint8_t header_buffer[0x2000]; /* hca header buffer data (probable max ~0x400) */ - hca_codec_data * data = NULL; /* vgmstream HCA context */ + hca_codec_data* data = NULL; /* vgmstream HCA context */ int header_size; int status; /* test header */ - if (read_streamfile(header_buffer, 0x00, 0x08, streamFile) != 0x08) + if (read_streamfile(header_buffer, 0x00, 0x08, sf) != 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) + if (read_streamfile(header_buffer, 0x00, header_size, sf) != header_size) goto fail; /* init vgmstream context */ @@ -39,8 +54,7 @@ hca_codec_data * init_hca(STREAMFILE *streamFile) { if (!data->sample_buffer) goto fail; /* load streamfile for reads */ - get_streamfile_name(streamFile,filename, sizeof(filename)); - data->streamfile = open_streamfile(streamFile,filename); + data->streamfile = reopen_streamfile(sf, 0); if (!data->streamfile) goto fail; /* set initial values */ @@ -53,7 +67,7 @@ fail: return NULL; } -void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do) { +void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { int samples_done = 0; const unsigned int channels = data->info.channelCount; const unsigned int blockSize = data->info.blockSize; @@ -120,7 +134,7 @@ void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do) { } } -void reset_hca(hca_codec_data * data) { +void reset_hca(hca_codec_data* data) { if (!data) return; clHCA_DecodeReset(data->handle); @@ -130,7 +144,7 @@ void reset_hca(hca_codec_data * data) { data->samples_to_discard = data->info.encoderDelay; } -void loop_hca(hca_codec_data * data, int32_t num_sample) { +void loop_hca(hca_codec_data* data, int32_t num_sample) { if (!data) return; /* manually calc loop values if not set (should only happen with installed/forced looping, @@ -149,7 +163,7 @@ void loop_hca(hca_codec_data * data, int32_t num_sample) { data->samples_to_discard = data->info.loopStartDelay; } -void free_hca(hca_codec_data * data) { +void free_hca(hca_codec_data* data) { if (!data) return; close_streamfile(data->streamfile); @@ -175,7 +189,7 @@ void free_hca(hca_codec_data * data) { /* 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) { +int test_hca_key(hca_codec_data* data, unsigned long long keycode) { size_t test_frames = 0, current_frame = 0, blank_frames = 0; int total_score = 0, found_regular_frame = 0; const unsigned int blockSize = data->info.blockSize; @@ -242,3 +256,16 @@ int test_hca_key(hca_codec_data * data, unsigned long long keycode) { clHCA_DecodeReset(data->handle); return total_score; } + +void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode) { + clHCA_SetKey(data->handle, (unsigned long long)keycode); +} + +clHCA_stInfo* hca_get_info(hca_codec_data* data) { + return &data->info; +} + +STREAMFILE* hca_get_streamfile(hca_codec_data* data) { + if (!data) return NULL; + return data->streamfile; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index 9f96d575f..4721bbdb3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -285,6 +285,60 @@ static void mtf_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, if (*step_index > 88) *step_index=88; } +/* IMA table pre-modified like this: + for i=0..89 + adpcm = clamp(adpcm[i], 0x1fff) * 4; +*/ +static const int16_t mul_adpcm_table[89] = { + 28, 32, 36, 40, 44, 48, 52, 56, + 64, 68, 76, 84, 92, 100, 112, 124, + 136, 148, 164, 180, 200, 220, 240, 264, + 292, 320, 352, 388, 428, 472, 520, 572, + 628, 692, 760, 836, 920, 1012, 1116, 1228, + 1348, 1484, 1632, 1796, 1976, 2176, 2392, 2632, + 2896, 3184, 3504, 3852, 4240, 4664, 5128, 5644, + 6208, 6828, 7512, 8264, 9088, 9996, 10996, 12096, + 13308, 14640, 16104, 17712, 19484, 21432, 23576, 25936, + 28528, 31380, 32764, 32764, 32764, 32764, 32764, 32764, + 32764, 32764, 32764, 32764, 32764, 32764, 32764, 32764, + 32764 +}; + +/* step table is the same */ + +/* ops per code, generated like this: + for i=0..15 + v = 0x800 + if (i & 1) v = 0x1800 + if (i & 2) v += 0x2000 + if (i & 4) v += 0x4000 + if (i & 8) v = -v; + mul_op_table[i] = v; +*/ +static const int16_t mul_delta_table[16] = { + 0x0800, 0x1800, 0x2800, 0x3800, 0x4800, 0x5800, 0x6800, 0x7800, + -0x0800,-0x1800,-0x2800,-0x3800,-0x4800,-0x5800,-0x6800,-0x7800 +}; + + +/* Crystal Dynamics IMA, reverse engineered from the exe, also info: https://github.com/sephiroth99/MulDeMu */ +static void cd_ima_expand_nibble(uint8_t byte, int shift, int32_t* hist1, int32_t* index) { + int code, sample, step, delta; + + /* could do the above table calcs during decode too */ + code = (byte >> shift) & 0xf; + sample = *hist1; + step = mul_adpcm_table[*index]; + + delta = (int16_t)((step * mul_delta_table[code]) >> 16); + sample += delta; + + *hist1 = clamp16(sample); + *index += IMA_IndexTable[code]; + if (*index < 0) *index=0; + if (*index > 88) *index=88; +} + /* ************************************ */ /* DVI/IMA */ /* ************************************ */ @@ -1156,6 +1210,62 @@ void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa stream->adpcm_step_index = step_index; } +/* test... */ +static inline int _clamp_s32(int value, int min, int max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; +} + +/* Crystal Dynamics IMA. Original code uses mind-bending intrinsics, so this may not be fully accurate. + * Has another table with delta_table MMX combos, and uses header sample (first nibble is always 0). */ +void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame[0x24] = {0}; + int i, frames_in, sample_pos = 0, block_samples, frame_size; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + off_t frame_offset; + + /* external interleave (fixed size), mono */ + frame_size = 0x24; + block_samples = (frame_size - 0x4) * 2; + frames_in = first_sample / block_samples; + first_sample = first_sample % block_samples; + + frame_offset = stream->offset + frame_size * frames_in; + read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */ + + /* normal header (hist+step+reserved), mono */ + if (first_sample == 0) { + hist1 = get_s16le(frame + 0x00); + step_index = get_u8(frame + 0x02); + step_index = _clamp_s32(step_index, 0, 88); + + /* write header sample (even samples per block, skips first nibble) */ + outbuf[sample_pos] = (short)(hist1); + sample_pos += channelspacing; + first_sample += 1; + samples_to_do -= 1; + } + + /* decode nibbles (layout: straight in mono) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int pos = 0x04 + (i/2); + int shift = (i&1 ? 4:0); /* low first, but first low nibble is skipped */ + + cd_ima_expand_nibble(frame[pos], shift, &hist1, &step_index); + outbuf[sample_pos] = (short)(hist1); + sample_pos += channelspacing; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + + /* ************************************************************* */ size_t ima_bytes_to_samples(size_t bytes, int channels) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/l5_555_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/l5_555_decoder.c index 0a12e8372..7b8a7aa4a 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/l5_555_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/l5_555_decoder.c @@ -8,48 +8,59 @@ static const int32_t l5_scales[32] = { 0x00130B82, 0x00182B83, 0x001EAC92, 0x0026EDB2, 0x00316777, 0x003EB2E6, 0x004F9232, 0x0064FBD1 }; -void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i=first_sample; - int32_t sample_count; +void decode_l5_555(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + uint8_t frame[0x12] = {0}; + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + uint16_t header; + uint8_t coef_index; - int framesin = first_sample/32; - - uint16_t header = (uint16_t)read_16bitLE(framesin*0x12+stream->offset,stream->streamfile); - int32_t pos_scale = l5_scales[(header>>5)&0x1f]; - int32_t neg_scale = l5_scales[header&0x1f]; - - int coef_index = (header >> 10) & 0x1f; int16_t hist1 = stream->adpcm_history1_16; int16_t hist2 = stream->adpcm_history2_16; int16_t hist3 = stream->adpcm_history3_16; - int32_t coef1 = stream->adpcm_coef_3by32[coef_index*3]; - int32_t coef2 = stream->adpcm_coef_3by32[coef_index*3+1]; - int32_t coef3 = stream->adpcm_coef_3by32[coef_index*3+2]; - /*printf("offset: %x\nscale: %d\nindex: %d (%lf,%lf)\nhist: %d %d\n", - (unsigned)stream->offset,scale,coef_index,coef1/2048.0,coef2/2048.0,hist1,hist2);*/ + int32_t coef1, coef2, coef3; + int32_t pos_scale, neg_scale; - first_sample = first_sample%32; + /* external interleave (fixed size), mono */ + bytes_per_frame = 0x12; + samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 32 */ + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; - for (i=first_sample,sample_count=0; ioffset+2+i/2,stream->streamfile); - int nibble = (i&1? - get_low_nibble_signed(sample_byte): - get_high_nibble_signed(sample_byte)); - int32_t prediction = - -(hist1 * coef1 + hist2 * coef2 + hist3 * coef3); + /* parse frame header */ + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + header = get_u32le(frame); + coef_index = (header >> 10) & 0x1f; + pos_scale = l5_scales[(header >> 5) & 0x1f]; + neg_scale = l5_scales[(header >> 0) & 0x1f]; - if (nibble >= 0) - { - outbuf[sample_count] = clamp16((prediction + nibble * pos_scale) >> 12); - } + coef1 = stream->adpcm_coef_3by32[coef_index * 3 + 0]; + coef2 = stream->adpcm_coef_3by32[coef_index * 3 + 1]; + coef3 = stream->adpcm_coef_3by32[coef_index * 3 + 2]; + + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int32_t prediction, sample = 0; + uint8_t nibbles = frame[0x02 + i/2]; + + sample = (i&1) ? + get_low_nibble_signed(nibbles): + get_high_nibble_signed(nibbles); + prediction = -(hist1 * coef1 + hist2 * coef2 + hist3 * coef3); + + if (sample >= 0) + sample = (prediction + sample * pos_scale) >> 12; else - { - outbuf[sample_count] = clamp16((prediction + nibble * neg_scale) >> 12); - } + sample = (prediction + sample * neg_scale) >> 12; + sample = clamp16(sample); + + outbuf[sample_count] = sample; + sample_count += channelspacing; hist3 = hist2; hist2 = hist1; - hist1 = outbuf[sample_count]; + hist1 = sample; } stream->adpcm_history1_16 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_bitreader.h b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_bitreader.h new file mode 100644 index 000000000..345f8aba4 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_bitreader.h @@ -0,0 +1,91 @@ +#ifndef _MPEG_BITREADER_H +#define _MPEG_BITREADER_H + +/* Simple bitreader for MPEG/standard bit format. + * Kept in .h since it's slightly faster (compiler can optimize statics better) */ + + +typedef struct { + uint8_t* buf; /* buffer to read/write */ + size_t bufsize; /* max size of the buffer */ + uint32_t b_off; /* current offset in bits inside the buffer */ +} bitstream_t; + +/* convenience util */ +static void init_bitstream(bitstream_t* b, uint8_t* buf, size_t bufsize) { + b->buf = buf; + b->bufsize = bufsize; + b->b_off = 0; +} + +/* Read bits (max 32) from buf and update the bit offset. Order is BE (MSF). */ +static int rb_bits(bitstream_t* ib, uint32_t bits, uint32_t* value) { + uint32_t shift, pos, val; + int i, bit_buf, bit_val; + + if (bits > 32 || ib->b_off + bits > ib->bufsize * 8) + goto fail; + + pos = ib->b_off / 8; /* byte offset */ + shift = ib->b_off % 8; /* bit sub-offset */ + + val = 0; + for (i = 0; i < bits; i++) { + bit_buf = (1U << (8-1-shift)) & 0xFF; /* bit check for buf */ + bit_val = (1U << (bits-1-i)); /* bit to set in value */ + + if (ib->buf[pos] & bit_buf) /* is bit in buf set? */ + val |= bit_val; /* set bit */ + + shift++; + if (shift % 8 == 0) { /* new byte starts */ + shift = 0; + pos++; + } + } + + *value = val; + ib->b_off += bits; + return 1; +fail: + VGM_LOG("BITREADER: read fail\n"); + *value = 0; + return 0; +} + +#ifndef BITSTREAM_READ_ONLY +/* Write bits (max 32) to buf and update the bit offset. Order is BE (MSF). */ +static int wb_bits(bitstream_t* ob, uint32_t bits, uint32_t value) { + uint32_t shift, pos; + int i, bit_val, bit_buf; + + if (bits > 32 || ob->b_off + bits > ob->bufsize * 8) + goto fail; + + pos = ob->b_off / 8; /* byte offset */ + shift = ob->b_off % 8; /* bit sub-offset */ + + for (i = 0; i < bits; i++) { + bit_val = (1U << (bits-1-i)); /* bit check for value */ + bit_buf = (1U << (8-1-shift)) & 0xFF; /* bit to set in buf */ + + if (value & bit_val) /* is bit in val set? */ + ob->buf[pos] |= bit_buf; /* set bit */ + else + ob->buf[pos] &= ~bit_buf; /* unset bit */ + + shift++; + if (shift % 8 == 0) { /* new byte starts */ + shift = 0; + pos++; + } + } + + ob->b_off += bits; + return 1; +fail: + return 0; +} +#endif + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c index 7bfd542ec..fafcad365 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c @@ -1,184 +1,184 @@ -#include "mpeg_decoder.h" - -#ifdef VGM_USE_MPEG - -/** - * AWC music uses blocks (sfx doesn't), the fun part being each channel has different num_samples/frames - * per block, so it's unsuitable for the normal "blocked" layout and parsed here instead. - * Channel data is separate within the block (first all frames of ch0, then ch1, etc), padded, and sometimes - * the last few frames of a channel are repeated in the new block (marked with the "discard samples" field). - */ - -/* block header size, algined/padded to 0x800 */ -static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, mpeg_codec_data *data) { - size_t header_size = 0; - int i; - int entries = data->config.channels; - int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE; - - for (i = 0; i < entries; i++) { - header_size += 0x18; - header_size += read_32bit(offset + 0x18*i + 0x04, streamFile) * 0x04; /* entries in the table */ - } - - if (header_size % 0x800) /* padded */ - header_size += 0x800 - (header_size % 0x800); - - return header_size; -} - -/* find data that repeats in the beginning of a new block at the end of last block */ -static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, off_t last_offset) { - uint8_t new_frame[0x1000];/* buffer to avoid fseek back and forth */ - mpeg_frame_info info; - off_t off; - int i; - - /* read block first frame */ - if ( !mpeg_get_frame_info(streamFile, new_offset, &info)) - goto fail; - if (info.frame_size > 0x1000) - goto fail; - if (read_streamfile(new_frame,new_offset, info.frame_size,streamFile) != info.frame_size) - goto fail; - - /* find the frame in last bytes of prev block */ - off = last_offset - 0x4000; /* typical max is 5-10 frames of ~0x200, no way to know exact size */ - while (off < last_offset) { - /* compare frame vs prev block data */ - for (i = 0; i < info.frame_size; i++) { - if ((uint8_t)read_8bit(off+i,streamFile) != new_frame[i]) - break; - } - - /* frame fully compared? */ - if (i == info.frame_size) - return last_offset - off; - else - off += i+1; - } - -fail: - VGM_LOG("AWC: can't find repeat size, new=0x%08x, last=0x%08x\n", (uint32_t)new_offset, (uint32_t)last_offset); - return 0; /* keep on truckin' */ -} - - -/* init config and validate */ -int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { - mpeg_frame_info info; - int is_music; - - /* start_offset can point to a block header that always starts with 0 (music) or normal data (sfx) */ - is_music = read_32bitBE(start_offset, streamFile) == 0x00000000; - if (is_music) - start_offset += get_block_header_size(streamFile, start_offset, data); - - /* get frame info at offset */ - if ( !mpeg_get_frame_info(streamFile, start_offset, &info)) - goto fail; - switch(info.layer) { - case 1: *coding_type = coding_MPEG_layer1; break; - case 2: *coding_type = coding_MPEG_layer2; break; - case 3: *coding_type = coding_MPEG_layer3; break; - default: goto fail; - } - data->channels_per_frame = info.channels; - data->samples_per_frame = info.frame_samples; - - - /* extra checks */ - if (is_music) { - if (data->config.chunk_size <= 0) - goto fail; /* needs block size */ - } - - /* no big encoder delay added (for sfx they can start in less than ~300 samples) */ - - return 1; -fail: - return 0; -} - - -/* writes data to the buffer and moves offsets, parsing AWC blocks */ -int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { - mpeg_custom_stream *ms = data->streams[num_stream]; - mpeg_frame_info info; - size_t current_data_size = 0, data_offset; - size_t file_size = get_streamfile_size(stream->streamfile); - int i; - - - /* blocked layout used for music */ - if (data->config.chunk_size) { - off_t last_offset = stream->offset; /* when block end needs to be known */ - - /* block ended for this channel, move to next block start */ - if (ms->current_size_count > 0 && ms->current_size_count == ms->current_size_target) { - //mpg123_open_feed(ms->m); //todo reset maybe needed? - - data_offset = stream->offset - stream->channel_start_offset; /* ignoring header */ - data_offset -= data_offset % data->config.chunk_size; /* start of current block */ - stream->offset = stream->channel_start_offset + data_offset + data->config.chunk_size; - - ms->current_size_count = 0; - ms->current_size_target = 0; - } - - /* just in case, shouldn't happen */ - if (stream->offset >= file_size) { - goto fail; - } - - /* block starts for this channel, point to mpeg data */ - if (ms->current_size_count == 0) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE; - off_t channel_offset = 0; - - /* block has a header with base info per channel and table per channel (see blocked_awc.c) */ - ms->decode_to_discard = read_32bit(stream->offset + 0x18*num_stream + 0x08, stream->streamfile); - ms->current_size_target = read_32bit(stream->offset + 0x18*num_stream + 0x14, stream->streamfile); - - for (i = 0; i < num_stream; i++) { /* num_stream serves as channel */ - size_t channel_size = read_32bit(stream->offset + 0x18*i + 0x14, stream->streamfile); - if (channel_size % 0x10) /* 32b aligned */ - channel_size += 0x10 - channel_size % 0x10; - - channel_offset += channel_size; - } - - //;VGM_ASSERT(ms->decode_to_discard > 0, "AWC: s%i discard of %x found at chunk %lx\n", num_stream, ms->decode_to_discard, stream->offset); - stream->offset += channel_offset + get_block_header_size(stream->streamfile, stream->offset, data); - - /* A new block may repeat frame bytes from prev block, and decode_to_discard has the number of repeated samples. - * However in RDR PS3 (not GTA5?) the value can be off (ie. discards 1152 but the repeat decodes to ~1152*4). - * I can't figure out why, so just find and skip the repeat data manually (probably better for mpg123 too) */ - if (ms->decode_to_discard) { - size_t repeat = get_repeated_data_size(stream->streamfile, stream->offset, last_offset); - if (repeat > 0) - ms->decode_to_discard = 0; - stream->offset += repeat; - ms->current_size_target -= repeat; - } - } - } - - - /* update frame */ - if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) ) - goto fail; - current_data_size = info.frame_size; - - ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset, current_data_size, stream->streamfile); - - stream->offset += current_data_size; - - ms->current_size_count += current_data_size; - - return 1; -fail: - return 0; -} - -#endif +#include "mpeg_decoder.h" + +#ifdef VGM_USE_MPEG + +/** + * AWC music uses blocks (sfx doesn't), the fun part being each channel has different num_samples/frames + * per block, so it's unsuitable for the normal "blocked" layout and parsed here instead. + * Channel data is separate within the block (first all frames of ch0, then ch1, etc), padded, and sometimes + * the last few frames of a channel are repeated in the new block (marked with the "discard samples" field). + */ + +/* block header size, algined/padded to 0x800 */ +static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, mpeg_codec_data *data) { + size_t header_size = 0; + int i; + int entries = data->config.channels; + int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE; + + for (i = 0; i < entries; i++) { + header_size += 0x18; + header_size += read_32bit(offset + 0x18*i + 0x04, streamFile) * 0x04; /* entries in the table */ + } + + if (header_size % 0x800) /* padded */ + header_size += 0x800 - (header_size % 0x800); + + return header_size; +} + +/* find data that repeats in the beginning of a new block at the end of last block */ +static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, off_t last_offset) { + uint8_t new_frame[0x1000];/* buffer to avoid fseek back and forth */ + mpeg_frame_info info; + off_t off; + int i; + + /* read block first frame */ + if ( !mpeg_get_frame_info(streamFile, new_offset, &info)) + goto fail; + if (info.frame_size > 0x1000) + goto fail; + if (read_streamfile(new_frame,new_offset, info.frame_size,streamFile) != info.frame_size) + goto fail; + + /* find the frame in last bytes of prev block */ + off = last_offset - 0x4000; /* typical max is 5-10 frames of ~0x200, no way to know exact size */ + while (off < last_offset) { + /* compare frame vs prev block data */ + for (i = 0; i < info.frame_size; i++) { + if ((uint8_t)read_8bit(off+i,streamFile) != new_frame[i]) + break; + } + + /* frame fully compared? */ + if (i == info.frame_size) + return last_offset - off; + else + off += i+1; + } + +fail: + VGM_LOG("AWC: can't find repeat size, new=0x%08x, last=0x%08x\n", (uint32_t)new_offset, (uint32_t)last_offset); + return 0; /* keep on truckin' */ +} + + +/* init config and validate */ +int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { + mpeg_frame_info info; + int is_music; + + /* start_offset can point to a block header that always starts with 0 (music) or normal data (sfx) */ + is_music = read_32bitBE(start_offset, streamFile) == 0x00000000; + if (is_music) + start_offset += get_block_header_size(streamFile, start_offset, data); + + /* get frame info at offset */ + if ( !mpeg_get_frame_info(streamFile, start_offset, &info)) + goto fail; + switch(info.layer) { + case 1: *coding_type = coding_MPEG_layer1; break; + case 2: *coding_type = coding_MPEG_layer2; break; + case 3: *coding_type = coding_MPEG_layer3; break; + default: goto fail; + } + data->channels_per_frame = info.channels; + data->samples_per_frame = info.frame_samples; + + + /* extra checks */ + if (is_music) { + if (data->config.chunk_size <= 0) + goto fail; /* needs block size */ + } + + /* no big encoder delay added (for sfx they can start in less than ~300 samples) */ + + return 1; +fail: + return 0; +} + + +/* writes data to the buffer and moves offsets, parsing AWC blocks */ +int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { + mpeg_custom_stream *ms = data->streams[num_stream]; + mpeg_frame_info info; + size_t current_data_size = 0, data_offset; + size_t file_size = get_streamfile_size(stream->streamfile); + int i; + + + /* blocked layout used for music */ + if (data->config.chunk_size) { + off_t last_offset = stream->offset; /* when block end needs to be known */ + + /* block ended for this channel, move to next block start */ + if (ms->current_size_count > 0 && ms->current_size_count == ms->current_size_target) { + //mpg123_open_feed(ms->m); //todo reset maybe needed? + + data_offset = stream->offset - stream->channel_start_offset; /* ignoring header */ + data_offset -= data_offset % data->config.chunk_size; /* start of current block */ + stream->offset = stream->channel_start_offset + data_offset + data->config.chunk_size; + + ms->current_size_count = 0; + ms->current_size_target = 0; + } + + /* just in case, shouldn't happen */ + if (stream->offset >= file_size) { + goto fail; + } + + /* block starts for this channel, point to mpeg data */ + if (ms->current_size_count == 0) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE; + off_t channel_offset = 0; + + /* block has a header with base info per channel and table per channel (see blocked_awc.c) */ + ms->decode_to_discard = read_32bit(stream->offset + 0x18*num_stream + 0x08, stream->streamfile); + ms->current_size_target = read_32bit(stream->offset + 0x18*num_stream + 0x14, stream->streamfile); + + for (i = 0; i < num_stream; i++) { /* num_stream serves as channel */ + size_t channel_size = read_32bit(stream->offset + 0x18*i + 0x14, stream->streamfile); + if (channel_size % 0x10) /* 32b aligned */ + channel_size += 0x10 - channel_size % 0x10; + + channel_offset += channel_size; + } + + //;VGM_ASSERT(ms->decode_to_discard > 0, "AWC: s%i discard of %x found at chunk %lx\n", num_stream, ms->decode_to_discard, stream->offset); + stream->offset += channel_offset + get_block_header_size(stream->streamfile, stream->offset, data); + + /* A new block may repeat frame bytes from prev block, and decode_to_discard has the number of repeated samples. + * However in RDR PS3 (not GTA5?) the value can be off (ie. discards 1152 but the repeat decodes to ~1152*4). + * I can't figure out why, so just find and skip the repeat data manually (probably better for mpg123 too) */ + if (ms->decode_to_discard) { + size_t repeat = get_repeated_data_size(stream->streamfile, stream->offset, last_offset); + if (repeat > 0) + ms->decode_to_discard = 0; + stream->offset += repeat; + ms->current_size_target -= repeat; + } + } + } + + + /* update frame */ + if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) ) + goto fail; + current_data_size = info.frame_size; + + ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset, current_data_size, stream->streamfile); + + stream->offset += current_data_size; + + ms->current_size_count += current_data_size; + + return 1; +fail: + return 0; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c index ecebf4998..4f0f19b47 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c @@ -1,4 +1,5 @@ #include "mpeg_decoder.h" +#include "mpeg_bitreader.h" #ifdef VGM_USE_MPEG @@ -27,9 +28,9 @@ /* helper to pass around */ typedef struct { - STREAMFILE *sf; + STREAMFILE* sf; off_t offset; - vgm_bitstream is; + bitstream_t is; uint8_t buf[EALAYER3_MAX_EA_FRAME_SIZE]; int leftover_bits; } ealayer3_buffer_t; @@ -82,21 +83,21 @@ typedef struct { } ealayer3_frame_t; -static int ealayer3_parse_frame(mpeg_codec_data *data, int num_stream, ealayer3_buffer_t *ib, ealayer3_frame_t *eaf); -static int ealayer3_parse_frame_v1(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf, int channels_per_frame, int is_v1b); -static int ealayer3_parse_frame_v2(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf); -static int ealayer3_parse_frame_common(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf); -static int ealayer3_rebuild_mpeg_frame(vgm_bitstream *is_0, ealayer3_frame_t *eaf_0, vgm_bitstream *is_1, ealayer3_frame_t *eaf_1, vgm_bitstream *os); -static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_t *eaf); -static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start); -static int ealayer3_is_empty_frame_v2p(STREAMFILE *sf, off_t offset); +static int ealayer3_parse_frame(mpeg_codec_data* data, int num_stream, ealayer3_buffer_t* ib, ealayer3_frame_t* eaf); +static int ealayer3_parse_frame_v1(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf, int channels_per_frame, int is_v1b); +static int ealayer3_parse_frame_v2(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf); +static int ealayer3_parse_frame_common(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf); +static int ealayer3_rebuild_mpeg_frame(bitstream_t* is_0, ealayer3_frame_t* eaf_0, bitstream_t* is_1, ealayer3_frame_t* eaf_1, bitstream_t* os); +static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, ealayer3_frame_t* eaf); +static int ealayer3_skip_data(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, int at_start); +static int ealayer3_is_empty_frame_v2p(STREAMFILE* sf, off_t offset); /* **************************************************************************** */ /* EXTERNAL API */ /* **************************************************************************** */ /* init codec from an EALayer3 frame */ -int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamfile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { +int mpeg_custom_setup_init_ealayer3(STREAMFILE* streamfile, off_t start_offset, mpeg_codec_data* data, coding_t *coding_type) { int ok; ealayer3_buffer_t ib = {0}; ealayer3_frame_t eaf; @@ -130,7 +131,7 @@ fail: } /* writes data to the buffer and moves offsets, transforming EALayer3 frames */ -int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { +int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream) { mpeg_custom_stream *ms = data->streams[num_stream]; int ok, granule_found; ealayer3_buffer_t ib_0 = {0}, ib_1 = {0}; @@ -148,7 +149,7 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data * /* read first frame/granule, or PCM-only frame (found alone at the end of SCHl streams) */ - { + { //;VGM_LOG("s%i: get granule0 at %lx\n", num_stream,stream->offset); if (!ealayer3_skip_data(stream, data, num_stream, 1)) goto fail; @@ -168,15 +169,15 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data * if (!ealayer3_skip_data(stream, data, num_stream, 0)) goto fail; - } + } - /* In EAL3 V2P sometimes there is a new SNS/SPS block between granules. Instead of trying to fix it here - * or in blocked layout (too complex/patchy), SNS/SPS uses a custom streamfile that simply removes all - * block headers, so this parser only sees raw EALayer3 data. It also discards samples, which confuses - * blocked layout calculations - * - * Similarly (as V2P decodes and writes 1 granule at a time) stream can end in granule0. Since mpg123 - * decodes in pairs we detect and feed it a fake end granule1, to get the last granule0's 576 samples. */ + /* In EAL3 V2P sometimes there is a new SNS/SPS block between granules. Instead of trying to fix it here + * or in blocked layout (too complex/patchy), SNS/SPS uses a custom streamfile that simply removes all + * block headers, so this parser only sees raw EALayer3 data. It also discards samples, which confuses + * blocked layout calculations + * + * Similarly (as V2P decodes and writes 1 granule at a time) stream can end in granule0. Since mpg123 + * decodes in pairs we detect and feed it a fake end granule1, to get the last granule0's 576 samples. */ granule_found = 0; /* get second frame/granule (MPEG1 only) if first granule was found */ @@ -219,10 +220,9 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data * /* rebuild EALayer3 frame to MPEG frame */ { - vgm_bitstream os = {0}; + bitstream_t os = {0}; - os.buf = ms->buffer; - os.bufsize = ms->buffer_size; + init_bitstream(&os, ms->buffer, ms->buffer_size); ok = ealayer3_rebuild_mpeg_frame(&ib_0.is, &eaf_0, &ib_1.is, &eaf_1, &os); if (!ok) goto fail; @@ -243,7 +243,7 @@ fail: /* Read at most N bits from streamfile. This makes more smaller reads (not good) but * allows exact frame size reading (good), as reading over a frame then reading back * is expensive in EALayer3 since it uses custom streamfiles. */ -static void fill_buf(ealayer3_buffer_t *ib, int bits) { +static void fill_buf(ealayer3_buffer_t* ib, int bits) { size_t read_size, bytes_size; int mod; @@ -273,7 +273,7 @@ static void fill_buf(ealayer3_buffer_t *ib, int bits) { ib->leftover_bits = (mod > 0 ? 8 - mod : 0); } -static int ealayer3_parse_frame(mpeg_codec_data *data, int num_stream, ealayer3_buffer_t *ib, ealayer3_frame_t *eaf) { +static int ealayer3_parse_frame(mpeg_codec_data* data, int num_stream, ealayer3_buffer_t* ib, ealayer3_frame_t* eaf) { int ok; /* We must pass this from state, as not all EA-frames have channel info. @@ -302,14 +302,14 @@ fail: /* read V1"a" (in SCHl) and V1"b" (in SNS) EALayer3 frame */ -static int ealayer3_parse_frame_v1(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf, int channels_per_frame, int is_v1b) { +static int ealayer3_parse_frame_v1(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf, int channels_per_frame, int is_v1b) { int ok; - vgm_bitstream *is = &ib->is; + bitstream_t* is = &ib->is; /* read EA-frame V1 header */ fill_buf(ib, 8); - r_bits(is, 8,&eaf->v1_pcm_flag); + rb_bits(is, 8,&eaf->v1_pcm_flag); eaf->pre_size = 1; /* 8b */ @@ -331,15 +331,15 @@ static int ealayer3_parse_frame_v1(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf, /* check PCM block */ if (eaf->v1_pcm_flag == 0xEE) { fill_buf(ib, 32); - r_bits(is, 16,&eaf->v1_offset_samples); /* PCM block offset in the buffer */ - r_bits(is, 16,&eaf->v1_pcm_samples); /* number of PCM samples, can be 0 */ + rb_bits(is, 16,&eaf->v1_offset_samples); /* PCM block offset in the buffer */ + rb_bits(is, 16,&eaf->v1_pcm_samples); /* number of PCM samples, can be 0 */ eaf->pre_size += 2+2; /* 16b+16b */ eaf->pcm_size = (2*eaf->v1_pcm_samples * channels_per_frame); if (is_v1b) { /* extra 32b in v1b */ fill_buf(ib, 32); - r_bits(is, 32,&eaf->v1_pcm_unknown); + rb_bits(is, 32,&eaf->v1_pcm_unknown); eaf->pre_size += 4; /* 32b */ @@ -356,26 +356,26 @@ fail: /* read V2"PCM" and V2"Spike" EALayer3 frame (exactly the same but V2P seems to have bigger * PCM blocks and maybe smaller frames) */ -static int ealayer3_parse_frame_v2(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf) { +static int ealayer3_parse_frame_v2(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf) { int ok; - vgm_bitstream *is = &ib->is; + bitstream_t* is = &ib->is; /* read EA-frame V2 header */ fill_buf(ib, 16); - r_bits(is, 1,&eaf->v2_extended_flag); - r_bits(is, 1,&eaf->v2_stereo_flag); - r_bits(is, 2,&eaf->v2_reserved); - r_bits(is, 12,&eaf->v2_frame_size); + rb_bits(is, 1,&eaf->v2_extended_flag); + rb_bits(is, 1,&eaf->v2_stereo_flag); + rb_bits(is, 2,&eaf->v2_reserved); + rb_bits(is, 12,&eaf->v2_frame_size); eaf->pre_size = 2; /* 16b */ if (eaf->v2_extended_flag) { fill_buf(ib, 32); - r_bits(is, 2,&eaf->v2_offset_mode); - r_bits(is, 10,&eaf->v2_offset_samples); - r_bits(is, 10,&eaf->v2_pcm_samples); - r_bits(is, 10,&eaf->v2_common_size); + rb_bits(is, 2,&eaf->v2_offset_mode); + rb_bits(is, 10,&eaf->v2_offset_samples); + rb_bits(is, 10,&eaf->v2_pcm_samples); + rb_bits(is, 10,&eaf->v2_common_size); eaf->pre_size += 4; /* 32b */ } @@ -406,7 +406,7 @@ fail: /* parses an EALayer3 frame (common part) */ -static int ealayer3_parse_frame_common(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf) { +static int ealayer3_parse_frame_common(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf) { /* index tables */ static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; static const int sample_rates[4][4] = { /* [version_index][sample rate index] */ @@ -417,17 +417,17 @@ static int ealayer3_parse_frame_common(ealayer3_buffer_t *ib, ealayer3_frame_t * }; static const int channels[4] = { 2,2,2, 1 }; /* [channel_mode] */ - vgm_bitstream *is = &ib->is; + bitstream_t* is = &ib->is; off_t start_b_off = is->b_off; int i, fill_bits, others_2_bits; /* read main header */ fill_buf(ib, 8); - r_bits(is, 2,&eaf->version_index); - r_bits(is, 2,&eaf->sample_rate_index); - r_bits(is, 2,&eaf->channel_mode); - r_bits(is, 2,&eaf->mode_extension); + rb_bits(is, 2,&eaf->version_index); + rb_bits(is, 2,&eaf->sample_rate_index); + rb_bits(is, 2,&eaf->channel_mode); + rb_bits(is, 2,&eaf->mode_extension); /* check empty frame */ @@ -455,7 +455,7 @@ static int ealayer3_parse_frame_common(ealayer3_buffer_t *ib, ealayer3_frame_t * /* read side info */ fill_buf(ib, 1); - r_bits(is, 1,&eaf->granule_index); + rb_bits(is, 1,&eaf->granule_index); fill_bits = (eaf->mpeg1 && eaf->granule_index == 1) ? 4 * eaf->channels : 0; fill_bits = fill_bits + (12 + 32 + others_2_bits) * eaf->channels; @@ -463,15 +463,15 @@ static int ealayer3_parse_frame_common(ealayer3_buffer_t *ib, ealayer3_frame_t * if (eaf->mpeg1 && eaf->granule_index == 1) { for (i = 0; i < eaf->channels; i++) { - r_bits(is, 4,&eaf->scfsi[i]); + rb_bits(is, 4,&eaf->scfsi[i]); } } for (i = 0; i < eaf->channels; i++) { - r_bits(is, 12,&eaf->main_data_size[i]); + rb_bits(is, 12,&eaf->main_data_size[i]); /* divided in 47b=32+15 (MPEG1) or 51b=32+19 (MPEG2), arbitrarily */ - r_bits(is, 32,&eaf->others_1[i]); - r_bits(is, others_2_bits,&eaf->others_2[i]); + rb_bits(is, 32,&eaf->others_1[i]); + rb_bits(is, others_2_bits,&eaf->others_2[i]); } @@ -496,7 +496,7 @@ fail: /* converts an EALAYER3 frame to a standard MPEG frame from pre-parsed info */ -static int ealayer3_rebuild_mpeg_frame(vgm_bitstream *is_0, ealayer3_frame_t *eaf_0, vgm_bitstream *is_1, ealayer3_frame_t *eaf_1, vgm_bitstream* os) { +static int ealayer3_rebuild_mpeg_frame(bitstream_t* is_0, ealayer3_frame_t* eaf_0, bitstream_t* is_1, ealayer3_frame_t* eaf_1, bitstream_t* os) { uint32_t c = 0; int i, j; int expected_bitrate_index, expected_frame_size; @@ -537,55 +537,55 @@ static int ealayer3_rebuild_mpeg_frame(vgm_bitstream *is_0, ealayer3_frame_t *ea #endif /* write MPEG1/2 frame header */ - w_bits(os, 11, 0x7FF); /* sync */ - w_bits(os, 2, eaf_0->version_index); - w_bits(os, 2, 0x01); /* layer III index */ - w_bits(os, 1, 1); /* "no CRC" flag */ - w_bits(os, 4, expected_bitrate_index); - w_bits(os, 2, eaf_0->sample_rate_index); - w_bits(os, 1, 0); /* padding */ - w_bits(os, 1, 0); /* private */ - w_bits(os, 2, eaf_0->channel_mode); - w_bits(os, 2, eaf_0->mode_extension); - w_bits(os, 1, 1); /* copyrighted */ - w_bits(os, 1, 1); /* original */ - w_bits(os, 2, 0); /* emphasis */ + wb_bits(os, 11, 0x7FF); /* sync */ + wb_bits(os, 2, eaf_0->version_index); + wb_bits(os, 2, 0x01); /* layer III index */ + wb_bits(os, 1, 1); /* "no CRC" flag */ + wb_bits(os, 4, expected_bitrate_index); + wb_bits(os, 2, eaf_0->sample_rate_index); + wb_bits(os, 1, 0); /* padding */ + wb_bits(os, 1, 0); /* private */ + wb_bits(os, 2, eaf_0->channel_mode); + wb_bits(os, 2, eaf_0->mode_extension); + wb_bits(os, 1, 1); /* copyrighted */ + wb_bits(os, 1, 1); /* original */ + wb_bits(os, 2, 0); /* emphasis */ if (eaf_0->mpeg1) { int private_bits = (eaf_0->channels==1 ? 5 : 3); /* write MPEG1 side info */ - w_bits(os, 9, 0); /* main data start (no bit reservoir) */ - w_bits(os, private_bits, 0); + wb_bits(os, 9, 0); /* main data start (no bit reservoir) */ + wb_bits(os, private_bits, 0); for (i = 0; i < eaf_1->channels; i++) { - w_bits(os, 4, eaf_1->scfsi[i]); /* saved in granule1 only */ + wb_bits(os, 4, eaf_1->scfsi[i]); /* saved in granule1 only */ } for (i = 0; i < eaf_0->channels; i++) { /* granule0 */ - w_bits(os, 12, eaf_0->main_data_size[i]); - w_bits(os, 32, eaf_0->others_1[i]); - w_bits(os, 47-32, eaf_0->others_2[i]); + wb_bits(os, 12, eaf_0->main_data_size[i]); + wb_bits(os, 32, eaf_0->others_1[i]); + wb_bits(os, 47-32, eaf_0->others_2[i]); } for (i = 0; i < eaf_1->channels; i++) { /* granule1 */ - w_bits(os, 12, eaf_1->main_data_size[i]); - w_bits(os, 32, eaf_1->others_1[i]); - w_bits(os, 47-32, eaf_1->others_2[i]); + wb_bits(os, 12, eaf_1->main_data_size[i]); + wb_bits(os, 32, eaf_1->others_1[i]); + wb_bits(os, 47-32, eaf_1->others_2[i]); } /* write MPEG1 main data */ is_0->b_off = eaf_0->data_offset_b; for (i = 0; i < eaf_0->channels; i++) { /* granule0 */ for (j = 0; j < eaf_0->main_data_size[i]; j++) { - r_bits(is_0, 1, &c); - w_bits(os, 1, c); + rb_bits(is_0, 1, &c); + wb_bits(os, 1, c); } } is_1->b_off = eaf_1->data_offset_b; for (i = 0; i < eaf_1->channels; i++) { /* granule1 */ for (j = 0; j < eaf_1->main_data_size[i]; j++) { - r_bits(is_1, 1, &c); - w_bits(os, 1, c); + rb_bits(is_1, 1, &c); + wb_bits(os, 1, c); } } } @@ -593,21 +593,21 @@ static int ealayer3_rebuild_mpeg_frame(vgm_bitstream *is_0, ealayer3_frame_t *ea int private_bits = (eaf_0->channels==1 ? 1 : 2); /* write MPEG2 side info */ - w_bits(os, 8, 0); /* main data start (no bit reservoir) */ - w_bits(os, private_bits, 0); + wb_bits(os, 8, 0); /* main data start (no bit reservoir) */ + wb_bits(os, private_bits, 0); for (i = 0; i < eaf_0->channels; i++) { - w_bits(os, 12, eaf_0->main_data_size[i]); - w_bits(os, 32, eaf_0->others_1[i]); - w_bits(os, 51-32, eaf_0->others_2[i]); + wb_bits(os, 12, eaf_0->main_data_size[i]); + wb_bits(os, 32, eaf_0->others_1[i]); + wb_bits(os, 51-32, eaf_0->others_2[i]); } /* write MPEG2 main data */ is_0->b_off = eaf_0->data_offset_b; for (i = 0; i < eaf_0->channels; i++) { for (j = 0; j < eaf_0->main_data_size[i]; j++) { - r_bits(is_0, 1, &c); - w_bits(os, 1, c); + rb_bits(is_0, 1, &c); + wb_bits(os, 1, c); } } } @@ -615,7 +615,7 @@ static int ealayer3_rebuild_mpeg_frame(vgm_bitstream *is_0, ealayer3_frame_t *ea /* align to closest 8b */ if (os->b_off % 8) { int align_bits = 8 - (os->b_off % 8); - w_bits(os, align_bits, 0); + wb_bits(os, align_bits, 0); } @@ -636,7 +636,7 @@ fail: return 0; } -static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_number, int channels_per_frame, int is_packed, STREAMFILE *sf) { +static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_number, int channels_per_frame, int is_packed, STREAMFILE* sf) { int i, ch; uint8_t pcm_block[1152 * 2 * 2]; /* assumed max: 1 MPEG frame samples * 16b * max channels */ size_t pcm_size = pcm_number * 2 * channels_per_frame; @@ -687,7 +687,7 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n /* write PCM block directly to sample buffer and setup decode discard (EALayer3 seems to use this as a prefetch of sorts). * Seems to alter decoded sample buffer to handle encoder delay/padding in a twisted way. */ -static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_t *eaf) { +static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, ealayer3_frame_t* eaf) { mpeg_custom_stream *ms = data->streams[num_stream]; int channels_per_frame = ms->channels_per_frame; size_t bytes_filled; @@ -817,7 +817,7 @@ fail: * * EALayer3 v1 in SCHl uses external offsets and 1ch multichannel instead. */ -static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) { +static int ealayer3_skip_data(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, int at_start) { int ok, i; ealayer3_buffer_t ib = {0}; ealayer3_frame_t eaf; @@ -845,7 +845,7 @@ fail: return 0; } -static int ealayer3_is_empty_frame_v2p(STREAMFILE *sf, off_t offset) { +static int ealayer3_is_empty_frame_v2p(STREAMFILE* sf, off_t offset) { /* V2P frame header should contain a valid frame size (lower 12b) */ uint16_t v2_header = read_u16be(offset, sf); return (v2_header % 0xFFF) == 0 || v2_header == 0xFFFF; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index 0a19a050e..cf421c63f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -3,11 +3,7 @@ #include "../vgmstream.h" #ifdef VGM_USE_MPEG -#ifdef __MACOSX__ #include -#else -#include -#endif #include "mpeg_decoder.h" @@ -509,8 +505,7 @@ void free_mpeg(mpeg_codec_data *data) { } /* seeks stream to 0 */ -void reset_mpeg(VGMSTREAM *vgmstream) { - mpeg_codec_data *data = vgmstream->codec_data; +void reset_mpeg(mpeg_codec_data* data) { if (!data) return; flush_mpeg(data); @@ -597,6 +592,10 @@ void flush_mpeg(mpeg_codec_data * data) { data->buffer_used = 0; } +int mpeg_get_sample_rate(mpeg_codec_data* data) { + return data->sample_rate_per_frame; +} + long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) { /* if not found just return 0 and expect to fail (if used for num_samples) */ if (!data->custom) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h index a8f117335..af8293c6f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h @@ -1,34 +1,87 @@ -#ifndef _MPEG_DECODER_H_ -#define _MPEG_DECODER_H_ - -#include "../vgmstream.h" -#include "../coding/coding.h" - -/* used by mpeg_decoder.c, but scattered in other .c files */ -#ifdef VGM_USE_MPEG -typedef struct { - int version; - int layer; - int bit_rate; - int sample_rate; - int frame_samples; - int frame_size; /* bytes */ - int channels; -} mpeg_frame_info; - -int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info); - -int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); -int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); -int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); -int mpeg_custom_setup_init_eamp3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); - -int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); -int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); -int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); -int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); -int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); - -#endif/* VGM_USE_MPEG */ - -#endif/*_MPEG_DECODER_H_ */ +#ifndef _MPEG_DECODER_H_ +#define _MPEG_DECODER_H_ + +#include "../vgmstream.h" +#include "../coding/coding.h" + +/* used by mpeg_decoder.c, but scattered in other .c files */ +#ifdef VGM_USE_MPEG +typedef struct { + int version; + int layer; + int bit_rate; + int sample_rate; + int frame_samples; + int frame_size; /* bytes */ + int channels; +} mpeg_frame_info; + +/* represents a single MPEG stream */ +typedef struct { + /* per stream as sometimes mpg123 must be fed in passes if data is big enough (ex. EALayer3 multichannel) */ + uint8_t *buffer; /* raw data buffer */ + size_t buffer_size; + size_t bytes_in_buffer; + int buffer_full; /* raw buffer has been filled */ + int buffer_used; /* raw buffer has been fed to the decoder */ + mpg123_handle *m; /* MPEG decoder */ + + uint8_t *output_buffer; /* decoded samples from this stream (in bytes for mpg123) */ + size_t output_buffer_size; + size_t samples_filled; /* data in the buffer (in samples) */ + size_t samples_used; /* data extracted from the buffer */ + + size_t current_size_count; /* data read (if the parser needs to know) */ + size_t current_size_target; /* max data, until something happens */ + size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */ + + int channels_per_frame; /* for rare cases that streams don't share this */ +} mpeg_custom_stream; + +struct mpeg_codec_data { + /* regular/single MPEG internals */ + uint8_t *buffer; /* raw data buffer */ + size_t buffer_size; + size_t bytes_in_buffer; + int buffer_full; /* raw buffer has been filled */ + int buffer_used; /* raw buffer has been fed to the decoder */ + mpg123_handle *m; /* MPEG decoder */ + struct mpg123_frameinfo mi; /* start info, so it's available even when resetting */ + + /* for internal use */ + int channels_per_frame; + int samples_per_frame; + /* for some calcs */ + int bitrate_per_frame; + int sample_rate_per_frame; + + /* custom MPEG internals */ + int custom; /* flag */ + mpeg_custom_t type; /* mpeg subtype */ + mpeg_custom_config config; /* config depending on the mode */ + + size_t default_buffer_size; + mpeg_custom_stream **streams; /* array of MPEG streams (ex. 2ch+2ch) */ + size_t streams_size; + + size_t skip_samples; /* base encoder delay */ + size_t samples_to_discard; /* for custom mpeg looping */ + +}; + +int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info); + +int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); +int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); +int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); +int mpeg_custom_setup_init_eamp3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); + +int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); +int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); +int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); +int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); +int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); + +#endif/* VGM_USE_MPEG */ + +#endif/*_MPEG_DECODER_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c index f76f2d0d3..3f1899db3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c @@ -31,8 +31,8 @@ void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t fir streamfile = ch1->streamfile; /* external interleave (variable size), stereo */ - bytes_per_frame = get_vgmstream_frame_size(vgmstream); - samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + bytes_per_frame = vgmstream->frame_size; + samples_per_frame = (vgmstream->frame_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; @@ -104,8 +104,8 @@ void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample_t * outbuf, int channelsp 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); + bytes_per_frame = vgmstream->frame_size; + samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2; frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; @@ -167,8 +167,8 @@ void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspac 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); + bytes_per_frame = vgmstream->frame_size; + samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2; frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c index 62fb87866..0e7beabf4 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c @@ -1,4 +1,33 @@ -/* originally from nwatowav.cc 2007.7.28 version, which read: */ +/* Originally from nwatowav.cc (2007.7.28 version) by jagarl. + * - http://www.creator.club.ne.jp/~jagarl/ + * + * Converted to .c by hcs (redone as a lib without RIFF/main handling), some cleanup by bnnm. + * + * nwa format (abridged from the original) + * NWA Header + * data offset index + * data block<0> + * data block<1> + * ... + * data block + * + * - NWA header: 0x2c with nwa info (channels, compression level, etc), no magic number + * - data offset index: pointers to data blocks + * - data block: variable sized DPCM blocks to fixed size PCM (a,b,c compresses to (a),b-a,c-b), + * DPCM codes use variable bits. Usually for 16-bit PCM ends ups using 6-8 bits. + * - Block format: + * - mono: initial PCM (8 or 16-bit) then bitstream + * - stereo: initial PCM for left + right channel then bitstream + * Differential accuracy isn't high so initial PCM is used to correct data in each block (?) + * - bitstream: Roughly each code has an 'exponent' (2 bits) + 'mantissa' (variable bits). + * Depending on compression level + type it configures final shift value and matissa bits. + * There is a run length encoding mode in some cases (Tomoyo After voice files). + * Bitstream bytes follow little endian. + * (some examples here in the original, see decoder). + * + * Original copyright: + */ + /* * Copyright 2001-2007 jagarl / Kazunori Ueno * All Rights Reserved. @@ -33,319 +62,380 @@ #include #include "nwa_decoder.h" -/* can serve up 8 bits at a time */ -static int -getbits (STREAMFILE *file, off_t *offset, int *shift, int bits) -{ - int ret; - if (*shift > 8) - { - ++*offset; - *shift -= 8; + +//NWAInfo::UseRunLength +static int is_use_runlength(NWAData* nwa) { + if (nwa->channels == 2 && nwa->bps == 16 && nwa->complevel == 2) { + return 0; /* sw2 */ } - ret = read_16bitLE(*offset,file) >> *shift; - *shift += bits; - return ret & ((1 << bits) - 1); /* mask */ + + if (nwa->complevel == 5) { + if (nwa->channels == 2) + return 0; // BGM*.nwa in Little Busters! + return 1; // Tomoyo After (.nwk koe file) + } + + return 0; } -NWAData * -open_nwa (STREAMFILE * streamFile, const char *filename) -{ +NWAData* nwalib_open(STREAMFILE* sf) { + uint8_t header[0x2c] = {0}; int i; - NWAData * const nwa = malloc(sizeof(NWAData)); + NWAData* const nwa = malloc(sizeof(NWAData)); if (!nwa) goto fail; - nwa->channels = read_16bitLE(0x00,streamFile); - nwa->bps = read_16bitLE(0x02,streamFile); - nwa->freq = read_32bitLE(0x04,streamFile); - nwa->complevel = read_32bitLE(0x08,streamFile); - nwa->blocks = read_32bitLE(0x10,streamFile); - nwa->datasize = read_32bitLE(0x14,streamFile); - nwa->compdatasize = read_32bitLE(0x18,streamFile); - nwa->samplecount = read_32bitLE(0x1c,streamFile); - nwa->blocksize = read_32bitLE(0x20,streamFile); - nwa->restsize = read_32bitLE(0x24,streamFile); + //NWAData::ReadHeader + + read_streamfile(header, 0x00, sizeof(header), sf); + nwa->channels = get_s16le(header+0x00); + nwa->bps = get_s16le(header+0x02); + nwa->freq = get_s32le(header+0x04); + nwa->complevel = get_s32le(header+0x08); + nwa->dummy = get_s32le(header+0x0c); + nwa->blocks = get_s32le(header+0x10); + nwa->datasize = get_s32le(header+0x14); + nwa->compdatasize = get_s32le(header+0x18); + nwa->samplecount = get_s32le(header+0x1c); + nwa->blocksize = get_s32le(header+0x20); + nwa->restsize = get_s32le(header+0x24); + nwa->dummy2 = get_s32le(header+0x28); + nwa->offsets = NULL; - nwa->buffer = NULL; - nwa->buffer_readpos = NULL; - nwa->file = NULL; + nwa->outdata = NULL; + nwa->outdata_readpos = NULL; + nwa->tmpdata = NULL; + nwa->filesize = get_streamfile_size(sf); - /* PCM not handled here */ - if (nwa->complevel < 0 || nwa->complevel > 5) goto fail; - if (nwa->channels != 1 && nwa->channels != 2) goto fail; + if (nwa->blocks <= 0 || nwa->blocks > 1000000) + /* 1時間を超える曲ってのはないでしょ*/ //surely there won't be songs over 1 hour + goto fail; - if (nwa->bps != 8 && nwa->bps != 16) goto fail; + // NWAData::CheckHeader: - if (nwa->blocks <= 0) goto fail; + if (nwa->channels != 1 && nwa->channels != 2) + goto fail; - if (nwa->compdatasize == 0 - || get_streamfile_size(streamFile) != nwa->compdatasize) goto fail; + if (nwa->bps != 8 && nwa->bps != 16) + goto fail; - if (nwa->datasize != nwa->samplecount * (nwa->bps/8)) goto fail; + // (PCM not handled) - if (nwa->samplecount != - (nwa->blocks-1) * nwa->blocksize + nwa->restsize) goto fail; + if (nwa->complevel < 0 || nwa->complevel > 5) + goto fail; - nwa->offsets = malloc(sizeof(off_t)*nwa->blocks); + if (nwa->filesize != nwa->compdatasize) + goto fail; + + + if (nwa->datasize != nwa->samplecount * (nwa->bps / 8)) + goto fail; + + if (nwa->samplecount != (nwa->blocks - 1) * nwa->blocksize + nwa->restsize) + goto fail; + + /* offset index 読み込み */ //read offset index + nwa->offsets = malloc(sizeof(off_t) * nwa->blocks); if (!nwa->offsets) goto fail; - for (i = 0; i < nwa->blocks; i++) - { - int32_t o = read_32bitLE(0x2c+i*4,streamFile); + for (i = 0; i < nwa->blocks; i++) { + int32_t o = read_s32le(0x2c + i*4, sf); if (o < 0) goto fail; nwa->offsets[i] = o; } - if (nwa->offsets[nwa->blocks-1] >= nwa->compdatasize) goto fail; - - if (nwa->restsize > nwa->blocksize) nwa->buffer = - malloc(sizeof(sample)*nwa->restsize); - else nwa->buffer = - malloc(sizeof(sample)*nwa->blocksize); - if (nwa->buffer == NULL) goto fail; - - nwa->buffer_readpos = nwa->buffer; - - nwa->samples_in_buffer = 0; + if (nwa->offsets[nwa->blocks-1] >= nwa->compdatasize) + goto fail; + nwa->use_runlength = is_use_runlength(nwa); nwa->curblock = 0; - /* if we got this far, it's probably safe */ - nwa->file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!nwa->file) goto fail; + + //extra + if (nwa->restsize > nwa->blocksize) { + nwa->outdata = malloc(sizeof(int16_t) * nwa->restsize); + } + else { + nwa->outdata = malloc(sizeof(int16_t) * nwa->blocksize); + } + if (!nwa->outdata) + goto fail; + + /* これ以上の大きさはないだろう、、、 */ //probably not over this size + nwa->tmpdata = malloc(sizeof(uint8_t) * nwa->blocksize * (nwa->bps / 8) * 2); + if (!nwa->tmpdata) + goto fail; + + nwa->outdata_readpos = nwa->outdata; + nwa->samples_in_buffer = 0; return nwa; fail: - if (nwa) - { - close_nwa(nwa); - } - + nwalib_close(nwa); return NULL; } -void -close_nwa(NWAData * nwa) -{ +void nwalib_close(NWAData * nwa) { if (!nwa) return; - if (nwa->offsets) - free (nwa->offsets); - nwa->offsets = NULL; - if (nwa->buffer) - free (nwa->buffer); - nwa->buffer = NULL; - if (nwa->file) - close_streamfile (nwa->file); - nwa->file = NULL; + free(nwa->offsets); + free(nwa->outdata); + free(nwa->tmpdata); free(nwa); } -void -reset_nwa(NWAData *nwa) -{ +//NWAData::Rewind +void nwalib_reset(NWAData* nwa) { nwa->curblock = 0; - nwa->buffer_readpos = nwa->buffer; + nwa->outdata_readpos = nwa->outdata; nwa->samples_in_buffer = 0; } -static int use_runlength(NWAData *nwa) -{ - if (nwa->channels == 2 && nwa->bps == 16 && nwa->complevel == 2) - { - /* sw2 */ - return 0; +// can serve up 8 bits at a time +static int getbits(const uint8_t** p_data, int* shift, int bits) { + int ret; + if (*shift > 8) { + (*p_data)++; + *shift -= 8; } - if (nwa->complevel == 5) - { - if (nwa->channels == 2) return 0; /* BGM*.nwa in Little Busters! */ - return 1; /* Tomoyo After (.nwk koe file) */ + + ret = get_s16le(*p_data) >> *shift; + *shift += bits; + return ret & ((1 << bits) - 1); /* mask */ +} + +// NWADecode +static void decode_block(NWAData* nwa, const uint8_t* data, int outdatasize) { + sample d[2]; + int i; + int shift = 0; + + int dsize = outdatasize / (nwa->bps / 8); + int flip_flag = 0; /* stereo 用 */ //for stereo + int runlength = 0; + + /* 最初のデータを読み込む */ //read initial data + for (i = 0; i < nwa->channels; i++) { + if (nwa->bps == 8) { + d[i] = get_s8(data); + data += 1; + } + else { /* nwa->bps == 16 */ + d[i] = get_s16le(data); + data += 2; + } } + + for (i = 0; i < dsize; i++) { + if (runlength == 0) { /* コピーループ中でないならデータ読み込み */ //read data if not in the copy loop + int type = getbits(&data, &shift, 3); + + /* type により分岐:0, 1-6, 7 */ //fork depending on type + if (type == 7) { + /* 7 : 大きな差分 */ //big diff + /* RunLength() 有効時(CompLevel==5, 音声ファイル) では無効 */ //invalid when using RLE (comp=5, voice file) + if (getbits(&data, &shift, 1) == 1) { + d[flip_flag] = 0; /* 未使用 */ //unused + } + else { + int BITS, SHIFT; + if (nwa->complevel >= 3) { + BITS = 8; + SHIFT = 9; + } + else { + BITS = 8 - nwa->complevel; + SHIFT = 2 + 7 + nwa->complevel; + } + + { + const int MASK1 = (1 << (BITS - 1)); + const int MASK2 = (1 << (BITS - 1)) - 1; + int b = getbits(&data, &shift, BITS); + if (b & MASK1) + d[flip_flag] -= (b & MASK2) << SHIFT; + else + d[flip_flag] += (b & MASK2) << SHIFT; + } + } + } + else if (type != 0) { + /* 1-6 : 通常の差分 */ //normal diff + int BITS, SHIFT; + if (nwa->complevel >= 3) { + BITS = nwa->complevel + 3; + SHIFT = 1 + type; + } + else { + BITS = 5 - nwa->complevel; + SHIFT = 2 + type + nwa->complevel; + } + { + const int MASK1 = (1 << (BITS - 1)); + const int MASK2 = (1 << (BITS - 1)) - 1; + int b = getbits(&data, &shift, BITS); + if (b & MASK1) + d[flip_flag] -= (b & MASK2) << SHIFT; + else + d[flip_flag] += (b & MASK2) << SHIFT; + } + } + else { /* type == 0 */ + /* ランレングス圧縮なしの場合はなにもしない */ //does nothing in case of no RLE compression + if (nwa->use_runlength) { + /* ランレングス圧縮ありの場合 */ //in case of RLE compression + runlength = getbits(&data, &shift, 1); + if (runlength == 1) { + runlength = getbits(&data, &shift, 2); + if (runlength == 3) { + runlength = getbits(&data, &shift, 8); + } + } + } + } + } + else { + runlength--; + } + + if (nwa->bps == 8) { + nwa->outdata[i] = d[flip_flag] * 256; //extra (original outputs 8-bit) + } + else { + nwa->outdata[i] = d[flip_flag]; + } + + if (nwa->channels == 2) + flip_flag ^= 1; /* channel 切り替え */ //channel swap + } + + nwa->samples_in_buffer = dsize; +} + +//NWAData::Decode +int nwalib_decode(STREAMFILE* sf, NWAData* nwa) { + /* some wav/pcm handling skipped here */ + + /* 今回読み込む/デコードするデータの大きさを得る */ //get current read/decode data size + int curblocksize, curcompsize; + if (nwa->curblock != nwa->blocks - 1) { + curblocksize = nwa->blocksize * (nwa->bps / 8); + curcompsize = nwa->offsets[nwa->curblock + 1] - nwa->offsets[nwa->curblock]; + if (curblocksize >= nwa->blocksize * (nwa->bps / 8) * 2) { + return -1; // Fatal error + } + } + else { //last block + curblocksize = nwa->restsize * (nwa->bps / 8); + curcompsize = nwa->blocksize * (nwa->bps / 8) * 2; + } + // (in practice compsize is ~200-400 and blocksize ~0x800, but last block can be different) + + /* データ読み込み */ //data read (may read less on last block?) + read_streamfile(nwa->tmpdata, nwa->offsets[nwa->curblock], curcompsize, sf); + + nwa->samples_in_buffer = 0; + nwa->outdata_readpos = nwa->outdata; + + decode_block(nwa, nwa->tmpdata, curblocksize); + + nwa->curblock++; //todo check not over max blocks? + return 0; } -static void -nwa_decode_block(NWAData *nwa) -{ - /* 今回読み込む/デコードするデータの大きさを得る */ - int curblocksize; - if (nwa->curblock != nwa->blocks - 1) - { - curblocksize = nwa->blocksize * (nwa->bps / 8); - //curcompsize = nwa->offsets[nwa->curblock + 1] - nwa->offsets[nwa->curblock]; - } - else - { - curblocksize = nwa->restsize * (nwa->bps / 8); - //curcompsize = nwa->blocksize * (nwa->bps / 8) * 2; - } - - nwa->samples_in_buffer = 0; - nwa->buffer_readpos = nwa->buffer; - - { - sample d[2]; - int i; - int shift = 0; - off_t offset = nwa->offsets[nwa->curblock]; - int dsize = curblocksize / (nwa->bps / 8); - int flip_flag = 0; /* stereo 用 */ - int runlength = 0; - - /* read initial sample value */ - for (i=0;ichannels;i++) - { - if (nwa->bps == 8) { d[i] = read_8bit(offset,nwa->file); } - else /* bps == 16 */ - { - d[i] = read_16bitLE(offset,nwa->file); - offset += 2; - } - } - - for (i = 0; i < dsize; i++) - { - if (runlength == 0) - { /* コピーループ中でないならデータ読み込み */ - int type = getbits(nwa->file, &offset, &shift, 3); - /* type により分岐:0, 1-6, 7 */ - if (type == 7) - { - /* 7 : 大きな差分 */ - /* RunLength() 有効時(CompLevel==5, 音声ファイル) では無効 */ - if (getbits(nwa->file, &offset, &shift, 1) == 1) - { - d[flip_flag] = 0; /* 未使用 */ - } - else - { - int BITS, SHIFT; - if (nwa->complevel >= 3) - { - BITS = 8; - SHIFT = 9; - } - else - { - BITS = 8 - nwa->complevel; - SHIFT = 2 + 7 + nwa->complevel; - } - { - const int MASK1 = (1 << (BITS - 1)); - const int MASK2 = (1 << (BITS - 1)) - 1; - int b = getbits(nwa->file, &offset, &shift, BITS); - if (b & MASK1) - d[flip_flag] -= (b & MASK2) << SHIFT; - else - d[flip_flag] += (b & MASK2) << SHIFT; - } - } - } - else if (type != 0) - { - /* 1-6 : 通常の差分 */ - int BITS, SHIFT; - if (nwa->complevel >= 3) - { - BITS = nwa->complevel + 3; - SHIFT = 1 + type; - } - else - { - BITS = 5 - nwa->complevel; - SHIFT = 2 + type + nwa->complevel; - } - { - const int MASK1 = (1 << (BITS - 1)); - const int MASK2 = (1 << (BITS - 1)) - 1; - int b = getbits(nwa->file, &offset, &shift, BITS); - if (b & MASK1) - d[flip_flag] -= (b & MASK2) << SHIFT; - else - d[flip_flag] += (b & MASK2) << SHIFT; - } - } - else - { /* type == 0 */ - /* ランレングス圧縮なしの場合はなにもしない */ - if (use_runlength(nwa)) - { - /* ランレングス圧縮ありの場合 */ - runlength = getbits(nwa->file, &offset, &shift, 1); - if (runlength == 1) - { - runlength = getbits(nwa->file, &offset, &shift, 2); - if (runlength == 3) - { - runlength = getbits(nwa->file, &offset, &shift, 8); - } - } - } - } - } - else - { - runlength--; - } - if (nwa->bps == 8) - { - nwa->buffer[i] = d[flip_flag]*0x100; - } - else - { - nwa->buffer[i] = d[flip_flag]; - } - nwa->samples_in_buffer++; - if (nwa->channels == 2) - flip_flag ^= 1; /* channel 切り替え */ - } - } - - nwa->curblock++; - return; -} - -void -seek_nwa(NWAData *nwa, int32_t seekpos) -{ - int dest_block = seekpos/(nwa->blocksize/nwa->channels); - int32_t remainder = seekpos%(nwa->blocksize/nwa->channels); +//NWAFILE::Seek (not too similar) +void nwalib_seek(STREAMFILE* sf, NWAData* nwa, int32_t seekpos) { + int dest_block = seekpos / (nwa->blocksize / nwa->channels); + int32_t remainder = seekpos % (nwa->blocksize / nwa->channels); nwa->curblock = dest_block; - nwa_decode_block(nwa); + nwalib_decode(sf, nwa); - nwa->buffer_readpos = nwa->buffer + remainder*nwa->channels; + nwa->outdata_readpos = nwa->outdata + remainder * nwa->channels; nwa->samples_in_buffer -= remainder*nwa->channels; } -/* interface to vgmstream */ -void -decode_nwa(NWAData *nwa, sample *outbuf, - int32_t samples_to_do) -{ - while (samples_to_do > 0) - { - int32_t samples_to_read; +/* ****************************************************************** */ - if (nwa->samples_in_buffer > 0) - { - samples_to_read = nwa->samples_in_buffer/nwa->channels; +#include "coding.h" + + +struct nwa_codec_data { + STREAMFILE* sf; + NWAData* nwa; +}; + +/* interface to vgmstream */ +void decode_nwa(nwa_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { + NWAData* nwa = data->nwa; + + while (samples_to_do > 0) { + if (nwa->samples_in_buffer > 0) { + int32_t samples_to_read = nwa->samples_in_buffer / nwa->channels; if (samples_to_read > samples_to_do) samples_to_read = samples_to_do; - memcpy(outbuf,nwa->buffer_readpos, - sizeof(sample)*samples_to_read*nwa->channels); + memcpy(outbuf, nwa->outdata_readpos, sizeof(sample_t) * samples_to_read * nwa->channels); - nwa->buffer_readpos += samples_to_read*nwa->channels; - nwa->samples_in_buffer -= samples_to_read*nwa->channels; - outbuf += samples_to_read*nwa->channels; + nwa->outdata_readpos += samples_to_read * nwa->channels; + nwa->samples_in_buffer -= samples_to_read * nwa->channels; + outbuf += samples_to_read * nwa->channels; samples_to_do -= samples_to_read; } - else - { - nwa_decode_block(nwa); + else { + int err = nwalib_decode(data->sf, nwa); + if (err < 0) { + VGM_LOG("NWA: decoding error\n"); + return; + } } } } + + +nwa_codec_data* init_nwa(STREAMFILE* sf) { + nwa_codec_data* data = NULL; + + data = malloc(sizeof(nwa_codec_data)); + if (!data) goto fail; + + data->nwa = nwalib_open(sf); + if (!data->nwa) goto fail; + + data->sf = reopen_streamfile(sf, 0); + if (!data->sf) goto fail; + + return data; + +fail: + free_nwa(data); + return NULL; +} + +void seek_nwa(nwa_codec_data* data, int32_t sample) { + if (!data) return; + + nwalib_seek(data->sf, data->nwa, sample); +} + +void reset_nwa(nwa_codec_data* data) { + if (!data) return; + + nwalib_reset(data->nwa); +} + +void free_nwa(nwa_codec_data* data) { + if (!data) return; + + close_streamfile(data->sf); + nwalib_close(data->nwa); + free(data); +} + +STREAMFILE* nwa_get_streamfile(nwa_codec_data* data) { + if (!data) return NULL; + + return data->sf; + } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h index 361736918..983a7c88d 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h @@ -1,4 +1,4 @@ -/* originally from nwatowav.cc 2007.7.28 version, which read: */ +/* derived from nwatowav.cc 2007.7.28 version, which read: */ /* * Copyright 2001-2007 jagarl / Kazunori Ueno * All Rights Reserved. @@ -39,33 +39,39 @@ #include "streamfile.h" #endif -typedef struct NWAData_s -{ +typedef struct NWAData_s { int channels; - int bps; /* bits per sample */ - int freq; /* samples per second */ - int complevel; /* compression level */ - int blocks; /* block count */ - int datasize; /* all data size */ - int compdatasize; /* compressed data size */ - int samplecount; /* all samples */ - int blocksize; /* samples per block */ - int restsize; /* samples of the last block */ + int bps; /* bits per sample */ + int freq; /* samples per second */ + + int complevel; /* compression level */ + int dummy; /* ? : 0x00 */ + + int blocks; /* block count */ + int datasize; /* all data size */ + + int compdatasize; /* compressed data size */ + int samplecount; /* all samples */ + int blocksize; /* samples per block */ + int restsize; /* samples of the last block */ + int dummy2; /* ? : 0x89 */ int curblock; - off_t *offsets; + off_t* offsets; + int filesize; - STREAMFILE *file; + int use_runlength; //extra - /* temporarily store samples */ - sample *buffer; - sample *buffer_readpos; + uint8_t *tmpdata; + int16_t *outdata; + int16_t *outdata_readpos; int samples_in_buffer; } NWAData; -NWAData *open_nwa(STREAMFILE *streamFile, const char *filename); -void close_nwa(NWAData *nwa); -void reset_nwa(NWAData *nwa); -void seek_nwa(NWAData *nwa, int32_t seekpos); +NWAData* nwalib_open(STREAMFILE* sf); +void nwalib_close(NWAData* nwa); +int nwalib_decode(STREAMFILE* sf, NWAData* nwa); +void nwalib_seek(STREAMFILE* sf, NWAData* nwa, int32_t seekpos); +void nwalib_reset(NWAData* nwa); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c index 83566456d..f27e46f6c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c @@ -265,8 +265,7 @@ void reset_ogg_vorbis(VGMSTREAM *vgmstream) { ov_pcm_seek(&data->ogg_vorbis_file, 0); } -void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) { - ogg_vorbis_codec_data *data = vgmstream->codec_data; +void seek_ogg_vorbis(ogg_vorbis_codec_data *data, int32_t num_sample) { if (!data) return; /* this seek crosslaps to avoid possible clicks, so seeking to 0 will diff --git a/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c index 2151c476d..352806813 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c @@ -17,7 +17,7 @@ static const int stex_indexes[16] = { /* OKI table (also from IMA) */ }; -static void pcfx_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample, int mode) { +static void pcfx_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample, int mode) { int code, step, delta; code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; @@ -57,7 +57,7 @@ static void pcfx_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int } } -static void oki16_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample) { +static void oki16_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample) { int code, step, delta; code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; @@ -79,7 +79,15 @@ static void oki16_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, in *out_sample = *hist1; } -static void oki4s_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample) { +/* Possible variation for adp_konami (Viper hardware): + * delta = ((n&7) + 0.5) * stepsize / 4; clamps 2047,-2048; nigh nibble first + * + * Results are very similar, but can't verify actual decoding, and oki4s is used in + * Jubeat (also Konami) so it makes sense they would have reused it. + * Viper sound chip may be a YMZ280B though. + */ + +static void oki4s_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample) { int code, step, delta; code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; @@ -116,7 +124,7 @@ static void oki4s_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, in * so it's needs GENH/TXTH. Sample rate can only be base_value divided by 1/2/3/4, where * base_value is approximately ~31468.5 (follows hardware clocks), mono or interleaved for stereo. */ -void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode) { +void decode_pcfx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode) { int i, sample_count = 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -137,7 +145,7 @@ void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin /* OKI variation with 16-bit output (vs standard's 12-bit), found in FrontWing's PS2 games (Sweet Legacy, Hooligan). * Reverse engineered from the ELF with help from the folks at hcs. */ -void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +void decode_oki16(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, sample_count = 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -169,7 +177,7 @@ void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspaci /* OKI variation with 16-bit output (vs standard's 12-bit) and pre-adjusted tables (shifted by 4), found in Jubeat Clan (AC). * Reverse engineered from the DLLs. */ -void decode_oki4s(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +void decode_oki4s(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, sample_count = 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c index 29d3d71da..9a5910f03 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c @@ -66,6 +66,8 @@ void decode_ptadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspaci index = frame[0x04]; VGM_ASSERT_ONCE(index > 12, "PTADPCM: incorrect index at %x\n", (uint32_t)frame_offset); + if (index > 12) + index = 12; /* write header samples (needed) */ if (sample_count >= first_sample && samples_done < samples_to_do) { @@ -108,5 +110,5 @@ void decode_ptadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspaci size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size) { if (channels <= 0 || frame_size < 0x06) return 0; - return (bytes / channels / frame_size) * ((frame_size-0x05) * 2); + return (bytes / (channels * frame_size)) * (2 + (frame_size-0x05) * 2); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tgcadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/tgcadpcm_decoder.c index 896a2ae03..2362e14b7 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/tgcadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/tgcadpcm_decoder.c @@ -1,42 +1,47 @@ #include "coding.h" /* Decodes SunPlus' ADPCM codec used on the Tiger Game.com. - * Reverse engineered from the Game.com's BIOS. */ - -static uint16_t slopeTable[64] = + * Highly improved, optimised signed 16-bit version of the algorithm. */ +static const int16_t slope_table[8][16] = { - 0x0000, 0x0100, 0x0200, 0x0400, 0x0610, 0x0810, 0x0C18, 0x1020, - 0x0100, 0x0300, 0x0508, 0x0908, 0x0D18, 0x1118, 0x1920, 0x2128, - 0x0208, 0x0508, 0x0810, 0x0E10, 0x1420, 0x1A20, 0x2628, 0x3230, - 0x0310, 0x0710, 0x0B18, 0x1318, 0x1B28, 0x2328, 0x2930, 0x4338, - 0x0418, 0x0918, 0x0E20, 0x1820, 0x2230, 0x2C30, 0x4038, 0x5438, - 0x0520, 0x0B20, 0x1128, 0x1D28, 0x2938, 0x3538, 0x4D38, 0x6F38, - 0x0628, 0x0D28, 0x1430, 0x2230, 0x3038, 0x3E38, 0x5A38, 0x7638, - 0x0730, 0x0F30, 0x1738, 0x2738, 0x3738, 0x4738, 0x6738, 0x7D38 + { 0, 0, 256, -256, 512, -512, 1024, -1024, 1536, -1536, 2048, -2048, 3072, -3072, 4096, -4096 }, + { 256, -256, 768, -768, 1280, -1280, 2304, -2304, 3328, -3328, 4352, -4352, 6400, -6400, 8448, -8448 }, + { 512, -512, 1280, -1280, 2048, -2048, 3584, -3584, 5120, -5120, 6656, -6656, 9728, -9728, 12800, -12800 }, + { 768, -768, 1792, -1792, 2816, -2816, 4864, -4864, 6912, -6912, 8960, -8960, 10496, -10496, 17152, -17152 }, + { 1024, -1024, 2304, -2304, 3584, -3584, 6144, -6144, 8704, -8704, 11264, -11264, 16384, -16384, 21504, -21504 }, + { 1280, -1280, 2816, -2816, 4352, -4352, 7424, -7424, 10496, -10496, 13568, -13568, 19712, -19712, 28416, -28416 }, + { 1536, -1536, 3328, -3328, 5120, -5120, 8704, -8704, 12288, -12288, 15872, -15872, 23040, -23040, 30208, -30208 }, + { 1792, -1792, 3840, -3840, 5888, -5888, 9984, -9984, 14080, -14080, 18176, -18176, 26368, -26368, 32000, -32000 }, +}; + +static const uint8_t next_step[8][16] = +{ + { 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 4 }, + { 0, 0, 0, 0, 1, 1, 1, 1, 3, 3, 3, 3, 4, 4, 5, 5 }, + { 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 5, 5, 6, 6 }, + { 2, 2, 2, 2, 3, 3, 3, 3, 5, 5, 5, 5, 6, 6, 7, 7 }, + { 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6, 7, 7, 7, 7 }, + { 4, 4, 4, 4, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7 }, + { 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7 }, + { 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 } }; void decode_tgc(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int32_t first_sample, int32_t samples_to_do) { for (int i = first_sample, sample_count = 0; i < first_sample + samples_to_do; i++, sample_count++) { - uint8_t samp = ((uint8_t)read_8bit(i/2, stream->streamfile) >> + uint8_t nibble = ((uint8_t)read_8bit(stream->offset + i/2, stream->streamfile) >> (i & 1 ? 4 : 0)) & 0xf; - uint8_t slopeIndex = stream->adpcm_scale | (samp >> 1); + stream->adpcm_history1_32 += slope_table[stream->adpcm_step_index][nibble]; + stream->adpcm_step_index = next_step [stream->adpcm_step_index][nibble]; - stream->adpcm_step_index = slopeTable[slopeIndex] >> 8; - stream->adpcm_scale = slopeTable[slopeIndex] & 0xff; + if (stream->adpcm_history1_32 < -32768) + stream->adpcm_history1_32 = -32768; - stream->adpcm_history1_16 += (samp & 1) ? - -stream->adpcm_step_index: - stream->adpcm_step_index; + if (stream->adpcm_history1_32 > 32767) + stream->adpcm_history1_32 = 32767; - if (stream->adpcm_history1_16 < 0) - stream->adpcm_history1_16 = 0; - - if (stream->adpcm_history1_16 > 0xff) - stream->adpcm_history1_16 = 0xff; - - outbuf[sample_count] = stream->adpcm_history1_16 * 0x100 - 0x8000; + outbuf[sample_count] = (sample_t)stream->adpcm_history1_32; } -} \ No newline at end of file +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_bitreader.h b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_bitreader.h new file mode 100644 index 000000000..89ac08175 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_bitreader.h @@ -0,0 +1,103 @@ +#ifndef _VORBIS_BITREADER_H +#define _VORBIS_BITREADER_H + +/* Simple bitreader for Vorbis' bit format. + * Kept in .h since it's slightly faster (compiler can optimize statics better) */ + + +typedef struct { + uint8_t* buf; /* buffer to read/write */ + size_t bufsize; /* max size of the buffer */ + uint32_t b_off; /* current offset in bits inside the buffer */ +} bitstream_t; + +/* convenience util */ +static void init_bitstream(bitstream_t* b, uint8_t* buf, size_t bufsize) { + b->buf = buf; + b->bufsize = bufsize; + b->b_off = 0; +} + +/* same as (1 << bits) - 1, but that seems to trigger some nasty UB when bits = 32 + * (though in theory (1 << 32) = 0, 0 - 1 = UINT_MAX, but gives 0 compiling in some cases, but not always) */ +static const uint32_t MASK_TABLE[33] = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, + 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, + 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, + 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff +}; + +/* Read bits (max 32) from buf and update the bit offset. Vorbis packs values in LSB order and byte by byte. + * (ex. from 2 bytes 00100111 00000001 we can could read 4b=0111 and 6b=010010, 6b=remainder (second value is split into the 2nd byte) */ +static int rv_bits(bitstream_t* ib, uint32_t bits, uint32_t* value) { + uint32_t shift, mask, pos, val; + + if (bits > 32 || ib->b_off + bits > ib->bufsize * 8) + goto fail; + + pos = ib->b_off / 8; /* byte offset */ + shift = ib->b_off % 8; /* bit sub-offset */ + mask = MASK_TABLE[bits]; /* to remove upper in highest byte */ + + val = ib->buf[pos+0] >> shift; + if (bits + shift > 8) { + val |= ib->buf[pos+1] << (8u - shift); + if (bits + shift > 16) { + val |= ib->buf[pos+2] << (16u - shift); + if (bits + shift > 24) { + val |= ib->buf[pos+3] << (24u - shift); + if (bits + shift > 32) { + val |= ib->buf[pos+4] << (32u - shift); /* upper bits are lost (shifting over 32) */ + } + } + } + } + + *value = (val & mask); + + ib->b_off += bits; + + return 1; +fail: + VGM_LOG_ONCE("BITREADER: read fail\n"); + *value = 0; + return 0; +} + +#ifndef BITSTREAM_READ_ONLY +/* Write bits (max 32) to buf and update the bit offset. Vorbis packs values in LSB order and byte by byte. + * (ex. writing 1101011010 from b_off 2 we get 01101011 00001101 (value split, and 11 in the first byte skipped)*/ +static int wv_bits(bitstream_t* ob, uint32_t bits, uint32_t value) { + uint32_t shift, mask, pos; + + if (bits > 32 || ob->b_off + bits > ob->bufsize*8) + goto fail; + + pos = ob->b_off / 8; /* byte offset */ + shift = ob->b_off % 8; /* bit sub-offset */ + mask = (1 << shift) - 1; /* to keep lower bits in lowest byte */ + + ob->buf[pos+0] = (value << shift) | (ob->buf[pos+0] & mask); + if (bits + shift > 8) { + ob->buf[pos+1] = value >> (8 - shift); + if (bits + shift > 16) { + ob->buf[pos+2] = value >> (16 - shift); + if (bits + shift > 24) { + ob->buf[pos+3] = value >> (24 - shift); + if (bits + shift > 32) { + /* upper bits are set to 0 (shifting unsigned) but shouldn't matter */ + ob->buf[pos+4] = value >> (32 - shift); + } + } + } + } + + ob->b_off += bits; + return 1; +fail: + VGM_LOG_ONCE("BITREADER: write fail\n"); + return 0; +} +#endif + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c index d04d199fd..4b887a461 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c @@ -1,230 +1,228 @@ -#include -#include "coding.h" -#include "vorbis_custom_decoder.h" - -#ifdef VGM_USE_VORBIS -#include - -#define VORBIS_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */ - -static void pcm_convert_float_to_16(int channels, sample_t * outbuf, int samples_to_do, float ** pcm); - -/** - * Inits a vorbis stream of some custom variety. - * - * Normally Vorbis packets are stored in .ogg, which is divided into OggS pages/packets, and the first packets contain necessary - * Vorbis setup. For custom vorbis the OggS layer is replaced/optimized, the setup can be modified or stored elsewhere - * (i.e.- in the .exe) and raw Vorbis packets may be modified as well, presumably to shave off some kb and/or obfuscate. - * We'll manually read/modify the data and decode it with libvorbis calls. - * - * Reference: https://www.xiph.org/vorbis/doc/libvorbis/overview.html - */ -vorbis_custom_codec_data * init_vorbis_custom(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config) { - vorbis_custom_codec_data * data = NULL; - int ok; - - /* init stuff */ - data = calloc(1,sizeof(vorbis_custom_codec_data)); - if (!data) goto fail; - - data->buffer_size = VORBIS_DEFAULT_BUFFER_SIZE; - data->buffer = calloc(sizeof(uint8_t), data->buffer_size); - if (!data->buffer) goto fail; - - /* keep around to decode too */ - data->type = type; - memcpy(&data->config, config, sizeof(vorbis_custom_config)); - - - /* init vorbis stream state, using 3 fake Ogg setup packets (info, comments, setup/codebooks) - * libvorbis expects parsed Ogg pages, but we'll fake them with our raw data instead */ - vorbis_info_init(&data->vi); - vorbis_comment_init(&data->vc); - - data->op.packet = data->buffer; - data->op.b_o_s = 1; /* fake headers start */ - - /* init header */ - switch(data->type) { - case VORBIS_FSB: ok = vorbis_custom_setup_init_fsb(streamFile, start_offset, data); break; - case VORBIS_WWISE: ok = vorbis_custom_setup_init_wwise(streamFile, start_offset, data); break; - case VORBIS_OGL: ok = vorbis_custom_setup_init_ogl(streamFile, start_offset, data); break; - case VORBIS_SK: ok = vorbis_custom_setup_init_sk(streamFile, start_offset, data); break; - case VORBIS_VID1: ok = vorbis_custom_setup_init_vid1(streamFile, start_offset, data); break; - default: goto fail; - } - if(!ok) goto fail; - - data->op.b_o_s = 0; /* end of fake headers */ - - /* init vorbis global and block state */ - if (vorbis_synthesis_init(&data->vd,&data->vi) != 0) goto fail; - if (vorbis_block_init(&data->vd,&data->vb) != 0) goto fail; - - - /* write output */ - config->data_start_offset = data->config.data_start_offset; - - - return data; - -fail: - VGM_LOG("VORBIS: init fail at around 0x%x\n", (uint32_t)start_offset); - free_vorbis_custom(data); - return NULL; -} - -/* Decodes Vorbis packets into a libvorbis sample buffer, and copies them to outbuf */ -void decode_vorbis_custom(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels) { - VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; - vorbis_custom_codec_data * data = vgmstream->codec_data; - size_t stream_size = get_streamfile_size(stream->streamfile); - //data->op.packet = data->buffer;/* implicit from init */ - int samples_done = 0; - - while (samples_done < samples_to_do) { - - /* extra EOF check for edge cases */ - if (stream->offset >= stream_size) { - memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); - break; - } - - - if (data->samples_full) { /* read more samples */ - int samples_to_get; - float **pcm; - - /* get PCM samples from libvorbis buffers */ - samples_to_get = vorbis_synthesis_pcmout(&data->vd, &pcm); - if (!samples_to_get) { - data->samples_full = 0; /* request more if empty*/ - continue; - } - - 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 convert from Vorbis float pcm to 16bit pcm */ - if (samples_to_get > samples_to_do - samples_done) - samples_to_get = samples_to_do - samples_done; - pcm_convert_float_to_16(data->vi.channels, outbuf + samples_done * channels, samples_to_get, pcm); - samples_done += samples_to_get; - } - - /* mark consumed samples from the buffer - * (non-consumed samples are returned in next vorbis_synthesis_pcmout calls) */ - vorbis_synthesis_read(&data->vd, samples_to_get); - } - else { /* read more data */ - int ok, rc; - - /* not actually needed, but feels nicer */ - data->op.granulepos += samples_to_do; /* can be changed next if desired */ - data->op.packetno++; - - /* read/transform data into the ogg_packet buffer and advance offsets */ - switch(data->type) { - case VORBIS_FSB: ok = vorbis_custom_parse_packet_fsb(stream, data); break; - case VORBIS_WWISE: ok = vorbis_custom_parse_packet_wwise(stream, data); break; - case VORBIS_OGL: ok = vorbis_custom_parse_packet_ogl(stream, data); break; - case VORBIS_SK: ok = vorbis_custom_parse_packet_sk(stream, data); break; - case VORBIS_VID1: ok = vorbis_custom_parse_packet_vid1(stream, data); break; - default: goto decode_fail; - } - if(!ok) { - goto decode_fail; - } - - - /* parse the fake ogg packet into a logical vorbis block */ - rc = vorbis_synthesis(&data->vb,&data->op); - if (rc == OV_ENOTAUDIO) { - VGM_LOG("Vorbis: not an audio packet (size=0x%x) @ %x\n",(size_t)data->op.bytes,(uint32_t)stream->offset); - //VGM_LOGB(data->op.packet, (size_t)data->op.bytes,0); - continue; /* rarely happens, seems ok? */ - } else if (rc != 0) goto decode_fail; - - /* finally decode the logical block into samples */ - rc = vorbis_synthesis_blockin(&data->vd,&data->vb); - if (rc != 0) goto decode_fail; /* ? */ - - - data->samples_full = 1; - } - } - - return; - -decode_fail: - /* on error just put some 0 samples */ - VGM_LOG("VORBIS: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done)); - memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample)); -} - -/* converts from internal Vorbis format to standard PCM (mostly from Xiph's decoder_example.c) */ -static void pcm_convert_float_to_16(int channels, sample_t * outbuf, int samples_to_do, float ** pcm) { - int ch, s; - sample_t *ptr; - float *channel; - - /* convert float PCM (multichannel float array, with pcm[0]=ch0, pcm[1]=ch1, pcm[2]=ch0, etc) - * to 16 bit signed PCM ints (host order) and interleave + fix clipping */ - for (ch = 0; ch < channels; ch++) { - /* channels should be in standard order unlike Ogg Vorbis (at least in FSB) */ - ptr = outbuf + ch; - channel = pcm[ch]; - for (s = 0; s < samples_to_do; s++) { - int val = (int)floor(channel[s] * 32767.0f + 0.5f); - if (val > 32767) val = 32767; - else if (val < -32768) val = -32768; - - *ptr = val; - ptr += channels; - } - } -} - -/* ********************************************** */ - -void free_vorbis_custom(vorbis_custom_codec_data * data) { - if (!data) - return; - - /* internal decoder cleanup */ - vorbis_block_clear(&data->vb); - vorbis_dsp_clear(&data->vd); - vorbis_comment_clear(&data->vc); - vorbis_info_clear(&data->vi); - - free(data->buffer); - free(data); -} - -void reset_vorbis_custom(VGMSTREAM *vgmstream) { - vorbis_custom_codec_data *data = vgmstream->codec_data; - if (!data) return; - - /* Seeking is provided by the Ogg layer, so with custom vorbis we'd need seek tables instead. - * To avoid having to parse different formats we'll just discard until the expected sample */ - vorbis_synthesis_restart(&data->vd); - data->samples_to_discard = 0; -} - -void seek_vorbis_custom(VGMSTREAM *vgmstream, int32_t num_sample) { - vorbis_custom_codec_data *data = vgmstream->codec_data; - if (!data) return; - - /* Seeking is provided by the Ogg layer, so with custom vorbis we'd need seek tables instead. - * To avoid having to parse different formats we'll just discard until the expected sample */ - vorbis_synthesis_restart(&data->vd); - data->samples_to_discard = num_sample; - if (vgmstream->loop_ch) - vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; -} - -#endif +#include +#include "coding.h" +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + +#define VORBIS_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */ + +static void pcm_convert_float_to_16(sample_t* outbuf, int samples_to_do, float** pcm, int channels); + +/** + * Inits a vorbis stream of some custom variety. + * + * Normally Vorbis packets are stored in .ogg, which is divided into OggS pages/packets, and the first packets contain necessary + * Vorbis setup. For custom vorbis the OggS layer is replaced/optimized, the setup can be modified or stored elsewhere + * (i.e.- in the .exe) and raw Vorbis packets may be modified as well, presumably to shave off some kb and/or obfuscate. + * We'll manually read/modify the data and decode it with libvorbis calls. + * + * Reference: https://www.xiph.org/vorbis/doc/libvorbis/overview.html + */ +vorbis_custom_codec_data* init_vorbis_custom(STREAMFILE* sf, off_t start_offset, vorbis_custom_t type, vorbis_custom_config* config) { + vorbis_custom_codec_data* data = NULL; + int ok; + + /* init stuff */ + data = calloc(1,sizeof(vorbis_custom_codec_data)); + if (!data) goto fail; + + data->buffer_size = VORBIS_DEFAULT_BUFFER_SIZE; + data->buffer = calloc(sizeof(uint8_t), data->buffer_size); + if (!data->buffer) goto fail; + + /* keep around to decode too */ + data->type = type; + memcpy(&data->config, config, sizeof(vorbis_custom_config)); + + + /* init vorbis stream state, using 3 fake Ogg setup packets (info, comments, setup/codebooks) + * libvorbis expects parsed Ogg pages, but we'll fake them with our raw data instead */ + vorbis_info_init(&data->vi); + vorbis_comment_init(&data->vc); + + data->op.packet = data->buffer; + data->op.b_o_s = 1; /* fake headers start */ + + /* init header */ + switch(data->type) { + case VORBIS_FSB: ok = vorbis_custom_setup_init_fsb(sf, start_offset, data); break; + case VORBIS_WWISE: ok = vorbis_custom_setup_init_wwise(sf, start_offset, data); break; + case VORBIS_OGL: ok = vorbis_custom_setup_init_ogl(sf, start_offset, data); break; + case VORBIS_SK: ok = vorbis_custom_setup_init_sk(sf, start_offset, data); break; + case VORBIS_VID1: ok = vorbis_custom_setup_init_vid1(sf, start_offset, data); break; + default: goto fail; + } + if(!ok) goto fail; + + data->op.b_o_s = 0; /* end of fake headers */ + + /* init vorbis global and block state */ + if (vorbis_synthesis_init(&data->vd,&data->vi) != 0) goto fail; + if (vorbis_block_init(&data->vd,&data->vb) != 0) goto fail; + + + /* write output */ + config->data_start_offset = data->config.data_start_offset; + + + return data; + +fail: + VGM_LOG("VORBIS: init fail at around 0x%x\n", (uint32_t)start_offset); + free_vorbis_custom(data); + return NULL; +} + +/* Decodes Vorbis packets into a libvorbis sample buffer, and copies them to outbuf */ +void decode_vorbis_custom(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; + vorbis_custom_codec_data* data = vgmstream->codec_data; + size_t stream_size = get_streamfile_size(stream->streamfile); + //data->op.packet = data->buffer;/* implicit from init */ + int samples_done = 0; + + while (samples_done < samples_to_do) { + + /* extra EOF check for edge cases */ + if (stream->offset >= stream_size) { + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); + break; + } + + + if (data->samples_full) { /* read more samples */ + int samples_to_get; + float **pcm; + + /* get PCM samples from libvorbis buffers */ + samples_to_get = vorbis_synthesis_pcmout(&data->vd, &pcm); + if (!samples_to_get) { + data->samples_full = 0; /* request more if empty*/ + continue; + } + + 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 convert from Vorbis float pcm to 16bit pcm */ + if (samples_to_get > samples_to_do - samples_done) + samples_to_get = samples_to_do - samples_done; + pcm_convert_float_to_16(outbuf + samples_done * channels, samples_to_get, pcm, data->vi.channels); + samples_done += samples_to_get; + } + + /* mark consumed samples from the buffer + * (non-consumed samples are returned in next vorbis_synthesis_pcmout calls) */ + vorbis_synthesis_read(&data->vd, samples_to_get); + } + else { /* read more data */ + int ok, rc; + + /* not actually needed, but feels nicer */ + data->op.granulepos += samples_to_do; /* can be changed next if desired */ + data->op.packetno++; + + /* read/transform data into the ogg_packet buffer and advance offsets */ + switch(data->type) { + case VORBIS_FSB: ok = vorbis_custom_parse_packet_fsb(stream, data); break; + case VORBIS_WWISE: ok = vorbis_custom_parse_packet_wwise(stream, data); break; + case VORBIS_OGL: ok = vorbis_custom_parse_packet_ogl(stream, data); break; + case VORBIS_SK: ok = vorbis_custom_parse_packet_sk(stream, data); break; + case VORBIS_VID1: ok = vorbis_custom_parse_packet_vid1(stream, data); break; + default: goto decode_fail; + } + if(!ok) { + goto decode_fail; + } + + + /* parse the fake ogg packet into a logical vorbis block */ + rc = vorbis_synthesis(&data->vb,&data->op); + if (rc == OV_ENOTAUDIO) { + VGM_LOG("Vorbis: not an audio packet (size=0x%x) @ %x\n",(size_t)data->op.bytes,(uint32_t)stream->offset); + //VGM_LOGB(data->op.packet, (size_t)data->op.bytes,0); + continue; /* rarely happens, seems ok? */ + } else if (rc != 0) goto decode_fail; + + /* finally decode the logical block into samples */ + rc = vorbis_synthesis_blockin(&data->vd,&data->vb); + if (rc != 0) goto decode_fail; /* ? */ + + + data->samples_full = 1; + } + } + + return; + +decode_fail: + /* on error just put some 0 samples */ + VGM_LOG("VORBIS: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done)); + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample)); +} + +/* converts from internal Vorbis format to standard PCM (mostly from Xiph's decoder_example.c) */ +static void pcm_convert_float_to_16(sample_t* outbuf, int samples_to_do, float** pcm, int channels) { + int ch, s; + sample_t* ptr; + float* channel; + + /* convert float PCM (multichannel float array, with pcm[0]=ch0, pcm[1]=ch1, pcm[2]=ch0, etc) + * to 16 bit signed PCM ints (host order) and interleave + fix clipping */ + for (ch = 0; ch < channels; ch++) { + /* channels should be in standard order unlike Ogg Vorbis (at least in FSB) */ + ptr = outbuf + ch; + channel = pcm[ch]; + for (s = 0; s < samples_to_do; s++) { + int val = (int)floor(channel[s] * 32767.0f + 0.5f); + if (val > 32767) val = 32767; + else if (val < -32768) val = -32768; + + *ptr = val; + ptr += channels; + } + } +} + +/* ********************************************** */ + +void free_vorbis_custom(vorbis_custom_codec_data* data) { + if (!data) + return; + + /* internal decoder cleanup */ + vorbis_block_clear(&data->vb); + vorbis_dsp_clear(&data->vd); + vorbis_comment_clear(&data->vc); + vorbis_info_clear(&data->vi); + + free(data->buffer); + free(data); +} + +void reset_vorbis_custom(VGMSTREAM* vgmstream) { + vorbis_custom_codec_data *data = vgmstream->codec_data; + if (!data) return; + + vorbis_synthesis_restart(&data->vd); + data->samples_to_discard = 0; +} + +void seek_vorbis_custom(VGMSTREAM* vgmstream, int32_t num_sample) { + vorbis_custom_codec_data *data = vgmstream->codec_data; + if (!data) return; + + /* Seeking is provided by the Ogg layer, so with custom vorbis we'd need seek tables instead. + * To avoid having to parse different formats we'll just discard until the expected sample */ + vorbis_synthesis_restart(&data->vd); + data->samples_to_discard = num_sample; + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h index 0801d91c1..8080800d2 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h @@ -1,22 +1,54 @@ -#ifndef _VORBIS_CUSTOM_DECODER_H_ -#define _VORBIS_CUSTOM_DECODER_H_ - -#include "../vgmstream.h" -#include "../coding/coding.h" - -/* used by vorbis_custom_decoder.c, but scattered in other .c files */ -#ifdef VGM_USE_VORBIS -int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); -int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); -int vorbis_custom_setup_init_ogl(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); -int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); -int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); - -int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); -int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); -int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); -int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); -int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); -#endif/* VGM_USE_VORBIS */ - -#endif/*_VORBIS_CUSTOM_DECODER_H_ */ +#ifndef _VORBIS_CUSTOM_DECODER_H_ +#define _VORBIS_CUSTOM_DECODER_H_ + +#include "../vgmstream.h" +#include "../coding/coding.h" + +/* used by vorbis_custom_decoder.c, but scattered in other .c files */ +#ifdef VGM_USE_VORBIS + +/* custom Vorbis without Ogg layer */ +struct vorbis_custom_codec_data { + vorbis_info vi; /* stream settings */ + vorbis_comment vc; /* stream comments */ + vorbis_dsp_state vd; /* decoder global state */ + vorbis_block vb; /* decoder local state */ + ogg_packet op; /* fake packet for internal use */ + + uint8_t* buffer; /* internal raw data buffer */ + size_t buffer_size; + + size_t samples_to_discard; /* for looping purposes */ + int samples_full; /* flag, samples available in vorbis buffers */ + + vorbis_custom_t type; /* Vorbis subtype */ + vorbis_custom_config config; /* config depending on the mode */ + + /* Wwise Vorbis: saved data to reconstruct modified packets */ + uint8_t mode_blockflag[64+1]; /* max 6b+1; flags 'n stuff */ + int mode_bits; /* bits to store mode_number */ + uint8_t prev_blockflag; /* blockflag in the last decoded packet */ + /* Ogg-style Vorbis: packet within a page */ + int current_packet; + /* reference for page/blocks */ + off_t block_offset; + size_t block_size; + + int prev_block_samples; /* count for optimization */ +}; + + +int vorbis_custom_setup_init_fsb(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); +int vorbis_custom_setup_init_wwise(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); +int vorbis_custom_setup_init_ogl(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); +int vorbis_custom_setup_init_sk(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); +int vorbis_custom_setup_init_vid1(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); + +int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); +#endif/* VGM_USE_VORBIS */ + +#endif/*_VORBIS_CUSTOM_DECODER_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c index 309283260..cb43c11f1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c @@ -1,269 +1,269 @@ -#include "vorbis_custom_decoder.h" - -#ifdef VGM_USE_VORBIS -#include - -#define FSB_VORBIS_USE_PRECOMPILED_FVS 1 /* if enabled vgmstream weights ~600kb more but doesn't need external .fvs packets */ -#if FSB_VORBIS_USE_PRECOMPILED_FVS -#include "vorbis_custom_data_fsb.h" -#endif - - -/* **************************************************************************** */ -/* DEFS */ -/* **************************************************************************** */ - -static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long); -static int build_header_comment(uint8_t * buf, size_t bufsize); -static int build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); - -static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); -static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); -static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); - - -/* **************************************************************************** */ -/* EXTERNAL API */ -/* **************************************************************************** */ - -/** - * FSB references an external setup packet by the setup_id, and packets have mini headers with the size. - * - * Format info from python-fsb5 (https://github.com/HearthSim/python-fsb5) and - * fsb-vorbis-extractor (https://github.com/tmiasko/fsb-vorbis-extractor). - */ -int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { - vorbis_custom_config cfg = data->config; - - data->op.bytes = build_header_identification(data->buffer, data->buffer_size, cfg.channels, cfg.sample_rate, 256, 2048); /* FSB default block sizes */ - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - - data->op.bytes = build_header_comment(data->buffer, data->buffer_size); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - - data->op.bytes = build_header_setup(data->buffer, data->buffer_size, cfg.setup_id, streamFile); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - - return 1; - -fail: - return 0; -} - - -int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { - size_t bytes; - - /* get next packet size from the FSB 16b header (doesn't count this 16b) */ - data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile); - stream->offset += 2; - if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ - - /* read raw block */ - bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); - stream->offset += data->op.bytes; - if (bytes != data->op.bytes) goto fail; /* wrong packet? */ - - return 1; - -fail: - return 0; -} - -/* **************************************************************************** */ -/* INTERNAL HELPERS */ -/* **************************************************************************** */ - -static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long) { - int bytes = 0x1e; - uint8_t blocksizes, exp_blocksize_0, exp_blocksize_1; - - if (bytes > bufsize) return 0; - - /* guetto log2 for allowed blocksizes (2-exp), could be improved */ - switch(blocksize_long) { - case 64: exp_blocksize_0 = 6; break; - case 128: exp_blocksize_0 = 7; break; - case 256: exp_blocksize_0 = 8; break; - case 512: exp_blocksize_0 = 9; break; - case 1024: exp_blocksize_0 = 10; break; - case 2048: exp_blocksize_0 = 11; break; - case 4096: exp_blocksize_0 = 12; break; - case 8192: exp_blocksize_0 = 13; break; - default: return 0; - } - switch(blocksize_short) { - case 64: exp_blocksize_1 = 6; break; - case 128: exp_blocksize_1 = 7; break; - case 256: exp_blocksize_1 = 8; break; - case 512: exp_blocksize_1 = 9; break; - case 1024: exp_blocksize_1 = 10; break; - case 2048: exp_blocksize_1 = 11; break; - case 4096: exp_blocksize_1 = 12; break; - case 8192: exp_blocksize_1 = 13; break; - default: return 0; - } - blocksizes = (exp_blocksize_0 << 4) | (exp_blocksize_1); - - put_8bit (buf+0x00, 0x01); /* packet_type (id) */ - memcpy (buf+0x01, "vorbis", 6); /* id */ - put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */ - put_8bit (buf+0x0b, channels); /* audio_channels */ - put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */ - put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */ - put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */ - put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */ - put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */ - put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */ - - return bytes; -} - -static int build_header_comment(uint8_t * buf, size_t bufsize) { - int bytes = 0x19; - - if (bytes > bufsize) return 0; - - put_8bit (buf+0x00, 0x03); /* packet_type (comments) */ - memcpy (buf+0x01, "vorbis", 6); /* id */ - put_32bitLE(buf+0x07, 0x09); /* vendor_length */ - memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */ - put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */ - put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */ - - return bytes; -} - -static int build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { - int bytes; - - /* try to locate from the precompiled list */ - bytes = load_fvs_array(buf, bufsize, setup_id, streamFile); - if (bytes) - return bytes; - - /* try to load from external files */ - bytes = load_fvs_file_single(buf, bufsize, setup_id, streamFile); - if (bytes) - return bytes; - - bytes = load_fvs_file_multi(buf, bufsize, setup_id, streamFile); - if (bytes) - return bytes; - - /* not found */ - VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id); - return 0; -} - -static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { - STREAMFILE * streamFileSetup = NULL; - - { - char setupname[PATH_LIMIT]; - char pathname[PATH_LIMIT]; - char *path; - - /* read "(dir/).fvs_{setup_id}" */ - streamFile->get_name(streamFile,pathname,sizeof(pathname)); - path = strrchr(pathname,DIR_SEPARATOR); - if (path) - *(path+1) = '\0'; - else - pathname[0] = '\0'; - - snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id); - streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); - } - - if (streamFileSetup) { - /* file found, get contents into the buffer */ - size_t bytes = streamFileSetup->get_size(streamFileSetup); - if (bytes > bufsize) goto fail; - - if (read_streamfile(buf, 0, bytes, streamFileSetup) != bytes) - goto fail; - - streamFileSetup->close(streamFileSetup); - return bytes; - } - -fail: - if (streamFileSetup) streamFileSetup->close(streamFileSetup); - return 0; -} - -static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { - STREAMFILE * streamFileSetup = NULL; - - { - char setupname[PATH_LIMIT]; - char pathname[PATH_LIMIT]; - char *path; - - /* read "(dir/).fvs" */ - streamFile->get_name(streamFile,pathname,sizeof(pathname)); - path = strrchr(pathname,DIR_SEPARATOR); - if (path) - *(path+1) = '\0'; - else - pathname[0] = '\0'; - - snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname); - streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); - } - - if (streamFileSetup) { - /* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */ - int entries, i; - uint32_t offset = 0, size = 0; - - if (read_32bitBE(0x0, streamFileSetup) != 0x56465653) goto fail; /* "VFVS" */ - entries = read_32bitLE(0x08, streamFileSetup); /* 0x04=v0, 0x0c-0x20: reserved */ - if (entries <= 0) goto fail; - - for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */ - if ((uint32_t)read_32bitLE(0x20 + i*0x10, streamFileSetup) == setup_id) { - offset = read_32bitLE(0x24 + i*0x10, streamFileSetup); - size = read_32bitLE(0x28 + i*0x10, streamFileSetup); - break; - } - } - if (!size || !offset || size > bufsize) goto fail; - - /* read into buf */ - if (read_streamfile(buf, offset, size, streamFileSetup) != size) - goto fail; - - streamFileSetup->close(streamFileSetup); - return size; - } - -fail: - if (streamFileSetup) streamFileSetup->close(streamFileSetup); - return 0; -} - -static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { -#if FSB_VORBIS_USE_PRECOMPILED_FVS - int i, list_length; - - list_length = sizeof(fvs_list) / sizeof(fvs_info); - for (i=0; i < list_length; i++) { - if (fvs_list[i].id == setup_id) { - if (fvs_list[i].size > bufsize) goto fail; - /* found: copy data as-is */ - memcpy(buf,fvs_list[i].setup, fvs_list[i].size); - return fvs_list[i].size; - } - } - -fail: -#endif - return 0; -} - -#endif +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + +#define FSB_VORBIS_USE_PRECOMPILED_FVS 1 /* if enabled vgmstream weights ~600kb more but doesn't need external .fvs packets */ +#if FSB_VORBIS_USE_PRECOMPILED_FVS +#include "vorbis_custom_data_fsb.h" +#endif + + +/* **************************************************************************** */ +/* DEFS */ +/* **************************************************************************** */ + +static int build_header_identification(uint8_t* buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long); +static int build_header_comment(uint8_t* buf, size_t bufsize); +static int build_header_setup(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf); + +static int load_fvs_file_single(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf); +static int load_fvs_file_multi(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf); +static int load_fvs_array(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf); + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * FSB references an external setup packet by the setup_id, and packets have mini headers with the size. + * + * Format info from python-fsb5 (https://github.com/HearthSim/python-fsb5) and + * fsb-vorbis-extractor (https://github.com/tmiasko/fsb-vorbis-extractor). + */ +int vorbis_custom_setup_init_fsb(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) { + vorbis_custom_config cfg = data->config; + + data->op.bytes = build_header_identification(data->buffer, data->buffer_size, cfg.channels, cfg.sample_rate, 256, 2048); /* FSB default block sizes */ + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + + data->op.bytes = build_header_comment(data->buffer, data->buffer_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ + + data->op.bytes = build_header_setup(data->buffer, data->buffer_size, cfg.setup_id, sf); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + + return 1; + +fail: + return 0; +} + + +int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) { + size_t bytes; + + /* get next packet size from the FSB 16b header (doesn't count this 16b) */ + data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile); + stream->offset += 2; + if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ + + /* read raw block */ + bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); + stream->offset += data->op.bytes; + if (bytes != data->op.bytes) goto fail; /* wrong packet? */ + + return 1; + +fail: + return 0; +} + +/* **************************************************************************** */ +/* INTERNAL HELPERS */ +/* **************************************************************************** */ + +static int build_header_identification(uint8_t* buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long) { + int bytes = 0x1e; + uint8_t blocksizes, exp_blocksize_0, exp_blocksize_1; + + if (bytes > bufsize) return 0; + + /* guetto log2 for allowed blocksizes (2-exp), could be improved */ + switch(blocksize_long) { + case 64: exp_blocksize_0 = 6; break; + case 128: exp_blocksize_0 = 7; break; + case 256: exp_blocksize_0 = 8; break; + case 512: exp_blocksize_0 = 9; break; + case 1024: exp_blocksize_0 = 10; break; + case 2048: exp_blocksize_0 = 11; break; + case 4096: exp_blocksize_0 = 12; break; + case 8192: exp_blocksize_0 = 13; break; + default: return 0; + } + switch(blocksize_short) { + case 64: exp_blocksize_1 = 6; break; + case 128: exp_blocksize_1 = 7; break; + case 256: exp_blocksize_1 = 8; break; + case 512: exp_blocksize_1 = 9; break; + case 1024: exp_blocksize_1 = 10; break; + case 2048: exp_blocksize_1 = 11; break; + case 4096: exp_blocksize_1 = 12; break; + case 8192: exp_blocksize_1 = 13; break; + default: return 0; + } + blocksizes = (exp_blocksize_0 << 4) | (exp_blocksize_1); + + put_8bit (buf+0x00, 0x01); /* packet_type (id) */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */ + put_8bit (buf+0x0b, channels); /* audio_channels */ + put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */ + put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */ + put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */ + put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */ + put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */ + put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */ + + return bytes; +} + +static int build_header_comment(uint8_t* buf, size_t bufsize) { + int bytes = 0x19; + + if (bytes > bufsize) return 0; + + put_8bit (buf+0x00, 0x03); /* packet_type (comments) */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + put_32bitLE(buf+0x07, 0x09); /* vendor_length */ + memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */ + put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */ + put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */ + + return bytes; +} + +static int build_header_setup(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) { + int bytes; + + /* try to locate from the precompiled list */ + bytes = load_fvs_array(buf, bufsize, setup_id, sf); + if (bytes) + return bytes; + + /* try to load from external files */ + bytes = load_fvs_file_single(buf, bufsize, setup_id, sf); + if (bytes) + return bytes; + + bytes = load_fvs_file_multi(buf, bufsize, setup_id, sf); + if (bytes) + return bytes; + + /* not found */ + VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id); + return 0; +} + +static int load_fvs_file_single(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) { + STREAMFILE* sf_setup = NULL; + + { + char setupname[PATH_LIMIT]; + char pathname[PATH_LIMIT]; + char *path; + + /* read "(dir/).fvs_{setup_id}" */ + sf->get_name(sf,pathname,sizeof(pathname)); + path = strrchr(pathname,DIR_SEPARATOR); + if (path) + *(path+1) = '\0'; + else + pathname[0] = '\0'; + + snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id); + sf_setup = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); + } + + if (sf_setup) { + /* file found, get contents into the buffer */ + size_t bytes = sf_setup->get_size(sf_setup); + if (bytes > bufsize) goto fail; + + if (read_streamfile(buf, 0, bytes, sf_setup) != bytes) + goto fail; + + sf_setup->close(sf_setup); + return bytes; + } + +fail: + if (sf_setup) sf_setup->close(sf_setup); + return 0; +} + +static int load_fvs_file_multi(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) { + STREAMFILE* sf_setup = NULL; + + { + char setupname[PATH_LIMIT]; + char pathname[PATH_LIMIT]; + char* path; + + /* read "(dir/).fvs" */ + sf->get_name(sf,pathname,sizeof(pathname)); + path = strrchr(pathname,DIR_SEPARATOR); + if (path) + *(path+1) = '\0'; + else + pathname[0] = '\0'; + + snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname); + sf_setup = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); + } + + if (sf_setup) { + /* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */ + int entries, i; + uint32_t offset = 0, size = 0; + + if (read_32bitBE(0x0, sf_setup) != 0x56465653) goto fail; /* "VFVS" */ + entries = read_32bitLE(0x08, sf_setup); /* 0x04=v0, 0x0c-0x20: reserved */ + if (entries <= 0) goto fail; + + for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */ + if ((uint32_t)read_32bitLE(0x20 + i*0x10, sf_setup) == setup_id) { + offset = read_32bitLE(0x24 + i*0x10, sf_setup); + size = read_32bitLE(0x28 + i*0x10, sf_setup); + break; + } + } + if (!size || !offset || size > bufsize) goto fail; + + /* read into buf */ + if (read_streamfile(buf, offset, size, sf_setup) != size) + goto fail; + + sf_setup->close(sf_setup); + return size; + } + +fail: + if (sf_setup) sf_setup->close(sf_setup); + return 0; +} + +static int load_fvs_array(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) { +#if FSB_VORBIS_USE_PRECOMPILED_FVS + int i, list_length; + + list_length = sizeof(fvs_list) / sizeof(fvs_info); + for (i=0; i < list_length; i++) { + if (fvs_list[i].id == setup_id) { + if (fvs_list[i].size > bufsize) goto fail; + /* found: copy data as-is */ + memcpy(buf,fvs_list[i].setup, fvs_list[i].size); + return fvs_list[i].size; + } + } + +fail: +#endif + return 0; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c index 8e746362a..bafa949c3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c @@ -1,71 +1,71 @@ -#include "vorbis_custom_decoder.h" - -#ifdef VGM_USE_VORBIS -#include - - -/* **************************************************************************** */ -/* EXTERNAL API */ -/* **************************************************************************** */ - -/** - * OGL removes the Ogg layer and uses 16b packet headers, that have the size of the next packet, but - * the lower 2b need to be removed (usually 00 but 01 for the id packet, not sure about the meaning). - */ -int vorbis_custom_setup_init_ogl(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { - off_t offset = start_offset; - size_t packet_size; - - /* read 3 packets with triad (id/comment/setup), each with an OGL header */ - - /* normal identificacion packet */ - packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; - if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - offset += 2+packet_size; - - /* normal comment packet */ - packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; - if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - offset += 2+packet_size; - - /* normal setup packet */ - packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; - if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - offset += 2+packet_size; - - /* data starts after triad */ - data->config.data_start_offset = offset; - - return 1; - -fail: - return 0; -} - - -int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { - size_t bytes; - - /* get next packet size from the OGL 16b header (upper 14b) */ - data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile) >> 2; - stream->offset += 2; - if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ - - /* read raw block */ - bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); - stream->offset += data->op.bytes; - if (bytes != data->op.bytes) goto fail; /* wrong packet? */ - - return 1; - -fail: - return 0; -} - -#endif +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * OGL removes the Ogg layer and uses 16b packet headers, that have the size of the next packet, but + * the lower 2b need to be removed (usually 00 but 01 for the id packet, not sure about the meaning). + */ +int vorbis_custom_setup_init_ogl(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) { + off_t offset = start_offset; + size_t packet_size; + + /* read 3 packets with triad (id/comment/setup), each with an OGL header */ + + /* normal identificacion packet */ + packet_size = (uint16_t)read_16bitLE(offset, sf) >> 2; + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, sf); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + offset += 2+packet_size; + + /* normal comment packet */ + packet_size = (uint16_t)read_16bitLE(offset, sf) >> 2; + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, sf); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ + offset += 2+packet_size; + + /* normal setup packet */ + packet_size = (uint16_t)read_16bitLE(offset, sf) >> 2; + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, sf); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + offset += 2+packet_size; + + /* data starts after triad */ + data->config.data_start_offset = offset; + + return 1; + +fail: + return 0; +} + + +int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) { + size_t bytes; + + /* get next packet size from the OGL 16b header (upper 14b) */ + data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile) >> 2; + stream->offset += 2; + if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ + + /* read raw block */ + bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); + stream->offset += data->op.bytes; + if (bytes != data->op.bytes) goto fail; /* wrong packet? */ + + return 1; + +fail: + return 0; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c index 408a7e200..9688eea8a 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c @@ -1,187 +1,187 @@ -#include "vorbis_custom_decoder.h" - -#ifdef VGM_USE_VORBIS -#include - -/* **************************************************************************** */ -/* DEFS */ -/* **************************************************************************** */ - -static int get_page_info(STREAMFILE *streamFile, off_t page_offset, off_t *out_packet_offset, size_t *out_packet_size, int *out_page_packets, int target_packet); -static int build_header(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile, off_t packet_offset, size_t packet_size); - - -/* **************************************************************************** */ -/* EXTERNAL API */ -/* **************************************************************************** */ - -/** - * SK just replaces the id 0x4F676753 ("OggS") by 0x11534B10 (\11"SK"\10), and the word "vorbis" by "SK" - * in init packets (for obfuscation, surely). So essentially we are parsing regular Ogg here. - * - * A simpler way to implement this would be in ogg_vorbis_file with read callbacks (pretend this is proof of concept). - */ -int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { - off_t offset = start_offset; - off_t id_offset = 0, comment_offset = 0, setup_offset = 0; - size_t id_size = 0, comment_size = 0, setup_size = 0; - int page_packets; - - /* rebuild header packets, they are standard except the "vorbis" keyword is replaced by "SK" */ - - /* first page has the id packet */ - if (!get_page_info(streamFile, offset, &id_offset, &id_size, &page_packets, 0)) goto fail; - if (page_packets != 1) goto fail; - offset = id_offset + id_size; - - /* second page has the comment and setup packets */ - if (!get_page_info(streamFile, offset, &comment_offset, &comment_size, &page_packets, 0)) goto fail; - if (page_packets != 2) goto fail; - if (!get_page_info(streamFile, offset, &setup_offset, &setup_size, &page_packets, 1)) goto fail; - if (page_packets != 2) goto fail; - offset = comment_offset + comment_size + setup_size; - - - /* init with all offsets found */ - data->op.bytes = build_header(data->buffer, data->buffer_size, streamFile, id_offset, id_size); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - - data->op.bytes = build_header(data->buffer, data->buffer_size, streamFile, comment_offset, comment_size); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - - data->op.bytes = build_header(data->buffer, data->buffer_size, streamFile, setup_offset, setup_size); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - - /* data starts after triad */ - data->config.data_start_offset = offset; - - return 1; - -fail: - return 0; -} - - -int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { - off_t packet_offset = 0; - size_t packet_size = 0; - int page_packets; - int res; - - /* read OggS/SK page and get current packet */ - res = get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, data->current_packet); - data->current_packet++; - if (!res || packet_size > data->buffer_size) goto fail; - - /* read raw block */ - data->op.bytes = read_streamfile(data->buffer, packet_offset, packet_size, stream->streamfile); - if (data->op.bytes != packet_size) goto fail; /* wrong packet? */ - - /* go next page when processed all packets in page */ - if (data->current_packet >= page_packets) { - if (!get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, -1)) goto fail; - stream->offset = packet_offset + packet_size; - data->current_packet = 0; - } - - return 1; - -fail: - return 0; -} - -/* **************************************************************************** */ -/* INTERNAL HELPERS */ -/* **************************************************************************** */ - -/** - * Get packet info from an Ogg page, from segment/packet N (-1 = all segments) - * - * Page format: - * 0x00(4): capture pattern ("OggS") - * 0x01(1): stream structure version - * 0x05(1): header type flag - * 0x06(8): absolute granule position - * 0x0e(4): stream serial number - * 0x12(4): page sequence number - * 0x16(4): page checksum - * 0x1a(1): page segments (total bytes in segment table) - * 0x1b(n): segment table (N bytes, 1 packet is sum of sizes until != 0xFF) - * 0x--(n): data - * Reference: https://xiph.org/ogg/doc/framing.html - */ -static int get_page_info(STREAMFILE *streamFile, off_t page_offset, off_t *out_packet_offset, size_t *out_packet_size, int *out_page_packets, int target_packet) { - off_t table_offset, current_packet_offset, target_packet_offset = 0; - size_t total_packets_size = 0, current_packet_size = 0, target_packet_size = 0; - int page_packets = 0; - uint8_t segments; - int i; - - - if (read_32bitBE(page_offset+0x00, streamFile) != 0x11534B10) /* \11"SK"\10 */ - goto fail; /* not a valid page */ - /* No point on validating other stuff, but they look legal enough (CRC too it seems) */ - - segments = (uint8_t)read_8bit(page_offset+0x1a, streamFile); - - table_offset = page_offset + 0x1b; - current_packet_offset = page_offset + 0x1b + segments; /* first packet starts after segments */ - - /* process segments */ - for (i = 0; i < segments; i++) { - uint8_t segment_size = (uint8_t)read_8bit(table_offset, streamFile); - total_packets_size += segment_size; - current_packet_size += segment_size; - table_offset += 0x01; - - if (segment_size != 0xFF) { /* packet complete */ - page_packets++; - - if (target_packet+1 == page_packets) { - target_packet_offset = current_packet_offset; - target_packet_size = current_packet_size; - } - - /* keep reading to fill page_packets */ - current_packet_offset += current_packet_size; /* move to next packet */ - current_packet_size = 0; - } - } - - /* < 0 is accepted and returns first offset and all packets sizes */ - if (target_packet+1 > page_packets) goto fail; - if (target_packet < 0) { - target_packet_offset = page_offset + 0x1b + segments; /* first */ - target_packet_size = total_packets_size; - } - - if (out_packet_offset) *out_packet_offset = target_packet_offset; - if (out_packet_size) *out_packet_size = target_packet_size; - if (out_page_packets) *out_page_packets = page_packets; - - return 1; - -fail: - //VGM_LOG("SK Vorbis: failed to read page @ 0x%08lx\n", page_offset); - return 0; -} - -/* rebuild a "SK" header packet to a "vorbis" one */ -static int build_header(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile, off_t packet_offset, size_t packet_size) { - int bytes; - - if (0x07+packet_size-0x03 > bufsize) return 0; - - put_8bit (buf+0x00, read_8bit(packet_offset,streamFile)); /* packet_type */ - memcpy (buf+0x01, "vorbis", 6); /* id */ - bytes = read_streamfile(buf+0x07,packet_offset+0x03, packet_size-0x03,streamFile); /* copy rest (all except id+"SK") */ - if (packet_size-0x03 != bytes) - return 0; - - return 0x07+packet_size-0x03; -} - -#endif +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + +/* **************************************************************************** */ +/* DEFS */ +/* **************************************************************************** */ + +static int get_page_info(STREAMFILE* sf, off_t page_offset, off_t* p_packet_offset, size_t* p_packet_size, int* p_page_packets, int target_packet); +static int build_header(uint8_t* buf, size_t bufsize, STREAMFILE* sf, off_t packet_offset, size_t packet_size); + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * SK just replaces the id 0x4F676753 ("OggS") by 0x11534B10 (\11"SK"\10), and the word "vorbis" by "SK" + * in init packets (for obfuscation, surely). So essentially we are parsing regular Ogg here. + * + * A simpler way to implement this would be in ogg_vorbis_file with read callbacks (pretend this is proof of concept). + */ +int vorbis_custom_setup_init_sk(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) { + off_t offset = start_offset; + off_t id_offset = 0, comment_offset = 0, setup_offset = 0; + size_t id_size = 0, comment_size = 0, setup_size = 0; + int page_packets; + + /* rebuild header packets, they are standard except the "vorbis" keyword is replaced by "SK" */ + + /* first page has the id packet */ + if (!get_page_info(sf, offset, &id_offset, &id_size, &page_packets, 0)) goto fail; + if (page_packets != 1) goto fail; + offset = id_offset + id_size; + + /* second page has the comment and setup packets */ + if (!get_page_info(sf, offset, &comment_offset, &comment_size, &page_packets, 0)) goto fail; + if (page_packets != 2) goto fail; + if (!get_page_info(sf, offset, &setup_offset, &setup_size, &page_packets, 1)) goto fail; + if (page_packets != 2) goto fail; + offset = comment_offset + comment_size + setup_size; + + + /* init with all offsets found */ + data->op.bytes = build_header(data->buffer, data->buffer_size, sf, id_offset, id_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + + data->op.bytes = build_header(data->buffer, data->buffer_size, sf, comment_offset, comment_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ + + data->op.bytes = build_header(data->buffer, data->buffer_size, sf, setup_offset, setup_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + + /* data starts after triad */ + data->config.data_start_offset = offset; + + return 1; + +fail: + return 0; +} + + +int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) { + off_t packet_offset = 0; + size_t packet_size = 0; + int page_packets; + int res; + + /* read OggS/SK page and get current packet */ + res = get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, data->current_packet); + data->current_packet++; + if (!res || packet_size > data->buffer_size) goto fail; + + /* read raw block */ + data->op.bytes = read_streamfile(data->buffer, packet_offset, packet_size, stream->streamfile); + if (data->op.bytes != packet_size) goto fail; /* wrong packet? */ + + /* go next page when processed all packets in page */ + if (data->current_packet >= page_packets) { + if (!get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, -1)) goto fail; + stream->offset = packet_offset + packet_size; + data->current_packet = 0; + } + + return 1; + +fail: + return 0; +} + +/* **************************************************************************** */ +/* INTERNAL HELPERS */ +/* **************************************************************************** */ + +/** + * Get packet info from an Ogg page, from segment/packet N (-1 = all segments) + * + * Page format: + * 0x00(4): capture pattern ("OggS") + * 0x01(1): stream structure version + * 0x05(1): header type flag + * 0x06(8): absolute granule position + * 0x0e(4): stream serial number + * 0x12(4): page sequence number + * 0x16(4): page checksum + * 0x1a(1): page segments (total bytes in segment table) + * 0x1b(n): segment table (N bytes, 1 packet is sum of sizes until != 0xFF) + * 0x--(n): data + * Reference: https://xiph.org/ogg/doc/framing.html + */ +static int get_page_info(STREAMFILE* sf, off_t page_offset, off_t* p_packet_offset, size_t* p_packet_size, int* p_page_packets, int target_packet) { + off_t table_offset, current_packet_offset, target_packet_offset = 0; + size_t total_packets_size = 0, current_packet_size = 0, target_packet_size = 0; + int page_packets = 0; + uint8_t segments; + int i; + + + if (read_32bitBE(page_offset+0x00, sf) != 0x11534B10) /* \11"SK"\10 */ + goto fail; /* not a valid page */ + /* No point on validating other stuff, but they look legal enough (CRC too it seems) */ + + segments = (uint8_t)read_8bit(page_offset+0x1a, sf); + + table_offset = page_offset + 0x1b; + current_packet_offset = page_offset + 0x1b + segments; /* first packet starts after segments */ + + /* process segments */ + for (i = 0; i < segments; i++) { + uint8_t segment_size = (uint8_t)read_8bit(table_offset, sf); + total_packets_size += segment_size; + current_packet_size += segment_size; + table_offset += 0x01; + + if (segment_size != 0xFF) { /* packet complete */ + page_packets++; + + if (target_packet+1 == page_packets) { + target_packet_offset = current_packet_offset; + target_packet_size = current_packet_size; + } + + /* keep reading to fill page_packets */ + current_packet_offset += current_packet_size; /* move to next packet */ + current_packet_size = 0; + } + } + + /* < 0 is accepted and returns first offset and all packets sizes */ + if (target_packet+1 > page_packets) goto fail; + if (target_packet < 0) { + target_packet_offset = page_offset + 0x1b + segments; /* first */ + target_packet_size = total_packets_size; + } + + if (p_packet_offset) *p_packet_offset = target_packet_offset; + if (p_packet_size) *p_packet_size = target_packet_size; + if (p_page_packets) *p_page_packets = page_packets; + + return 1; + +fail: + //VGM_LOG("SK Vorbis: failed to read page @ 0x%08lx\n", page_offset); + return 0; +} + +/* rebuild a "SK" header packet to a "vorbis" one */ +static int build_header(uint8_t* buf, size_t bufsize, STREAMFILE* sf, off_t packet_offset, size_t packet_size) { + int bytes; + + if (0x07+packet_size-0x03 > bufsize) return 0; + + put_8bit (buf+0x00, read_8bit(packet_offset,sf)); /* packet_type */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + bytes = read_streamfile(buf+0x07,packet_offset+0x03, packet_size-0x03,sf); /* copy rest (all except id+"SK") */ + if (packet_size-0x03 != bytes) + return 0; + + return 0x07+packet_size-0x03; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c index ec869db33..e93d9e603 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c @@ -1,5 +1,8 @@ #include "vorbis_custom_decoder.h" +#define BITSTREAM_READ_ONLY /* config */ +#include "vorbis_bitreader.h" + #ifdef VGM_USE_VORBIS #include @@ -8,8 +11,8 @@ /* DEFS */ /* **************************************************************************** */ -static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size); -static int build_header_comment(uint8_t * buf, size_t bufsize); +static int get_packet_header(STREAMFILE* sf, off_t* offset, size_t* size); +static int build_header_comment(uint8_t* buf, size_t bufsize); /* **************************************************************************** */ @@ -21,16 +24,16 @@ static int build_header_comment(uint8_t * buf, size_t bufsize); * * Info from hcs's vid1_2ogg: https://github.com/hcs64/vgm_ripping/tree/master/demux/vid1_2ogg */ -int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { +int vorbis_custom_setup_init_vid1(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) { off_t offset = start_offset; size_t packet_size = 0; /* read header packets (id/setup), each with an VID1 header */ /* normal identificacion packet */ - get_packet_header(streamFile, &offset, &packet_size); + get_packet_header(sf, &offset, &packet_size); if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile); + data->op.bytes = read_streamfile(data->buffer,offset,packet_size, sf); if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ offset += packet_size; @@ -40,9 +43,9 @@ int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vo if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ /* normal setup packet */ - get_packet_header(streamFile, &offset, &packet_size); + get_packet_header(sf, &offset, &packet_size); if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile); + data->op.bytes = read_streamfile(data->buffer,offset,packet_size, sf); if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ offset += packet_size; @@ -53,7 +56,7 @@ fail: } -int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { +int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) { size_t bytes; @@ -100,7 +103,7 @@ fail: /* INTERNAL HELPERS */ /* **************************************************************************** */ -static int build_header_comment(uint8_t * buf, size_t bufsize) { +static int build_header_comment(uint8_t* buf, size_t bufsize) { int bytes = 0x19; if (bytes > bufsize) return 0; @@ -116,26 +119,24 @@ static int build_header_comment(uint8_t * buf, size_t bufsize) { } /* read header in Vorbis bitpacking format */ -static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size) { +static int get_packet_header(STREAMFILE* sf, off_t* offset, size_t* size) { uint8_t ibuf[0x04]; /* header buffer */ size_t ibufsize = 0x04; /* header ~max */ - vgm_bitstream ib = {0}; + bitstream_t ib = {0}; uint32_t size_bits; - if (read_streamfile(ibuf,(*offset),ibufsize, streamFile) != ibufsize) + if (read_streamfile(ibuf,(*offset),ibufsize, sf) != ibufsize) goto fail; - ib.buf = ibuf; - ib.bufsize = ibufsize; - ib.b_off = 0; - ib.mode = BITSTREAM_VORBIS; + + init_bitstream(&ib, ibuf, ibufsize); /* read using Vorbis weird LSF */ - r_bits(&ib, 4,&size_bits); - r_bits(&ib, (size_bits+1),(uint32_t*)size); + rv_bits(&ib, 4,&size_bits); + rv_bits(&ib, (size_bits+1),(uint32_t*)size); /* special meaning, seen in silent frames */ - if (size_bits == 0 && *size == 0 && (uint8_t)read_8bit(*offset, streamFile)==0x80) { + if (size_bits == 0 && *size == 0 && (uint8_t)read_8bit(*offset, sf) == 0x80) { *size = 0x01; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c index eff24352b..1eeb38aff 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c @@ -1,1222 +1,1263 @@ -#include "vorbis_custom_decoder.h" - -#ifdef VGM_USE_VORBIS -#include - -#define WWISE_VORBIS_USE_PRECOMPILED_WVC 1 /* if enabled vgmstream weights ~150kb more but doesn't need external .wvc packets */ -#if WWISE_VORBIS_USE_PRECOMPILED_WVC -#include "vorbis_custom_data_wwise.h" -#endif - - -/* **************************************************************************** */ -/* DEFS */ -/* **************************************************************************** */ - -static size_t build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long); -static size_t build_header_comment(uint8_t * buf, size_t bufsize); -static size_t get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian); -static size_t rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian); -static size_t rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels); - -static int ww2ogg_generate_vorbis_packet(vgm_bitstream * ow, vgm_bitstream * iw, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian); -static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw, vorbis_custom_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile); -static int ww2ogg_codebook_library_copy(vgm_bitstream * ow, vgm_bitstream * iw); -static int ww2ogg_codebook_library_rebuild(vgm_bitstream * ow, vgm_bitstream * iw, size_t cb_size, STREAMFILE *streamFile); -static int ww2ogg_codebook_library_rebuild_by_id(vgm_bitstream * ow, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile); -static int ww2ogg_tremor_ilog(unsigned int v); -static unsigned int ww2ogg_tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions); - -static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile); -static int load_wvc_file(uint8_t * buf, size_t bufsize, uint32_t codebook_id, STREAMFILE *streamFile); -static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type); - - -/* **************************************************************************** */ -/* EXTERNAL API */ -/* **************************************************************************** */ - -/** - * Wwise stores a reduced setup, and packets have mini headers with the size, and data packets - * may reduced as well. The format evolved over time so there are many variations. - * The Wwise implementation uses Tremor (fixed-point Vorbis) but shouldn't matter. - * - * Format reverse-engineered by hcs in ww2ogg (https://github.com/hcs64/ww2ogg). - */ -int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { - size_t header_size, packet_size; - vorbis_custom_config cfg = data->config; - - if (cfg.setup_type == WWV_HEADER_TRIAD) { - /* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */ - off_t offset = start_offset; - - /* normal identificacion packet */ - header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian); - if (!header_size || packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - offset += header_size + packet_size; - - /* normal comment packet */ - header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian); - if (!header_size || packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - offset += header_size + packet_size; - - /* normal setup packet */ - header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian); - if (!header_size || packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - offset += header_size + packet_size; - } - else { - /* rebuild headers */ - - /* new identificacion packet */ - data->op.bytes = build_header_identification(data->buffer, data->buffer_size, cfg.channels, cfg.sample_rate, cfg.blocksize_0_exp, cfg.blocksize_1_exp); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - - /* new comment packet */ - data->op.bytes = build_header_comment(data->buffer, data->buffer_size); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - - /* rebuild setup packet */ - data->op.bytes = rebuild_setup(data->buffer, data->buffer_size, streamFile, start_offset, data, cfg.big_endian, cfg.channels); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - } - - return 1; - -fail: - return 0; -} - - -int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { - size_t header_size, packet_size = 0; - - /* reconstruct a Wwise packet, if needed; final bytes may be bigger than packet_size so we get the header offsets here */ - header_size = get_packet_header(stream->streamfile, stream->offset, data->config.header_type, (int*)&data->op.granulepos, &packet_size, data->config.big_endian); - if (!header_size || packet_size > data->buffer_size) goto fail; - - data->op.bytes = rebuild_packet(data->buffer, data->buffer_size, stream->streamfile,stream->offset, data, data->config.big_endian); - stream->offset += header_size + packet_size; - if (!data->op.bytes || data->op.bytes >= 0xFFFF) goto fail; - - return 1; - -fail: - return 0; -} - -/* **************************************************************************** */ -/* INTERNAL HELPERS */ -/* **************************************************************************** */ - -/* loads info from a wwise packet header */ -static size_t get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; - int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; - - /* packet size doesn't include header size */ - switch(header_type) { - case WWV_TYPE_8: /* size 4+4 */ - *packet_size = (uint32_t)read_32bit(offset, streamFile); - *granulepos = read_32bit(offset+4, streamFile); - return 8; - - case WWV_TYPE_6: /* size 4+2 */ - *packet_size = (uint16_t)read_16bit(offset, streamFile); - *granulepos = read_32bit(offset+2, streamFile); - return 6; - - case WWV_TYPE_2: /* size 2 */ - *packet_size = (uint16_t)read_16bit(offset, streamFile); - *granulepos = 0; /* granule is an arbitrary unit so we could use offset instead; libvorbis has no actually need it actually */ - return 2; - break; - default: /* ? */ - return 0; - } -} - -/* Transforms a Wwise data packet into a real Vorbis one (depending on config) */ -static size_t rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian) { - vgm_bitstream ow, iw; - int rc, granulepos; - size_t header_size, packet_size; - - size_t ibufsize = 0x8000; /* arbitrary max size of a setup packet */ - uint8_t ibuf[0x8000]; /* Wwise setup packet buffer */ - if (obufsize < ibufsize) goto fail; /* arbitrary expected min */ - - header_size = get_packet_header(streamFile, offset, data->config.header_type, &granulepos, &packet_size, big_endian); - if (!header_size || packet_size > obufsize) goto fail; - - /* load Wwise data into internal buffer */ - if (read_streamfile(ibuf,offset+header_size,packet_size, streamFile)!=packet_size) - goto fail; - - /* prepare helper structs */ - ow.buf = obuf; - ow.bufsize = obufsize; - ow.b_off = 0; - ow.mode = BITSTREAM_VORBIS; - - iw.buf = ibuf; - iw.bufsize = ibufsize; - iw.b_off = 0; - iw.mode = BITSTREAM_VORBIS; - - rc = ww2ogg_generate_vorbis_packet(&ow,&iw, streamFile,offset, data, big_endian); - if (!rc) goto fail; - - if (ow.b_off % 8 != 0) { - //VGM_LOG("Wwise Vorbis: didn't write exactly audio packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); - goto fail; - } - - - return ow.b_off / 8; -fail: - return 0; -} - - -/* Transforms a Wwise setup packet into a real Vorbis one (depending on config). */ -static size_t rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels) { - vgm_bitstream ow, iw; - int rc, granulepos; - size_t header_size, packet_size; - - size_t ibufsize = 0x8000; /* arbitrary max size of a setup packet */ - uint8_t ibuf[0x8000]; /* Wwise setup packet buffer */ - if (obufsize < ibufsize) goto fail; /* arbitrary expected min */ - - /* read Wwise packet header */ - header_size = get_packet_header(streamFile, offset, data->config.header_type, &granulepos, &packet_size, big_endian); - if (!header_size || packet_size > ibufsize) goto fail; - - /* load Wwise setup into internal buffer */ - if (read_streamfile(ibuf,offset+header_size,packet_size, streamFile)!=packet_size) - goto fail; - - /* prepare helper structs */ - ow.buf = obuf; - ow.bufsize = obufsize; - ow.b_off = 0; - ow.mode = BITSTREAM_VORBIS; - - iw.buf = ibuf; - iw.bufsize = ibufsize; - iw.b_off = 0; - iw.mode = BITSTREAM_VORBIS; - - rc = ww2ogg_generate_vorbis_setup(&ow,&iw, data, channels, packet_size, streamFile); - if (!rc) goto fail; - - if (ow.b_off % 8 != 0) { - //VGM_LOG("Wwise Vorbis: didn't write exactly setup packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); - goto fail; - } - - - return ow.b_off / 8; -fail: - return 0; -} - -static size_t build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_0_exp, int blocksize_1_exp) { - size_t bytes = 0x1e; - uint8_t blocksizes; - - if (bytes > bufsize) return 0; - - blocksizes = (blocksize_0_exp << 4) | (blocksize_1_exp); - - put_8bit (buf+0x00, 0x01); /* packet_type (id) */ - memcpy (buf+0x01, "vorbis", 6); /* id */ - put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */ - put_8bit (buf+0x0b, channels); /* audio_channels */ - put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */ - put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */ - put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */ - put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */ - put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */ - put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */ - - return bytes; -} - -static size_t build_header_comment(uint8_t * buf, size_t bufsize) { - size_t bytes = 0x19; - - if (bytes > bufsize) return 0; - - put_8bit (buf+0x00, 0x03); /* packet_type (comments) */ - memcpy (buf+0x01, "vorbis", 6); /* id */ - put_32bitLE(buf+0x07, 0x09); /* vendor_length */ - memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */ - put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */ - put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */ - - return bytes; -} - -/* **************************************************************************** */ -/* INTERNAL WW2OGG STUFF */ -/* **************************************************************************** */ -/* The following code was mostly and manually converted from hcs's ww2ogg. - * Could be simplified but roughly tries to preserve the structure in case fixes have to be backported. - * - * Some validations are ommited (ex. read/write), as incorrect data should be rejected by libvorbis. - * To avoid GCC complaining all values are init to 0, and some that do need it are init again, for clarity. - * Reads/writes unsigned ints as most are bit values less than 32 and with no sign meaning. - */ - -/* Copy packet as-is or rebuild first byte if mod_packets is used. - * (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-720004.3) */ -static int ww2ogg_generate_vorbis_packet(vgm_bitstream * ow, vgm_bitstream * iw, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian) { - int i,granule; - size_t header_size, packet_size, data_size; - - header_size = get_packet_header(streamFile,offset, data->config.header_type, &granule, &packet_size, big_endian); - if (!header_size || packet_size > iw->bufsize) goto fail; - - data_size = get_streamfile_size(streamFile);//todo get external data_size - - if (offset + header_size + packet_size > data_size) { - VGM_LOG("Wwise Vorbis: page header truncated\n"); - goto fail; - } - - /* this may happen in the first packet; maybe it's for the encoder delay but doesn't seem to affect libvorbis */ - //VGM_ASSERT(granule < 0, "Wwise Vorbis: negative granule %i @ 0x%lx\n", granule, offset); - - - if (data->config.packet_type == WWV_MODIFIED) { - /* rebuild first bits of packet type and window info (for the i-MDCT) */ - uint32_t packet_type = 0, mode_number = 0, remainder = 0; - - if (!data->mode_blockflag) { /* config error */ - VGM_LOG("Wwise Vorbis: didn't load mode_blockflag\n"); - goto fail; - } - - /* audio packet type */ - packet_type = 0; - w_bits(ow, 1, packet_type); - - /* collect this packet mode from the first byte */ - r_bits(iw, data->mode_bits,&mode_number); /* max 6b */ - w_bits(ow, data->mode_bits, mode_number); - r_bits(iw, 8-data->mode_bits,&remainder); - - /* adjust window info */ - if (data->mode_blockflag[mode_number]) { - /* long window: peek at next frame to find flags */ - off_t next_offset = offset + header_size + packet_size; - uint32_t next_blockflag = 0, prev_window_type = 0, next_window_type = 0; - - next_blockflag = 0; - /* check if more data / not eof */ - if (next_offset + header_size <= data_size) { - size_t next_header_size, next_packet_size; - int next_granule; - - next_header_size = get_packet_header(streamFile,next_offset, data->config.header_type, &next_granule, &next_packet_size, big_endian); - if (!next_header_size) goto fail; - - if (next_packet_size > 0) { - /* get next first byte to read next_mode_number */ - uint32_t next_mode_number; - uint8_t nbuf[1]; - vgm_bitstream nw; - - nw.buf = nbuf; - nw.bufsize = 1; - nw.b_off = 0; - nw.mode = BITSTREAM_VORBIS; - - - if (read_streamfile(nw.buf, next_offset + next_header_size, nw.bufsize, streamFile) != nw.bufsize) - goto fail; - - r_bits(&nw, data->mode_bits,&next_mode_number); /* max 6b */ - - next_blockflag = data->mode_blockflag[next_mode_number]; - } - } - - prev_window_type = data->prev_blockflag; - w_bits(ow, 1, prev_window_type); - - next_window_type = next_blockflag; - w_bits(ow, 1, next_window_type); - } - - data->prev_blockflag = data->mode_blockflag[mode_number]; /* save for next packet */ - - w_bits(ow, 8-data->mode_bits, remainder); /* this *isn't* byte aligned (ex. could be 10 bits written) */ - } - else { - /* normal packets: first byte unchanged */ - uint32_t c = 0; - - r_bits(iw, 8, &c); - w_bits(ow, 8, c); - } - - - /* remainder of packet (not byte-aligned when using mod_packets) */ - for (i = 1; i < packet_size; i++) { - uint32_t c = 0; - - r_bits(iw, 8, &c); - w_bits(ow, 8, c); - } - - /* remove trailing garbage bits */ - if (ow->b_off % 8 != 0) { - uint32_t padding = 0; - int padding_bits = 8 - (ow->b_off % 8); - - w_bits(ow, padding_bits, padding); - } - - - return 1; -fail: - return 0; -} - - -/* Rebuild a Wwise setup (simplified with removed stuff), recreating all six setup parts. - * (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-650004.2.4) */ -static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw, vorbis_custom_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile) { - int i,j,k; - uint32_t codebook_count = 0, floor_count = 0, residue_count = 0; - uint32_t codebook_count_less1 = 0; - uint32_t time_count_less1 = 0, dummy_time_value = 0; - - - /* packet header */ - put_8bit(ow->buf+0x00, 0x05); /* packet_type (setup) */ - memcpy (ow->buf+0x01, "vorbis", 6); /* id */ - ow->b_off += (1+6) * 8; /* bit offset of output (Vorbis) setup, after fake type + id */ - - - /* Codebooks */ - r_bits(iw, 8,&codebook_count_less1); - w_bits(ow, 8, codebook_count_less1); - codebook_count = codebook_count_less1 + 1; - - if (data->config.setup_type == WWV_FULL_SETUP) { - /* rebuild Wwise codebooks: untouched */ - for (i = 0; i < codebook_count; i++) { - if(!ww2ogg_codebook_library_copy(ow, iw)) goto fail; - } - } - else if (data->config.setup_type == WWV_INLINE_CODEBOOKS) { - /* rebuild Wwise codebooks: inline in simplified format */ - for (i = 0; i < codebook_count; i++) { - if(!ww2ogg_codebook_library_rebuild(ow, iw, 0, streamFile)) goto fail; - } - } - else { - /* rebuild Wwise codebooks: external (referenced by id) in simplified format */ - for (i = 0; i < codebook_count; i++) { - int rc; - uint32_t codebook_id = 0; - - r_bits(iw, 10,&codebook_id); - - rc = ww2ogg_codebook_library_rebuild_by_id(ow, codebook_id, data->config.setup_type, streamFile); - if (!rc) goto fail; - } - } - - - /* Time domain transforms */ - time_count_less1 = 0; - w_bits(ow, 6, time_count_less1); - dummy_time_value = 0; - w_bits(ow, 16, dummy_time_value); - - - if (data->config.setup_type == WWV_FULL_SETUP) { - /* rest of setup is untouched, copy bits */ - uint32_t bitly = 0; - uint32_t total_bits_read = iw->b_off; - uint32_t setup_packet_size_bits = packet_size*8; - - while (total_bits_read < setup_packet_size_bits) { - r_bits(iw, 1,&bitly); - w_bits(ow, 1, bitly); - total_bits_read = iw->b_off; - } - } - else { - /* rest of setup is altered, reconstruct */ - uint32_t floor_count_less1 = 0, floor1_multiplier_less1 = 0, rangebits = 0; - uint32_t residue_count_less1 = 0; - uint32_t mapping_count_less1 = 0, mapping_count = 0; - uint32_t mode_count_less1 = 0, mode_count = 0; - - - /* Floors */ - r_bits(iw, 6,&floor_count_less1); - w_bits(ow, 6, floor_count_less1); - floor_count = floor_count_less1 + 1; - - for (i = 0; i < floor_count; i++) { - uint32_t floor_type = 0, floor1_partitions = 0; - uint32_t maximum_class = 0; - uint32_t floor1_partition_class_list[32]; /* max 5b */ - uint32_t floor1_class_dimensions_list[16+1]; /* max 4b+1 */ - - // Always floor type 1 - floor_type = 1; - w_bits(ow, 16, floor_type); - - r_bits(iw, 5,&floor1_partitions); - w_bits(ow, 5, floor1_partitions); - - memset(floor1_partition_class_list, 0, sizeof(uint32_t)*32); - - maximum_class = 0; - for (j = 0; j < floor1_partitions; j++) { - uint32_t floor1_partition_class = 0; - - r_bits(iw, 4,&floor1_partition_class); - w_bits(ow, 4, floor1_partition_class); - - floor1_partition_class_list[j] = floor1_partition_class; - - if (floor1_partition_class > maximum_class) - maximum_class = floor1_partition_class; - } - - memset(floor1_class_dimensions_list, 0, sizeof(uint32_t)*(16+1)); - - for (j = 0; j <= maximum_class; j++) { - uint32_t class_dimensions_less1 = 0, class_subclasses = 0; - - r_bits(iw, 3,&class_dimensions_less1); - w_bits(ow, 3, class_dimensions_less1); - - floor1_class_dimensions_list[j] = class_dimensions_less1 + 1; - - r_bits(iw, 2,&class_subclasses); - w_bits(ow, 2, class_subclasses); - - if (0 != class_subclasses) { - uint32_t masterbook = 0; - - r_bits(iw, 8,&masterbook); - w_bits(ow, 8, masterbook); - - if (masterbook >= codebook_count) { - VGM_LOG("Wwise Vorbis: invalid floor1 masterbook\n"); - goto fail; - } - } - - for (k = 0; k < (1U<= 0 && subclass_book >= codebook_count) { - VGM_LOG("Wwise Vorbis: invalid floor1 subclass book\n"); - goto fail; - } - } - } - - r_bits(iw, 2,&floor1_multiplier_less1); - w_bits(ow, 2, floor1_multiplier_less1); - - r_bits(iw, 4,&rangebits); - w_bits(ow, 4, rangebits); - - for (j = 0; j < floor1_partitions; j++) { - uint32_t current_class_number = 0; - - current_class_number = floor1_partition_class_list[j]; - for (k = 0; k < floor1_class_dimensions_list[current_class_number]; k++) { - uint32_t X = 0; /* max 4b (15) */ - - r_bits(iw, rangebits,&X); - w_bits(ow, rangebits, X); - } - } - } - - - /* Residues */ - r_bits(iw, 6,&residue_count_less1); - w_bits(ow, 6, residue_count_less1); - residue_count = residue_count_less1 + 1; - - for (i = 0; i < residue_count; i++) { - uint32_t residue_type = 0, residue_classifications = 0; - uint32_t residue_begin = 0, residue_end = 0, residue_partition_size_less1 = 0, residue_classifications_less1 = 0, residue_classbook = 0; - uint32_t residue_cascade[64+1]; /* 6b +1 */ - - r_bits(iw, 2,&residue_type); - w_bits(ow, 16, residue_type); /* 2b to 16b */ - - if (residue_type > 2) { - VGM_LOG("Wwise Vorbis: invalid residue type\n"); - goto fail; - } - - r_bits(iw, 24,&residue_begin); - w_bits(ow, 24, residue_begin); - r_bits(iw, 24,&residue_end); - w_bits(ow, 24, residue_end); - r_bits(iw, 24,&residue_partition_size_less1); - w_bits(ow, 24, residue_partition_size_less1); - r_bits(iw, 6,&residue_classifications_less1); - w_bits(ow, 6, residue_classifications_less1); - r_bits(iw, 8,&residue_classbook); - w_bits(ow, 8, residue_classbook); - residue_classifications = residue_classifications_less1 + 1; - - if (residue_classbook >= codebook_count) { - VGM_LOG("Wwise Vorbis: invalid residue classbook\n"); - goto fail; - } - - memset(residue_cascade, 0, sizeof(uint32_t)*(64+1)); - - for (j = 0; j < residue_classifications; j++) { - uint32_t high_bits = 0, low_bits = 0, bitflag = 0; - - high_bits = 0; - - r_bits(iw, 3,&low_bits); - w_bits(ow, 3, low_bits); - - r_bits(iw, 1,&bitflag); - w_bits(ow, 1, bitflag); - if (bitflag) { - r_bits(iw, 5,&high_bits); - w_bits(ow, 5, high_bits); - } - - residue_cascade[j] = high_bits * 8 + low_bits; - } - - for (j = 0; j < residue_classifications; j++) { - for (k = 0; k < 8; k++) { - if (residue_cascade[j] & (1 << k)) { - uint32_t residue_book = 0; - - r_bits(iw, 8,&residue_book); - w_bits(ow, 8, residue_book); - - if (residue_book >= codebook_count) { - VGM_LOG("Wwise Vorbis: invalid residue book\n"); - goto fail; - } - } - } - } - } - - - /* Mappings */ - r_bits(iw, 6,&mapping_count_less1); - w_bits(ow, 6, mapping_count_less1); - mapping_count = mapping_count_less1 + 1; - - for (i = 0; i < mapping_count; i++) { - uint32_t mapping_type = 0, submaps_flag = 0, submaps = 0, square_polar_flag = 0; - uint32_t mapping_reserved = 0; - - // always mapping type 0, the only one - mapping_type = 0; - w_bits(ow, 16, mapping_type); - - r_bits(iw, 1,&submaps_flag); - w_bits(ow, 1, submaps_flag); - - submaps = 1; - if (submaps_flag) { - uint32_t submaps_less1 = 0; - - r_bits(iw, 4,&submaps_less1); - w_bits(ow, 4, submaps_less1); - submaps = submaps_less1 + 1; - } - - r_bits(iw, 1,&square_polar_flag); - w_bits(ow, 1, square_polar_flag); - - if (square_polar_flag) { - uint32_t coupling_steps_less1 = 0, coupling_steps = 0; - - r_bits(iw, 8,&coupling_steps_less1); - w_bits(ow, 8, coupling_steps_less1); - coupling_steps = coupling_steps_less1 + 1; - - for (j = 0; j < coupling_steps; j++) { - uint32_t magnitude = 0, angle = 0; - int magnitude_bits = ww2ogg_tremor_ilog(channels-1); - int angle_bits = ww2ogg_tremor_ilog(channels-1); - - r_bits(iw, magnitude_bits,&magnitude); - w_bits(ow, magnitude_bits, magnitude); - r_bits(iw, angle_bits,&angle); - w_bits(ow, angle_bits, angle); - - if (angle == magnitude || magnitude >= channels || angle >= channels) { - VGM_LOG("Wwise Vorbis: invalid coupling (angle=%i, mag=%i, ch=%i)\n", angle, magnitude,channels); - goto fail; - } - } - } - - // a rare reserved field not removed by Ak! - r_bits(iw, 2,&mapping_reserved); - w_bits(ow, 2, mapping_reserved); - if (0 != mapping_reserved) { - VGM_LOG("Wwise Vorbis: mapping reserved field nonzero\n"); - goto fail; - } - - if (submaps > 1) { - for (j = 0; j < channels; j++) { - uint32_t mapping_mux = 0; - - r_bits(iw, 4,&mapping_mux); - w_bits(ow, 4, mapping_mux); - if (mapping_mux >= submaps) { - VGM_LOG("Wwise Vorbis: mapping_mux >= submaps\n"); - goto fail; - } - } - } - - for (j = 0; j < submaps; j++) { - uint32_t time_config = 0, floor_number = 0, residue_number = 0; - - // Another! Unused time domain transform configuration placeholder! - r_bits(iw, 8,&time_config); - w_bits(ow, 8, time_config); - - r_bits(iw, 8,&floor_number); - w_bits(ow, 8, floor_number); - if (floor_number >= floor_count) { - VGM_LOG("Wwise Vorbis: invalid floor mapping\n"); - goto fail; - } - - r_bits(iw, 8,&residue_number); - w_bits(ow, 8, residue_number); - if (residue_number >= residue_count) { - VGM_LOG("Wwise Vorbis: invalid residue mapping\n"); - goto fail; - } - } - } - - - /* Modes */ - r_bits(iw, 6,&mode_count_less1); - w_bits(ow, 6, mode_count_less1); - mode_count = mode_count_less1 + 1; - - memset(data->mode_blockflag, 0, sizeof(uint8_t)*(64+1)); /* up to max mode_count */ - data->mode_bits = ww2ogg_tremor_ilog(mode_count-1); /* for mod_packets */ - - for (i = 0; i < mode_count; i++) { - uint32_t block_flag = 0, windowtype = 0, transformtype = 0, mapping = 0; - - r_bits(iw, 1,&block_flag); - w_bits(ow, 1, block_flag); - - data->mode_blockflag[i] = (block_flag != 0); /* for mod_packets */ - - windowtype = 0; - transformtype = 0; - w_bits(ow, 16, windowtype); - w_bits(ow, 16, transformtype); - - r_bits(iw, 8,&mapping); - w_bits(ow, 8, mapping); - if (mapping >= mapping_count) { - VGM_LOG("Wwise Vorbis: invalid mode mapping\n"); - goto fail; - } - } - } - - - /* end flag */ - { - uint32_t framing = 0; - - framing = 1; - w_bits(ow, 1, framing); - } - - /* remove trailing garbage bits */ - if (ow->b_off % 8 != 0) { - uint32_t padding = 0; - int padding_bits = 8 - (ow->b_off % 8); - - w_bits(ow, padding_bits, padding); - } - - - return 1; -fail: - return 0; -} - - -/* copies Vorbis codebooks (untouched, but size uncertain) */ -static int ww2ogg_codebook_library_copy(vgm_bitstream * ow, vgm_bitstream * iw) { - int i; - uint32_t id = 0, dimensions = 0, entries = 0; - uint32_t ordered = 0, lookup_type = 0; - - r_bits(iw, 24,&id); - w_bits(ow, 24, id); - r_bits(iw, 16,&dimensions); - w_bits(ow, 16, dimensions); - r_bits(iw, 24,&entries); - w_bits(ow, 24, entries); - - if (0x564342 != id) { /* "VCB" */ - VGM_LOG("Wwise Vorbis: invalid codebook identifier\n"); - goto fail; - } - - /* codeword lengths */ - r_bits(iw, 1,&ordered); - w_bits(ow, 1, ordered); - if (ordered) { - uint32_t initial_length = 0, current_entry = 0; - - r_bits(iw, 5,&initial_length); - w_bits(ow, 5, initial_length); - - current_entry = 0; - while (current_entry < entries) { - uint32_t number = 0; - int number_bits = ww2ogg_tremor_ilog(entries-current_entry); - - r_bits(iw, number_bits,&number); - w_bits(ow, number_bits, number); - current_entry += number; - } - if (current_entry > entries) { - VGM_LOG("Wwise Vorbis: current_entry out of range\n"); - goto fail; - } - } - else { - uint32_t sparse = 0; - - r_bits(iw, 1,&sparse); - w_bits(ow, 1, sparse); - - for (i = 0; i < entries; i++) { - uint32_t present_bool = 0; - - present_bool = 1; - if (sparse) { - uint32_t present = 0; - - r_bits(iw, 1,&present); - w_bits(ow, 1, present); - - present_bool = (0 != present); - } - - if (present_bool) { - uint32_t codeword_length = 0; - - r_bits(iw, 5,&codeword_length); - w_bits(ow, 5, codeword_length); - } - } - } - - - /* lookup table */ - r_bits(iw, 4,&lookup_type); - w_bits(ow, 4, lookup_type); - - if (0 == lookup_type) { - //VGM_LOG("Wwise Vorbis: no lookup table\n"); - } - else if (1 == lookup_type) { - //VGM_LOG("Wwise Vorbis: lookup type 1\n"); - uint32_t quantvals = 0, min = 0, max = 0; - uint32_t value_length = 0, sequence_flag = 0; - - r_bits(iw, 32,&min); - w_bits(ow, 32, min); - r_bits(iw, 32,&max); - w_bits(ow, 32, max); - r_bits(iw, 4,&value_length); - w_bits(ow, 4, value_length); - r_bits(iw, 1,&sequence_flag); - w_bits(ow, 1, sequence_flag); - - quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions); - for (i = 0; i < quantvals; i++) { - uint32_t val = 0, val_bits = 0; - val_bits = value_length+1; - - r_bits(iw, val_bits,&val); - w_bits(ow, val_bits, val); - } - } - else if (2 == lookup_type) { - VGM_LOG("Wwise Vorbis: didn't expect lookup type 2\n"); - goto fail; - } - else { - VGM_LOG("Wwise Vorbis: invalid lookup type\n"); - goto fail; - } - - - return 1; -fail: - return 0; -} - - -/* rebuilds a Wwise codebook into a Vorbis codebook */ -static int ww2ogg_codebook_library_rebuild(vgm_bitstream * ow, vgm_bitstream * iw, size_t cb_size, STREAMFILE *streamFile) { - int i; - uint32_t id = 0, dimensions = 0, entries = 0; - uint32_t ordered = 0, lookup_type = 0; - - id = 0x564342; /* "VCB" */ - - w_bits(ow, 24, id); - r_bits(iw, 4,&dimensions); - w_bits(ow, 16, dimensions); /* 4 to 16 */ - r_bits(iw, 14,&entries); - w_bits(ow, 24, entries); /* 14 to 24*/ - - /* codeword lengths */ - r_bits(iw, 1,&ordered); - w_bits(ow, 1, ordered); - if (ordered) { - uint32_t initial_length = 0, current_entry = 0; - - r_bits(iw, 5,&initial_length); - w_bits(ow, 5, initial_length); - - current_entry = 0; - while (current_entry < entries) { - uint32_t number = 0; - int number_bits = ww2ogg_tremor_ilog(entries-current_entry); - - r_bits(iw, number_bits,&number); - w_bits(ow, number_bits, number); - current_entry += number; - } - if (current_entry > entries) { - VGM_LOG("Wwise Vorbis: current_entry out of range\n"); - goto fail; - } - } - else { - uint32_t codeword_length_length = 0, sparse = 0; - - r_bits(iw, 3,&codeword_length_length); - r_bits(iw, 1,&sparse); - w_bits(ow, 1, sparse); - - if (0 == codeword_length_length || 5 < codeword_length_length) { - VGM_LOG("Wwise Vorbis: nonsense codeword length\n"); - goto fail; - } - - for (i = 0; i < entries; i++) { - uint32_t present_bool = 0; - - present_bool = 1; - if (sparse) { - uint32_t present = 0; - - r_bits(iw, 1,&present); - w_bits(ow, 1, present); - - present_bool = (0 != present); - } - - if (present_bool) { - uint32_t codeword_length = 0; - - r_bits(iw, codeword_length_length,&codeword_length); - w_bits(ow, 5, codeword_length); /* max 7 (3b) to 5 */ - } - } - } - - - /* lookup table */ - r_bits(iw, 1,&lookup_type); - w_bits(ow, 4, lookup_type); /* 1 to 4 */ - - if (0 == lookup_type) { - //VGM_LOG("Wwise Vorbis: no lookup table\n"); - } - else if (1 == lookup_type) { - //VGM_LOG("Wwise Vorbis: lookup type 1\n"); - uint32_t quantvals = 0, min = 0, max = 0; - uint32_t value_length = 0, sequence_flag = 0; - - r_bits(iw, 32,&min); - w_bits(ow, 32, min); - r_bits(iw, 32,&max); - w_bits(ow, 32, max); - r_bits(iw, 4,&value_length); - w_bits(ow, 4, value_length); - r_bits(iw, 1,&sequence_flag); - w_bits(ow, 1, sequence_flag); - - quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions); - for (i = 0; i < quantvals; i++) { - uint32_t val = 0, val_bits = 0; - val_bits = value_length+1; - - r_bits(iw, val_bits,&val); - w_bits(ow, val_bits, val); - } - } - else if (2 == lookup_type) { - VGM_LOG("Wwise Vorbis: didn't expect lookup type 2\n"); - goto fail; - } - else { - VGM_LOG("Wwise Vorbis: invalid lookup type\n"); - goto fail; - } - - - /* check that we used exactly all bytes */ - /* note: if all bits are used in the last byte there will be one extra 0 byte */ - if ( 0 != cb_size && iw->b_off/8+1 != cb_size ) { - //VGM_LOG("Wwise Vorbis: codebook size mistach (expected 0x%x, wrote 0x%lx)\n", cb_size, iw->b_off/8+1); - goto fail; - } - - return 1; -fail: - return 0; -} - -/* rebuilds an external Wwise codebook referenced by id to a Vorbis codebook */ -static int ww2ogg_codebook_library_rebuild_by_id(vgm_bitstream * ow, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile) { - size_t ibufsize = 0x8000; /* arbitrary max size of a codebook */ - uint8_t ibuf[0x8000]; /* Wwise codebook buffer */ - size_t cb_size; - vgm_bitstream iw; - - cb_size = load_wvc(ibuf,ibufsize, codebook_id, setup_type, streamFile); - if (cb_size == 0) goto fail; - - iw.buf = ibuf; - iw.bufsize = ibufsize; - iw.b_off = 0; - iw.mode = BITSTREAM_VORBIS; - - return ww2ogg_codebook_library_rebuild(ow, &iw, cb_size, streamFile); -fail: - return 0; -} - - -/* fixed-point ilog from Xiph's Tremor */ -static int ww2ogg_tremor_ilog(unsigned int v) { - int ret=0; - while(v){ - ret++; - v>>=1; - } - return(ret); -} -/* quantvals-something from Xiph's Tremor */ -static unsigned int ww2ogg_tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions) { - /* get us a starting hint, we'll polish it below */ - int bits=ww2ogg_tremor_ilog(entries); - int vals=entries>>((bits-1)*(dimensions-1)/dimensions); - - while(1){ - unsigned long acc=1; - unsigned long acc1=1; - unsigned int i; - for(i=0;ientries){ - return(vals); - }else{ - if(acc>entries){ - vals--; - }else{ - vals++; - } - } - } -} - - -/* **************************************************************************** */ -/* INTERNAL UTILS */ -/* **************************************************************************** */ - -/* loads an external Wwise Vorbis Codebooks file (wvc) referenced by ID and returns size */ -static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile) { - size_t bytes; - - /* try to locate from the precompiled list */ - bytes = load_wvc_array(ibuf, ibufsize, codebook_id, setup_type); - if (bytes) - return bytes; - - /* try to load from external file (ignoring type, just use file if found) */ - bytes = load_wvc_file(ibuf, ibufsize, codebook_id, streamFile); - if (bytes) - return bytes; - - /* not found */ - VGM_LOG("Wwise Vorbis: codebook_id %04x not found\n", codebook_id); - return 0; -} - -static int load_wvc_file(uint8_t * buf, size_t bufsize, uint32_t codebook_id, STREAMFILE *streamFile) { - STREAMFILE * streamFileWvc = NULL; - size_t wvc_size = 0; - - { - char setupname[PATH_LIMIT]; - char pathname[PATH_LIMIT]; - char *path; - - /* read "(dir/).wvc" */ - streamFile->get_name(streamFile,pathname,sizeof(pathname)); - path = strrchr(pathname,DIR_SEPARATOR); - if (path) - *(path+1) = '\0'; - else - pathname[0] = '\0'; - - snprintf(setupname,PATH_LIMIT,"%s.wvc", pathname); - streamFileWvc = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileWvc) goto fail; - - wvc_size = streamFileWvc->get_size(streamFileWvc); - } - - /* find codebook and copy to buffer */ - { - off_t table_start, codebook_offset; - size_t codebook_size; - int codebook_count; - - /* at the end of the WVC is an offset table, and we need to find codebook id (number) offset */ - table_start = read_32bitLE(wvc_size - 4, streamFileWvc); /* last offset */ - codebook_count = ((wvc_size - table_start) / 4) - 1; - if (table_start > wvc_size || codebook_id >= codebook_count) goto fail; - - codebook_offset = read_32bitLE(table_start + codebook_id*4, streamFileWvc); - codebook_size = read_32bitLE(table_start + codebook_id*4 + 4, streamFileWvc) - codebook_offset; - if (codebook_size > bufsize) goto fail; - - if (read_streamfile(buf, codebook_offset, codebook_size, streamFileWvc) != codebook_size) - goto fail; - streamFileWvc->close(streamFileWvc); - - return codebook_size; - } - - -fail: - if (streamFileWvc) streamFileWvc->close(streamFileWvc); - return 0; -} - -static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type) { -#if WWISE_VORBIS_USE_PRECOMPILED_WVC - - /* get pointer to array */ - { - int i, list_length; - const wvc_info * wvc_list; - - switch (setup_type) { - case WWV_EXTERNAL_CODEBOOKS: - wvc_list = wvc_list_standard; - list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); - break; - case WWV_AOTUV603_CODEBOOKS: - wvc_list = wvc_list_aotuv603; - list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); - break; - default: - goto fail; - } - - for (i=0; i < list_length; i++) { - if (wvc_list[i].id == codebook_id) { - if (wvc_list[i].size > bufsize) goto fail; - /* found: copy data as-is */ - memcpy(buf,wvc_list[i].codebook, wvc_list[i].size); - return wvc_list[i].size; - } - } - } - - // this can be used if the lists contained a 1:1 dump of the codebook files -#if 0 - if (wvc == NULL) goto fail; - /* find codebook and copy to buffer */ - { - off_t table_start, codebook_offset; - size_t codebook_size; - int codebook_count; - - /* at the end of the WVC is an offset table, and we need to find codebook id (number) offset */ - table_start = get_32bitLE(wvc + wvc_size - 4); /* last offset */ - codebook_count = ((wvc_size - table_start) / 4) - 1; - if (codebook_id >= codebook_count) goto fail; - - codebook_offset = get_32bitLE(wvc + table_start + codebook_id*4); - codebook_size = get_32bitLE(wvc + table_start + codebook_id*4 + 4) - codebook_offset; - if (codebook_size > bufsize) goto fail; - - memcpy(buf, wvc+codebook_offset, codebook_size); - - return codebook_size; - } -#endif - -fail: -#endif - return 0; -} - -#endif +#include "vorbis_custom_decoder.h" +#include "vorbis_bitreader.h" + +#ifdef VGM_USE_VORBIS +#include + +#define WWISE_VORBIS_USE_PRECOMPILED_WVC 1 /* if enabled vgmstream weights ~150kb more but doesn't need external .wvc packets */ +#if WWISE_VORBIS_USE_PRECOMPILED_WVC +#include "vorbis_custom_data_wwise.h" +#endif + + +/* **************************************************************************** */ +/* DEFS */ +/* **************************************************************************** */ + +typedef struct { + size_t header_size; + size_t packet_size; + int granulepos; + + int has_next; + uint8_t inxt[0x01]; +} wpacket_t; + +static size_t build_header_identification(uint8_t* buf, size_t bufsize, vorbis_custom_config* cfg); +static size_t build_header_comment(uint8_t* buf, size_t bufsize); + +static int read_packet(wpacket_t* wp, uint8_t* ibuf, size_t ibufsize, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data, int is_setup); +static size_t rebuild_packet(uint8_t* obuf, size_t obufsize, wpacket_t* wp, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data); +static size_t rebuild_setup(uint8_t* obuf, size_t obufsize, wpacket_t* wp, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data); + +static int ww2ogg_generate_vorbis_packet(bitstream_t* ow, bitstream_t* iw, wpacket_t* wp, vorbis_custom_codec_data* data); +static int ww2ogg_generate_vorbis_setup(bitstream_t* ow, bitstream_t* iw, vorbis_custom_codec_data* data, size_t packet_size, STREAMFILE* sf); + +static int load_wvc(uint8_t* ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE* sf); +static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STREAMFILE* sf); +static int load_wvc_array(uint8_t* buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type); + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * Wwise stores a reduced setup, and packets have mini headers with the size, and data packets + * may reduced as well. The format evolved over time so there are many variations. + * The Wwise implementation uses Tremor (fixed-point Vorbis) but shouldn't matter. + * + * Format reverse-engineered by hcs in ww2ogg (https://github.com/hcs64/ww2ogg). + */ +int vorbis_custom_setup_init_wwise(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) { + wpacket_t wp = {0}; + int ok; + + + if (data->config.setup_type == WWV_HEADER_TRIAD) { + /* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */ + off_t offset = start_offset; + + /* normal identificacion packet */ + ok = read_packet(&wp, data->buffer, data->buffer_size, sf, offset, data, 1); + if (!ok) goto fail; + data->op.bytes = wp.packet_size; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; + offset += wp.header_size + wp.packet_size; + + /* normal comment packet */ + ok = read_packet(&wp, data->buffer, data->buffer_size, sf, offset, data, 1); + if (!ok) goto fail; + data->op.bytes = wp.packet_size; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; + offset += wp.header_size + wp.packet_size; + + /* normal setup packet */ + ok = read_packet(&wp, data->buffer, data->buffer_size, sf, offset, data, 1); + if (!ok) goto fail; + data->op.bytes = wp.packet_size; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; + offset += wp.header_size + wp.packet_size; + } + else { + /* rebuild headers */ + + /* new identificacion packet */ + data->op.bytes = build_header_identification(data->buffer, data->buffer_size, &data->config); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + + /* new comment packet */ + data->op.bytes = build_header_comment(data->buffer, data->buffer_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ + + /* rebuild setup packet */ + data->op.bytes = rebuild_setup(data->buffer, data->buffer_size, &wp, sf, start_offset, data); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + } + + return 1; + +fail: + return 0; +} + + +int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) { + wpacket_t wp = {0}; + + /* reconstruct a Wwise packet, if needed; final bytes may be bigger than packet_size */ + data->op.bytes = rebuild_packet(data->buffer, data->buffer_size, &wp, stream->streamfile, stream->offset, data); + stream->offset += wp.header_size + wp.packet_size; + if (!data->op.bytes || data->op.bytes >= 0xFFFF) goto fail; + + data->op.granulepos = wp.granulepos; + + return 1; + +fail: + return 0; +} + +/* **************************************************************************** */ +/* INTERNAL HELPERS */ +/* **************************************************************************** */ + +static int read_packet(wpacket_t* wp, uint8_t* ibuf, size_t ibufsize, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data, int is_setup) { + uint32_t (*get_u32)(const uint8_t*) = data->config.big_endian ? get_u32be : get_u32le; + uint16_t (*get_u16)(const uint8_t*) = data->config.big_endian ? get_u16be : get_u16le; + int32_t (*get_s32)(const uint8_t*) = data->config.big_endian ? get_s32be : get_s32le; + + /* read header info (packet size doesn't include header size) */ + switch(data->config.header_type) { + case WWV_TYPE_8: + wp->header_size = 0x08; + read_streamfile(ibuf, offset, wp->header_size, sf); + wp->packet_size = get_u32(ibuf + 0x00); + wp->granulepos = get_s32(ibuf + 0x04); + break; + + case WWV_TYPE_6: + wp->header_size = 0x06; + read_streamfile(ibuf, offset, wp->header_size, sf); + wp->packet_size = get_u16(ibuf + 0x00); + wp->granulepos = get_s32(ibuf + 0x02); + break; + + case WWV_TYPE_2: + wp->header_size = 0x02; + read_streamfile(ibuf, offset, wp->header_size, sf); + wp->packet_size = get_u16(ibuf + 0x00); + wp->granulepos = 0; /* granule is an arbitrary unit so we could use offset instead; libvorbis has no need for it */ + break; + + default: /* ? */ + wp->header_size = 0; + wp->packet_size = 0; + wp->granulepos = 0; + break; + } + + if (wp->header_size == 0 || wp->packet_size == 0) + goto fail; + + /* read packet data */ + { + size_t read_size = wp->packet_size; + size_t read; + + /* mod packets need next packet's first byte (6 bits) except at EOF, so read now too */ + if (!is_setup && data->config.packet_type == WWV_MODIFIED) { + read_size += wp->header_size + 0x01; + } + + if (!wp->header_size || read_size > ibufsize) + goto fail; + + read = read_streamfile(ibuf, offset + wp->header_size, read_size, sf); + if (read < wp->packet_size) { + VGM_LOG("Wwise Vorbis: truncated packet\n"); + goto fail; + } + + if (!is_setup && data->config.packet_type == WWV_MODIFIED && read == read_size) { + wp->has_next = 1; + wp->inxt[0] = ibuf[wp->packet_size + wp->header_size]; + } + else { + wp->has_next = 0; + } + } + + return 1; +fail: + return 0; +} + + +/* Transforms a Wwise data packet into a real Vorbis one (depending on config) */ +static size_t rebuild_packet(uint8_t* obuf, size_t obufsize, wpacket_t* wp, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data) { + bitstream_t ow, iw; + int ok; + uint8_t ibuf[0x8000]; /* arbitrary max */ + size_t ibufsize = sizeof(ibuf); + + if (obufsize < ibufsize) /* arbitrary min */ + goto fail; + + ok = read_packet(wp, ibuf, ibufsize, sf, offset, data, 0); + if (!ok) goto fail; + + init_bitstream(&ow, obuf, obufsize); + init_bitstream(&iw, ibuf, ibufsize); + + ok = ww2ogg_generate_vorbis_packet(&ow, &iw, wp, data); + if (!ok) goto fail; + + if (ow.b_off % 8 != 0) { + //VGM_LOG("Wwise Vorbis: didn't write exactly audio packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); + goto fail; + } + + + return ow.b_off / 8; +fail: + return 0; +} + +/* Transforms a Wwise setup packet into a real Vorbis one (depending on config). */ +static size_t rebuild_setup(uint8_t* obuf, size_t obufsize, wpacket_t* wp, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data) { + bitstream_t ow, iw; + int ok; + uint8_t ibuf[0x8000]; /* arbitrary max */ + size_t ibufsize = sizeof(ibuf); + + if (obufsize < ibufsize) /* arbitrary min */ + goto fail; + + ok = read_packet(wp, ibuf, ibufsize, sf, offset, data, 1); + if (!ok) goto fail; + + init_bitstream(&ow, obuf, obufsize); + init_bitstream(&iw, ibuf, ibufsize); + + ok = ww2ogg_generate_vorbis_setup(&ow,&iw, data, wp->packet_size, sf); + if (!ok) goto fail; + + if (ow.b_off % 8 != 0) { + //VGM_LOG("Wwise Vorbis: didn't write exactly setup packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); + goto fail; + } + + + return ow.b_off / 8; +fail: + return 0; +} + +static size_t build_header_identification(uint8_t* buf, size_t bufsize, vorbis_custom_config* cfg) { + size_t bytes = 0x1e; + uint8_t blocksizes; + + if (bytes > bufsize) return 0; + + blocksizes = (cfg->blocksize_0_exp << 4) | (cfg->blocksize_1_exp); + + put_8bit (buf+0x00, 0x01); /* packet_type (id) */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */ + put_8bit (buf+0x0b, cfg->channels); /* audio_channels */ + put_32bitLE(buf+0x0c, cfg->sample_rate);/* audio_sample_rate */ + put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */ + put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */ + put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */ + put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */ + put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */ + + return bytes; +} + +static size_t build_header_comment(uint8_t* buf, size_t bufsize) { + size_t bytes = 0x19; + + if (bytes > bufsize) return 0; + + put_8bit (buf+0x00, 0x03); /* packet_type (comments) */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + put_32bitLE(buf+0x07, 0x09); /* vendor_length */ + memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */ + put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */ + put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */ + + return bytes; +} + + +/* copy packet bytes, where input/output bufs may not be byte-aligned (so no memcpy) */ +static int copy_bytes(bitstream_t* ob, bitstream_t* ib, uint32_t bytes) { + int i; + +#if 0 + /* in theory this would be faster, but not clear results; maybe default is just optimized by compiler */ + for (i = 0; i < bytes / 4; i++) { + uint32_t c = 0; + + rv_bits(ib, 32, &c); + wv_bits(ob, 32, c); + } + for (i = 0; i < bytes % 4; i++) { + uint32_t c = 0; + + rv_bits(ib, 8, &c); + wv_bits(ob, 8, c); + } +#endif + +#if 0 + /* output bits are never(?) byte aligned but input always is, yet this doesn't seem any faster */ + if (ib->b_off % 8 == 0) { + int iw_pos = ib->b_off / 8; + + for (i = 0; i < bytes; i++, iw_pos++) { + uint32_t c = ib->buf[iw_pos]; + + //rv_bits(ib, 8, &c); + wv_bits(ob, 8, c); + } + + ib->b_off += bytes * 8; + return 1; + } +#endif + + for (i = 0; i < bytes; i++) { + uint32_t c = 0; + + rv_bits(ib, 8, &c); + wv_bits(ob, 8, c); + } + + return 1; +} + +/* **************************************************************************** */ +/* INTERNAL WW2OGG STUFF */ +/* **************************************************************************** */ +/* The following code was mostly and manually converted from hcs's ww2ogg (https://github.com/hcs64/ww2ogg). + * Could be simplified but roughly tries to preserve the structure for comparison. + * + * Some validations are ommited (ex. read/write), as incorrect data should be rejected by libvorbis. + * To avoid GCC complaining all values are init to 0, and some that do need it are init again, for clarity. + * Reads/writes unsigned ints as most are bit values less than 32 and with no sign meaning. + */ + +/* Copy packet as-is or rebuild to standard Vorbis packet if mod_packets is used. + * (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-720004.3) */ +static int ww2ogg_generate_vorbis_packet(bitstream_t* ow, bitstream_t* iw, wpacket_t* wp, vorbis_custom_codec_data* data) { + + /* this may happen in the first packet; maybe it's for the encoder delay but doesn't seem to affect libvorbis */ + //VGM_ASSERT(granule < 0, "Wwise Vorbis: negative granule %i @ 0x%lx\n", granule, offset); + + + if (data->config.packet_type == WWV_MODIFIED) { + /* rebuild first bits of packet type and window info (for the i-MDCT) */ + uint32_t packet_type = 0, mode_number = 0, remainder = 0; + + if (!data->mode_blockflag) { /* config error */ + VGM_LOG("Wwise Vorbis: didn't load mode_blockflag\n"); + goto fail; + } + + /* audio packet type */ + packet_type = 0; + wv_bits(ow, 1, packet_type); + + /* collect this packet mode from the first byte */ + rv_bits(iw, data->mode_bits,&mode_number); /* max 6b */ + wv_bits(ow, data->mode_bits, mode_number); + + rv_bits(iw, 8-data->mode_bits,&remainder); + + /* adjust window info */ + if (data->mode_blockflag[mode_number]) { + /* long window: peek at next frame to find flags */ + uint32_t next_blockflag = 0, prev_window_type = 0, next_window_type = 0; + + if (wp->has_next) { + /* get next first byte to read next_mode_number */ + uint32_t next_mode_number; + bitstream_t nw; + + init_bitstream(&nw, wp->inxt, sizeof(wp->inxt)); + + rv_bits(&nw, data->mode_bits,&next_mode_number); /* max 6b */ + + next_blockflag = data->mode_blockflag[next_mode_number]; + } + else { + /* EOF (probably doesn't matter) */ + next_blockflag = 0; + } + + prev_window_type = data->prev_blockflag; + wv_bits(ow, 1, prev_window_type); + + next_window_type = next_blockflag; + wv_bits(ow, 1, next_window_type); + } + + data->prev_blockflag = data->mode_blockflag[mode_number]; /* save for next packet */ + + wv_bits(ow, 8-data->mode_bits, remainder); + + /* rest of the packet (input/output bytes aren't byte aligned here, so no memcpy) */ + copy_bytes(ow, iw, wp->packet_size - 1); + + /* remove trailing garbage bits (probably unneeded) */ + if (ow->b_off % 8 != 0) { + uint32_t padding = 0; + int padding_bits = 8 - (ow->b_off % 8); + + wv_bits(ow, padding_bits, padding); + } + } + else { + /* normal packets */ + + /* can directly copy (much, much faster), but least common case vs the above... */ + memcpy(ow->buf + ow->b_off / 8, iw->buf + iw->b_off / 8, wp->packet_size); + ow->b_off += wp->packet_size * 8; + iw->b_off += wp->packet_size * 8; + } + + + return 1; +fail: + return 0; +} + +/*******************************************************************************/ + +/* fixed-point ilog from Xiph's Tremor */ +static int ww2ogg_tremor_ilog(unsigned int v) { + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +/* quantvals-something from Xiph's Tremor */ +static unsigned int ww2ogg_tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions) { + /* get us a starting hint, we'll polish it below */ + int bits=ww2ogg_tremor_ilog(entries); + int vals=entries>>((bits-1)*(dimensions-1)/dimensions); + + while(1){ + unsigned long acc=1; + unsigned long acc1=1; + unsigned int i; + for(i=0;ientries){ + return(vals); + }else{ + if(acc>entries){ + vals--; + }else{ + vals++; + } + } + } +} + + +/* copies Vorbis codebooks (untouched, but size uncertain) */ +static int ww2ogg_codebook_library_copy(bitstream_t* ow, bitstream_t* iw) { + int i; + uint32_t id = 0, dimensions = 0, entries = 0; + uint32_t ordered = 0, lookup_type = 0; + + rv_bits(iw, 24,&id); + wv_bits(ow, 24, id); + rv_bits(iw, 16,&dimensions); + wv_bits(ow, 16, dimensions); + rv_bits(iw, 24,&entries); + wv_bits(ow, 24, entries); + + if (0x564342 != id) { /* "VCB" */ + VGM_LOG("Wwise Vorbis: invalid codebook identifier\n"); + goto fail; + } + + /* codeword lengths */ + rv_bits(iw, 1,&ordered); + wv_bits(ow, 1, ordered); + if (ordered) { + uint32_t initial_length = 0, current_entry = 0; + + rv_bits(iw, 5,&initial_length); + wv_bits(ow, 5, initial_length); + + current_entry = 0; + while (current_entry < entries) { + uint32_t number = 0; + int numberv_bits = ww2ogg_tremor_ilog(entries-current_entry); + + rv_bits(iw, numberv_bits,&number); + wv_bits(ow, numberv_bits, number); + current_entry += number; + } + if (current_entry > entries) { + VGM_LOG("Wwise Vorbis: current_entry out of range\n"); + goto fail; + } + } + else { + uint32_t sparse = 0; + + rv_bits(iw, 1,&sparse); + wv_bits(ow, 1, sparse); + + for (i = 0; i < entries; i++) { + uint32_t present_bool = 0; + + present_bool = 1; + if (sparse) { + uint32_t present = 0; + + rv_bits(iw, 1,&present); + wv_bits(ow, 1, present); + + present_bool = (0 != present); + } + + if (present_bool) { + uint32_t codeword_length = 0; + + rv_bits(iw, 5,&codeword_length); + wv_bits(ow, 5, codeword_length); + } + } + } + + + /* lookup table */ + rv_bits(iw, 4,&lookup_type); + wv_bits(ow, 4, lookup_type); + + if (0 == lookup_type) { + //VGM_LOG("Wwise Vorbis: no lookup table\n"); + } + else if (1 == lookup_type) { + //VGM_LOG("Wwise Vorbis: lookup type 1\n"); + uint32_t quantvals = 0, min = 0, max = 0; + uint32_t value_length = 0, sequence_flag = 0; + + rv_bits(iw, 32,&min); + wv_bits(ow, 32, min); + rv_bits(iw, 32,&max); + wv_bits(ow, 32, max); + rv_bits(iw, 4,&value_length); + wv_bits(ow, 4, value_length); + rv_bits(iw, 1,&sequence_flag); + wv_bits(ow, 1, sequence_flag); + + quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions); + for (i = 0; i < quantvals; i++) { + uint32_t val = 0, val_bits = 0; + val_bits = value_length+1; + + rv_bits(iw, val_bits,&val); + wv_bits(ow, val_bits, val); + } + } + else if (2 == lookup_type) { + VGM_LOG("Wwise Vorbis: didn't expect lookup type 2\n"); + goto fail; + } + else { + VGM_LOG("Wwise Vorbis: invalid lookup type\n"); + goto fail; + } + + return 1; +fail: + return 0; +} + +/* rebuilds a Wwise codebook into a Vorbis codebook */ +static int ww2ogg_codebook_library_rebuild(bitstream_t* ow, bitstream_t* iw, size_t cb_size, STREAMFILE* sf) { + int i; + uint32_t id = 0, dimensions = 0, entries = 0; + uint32_t ordered = 0, lookup_type = 0; + + id = 0x564342; /* "VCB" */ + + wv_bits(ow, 24, id); + rv_bits(iw, 4,&dimensions); + wv_bits(ow, 16, dimensions); /* 4 to 16 */ + rv_bits(iw, 14,&entries); + wv_bits(ow, 24, entries); /* 14 to 24*/ + + /* codeword lengths */ + rv_bits(iw, 1,&ordered); + wv_bits(ow, 1, ordered); + if (ordered) { + uint32_t initial_length = 0, current_entry = 0; + + rv_bits(iw, 5,&initial_length); + wv_bits(ow, 5, initial_length); + + current_entry = 0; + while (current_entry < entries) { + uint32_t number = 0; + int numberv_bits = ww2ogg_tremor_ilog(entries-current_entry); + + rv_bits(iw, numberv_bits,&number); + wv_bits(ow, numberv_bits, number); + current_entry += number; + } + if (current_entry > entries) { + VGM_LOG("Wwise Vorbis: current_entry out of range\n"); + goto fail; + } + } + else { + uint32_t codeword_length_length = 0, sparse = 0; + + rv_bits(iw, 3,&codeword_length_length); + rv_bits(iw, 1,&sparse); + wv_bits(ow, 1, sparse); + + if (0 == codeword_length_length || 5 < codeword_length_length) { + VGM_LOG("Wwise Vorbis: nonsense codeword length\n"); + goto fail; + } + + for (i = 0; i < entries; i++) { + uint32_t present_bool = 0; + + present_bool = 1; + if (sparse) { + uint32_t present = 0; + + rv_bits(iw, 1,&present); + wv_bits(ow, 1, present); + + present_bool = (0 != present); + } + + if (present_bool) { + uint32_t codeword_length = 0; + + rv_bits(iw, codeword_length_length,&codeword_length); + wv_bits(ow, 5, codeword_length); /* max 7 (3b) to 5 */ + } + } + } + + + /* lookup table */ + rv_bits(iw, 1,&lookup_type); + wv_bits(ow, 4, lookup_type); /* 1 to 4 */ + + if (0 == lookup_type) { + //VGM_LOG("Wwise Vorbis: no lookup table\n"); + } + else if (1 == lookup_type) { + //VGM_LOG("Wwise Vorbis: lookup type 1\n"); + uint32_t quantvals = 0, min = 0, max = 0; + uint32_t value_length = 0, sequence_flag = 0; + + rv_bits(iw, 32,&min); + wv_bits(ow, 32, min); + rv_bits(iw, 32,&max); + wv_bits(ow, 32, max); + rv_bits(iw, 4,&value_length); + wv_bits(ow, 4, value_length); + rv_bits(iw, 1,&sequence_flag); + wv_bits(ow, 1, sequence_flag); + + quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions); + for (i = 0; i < quantvals; i++) { + uint32_t val = 0, val_bits = 0; + val_bits = value_length+1; + + rv_bits(iw, val_bits,&val); + wv_bits(ow, val_bits, val); + } + } + else if (2 == lookup_type) { + VGM_LOG("Wwise Vorbis: didn't expect lookup type 2\n"); + goto fail; + } + else { + VGM_LOG("Wwise Vorbis: invalid lookup type\n"); + goto fail; + } + + + /* check that we used exactly all bytes */ + /* note: if all bits are used in the last byte there will be one extra 0 byte */ + if ( 0 != cb_size && iw->b_off/8+1 != cb_size ) { + //VGM_LOG("Wwise Vorbis: codebook size mistach (expected 0x%x, wrote 0x%lx)\n", cb_size, iw->b_off/8+1); + goto fail; + } + + return 1; +fail: + return 0; +} + +/* rebuilds an external Wwise codebook referenced by id to a Vorbis codebook */ +static int ww2ogg_codebook_library_rebuild_by_id(bitstream_t* ow, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE* sf) { + size_t ibufsize = 0x8000; /* arbitrary max size of a codebook */ + uint8_t ibuf[0x8000]; /* Wwise codebook buffer */ + size_t cb_size; + bitstream_t iw; + + cb_size = load_wvc(ibuf,ibufsize, codebook_id, setup_type, sf); + if (cb_size == 0) goto fail; + + init_bitstream(&iw, ibuf, ibufsize); + + return ww2ogg_codebook_library_rebuild(ow, &iw, cb_size, sf); +fail: + return 0; +} + +/* Rebuild a Wwise setup (simplified with removed stuff), recreating all six setup parts. + * (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-650004.2.4) */ +static int ww2ogg_generate_vorbis_setup(bitstream_t* ow, bitstream_t* iw, vorbis_custom_codec_data* data, size_t packet_size, STREAMFILE* sf) { + int i, j, k; + int channels = data->config.channels; + uint32_t codebook_count = 0, floor_count = 0, residue_count = 0; + uint32_t codebook_count_less1 = 0; + uint32_t time_count_less1 = 0, dummy_time_value = 0; + + + /* packet header */ + put_8bit(ow->buf+0x00, 0x05); /* packet_type (setup) */ + memcpy (ow->buf+0x01, "vorbis", 6); /* id */ + ow->b_off += (1+6) * 8; /* bit offset of output (Vorbis) setup, after fake type + id */ + + + /* Codebooks */ + rv_bits(iw, 8,&codebook_count_less1); + wv_bits(ow, 8, codebook_count_less1); + codebook_count = codebook_count_less1 + 1; + + if (data->config.setup_type == WWV_FULL_SETUP) { + /* rebuild Wwise codebooks: untouched */ + for (i = 0; i < codebook_count; i++) { + if (!ww2ogg_codebook_library_copy(ow, iw)) + goto fail; + } + } + else if (data->config.setup_type == WWV_INLINE_CODEBOOKS) { + /* rebuild Wwise codebooks: inline in simplified format */ + for (i = 0; i < codebook_count; i++) { + if (!ww2ogg_codebook_library_rebuild(ow, iw, 0, sf)) + goto fail; + } + } + else { + /* rebuild Wwise codebooks: external (referenced by id) in simplified format */ + for (i = 0; i < codebook_count; i++) { + int rc; + uint32_t codebook_id = 0; + + rv_bits(iw, 10,&codebook_id); + + rc = ww2ogg_codebook_library_rebuild_by_id(ow, codebook_id, data->config.setup_type, sf); + if (!rc) goto fail; + } + } + + + /* Time domain transforms */ + time_count_less1 = 0; + wv_bits(ow, 6, time_count_less1); + dummy_time_value = 0; + wv_bits(ow, 16, dummy_time_value); + + + if (data->config.setup_type == WWV_FULL_SETUP) { + /* rest of setup is untouched, copy bits */ + uint32_t bitly = 0; + uint32_t total_bits_read = iw->b_off; + uint32_t setup_packet_size_bits = packet_size * 8; + + while (total_bits_read < setup_packet_size_bits) { + rv_bits(iw, 1,&bitly); + wv_bits(ow, 1, bitly); + total_bits_read = iw->b_off; + } + } + else { + /* rest of setup is altered, reconstruct */ + uint32_t floor_count_less1 = 0, floor1_multiplier_less1 = 0, rangebits = 0; + uint32_t residue_count_less1 = 0; + uint32_t mapping_count_less1 = 0, mapping_count = 0; + uint32_t mode_count_less1 = 0, mode_count = 0; + + + /* Floors */ + rv_bits(iw, 6,&floor_count_less1); + wv_bits(ow, 6, floor_count_less1); + floor_count = floor_count_less1 + 1; + + for (i = 0; i < floor_count; i++) { + uint32_t floor_type = 0, floor1_partitions = 0; + uint32_t maximum_class = 0; + uint32_t floor1_partition_class_list[32]; /* max 5b */ + uint32_t floor1_class_dimensions_list[16+1]; /* max 4b+1 */ + + // Always floor type 1 + floor_type = 1; + wv_bits(ow, 16, floor_type); + + rv_bits(iw, 5,&floor1_partitions); + wv_bits(ow, 5, floor1_partitions); + + memset(floor1_partition_class_list, 0, sizeof(uint32_t)*32); + + maximum_class = 0; + for (j = 0; j < floor1_partitions; j++) { + uint32_t floor1_partition_class = 0; + + rv_bits(iw, 4,&floor1_partition_class); + wv_bits(ow, 4, floor1_partition_class); + + floor1_partition_class_list[j] = floor1_partition_class; + + if (floor1_partition_class > maximum_class) + maximum_class = floor1_partition_class; + } + + memset(floor1_class_dimensions_list, 0, sizeof(uint32_t)*(16+1)); + + for (j = 0; j <= maximum_class; j++) { + uint32_t class_dimensions_less1 = 0, class_subclasses = 0; + + rv_bits(iw, 3,&class_dimensions_less1); + wv_bits(ow, 3, class_dimensions_less1); + + floor1_class_dimensions_list[j] = class_dimensions_less1 + 1; + + rv_bits(iw, 2,&class_subclasses); + wv_bits(ow, 2, class_subclasses); + + if (0 != class_subclasses) { + uint32_t masterbook = 0; + + rv_bits(iw, 8,&masterbook); + wv_bits(ow, 8, masterbook); + + if (masterbook >= codebook_count) { + VGM_LOG("Wwise Vorbis: invalid floor1 masterbook\n"); + goto fail; + } + } + + for (k = 0; k < (1U<= 0 && subclass_book >= codebook_count) { + VGM_LOG("Wwise Vorbis: invalid floor1 subclass book\n"); + goto fail; + } + } + } + + rv_bits(iw, 2,&floor1_multiplier_less1); + wv_bits(ow, 2, floor1_multiplier_less1); + + rv_bits(iw, 4,&rangebits); + wv_bits(ow, 4, rangebits); + + for (j = 0; j < floor1_partitions; j++) { + uint32_t current_class_number = 0; + + current_class_number = floor1_partition_class_list[j]; + for (k = 0; k < floor1_class_dimensions_list[current_class_number]; k++) { + uint32_t X = 0; /* max 4b (15) */ + + rv_bits(iw, rangebits,&X); + wv_bits(ow, rangebits, X); + } + } + } + + + /* Residues */ + rv_bits(iw, 6,&residue_count_less1); + wv_bits(ow, 6, residue_count_less1); + residue_count = residue_count_less1 + 1; + + for (i = 0; i < residue_count; i++) { + uint32_t residue_type = 0, residue_classifications = 0; + uint32_t residue_begin = 0, residue_end = 0, residue_partition_size_less1 = 0, residue_classifications_less1 = 0, residue_classbook = 0; + uint32_t residue_cascade[64+1]; /* 6b +1 */ + + rv_bits(iw, 2,&residue_type); + wv_bits(ow, 16, residue_type); /* 2b to 16b */ + + if (residue_type > 2) { + VGM_LOG("Wwise Vorbis: invalid residue type\n"); + goto fail; + } + + rv_bits(iw, 24,&residue_begin); + wv_bits(ow, 24, residue_begin); + rv_bits(iw, 24,&residue_end); + wv_bits(ow, 24, residue_end); + rv_bits(iw, 24,&residue_partition_size_less1); + wv_bits(ow, 24, residue_partition_size_less1); + rv_bits(iw, 6,&residue_classifications_less1); + wv_bits(ow, 6, residue_classifications_less1); + rv_bits(iw, 8,&residue_classbook); + wv_bits(ow, 8, residue_classbook); + residue_classifications = residue_classifications_less1 + 1; + + if (residue_classbook >= codebook_count) { + VGM_LOG("Wwise Vorbis: invalid residue classbook\n"); + goto fail; + } + + memset(residue_cascade, 0, sizeof(uint32_t)*(64+1)); + + for (j = 0; j < residue_classifications; j++) { + uint32_t high_bits = 0, lowv_bits = 0, bitflag = 0; + + high_bits = 0; + + rv_bits(iw, 3,&lowv_bits); + wv_bits(ow, 3, lowv_bits); + + rv_bits(iw, 1,&bitflag); + wv_bits(ow, 1, bitflag); + if (bitflag) { + rv_bits(iw, 5,&high_bits); + wv_bits(ow, 5, high_bits); + } + + residue_cascade[j] = high_bits * 8 + lowv_bits; + } + + for (j = 0; j < residue_classifications; j++) { + for (k = 0; k < 8; k++) { + if (residue_cascade[j] & (1 << k)) { + uint32_t residue_book = 0; + + rv_bits(iw, 8,&residue_book); + wv_bits(ow, 8, residue_book); + + if (residue_book >= codebook_count) { + VGM_LOG("Wwise Vorbis: invalid residue book\n"); + goto fail; + } + } + } + } + } + + + /* Mappings */ + rv_bits(iw, 6,&mapping_count_less1); + wv_bits(ow, 6, mapping_count_less1); + mapping_count = mapping_count_less1 + 1; + + for (i = 0; i < mapping_count; i++) { + uint32_t mapping_type = 0, submaps_flag = 0, submaps = 0, square_polar_flag = 0; + uint32_t mapping_reserved = 0; + + // always mapping type 0, the only one + mapping_type = 0; + wv_bits(ow, 16, mapping_type); + + rv_bits(iw, 1,&submaps_flag); + wv_bits(ow, 1, submaps_flag); + + submaps = 1; + if (submaps_flag) { + uint32_t submaps_less1 = 0; + + rv_bits(iw, 4,&submaps_less1); + wv_bits(ow, 4, submaps_less1); + submaps = submaps_less1 + 1; + } + + rv_bits(iw, 1,&square_polar_flag); + wv_bits(ow, 1, square_polar_flag); + + if (square_polar_flag) { + uint32_t coupling_steps_less1 = 0, coupling_steps = 0; + + rv_bits(iw, 8,&coupling_steps_less1); + wv_bits(ow, 8, coupling_steps_less1); + coupling_steps = coupling_steps_less1 + 1; + + for (j = 0; j < coupling_steps; j++) { + uint32_t magnitude = 0, angle = 0; + int magnitude_bits = ww2ogg_tremor_ilog(channels-1); + int angle_bits = ww2ogg_tremor_ilog(channels-1); + + rv_bits(iw, magnitude_bits,&magnitude); + wv_bits(ow, magnitude_bits, magnitude); + rv_bits(iw, angle_bits,&angle); + wv_bits(ow, angle_bits, angle); + + if (angle == magnitude || magnitude >= channels || angle >= channels) { + VGM_LOG("Wwise Vorbis: invalid coupling (angle=%i, mag=%i, ch=%i)\n", angle, magnitude,channels); + goto fail; + } + } + } + + // a rare reserved field not removed by Ak! + rv_bits(iw, 2,&mapping_reserved); + wv_bits(ow, 2, mapping_reserved); + if (0 != mapping_reserved) { + VGM_LOG("Wwise Vorbis: mapping reserved field nonzero\n"); + goto fail; + } + + if (submaps > 1) { + for (j = 0; j < channels; j++) { + uint32_t mapping_mux = 0; + + rv_bits(iw, 4,&mapping_mux); + wv_bits(ow, 4, mapping_mux); + if (mapping_mux >= submaps) { + VGM_LOG("Wwise Vorbis: mapping_mux >= submaps\n"); + goto fail; + } + } + } + + for (j = 0; j < submaps; j++) { + uint32_t time_config = 0, floor_number = 0, residue_number = 0; + + // Another! Unused time domain transform configuration placeholder! + rv_bits(iw, 8,&time_config); + wv_bits(ow, 8, time_config); + + rv_bits(iw, 8,&floor_number); + wv_bits(ow, 8, floor_number); + if (floor_number >= floor_count) { + VGM_LOG("Wwise Vorbis: invalid floor mapping\n"); + goto fail; + } + + rv_bits(iw, 8,&residue_number); + wv_bits(ow, 8, residue_number); + if (residue_number >= residue_count) { + VGM_LOG("Wwise Vorbis: invalid residue mapping\n"); + goto fail; + } + } + } + + + /* Modes */ + rv_bits(iw, 6,&mode_count_less1); + wv_bits(ow, 6, mode_count_less1); + mode_count = mode_count_less1 + 1; + + memset(data->mode_blockflag, 0, sizeof(uint8_t)*(64+1)); /* up to max mode_count */ + data->mode_bits = ww2ogg_tremor_ilog(mode_count-1); /* for mod_packets */ + + for (i = 0; i < mode_count; i++) { + uint32_t block_flag = 0, windowtype = 0, transformtype = 0, mapping = 0; + + rv_bits(iw, 1,&block_flag); + wv_bits(ow, 1, block_flag); + + data->mode_blockflag[i] = (block_flag != 0); /* for mod_packets */ + + windowtype = 0; + transformtype = 0; + wv_bits(ow, 16, windowtype); + wv_bits(ow, 16, transformtype); + + rv_bits(iw, 8,&mapping); + wv_bits(ow, 8, mapping); + if (mapping >= mapping_count) { + VGM_LOG("Wwise Vorbis: invalid mode mapping\n"); + goto fail; + } + } + } + + + /* end flag */ + { + uint32_t framing = 0; + + framing = 1; + wv_bits(ow, 1, framing); + } + + /* remove trailing garbage bits */ + if (ow->b_off % 8 != 0) { + uint32_t padding = 0; + int padding_bits = 8 - (ow->b_off % 8); + + wv_bits(ow, padding_bits, padding); + } + + + return 1; +fail: + return 0; +} + + +/* **************************************************************************** */ +/* INTERNAL UTILS */ +/* **************************************************************************** */ + +/* loads an external Wwise Vorbis Codebooks file (wvc) referenced by ID and returns size */ +static int load_wvc(uint8_t* ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE* sf) { + size_t bytes; + + /* try to locate from the precompiled list */ + bytes = load_wvc_array(ibuf, ibufsize, codebook_id, setup_type); + if (bytes) + return bytes; + + /* try to load from external file (ignoring type, just use file if found) */ + bytes = load_wvc_file(ibuf, ibufsize, codebook_id, sf); + if (bytes) + return bytes; + + /* not found */ + VGM_LOG("Wwise Vorbis: codebook_id %04x not found\n", codebook_id); + return 0; +} + +static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STREAMFILE* sf) { + STREAMFILE* sfWvc = NULL; + size_t wvc_size = 0; + + { + char setupname[PATH_LIMIT]; + char pathname[PATH_LIMIT]; + char *path; + + /* read "(dir/).wvc" */ + sf->get_name(sf,pathname,sizeof(pathname)); + path = strrchr(pathname,DIR_SEPARATOR); + if (path) + *(path+1) = '\0'; + else + pathname[0] = '\0'; + + snprintf(setupname,PATH_LIMIT,"%s.wvc", pathname); + sfWvc = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!sfWvc) goto fail; + + wvc_size = sfWvc->get_size(sfWvc); + } + + /* find codebook and copy to buffer */ + { + off_t table_start, codebook_offset; + size_t codebook_size; + int codebook_count; + + /* at the end of the WVC is an offset table, and we need to find codebook id (number) offset */ + table_start = read_u32le(wvc_size - 4, sfWvc); /* last offset */ + codebook_count = ((wvc_size - table_start) / 4) - 1; + if (table_start > wvc_size || codebook_id >= codebook_count) goto fail; + + codebook_offset = read_u32le(table_start + codebook_id*4, sfWvc); + codebook_size = read_u32le(table_start + codebook_id*4 + 4, sfWvc) - codebook_offset; + if (codebook_size > bufsize) goto fail; + + if (read_streamfile(buf, codebook_offset, codebook_size, sfWvc) != codebook_size) + goto fail; + sfWvc->close(sfWvc); + + return codebook_size; + } + + +fail: + if (sfWvc) sfWvc->close(sfWvc); + return 0; +} + +static int load_wvc_array(uint8_t* buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type) { +#if WWISE_VORBIS_USE_PRECOMPILED_WVC + + /* get pointer to array */ + { + int i, list_length; + const wvc_info * wvc_list; + + switch (setup_type) { + case WWV_EXTERNAL_CODEBOOKS: + wvc_list = wvc_list_standard; + list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); + break; + case WWV_AOTUV603_CODEBOOKS: + wvc_list = wvc_list_aotuv603; + list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); + break; + default: + goto fail; + } + + for (i=0; i < list_length; i++) { + if (wvc_list[i].id == codebook_id) { + if (wvc_list[i].size > bufsize) goto fail; + /* found: copy data as-is */ + memcpy(buf,wvc_list[i].codebook, wvc_list[i].size); + return wvc_list[i].size; + } + } + } + + // this can be used if the lists contained a 1:1 dump of the codebook files +#if 0 + if (wvc == NULL) goto fail; + /* find codebook and copy to buffer */ + { + off_t table_start, codebook_offset; + size_t codebook_size; + int codebook_count; + + /* at the end of the WVC is an offset table, and we need to find codebook id (number) offset */ + table_start = get_32bitLE(wvc + wvc_size - 4); /* last offset */ + codebook_count = ((wvc_size - table_start) / 4) - 1; + if (codebook_id >= codebook_count) goto fail; + + codebook_offset = get_32bitLE(wvc + table_start + codebook_id*4); + codebook_size = get_32bitLE(wvc + table_start + codebook_id*4 + 4) - codebook_offset; + if (codebook_size > bufsize) goto fail; + + memcpy(buf, wvc+codebook_offset, codebook_size); + + return codebook_size; + } +#endif + +fail: +#endif + return 0; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/decode.c b/Frameworks/vgmstream/vgmstream/src/decode.c new file mode 100644 index 000000000..e74ec0f10 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/decode.c @@ -0,0 +1,1481 @@ +#include "vgmstream.h" +#include "decode.h" +#include "layout/layout.h" +#include "coding/coding.h" +#include "mixing.h" +#include "plugins.h" + +/* custom codec handling, not exactly "decode" stuff but here to simplify adding new codecs */ + + +void free_codec(VGMSTREAM* vgmstream) { + +#ifdef VGM_USE_VORBIS + if (vgmstream->coding_type == coding_OGG_VORBIS) { + free_ogg_vorbis(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_VORBIS_custom) { + free_vorbis_custom(vgmstream->codec_data); + } +#endif + + if (vgmstream->coding_type == coding_CIRCUS_VQ) { + free_circus_vq(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_RELIC) { + free_relic(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_CRI_HCA) { + free_hca(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_UBI_ADPCM) { + free_ubi_adpcm(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_IMUSE) { + free_imuse(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_EA_MT) { + free_ea_mt(vgmstream->codec_data, vgmstream->channels); + } + +#ifdef VGM_USE_FFMPEG + if (vgmstream->coding_type == coding_FFmpeg) { + free_ffmpeg(vgmstream->codec_data); + } +#endif + +#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) + if (vgmstream->coding_type == coding_MP4_AAC) { + free_mp4_aac(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_MPEG + if (vgmstream->coding_type == coding_MPEG_custom || + vgmstream->coding_type == coding_MPEG_ealayer3 || + vgmstream->coding_type == coding_MPEG_layer1 || + vgmstream->coding_type == coding_MPEG_layer2 || + vgmstream->coding_type == coding_MPEG_layer3) { + free_mpeg(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_G7221 + if (vgmstream->coding_type == coding_G7221C) { + free_g7221(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_G719 + if (vgmstream->coding_type == coding_G719) { + free_g719(vgmstream->codec_data, vgmstream->channels); + } +#endif + +#ifdef VGM_USE_MAIATRAC3PLUS + if (vgmstream->coding_type == coding_AT3plus) { + free_at3plus(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_ATRAC9 + if (vgmstream->coding_type == coding_ATRAC9) { + free_atrac9(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_CELT + if (vgmstream->coding_type == coding_CELT_FSB) { + free_celt_fsb(vgmstream->codec_data); + } +#endif + + if (vgmstream->coding_type == coding_ACM) { + free_acm(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_NWA) { + free_nwa(vgmstream->codec_data); + } +} + + +void seek_codec(VGMSTREAM* vgmstream) { + if (vgmstream->coding_type == coding_CIRCUS_VQ) { + seek_circus_vq(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_RELIC) { + seek_relic(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_CRI_HCA) { + loop_hca(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_UBI_ADPCM) { + seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_IMUSE) { + seek_imuse(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_EA_MT) { + seek_ea_mt(vgmstream, vgmstream->loop_current_sample); + } + +#ifdef VGM_USE_VORBIS + if (vgmstream->coding_type == coding_OGG_VORBIS) { + seek_ogg_vorbis(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_VORBIS_custom) { + seek_vorbis_custom(vgmstream, vgmstream->loop_current_sample); + } +#endif + +#ifdef VGM_USE_FFMPEG + if (vgmstream->coding_type == coding_FFmpeg) { + seek_ffmpeg(vgmstream->codec_data, vgmstream->loop_current_sample); + } +#endif + +#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) + if (vgmstream->coding_type == coding_MP4_AAC) { + seek_mp4_aac(vgmstream, vgmstream->loop_sample); + } +#endif + +#ifdef VGM_USE_MAIATRAC3PLUS + if (vgmstream->coding_type == coding_AT3plus) { + seek_at3plus(vgmstream, vgmstream->loop_current_sample); + } +#endif + +#ifdef VGM_USE_ATRAC9 + if (vgmstream->coding_type == coding_ATRAC9) { + seek_atrac9(vgmstream, vgmstream->loop_current_sample); + } +#endif + +#ifdef VGM_USE_CELT + if (vgmstream->coding_type == coding_CELT_FSB) { + seek_celt_fsb(vgmstream, vgmstream->loop_current_sample); + } +#endif + +#ifdef VGM_USE_MPEG + if (vgmstream->coding_type == coding_MPEG_custom || + vgmstream->coding_type == coding_MPEG_ealayer3 || + vgmstream->coding_type == coding_MPEG_layer1 || + vgmstream->coding_type == coding_MPEG_layer2 || + vgmstream->coding_type == coding_MPEG_layer3) { + seek_mpeg(vgmstream, vgmstream->loop_current_sample); + } +#endif + + if (vgmstream->coding_type == coding_NWA) { + seek_nwa(vgmstream->codec_data, vgmstream->loop_current_sample); + } +} + + +void reset_codec(VGMSTREAM* vgmstream) { + +#ifdef VGM_USE_VORBIS + if (vgmstream->coding_type == coding_OGG_VORBIS) { + reset_ogg_vorbis(vgmstream); + } + + if (vgmstream->coding_type == coding_VORBIS_custom) { + reset_vorbis_custom(vgmstream); + } +#endif + + if (vgmstream->coding_type == coding_CIRCUS_VQ) { + reset_circus_vq(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_RELIC) { + reset_relic(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_CRI_HCA) { + reset_hca(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_UBI_ADPCM) { + reset_ubi_adpcm(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_IMUSE) { + reset_imuse(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_EA_MT) { + reset_ea_mt(vgmstream); + } + +#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) + if (vgmstream->coding_type == coding_MP4_AAC) { + reset_mp4_aac(vgmstream); + } +#endif + +#ifdef VGM_USE_MPEG + if (vgmstream->coding_type == coding_MPEG_custom || + vgmstream->coding_type == coding_MPEG_ealayer3 || + vgmstream->coding_type == coding_MPEG_layer1 || + vgmstream->coding_type == coding_MPEG_layer2 || + vgmstream->coding_type == coding_MPEG_layer3) { + reset_mpeg(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_G7221 + if (vgmstream->coding_type == coding_G7221C) { + reset_g7221(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_G719 + if (vgmstream->coding_type == coding_G719) { + reset_g719(vgmstream->codec_data, vgmstream->channels); + } +#endif + +#ifdef VGM_USE_MAIATRAC3PLUS + if (vgmstream->coding_type == coding_AT3plus) { + reset_at3plus(vgmstream); + } +#endif + +#ifdef VGM_USE_ATRAC9 + if (vgmstream->coding_type == coding_ATRAC9) { + reset_atrac9(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_CELT + if (vgmstream->coding_type == coding_CELT_FSB) { + reset_celt_fsb(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_FFMPEG + if (vgmstream->coding_type == coding_FFmpeg) { + reset_ffmpeg(vgmstream->codec_data); + } +#endif + + if (vgmstream->coding_type == coding_ACM) { + reset_acm(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_NWA) { + reset_nwa(vgmstream->codec_data); + } +} + + +/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */ +int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { + /* Value returned here is the max (or less) that vgmstream will ask a decoder per + * "decode_x" call. Decoders with variable samples per frame or internal discard + * may return 0 here and handle arbitrary samples_to_do values internally + * (or some internal sample buffer max too). */ + + switch (vgmstream->coding_type) { + case coding_SILENCE: + return 0; + + case coding_CRI_ADX: + case coding_CRI_ADX_fixed: + case coding_CRI_ADX_exp: + case coding_CRI_ADX_enc_8: + case coding_CRI_ADX_enc_9: + return (vgmstream->interleave_block_size - 2) * 2; + + case coding_NGC_DSP: + case coding_NGC_DSP_subint: + return 14; + case coding_NGC_AFC: + case coding_VADPCM: + return 16; + case coding_NGC_DTK: + return 28; + case coding_G721: + return 1; + + case coding_PCM16LE: + case coding_PCM16BE: + case coding_PCM16_int: + case coding_PCM8: + case coding_PCM8_int: + case coding_PCM8_U: + case coding_PCM8_U_int: + case coding_PCM8_SB: + case coding_ULAW: + case coding_ULAW_int: + case coding_ALAW: + case coding_PCMFLOAT: + return 1; +#ifdef VGM_USE_VORBIS + case coding_OGG_VORBIS: + case coding_VORBIS_custom: +#endif +#ifdef VGM_USE_MPEG + case coding_MPEG_custom: + case coding_MPEG_ealayer3: + case coding_MPEG_layer1: + case coding_MPEG_layer2: + case coding_MPEG_layer3: +#endif + case coding_SDX2: + case coding_SDX2_int: + case coding_CBD2: + case coding_ACM: + case coding_DERF: + case coding_NWA: + case coding_SASSC: + case coding_CIRCUS_ADPCM: + return 1; + + case coding_IMA: + case coding_DVI_IMA: + case coding_SNDS_IMA: + case coding_OTNS_IMA: + case coding_UBI_IMA: + case coding_OKI16: + case coding_OKI4S: + case coding_MTF_IMA: + return 1; + case coding_PCM4: + case coding_PCM4_U: + case coding_IMA_int: + case coding_DVI_IMA_int: + case coding_3DS_IMA: + case coding_WV6_IMA: + case coding_ALP_IMA: + case coding_FFTA2_IMA: + case coding_BLITZ_IMA: + case coding_PCFX: + return 2; + case coding_XBOX_IMA: + case coding_XBOX_IMA_mch: + case coding_XBOX_IMA_int: + case coding_FSB_IMA: + case coding_WWISE_IMA: + case coding_CD_IMA: + return 64; + case coding_APPLE_IMA4: + return 64; + case coding_MS_IMA: + case coding_REF_IMA: + return ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; + case coding_RAD_IMA: + return (vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels; + case coding_NDS_IMA: + case coding_DAT4_IMA: + return (vgmstream->interleave_block_size - 0x04) * 2; + case coding_AWC_IMA: + return (0x800 - 0x04) * 2; + case coding_RAD_IMA_mono: + return 32; + case coding_H4M_IMA: + return 0; /* variable (block-controlled) */ + + case coding_XA: + return 28*8 / vgmstream->channels; /* 8 subframes per frame, mono/stereo */ + case coding_PSX: + case coding_PSX_badflags: + case coding_HEVAG: + return 28; + case coding_PSX_cfg: + case coding_PSX_pivotal: + return (vgmstream->interleave_block_size - 0x01) * 2; /* size 0x01 header */ + + case coding_EA_XA: + case coding_EA_XA_int: + case coding_EA_XA_V2: + case coding_MAXIS_XA: + return 28; + case coding_EA_XAS_V0: + return 32; + case coding_EA_XAS_V1: + return 128; + + case coding_MSADPCM: + return (vgmstream->frame_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; + case coding_MSADPCM_int: + case coding_MSADPCM_ck: + return (vgmstream->frame_size - 0x07)*2 + 2; + case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ + return vgmstream->ws_output_size; + case coding_AICA: + return 1; + case coding_AICA_int: + return 2; + case coding_ASKA: + return (0x40-0x04*vgmstream->channels) * 2 / vgmstream->channels; + case coding_NXAP: + return (0x40-0x04) * 2; + case coding_NDS_PROCYON: + return 30; + case coding_L5_555: + return 32; + case coding_LSF: + return 54; + +#ifdef VGM_USE_G7221 + case coding_G7221C: + return 32000/50; /* Siren7: 16000/50 */ +#endif +#ifdef VGM_USE_G719 + case coding_G719: + return 48000/50; +#endif +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: + return 0; +#endif + case coding_MTAF: + return 128*2; + case coding_MTA2: + return 128*2; + case coding_MC3: + return 10; + case coding_FADPCM: + return 256; /* (0x8c - 0xc) * 2 */ + case coding_ASF: + return 32; /* (0x11 - 0x1) * 2 */ + case coding_DSA: + return 14; /* (0x08 - 0x1) * 2 */ + case coding_XMD: + return (vgmstream->interleave_block_size - 0x06)*2 + 2; + case coding_PTADPCM: + return (vgmstream->interleave_block_size - 0x05)*2 + 2; + case coding_UBI_ADPCM: + return 0; /* varies per mode */ + case coding_IMUSE: + return 0; /* varies per frame */ + case coding_EA_MT: + return 0; /* 432, but variable in looped files */ + case coding_CIRCUS_VQ: + return 0; + case coding_RELIC: + return 0; /* 512 */ + case coding_CRI_HCA: + return 0; /* 1024 - delay/padding (which can be bigger than 1024) */ +#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) + case coding_MP4_AAC: + return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame; +#endif +#ifdef VGM_USE_MAIATRAC3PLUS + case coding_AT3plus: + return 2048 - ((maiatrac3plus_codec_data*)vgmstream->codec_data)->samples_discard; +#endif +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: + return 0; /* varies with config data, usually 256 or 1024 */ +#endif +#ifdef VGM_USE_CELT + case coding_CELT_FSB: + return 0; /* 512? */ +#endif + default: + return 0; + } +} + +/* Get the number of bytes of a single frame (smallest self-contained byte group, 1/N channels) */ +int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { + switch (vgmstream->coding_type) { + case coding_SILENCE: + return 0; + + case coding_CRI_ADX: + case coding_CRI_ADX_fixed: + case coding_CRI_ADX_exp: + case coding_CRI_ADX_enc_8: + case coding_CRI_ADX_enc_9: + return vgmstream->interleave_block_size; + + case coding_NGC_DSP: + return 0x08; + case coding_NGC_DSP_subint: + return 0x08 * vgmstream->channels; + case coding_NGC_AFC: + case coding_VADPCM: + return 0x09; + case coding_NGC_DTK: + return 0x20; + case coding_G721: + return 0; + + case coding_PCM16LE: + case coding_PCM16BE: + case coding_PCM16_int: + return 0x02; + case coding_PCM8: + case coding_PCM8_int: + case coding_PCM8_U: + case coding_PCM8_U_int: + case coding_PCM8_SB: + case coding_ULAW: + case coding_ULAW_int: + case coding_ALAW: + return 0x01; + case coding_PCMFLOAT: + return 0x04; + + case coding_SDX2: + case coding_SDX2_int: + case coding_CBD2: + case coding_DERF: + case coding_NWA: + case coding_SASSC: + case coding_CIRCUS_ADPCM: + return 0x01; + + case coding_PCM4: + case coding_PCM4_U: + case coding_IMA: + case coding_IMA_int: + case coding_DVI_IMA: + case coding_DVI_IMA_int: + case coding_3DS_IMA: + case coding_WV6_IMA: + case coding_ALP_IMA: + case coding_FFTA2_IMA: + case coding_BLITZ_IMA: + case coding_PCFX: + case coding_OKI16: + case coding_OKI4S: + case coding_MTF_IMA: + return 0x01; + case coding_MS_IMA: + case coding_RAD_IMA: + case coding_NDS_IMA: + case coding_DAT4_IMA: + case coding_REF_IMA: + return vgmstream->interleave_block_size; + case coding_AWC_IMA: + return 0x800; + case coding_RAD_IMA_mono: + return 0x14; + case coding_SNDS_IMA: + case coding_OTNS_IMA: + return 0; //todo: 0x01? + case coding_UBI_IMA: /* variable (PCM then IMA) */ + return 0; + case coding_XBOX_IMA: + //todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs + return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48; + case coding_XBOX_IMA_int: + case coding_WWISE_IMA: + case coding_CD_IMA: + return 0x24; + case coding_XBOX_IMA_mch: + case coding_FSB_IMA: + return 0x24 * vgmstream->channels; + case coding_APPLE_IMA4: + return 0x22; + case coding_H4M_IMA: + return 0x00; /* variable (block-controlled) */ + + case coding_XA: + return 0x80; + case coding_PSX: + case coding_PSX_badflags: + case coding_HEVAG: + return 0x10; + case coding_PSX_cfg: + case coding_PSX_pivotal: + return vgmstream->interleave_block_size; + + case coding_EA_XA: + return 0x1E; + case coding_EA_XA_int: + return 0x0F; + case coding_MAXIS_XA: + return 0x0F*vgmstream->channels; + case coding_EA_XA_V2: + return 0; /* variable (ADPCM frames of 0x0f or PCM frames of 0x3d) */ + case coding_EA_XAS_V0: + return 0xF+0x02+0x02; + case coding_EA_XAS_V1: + return 0x4c*vgmstream->channels; + + case coding_MSADPCM: + case coding_MSADPCM_int: + case coding_MSADPCM_ck: + return vgmstream->frame_size; + case coding_WS: + return vgmstream->current_block_size; + case coding_AICA: + case coding_AICA_int: + return 0x01; + case coding_ASKA: + case coding_NXAP: + return 0x40; + case coding_NDS_PROCYON: + return 0x10; + case coding_L5_555: + return 0x12; + case coding_LSF: + return 0x1C; + +#ifdef VGM_USE_G7221 + case coding_G7221C: +#endif +#ifdef VGM_USE_G719 + case coding_G719: +#endif +#ifdef VGM_USE_MAIATRAC3PLUS + case coding_AT3plus: +#endif +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: +#endif + case coding_MTAF: + return vgmstream->interleave_block_size; + case coding_MTA2: + return 0x90; + case coding_MC3: + return 0x04; + case coding_FADPCM: + return 0x8c; + case coding_ASF: + return 0x11; + case coding_DSA: + return 0x08; + case coding_XMD: + return vgmstream->interleave_block_size; + case coding_PTADPCM: + return vgmstream->interleave_block_size; + case coding_UBI_ADPCM: + return 0; /* varies per mode? */ + case coding_IMUSE: + return 0; /* varies per frame */ + case coding_EA_MT: + return 0; /* variable (frames of bit counts or PCM frames) */ +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: + return 0; /* varies with config data, usually 0x100-200 */ +#endif +#ifdef VGM_USE_CELT + case coding_CELT_FSB: + return 0; /* varies, usually 0x80-100 */ +#endif + default: /* Vorbis, MPEG, ACM, etc */ + return 0; + } +} + +/* In NDS IMA the frame size is the block size, so the last one is short */ +int get_vgmstream_samples_per_shortframe(VGMSTREAM* vgmstream) { + switch (vgmstream->coding_type) { + case coding_NDS_IMA: + return (vgmstream->interleave_last_block_size-4)*2; + default: + return get_vgmstream_samples_per_frame(vgmstream); + } +} + +int get_vgmstream_shortframe_size(VGMSTREAM* vgmstream) { + switch (vgmstream->coding_type) { + case coding_NDS_IMA: + return vgmstream->interleave_last_block_size; + default: + return get_vgmstream_frame_size(vgmstream); + } +} + +/* Decode samples into the buffer. Assume that we have written samples_written into the + * buffer already, and we have samples_to_do consecutive samples ahead of us (won't call + * more than one frame if configured above to do so). + * Called by layouts since they handle samples written/to_do */ +void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_do, sample_t* buffer) { + int ch; + + buffer += samples_written * vgmstream->channels; /* passed externally to simplify I guess */ + + switch (vgmstream->coding_type) { + case coding_SILENCE: + memset(buffer, 0, samples_to_do * vgmstream->channels * sizeof(sample_t)); + break; + + case coding_CRI_ADX: + case coding_CRI_ADX_exp: + case coding_CRI_ADX_fixed: + case coding_CRI_ADX_enc_8: + case coding_CRI_ADX_enc_9: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_adx(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->interleave_block_size, vgmstream->coding_type); + } + break; + case coding_NGC_DSP: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dsp(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_NGC_DSP_subint: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dsp_subint(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + vgmstream->interleave_block_size); + } + break; + + case coding_PCM16LE: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16le(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM16BE: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16be(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM16_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->codec_endian); + } + break; + case coding_PCM8: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM8_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM8_U: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_unsigned(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM8_U_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_unsigned_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM8_SB: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_sb(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM4: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm4(vgmstream,&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_PCM4_U: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm4_unsigned(vgmstream, &vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + + case coding_ULAW: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ulaw(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_ULAW_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ulaw_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_ALAW: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_alaw(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCMFLOAT: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcmfloat(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->codec_endian); + } + break; + + case coding_NDS_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_nds_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_DAT4_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_dat4_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_XBOX_IMA: + case coding_XBOX_IMA_int: { + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_XBOX_IMA); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xbox_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_stereo); + } + break; + } + case coding_XBOX_IMA_mch: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xbox_ima_mch(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_MS_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ms_ima(vgmstream,&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_RAD_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_rad_ima(vgmstream,&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_RAD_IMA_mono: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_rad_ima_mono(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_NGC_DTK: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dtk(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_G721: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g721(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_NGC_AFC: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_afc(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_VADPCM: { + int order = vgmstream->codec_config; + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_vadpcm(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, order); + } + break; + } + case coding_PSX: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + 0, vgmstream->codec_config); + } + break; + case coding_PSX_badflags: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + 1, vgmstream->codec_config); + } + break; + case coding_PSX_cfg: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx_configurable(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->interleave_block_size, vgmstream->codec_config); + } + break; + case coding_PSX_pivotal: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx_pivotal(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->interleave_block_size); + } + break; + case coding_HEVAG: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_hevag(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_XA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xa(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_EA_XA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xa(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_EA_XA_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xa_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_EA_XA_V2: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xa_v2(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_MAXIS_XA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_maxis_xa(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_EA_XAS_V0: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xas_v0(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_EA_XAS_V1: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xas_v1(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; +#ifdef VGM_USE_VORBIS + case coding_OGG_VORBIS: + decode_ogg_vorbis(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); + break; + + case coding_VORBIS_custom: + decode_vorbis_custom(vgmstream, buffer, samples_to_do, vgmstream->channels); + break; +#endif + case coding_CIRCUS_VQ: + decode_circus_vq(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); + break; + case coding_RELIC: + decode_relic(&vgmstream->ch[0], vgmstream->codec_data, buffer, samples_to_do); + break; + case coding_CRI_HCA: + decode_hca(vgmstream->codec_data, buffer, samples_to_do); + break; +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: + decode_ffmpeg(vgmstream, buffer, samples_to_do, vgmstream->channels); + break; +#endif +#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) + case coding_MP4_AAC: + decode_mp4_aac(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); + break; +#endif + case coding_SDX2: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_sdx2(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_SDX2_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_sdx2_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_CBD2: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cbd2(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_CBD2_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cbd2_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_DERF: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_derf(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_CIRCUS_ADPCM: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_circus_adpcm(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + + case coding_IMA: + case coding_IMA_int: + case coding_DVI_IMA: + case coding_DVI_IMA_int: { + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_IMA) + || (vgmstream->channels > 1 && vgmstream->coding_type == coding_DVI_IMA); + int is_high_first = vgmstream->coding_type == coding_DVI_IMA + || vgmstream->coding_type == coding_DVI_IMA_int; + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_standard_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + is_stereo, is_high_first); + } + break; + } + case coding_MTF_IMA: { + int is_stereo = (vgmstream->channels > 1); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mtf_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_stereo); + } + break; + } + case coding_3DS_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_3ds_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_WV6_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_wv6_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_ALP_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_alp_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_FFTA2_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ffta2_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_BLITZ_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_blitz_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + + case coding_APPLE_IMA4: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_apple_ima4(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_SNDS_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_snds_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_OTNS_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_otns_ima(vgmstream, &vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_FSB_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_fsb_ima(vgmstream, &vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_WWISE_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_wwise_ima(vgmstream,&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_REF_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ref_ima(vgmstream,&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_AWC_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_awc_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_UBI_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ubi_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + vgmstream->codec_config); + } + break; + case coding_H4M_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + uint16_t frame_format = (uint16_t)((vgmstream->codec_config >> 8) & 0xFFFF); + + decode_h4m_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + frame_format); + } + break; + case coding_CD_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cd_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + + case coding_WS: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ws(vgmstream, ch, buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + +#ifdef VGM_USE_MPEG + case coding_MPEG_custom: + case coding_MPEG_ealayer3: + case coding_MPEG_layer1: + case coding_MPEG_layer2: + case coding_MPEG_layer3: + decode_mpeg(vgmstream, buffer, samples_to_do, vgmstream->channels); + break; +#endif +#ifdef VGM_USE_G7221 + case coding_G7221C: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g7221(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); + } + break; +#endif +#ifdef VGM_USE_G719 + case coding_G719: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g719(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); + } + break; +#endif +#ifdef VGM_USE_MAIATRAC3PLUS + case coding_AT3plus: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_at3plus(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); + } + break; +#endif +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: + decode_atrac9(vgmstream, buffer, samples_to_do, vgmstream->channels); + break; +#endif +#ifdef VGM_USE_CELT + case coding_CELT_FSB: + decode_celt_fsb(vgmstream, buffer, samples_to_do, vgmstream->channels); + break; +#endif + case coding_ACM: + decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); + break; + case coding_NWA: + decode_nwa(vgmstream->codec_data, buffer, samples_to_do); + break; + case coding_MSADPCM: + case coding_MSADPCM_int: + if (vgmstream->channels == 1 || vgmstream->coding_type == coding_MSADPCM_int) { + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_msadpcm_mono(vgmstream,buffer+ch, + vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); + } + } + else if (vgmstream->channels == 2) { + decode_msadpcm_stereo(vgmstream, buffer, vgmstream->samples_into_block,samples_to_do); + } + break; + case coding_MSADPCM_ck: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_msadpcm_ck(vgmstream, buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_AICA: + case coding_AICA_int: { + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_AICA); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_aica(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + is_stereo); + } + break; + } + case coding_ASKA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_aska(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_NXAP: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_nxap(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_TGC: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_tgc(&vgmstream->ch[ch], buffer+ch, + vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_NDS_PROCYON: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_nds_procyon(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_L5_555: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_l5_555(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_SASSC: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_sassc(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_LSF: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_lsf(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_MTAF: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mtaf(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_MTA2: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mta2(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_MC3: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mc3(vgmstream, &vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_FADPCM: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_fadpcm(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels,vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_ASF: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_asf(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_DSA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_dsa(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_XMD: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xmd(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->interleave_block_size); + } + break; + case coding_PTADPCM: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ptadpcm(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->interleave_block_size); + } + break; + case coding_PCFX: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcfx(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->codec_config); + } + break; + case coding_OKI16: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_oki16(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + + case coding_OKI4S: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_oki4s(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + + case coding_UBI_ADPCM: + decode_ubi_adpcm(vgmstream, buffer, samples_to_do); + break; + + case coding_IMUSE: + decode_imuse(vgmstream, buffer, samples_to_do); + break; + + case coding_EA_MT: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_mt(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); + } + break; + default: + break; + } +} + +/* Calculate number of consecutive samples we can decode. Takes into account hitting + * a loop start or end, or going past a single frame. */ +int get_vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMSTREAM* vgmstream) { + int samples_to_do; + int samples_left_this_block; + + samples_left_this_block = samples_this_block - vgmstream->samples_into_block; + samples_to_do = samples_left_this_block; /* by default decodes all samples left */ + + /* fun loopy crap, why did I think this would be any simpler? */ + if (vgmstream->loop_flag) { + int samples_after_decode = vgmstream->current_sample + samples_left_this_block; + + /* are we going to hit the loop end during this block? */ + if (samples_after_decode > vgmstream->loop_end_sample) { + /* only do samples up to loop end */ + samples_to_do = vgmstream->loop_end_sample - vgmstream->current_sample; + } + + /* are we going to hit the loop start during this block? (first time only) */ + if (samples_after_decode > vgmstream->loop_start_sample && !vgmstream->hit_loop) { + /* only do samples up to loop start */ + samples_to_do = vgmstream->loop_start_sample - vgmstream->current_sample; + } + } + + /* if it's a framed encoding don't do more than one frame */ + if (samples_per_frame > 1 && (vgmstream->samples_into_block % samples_per_frame) + samples_to_do > samples_per_frame) + samples_to_do = samples_per_frame - (vgmstream->samples_into_block % samples_per_frame); + + return samples_to_do; +} + +/* Detect loop start and save values, or detect loop end and restore (loop back). + * Returns 1 if loop was done. */ +int vgmstream_do_loop(VGMSTREAM* vgmstream) { + /*if (!vgmstream->loop_flag) return 0;*/ + + /* is this the loop end? = new loop, continue from loop_start_sample */ + if (vgmstream->current_sample == vgmstream->loop_end_sample) { + + /* disable looping if target count reached and continue normally + * (only needed with the "play stream end after looping N times" option enabled) */ + vgmstream->loop_count++; + if (vgmstream->loop_target && vgmstream->loop_target == vgmstream->loop_count) { + vgmstream->loop_flag = 0; /* could be improved but works ok, will be restored on resets */ + return 0; + } + + /* against everything I hold sacred, preserve adpcm history before looping for certain types */ + if (vgmstream->meta_type == meta_DSP_STD || + vgmstream->meta_type == meta_DSP_RS03 || + vgmstream->meta_type == meta_DSP_CSTR || + vgmstream->coding_type == coding_PSX || + vgmstream->coding_type == coding_PSX_badflags) { + int ch; + for (ch = 0; ch < vgmstream->channels; ch++) { + vgmstream->loop_ch[ch].adpcm_history1_16 = vgmstream->ch[ch].adpcm_history1_16; + vgmstream->loop_ch[ch].adpcm_history2_16 = vgmstream->ch[ch].adpcm_history2_16; + vgmstream->loop_ch[ch].adpcm_history1_32 = vgmstream->ch[ch].adpcm_history1_32; + vgmstream->loop_ch[ch].adpcm_history2_32 = vgmstream->ch[ch].adpcm_history2_32; + } + } + + /* loop codecs */ + seek_codec(vgmstream); + + /* restore! */ + memcpy(vgmstream->ch, vgmstream->loop_ch, sizeof(VGMSTREAMCHANNEL) * vgmstream->channels); + vgmstream->current_sample = vgmstream->loop_current_sample; + vgmstream->samples_into_block = vgmstream->loop_samples_into_block; + vgmstream->current_block_size = vgmstream->loop_block_size; + vgmstream->current_block_samples = vgmstream->loop_block_samples; + vgmstream->current_block_offset = vgmstream->loop_block_offset; + vgmstream->next_block_offset = vgmstream->loop_next_block_offset; + //vgmstream->pstate = vgmstream->lstate; /* play state is applied over loops */ + + /* loop layouts (after restore, in case layout needs state manipulations) */ + switch(vgmstream->layout_type) { + case layout_segmented: + loop_layout_segmented(vgmstream, vgmstream->loop_current_sample); + break; + case layout_layered: + loop_layout_layered(vgmstream, vgmstream->loop_current_sample); + break; + default: + break; + } + + return 1; /* looped */ + } + + + /* is this the loop start? save if we haven't saved yet (right when first loop starts) */ + if (!vgmstream->hit_loop && vgmstream->current_sample == vgmstream->loop_start_sample) { + /* save! */ + memcpy(vgmstream->loop_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); + vgmstream->loop_current_sample = vgmstream->current_sample; + vgmstream->loop_samples_into_block = vgmstream->samples_into_block; + vgmstream->loop_block_size = vgmstream->current_block_size; + vgmstream->loop_block_samples = vgmstream->current_block_samples; + vgmstream->loop_block_offset = vgmstream->current_block_offset; + vgmstream->loop_next_block_offset = vgmstream->next_block_offset; + //vgmstream->lstate = vgmstream->pstate; /* play state is applied over loops */ + + vgmstream->hit_loop = 1; /* info that loop is now ready to use */ + } + + return 0; /* not looped */ +} diff --git a/Frameworks/vgmstream/vgmstream/src/decode.h b/Frameworks/vgmstream/vgmstream/src/decode.h new file mode 100644 index 000000000..01adf3586 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/decode.h @@ -0,0 +1,32 @@ +#ifndef _DECODE_H +#define _DECODE_H + +#include "vgmstream.h" + +void free_codec(VGMSTREAM* vgmstream); +void seek_codec(VGMSTREAM* vgmstream); +void reset_codec(VGMSTREAM* vgmstream); + +/* Decode samples into the buffer. Assume that we have written samples_written into the + * buffer already, and we have samples_to_do consecutive samples ahead of us. */ +void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_do, sample_t* buffer); + +/* Detect loop start and save values, or detect loop end and restore (loop back). Returns 1 if loop was done. */ +int vgmstream_do_loop(VGMSTREAM* vgmstream); + +/* Calculate number of consecutive samples to do (taking into account stopping for loop start and end) */ +int get_vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMSTREAM* vgmstream); + + +/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */ +int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream); + +/* Get the number of bytes of a single frame (smallest self-contained byte group, 1/N channels) */ +int get_vgmstream_frame_size(VGMSTREAM* vgmstream); + +/* In NDS IMA the frame size is the block size, but last one is shorter */ +int get_vgmstream_samples_per_shortframe(VGMSTREAM* vgmstream); +int get_vgmstream_shortframe_size(VGMSTREAM* vgmstream); + + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 6fcb9bf8d..7eca85a3c 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -138,7 +138,7 @@ static const char* extension_list[] = { "cxs", "da", - "dat", + //"dat", //common "data", "dax", "dbm", @@ -150,6 +150,7 @@ static const char* extension_list[] = { "diva", "dmsg", "ds2", //txth/reserved [Star Wars Bounty Hunter (GC)] + "dsb", "dsf", "dsp", "dspw", @@ -242,7 +243,6 @@ static const char* extension_list[] = { "kat", "kces", "kcey", //fake extension/header id for .pcm (renamed, to be removed) - "khv", //fake extension/header id for .vas (renamed, to be removed) "km9", "kovs", //fake extension/header id for .kvs "kns", @@ -395,6 +395,7 @@ static const char* extension_list[] = { "rsd", "rsf", "rsm", + "rsp", "rstm", //fake extension/header id for .rstm (in bigfiles) "rvws", "rwar", @@ -559,6 +560,7 @@ static const char* extension_list[] = { "wem", "wii", "wip", //txth/reserved [Colin McRae DiRT (PC)] + "wlv", //txth/reserved [ToeJam & Earl III: Mission to Earth (DC)] "wma", //common "wmus", "wp2", @@ -609,6 +611,7 @@ static const char* extension_list[] = { "zsm", "zss", "zwdsp", + "zwv", "vgmstream" /* fake extension, catch-all for FFmpeg/txth/etc */ @@ -621,6 +624,7 @@ static const char* common_extension_list[] = { "aif", //common "aiff", //common "bin", //common + "dat", //common "flac", //common "m4a", //common "m4v", //common @@ -666,6 +670,8 @@ typedef struct { static const coding_info coding_info_list[] = { + {coding_SILENCE, "Silence"}, + {coding_PCM16LE, "Little Endian 16-bit PCM"}, {coding_PCM16BE, "Big Endian 16-bit PCM"}, {coding_PCM16_int, "16-bit PCM with 2 byte interleave (block)"}, @@ -737,6 +743,7 @@ static const coding_info coding_info_list[] = { {coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"}, {coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"}, {coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"}, + {coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"}, {coding_MSADPCM, "Microsoft 4-bit ADPCM"}, {coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"}, @@ -863,6 +870,7 @@ static const layout_info layout_info_list[] = { }; static const meta_info meta_info_list[] = { + {meta_SILENCE, "Silence"}, {meta_RSTM, "Nintendo RSTM header"}, {meta_STRM, "Nintendo STRM header"}, {meta_ADX_03, "CRI ADX header type 03"}, @@ -896,7 +904,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_OMU, "Alter Echo OMU Header"}, {meta_DSP_STM, "Intelligent Systems STM header"}, {meta_PS2_EXST, "Sony EXST header"}, - {meta_PS2_SVAG, "Konami SVAG header"}, + {meta_SVAG_KCET, "Konami SVAG header"}, {meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"}, {meta_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"}, {meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"}, @@ -945,11 +953,11 @@ static const meta_info meta_info_list[] = { {meta_EA_1SNH, "Electronic Arts 1SNh header"}, {meta_EA_EACS, "Electronic Arts EACS header"}, {meta_SL3, "Atari Melbourne House SL3 header"}, - {meta_FSB1, "FMOD Sample Bank (FSB1) Header"}, - {meta_FSB2, "FMOD Sample Bank (FSB2) Header"}, - {meta_FSB3, "FMOD Sample Bank (FSB3) Header"}, - {meta_FSB4, "FMOD Sample Bank (FSB4) Header"}, - {meta_FSB5, "FMOD Sample Bank (FSB5) Header"}, + {meta_FSB1, "FMOD FSB1 header"}, + {meta_FSB2, "FMOD FSB2 header"}, + {meta_FSB3, "FMOD FSB3 header"}, + {meta_FSB4, "FMOD FSB4 header"}, + {meta_FSB5, "FMOD FSB5 header"}, {meta_RWX, "RWX Header"}, {meta_XWB, "Microsoft XWB header"}, {meta_PS2_XA30, "Reflections XA30 PS2 header"}, @@ -1062,7 +1070,7 @@ static const meta_info meta_info_list[] = { {meta_NGC_SCK_DSP, "The Scorpion King SCK Header"}, {meta_CAFF, "Apple Core Audio Format File header"}, {meta_PC_MXST, "Lego Island MxSt Header"}, - {meta_SAB, "Team17 SAB header"}, + {meta_SAB, "Sensaura SAB header"}, {meta_MAXIS_XA, "Maxis XAI/XAJ Header"}, {meta_EXAKT_SC, "assumed Activision / EXAKT SC by extension"}, {meta_WII_BNS, "Nintendo BNS header"}, @@ -1152,7 +1160,7 @@ static const meta_info meta_info_list[] = { {meta_MCA, "Capcom MCA header"}, {meta_XB3D_ADX, "Xenoblade 3D ADX header"}, {meta_HCA, "CRI HCA header"}, - {meta_PS2_SVAG_SNK, "SNK SVAG header"}, + {meta_SVAG_SNK, "SNK SVAG header"}, {meta_PS2_VDS_VDM, "Procyon Studio VDS/VDM header"}, {meta_FFMPEG, "FFmpeg supported file format"}, {meta_X360_CXS, "tri-Crescendo CXS header"}, @@ -1300,9 +1308,10 @@ static const meta_info meta_info_list[] = { {meta_KTSR, "Koei Tecmo KTSR header"}, {meta_KAT, "Sega KAT header"}, {meta_PCM_SUCCESS, "Success PCM header"}, + {meta_ADP_KONAMI, "Konami ADP header"}, }; -void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t out_size) { +void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { int i, list_length; const char *description; @@ -1313,7 +1322,8 @@ void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t ou layered_layout_data* layout_data = vgmstream->layout_data; get_vgmstream_coding_description(layout_data->layers[0], out, out_size); return; - } else if (vgmstream->layout_type == layout_segmented) { + } + else if (vgmstream->layout_type == layout_segmented) { segmented_layout_data* layout_data = vgmstream->layout_data; get_vgmstream_coding_description(layout_data->segments[0], out, out_size); return; @@ -1342,7 +1352,8 @@ void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t ou strncpy(out, description, out_size); } -const char * get_vgmstream_layout_name(layout_t layout_type) { + +static const char* get_layout_name(layout_t layout_type) { int i, list_length; list_length = sizeof(layout_info_list) / sizeof(layout_info); @@ -1353,40 +1364,103 @@ const char * get_vgmstream_layout_name(layout_t layout_type) { return NULL; } -void get_vgmstream_layout_description(VGMSTREAM *vgmstream, char *out, size_t out_size) { - char temp[256]; - VGMSTREAM* vgmstreamsub = NULL; - const char* description; - description = get_vgmstream_layout_name(vgmstream->layout_type); +static int has_sublayouts(VGMSTREAM** vgmstreams, int count) { + int i; + for (i = 0; i < count; i++) { + if (vgmstreams[i]->layout_type == layout_segmented || vgmstreams[i]->layout_type == layout_layered) + return 1; + } + return 0; +} + +/* Makes a mixed description, considering a segments/layers can contain segments/layers infinitely, like: + * + * "(L3[S2L2]S3)" "(S3[L2[S2S2]])" + * L3 S3 + * S2 L2 + * file S2 + * file file + * file file + * L2 file + * file file + * file file + * + * ("mixed" is added externally) + */ +static int get_layout_mixed_description(VGMSTREAM* vgmstream, char* dst, int dst_size) { + int i, count, done = 0; + VGMSTREAM** vgmstreams = NULL; + + if (vgmstream->layout_type == layout_layered) { + layered_layout_data* data = vgmstream->layout_data; + vgmstreams = data->layers; + count = data->layer_count; + done = snprintf(dst, dst_size, "L%i", count); + } + else if (vgmstream->layout_type == layout_segmented) { + segmented_layout_data* data = vgmstream->layout_data; + vgmstreams = data->segments; + count = data->segment_count; + done = snprintf(dst, dst_size, "S%i", count); + } + + if (!vgmstreams || done == 0 || done >= dst_size) + return 0; + + if (!has_sublayouts(vgmstreams, count)) + return done; + + if (done + 1 < dst_size) { + dst[done++] = '['; + } + + for (i = 0; i < count; i++) { + done += get_layout_mixed_description(vgmstreams[i], dst + done, dst_size - done); + } + + if (done + 1 < dst_size) { + dst[done++] = ']'; + } + + return done; +} + +void get_vgmstream_layout_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { + const char* description; + int mixed = 0; + + description = get_layout_name(vgmstream->layout_type); if (!description) description = "INCONCEIVABLE"; if (vgmstream->layout_type == layout_layered) { - vgmstreamsub = ((layered_layout_data*)vgmstream->layout_data)->layers[0]; - snprintf(temp, sizeof(temp), "%s (%i layers)", description, ((layered_layout_data*)vgmstream->layout_data)->layer_count); - } else if (vgmstream->layout_type == layout_segmented) { - snprintf(temp, sizeof(temp), "%s (%i segments)", description, ((segmented_layout_data*)vgmstream->layout_data)->segment_count); - vgmstreamsub = ((segmented_layout_data*)vgmstream->layout_data)->segments[0]; - } else { - snprintf(temp, sizeof(temp), "%s", description); + layered_layout_data* data = vgmstream->layout_data; + mixed = has_sublayouts(data->layers, data->layer_count); + if (!mixed) + snprintf(out, out_size, "%s (%i layers)", description, data->layer_count); + } + else if (vgmstream->layout_type == layout_segmented) { + segmented_layout_data* data = vgmstream->layout_data; + mixed = has_sublayouts(data->segments, data->segment_count); + if (!mixed) + snprintf(out, out_size, "%s (%i segments)", description, data->segment_count); + } + else { + snprintf(out, out_size, "%s", description); } - strncpy(out, temp, out_size); - /* layouts can contain layouts infinitely let's leave it at one level deep (most common) */ - /* TODO: improve this somehow */ - if (vgmstreamsub && vgmstreamsub->layout_type == layout_layered) { - description = get_vgmstream_layout_name(vgmstreamsub->layout_type); - snprintf(temp, sizeof(temp), " + %s (%i layers)", description, ((layered_layout_data*)vgmstreamsub->layout_data)->layer_count); - concatn(out_size, out, temp); - } else if (vgmstreamsub && vgmstreamsub->layout_type == layout_segmented) { - description = get_vgmstream_layout_name(vgmstreamsub->layout_type); - snprintf(temp, sizeof(temp), " + %s (%i segments)", description, ((segmented_layout_data*)vgmstream->layout_data)->segment_count); - concatn(out_size, out, temp); + if (mixed) { + char tmp[256] = {0}; + + get_layout_mixed_description(vgmstream, tmp, sizeof(tmp) - 1); + snprintf(out, out_size, "mixed (%s)", tmp); + return; } } -void get_vgmstream_meta_description(VGMSTREAM *vgmstream, char *out, size_t out_size) { + +void get_vgmstream_meta_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { int i, list_length; - const char *description; + const char* description; description = "THEY SHOULD HAVE SENT A POET"; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index 323353994..fe7696c63 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -1,11 +1,12 @@ #include "layout.h" #include "../vgmstream.h" +#include "../decode.h" /* 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_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { +void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream) { int samples_written = 0; int frame_size, samples_per_frame, samples_this_block; @@ -52,7 +53,7 @@ void render_vgmstream_blocked(sample_t * buffer, int32_t sample_count, VGMSTREAM break; } - samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); + samples_to_do = get_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; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c index 3aab1668a..ac14fc3d8 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c @@ -6,9 +6,10 @@ void block_update_ast(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; size_t block_data, header_size; + int32_t(*read_32bit)(off_t, STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; /* 0x00: "BLCK", rest: null */ - block_data = read_32bitBE(block_offset+0x04,streamFile); + block_data = read_32bit(block_offset+0x04,streamFile); header_size = 0x20; vgmstream->current_block_offset = block_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/flat.c b/Frameworks/vgmstream/vgmstream/src/layout/flat.c index ae5b91e9e..547cd55bb 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/flat.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/flat.c @@ -1,10 +1,11 @@ #include "layout.h" #include "../vgmstream.h" +#include "../decode.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_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { +void render_vgmstream_flat(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { int samples_written = 0; int samples_per_frame, samples_this_block; @@ -20,21 +21,23 @@ void render_vgmstream_flat(sample_t * buffer, int32_t sample_count, VGMSTREAM * continue; } - samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); + samples_to_do = get_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 0 found\n"); /* could happen when calling render at EOF? */ - //VGM_LOG("layout_flat: tb=%i sib=%i, spf=%i\n", samples_this_block, vgmstream->samples_into_block, samples_per_frame); - memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t)); - break; + if (samples_to_do == 0) { /* when decoding more than num_samples */ + VGM_LOG("FLAT: samples_to_do 0\n"); + goto decode_fail; } - decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); + decode_vgmstream(vgmstream, samples_written, samples_to_do, outbuf); samples_written += samples_to_do; vgmstream->current_sample += samples_to_do; vgmstream->samples_into_block += samples_to_do; } + + return; +decode_fail: + memset(outbuf + samples_written * vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t)); } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c index 94715a3f8..fb707aa12 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c @@ -1,5 +1,6 @@ #include "layout.h" #include "../vgmstream.h" +#include "../decode.h" /* Decodes samples for interleaved streams. @@ -82,7 +83,7 @@ void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTR continue; } - samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); + samples_to_do = get_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; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layered.c b/Frameworks/vgmstream/vgmstream/src/layout/layered.c index 24b8f6d66..de392ef8d 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layered.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/layered.c @@ -1,34 +1,49 @@ #include "layout.h" #include "../vgmstream.h" +#include "../decode.h" #include "../mixing.h" +#include "../plugins.h" - -/* NOTE: if loop settings change the layered vgmstreams must be notified (preferably using vgmstream_force_loop) */ #define VGMSTREAM_MAX_LAYERS 255 #define VGMSTREAM_LAYER_SAMPLE_BUFFER 8192 /* 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. + * Similar to flat layout, but decoded vgmstream are mixed into a final buffer, each vgmstream + * may have different codecs and 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_t * outbuf, int32_t sample_count, VGMSTREAM * vgmstream) { +void render_vgmstream_layered(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { int samples_written = 0; - layered_layout_data *data = vgmstream->layout_data; + layered_layout_data* data = vgmstream->layout_data; + int samples_per_frame, samples_this_block; + samples_per_frame = VGMSTREAM_LAYER_SAMPLE_BUFFER; + samples_this_block = vgmstream->num_samples; /* do all samples if possible */ while (samples_written < sample_count) { - int samples_to_do = VGMSTREAM_LAYER_SAMPLE_BUFFER; - int layer, ch = 0; + int samples_to_do; + int layer, ch; + + if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { + /* handle looping (loop_layout has been called below) */ + continue; + } + + samples_to_do = get_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) { /* when decoding more than num_samples */ + VGM_LOG("LAYERED: samples_to_do 0\n"); + goto decode_fail; + } + + /* decode all layers */ + ch = 0; for (layer = 0; layer < data->layer_count; layer++) { int s, layer_ch, layer_channels; - /* each layer will handle its own looping/mixing internally */ - /* layers may have its own number of channels */ mixing_info(data->layers[layer], NULL, &layer_channels); @@ -49,12 +64,48 @@ void render_vgmstream_layered(sample_t * outbuf, int32_t sample_count, VGMSTREAM } } + samples_written += samples_to_do; - /* needed for info (ex. for mixing) */ - vgmstream->current_sample = data->layers[0]->current_sample; - vgmstream->loop_count = data->layers[0]->loop_count; - //vgmstream->samples_into_block = 0; /* handled in each layer */ + vgmstream->current_sample += samples_to_do; + vgmstream->samples_into_block += samples_to_do; } + + return; +decode_fail: + memset(outbuf + samples_written * data->output_channels, 0, (sample_count - samples_written) * data->output_channels * sizeof(sample_t)); +} + + +void loop_layout_layered(VGMSTREAM* vgmstream, int32_t loop_sample) { + int layer; + layered_layout_data* data = vgmstream->layout_data; + + + for (layer = 0; layer < data->layer_count; layer++) { + if (data->external_looping) { + /* looping is applied over resulting decode, as each layer is its own "solid" block with + * config and needs 'external' seeking */ + seek_vgmstream(data->layers[layer], loop_sample); + } + else { + /* looping is aplied as internal loops. normally each layer does it automatically, but + * just calls do_loop manually to behave a bit more controlled, and so that manual + * calls to do_loop work (used in seek_vgmstream) */ + if (data->layers[layer]->loop_flag) { /* mixing looping and non-looping layers is allowed */ + data->layers[layer]->current_sample = data->layers[layer]->loop_end_sample; /* forces do loop */ + vgmstream_do_loop(data->layers[layer]); /* guaranteed to work should loop_layout be called */ + } + else { + /* needed when mixing non-looping layers and installing loop externally */ + seek_vgmstream(data->layers[layer], loop_sample); + } + } + } + + /* could always call seek_vgmstream, but it's not optimized to loop non-config vgmstreams ATM */ + + vgmstream->current_sample = loop_sample; + vgmstream->samples_into_block = loop_sample; } @@ -88,12 +139,12 @@ int setup_layout_layered(layered_layout_data* data) { int layer_input_channels, layer_output_channels; if (data->layers[i] == NULL) { - VGM_LOG("layered: no vgmstream in %i\n", i); + VGM_LOG("LAYERED: no vgmstream in %i\n", i); goto fail; } if (data->layers[i]->num_samples <= 0) { - VGM_LOG("layered: no samples in %i\n", i); + VGM_LOG("LAYERED: no samples in %i\n", i); goto fail; } @@ -107,21 +158,25 @@ int setup_layout_layered(layered_layout_data* data) { if (i > 0) { /* a bit weird, but no matter */ if (data->layers[i]->sample_rate != data->layers[i-1]->sample_rate) { - VGM_LOG("layered: layer %i has different sample rate\n", i); + VGM_LOG("LAYERED: layer %i has different sample rate\n", i); } /* also weird */ if (data->layers[i]->coding_type != data->layers[i-1]->coding_type) { - VGM_LOG("layered: layer %i has different coding type\n", i); + VGM_LOG("LAYERED: layer %i has different coding type\n", i); } } - /* loops and other values could be mismatched but hopefully not */ + /* loops and other values could be mismatched, but should be handled on allocate */ + /* init mixing */ + mixing_setup(data->layers[i], VGMSTREAM_LAYER_SAMPLE_BUFFER); - setup_vgmstream(data->layers[i]); /* final setup in case the VGMSTREAM was created manually */ + /* allow config if set for fine-tuned parts (usually TXTP only) */ + data->layers[i]->config_enabled = data->layers[i]->config.config_set; - mixing_setup(data->layers[i], VGMSTREAM_LAYER_SAMPLE_BUFFER); /* init mixing */ + /* final setup in case the VGMSTREAM was created manually */ + setup_vgmstream(data->layers[i]); } if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS) @@ -168,29 +223,66 @@ void reset_layout_layered(layered_layout_data *data) { } /* helper for easier creation of layers */ -VGMSTREAM *allocate_layered_vgmstream(layered_layout_data* data) { - VGMSTREAM *vgmstream = NULL; - int i, channels, loop_flag; +VGMSTREAM* allocate_layered_vgmstream(layered_layout_data* data) { + VGMSTREAM* vgmstream = NULL; + int i, channels, loop_flag, sample_rate, external_looping; + int32_t num_samples, loop_start, loop_end; + int delta = 1024; /* get data */ channels = data->output_channels; + + num_samples = 0; loop_flag = 1; + loop_start = data->layers[0]->loop_start_sample; + loop_end = data->layers[0]->loop_end_sample; + external_looping = 0; + sample_rate = 0; for (i = 0; i < data->layer_count; i++) { - if (loop_flag && !data->layers[i]->loop_flag) + int32_t layer_samples = vgmstream_get_samples(data->layers[i]); + int layer_loop = data->layers[i]->loop_flag; + int32_t layer_loop_start = data->layers[i]->loop_start_sample; + int32_t layer_loop_end = data->layers[i]->loop_end_sample; + int layer_rate = data->layers[i]->sample_rate; + + /* internal has own config (and maybe looping), looping now must be done on layout level + * (instead of on each layer, that is faster) */ + if (data->layers[i]->config_enabled) { loop_flag = 0; + layer_loop = 0; + external_looping = 1; + } + + /* all layers should share loop pointsto consider looping enabled, + * but allow some leeway (ex. Dragalia Lost bgm+vocals ~12 samples) */ + if (!layer_loop + || !(loop_start >= layer_loop_start - delta && loop_start <= layer_loop_start + delta) + || !(loop_end >= layer_loop_end - delta && loop_start <= layer_loop_end + delta)) { + loop_flag = 0; + loop_start = 0; + loop_end = 0; + } + + if (num_samples < layer_samples) /* max */ + num_samples = layer_samples; + + if (sample_rate < layer_rate) + sample_rate = layer_rate; } + data->external_looping = external_looping; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - vgmstream->meta_type = data->layers[0]->meta_type; - vgmstream->sample_rate = data->layers[0]->sample_rate; - vgmstream->num_samples = data->layers[0]->num_samples; - vgmstream->loop_start_sample = data->layers[0]->loop_start_sample; - vgmstream->loop_end_sample = data->layers[0]->loop_end_sample; - vgmstream->coding_type = data->layers[0]->coding_type; + 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 = data->layers[0]->meta_type; /* info */ + vgmstream->coding_type = data->layers[0]->coding_type; /* info */ vgmstream->layout_type = layout_layered; vgmstream->layout_data = data; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index 488f3b191..2b6405dc2 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -5,67 +5,69 @@ #include "../vgmstream.h" /* blocked layouts */ -void render_vgmstream_blocked(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); -void block_update(off_t block_offset, VGMSTREAM * vgmstream); +void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); +void block_update(off_t block_offset, VGMSTREAM* vgmstream); -void block_update_ast(off_t block_ofset, VGMSTREAM * vgmstream); -void block_update_mxch(off_t block_ofset, VGMSTREAM * vgmstream); -void block_update_halpst(off_t block_ofset, VGMSTREAM * vgmstream); -void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_caf(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_wsi(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_str_snds(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ws_aud(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_matx(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_dec(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_mul(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_gsb(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_xvas(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_thp(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_filp(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ivaud(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_adm(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_bdsp(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_tra(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ps2_iab(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_vs_str(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_rws(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_hwas(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_vawx(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_xvag_subsong(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ea_wve_au00(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_vs_square(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_ast(off_t block_ofset, VGMSTREAM* vgmstream); +void block_update_mxch(off_t block_ofset, VGMSTREAM* vgmstream); +void block_update_halpst(off_t block_ofset, VGMSTREAM* vgmstream); +void block_update_xa(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ea_schl(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_caf(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_wsi(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_str_snds(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ws_aud(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_matx(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_dec(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_vs(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_mul(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_gsb(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_xvas(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_thp(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_filp(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ivaud(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ea_swvr(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_adm(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_bdsp(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_tra(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ps2_iab(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_vs_str(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_rws(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_hwas(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ea_sns(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_awc(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_vgs(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_vawx(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_xvag_subsong(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ea_wve_au00(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_sthd(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_h4m(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_xa_aiff(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_vs_square(off_t block_offset, VGMSTREAM* vgmstream); void block_update_vid1(off_t block_offset, VGMSTREAM* vgmstream); void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream); /* other layouts */ -void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_interleave(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); -void render_vgmstream_flat(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_flat(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); -void render_vgmstream_segmented(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_segmented(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); segmented_layout_data* init_layout_segmented(int segment_count); int setup_layout_segmented(segmented_layout_data* data); -void free_layout_segmented(segmented_layout_data *data); -void reset_layout_segmented(segmented_layout_data *data); +void free_layout_segmented(segmented_layout_data* data); +void reset_layout_segmented(segmented_layout_data* data); +void loop_layout_segmented(VGMSTREAM* vgmstream, int32_t seek_sample); VGMSTREAM *allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment); -void render_vgmstream_layered(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_layered(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); layered_layout_data* init_layout_layered(int layer_count); int setup_layout_layered(layered_layout_data* data); -void free_layout_layered(layered_layout_data *data); -void reset_layout_layered(layered_layout_data *data); +void free_layout_layered(layered_layout_data* data); +void reset_layout_layered(layered_layout_data* data); +void loop_layout_layered(VGMSTREAM* vgmstream, int32_t seek_sample); VGMSTREAM *allocate_layered_vgmstream(layered_layout_data* data); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c index 7965dc9b1..035b0a223 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c @@ -1,6 +1,8 @@ #include "layout.h" #include "../vgmstream.h" +#include "../decode.h" #include "../mixing.h" +#include "../plugins.h" #define VGMSTREAM_MAX_SEGMENTS 1024 #define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192 @@ -9,88 +11,67 @@ /* 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_t * outbuf, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written = 0, loop_samples_skip = 0; - segmented_layout_data *data = vgmstream->layout_data; +void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { + int samples_written = 0, samples_this_block; + segmented_layout_data* data = vgmstream->layout_data; int use_internal_buffer = 0; - - /* normally uses outbuf directly (faster) but could need internal buffer if downmixing */ + /* normally uses outbuf directly (faster?) but could need internal buffer if downmixing */ if (vgmstream->channels != data->input_channels) { use_internal_buffer = 1; } + if (data->current_segment >= data->segment_count) { + VGM_LOG("SEGMENT: wrong current segment\n"); + goto decode_fail; + } + + samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]); while (samples_written < sample_count) { int samples_to_do; - int samples_this_segment = data->segments[data->current_segment]->num_samples; if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { - int segment, loop_segment, total_samples; + /* handle looping (loop_layout has been called below, changes segments/state) */ + samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]); + continue; + } - /* handle looping by finding loop segment and loop_start inside that segment */ - loop_segment = 0; - total_samples = 0; - while (total_samples < vgmstream->num_samples) { - int32_t segment_samples = data->segments[loop_segment]->num_samples; + /* detect segment change and restart (after loop, but before decode, to allow looping to kick in) */ + if (vgmstream->samples_into_block >= samples_this_block) { + data->current_segment++; - if (vgmstream->loop_sample >= total_samples && vgmstream->loop_sample < total_samples + segment_samples) { - loop_samples_skip = vgmstream->loop_sample - total_samples; - break; /* loop_start falls within loop_segment's samples */ - } - total_samples += segment_samples; - loop_segment++; + if (data->current_segment >= data->segment_count) { /* when decoding more than num_samples */ + VGM_LOG("SEGMENTED: reached last segment\n"); + goto decode_fail; } - if (loop_segment == data->segment_count) { - VGM_LOG("segmented_layout: can't find loop segment\n"); - loop_segment = 0; - } - - data->current_segment = loop_segment; - - /* loops can span multiple segments */ - for (segment = loop_segment; segment < data->segment_count; segment++) { - reset_vgmstream(data->segments[segment]); - } + /* in case of looping spanning multiple segments */ + reset_vgmstream(data->segments[data->current_segment]); + samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]); vgmstream->samples_into_block = 0; continue; } - samples_to_do = vgmstream_samples_to_do(samples_this_segment, sample_count, vgmstream); + + samples_to_do = get_vgmstream_samples_to_do(samples_this_block, sample_count, vgmstream); if (samples_to_do > sample_count - samples_written) samples_to_do = sample_count - samples_written; if (samples_to_do > VGMSTREAM_SEGMENT_SAMPLE_BUFFER /*&& use_internal_buffer*/) /* always for fade/etc mixes */ samples_to_do = VGMSTREAM_SEGMENT_SAMPLE_BUFFER; - /* segment looping: discard until actual start */ - if (loop_samples_skip > 0) { - if (samples_to_do > loop_samples_skip) - samples_to_do = loop_samples_skip; - } - - /* detect segment change and restart */ - if (samples_to_do == 0) { - data->current_segment++; - reset_vgmstream(data->segments[data->current_segment]); - vgmstream->samples_into_block = 0; - continue; + if (samples_to_do < 0) { /* 0 is ok? */ + VGM_LOG("SEGMENTED: wrong samples_to_do %i found\n", samples_to_do); + goto decode_fail; } render_vgmstream( use_internal_buffer ? - data->buffer : - &outbuf[samples_written * data->output_channels], + data->buffer : &outbuf[samples_written * data->output_channels], samples_to_do, data->segments[data->current_segment]); - if (loop_samples_skip > 0) { - loop_samples_skip -= samples_to_do; - vgmstream->samples_into_block += samples_to_do; - continue; - } - if (use_internal_buffer) { int s; for (s = 0; s < samples_to_do * data->output_channels; s++) { @@ -102,11 +83,42 @@ void render_vgmstream_segmented(sample_t * outbuf, int32_t sample_count, VGMSTRE vgmstream->current_sample += samples_to_do; vgmstream->samples_into_block += samples_to_do; } + + return; +decode_fail: + memset(outbuf + samples_written * data->output_channels, 0, (sample_count - samples_written) * data->output_channels * sizeof(sample_t)); +} + +void loop_layout_segmented(VGMSTREAM* vgmstream, int32_t loop_sample) { + int segment, total_samples; + segmented_layout_data* data = vgmstream->layout_data; + + segment = 0; + total_samples = 0; + while (total_samples < vgmstream->num_samples) { + int32_t segment_samples = vgmstream_get_samples(data->segments[segment]); + + /* find if loop falls within segment's samples */ + if (loop_sample >= total_samples && loop_sample < total_samples + segment_samples) { + int32_t loop_relative = loop_sample - total_samples; + + seek_vgmstream(data->segments[segment], loop_relative); + data->current_segment = segment; + vgmstream->samples_into_block = loop_relative; + break; + } + total_samples += segment_samples; + segment++; + } + + if (segment == data->segment_count) { + VGM_LOG("SEGMENTED: can't find loop segment\n"); + } } segmented_layout_data* init_layout_segmented(int segment_count) { - segmented_layout_data *data = NULL; + segmented_layout_data* data = NULL; if (segment_count <= 0 || segment_count > VGMSTREAM_MAX_SEGMENTS) goto fail; @@ -135,22 +147,27 @@ int setup_layout_segmented(segmented_layout_data* data) { for (i = 0; i < data->segment_count; i++) { int segment_input_channels, segment_output_channels; + /* allow config if set for fine-tuned parts (usually TXTP only) */ + data->segments[i]->config_enabled = data->segments[i]->config.config_set; + if (data->segments[i] == NULL) { - VGM_LOG("segmented: no vgmstream in segment %i\n", i); + VGM_LOG("SEGMENTED: no vgmstream in segment %i\n", i); goto fail; } - if (data->segments[i]->num_samples <= 0) { - VGM_LOG("segmented: no samples in segment %i\n", i); + VGM_LOG("SEGMENTED: no samples in segment %i\n", i); goto fail; } - /* disable so that looping is controlled by render_vgmstream_segmented */ if (data->segments[i]->loop_flag != 0) { - VGM_LOG("segmented: segment %i is looped\n", i); - data->segments[i]->loop_flag = 0; + VGM_LOG("SEGMENTED: segment %i is looped\n", i); + + /* config allows internal loops */ + if (!data->segments[i]->config_enabled) { + data->segments[i]->loop_flag = 0; + } } /* different segments may have different input channels, though output should be @@ -166,13 +183,13 @@ int setup_layout_segmented(segmented_layout_data* data) { mixing_info(data->segments[i-1], NULL, &prev_output_channels); if (segment_output_channels != prev_output_channels) { - VGM_LOG("segmented: segment %i has wrong channels %i vs prev channels %i\n", i, segment_output_channels, prev_output_channels); + VGM_LOG("SEGMENTED: segment %i has wrong channels %i vs prev channels %i\n", i, segment_output_channels, prev_output_channels); goto fail; } /* a bit weird, but no matter */ if (data->segments[i]->sample_rate != data->segments[i-1]->sample_rate) { - VGM_LOG("segmented: segment %i has different sample rate\n", i); + VGM_LOG("SEGMENTED: segment %i has different sample rate\n", i); } /* perfectly acceptable */ @@ -180,10 +197,11 @@ int setup_layout_segmented(segmented_layout_data* data) { // goto fail; } + /* init mixing */ + mixing_setup(data->segments[i], VGMSTREAM_SEGMENT_SAMPLE_BUFFER); - setup_vgmstream(data->segments[i]); /* final setup in case the VGMSTREAM was created manually */ - - mixing_setup(data->segments[i], VGMSTREAM_SEGMENT_SAMPLE_BUFFER); /* init mixing */ + /* final setup in case the VGMSTREAM was created manually */ + setup_vgmstream(data->segments[i]); } if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS) @@ -202,7 +220,7 @@ fail: return 0; /* caller is expected to free */ } -void free_layout_segmented(segmented_layout_data *data) { +void free_layout_segmented(segmented_layout_data* data) { int i, j; if (!data) @@ -228,7 +246,7 @@ void free_layout_segmented(segmented_layout_data *data) { free(data); } -void reset_layout_segmented(segmented_layout_data *data) { +void reset_layout_segmented(segmented_layout_data* data) { int i; if (!data) @@ -241,21 +259,27 @@ void reset_layout_segmented(segmented_layout_data *data) { } /* helper for easier creation of segments */ -VGMSTREAM *allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment) { - VGMSTREAM *vgmstream = NULL; +VGMSTREAM* allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment) { + VGMSTREAM* vgmstream = NULL; int channel_layout; - int i, num_samples, loop_start, loop_end; + int i, sample_rate; + int32_t num_samples, loop_start, loop_end; /* save data */ channel_layout = data->segments[0]->channel_layout; num_samples = 0; loop_start = 0; loop_end = 0; + sample_rate = 0; for (i = 0; i < data->segment_count; i++) { + /* needs get_samples since element may use play settings */ + int32_t segment_samples = vgmstream_get_samples(data->segments[i]); + int segment_rate = data->segments[i]->sample_rate; + if (loop_flag && i == loop_start_segment) loop_start = num_samples; - num_samples += data->segments[i]->num_samples; + num_samples += segment_samples; if (loop_flag && i == loop_end_segment) loop_end = num_samples; @@ -263,6 +287,9 @@ VGMSTREAM *allocate_segmented_vgmstream(segmented_layout_data* data, int loop_fl /* inherit first segment's layout but only if all segments' layout match */ if (channel_layout != 0 && channel_layout != data->segments[i]->channel_layout) channel_layout = 0; + + if (sample_rate < segment_rate) + sample_rate = segment_rate; } /* respect loop_flag even when no loop_end found as it's possible file loops are set outside */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/acb.c b/Frameworks/vgmstream/vgmstream/src/meta/acb.c index e143ce8f6..de32d6925 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/acb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/acb.c @@ -75,6 +75,7 @@ fail: //TODO: could pre-load all sections first, but needs cache for multiple subsongs (+semaphs, if multiple read the same thing) #define ACB_TABLE_BUFFER_CUENAME 0x8000 #define ACB_TABLE_BUFFER_CUE 0x40000 +#define ACB_TABLE_BUFFER_BLOCKSEQUENCE 0x8000 #define ACB_TABLE_BUFFER_BLOCK 0x8000 #define ACB_TABLE_BUFFER_SEQUENCE 0x40000 #define ACB_TABLE_BUFFER_TRACK 0x10000 @@ -106,6 +107,7 @@ typedef struct { utf_context *CueNameTable; utf_context *CueTable; + utf_context *BlockSequenceTable; utf_context *BlockTable; utf_context *SequenceTable; utf_context *TrackTable; @@ -115,6 +117,7 @@ typedef struct { STREAMFILE* CueNameSf; STREAMFILE* CueSf; + STREAMFILE* BlockSequenceSf; STREAMFILE* BlockSf; STREAMFILE* SequenceSf; STREAMFILE* TrackSf; @@ -158,7 +161,6 @@ static int open_utf_subtable(acb_header* acb, STREAMFILE* *TableSf, utf_context* if (!*Table) goto fail; //;VGM_LOG("ACB: loaded table %s\n", TableName); - //;VGM_LOG("ACB: sf=%x\n", (uint32_t)*TableSf); return 1; fail: return 0; @@ -179,7 +181,7 @@ static void acb_cpy(char* dst, int dst_max, const char* src) { strcpy(dst, src); } -static void add_acb_name(acb_header* acb, int8_t Waveform_Streaming) { +static void add_acb_name(acb_header* acb, int8_t Streaming) { /* ignore name repeats */ if (acb->awbname_count) { @@ -198,7 +200,7 @@ static void add_acb_name(acb_header* acb, int8_t Waveform_Streaming) { else { acb_cpy(acb->name, sizeof(acb->name), acb->cuename_name); } - if (Waveform_Streaming == 2 && acb->is_memory) { + if (Streaming == 2 && acb->is_memory) { acb_cat(acb->name, sizeof(acb->name), " [pre]"); } @@ -211,35 +213,38 @@ static void add_acb_name(acb_header* acb, int8_t Waveform_Streaming) { } +/*******************************************************************************/ +/* OBJECT HANDLERS */ + static int load_acb_waveform(acb_header* acb, int16_t Index) { - uint16_t Waveform_Id; - uint8_t Waveform_Streaming; + uint16_t Id; + uint8_t Streaming; /* read Waveform[Index] */ if (!open_utf_subtable(acb, &acb->WaveformSf, &acb->WaveformTable, "WaveformTable", NULL, ACB_TABLE_BUFFER_WAVEFORM)) goto fail; - if (!utf_query_u16(acb->WaveformTable, Index, "Id", &Waveform_Id)) { /* older versions use Id */ + if (!utf_query_u16(acb->WaveformTable, Index, "Id", &Id)) { /* older versions use Id */ if (acb->is_memory) { - if (!utf_query_u16(acb->WaveformTable, Index, "MemoryAwbId", &Waveform_Id)) + if (!utf_query_u16(acb->WaveformTable, Index, "MemoryAwbId", &Id)) goto fail; } else { - if (!utf_query_u16(acb->WaveformTable, Index, "StreamAwbId", &Waveform_Id)) + if (!utf_query_u16(acb->WaveformTable, Index, "StreamAwbId", &Id)) goto fail; } } - if (!utf_query_u8(acb->WaveformTable, Index, "Streaming", &Waveform_Streaming)) + if (!utf_query_u8(acb->WaveformTable, Index, "Streaming", &Streaming)) goto fail; - //;VGM_LOG("ACB: Waveform[%i]: Id=%i, Streaming=%i\n", Index, Waveform_Id, Waveform_Streaming); + //;VGM_LOG("ACB: Waveform[%i]: Id=%i, Streaming=%i\n", Index, Id, Streaming); /* not found but valid */ - if (Waveform_Id != acb->target_waveid) + if (Id != acb->target_waveid) return 1; /* must match our target's (0=memory, 1=streaming, 2=memory (prefetch)+stream) */ - if ((acb->is_memory && Waveform_Streaming == 1) || (!acb->is_memory && Waveform_Streaming == 0)) + if ((acb->is_memory && Streaming == 1) || (!acb->is_memory && Streaming == 0)) return 1; /* aaand finally get name (phew) */ - add_acb_name(acb, Waveform_Streaming); + add_acb_name(acb, Streaming); return 1; fail: @@ -251,19 +256,18 @@ static int load_acb_sequence(acb_header* acb, int16_t Index); static int load_acb_synth(acb_header* acb, int16_t Index) { int i, count; - uint8_t Synth_Type; - uint32_t Synth_ReferenceItems_offset; - uint32_t Synth_ReferenceItems_size; + uint8_t Type; + uint32_t ReferenceItems_offset, ReferenceItems_size; /* read Synth[Index] */ if (!open_utf_subtable(acb, &acb->SynthSf, &acb->SynthTable, "SynthTable", NULL, ACB_TABLE_BUFFER_SYNTH)) goto fail; - if (!utf_query_u8(acb->SynthTable, Index, "Type", &Synth_Type)) + if (!utf_query_u8(acb->SynthTable, Index, "Type", &Type)) goto fail; - if (!utf_query_data(acb->SynthTable, Index, "ReferenceItems", &Synth_ReferenceItems_offset, &Synth_ReferenceItems_size)) + if (!utf_query_data(acb->SynthTable, Index, "ReferenceItems", &ReferenceItems_offset, &ReferenceItems_size)) goto fail; - //;VGM_LOG("ACB: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, Synth_Type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size); + //;VGM_LOG("ACB: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, Type, ReferenceItems_offset, ReferenceItems_size); acb->synth_depth++; @@ -271,6 +275,9 @@ static int load_acb_synth(acb_header* acb, int16_t Index) { VGM_LOG("ACB: Synth depth too high\n"); goto fail; /* max Synth > Synth > Waveform (ex. Yakuza 6) */ } + + //todo .CommandIndex > CommandTable + //todo .TrackValues > TrackTable? /* Cue.ReferenceType 2 uses Synth.Type, while 3 always sets it to 0 and uses Sequence.Type instead * Both look the same and probably affect which item in the ReferenceItems list is picked: @@ -287,35 +294,37 @@ static int load_acb_synth(acb_header* acb, int16_t Index) { * Since we want to find all possible Waveforms that could match our id, we ignore Type and just parse all ReferenceItems. */ - count = Synth_ReferenceItems_size / 0x04; + count = ReferenceItems_size / 0x04; for (i = 0; i < count; i++) { - uint16_t Synth_ReferenceItem_type = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x00, acb->SynthSf); - uint16_t Synth_ReferenceItem_index = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x02, acb->SynthSf); - //;VGM_LOG("ACB: Synth.ReferenceItem: type=%x, index=%x\n", Synth_ReferenceItem_type, Synth_ReferenceItem_index); + uint16_t item_type = read_u16be(ReferenceItems_offset + i*0x04 + 0x00, acb->SynthSf); + uint16_t item_index = read_u16be(ReferenceItems_offset + i*0x04 + 0x02, acb->SynthSf); + //;VGM_LOG("ACB: Synth.ReferenceItem: type=%x, index=%x\n", item_type, item_index); - switch(Synth_ReferenceItem_type) { + switch(item_type) { case 0x00: /* no reference */ count = 0; break; case 0x01: /* Waveform (most common) */ - if (!load_acb_waveform(acb, Synth_ReferenceItem_index)) + if (!load_acb_waveform(acb, item_index)) goto fail; break; case 0x02: /* Synth, possibly random (rare, found in Sonic Lost World with ReferenceType 2) */ - if (!load_acb_synth(acb, Synth_ReferenceItem_index)) + if (!load_acb_synth(acb, item_index)) goto fail; break; case 0x03: /* Sequence of Synths w/ % in Synth.TrackValues (rare, found in Sonic Lost World with ReferenceType 2) */ - if (!load_acb_sequence(acb, Synth_ReferenceItem_index)) + if (!load_acb_sequence(acb, item_index)) goto fail; break; + /* others: same as cue's ReferenceType? */ + case 0x06: /* this seems to point to Synth but results don't make sense (rare, from Sonic Lost World) */ default: /* undefined/crashes AtomViewer */ - VGM_LOG("ACB: unknown Synth.ReferenceItem type %x at %x + %x\n", Synth_ReferenceItem_type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size); + VGM_LOG("ACB: unknown Synth.ReferenceItem type %x at %x + %x\n", item_type, ReferenceItems_offset, ReferenceItems_size); count = 0; /* force end without failing */ break; } @@ -328,98 +337,133 @@ fail: return 0; } + +static int load_acb_command_tlvs(acb_header* acb, STREAMFILE* sf, uint32_t Command_offset, uint32_t Command_size) { + uint32_t offset = Command_offset; + uint32_t max_offset = Command_offset + Command_size; + uint16_t tlv_code, tlv_type, tlv_index; + uint8_t tlv_size; + + //todo read full offsets + + /* read a (name)Command multiple TLV data */ + while (offset < max_offset) { + tlv_code = read_u16be(offset + 0x00, sf); + tlv_size = read_u8 (offset + 0x02, sf); + offset += 0x03; + + /* There are around 160 codes (some unused), with things like set volume, pan, stop, mute, and so on. + * Multiple commands are linked and only "note on" seems to point so other objects, so maybe others + * apply to current object (since there is "note off" without reference. */ + switch(tlv_code) { + case 2000: /* noteOn */ + case 2003: /* noteOnWithNo plus 16b (null?) [rare, ex. PES 2014] */ + if (tlv_size < 0x04) { + VGM_LOG("ACB: TLV with unknown size\n"); + break; + } + + tlv_type = read_u16be(offset + 0x00, sf); /* ReferenceItem */ + tlv_index = read_u16be(offset + 0x02, sf); + //;VGM_LOG("ACB: TLV at %x: type %x, index=%x\n", offset, tlv_type, tlv_index); + + /* same as Synth's ReferenceItem type? */ + switch(tlv_type) { + case 0x02: /* Synth (common) */ + if (!load_acb_synth(acb, tlv_index)) + goto fail; + break; + + case 0x03: /* Sequence (common, ex. Yakuza 6, Yakuza Kiwami 2) */ + if (!load_acb_sequence(acb, tlv_index)) + goto fail; + break; + + default: + VGM_LOG("ACB: unknown TLV type %x at %x + %x\n", tlv_type, offset, tlv_size); + max_offset = 0; /* force end without failing */ + break; + } + break; + + case 2004: /* noteOnWithDuration */ + /* same as the above plus extra field */ + //;VGM_LOG("ACB: TLV at %x: usable code %i?\n", offset-0x03, tlv_code); + break; + + case 33: /* mute */ + case 124: /* stopAtLoopEnd */ + case 1000: /* noteOff */ + case 1251: /* sequenceCallbackWithId */ + case 1252: /* sequenceCallbackWithString */ + case 1253: /* sequenceCallbackWithIdAndString */ + case 2002: /* setSynthOrWaveform */ + case 4051: /* transitionTrack */ + case 7102: /* muteTrackAction */ + case 7100: /* startAction */ + case 7101: /* stopAction */ + /* may be needed? */ + //;VGM_LOG("ACB: TLV at %x: check code %i?\n", offset-0x03, tlv_code); + break; + + case 0: /* no-op */ + case 998: /* sequenceStartRandom (plays following note ons in random?)*/ + case 999: /* sequenceStart (plays following note ons in sequence?) */ + default: /* ignore others */ + break; + } + + offset += tlv_size; + } + + return 1; +fail: + return 0; +} + static int load_acb_track_event_command(acb_header* acb, int16_t Index) { - uint16_t Track_EventIndex; - uint32_t Track_Command_offset; - uint32_t Track_Command_size; + uint16_t EventIndex; + uint32_t Command_offset, Command_size; /* read Track[Index] */ if (!open_utf_subtable(acb, &acb->TrackSf, &acb->TrackTable, "TrackTable", NULL, ACB_TABLE_BUFFER_TRACK )) goto fail; - if (!utf_query_u16(acb->TrackTable, Index, "EventIndex", &Track_EventIndex)) + if (!utf_query_u16(acb->TrackTable, Index, "EventIndex", &EventIndex)) goto fail; - //;VGM_LOG("ACB: Track[%i]: EventIndex=%i\n", Index, Track_EventIndex); + //;VGM_LOG("ACB: Track[%i]: EventIndex=%i\n", Index, EventIndex); + + //todo CommandIndex? + + /* happens with some odd track without anything useful */ + if (EventIndex == 65535) + return 1; /* next link varies with version, check by table existence */ if (acb->has_CommandTable) { /* <=v1.27 */ /* read Command[EventIndex] */ if (!open_utf_subtable(acb, &acb->TrackCommandSf, &acb->TrackCommandTable, "CommandTable", NULL, ACB_TABLE_BUFFER_TRACKCOMMAND)) goto fail; - if (!utf_query_data(acb->TrackCommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size)) + if (!utf_query_data(acb->TrackCommandTable, EventIndex, "Command", &Command_offset, &Command_size)) goto fail; - //;VGM_LOG("ACB: Command[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size); + //;VGM_LOG("ACB: Command[%i]: Command={%x,%x}\n", EventIndex, Command_offset,Command_size); } else if (acb->has_TrackEventTable) { /* >=v1.28 */ /* read TrackEvent[EventIndex] */ if (!open_utf_subtable(acb, &acb->TrackCommandSf, &acb->TrackCommandTable, "TrackEventTable", NULL, ACB_TABLE_BUFFER_TRACKCOMMAND)) goto fail; - if (!utf_query_data(acb->TrackCommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size)) + if (!utf_query_data(acb->TrackCommandTable, EventIndex, "Command", &Command_offset, &Command_size)) goto fail; - //;VGM_LOG("ACB: TrackEvent[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size); + //;VGM_LOG("ACB: TrackEvent[%i]: Command={%x,%x}\n", EventIndex, Command_offset,Command_size); } else { VGM_LOG("ACB: unknown command table\n"); goto fail; } - /* read Command (some kind of multiple TLVs, this seems ok) */ - { - uint32_t offset = Track_Command_offset; - uint32_t max_offset = Track_Command_offset + Track_Command_size; - uint16_t tlv_code, tlv_type, tlv_index; - uint8_t tlv_size; - - - while (offset < max_offset) { - tlv_code = read_u16be(offset + 0x00, acb->TrackCommandSf); - tlv_size = read_u8 (offset + 0x02, acb->TrackCommandSf); - offset += 0x03; - - if (tlv_code == 0x07D0) { - if (tlv_size < 0x04) { - VGM_LOG("ACB: TLV with unknown size\n"); - break; - } - - tlv_type = read_u16be(offset + 0x00, acb->TrackCommandSf); - tlv_index = read_u16be(offset + 0x02, acb->TrackCommandSf); - //;VGM_LOG("ACB: TLV at %x: type %x, index=%x\n", offset, tlv_type, tlv_index); - - /* probably same as Synth_ReferenceItem_type */ - switch(tlv_type) { - - case 0x02: /* Synth (common) */ - if (!load_acb_synth(acb, tlv_index)) - goto fail; - break; - - case 0x03: /* Sequence of Synths (common, ex. Yakuza 6, Yakuza Kiwami 2) */ - if (!load_acb_sequence(acb, tlv_index)) - goto fail; - break; - - /* possible values? (from debug): - * - sequence - * - track - * - synth - * - trackEvent - * - seqParameterPallet, - * - trackParameterPallet, - * - synthParameterPallet, - */ - default: - VGM_LOG("ACB: unknown TLV type %x at %x + %x\n", tlv_type, offset, tlv_size); - max_offset = 0; /* force end without failing */ - break; - } - } - - /* 0x07D1 comes suspiciously often paired with 0x07D0 too */ - - offset += tlv_size; - } - } + /* read Command's TLVs */ + if (!load_acb_command_tlvs(acb, acb->TrackCommandSf, Command_offset, Command_size)) + goto fail; return 1; fail: @@ -428,19 +472,20 @@ fail: static int load_acb_sequence(acb_header* acb, int16_t Index) { int i; - uint16_t Sequence_NumTracks; - uint32_t Sequence_TrackIndex_offset; - uint32_t Sequence_TrackIndex_size; + uint16_t NumTracks; + uint32_t TrackIndex_offset, TrackIndex_size; /* read Sequence[Index] */ if (!open_utf_subtable(acb, &acb->SequenceSf, &acb->SequenceTable, "SequenceTable", NULL, ACB_TABLE_BUFFER_SEQUENCE)) goto fail; - if (!utf_query_u16(acb->SequenceTable, Index, "NumTracks", &Sequence_NumTracks)) + if (!utf_query_u16(acb->SequenceTable, Index, "NumTracks", &NumTracks)) goto fail; - if (!utf_query_data(acb->SequenceTable, Index, "TrackIndex", &Sequence_TrackIndex_offset, &Sequence_TrackIndex_size)) + if (!utf_query_data(acb->SequenceTable, Index, "TrackIndex", &TrackIndex_offset, &TrackIndex_size)) goto fail; - //;VGM_LOG("ACB: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Sequence_NumTracks, Sequence_TrackIndex_offset,Sequence_TrackIndex_size); + //;VGM_LOG("ACB: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, NumTracks, TrackIndex_offset,TrackIndex_size); + + //todo .CommandIndex > SequenceCommand? acb->sequence_depth++; @@ -449,16 +494,16 @@ static int load_acb_sequence(acb_header* acb, int16_t Index) { goto fail; /* max Sequence > Sequence > Sequence > Synth > Waveform (ex. Yakuza 6) */ } - if (Sequence_NumTracks * 0x02 > Sequence_TrackIndex_size) { /* padding may exist */ + if (NumTracks * 0x02 > TrackIndex_size) { /* padding may exist */ VGM_LOG("ACB: wrong Sequence.TrackIndex size\n"); goto fail; } /* read Tracks inside Sequence */ - for (i = 0; i < Sequence_NumTracks; i++) { - int16_t Sequence_TrackIndex_index = read_s16be(Sequence_TrackIndex_offset + i*0x02, acb->SequenceSf); + for (i = 0; i < NumTracks; i++) { + int16_t TrackIndex_index = read_s16be(TrackIndex_offset + i*0x02, acb->SequenceSf); - if (!load_acb_track_event_command(acb, Sequence_TrackIndex_index)) + if (!load_acb_track_event_command(acb, TrackIndex_index)) goto fail; } @@ -471,75 +516,129 @@ fail: static int load_acb_block(acb_header* acb, int16_t Index) { int i; - uint16_t Block_NumTracks; - uint32_t Block_TrackIndex_offset; - uint32_t Block_TrackIndex_size; + uint16_t NumTracks; + uint32_t TrackIndex_offset, TrackIndex_size; /* read Block[Index] */ if (!open_utf_subtable(acb, &acb->BlockSf, &acb->BlockTable, "BlockTable", NULL, ACB_TABLE_BUFFER_BLOCK)) goto fail; - if (!utf_query_u16(acb->BlockTable, Index, "NumTracks", &Block_NumTracks)) + if (!utf_query_u16(acb->BlockTable, Index, "NumTracks", &NumTracks)) goto fail; - if (!utf_query_data(acb->BlockTable, Index, "TrackIndex", &Block_TrackIndex_offset, &Block_TrackIndex_size)) + if (!utf_query_data(acb->BlockTable, Index, "TrackIndex", &TrackIndex_offset, &TrackIndex_size)) goto fail; - //;VGM_LOG("ACB: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Block_NumTracks, Block_TrackIndex_offset,Block_TrackIndex_size); + //;VGM_LOG("ACB: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, NumTracks, TrackIndex_offset,TrackIndex_size); - if (Block_NumTracks * 0x02 > Block_TrackIndex_size) { /* padding may exist */ + if (NumTracks * 0x02 > TrackIndex_size) { /* padding may exist */ VGM_LOG("ACB: wrong Block.TrackIndex size\n"); goto fail; } + + //todo .ActionTrackStartIndex/NumActionTracks > ? /* read Tracks inside Block */ - for (i = 0; i < Block_NumTracks; i++) { - int16_t Block_TrackIndex_index = read_s16be(Block_TrackIndex_offset + i*0x02, acb->BlockSf); + for (i = 0; i < NumTracks; i++) { + int16_t TrackIndex_index = read_s16be(TrackIndex_offset + i*0x02, acb->BlockSf); - if (!load_acb_track_event_command(acb, Block_TrackIndex_index)) + if (!load_acb_track_event_command(acb, TrackIndex_index)) goto fail; } return 1; fail: return 0; +} +static int load_acb_blocksequence(acb_header* acb, int16_t Index) { + int i; + + uint16_t NumTracks; + uint32_t TrackIndex_offset, TrackIndex_size; + uint16_t NumBlocks; + uint32_t BlockIndex_offset, BlockIndex_size; + + + /* read BlockSequence[Index] */ + if (!open_utf_subtable(acb, &acb->BlockSequenceSf, &acb->BlockSequenceTable, "BlockSequenceTable", NULL, ACB_TABLE_BUFFER_BLOCKSEQUENCE)) + goto fail; + + if (!utf_query_u16(acb->BlockSequenceTable, Index, "NumTracks", &NumTracks)) + goto fail; + if (!utf_query_data(acb->BlockSequenceTable, Index, "TrackIndex", &TrackIndex_offset, &TrackIndex_size)) + goto fail; + if (!utf_query_u16(acb->BlockSequenceTable, Index, "NumBlocks", &NumBlocks)) + goto fail; + if (!utf_query_data(acb->BlockSequenceTable, Index, "BlockIndex", &BlockIndex_offset, &BlockIndex_size)) + goto fail; + //;VGM_LOG("ACB: BlockSequence[%i]: NumTracks=%i, TrackIndex={%x, %x}, NumBlocks=%i, BlockIndex={%x, %x}\n", Index, NumTracks, TrackIndex_offset,TrackIndex_size, NumBlocks, BlockIndex_offset,BlockIndex_size); + + + if (NumTracks * 0x02 > TrackIndex_size) { /* padding may exist */ + VGM_LOG("ACB: wrong BlockSequence.TrackIndex size\n"); + goto fail; + } + + /* read Tracks inside BlockSequence */ + for (i = 0; i < NumTracks; i++) { + int16_t TrackIndex_index = read_s16be(TrackIndex_offset + i*0x02, acb->BlockSequenceSf); + + if (!load_acb_track_event_command(acb, TrackIndex_index)) + goto fail; + } + + if (NumBlocks * 0x02 > BlockIndex_size) { + VGM_LOG("ACB: wrong BlockSequence.BlockIndex size\n"); + goto fail; + } + + /* read Blocks inside BlockSequence */ + for (i = 0; i < NumBlocks; i++) { + int16_t BlockIndex_index = read_s16be(BlockIndex_offset + i*0x02, acb->BlockSequenceSf); + + if (!load_acb_block(acb, BlockIndex_index)) + goto fail; + } + + return 1; +fail: + return 0; } static int load_acb_cue(acb_header* acb, int16_t Index) { - uint8_t Cue_ReferenceType; - uint16_t Cue_ReferenceIndex; + uint8_t ReferenceType; + uint16_t ReferenceIndex; /* read Cue[Index] */ if (!open_utf_subtable(acb, &acb->CueSf, &acb->CueTable, "CueTable", NULL, ACB_TABLE_BUFFER_CUE)) goto fail; - if (!utf_query_u8(acb->CueTable, Index, "ReferenceType", &Cue_ReferenceType)) + if (!utf_query_u8(acb->CueTable, Index, "ReferenceType", &ReferenceType)) goto fail; - if (!utf_query_u16(acb->CueTable, Index, "ReferenceIndex", &Cue_ReferenceIndex)) + if (!utf_query_u16(acb->CueTable, Index, "ReferenceIndex", &ReferenceIndex)) goto fail; - //;VGM_LOG("ACB: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, Cue_ReferenceType, Cue_ReferenceIndex); + //;VGM_LOG("ACB: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, ReferenceType, ReferenceIndex); /* usually older games use older references but not necessarily */ - switch(Cue_ReferenceType) { + switch(ReferenceType) { case 0x01: /* Cue > Waveform (ex. PES 2015) */ - if (!load_acb_waveform(acb, Cue_ReferenceIndex)) + if (!load_acb_waveform(acb, ReferenceIndex)) goto fail; break; case 0x02: /* Cue > Synth > Waveform (ex. Ukiyo no Roushi) */ - if (!load_acb_synth(acb, Cue_ReferenceIndex)) + if (!load_acb_synth(acb, ReferenceIndex)) goto fail; break; case 0x03: /* Cue > Sequence > Track > Command > Synth > Waveform (ex. Valkyrie Profile anatomia, Yakuza Kiwami 2) */ - if (!load_acb_sequence(acb, Cue_ReferenceIndex)) + if (!load_acb_sequence(acb, ReferenceIndex)) goto fail; break; - //todo "blockSequence"? - case 0x08: /* Cue > Block > Track > Command > Synth > Waveform (ex. Sonic Lost World, rare) */ - if (!load_acb_block(acb, Cue_ReferenceIndex)) + case 0x08: /* Cue > BlockSequence > Track / Block > Track > Command > Synth > Waveform (ex. Sonic Lost World, Kandagawa Jet Girls, rare) */ + if (!load_acb_blocksequence(acb, ReferenceIndex)) goto fail; break; @@ -552,7 +651,7 @@ static int load_acb_cue(acb_header* acb, int16_t Index) { case 0x0a: /* "eventCue_UnUse" */ case 0x0b: /* "soundGenerator" */ default: - VGM_LOG("ACB: unknown Cue.ReferenceType=%x, Cue.ReferenceIndex=%x\n", Cue_ReferenceType, Cue_ReferenceIndex); + VGM_LOG("ACB: unknown Cue.ReferenceType=%x, Cue.ReferenceIndex=%x\n", ReferenceType, ReferenceIndex); break; /* ignore and continue */ } @@ -560,29 +659,28 @@ static int load_acb_cue(acb_header* acb, int16_t Index) { return 1; fail: return 0; - } static int load_acb_cuename(acb_header* acb, int16_t Index) { - uint16_t CueName_CueIndex; - const char* CueName_CueName; + uint16_t CueIndex; + const char* CueName; /* read CueName[Index] */ if (!open_utf_subtable(acb, &acb->CueNameSf, &acb->CueNameTable, "CueNameTable", NULL, ACB_TABLE_BUFFER_CUENAME)) goto fail; - if (!utf_query_u16(acb->CueNameTable, Index, "CueIndex", &CueName_CueIndex)) + if (!utf_query_u16(acb->CueNameTable, Index, "CueIndex", &CueIndex)) goto fail; - if (!utf_query_string(acb->CueNameTable, Index, "CueName", &CueName_CueName)) + if (!utf_query_string(acb->CueNameTable, Index, "CueName", &CueName)) goto fail; - //;VGM_LOG("ACB: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, CueName_CueIndex, CueName_CueName); + //;VGM_LOG("ACB: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, CueIndex, CueName); /* save as will be needed if references waveform */ acb->cuename_index = Index; - acb->cuename_name = CueName_CueName; + acb->cuename_name = CueName; - if (!load_acb_cue(acb, CueName_CueIndex)) + if (!load_acb_cue(acb, CueIndex)) goto fail; return 1; @@ -615,10 +713,10 @@ void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int is * - CueName > Cue > Sequence > Track > TrackEvent > Command > Synth > Synth > Waveform (type 3, >=v1.28) * - CueName > Cue > Sequence > Track > TrackEvent > Command > Sequence > (...) > Synth > Waveform (type 3, >=v1.28) * - CueName > Cue > Block > Track > Command > Synth > Synth > Waveform (type 8) - * - others should be possible but haven't been observed + * - others should be possible * Atom Craft may only target certain .acb versions so some links are later removed - * Not all cues to point to though Waveforms, as some are just config events/commands. - * .acb link to .awb by name (loaded manually), though they have a checksum/hash to validate. + * Not all cues to point to Waveforms, some are just config events/commands. + * .acb link to .awb by name (loaded manually), though they have a checksum/hash/header to validate. */ //;VGM_LOG("ACB: find waveid=%i\n", waveid); @@ -654,6 +752,8 @@ fail: utf_close(acb.CueNameTable); utf_close(acb.CueTable); + utf_close(acb.BlockSequenceTable); + utf_close(acb.BlockTable); utf_close(acb.SequenceTable); utf_close(acb.TrackTable); utf_close(acb.TrackCommandTable); @@ -662,6 +762,8 @@ fail: close_streamfile(acb.CueNameSf); close_streamfile(acb.CueSf); + close_streamfile(acb.BlockSequenceSf); + close_streamfile(acb.BlockSf); close_streamfile(acb.SequenceSf); close_streamfile(acb.TrackSf); close_streamfile(acb.TrackCommandSf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adp_konami.c b/Frameworks/vgmstream/vgmstream/src/meta/adp_konami.c new file mode 100644 index 000000000..8d458eb5c --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/adp_konami.c @@ -0,0 +1,51 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* ADP - from Konami Viper arcade games [ParaParaParadise 2ndMIX (AC)] */ +VGMSTREAM* init_vgmstream_adp_konami(STREAMFILE* sf) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channels; + size_t data_size, file_size; + + + /* checks */ + if (!check_extensions(sf, "adp")) + goto fail; + if (read_u32be(0x00,sf) != 0x41445002) /* "ADP\2" */ + goto fail; + + start_offset = 0x10; + channels = 2; /* probably @0x03 */ + loop_flag = 0; + + data_size = read_u32be(0x04,sf); + file_size = get_streamfile_size(sf); + if (!(data_size + start_offset - 0x04 <= file_size && + data_size + start_offset + 0x04 >= file_size)) /* 1 byte padding in some files */ + goto fail; + + if (read_u32be(0x08,sf) != 0 || read_u32be(0x0c,sf) != 0) /* maybe reserved for loop points */ + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_ADP_KONAMI; + vgmstream->sample_rate = 44100; + + vgmstream->num_samples = oki_bytes_to_samples(data_size, channels); + + vgmstream->coding_type = coding_OKI4S; + vgmstream->layout_type = layout_none; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx.c b/Frameworks/vgmstream/vgmstream/src/meta/adx.c index 5b90087c5..35d721b52 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx.c @@ -79,12 +79,14 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { coding_type = coding_CRI_ADX_enc_8; version = 0x0400; } + VGM_ASSERT(version != 0x0400, "ADX: keystring not found\n"); } else if (version == 0x0409) { if (find_adx_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) { coding_type = coding_CRI_ADX_enc_9; version = 0x0400; } + VGM_ASSERT(version != 0x0400, "ADX: keycode not found\n"); } /* version + extra data */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index ea27d5426..158b978ca 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -196,6 +196,9 @@ static const adxkey_info adxkey8_list[] = { /* 428: Fuusasareta Shibuya de (PS3) */ {0x52ff,0x649f,0x448f, "hj1kviaqqdzUacryoacwmscfvwtlfkVbbbqpqmzqnbile2euljywazejgyxxvqlf",0}, + /* Mirai Nikki: 13-ninme no Nikki Shoyuusha Re-Write (PSP) */ + {0x58a3,0x66f5,0x599f, "FDRW17th",0}, + }; static const adxkey_info adxkey9_list[] = { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/akb.c b/Frameworks/vgmstream/vgmstream/src/meta/akb.c index 422a5676f..15a12ea8f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/akb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/akb.c @@ -128,12 +128,8 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) { /* Alt decoding without libvorbis (minor number of beginning samples difference). * Otherwise same output with (inaudible) +-1 lower byte differences due to rounding. */ case 0x05: { /* Ogg Vorbis [Final Fantasy VI (iOS), Dragon Quest II-VI (iOS)] */ - ffmpeg_codec_data *ffmpeg_data; - - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size); - if ( !ffmpeg_data ) goto fail; - - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset,stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; /* These oggs have loop info in the comments, too */ @@ -149,12 +145,8 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG case 0x06: { /* M4A with AAC [The World Ends with You (iPad)] */ /* init_vgmstream_akb_mp4 above has priority, but this works fine too */ - ffmpeg_codec_data *ffmpeg_data; - - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size-start_offset); - if ( !ffmpeg_data ) goto fail; - - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset,stream_size-start_offset); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ast.c b/Frameworks/vgmstream/vgmstream/src/meta/ast.c index ede135947..5bd2f94ad 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ast.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ast.c @@ -7,37 +7,51 @@ VGMSTREAM * init_vgmstream_ast(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count, codec; + int big_endian; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; /* checks */ if (!check_extensions(streamFile, "ast")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */ + + if (((uint32_t)read_32bitBE(0x00, streamFile) == 0x5354524D) && /* "STRM" */ + ((uint32_t)read_32bitBE(0x40, streamFile) == 0x424C434B)) { /* "BLCK" */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + big_endian = 1; + } else if (((uint32_t)read_32bitBE(0x00, streamFile) == 0x4D525453) && /* "MRTS" */ // Super Mario Galaxy (Super Mario 3D All-Stars (Switch)) + ((uint32_t)read_32bitBE(0x40, streamFile) == 0x4B434C42)) { /* "KCLB" */ + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + big_endian = 0; + } else { goto fail; - if (read_16bitBE(0x0a,streamFile) != 0x10) /* ? */ + } + + if (read_16bit(0x0a,streamFile) != 0x10) /* ? */ goto fail; - if (read_32bitBE(0x04,streamFile)+0x40 != get_streamfile_size(streamFile)) - goto fail; - codec = read_16bitBE(0x08,streamFile); - channel_count = read_16bitBE(0x0c,streamFile); - loop_flag = read_16bitBE(0x0e,streamFile); - //max_block = read_32bitBE(0x20,streamFile); - - start_offset = 0x40; - if (read_32bitBE(start_offset,streamFile) != 0x424C434B) /* "BLCK" */ + if (read_32bit(0x04,streamFile)+0x40 != get_streamfile_size(streamFile)) goto fail; + codec = read_16bitBE(0x08,streamFile); // always big-endian? + channel_count = read_16bit(0x0c,streamFile); + loop_flag = read_16bit(0x0e,streamFile); + //max_block = read_32bit(0x20,streamFile); + start_offset = 0x40; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_AST; - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - vgmstream->num_samples = read_32bitBE(0x14,streamFile); - vgmstream->loop_start_sample = read_32bitBE(0x18,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x1c,streamFile); + vgmstream->sample_rate = read_32bit(0x10,streamFile); + vgmstream->num_samples = read_32bit(0x14,streamFile); + vgmstream->loop_start_sample = read_32bit(0x18,streamFile); + vgmstream->loop_end_sample = read_32bit(0x1c,streamFile); + vgmstream->codec_endian = big_endian; vgmstream->layout_type = layout_blocked_ast; switch (codec) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awb.c b/Frameworks/vgmstream/vgmstream/src/meta/awb.c index ede63671b..b0be134b0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awb.c @@ -1,235 +1,236 @@ -#include "meta.h" -#include "../coding/coding.h" - -typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP } awb_type; - -static void load_awb_name(STREAMFILE *streamFile, STREAMFILE *acbFile, VGMSTREAM *vgmstream, int waveid); - -/* AFS2/AWB (Atom Wave Bank) - CRI container of streaming audio, often together with a .acb cue sheet */ -VGMSTREAM * init_vgmstream_awb(STREAMFILE *streamFile) { - return init_vgmstream_awb_memory(streamFile, NULL); -} - -VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE *streamFile, STREAMFILE *acbFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; - off_t offset, subfile_offset, subfile_next; - size_t subfile_size; - int total_subsongs, target_subsong = streamFile->stream_index; - //uint32_t flags; - uint8_t offset_size; - uint16_t alignment, subkey; - awb_type type; - char *extension = NULL; - int waveid; - - - /* checks - * .awb: standard - * .afs2: sometimes [Okami HD (PS4)] */ - if (!check_extensions(streamFile, "awb,afs2")) - goto fail; - if (read_u32be(0x00,streamFile) != 0x41465332) /* "AFS2" */ - goto fail; - - //flags = read_32bitLE(0x08,streamFile); - /* 0x04(1): version? 0x01=common, 0x02=2018+ (no apparent differences) */ - offset_size = read_u8(0x05,streamFile); - /* 0x06(2): always 0x0002? */ - total_subsongs = read_s32le(0x08,streamFile); - alignment = read_u16le(0x0c,streamFile); - subkey = read_u16le(0x0e,streamFile); - - if (target_subsong == 0) target_subsong = 1; - if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; - - offset = 0x10; - - /* id(?) table: read target */ - { - off_t waveid_offset = offset + (target_subsong-1) * 0x02; - - waveid = read_u16le(waveid_offset,streamFile); - - offset += total_subsongs * 0x02; - } - - /* offset table: find target */ - { - off_t file_size = get_streamfile_size(streamFile); - - /* last sub-offset is always file end, so table entries = total_subsongs+1 */ - offset += (target_subsong-1) * offset_size; - - switch(offset_size) { - case 0x04: /* common */ - subfile_offset = read_u32le(offset+0x00,streamFile); - subfile_next = read_u32le(offset+0x04,streamFile); - break; - case 0x02: /* mostly sfx in .acb */ - subfile_offset = read_u16le(offset+0x00,streamFile); - subfile_next = read_u16le(offset+0x02,streamFile); - break; - default: - VGM_LOG("AWB: unknown offset size\n"); - goto fail; - } - - /* offset are absolute but sometimes misaligned (specially first that just points to offset table end) */ - subfile_offset += (subfile_offset % alignment) ? - alignment - (subfile_offset % alignment) : 0; - subfile_next += (subfile_next % alignment) && subfile_next < file_size ? - alignment - (subfile_next % alignment) : 0; - subfile_size = subfile_next - subfile_offset; - } - - //;VGM_LOG("AWB: subfile offset=%lx + %x\n", subfile_offset, subfile_size); - - /* autodetect as there isn't anything, plus can mix types - * (waveid<>codec info is usually in the companion .acb) */ - if (read_u16be(subfile_offset, streamFile) == 0x8000) { /* ADX id (type 0) */ - type = ADX; - extension = "adx"; - } - else if ((read_u32be(subfile_offset,streamFile) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" (type 2=HCA, 6=HCA-MX) */ - type = HCA; - extension = "hca"; - } - else if (read_u32be(subfile_offset,streamFile) == 0x56414770) { /* "VAGp" (type 7=VAG, 10=HEVAG) */ - type = VAG; - extension = "vag"; - } - else if (read_u32be(subfile_offset,streamFile) == 0x52494646) { /* "RIFF" (type 8=ATRAC3, 11=ATRAC9) */ - type = RIFF; - extension = "wav"; - } - else if (read_u32be(subfile_offset,streamFile) == 0x43574156) { /* "CWAV" (type 9) */ - type = CWAV; - extension = "bcwav"; - } - else if (read_u32be(subfile_offset + 0x08,streamFile) >= 8000 && - read_u32be(subfile_offset + 0x08,streamFile) <= 48000 && - read_u16be(subfile_offset + 0x0e,streamFile) == 0 && - read_u32be(subfile_offset + 0x18,streamFile) == 2 && - read_u32be(subfile_offset + 0x50,streamFile) == 0) { /* probably should call some check function (type 13) */ - type = DSP; - extension = "dsp"; - } - else { - VGM_LOG("AWB: unknown codec\n"); - goto fail; - } - - - temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, extension); - if (!temp_streamFile) goto fail; - - switch(type) { - case HCA: /* most common */ - vgmstream = init_vgmstream_hca_subkey(temp_streamFile, subkey); - if (!vgmstream) goto fail; - break; - case ADX: /* Okami HD (PS4) */ - vgmstream = init_vgmstream_adx(temp_streamFile); - if (!vgmstream) goto fail; - break; - case VAG: /* Ukiyo no Roushi (Vita) */ - vgmstream = init_vgmstream_vag(temp_streamFile); - if (!vgmstream) goto fail; - break; - case RIFF: /* Ukiyo no Roushi (Vita) */ - vgmstream = init_vgmstream_riff(temp_streamFile); - if (!vgmstream) goto fail; - break; - case CWAV: /* Sonic: Lost World (3DS) */ - vgmstream = init_vgmstream_rwsd(temp_streamFile); - if (!vgmstream) goto fail; - break; - case DSP: /* Sonic: Lost World (WiiU) */ - vgmstream = init_vgmstream_ngc_dsp_std(temp_streamFile); - if (!vgmstream) goto fail; - break; - default: - goto fail; - } - - vgmstream->num_streams = total_subsongs; - - /* try to load cue names */ - load_awb_name(streamFile, acbFile, vgmstream, waveid); - - close_streamfile(temp_streamFile); - return vgmstream; - -fail: - close_streamfile(temp_streamFile); - close_vgmstream(vgmstream); - return NULL; -} - - -static void load_awb_name(STREAMFILE *streamFile, STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid) { - int is_memory = (acbFile != NULL); - - /* .acb is passed when loading memory .awb inside .acb */ - if (!is_memory) { - /* load companion .acb using known pairs */ //todo improve, see xsb code - char filename[PATH_LIMIT]; - int len_name, len_cmp; - - - /* try (name).awb + (name).awb */ - acbFile = open_streamfile_by_ext(streamFile, "acb"); - - /* try (name)_streamfiles.awb + (name).acb */ - if (!acbFile) { - char *cmp = "_streamfiles"; - get_streamfile_basename(streamFile, filename, sizeof(filename)); - len_name = strlen(filename); - len_cmp = strlen(cmp); - - if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { - filename[len_name - len_cmp] = '\0'; - strcat(filename, ".acb"); - acbFile = open_streamfile_by_filename(streamFile, filename); - } - } - - /* try (name)_STR.awb + (name).acb */ - if (!acbFile) { - char *cmp = "_STR"; - get_streamfile_basename(streamFile, filename, sizeof(filename)); - len_name = strlen(filename); - len_cmp = strlen(cmp); - - if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { - filename[len_name - len_cmp] = '\0'; - strcat(filename, ".acb"); - acbFile = open_streamfile_by_filename(streamFile, filename); - } - } - - /* try (name)_(name)_R001.awb + (name).acb [Sengoku Basara Battle Party (Mobile)] */ - if (!acbFile) { - char *cmp = "_R001"; - get_streamfile_basename(streamFile, filename, sizeof(filename)); - len_name = strlen(filename); - len_cmp = strlen(cmp); - - if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { - filename[(len_name - len_cmp) / 2] = '\0'; - strcat(filename, ".acb"); - VGM_LOG("%s\n", filename); - acbFile = open_streamfile_by_filename(streamFile, filename); - } - } - - /* probably loaded */ - load_acb_wave_name(acbFile, vgmstream, waveid, is_memory); - - close_streamfile(acbFile); - } - else { - load_acb_wave_name(acbFile, vgmstream, waveid, is_memory); - } -} +#include "meta.h" +#include "../coding/coding.h" + +typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP } awb_type; + +static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid); + +/* AFS2/AWB (Atom Wave Bank) - CRI container of streaming audio, often together with a .acb cue sheet */ +VGMSTREAM* init_vgmstream_awb(STREAMFILE* sf) { + return init_vgmstream_awb_memory(sf, NULL); +} + +VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + off_t offset, subfile_offset, subfile_next; + size_t subfile_size; + int total_subsongs, target_subsong = sf->stream_index; + //uint32_t flags; + uint8_t offset_size; + uint16_t alignment, subkey; + awb_type type; + const char* extension = NULL; + int waveid; + + + /* checks + * .awb: standard + * .afs2: sometimes [Okami HD (PS4)] */ + if (!check_extensions(sf, "awb,afs2")) + goto fail; + if (read_u32be(0x00,sf) != 0x41465332) /* "AFS2" */ + goto fail; + + //flags = read_32bitLE(0x08,sf); + /* 0x04(1): version? 0x01=common, 0x02=2018+ (no apparent differences) */ + offset_size = read_u8(0x05,sf); + /* 0x06(2): always 0x0002? */ + total_subsongs = read_s32le(0x08,sf); + alignment = read_u16le(0x0c,sf); + subkey = read_u16le(0x0e,sf); + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; + + offset = 0x10; + + /* id(?) table: read target */ + { + off_t waveid_offset = offset + (target_subsong-1) * 0x02; + + waveid = read_u16le(waveid_offset,sf); + + offset += total_subsongs * 0x02; + } + + /* offset table: find target */ + { + off_t file_size = get_streamfile_size(sf); + + /* last sub-offset is always file end, so table entries = total_subsongs+1 */ + offset += (target_subsong-1) * offset_size; + + switch(offset_size) { + case 0x04: /* common */ + subfile_offset = read_u32le(offset+0x00,sf); + subfile_next = read_u32le(offset+0x04,sf); + break; + case 0x02: /* mostly sfx in .acb */ + subfile_offset = read_u16le(offset+0x00,sf); + subfile_next = read_u16le(offset+0x02,sf); + break; + default: + VGM_LOG("AWB: unknown offset size\n"); + goto fail; + } + + /* offset are absolute but sometimes misaligned (specially first that just points to offset table end) */ + subfile_offset += (subfile_offset % alignment) ? + alignment - (subfile_offset % alignment) : 0; + subfile_next += (subfile_next % alignment) && subfile_next < file_size ? + alignment - (subfile_next % alignment) : 0; + subfile_size = subfile_next - subfile_offset; + } + + //;VGM_LOG("AWB: subfile offset=%lx + %x\n", subfile_offset, subfile_size); + + /* autodetect as there isn't anything, plus can mix types + * (waveid<>codec info is usually in the companion .acb) */ + if (read_u16be(subfile_offset, sf) == 0x8000) { /* ADX id (type 0) */ + type = ADX; + extension = "adx"; + } + else if ((read_u32be(subfile_offset,sf) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" (type 2=HCA, 6=HCA-MX) */ + type = HCA; + extension = "hca"; + } + else if (read_u32be(subfile_offset,sf) == 0x56414770) { /* "VAGp" (type 7=VAG, 10=HEVAG) */ + type = VAG; + extension = "vag"; + } + else if (read_u32be(subfile_offset,sf) == 0x52494646) { /* "RIFF" (type 8=ATRAC3, 11=ATRAC9) */ + type = RIFF; + extension = "wav"; + subfile_size = read_u32le(subfile_offset + 0x04,sf) + 0x08; /* rough size, use RIFF's */ + } + else if (read_u32be(subfile_offset,sf) == 0x43574156) { /* "CWAV" (type 9) */ + type = CWAV; + extension = "bcwav"; + } + else if (read_u32be(subfile_offset + 0x08,sf) >= 8000 && + read_u32be(subfile_offset + 0x08,sf) <= 48000 && + read_u16be(subfile_offset + 0x0e,sf) == 0 && + read_u32be(subfile_offset + 0x18,sf) == 2 && + read_u32be(subfile_offset + 0x50,sf) == 0) { /* probably should call some check function (type 13) */ + type = DSP; + extension = "dsp"; + } + else { + VGM_LOG("AWB: unknown codec\n"); + goto fail; + } + + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension); + if (!temp_sf) goto fail; + + switch(type) { + case HCA: /* most common */ + vgmstream = init_vgmstream_hca_subkey(temp_sf, subkey); + if (!vgmstream) goto fail; + break; + case ADX: /* Okami HD (PS4) */ + vgmstream = init_vgmstream_adx(temp_sf); + if (!vgmstream) goto fail; + break; + case VAG: /* Ukiyo no Roushi (Vita) */ + vgmstream = init_vgmstream_vag(temp_sf); + if (!vgmstream) goto fail; + break; + case RIFF: /* Ukiyo no Roushi (Vita) */ + vgmstream = init_vgmstream_riff(temp_sf); + if (!vgmstream) goto fail; + break; + case CWAV: /* Sonic: Lost World (3DS) */ + vgmstream = init_vgmstream_rwsd(temp_sf); + if (!vgmstream) goto fail; + break; + case DSP: /* Sonic: Lost World (WiiU) */ + vgmstream = init_vgmstream_ngc_dsp_std(temp_sf); + if (!vgmstream) goto fail; + break; + default: + goto fail; + } + + vgmstream->num_streams = total_subsongs; + + /* try to load cue names */ + load_awb_name(sf, sf_acb, vgmstream, waveid); + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} + + +static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid) { + int is_memory = (sf_acb != NULL); + + /* .acb is passed when loading memory .awb inside .acb */ + if (!is_memory) { + /* load companion .acb using known pairs */ //todo improve, see xsb code + char filename[PATH_LIMIT]; + int len_name, len_cmp; + + + /* try (name).awb + (name).awb */ + sf_acb = open_streamfile_by_ext(sf, "acb"); + + /* try (name)_streamfiles.awb + (name).acb */ + if (!sf_acb) { + char *cmp = "_streamfiles"; + get_streamfile_basename(sf, filename, sizeof(filename)); + len_name = strlen(filename); + len_cmp = strlen(cmp); + + if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { + filename[len_name - len_cmp] = '\0'; + strcat(filename, ".acb"); + sf_acb = open_streamfile_by_filename(sf, filename); + } + } + + /* try (name)_STR.awb + (name).acb */ + if (!sf_acb) { + char *cmp = "_STR"; + get_streamfile_basename(sf, filename, sizeof(filename)); + len_name = strlen(filename); + len_cmp = strlen(cmp); + + if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { + filename[len_name - len_cmp] = '\0'; + strcat(filename, ".acb"); + sf_acb = open_streamfile_by_filename(sf, filename); + } + } + + /* try (name)_(name)_R001.awb + (name).acb [Sengoku Basara Battle Party (Mobile)] */ + if (!sf_acb) { + char *cmp = "_R001"; + get_streamfile_basename(sf, filename, sizeof(filename)); + len_name = strlen(filename); + len_cmp = strlen(cmp); + + if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { + filename[(len_name - len_cmp) / 2] = '\0'; + strcat(filename, ".acb"); + VGM_LOG("%s\n", filename); + sf_acb = open_streamfile_by_filename(sf, filename); + } + } + + /* probably loaded */ + load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory); + + close_streamfile(sf_acb); + } + else { + load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory); + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c index 7b5606c39..2eceef972 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c @@ -8,9 +8,10 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { STREAMFILE* temp_sf = NULL; off_t subfile_offset, base_offset = 0; size_t subfile_size; - uint32_t subfile_id, header_id; - int big_endian, version, is_dummy = 0; + uint32_t subfile_id; + int big_endian, version, is_riff = 0, is_dummy = 0, is_wmid = 0; uint32_t (*read_u32)(off_t,STREAMFILE*); + float (*read_f32)(off_t,STREAMFILE*); int total_subsongs, target_subsong = sf->stream_index; @@ -24,6 +25,7 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { goto fail; big_endian = guess_endianness32bit(base_offset + 0x04, sf); read_u32 = big_endian ? read_u32be : read_u32le; + read_f32 = big_endian ? read_f32be : read_f32le; /* Wwise banks have event/track/sequence/etc info in the HIRC chunk, as well @@ -90,27 +92,38 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { subfile_offset = read_u32(offset + 0x04, sf) + data_offset; subfile_size = read_u32(offset + 0x08, sf); } + + //;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size); - /* some indexes don't have that, but for now leave a dummy song for easier HIRC mapping */ + /* detect format */ if (subfile_offset <= 0 || subfile_size <= 0) { - //;VGM_LOG("BKHD: dummy entry"); - temp_sf = setup_subfile_streamfile(sf, 0x00, 0x10, "raw"); - if (!temp_sf) goto fail; - - //todo make some better silent entry - vgmstream = init_vgmstream_raw_pcm(temp_sf); - if (!vgmstream) goto fail; - + /* some indexes don't have data */ is_dummy = 1; } + else if (read_u32be(subfile_offset + 0x00, sf) == 0x52494646 || /* "RIFF" */ + read_u32be(subfile_offset + 0x00, sf) == 0x52494658) { /* "RIFX" */ + is_riff = 1; + } + else if (read_f32(subfile_offset + 0x02, sf) >= 30.0 && + read_f32(subfile_offset + 0x02, sf) <= 250.0) { + /* ignore Wwise's custom .wmid (similar to a regular midi but with simplified + * chunks and custom fields: 0x00=MThd's division, 0x02: bpm (new), etc) */ + is_wmid = 1; + } + /* default is sfx */ + + + if (is_dummy || is_wmid) { + /* for now leave a dummy song for easier .bnk index-to-subsong mapping */ + vgmstream = init_vgmstream_silence(0, 0, 0); + if (!vgmstream) goto fail; + } else { - //;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size); /* could pass .wem but few files need memory .wem detection */ temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL); if (!temp_sf) goto fail; - header_id = read_u32be(0x00, temp_sf); - if (header_id == 0x52494646 || header_id == 0x52494658) { /* "RIFF" / "RIFX" */ + if (is_riff) { vgmstream = init_vgmstream_wwise(temp_sf); if (!vgmstream) goto fail; } @@ -124,7 +137,9 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { vgmstream->num_streams = total_subsongs; if (is_dummy) - snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%s", "dummy"); + snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u/dummy", subfile_id); + else if (is_wmid) + snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u/wmid", subfile_id); else if (subfile_id != 0xFFFFFFFF) snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/brstm.c b/Frameworks/vgmstream/vgmstream/src/meta/brstm.c index 960c21b37..80f811957 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/brstm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/brstm.c @@ -106,26 +106,39 @@ VGMSTREAM * init_vgmstream_brstm(STREAMFILE *streamFile) { if (vgmstream->coding_type == coding_NGC_DSP) { off_t coef_offset; - off_t coef_offset1; - off_t coef_offset2; + off_t head_part3_offset; + off_t adpcm_header_offset; int i,j; - int coef_spacing = 0x38; + int coef_spacing; if (atlus_shrunken_head) { coef_offset = 0x50; coef_spacing = 0x30; + + for (j = 0; j < vgmstream->channels; j++) { + for (i = 0; i < 16; i++) { + vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(head_offset + coef_offset + j * coef_spacing + i * 2,streamFile); + } + } } else { - coef_offset1=read_32bitBE(head_offset+0x1c,streamFile); - coef_offset2=read_32bitBE(head_offset+0x10+coef_offset1,streamFile); - coef_offset=coef_offset2+0x10; - } + head_part3_offset = read_32bitBE(head_offset + 0x1c, streamFile); - for (j=0;jchannels;j++) { - for (i=0;i<16;i++) { - vgmstream->ch[j].adpcm_coef[i]=read_16bitBE(head_offset+coef_offset+j*coef_spacing+i*2,streamFile); + for (j = 0; j < vgmstream->channels; j++) { + adpcm_header_offset = head_offset + 0x08 + + head_part3_offset + 0x04 /* skip over HEAD part 3 */ + + j * 0x08 /* skip to channel's ADPCM offset table */ + + 0x04; /* ADPCM header offset field */ + + coef_offset = head_offset + 0x08 + + read_32bitBE(adpcm_header_offset, streamFile) + + 0x08; /* coeffs field */ + + for (i = 0; i < 16; i++) { + vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_offset + i * 2, streamFile); + } } } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bsf.c b/Frameworks/vgmstream/vgmstream/src/meta/bsf.c new file mode 100644 index 000000000..7e2cc22da --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/bsf.c @@ -0,0 +1,97 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* .bsf - from Kuju games [Reign of Fire ((PS2/GC/Xbox)] */ +VGMSTREAM* init_vgmstream_bsf(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + uint32_t subfile_type; + off_t subfile_name; + off_t subfile_offset; + size_t subfile_size; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + if (!check_extensions(sf,"bsf")) + goto fail; + if (read_u32le(0x00,sf) != 0x42534648) /* "BSFH" (notice chunks are LE even on GC) */ + goto fail; + + total_subsongs = read_u32le(0x08,sf); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + /* dumb container of other formats */ + { + int i; + off_t offset = 0x08 + read_u32le(0x04,sf); /* 0x04: chunk size */ + + subfile_type = 0; + for (i = 0; i < total_subsongs; i++) { + /* subsong header "xxxH" */ + //uint32_t head_type = read_u32le(offset + 0x00,sf); + uint32_t head_size = read_u32le(offset + 0x04,sf); + /* 0x08: name + * 0x28: audio config? */ + /* subsong data "xxxD" */ + uint32_t data_type = read_u32le(offset + 0x08 + head_size + 0x00,sf); + uint32_t data_size = read_u32le(offset + 0x08 + head_size + 0x04,sf); + + if (i + 1 == target_subsong) { + subfile_name = offset + 0x08; + subfile_type = data_type; + subfile_size = data_size; + subfile_offset = offset + 0x08 + head_size + 0x08; + break; + } + + offset += 0x08 + head_size + 0x08 + data_size; + } + + if (subfile_type == 0) + goto fail; + } + + + switch(subfile_type) { + case 0x44535044: /* "DSPD" */ + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "dsp"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_ngc_dsp_std(temp_sf); + if (!vgmstream) goto fail; + break; + + case 0x56414744: /* "VAGD" */ + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "vag"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_vag(temp_sf); + if (!vgmstream) goto fail; + break; + + case 0x57415644: /* "WAVD" */ + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "wav"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_riff(temp_sf); + if (!vgmstream) goto fail; + break; + + default: + goto fail; + } + + vgmstream->num_streams = total_subsongs; + read_string(vgmstream->stream_name, 0x20, subfile_name, sf); + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.c b/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.c index 5b8ec2aee..681b4d35d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.c @@ -23,6 +23,7 @@ static size_t deblock_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_ data->block_size = 0; data->data_size = 0; data->skip_size = 0; + data->chunk_size = 0; data->step_count = data->cfg.step_start; //data->read_count = data->cfg.read_count; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.h index ba6cc81d0..aec9e4178 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.h @@ -49,6 +49,7 @@ struct deblock_io_data { off_t block_size; /* current block (added to physical offset) */ off_t skip_size; /* data to skip from block start to reach data (like a header) */ off_t data_size; /* usable data in a block (added to logical offset) */ + off_t chunk_size; /* current super-block size (for complex blocks, handled manually) */ int step_count; /* number of blocks to step over */ //int read_count; /* number of blocks to read */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dsb.c b/Frameworks/vgmstream/vgmstream/src/meta/dsb.c new file mode 100644 index 000000000..24f0cc276 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/dsb.c @@ -0,0 +1,54 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .dsb - from Namco games [Taiko no Tatsujin DS: Dororon! Yokai Daikessen!! (DS)] */ +VGMSTREAM* init_vgmstream_dsb(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + off_t subfile_offset; + size_t subfile_size; + + + /* checks */ + if (!check_extensions(sf,"dsb")) + goto fail; + if (read_u32be(0x00,sf) != 0x44535342) /* "DSSB" */ + goto fail; + if (read_u32be(0x40,sf) != 0x44535354) /* "DSST" */ + goto fail; + + /* - DDSB: + * 0x04: chunk size + * 0x08: file name + * 0x14: sample rate + * 0x18: v01? + * 0x1c: file size + * 0x20: DSST offset + * + * - DDST: + * 0x44: chunk size + * 0x48: file name + * 0x58: small signed number? + * 0x5c: data size (with padding) + * 0x60: small signed number? + * 0x64: ? + * rest: null + */ + + subfile_offset = 0x80; + subfile_size = read_u32be(0x80 + 0x04, sf) + 0x08; /* files are padded so use BNSF */ + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "bnsf"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_bnsf(temp_sf); + if (!vgmstream) goto fail; + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 5dbccfe03..348b9d4c6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -598,7 +598,7 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) /* EA MPF/MUS combo - used in older 7th gen games for storing interactive music */ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { - uint32_t num_tracks, track_start, track_hash, mus_sounds, mus_stream = 0; + uint32_t num_tracks, track_start, track_hash = 0, mus_sounds, mus_stream = 0; uint8_t version, sub_version; off_t tracks_table, samples_table, eof_offset, table_offset, entry_offset, snr_offset, sns_offset; int32_t(*read_32bit)(off_t, STREAMFILE*); @@ -639,6 +639,9 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { entry_offset = read_32bit(tracks_table + i * 0x04, sf) * 0x04; track_start = read_32bit(entry_offset + 0x00, sf); + if (track_start == 0 && i != 0) + continue; /* empty track */ + if (track_start <= target_stream - 1) { track_hash = read_32bitBE(entry_offset + 0x08, sf); is_ram = (track_hash == 0xF1F1F1F1); @@ -649,8 +652,6 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { goto fail; track_hash = read_32bitBE(entry_offset + 0x14, sf); - if (track_hash == 0xF1F1F1F1) - continue; /* empty track */ } else { if (read_32bitBE(entry_offset + 0x0c, sf) == 0x00) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index 4f33a252a..046b1a4b1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -36,7 +36,7 @@ typedef struct { static layered_layout_data* build_layered_fsb5_celt(STREAMFILE* sf, fsb5_header* fsb5); static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE* sf, fsb5_header* fsb5, off_t configs_offset, size_t configs_size); -/* FSB5 - FMOD Studio multiplatform format */ +/* FSB5 - Firelight's FMOD Studio SoundBank format */ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; fsb5_header fsb5 = {0}; @@ -211,6 +211,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { /* found in some XMA2/Vorbis/FADPCM */ VGM_LOG("FSB5: stream %i flag %x with value %08x\n", i, extraflag_type, read_32bitLE(extraflag_offset+0x04,sf)); break; + case 0x0e: /* number of layered Vorbis channels [Invisible, Inc. (Switch)] */ default: VGM_LOG("FSB5: stream %i unknown flag 0x%x at %x + 0x04 (size 0x%x)\n", i, extraflag_type, (uint32_t)extraflag_offset, extraflag_size); break; @@ -219,13 +220,19 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { extraflag_offset += 0x04 + extraflag_size; stream_header_size += 0x04 + extraflag_size; - } while (extraflag_end != 0x00); + } + while (extraflag_end != 0x00); } /* target found */ if (i + 1 == target_subsong) { fsb5.stream_offset = fsb5.base_header_size + fsb5.sample_header_size + fsb5.name_table_size + data_offset; + /* catch bad rips (like incorrectly split +1.5GB .fsb with wrong header+data) */ + if (fsb5.stream_offset > get_streamfile_size(sf)) + goto fail; + + /* get stream size from next stream offset or full size if there is only one */ if (i + 1 == fsb5.total_subsongs) { fsb5.stream_size = fsb5.sample_data_size - data_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c index 37f732582..2ea1b2176 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c @@ -7,7 +7,7 @@ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { STREAMFILE* temp_sf = NULL; off_t subfile_offset, chunk_offset, bank_offset, offset; size_t subfile_size, bank_size; - int is_old = 0; + uint32_t version = 0; /* checks */ @@ -18,6 +18,7 @@ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { goto fail; if (read_u32be(0x08,sf) != 0x46455620) /* "FEV " */ goto fail; + version = read_u32le(0x14,sf); /* newer FEV have some kind of sub-version at 0x18 */ /* .fev is an event format referencing various external .fsb, but FMOD can bake .fev and .fsb to * form a .bank, which is the format we support here (regular .fev is complex and not very interesting). @@ -42,15 +43,14 @@ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { goto fail; switch(chunk_type) { - case 0x4C495354: /* "LIST" with "SNDH" (older) */ + case 0x4C495354: /* "LIST" with "SNDH" (usually v0x28 but also in Fall Guys's BNK_Music_RoundReveal) */ if (read_u32be(offset + 0x04, sf) == 0x534E4448) { bank_offset = offset + 0x0c; bank_size = read_s32le(offset + 0x08,sf); - is_old = 1; } break; - case 0x534E4448: /* "SNDH" (newer) */ + case 0x534E4448: /* "SNDH" */ bank_offset = offset; bank_size = chunk_size; break; @@ -65,10 +65,18 @@ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { if (bank_offset == 0) goto fail; - /* 0x00: unknown (version? ex LE: 0x00080003, 0x00080005) */ + /* 0x00: unknown (chunk version? ex LE: 0x00080003, 0x00080005) */ { + /* versions: + * 0x28: Transistor (iOS) [+2015] + * 0x50: Runic Rampage (PC), Forza 7 (PC), Shantae: Half Genie Hero (Switch) [+2017] + * 0x58: Mana Spark (PC), Shantae and the Seven Sirens (PC) [+2018] + * 0x63: Banner Saga 3 (PC) [+2018] + * 0x64: Guacamelee! Super Turbo Championship Edition (Switch) [+2018] + * 0x65: Carrion (Switch) [+2020] + * 0x7D: Fall Guys (PC) [+2020] */ + size_t entry_size = version <= 0x28 ? 0x04 : 0x08; int banks; - size_t entry_size = is_old ? 0x04 : 0x08; /* multiple banks is possible but rare (only seen an extra "Silence" FSB5 in Guacamelee 2 (Switch), * which on PC is a regular subsong in the only FSB5) */ @@ -88,7 +96,7 @@ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { if (bank_subsongs != 1) goto fail; } - if (is_old) { + if (version <= 0x28) { subfile_offset = read_u32le(bank_offset+0x04,sf); subfile_size = /* meh */ read_u32le(subfile_offset + 0x0C,sf) + @@ -105,8 +113,14 @@ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "fsb"); if (!temp_sf) goto fail; - vgmstream = init_vgmstream_fsb5(temp_sf); - close_streamfile(temp_sf); + if (read_u32be(0x00, temp_sf) == 0x46534235) { + vgmstream = init_vgmstream_fsb5(temp_sf); + close_streamfile(temp_sf); + } + else { //other flag? + vgmstream = init_vgmstream_fsb_encrypted(temp_sf); + close_streamfile(temp_sf); + } return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c index 289287b87..10d30b033 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c @@ -66,8 +66,7 @@ VGMSTREAM* init_vgmstream_fsb_encrypted(STREAMFILE* sf) { vgmstream = init_vgmstream_fsb(temp_sf); } - if (vgmstream) - dump_streamfile(temp_sf, 0); + //;if (vgmstream) dump_streamfile(temp_sf, 0); close_streamfile(temp_sf); if (vgmstream) break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h index eae35d464..578f1df04 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h @@ -13,22 +13,23 @@ typedef struct { /* Encrypted FSB info from guessfsb and fsbext */ static size_t fsb_decryption_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t length, fsb_decryption_data* data) { static const unsigned char reverse_bits_table[] = { /* LUT to simplify, could use some bitswap function */ - 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, - 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, - 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, - 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, - 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, - 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, - 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, - 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, - 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, - 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, - 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, - 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, - 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, - 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, - 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, - 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF + //00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, //00 + 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, //10 + 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, //20 + 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, //30 + 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, //40 + 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, //50 + 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, //60 + 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, //70 + 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, //80 + 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, //90 + 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, //A0 + 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, //B0 + 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, //C0 + 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, //D0 + 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, //E0 + 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF //F0 }; size_t bytes_read; int i; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h index cb44125cc..b56530d13 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -78,6 +78,12 @@ static const uint8_t key_gh5[] = { 0xFC,0xF9,0xE4,0xB3,0xF5,0x57,0x5C,0xA5,0xAC, /* Sekiro: Shadows Die Twice (PC) */ //"G0KTrWjS9syqF7vVD6RaVXlFD91gMgkC" static const uint8_t key_sek[] = { 0x47,0x30,0x4B,0x54,0x72,0x57,0x6A,0x53,0x39,0x73,0x79,0x71,0x46,0x37,0x76,0x56,0x44,0x36,0x52,0x61,0x56,0x58,0x6C,0x46,0x44,0x39,0x31,0x67,0x4D,0x67,0x6B,0x43 }; +/* SCP: Unity (PC) */ //"BasicEncryptionKey" +static const uint8_t key_scp[] = { 0x42,0x61,0x73,0x69,0x63,0x45,0x6E,0x63,0x72,0x79,0x70,0x74,0x69,0x6F,0x6E,0x4B,0x65,0x79 }; + +/* Guitar Hero: Metallica (X360) */ +static const uint8_t key_ghm[] = { 0x8C,0xFA,0xF3,0x14,0xB1,0x53,0xDA,0xAB,0x2B,0x82,0x6B,0xD5,0x55,0x16,0xCF,0x01,0x90,0x20,0x28,0x14,0xB1,0x53,0xD8 }; + // Unknown: // - Battle: Los Angeles // - Guitar Hero: Warriors of Rock, DJ hero FSB @@ -86,13 +92,13 @@ static const uint8_t key_sek[] = { 0x47,0x30,0x4B,0x54,0x72,0x57,0x6A,0x53,0x39, static const fsbkey_info fsbkey_list[] = { { 0,0, sizeof(key_dj2),key_dj2 }, - { 0,0, sizeof(key_dfp),key_dfp }, + { 0,0, sizeof(key_dfp),key_dfp },//FSB4 { 1,0, sizeof(key_dfp),key_dfp },//untested { 1,1, sizeof(key_dfp),key_dfp },//untested - { 1,0, sizeof(key_npp),key_npp }, - { 1,0, sizeof(key_sms),key_sms }, - { 1,0, sizeof(key_gfs),key_gfs }, - { 1,0, sizeof(key_rev),key_rev }, + { 1,0, sizeof(key_npp),key_npp },//FSB5 + { 1,0, sizeof(key_sms),key_sms },//FSB5 + { 1,0, sizeof(key_gfs),key_gfs },//FSB5 + { 1,0, sizeof(key_rev),key_rev },//FSB5 { 1,0, sizeof(key_ds3),key_ds3 },//untested { 1,1, sizeof(key_ds3),key_ds3 }, { 1,0, sizeof(key_mkx),key_mkx },//untested @@ -141,6 +147,8 @@ static const fsbkey_info fsbkey_list[] = { { 0,1, sizeof(key_mtj),key_mtj },// FSB3 { 0,1, sizeof(key_gh5),key_gh5 },// FSB4 { 1,0, sizeof(key_sek),key_sek },// FSB5 + { 1,0, sizeof(key_scp),key_scp },// FSB5 + { 0,1, sizeof(key_ghm),key_ghm },// FSB4 }; static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c b/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c index 71f5bbf1c..b04fdf0a6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c @@ -1,149 +1,147 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../coding/coding.h" - -/* GSP+GSB - from Tecmo's Super Swing Golf 1 & 2 (Wii), Quantum Theory (PS3/X360) */ -VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamHeader = NULL; - int loop_flag, channel_count, sample_rate, num_samples, loop_start, loop_end; - off_t start_offset, chunk_offset, first_offset; - size_t data_size; - int codec; - - - /* checks */ - if (!check_extensions(streamFile,"gsb")) - goto fail; - - streamHeader = open_streamfile_by_ext(streamFile, "gsp"); - if (!streamHeader) goto fail; - - if (read_32bitBE(0x00,streamHeader) != 0x47534E44) /* "GSND" */ - goto fail; - /* 0x04: version? */ - /* 0x08: 1? */ - /* 0x0c: 0? */ - first_offset = read_32bitBE(0x10,streamHeader); /* usually 0x14 */ - - if (!find_chunk_be(streamHeader, 0x48454144,first_offset,1, &chunk_offset,NULL)) /* "HEAD" */ - goto fail; - /* 0x00: header size */ - /* 0x04: num_chunks */ - - if (!find_chunk_be(streamHeader, 0x44415441,first_offset,1, &chunk_offset,NULL)) /* "DATA" */ - goto fail; - data_size = read_32bitBE(chunk_offset + 0x00,streamHeader); - codec = read_32bitBE(chunk_offset + 0x04,streamHeader); - sample_rate = read_32bitBE(chunk_offset + 0x08,streamHeader); - /* 0x0c: always 16? */ - channel_count = read_16bitBE(chunk_offset + 0x0e,streamHeader); - /* 0x10: always 0? */ - num_samples = read_32bitBE(chunk_offset + 0x14,streamHeader); - /* 0x18: always 0? */ - /* 0x1c: unk (varies with codec_id) */ - - if (!find_chunk_be(streamHeader, 0x42534943,first_offset,1, &chunk_offset,NULL)) /* "BSIC" */ - goto fail; - /* 0x00/0x04: probably volume/pan/etc floats (1.0) */ - /* 0x08: null? */ - loop_flag = read_8bit(chunk_offset+0x0c,streamHeader); - loop_start = read_32bitBE(chunk_offset+0x10,streamHeader); - loop_end = read_32bitBE(chunk_offset+0x14,streamHeader); - - //if (!find_chunk_be(streamHeader, 0x4E414D45,first_offset,1, &chunk_offset,NULL)) /* "NAME" */ - // goto fail; - /* 0x00: name_size */ - /* 0x04+: name (same as filename) */ - - - start_offset = 0x00; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_GSP_GSB; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end; - - switch (codec) { - case 0x04: { /* DSP [Super Swing Golf (Wii)] */ - size_t block_header_size; - size_t num_blocks; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_blocked_gsb; - - if (!find_chunk_be(streamHeader, 0x47434558,first_offset,1, &chunk_offset,NULL)) /* "GCEX" */ - goto fail; - - //vgmstream->current_block_size = read_32bitBE(chunk_offset+0x00,streamHeader); - block_header_size = read_32bitBE(chunk_offset+0x04,streamHeader); - num_blocks = read_32bitBE(chunk_offset+0x08,streamHeader); - vgmstream->num_samples = (data_size - block_header_size * num_blocks) / 8 / vgmstream->channels * 14; - /* 0x0c+: unk */ - - dsp_read_coefs_be(vgmstream, streamHeader, chunk_offset+0x18, 0x30); - break; - } -#ifdef VGM_USE_FFMPEG - case 0x08: { /* ATRAC3 [Quantum Theory (PS3)] */ - int block_align, encoder_delay; - - block_align = 0x98 * vgmstream->channels; - encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ - vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; - /* fix num_samples as header samples seem to be modified to match altered (49999/48001) sample rates somehow */ - - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ - vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); //- encoder_delay - vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; - break; - } - - case 0x09: { /* XMA2 [Quantum Theory (PS3)] */ - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[200]; - int32_t bytes; - - if (!find_chunk_be(streamHeader, 0x584D4558,first_offset,1, &chunk_offset,NULL)) /* "XMEX" */ - goto fail; - /* 0x00: fmt0x166 header (BE) */ - /* 0x34: seek table */ - - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, chunk_offset,0x34, data_size, streamHeader, 1); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); /* samples are ok */ - break; - } -#endif - default: - goto fail; - } - - - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) - goto fail; - close_streamfile(streamHeader); - return vgmstream; - -fail: - close_streamfile(streamHeader); - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + +/* GSP+GSB - from Tecmo's Super Swing Golf 1 & 2 (Wii), Quantum Theory (PS3/X360) */ +VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + int loop_flag, channel_count, sample_rate, num_samples, loop_start, loop_end; + off_t start_offset, chunk_offset, first_offset; + size_t data_size; + int codec; + + + /* checks */ + if (!check_extensions(streamFile,"gsb")) + goto fail; + + streamHeader = open_streamfile_by_ext(streamFile, "gsp"); + if (!streamHeader) goto fail; + + if (read_32bitBE(0x00,streamHeader) != 0x47534E44) /* "GSND" */ + goto fail; + /* 0x04: version? */ + /* 0x08: 1? */ + /* 0x0c: 0? */ + first_offset = read_32bitBE(0x10,streamHeader); /* usually 0x14 */ + + if (!find_chunk_be(streamHeader, 0x48454144,first_offset,1, &chunk_offset,NULL)) /* "HEAD" */ + goto fail; + /* 0x00: header size */ + /* 0x04: num_chunks */ + + if (!find_chunk_be(streamHeader, 0x44415441,first_offset,1, &chunk_offset,NULL)) /* "DATA" */ + goto fail; + data_size = read_32bitBE(chunk_offset + 0x00,streamHeader); + codec = read_32bitBE(chunk_offset + 0x04,streamHeader); + sample_rate = read_32bitBE(chunk_offset + 0x08,streamHeader); + /* 0x0c: always 16? */ + channel_count = read_16bitBE(chunk_offset + 0x0e,streamHeader); + /* 0x10: always 0? */ + num_samples = read_32bitBE(chunk_offset + 0x14,streamHeader); + /* 0x18: always 0? */ + /* 0x1c: unk (varies with codec_id) */ + + if (!find_chunk_be(streamHeader, 0x42534943,first_offset,1, &chunk_offset,NULL)) /* "BSIC" */ + goto fail; + /* 0x00/0x04: probably volume/pan/etc floats (1.0) */ + /* 0x08: null? */ + loop_flag = read_8bit(chunk_offset+0x0c,streamHeader); + loop_start = read_32bitBE(chunk_offset+0x10,streamHeader); + loop_end = read_32bitBE(chunk_offset+0x14,streamHeader); + + //if (!find_chunk_be(streamHeader, 0x4E414D45,first_offset,1, &chunk_offset,NULL)) /* "NAME" */ + // goto fail; + /* 0x00: name_size */ + /* 0x04+: name (same as filename) */ + + + start_offset = 0x00; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_GSP_GSB; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + switch (codec) { + case 0x04: { /* DSP [Super Swing Golf (Wii)] */ + size_t block_header_size; + size_t num_blocks; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_blocked_gsb; + + if (!find_chunk_be(streamHeader, 0x47434558,first_offset,1, &chunk_offset,NULL)) /* "GCEX" */ + goto fail; + + //vgmstream->current_block_size = read_32bitBE(chunk_offset+0x00,streamHeader); + block_header_size = read_32bitBE(chunk_offset+0x04,streamHeader); + num_blocks = read_32bitBE(chunk_offset+0x08,streamHeader); + vgmstream->num_samples = (data_size - block_header_size * num_blocks) / 8 / vgmstream->channels * 14; + /* 0x0c+: unk */ + + dsp_read_coefs_be(vgmstream, streamHeader, chunk_offset+0x18, 0x30); + break; + } +#ifdef VGM_USE_FFMPEG + case 0x08: { /* ATRAC3 [Quantum Theory (PS3)] */ + int block_align, encoder_delay; + + block_align = 0x98 * vgmstream->channels; + encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ + vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; + /* fix num_samples as header samples seem to be modified to match altered (49999/48001) sample rates somehow */ + + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ + vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); //- encoder_delay + vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; + break; + } + + case 0x09: { /* XMA2 [Quantum Theory (PS3)] */ + uint8_t buf[0x100]; + int32_t bytes; + + if (!find_chunk_be(streamHeader, 0x584D4558,first_offset,1, &chunk_offset,NULL)) /* "XMEX" */ + goto fail; + /* 0x00: fmt0x166 header (BE) */ + /* 0x34: seek table */ + + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, chunk_offset,0x34, data_size, streamHeader, 1); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); /* samples are ok */ + break; + } +#endif + default: + goto fail; + } + + + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + close_streamfile(streamHeader); + return vgmstream; + +fail: + close_streamfile(streamHeader); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index d4d032436..3bfbd9d82 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -10,69 +10,73 @@ static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t /* CRI HCA - streamed audio from CRI ADX2/Atom middleware */ -VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) { - return init_vgmstream_hca_subkey(streamFile, 0x0000); +VGMSTREAM * init_vgmstream_hca(STREAMFILE* sf) { + return init_vgmstream_hca_subkey(sf, 0x0000); } -VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) { +VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) { VGMSTREAM * vgmstream = NULL; - hca_codec_data * hca_data = NULL; + hca_codec_data* hca_data = NULL; + clHCA_stInfo* hca_info; /* checks */ - if ( !check_extensions(streamFile, "hca")) + if (!check_extensions(sf, "hca")) return NULL; - if (((uint32_t)read_32bitBE(0x00,streamFile) & 0x7f7f7f7f) != 0x48434100) /* "HCA\0", possibly masked */ + + if ((read_u32be(0x00,sf) & 0x7F7F7F7F) != 0x48434100) /* "HCA\0", possibly masked */ goto fail; /* init vgmstream and library's context, will validate the HCA */ - hca_data = init_hca(streamFile); + hca_data = init_hca(sf); if (!hca_data) goto fail; + hca_info = hca_get_info(hca_data); + /* find decryption key in external file or preloaded list */ - if (hca_data->info.encryptionEnabled) { + if (hca_info->encryptionEnabled) { uint64_t keycode = 0; uint8_t keybuf[0x08+0x02]; size_t keysize; - keysize = read_key_file(keybuf, 0x08+0x04, streamFile); + keysize = read_key_file(keybuf, 0x08+0x04, sf); if (keysize == 0x08) { /* standard */ - keycode = (uint64_t)get_64bitBE(keybuf+0x00); + keycode = get_u64be(keybuf+0x00); if (subkey) { keycode = keycode * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) ); } } else if (keysize == 0x08+0x02) { /* seed key + AWB subkey */ - uint64_t file_key = (uint64_t)get_64bitBE(keybuf+0x00); - uint16_t file_sub = (uint16_t)get_16bitBE(keybuf+0x08); + uint64_t file_key = get_u64be(keybuf+0x00); + uint16_t file_sub = get_u16be(keybuf+0x08); keycode = file_key * ( ((uint64_t)file_sub << 16u) | ((uint16_t)~file_sub + 2u) ); } #ifdef HCA_BRUTEFORCE else if (1) { - bruteforce_hca_key(streamFile, hca_data, &keycode, subkey); + bruteforce_hca_key(sf, hca_data, &keycode, subkey); } #endif else { find_hca_key(hca_data, &keycode, subkey); } - clHCA_SetKey(hca_data->handle, (unsigned long long)keycode); //maybe should be done through hca_decoder.c? + hca_set_encryption_key(hca_data, keycode); } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(hca_data->info.channelCount, hca_data->info.loopEnabled); + vgmstream = allocate_vgmstream(hca_info->channelCount, hca_info->loopEnabled); if (!vgmstream) goto fail; vgmstream->meta_type = meta_HCA; - vgmstream->sample_rate = hca_data->info.samplingRate; + vgmstream->sample_rate = hca_info->samplingRate; - vgmstream->num_samples = hca_data->info.blockCount * hca_data->info.samplesPerBlock - - hca_data->info.encoderDelay - hca_data->info.encoderPadding; - vgmstream->loop_start_sample = hca_data->info.loopStartBlock * hca_data->info.samplesPerBlock - - hca_data->info.encoderDelay + hca_data->info.loopStartDelay; - vgmstream->loop_end_sample = hca_data->info.loopEndBlock * hca_data->info.samplesPerBlock - - hca_data->info.encoderDelay + (hca_data->info.samplesPerBlock - hca_data->info.loopEndPadding); + vgmstream->num_samples = hca_info->blockCount * hca_info->samplesPerBlock - + hca_info->encoderDelay - hca_info->encoderPadding; + vgmstream->loop_start_sample = hca_info->loopStartBlock * hca_info->samplesPerBlock - + hca_info->encoderDelay + hca_info->loopStartDelay; + vgmstream->loop_end_sample = hca_info->loopEndBlock * hca_info->samplesPerBlock - + hca_info->encoderDelay + (hca_info->samplesPerBlock - hca_info->loopEndPadding); /* After loop end CRI's encoder removes the rest of the original samples and puts some * garbage in the last frame that should be ignored. Optionally it can encode fully preserving * the file too, but it isn't detectable, so we'll allow the whole thing just in case */ @@ -80,10 +84,10 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) { // vgmstream->num_samples = vgmstream->loop_end_sample; /* this can happen in preloading HCA from memory AWB */ - if (hca_data->info.blockCount * hca_data->info.blockSize > get_streamfile_size(streamFile)) { - unsigned int max_block = get_streamfile_size(streamFile) / hca_data->info.blockSize; - vgmstream->num_samples = max_block * hca_data->info.samplesPerBlock - - hca_data->info.encoderDelay - hca_data->info.encoderPadding; + if (hca_info->blockCount * hca_info->blockSize > get_streamfile_size(sf)) { + unsigned int max_block = get_streamfile_size(sf) / hca_info->blockSize; + vgmstream->num_samples = max_block * hca_info->samplesPerBlock - + hca_info->encoderDelay - hca_info->encoderPadding; } vgmstream->coding_type = coding_CRI_HCA; @@ -115,7 +119,7 @@ fail: } -static inline void test_key(hca_codec_data * hca_data, uint64_t key, uint16_t subkey, int *best_score, uint64_t *best_keycode) { +static inline void test_key(hca_codec_data* hca_data, uint64_t key, uint16_t subkey, int* best_score, uint64_t* best_keycode) { int score; if (subkey) { @@ -179,7 +183,7 @@ static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigne uint8_t* buf = NULL; int best_score = -1; off_t keys_size, bytes; - int i, pos; + int pos; VGM_LOG("HCA: test keys\n"); @@ -207,17 +211,20 @@ static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigne //key = ((uint64_t)get_u32le(buf + pos + 0x00) << 32) | ((uint64_t)get_u32le(buf + pos + 0x04) << 0); //key = ((uint64_t)get_u32be(buf + pos + 0x00) << 0 ) | ((uint64_t)get_u32be(buf + pos + 0x04) << 32); //key = ((uint64_t)get_u32be(buf + pos + 0x00) << 32) | ((uint64_t)get_u32be(buf + pos + 0x04) << 0); + //key = ((uint64_t)get_u32le(buf + pos + 0x00) << 0 ) | 0; /* upper bytes not set, ex. P5 */ + //key = ((uint64_t)get_u32be(buf + pos + 0x00) << 0 ) | 0; /* upper bytes not set, ex. P5 */ if (key == 0) continue; - test_key(hca_data, keys[i], subkey, &best_score, out_keycode); + test_key(hca_data, key, subkey, &best_score, out_keycode); if (best_score == 1) goto done; VGM_ASSERT(pos % 0x100000 == 0, "HCA: pos %x...\n", pos); /* observed files have aligned keys in the .text section, change if needed */ - pos += 0x04; //pos++; + pos += 0x04; + //pos++; } done: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index fd1ba4765..a6bb11c38 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -26,7 +26,7 @@ static const hcakey_info hcakey_list[] = { // Phantasy Star Online 2 (multi?) // used by most console games - {0xCC55463930DBE1AB}, // CC55463930DBE1AB / 14723751768204501419 + {14723751768204501419u}, // CC55463930DBE1AB // Old Phantasy Star Online 2 (multi?) {61891147883431481}, // 30DBE1ABCC554639 @@ -103,7 +103,7 @@ static const hcakey_info hcakey_list[] = { {8910}, // 00000000000022CE // Tokyo 7th Sisters (iOS/Android) *unconfirmed - {0xFDAE531AAB414BA1}, // FDAE531AAB414BA1 + {18279639311550860193u}, // FDAE531AAB414BA1 // One Piece Dance Battle (iOS/Android) {1905818}, // 00000000001D149A @@ -118,7 +118,7 @@ static const hcakey_info hcakey_list[] = { {4867249871962584729}, // 438BF1F883653699 // Tekken Mobile (iOS/Android) - {0xFFFFFFFFFFFFFFFF}, // FFFFFFFFFFFFFFFF / 18446744073709551615 + {18446744073709551615u}, // FFFFFFFFFFFFFFFF // Tales of the Rays (iOS/Android) {9516284}, // 00000000009134FC @@ -136,7 +136,7 @@ static const hcakey_info hcakey_list[] = { {1224}, // 00000000000004C8 // Schoolgirl Strikers ~Twinkle Melodies~ (iOS/Android) - {0xDB5B61B8343D0000}, // DB5B61B8343D0000 + {15806334760965177344u}, // DB5B61B8343D0000 // Bad Apple Wars (PSVita) {241352432}, // 000000000E62BEF0 @@ -163,7 +163,7 @@ static const hcakey_info hcakey_list[] = { {11708691}, // 0000000000B2A913 // Monster Gear Versus (iOS/Android) - {0xB1E30F346415B475}, // B1E30F346415B475 + {12818105682118423669u}, // B1E30F346415B475 // Yumeiro Cast (iOS/Android) {14418}, // 0000000000003852 @@ -172,13 +172,13 @@ static const hcakey_info hcakey_list[] = { {1000}, // 00000000000003E8 // Zero kara Hajimeru Mahou no Sho (iOS/Android) - {0xD2E836E662F20000}, // D2E836E662F20000 + {15197457305692143616u}, // D2E836E662F20000 // Soul Reverse Zero (iOS/Android) {2873513618}, // 00000000AB465692 // Jojo's Bizarre Adventure: Diamond Records (iOS/Android) [additional data] - {0x820212864CAB35DE}, // 820212864CAB35DE + {9368070542905259486u}, // 820212864CAB35DE // HUNTER x HUNTER: World Hunt (iOS/Android) {71777214294589695}, // 00FF00FF00FF00FF @@ -317,7 +317,7 @@ static const hcakey_info hcakey_list[] = { {3957325206121219506}, // 36EB3E4EE38E05B2 /* Idola: Phantasy Star Saga (Android) */ - {0xA86BF72B4C852CA7}, // A86BF72B4C852CA7 / 12136065386219383975 + {12136065386219383975u}, // A86BF72B4C852CA7 /* Arca Last (Android) */ {612310807}, // 00000000247F1F17 @@ -329,7 +329,7 @@ static const hcakey_info hcakey_list[] = { {3315495188}, // 00000000C59E7114 /* Inazuma Eleven SD (Android) */ - {0xC436E03737D55B5F}, // C436E03737D55B5F / 14138734607940803423 + {14138734607940803423u}, // C436E03737D55B5F /* Detective Conan Runner / Case Closed Runner (Android) */ {1175268187653273344}, // 104f643098e3f700 @@ -340,6 +340,39 @@ static const hcakey_info hcakey_list[] = { /* 22/7 Ongaku no Jikan (Android) */ {20190906}, // 00000000013416BA + /* Cardcaptor Sakura: Happiness Memories (Android) */ + {625144437747651}, // 00023890C8252FC3 + + /* Digimon Story: Cyber Sleuth (PC) */ + {2897314143465725881}, // 283553DCE3FD5FB9 + + /* Alice Re:Code (Android) */ + {9422596198430275382u}, // 82C3C951C561F736 + + /* Tokyo 7th Sisters (Android) */ + {18279639311550860193u}, // FDAE531AAB414BA1 + + /* High School Fleet: Kantai Battle de Pinch! (Mobile) */ + {43472919336422565}, // 009A7263CA658CA5 + + /* Disney's Twisted Wonderland (Android) */ + {2895000877}, // 00000000AC8E352D + + /* B-PROJECT Kaikan Everyday (Android) */ + {12316546176516217334u}, // AAED297DDEF1D9F6 + + /* HELIOS Rising Heroes (Android) */ + {311981570940334162}, // 04546195F85DF052 + + /* World Ends's Club (iOS) */ + {50979632184989243}, // 00B51DB4932A963B + + /* Kandagawa Jet Girls (PC) */ + {6235253715273671}, // 001626EE22C887C7 + + /* Re:Zero - Lost in Memories (Android) */ + {1611432018519751642}, // 165CF4E2138F7BDA + /* Dragalia Lost (iOS/Android) */ {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ivaud.c b/Frameworks/vgmstream/vgmstream/src/meta/ivaud.c index 8a85a5567..0fd4a368c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ivaud.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ivaud.c @@ -1,192 +1,232 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../util.h" - -typedef struct { - int is_music; - - int total_subsongs; - - int channel_count; - int sample_rate; - int codec; - int num_samples; - - size_t block_count; - size_t block_size; - - off_t stream_offset; - size_t stream_size; - -} ivaud_header; - -static int parse_ivaud_header(STREAMFILE* streamFile, ivaud_header* ivaud); - - -/* .ivaud - from GTA IV (PC) */ -VGMSTREAM * init_vgmstream_ivaud(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - ivaud_header ivaud = {0}; - int loop_flag; - - /* checks */ - /* (hashed filenames are likely extensionless and .ivaud is added by tools) */ - if (!check_extensions(streamFile, "ivaud,")) - goto fail; - - /* check header */ - if (!parse_ivaud_header(streamFile, &ivaud)) - goto fail; - - - loop_flag = 0; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(ivaud.channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = ivaud.sample_rate; - vgmstream->num_samples = ivaud.num_samples; - vgmstream->num_streams = ivaud.total_subsongs; - vgmstream->stream_size = ivaud.stream_size; - vgmstream->meta_type = meta_IVAUD; - - switch(ivaud.codec) { - case 0x0001: /* common in sfx, uncommon in music (ex. EP2_SFX/MENU_MUSIC) */ - vgmstream->coding_type = coding_PCM16LE; - vgmstream->layout_type = ivaud.is_music ? layout_blocked_ivaud : layout_none; - vgmstream->full_block_size = ivaud.block_size; - break; - - case 0x0400: - vgmstream->coding_type = coding_IMA_int; - vgmstream->layout_type = ivaud.is_music ? layout_blocked_ivaud : layout_none; - vgmstream->full_block_size = ivaud.block_size; - break; - - default: - VGM_LOG("IVAUD: unknown codec 0x%x\n", ivaud.codec); - goto fail; - } - - - if (!vgmstream_open_stream(vgmstream,streamFile,ivaud.stream_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - -/* Parse Rockstar's .ivaud header (much info from SparkIV). */ -static int parse_ivaud_header(STREAMFILE* streamFile, ivaud_header* ivaud) { - int target_subsong = streamFile->stream_index; - - - /* use bank's stream count to detect */ - ivaud->is_music = (read_32bitLE(0x10,streamFile) == 0); - - if (ivaud->is_music) { - off_t block_table_offset, channel_table_offset, channel_info_offset; - - /* music header */ - block_table_offset = read_32bitLE(0x00,streamFile); /* 64b */ - ivaud->block_count = read_32bitLE(0x08,streamFile); - ivaud->block_size = read_32bitLE(0x0c,streamFile); /* 64b, uses padded blocks */ - channel_table_offset = read_32bitLE(0x14,streamFile); /* 64b */ - /* 0x1c(8): block_table_offset again? */ - ivaud->channel_count = read_32bitLE(0x24,streamFile); - /* 0x28(4): unknown entries? */ - ivaud->stream_offset = read_32bitLE(0x2c,streamFile); - channel_info_offset = channel_table_offset + ivaud->channel_count*0x10; - - if ((ivaud->block_count * ivaud->block_size) + ivaud->stream_offset != get_streamfile_size(streamFile)) { - VGM_LOG("IVAUD: bad file size\n"); - goto fail; - } - - /* channel table (one entry per channel, points to channel info) */ - /* 0x00(8): offset within channel_info_offset */ - /* 0x08(4): hash */ - /* 0x0c(4): size */ - - /* channel info (one entry per channel) */ - /* 0x00(8): offset within data (should be 0) */ - /* 0x08(4): hash */ - /* 0x0c(4): half num_samples? */ - ivaud->num_samples = read_32bitLE(channel_info_offset+0x10,streamFile); - /* 0x14(4): unknown (-1) */ - /* 0x18(2): sample rate */ - /* 0x1a(2): unknown */ - ivaud->codec = read_32bitLE(channel_info_offset+0x1c,streamFile); - /* (when codec is IMA) */ - /* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */ - /* rest: unknown data */ - - /* block table (one entry per block) */ - /* 0x00: data size processed up to this block (doesn't count block padding) */ - ivaud->sample_rate = read_32bitLE(block_table_offset + 0x04,streamFile); - /* sample_rate should agree with each channel in the channel table */ - - - ivaud->total_subsongs = 1; - ivaud->stream_size = get_streamfile_size(streamFile); - } - else { - off_t stream_table_offset, stream_info_offset, stream_entry_offset; - - /* bank header */ - stream_table_offset = read_32bitLE(0x00,streamFile); /* 64b */ - /* 0x08(8): header size? start offset? */ - ivaud->total_subsongs = read_32bitLE(0x10,streamFile); - /* 0x14(4): unknown */ - ivaud->stream_offset = read_32bitLE(0x18,streamFile); /* base start_offset */ - - if (target_subsong == 0) target_subsong = 1; - if (target_subsong < 0 || target_subsong > ivaud->total_subsongs || ivaud->total_subsongs < 1) goto fail; - - if (stream_table_offset != 0x1c) - goto fail; - stream_info_offset = stream_table_offset + 0x10*ivaud->total_subsongs; - - /* stream table (one entry per stream, points to stream info) */ - stream_entry_offset = read_32bitLE(stream_table_offset + 0x10*(target_subsong-1) + 0x00,streamFile); /* within stream info */ - /* 0x00(8): offset within stream_info_offset */ - /* 0x08(4): hash */ - /* 0x0c(4): size */ - - /* stream info (one entry per stream) */ - ivaud->stream_offset += read_32bitLE(stream_info_offset+stream_entry_offset+0x00,streamFile); /* 64b, within data */ - /* 0x08(4): hash */ - /* 0x0c(4): half num_samples? */ - ivaud->num_samples = read_32bitLE(stream_info_offset+stream_entry_offset+0x10,streamFile); - /* 0x14(4): unknown (-1) */ - ivaud->sample_rate = (uint16_t)read_16bitLE(stream_info_offset+stream_entry_offset+0x18,streamFile); - /* 0x1a(2): unknown */ - ivaud->codec = read_32bitLE(stream_info_offset+stream_entry_offset+0x1c,streamFile); - /* (when codec is IMA) */ - /* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */ - /* rest: unknown data */ - - ivaud->channel_count = 1; - - /* ghetto size calculator (could substract offsets but streams are not ordered) */ - switch(ivaud->codec) { - case 0x0001: - ivaud->stream_size = ivaud->num_samples * 2; /* double 16b PCM */ - break; - case 0x0400: - ivaud->stream_size = ivaud->num_samples / 2; /* half nibbles */ - break; - default: - break; - } - } - - - return 1; -fail: - return 0; -} +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + +typedef struct { + int is_music; + + int total_subsongs; + + int channel_count; + int sample_rate; + int codec; + int num_samples; + + size_t block_count; + size_t block_size; + + off_t stream_offset; + size_t stream_size; + + int big_endian; +} ivaud_header; + +static int parse_ivaud_header(STREAMFILE* sf, ivaud_header* ivaud); + + +/* .ivaud - from GTA IV (PC/PS3/X360) */ +VGMSTREAM* init_vgmstream_ivaud(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + ivaud_header ivaud = {0}; + int loop_flag; + + /* checks */ + /* (hashed filenames are likely extensionless and .ivaud is added by tools) */ + if (!check_extensions(sf, "ivaud,")) + goto fail; + + /* check header */ + if (!parse_ivaud_header(sf, &ivaud)) + goto fail; + + + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(ivaud.channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = ivaud.sample_rate; + vgmstream->num_samples = ivaud.num_samples; + vgmstream->num_streams = ivaud.total_subsongs; + vgmstream->stream_size = ivaud.stream_size; + vgmstream->meta_type = meta_IVAUD; + + switch(ivaud.codec) { + case 0x0001: /* common in sfx, uncommon in music (ex. EP2_SFX/MENU_MUSIC) */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = ivaud.is_music ? layout_blocked_ivaud : layout_none; + vgmstream->full_block_size = ivaud.block_size; + break; + +#ifdef VGM_USE_FFMPEG + case 0x0000: { /* XMA2 (X360) */ + uint8_t buf[0x100]; + size_t bytes; + + if (ivaud.is_music) { + goto fail; + } + else { + /* regular XMA for sfx */ + bytes = ffmpeg_make_riff_xma1(buf, 0x100, ivaud.num_samples, ivaud.stream_size, ivaud.channel_count, ivaud.sample_rate, 0); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, ivaud.stream_offset, ivaud.stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, sf, ivaud.stream_offset, ivaud.stream_size, 0, 0,0); /* samples are ok? */ + } + break; + } +#endif + +#ifdef VGM_USE_MPEG + case 0x0100: { /* MPEG (PS3) */ + mpeg_custom_config cfg = {0}; + + if (ivaud.is_music) { + goto fail; + } + else { + cfg.chunk_size = ivaud.block_size; + cfg.big_endian = ivaud.big_endian; + + vgmstream->codec_data = init_mpeg_custom(sf, ivaud.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + } + break; + } +#endif + + case 0x0400: /* PC */ + vgmstream->coding_type = coding_IMA_int; + vgmstream->layout_type = ivaud.is_music ? layout_blocked_ivaud : layout_none; + vgmstream->full_block_size = ivaud.block_size; + break; + + default: + VGM_LOG("IVAUD: unknown codec 0x%x\n", ivaud.codec); + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream,sf,ivaud.stream_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* Parse Rockstar's .ivaud header (much info from SparkIV). */ +static int parse_ivaud_header(STREAMFILE* sf, ivaud_header* ivaud) { + int target_subsong = sf->stream_index; + uint64_t (*read_u64)(off_t,STREAMFILE*); + uint32_t (*read_u32)(off_t,STREAMFILE*); + uint16_t (*read_u16)(off_t,STREAMFILE*); + + + ivaud->big_endian = read_u32be(0x00, sf) == 0; /* table offset at 0x04 > BE (64b) */ + read_u64 = ivaud->big_endian ? read_u64be : read_u64le; + read_u32 = ivaud->big_endian ? read_u32be : read_u32le; + read_u16 = ivaud->big_endian ? read_u16be : read_u16le; + + /* use bank's stream count to detect */ + ivaud->is_music = (read_u32(0x10,sf) == 0); + + if (ivaud->is_music) { + off_t block_table_offset, channel_table_offset, channel_info_offset; + + /* music header */ + block_table_offset = read_u64(0x00,sf); + ivaud->block_count = read_u32(0x08,sf); + ivaud->block_size = read_u32(0x0c,sf); /* uses padded blocks */ + /* 0x10(4): stream count */ + channel_table_offset = read_u64(0x14,sf); + /* 0x1c(8): block_table_offset again? */ + ivaud->channel_count = read_u32(0x24,sf); + /* 0x28(4): unknown entries? */ + ivaud->stream_offset = read_u32(0x2c,sf); + channel_info_offset = channel_table_offset + ivaud->channel_count * 0x10; + + if ((ivaud->block_count * ivaud->block_size) + ivaud->stream_offset != get_streamfile_size(sf)) { + VGM_LOG("IVAUD: bad file size\n"); + goto fail; + } + + /* channel table (one entry per channel, points to channel info) */ + /* 0x00(8): offset within channel_info_offset */ + /* 0x08(4): hash */ + /* 0x0c(4): size */ + + /* channel info (one entry per channel) */ + /* 0x00(8): offset within data (should be 0) */ + /* 0x08(4): hash */ + /* 0x0c(4): half num_samples? */ + ivaud->num_samples = read_u32(channel_info_offset+0x10,sf); + /* 0x14(4): unknown (-1) */ + /* 0x18(2): sample rate */ + /* 0x1a(2): unknown */ + ivaud->codec = read_u32(channel_info_offset+0x1c,sf); + /* (when codec is IMA) */ + /* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */ + /* rest: unknown data */ + + /* block table (one entry per block) */ + /* 0x00: data size processed up to this block (doesn't count block padding) */ + ivaud->sample_rate = read_u32(block_table_offset + 0x04,sf); + /* sample_rate should agree with each channel in the channel table */ + + + ivaud->total_subsongs = 1; + ivaud->stream_size = get_streamfile_size(sf); + } + else { + off_t stream_table_offset, stream_info_offset, stream_entry_offset, offset; + + /* bank header */ + stream_table_offset = read_u64(0x00,sf); + /* 0x08(8): header size? start offset? */ + ivaud->total_subsongs = read_u32(0x10,sf); + /* 0x14(4): unknown */ + ivaud->stream_offset = read_u32(0x18,sf); /* base start_offset */ + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > ivaud->total_subsongs || ivaud->total_subsongs < 1) goto fail; + + if (stream_table_offset != 0x1c) + goto fail; + stream_info_offset = stream_table_offset + 0x10*ivaud->total_subsongs; + + /* stream table (one entry per stream, points to stream info) */ + stream_entry_offset = read_u64(stream_table_offset + 0x10*(target_subsong-1) + 0x00,sf); /* within stream info */ + /* 0x00(8): offset within stream_info_offset */ + /* 0x08(4): hash */ + /* 0x0c(4): some offset/size */ + + /* stream info (one entry per stream) */ + offset = stream_info_offset + stream_entry_offset; + ivaud->stream_offset += read_u64(offset+0x00,sf); /* within data */ + /* 0x08(4): hash */ + ivaud->stream_size = read_u32(offset+0x0c,sf); + ivaud->num_samples = read_u32(offset+0x10,sf); + /* 0x14(4): unknown (-1) */ + ivaud->sample_rate = read_u16(offset+0x18,sf); + /* 0x1a(2): unknown */ + ivaud->codec = read_u32(offset+0x1c,sf); + /* (when codec is IMA) */ + /* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */ + /* rest: unknown data */ + + ivaud->channel_count = 1; + } + + + return 1; +fail: + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktsc.c b/Frameworks/vgmstream/vgmstream/src/meta/ktsc.c new file mode 100644 index 000000000..e438075c4 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktsc.c @@ -0,0 +1,60 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + + +/* KTSC - Koei Tecmo KTSR container */ +VGMSTREAM* init_vgmstream_ktsc(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE *temp_sf = NULL; + int target_subsong = sf->stream_index, total_subsongs; + off_t offset, subfile_offset; + size_t subfile_size; + + + /* checks */ + /* .ktsl2asbin: common [Atelier Ryza (PC)] */ + if (!check_extensions(sf, "ktsl2asbin")) + goto fail; + + /* KTSC is a container of KTSRs, but can't be extracted easily as they use absolute pointers to the + * same stream companion file. KTSRs may have subsongs, but only seem to have 1, so use KTSC's subsongs. */ + if (read_u32be(0x00, sf) != 0x4B545343) /* "KTSC" */ + goto fail; + if (read_u32be(0x04, sf) != 0x01000001) /* version? */ + goto fail; + + if (target_subsong == 0) target_subsong = 1; + total_subsongs = read_u32le(0x08, sf); + if (target_subsong > total_subsongs) + goto fail; + + /* 0x0c: CRC(?) table start */ + offset = read_u32le(0x10, sf); + /* 0x14: file size */ + /* 0x18: header end */ + /* 0x1c: null */ + /* 0x20+: CRC(?) table, 1 entry per file */ + + subfile_offset = read_u32le(offset + 0x04 * (target_subsong - 1), sf); + subfile_size = read_u32le(subfile_offset + 0x1c, sf); /* from header, meh */ + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL); + if (!temp_sf) goto fail; + + temp_sf->stream_index = 1; + vgmstream = init_vgmstream_ktsr(temp_sf); + if (!vgmstream) goto fail; + + if (vgmstream->num_streams > 1) + goto fail; + vgmstream->num_streams = total_subsongs; + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c b/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c index 63223977c..ce32b772b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c @@ -124,29 +124,6 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { vgmstream->layout_type = layout_layered; vgmstream->coding_type = coding_ATRAC9; break; - -#if 0 - atrac9_config cfg = {0}; - if (ktsr.channels > 1) { - VGM_LOG("1\n"); - goto fail; - } - - /* 0x00: samples per frame */ - /* 0x02: frame size */ - cfg.config_data = read_u32be(ktsr.extra_offset + 0x04, sf_b); - if ((cfg.config_data & 0xFF) == 0xFE) /* later versions(?) in LE */ - cfg.config_data = read_u32le(ktsr.extra_offset + 0x04, sf_b); - - cfg.channels = vgmstream->channels; - cfg.encoder_delay = 256; /* observed default (ex. Attack on Titan PC vs Vita) */ - - vgmstream->codec_data = init_atrac9(&cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_ATRAC9; - vgmstream->layout_type = layout_none; - break; -#endif } #endif @@ -312,6 +289,8 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, off_t offset) { switch(type) { /* hash-id? */ case 0x38D0437D: /* external [Nioh (PC), Atelier Ryza (PC)] */ + case 0xDF92529F: /* external [Atelier Ryza (PC)] */ + case 0x6422007C: /* external [Atelier Ryza (PC)] */ /* 08 subtype? (ex. 0x522B86B9) * 0c channels * 10 ? (always 0x002706B8) @@ -465,7 +444,6 @@ static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf, uint32_t target_id offset += size; } - } static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { @@ -503,6 +481,7 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { case 0x6172DBA8: /* padding (empty) */ case 0xBD888C36: /* config (floats, stream id, etc, may have extended name) */ case 0xC9C48EC1: /* unknown (has some string inside like "boss") */ + case 0xA9D23BF1: /* "state container", some kind of config/floats, witgh names like 'State_bgm01'..N */ break; case 0xC5CCCB70: /* sound (internal data or external stream) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 8063e203a..42f158762 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -3,6 +3,9 @@ #include "../vgmstream.h" +VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples); + + VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_afc(STREAMFILE *streamFile); @@ -76,7 +79,7 @@ VGMSTREAM * init_vgmstream_raw_int(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_exst(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps2_svag(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_svag_kcet(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile); @@ -565,7 +568,7 @@ VGMSTREAM * init_vgmstream_mca(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_btsnd(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_svag_snk(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_svag_snk(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_xma(STREAMFILE* streamFile); @@ -907,4 +910,14 @@ VGMSTREAM* init_vgmstream_kat(STREAMFILE* sf); VGMSTREAM* init_vgmstream_pcm_success(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ktsc(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_adp_konami(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_zwv(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_dsb(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_bsf(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/msf.c b/Frameworks/vgmstream/vgmstream/src/meta/msf.c index 9dc94fd56..332dc643e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/msf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/msf.c @@ -128,17 +128,15 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { #endif #if defined(VGM_USE_MPEG) case 0x07: { /* MPEG (CBR LAME MP3) [Dengeki Bunko Fighting Climax (PS3)] */ - mpeg_codec_data *mpeg_data = NULL; - mpeg_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; + vgmstream->codec_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels); + if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; - vgmstream->num_samples = mpeg_bytes_to_samples(data_size, mpeg_data); + vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data); if (loop_flag) { - vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); - vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); + vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, vgmstream->codec_data); + vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, vgmstream->codec_data); /* loops are always aligned to CBR frame beginnings */ } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mul.c b/Frameworks/vgmstream/vgmstream/src/meta/mul.c index 7d2bf3e9f..1b3c5ed54 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mul.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mul.c @@ -3,15 +3,15 @@ #include "../coding/coding.h" #include "mul_streamfile.h" -typedef enum { PSX, DSP, XBOX, XMA1 } mul_codec; +typedef enum { PSX, DSP, IMA, XMA1, FSB4 } mul_codec; -static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset); - -static layered_layout_data* build_layered_mul(STREAMFILE *sf_data, int big_endian, VGMSTREAM* vgmstream); +static off_t get_start_offset(STREAMFILE* sf); +static int guess_codec(STREAMFILE* sf, off_t start_offset, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset); +static layered_layout_data* build_layered_mul(STREAMFILE* sf, off_t offset, int big_endian, VGMSTREAM* vgmstream, mul_codec codec); /* .MUL - from Crystal Dynamics games [Legacy of Kain: Defiance (PS2), Tomb Raider Underworld (multi)] */ -VGMSTREAM * init_vgmstream_mul(STREAMFILE *sf) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_mul(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset, coefs_offset = 0; int loop_flag, channel_count, sample_rate, num_samples, loop_start; int big_endian; @@ -48,21 +48,24 @@ VGMSTREAM * init_vgmstream_mul(STREAMFILE *sf) { /* 0x34: id? */ /* 0x38+: channel config until ~0x100? (multiple 0x3F800000 / 1.0f depending on the number of channels) */ - /* test known versions (later versions start from 0x24 instead of 0x20) */ - if (!(read_u32(0x38,sf) == 0x3F800000 || - read_u32(0x3c,sf) == 0x3F800000)) /* Tomb Raider Underworld */ + /* test known "version" (some float) later versions start from 0x24 instead of 0x20 */ + if (!(read_u32(0x38,sf) == 0x3F800000 || /* common */ + read_u32(0x38,sf) == 0x4530F000 || /* Avengers */ + read_u32(0x3c,sf) == 0x3F800000)) /* Tomb Raider Underworld */ goto fail; loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */ - start_offset = 0x800; + + start_offset = get_start_offset(sf); + if (!start_offset) goto fail; /* format is pretty limited so we need to guess codec */ - if (!guess_codec(sf, big_endian, channel_count, &codec, &coefs_offset)) + if (!guess_codec(sf, start_offset, big_endian, channel_count, &codec, &coefs_offset)) goto fail; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_MUL; @@ -86,21 +89,18 @@ VGMSTREAM * init_vgmstream_mul(STREAMFILE *sf) { dsp_read_hist_be (vgmstream,sf,coefs_offset+0x24,0x2e); break; - case XBOX: - vgmstream->coding_type = coding_XBOX_IMA_int; + case IMA: + vgmstream->coding_type = coding_CD_IMA; vgmstream->layout_type = layout_blocked_mul; break; -#ifdef VGM_USE_FFMPEG - case XMA1: { - - vgmstream->layout_data = build_layered_mul(sf, big_endian, vgmstream); + case XMA1: + case FSB4: + vgmstream->layout_data = build_layered_mul(sf, start_offset, big_endian, vgmstream, codec); if (!vgmstream->layout_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; + //vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_layered; break; - } -#endif default: goto fail; @@ -115,7 +115,19 @@ fail: return NULL; } -static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset) { +static off_t get_start_offset(STREAMFILE* sf) { + + /* find first block with header info */ + if (read_u32be(0x0800,sf) != 0 || read_u32be(0x0804,sf) != 0) /* earlier games */ + return 0x800; + + if (read_u32be(0x2000,sf) != 0 || read_u32be(0x2004,sf) != 0) /* later games */ + return 0x2000; + + return 0; +} + +static int guess_codec(STREAMFILE* sf, off_t start_offset, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset) { uint32_t (*read_u32)(off_t,STREAMFILE*); read_u32 = big_endian ? read_u32be : read_u32le; @@ -141,14 +153,11 @@ static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* else { //if (ps_check_format(sf, 0x820, 0x100)) { /* may be PS3/X360, tested below [Tomb Raider 7 (PS3)] */ } - - // todo test XMA1 (X360): N mono streams (layered), each block has 1 sub-blocks of 0x800 packet per channel } - { int i; - off_t offset = 0x800; + off_t offset = start_offset; size_t file_size = get_streamfile_size(sf); size_t frame_size; @@ -163,6 +172,12 @@ static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* continue; /* not audio */ } + /* test FSB4 header */ + if (read_u32be(offset + 0x10, sf) == 0x46534234 || read_u32be(offset + 0x20, sf) == 0x46534234) { + *p_codec = FSB4; + return 1; + } + /* test XMA1 (X360): has sub-blocks of 0x800 per channel */ if (block_size == 0x810 * channels) { for (i = 0; i < channels; i++) { @@ -195,7 +210,7 @@ static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* break; } if (i == data_size / frame_size) { - *p_codec = XBOX; + *p_codec = IMA; return 1; } @@ -207,8 +222,8 @@ static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* } -/* MUL contain one XMA1 streams per channel so we need the usual voodoo */ -static layered_layout_data* build_layered_mul(STREAMFILE *sf_data, int big_endian, VGMSTREAM* vgmstream) { +/* MUL contain one XMA1/FSB streams per channel so we need the usual voodoo */ +static layered_layout_data* build_layered_mul(STREAMFILE* sf, off_t offset, int big_endian, VGMSTREAM* vgmstream, mul_codec codec) { layered_layout_data* data = NULL; STREAMFILE* temp_sf = NULL; int i, layers = vgmstream->channels; @@ -219,47 +234,60 @@ static layered_layout_data* build_layered_mul(STREAMFILE *sf_data, int big_endia if (!data) goto fail; for (i = 0; i < layers; i++) { - int layer_channels = 1; - - - /* build the layer VGMSTREAM */ - data->layers[i] = allocate_vgmstream(layer_channels, 0); - if (!data->layers[i]) goto fail; - - data->layers[i]->sample_rate = vgmstream->sample_rate; - data->layers[i]->num_samples = vgmstream->num_samples; + switch(codec) { #ifdef VGM_USE_FFMPEG - { - uint8_t buf[0x100]; - int bytes; - size_t stream_size; + case XMA1: { + uint8_t buf[0x100]; + int bytes; + size_t stream_size; + int layer_channels = 1; - temp_sf = setup_mul_streamfile(sf_data, big_endian, i, layers); - if (!temp_sf) goto fail; + temp_sf = setup_mul_streamfile(sf, offset, big_endian, i, layers, NULL); + if (!temp_sf) goto fail; - stream_size = get_streamfile_size(temp_sf); + stream_size = get_streamfile_size(temp_sf); - bytes = ffmpeg_make_riff_xma1(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, 0); - data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00, stream_size); - if (!data->layers[i]->codec_data) goto fail; + /* build the layer VGMSTREAM */ + data->layers[i] = allocate_vgmstream(layer_channels, 0); + if (!data->layers[i]) goto fail; - data->layers[i]->coding_type = coding_FFmpeg; - data->layers[i]->layout_type = layout_none; - data->layers[i]->stream_size = stream_size; + data->layers[i]->sample_rate = vgmstream->sample_rate; + data->layers[i]->num_samples = vgmstream->num_samples; - xma_fix_raw_samples(data->layers[i], temp_sf, 0x00,stream_size, 0, 0,0); /* ? */ + bytes = ffmpeg_make_riff_xma1(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, 0); + data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00, stream_size); + if (!data->layers[i]->codec_data) goto fail; - close_streamfile(temp_sf); - temp_sf = NULL; - } -#else - goto fail; + data->layers[i]->coding_type = coding_FFmpeg; + data->layers[i]->layout_type = layout_none; + data->layers[i]->stream_size = stream_size; + + xma_fix_raw_samples(data->layers[i], temp_sf, 0x00,stream_size, 0, 0,0); /* ? */ + break; + } #endif + case FSB4: { /* FSB4 w/ mono MP3 [Tomb Raider 8 (PS3), Avengers (PC)] */ + temp_sf = setup_mul_streamfile(sf, offset, big_endian, i, layers, "fsb"); + if (!temp_sf) goto fail; + + data->layers[i] = init_vgmstream_fsb(temp_sf); + if (!data->layers[i]) goto fail; + break; + } + + default: + goto fail; + } + + close_streamfile(temp_sf); + temp_sf = NULL; } if (!setup_layout_layered(data)) goto fail; + + vgmstream->coding_type = data->layers[0]->coding_type; return data; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mul_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/mul_streamfile.h index 842a86580..2e5489bb2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mul_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/mul_streamfile.h @@ -2,56 +2,71 @@ #define _MUL_STREAMFILE_H_ #include "deblock_streamfile.h" + static void block_callback(STREAMFILE* sf, deblock_io_data* data) { - uint32_t block_type; - size_t block_size; uint32_t (*read_u32)(off_t,STREAMFILE*) = data->cfg.big_endian ? read_u32be : read_u32le; + /* Blocks have base header + sub-blocks with track sub-header. + * Some blocks don't contain all channels (instead they begin next block). */ - if (data->physical_offset == 0) { - data->block_size = 0x800; + if (data->chunk_size && data->chunk_size < 0x10) { + /* padding after all sub-blocks */ + data->block_size = data->chunk_size; data->data_size = 0; data->skip_size = 0; - return; + data->chunk_size = 0; } + else if (data->chunk_size) { + /* audio block sub-headers, ignore data for other tracks */ + uint32_t track_size = read_u32(data->physical_offset + 0x00, sf); + uint32_t track_number = read_u32(data->physical_offset + 0x04, sf); + /* 0x08: dummy (may contain un-init'd data) */ + /* 0x0c: dummy (may contain un-init'd data) */ - block_type = read_u32(data->physical_offset + 0x00, sf); - block_size = read_u32(data->physical_offset + 0x04, sf); /* not including main header */ - - /* some blocks only contain half of data (continues in next block) so use track numbers */ - - if (block_type == 0x00 && block_size != 0) { - /* header block */ - data->block_size = 0x10; + data->block_size = 0x10 + track_size; data->data_size = 0; data->skip_size = 0; - } - else if (block_type == 0x00000800) { - data->block_size = 0x810; - /* actually sub-block with size + number, kinda half-assed but meh... */ - if (block_size == data->cfg.track_number) { - data->data_size = 0x800; + if (track_number == data->cfg.track_number) { + data->data_size = track_size; data->skip_size = 0x10; } - else{ - data->data_size = 0; - data->skip_size = 0; - } + + data->chunk_size -= data->block_size; } else { - /* non-audio block */ - data->block_size = block_size + 0x10; + /* base block header */ + uint32_t block_type = read_u32(data->physical_offset + 0x00, sf); + uint32_t block_size = read_u32(data->physical_offset + 0x04, sf); + /* 0x08: dummy */ + /* 0x0c: dummy */ + + /* blocks are padded after all sub-blocks */ + if (block_size % 0x10) { + block_size = block_size + 0x10 - (block_size % 0x10); + } + data->data_size = 0; data->skip_size = 0; + + if (block_type == 0x00 && block_size != 0) { + /* audio block */ + data->block_size = 0x10; + data->chunk_size = block_size; + } + else { + /* non-audio block (or empty audio block) */ + data->block_size = block_size + 0x10; + } } } /* Deinterleaves MUL streams */ -static STREAMFILE* setup_mul_streamfile(STREAMFILE* sf, int big_endian, int track_number, int track_count) { +static STREAMFILE* setup_mul_streamfile(STREAMFILE* sf, off_t offset, int big_endian, int track_number, int track_count, const char* extension) { STREAMFILE *new_sf = NULL; deblock_config_t cfg = {0}; + cfg.stream_start = offset; cfg.big_endian = big_endian; cfg.track_number = track_number; cfg.track_count = track_count; @@ -59,6 +74,8 @@ static STREAMFILE* setup_mul_streamfile(STREAMFILE* sf, int big_endian, int trac new_sf = open_wrap_streamfile(sf); new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); + if (extension) + new_sf = open_fakename_streamfile_f(new_sf, NULL, extension); return new_sf; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/naac.c b/Frameworks/vgmstream/vgmstream/src/meta/naac.c index b657e598b..9f649cf45 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/naac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/naac.c @@ -1,66 +1,61 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* .NAAC - from Namco 3DS games (Ace Combat - Assault Horizon Legacy, Taiko no Tatsujin Don to Katsu no Jikuu Daibouken) */ -VGMSTREAM * init_vgmstream_naac(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - size_t data_size; - - /* check extension */ - if ( !check_extensions(streamFile,"naac") ) - goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x41414320) /* "AAC " */ - goto fail; - if (read_32bitLE(0x04,streamFile) != 0x01) /* version? */ - goto fail; - - start_offset = 0x1000; - loop_flag = (read_32bitLE(0x18,streamFile) != 0); - channel_count = read_32bitLE(0x08,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); - vgmstream->num_samples = read_32bitLE(0x10,streamFile); /* without skip_samples */ - vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile); /* with skip_samples */ - vgmstream->loop_end_sample = read_32bitLE(0x18,streamFile); - /* 0x1c: loop start offset, 0x20: loop end offset (within data) */ - data_size = read_32bitLE(0x24,streamFile); - /* 0x28: unknown; 0x2c: table start offset?; 0x30: seek table (always 0xFD0, padded) */ - - vgmstream->meta_type = meta_NAAC; - -#ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; - - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* observed default, some files start without silence though seems correct when loop_start=0 */ - if (!ffmpeg_data->skipSamples) /* FFmpeg doesn't seem to use not report it */ - ffmpeg_set_skip_samples(ffmpeg_data, 1024); - vgmstream->num_samples -= 1024; - } -#else - goto fail; -#endif - - /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + +/* .NAAC - from Namco 3DS games (Ace Combat - Assault Horizon Legacy, Taiko no Tatsujin Don to Katsu no Jikuu Daibouken) */ +VGMSTREAM* init_vgmstream_naac(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + size_t data_size; + + /* check extension */ + if ( !check_extensions(sf,"naac") ) + goto fail; + + /* check header */ + if (read_32bitBE(0x00,sf) != 0x41414320) /* "AAC " */ + goto fail; + if (read_32bitLE(0x04,sf) != 0x01) /* version? */ + goto fail; + + start_offset = 0x1000; + loop_flag = (read_32bitLE(0x18,sf) != 0); + channel_count = read_32bitLE(0x08,sf); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x0c,sf); + vgmstream->num_samples = read_32bitLE(0x10,sf); /* without skip_samples */ + vgmstream->loop_start_sample = read_32bitLE(0x14,sf); /* with skip_samples */ + vgmstream->loop_end_sample = read_32bitLE(0x18,sf); + /* 0x1c: loop start offset, 0x20: loop end offset (within data) */ + data_size = read_32bitLE(0x24,sf); + /* 0x28: unknown; 0x2c: table start offset?; 0x30: seek table (always 0xFD0, padded) */ + + vgmstream->meta_type = meta_NAAC; + +#ifdef VGM_USE_FFMPEG + { + vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* observed default, some files start without silence though seems correct when loop_start=0 */ + ffmpeg_set_skip_samples(vgmstream->codec_data, 1024); /* raw AAC doesn't set this */ + vgmstream->num_samples -= 1024; + } +#else + goto fail; +#endif + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index b1a9e760e..bb785523d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -29,8 +29,8 @@ struct dsp_header { /* read the above struct; returns nonzero on failure */ static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE *streamFile, int big_endian) { - int32_t (*get_32bit)(uint8_t *) = big_endian ? get_32bitBE : get_32bitLE; - int16_t (*get_16bit)(uint8_t *) = big_endian ? get_16bitBE : get_16bitLE; + int32_t (*get_32bit)(const uint8_t *) = big_endian ? get_32bitBE : get_32bitLE; + int16_t (*get_16bit)(const uint8_t *) = big_endian ? get_16bitBE : get_16bitLE; int i; uint8_t buf[0x4e]; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nwa.c b/Frameworks/vgmstream/vgmstream/src/meta/nwa.c index 26003fbbd..ca554442f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nwa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nwa.c @@ -1,18 +1,15 @@ #include "meta.h" -#include "../util.h" -#include "../coding/nwa_decoder.h" +#include "../coding/coding.h" #include #include -static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start); -static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start, int32_t *out_loop_end); -static nwa_codec_data *open_nwa_vgmstream(STREAMFILE *streamFile); -static void free_nwa_vgmstream(nwa_codec_data *data); +static int get_loops_nwainfo_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start); +static int get_loops_gameexe_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start, int32_t *p_loop_end); /* NWA - Visual Art's streams [Air (PC), Clannad (PC)] */ -VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_nwa(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; int channel_count, loop_flag = 0; int32_t loop_start_sample = 0, loop_end_sample = 0; @@ -21,26 +18,26 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile, "nwa")) + if (!check_extensions(sf, "nwa")) goto fail; - channel_count = read_16bitLE(0x00,streamFile); + channel_count = read_16bitLE(0x00,sf); if (channel_count != 1 && channel_count != 2) goto fail; /* check if we're using raw pcm */ - if ( read_32bitLE(0x08,streamFile)==-1 || /* compression level */ - read_32bitLE(0x10,streamFile)==0 || /* block count */ - read_32bitLE(0x18,streamFile)==0 || /* compressed data size */ - read_32bitLE(0x20,streamFile)==0 || /* block size */ - read_32bitLE(0x24,streamFile)==0 ) { /* restsize */ + if ( read_32bitLE(0x08,sf)==-1 || /* compression level */ + read_32bitLE(0x10,sf)==0 || /* block count */ + read_32bitLE(0x18,sf)==0 || /* compressed data size */ + read_32bitLE(0x20,sf)==0 || /* block size */ + read_32bitLE(0x24,sf)==0 ) { /* restsize */ compression_level = -1; } else { - compression_level = read_32bitLE(0x08,streamFile); + compression_level = read_32bitLE(0x08,sf); } /* loop points come from external files */ - nwainfo_ini_found = get_loops_nwainfo_ini(streamFile, &loop_flag, &loop_start_sample); - gameexe_ini_found = !nwainfo_ini_found && get_loops_gameexe_ini(streamFile, &loop_flag, &loop_start_sample, &loop_end_sample); + nwainfo_ini_found = get_loops_nwainfo_ini(sf, &loop_flag, &loop_start_sample); + gameexe_ini_found = !nwainfo_ini_found && get_loops_gameexe_ini(sf, &loop_flag, &loop_start_sample, &loop_end_sample); start_offset = 0x2c; @@ -48,12 +45,12 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - vgmstream->num_samples = read_32bitLE(0x1c,streamFile) / channel_count; + vgmstream->sample_rate = read_32bitLE(0x04,sf); + vgmstream->num_samples = read_32bitLE(0x1c,sf) / channel_count; switch(compression_level) { case -1: - switch (read_16bitLE(0x02,streamFile)) { + switch (read_16bitLE(0x02,sf)) { case 8: vgmstream->coding_type = coding_PCM8; vgmstream->interleave_block_size = 0x01; @@ -76,7 +73,7 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { case 5: vgmstream->coding_type = coding_NWA; vgmstream->layout_type = layout_none; - vgmstream->codec_data = open_nwa_vgmstream(streamFile); + vgmstream->codec_data = init_nwa(sf); if (!vgmstream->codec_data) goto fail; break; @@ -103,7 +100,7 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; @@ -114,8 +111,8 @@ fail: /* try to locate NWAINFO.INI in the same directory */ -static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start) { - STREAMFILE *streamLoops; +static int get_loops_nwainfo_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start) { + STREAMFILE *sf_loop; char namebase[PATH_LIMIT]; const char * ext; int length; @@ -127,15 +124,15 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int int32_t loop_start_sample = 0; - streamLoops = open_streamfile_by_filename(streamFile, "NWAINFO.INI"); - if (!streamLoops) goto fail; + sf_loop = open_streamfile_by_filename(sf, "NWAINFO.INI"); + if (!sf_loop) goto fail; - get_streamfile_filename(streamFile,namebase,PATH_LIMIT); + get_streamfile_filename(sf,namebase,PATH_LIMIT); /* ini found, try to find our name */ ext = filename_extension(namebase); length = ext - 1 - namebase; - file_size = get_streamfile_size(streamLoops); + file_size = get_streamfile_size(sf_loop); for (found = 0, offset = 0; !found && offset < file_size; offset++) { off_t suboffset; @@ -145,12 +142,12 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int for (suboffset = offset; suboffset < file_size && suboffset-offset < length && - read_8bit(suboffset,streamLoops) == namebase[suboffset-offset]; + read_8bit(suboffset,sf_loop) == namebase[suboffset-offset]; suboffset++) { /* skip */ } - if (suboffset-offset==length && read_8bit(suboffset,streamLoops)==0x09) { /* tab */ + if (suboffset-offset==length && read_8bit(suboffset,sf_loop)==0x09) { /* tab */ found = 1; found_off = suboffset+1; } @@ -160,7 +157,7 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int if (found) { char loopstring[9] = {0}; - if (read_streamfile((uint8_t*)loopstring,found_off,8,streamLoops) == 8) { + if (read_streamfile((uint8_t*)loopstring,found_off,8,sf_loop) == 8) { loop_start_sample = atol(loopstring); if (loop_start_sample > 0) loop_flag = 1; @@ -168,22 +165,22 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int } - *out_loop_flag = loop_flag; - *out_loop_start = loop_start_sample; + *p_loop_flag = loop_flag; + *p_loop_start = loop_start_sample; - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 1; fail: - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 0; } /* try to locate Gameexe.ini in the same directory */ -static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start, int32_t *out_loop_end) { - STREAMFILE *streamLoops; +static int get_loops_gameexe_ini(STREAMFILE* sf, int* p_loop_flag, int32_t* p_loop_start, int32_t* p_loop_end) { + STREAMFILE*sf_loop; char namebase[PATH_LIMIT]; - const char * ext; + const char* ext; int length; int found; off_t offset; @@ -193,15 +190,15 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int int32_t loop_start_sample = 0, loop_end_sample = 0; - streamLoops = open_streamfile_by_filename(streamFile, "Gameexe.ini"); - if (!streamLoops) goto fail; + sf_loop = open_streamfile_by_filename(sf, "Gameexe.ini"); + if (!sf_loop) goto fail; - get_streamfile_filename(streamFile,namebase,PATH_LIMIT); + get_streamfile_filename(sf,namebase,PATH_LIMIT); /* ini found, try to find our name */ ext = filename_extension(namebase); length = ext-1-namebase; - file_size = get_streamfile_size(streamLoops); + file_size = get_streamfile_size(sf_loop); /* format of line is: * #DSTRACK = 00000000 - eeeeeeee - ssssssss = "name" = "name2?" @@ -212,20 +209,20 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int off_t suboffset; uint8_t buf[10]; - if (read_8bit(offset,streamLoops)!='#') continue; - if (read_streamfile(buf,offset+1,10,streamLoops)!=10) break; + if (read_8bit(offset,sf_loop)!='#') continue; + if (read_streamfile(buf,offset+1,10,sf_loop)!=10) break; if (memcmp("DSTRACK = ",buf,10)) continue; - if (read_8bit(offset+44,streamLoops)!='\"') continue; + if (read_8bit(offset+44,sf_loop)!='\"') continue; for (suboffset = offset+45; suboffset < file_size && suboffset-offset-45 < length && - tolower(read_8bit(suboffset,streamLoops)) == tolower(namebase[suboffset-offset-45]); + tolower(read_8bit(suboffset,sf_loop)) == tolower(namebase[suboffset-offset-45]); suboffset++) { /* skip */ } - if (suboffset-offset-45==length && read_8bit(suboffset,streamLoops)=='\"') { /* tab */ + if (suboffset-offset-45==length && read_8bit(suboffset,sf_loop)=='\"') { /* tab */ found = 1; found_off = offset+22; /* loop end */ } @@ -234,9 +231,9 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int if (found) { char loopstring[9] = {0}; int start_ok = 0, end_ok = 0; - int32_t total_samples = read_32bitLE(0x1c,streamFile) / read_16bitLE(0x00,streamFile); + int32_t total_samples = read_32bitLE(0x1c,sf) / read_16bitLE(0x00,sf); - if (read_streamfile((uint8_t*)loopstring,found_off,8,streamLoops)==8) + if (read_streamfile((uint8_t*)loopstring,found_off,8,sf_loop)==8) { if (!memcmp("99999999",loopstring,8)) { loop_end_sample = total_samples; @@ -246,7 +243,7 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int } end_ok = 1; } - if (read_streamfile((uint8_t*)loopstring,found_off+11,8,streamLoops)==8) + if (read_streamfile((uint8_t*)loopstring,found_off+11,8,sf_loop)==8) { if (!memcmp("99999999",loopstring,8)) { /* not ok to start at last sample, @@ -265,43 +262,14 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int } /* if found file name in INI */ - *out_loop_flag = loop_flag; - *out_loop_start = loop_start_sample; - *out_loop_end = loop_end_sample; + *p_loop_flag = loop_flag; + *p_loop_start = loop_start_sample; + *p_loop_end = loop_end_sample; - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 1; fail: - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 0; } - - -static nwa_codec_data *open_nwa_vgmstream(STREAMFILE *streamFile) { - nwa_codec_data *data = NULL; - char filename[PATH_LIMIT]; - - streamFile->get_name(streamFile,filename,sizeof(filename)); - - data = malloc(sizeof(nwa_codec_data)); - if (!data) goto fail; - - data->nwa = open_nwa(streamFile,filename); - if (!data->nwa) goto fail; - - return data; - -fail: - free_nwa_vgmstream(data); - return NULL; -} - -static void free_nwa_vgmstream(nwa_codec_data *data) { - if (data) { - if (data->nwa) { - close_nwa(data->nwa); - } - free(data); - } -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c index d1ed10f44..07c46558c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -431,6 +431,9 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb data = init_ogg_vorbis(streamFile, start, stream_size, &io); if (!data) goto fail; + ogg_vorbis_get_info(data, &channels, &sample_rate); + ogg_vorbis_get_samples(data, &num_samples); /* let libvorbisfile find total samples */ + /* search for loop comments */ {//todo ignore if loop flag already set? const char * comment = NULL; @@ -439,11 +442,13 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb if (strstr(comment,"loop_start=") == comment || /* PSO4 */ strstr(comment,"LOOP_START=") == comment || /* PSO4 */ + strstr(comment,"LOOPPOINT=") == comment || /* Sonic Robo Blast 2 */ strstr(comment,"COMMENT=LOOPPOINT=") == comment || strstr(comment,"LOOPSTART=") == comment || strstr(comment,"um3.stream.looppoint.start=") == comment || strstr(comment,"LOOP_BEGIN=") == comment || /* Hatsune Miku: Project Diva F (PS3) */ strstr(comment,"LoopStart=") == comment || /* Devil May Cry 4 (PC) */ + strstr(comment,"LOOP=") == comment || /* Duke Nukem 3D: 20th Anniversary World Tour */ strstr(comment,"XIPH_CUE_LOOPSTART=") == comment) { /* Super Mario Run (Android) */ loop_start = atol(strrchr(comment,'=')+1); loop_flag = (loop_start >= 0); @@ -512,6 +517,12 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb loop_end_found = 1; } } + else if (strstr(comment,"LOOPMS=") == comment) { /* Sonic Robo Blast 2 */ + /* Convert from milliseconds to samples. */ + /* (x ms) * (y samples/s) / (1000 ms/s) */ + loop_start = atol(strrchr(comment,'=')+1) * sample_rate / 1000; + loop_flag = (loop_start >= 0); + } /* Hatsune Miku Project DIVA games, though only 'Arcade Future Tone' has >4ch files * ENCODER tag is common but ogg_vorbis_encode looks unique enough @@ -529,8 +540,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb } ogg_vorbis_set_disable_reordering(data, disable_reordering); - ogg_vorbis_get_info(data, &channels, &sample_rate); - ogg_vorbis_get_samples(data, &num_samples); /* let libvorbisfile find total samples */ /* build the VGMSTREAM */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c index c3eea49d4..5def76f73 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c @@ -1,11 +1,11 @@ #include "meta.h" #include "../coding/coding.h" -static size_t joe_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave); +static size_t joe_find_padding(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave); /* .JOE - from Asobo Studio games [Up (PS2), Wall-E (PS2)] */ -VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_ps2_joe(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; int channel_count, loop_flag, sample_rate; int32_t num_samples; @@ -13,54 +13,59 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile, "joe")) + if (!check_extensions(sf, "joe")) goto fail; - file_size = get_streamfile_size(streamFile); - data_size = read_32bitLE(0x04,streamFile); - unknown1 = read_32bitLE(0x08,streamFile); - unknown2 = read_32bitLE(0x0c,streamFile); + file_size = get_streamfile_size(sf); + data_size = read_u32le(0x04,sf); + unknown1 = read_u32le(0x08,sf); + unknown2 = read_u32le(0x0c,sf); /* detect version */ - if (data_size / 2 == file_size - 0x10 - && unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm (PS2) */ + if (data_size == file_size - 0x800 + && unknown1 == 0x00002000 && unknown2 == 0xFFFFFFFF) { /* NYR (PS2) */ + interleave = 0x2000; + start_offset = 0x800; + } + else if (data_size / 2 == file_size - 0x10 + && unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm (PS2) */ data_size = data_size / 2; interleave = 0x4000; start_offset = 0x10; - } else if (data_size / 2 == file_size - 0x10 - && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks (PS2) */ + } + else if (data_size / 2 == file_size - 0x10 + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks (PS2) */ data_size = data_size / 2; interleave = 0x8000; start_offset = 0x10; - } else if (data_size == file_size - 0x10 - && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series (PS2) */ + } + else if (data_size == file_size - 0x10 + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series (PS2) */ interleave = 0x8000; start_offset = 0x10; - } else if (data_size == file_size - 0x4020) { /* CT Special Forces (PS2), and all games beyond */ - interleave = unknown1; /* always 0? */ - if (!interleave) - interleave = 0x10; - start_offset = 0x4020; /* header padding contains garbage */ - } else { + } + else if (data_size == file_size - 0x4020) { /* Counter Terrorism Special Forces (PS2), all games beyond */ + /* header can be section(?) table (0x08=entry count) then 0xCCCCCCCC, all 0s, or all 0xCCCCCCCC (no table) */ + interleave = 0x10; + start_offset = 0x4020; + } + else { goto fail; } //start_offset = file_size - data_size; /* also ok */ channel_count = 2; loop_flag = 0; - sample_rate = read_32bitLE(0x00,streamFile); - - /* the file's end is padded with either 0xcdcdcdcd or zeroes */ - padding_size = joe_find_padding(streamFile, start_offset, data_size, channel_count, interleave); - if (padding_size == SIZE_MAX) - goto fail; + sample_rate = read_s32le(0x00,sf); + /* the file's end is padded with either 0xcdcdcdcd or zeroes (but not always, ex. NYR) */ + padding_size = joe_find_padding(sf, start_offset, data_size, channel_count, interleave); data_size -= padding_size; num_samples = ps_bytes_to_samples(data_size, channel_count); /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; @@ -72,7 +77,7 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { vgmstream->interleave_block_size = interleave; vgmstream->meta_type = meta_PS2_JOE; - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -81,23 +86,23 @@ fail: return NULL; } -static size_t joe_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave) { - uint8_t flag; +static size_t joe_find_padding(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave) { + uint32_t pad; off_t min_offset, offset; size_t frame_size = 0x10; size_t padding_size = 0; size_t interleave_consumed = 0; if (data_size == 0 || channels == 0 || (channels > 0 && interleave == 0)) - return SIZE_MAX; + return 0; offset = start_offset + data_size - interleave * (channels - 1); min_offset = start_offset; while (offset > min_offset) { offset -= frame_size; - flag = read_8bit(offset + 0x01, streamFile); - if (flag == 0x03) + pad = read_u32be(offset, sf); + if (pad != 0xCDCDCDCD && pad != 0x00000000) break; padding_size += frame_size * channels; @@ -111,7 +116,7 @@ static size_t joe_find_padding(STREAMFILE *streamFile, off_t start_offset, size_ } if (padding_size >= data_size) - return SIZE_MAX; + return 0; return padding_size; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag_snk.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag_snk.c deleted file mode 100644 index 6bcb220ce..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag_snk.c +++ /dev/null @@ -1,80 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* PS2 SVAG (SNK) - * - * Found in SNK's World Heroes Anthology and Fatal Fury Battle Archives 2, maybe others - * No relation with Konami's SVAG. - */ - -VGMSTREAM * init_vgmstream_ps2_svag_snk(STREAMFILE* streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - off_t start_offset = 0x20; - - int loop_flag; - int channel_count; - int loop_start_block; - int loop_end_block; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("svag",filename_extension(filename))) goto fail; - - /* check SNK SVAG Header ("VAGm") */ - if (read_32bitBE(0x00,streamFile) != 0x5641476D) - goto fail; - - - channel_count = read_32bitLE(0x0c,streamFile); - - loop_start_block = read_32bitLE(0x18,streamFile); - loop_end_block = read_32bitLE(0x1c,streamFile); - - loop_flag = loop_end_block > 0; /* loop_start_block can be 0 */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* header data */ - vgmstream->coding_type = coding_PSX; - vgmstream->meta_type = meta_PS2_SVAG_SNK; - - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - vgmstream->num_samples = read_32bitLE(0x10,streamFile) * 28; /* size in blocks */ - if( vgmstream->loop_flag ) { - vgmstream->loop_start_sample = loop_start_block * 28; - vgmstream->loop_end_sample = loop_end_block * 28; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = - start_offset + vgmstream->interleave_block_size*i; - } - } - - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/redspark.c b/Frameworks/vgmstream/vgmstream/src/meta/redspark.c index fe8f0b93c..15c39be8e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/redspark.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/redspark.c @@ -23,8 +23,8 @@ VGMSTREAM * init_vgmstream_redspark(STREAMFILE *streamFile) { uint32_t key; enum {encsize = 0x1000}; uint8_t buf[encsize]; - int32_t(*get_32bit)(uint8_t *p) = NULL; - int16_t(*get_16bit)(uint8_t *p) = NULL; + int32_t(*get_32bit)(const uint8_t *p) = NULL; + int16_t(*get_16bit)(const uint8_t *p) = NULL; get_16bit = get_16bitBE; get_32bit = get_32bitBE; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index d61ade953..e8dea9b41 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -9,7 +9,7 @@ /* return milliseconds */ -static long parse_adtl_marker(unsigned char * marker) { +static long parse_adtl_marker(unsigned char* marker) { long hh,mm,ss,ms; if (memcmp("Marker ",marker,7)) return -1; @@ -21,14 +21,14 @@ static long parse_adtl_marker(unsigned char * marker) { } /* loop points have been found hiding here */ -static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, long *loop_start, long *loop_end, int *loop_flag) { +static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE* sf, long* loop_start, long* loop_end, int* loop_flag) { int loop_start_found = 0; int loop_end_found = 0; off_t current_chunk = adtl_offset+0x04; while (current_chunk < adtl_offset + adtl_length) { - uint32_t chunk_type = read_32bitBE(current_chunk+0x00,streamFile); - off_t chunk_size = read_32bitLE(current_chunk+0x04,streamFile); + uint32_t chunk_type = read_32bitBE(current_chunk+0x00,sf); + off_t chunk_size = read_32bitLE(current_chunk+0x04,sf); if (current_chunk+0x08+chunk_size > adtl_offset+adtl_length) return; @@ -37,12 +37,12 @@ static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *stream case 0x6c61626c: { /* "labl" */ unsigned char *labelcontent = malloc(chunk_size-0x04); if (!labelcontent) return; - if (read_streamfile(labelcontent,current_chunk+0x0c, chunk_size-0x04,streamFile) != chunk_size-0x04) { + if (read_streamfile(labelcontent,current_chunk+0x0c, chunk_size-0x04,sf) != chunk_size-0x04) { free(labelcontent); return; } - switch (read_32bitLE(current_chunk+8,streamFile)) { + switch (read_32bitLE(current_chunk+8,sf)) { case 1: if (!loop_start_found && (*loop_start = parse_adtl_marker(labelcontent)) >= 0) loop_start_found = 1; @@ -95,29 +95,29 @@ typedef struct { int is_at9; } riff_fmt_chunk; -static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, riff_fmt_chunk * fmt, int mwv) { +static int read_fmt(int big_endian, STREAMFILE* sf, off_t current_chunk, riff_fmt_chunk* fmt, int mwv) { int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; fmt->offset = current_chunk; - fmt->size = read_32bit(current_chunk+0x04,streamFile); + fmt->size = read_32bit(current_chunk+0x04,sf); /* WAVEFORMAT */ - fmt->codec = (uint16_t)read_16bit(current_chunk+0x08,streamFile); - fmt->channel_count = read_16bit(current_chunk+0x0a,streamFile); - fmt->sample_rate = read_32bit(current_chunk+0x0c,streamFile); - //fmt->avg_bps = read_32bit(current_chunk+0x10,streamFile); - fmt->block_size = read_16bit(current_chunk+0x14,streamFile); - fmt->bps = read_16bit(current_chunk+0x16,streamFile); + fmt->codec = (uint16_t)read_16bit(current_chunk+0x08,sf); + fmt->channel_count = read_16bit(current_chunk+0x0a,sf); + fmt->sample_rate = read_32bit(current_chunk+0x0c,sf); + //fmt->avg_bps = read_32bit(current_chunk+0x10,sf); + fmt->block_size = read_16bit(current_chunk+0x14,sf); + fmt->bps = read_16bit(current_chunk+0x16,sf); /* WAVEFORMATEX */ if (fmt->size >= 0x10) { - fmt->extra_size = read_16bit(current_chunk+0x18,streamFile); + fmt->extra_size = read_16bit(current_chunk+0x18,sf); /* 0x1a+ depends on codec (ex. coef table for MSADPCM, samples_per_frame in MS-IMA, etc) */ } /* WAVEFORMATEXTENSIBLE */ if (fmt->codec == 0xFFFE && fmt->extra_size >= 0x16) { - //fmt->extra_samples = read_16bit(current_chunk+0x1a,streamFile); /* valid_bits_per_sample or samples_per_block */ - fmt->channel_layout = read_32bit(current_chunk+0x1c,streamFile); + //fmt->extra_samples = read_16bit(current_chunk+0x1a,sf); /* valid_bits_per_sample or samples_per_block */ + fmt->channel_layout = read_32bit(current_chunk+0x1c,sf); /* 0x10 guid at 0x20 */ /* happens in various .at3/at9, may be a bug in their encoder b/c MS's defs set mono as FC */ @@ -158,7 +158,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk case 0x02: /* MSADPCM */ if (fmt->bps == 4) { fmt->coding_type = coding_MSADPCM; - if (!msadpcm_check_coefs(streamFile, fmt->offset + 0x08 + 0x14)) + if (!msadpcm_check_coefs(sf, fmt->offset + 0x08 + 0x14)) goto fail; } else if (fmt->bps == 16 && fmt->block_size == 0x02 * fmt->channel_count && fmt->size == 0x14) { @@ -188,7 +188,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk case 0x007A: /* MS IMA ADPCM [LA Rush (PC), Psi Ops (PC)] (unofficial) */ /* 0x007A is apparently "Voxware SC3" but in .MED it's just MS-IMA (0x11) */ - if (!check_extensions(streamFile,"med")) + if (!check_extensions(sf,"med")) goto fail; if (fmt->bps == 4) /* normal MS IMA */ @@ -232,11 +232,11 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk #endif case 0xFFFE: { /* WAVEFORMATEXTENSIBLE (see ksmedia.h for known GUIDs) */ - uint32_t guid1 = (uint32_t)read_32bit (current_chunk+0x20,streamFile); - uint32_t guid2 = ((uint16_t)read_16bit (current_chunk+0x24,streamFile) << 16u) | - ((uint16_t)read_16bit (current_chunk+0x26,streamFile)); - uint32_t guid3 = (uint32_t)read_32bitBE(current_chunk+0x28,streamFile); - uint32_t guid4 = (uint32_t)read_32bitBE(current_chunk+0x2c,streamFile); + uint32_t guid1 = (uint32_t)read_32bit (current_chunk+0x20,sf); + uint32_t guid2 = ((uint16_t)read_16bit (current_chunk+0x24,sf) << 16u) | + ((uint16_t)read_16bit (current_chunk+0x26,sf)); + uint32_t guid3 = (uint32_t)read_32bitBE(current_chunk+0x28,sf); + uint32_t guid4 = (uint32_t)read_32bitBE(current_chunk+0x2c,sf); //;VGM_LOG("RIFF: guid %08x %08x %08x %08x\n", guid1, guid2, guid3, guid4); /* PCM GUID (0x00000001,0000,0010,80,00,00,AA,00,38,9B,71) */ @@ -255,7 +255,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk /* ATRAC3plus GUID (0xE923AABF,CB58,4471,A1,19,FF,FA,01,E4,CE,62) */ if (guid1 == 0xE923AABF && guid2 == 0xCB584471 && guid3 == 0xA119FFFA && guid4 == 0x01E4CE62) { #ifdef VGM_USE_MAIATRAC3PLUS - uint16_t bztmp = read_16bit(current_chunk+0x32,streamFile); + uint16_t bztmp = read_16bit(current_chunk+0x32,sf); bztmp = (bztmp >> 8) | (bztmp << 8); fmt->coding_type = coding_AT3plus; fmt->block_size = (bztmp & 0x3FF) * 8 + 8; /* should match fmt->block_size */ @@ -294,12 +294,12 @@ fail: return 0; } -static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset); -static size_t get_ue4_msadpcm_interleave(STREAMFILE *sf, riff_fmt_chunk *fmt, off_t start, size_t size); +static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* sf, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset); +static size_t get_ue4_msadpcm_interleave(STREAMFILE* sf, riff_fmt_chunk* fmt, off_t start, size_t size); -VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; riff_fmt_chunk fmt = {0}; size_t file_size, riff_size, data_size = 0; @@ -317,7 +317,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { int FormatChunkFound = 0, DataChunkFound = 0, JunkFound = 0; - int mwv = 0; /* Level-5 .mwv (Dragon Quest VIII, Rogue Galaxy) */ + int mwv = 0; off_t mwv_pflt_offset = -1; off_t mwv_ctrl_offset = -1; @@ -346,11 +346,12 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { * .aud: EA Replay ATRAC3 * .at9: standard ATRAC9 * .saf: Whacked! (Xbox) + * .mwv: Level-5 games [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)] */ - if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf") ) { + if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf") ) { ; } - else if ( check_extensions(streamFile, "mwv") ) { + else if ( check_extensions(sf, "mwv") ) { mwv = 1; } else { @@ -358,56 +359,72 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { } /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */ + if (read_32bitBE(0x00,sf) != 0x52494646) /* "RIFF" */ goto fail; - if (read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */ + if (read_32bitBE(0x08,sf) != 0x57415645) /* "WAVE" */ goto fail; - riff_size = read_32bitLE(0x04,streamFile); - file_size = get_streamfile_size(streamFile); + riff_size = read_32bitLE(0x04,sf); + file_size = get_streamfile_size(sf); /* some games have wonky sizes, selectively fix to catch bad rips and new mutations */ - { - uint16_t codec = read_16bitLE(0x14,streamFile); - if (riff_size+0x08+0x01 == file_size) - riff_size += 0x01; /* [Shikkoku no Sharnoth (PC)] */ + if (file_size != riff_size + 0x08) { + uint16_t codec = read_16bitLE(0x14,sf); - else if (riff_size == file_size && codec == 0x0069) + if (codec == 0x6771 && riff_size + 0x08 + 0x01 == file_size) + riff_size += 0x01; /* [Shikkoku no Sharnoth (PC)] (Sony Sound Forge?) */ + + else if (codec == 0x0069 && riff_size == file_size) riff_size -= 0x08; /* [Dynasty Warriors 3 (Xbox), BloodRayne (Xbox)] */ - else if (riff_size + 0x04 == file_size && codec == 0x0069) + else if (codec == 0x0069 && riff_size + 0x04 == file_size) riff_size -= 0x04; /* [Halo 2 (PC)] (possibly bad extractor? 'Gravemind Tool') */ - else if (riff_size + 0x04 == file_size && codec == 0x0000) + else if (codec == 0x0000 && riff_size + 0x04 == file_size) riff_size -= 0x04; /* [Headhunter (DC), Bomber hehhe (DC)] */ - else if (riff_size == file_size && codec == 0x0000) + else if (codec == 0x0000 && riff_size == file_size) riff_size -= 0x08; /* [Rayman 2 (DC)] */ - else if (riff_size + 0x02 + 0x08 == file_size && codec == 0x0000) + else if (codec == 0x0000 && riff_size + 0x08 + 0x02 == file_size) riff_size -= 0x02; /* [Rayman 2 (DC)]-dcz */ - else if (riff_size == file_size && codec == 0x0300) + else if (codec == 0x0300 && riff_size == file_size) riff_size -= 0x08; /* [Chrono Ma:gia (Android)] */ - else if (riff_size >= file_size && read_32bitBE(0x24,streamFile) == 0x4E584246) /* "NXBF" */ + else if (codec == 0xFFFE && riff_size + 0x08 + 0x18 == file_size) + riff_size += 0x18; /* [F1 2011 (Vita)] (adds a "pada" chunk but RIFF size wasn't updated) */ + + else if (mwv) { + int channels = read_16bitLE(0x16, sf); /* [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)] */ + size_t file_size_fixed = riff_size + 0x08 + 0x04 * (channels - 1); + + if (file_size_fixed <= file_size && file_size - file_size_fixed < 0x10) + { + /* files inside HD6/DAT are also padded to 0x10 so need to fix file_size */ + file_size = file_size_fixed; + riff_size = file_size - 0x08; + } + } + + else if (riff_size >= file_size && read_32bitBE(0x24,sf) == 0x4E584246) /* "NXBF" */ riff_size = file_size - 0x08; /* [R:Racing Evolution (Xbox)] */ - else if (riff_size / 4 + 0x4800 + 0x3c == file_size && codec == 0x0011) /* riff_size ~= data_size * 4, always has fact and 0x14 fmt */ - riff_size = file_size - 0x08; /* [Asphalt 6 (iOS)] (sfx/memory wavs have ok sizes, only streaming wavs?) */ + else if (codec == 0x0011 && (riff_size / 2 / 2 == read_32bitLE(0x30,sf))) /* riff_size = pcm_size (always stereo, has fact at 0x30) */ + riff_size = file_size - 0x08; /* [Asphalt 6 (iOS)] (sfx/memory wavs have ok sizes?) */ } /* check for truncated RIFF */ - if (file_size < riff_size+0x08) + if (file_size != riff_size + 0x08) goto fail; /* read through chunks to verify format and find metadata */ { off_t current_chunk = 0x0c; /* start with first chunk */ - while (current_chunk < file_size && current_chunk < riff_size+8) { - uint32_t chunk_id = read_32bitBE(current_chunk + 0x00,streamFile); /* FOURCC */ - size_t chunk_size = read_32bitLE(current_chunk + 0x04,streamFile); + while (current_chunk < file_size) { + uint32_t chunk_id = read_32bitBE(current_chunk + 0x00,sf); /* FOURCC */ + size_t chunk_size = read_32bitLE(current_chunk + 0x04,sf); if (current_chunk + 0x08 + chunk_size > file_size) goto fail; @@ -417,7 +434,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { if (FormatChunkFound) goto fail; /* only one per file */ FormatChunkFound = 1; - if (!read_fmt(0, streamFile, current_chunk, &fmt, mwv)) + if (!read_fmt(0, sf, current_chunk, &fmt, mwv)) goto fail; /* some Dreamcast/Naomi games again [Headhunter (DC), Bomber hehhe (DC), Rayman 2 (DC)] */ @@ -434,11 +451,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { break; case 0x4C495354: /* "LIST" */ - switch (read_32bitBE(current_chunk+0x08, streamFile)) { + switch (read_32bitBE(current_chunk+0x08, sf)) { case 0x6164746C: /* "adtl" */ /* yay, atdl is its own little world */ parse_adtl(current_chunk + 8, chunk_size, - streamFile, + sf, &loop_start_ms,&loop_end_ms,&loop_flag); break; default: @@ -450,13 +467,13 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* check loop count/loop info (most common) */ /* 0x00: manufacturer id, 0x04: product id, 0x08: sample period, 0x0c: unity node, * 0x10: pitch fraction, 0x14: SMPTE format, 0x18: SMPTE offset, 0x1c: loop count, 0x20: sampler data */ - if (read_32bitLE(current_chunk+0x08+0x1c, streamFile) == 1) { /* handle only one loop (could contain N MIDILoop) */ + if (read_32bitLE(current_chunk+0x08+0x1c, sf) == 1) { /* handle only one loop (could contain N MIDILoop) */ /* 0x24: cue point id, 0x28: type (0=forward, 1=alternating, 2=backward) * 0x2c: start, 0x30: end, 0x34: fraction, 0x38: play count */ - if (read_32bitLE(current_chunk+0x08+0x28, streamFile) == 0) { /* loop forward */ + if (read_32bitLE(current_chunk+0x08+0x28, sf) == 0) { /* loop forward */ loop_flag = 1; - loop_start_smpl = read_32bitLE(current_chunk+0x08+0x2c, streamFile); - loop_end_smpl = read_32bitLE(current_chunk+0x08+0x30, streamFile) + 1; /* must add 1 as per spec (ok for standard WAV/AT3/AT9) */ + loop_start_smpl = read_32bitLE(current_chunk+0x08+0x2c, sf); + loop_end_smpl = read_32bitLE(current_chunk+0x08+0x30, sf) + 1; /* must add 1 as per spec (ok for standard WAV/AT3/AT9) */ } } break; @@ -465,14 +482,14 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* check loop count/info (found in some Xbox games: Halo (non-looping), Dynasty Warriors 3, Crimson Sea) */ /* 0x00: size, 0x04: unity note, 0x06: fine tune, 0x08: gain, 0x10: loop count */ if (chunk_size >= 0x24 - && read_32bitLE(current_chunk+0x08+0x00, streamFile) == 0x14 - && read_32bitLE(current_chunk+0x08+0x10, streamFile) > 0 - && read_32bitLE(current_chunk+0x08+0x14, streamFile) == 0x10) { + && read_32bitLE(current_chunk+0x08+0x00, sf) == 0x14 + && read_32bitLE(current_chunk+0x08+0x10, sf) > 0 + && read_32bitLE(current_chunk+0x08+0x14, sf) == 0x10) { /* 0x14: size, 0x18: loop type (0=forward, 1=release), 0x1c: loop start, 0x20: loop length */ - if (read_32bitLE(current_chunk+0x08+0x18, streamFile) == 0) { /* loop forward */ + if (read_32bitLE(current_chunk+0x08+0x18, sf) == 0) { /* loop forward */ loop_flag = 1; - loop_start_wsmp = read_32bitLE(current_chunk+0x08+0x1c, streamFile); - loop_end_wsmp = read_32bitLE(current_chunk+0x08+0x20, streamFile); /* must not add 1 as per spec */ + loop_start_wsmp = read_32bitLE(current_chunk+0x08+0x1c, sf); + loop_end_wsmp = read_32bitLE(current_chunk+0x08+0x20, sf); /* must not add 1 as per spec */ loop_end_wsmp += loop_start_wsmp; } } @@ -480,24 +497,24 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { case 0x66616374: /* "fact" */ if (chunk_size == 0x04) { /* standard (usually for ADPCM, MS recommends to set for non-PCM codecs) */ - fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + fact_sample_count = read_32bitLE(current_chunk+0x08, sf); } - else if (chunk_size == 0x10 && read_32bitBE(current_chunk+0x08+0x04, streamFile) == 0x4C794E20) { /* "LyN " */ + else if (chunk_size == 0x10 && read_32bitBE(current_chunk+0x08+0x04, sf) == 0x4C794E20) { /* "LyN " */ goto fail; /* parsed elsewhere */ } else if ((fmt.is_at3 || fmt.is_at3p) && chunk_size == 0x08) { /* early AT3 (mainly PSP games) */ - fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); - fact_sample_skip = read_32bitLE(current_chunk+0x0c, streamFile); /* base skip samples */ + fact_sample_count = read_32bitLE(current_chunk+0x08, sf); + fact_sample_skip = read_32bitLE(current_chunk+0x0c, sf); /* base skip samples */ } else if ((fmt.is_at3 || fmt.is_at3p) && chunk_size == 0x0c) { /* late AT3 (mainly PS3 games and few PSP games) */ - fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + fact_sample_count = read_32bitLE(current_chunk+0x08, sf); /* 0x0c: base skip samples, ignored by decoder */ - fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); /* skip samples with extra 184 */ + fact_sample_skip = read_32bitLE(current_chunk+0x10, sf); /* skip samples with extra 184 */ } else if (fmt.is_at9 && chunk_size == 0x0c) { - fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + fact_sample_count = read_32bitLE(current_chunk+0x08, sf); /* 0x0c: base skip samples (same as next field) */ - fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); + fact_sample_skip = read_32bitLE(current_chunk+0x10, sf); } break; @@ -508,18 +525,18 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { case 0x6374726c: /* "ctrl" (.mwv extension) */ if (!mwv) break; - loop_flag = read_32bitLE(current_chunk+0x08, streamFile); + loop_flag = read_32bitLE(current_chunk+0x08, sf); mwv_ctrl_offset = current_chunk; break; case 0x63756520: /* "cue " (used in Source Engine for storing loop points) */ if (fmt.coding_type == coding_PCM16LE || fmt.coding_type == coding_MSADPCM) { - uint32_t num_cues = read_32bitLE(current_chunk + 0x08, streamFile); + uint32_t num_cues = read_32bitLE(current_chunk + 0x08, sf); if (num_cues > 0) { /* The second cue sets loop end point but it's not actually used by the engine. */ loop_flag = 1; - loop_start_cue = read_32bitLE(current_chunk + 0x20, streamFile); + loop_start_cue = read_32bitLE(current_chunk + 0x20, sf); } } break; @@ -531,7 +548,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* 0x08: data size */ /* 0x0c: channels */ /* 0x10: null */ - loop_start_nxbf = read_32bitLE(current_chunk + 0x08 + 0x14, streamFile); + loop_start_nxbf = read_32bitLE(current_chunk + 0x08 + 0x14, sf); /* 0x18: sample rate */ /* 0x1c: volume? (0x3e8 = 1000 = max) */ /* 0x20: type/flags? */ @@ -569,20 +586,20 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { * As JUNK is legal (if unusual) we only reject those codecs. * (ex. Cave PC games have PCM16LE + JUNK + smpl created by "Samplitude software") */ if (JunkFound - && check_extensions(streamFile,"wav,lwav") /* for some .MED IMA */ + && check_extensions(sf,"wav,lwav") /* for some .MED IMA */ && (fmt.coding_type==coding_MSADPCM /*|| fmt.coding_type==coding_MS_IMA*/ || fmt.coding_type==coding_XBOX_IMA)) goto fail; /* ignore Beyond Good & Evil HD PS3 evil reuse of PCM codec */ if (fmt.coding_type == coding_PCM16LE && - read_32bitBE(start_offset+0x00, streamFile) == 0x4D534643 && /* "MSF\43" */ - read_32bitBE(start_offset+0x34, streamFile) == 0xFFFFFFFF && /* always */ - read_32bitBE(start_offset+0x38, streamFile) == 0xFFFFFFFF && - read_32bitBE(start_offset+0x3c, streamFile) == 0xFFFFFFFF) + read_32bitBE(start_offset+0x00, sf) == 0x4D534643 && /* "MSF\43" */ + read_32bitBE(start_offset+0x34, sf) == 0xFFFFFFFF && /* always */ + read_32bitBE(start_offset+0x38, sf) == 0xFFFFFFFF && + read_32bitBE(start_offset+0x3c, sf) == 0xFFFFFFFF) goto fail; /* ignore Gitaroo Man Live! (PSP) multi-RIFF (to allow chunked TXTH) */ - if (fmt.is_at3 && get_streamfile_size(streamFile) > 0x2800 && read_32bitBE(0x2800, streamFile) == 0x52494646) { /* "RIFF" */ + if (fmt.is_at3 && get_streamfile_size(sf) > 0x2800 && read_32bitBE(0x2800, sf) == 0x52494646) { /* "RIFF" */ goto fail; } @@ -647,17 +664,17 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { { int i, ch; const int filter_order = 3; - int filter_count = read_32bitLE(mwv_pflt_offset+0x0c, streamFile); + int filter_count = read_32bitLE(mwv_pflt_offset+0x0c, sf); if (filter_count > 0x20) goto fail; if (mwv_pflt_offset == -1 || - read_32bitLE(mwv_pflt_offset+0x08, streamFile) != filter_order || - read_32bitLE(mwv_pflt_offset+0x04, streamFile) < 8 + filter_count * 4 * filter_order) + read_32bitLE(mwv_pflt_offset+0x08, sf) != filter_order || + read_32bitLE(mwv_pflt_offset+0x04, sf) < 8 + filter_count * 4 * filter_order) goto fail; for (ch = 0; ch < fmt.channel_count; ch++) { for (i = 0; i < filter_count * filter_order; i++) { - int coef = read_32bitLE(mwv_pflt_offset+0x10+i*0x04, streamFile); + int coef = read_32bitLE(mwv_pflt_offset+0x10+i*0x04, sf); vgmstream->ch[ch].adpcm_coef_3by32[i] = coef; } } @@ -697,7 +714,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { case coding_FFmpeg: { if (!fmt.is_at3 && !fmt.is_at3p) goto fail; - vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, 0x00, NULL); + vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, 0x00, NULL); if (!vgmstream->codec_data) goto fail; vgmstream->num_samples = fact_sample_count; @@ -730,7 +747,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { atrac9_config cfg = {0}; cfg.channels = vgmstream->channels; - cfg.config_data = read_32bitBE(fmt.offset+0x08+0x2c,streamFile); + cfg.config_data = read_32bitBE(fmt.offset+0x08+0x2c,sf); cfg.encoder_delay = fact_sample_skip; vgmstream->codec_data = init_atrac9(&cfg); @@ -749,7 +766,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { #ifdef VGM_USE_VORBIS case coding_OGG_VORBIS: { /* special handling of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */ - STREAMFILE *temp_sf = setup_riff_ogg_streamfile(streamFile, start_offset, data_size); + STREAMFILE *temp_sf = setup_riff_ogg_streamfile(sf, start_offset, data_size); if (!temp_sf) goto fail; vgmstream->codec_data = init_ogg_vorbis(temp_sf, 0x00, get_streamfile_size(temp_sf), NULL); @@ -766,13 +783,13 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { } /* UE4 uses interleaved mono MSADPCM, try to autodetect without breaking normal MSADPCM */ - if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, streamFile, &fmt, fact_sample_count, start_offset)) { + if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, sf, &fmt, fact_sample_count, start_offset)) { vgmstream->coding_type = coding_MSADPCM_int; vgmstream->frame_size = fmt.block_size; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = get_ue4_msadpcm_interleave(streamFile, &fmt, start_offset, data_size); + vgmstream->interleave_block_size = get_ue4_msadpcm_interleave(sf, &fmt, start_offset, data_size); if (fmt.size == 0x36) - vgmstream->num_samples = read_s32le(fmt.offset+0x08+0x32, streamFile); + vgmstream->num_samples = read_s32le(fmt.offset+0x08+0x32, sf); } /* Dynasty Warriors 5 (Xbox) 6ch interleaves stereo frames, probably not official */ @@ -807,7 +824,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->meta_type = meta_RIFF_WAVE_wsmp; } else if (mwv && mwv_ctrl_offset != -1) { - vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, streamFile); + vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, sf); vgmstream->loop_end_sample = vgmstream->num_samples; } else if (loop_start_cue != -1) { @@ -827,7 +844,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->meta_type = meta_RIFF_WAVE_MWV; } - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -838,14 +855,14 @@ fail: } /* UE4 MSADPCM is quite normal but has a few minor quirks we can use to detect it */ -static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start) { +static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* sf, riff_fmt_chunk* fmt, int fact_sample_count, off_t start) { /* multichannel ok */ if (fmt->channel_count < 2) goto fail; /* UE4 class is "ADPCM", assume it's the extension too */ - if (!check_extensions(streamFile, "adpcm")) + if (!check_extensions(sf, "adpcm")) goto fail; /* UE4 encoder doesn't add "fact" */ @@ -864,13 +881,13 @@ static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt if (fmt->size == 0x32) { off_t offset = start; off_t max_offset = 5 * fmt->block_size; /* try N blocks */ - if (max_offset > get_streamfile_size(streamFile)) - max_offset = get_streamfile_size(streamFile); + if (max_offset > get_streamfile_size(sf)) + max_offset = get_streamfile_size(sf); /* their encoder doesn't calculate optimal coefs and uses fixed values every frame * (could do it for fmt size 0x36 too but maybe they'll fix it in the future) */ while (offset <= max_offset) { - if (read_8bit(offset+0x00, streamFile) != 0 || read_16bitLE(offset+0x01, streamFile) != 0x00E6) + if (read_8bit(offset+0x00, sf) != 0 || read_16bitLE(offset+0x01, sf) != 0x00E6) goto fail; offset += fmt->block_size; } @@ -883,7 +900,7 @@ fail: /* for maximum annoyance later UE4 versions (~v4.2x?) interleave single frames instead of * half interleave, but don't have flags to detect so we need some heuristics */ -static size_t get_ue4_msadpcm_interleave(STREAMFILE *sf, riff_fmt_chunk *fmt, off_t start, size_t size) { +static size_t get_ue4_msadpcm_interleave(STREAMFILE* sf, riff_fmt_chunk* fmt, off_t start, size_t size) { size_t v1_interleave = size / fmt->channel_count; size_t v2_interleave = fmt->block_size; uint8_t nibbles1[0x08] = {0}; @@ -940,8 +957,8 @@ static size_t get_ue4_msadpcm_interleave(STREAMFILE *sf, riff_fmt_chunk *fmt, of } /* same but big endian, seen in the spec and in Kitchenette (PC) (possibly from Adobe Director) */ -VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_rifx(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; riff_fmt_chunk fmt = {0}; size_t file_size, riff_size, data_size = 0; @@ -955,17 +972,17 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { /* check extension, case insensitive */ - if ( !check_extensions(streamFile, "wav,lwav") ) + if ( !check_extensions(sf, "wav,lwav") ) goto fail; /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x52494658) /* "RIFX" */ + if (read_32bitBE(0x00,sf) != 0x52494658) /* "RIFX" */ goto fail; - if (read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */ + if (read_32bitBE(0x08,sf) != 0x57415645) /* "WAVE" */ goto fail; - riff_size = read_32bitBE(0x04,streamFile); - file_size = get_streamfile_size(streamFile); + riff_size = read_32bitBE(0x04,sf); + file_size = get_streamfile_size(sf); /* check for truncated RIFF */ if (file_size < riff_size+8) goto fail; @@ -975,8 +992,8 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { off_t current_chunk = 0xc; /* start with first chunk */ while (current_chunk < file_size && current_chunk < riff_size+8) { - uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitBE(current_chunk+4,streamFile); + uint32_t chunk_type = read_32bitBE(current_chunk,sf); + off_t chunk_size = read_32bitBE(current_chunk+4,sf); if (current_chunk+8+chunk_size > file_size) goto fail; @@ -986,7 +1003,7 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { if (FormatChunkFound) goto fail; FormatChunkFound = 1; - if (!read_fmt(1, streamFile, current_chunk, &fmt, 0)) + if (!read_fmt(1, sf, current_chunk, &fmt, 0)) goto fail; break; @@ -1000,11 +1017,11 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { break; case 0x736D706C: /* smpl */ /* check loop count and loop info */ - if (read_32bitBE(current_chunk+0x24, streamFile)==1) { - if (read_32bitBE(current_chunk+0x2c+4, streamFile)==0) { + if (read_32bitBE(current_chunk+0x24, sf)==1) { + if (read_32bitBE(current_chunk+0x2c+4, sf)==0) { loop_flag = 1; - loop_start_offset = read_32bitBE(current_chunk+0x2c+8, streamFile); - loop_end_offset = read_32bitBE(current_chunk+0x2c+0xc,streamFile) + 1; + loop_start_offset = read_32bitBE(current_chunk+0x2c+8, sf); + loop_end_offset = read_32bitBE(current_chunk+0x2c+0xc,sf) + 1; } } break; @@ -1062,7 +1079,7 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { } - if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c index d1c8502b4..99bd6f95f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c @@ -13,7 +13,7 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile,"rsd")) + if (!check_extensions(streamFile,"rsd,rsp")) goto fail; if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x52534400) /* "RSD\00" */ goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sab.c b/Frameworks/vgmstream/vgmstream/src/meta/sab.c index 3fce2b609..b7b8fbc2b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sab.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sab.c @@ -1,157 +1,276 @@ -#include "meta.h" -#include "../util.h" -#include "../coding/coding.h" - -static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int target_stream); - -/* SAB - from Worms 4: Mayhem (PC/Xbox/PS2) */ -VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, stream_size, loop_start, loop_end; - int total_subsongs, target_subsong = streamFile->stream_index; - - /* .sab: main, .sob: config/names */ - if (!check_extensions(streamFile,"sab")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x43535732 && /* "CSW2" (Windows) */ - read_32bitBE(0x00,streamFile) != 0x43535032 && /* "CSP2" (PS2) */ - read_32bitBE(0x00,streamFile) != 0x43535832) /* "CSX2" (Xbox) */ - goto fail; - - is_stream = read_32bitLE(0x04,streamFile) & 0x04; /* other flags don't seem to matter */ - total_subsongs = is_stream ? 1 : read_32bitLE(0x08,streamFile); - if (target_subsong == 0) target_subsong = 1; - if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - - align = read_32bitLE(0x0c,streamFile); /* doubles as interleave */ - - /* stream config */ - codec = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x00,streamFile); - channel_count = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x04,streamFile); - sample_rate = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x08,streamFile); - stream_size = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x0c,streamFile); - loop_start = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x10,streamFile); - loop_end = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x14,streamFile); - loop_flag = (loop_end > 0); - - start_offset = 0x18 + 0x1c*total_subsongs; - if (start_offset % align) - start_offset += align - (start_offset % align); - start_offset += read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x18,streamFile); - - if (is_stream) { - channel_count = read_32bitLE(0x08,streamFile); /* uncommon, but non-stream stereo exists */ - stream_size *= channel_count; - } - - /* 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_SAB; - - switch(codec) { - case 0x01: /* PC */ - vgmstream->coding_type = coding_PCM16LE; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = is_stream ? align : 0x02; - - vgmstream->num_samples = pcm_bytes_to_samples(stream_size, vgmstream->channels, 16); - vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, vgmstream->channels, 16); - vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, vgmstream->channels, 16); - - break; - - case 0x04: /* PS2 */ - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = is_stream ? align : 0x10; - - vgmstream->num_samples = ps_bytes_to_samples(stream_size, vgmstream->channels); - vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels); - vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels); - break; - - case 0x08: /* Xbox */ - vgmstream->coding_type = is_stream ? coding_XBOX_IMA_int : coding_XBOX_IMA; - vgmstream->layout_type = is_stream ? layout_interleave : layout_none; - vgmstream->interleave_block_size = is_stream ? align : 0x00; - - vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, vgmstream->channels); - vgmstream->loop_start_sample = xbox_ima_bytes_to_samples(loop_start, vgmstream->channels); - vgmstream->loop_end_sample = xbox_ima_bytes_to_samples(loop_end, vgmstream->channels); - break; - - default: - VGM_LOG("SAB: unknown codec\n"); - goto fail; - } - - get_stream_name(vgmstream->stream_name, streamFile, target_subsong); - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - -/* multiple streams may share a name, also sometimes won't a the name */ -static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int target_stream) { - STREAMFILE * streamInfo = NULL; - int i, j, total_cues, num_cue = -1; - size_t name_size = 0; - off_t name_offset = 0x10; - - streamInfo = open_streamfile_by_ext(streamFile, "sob"); - if (!streamInfo) goto end; - if (read_32bitBE(0x00,streamInfo) != 0x43544632) /* "CTF2" */ - goto end; - - total_cues = read_32bitLE(0x08,streamInfo); - - for (i = 0; i < total_cues; i++) { - uint32_t flags, num_subsections, subsection_1_size, subsection_2_size; - - flags = (uint32_t)read_32bitLE(name_offset + 0x00,streamInfo); - num_subsections = (uint32_t)read_32bitLE(name_offset + 0x20,streamInfo); - subsection_1_size = (flags & 0x00000001) ? 0x40 : 0x00; - subsection_1_size +=(flags & 0x00000040) ? 0x20 : 0x00; - subsection_2_size = (flags & 0x00000100) ? 0x1c : 0x10; - - for (j = 0; j < num_subsections; j++) { - int num_stream = read_32bitLE(name_offset + 0x2c + subsection_1_size + j*subsection_2_size + 0x08,streamInfo); - if (target_stream-1 == num_stream) - num_cue = i; - } - - name_offset += 0x2c + subsection_1_size + subsection_2_size * num_subsections; - } - if (num_cue < 0) - goto end; - - for (i = 0; i < total_cues; i++) { - /* 0x00: id */ - name_size = read_32bitLE(name_offset + 0x04,streamInfo); /* non null-terminated */ - if (i == num_cue) { - name_offset += 0x08; - break; - } - name_offset += 0x08 + name_size; - } - - if (name_size > STREAM_NAME_SIZE-1) - name_size = STREAM_NAME_SIZE-1; - - read_string(stream_name,name_size+1, name_offset,streamInfo); - -end: - if (streamInfo) - close_streamfile(streamInfo); -} +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" +#include "sab_streamfile.h" + +typedef struct { + int total_subsongs; + int target_subsong; + int is_stream; + int is_extra; + + uint32_t flags; + uint32_t sound_count; + uint32_t block_size; + + uint32_t codec; + int loop_flag; + int channel_count; + int sample_rate; + uint32_t stream_size; + int32_t loop_start; + int32_t loop_end; + off_t stream_offset; +} sab_header; + + +static int parse_sab(STREAMFILE* sf, sab_header* sab); +static VGMSTREAM* build_layered_vgmstream(STREAMFILE* sf, sab_header* sab); +static void get_stream_name(char* stream_name, STREAMFILE* sf, int target_stream); + +/* SAB - from Sensaura GameCODA middleware games [Men of Valor (multi), Worms 4: Mayhem (multi), Just Cause (multi)] */ +VGMSTREAM* init_vgmstream_sab(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + sab_header sab = {0}; + + + /* .sab: main, .sob: cue (has config/names) */ + if (!check_extensions(sf,"sab")) + goto fail; + if (read_u32be(0x00,sf) != 0x43535732 && /* "CSW2" (Windows) */ + read_u32be(0x00,sf) != 0x43535032 && /* "CSP2" (PS2) */ + read_u32be(0x00,sf) != 0x43535832) /* "CSX2" (Xbox) */ + goto fail; + + if (!parse_sab(sf, &sab)) + goto fail; + + /* sab can be (both cases handled here): + * - bank: multiple subsongs (each a different header) + * - stream: layers used as different stereo tracks (Men of Valor) or mono tracks to create stereo (Worms 4) + */ + vgmstream = build_layered_vgmstream(sf, &sab); + if (!vgmstream) goto fail; + + vgmstream->num_streams = sab.total_subsongs; + + get_stream_name(vgmstream->stream_name, sf, sab.target_subsong); + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +//todo doesn't seem correct always (also multiple .sob may share .sab) +/* multiple streams may share a name */ +static void get_stream_name(char* stream_name, STREAMFILE* sf, int target_stream) { + STREAMFILE* sf_info = NULL; + int i, j, total_cues, num_cue = -1; + size_t name_size = 0; + off_t name_offset = 0x10; + + sf_info = open_streamfile_by_ext(sf, "sob"); + if (!sf_info) goto end; + if (read_u32be(0x00,sf_info) != 0x43544632) /* "CTF2" */ + goto end; + + total_cues = read_u32le(0x08,sf_info); + if (total_cues > 0x1000) + goto end; + + for (i = 0; i < total_cues; i++) { + uint32_t flags, num_subsections, subsection_1_size, subsection_2_size; + + flags = read_u32le(name_offset + 0x00,sf_info); + num_subsections = read_u32le(name_offset + 0x20,sf_info); + subsection_1_size = (flags & 0x00000001) ? 0x40 : 0x00; + subsection_1_size +=(flags & 0x00000040) ? 0x20 : 0x00; + subsection_2_size = (flags & 0x00000100) ? 0x1c : 0x10; //todo not always correct + + if (num_subsections > 0x1000) + goto end; /* bad read */ + + for (j = 0; j < num_subsections; j++) { + int num_stream = read_u32le(name_offset + 0x2c + subsection_1_size + j*subsection_2_size + 0x08,sf_info); + if (target_stream - 1 == num_stream) + num_cue = i; + } + + name_offset += 0x2c + subsection_1_size + subsection_2_size * num_subsections; + } + if (num_cue < 0) + goto end; + + for (i = 0; i < total_cues; i++) { + /* 0x00: id */ + name_size = read_u32le(name_offset + 0x04,sf_info); /* non null-terminated */ + if (i == num_cue) { + name_offset += 0x08; + break; + } + name_offset += 0x08 + name_size; + } + + if (name_size > STREAM_NAME_SIZE - 1) + name_size = STREAM_NAME_SIZE - 1; + + read_string(stream_name,name_size+1, name_offset,sf_info); + +end: + close_streamfile(sf_info); +} + +static int parse_sab(STREAMFILE* sf, sab_header* sab) { + off_t entry_offset; + + sab->flags = read_u32le(0x04,sf); /* upper byte is always 0x02 (version?) */ + sab->sound_count = read_u32le(0x08,sf); + sab->block_size = read_u32le(0x0c,sf); + /* 0x10: number of blocks */ + /* 0x14: file id? */ + + sab->is_stream = sab->flags & 0x04; /* "not bank" */ + sab->is_extra = sab->flags & 0x10; /* not used in Worms 4 */ + /* flags 1/2 are also common, no flags in banks */ + + sab->total_subsongs = sab->is_stream ? 1 : sab->sound_count; + sab->target_subsong = sf->stream_index; + if (sab->target_subsong == 0) sab->target_subsong = 1; + if (sab->target_subsong < 0 || sab->target_subsong > sab->total_subsongs || sab->total_subsongs < 1) goto fail; + + /* stream config */ + entry_offset = 0x18 + 0x1c * (sab->target_subsong - 1); + sab->codec = read_u32le(entry_offset + 0x00,sf); + sab->channel_count = read_u32le(entry_offset + 0x04,sf); + sab->sample_rate = read_u32le(entry_offset + 0x08,sf); + sab->stream_size = read_u32le(entry_offset + 0x0c,sf); + sab->loop_start = read_u32le(entry_offset + 0x10,sf); + sab->loop_end = read_u32le(entry_offset + 0x14,sf); + sab->loop_flag = (sab->loop_end > 0); + + if (sab->is_stream) { + sab->stream_offset = sab->block_size; + } + else { + sab->stream_offset = 0x18 + 0x1c * sab->total_subsongs; + if (sab->stream_offset % sab->block_size) + sab->stream_offset += sab->block_size - (sab->stream_offset % sab->block_size); + sab->stream_offset += read_u32le(0x18 + 0x1c * (sab->target_subsong - 1) + 0x18,sf); + } + + /* some extra values (counts?) and name at start */ + if (sab->is_extra) + sab->stream_offset += sab->block_size; + + return 1; +fail: + return 0; +} + +static VGMSTREAM* build_vgmstream(STREAMFILE* sf, sab_header* sab) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + + /* in streams streamfile is de-chunked so start becomes 0 */ + start_offset = sab->is_stream ? + 0 : + sab->stream_offset; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(sab->channel_count, sab->loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sab->sample_rate; + vgmstream->stream_size = sab->stream_size; + vgmstream->meta_type = meta_SAB; + + switch(sab->codec) { + case 0x01: /* PC */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + vgmstream->num_samples = pcm_bytes_to_samples(sab->stream_size, vgmstream->channels, 16); + vgmstream->loop_start_sample = pcm_bytes_to_samples(sab->loop_start, vgmstream->channels, 16); + vgmstream->loop_end_sample = pcm_bytes_to_samples(sab->loop_end, vgmstream->channels, 16); + break; + + case 0x04: /* PS2 */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->num_samples = ps_bytes_to_samples(sab->stream_size, vgmstream->channels); + vgmstream->loop_start_sample = ps_bytes_to_samples(sab->loop_start, vgmstream->channels); + vgmstream->loop_end_sample = ps_bytes_to_samples(sab->loop_end, vgmstream->channels); + break; + + case 0x08: /* Xbox */ + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = xbox_ima_bytes_to_samples(sab->stream_size, vgmstream->channels); + vgmstream->loop_start_sample = xbox_ima_bytes_to_samples(sab->loop_start, vgmstream->channels); + vgmstream->loop_end_sample = xbox_ima_bytes_to_samples(sab->loop_end, vgmstream->channels); + break; + + default: + VGM_LOG("SAB: unknown codec\n"); + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +static VGMSTREAM* build_layered_vgmstream(STREAMFILE* sf, sab_header* sab) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + layered_layout_data* data = NULL; + int i; + + if (sab->sound_count == 1 || !sab->is_stream) { + return build_vgmstream(sf, sab); + } + + /* init layout */ + data = init_layout_layered(sab->sound_count); + if (!data) goto fail; + + /* de-chunk audio layers */ + for (i = 0; i < sab->sound_count; i++) { + temp_sf = setup_sab_streamfile(sf, sab->stream_offset, sab->sound_count, i, sab->block_size); + if (!temp_sf) goto fail; + + data->layers[i] = build_vgmstream(temp_sf, sab); + if (!data->layers[i]) goto fail; + + close_streamfile(temp_sf); + temp_sf = NULL; + } + + /* setup VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + + /* build the layout VGMSTREAM */ + vgmstream = allocate_layered_vgmstream(data); + if (!vgmstream) goto fail; + + //sab->stream_size = sab->stream_size * sab->sound_count; /* ? */ + + return vgmstream; +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + if (!vgmstream) + free_layout_layered(data); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sab_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/sab_streamfile.h new file mode 100644 index 000000000..1b76c8019 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sab_streamfile.h @@ -0,0 +1,20 @@ +#ifndef _SAB_STREAMFILE_H_ +#define _SAB_STREAMFILE_H_ +#include "deblock_streamfile.h" + +static STREAMFILE* setup_sab_streamfile(STREAMFILE* sf, off_t stream_start, int stream_count, int stream_number, size_t interleave) { + STREAMFILE* new_sf = NULL; + deblock_config_t cfg = {0}; + + cfg.stream_start = stream_start; + cfg.chunk_size = interleave; + cfg.step_start = stream_number; + cfg.step_count = stream_count; + + /* setup sf */ + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); + return new_sf; +} + +#endif /* _SAB_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index b107b22b3..91af9aec7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -3,36 +3,36 @@ /* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX) */ -VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamHeader = NULL; +VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf_head = NULL; off_t start_offset, data_offset, chunk_offset, name_offset = 0; size_t stream_size; int is_sgx, is_sgb = 0; int loop_flag, channels, codec; int sample_rate, num_samples, loop_start_sample, loop_end_sample; - int total_subsongs, target_subsong = streamFile->stream_index; + int total_subsongs, target_subsong = sf->stream_index; /* check extension, case insensitive */ /* .sgx: header+data (Genji), .sgd: header+data, .sgh/sgd: header/data */ - if (!check_extensions(streamFile,"sgx,sgd,sgb")) + if (!check_extensions(sf,"sgx,sgd,sgb")) goto fail; - is_sgx = check_extensions(streamFile,"sgx"); - is_sgb = check_extensions(streamFile,"sgb"); + is_sgx = check_extensions(sf,"sgx"); + is_sgb = check_extensions(sf,"sgb"); /* SGB+SGH: use SGH as header; otherwise use the current file as header */ if (is_sgb) { - streamHeader = open_streamfile_by_ext(streamFile, "sgh"); - if (!streamHeader) goto fail; + sf_head = open_streamfile_by_ext(sf, "sgh"); + if (!sf_head) goto fail; } else { - streamHeader = streamFile; + sf_head = sf; } /* SGXD base (size 0x10) */ - if (read_32bitBE(0x00,streamHeader) != 0x53475844) /* "SGXD" */ + if (read_32bitBE(0x00,sf_head) != 0x53475844) /* "SGXD" */ goto fail; /* 0x04 SGX: full header_size; SGD/SGH: unknown header_size (counting from 0x0/0x8/0x10, varies) */ /* 0x08 SGX: first chunk offset? (0x10); SGD/SGH: full header_size */ @@ -40,24 +40,24 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { if (is_sgb) { data_offset = 0x00; } else if ( is_sgx ) { - data_offset = read_32bitLE(0x04,streamHeader); + data_offset = read_32bitLE(0x04,sf_head); } else { - data_offset = read_32bitLE(0x08,streamHeader); + data_offset = read_32bitLE(0x08,sf_head); } /* typical chunks: WAVE, RGND, NAME (strings for WAVE or RGND), SEQD (related to SFX), WSUR, WMKR, BUSS */ /* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */ if (is_sgx) { /* position after chunk+size */ - if (read_32bitBE(0x10,streamHeader) != 0x57415645) goto fail; /* "WAVE" */ + if (read_32bitBE(0x10,sf_head) != 0x57415645) goto fail; /* "WAVE" */ chunk_offset = 0x18; } else { - if (!find_chunk_le(streamHeader, 0x57415645,0x10,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */ + if (!find_chunk_le(sf_head, 0x57415645,0x10,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */ } /* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */ /* check multi-streams (usually only SE containers; Puppeteer) */ - total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); + total_subsongs = read_32bitLE(chunk_offset+0x04,sf_head); if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; @@ -68,11 +68,11 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { /* 0x00 ? (00/01/02) */ if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */ - name_offset = read_32bitLE(chunk_offset+0x04,streamHeader); - codec = read_8bit(chunk_offset+0x08,streamHeader); - channels = read_8bit(chunk_offset+0x09,streamHeader); + name_offset = read_32bitLE(chunk_offset+0x04,sf_head); + codec = read_8bit(chunk_offset+0x08,sf_head); + channels = read_8bit(chunk_offset+0x09,sf_head); /* 0x0a null */ - sample_rate = read_32bitLE(chunk_offset+0x0c,streamHeader); + sample_rate = read_32bitLE(chunk_offset+0x0c,sf_head); /* 0x10 info_type: meaning of the next value * (00=null, 30/40=data size without padding (ADPCM, ATRAC3plus), 80/A0=block size (AC3) */ @@ -80,15 +80,15 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { /* 0x18 unknown (ex. 0x0008/0010/3307/CC02/etc)x2 */ /* 0x1c null */ - num_samples = read_32bitLE(chunk_offset+0x20,streamHeader); - loop_start_sample = read_32bitLE(chunk_offset+0x24,streamHeader); - loop_end_sample = read_32bitLE(chunk_offset+0x28,streamHeader); - stream_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ + num_samples = read_32bitLE(chunk_offset+0x20,sf_head); + loop_start_sample = read_32bitLE(chunk_offset+0x24,sf_head); + loop_end_sample = read_32bitLE(chunk_offset+0x28,sf_head); + stream_size = read_32bitLE(chunk_offset+0x2c,sf_head); /* stream size (without padding) / interleave (for type3) */ if (is_sgx) { stream_offset = 0x0; } else{ - stream_offset = read_32bitLE(chunk_offset+0x30,streamHeader); + stream_offset = read_32bitLE(chunk_offset+0x30,sf_head); } /* 0x34 SGX: unknown; SGD/SGH: stream size (with padding) / interleave */ @@ -109,7 +109,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SGXD; if (name_offset) - read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf_head); switch (codec) { @@ -121,7 +121,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { #ifdef VGM_USE_VORBIS case 0x02: /* Ogg Vorbis [Ni no Kuni: Wrath of the White Witch Remastered (PC)] (codec hijack?) */ - vgmstream->codec_data = init_ogg_vorbis(streamFile, start_offset, stream_size, NULL); + vgmstream->codec_data = init_ogg_vorbis(sf, start_offset, stream_size, NULL); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_OGG_VORBIS; vgmstream->layout_type = layout_none; @@ -143,7 +143,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG case 0x04: { /* ATRAC3plus [Kurohyo 1/2 (PSP), BraveStory (PSP)] */ - vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, start_offset, NULL); + vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, NULL); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -163,20 +163,15 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG case 0x06: { /* AC3 [Tokyo Jungle (PS3), Afrika (PS3)] */ - ffmpeg_codec_data *ffmpeg_data; - - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset, stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - /* manually set skip_samples if FFmpeg didn't do it */ - if (ffmpeg_data->skipSamples <= 0) { - /* PS3 AC3 consistently has 256 encoder delay samples, and there are ~1000-2000 samples after num_samples. - * Skipping them marginally improves full loops in some Tokyo Jungle tracks (ex. a_1.sgd). */ - ffmpeg_set_skip_samples(ffmpeg_data, 256); - } + /* PS3 AC3 consistently has 256 encoder delay samples, and there are ~1000-2000 samples after num_samples. + * Skipping them marginally improves full loops in some Tokyo Jungle tracks (ex. a_1.sgd). */ + ffmpeg_set_skip_samples(vgmstream->codec_data, 256); + /* SGXD loop/sample values are relative (without skip samples), no need to adjust */ break; @@ -188,15 +183,14 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { goto fail; } - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; - if (is_sgb && streamHeader) close_streamfile(streamHeader); + if (is_sgb && sf_head) close_streamfile(sf_head); return vgmstream; fail: - if (is_sgb && streamHeader) close_streamfile(streamHeader); + if (is_sgb && sf_head) close_streamfile(sf_head); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/silence.c b/Frameworks/vgmstream/vgmstream/src/meta/silence.c new file mode 100644 index 000000000..28f7d0198 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/silence.c @@ -0,0 +1,29 @@ +#include "meta.h" + +/* silent stream - mainly for engines that need them or dummy subsongs */ +VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples) { + VGMSTREAM* vgmstream = NULL; + + if (channels <= 0) + channels = 2; + if (sample_rate <= 0) + sample_rate = 48000; + if (num_samples <= 0) + num_samples = 1.0 * sample_rate; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, 0); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SILENCE; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + + vgmstream->coding_type = coding_SILENCE; + vgmstream->layout_type = layout_none; + + return vgmstream; +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index 286a5f9d4..35bb8bc0c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -241,22 +241,20 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { #ifdef VGM_USE_MPEG case 0x07: { /* MPEG [Final Fantasy XIII (PS3)] */ - mpeg_codec_data *mpeg_data = NULL; mpeg_custom_config cfg = {0}; cfg.interleave = 0x800; /* for multistream [Final Fantasy XIII-2 (PS3)], otherwise ignored */ cfg.data_size = stream_size; - mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; + vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg); + if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; /* some Drakengard 3, Kingdom Hearts HD have adjusted sample rate (47999, 44099), for looping? */ - vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data); - vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); - vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); + vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, vgmstream->codec_data); + vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, vgmstream->codec_data); + vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, vgmstream->codec_data); /* somehow loops offsets aren't always frame-aligned, and the code below supposedly helped, * but there isn't much difference since MPEG loops are rough (1152-aligned). Seems it diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c index 09b32b9ae..43de6e8f8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c @@ -230,7 +230,6 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) { #ifdef VGM_USE_MPEG case 0x06: { /* MSMP3 (MSF subfile) [Dragon Quest Builders (PS3)] */ - mpeg_codec_data *mpeg_data = NULL; mpeg_custom_config cfg = {0}; start_offset = sead.extradata_offset + sead.extradata_size; @@ -238,12 +237,11 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) { /* extradata: */ /* proper MSF header, but sample rate/loops are ignored in favor of SAB's */ - mpeg_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; + vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; - vgmstream->num_samples = mpeg_bytes_to_samples(sead.stream_size, mpeg_data); + vgmstream->num_samples = mpeg_bytes_to_samples(sead.stream_size, vgmstream->codec_data); vgmstream->loop_start_sample = sead.loop_start; vgmstream->loop_end_sample = sead.loop_end; break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/strm_abylight.c b/Frameworks/vgmstream/vgmstream/src/meta/strm_abylight.c index c5f884da6..4b619d651 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/strm_abylight.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/strm_abylight.c @@ -1,71 +1,65 @@ -#include "meta.h" -#include "../coding/coding.h" - - -/* .STRM - from Abylight 3DS games [Cursed Castilla (3DS)] */ -VGMSTREAM * init_vgmstream_strm_abylight(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count, sample_rate; - size_t data_size; - - - /* check extension */ - if ( !check_extensions(streamFile,"strm") ) - goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */ - goto fail; - if (read_32bitLE(0x04,streamFile) != 0x03E8) /* version 1000? */ - goto fail; - - loop_flag = 0; - channel_count = 2; /* there are various possible fields but all files are stereo */ - sample_rate = read_32bitLE(0x08,streamFile); - - start_offset = 0x1e; - data_size = read_32bitLE(0x10,streamFile); - if (data_size != get_streamfile_size(streamFile) - start_offset) - goto fail; - if (data_size != read_32bitLE(0x18,streamFile)) - goto fail; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = aac_get_samples(streamFile, start_offset, data_size); - - vgmstream->meta_type = meta_STRM_ABYLIGHT; - -#ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; - - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* apparently none, or maybe ~600 */ - //if (!ffmpeg_data->skipSamples) - // ffmpeg_set_skip_samples(ffmpeg_data, 1024); - //vgmstream->num_samples -= 1024; - } -#else - goto fail; -#endif - - /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* .STRM - from Abylight 3DS games [Cursed Castilla (3DS)] */ +VGMSTREAM* init_vgmstream_strm_abylight(STREAMFILE* sf) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate; + size_t data_size; + + + /* checks */ + if ( !check_extensions(sf,"strm") ) + goto fail; + + if (read_32bitBE(0x00,sf) != 0x5354524D) /* "STRM" */ + goto fail; + if (read_32bitLE(0x04,sf) != 0x03E8) /* version 1000? */ + goto fail; + + loop_flag = 0; + channel_count = 2; /* there are various possible fields but all files are stereo */ + sample_rate = read_32bitLE(0x08,sf); + + start_offset = 0x1e; + data_size = read_32bitLE(0x10,sf); + if (data_size != get_streamfile_size(sf) - start_offset) + goto fail; + if (data_size != read_32bitLE(0x18,sf)) + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = aac_get_samples(sf, start_offset, data_size); + + vgmstream->meta_type = meta_STRM_ABYLIGHT; + +#ifdef VGM_USE_FFMPEG + { + vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset, data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* apparently none, or maybe ~600 */ + //ffmpeg_set_skip_samples(ffmpeg_data, 1024); + //vgmstream->num_samples -= 1024; + } +#else + goto fail; +#endif + + if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c b/Frameworks/vgmstream/vgmstream/src/meta/svag_kcet.c similarity index 51% rename from Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c rename to Frameworks/vgmstream/vgmstream/src/meta/svag_kcet.c index 4b9bc17d2..18e09b93c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/svag_kcet.c @@ -2,51 +2,54 @@ #include "../coding/coding.h" /* SVAG - from Konami Tokyo games [OZ (PS2), Neo Contra (PS2), Silent Hill 2 (PS2)] */ -VGMSTREAM * init_vgmstream_ps2_svag(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_svag_kcet(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; size_t data_size; int loop_flag, channel_count; /* checks */ - if (!check_extensions(streamFile, "svag")) + if (!check_extensions(sf, "svag")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x53766167) /* "Svag" */ + if (read_32bitBE(0x00,sf) != 0x53766167) /* "Svag" */ goto fail; - channel_count = read_16bitLE(0x0C,streamFile); /* always 2? ("S"tereo vag?) */ - loop_flag = (read_32bitLE(0x14,streamFile)==1); + channel_count = read_16bitLE(0x0C,sf); /* always 2? ("S"tereo vag?) */ + loop_flag = (read_32bitLE(0x14,sf) ==1); - /* header repeated at 0x400 presumably for stereo */ - if (channel_count > 1 && read_32bitBE(0x400,streamFile) != 0x53766167) /* "Svag" */ + /* test padding (a set "KCE-Tokyo ..." phrase) after 0x1c to catch bad rips, + * at 0x400 may be header again (Silent Hill 2) or more padding (Silent Scope 2) */ + if (channel_count > 1 && + read_32bitBE(0x400,sf) != 0x53766167 && /* "Svag" */ + read_32bitBE(0x400,sf) != 0x44657369) /* "Desi" */ goto fail; start_offset = 0x800; - data_size = read_32bitLE(0x04,streamFile); + data_size = read_32bitLE(0x04,sf); /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitLE(0x08,streamFile); + vgmstream->sample_rate = read_32bitLE(0x08,sf); - vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile), vgmstream->channels); + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,sf), vgmstream->channels); if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x18,streamFile)*vgmstream->channels, vgmstream->channels); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x18,sf)*vgmstream->channels, vgmstream->channels); vgmstream->loop_end_sample = vgmstream->num_samples; } - vgmstream->meta_type = meta_PS2_SVAG; + vgmstream->meta_type = meta_SVAG_KCET; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile); + vgmstream->interleave_block_size = read_32bitLE(0x10,sf); if (vgmstream->interleave_block_size) vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/svag_snk.c b/Frameworks/vgmstream/vgmstream/src/meta/svag_snk.c new file mode 100644 index 000000000..79ca24ecd --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/svag_snk.c @@ -0,0 +1,46 @@ +#include "meta.h" +#include "../util.h" + +/* .SVAG - from SNK games [World Heroes Anthology (PS2), Fatal Fury Battle Archives 2 (PS2)] */ +VGMSTREAM* init_vgmstream_svag_snk(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, loop_start_block, loop_end_block; + + /* checks */ + if (!check_extensions(sf, "svag")) + goto fail; + if (read_32bitBE(0x00,sf) != 0x5641476D) /* "VAGm" */ + goto fail; + + channel_count = read_32bitLE(0x0c,sf); + loop_start_block = read_32bitLE(0x18,sf); + loop_end_block = read_32bitLE(0x1c,sf); + loop_flag = loop_end_block > 0; /* loop_start_block can be 0 */ + start_offset = 0x20; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SVAG_SNK; + + vgmstream->sample_rate = read_32bitLE(0x08,sf); + vgmstream->num_samples = read_32bitLE(0x10,sf) * 28; /* size in blocks */ + vgmstream->loop_start_sample = loop_start_block * 28; + vgmstream->loop_end_sample = loop_end_block * 28; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 1b792352f..1319d7941 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -414,8 +414,8 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { /* get coefs */ { - int16_t (*read_16bit)(off_t , STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE; - int16_t (*get_16bit)(uint8_t * p) = txth.coef_big_endian ? get_16bitBE : get_16bitLE; + int16_t (*read_16bit)(off_t, STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE; + int16_t (*get_16bit)(const uint8_t* p) = txth.coef_big_endian ? get_16bitBE : get_16bitLE; for (i = 0; i < vgmstream->channels; i++) { if (txth.coef_mode == 0) { /* normal coefs */ @@ -579,7 +579,7 @@ fail: static VGMSTREAM* init_subfile(txth_header* txth) { VGMSTREAM* vgmstream = NULL; char extension[PATH_LIMIT]; - STREAMFILE* streamSubfile = NULL; + STREAMFILE* sf_sub = NULL; if (txth->subfile_size == 0) { @@ -604,10 +604,12 @@ static VGMSTREAM* init_subfile(txth_header* txth) { strcpy(extension, "subfile_txth."); strcat(extension, txth->subfile_extension); - streamSubfile = setup_subfile_streamfile(txth->sf_body, txth->subfile_offset, txth->subfile_size, extension); - if (!streamSubfile) goto fail; + sf_sub = setup_subfile_streamfile(txth->sf_body, txth->subfile_offset, txth->subfile_size, extension); + if (!sf_sub) goto fail; - vgmstream = init_vgmstream_from_STREAMFILE(streamSubfile); + sf_sub->stream_index = txth->sf->stream_index; /* in case of subfiles with subsongs */ + + vgmstream = init_vgmstream_from_STREAMFILE(sf_sub); if (!vgmstream) goto fail; /* apply some fields */ @@ -643,11 +645,11 @@ static VGMSTREAM* init_subfile(txth_header* txth) { // txth->loop_flag = vgmstream->loop_flag; - close_streamfile(streamSubfile); + close_streamfile(sf_sub); return vgmstream; fail: - close_streamfile(streamSubfile); + close_streamfile(sf_sub); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c index 550c33edc..d02e6b952 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c @@ -2,12 +2,14 @@ #include "../coding/coding.h" #include "../layout/layout.h" #include "../mixing.h" +#include "../plugins.h" #define TXTP_LINE_MAX 1024 #define TXTP_MIXING_MAX 512 #define TXTP_GROUP_MODE_SEGMENTED 'S' #define TXTP_GROUP_MODE_LAYERED 'L' +#define TXTP_GROUP_MODE_RANDOM 'R' #define TXTP_GROUP_REPEAT 'R' #define TXTP_POSITION_LOOPS 'L' @@ -62,13 +64,17 @@ typedef struct { typedef struct { + /* main entry */ char filename[TXTP_LINE_MAX]; + int silent; + /* TXTP settings (applied at the end) */ int range_start; int range_end; int subsong; uint32_t channel_mask; + int mixing_count; txtp_mix_data mixing[TXTP_MIXING_MAX]; @@ -95,21 +101,23 @@ typedef struct { char type; int count; char repeat; + int selected; - txtp_entry group_config; + txtp_entry group_settings; } txtp_group; typedef struct { - txtp_entry *entry; + txtp_entry* entry; size_t entry_count; size_t entry_max; - txtp_group *group; + txtp_group* group; size_t group_count; size_t group_max; + int group_pos; /* entry counter for groups */ - VGMSTREAM* *vgmstream; + VGMSTREAM** vgmstream; size_t vgmstream_count; uint32_t loop_start_segment; @@ -125,128 +133,36 @@ typedef struct { int is_single; } txtp_header; -static txtp_header* parse_txtp(STREAMFILE* streamFile); +static txtp_header* parse_txtp(STREAMFILE* sf); +static int parse_entries(txtp_header* txtp, STREAMFILE* sf); +static int parse_groups(txtp_header* txtp); static void clean_txtp(txtp_header* txtp, int fail); -static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current); +static void apply_settings(VGMSTREAM* vgmstream, txtp_entry* current); void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command); -static int make_group_segment(txtp_header* txtp, int from, int count); -static int make_group_layer(txtp_header* txtp, int from, int count); - /* TXTP - an artificial playlist-like format to play files with segments/layers/config */ -VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; +VGMSTREAM* init_vgmstream_txtp(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; txtp_header* txtp = NULL; - int i; + int ok; /* checks */ - if (!check_extensions(streamFile, "txtp")) + if (!check_extensions(sf, "txtp")) goto fail; - /* read .txtp with all files and config */ - txtp = parse_txtp(streamFile); + /* read .txtp with all files and settings */ + txtp = parse_txtp(sf); if (!txtp) goto fail; - /* post-process */ - { - if (txtp->entry_count == 0) - goto fail; + /* process files in the .txtp */ + ok = parse_entries(txtp, sf); + if (!ok) goto fail; - txtp->vgmstream = calloc(txtp->entry_count, sizeof(VGMSTREAM*)); - if (!txtp->vgmstream) goto fail; - - txtp->vgmstream_count = txtp->entry_count; - } - - - /* detect single files before grouping */ - if (txtp->group_count == 0 && txtp->vgmstream_count == 1) { - txtp->is_single = 1; - txtp->is_segmented = 0; - txtp->is_layered = 0; - } - - - /* open all entry files first as they'll be modified by modes */ - for (i = 0; i < txtp->vgmstream_count; i++) { - STREAMFILE* temp_streamFile = open_streamfile_by_filename(streamFile, txtp->entry[i].filename); - if (!temp_streamFile) { - VGM_LOG("TXTP: cannot open streamfile for %s\n", txtp->entry[i].filename); - goto fail; - } - temp_streamFile->stream_index = txtp->entry[i].subsong; - - txtp->vgmstream[i] = init_vgmstream_from_STREAMFILE(temp_streamFile); - close_streamfile(temp_streamFile); - if (!txtp->vgmstream[i]) { - VGM_LOG("TXTP: cannot open vgmstream for %s#%i\n", txtp->entry[i].filename, txtp->entry[i].subsong); - goto fail; - } - - apply_config(txtp->vgmstream[i], &txtp->entry[i]); - } - - - /* group files as needed */ - for (i = 0; i < txtp->group_count; i++) { - txtp_group *grp = &txtp->group[i]; - int pos, groups; - - //;VGM_LOG("TXTP: apply group %i%c%i%c\n",txtp->group[i].position,txtp->group[i].type,txtp->group[i].count,txtp->group[i].repeat); - - /* special meaning of "all files" */ - if (grp->position < 0 || grp->position >= txtp->vgmstream_count) - grp->position = 0; - if (grp->count <= 0) - grp->count = txtp->vgmstream_count - grp->position; - - /* repeats N groups (trailing files are not grouped) */ - if (grp->repeat == TXTP_GROUP_REPEAT) { - groups = ((txtp->vgmstream_count - grp->position) / grp->count); - } - else { - groups = 1; - } - - /* as groups are compacted position goes 1 by 1 */ - for (pos = grp->position; pos < grp->position + groups; pos++) { - //;VGM_LOG("TXTP: group=%i, count=%i, groups=%i\n", pos, grp->count, groups); - switch(grp->type) { - case TXTP_GROUP_MODE_LAYERED: - if (!make_group_layer(txtp, pos, grp->count)) - goto fail; - break; - case TXTP_GROUP_MODE_SEGMENTED: - if (!make_group_segment(txtp, pos, grp->count)) - goto fail; - break; - default: - goto fail; - } - } - - /* group may also have config (like downmixing) */ - apply_config(txtp->vgmstream[grp->position], &grp->group_config); - } - - /* final tweaks (should be integrated with the above?) */ - if (txtp->is_layered) { - if (!make_group_layer(txtp, 0, txtp->vgmstream_count)) - goto fail; - } - if (txtp->is_segmented) { - if (!make_group_segment(txtp, 0, txtp->vgmstream_count)) - goto fail; - } - if (txtp->is_single) { - /* special case of setting start_segment to force/overwrite looping - * (better to use #E but left for compatibility with older TXTPs) */ - if (txtp->loop_start_segment == 1 && !txtp->loop_end_segment) { - vgmstream_force_loop(txtp->vgmstream[0], 1, txtp->vgmstream[0]->loop_start_sample, txtp->vgmstream[0]->num_samples); - } - } + /* group files into layouts */ + ok = parse_groups(txtp); + if (!ok) goto fail; /* may happen if using mixed mode but some files weren't grouped */ @@ -255,12 +171,7 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { goto fail; } - /* apply default config to the resulting file */ - if (txtp->default_entry_set) { - apply_config(txtp->vgmstream[0], &txtp->default_entry); - } - - + /* should result in a final, single vgmstream possibly containing multiple vgmstreams */ vgmstream = txtp->vgmstream[0]; clean_txtp(txtp, 0); @@ -271,6 +182,125 @@ fail: return NULL; } +static void clean_txtp(txtp_header* txtp, int fail) { + int i, start; + + if (!txtp) + return; + + /* returns first vgmstream on success so it's not closed */ + start = fail ? 0 : 1; + + for (i = start; i < txtp->vgmstream_count; i++) { + close_vgmstream(txtp->vgmstream[i]); + } + + free(txtp->vgmstream); + free(txtp->group); + free(txtp->entry); + free(txtp); +} + +//todo fragment parser later + +/*******************************************************************************/ +/* ENTRIES */ +/*******************************************************************************/ + +static int parse_silents(txtp_header* txtp) { + int i; + int channels = 0; + int sample_rate = 0; + int32_t num_samples = 0; + + /* silents use same channels as close files */ + for (i = 0; i < txtp->vgmstream_count; i++) { + if (!txtp->entry[i].silent) { + channels = txtp->vgmstream[i]->channels; + sample_rate = txtp->vgmstream[i]->sample_rate; + break; + } + } + + /* actually open silents */ + for (i = 0; i < txtp->vgmstream_count; i++) { + if (!txtp->entry[i].silent) + continue; + + txtp->vgmstream[i] = init_vgmstream_silence(channels, sample_rate, num_samples); + if (!txtp->vgmstream[i]) goto fail; + + apply_settings(txtp->vgmstream[i], &txtp->entry[i]); + } + + return 1; +fail: + return 0; +} + +static int is_silent(txtp_entry* entry) { + /* should also contain "." in the filename for commands with seconds ("1.0") to work */ + return entry->filename[0] == '?'; +} + +/* open all entries and apply settings to resulting VGMSTREAMs */ +static int parse_entries(txtp_header* txtp, STREAMFILE* sf) { + int i; + int has_silents = 0; + + + if (txtp->entry_count == 0) + goto fail; + + txtp->vgmstream = calloc(txtp->entry_count, sizeof(VGMSTREAM*)); + if (!txtp->vgmstream) goto fail; + + txtp->vgmstream_count = txtp->entry_count; + + + /* open all entry files first as they'll be modified by modes */ + for (i = 0; i < txtp->vgmstream_count; i++) { + STREAMFILE* temp_sf = NULL; + + /* silent entry ignore */ + if (is_silent(&txtp->entry[i])) { + txtp->entry[i].silent = 1; + has_silents = 1; + continue; + } + + temp_sf = open_streamfile_by_filename(sf, txtp->entry[i].filename); + if (!temp_sf) { + VGM_LOG("TXTP: cannot open streamfile for %s\n", txtp->entry[i].filename); + goto fail; + } + temp_sf->stream_index = txtp->entry[i].subsong; + + txtp->vgmstream[i] = init_vgmstream_from_STREAMFILE(temp_sf); + close_streamfile(temp_sf); + if (!txtp->vgmstream[i]) { + VGM_LOG("TXTP: cannot open vgmstream for %s#%i\n", txtp->entry[i].filename, txtp->entry[i].subsong); + goto fail; + } + + apply_settings(txtp->vgmstream[i], &txtp->entry[i]); + } + + if (has_silents) { + if (!parse_silents(txtp)) + goto fail; + } + + return 1; +fail: + return 0; +} + + +/*******************************************************************************/ +/* GROUPS */ +/*******************************************************************************/ + static void update_vgmstream_list(VGMSTREAM* vgmstream, txtp_header* txtp, int position, int count) { int i; @@ -288,14 +318,15 @@ static void update_vgmstream_list(VGMSTREAM* vgmstream, txtp_header* txtp, int p //;VGM_LOG("TXTP: compact vgmstreams=%i\n", txtp->vgmstream_count); } -static int make_group_segment(txtp_header* txtp, int position, int count) { - VGMSTREAM * vgmstream = NULL; +static int make_group_segment(txtp_header* txtp, int is_group, int position, int count) { + VGMSTREAM* vgmstream = NULL; segmented_layout_data *data_s = NULL; int i, loop_flag = 0; - if (count == 1) { /* nothing to do */ - //;VGM_LOG("TXTP: ignored segments of 1\n"); + /* allowed for actual groups (not final "mode"), otherwise skip to optimize */ + if (!is_group && count == 1) { + //;VGM_LOG("TXTP: ignored single group\n"); return 1; } @@ -336,7 +367,7 @@ static int make_group_segment(txtp_header* txtp, int position, int count) { if (!vgmstream) goto fail; /* custom meta name if all parts don't match */ - for (i = 0; i < data_s->segment_count; i++) { + for (i = 0; i < count; i++) { if (vgmstream->meta_type != data_s->segments[i]->meta_type) { vgmstream->meta_type = meta_TXTP; break; @@ -346,7 +377,7 @@ static int make_group_segment(txtp_header* txtp, int position, int count) { /* fix loop keep */ if (loop_flag && txtp->is_loop_keep) { int32_t current_samples = 0; - for (i = 0; i < data_s->segment_count; i++) { + for (i = 0; i < count; i++) { if (txtp->loop_start_segment == i+1 /*&& data_s->segments[i]->loop_start_sample*/) { vgmstream->loop_start_sample = current_samples + data_s->segments[i]->loop_start_sample; } @@ -371,14 +402,15 @@ fail: return 0; } -static int make_group_layer(txtp_header* txtp, int position, int count) { - VGMSTREAM * vgmstream = NULL; - layered_layout_data * data_l = NULL; +static int make_group_layer(txtp_header* txtp, int is_group, int position, int count) { + VGMSTREAM* vgmstream = NULL; + layered_layout_data* data_l = NULL; int i; - if (count == 1) { /* nothing to do */ - //;VGM_LOG("TXTP: ignored layer of 1\n"); + /* allowed for actual groups (not final mode), otherwise skip to optimize */ + if (!is_group && count == 1) { + //;VGM_LOG("TXTP: ignored single group\n"); return 1; } @@ -414,6 +446,12 @@ static int make_group_layer(txtp_header* txtp, int position, int count) { } } + /* loop settings only make sense if this group becomes final vgmstream */ + if (position == 0 && txtp->vgmstream_count == count) { + if (txtp->is_loop_auto && !vgmstream->loop_flag) { + vgmstream_force_loop(vgmstream, 1, 0, vgmstream->num_samples); + } + } /* set new vgmstream and reorder positions */ update_vgmstream_list(vgmstream, txtp, position, count); @@ -426,37 +464,198 @@ fail: return 0; } +static int make_group_random(txtp_header* txtp, int is_group, int position, int count, int selected) { + VGMSTREAM* vgmstream = NULL; + int i; -static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) { - - if (current->config.play_forever) { - vgmstream->config.play_forever = current->config.play_forever; - } - if (current->config.loop_count_set) { - vgmstream->config.loop_count_set = 1; - vgmstream->config.loop_count = current->config.loop_count; - } - if (current->config.fade_time_set) { - vgmstream->config.fade_time_set = 1; - vgmstream->config.fade_time = current->config.fade_time; - } - if (current->config.fade_delay_set) { - vgmstream->config.fade_delay_set = 1; - vgmstream->config.fade_delay = current->config.fade_delay; - } - if (current->config.ignore_fade) { - vgmstream->config.ignore_fade = current->config.ignore_fade; - } - if (current->config.force_loop) { - vgmstream->config.force_loop = current->config.force_loop; - } - if (current->config.really_force_loop) { - vgmstream->config.really_force_loop = current->config.really_force_loop; - } - if (current->config.ignore_loop) { - vgmstream->config.ignore_loop = current->config.ignore_loop; + /* allowed for actual groups (not final mode), otherwise skip to optimize */ + if (!is_group && count == 1) { + //;VGM_LOG("TXTP: ignored single group\n"); + return 1; } + if (position + count > txtp->vgmstream_count || position < 0 || count < 0) { + VGM_LOG("TXTP: ignored random position=%i, count=%i, entries=%i\n", position, count, txtp->vgmstream_count); + return 1; + } + + + /* 0=actually random for fun and testing, but undocumented since random music is kinda weird, may change anytime + * (plus foobar caches song duration so it can get strange if randoms are too different) */ + if (selected < 0) { + static int random_seed = 0; + srand((unsigned)txtp + random_seed++); /* whatevs */ + selected = (rand() % count); /* 0..count-1 */ + //;VGM_LOG("TXTP: autoselected random %i\n", selected); + } + + if (selected < 0 || selected >= count) { + goto fail; + } + + /* get selected and remove non-selected */ + vgmstream = txtp->vgmstream[position + selected]; + txtp->vgmstream[position + selected] = NULL; + for (i = 0; i < count; i++) { + close_vgmstream(txtp->vgmstream[i + position]); + } + + /* set new vgmstream and reorder positions */ + update_vgmstream_list(vgmstream, txtp, position, count); + + return 1; +fail: + close_vgmstream(vgmstream); + return 0; +} + +static int parse_groups(txtp_header* txtp) { + int i; + + /* detect single files before grouping */ + if (txtp->group_count == 0 && txtp->vgmstream_count == 1) { + txtp->is_single = 1; + txtp->is_segmented = 0; + txtp->is_layered = 0; + } + + /* group files as needed */ + for (i = 0; i < txtp->group_count; i++) { + txtp_group *grp = &txtp->group[i]; + int pos, groups; + + //;VGM_LOG("TXTP: apply group %i%c%i%c\n",txtp->group[i].position,txtp->group[i].type,txtp->group[i].count,txtp->group[i].repeat); + + /* special meaning of "all files" */ + if (grp->position < 0 || grp->position >= txtp->vgmstream_count) + grp->position = 0; + if (grp->count <= 0) + grp->count = txtp->vgmstream_count - grp->position; + + /* repeats N groups (trailing files are not grouped) */ + if (grp->repeat == TXTP_GROUP_REPEAT) { + groups = ((txtp->vgmstream_count - grp->position) / grp->count); + } + else { + groups = 1; + } + + /* as groups are compacted position goes 1 by 1 */ + for (pos = grp->position; pos < grp->position + groups; pos++) { + //;VGM_LOG("TXTP: group=%i, count=%i, groups=%i\n", pos, grp->count, groups); + switch(grp->type) { + case TXTP_GROUP_MODE_LAYERED: + if (!make_group_layer(txtp, 1, pos, grp->count)) + goto fail; + break; + case TXTP_GROUP_MODE_SEGMENTED: + if (!make_group_segment(txtp, 1, pos, grp->count)) + goto fail; + break; + case TXTP_GROUP_MODE_RANDOM: + if (!make_group_random(txtp, 1, pos, grp->count, grp->selected)) + goto fail; + break; + default: + goto fail; + } + } + + /* group may also have settings (like downmixing) */ + apply_settings(txtp->vgmstream[grp->position], &grp->group_settings); + } + + /* final tweaks (should be integrated with the above?) */ + if (txtp->is_layered) { + if (!make_group_layer(txtp, 0, 0, txtp->vgmstream_count)) + goto fail; + } + if (txtp->is_segmented) { + if (!make_group_segment(txtp, 0, 0, txtp->vgmstream_count)) + goto fail; + } + if (txtp->is_single) { + /* special case of setting start_segment to force/overwrite looping + * (better to use #E but left for compatibility with older TXTPs) */ + if (txtp->loop_start_segment == 1 && !txtp->loop_end_segment) { + vgmstream_force_loop(txtp->vgmstream[0], 1, txtp->vgmstream[0]->loop_start_sample, txtp->vgmstream[0]->num_samples); + } + } + + /* apply default settings to the resulting file */ + if (txtp->default_entry_set) { + apply_settings(txtp->vgmstream[0], &txtp->default_entry); + } + + return 1; +fail: + return 0; +} + + +/*******************************************************************************/ +/* CONFIG */ +/*******************************************************************************/ + +static void copy_flag(int* dst_flag, int* src_flag) { + if (!*src_flag) + return; + *dst_flag = 1; +} + +static void copy_secs(int* dst_flag, double* dst_secs, int* src_flag, double* src_secs) { + if (!*src_flag) + return; + *dst_flag = 1; + *dst_secs = *src_secs; +} + +static void copy_time(int* dst_flag, int32_t* dst_time, double* dst_time_s, int* src_flag, int32_t* src_time, double* src_time_s) { + if (!*src_flag) + return; + *dst_flag = 1; + *dst_time = *src_time; + *dst_time_s = *src_time_s; +} + +static void copy_config(play_config_t* dst, play_config_t* src) { + if (!src->config_set) + return; + + dst->config_set = 1; + copy_flag(&dst->play_forever, &src->play_forever); + copy_flag(&dst->ignore_fade, &src->ignore_fade); + copy_flag(&dst->force_loop, &src->force_loop); + copy_flag(&dst->really_force_loop, &src->really_force_loop); + copy_flag(&dst->ignore_loop, &src->ignore_loop); + copy_secs(&dst->loop_count_set, &dst->loop_count, &src->loop_count_set, &src->loop_count); + copy_secs(&dst->fade_time_set, &dst->fade_time, &src->fade_time_set, &src->fade_time); + copy_secs(&dst->fade_delay_set, &dst->fade_delay, &src->fade_delay_set, &src->fade_delay); + copy_time(&dst->pad_begin_set, &dst->pad_begin, &dst->pad_begin_s, &src->pad_begin_set, &src->pad_begin, &src->pad_begin_s); + copy_time(&dst->pad_end_set, &dst->pad_end, &dst->pad_end_s, &src->pad_end_set, &src->pad_end, &src->pad_end_s); + copy_time(&dst->trim_begin_set, &dst->trim_begin, &dst->trim_begin_s, &src->trim_begin_set, &src->trim_begin, &src->trim_begin_s); + copy_time(&dst->trim_end_set, &dst->trim_end, &dst->trim_end_s, &src->trim_end_set, &src->trim_end, &src->trim_end_s); + copy_time(&dst->body_time_set, &dst->body_time, &dst->body_time_s, &src->body_time_set, &src->body_time, &src->body_time_s); +} + +#if 0 +static void init_config(VGMSTREAM* vgmstream) { + play_config_t* cfg = &vgmstream->config; + + //todo only on segmented/layered? + if (cfg->play_forever + cfg->loop_count_set || cfg->fade_time_set || cfg->fade_delay_set || + cfg->pad_begin_set || cfg->pad_end_set || cfg->trim_begin_set || cfg->trim_end_set || + cfg->body_time_set) { + VGM_LOG("setup!\n"); + + } +} +#endif + +static void apply_settings(VGMSTREAM* vgmstream, txtp_entry* current) { + + /* base settings */ if (current->sample_rate > 0) { vgmstream->sample_rate = current->sample_rate; } @@ -479,14 +678,15 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) { if (current->trim_set) { if (current->trim_second != 0.0) { - current->trim_sample = current->trim_second * vgmstream->sample_rate; + /* trim sample can become 0 here when second is too small (rounded) */ + current->trim_sample = (double)current->trim_second * (double)vgmstream->sample_rate; } if (current->trim_sample < 0) { vgmstream->num_samples += current->trim_sample; /* trim from end (add negative) */ } - else if (vgmstream->num_samples > current->trim_sample) { - vgmstream->num_samples = current->trim_sample; /* trim to value */ + else if (current->trim_sample > 0 && vgmstream->num_samples > current->trim_sample) { + vgmstream->num_samples = current->trim_sample; /* trim to value >0 */ } /* readjust after triming if it went over (could check for more edge cases but eh) */ @@ -567,30 +767,19 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) { } } } + + + /* default play config (last after sample rate mods/mixing/etc) */ + copy_config(&vgmstream->config, ¤t->config); + setup_state_vgmstream(vgmstream); + /* config is enabled in layouts or externally (for compatibility, since we don't know yet if this + * VGMSTREAM will part of a layout, or is enabled externally to not mess up plugins's calcs) */ } -/* ********************************** */ - -static void clean_filename(char * filename) { - int i; - size_t len; - - if (filename[0] == '\0') - return; - - /* normalize paths */ - fix_dir_separators(filename); - - /* remove trailing spaces */ - len = strlen(filename); - for (i = len-1; i > 0; i--) { - if (filename[i] != ' ') - break; - filename[i] = '\0'; - } - -} +/*******************************************************************************/ +/* PARSER - HELPERS */ +/*******************************************************************************/ /* sscanf 101: "matches = sscanf(string-from, string-commands, parameters...)" * - reads linearly and matches "%" commands to input parameters as found @@ -606,13 +795,13 @@ static void clean_filename(char * filename) { * - %n: special match (not counted in return value), chars consumed until that point (can appear and be set multiple times) */ -static int get_double(const char * config, double *value, int *is_set) { +static int get_double(const char* params, double *value, int *is_set) { int n, m; double temp; if (is_set) *is_set = 0; - m = sscanf(config, " %lf%n", &temp,&n); + m = sscanf(params, " %lf%n", &temp,&n); if (m != 1 || temp < 0) return 0; @@ -621,11 +810,11 @@ static int get_double(const char * config, double *value, int *is_set) { return n; } -static int get_int(const char * config, int *value) { +static int get_int(const char* params, int *value) { int n,m; int temp; - m = sscanf(config, " %d%n", &temp,&n); + m = sscanf(params, " %d%n", &temp,&n); if (m != 1 || temp < 0) return 0; @@ -633,13 +822,13 @@ static int get_int(const char * config, int *value) { return n; } -static int get_position(const char * config, double *value_f, char *value_type) { +static int get_position(const char* params, double* value_f, char* value_type) { int n,m; double temp_f; char temp_c; /* test if format is position: N.n(type) */ - m = sscanf(config, " %lf%c%n", &temp_f,&temp_c,&n); + m = sscanf(params, " %lf%c%n", &temp_f,&temp_c,&n); if (m != 2 || temp_f < 0.0) return 0; /* test accepted chars as it will capture anything */ @@ -652,16 +841,16 @@ static int get_position(const char * config, double *value_f, char *value_type) } -static int get_time(const char * config, double *value_f, int32_t *value_i) { +static int get_time(const char* params, double* value_f, int32_t* value_i) { int n,m; int temp_i1, temp_i2; double temp_f1, temp_f2; char temp_c; /* test if format is hour: N:N(.n) or N_N(.n) */ - m = sscanf(config, " %d%c%d%n", &temp_i1,&temp_c,&temp_i2,&n); + m = sscanf(params, " %d%c%d%n", &temp_i1,&temp_c,&temp_i2,&n); if (m == 3 && (temp_c == ':' || temp_c == '_')) { - m = sscanf(config, " %lf%c%lf%n", &temp_f1,&temp_c,&temp_f2,&n); + m = sscanf(params, " %lf%c%lf%n", &temp_f1,&temp_c,&temp_f2,&n); if (m != 3 || /*temp_f1 < 0.0 ||*/ temp_f1 >= 60.0 || temp_f2 < 0.0 || temp_f2 >= 60.0) return 0; @@ -670,9 +859,9 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) { } /* test if format is seconds: N.n */ - m = sscanf(config, " %d.%d%n", &temp_i1,&temp_i2,&n); + m = sscanf(params, " %d.%d%n", &temp_i1,&temp_i2,&n); if (m == 2) { - m = sscanf(config, " %lf%n", &temp_f1,&n); + m = sscanf(params, " %lf%n", &temp_f1,&n); if (m != 1 /*|| temp_f1 < 0.0*/) return 0; *value_f = temp_f1; @@ -680,7 +869,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) { } /* test is format is hex samples: 0xN */ - m = sscanf(config, " 0x%x%n", &temp_i1,&n); + m = sscanf(params, " 0x%x%n", &temp_i1,&n); if (m == 1) { /* allow negative samples for special meanings */ //if (temp_i1 < 0) @@ -691,7 +880,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) { } /* assume format is samples: N */ - m = sscanf(config, " %d%n", &temp_i1,&n); + m = sscanf(params, " %d%n", &temp_i1,&n); if (m == 1) { /* allow negative samples for special meanings */ //if (temp_i1 < 0) @@ -704,12 +893,19 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) { return 0; } -static int get_bool(const char * config, int *value) { +static int get_time_f(const char* params, double* value_f, int32_t* value_i, int* flag) { + int n = get_time(params, value_f, value_i); + if (n > 0) + *flag = 1; + return n; +} + +static int get_bool(const char* params, int* value) { int n,m; char temp; n = 0; /* init as it's not matched if c isn't */ - m = sscanf(config, " %c%n", &temp, &n); + m = sscanf(params, " %c%n", &temp, &n); if (m >= 1 && !(temp == '#' || temp == '\r' || temp == '\n')) return 0; /* ignore if anything non-space/comment matched */ @@ -719,21 +915,21 @@ static int get_bool(const char * config, int *value) { return n; } -static int get_mask(const char * config, uint32_t *value) { +static int get_mask(const char* params, uint32_t* value) { int n, m, total_n = 0; int temp1,temp2, r1, r2; int i; char cmd; uint32_t mask = *value; - while (config[0] != '\0') { - m = sscanf(config, " %c%n", &cmd,&n); /* consume comma */ + while (params[0] != '\0') { + m = sscanf(params, " %c%n", &cmd,&n); /* consume comma */ if (m == 1 && (cmd == ',' || cmd == '-')) { /* '-' is alt separator (space is ok too, implicitly) */ - config += n; + params += n; continue; } - m = sscanf(config, " %d%n ~ %d%n", &temp1,&n, &temp2,&n); + m = sscanf(params, " %d%n ~ %d%n", &temp1,&n, &temp2,&n); if (m == 1) { /* single values */ r1 = temp1 - 1; r2 = temp1 - 1; @@ -753,11 +949,11 @@ static int get_mask(const char * config, uint32_t *value) { mask |= (1 << i); } - config += n; + params += n; total_n += n; - if (config[0]== ',' || config[0]== '-') - config++; + if (params[0]== ',' || params[0]== '-') + params++; } *value = mask; @@ -765,55 +961,55 @@ static int get_mask(const char * config, uint32_t *value) { } -static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) { +static int get_fade(const char* params, txtp_mix_data* mix, int* p_n) { int n, m, tn = 0; char type, separator; - m = sscanf(config, " %d %c%n", &mix->ch_dst, &type, &n); + m = sscanf(params, " %d %c%n", &mix->ch_dst, &type, &n); if (m != 2 || n == 0) goto fail; - config += n; + params += n; tn += n; if (type == '^') { /* full definition */ - m = sscanf(config, " %lf ~ %lf = %c @%n", &mix->vol_start, &mix->vol_end, &mix->shape, &n); + m = sscanf(params, " %lf ~ %lf = %c @%n", &mix->vol_start, &mix->vol_end, &mix->shape, &n); if (m != 3 || n == 0) goto fail; - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_pre, &mix->sample_pre); + n = get_time(params, &mix->time_pre, &mix->sample_pre); if (n == 0) goto fail; - config += n; + params += n; tn += n; - m = sscanf(config, " %c%n", &separator, &n); + m = sscanf(params, " %c%n", &separator, &n); if ( m != 1 || n == 0 || separator != '~') goto fail; - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_start, &mix->sample_start); + n = get_time(params, &mix->time_start, &mix->sample_start); if (n == 0) goto fail; - config += n; + params += n; tn += n; - m = sscanf(config, " %c%n", &separator, &n); + m = sscanf(params, " %c%n", &separator, &n); if (m != 1 || n == 0 || separator != '+') goto fail; - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_end, &mix->sample_end); + n = get_time(params, &mix->time_end, &mix->sample_end); if (n == 0) goto fail; - config += n; + params += n; tn += n; - m = sscanf(config, " %c%n", &separator, &n); + m = sscanf(params, " %c%n", &separator, &n); if (m != 1 || n == 0 || separator != '~') goto fail; - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_post, &mix->sample_post); + n = get_time(params, &mix->time_post, &mix->sample_post); if (n == 0) goto fail; - config += n; + params += n; tn += n; } else { @@ -835,24 +1031,24 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) { mix->time_pre = -1.0; mix->sample_pre = -1; - n = get_position(config, &mix->position, &mix->position_type); + n = get_position(params, &mix->position, &mix->position_type); //if (n == 0) goto fail; /* optional */ - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_start, &mix->sample_start); + n = get_time(params, &mix->time_start, &mix->sample_start); if (n == 0) goto fail; - config += n; + params += n; tn += n; - m = sscanf(config, " %c%n", &separator, &n); + m = sscanf(params, " %c%n", &separator, &n); if (m != 1 || n == 0 || separator != '+') goto fail; - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_end, &mix->sample_end); + n = get_time(params, &mix->time_end, &mix->sample_end); if (n == 0) goto fail; - config += n; + params += n; tn += n; mix->time_post = -1.0; @@ -861,14 +1057,18 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) { mix->time_end = mix->time_start + mix->time_end; /* defined as length */ - *out_n = tn; + *p_n = tn; return 1; fail: return 0; } -void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) { - if (cfg->mixing_count + 1 > TXTP_MIXING_MAX) { +/*******************************************************************************/ +/* PARSER - MAIN */ +/*******************************************************************************/ + +void add_mixing(txtp_entry* entry, txtp_mix_data* mix, txtp_mix_t command) { + if (entry->mixing_count + 1 > TXTP_MIXING_MAX) { VGM_LOG("TXTP: too many mixes\n"); return; } @@ -879,323 +1079,324 @@ void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) { mix->ch_src--; mix->command = command; - cfg->mixing[cfg->mixing_count] = *mix; /* memcpy'ed */ - cfg->mixing_count++; + entry->mixing[entry->mixing_count] = *mix; /* memcpy'ed */ + entry->mixing_count++; } +static void add_settings(txtp_entry* current, txtp_entry* entry, const char* filename) { -static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filename) { - - /* don't memcopy to allow list additions and ignore values not set, - * as current can be "default" config */ + /* don't memcopy to allow list additions and ignore values not set, as current can be "default" settings */ //*current = *cfg; if (filename) strcpy(current->filename, filename); - if (cfg->subsong) - current->subsong = cfg->subsong; - if (cfg->channel_mask) - current->channel_mask = cfg->channel_mask; + /* play config */ + copy_config(¤t->config, &entry->config); - if (cfg->mixing_count > 0) { + /* file settings */ + if (entry->subsong) + current->subsong = entry->subsong; + + if (entry->sample_rate > 0) + current->sample_rate = entry->sample_rate; + + if (entry->channel_mask) + current->channel_mask = entry->channel_mask; + + if (entry->loop_install_set) { + current->loop_install_set = entry->loop_install_set; + current->loop_end_max = entry->loop_end_max; + current->loop_start_sample = entry->loop_start_sample; + current->loop_start_second = entry->loop_start_second; + current->loop_end_sample = entry->loop_end_sample; + current->loop_end_second = entry->loop_end_second; + } + + if (entry->trim_set) { + current->trim_set = entry->trim_set; + current->trim_second = entry->trim_second; + current->trim_sample = entry->trim_sample; + } + + if (entry->mixing_count > 0) { int i; - for (i = 0; i < cfg->mixing_count; i++) { - current->mixing[current->mixing_count] = cfg->mixing[i]; + for (i = 0; i < entry->mixing_count; i++) { + current->mixing[current->mixing_count] = entry->mixing[i]; current->mixing_count++; } } - - if (cfg->config.play_forever) { - current->config.play_forever = cfg->config.play_forever; - } - if (cfg->config.loop_count_set) { - current->config.loop_count_set = 1; - current->config.loop_count = cfg->config.loop_count; - } - if (cfg->config.fade_time_set) { - current->config.fade_time_set = 1; - current->config.fade_time = cfg->config.fade_time; - } - if (cfg->config.fade_delay_set) { - current->config.fade_delay_set = 1; - current->config.fade_delay = cfg->config.fade_delay; - } - if (cfg->config.ignore_fade) { - current->config.ignore_fade = cfg->config.ignore_fade; - } - if (cfg->config.force_loop) { - current->config.force_loop = cfg->config.force_loop; - } - if (cfg->config.really_force_loop) { - current->config.really_force_loop = cfg->config.really_force_loop; - } - if (cfg->config.ignore_loop) { - current->config.ignore_loop = cfg->config.ignore_loop; - } - - if (cfg->sample_rate > 0) { - current->sample_rate = cfg->sample_rate; - } - - if (cfg->loop_install_set) { - current->loop_install_set = cfg->loop_install_set; - current->loop_end_max = cfg->loop_end_max; - current->loop_start_sample = cfg->loop_start_sample; - current->loop_start_second = cfg->loop_start_second; - current->loop_end_sample = cfg->loop_end_sample; - current->loop_end_second = cfg->loop_end_second; - } - - if (cfg->trim_set) { - current->trim_set = cfg->trim_set; - current->trim_second = cfg->trim_second; - current->trim_sample = cfg->trim_sample; - } } -static void parse_config(txtp_entry *cfg, char *config) { - /* parse config: #(commands) */ +static void parse_params(txtp_entry* entry, char* params) { + /* parse params: #(commands) */ int n, nc, nm, mc; char command[TXTP_LINE_MAX] = {0}; + play_config_t* tcfg = &entry->config; - cfg->range_start = 0; - cfg->range_end = 1; + entry->range_start = 0; + entry->range_end = 1; - while (config != NULL) { + while (params != NULL) { /* position in next #(command) */ - config = strchr(config, '#'); - if (!config) break; - //;VGM_LOG("TXTP: config='%s'\n", config); + params = strchr(params, '#'); + if (!params) break; + //;VGM_LOG("TXTP: params='%s'\n", params); /* get command until next space/number/comment/end */ command[0] = '\0'; - mc = sscanf(config, "#%n%[^ #0-9\r\n]%n", &nc, command, &nc); + mc = sscanf(params, "#%n%[^ #0-9\r\n]%n", &nc, command, &nc); //;VGM_LOG("TXTP: command='%s', nc=%i, mc=%i\n", command, nc, mc); if (mc <= 0 && nc == 0) break; - config[0] = '\0'; //todo don't modify input string and properly calculate filename end + params[0] = '\0'; //todo don't modify input string and properly calculate filename end - config += nc; /* skip '#' and command */ + params += nc; /* skip '#' and command */ /* check command string (though at the moment we only use single letters) */ if (strcmp(command,"c") == 0) { /* channel mask: file.ext#c1,2 = play channels 1,2 and mutes rest */ - config += get_mask(config, &cfg->channel_mask); - //;VGM_LOG("TXTP: channel_mask ");{int i; for (i=0;i<16;i++)VGM_LOG("%i ",(cfg->channel_mask>>i)&1);}VGM_LOG("\n"); + params += get_mask(params, &entry->channel_mask); + //;VGM_LOG("TXTP: channel_mask ");{int i; for (i=0;i<16;i++)VGM_LOG("%i ",(entry->channel_mask>>i)&1);}VGM_LOG("\n"); } else if (strcmp(command,"m") == 0) { /* channel mixing: file.ext#m(sub-command),(sub-command),etc */ char cmd; - while (config[0] != '\0') { + while (params[0] != '\0') { txtp_mix_data mix = {0}; - //;VGM_LOG("TXTP: subcommand='%s'\n", config); + //;VGM_LOG("TXTP: subcommand='%s'\n", params); //todo use strchr instead? - if (sscanf(config, " %c%n", &cmd, &n) == 1 && n != 0 && cmd == ',') { - config += n; + if (sscanf(params, " %c%n", &cmd, &n) == 1 && n != 0 && cmd == ',') { + params += n; continue; } - if (sscanf(config, " %d - %d%n", &mix.ch_dst, &mix.ch_src, &n) == 2 && n != 0) { + if (sscanf(params, " %d - %d%n", &mix.ch_dst, &mix.ch_src, &n) == 2 && n != 0) { //;VGM_LOG("TXTP: mix %i-%i\n", mix.ch_dst, mix.ch_src); - add_mixing(cfg, &mix, MIX_SWAP); /* N-M: swaps M with N */ - config += n; + add_mixing(entry, &mix, MIX_SWAP); /* N-M: swaps M with N */ + params += n; continue; } - if ((sscanf(config, " %d + %d * %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0) || - (sscanf(config, " %d + %d x %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0)) { + if ((sscanf(params, " %d + %d * %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0) || + (sscanf(params, " %d + %d x %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0)) { //;VGM_LOG("TXTP: mix %i+%i*%f\n", mix.ch_dst, mix.ch_src, mix.vol); - add_mixing(cfg, &mix, MIX_ADD_VOLUME); /* N+M*V: mixes M*volume to N */ - config += n; + add_mixing(entry, &mix, MIX_ADD_VOLUME); /* N+M*V: mixes M*volume to N */ + params += n; continue; } - if (sscanf(config, " %d + %d%n", &mix.ch_dst, &mix.ch_src, &n) == 2 && n != 0) { + if (sscanf(params, " %d + %d%n", &mix.ch_dst, &mix.ch_src, &n) == 2 && n != 0) { //;VGM_LOG("TXTP: mix %i+%i\n", mix.ch_dst, mix.ch_src); - add_mixing(cfg, &mix, MIX_ADD); /* N+M: mixes M to N */ - config += n; + add_mixing(entry, &mix, MIX_ADD); /* N+M: mixes M to N */ + params += n; continue; } - if ((sscanf(config, " %d * %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0) || - (sscanf(config, " %d x %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) { + if ((sscanf(params, " %d * %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0) || + (sscanf(params, " %d x %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) { //;VGM_LOG("TXTP: mix %i*%f\n", mix.ch_dst, mix.vol); - add_mixing(cfg, &mix, MIX_VOLUME); /* N*V: changes volume of N */ - config += n; + add_mixing(entry, &mix, MIX_VOLUME); /* N*V: changes volume of N */ + params += n; continue; } - if ((sscanf(config, " %d = %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) { + if ((sscanf(params, " %d = %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) { //;VGM_LOG("TXTP: mix %i=%f\n", mix.ch_dst, mix.vol); - add_mixing(cfg, &mix, MIX_LIMIT); /* N=V: limits volume of N */ - config += n; + add_mixing(entry, &mix, MIX_LIMIT); /* N=V: limits volume of N */ + params += n; continue; } - if (sscanf(config, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'D') { + if (sscanf(params, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'D') { //;VGM_LOG("TXTP: mix %iD\n", mix.ch_dst); - add_mixing(cfg, &mix, MIX_KILLMIX); /* ND: downmix N and all following channels */ - config += n; + add_mixing(entry, &mix, MIX_KILLMIX); /* ND: downmix N and all following channels */ + params += n; continue; } - if (sscanf(config, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'd') { + if (sscanf(params, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'd') { //;VGM_LOG("TXTP: mix %id\n", mix.ch_dst); - add_mixing(cfg, &mix, MIX_DOWNMIX);/* Nd: downmix N only */ - config += n; + add_mixing(entry, &mix, MIX_DOWNMIX);/* Nd: downmix N only */ + params += n; continue; } - if (sscanf(config, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'u') { + if (sscanf(params, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'u') { //;VGM_LOG("TXTP: mix %iu\n", mix.ch_dst); - add_mixing(cfg, &mix, MIX_UPMIX); /* Nu: upmix N */ - config += n; + add_mixing(entry, &mix, MIX_UPMIX); /* Nu: upmix N */ + params += n; continue; } - if (get_fade(config, &mix, &n) != 0) { + if (get_fade(params, &mix, &n) != 0) { //;VGM_LOG("TXTP: fade %d^%f~%f=%c@%f~%f+%f~%f\n", // mix.ch_dst, mix.vol_start, mix.vol_end, mix.shape, // mix.time_pre, mix.time_start, mix.time_end, mix.time_post); - add_mixing(cfg, &mix, MIX_FADE); /* N^V1~V2@T1~T2+T3~T4: fades volumes between positions */ - config += n; + add_mixing(entry, &mix, MIX_FADE); /* N^V1~V2@T1~T2+T3~T4: fades volumes between positions */ + params += n; continue; } break; /* unknown mix/new command/end */ } } - else if (strcmp(command,"s") == 0 || (nc == 1 && config[0] >= '0' && config[0] <= '9')) { + else if (strcmp(command,"s") == 0 || (nc == 1 && params[0] >= '0' && params[0] <= '9')) { /* subsongs: file.ext#s2 = play subsong 2, file.ext#2~10 = play subsong range */ int subsong_start = 0, subsong_end = 0; - //todo also advance config? - if (sscanf(config, " %d ~ %d", &subsong_start, &subsong_end) == 2) { + //todo also advance params? + if (sscanf(params, " %d ~ %d", &subsong_start, &subsong_end) == 2) { if (subsong_start > 0 && subsong_end > 0) { - cfg->range_start = subsong_start-1; - cfg->range_end = subsong_end; + entry->range_start = subsong_start-1; + entry->range_end = subsong_end; } //;VGM_LOG("TXTP: subsong range %i~%i\n", range_start, range_end); } - else if (sscanf(config, " %d", &subsong_start) == 1) { + else if (sscanf(params, " %d", &subsong_start) == 1) { if (subsong_start > 0) { - cfg->range_start = subsong_start-1; - cfg->range_end = subsong_start; + entry->range_start = subsong_start-1; + entry->range_end = subsong_start; } //;VGM_LOG("TXTP: subsong single %i-%i\n", range_start, range_end); } - else { /* wrong config, ignore */ + else { /* wrong setting, ignore */ //;VGM_LOG("TXTP: subsong none\n"); } } + + /* play config */ else if (strcmp(command,"i") == 0) { - config += get_bool(config, &cfg->config.ignore_loop); - //;VGM_LOG("TXTP: ignore_loop=%i\n", cfg->config.ignore_loop); + params += get_bool(params, &tcfg->ignore_loop); + tcfg->config_set = 1; } else if (strcmp(command,"e") == 0) { - config += get_bool(config, &cfg->config.force_loop); - //;VGM_LOG("TXTP: force_loop=%i\n", cfg->config.force_loop); + params += get_bool(params, &tcfg->force_loop); + tcfg->config_set = 1; } else if (strcmp(command,"E") == 0) { - config += get_bool(config, &cfg->config.really_force_loop); - //;VGM_LOG("TXTP: really_force_loop=%i\n", cfg->config.really_force_loop); + params += get_bool(params, &tcfg->really_force_loop); + tcfg->config_set = 1; } else if (strcmp(command,"F") == 0) { - config += get_bool(config, &cfg->config.ignore_fade); - //;VGM_LOG("TXTP: ignore_fade=%i\n", cfg->config.ignore_fade); + params += get_bool(params, &tcfg->ignore_fade); + tcfg->config_set = 1; } else if (strcmp(command,"L") == 0) { - config += get_bool(config, &cfg->config.play_forever); - //;VGM_LOG("TXTP: play_forever=%i\n", cfg->config.play_forever); + params += get_bool(params, &tcfg->play_forever); + tcfg->config_set = 1; } else if (strcmp(command,"l") == 0) { - config += get_double(config, &cfg->config.loop_count, &cfg->config.loop_count_set); - if (cfg->config.loop_count < 0) - cfg->config.loop_count_set = 0; - //;VGM_LOG("TXTP: loop_count=%f\n", cfg->config.loop_count); + params += get_double(params, &tcfg->loop_count, &tcfg->loop_count_set); + if (tcfg->loop_count < 0) + tcfg->loop_count_set = 0; + tcfg->config_set = 1; } else if (strcmp(command,"f") == 0) { - config += get_double(config, &cfg->config.fade_time, &cfg->config.fade_time_set); - if (cfg->config.fade_time < 0) - cfg->config.fade_time_set = 0; - //;VGM_LOG("TXTP: fade_time=%f\n", cfg->config.fade_time); + params += get_double(params, &tcfg->fade_time, &tcfg->fade_time_set); + if (tcfg->fade_time < 0) + tcfg->fade_time_set = 0; + tcfg->config_set = 1; } else if (strcmp(command,"d") == 0) { - config += get_double(config, &cfg->config.fade_delay, &cfg->config.fade_delay_set); - if (cfg->config.fade_delay < 0) - cfg->config.fade_delay_set = 0; - //;VGM_LOG("TXTP: fade_delay %f\n", cfg->config.fade_delay); + params += get_double(params, &tcfg->fade_delay, &tcfg->fade_delay_set); + if (tcfg->fade_delay < 0) + tcfg->fade_delay_set = 0; + tcfg->config_set = 1; } + else if (strcmp(command,"p") == 0) { + params += get_time_f(params, &tcfg->pad_begin_s, &tcfg->pad_begin, &tcfg->pad_begin_set); + tcfg->config_set = 1; + } + else if (strcmp(command,"P") == 0) { + params += get_time_f(params, &tcfg->pad_end_s, &tcfg->pad_end, &tcfg->pad_end_set); + tcfg->config_set = 1; + } + else if (strcmp(command,"r") == 0) { + params += get_time_f(params, &tcfg->trim_begin_s, &tcfg->trim_begin, &tcfg->trim_begin_set); + tcfg->config_set = 1; + } + else if (strcmp(command,"R") == 0) { + params += get_time_f(params, &tcfg->trim_end_s, &tcfg->trim_end, &tcfg->trim_end_set); + tcfg->config_set = 1; + } + else if (strcmp(command,"b") == 0) { + params += get_time_f(params, &tcfg->body_time_s, &tcfg->body_time, &tcfg->body_time_set); + tcfg->config_set = 1; + } + + /* other settings */ else if (strcmp(command,"h") == 0) { - config += get_int(config, &cfg->sample_rate); + params += get_int(params, &entry->sample_rate); //;VGM_LOG("TXTP: sample_rate %i\n", cfg->sample_rate); } else if (strcmp(command,"I") == 0) { - n = get_time(config, &cfg->loop_start_second, &cfg->loop_start_sample); + n = get_time(params, &entry->loop_start_second, &entry->loop_start_sample); if (n > 0) { /* first value must exist */ - config += n; + params += n; - n = get_time(config, &cfg->loop_end_second, &cfg->loop_end_sample); + n = get_time(params, &entry->loop_end_second, &entry->loop_end_sample); if (n == 0) { /* second value is optional */ - cfg->loop_end_max = 1; + entry->loop_end_max = 1; } - config += n; - cfg->loop_install_set = 1; + params += n; + entry->loop_install_set = 1; } - //;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", cfg->loop_install, cfg->loop_end_max, - // cfg->loop_start_sample, cfg->loop_end_sample, cfg->loop_start_second, cfg->loop_end_second); + //;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", entry->loop_install, entry->loop_end_max, + // entry->loop_start_sample, entry->loop_end_sample, entry->loop_start_second, entry->loop_end_second); } else if (strcmp(command,"t") == 0) { - n = get_time(config, &cfg->trim_second, &cfg->trim_sample); - cfg->trim_set = (n > 0); - //;VGM_LOG("TXTP: trim %i - %f / %i\n", cfg->trim_set, cfg->trim_second, cfg->trim_sample); + entry->trim_set = get_time(params, &entry->trim_second, &entry->trim_sample); + //;VGM_LOG("TXTP: trim %i - %f / %i\n", entry->trim_set, entry->trim_second, entry->trim_sample); } + //todo cleanup + /* macros */ else if (strcmp(command,"@volume") == 0) { txtp_mix_data mix = {0}; - nm = get_double(config, &mix.vol, NULL); - config += nm; + nm = get_double(params, &mix.vol, NULL); + params += nm; if (nm == 0) continue; - nm = get_mask(config, &mix.mask); - config += nm; + nm = get_mask(params, &mix.mask); + params += nm; - add_mixing(cfg, &mix, MACRO_VOLUME); + add_mixing(entry, &mix, MACRO_VOLUME); } else if (strcmp(command,"@track") == 0 || strcmp(command,"C") == 0 ) { txtp_mix_data mix = {0}; - nm = get_mask(config, &mix.mask); - config += nm; + nm = get_mask(params, &mix.mask); + params += nm; if (nm == 0) continue; - add_mixing(cfg, &mix, MACRO_TRACK); + add_mixing(entry, &mix, MACRO_TRACK); } else if (strcmp(command,"@layer-v") == 0 || strcmp(command,"@layer-b") == 0 || strcmp(command,"@layer-e") == 0) { txtp_mix_data mix = {0}; - nm = get_int(config, &mix.max); - config += nm; - if (nm == 0) continue; + nm = get_int(params, &mix.max); + params += nm; - nm = get_mask(config, &mix.mask); - config += nm; + if (nm > 0) { /* max is optional (auto-detects and uses max channels) */ + nm = get_mask(params, &mix.mask); + params += nm; + } mix.mode = command[7]; /* pass letter */ - add_mixing(cfg, &mix, MACRO_LAYER); + add_mixing(entry, &mix, MACRO_LAYER); } else if (strcmp(command,"@crosslayer-v") == 0 || strcmp(command,"@crosslayer-b") == 0 || @@ -1211,23 +1412,23 @@ static void parse_config(txtp_entry *cfg, char *config) { mix.mode = command[12]; /* pass letter */ } - nm = get_int(config, &mix.max); - config += nm; + nm = get_int(params, &mix.max); + params += nm; if (nm == 0) continue; - add_mixing(cfg, &mix, type); + add_mixing(entry, &mix, type); } else if (strcmp(command,"@downmix") == 0) { txtp_mix_data mix = {0}; mix.max = 2; /* stereo only for now */ - //nm = get_int(config, &mix.max); - //config += nm; + //nm = get_int(params, &mix.max); + //params += nm; //if (nm == 0) continue; - add_mixing(cfg, &mix, MACRO_DOWNMIX); + add_mixing(entry, &mix, MACRO_DOWNMIX); } - else if (config[nc] == ' ') { + else if (params[nc] == ' ') { //;VGM_LOG("TXTP: comment\n"); break; /* comment, ignore rest */ } @@ -1241,14 +1442,21 @@ static void parse_config(txtp_entry *cfg, char *config) { } - -static int add_group(txtp_header * txtp, char *line) { +static int add_group(txtp_header* txtp, char* line) { int n, m; txtp_group cfg = {0}; + int auto_pos = 0; + char c; /* parse group: (position)(type)(count)(repeat) #(commands) */ //;VGM_LOG("TXTP: parse group '%s'\n", line); + m = sscanf(line, " %c%n", &c, &n); + if (m == 1 && c == '-') { + auto_pos = 1; + line += n; + } + m = sscanf(line, " %d%n", &cfg.position, &n); if (m == 1) { cfg.position--; /* externally 1=first but internally 0=first */ @@ -1267,13 +1475,41 @@ static int add_group(txtp_header * txtp, char *line) { m = sscanf(line, " %c%n", &cfg.repeat, &n); if (m == 1 && cfg.repeat == TXTP_GROUP_REPEAT) { + auto_pos = 0; line += n; } + m = sscanf(line, " >%d%n", &cfg.selected, &n); + if (m == 1) { + cfg.selected--; /* externally 1=first but internally 0=first */ + line += n; + } - parse_config(&cfg.group_config, line); + parse_params(&cfg.group_settings, line); - //;VGM_LOG("TXTP: parsed group %i%c%i%c\n",cfg.position+1,cfg.type,cfg.count,cfg.repeat); + /* Groups can use "auto" position of last N files, so we need a counter that changes like this: + * #layer of 2 (pos = 0) + * #sequence of 2 + * bgm pos +1 > 1 + * bgm pos +1 > 2 + * group = -S2 pos -2 +1 > 1 (group is at 1 now since it "collapses" wems but becomes a position) + * #sequence of 3 + * bgm pos +1 > 2 + * bgm pos +1 > 3 + * #sequence of 2 + * bgm pos +1 > 4 + * bgm pos +1 > 5 + * group = -S2 pos -2 +1 > 4 (groups is at 4 now since are uncollapsed wems at 2/3) + * group = -S3 pos -3 +1 > 2 + * group = -L2 pos -2 +1 > 1 + */ + txtp->group_pos++; + txtp->group_pos -= cfg.count; + if (auto_pos) { + cfg.position = txtp->group_pos - 1; /* internally 1 = first */ + } + + //;VGM_LOG("TXTP: parsed group %i%c%i%c, auto=%i\n",cfg.position+1,cfg.type,cfg.count,cfg.repeat, auto_pos); /* add final group */ { @@ -1299,52 +1535,72 @@ fail: } -static int add_entry(txtp_header * txtp, char *filename, int is_default) { +static void clean_filename(char* filename) { int i; - txtp_entry cfg = {0}; + size_t len; + + if (filename[0] == '\0') + return; + + /* normalize paths */ + fix_dir_separators(filename); + + /* remove trailing spaces */ + len = strlen(filename); + for (i = len-1; i > 0; i--) { + if (filename[i] != ' ') + break; + filename[i] = '\0'; + } + +} + +static int add_entry(txtp_header* txtp, char* filename, int is_default) { + int i; + txtp_entry entry = {0}; //;VGM_LOG("TXTP: filename=%s\n", filename); /* parse filename: file.ext#(commands) */ { - char *config; + char* params; if (is_default) { - config = filename; /* multiple commands without filename */ + params = filename; /* multiple commands without filename */ } else { - /* find config start (filenames and config can contain multiple dots and #, - * so this may be fooled by certain patterns of . and #) */ - config = strchr(filename, '.'); /* first dot (may be a false positive) */ - if (!config) /* extensionless */ - config = filename; - config = strchr(config, '#'); /* next should be config */ - if (!config) /* no config */ - config = NULL; + /* find settings start after filenames (filenames can also contain dots and #, + * so this may be fooled by certain patterns) */ + params = strchr(filename, '.'); /* first dot (may be a false positive) */ + if (!params) /* extensionless */ + params = filename; + params = strchr(params, '#'); /* next should be actual settings */ + if (!params) + params = NULL; } - parse_config(&cfg, config); + parse_params(&entry, params); } clean_filename(filename); //;VGM_LOG("TXTP: clean filename='%s'\n", filename); - /* config that applies to all files */ + /* settings that applies to final vgmstream */ if (is_default) { txtp->default_entry_set = 1; - add_config(&txtp->default_entry, &cfg, NULL); + add_settings(&txtp->default_entry, &entry, NULL); return 1; } /* add final entry */ - for (i = cfg.range_start; i < cfg.range_end; i++){ - txtp_entry *current; + for (i = entry.range_start; i < entry.range_end; i++){ + txtp_entry* current; /* resize in steps if not enough */ if (txtp->entry_count+1 > txtp->entry_max) { - txtp_entry *temp_entry; + txtp_entry* temp_entry; txtp->entry_max += 5; temp_entry = realloc(txtp->entry, sizeof(txtp_entry) * txtp->entry_max); @@ -1355,11 +1611,12 @@ static int add_entry(txtp_header * txtp, char *filename, int is_default) { /* new entry */ current = &txtp->entry[txtp->entry_count]; memset(current,0, sizeof(txtp_entry)); - cfg.subsong = (i+1); + entry.subsong = (i+1); - add_config(current, &cfg, filename); + add_settings(current, &entry, filename); txtp->entry_count++; + txtp->group_pos++; } return 1; @@ -1367,9 +1624,12 @@ fail: return 0; } -/* ************************************************************************ */ -static int is_substring(const char * val, const char * cmp) { +/*******************************************************************************/ +/* PARSER - BASE */ +/*******************************************************************************/ + +static int is_substring(const char* val, const char* cmp) { int n; char subval[TXTP_LINE_MAX] = {0}; @@ -1382,7 +1642,7 @@ static int is_substring(const char * val, const char * cmp) { return n; } -static int parse_num(const char * val, uint32_t * out_value) { +static int parse_num(const char* val, uint32_t* out_value) { int hex = (val[0]=='0' && val[1]=='x'); if (sscanf(val, hex ? "%x" : "%u", out_value) != 1) goto fail; @@ -1392,7 +1652,7 @@ fail: return 0; } -static int parse_keyval(txtp_header * txtp, const char * key, const char * val) { +static int parse_keyval(txtp_header* txtp, const char* key, const char* val) { //;VGM_LOG("TXTP: key=val '%s'='%s'\n", key,val); @@ -1451,10 +1711,10 @@ fail: return 0; } -static txtp_header* parse_txtp(STREAMFILE* streamFile) { +static txtp_header* parse_txtp(STREAMFILE* sf) { txtp_header* txtp = NULL; off_t txt_offset = 0x00; - off_t file_size = get_streamfile_size(streamFile); + off_t file_size = get_streamfile_size(sf); txtp = calloc(1,sizeof(txtp_header)); @@ -1466,7 +1726,7 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { /* skip BOM if needed */ if (file_size > 0 && - ((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF)) + ((uint16_t)read_16bitLE(0x00, sf) == 0xFFFE || (uint16_t)read_16bitLE(0x00, sf) == 0xFEFF)) txt_offset = 0x02; /* read and parse lines */ @@ -1476,7 +1736,7 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { char filename[TXTP_LINE_MAX] = {0}; int ok, bytes_read, line_ok; - bytes_read = read_line(line, sizeof(line), txt_offset, streamFile, &line_ok); + bytes_read = read_line(line, sizeof(line), txt_offset, sf, &line_ok); if (!line_ok) goto fail; txt_offset += bytes_read; @@ -1496,17 +1756,17 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { if (filename[0] == '#') continue; /* simple comment */ - /* filename with config */ + /* filename with settings */ if (!add_entry(txtp, filename, 0)) goto fail; } /* mini-txth: if no entries are set try with filename, ex. from "song.ext#3.txtp" use "song.ext#3" - * (it's possible to have default "commands" inside the .txtp plus filename+config) */ + * (it's possible to have default "commands" inside the .txtp plus filename+settings) */ if (txtp->entry_count == 0) { char filename[PATH_LIMIT] = {0}; - get_streamfile_basename(streamFile, filename, sizeof(filename)); + get_streamfile_basename(sf, filename, sizeof(filename)); add_entry(txtp, filename, 0); } @@ -1517,22 +1777,3 @@ fail: clean_txtp(txtp, 1); return NULL; } - -static void clean_txtp(txtp_header* txtp, int fail) { - int i, start; - - if (!txtp) - return; - - /* returns first vgmstream on success so it's not closed */ - start = fail ? 0 : 1; - - for (i = start; i < txtp->vgmstream_count; i++) { - close_vgmstream(txtp->vgmstream[i]); - } - - free(txtp->vgmstream); - free(txtp->group); - free(txtp->entry); - free(txtp); -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c index a79955aad..d21102be4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c @@ -124,47 +124,47 @@ typedef struct { int allowed_types[16]; } ubi_bao_header; -static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset); -static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, int target_subsong); -static int parse_pk(ubi_bao_header * bao, STREAMFILE *streamFile); -static VGMSTREAM * init_vgmstream_ubi_bao_header(ubi_bao_header * bao, STREAMFILE *streamFile); -static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *streamFile); -static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE *streamFile); -static int find_package_bao(uint32_t bao_id, STREAMFILE *streamFile, off_t *out_offset, size_t *out_size); +static int parse_header(ubi_bao_header* bao, STREAMFILE* sf, off_t offset); +static int parse_bao(ubi_bao_header* bao, STREAMFILE* sf, off_t offset, int target_subsong); +static int parse_pk(ubi_bao_header* bao, STREAMFILE* sf); +static VGMSTREAM* init_vgmstream_ubi_bao_header(ubi_bao_header* bao, STREAMFILE* sf); +static STREAMFILE* setup_bao_streamfile(ubi_bao_header* bao, STREAMFILE* sf); +static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE* sf); +static int find_package_bao(uint32_t target_id, STREAMFILE* sf, off_t* p_offset, size_t* p_size); -static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile); -static void config_bao_endian(ubi_bao_header * bao, off_t offset, STREAMFILE *streamFile); -static void build_readable_name(char * buf, size_t buf_size, ubi_bao_header * bao); +static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf); +static void config_bao_endian(ubi_bao_header* bao, off_t offset, STREAMFILE* sf); +static void build_readable_name(char* buf, size_t buf_size, ubi_bao_header* bao); /* .PK - packages with BAOs from Ubisoft's sound engine ("DARE") games in 2008+ */ -VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ubi_bao_pk(STREAMFILE* sf) { ubi_bao_header bao = { 0 }; /* checks */ - if (!check_extensions(streamFile, "pk,lpk,cpk")) + if (!check_extensions(sf, "pk,lpk,cpk")) goto fail; /* package .pk+spk (or .lpk+lspk for localized) database-like format, evolved from Ubi sbN/smN. * .pk has an index pointing to memory BAOs and tables with external stream BAOs in .spk. */ /* main parse */ - if (!parse_pk(&bao, streamFile)) + if (!parse_pk(&bao, sf)) goto fail; build_readable_name(bao.readable_name, sizeof(bao.readable_name), &bao); - return init_vgmstream_ubi_bao_header(&bao, streamFile); + return init_vgmstream_ubi_bao_header(&bao, sf); fail: return NULL; } /* .BAO - single BAO files from Ubisoft's sound engine ("DARE") games in 2008+ */ -VGMSTREAM * init_vgmstream_ubi_bao_atomic(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ubi_bao_atomic(STREAMFILE* sf) { ubi_bao_header bao = { 0 }; - STREAMFILE * streamData = NULL; + STREAMFILE* streamData = NULL; /* checks */ - if (!check_extensions(streamFile, "bao,")) + if (!check_extensions(sf, "bao,")) goto fail; /* atomic .bao+bao/sbao found in .forge and similar bigfiles. The bigfile acts as index, but @@ -172,21 +172,21 @@ VGMSTREAM * init_vgmstream_ubi_bao_atomic(STREAMFILE *streamFile) { * be other) we can simulate it. Extension is .bao/sbao or extensionaless in some games. */ /* format: 0x01=AC1, 0x02=POP2008 */ - if (read_8bit(0x00, streamFile) != 0x01 && read_8bit(0x00, streamFile) != 0x02) + if (read_8bit(0x00, sf) != 0x01 && read_8bit(0x00, sf) != 0x02) goto fail; bao.is_atomic = 1; - bao.version = read_32bitBE(0x00, streamFile) & 0x00FFFFFF; - if (!config_bao_version(&bao, streamFile)) + bao.version = read_32bitBE(0x00, sf) & 0x00FFFFFF; + if (!config_bao_version(&bao, sf)) goto fail; /* main parse */ - if (!parse_bao(&bao, streamFile, 0x00, 1)) + if (!parse_bao(&bao, sf, 0x00, 1)) goto fail; build_readable_name(bao.readable_name, sizeof(bao.readable_name), &bao); - return init_vgmstream_ubi_bao_header(&bao, streamFile); + return init_vgmstream_ubi_bao_header(&bao, sf); fail: close_streamfile(streamData); return NULL; @@ -194,11 +194,11 @@ fail: #if 0 /* .SPK - special mini package with BAOs [Avatar (PS3)] */ -VGMSTREAM * init_vgmstream_ubi_bao_spk(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ubi_bao_spk(STREAMFILE* sf) { ubi_bao_header bao = { 0 }; /* checks */ - if (!check_extensions(streamFile, "spk")) + if (!check_extensions(sf, "spk")) goto fail; /* Variation of .pk: @@ -221,8 +221,8 @@ VGMSTREAM * init_vgmstream_ubi_bao_spk(STREAMFILE *streamFile) { /* ************************************************************************* */ -static VGMSTREAM * init_vgmstream_ubi_bao_base(ubi_bao_header * bao, STREAMFILE *streamHead, STREAMFILE * streamData) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_base(ubi_bao_header* bao, STREAMFILE* streamHead, STREAMFILE* streamData) { + VGMSTREAM* vgmstream = NULL; off_t start_offset = 0x00; @@ -271,14 +271,14 @@ static VGMSTREAM * init_vgmstream_ubi_bao_base(ubi_bao_header * bao, STREAMFILE break; #ifdef VGM_USE_FFMPEG - //todo: some XMA1 decode a bit strangely at certain positions (FFmpeg bug?) + //TODO: Ubi XMA1 (raw or fmt) is a bit strange, FFmpeg decodes some frames slightly wrong (see Ubi SB) case RAW_XMA1: case RAW_XMA2_OLD: case RAW_XMA2_NEW: { uint8_t buf[0x100]; size_t bytes, chunk_size, data_size; off_t chunk_offset; - STREAMFILE *streamXMA; + STREAMFILE* streamXMA; switch(bao->codec) { case RAW_XMA1: chunk_size = 0x20; break; @@ -407,14 +407,14 @@ fail: return NULL; } -static VGMSTREAM * init_vgmstream_ubi_bao_audio(ubi_bao_header * bao, STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamData = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_audio(ubi_bao_header* bao, STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* streamData = NULL; - streamData = setup_bao_streamfile(bao, streamFile); + streamData = setup_bao_streamfile(bao, sf); if (!streamData) goto fail; - vgmstream = init_vgmstream_ubi_bao_base(bao, streamFile, streamData); + vgmstream = init_vgmstream_ubi_bao_base(bao, sf, streamData); if (!vgmstream) goto fail; close_streamfile(streamData); @@ -428,15 +428,15 @@ fail: return NULL; } -static VGMSTREAM * init_vgmstream_ubi_bao_layer(ubi_bao_header *bao, STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_layer(ubi_bao_header* bao, STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; layered_layout_data* data = NULL; - STREAMFILE* temp_streamFile = NULL; - STREAMFILE * streamData = NULL; + STREAMFILE* temp_sf = NULL; + STREAMFILE* streamData = NULL; size_t full_stream_size = bao->stream_size; int i, total_channels = 0; - streamData = setup_bao_streamfile(bao, streamFile); + streamData = setup_bao_streamfile(bao, sf); if (!streamData) goto fail; /* init layout */ @@ -447,19 +447,19 @@ static VGMSTREAM * init_vgmstream_ubi_bao_layer(ubi_bao_header *bao, STREAMFILE for (i = 0; i < bao->layer_count; i++) { /* prepare streamfile from a single layer section */ - temp_streamFile = setup_ubi_bao_streamfile(streamData, 0x00, full_stream_size, i, bao->layer_count, bao->big_endian); - if (!temp_streamFile) goto fail; + temp_sf = setup_ubi_bao_streamfile(streamData, 0x00, full_stream_size, i, bao->layer_count, bao->big_endian); + if (!temp_sf) goto fail; - bao->stream_size = get_streamfile_size(temp_streamFile); + bao->stream_size = get_streamfile_size(temp_sf); bao->channels = bao->layer_channels[i]; total_channels += bao->layer_channels[i]; /* build the layer VGMSTREAM (standard sb with custom streamfile) */ - data->layers[i] = init_vgmstream_ubi_bao_base(bao, streamFile, temp_streamFile); + data->layers[i] = init_vgmstream_ubi_bao_base(bao, sf, temp_sf); if (!data->layers[i]) goto fail; - close_streamfile(temp_streamFile); - temp_streamFile = NULL; + close_streamfile(temp_sf); + temp_sf = NULL; } if (!setup_layout_layered(data)) @@ -486,7 +486,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_layer(ubi_bao_header *bao, STREAMFILE return vgmstream; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); close_streamfile(streamData); if (vgmstream) close_vgmstream(vgmstream); @@ -497,8 +497,8 @@ fail: return NULL; } -static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_sequence(ubi_bao_header* bao, STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; STREAMFILE* streamChain = NULL; segmented_layout_data* data = NULL; int i; @@ -518,7 +518,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFI if (bao->is_atomic) { /* open memory audio BAO */ - streamChain = open_atomic_bao(bao->cfg.file_type, entry_id, 0, streamFile); + streamChain = open_atomic_bao(bao->cfg.file_type, entry_id, 0, sf); if (!streamChain) { VGM_LOG("UBI BAO: chain BAO %08x not found\n", entry_id); goto fail; @@ -535,13 +535,13 @@ static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFI else { /* find memory audio BAO */ off_t entry_offset; - if (!find_package_bao(entry_id, streamFile, &entry_offset, NULL)) { + if (!find_package_bao(entry_id, sf, &entry_offset, NULL)) { VGM_LOG("UBI BAO: expected chain id %08x not found\n", entry_id); goto fail; } /* parse BAO */ - if (!parse_header(&temp_bao, streamFile, entry_offset)) + if (!parse_header(&temp_bao, sf, entry_offset)) goto fail; } @@ -551,7 +551,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFI } /* build the layer VGMSTREAM (current sb entry config) */ - data->segments[i] = init_vgmstream_ubi_bao_header(&temp_bao, streamFile); + data->segments[i] = init_vgmstream_ubi_bao_header(&temp_bao, sf); if (!data->segments[i]) goto fail; if (i == bao->sequence_loop) @@ -599,38 +599,38 @@ fail: } -static size_t silence_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, void* data) { +static size_t silence_io_read(STREAMFILE* streamfile, uint8_t *dest, off_t offset, size_t length, void* data) { int i; for (i = 0; i < length; i++) { dest[i] = 0; } return length; /* pretend we read zeroes */ } -static size_t silence_io_size(STREAMFILE *streamfile, void* data) { +static size_t silence_io_size(STREAMFILE* streamfile, void* data) { return 0x7FFFFFF; /* whatevs */ } -static STREAMFILE* setup_silence_streamfile(STREAMFILE *streamFile) { - STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; +static STREAMFILE* setup_silence_streamfile(STREAMFILE* sf) { + STREAMFILE* temp_sf = NULL, *new_sf = NULL; /* setup custom streamfile */ - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_wrap_streamfile(sf); + if (!new_sf) goto fail; + temp_sf = new_sf; - new_streamFile = open_io_streamfile(temp_streamFile, NULL,0, silence_io_read,silence_io_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_io_streamfile(temp_sf, NULL,0, silence_io_read,silence_io_size); + if (!new_sf) goto fail; + temp_sf = new_sf; - return temp_streamFile; + return temp_sf; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return NULL; } -static VGMSTREAM * init_vgmstream_ubi_bao_silence(ubi_bao_header *bao, STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_silence(ubi_bao_header* bao, STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; int channel_count, sample_rate; channel_count = bao->channels; @@ -658,22 +658,22 @@ static VGMSTREAM * init_vgmstream_ubi_bao_silence(ubi_bao_header *bao, STREAMFIL vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; - temp_streamFile = setup_silence_streamfile(streamFile); - if ( !vgmstream_open_stream(vgmstream, temp_streamFile, 0x00) ) + temp_sf = setup_silence_streamfile(sf); + if ( !vgmstream_open_stream(vgmstream, temp_sf, 0x00) ) goto fail; - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return vgmstream; fail: close_vgmstream(vgmstream); - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return vgmstream; } -static VGMSTREAM * init_vgmstream_ubi_bao_header(ubi_bao_header * bao, STREAMFILE * streamFile) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_header(ubi_bao_header* bao, STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; if (bao->total_subsongs <= 0) { VGM_LOG("UBI BAO: no subsongs\n"); @@ -691,19 +691,19 @@ static VGMSTREAM * init_vgmstream_ubi_bao_header(ubi_bao_header * bao, STREAMFIL switch(bao->type) { case UBI_AUDIO: - vgmstream = init_vgmstream_ubi_bao_audio(bao, streamFile); + vgmstream = init_vgmstream_ubi_bao_audio(bao, sf); break; case UBI_LAYER: - vgmstream = init_vgmstream_ubi_bao_layer(bao, streamFile); + vgmstream = init_vgmstream_ubi_bao_layer(bao, sf); break; case UBI_SEQUENCE: - vgmstream = init_vgmstream_ubi_bao_sequence(bao, streamFile); + vgmstream = init_vgmstream_ubi_bao_sequence(bao, sf); break; case UBI_SILENCE: - vgmstream = init_vgmstream_ubi_bao_silence(bao, streamFile); + vgmstream = init_vgmstream_ubi_bao_silence(bao, sf); break; default: @@ -727,24 +727,24 @@ fail: * BAOs pointing to internal/external stream BAOs (.spk is the same, with stream BAOs only). * A fun feature of .pk is that different BAOs in a .pk can point to different .spk BAOs * that actually hold the same data, with different GUID too, somehow. */ -static int parse_pk(ubi_bao_header * bao, STREAMFILE *streamFile) { +static int parse_pk(ubi_bao_header* bao, STREAMFILE* sf) { int i; int index_entries; size_t index_size, index_header_size; off_t bao_offset; - int target_subsong = streamFile->stream_index; - STREAMFILE *streamIndex = NULL; - STREAMFILE *streamTest = NULL; + int target_subsong = sf->stream_index; + STREAMFILE* streamIndex = NULL; + STREAMFILE* streamTest = NULL; /* format: 0x01=package index, 0x02=package BAO */ - if (read_8bit(0x00, streamFile) != 0x01) + if (read_8bit(0x00, sf) != 0x01) goto fail; /* index and resources are always LE */ if (target_subsong <= 0) target_subsong = 1; - bao->version = read_32bitBE(0x00, streamFile) & 0x00FFFFFF; - index_size = read_32bitLE(0x04, streamFile); /* can be 0, not including */ + bao->version = read_32bitBE(0x00, sf) & 0x00FFFFFF; + index_size = read_32bitLE(0x04, sf); /* can be 0, not including */ /* 0x08: resource table offset, always found even if not used */ /* 0x0c: always 0? */ /* 0x10: unknown, null if no entries */ @@ -755,7 +755,7 @@ static int parse_pk(ubi_bao_header * bao, STREAMFILE *streamFile) { /* 0x30(10): parent GUID? may be same as 0x18, may be shared with other files */ /* (the above values seem ignored by games, probably just info for their tools) */ - if (!config_bao_version(bao, streamFile)) + if (!config_bao_version(bao, sf)) goto fail; @@ -769,10 +769,10 @@ static int parse_pk(ubi_bao_header * bao, STREAMFILE *streamFile) { } /* use smaller I/O buffers for performance, as this read lots of small headers all over the place */ - streamIndex = reopen_streamfile(streamFile, index_size); + streamIndex = reopen_streamfile(sf, index_size); if (!streamIndex) goto fail; - streamTest = reopen_streamfile(streamFile, 0x100); + streamTest = reopen_streamfile(sf, 0x100); if (!streamTest) goto fail; /* parse index to get target subsong N = Nth valid header BAO */ @@ -804,7 +804,7 @@ fail: /* ************************************************************************* */ -static void build_readable_name(char * buf, size_t buf_size, ubi_bao_header * bao) { +static void build_readable_name(char* buf, size_t buf_size, ubi_bao_header* bao) { const char *grp_name; const char *pft_name; const char *typ_name; @@ -858,42 +858,42 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_bao_header * ba } } -static int parse_type_audio(ubi_bao_header * bao, off_t offset, STREAMFILE* streamFile) { +static int parse_type_audio(ubi_bao_header* bao, off_t offset, STREAMFILE* sf) { int32_t (*read_32bit)(off_t,STREAMFILE*) = bao->big_endian ? read_32bitBE : read_32bitLE; off_t h_offset = offset + bao->header_skip; /* audio header */ bao->type = UBI_AUDIO; - bao->stream_size = read_32bit(h_offset + bao->cfg.audio_stream_size, streamFile); - bao->stream_id = read_32bit(h_offset + bao->cfg.audio_stream_id, streamFile); - bao->is_external = read_32bit(h_offset + bao->cfg.audio_external_flag, streamFile) & bao->cfg.audio_external_and; - bao->loop_flag = read_32bit(h_offset + bao->cfg.audio_loop_flag, streamFile) & bao->cfg.audio_loop_and; - bao->channels = read_32bit(h_offset + bao->cfg.audio_channels, streamFile); - bao->sample_rate = read_32bit(h_offset + bao->cfg.audio_sample_rate, streamFile); + bao->stream_size = read_32bit(h_offset + bao->cfg.audio_stream_size, sf); + bao->stream_id = read_32bit(h_offset + bao->cfg.audio_stream_id, sf); + bao->is_external = read_32bit(h_offset + bao->cfg.audio_external_flag, sf) & bao->cfg.audio_external_and; + bao->loop_flag = read_32bit(h_offset + bao->cfg.audio_loop_flag, sf) & bao->cfg.audio_loop_and; + bao->channels = read_32bit(h_offset + bao->cfg.audio_channels, sf); + bao->sample_rate = read_32bit(h_offset + bao->cfg.audio_sample_rate, sf); /* prefetch data is in another internal BAO right after the base header */ if (bao->cfg.audio_prefetch_size) { - bao->prefetch_size = read_32bit(h_offset + bao->cfg.audio_prefetch_size, streamFile); + bao->prefetch_size = read_32bit(h_offset + bao->cfg.audio_prefetch_size, sf); bao->is_prefetched = (bao->prefetch_size > 0); } if (bao->loop_flag) { - bao->loop_start = read_32bit(h_offset + bao->cfg.audio_num_samples, streamFile); - bao->num_samples = read_32bit(h_offset + bao->cfg.audio_num_samples2, streamFile) + bao->loop_start; + bao->loop_start = read_32bit(h_offset + bao->cfg.audio_num_samples, sf); + bao->num_samples = read_32bit(h_offset + bao->cfg.audio_num_samples2, sf) + bao->loop_start; } else { - bao->num_samples = read_32bit(h_offset + bao->cfg.audio_num_samples, streamFile); + bao->num_samples = read_32bit(h_offset + bao->cfg.audio_num_samples, sf); } - bao->stream_type = read_32bit(h_offset + bao->cfg.audio_stream_type, streamFile); + bao->stream_type = read_32bit(h_offset + bao->cfg.audio_stream_type, sf); return 1; //fail: // return 0; } -static int parse_type_sequence(ubi_bao_header * bao, off_t offset, STREAMFILE* streamFile) { +static int parse_type_sequence(ubi_bao_header* bao, off_t offset, STREAMFILE* sf) { int32_t (*read_32bit)(off_t,STREAMFILE*) = bao->big_endian ? read_32bitBE : read_32bitLE; off_t h_offset = offset + bao->header_skip; off_t table_offset; @@ -906,9 +906,9 @@ static int parse_type_sequence(ubi_bao_header * bao, off_t offset, STREAMFILE* s goto fail; } - bao->sequence_loop = read_32bit(h_offset + bao->cfg.sequence_sequence_loop, streamFile); - bao->sequence_single = read_32bit(h_offset + bao->cfg.sequence_sequence_single, streamFile); - bao->sequence_count = read_32bit(h_offset + bao->cfg.sequence_sequence_count, streamFile); + bao->sequence_loop = read_32bit(h_offset + bao->cfg.sequence_sequence_loop, sf); + bao->sequence_single = read_32bit(h_offset + bao->cfg.sequence_sequence_single, sf); + bao->sequence_count = read_32bit(h_offset + bao->cfg.sequence_sequence_count, sf); if (bao->sequence_count > BAO_MAX_CHAIN_COUNT) { VGM_LOG("UBI BAO: incorrect sequence count\n"); goto fail; @@ -917,7 +917,7 @@ static int parse_type_sequence(ubi_bao_header * bao, off_t offset, STREAMFILE* s /* get chain in extra table */ table_offset = offset + bao->header_size; for (i = 0; i < bao->sequence_count; i++) { - uint32_t entry_id = (uint32_t)read_32bit(table_offset + bao->cfg.sequence_entry_number, streamFile); + uint32_t entry_id = (uint32_t)read_32bit(table_offset + bao->cfg.sequence_entry_number, sf); bao->sequence_chain[i] = entry_id; @@ -930,7 +930,7 @@ fail: } -static int parse_type_layer(ubi_bao_header * bao, off_t offset, STREAMFILE* streamFile) { +static int parse_type_layer(ubi_bao_header* bao, off_t offset, STREAMFILE* sf) { int32_t (*read_32bit)(off_t,STREAMFILE*) = bao->big_endian ? read_32bitBE : read_32bitLE; off_t h_offset = offset + bao->header_skip; off_t table_offset; @@ -944,31 +944,31 @@ static int parse_type_layer(ubi_bao_header * bao, off_t offset, STREAMFILE* stre goto fail; } - bao->layer_count = read_32bit(h_offset + bao->cfg.layer_layer_count, streamFile); + bao->layer_count = read_32bit(h_offset + bao->cfg.layer_layer_count, sf); if (bao->layer_count > BAO_MAX_LAYER_COUNT) { VGM_LOG("UBI BAO: incorrect layer count\n"); goto fail; } - bao->is_external = read_32bit(h_offset + bao->cfg.layer_external_flag, streamFile) & bao->cfg.layer_external_and; - bao->stream_size = read_32bit(h_offset + bao->cfg.layer_stream_size, streamFile); - bao->stream_id = read_32bit(h_offset + bao->cfg.layer_stream_id, streamFile); + bao->is_external = read_32bit(h_offset + bao->cfg.layer_external_flag, sf) & bao->cfg.layer_external_and; + bao->stream_size = read_32bit(h_offset + bao->cfg.layer_stream_size, sf); + bao->stream_id = read_32bit(h_offset + bao->cfg.layer_stream_id, sf); if (bao->cfg.layer_prefetch_size) { - bao->prefetch_size = read_32bit(h_offset + bao->cfg.layer_prefetch_size, streamFile); + bao->prefetch_size = read_32bit(h_offset + bao->cfg.layer_prefetch_size, sf); bao->is_prefetched = (bao->prefetch_size > 0); } /* extra cue table (rare, has N variable-sized labels + cue table pointing to them) */ if (bao->cfg.layer_cue_labels) { - cues_size += read_32bit(h_offset + bao->cfg.layer_cue_labels, streamFile); + cues_size += read_32bit(h_offset + bao->cfg.layer_cue_labels, sf); } if (bao->cfg.layer_cue_count) { - cues_size += read_32bit(h_offset + bao->cfg.layer_cue_count, streamFile) * 0x08; + cues_size += read_32bit(h_offset + bao->cfg.layer_cue_count, sf) * 0x08; } if (bao->cfg.layer_extra_size) { - bao->extra_size = read_32bit(h_offset + bao->cfg.layer_extra_size, streamFile); + bao->extra_size = read_32bit(h_offset + bao->cfg.layer_extra_size, sf); } else { bao->extra_size = cues_size + bao->layer_count * bao->cfg.layer_entry_size + cues_size; @@ -976,16 +976,16 @@ static int parse_type_layer(ubi_bao_header * bao, off_t offset, STREAMFILE* stre /* get 1st layer header in extra table and validate all headers match */ table_offset = offset + bao->header_size + cues_size; - //bao->channels = read_32bit(table_offset + bao->cfg.layer_channels, streamFile); - bao->sample_rate = read_32bit(table_offset + bao->cfg.layer_sample_rate, streamFile); - bao->stream_type = read_32bit(table_offset + bao->cfg.layer_stream_type, streamFile); - bao->num_samples = read_32bit(table_offset + bao->cfg.layer_num_samples, streamFile); + //bao->channels = read_32bit(table_offset + bao->cfg.layer_channels, sf); + bao->sample_rate = read_32bit(table_offset + bao->cfg.layer_sample_rate, sf); + bao->stream_type = read_32bit(table_offset + bao->cfg.layer_stream_type, sf); + bao->num_samples = read_32bit(table_offset + bao->cfg.layer_num_samples, sf); for (i = 0; i < bao->layer_count; i++) { - int channels = read_32bit(table_offset + bao->cfg.layer_channels, streamFile); - int sample_rate = read_32bit(table_offset + bao->cfg.layer_sample_rate, streamFile); - int stream_type = read_32bit(table_offset + bao->cfg.layer_stream_type, streamFile); - int num_samples = read_32bit(table_offset + bao->cfg.layer_num_samples, streamFile); + int channels = read_32bit(table_offset + bao->cfg.layer_channels, sf); + int sample_rate = read_32bit(table_offset + bao->cfg.layer_sample_rate, sf); + int stream_type = read_32bit(table_offset + bao->cfg.layer_stream_type, sf); + int num_samples = read_32bit(table_offset + bao->cfg.layer_num_samples, sf); if (bao->sample_rate != sample_rate || bao->stream_type != stream_type) { VGM_LOG("UBI BAO: layer headers don't match at %x\n", (uint32_t)table_offset); @@ -1010,7 +1010,7 @@ fail: return 0; } -static int parse_type_silence(ubi_bao_header * bao, off_t offset, STREAMFILE* streamFile) { +static int parse_type_silence(ubi_bao_header* bao, off_t offset, STREAMFILE* sf) { float (*read_f32)(off_t,STREAMFILE*) = bao->big_endian ? read_f32be : read_f32le; off_t h_offset = offset + bao->header_skip; @@ -1021,7 +1021,7 @@ static int parse_type_silence(ubi_bao_header * bao, off_t offset, STREAMFILE* st goto fail; } - bao->duration = read_f32(h_offset + bao->cfg.silence_duration_float, streamFile); + bao->duration = read_f32(h_offset + bao->cfg.silence_duration_float, sf); if (bao->duration <= 0.0f) { VGM_LOG("UBI BAO: bad duration %f at %x\n", bao->duration, (uint32_t)offset); goto fail; @@ -1033,7 +1033,7 @@ fail: } /* adjust some common values */ -static int parse_values(ubi_bao_header * bao, STREAMFILE *streamFile) { +static int parse_values(ubi_bao_header* bao, STREAMFILE* sf) { if (bao->type == UBI_SEQUENCE || bao->type == UBI_SILENCE) return 1; @@ -1091,7 +1091,7 @@ fail: /* set actual offsets in various places */ -static int parse_offsets(ubi_bao_header * bao, STREAMFILE *streamFile) { +static int parse_offsets(ubi_bao_header* bao, STREAMFILE* sf) { off_t bao_offset; size_t bao_size; @@ -1126,7 +1126,7 @@ static int parse_offsets(ubi_bao_header * bao, STREAMFILE *streamFile) { } else { if (bao->is_prefetched) { - if (!find_package_bao(bao->prefetch_id, streamFile, &bao_offset, &bao_size)) { + if (!find_package_bao(bao->prefetch_id, sf, &bao_offset, &bao_size)) { VGM_LOG("UBI BAO: expected prefetch id %08x not found\n", bao->prefetch_id); goto fail; } @@ -1141,22 +1141,22 @@ static int parse_offsets(ubi_bao_header * bao, STREAMFILE *streamFile) { if (bao->is_external) { int i; off_t offset; - off_t resources_offset = read_32bitLE(0x08, streamFile); - int resources_count = read_32bitLE(resources_offset+0x00, streamFile); - size_t strings_size = read_32bitLE(resources_offset+0x04, streamFile); + off_t resources_offset = read_32bitLE(0x08, sf); + int resources_count = read_32bitLE(resources_offset+0x00, sf); + size_t strings_size = read_32bitLE(resources_offset+0x04, sf); /* parse resource table to external stream (may be empty, or exist even with nothing in the file) */ offset = resources_offset + 0x04+0x04 + strings_size; for (i = 0; i < resources_count; i++) { - uint32_t resource_id = read_32bitLE(offset+0x10*i+0x00, streamFile); - off_t name_offset = read_32bitLE(offset+0x10*i+0x04, streamFile); - off_t resource_offset = read_32bitLE(offset+0x10*i+0x08, streamFile); - size_t resource_size = read_32bitLE(offset+0x10*i+0x0c, streamFile); + uint32_t resource_id = read_32bitLE(offset+0x10*i+0x00, sf); + off_t name_offset = read_32bitLE(offset+0x10*i+0x04, sf); + off_t resource_offset = read_32bitLE(offset+0x10*i+0x08, sf); + size_t resource_size = read_32bitLE(offset+0x10*i+0x0c, sf); if (resource_id == bao->stream_id) { bao->stream_offset = resource_offset + bao->stream_skip; - read_string(bao->resource_name,255, resources_offset + 0x04+0x04 + name_offset, streamFile); + read_string(bao->resource_name,255, resources_offset + 0x04+0x04 + name_offset, sf); if (bao->stream_size != resource_size - bao->stream_skip + bao->prefetch_size) { VGM_ASSERT(bao->stream_size != resource_size - bao->stream_skip + bao->prefetch_size, @@ -1173,7 +1173,7 @@ static int parse_offsets(ubi_bao_header * bao, STREAMFILE *streamFile) { } } else { - if (!find_package_bao(bao->stream_id, streamFile, &bao_offset, &bao_size)) { + if (!find_package_bao(bao->stream_id, sf, &bao_offset, &bao_size)) { VGM_LOG("UBI BAO: expected internal id %08x not found\n", bao->stream_id); goto fail; } @@ -1202,13 +1202,13 @@ fail: } /* parse a single known header resource at offset (see config_bao for info) */ -static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) { +static int parse_header(ubi_bao_header* bao, STREAMFILE* sf, off_t offset) { int32_t (*read_32bit)(off_t,STREAMFILE*) = bao->big_endian ? read_32bitBE : read_32bitLE; bao->header_offset = offset; - bao->header_format = read_8bit (offset + 0x00, streamFile); /* 0x01: atomic, 0x02: package */ - bao->header_version = read_32bitBE(offset + 0x00, streamFile) & 0x00FFFFFF; + bao->header_format = read_8bit (offset + 0x00, sf); /* 0x01: atomic, 0x02: package */ + bao->header_version = read_32bitBE(offset + 0x00, sf) & 0x00FFFFFF; if (bao->version != bao->header_version) { VGM_LOG("UBI BAO: mismatched header version at %x\n", (uint32_t)offset); goto fail; @@ -1229,8 +1229,8 @@ static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offs * 0x1c: fixed value? */ bao->header_skip = bao->cfg.header_skip; - bao->header_id = read_32bit(offset + bao->header_skip + 0x00, streamFile); - bao->header_type = read_32bit(offset + bao->header_skip + 0x04, streamFile); + bao->header_id = read_32bit(offset + bao->header_skip + 0x00, sf); + bao->header_type = read_32bit(offset + bao->header_skip + 0x04, sf); bao->header_size = bao->cfg.header_base_size; @@ -1241,9 +1241,9 @@ static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offs } /* detect extra unused field in PC/Wii * (could be improved but no apparent flags or anything useful) */ - else if (get_streamfile_size(streamFile) > offset + bao->header_size) { + else if (get_streamfile_size(sf) > offset + bao->header_size) { /* may read next BAO version, layer header, cues, resource table size, etc, always > 1 */ - int32_t end_field = read_32bit(offset + bao->header_size, streamFile); + int32_t end_field = read_32bit(offset + bao->header_size, sf); if (end_field == -1 || end_field == 0 || end_field == 1) /* some count? */ bao->header_size += 0x04; @@ -1251,19 +1251,19 @@ static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offs switch(bao->header_type) { case 0x01: - if (!parse_type_audio(bao, offset, streamFile)) + if (!parse_type_audio(bao, offset, sf)) goto fail; break; case 0x05: - if (!parse_type_sequence(bao, offset, streamFile)) + if (!parse_type_sequence(bao, offset, sf)) goto fail; break; case 0x06: - if (!parse_type_layer(bao, offset, streamFile)) + if (!parse_type_layer(bao, offset, sf)) goto fail; break; case 0x08: - if (!parse_type_silence(bao, offset, streamFile)) + if (!parse_type_silence(bao, offset, sf)) goto fail; break; default: @@ -1271,10 +1271,10 @@ static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offs goto fail; } - if (!parse_values(bao, streamFile)) + if (!parse_values(bao, sf)) goto fail; - if (!parse_offsets(bao, streamFile)) + if (!parse_offsets(bao, sf)) goto fail; return 1; @@ -1282,16 +1282,16 @@ fail: return 0; } -static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, int target_subsong) { +static int parse_bao(ubi_bao_header* bao, STREAMFILE* sf, off_t offset, int target_subsong) { int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; uint32_t bao_class, header_type; - /*bao_version =*/ read_32bitBE(offset+0x00, streamFile); /* force buffer read */ + /*bao_version =*/ read_32bitBE(offset+0x00, sf); /* force buffer read */ - config_bao_endian(bao, offset, streamFile); + config_bao_endian(bao, offset, sf); read_32bit = bao->big_endian ? read_32bitBE : read_32bitLE; - bao_class = read_32bit(offset+bao->cfg.bao_class, streamFile); + bao_class = read_32bit(offset+bao->cfg.bao_class, sf); if (bao_class & 0x0FFFFFFF) { VGM_LOG("UBI BAO: unknown class %x at %x\n", bao_class, (uint32_t)offset); goto fail; @@ -1301,7 +1301,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, if (bao_class != 0x20000000) /* ignore non-header classes */ return 1; - header_type = read_32bit(offset + bao->cfg.header_skip + 0x04, streamFile); + header_type = read_32bit(offset + bao->cfg.header_skip + 0x04, sf); if (header_type > 9) { VGM_LOG("UBI BAO: unknown type %x at %x\n", header_type, (uint32_t)offset); goto fail; @@ -1317,7 +1317,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, if (target_subsong != bao->total_subsongs) return 1; - if (!parse_header(bao, streamFile, offset)) + if (!parse_header(bao, sf, offset)) goto fail; return 1; @@ -1328,8 +1328,8 @@ fail: /* ************************************************************************* */ /* opens a file BAO's companion BAO (memory or stream) */ -static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE *streamFile) { - STREAMFILE *streamBAO = NULL; +static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE* sf) { + STREAMFILE* sf_bao = NULL; char buf[255]; size_t buf_size = 255; @@ -1342,63 +1342,63 @@ static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, in * .forge data can be uncompressed (stream BAOs) and compressed (subfiles per area with memory BAOs). */ if (is_stream) { snprintf(buf,buf_size, "Common_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; strcat(buf,".sbao"); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "English_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "French_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Spanish_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "German_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Italian_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Japanese_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Korean_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Russian_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Czech_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Polish_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; /* these are all of the languages that were referenced in Assassin's Creed exe (out of each platform), there may be more */ } else { snprintf(buf,buf_size, "BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; strcat(buf,".bao"); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; } goto fail; @@ -1407,33 +1407,33 @@ static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, in goto fail; } - return streamBAO; /* may be NULL */ + return sf_bao; /* may be NULL */ fail: - close_streamfile(streamBAO); + close_streamfile(sf_bao); VGM_LOG("UBI BAO: failed opening atomic BAO id %08x\n", file_id); return NULL; } -static int find_package_bao(uint32_t target_id, STREAMFILE *streamFile, off_t *out_offset, size_t *out_size) { +static int find_package_bao(uint32_t target_id, STREAMFILE* sf, off_t* p_offset, size_t* p_size) { int i; int index_entries; off_t bao_offset; size_t index_size, index_header_size; - index_size = read_32bitLE(0x04, streamFile); + index_size = read_32bitLE(0x04, sf); index_entries = index_size / 0x08; index_header_size = 0x40; /* parse index to get target BAO */ bao_offset = index_header_size + index_size; for (i = 0; i < index_entries; i++) { - uint32_t bao_id = read_32bitLE(index_header_size + 0x08*i + 0x00, streamFile); - size_t bao_size = read_32bitLE(index_header_size + 0x08*i + 0x04, streamFile); + uint32_t bao_id = read_32bitLE(index_header_size + 0x08*i + 0x00, sf); + size_t bao_size = read_32bitLE(index_header_size + 0x08*i + 0x04, sf); if (bao_id == target_id) { - if (out_offset) *out_offset = bao_offset; - if (out_size) *out_size = bao_size; + if (p_offset) *p_offset = bao_offset; + if (p_size) *p_size = bao_size; return 1; } bao_offset += bao_size; @@ -1444,10 +1444,10 @@ static int find_package_bao(uint32_t target_id, STREAMFILE *streamFile, off_t *o /* create a usable streamfile */ -static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *streamFile) { - STREAMFILE *new_streamFile = NULL; - STREAMFILE *temp_streamFile = NULL; - STREAMFILE *stream_segments[2] = { 0 }; +static STREAMFILE* setup_bao_streamfile(ubi_bao_header* bao, STREAMFILE* sf) { + STREAMFILE* new_sf = NULL; + STREAMFILE* temp_sf = NULL; + STREAMFILE* stream_segments[2] = { 0 }; /* Audio comes in "memory" and "streaming" BAOs. When "prefetched" flag is * on we need to join memory and streamed part as they're stored separately. @@ -1463,104 +1463,104 @@ static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *stream if (bao->is_atomic) { /* file BAOs re-open new STREAMFILEs so no need to wrap them */ if (bao->is_prefetched) { - new_streamFile = open_atomic_bao(bao->cfg.file_type, bao->prefetch_id, 0, streamFile); - if (!new_streamFile) goto fail; - stream_segments[0] = new_streamFile; + new_sf = open_atomic_bao(bao->cfg.file_type, bao->prefetch_id, 0, sf); + if (!new_sf) goto fail; + stream_segments[0] = new_sf; - new_streamFile = open_clamp_streamfile(stream_segments[0], bao->prefetch_offset, bao->prefetch_size); - if (!new_streamFile) goto fail; - stream_segments[0] = new_streamFile; + new_sf = open_clamp_streamfile(stream_segments[0], bao->prefetch_offset, bao->prefetch_size); + if (!new_sf) goto fail; + stream_segments[0] = new_sf; if (bao->stream_size - bao->prefetch_size != 0) { - new_streamFile = open_atomic_bao(bao->cfg.file_type, bao->stream_id, 1, streamFile); - if (!new_streamFile) goto fail; - stream_segments[1] = new_streamFile; + new_sf = open_atomic_bao(bao->cfg.file_type, bao->stream_id, 1, sf); + if (!new_sf) goto fail; + stream_segments[1] = new_sf; - new_streamFile = open_clamp_streamfile(stream_segments[1], bao->stream_offset, (bao->stream_size - bao->prefetch_size)); - if (!new_streamFile) goto fail; - stream_segments[1] = new_streamFile; + new_sf = open_clamp_streamfile(stream_segments[1], bao->stream_offset, (bao->stream_size - bao->prefetch_size)); + if (!new_sf) goto fail; + stream_segments[1] = new_sf; - new_streamFile = open_multifile_streamfile(stream_segments, 2); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_multifile_streamfile(stream_segments, 2); + if (!new_sf) goto fail; + temp_sf = new_sf; stream_segments[0] = NULL; stream_segments[1] = NULL; } else { /* weird but happens, streamed chunk is empty in this case */ - temp_streamFile = new_streamFile; + temp_sf = new_sf; stream_segments[0] = NULL; } } else { - new_streamFile = open_atomic_bao(bao->cfg.file_type, bao->stream_id, bao->is_external, streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_atomic_bao(bao->cfg.file_type, bao->stream_id, bao->is_external, sf); + if (!new_sf) goto fail; + temp_sf = new_sf; - new_streamFile = open_clamp_streamfile(temp_streamFile, bao->stream_offset, bao->stream_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_clamp_streamfile(temp_sf, bao->stream_offset, bao->stream_size); + if (!new_sf) goto fail; + temp_sf = new_sf; } } else { if (bao->is_prefetched) { - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - stream_segments[0] = new_streamFile; + new_sf = open_wrap_streamfile(sf); + if (!new_sf) goto fail; + stream_segments[0] = new_sf; - new_streamFile = open_clamp_streamfile(stream_segments[0], bao->prefetch_offset, bao->prefetch_size); - if (!new_streamFile) goto fail; - stream_segments[0] = new_streamFile; + new_sf = open_clamp_streamfile(stream_segments[0], bao->prefetch_offset, bao->prefetch_size); + if (!new_sf) goto fail; + stream_segments[0] = new_sf; if (bao->stream_size - bao->prefetch_size != 0) { - new_streamFile = open_streamfile_by_filename(streamFile, bao->resource_name); - if (!new_streamFile) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } - stream_segments[1] = new_streamFile; + new_sf = open_streamfile_by_filename(sf, bao->resource_name); + if (!new_sf) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } + stream_segments[1] = new_sf; - new_streamFile = open_clamp_streamfile(stream_segments[1], bao->stream_offset, (bao->stream_size - bao->prefetch_size)); - if (!new_streamFile) goto fail; - stream_segments[1] = new_streamFile; - temp_streamFile = NULL; + new_sf = open_clamp_streamfile(stream_segments[1], bao->stream_offset, (bao->stream_size - bao->prefetch_size)); + if (!new_sf) goto fail; + stream_segments[1] = new_sf; + temp_sf = NULL; - new_streamFile = open_multifile_streamfile(stream_segments, 2); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_multifile_streamfile(stream_segments, 2); + if (!new_sf) goto fail; + temp_sf = new_sf; stream_segments[0] = NULL; stream_segments[1] = NULL; } else { /* weird but happens, streamed chunk is empty in this case */ - temp_streamFile = new_streamFile; + temp_sf = new_sf; stream_segments[0] = NULL; } } else if (bao->is_external) { - new_streamFile = open_streamfile_by_filename(streamFile, bao->resource_name); - if (!new_streamFile) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } - temp_streamFile = new_streamFile; + new_sf = open_streamfile_by_filename(sf, bao->resource_name); + if (!new_sf) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } + temp_sf = new_sf; - new_streamFile = open_clamp_streamfile(temp_streamFile, bao->stream_offset, bao->stream_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_clamp_streamfile(temp_sf, bao->stream_offset, bao->stream_size); + if (!new_sf) goto fail; + temp_sf = new_sf; } else { - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_wrap_streamfile(sf); + if (!new_sf) goto fail; + temp_sf = new_sf; - new_streamFile = open_clamp_streamfile(temp_streamFile, bao->stream_offset, bao->stream_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_clamp_streamfile(temp_sf, bao->stream_offset, bao->stream_size); + if (!new_sf) goto fail; + temp_sf = new_sf; } } - return temp_streamFile; + return temp_sf; fail: close_streamfile(stream_segments[0]); close_streamfile(stream_segments[1]); - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); VGM_LOG("UBI BAO: failed streamfile setup\n"); return NULL; @@ -1568,23 +1568,23 @@ fail: /* ************************************************************************* */ -static void config_bao_endian(ubi_bao_header * bao, off_t offset, STREAMFILE *streamFile) { +static void config_bao_endian(ubi_bao_header* bao, off_t offset, STREAMFILE* sf) { /* Detect endianness using the 'class' field (the 'header skip' field is LE in early * versions, and was removed in later versions). * This could be done once as all BAOs share endianness */ /* negate as fields looks like LE (0xN0000000) */ - bao->big_endian = !guess_endianness32bit(offset+bao->cfg.bao_class, streamFile); + bao->big_endian = !guess_endianness32bit(offset+bao->cfg.bao_class, sf); } -static void config_bao_entry(ubi_bao_header * bao, size_t header_base_size, size_t header_skip) { +static void config_bao_entry(ubi_bao_header* bao, size_t header_base_size, size_t header_skip) { bao->cfg.header_base_size = header_base_size; bao->cfg.header_skip = header_skip; } -static void config_bao_audio_b(ubi_bao_header * bao, off_t stream_size, off_t stream_id, off_t external_flag, off_t loop_flag, int external_and, int loop_and) { +static void config_bao_audio_b(ubi_bao_header* bao, off_t stream_size, off_t stream_id, off_t external_flag, off_t loop_flag, int external_and, int loop_and) { /* audio header base */ bao->cfg.audio_stream_size = stream_size; bao->cfg.audio_stream_id = stream_id; @@ -1593,7 +1593,7 @@ static void config_bao_audio_b(ubi_bao_header * bao, off_t stream_size, off_t st bao->cfg.audio_external_and = external_and; bao->cfg.audio_loop_and = loop_and; } -static void config_bao_audio_m(ubi_bao_header * bao, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t stream_type, off_t prefetch_size) { +static void config_bao_audio_m(ubi_bao_header* bao, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t stream_type, off_t prefetch_size) { /* audio header main */ bao->cfg.audio_channels = channels; bao->cfg.audio_sample_rate = sample_rate; @@ -1605,7 +1605,7 @@ static void config_bao_audio_m(ubi_bao_header * bao, off_t channels, off_t sampl bao->cfg.audio_prefetch_size = prefetch_size; } -static void config_bao_sequence(ubi_bao_header * bao, off_t sequence_count, off_t sequence_single, off_t sequence_loop, off_t entry_size) { +static void config_bao_sequence(ubi_bao_header* bao, off_t sequence_count, off_t sequence_single, off_t sequence_loop, off_t entry_size) { /* sequence header and chain table */ bao->cfg.sequence_sequence_count = sequence_count; bao->cfg.sequence_sequence_single = sequence_single; @@ -1614,7 +1614,7 @@ static void config_bao_sequence(ubi_bao_header * bao, off_t sequence_count, off_ bao->cfg.sequence_entry_number = 0x00; } -static void config_bao_layer_m(ubi_bao_header * bao, off_t stream_id, off_t layer_count, off_t external_flag, off_t stream_size, off_t extra_size, off_t prefetch_size, off_t cue_count, off_t cue_labels, int external_and) { +static void config_bao_layer_m(ubi_bao_header* bao, off_t stream_id, off_t layer_count, off_t external_flag, off_t stream_size, off_t extra_size, off_t prefetch_size, off_t cue_count, off_t cue_labels, int external_and) { /* layer header in the main part */ bao->cfg.layer_stream_id = stream_id; bao->cfg.layer_layer_count = layer_count; @@ -1626,7 +1626,7 @@ static void config_bao_layer_m(ubi_bao_header * bao, off_t stream_id, off_t laye bao->cfg.layer_cue_labels = cue_labels; bao->cfg.layer_external_and = external_and; } -static void config_bao_layer_e(ubi_bao_header * bao, off_t entry_size, off_t sample_rate, off_t channels, off_t stream_type, off_t num_samples) { +static void config_bao_layer_e(ubi_bao_header* bao, off_t entry_size, off_t sample_rate, off_t channels, off_t stream_type, off_t num_samples) { /* layer sub-headers in extra table */ bao->cfg.layer_entry_size = entry_size; bao->cfg.layer_sample_rate = sample_rate; @@ -1635,13 +1635,14 @@ static void config_bao_layer_e(ubi_bao_header * bao, off_t entry_size, off_t sam bao->cfg.layer_num_samples = num_samples; } -static void config_bao_silence_f(ubi_bao_header * bao, off_t duration) { +static void config_bao_silence_f(ubi_bao_header* bao, off_t duration) { /* silence headers in float value */ bao->cfg.silence_duration_float = duration; } -static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { +static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf) { + uint32_t version; /* Ubi BAO evolved from Ubi SB and are conceptually quite similar, see that first. * @@ -1659,7 +1660,7 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { * - 0x60000000: unused? * - 0x70000000: info? has a count+table of id-things * - 0x80000000: unknown (some floats?) - * - 0x90000000: unknown (some kind of command config?), rare [Ghost Recon Future Soldier (PC)] + * - 0x90000000: unknown (some kind of command config?), rare [Ghost Recon Future Soldier (PC), Drawsome (Wii)] * Class 1/2/3 are roughly equivalent to Ubi SB's section1/2/3, and class 4 is * basically .spN project files. * @@ -1704,10 +1705,26 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { bao->cfg.header_id = 0x00; bao->cfg.header_type = 0x04; + version = bao->version; + + /* 2 configs with same ID, autodetect */ + if (version == 0x00220015) { + off_t header_size = 0x40 + read_32bitLE(0x04, sf); /* first is always LE */ + + /* next BAO uses machine endianness, entry should always exist + * (maybe should use project BAO to detect?) */ + if (guess_endianness32bit(header_size + 0x04, sf)) { + version |= 0xFF00; /* signal Wii=BE, but don't modify original */ + } + } + + /* config per version*/ - switch(bao->version) { + switch(version) { case 0x001B0100: /* Assassin's Creed (PS3/X360/PC)-atomic-forge */ + //case 0x001B0100: /* My Fitness Coach (Wii)-atomic-forge */ + //case 0x001B0100: /* Your Shape featuring Jenny McCarthy (Wii)-atomic-forge */ config_bao_entry(bao, 0xA4, 0x28); /* PC: 0xA8, PS3/X360: 0xA4 */ config_bao_audio_b(bao, 0x08, 0x1c, 0x28, 0x34, 1, 1); /* 0x2c: prefetch flag? */ @@ -1735,6 +1752,7 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { case 0x001F0011: /* Naruto: The Broken Bond (X360)-package */ case 0x0021000C: /* Splinter Cell: Conviction (E3 2009 Demo)(X360)-package */ case 0x0022000D: /* Just Dance (Wii)-package */ + case 0x0022FF15: /* James Cameron's Avatar: The Game (Wii)-package */ case 0x0022001B: /* Prince of Persia: The Forgotten Sands (Wii)-package */ config_bao_entry(bao, 0xA4, 0x28); /* PC/Wii: 0xA8 */ @@ -1826,7 +1844,19 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { bao->cfg.file_type = UBI_FORGE_b; return 1; - case 0x00280303: /* Tom Clancy's Ghost Recon Future Soldier (PC/PS3)-pk */ + case 0x00270102: /* Drawsome (Wii)-package */ + config_bao_entry(bao, 0xAC, 0x28); + + config_bao_audio_b(bao, 0x08, 0x28, 0x2c, 0x38, 1, 1); + config_bao_audio_m(bao, 0x44, 0x4c, 0x54, 0x5c, 0x64, 0x70); + + config_bao_sequence(bao, 0x38, 0x2c, 0x28, 0x14); + + bao->cfg.codec_map[0x02] = UBI_IMA; + + return 1; + + case 0x00280303: /* Tom Clancy's Ghost Recon Future Soldier (PC/PS3)-package */ config_bao_entry(bao, 0xBC, 0x28); /* PC/PS3: 0xBC */ config_bao_audio_b(bao, 0x08, 0x38, 0x3c, 0x48, 1, 1); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c index 4e8200b84..ceafaa04b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c @@ -154,7 +154,6 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) { #ifdef VGM_USE_MPEG case 0x5051: { /* MPEG (PS3/PC), interleaved 1ch */ - mpeg_codec_data *mpeg_data = NULL; mpeg_custom_config cfg = {0}; int i; @@ -175,9 +174,8 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) { cfg.data_size = data_size; //todo data parsing looks correct but some files decode a bit wrong at the end (ex. Tintin: Music~Boss~Allan~Victory~02) - mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_LYN, &cfg); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; + vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_LYN, &cfg); + if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index 540537f6f..37ddc39de 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -1116,7 +1116,6 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h // Probably a beta/custom encoder that creates some buggy frames, that a real X360 handles ok, but trips FFmpeg // xmaencode decodes correctly if counters are fixed (otherwise has clicks on every frame). case FMT_XMA1: { - ffmpeg_codec_data *ffmpeg_data; uint8_t buf[0x100]; uint32_t sec1_num, sec2_num, sec3_num, bits_per_frame; uint8_t flag; @@ -1147,9 +1146,8 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, header_offset, chunk_size, data_size, sf_data, 1); - ffmpeg_data = init_ffmpeg_header_offset(sf_data, buf, bytes, start_offset, data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(sf_data, buf, bytes, start_offset, data_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -1158,7 +1156,6 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h } case RAW_XMA1: { - ffmpeg_codec_data *ffmpeg_data; uint8_t buf[0x100]; size_t bytes, chunk_size; off_t header_offset; @@ -1173,9 +1170,8 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, header_offset, chunk_size, sb->stream_size, sf_head, 1); - ffmpeg_data = init_ffmpeg_header_offset(sf_data, buf, bytes, start_offset, sb->stream_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(sf_data, buf, bytes, start_offset, sb->stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -3844,22 +3840,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } - /* TMNT (2007)(GC)-bank */ - if (sb->version == 0x00190002 && sb->platform == UBI_GC) { - config_sb_entry(sb, 0x68, 0x6c); - - config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); /* assumed groud_id */ - config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c); - - config_sb_sequence(sb, 0x2c, 0x14); - - config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40); - config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); - - config_sb_silence_f(sb, 0x1c); - return 1; - } - /* TMNT (2007)(PS2)-bank */ if (sb->version == 0x00190002 && sb->platform == UBI_PS2) { config_sb_entry(sb, 0x48, 0x5c); @@ -3876,6 +3856,24 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } + /* TMNT (2007)(GC)-bank */ + /* Surf's Up (2007)(GC)-bank 0x00190005 */ + if ((sb->version == 0x00190002 && sb->platform == UBI_GC) || + (sb->version == 0x00190005 && sb->platform == UBI_GC)) { + config_sb_entry(sb, 0x68, 0x6c); + + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); /* assumed groud_id */ + config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c); + + config_sb_sequence(sb, 0x2c, 0x14); + + config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); + + config_sb_silence_f(sb, 0x1c); + return 1; + } + /* TMNT (2007)(X360)-bank 0x00190002 */ /* My Word Coach (2007)(Wii)-bank 0x00190002 */ /* Prince of Persia: Rival Swords (2007)(Wii)-bank 0x00190003 */ @@ -3974,8 +3972,13 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } + /* Petz Sports: Dog Playground (2008)(Wii)-bank */ + /* Cloudy with a Chance of Meatballs (2009)(Wii)-bank */ + /* Grey's Anatomy: The Video Game (2009)(Wii)-bank */ + /* NCIS: Based on the TV Series (2011)(Wii)-bank */ /* Splinter Cell Classic Trilogy HD (2011)(PS3)-map */ - if (sb->version == 0x001D0000 && sb->platform == UBI_PS3) { + if ((sb->version == 0x001D0000 && sb->platform == UBI_PS3) || + (sb->version == 0x001D0000 && sb->platform == UBI_WII)) { config_sb_entry(sb, 0x5c, 0x80); sb->cfg.audio_interleave = 0x10; sb->cfg.audio_fix_psx_samples = 1; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vag.c b/Frameworks/vgmstream/vgmstream/src/meta/vag.c index eba4bdb55..f248b1538 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vag.c @@ -20,9 +20,8 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { * .str: Ben10 Galactic Racing * .vig: MX vs. ATV Untamed (PS2) * .l/r: Crash Nitro Kart (PS2), Gradius V (PS2) - * .vas: Kingdom Hearts II (PS2) - * .khv: fake for .vas */ - if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r,vas,khv") ) + * .vas: Kingdom Hearts II (PS2) */ + if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r,vas") ) goto fail; /* check VAG Header */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vawx.c b/Frameworks/vgmstream/vgmstream/src/meta/vawx.c index fceddac19..ef40a9802 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vawx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vawx.c @@ -1,107 +1,105 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../coding/coding.h" - - -/* VAWX - found in feelplus games [No More Heroes: Heroes Paradise (PS3/X360), Moon Diver (PS3/X360)] */ -VGMSTREAM * init_vgmstream_vawx(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset, data_size; - int loop_flag = 0, channel_count, codec; - - - /* checks */ - /* .xwv: actual extension [Moon Diver (PS3/X360)] - * .vawx: header id */ - if ( !check_extensions(streamFile, "xwv,vawx") ) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x56415758) /* "VAWX" */ - goto fail; - - loop_flag = read_8bit(0x37,streamFile); - channel_count = read_8bit(0x39,streamFile); - start_offset = 0x800; /* ? read_32bitLE(0x0c,streamFile); */ - codec = read_8bit(0x36,streamFile); /* could be at 0x38 too */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* 0x04: filesize */ - /* 0x16: file id */ - vgmstream->num_samples = read_32bitBE(0x3c,streamFile); - vgmstream->sample_rate = read_32bitBE(0x40,streamFile); - - vgmstream->meta_type = meta_VAWX; - - switch(codec) { - case 2: /* PS-ADPCM */ - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = channel_count == 6 ? layout_blocked_vawx : layout_interleave; - vgmstream->interleave_block_size = 0x10; - - vgmstream->loop_start_sample = read_32bitBE(0x44,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x48,streamFile); - - break; - -#ifdef VGM_USE_FFMPEG - case 1: { /* XMA2 */ - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[0x100]; - int32_t bytes, block_size, block_count; - - data_size = get_streamfile_size(streamFile)-start_offset; - block_size = 0x10000; /* VAWX default */ - block_count = (uint16_t)read_16bitBE(0x3A, streamFile); /* also at 0x56 */ - - bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - vgmstream->loop_start_sample = read_32bitBE(0x44,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x48,streamFile); - - //todo fix loops/samples vs ATRAC3 - /* may be only applying end_skip to num_samples? */ - xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); - break; - } - - case 7: { /* ATRAC3 */ - int block_align, encoder_delay; - - data_size = read_32bitBE(0x54,streamFile); - block_align = 0x98 * vgmstream->channels; - encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ - vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; /* original samples break looping in some files otherwise */ - - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ - vgmstream->loop_start_sample = atrac3_bytes_to_samples(read_32bitBE(0x44,streamFile), block_align); //- encoder_delay - vgmstream->loop_end_sample = atrac3_bytes_to_samples(read_32bitBE(0x48,streamFile), block_align) - encoder_delay; - break; - } -#endif - default: - goto fail; - - } - - - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + + +/* VAWX - found in feelplus games [No More Heroes: Heroes Paradise (PS3/X360), Moon Diver (PS3/X360)] */ +VGMSTREAM* init_vgmstream_vawx(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, data_size; + int loop_flag = 0, channel_count, codec; + + + /* checks */ + /* .xwv: actual extension [Moon Diver (PS3/X360)] + * .vawx: header id */ + if ( !check_extensions(sf, "xwv,vawx") ) + goto fail; + if (read_32bitBE(0x00,sf) != 0x56415758) /* "VAWX" */ + goto fail; + + loop_flag = read_8bit(0x37,sf); + channel_count = read_8bit(0x39,sf); + start_offset = 0x800; /* ? read_32bitLE(0x0c,sf); */ + codec = read_8bit(0x36,sf); /* could be at 0x38 too */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + /* 0x04: filesize */ + /* 0x16: file id */ + vgmstream->num_samples = read_32bitBE(0x3c,sf); + vgmstream->sample_rate = read_32bitBE(0x40,sf); + + vgmstream->meta_type = meta_VAWX; + + switch(codec) { + case 2: /* PS-ADPCM */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = channel_count == 6 ? layout_blocked_vawx : layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->loop_start_sample = read_32bitBE(0x44,sf); + vgmstream->loop_end_sample = read_32bitBE(0x48,sf); + + break; + +#ifdef VGM_USE_FFMPEG + case 1: { /* XMA2 */ + uint8_t buf[0x100]; + int32_t bytes, block_size, block_count; + + data_size = get_streamfile_size(sf)-start_offset; + block_size = 0x10000; /* VAWX default */ + block_count = (uint16_t)read_16bitBE(0x3A, sf); /* also at 0x56 */ + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->loop_start_sample = read_32bitBE(0x44,sf); + vgmstream->loop_end_sample = read_32bitBE(0x48,sf); + + //todo fix loops/samples vs ATRAC3 + /* may be only applying end_skip to num_samples? */ + xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, 0,0); + break; + } + + case 7: { /* ATRAC3 */ + int block_align, encoder_delay; + + data_size = read_32bitBE(0x54,sf); + block_align = 0x98 * vgmstream->channels; + encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ + vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; /* original samples break looping in some files otherwise */ + + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ + vgmstream->loop_start_sample = atrac3_bytes_to_samples(read_32bitBE(0x44,sf), block_align); //- encoder_delay + vgmstream->loop_end_sample = atrac3_bytes_to_samples(read_32bitBE(0x48,sf), block_align) - encoder_delay; + break; + } +#endif + default: + goto fail; + + } + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index 983e922e5..c545054af 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -225,8 +225,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { goto fail; } - if (ww.codec == PCM || ww.codec == IMA || ww.codec == DSP || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX || ww.codec == OPUS) { - ww.truncated = 1; /* only seen those, probably all exist */ + if (ww.codec == PCM || ww.codec == IMA || ww.codec == VORBIS || ww.codec == DSP || ww.codec == XMA2 || + ww.codec == OPUSNX || ww.codec == OPUS || ww.codec == PTADPCM) { + ww.truncated = 1; /* only seen those, probably all exist (XWMA, AAC, HEVAG, ATRAC9?) */ } else { VGM_LOG("WWISE: wrong size, maybe truncated\n"); goto fail; @@ -373,7 +374,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { else { /* newer Wwise (>2012) */ off_t extra_offset = ww.fmt_offset + 0x18; /* after flag + channels */ - int is_wem = check_extensions(sf,"wem"); + int is_wem = check_extensions(sf,"wem,bnk"); /* use extension as a guide for faster vorbis inits */ switch(ww.extra_size) { case 0x30: @@ -382,7 +383,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { cfg.header_type = WWV_TYPE_2; cfg.packet_type = WWV_MODIFIED; - /* setup not detectable by header, so we'll try both; hopefully libvorbis will reject wrong codebooks + /* setup not detectable by header, so we'll try both; libvorbis should reject wrong codebooks * - standard: early (<2012), ex. The King of Fighters XIII (X360)-2011/11, .ogg (cbs are from aoTuV, too) * - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed (PC)-2012/11, .wem */ cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */ @@ -679,6 +680,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->interleave_block_size = ww.block_align / ww.channels; //vgmstream->codec_endian = ww.big_endian; //? + if (ww.truncated) { + ww.data_size = ww.file_size - ww.data_offset; + } + vgmstream->num_samples = ptadpcm_bytes_to_samples(ww.data_size, ww.channels, vgmstream->interleave_block_size); break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/x360_cxs.c b/Frameworks/vgmstream/vgmstream/src/meta/x360_cxs.c index 5e5df7280..eb080a4a3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/x360_cxs.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/x360_cxs.c @@ -1,66 +1,63 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* CXS - found in Eternal Sonata (X360) */ -VGMSTREAM * init_vgmstream_x360_cxs(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - - /* checks */ - if ( !check_extensions(streamFile,"cxs")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x43585320) /* "CXS " */ - goto fail; - - loop_flag = read_32bitBE(0x18,streamFile) > 0; - channel_count = read_32bitBE(0x0c,streamFile); - start_offset = read_32bitBE(0x04,streamFile) + read_32bitBE(0x28,streamFile); /* assumed, seek table always at 0x800 */ - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* 0x04: data start? */ - vgmstream->sample_rate = read_32bitBE(0x08,streamFile); - vgmstream->num_samples = read_32bitBE(0x10,streamFile); - vgmstream->loop_start_sample = read_32bitBE(0x14,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile); - /* 0x1c: below */ - - vgmstream->meta_type = meta_X360_CXS; - -#ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[100]; - size_t bytes, datasize, block_size, block_count; - - block_count = read_32bitBE(0x1c,streamFile); - block_size = read_32bitBE(0x20,streamFile); - datasize = read_32bitBE(0x24,streamFile); - - bytes = ffmpeg_make_riff_xma2(buf,100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - if (bytes <= 0) goto fail; - - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,datasize); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - xma_fix_raw_samples(vgmstream, streamFile, start_offset,datasize, 0, 0,1); /* num samples are ok */ - } -#else - goto fail; -#endif - - /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + +/* CXS - found in Eternal Sonata (X360) */ +VGMSTREAM* init_vgmstream_x360_cxs(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + /* checks */ + if ( !check_extensions(sf,"cxs")) + goto fail; + if (read_32bitBE(0x00,sf) != 0x43585320) /* "CXS " */ + goto fail; + + loop_flag = read_32bitBE(0x18,sf) > 0; + channel_count = read_32bitBE(0x0c,sf); + start_offset = read_32bitBE(0x04,sf) + read_32bitBE(0x28,sf); /* assumed, seek table always at 0x800 */ + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + /* 0x04: data start? */ + vgmstream->sample_rate = read_32bitBE(0x08,sf); + vgmstream->num_samples = read_32bitBE(0x10,sf); + vgmstream->loop_start_sample = read_32bitBE(0x14,sf); + vgmstream->loop_end_sample = read_32bitBE(0x18,sf); + /* 0x1c: below */ + + vgmstream->meta_type = meta_X360_CXS; + +#ifdef VGM_USE_FFMPEG + { + uint8_t buf[0x100]; + size_t bytes, datasize, block_size, block_count; + + block_count = read_32bitBE(0x1c,sf); + block_size = read_32bitBE(0x20,sf); + datasize = read_32bitBE(0x24,sf); + + bytes = ffmpeg_make_riff_xma2(buf,100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + if (bytes <= 0) goto fail; + + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,datasize); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, sf, start_offset,datasize, 0, 0,1); /* num samples are ok */ + } +#else + goto fail; +#endif + + if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c b/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c index 9573b6128..cf37a03ba 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c @@ -142,7 +142,6 @@ VGMSTREAM *init_vgmstream_xmv_valve(STREAMFILE *streamFile) { break; #ifdef VGM_USE_FFMPEG case 0x01: { /* XMA */ - ffmpeg_codec_data *ffmpeg_data; uint8_t buf[0x100]; int block_count, block_size; size_t bytes; @@ -152,10 +151,8 @@ VGMSTREAM *init_vgmstream_xmv_valve(STREAMFILE *streamFile) { bytes = ffmpeg_make_riff_xma2(buf, 0x100, num_samples, data_size, channels, sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, data_size); - if (!ffmpeg_data) goto fail; - - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, data_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->loop_end_sample -= loop_end_skip; @@ -166,16 +163,13 @@ VGMSTREAM *init_vgmstream_xmv_valve(STREAMFILE *streamFile) { #endif #ifdef VGM_USE_MPEG case 0x03: { /* MP3 */ - mpeg_codec_data *mpeg_data; coding_t mpeg_coding; if (loop_flag) /* should never happen, Source cannot loop MP3 */ goto fail; - mpeg_data = init_mpeg(streamFile, start_offset, &mpeg_coding, channels); - if (!mpeg_data) goto fail; - - vgmstream->codec_data = mpeg_data; + vgmstream->codec_data = init_mpeg(streamFile, start_offset, &mpeg_coding, channels); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = mpeg_coding; vgmstream->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 5fe169e31..36a62bcfd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -75,40 +75,43 @@ typedef struct { int fix_xma_loop_samples; } xwb_header; -static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile); +static void get_name(char* buf, size_t maxsize, int target_subsong, xwb_header* xwb, STREAMFILE* sf); /* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */ -VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_xwb(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset, offset, suboffset; xwb_header xwb = {0}; - int target_subsong = streamFile->stream_index; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int target_subsong = sf->stream_index; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; /* checks */ /* .xwb: standard * .xna: Touhou Makukasai ~ Fantasy Danmaku Festival (PC) * (extensionless): Ikaruga (X360/PC), Grabbed by the Ghoulies (Xbox) */ - if (!check_extensions(streamFile,"xwb,xna,")) + if (!check_extensions(sf,"xwb,xna,")) goto fail; - if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* "WBND" (LE) */ - (read_32bitBE(0x00,streamFile) != 0x444E4257)) /* "DNBW" (BE) */ + if ((read_u32be(0x00,sf) != 0x57424E44) && /* "WBND" (LE) */ + (read_u32be(0x00,sf) != 0x444E4257)) /* "DNBW" (BE) */ goto fail; - xwb.little_endian = read_32bitBE(0x00,streamFile) == 0x57424E44; /* WBND */ + xwb.little_endian = read_u32be(0x00,sf) == 0x57424E44; /* WBND */ if (xwb.little_endian) { - read_32bit = read_32bitLE; + read_u32 = read_u32le; + read_s32 = read_s32le; } else { - read_32bit = read_32bitBE; + read_u32 = read_u32be; + read_s32 = read_s32be; } /* read main header (WAVEBANKHEADER) */ - xwb.version = read_32bit(0x04, streamFile); /* XACT3: 0x04=tool version, 0x08=header version */ + xwb.version = read_u32(0x04, sf); /* XACT3: 0x04=tool version, 0x08=header version */ - /* Crackdown 1 (X360), essentially XACT2 but may have split header in some cases */ + /* Crackdown 1 (X360), essentially XACT2 but may have split header in some cases, compact entries change */ if (xwb.version == XACT_CRACKDOWN) { xwb.version = XACT2_2_MAX; xwb.is_crackdown = 1; @@ -116,15 +119,15 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* read segment offsets (SEGIDX) */ if (xwb.version <= XACT1_0_MAX) { - xwb.total_subsongs = read_32bit(0x0c, streamFile); - read_string(xwb.wavebank_name,0x10+1, 0x10, streamFile); /* null-terminated */ + xwb.total_subsongs = read_s32(0x0c, sf); + read_string(xwb.wavebank_name,0x10+1, 0x10, sf); /* null-terminated */ xwb.base_offset = 0; xwb.base_size = 0; xwb.entry_offset = 0x50; xwb.entry_elem_size = 0x14; xwb.entry_size = xwb.entry_elem_size * xwb.total_subsongs; xwb.data_offset = xwb.entry_offset + xwb.entry_size; - xwb.data_size = get_streamfile_size(streamFile) - xwb.data_offset; + xwb.data_size = get_streamfile_size(sf) - xwb.data_offset; xwb.names_offset = 0; xwb.names_size = 0; @@ -134,52 +137,52 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { } else { offset = xwb.version <= XACT2_2_MAX ? 0x08 : 0x0c; - xwb.base_offset = read_32bit(offset+0x00, streamFile);//BANKDATA - xwb.base_size = read_32bit(offset+0x04, streamFile); - xwb.entry_offset= read_32bit(offset+0x08, streamFile);//ENTRYMETADATA - xwb.entry_size = read_32bit(offset+0x0c, streamFile); + xwb.base_offset = read_s32(offset+0x00, sf);//BANKDATA + xwb.base_size = read_s32(offset+0x04, sf); + xwb.entry_offset= read_s32(offset+0x08, sf);//ENTRYMETADATA + xwb.entry_size = read_s32(offset+0x0c, sf); /* read extra segments (values can be 0 == no segment) */ if (xwb.version <= XACT1_1_MAX) { - xwb.names_offset = read_32bit(offset+0x10, streamFile);//ENTRYNAMES - xwb.names_size = read_32bit(offset+0x14, streamFile); + xwb.names_offset = read_s32(offset+0x10, sf);//ENTRYNAMES + xwb.names_size = read_s32(offset+0x14, sf); xwb.names_entry_size= 0x40; xwb.extra_offset = 0; xwb.extra_size = 0; suboffset = 0x04*2; } else if (xwb.version <= XACT2_1_MAX) { - xwb.names_offset = read_32bit(offset+0x10, streamFile);//ENTRYNAMES - xwb.names_size = read_32bit(offset+0x14, streamFile); + xwb.names_offset = read_s32(offset+0x10, sf);//ENTRYNAMES + xwb.names_size = read_s32(offset+0x14, sf); xwb.names_entry_size= 0x40; - xwb.extra_offset = read_32bit(offset+0x18, streamFile);//EXTRA - xwb.extra_size = read_32bit(offset+0x1c, streamFile); + xwb.extra_offset = read_s32(offset+0x18, sf);//EXTRA + xwb.extra_size = read_s32(offset+0x1c, sf); suboffset = 0x04*2 + 0x04*2; } else { - xwb.extra_offset = read_32bit(offset+0x10, streamFile);//SEEKTABLES - xwb.extra_size = read_32bit(offset+0x14, streamFile); - xwb.names_offset = read_32bit(offset+0x18, streamFile);//ENTRYNAMES - xwb.names_size = read_32bit(offset+0x1c, streamFile); + xwb.extra_offset = read_s32(offset+0x10, sf);//SEEKTABLES + xwb.extra_size = read_s32(offset+0x14, sf); + xwb.names_offset = read_s32(offset+0x18, sf);//ENTRYNAMES + xwb.names_size = read_s32(offset+0x1c, sf); xwb.names_entry_size= 0x40; suboffset = 0x04*2 + 0x04*2; } - xwb.data_offset = read_32bit(offset+0x10+suboffset, streamFile);//ENTRYWAVEDATA - xwb.data_size = read_32bit(offset+0x14+suboffset, streamFile); + xwb.data_offset = read_s32(offset+0x10+suboffset, sf);//ENTRYWAVEDATA + xwb.data_size = read_s32(offset+0x14+suboffset, sf); /* for Techland's XWB with no data */ if (xwb.base_offset == 0) goto fail; /* read base entry (WAVEBANKDATA) */ offset = xwb.base_offset; - xwb.base_flags = (uint32_t)read_32bit(offset+0x00, streamFile); - xwb.total_subsongs = read_32bit(offset+0x04, streamFile); - read_string(xwb.wavebank_name,0x40+1, offset+0x08, streamFile); /* null-terminated */ + xwb.base_flags = read_u32(offset+0x00, sf); + xwb.total_subsongs = read_s32(offset+0x04, sf); + read_string(xwb.wavebank_name,0x40+1, offset+0x08, sf); /* null-terminated */ suboffset = 0x08 + (xwb.version <= XACT1_1_MAX ? 0x10 : 0x40); - xwb.entry_elem_size = read_32bit(offset+suboffset+0x00, streamFile); + xwb.entry_elem_size = read_s32(offset+suboffset+0x00, sf); /* suboff+0x04: meta name entry size */ - xwb.entry_alignment = read_32bit(offset+suboffset+0x08, streamFile); /* usually 1 dvd sector */ - xwb.format = read_32bit(offset+suboffset+0x0c, streamFile); /* compact mode only */ + xwb.entry_alignment = read_s32(offset+suboffset+0x08, sf); /* usually 1 dvd sector */ + xwb.format = read_s32(offset+suboffset+0x0c, sf); /* compact mode only */ /* suboff+0x10: build time 64b (XACT2/3) */ } @@ -192,20 +195,34 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* read stream entry (WAVEBANKENTRY) */ offset = xwb.entry_offset + (target_subsong-1) * xwb.entry_elem_size; - if (xwb.base_flags & WAVEBANK_FLAGS_COMPACT) { /* compact entry [NFL Fever 2004 demo from Amped 2 (Xbox)] */ + + if ((xwb.base_flags & WAVEBANK_FLAGS_COMPACT) && xwb.is_crackdown) { + /* mutant compact (w/ entry_elem_size=0x08) [Crackdown (X360)] */ + uint32_t entry, size_sectors, sector_offset; + + entry = read_u32(offset+0x00, sf); + size_sectors = ((entry >> 19) & 0x1FFF); /* 13b, exact size in sectors */ + sector_offset = (entry & 0x7FFFF); /* 19b, offset within data in sectors */ + xwb.stream_size = size_sectors * xwb.entry_alignment; + xwb.num_samples = read_u32(offset+0x04, sf); + + xwb.stream_offset = xwb.data_offset + sector_offset * xwb.entry_alignment; + } + else if (xwb.base_flags & WAVEBANK_FLAGS_COMPACT) { + /* compact entry [NFL Fever 2004 demo from Amped 2 (Xbox)] */ uint32_t entry, size_deviation, sector_offset; off_t next_stream_offset; - entry = (uint32_t)read_32bit(offset+0x00, streamFile); + entry = read_u32(offset+0x00, sf); size_deviation = ((entry >> 21) & 0x7FF); /* 11b, padding data for sector alignment in bytes*/ sector_offset = (entry & 0x1FFFFF); /* 21b, offset within data in sectors */ - xwb.stream_offset = xwb.data_offset + sector_offset*xwb.entry_alignment; + xwb.stream_offset = xwb.data_offset + sector_offset * xwb.entry_alignment; /* find size using next offset */ if (target_subsong < xwb.total_subsongs) { - uint32_t next_entry = (uint32_t)read_32bit(offset+0x04, streamFile); - next_stream_offset = xwb.data_offset + (next_entry & 0x1FFFFF)*xwb.entry_alignment; + uint32_t next_entry = read_u32(offset + xwb.entry_elem_size, sf); + next_stream_offset = xwb.data_offset + (next_entry & 0x1FFFFF) * xwb.entry_alignment; } else { /* for last entry (or first, when subsongs = 1) */ next_stream_offset = xwb.data_offset + xwb.data_size; @@ -213,31 +230,31 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { xwb.stream_size = next_stream_offset - xwb.stream_offset - size_deviation; } else if (xwb.version <= XACT1_0_MAX) { - xwb.format = (uint32_t)read_32bit(offset+0x00, streamFile); - xwb.stream_offset = xwb.data_offset + (uint32_t)read_32bit(offset+0x04, streamFile); - xwb.stream_size = (uint32_t)read_32bit(offset+0x08, streamFile); + xwb.format = read_u32(offset+0x00, sf); + xwb.stream_offset = xwb.data_offset + read_u32(offset+0x04, sf); + xwb.stream_size = read_u32(offset+0x08, sf); - xwb.loop_start = (uint32_t)read_32bit(offset+0x0c, streamFile); - xwb.loop_end = (uint32_t)read_32bit(offset+0x10, streamFile);//length + xwb.loop_start = read_u32(offset+0x0c, sf); + xwb.loop_end = read_u32(offset+0x10, sf);//length } else { - uint32_t entry_info = (uint32_t)read_32bit(offset+0x00, streamFile); + uint32_t entry_info = read_u32(offset+0x00, sf); if (xwb.version <= XACT1_1_MAX) { xwb.entry_flags = entry_info; } else { xwb.entry_flags = (entry_info) & 0xF; /*4b*/ xwb.num_samples = (entry_info >> 4) & 0x0FFFFFFF; /*28b*/ } - xwb.format = (uint32_t)read_32bit(offset+0x04, streamFile); - xwb.stream_offset = xwb.data_offset + (uint32_t)read_32bit(offset+0x08, streamFile); - xwb.stream_size = (uint32_t)read_32bit(offset+0x0c, streamFile); + xwb.format = read_u32(offset+0x04, sf); + xwb.stream_offset = xwb.data_offset + read_u32(offset+0x08, sf); + xwb.stream_size = read_u32(offset+0x0c, sf); if (xwb.version <= XACT2_1_MAX) { /* LoopRegion (bytes) */ - xwb.loop_start = (uint32_t)read_32bit(offset+0x10, streamFile); - xwb.loop_end = (uint32_t)read_32bit(offset+0x14, streamFile);//length (LoopRegion) or offset (XMALoopRegion in late XACT2) + xwb.loop_start = read_u32(offset+0x10, sf); + xwb.loop_end = read_u32(offset+0x14, sf);//length (LoopRegion) or offset (XMALoopRegion in late XACT2) } else { /* LoopRegion (samples) */ - xwb.loop_start_sample = (uint32_t)read_32bit(offset+0x10, streamFile); - xwb.loop_end_sample = (uint32_t)read_32bit(offset+0x14, streamFile) + xwb.loop_start_sample; + xwb.loop_start_sample = read_u32(offset+0x10, sf); + xwb.loop_end_sample = read_u32(offset+0x14, sf) + xwb.loop_start_sample; } } @@ -326,9 +343,9 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { } else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2 && xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04 - && read_32bitLE(xwb.stream_offset + 0x08, streamFile) == xwb.sample_rate /* DSP header */ - && read_16bitLE(xwb.stream_offset + 0x0e, streamFile) == 0 - && read_32bitLE(xwb.stream_offset + 0x18, streamFile) == 2 + && read_u32le(xwb.stream_offset + 0x08, sf) == xwb.sample_rate /* DSP header */ + && read_u16le(xwb.stream_offset + 0x0e, sf) == 0 + && read_u32le(xwb.stream_offset + 0x18, sf) == 2 /*&& xwb.data_size == 0x55951c1c*/) { /* some kind of id in Stardew Valley? */ /* Stardew Valley (Switch), Skulls of the Shogun (Switch): full interleaved DSPs (including headers) */ xwb.codec = DSP; @@ -348,9 +365,9 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* Oddworld OGG the data_size value is size of uncompressed bytes instead; DSP uses some id/config as value */ if (xwb.codec != OGG && xwb.codec != DSP && xwb.codec != ATRAC9_RIFF) { /* some low-q rips don't remove padding, relax validation a bit */ - if (xwb.data_offset + xwb.stream_size > get_streamfile_size(streamFile)) + if (xwb.data_offset + xwb.stream_size > get_streamfile_size(sf)) goto fail; - //if (xwb.data_offset + xwb.data_size > get_streamfile_size(streamFile)) /* badly split */ + //if (xwb.data_offset + xwb.data_size > get_streamfile_size(sf)) /* badly split */ // goto fail; } @@ -394,7 +411,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { msd.loop_end_subframe = ((xwb.loop_end >> 2) & 0x3) + 1; /* 2b */ msd.loop_start_subframe = ((xwb.loop_end >> 0) & 0x3) + 1; /* 2b */ - xma_get_samples(&msd, streamFile); + xma_get_samples(&msd, sf); xwb.loop_start_sample = msd.loop_start_sample; xwb.loop_end_sample = msd.loop_end_sample; @@ -404,7 +421,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* for XWB v22 (and below?) this seems normal [Project Gotham Racing (X360)] */ if (xwb.num_samples == 0) { - xwb.num_samples = msd.num_samples; + xwb.num_samples = msd.num_samples; xwb.fix_xma_num_samples = 1; } } @@ -431,7 +448,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { vgmstream->num_streams = xwb.total_subsongs; vgmstream->stream_size = xwb.stream_size; vgmstream->meta_type = meta_XWB; - get_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_subsong, &xwb, streamFile); + get_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_subsong, &xwb, sf); switch(xwb.codec) { case PCM: /* Unreal Championship (Xbox)[PCM8], KOF2003 (Xbox)[PCM16LE], Otomedius (X360)[PCM16BE] */ @@ -458,12 +475,12 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { int bytes; bytes = ffmpeg_make_riff_xma1(buf,0x100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, 0); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, xwb.stream_offset,xwb.stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - xma_fix_raw_samples(vgmstream, streamFile, xwb.stream_offset,xwb.stream_size, 0, xwb.fix_xma_num_samples,xwb.fix_xma_loop_samples); + xma_fix_raw_samples(vgmstream, sf, xwb.stream_offset,xwb.stream_size, 0, xwb.fix_xma_num_samples,xwb.fix_xma_loop_samples); /* this fixes some XMA1, perhaps the above isn't reading end_skip correctly (doesn't happen for all files though) */ if (vgmstream->loop_flag && @@ -482,19 +499,19 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { block_count = xwb.stream_size / block_size + (xwb.stream_size % block_size ? 1 : 0); bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, xwb.stream_offset,xwb.stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - xma_fix_raw_samples(vgmstream, streamFile, xwb.stream_offset,xwb.stream_size, 0, xwb.fix_xma_num_samples,xwb.fix_xma_loop_samples); + xma_fix_raw_samples(vgmstream, sf, xwb.stream_offset,xwb.stream_size, 0, xwb.fix_xma_num_samples,xwb.fix_xma_loop_samples); break; } case WMA: { /* WMAudio1 (WMA v2): Prince of Persia 2 port (Xbox) */ ffmpeg_codec_data *ffmpeg_data = NULL; - ffmpeg_data = init_ffmpeg_offset(streamFile, xwb.stream_offset,xwb.stream_size); + ffmpeg_data = init_ffmpeg_offset(sf, xwb.stream_offset,xwb.stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; @@ -520,7 +537,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { wma_codec = xwb.bits_per_sample ? 0x162 : 0x161; /* 0=WMAudio2, 1=WMAudio3 */ bytes = ffmpeg_make_riff_xwma(buf,0x100, wma_codec, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, xwb.stream_offset,xwb.stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -534,7 +551,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { encoder_delay = 1024; /* assumed */ vgmstream->num_samples -= encoder_delay; - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, xwb.stream_offset,xwb.stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, xwb.stream_offset,xwb.stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -543,7 +560,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { #endif #ifdef VGM_USE_VORBIS case OGG: { /* Oddworld: Strangers Wrath (iOS/Android) extension */ - vgmstream->codec_data = init_ogg_vorbis(streamFile, xwb.stream_offset, xwb.stream_size, NULL); + vgmstream->codec_data = init_ogg_vorbis(sf, xwb.stream_offset, xwb.stream_size, NULL); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_OGG_VORBIS; vgmstream->layout_type = layout_none; @@ -556,8 +573,8 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = xwb.stream_size / xwb.channels; - dsp_read_coefs(vgmstream,streamFile,xwb.stream_offset + 0x1c,vgmstream->interleave_block_size,!xwb.little_endian); - dsp_read_hist (vgmstream,streamFile,xwb.stream_offset + 0x3c,vgmstream->interleave_block_size,!xwb.little_endian); + dsp_read_coefs(vgmstream,sf,xwb.stream_offset + 0x1c,vgmstream->interleave_block_size,!xwb.little_endian); + dsp_read_hist (vgmstream,sf,xwb.stream_offset + 0x3c,vgmstream->interleave_block_size,!xwb.little_endian); xwb.stream_offset += 0x60; /* skip DSP header */ break; } @@ -565,16 +582,16 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { #ifdef VGM_USE_ATRAC9 case ATRAC9_RIFF: { /* Stardew Valley (Vita) extension */ VGMSTREAM *temp_vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; + STREAMFILE* temp_sf = NULL; /* standard RIFF, use subfile (seems doesn't use xwb loops) */ VGM_ASSERT(xwb.loop_flag, "XWB: RIFF ATRAC9 loop flag found\n"); - temp_streamFile = setup_subfile_streamfile(streamFile, xwb.stream_offset,xwb.stream_size, "at9"); - if (!temp_streamFile) goto fail; + temp_sf = setup_subfile_streamfile(sf, xwb.stream_offset,xwb.stream_size, "at9"); + if (!temp_sf) goto fail; - temp_vgmstream = init_vgmstream_riff(temp_streamFile); - close_streamfile(temp_streamFile); + temp_vgmstream = init_vgmstream_riff(temp_sf); + close_streamfile(temp_sf); if (!temp_vgmstream) goto fail; temp_vgmstream->num_streams = vgmstream->num_streams; @@ -594,7 +611,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { start_offset = xwb.stream_offset; - if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + if ( !vgmstream_open_stream(vgmstream,sf,start_offset) ) goto fail; return vgmstream; @@ -605,13 +622,13 @@ fail: /* ****************************************************************************** */ -static int get_xwb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile) { +static int get_xwb_name(char* buf, size_t maxsize, int target_subsong, xwb_header* xwb, STREAMFILE* sf) { size_t read; if (!xwb->names_offset || !xwb->names_size || xwb->names_entry_size > maxsize) goto fail; - read = read_string(buf,xwb->names_entry_size, xwb->names_offset + xwb->names_entry_size*(target_subsong-1),streamFile); + read = read_string(buf,xwb->names_entry_size, xwb->names_offset + xwb->names_entry_size*(target_subsong-1),sf); if (read == 0) goto fail; return 1; @@ -620,11 +637,11 @@ fail: return 0; } -static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile) { +static int get_xsb_name(char* buf, size_t maxsize, int target_subsong, xwb_header* xwb, STREAMFILE* sf) { xsb_header xsb = {0}; xsb.selected_stream = target_subsong - 1; - if (!parse_xsb(&xsb, streamFile, xwb->wavebank_name)) + if (!parse_xsb(&xsb, sf, xwb->wavebank_name)) goto fail; if ((xwb->version <= XACT1_1_MAX && xsb.version > XSB_XACT1_2_MAX) || @@ -649,12 +666,12 @@ static int get_wbh_name(char* buf, size_t maxsize, int target_subsong, xwb_heade int version, name_count; off_t offset, name_number; - if (read_32bitBE(0x00, sf) != 0x57424844) /* "WBHD" */ + if (read_u32be(0x00, sf) != 0x57424844) /* "WBHD" */ goto fail; - version = read_32bitLE(0x04, sf); + version = read_u32le(0x04, sf); if (version != 1) goto fail; - name_count = read_32bitLE(0x08, sf); + name_count = read_u32le(0x08, sf); if (selected_stream > name_count) goto fail; @@ -682,19 +699,19 @@ fail: return 0; } -static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamXwb) { - STREAMFILE *sf_name = NULL; +static void get_name(char* buf, size_t maxsize, int target_subsong, xwb_header* xwb, STREAMFILE* sf_xwb) { + STREAMFILE* sf_name = NULL; int name_found; /* try to get the stream name in the .xwb, though they are very rarely included */ - name_found = get_xwb_name(buf, maxsize, target_subsong, xwb, streamXwb); + name_found = get_xwb_name(buf, maxsize, target_subsong, xwb, sf_xwb); if (name_found) return; /* try again in a companion files */ if (xwb->version == 1) { /* .wbh, a simple name container */ - sf_name = open_streamfile_by_ext(streamXwb, "wbh"); + sf_name = open_streamfile_by_ext(sf_xwb, "wbh"); if (!sf_name) return; /* rarely found [Pac-Man World 2 (Xbox)] */ name_found = get_wbh_name(buf, maxsize, target_subsong, xwb, sf_name); @@ -702,7 +719,7 @@ static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header } else { /* .xsb, a comically complex cue format */ - sf_name = open_xsb_filename_pair(streamXwb); + sf_name = open_xsb_filename_pair(sf_xwb); if (!sf_name) return; /* not all xwb have xsb though */ name_found = get_xsb_name(buf, maxsize, target_subsong, xwb, sf_name); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwc.c b/Frameworks/vgmstream/vgmstream/src/meta/xwc.c index 3bd30ac25..659cf4ee5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwc.c @@ -67,7 +67,7 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) { if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; - vgmstream->sample_rate = ((mpeg_codec_data*)vgmstream->codec_data)->sample_rate_per_frame; + vgmstream->sample_rate = mpeg_get_sample_rate(vgmstream->codec_data); break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/zwv.c b/Frameworks/vgmstream/vgmstream/src/meta/zwv.c new file mode 100644 index 000000000..70f436cc9 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/zwv.c @@ -0,0 +1,52 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .zwv - from Namco games [THE iDOLM@STER Shiny TV (PS3)] */ +VGMSTREAM* init_vgmstream_zwv(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + off_t subfile_offset; + size_t subfile_size; + + + /* checks */ + if (!check_extensions(sf,"zwv")) + goto fail; + if (read_u32be(0x00,sf) != 0x77617665) /* "wave" */ + goto fail; + + /* has a mini header then a proper MSF: + * 0x04: null + * 0x08: null + * 0x0c: version/config? (0x06040000) + * 0x10: version/config? (0x00030210) + * 0x14: sample rate + * 0x18: ? (related to sample rate) + * 0x1c: null + * 0x20: data offset + * 0x24: data size + * 0x28: loop flag (0x30+ is removed if no loop) + * 0x2c: ? (related to loop, or null) + * 0x30: null + * 0x30: loop start offset (same as MSF) + * 0x30: loop end offset (same as MSF start+length) + * 0x3c: null + */ + + subfile_offset = read_u32be(0x20, sf) - 0x40; + subfile_size = read_u32be(0x24, sf) + 0x40; + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "msf"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_msf(temp_sf); + if (!vgmstream) goto fail; + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/mixing.c b/Frameworks/vgmstream/vgmstream/src/mixing.c index e4bd02c7f..43957e8f5 100644 --- a/Frameworks/vgmstream/vgmstream/src/mixing.c +++ b/Frameworks/vgmstream/vgmstream/src/mixing.c @@ -119,6 +119,10 @@ static int is_active(mixing_data *data, int32_t current_start, int32_t current_e static int32_t get_current_pos(VGMSTREAM* vgmstream, int32_t sample_count) { int32_t current_pos; + if (vgmstream->config_enabled) { + return vgmstream->pstate.play_position; + } + if (vgmstream->loop_flag && vgmstream->loop_count > 0) { int loop_pre = vgmstream->loop_start_sample; /* samples before looping */ int loop_into = (vgmstream->current_sample - vgmstream->loop_start_sample); /* samples after loop */ @@ -713,12 +717,40 @@ void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask) { } } + +/* get highest channel count */ +static int get_layered_max_channels(VGMSTREAM* vgmstream) { + int i, max; + layered_layout_data* data; + + if (vgmstream->layout_type != layout_layered) + return 0; + + data = vgmstream->layout_data; + + max = 0; + for (i = 0; i < data->layer_count; i++) { + int output_channels = 0; + + mixing_info(data->layers[i], NULL, &output_channels); + + if (max < output_channels) + max = output_channels; + } + + return max; +} + void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode) { mixing_data *data = vgmstream->mixing_data; int current, ch, output_channels, selected_channels; if (!data) return; + + if (max == 0) /* auto calculate */ + max = get_layered_max_channels(vgmstream); + if (max <= 0 || data->output_channels <= max) return; @@ -807,8 +839,11 @@ void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max) { /* set loops to hear all track changes */ track_num = output_channels / max; - if (vgmstream->config.loop_count < track_num) + if (vgmstream->config.loop_count < track_num) { vgmstream->config.loop_count = track_num; + vgmstream->config.loop_count_set = 1; + vgmstream->config.config_set = 1; + } ch = 0; for (track = 0; track < track_num; track++) { @@ -868,8 +903,11 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode) { /* set loops to hear all track changes */ layer_num = output_channels / max; - if (vgmstream->config.loop_count < layer_num) + if (vgmstream->config.loop_count < layer_num) { vgmstream->config.loop_count = layer_num; + vgmstream->config.loop_count_set = 1; + vgmstream->config.config_set = 1; + } /* mode 'v': constant volume * mode 'e': sets fades to successively lower/equalize volume per loop for each layer @@ -1062,12 +1100,6 @@ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) { if (!data) goto fail; - /* a bit wonky but eh... */ - if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) { - vgmstream->channel_layout = 0; - ((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = 0; - } - /* special value to not actually enable anything (used to query values) */ if (max_sample_count <= 0) goto fail; @@ -1079,6 +1111,12 @@ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) { data->mixbuf = mixbuf_re; data->mixing_on = 1; + /* a bit wonky but eh... */ + if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) { + vgmstream->channel_layout = 0; + ((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = 0; + } + /* since data exists on its own memory and pointer is already set * there is no need to propagate to start_vgmstream */ @@ -1089,7 +1127,7 @@ fail: return; } -void mixing_info(VGMSTREAM * vgmstream, int *out_input_channels, int *out_output_channels) { +void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_channels) { mixing_data *data = vgmstream->mixing_data; int input_channels, output_channels; @@ -1101,11 +1139,13 @@ void mixing_info(VGMSTREAM * vgmstream, int *out_input_channels, int *out_output else input_channels = vgmstream->channels; - if (out_input_channels) *out_input_channels = input_channels; - if (out_output_channels) *out_output_channels = output_channels; + if (p_input_channels) *p_input_channels = input_channels; + if (p_output_channels) *p_output_channels = output_channels; //;VGM_LOG("MIX: channels %i, in=%i, out=%i, mix=%i\n", vgmstream->channels, input_channels, output_channels, data->mixing_channels); return; fail: + if (p_input_channels) *p_input_channels = vgmstream->channels; + if (p_output_channels) *p_output_channels = vgmstream->channels; return; } diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.c b/Frameworks/vgmstream/vgmstream/src/plugins.c index 1e982bb69..085c3dad2 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.c +++ b/Frameworks/vgmstream/vgmstream/src/plugins.c @@ -63,6 +63,156 @@ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) { return 0; } +void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM* vgmstream, vgmstream_title_t* cfg) { + const char *pos; + char* pos2; + char temp[1024]; + + + /* name without path */ + pos = strrchr(filename, '\\'); + if (!pos) + pos = strrchr(filename, '/'); + if (!pos) + pos = filename; + else + pos++; + strncpy(buf, pos, buf_len); + + /* name without extension */ + pos2 = strrchr(buf, '.'); + if (pos2) + pos2[0] = '\0'; + + { + const char* stream_name = vgmstream->stream_name; + int total_subsongs = vgmstream->num_streams; + int target_subsong = vgmstream->stream_index; + //int is_first = vgmstream->stream_index == 0; + //int is_txtp = ; //todo don't show number/name for txtp but show for mini-txtp + int show_name; + + if (target_subsong == 0) + target_subsong = 1; + + /* show number if file has more than 1 subsong */ + if (total_subsongs > 1) { + if (cfg && cfg->subsong_range) + snprintf(temp, sizeof(temp), "%s#1~%i", buf, total_subsongs); + else + snprintf(temp, sizeof(temp), "%s#%i", buf, target_subsong); + strncpy(buf, temp, buf_len); + } + + /* show name for some cases */ + show_name = (total_subsongs > 0 && (!cfg || !cfg->subsong_range)) || + (cfg && cfg->force_title); + if (stream_name[0] != '\0' && show_name) { + snprintf(temp, sizeof(temp), "%s (%s)", buf, stream_name); + strncpy(buf, temp, buf_len); + } + } +} + + +static void copy_time(int* dst_flag, int32_t* dst_time, double* dst_time_s, int* src_flag, int32_t* src_time, double* src_time_s) { + if (!*src_flag) + return; + *dst_flag = 1; + *dst_time = *src_time; + *dst_time_s = *src_time_s; +} + +//todo reuse in txtp? +static void load_default_config(play_config_t* def, play_config_t* tcfg) { + + /* loop limit: txtp #L > txtp #l > player #L > player #l */ + if (tcfg->play_forever) { + def->play_forever = 1; + def->ignore_loop = 0; + } + if (tcfg->loop_count_set) { + def->loop_count = tcfg->loop_count; + def->loop_count_set = 1; + def->ignore_loop = 0; + if (!tcfg->play_forever) + def->play_forever = 0; + } + + /* fade priority: #F > #f, #d */ + if (tcfg->ignore_fade) { + def->ignore_fade = 1; + } + if (tcfg->fade_delay_set) { + def->fade_delay = tcfg->fade_delay; + def->fade_delay_set = 1; + } + if (tcfg->fade_time_set) { + def->fade_time = tcfg->fade_time; + def->fade_time_set = 1; + } + + /* loop priority: #i > #e > #E */ + if (tcfg->really_force_loop) { + def->ignore_loop = 0; + def->force_loop = 0; + def->really_force_loop = 1; + } + if (tcfg->force_loop) { + def->ignore_loop = 0; + def->force_loop = 1; + def->really_force_loop = 0; + } + if (tcfg->ignore_loop) { + def->ignore_loop = 1; + def->force_loop = 0; + def->really_force_loop = 0; + } + + copy_time(&def->pad_begin_set, &def->pad_begin, &def->pad_begin_s, &tcfg->pad_begin_set, &tcfg->pad_begin, &tcfg->pad_begin_s); + copy_time(&def->pad_end_set, &def->pad_end, &def->pad_end_s, &tcfg->pad_end_set, &tcfg->pad_end, &tcfg->pad_end_s); + copy_time(&def->trim_begin_set, &def->trim_begin, &def->trim_begin_s, &tcfg->trim_begin_set, &tcfg->trim_begin, &tcfg->trim_begin_s); + copy_time(&def->trim_end_set, &def->trim_end, &def->trim_end_s, &tcfg->trim_end_set, &tcfg->trim_end, &tcfg->trim_end_s); + copy_time(&def->body_time_set, &def->body_time, &def->body_time_s, &tcfg->body_time_set, &tcfg->body_time, &tcfg->body_time_s); +} + +static void load_player_config(play_config_t* def, vgmstream_cfg_t* vcfg) { + def->play_forever = vcfg->play_forever; + def->ignore_loop = vcfg->ignore_loop; + def->force_loop = vcfg->force_loop; + def->really_force_loop = vcfg->really_force_loop; + def->ignore_fade = vcfg->ignore_fade; + + def->loop_count = vcfg->loop_count; + def->loop_count_set = 1; + def->fade_delay = vcfg->fade_delay; + def->fade_delay_set = 1; + def->fade_time = vcfg->fade_time; + def->fade_time_set = 1; +} + +void vgmstream_apply_config(VGMSTREAM* vgmstream, vgmstream_cfg_t* vcfg) { + play_config_t defs = {0}; + play_config_t* def = &defs; /* for convenience... */ + play_config_t* tcfg = &vgmstream->config; + + + load_player_config(def, vcfg); + def->config_set = 1; + + if (!vcfg->disable_config_override) + load_default_config(def, tcfg); + + if (!vcfg->allow_play_forever) + def->play_forever = 0; + + /* copy final config back */ + *tcfg = *def; + + vgmstream->config_enabled = def->config_set; + setup_state_vgmstream(vgmstream); +} + /* ****************************************** */ /* TAGS: loads key=val tags from a file */ /* ****************************************** */ @@ -91,6 +241,7 @@ struct VGMSTREAM_TAGS { int autotrack_on; int autotrack_written; int track_count; + int exact_match; int autoalbum_on; int autoalbum_written; @@ -213,22 +364,27 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) { if (line[0] == '#') { /* find possible global command */ - ok = sscanf(line, "# $%[^ \t] %[^\r\n]", tags->key,tags->val); + ok = sscanf(line, "# $%n%[^ \t]%n %[^\r\n]", &n1, tags->key, &n2, tags->val); if (ok == 1 || ok == 2) { - if (strcasecmp(tags->key,"AUTOTRACK") == 0) { + int key_len = n2 - n1; + if (strncasecmp(tags->key, "AUTOTRACK", key_len) == 0) { tags->autotrack_on = 1; } - else if (strcasecmp(tags->key,"AUTOALBUM") == 0) { + else if (strncasecmp(tags->key, "AUTOALBUM", key_len) == 0) { tags->autoalbum_on = 1; } + else if (strncasecmp(tags->key, "EXACTMATCH", key_len) == 0) { + tags->exact_match = 1; + VGM_LOG("exact\n"); + } continue; /* not an actual tag */ } /* find possible global tag */ - ok = sscanf(line, "# @%[^@]@ %[^\r\n]", tags->key,tags->val); /* key with spaces */ + ok = sscanf(line, "# @%[^@]@ %[^\r\n]", tags->key, tags->val); /* key with spaces */ if (ok != 2) - ok = sscanf(line, "# @%[^ \t] %[^\r\n]", tags->key,tags->val); /* key without */ + ok = sscanf(line, "# @%[^ \t] %[^\r\n]", tags->key, tags->val); /* key without */ if (ok == 2) { tags_clean(tags); return 1; @@ -244,20 +400,28 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) { int currentname_len = n2 - n1; int filename_found = 0; - /* we want to find file with the same name (case insensitive), OR a virtual .txtp with - * the filename inside (so 'file.adx' gets tags from 'file.adx#i.txtp', reading - * tags even if we don't open !tags.m3u with virtual .txtp directly) */ + /* we want to match file with the same name (case insensitive), OR a virtual .txtp with + * the filename inside to ease creation of tag files with config, also check end char to + * tell apart the unlikely case of having both 'bgm01.ad.txtp' and 'bgm01.adp.txtp' */ - /* strcasecmp works ok even for UTF-8 */ - if (currentname_len >= tags->targetname_len && /* starts with targetname */ - strncasecmp(currentname, tags->targetname, tags->targetname_len) == 0) { - - if (currentname_len == tags->targetname_len) { /* exact match */ - filename_found = 1; + /* try exact match (strcasecmp works ok even for UTF-8) */ + if (currentname_len == tags->targetname_len && + strncasecmp(currentname, tags->targetname, currentname_len) == 0) { + filename_found = 1; + } + else if (!tags->exact_match) { + /* try tagfile is "bgm.adx" + target is "bgm.adx #(cfg) .txtp" */ + if (currentname_len < tags->targetname_len && + strncasecmp(currentname, tags->targetname, currentname_len) == 0 && + vgmstream_is_virtual_filename(tags->targetname)) { + char c = tags->targetname[currentname_len]; + filename_found = (c==' ' || c == '.' || c == '#'); } - else if (vgmstream_is_virtual_filename(currentname)) { /* ends with .txth */ + /* tagfile has "bgm.adx (...) .txtp" + target has "bgm.adx" */ + else if (tags->targetname_len < currentname_len && + strncasecmp(tags->targetname, currentname, tags->targetname_len) == 0 && + vgmstream_is_virtual_filename(currentname)) { char c = currentname[tags->targetname_len]; - /* tell apart the unlikely case of having both 'bgm01.ad.txtp' and 'bgm01.adp.txtp' */ filename_found = (c==' ' || c == '.' || c == '#'); } } @@ -329,6 +493,10 @@ void vgmstream_tags_reset(VGMSTREAM_TAGS* tags, const char* target_filename) { void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels) { mixing_setup(vgmstream, max_sample_count); mixing_info(vgmstream, input_channels, output_channels); + + /* update internals */ + mixing_info(vgmstream, &vgmstream->pstate.input_channels, &vgmstream->pstate.output_channels); + setup_vgmstream(vgmstream); } void vgmstream_mixing_autodownmix(VGMSTREAM *vgmstream, int max_channels) { diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.h b/Frameworks/vgmstream/vgmstream/src/plugins.h index 9133eae84..fff652e26 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.h +++ b/Frameworks/vgmstream/vgmstream/src/plugins.h @@ -5,6 +5,25 @@ #define _PLUGINS_H_ #include "streamfile.h" +//todo rename to api.h once public enough + + +#if 0 +/* define standard C param call and name mangling (to avoid __stdcall / .defs) */ +//#define VGMSTREAM_CALL __cdecl //needed? + +/* define external function types (during compilation) */ +#if defined(VGMSTREAM_EXPORT) + #define VGMSTREAM_API __declspec(dllexport) /* when exporting/creating vgmstream DLL */ +#elif defined(VGMSTREAM_IMPORT) + #define VGMSTREAM_API __declspec(dllimport) /* when importing/linking vgmstream DLL */ +#else + #define VGMSTREAM_API /* nothing, internal/default */ +#endif + +//VGMSTREAM_API void VGMSTREAM_CALL vgmstream_function(void); +#endif + /* ****************************************** */ /* CONTEXT: simplifies plugin code */ @@ -21,42 +40,139 @@ typedef struct { /* returns if vgmstream can parse file by extension */ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg); -#if 0 - -/* opaque player state */ -typedef struct VGMSTREAM_CTX VGMSTREAM_CTX; typedef struct { - //... -} VGMSTREAM_CTX_INFO; + int allow_play_forever; + int disable_config_override; -VGMSTREAM_CTX* vgmstream_ctx_init(...); + /* song mofidiers */ + int play_forever; /* keeps looping forever (needs loop points) */ + int ignore_loop; /* ignores loops points */ + int force_loop; /* enables full loops (0..samples) if file doesn't have loop points */ + int really_force_loop; /* forces full loops even if file has loop points */ + int ignore_fade; /* don't fade after N loops */ -VGMSTREAM_CTX* vgmstream_ctx_format_check(...); -VGMSTREAM_CTX* vgmstream_ctx_set_format_whilelist(...); -VGMSTREAM_CTX* vgmstream_ctx_set_format_blacklist(...); + /* song processing */ + double loop_count; /* target loops */ + double fade_delay; /* fade delay after target loops */ + double fade_time; /* fade period after target loops */ -VGMSTREAM_CTX* vgmstream_ctx_set_file(...); + //int downmix; /* max number of channels allowed (0=disable downmix) */ -VGMSTREAM_CTX* vgmstream_ctx_get_config(...); +} vgmstream_cfg_t; -VGMSTREAM_CTX* vgmstream_ctx_set_config(...); +// WARNING: these are not stable and may change anytime without notice +void vgmstream_apply_config(VGMSTREAM* vgmstream, vgmstream_cfg_t* pcfg); +int32_t vgmstream_get_samples(VGMSTREAM* vgmstream); +int vgmstream_get_play_forever(VGMSTREAM* vgmstream); +void vgmstream_set_play_forever(VGMSTREAM* vgmstream, int enabled); -VGMSTREAM_CTX* vgmstream_ctx_get_buffer(...); -VGMSTREAM_CTX* vgmstream_ctx_get_info(...); +typedef struct { + int force_title; + int subsong_range; +} vgmstream_title_t; -VGMSTREAM_CTX* vgmstream_ctx_describe(...); +/* get a simple title for plugins */ +void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM* vgmstream, vgmstream_title_t* cfg); -VGMSTREAM_CTX* vgmstream_ctx_get_title(...); -VGMSTREAM_CTX* vgmstream_ctx_get_tagfile(...); -VGMSTREAM_CTX* vgmstream_ctx_play(...); +#if 0 +//possible future public/opaque API -VGMSTREAM_CTX* vgmstream_ctx_seek(...); +/* opaque player state */ +//#define VGMSTREAM_CTX_VERSION 1 +typedef struct VGMSTREAM_CTX VGMSTREAM_CTX; -VGMSTREAM_CTX* vgmstream_ctx_close(...); + +/* Setups base vgmstream player context. */ +VGMSTREAM_CTX* vgmstream_init_ctx(void); + + +/* Sets default config, that will be applied to song on open (some formats like TXTP may override + * these settings). + * May only be called without song loaded (before _open or after _close), otherwise ignored. */ +void vgmstream_set_config(VGMSTREAM_CTX* vctx, VGMSTREAM_CFG* vcfg); + +void vgmstream_set_buffer(VGMSTREAM_CTX* vctx, int samples, int max_samples); + +/* Opens a new STREAMFILE to play. Returns < 0 on error when the file isn't recogniced. + * If file has subsongs, first open usually loads first subsong. get_info then can be used to check + * whether file has more subsongs (total_subsongs > 1), and call others. + * */ +int vgmstream_open(STREAMFILE* sf); +int vgmstream_open_subsong(STREAMFILE* sf, int subsong); + +typedef struct { + const int channels; + const int sample_rate; + + const int sample_count; /* file's samples (not final duration) */ + const int loop_start_sample; + const int loop_end_sample; + const int loop_flag; + + const int current_subsong; /* 0=not set, N=loaded subsong N */ + const int total_subsongs; /* 0=format has no subsongs, N=has N subsongs */ + const int file_bitrate; /* file's average bitrate */ + //const int codec_bitrate; /* codec's average bitrate */ + + /* descriptions */ + //const char* codec; + //const char* layout; + //const char* metadata; + + //int type; /* 0=pcm16, 1=float32, always interleaved: [0]=ch0, [1]=ch1 ... */ +} VGMSTREAM_INFO; + +/* Get info from current song. */ +void vgmstream_ctx_get_info(VGMSTREAM_CTX* vctx, VGMSTREAM_INFO* vinfo); + + +/* Gets final time based on config and current song. If config is set to "play forever" + * this still returns final time based on config as a reference. Returns > 0 on success. */ +int32_t vgmstream_get_total_time(VGMSTREAM_CTX* vctx); +double vgmstream_get_total_samples(VGMSTREAM_CTX* vctx); + + +/* Gets current position within song. When "play forever" is set, it'll clamp results to total_time. */ +int32_t vgmstream_get_current_time(VGMSTREAM_CTX* vctx); +double vgmstream_get_current_samples(VGMSTREAM_CTX* vctx); + + +/* Seeks to position */ +VGMSTREAM_CTX* vgmstream_seek_absolute_sample(VGMSTREAM_CTX* vctx, int32_t sample); +VGMSTREAM_CTX* vgmstream_seek_absolute_time(VGMSTREAM_CTX* vctx, double time); +VGMSTREAM_CTX* vgmstream_seek_current_sample(VGMSTREAM_CTX* vctx, int32_t sample); +VGMSTREAM_CTX* vgmstream_seek_current_time(VGMSTREAM_CTX* vctx, double time); + + +/* Closes current song. */ +void vgmstream_close(VGMSTREAM_CTX* vctx); + +/* Frees vgmstream context. */ +void vgmstream_free_ctx(VGMSTREAM_CTX* vctx); + + +/* Converts samples. returns number of rendered samples, or <=0 if no more + * samples left (will fill buffer with silence) */ +int vgmstream_play(VGMSTREAM_CTX* vctx); + + +#if 0 +void vgmstream_get_buffer(...); + +void vgmstream_format_check(...); +void vgmstream_set_format_whilelist(...); +void vgmstream_set_format_blacklist(...); + +const char* vgmstream_describe(...); + +const char* vgmstream_get_title(...); + +VGMSTREAM_TAGS* vgmstream_get_tagfile(...); +#endif #endif diff --git a/Frameworks/vgmstream/vgmstream/src/render.c b/Frameworks/vgmstream/vgmstream/src/render.c new file mode 100644 index 000000000..1bcd8ffea --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/render.c @@ -0,0 +1,709 @@ +#include "vgmstream.h" +#include "layout/layout.h" +#include "render.h" +#include "decode.h" +#include "mixing.h" +#include "plugins.h" + + +/* VGMSTREAM RENDERING + * Caller asks for N samples in buf. vgmstream then calls layouts, that call decoders, and some optional pre/post-processing. + * Processing must be enabled externally: padding/trimming (config), mixing (output changes), resampling, etc. + * + * - MIXING + * After decoding sometimes we need to change number of channels, volume, etc. This is applied in order as + * a mixing chain, and modifies the final buffer (see mixing.c). + * + * - CONFIG + * A VGMSTREAM can work in 2 modes, defaults to simple mode: + * - simple mode (lib-like): decodes/loops forever and results are controlled externally (fades/max time/etc). + * - config mode (player-like): everything is internally controlled (pads/trims/time/fades/etc may be applied). + * + * It's done this way mainly for compatibility and to enable complex TXTP for layers/segments in selected cases + * (Wwise emulation). Could apply always some config like begin trim/padding + modify get_vgmstream_samples, but + * external caller may read loops/samples manually or apply its own config/fade, and changed output would mess it up. + * + * To enable config mode it needs 2 steps: + * - add some internal config settings (via TXTP, or passed by plugin). + * - enable flag with function (to signal "really delegate all decoding to vgmstream"). + * Once done, plugin should simply decode until max samples (calculated by vgmstream). + * + * For complex layouts, behavior of "internal" (single segment/layer) and "external" (main) VGMSTREAMs is + * a bit complex. Internals' enable flag if play config exists (via TXTP), and this allows each part to be + * padded/trimmed/set time/loop/etc individually. + * + * Config mode in the external VGMSTREAM is mostly straighforward with segments: + * - each internal is always decoded separatedly (in simple or config mode) and results in N samples + * - segments may even loop "internally" before moving to next segment (by default they don't) + * - external's samples is the sum of all segments' N samples + * - looping, fades, etc then can be applied in the external part normally. + * + * With layers it's a bit more complex: + * - external's samples is the max of all layers + * - in simple mode external uses internal's looping to loop (for performance) + * - if layers' config mode is set, external can't rely on internal looping, so it uses it's own + * + * Layouts can contain layouts in cascade, so behavior can be a bit hard to understand at times. + * This mainly applies to TXTP, segments/layers in metas usually don't need to trigger config mode. + */ + + +int vgmstream_get_play_forever(VGMSTREAM* vgmstream) { + return vgmstream->config.play_forever; +} + +void vgmstream_set_play_forever(VGMSTREAM* vgmstream, int enabled) { + /* sometimes we need to enable/disable right before playback + * (play config is left untouched, should mix ok as this flag is only used during + * render, while config is always prepared as if play forever wasn't enabled) */ + vgmstream->config.play_forever = enabled; + setup_vgmstream(vgmstream); /* update config */ +} + +int32_t vgmstream_get_samples(VGMSTREAM* vgmstream) { + if (!vgmstream->config_enabled || !vgmstream->config.config_set) + return vgmstream->num_samples; + return vgmstream->pstate.play_duration; +} + +/* calculate samples based on player's config */ +int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM* vgmstream) { + if (vgmstream->loop_flag) { + if (vgmstream->loop_target == (int)looptimes) { /* set externally, as this function is info-only */ + /* Continue playing the file normally after looping, instead of fading. + * Most files cut abruply after the loop, but some do have proper endings. + * With looptimes = 1 this option should give the same output vs loop disabled */ + int loop_count = (int)looptimes; /* no half loops allowed */ + return vgmstream->loop_start_sample + + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count + + (vgmstream->num_samples - vgmstream->loop_end_sample); + } + else { + return vgmstream->loop_start_sample + + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * looptimes + + (fadedelayseconds + fadeseconds) * vgmstream->sample_rate; + } + } + else { + return vgmstream->num_samples; + } +} + +/*****************************************************************************/ + +static void setup_state_modifiers(VGMSTREAM* vgmstream) { + play_config_t* pc = &vgmstream->config; + + /* apply final config */ + if (pc->really_force_loop) { + vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples); + } + if (pc->force_loop && !vgmstream->loop_flag) { + vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples); + } + if (pc->ignore_loop) { + vgmstream_force_loop(vgmstream, 0, 0,0); + } + + if (!vgmstream->loop_flag) { + pc->play_forever = 0; + } + if (pc->play_forever) { + pc->ignore_fade = 0; + } + + + /* loop N times, but also play stream end instead of fading out */ + if (pc->ignore_fade) { + vgmstream_set_loop_target(vgmstream, (int)pc->loop_count); + pc->fade_time = 0; + pc->fade_delay = 0; + } +} + +static void setup_state_processing(VGMSTREAM* vgmstream) { + play_state_t* ps = &vgmstream->pstate; + play_config_t* pc = &vgmstream->config; + double sample_rate = vgmstream->sample_rate; + + /* time to samples */ + if (pc->pad_begin_s) + pc->pad_begin = pc->pad_begin_s * sample_rate; + if (pc->pad_end_s) + pc->pad_end = pc->pad_end_s * sample_rate; + if (pc->trim_begin_s) + pc->trim_begin = pc->trim_begin_s * sample_rate; + if (pc->trim_end_s) + pc->trim_end = pc->trim_end_s * sample_rate; + if (pc->body_time_s) + pc->body_time = pc->body_time_s * sample_rate; + //todo fade time also set to samples + + /* samples before all decode */ + ps->pad_begin_duration = pc->pad_begin; + + /* removed samples from first decode */ + ps->trim_begin_duration = pc->trim_begin; + + /* main samples part */ + ps->body_duration = 0; + if (pc->body_time) { + ps->body_duration += pc->body_time; /* whether it loops or not */ + } + else if (vgmstream->loop_flag) { + double loop_count = 1.0; + if (pc->loop_count_set) /* may set 0.0 on purpose I guess */ + loop_count = pc->loop_count; + + ps->body_duration += vgmstream->loop_start_sample; + if (pc->ignore_fade) { + ps->body_duration += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * (int)loop_count; + ps->body_duration += (vgmstream->num_samples - vgmstream->loop_end_sample); + } + else { + ps->body_duration += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count; + } + } + else { + ps->body_duration += vgmstream->num_samples; + } + + /* samples from some modify body */ + if (pc->trim_begin) + ps->body_duration -= pc->trim_begin; + if (pc->trim_end) + ps->body_duration -= pc->trim_end; + if (pc->fade_delay && vgmstream->loop_flag) + ps->body_duration += pc->fade_delay * vgmstream->sample_rate; + + /* samples from fade part */ + if (pc->fade_time && vgmstream->loop_flag) + ps->fade_duration = pc->fade_time * vgmstream->sample_rate; + + /* samples from last part (anything beyond this is empty, unless play forever is set) */ + ps->pad_end_duration = pc->pad_end; + + /* final count */ + ps->play_duration = ps->pad_begin_duration + ps->body_duration + ps->fade_duration + ps->pad_end_duration; + ps->play_position = 0; + + /* values too big can overflow, just ignore */ + if (ps->pad_begin_duration < 0) + ps->pad_begin_duration = 0; + if (ps->body_duration < 0) + ps->body_duration = 0; + if (ps->fade_duration < 0) + ps->fade_duration = 0; + if (ps->pad_end_duration < 0) + ps->pad_end_duration = 0; + if (ps->play_duration < 0) + ps->play_duration = 0; + + ps->pad_begin_left = ps->pad_begin_duration; + ps->trim_begin_left = ps->trim_begin_duration; + ps->fade_left = ps->fade_duration; + ps->fade_start = ps->pad_begin_duration + ps->body_duration; + //ps->pad_end_left = ps->pad_end_duration; + ps->pad_end_start = ps->fade_start + ps->fade_duration; + + /* other info (updated once mixing is enabled) */ + ps->input_channels = vgmstream->channels; + ps->output_channels = vgmstream->channels; +} + +void setup_state_vgmstream(VGMSTREAM* vgmstream) { + if (!vgmstream->config.config_set) + return; + + setup_state_modifiers(vgmstream); + setup_state_processing(vgmstream); + setup_vgmstream(vgmstream); /* save current config for reset */ +} + +/*****************************************************************************/ + +void free_layout(VGMSTREAM* vgmstream) { + + if (vgmstream->layout_type == layout_segmented) { + free_layout_segmented(vgmstream->layout_data); + } + + if (vgmstream->layout_type == layout_layered) { + free_layout_layered(vgmstream->layout_data); + } +} + +void reset_layout(VGMSTREAM* vgmstream) { + + if (vgmstream->layout_type == layout_segmented) { + reset_layout_segmented(vgmstream->layout_data); + } + + if (vgmstream->layout_type == layout_layered) { + reset_layout_layered(vgmstream->layout_data); + } +} + +static int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) { + + /* current_sample goes between loop points (if looped) or up to max samples, + * must detect beyond that decoders would encounter garbage data */ + + /* not ">=" to allow layouts to loop in some cases when == happens */ + if (vgmstream->current_sample > vgmstream->num_samples) { + int channels = vgmstream->channels; + + memset(buf, 0, sample_count * sizeof(sample_t) * channels); + return sample_count; + } + + switch (vgmstream->layout_type) { + case layout_interleave: + render_vgmstream_interleave(buf, sample_count, vgmstream); + break; + case layout_none: + render_vgmstream_flat(buf, sample_count, vgmstream); + break; + case layout_blocked_mxch: + case layout_blocked_ast: + case layout_blocked_halpst: + case layout_blocked_xa: + case layout_blocked_ea_schl: + case layout_blocked_ea_1snh: + case layout_blocked_caf: + case layout_blocked_wsi: + case layout_blocked_str_snds: + case layout_blocked_ws_aud: + case layout_blocked_matx: + case layout_blocked_dec: + case layout_blocked_vs: + case layout_blocked_mul: + case layout_blocked_gsb: + case layout_blocked_xvas: + case layout_blocked_thp: + case layout_blocked_filp: + case layout_blocked_ivaud: + case layout_blocked_ea_swvr: + case layout_blocked_adm: + case layout_blocked_bdsp: + case layout_blocked_tra: + case layout_blocked_ps2_iab: + case layout_blocked_vs_str: + case layout_blocked_rws: + case layout_blocked_hwas: + case layout_blocked_ea_sns: + case layout_blocked_awc: + case layout_blocked_vgs: + case layout_blocked_vawx: + case layout_blocked_xvag_subsong: + case layout_blocked_ea_wve_au00: + case layout_blocked_ea_wve_ad10: + case layout_blocked_sthd: + case layout_blocked_h4m: + case layout_blocked_xa_aiff: + case layout_blocked_vs_square: + case layout_blocked_vid1: + case layout_blocked_ubi_sce: + render_vgmstream_blocked(buf, sample_count, vgmstream); + break; + case layout_segmented: + render_vgmstream_segmented(buf, sample_count,vgmstream); + break; + case layout_layered: + render_vgmstream_layered(buf, sample_count, vgmstream); + break; + default: + break; + } + + if (vgmstream->current_sample > vgmstream->num_samples) { + int channels = vgmstream->channels; + int32_t excess, decoded; + + excess = (vgmstream->current_sample - vgmstream->num_samples); + if (excess > sample_count) + excess = sample_count; + decoded = sample_count - excess; + + memset(buf + decoded * channels, 0, excess * sizeof(sample_t) * channels); + return sample_count; + } + + return sample_count; +} + + +static void render_trim(VGMSTREAM* vgmstream) { + sample_t* tmpbuf = vgmstream->tmpbuf; + size_t tmpbuf_size = vgmstream->tmpbuf_size; + int32_t buf_samples = tmpbuf_size / vgmstream->channels; /* base channels, no need to apply mixing */ + + while (vgmstream->pstate.trim_begin_left) { + int to_do = vgmstream->pstate.trim_begin_left; + if (to_do > buf_samples) + to_do = buf_samples; + + render_layout(tmpbuf, to_do, vgmstream); + /* no mixing */ + vgmstream->pstate.trim_begin_left -= to_do; + } +} + +static int render_pad_begin(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_do) { + int channels = vgmstream->pstate.output_channels; + int to_do = vgmstream->pstate.pad_begin_left; + if (to_do > samples_to_do) + to_do = samples_to_do; + + memset(buf, 0, to_do * sizeof(sample_t) * channels); + vgmstream->pstate.pad_begin_left -= to_do; + + return to_do; +} + +static int render_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) { + play_state_t* ps = &vgmstream->pstate; + //play_config_t* pc = &vgmstream->config; + + //if (!ps->fade_left || pc->play_forever) + // return; + //if (ps->play_position + samples_done < ps->fade_start) + // return; + + { + int s, ch, start, fade_pos; + int channels = ps->output_channels; + int32_t to_do = ps->fade_left; + + if (ps->play_position < ps->fade_start) { + start = samples_done - (ps->play_position + samples_done - ps->fade_start); + fade_pos = 0; + } + else { + start = 0; + fade_pos = ps->play_position - ps->fade_start; + } + + if (to_do > samples_done - start) + to_do = samples_done - start; + + //TODO: use delta fadedness to improve performance? + for (s = start; s < start + to_do; s++, fade_pos++) { + double fadedness = (double)(ps->fade_duration - fade_pos) / ps->fade_duration; + for (ch = 0; ch < channels; ch++) { + buf[s*channels + ch] = (sample_t)buf[s*channels + ch] * fadedness; + } + } + + ps->fade_left -= to_do; + + /* next samples after fade end would be pad end/silence, so we can just memset */ + memset(buf + (start + to_do) * channels, 0, (samples_done - to_do - start) * sizeof(sample_t) * channels); + + return samples_done; + } +} + +static int render_pad_end(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_do) { + int channels = vgmstream->pstate.output_channels; + + /* since anything beyond pad end is silence no need to check ranges */ + + memset(buf, 0, samples_to_do * sizeof(sample_t) * channels); + return samples_to_do; +} + + +/* Decode data into sample buffer. Controls the "external" part of the decoding, + * while layout/decode control the "internal" part. */ +int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) { + play_state_t* ps = &vgmstream->pstate; + int samples_to_do = sample_count; + int samples_done = 0; + int done; + sample_t* tmpbuf = buf; + + + /* simple mode with no settings (just skip everything below) */ + if (!vgmstream->config_enabled) { + render_layout(buf, samples_to_do, vgmstream); + mix_vgmstream(buf, samples_to_do, vgmstream); + return samples_to_do; + } + + + /* trim may go first since it doesn't need output nor changes totals */ + if (ps->trim_begin_left) { + render_trim(vgmstream); + } + + /* adds empty samples to buf */ + if (ps->pad_begin_left) { + done = render_pad_begin(vgmstream, tmpbuf, samples_to_do); + samples_done += done; + samples_to_do -= done; + tmpbuf += done * vgmstream->pstate.output_channels; /* as if mixed */ + } + + /* end padding (done before to avoid decoding if possible) */ + if (!vgmstream->config.play_forever /* && ps->pad_end_left */ + && ps->play_position + samples_done >= ps->pad_end_start) { + done = render_pad_end(vgmstream, tmpbuf, samples_to_do); + samples_done += done; + samples_to_do -= done; + tmpbuf += done * vgmstream->pstate.output_channels; /* as if mixed */ + } + + /* main decode */ + { //if (samples_to_do) /* 0 ok, less likely */ + done = render_layout(tmpbuf, samples_to_do, vgmstream); + + mix_vgmstream(tmpbuf, done, vgmstream); + + samples_done += done; + + /* simple fadeout */ + if (!vgmstream->config.play_forever && ps->fade_left + && ps->play_position + done >= ps->fade_start) { + render_fade(vgmstream, tmpbuf, done); + } + + tmpbuf += done * vgmstream->pstate.output_channels; + } + + + vgmstream->pstate.play_position += samples_done; + + /* signal end */ + if (!vgmstream->config.play_forever + && ps->play_position > ps->play_duration) { + int excess = ps->play_position - ps->play_duration; + if (excess > sample_count) + excess = sample_count; + + samples_done = (sample_count - excess); + + ps->play_position = ps->play_duration; + } + + return samples_done; +} + +/*****************************************************************************/ + +static void seek_force_loop(VGMSTREAM* vgmstream, int loop_count) { + /* only called after hit loop */ + if (!vgmstream->hit_loop) + return; + + /* pretend decoder reached loop end so state is set to loop start */ + vgmstream->loop_count = loop_count - 1; /* seeking to first loop musy become ++ > 0 */ + vgmstream->current_sample = vgmstream->loop_end_sample; + vgmstream_do_loop(vgmstream); +} + +static void seek_force_decode(VGMSTREAM* vgmstream, int samples) { + sample_t* tmpbuf = vgmstream->tmpbuf; + size_t tmpbuf_size = vgmstream->tmpbuf_size; + int32_t buf_samples = tmpbuf_size / vgmstream->channels; /* base channels, no need to apply mixing */ + + while (samples) { + int to_do = samples; + if (to_do > buf_samples) + to_do = buf_samples; + + render_layout(tmpbuf, to_do, vgmstream); + /* no mixing */ + samples -= to_do; + } +} + +void seek_vgmstream(VGMSTREAM* vgmstream, int32_t seek_sample) { + play_state_t* ps = &vgmstream->pstate; + int play_forever = vgmstream->config.play_forever; + + int32_t decode_samples = 0; + int loop_count = -1; + int is_looped = vgmstream->loop_flag || vgmstream->loop_target > 0; /* loop target disabled loop flag during decode */ + + + /* will decode and loop until seek sample, but slower */ + //todo apply same loop logic as below, or pretend we have play_forever + settings? + if (!vgmstream->config_enabled) { + //;VGM_LOG("SEEK: simple seek=%i, cur=%i\n", seek_sample, vgmstream->current_sample); + if (seek_sample < vgmstream->current_sample) { + decode_samples = seek_sample; + reset_vgmstream(vgmstream); + } + else { + decode_samples = seek_sample - vgmstream->current_sample; + } + + seek_force_decode(vgmstream, decode_samples); + return; + } + + //todo could improve performance bit if hit_loop wasn't lost when calling reset + //todo wrong seek with ignore fade, also for layered layers (pass count to force loop + layers) + + + /* seeking to requested sample normally means decoding and discarding up to that point (from + * the beginning, or current position), but can be optimized a bit to decode less with some tricks: + * - seek may fall in part of the song that isn't actually decoding (due to config, like padding) + * - if file loops there is no need to decode N full loops, seek can be set relative to loop region + * - can decode to seek sample from current position or loop start depending on lowest + * + * some of the cases below could be simplified but the logic to get this going is kinda mind-bending + * + * (ex. with file = 100, pad=5s, trim=3s, loop=20s..90s) + * | pad-begin | body-begin | body-loop0 | body-loop1 | body-loop2 | fade | pad-end + beyond) + * 0 5s (-3s) 25s 95s 165s 235s 245s Ns + */ + + if (seek_sample < 0) + seek_sample = 0; + if (seek_sample > ps->play_duration && !play_forever) /* play forever can seek to any loop */ + seek_sample = ps->play_duration; + + //;VGM_LOG("SEEK: seek sample=%i, is_looped=%i\n", seek_sample, is_looped); + + /* start/pad-begin: consume pad samples */ + if (seek_sample < ps->pad_begin_duration) { + /* seek=3: pad=5-3=2 */ + decode_samples = 0; + + reset_vgmstream(vgmstream); + ps->pad_begin_left = ps->pad_begin_duration - seek_sample; + + //;VGM_LOG("SEEK: pad start / dec=%i\n", decode_samples); + } + + /* body: find position relative to decoder's current sample */ + else if (play_forever || seek_sample < ps->pad_begin_duration + ps->body_duration + ps->fade_duration) { + /* seek=10 would be seekr=10-5+3=8 inside decoder */ + int32_t seek_relative = seek_sample - ps->pad_begin_duration + ps->trim_begin_duration; + + + //;VGM_LOG("SEEK: body / seekr=%i, curr=%i\n", seek_relative, vgmstream->current_sample); + + /* seek can be in some part of the body, depending on looped/decoder's current/etc */ + if (!is_looped && seek_relative < vgmstream->current_sample) { + /* seekr=50s, curr=95 > restart + decode=50s */ + decode_samples = seek_relative; + reset_vgmstream(vgmstream); + + //;VGM_LOG("SEEK: non-loop reset / dec=%i\n", decode_samples); + } + else if (!is_looped && seek_relative < vgmstream->num_samples) { + /* seekr=95s, curr=50 > decode=95-50=45s */ + decode_samples = seek_relative - vgmstream->current_sample; + + //;VGM_LOG("SEEK: non-loop forward / dec=%i\n", decode_samples); + } + else if (!is_looped) { + /* seekr=120s (outside decode, can happen when body is set manually) */ + decode_samples = 0; + vgmstream->current_sample = vgmstream->num_samples + 1; + + //;VGM_LOG("SEEK: non-loop silence / dec=%i\n", decode_samples); + } + else if (seek_relative < vgmstream->loop_start_sample) { + /* seekr=6s > 6-5+3 > seek=4s inside decoder < 20s: decode 4s from start, or 1s if current was at 3s */ + + if (seek_relative < vgmstream->current_sample) { + /* seekr=9s, current=10s > decode=9s from start */ + decode_samples = seek_relative; + reset_vgmstream(vgmstream); + + //;VGM_LOG("SEEK: loop start reset / dec=%i\n", decode_samples); + } + else { + /* seekr=9s, current=8s > decode=1s from current */ + decode_samples = seek_relative - vgmstream->current_sample; + + //;VGM_LOG("SEEK: loop start forward / dec=%i\n", decode_samples); + } + } + else { + /* seek can be clamped between loop parts (relative to decoder's current_sample) to minimize decoding */ + int32_t loop_body, loop_seek, loop_curr; + + /* current must have reached loop start at some point */ + if (!vgmstream->hit_loop) { + int32_t skip_samples; + + if (vgmstream->current_sample >= vgmstream->loop_start_sample) { + VGM_LOG("SEEK: bad current sample %i vs %i\n", vgmstream->current_sample, vgmstream->loop_start_sample); + reset_vgmstream(vgmstream); + } + + skip_samples = (vgmstream->loop_start_sample - vgmstream->current_sample); + //;VGM_LOG("SEEK: must loop / skip=%i, curr=%i\n", skip_samples, vgmstream->current_sample); + + seek_force_decode(vgmstream, skip_samples); + } + + /* current must be in loop area (shouldn't happen?) */ + if (vgmstream->current_sample < vgmstream->loop_start_sample + || vgmstream->current_sample < vgmstream->loop_end_sample) { + //;VGM_LOG("SEEK: current outside loop area / curr=%i, ls=%i, le=%i\n", vgmstream->current_sample, vgmstream->current_sample, vgmstream->loop_end_sample); + seek_force_loop(vgmstream, 0); + } + + + loop_body = (vgmstream->loop_end_sample - vgmstream->loop_start_sample); + loop_seek = seek_relative - vgmstream->loop_start_sample; + loop_count = loop_seek / loop_body; + loop_seek = loop_seek % loop_body; + loop_curr = vgmstream->current_sample - vgmstream->loop_start_sample; + + /* when "ignore fade" is used and seek falls into non-fade part, this needs to seek right before it + so when calling seek_force_loop detection kicks in, and non-fade then decodes normally */ + if (vgmstream->loop_target && vgmstream->loop_target == loop_count) { + loop_seek = loop_body; + } + + //;VGM_LOG("SEEK: in loop / seekl=%i, loops=%i, cur=%i, dec=%i\n", loop_seek, loop_count, loop_curr, decode_samples); + if (loop_seek < loop_curr) { + decode_samples += loop_seek; + seek_force_loop(vgmstream, loop_count); + + //;VGM_LOG("SEEK: loop reset / dec=%i, loop=%i\n", decode_samples, loop_count); + } + else { + decode_samples += (loop_seek - loop_curr); + + //;VGM_LOG("SEEK: loop forward / dec=%i, loop=%i\n", decode_samples, loop_count); + } + + /* adjust fade if seek ends in fade region */ + if (!play_forever + && seek_sample >= ps->pad_begin_duration + ps->body_duration + && seek_sample < ps->pad_begin_duration + ps->body_duration + ps->fade_duration) { + ps->fade_left = ps->pad_begin_duration + ps->body_duration + ps->fade_duration - seek_sample; + //;VGM_LOG("SEEK: in fade / fade=%i, %i\n", ps->fade_left, ps->fade_duration); + } + } + + /* done at the end in case of reset */ + ps->pad_begin_left = 0; + ps->trim_begin_left = 0; + } + + /* pad end and beyond: ignored */ + else { + decode_samples = 0; + ps->pad_begin_left = 0; + ps->trim_begin_left = 0; + if (!is_looped) + vgmstream->current_sample = vgmstream->num_samples + 1; + + //;VGM_LOG("SEEK: end silence / dec=%i\n", decode_samples); + /* looping decoder state isn't changed (seek backwards could use current sample) */ + } + + + seek_force_decode(vgmstream, decode_samples); + + vgmstream->pstate.play_position = seek_sample; +} diff --git a/Frameworks/vgmstream/vgmstream/src/render.h b/Frameworks/vgmstream/vgmstream/src/render.h new file mode 100644 index 000000000..d9461367d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/render.h @@ -0,0 +1,10 @@ +#ifndef _RENDER_H +#define _RENDER_H + +#include "vgmstream.h" + +void free_layout(VGMSTREAM* vgmstream); +void reset_layout(VGMSTREAM* vgmstream); + + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 47688922e..1f9b36e85 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -1000,7 +1000,8 @@ size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf) { size_t pos; for (pos = 0; pos < buf_size; pos++) { - char c = read_8bit(offset + pos, sf); + uint8_t byte = read_u8(offset + pos, sf); + char c = (char)byte; if (buf) buf[pos] = c; if (c == '\0') return pos; @@ -1008,7 +1009,8 @@ size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf) { if (buf) buf[pos] = '\0'; return buf_size; } - if (c < 0x20 || (uint8_t)c > 0xA5) + /* UTF-8 only goes to 0x7F, but allow a bunch of Windows-1252 codes that some games use */ + if (byte < 0x20 || byte > 0xF0) goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 4d32231c1..72b7d343b 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -65,13 +65,13 @@ * to do file operations, as plugins may need to provide their own callbacks. * Reads from arbitrary offsets, meaning internally may need fseek equivalents during reads. */ typedef struct _STREAMFILE { - size_t (*read)(struct _STREAMFILE *, uint8_t * dst, off_t offset, size_t length); - size_t (*get_size)(struct _STREAMFILE *); - off_t (*get_offset)(struct _STREAMFILE *); //todo: DO NOT USE, NOT RESET PROPERLY (remove?) + size_t (*read)(struct _STREAMFILE*, uint8_t* dst, off_t offset, size_t length); + size_t (*get_size)(struct _STREAMFILE*); + off_t (*get_offset)(struct _STREAMFILE*); //todo: DO NOT USE, NOT RESET PROPERLY (remove?) /* for dual-file support */ - void (*get_name)(struct _STREAMFILE * ,char *name, size_t length); - struct _STREAMFILE * (*open)(struct _STREAMFILE *, const char * const filename, size_t buffersize); - void (*close)(struct _STREAMFILE *); + void (*get_name)(struct _STREAMFILE*, char* name, size_t length); + struct _STREAMFILE* (*open)(struct _STREAMFILE*, const char* const filename, size_t buffersize); + void (*close)(struct _STREAMFILE*); /* Substream selection for files with subsongs. Manually used in metas if supported. @@ -86,152 +86,152 @@ typedef struct _STREAMFILE { /* Opens a standard STREAMFILE, opening from path. * Uses stdio (FILE) for operations, thus plugins may not want to use it. */ -STREAMFILE* open_stdio_streamfile(const char *filename); +STREAMFILE* open_stdio_streamfile(const char* filename); /* Opens a standard STREAMFILE from a pre-opened FILE. */ -STREAMFILE* open_stdio_streamfile_by_file(FILE *file, const char *filename); +STREAMFILE* open_stdio_streamfile_by_file(FILE* file, const char* filename); /* Opens a STREAMFILE that does buffered IO. * Can be used when the underlying IO may be slow (like when using custom IO). * Buffer size is optional. */ -STREAMFILE* open_buffer_streamfile(STREAMFILE *streamfile, size_t buffer_size); -STREAMFILE* open_buffer_streamfile_f(STREAMFILE *streamfile, size_t buffer_size); +STREAMFILE* open_buffer_streamfile(STREAMFILE* sf, size_t buffer_size); +STREAMFILE* open_buffer_streamfile_f(STREAMFILE* sf, size_t buffer_size); /* Opens a STREAMFILE that doesn't close the underlying streamfile. * Calls to open won't wrap the new SF (assumes it needs to be closed). * Can be used in metas to test custom IO without closing the external SF. */ -STREAMFILE* open_wrap_streamfile(STREAMFILE *streamfile); -STREAMFILE* open_wrap_streamfile_f(STREAMFILE *streamfile); +STREAMFILE* open_wrap_streamfile(STREAMFILE* sf); +STREAMFILE* open_wrap_streamfile_f(STREAMFILE* sf); /* Opens a STREAMFILE that clamps reads to a section of a larger streamfile. * Can be used with subfiles inside a bigger file (to fool metas, or to simplify custom IO). */ -STREAMFILE* open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size); -STREAMFILE* open_clamp_streamfile_f(STREAMFILE *streamfile, off_t start, size_t size); +STREAMFILE* open_clamp_streamfile(STREAMFILE* sf, off_t start, size_t size); +STREAMFILE* open_clamp_streamfile_f(STREAMFILE* sf, off_t start, size_t size); /* Opens a STREAMFILE that uses custom IO for streamfile reads. * Can be used to modify data on the fly (ex. decryption), or even transform it from a format to another. * Data is an optional state struct of some size what will be malloc+copied on open. */ -STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback); -STREAMFILE* open_io_streamfile_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback); +STREAMFILE* open_io_streamfile(STREAMFILE* sf, void* data, size_t data_size, void* read_callback, void* size_callback); +STREAMFILE* open_io_streamfile_f(STREAMFILE* sf, void* data, size_t data_size, void* read_callback, void* size_callback); /* Same, but calls init on SF open and close on close, when malloc/free is needed. * Data struct may be used to hold malloc'd pointers and stuff. */ -STREAMFILE* open_io_streamfile_ex(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback); -STREAMFILE* open_io_streamfile_ex_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback); +STREAMFILE* open_io_streamfile_ex(STREAMFILE* sf, void* data, size_t data_size, void* read_callback, void* size_callback, void* init_callback, void* close_callback); +STREAMFILE* open_io_streamfile_ex_f(STREAMFILE* sf, void* data, size_t data_size, void* read_callback, void* size_callback, void* init_callback, void* close_callback); /* Opens a STREAMFILE that reports a fake name, but still re-opens itself properly. * Can be used to trick a meta's extension check (to call from another, with a modified SF). * When fakename isn't supplied it's read from the streamfile, and the extension swapped with fakeext. * If the fakename is an existing file, open won't work on it as it'll reopen the fake-named streamfile. */ -STREAMFILE* open_fakename_streamfile(STREAMFILE *streamfile, const char * fakename, const char * fakeext); -STREAMFILE* open_fakename_streamfile_f(STREAMFILE *streamfile, const char * fakename, const char * fakeext); +STREAMFILE* open_fakename_streamfile(STREAMFILE* sf, const char* fakename, const char* fakeext); +STREAMFILE* open_fakename_streamfile_f(STREAMFILE* sf, const char* fakename, const char* fakeext); /* Opens streamfile formed from multiple streamfiles, their data joined during reads. * Can be used when data is segmented in multiple separate files. * The first streamfile is used to get names, stream index and so on. */ -STREAMFILE* open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size); -STREAMFILE* open_multifile_streamfile_f(STREAMFILE **streamfiles, size_t streamfiles_size); +STREAMFILE* open_multifile_streamfile(STREAMFILE** sfs, size_t sfs_size); +STREAMFILE* open_multifile_streamfile_f(STREAMFILE** sfs, size_t sfs_size); /* Opens a STREAMFILE from a (path)+filename. * Just a wrapper, to avoid having to access the STREAMFILE's callbacks directly. */ -STREAMFILE* open_streamfile(STREAMFILE *streamfile, const char * pathname); +STREAMFILE* open_streamfile(STREAMFILE* sf, const char* pathname); /* Opens a STREAMFILE from a base pathname + new extension * Can be used to get companion headers. */ -STREAMFILE* open_streamfile_by_ext(STREAMFILE *streamfile, const char * ext); +STREAMFILE* open_streamfile_by_ext(STREAMFILE* sf, const char* ext); /* Opens a STREAMFILE from a base path + new filename. * Can be used to get companion files. Relative paths like * './filename', '../filename', 'dir/filename' also work. */ -STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * filename); +STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename); /* Reopen a STREAMFILE with a different buffer size, for fine-tuned bigfile parsing. * Uses default buffer size when buffer_size is 0 */ -STREAMFILE * reopen_streamfile(STREAMFILE *streamfile, size_t buffer_size); +STREAMFILE* reopen_streamfile(STREAMFILE* sf, size_t buffer_size); /* close a file, destroy the STREAMFILE object */ -static inline void close_streamfile(STREAMFILE * streamfile) { - if (streamfile!=NULL) - streamfile->close(streamfile); +static inline void close_streamfile(STREAMFILE* sf) { + if (sf != NULL) + sf->close(sf); } /* read from a file, returns number of bytes read */ -static inline size_t read_streamfile(uint8_t *dst, off_t offset, size_t length, STREAMFILE *streamfile) { - return streamfile->read(streamfile, dst, offset,length); +static inline size_t read_streamfile(uint8_t *dst, off_t offset, size_t length, STREAMFILE* sf) { + return sf->read(sf, dst, offset,length); } /* return file size */ -static inline size_t get_streamfile_size(STREAMFILE * streamfile) { - return streamfile->get_size(streamfile); +static inline size_t get_streamfile_size(STREAMFILE* sf) { + return sf->get_size(sf); } /* Sometimes you just need an int, and we're doing the buffering. * Note, however, that if these fail to read they'll return -1, * so that should not be a valid value or there should be some backup. */ -static inline int16_t read_16bitLE(off_t offset, STREAMFILE * streamfile) { +static inline int16_t read_16bitLE(off_t offset, STREAMFILE* sf) { uint8_t buf[2]; - if (read_streamfile(buf,offset,2,streamfile)!=2) return -1; + if (read_streamfile(buf,offset,2,sf)!=2) return -1; return get_16bitLE(buf); } -static inline int16_t read_16bitBE(off_t offset, STREAMFILE * streamfile) { +static inline int16_t read_16bitBE(off_t offset, STREAMFILE* sf) { uint8_t buf[2]; - if (read_streamfile(buf,offset,2,streamfile)!=2) return -1; + if (read_streamfile(buf,offset,2,sf)!=2) return -1; return get_16bitBE(buf); } -static inline int32_t read_32bitLE(off_t offset, STREAMFILE * streamfile) { +static inline int32_t read_32bitLE(off_t offset, STREAMFILE* sf) { uint8_t buf[4]; - if (read_streamfile(buf,offset,4,streamfile)!=4) return -1; + if (read_streamfile(buf,offset,4,sf)!=4) return -1; return get_32bitLE(buf); } -static inline int32_t read_32bitBE(off_t offset, STREAMFILE * streamfile) { +static inline int32_t read_32bitBE(off_t offset, STREAMFILE* sf) { uint8_t buf[4]; - if (read_streamfile(buf,offset,4,streamfile)!=4) return -1; + if (read_streamfile(buf,offset,4,sf)!=4) return -1; return get_32bitBE(buf); } -static inline int64_t read_64bitLE(off_t offset, STREAMFILE * streamfile) { +static inline int64_t read_64bitLE(off_t offset, STREAMFILE* sf) { uint8_t buf[8]; - if (read_streamfile(buf,offset,8,streamfile)!=8) return -1; + if (read_streamfile(buf,offset,8,sf)!=8) return -1; return get_64bitLE(buf); } -static inline int64_t read_64bitBE(off_t offset, STREAMFILE * streamfile) { +static inline int64_t read_64bitBE(off_t offset, STREAMFILE* sf) { uint8_t buf[8]; - if (read_streamfile(buf,offset,8,streamfile)!=8) return -1; + if (read_streamfile(buf,offset,8,sf)!=8) return -1; return get_64bitBE(buf); } -static inline int8_t read_8bit(off_t offset, STREAMFILE * streamfile) { +static inline int8_t read_8bit(off_t offset, STREAMFILE* sf) { uint8_t buf[1]; - if (read_streamfile(buf,offset,1,streamfile)!=1) return -1; + if (read_streamfile(buf,offset,1,sf)!=1) return -1; return buf[0]; } /* alias of the above */ -static inline int8_t read_s8 (off_t offset, STREAMFILE *sf) { return read_8bit(offset, sf); } -static inline uint8_t read_u8 (off_t offset, STREAMFILE *sf) { return (uint8_t) read_8bit(offset, sf); } -static inline int16_t read_s16le(off_t offset, STREAMFILE *sf) { return read_16bitLE(offset, sf); } -static inline uint16_t read_u16le(off_t offset, STREAMFILE *sf) { return (uint16_t)read_16bitLE(offset, sf); } -static inline int16_t read_s16be(off_t offset, STREAMFILE *sf) { return read_16bitBE(offset, sf); } -static inline uint16_t read_u16be(off_t offset, STREAMFILE *sf) { return (uint16_t)read_16bitBE(offset, sf); } -static inline int32_t read_s32le(off_t offset, STREAMFILE *sf) { return read_32bitLE(offset, sf); } -static inline uint32_t read_u32le(off_t offset, STREAMFILE *sf) { return (uint32_t)read_32bitLE(offset, sf); } -static inline int32_t read_s32be(off_t offset, STREAMFILE *sf) { return read_32bitBE(offset, sf); } -static inline uint32_t read_u32be(off_t offset, STREAMFILE *sf) { return (uint32_t)read_32bitBE(offset, sf); } -static inline int64_t read_s64be(off_t offset, STREAMFILE *sf) { return read_64bitBE(offset, sf); } -static inline uint64_t read_u64be(off_t offset, STREAMFILE *sf) { return (uint64_t)read_64bitBE(offset, sf); } -static inline int64_t read_s64le(off_t offset, STREAMFILE *sf) { return read_64bitLE(offset, sf); } -static inline uint64_t read_u64le(off_t offset, STREAMFILE *sf) { return (uint64_t)read_64bitLE(offset, sf); } +static inline int8_t read_s8 (off_t offset, STREAMFILE* sf) { return read_8bit(offset, sf); } +static inline uint8_t read_u8 (off_t offset, STREAMFILE* sf) { return (uint8_t) read_8bit(offset, sf); } +static inline int16_t read_s16le(off_t offset, STREAMFILE* sf) { return read_16bitLE(offset, sf); } +static inline uint16_t read_u16le(off_t offset, STREAMFILE* sf) { return (uint16_t)read_16bitLE(offset, sf); } +static inline int16_t read_s16be(off_t offset, STREAMFILE* sf) { return read_16bitBE(offset, sf); } +static inline uint16_t read_u16be(off_t offset, STREAMFILE* sf) { return (uint16_t)read_16bitBE(offset, sf); } +static inline int32_t read_s32le(off_t offset, STREAMFILE* sf) { return read_32bitLE(offset, sf); } +static inline uint32_t read_u32le(off_t offset, STREAMFILE* sf) { return (uint32_t)read_32bitLE(offset, sf); } +static inline int32_t read_s32be(off_t offset, STREAMFILE* sf) { return read_32bitBE(offset, sf); } +static inline uint32_t read_u32be(off_t offset, STREAMFILE* sf) { return (uint32_t)read_32bitBE(offset, sf); } +static inline int64_t read_s64be(off_t offset, STREAMFILE* sf) { return read_64bitBE(offset, sf); } +static inline uint64_t read_u64be(off_t offset, STREAMFILE* sf) { return (uint64_t)read_64bitBE(offset, sf); } +static inline int64_t read_s64le(off_t offset, STREAMFILE* sf) { return read_64bitLE(offset, sf); } +static inline uint64_t read_u64le(off_t offset, STREAMFILE* sf) { return (uint64_t)read_64bitLE(offset, sf); } /* The recommended int-to-float type punning in C is through union, but pointer casting * works too (though less portable due to aliasing rules?). For C++ memcpy seems * recommended. Both work in GCC and VS2015+ (not sure about older, ifdef as needed). */ -static inline float read_f32be(off_t offset, STREAMFILE *sf) { +static inline float read_f32be(off_t offset, STREAMFILE* sf) { union { uint32_t u32; float f32; @@ -239,7 +239,7 @@ static inline float read_f32be(off_t offset, STREAMFILE *sf) { temp.u32 = read_u32be(offset, sf); return temp.f32; } -static inline float read_f32le(off_t offset, STREAMFILE * sf) { +static inline float read_f32le(off_t offset, STREAMFILE* sf) { union { uint32_t u32; float f32; @@ -248,12 +248,12 @@ static inline float read_f32le(off_t offset, STREAMFILE * sf) { return temp.f32; } #if 0 -static inline float read_f32be_p(off_t offset, STREAMFILE *sf) { +static inline float read_f32be_p(off_t offset, STREAMFILE* sf) { uint32_t sample_int = read_u32be(offset, sf); float* sample_float = (float*)&sample_int; return *sample_float; } -static inline float read_f32be_m(off_t offset, STREAMFILE *sf) { +static inline float read_f32be_m(off_t offset, STREAMFILE* sf) { uint32_t sample_int = read_u32be(offset, sf); float sample_float; memcpy(&sample_float, &sample_int, sizeof(uint32_t)); @@ -288,18 +288,18 @@ void sf_reader_init(sf_reader *r, int big_endian) { */ #endif #if 0 //todo improve + test + simplify code (maybe not inline?) -static inline int read_s4h(off_t offset, STREAMFILE * streamfile) { +static inline int read_s4h(off_t offset, STREAMFILE* sf) { uint8_t byte = read_u8(offset, streamfile); return get_nibble_signed(byte, 1); } -static inline int read_u4h(off_t offset, STREAMFILE * streamfile) { +static inline int read_u4h(off_t offset, STREAMFILE* sf) { uint8_t byte = read_u8(offset, streamfile); return (byte >> 4) & 0x0f; } -static inline int read_s4l(off_t offset, STREAMFILE * streamfile) { +static inline int read_s4l(off_t offset, STREAMFILE* sf) { ... } -static inline int read_u4l(off_t offset, STREAMFILE * streamfile) { +static inline int read_u4l(off_t offset, STREAMFILE* sf) { ... } static inline int max_s32(int32_t a, int32_t b) { return a > b ? a : b; } @@ -309,14 +309,14 @@ static inline int min_s32(int32_t a, int32_t b) { return a < b ? a : b; } //TODO: maybe move to streamfile.c /* guess byte endianness from a given value, return true if big endian and false if little endian */ -static inline int guess_endianness16bit(off_t offset, STREAMFILE * streamfile) { +static inline int guess_endianness16bit(off_t offset, STREAMFILE* sf) { uint8_t buf[0x02]; - if (read_streamfile(buf,offset,0x02,streamfile) != 0x02) return -1; /* ? */ + if (read_streamfile(buf, offset, 0x02, sf) != 0x02) return -1; /* ? */ return (uint16_t)get_16bitLE(buf) > (uint16_t)get_16bitBE(buf) ? 1 : 0; } -static inline int guess_endianness32bit(off_t offset, STREAMFILE * streamfile) { +static inline int guess_endianness32bit(off_t offset, STREAMFILE* sf) { uint8_t buf[0x04]; - if (read_streamfile(buf,offset,0x04,streamfile) != 0x04) return -1; /* ? */ + if (read_streamfile(buf, offset, 0x04, sf) != 0x04) return -1; /* ? */ return (uint32_t)get_32bitLE(buf) > (uint32_t)get_32bitBE(buf) ? 1 : 0; } @@ -331,10 +331,10 @@ static inline size_t align_size_to_block(size_t value, size_t block_align) { /* Read into dst a line delimited by CRLF (Windows) / LF (Unux) / CR (Mac) / EOF, null-terminated * and without line feeds. Returns bytes read (including CR/LF), *not* the same as string length. * p_line_ok is set to 1 if the complete line was read; pass NULL to ignore. */ -size_t read_line(char *buf, int buf_size, off_t offset, STREAMFILE *sf, int *p_line_ok); +size_t read_line(char* buf, int buf_size, off_t offset, STREAMFILE* sf, int* p_line_ok); /* reads a c-string (ANSI only), up to bufsize or NULL, returning size. buf is optional (works as get_string_size). */ -size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf); +size_t read_string(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf); /* reads a UTF16 string... but actually only as ANSI (discards the upper byte) */ size_t read_string_utf16(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf, int big_endian); size_t read_string_utf16le(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf); @@ -343,31 +343,31 @@ size_t read_string_utf16be(char* buf, size_t buf_size, off_t offset, STREAMFILE* /* Opens a file containing decryption keys and copies to buffer. * Tries "(name.ext)key" (per song), "(.ext)key" (per folder) keynames. * returns size of key if found and copied */ -size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf); +size_t read_key_file(uint8_t* buf, size_t buf_size, STREAMFILE* sf); /* hack to allow relative paths in various OSs */ -void fix_dir_separators(char *filename); +void fix_dir_separators(char* filename); /* Checks if the stream filename is one of the extensions (comma-separated, ex. "adx" or "adx,aix"). * Empty is ok to accept files without extension ("", "adx,,aix"). Returns 0 on failure */ -int check_extensions(STREAMFILE *streamFile, const char * cmp_exts); +int check_extensions(STREAMFILE* sf, const char* cmp_exts); /* chunk-style file helpers */ -int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); -int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); -int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_size, int zero_size_end); +int find_chunk_be(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t* p_chunk_offset, size_t* p_chunk_size); +int find_chunk_le(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t* p_chunk_offset, size_t* p_chunk_size); +int find_chunk(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t* p_chunk_offset, size_t* p_chunk_size, int big_endian_size, int zero_size_end); /* find a RIFF-style chunk (with chunk_size not including id and size) */ -int find_chunk_riff_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size); -int find_chunk_riff_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size); +int find_chunk_riff_le(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t* p_chunk_offset, size_t* p_chunk_size); +int find_chunk_riff_be(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t* p_chunk_offset, size_t* p_chunk_size); /* same with chunk ids in variable endianess (so instead of "fmt " has " tmf" */ -int find_chunk_riff_ve(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian); +int find_chunk_riff_ve(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t* p_chunk_offset, size_t* p_chunk_size, int big_endian); /* filename helpers */ -void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size); -void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size); -void get_streamfile_basename(STREAMFILE *streamFile, char * buffer, size_t size); -void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size); -void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size); +void get_streamfile_name(STREAMFILE* sf, char* buf, size_t size); +void get_streamfile_filename(STREAMFILE* sf, char* buf, size_t size); +void get_streamfile_basename(STREAMFILE* sf, char* buf, size_t size); +void get_streamfile_path(STREAMFILE* sf, char* buf, size_t size); +void get_streamfile_ext(STREAMFILE* sf, char* buf, size_t size); -void dump_streamfile(STREAMFILE *streamFile, int num); +void dump_streamfile(STREAMFILE* sf, int num); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/util.h b/Frameworks/vgmstream/vgmstream/src/util.h index 21a98d89b..f27a8a400 100644 --- a/Frameworks/vgmstream/vgmstream/src/util.h +++ b/Frameworks/vgmstream/vgmstream/src/util.h @@ -11,55 +11,55 @@ /* host endian independent multi-byte integer reading */ -static inline int16_t get_16bitBE(uint8_t * p) { +static inline int16_t get_16bitBE(const uint8_t* p) { return (p[0]<<8) | (p[1]); } -static inline int16_t get_16bitLE(uint8_t * p) { +static inline int16_t get_16bitLE(const uint8_t* p) { return (p[0]) | (p[1]<<8); } -static inline int32_t get_32bitBE(uint8_t * p) { +static inline int32_t get_32bitBE(const uint8_t* p) { return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | (p[3]); } -static inline int32_t get_32bitLE(uint8_t * p) { +static inline int32_t get_32bitLE(const uint8_t* p) { return (p[0]) | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); } -static inline int64_t get_64bitBE(uint8_t * p) { +static inline int64_t get_64bitBE(const uint8_t* p) { return (uint64_t)(((uint64_t)p[0]<<56) | ((uint64_t)p[1]<<48) | ((uint64_t)p[2]<<40) | ((uint64_t)p[3]<<32) | ((uint64_t)p[4]<<24) | ((uint64_t)p[5]<<16) | ((uint64_t)p[6]<<8) | ((uint64_t)p[7])); } -static inline int64_t get_64bitLE(uint8_t * p) { +static inline int64_t get_64bitLE(const uint8_t* p) { return (uint64_t)(((uint64_t)p[0]) | ((uint64_t)p[1]<<8) | ((uint64_t)p[2]<<16) | ((uint64_t)p[3]<<24) | ((uint64_t)p[4]<<32) | ((uint64_t)p[5]<<40) | ((uint64_t)p[6]<<48) | ((uint64_t)p[7]<<56)); } /* alias of the above */ -static inline int8_t get_s8 (uint8_t *p) { return ( int8_t)p[0]; } -static inline uint8_t get_u8 (uint8_t *p) { return (uint8_t)p[0]; } -static inline int16_t get_s16le(uint8_t *p) { return ( int16_t)get_16bitLE(p); } -static inline uint16_t get_u16le(uint8_t *p) { return (uint16_t)get_16bitLE(p); } -static inline int16_t get_s16be(uint8_t *p) { return ( int16_t)get_16bitBE(p); } -static inline uint16_t get_u16be(uint8_t *p) { return (uint16_t)get_16bitBE(p); } -static inline int32_t get_s32le(uint8_t *p) { return ( int32_t)get_32bitLE(p); } -static inline uint32_t get_u32le(uint8_t *p) { return (uint32_t)get_32bitLE(p); } -static inline int32_t get_s32be(uint8_t *p) { return ( int32_t)get_32bitBE(p); } -static inline uint32_t get_u32be(uint8_t *p) { return (uint32_t)get_32bitBE(p); } -static inline int64_t get_s64be(uint8_t *p) { return ( int64_t)get_64bitLE(p); } -static inline uint64_t get_u64be(uint8_t *p) { return (uint64_t)get_64bitLE(p); } -static inline int64_t get_s64le(uint8_t *p) { return ( int64_t)get_64bitBE(p); } -static inline uint64_t get_u64le(uint8_t *p) { return (uint64_t)get_64bitBE(p); } +static inline int8_t get_s8 (const uint8_t* p) { return ( int8_t)p[0]; } +static inline uint8_t get_u8 (const uint8_t* p) { return (uint8_t)p[0]; } +static inline int16_t get_s16le(const uint8_t* p) { return ( int16_t)get_16bitLE(p); } +static inline uint16_t get_u16le(const uint8_t* p) { return (uint16_t)get_16bitLE(p); } +static inline int16_t get_s16be(const uint8_t* p) { return ( int16_t)get_16bitBE(p); } +static inline uint16_t get_u16be(const uint8_t* p) { return (uint16_t)get_16bitBE(p); } +static inline int32_t get_s32le(const uint8_t* p) { return ( int32_t)get_32bitLE(p); } +static inline uint32_t get_u32le(const uint8_t* p) { return (uint32_t)get_32bitLE(p); } +static inline int32_t get_s32be(const uint8_t* p) { return ( int32_t)get_32bitBE(p); } +static inline uint32_t get_u32be(const uint8_t* p) { return (uint32_t)get_32bitBE(p); } +static inline int64_t get_s64le(const uint8_t* p) { return ( int64_t)get_64bitLE(p); } +static inline uint64_t get_u64le(const uint8_t* p) { return (uint64_t)get_64bitLE(p); } +static inline int64_t get_s64be(const uint8_t* p) { return ( int64_t)get_64bitBE(p); } +static inline uint64_t get_u64be(const uint8_t* p) { return (uint64_t)get_64bitBE(p); } -void put_8bit(uint8_t * buf, int8_t i); +void put_8bit(uint8_t* buf, int8_t i); -void put_16bitLE(uint8_t * buf, int16_t i); +void put_16bitLE(uint8_t* buf, int16_t i); -void put_32bitLE(uint8_t * buf, int32_t i); +void put_32bitLE(uint8_t* buf, int32_t i); -void put_16bitBE(uint8_t * buf, int16_t i); +void put_16bitBE(uint8_t* buf, int16_t i); -void put_32bitBE(uint8_t * buf, int32_t i); +void put_32bitBE(uint8_t* buf, int32_t i); /* alias of the above */ //TODO: improve #define put_u8 put_8bit @@ -67,6 +67,11 @@ void put_32bitBE(uint8_t * buf, int32_t i); #define put_u32le put_32bitLE #define put_u16be put_16bitBE #define put_u32be put_32bitBE +#define put_s8 put_8bit +#define put_s16le put_16bitLE +#define put_s32le put_32bitLE +#define put_s16be put_16bitBE +#define put_s32be put_32bitBE /* signed nibbles come up a lot */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index e58ab5326..fb46a8684 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -9,13 +9,15 @@ #include "meta/meta.h" #include "layout/layout.h" #include "coding/coding.h" +#include "decode.h" +#include "render.h" #include "mixing.h" -static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *streamFile, VGMSTREAM* (*init_vgmstream_function)(STREAMFILE*)); +static void try_dual_file_stereo(VGMSTREAM* opened_vgmstream, STREAMFILE* sf, VGMSTREAM* (*init_vgmstream_function)(STREAMFILE*)); /* list of metadata parser functions that will recognize files, used on init */ -VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { +VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_adx, init_vgmstream_brstm, init_vgmstream_bfwav, @@ -45,7 +47,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_rxw, init_vgmstream_ngc_dsp_stm, init_vgmstream_ps2_exst, - init_vgmstream_ps2_svag, + init_vgmstream_svag_kcet, init_vgmstream_mib_mih, init_vgmstream_ngc_mpdsp, init_vgmstream_ps2_mic, @@ -297,7 +299,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_kt_wiibgm, init_vgmstream_ktss, init_vgmstream_hca, - init_vgmstream_ps2_svag_snk, + init_vgmstream_svag_snk, init_vgmstream_ps2_vds_vdm, init_vgmstream_x360_cxs, init_vgmstream_dsp_adx, @@ -501,6 +503,11 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_mups, init_vgmstream_kat, init_vgmstream_pcm_success, + init_vgmstream_ktsc, + init_vgmstream_adp_konami, + init_vgmstream_zwv, + init_vgmstream_dsb, + init_vgmstream_bsf, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ @@ -518,18 +525,22 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { }; +/*****************************************************************************/ +/* INIT/META */ +/*****************************************************************************/ + /* internal version with all parameters */ -static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { +static VGMSTREAM* init_vgmstream_internal(STREAMFILE* sf) { int i, fcns_size; - - if (!streamFile) + + if (!sf) return NULL; fcns_size = (sizeof(init_vgmstream_functions)/sizeof(init_vgmstream_functions[0])); /* try a series of formats, see which works */ for (i = 0; i < fcns_size; i++) { /* call init function and see if valid VGMSTREAM was returned */ - VGMSTREAM * vgmstream = (init_vgmstream_functions[i])(streamFile); + VGMSTREAM* vgmstream = (init_vgmstream_functions[i])(sf); if (!vgmstream) continue; @@ -562,7 +573,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { /* test if candidate for dual stereo */ if (vgmstream->channels == 1 && vgmstream->allow_dual_stereo == 1) { - try_dual_file_stereo(vgmstream, streamFile, init_vgmstream_functions[i]); + try_dual_file_stereo(vgmstream, sf, init_vgmstream_functions[i]); } /* clean as loops are readable metadata but loop fields may contain garbage @@ -575,7 +586,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG /* check FFmpeg streams here, for lack of a better place */ if (vgmstream->coding_type == coding_FFmpeg) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; + ffmpeg_codec_data *data = vgmstream->codec_data; if (data && data->streamCount && !vgmstream->num_streams) { vgmstream->num_streams = data->streamCount; } @@ -612,7 +623,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { /* save info */ /* stream_index 0 may be used by plugins to signal "vgmstream default" (IOW don't force to 1) */ if (vgmstream->stream_index == 0) { - vgmstream->stream_index = streamFile->stream_index; + vgmstream->stream_index = sf->stream_index; } @@ -625,7 +636,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { return NULL; } -void setup_vgmstream(VGMSTREAM * vgmstream) { +void setup_vgmstream(VGMSTREAM* vgmstream) { /* save start things so we can restart when seeking */ memcpy(vgmstream->start_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); @@ -637,22 +648,22 @@ void setup_vgmstream(VGMSTREAM * vgmstream) { /* format detection and VGMSTREAM setup, uses default parameters */ -VGMSTREAM * init_vgmstream(const char * const filename) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *streamFile = open_stdio_streamfile(filename); - if (streamFile) { - vgmstream = init_vgmstream_from_STREAMFILE(streamFile); - close_streamfile(streamFile); +VGMSTREAM* init_vgmstream(const char* const filename) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf = open_stdio_streamfile(filename); + if (sf) { + vgmstream = init_vgmstream_from_STREAMFILE(sf); + close_streamfile(sf); } return vgmstream; } -VGMSTREAM * init_vgmstream_from_STREAMFILE(STREAMFILE *streamFile) { - return init_vgmstream_internal(streamFile); +VGMSTREAM* init_vgmstream_from_STREAMFILE(STREAMFILE* sf) { + return init_vgmstream_internal(sf); } /* Reset a VGMSTREAM to its state at the start of playback (when a plugin seeks back to zero). */ -void reset_vgmstream(VGMSTREAM * vgmstream) { +void reset_vgmstream(VGMSTREAM* vgmstream) { /* reset the VGMSTREAM and channels back to their original state */ memcpy(vgmstream, vgmstream->start_vgmstream, sizeof(VGMSTREAM)); @@ -662,118 +673,17 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { * Otherwise hit_loop will be 0 and it will be copied over anyway when we * really hit the loop start. */ - /* reset custom codec */ -#ifdef VGM_USE_VORBIS - if (vgmstream->coding_type == coding_OGG_VORBIS) { - reset_ogg_vorbis(vgmstream); - } + reset_codec(vgmstream); - if (vgmstream->coding_type == coding_VORBIS_custom) { - reset_vorbis_custom(vgmstream); - } -#endif - - if (vgmstream->coding_type == coding_CIRCUS_VQ) { - reset_circus_vq(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_RELIC) { - reset_relic(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_CRI_HCA) { - reset_hca(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_UBI_ADPCM) { - reset_ubi_adpcm(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_IMUSE) { - reset_imuse(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_EA_MT) { - reset_ea_mt(vgmstream); - } - -#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - if (vgmstream->coding_type == coding_MP4_AAC) { - reset_mp4_aac(vgmstream); - } -#endif - -#ifdef VGM_USE_MPEG - if (vgmstream->coding_type == coding_MPEG_custom || - vgmstream->coding_type == coding_MPEG_ealayer3 || - vgmstream->coding_type == coding_MPEG_layer1 || - vgmstream->coding_type == coding_MPEG_layer2 || - vgmstream->coding_type == coding_MPEG_layer3) { - reset_mpeg(vgmstream); - } -#endif - -#ifdef VGM_USE_G7221 - if (vgmstream->coding_type == coding_G7221C) { - reset_g7221(vgmstream->codec_data); - } -#endif - -#ifdef VGM_USE_G719 - if (vgmstream->coding_type == coding_G719) { - reset_g719(vgmstream->codec_data, vgmstream->channels); - } -#endif - -#ifdef VGM_USE_MAIATRAC3PLUS - if (vgmstream->coding_type == coding_AT3plus) { - reset_at3plus(vgmstream); - } -#endif - -#ifdef VGM_USE_ATRAC9 - if (vgmstream->coding_type == coding_ATRAC9) { - reset_atrac9(vgmstream); - } -#endif - -#ifdef VGM_USE_CELT - if (vgmstream->coding_type == coding_CELT_FSB) { - reset_celt_fsb(vgmstream); - } -#endif - -#ifdef VGM_USE_FFMPEG - if (vgmstream->coding_type == coding_FFmpeg) { - reset_ffmpeg(vgmstream); - } -#endif - - if (vgmstream->coding_type == coding_ACM) { - reset_acm(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_NWA) { - nwa_codec_data *data = vgmstream->codec_data; - if (data) reset_nwa(data->nwa); - } - - /* reset custom layouts */ - if (vgmstream->layout_type == layout_segmented) { - reset_layout_segmented(vgmstream->layout_data); - } - - if (vgmstream->layout_type == layout_layered) { - reset_layout_layered(vgmstream->layout_data); - } + reset_layout(vgmstream); /* note that this does not reset the constituent STREAMFILES - * (vgmstream->ch[N].streamfiles' internal state, though shouldn't matter) */ + * (vgmstream->ch[N].streamfiles' internal state, like internal offset, though shouldn't matter) */ } /* Allocate memory and setup a VGMSTREAM */ -VGMSTREAM * allocate_vgmstream(int channel_count, int loop_flag) { - VGMSTREAM * vgmstream; +VGMSTREAM* allocate_vgmstream(int channel_count, int loop_flag) { + VGMSTREAM* vgmstream; /* up to ~16-24 aren't too rare for multilayered files, more is probably a bug */ if (channel_count <= 0 || channel_count > VGMSTREAM_MAX_CHANNELS) { @@ -801,7 +711,7 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int loop_flag) { /* create vgmstream + main structs (other data is 0'ed) */ vgmstream = calloc(1,sizeof(VGMSTREAM)); if (!vgmstream) return NULL; - + vgmstream->start_vgmstream = calloc(1,sizeof(VGMSTREAM)); if (!vgmstream->start_vgmstream) goto fail; @@ -816,16 +726,25 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int loop_flag) { if (!vgmstream->loop_ch) goto fail; } + /* garbage buffer for decode discarding (local bufs may cause stack overflows with segments/layers) + * in theory the bigger the better but in practice there isn't much difference */ + vgmstream->tmpbuf_size = 0x10000; /* for all channels */ + vgmstream->tmpbuf = malloc(sizeof(sample_t) * vgmstream->tmpbuf_size); + + vgmstream->channels = channel_count; vgmstream->loop_flag = loop_flag; mixing_init(vgmstream); /* pre-init */ + /* BEWARE: try_dual_file_stereo does some free'ing too */ + //vgmstream->stream_name_size = STREAM_NAME_SIZE; return vgmstream; fail: if (vgmstream) { mixing_close(vgmstream); + free(vgmstream->tmpbuf); free(vgmstream->ch); free(vgmstream->start_ch); free(vgmstream->loop_ch); @@ -835,139 +754,15 @@ fail: return NULL; } -void close_vgmstream(VGMSTREAM * vgmstream) { +void close_vgmstream(VGMSTREAM* vgmstream) { if (!vgmstream) return; - /* free custom codecs */ -#ifdef VGM_USE_VORBIS - if (vgmstream->coding_type == coding_OGG_VORBIS) { - free_ogg_vorbis(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } + free_codec(vgmstream); + vgmstream->codec_data = NULL; - if (vgmstream->coding_type == coding_VORBIS_custom) { - free_vorbis_custom(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - - if (vgmstream->coding_type == coding_CIRCUS_VQ) { - free_circus_vq(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_RELIC) { - free_relic(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_CRI_HCA) { - free_hca(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_UBI_ADPCM) { - free_ubi_adpcm(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_IMUSE) { - free_imuse(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_EA_MT) { - free_ea_mt(vgmstream->codec_data, vgmstream->channels); - vgmstream->codec_data = NULL; - } - -#ifdef VGM_USE_FFMPEG - if (vgmstream->coding_type == coding_FFmpeg) { - free_ffmpeg(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - -#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - if (vgmstream->coding_type == coding_MP4_AAC) { - free_mp4_aac(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - -#ifdef VGM_USE_MPEG - if (vgmstream->coding_type == coding_MPEG_custom || - vgmstream->coding_type == coding_MPEG_ealayer3 || - vgmstream->coding_type == coding_MPEG_layer1 || - vgmstream->coding_type == coding_MPEG_layer2 || - vgmstream->coding_type == coding_MPEG_layer3) { - free_mpeg(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - -#ifdef VGM_USE_G7221 - if (vgmstream->coding_type == coding_G7221C) { - free_g7221(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - -#ifdef VGM_USE_G719 - if (vgmstream->coding_type == coding_G719) { - free_g719(vgmstream->codec_data, vgmstream->channels); - vgmstream->codec_data = NULL; - } -#endif - -#ifdef VGM_USE_MAIATRAC3PLUS - if (vgmstream->coding_type == coding_AT3plus) { - free_at3plus(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - -#ifdef VGM_USE_ATRAC9 - if (vgmstream->coding_type == coding_ATRAC9) { - free_atrac9(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - -#ifdef VGM_USE_CELT - if (vgmstream->coding_type == coding_CELT_FSB) { - free_celt_fsb(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - - if (vgmstream->coding_type == coding_ACM) { - free_acm(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_NWA) { - if (vgmstream->codec_data) { - nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data; - if (data->nwa) - close_nwa(data->nwa); - free(data); - vgmstream->codec_data = NULL; - } - } - - - /* free custom layouts */ - if (vgmstream->layout_type == layout_segmented) { - free_layout_segmented(vgmstream->layout_data); - vgmstream->layout_data = NULL; - } - - if (vgmstream->layout_type == layout_layered) { - free_layout_layered(vgmstream->layout_data); - vgmstream->layout_data = NULL; - } + free_layout(vgmstream); + vgmstream->layout_data = NULL; /* now that the special cases have had their chance, clean up the standard items */ @@ -990,6 +785,7 @@ void close_vgmstream(VGMSTREAM * vgmstream) { } mixing_close(vgmstream); + free(vgmstream->tmpbuf); free(vgmstream->ch); free(vgmstream->start_ch); free(vgmstream->loop_ch); @@ -997,43 +793,30 @@ void close_vgmstream(VGMSTREAM * vgmstream) { free(vgmstream); } -/* calculate samples based on player's config */ -int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM * vgmstream) { - if (vgmstream->loop_flag) { - if (vgmstream->loop_target == (int)looptimes) { /* set externally, as this function is info-only */ - /* Continue playing the file normally after looping, instead of fading. - * Most files cut abruply after the loop, but some do have proper endings. - * With looptimes = 1 this option should give the same output vs loop disabled */ - int loop_count = (int)looptimes; /* no half loops allowed */ - return vgmstream->loop_start_sample - + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count - + (vgmstream->num_samples - vgmstream->loop_end_sample); - } - else { - return vgmstream->loop_start_sample - + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * looptimes - + (fadedelayseconds + fadeseconds) * vgmstream->sample_rate; - } - } - else { - return vgmstream->num_samples; - } -} - void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sample, int loop_end_sample) { if (!vgmstream) return; + /* ignore bad values (may happen with layers + TXTP loop install) */ + if (loop_flag && (loop_start_sample < 0 || + loop_start_sample > loop_end_sample || + loop_end_sample > vgmstream->num_samples)) + return; + /* this requires a bit more messing with the VGMSTREAM than I'm comfortable with... */ if (loop_flag && !vgmstream->loop_flag && !vgmstream->loop_ch) { - vgmstream->loop_ch = calloc(vgmstream->channels,sizeof(VGMSTREAMCHANNEL)); + vgmstream->loop_ch = calloc(vgmstream->channels, sizeof(VGMSTREAMCHANNEL)); if (!vgmstream->loop_ch) loop_flag = 0; /* ??? */ } +#if 0 + /* allow in case loop_flag is re-enabled later */ else if (!loop_flag && vgmstream->loop_flag) { - free(vgmstream->loop_ch); /* not important though */ + free(vgmstream->loop_ch); vgmstream->loop_ch = NULL; } +#endif vgmstream->loop_flag = loop_flag; + if (loop_flag) { vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; @@ -1048,9 +831,12 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa /* propagate changes to layouts that need them */ if (vgmstream->layout_type == layout_layered) { int i; - layered_layout_data *data = vgmstream->layout_data; + layered_layout_data* data = vgmstream->layout_data; + + /* layered loops using the internal VGMSTREAMs */ for (i = 0; i < data->layer_count; i++) { - vgmstream_force_loop(data->layers[i], loop_flag, loop_start_sample, loop_end_sample); + if (!data->layers[i]->config_enabled) /* only in simple mode */ + vgmstream_force_loop(data->layers[i], loop_flag, loop_start_sample, loop_end_sample); /* layer's force_loop also calls setup_vgmstream, no need to do it here */ } } @@ -1063,6 +849,8 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target) { if (!vgmstream) return; + if (!vgmstream->loop_flag) return; + vgmstream->loop_target = loop_target; /* loop count must be rounded (int) as otherwise target is meaningless */ @@ -1080,1320 +868,13 @@ void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target) { } -/* Decode data into sample buffer */ -void render_vgmstream(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - switch (vgmstream->layout_type) { - case layout_interleave: - render_vgmstream_interleave(buffer,sample_count,vgmstream); - break; - case layout_none: - render_vgmstream_flat(buffer,sample_count,vgmstream); - break; - case layout_blocked_mxch: - case layout_blocked_ast: - case layout_blocked_halpst: - case layout_blocked_xa: - case layout_blocked_ea_schl: - case layout_blocked_ea_1snh: - case layout_blocked_caf: - case layout_blocked_wsi: - case layout_blocked_str_snds: - case layout_blocked_ws_aud: - case layout_blocked_matx: - case layout_blocked_dec: - case layout_blocked_vs: - case layout_blocked_mul: - case layout_blocked_gsb: - case layout_blocked_xvas: - case layout_blocked_thp: - case layout_blocked_filp: - case layout_blocked_ivaud: - case layout_blocked_ea_swvr: - case layout_blocked_adm: - case layout_blocked_bdsp: - case layout_blocked_tra: - case layout_blocked_ps2_iab: - case layout_blocked_vs_str: - case layout_blocked_rws: - case layout_blocked_hwas: - case layout_blocked_ea_sns: - case layout_blocked_awc: - case layout_blocked_vgs: - case layout_blocked_vawx: - case layout_blocked_xvag_subsong: - case layout_blocked_ea_wve_au00: - case layout_blocked_ea_wve_ad10: - case layout_blocked_sthd: - case layout_blocked_h4m: - case layout_blocked_xa_aiff: - case layout_blocked_vs_square: - case layout_blocked_vid1: - case layout_blocked_ubi_sce: - render_vgmstream_blocked(buffer,sample_count,vgmstream); - break; - case layout_segmented: - render_vgmstream_segmented(buffer,sample_count,vgmstream); - break; - case layout_layered: - render_vgmstream_layered(buffer,sample_count,vgmstream); - break; - default: - break; - } - - mix_vgmstream(buffer, sample_count, vgmstream); -} - -/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */ -int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { - /* Value returned here is the max (or less) that vgmstream will ask a decoder per - * "decode_x" call. Decoders with variable samples per frame or internal discard - * may return 0 here and handle arbitrary samples_to_do values internally - * (or some internal sample buffer max too). */ - - switch (vgmstream->coding_type) { - case coding_CRI_ADX: - case coding_CRI_ADX_fixed: - case coding_CRI_ADX_exp: - case coding_CRI_ADX_enc_8: - case coding_CRI_ADX_enc_9: - return (vgmstream->interleave_block_size - 2) * 2; - - case coding_NGC_DSP: - case coding_NGC_DSP_subint: - return 14; - case coding_NGC_AFC: - case coding_VADPCM: - return 16; - case coding_NGC_DTK: - return 28; - case coding_G721: - return 1; - - case coding_PCM16LE: - case coding_PCM16BE: - case coding_PCM16_int: - case coding_PCM8: - case coding_PCM8_int: - case coding_PCM8_U: - case coding_PCM8_U_int: - case coding_PCM8_SB: - case coding_ULAW: - case coding_ULAW_int: - case coding_ALAW: - case coding_PCMFLOAT: - return 1; -#ifdef VGM_USE_VORBIS - case coding_OGG_VORBIS: - case coding_VORBIS_custom: -#endif -#ifdef VGM_USE_MPEG - case coding_MPEG_custom: - case coding_MPEG_ealayer3: - case coding_MPEG_layer1: - case coding_MPEG_layer2: - case coding_MPEG_layer3: -#endif - case coding_SDX2: - case coding_SDX2_int: - case coding_CBD2: - case coding_ACM: - case coding_DERF: - case coding_NWA: - case coding_SASSC: - case coding_CIRCUS_ADPCM: - return 1; - - case coding_IMA: - case coding_DVI_IMA: - case coding_SNDS_IMA: - case coding_OTNS_IMA: - case coding_UBI_IMA: - case coding_OKI16: - case coding_OKI4S: - case coding_MTF_IMA: - return 1; - case coding_PCM4: - case coding_PCM4_U: - case coding_IMA_int: - case coding_DVI_IMA_int: - case coding_3DS_IMA: - case coding_WV6_IMA: - case coding_ALP_IMA: - case coding_FFTA2_IMA: - case coding_BLITZ_IMA: - case coding_PCFX: - return 2; - case coding_XBOX_IMA: - case coding_XBOX_IMA_mch: - case coding_XBOX_IMA_int: - case coding_FSB_IMA: - case coding_WWISE_IMA: - return 64; - case coding_APPLE_IMA4: - return 64; - case coding_MS_IMA: - case coding_REF_IMA: - return ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; - case coding_RAD_IMA: - return (vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels; - case coding_NDS_IMA: - case coding_DAT4_IMA: - return (vgmstream->interleave_block_size - 0x04) * 2; - case coding_AWC_IMA: - return (0x800 - 0x04) * 2; - case coding_RAD_IMA_mono: - return 32; - case coding_H4M_IMA: - return 0; /* variable (block-controlled) */ - - case coding_XA: - return 28*8 / vgmstream->channels; /* 8 subframes per frame, mono/stereo */ - case coding_PSX: - case coding_PSX_badflags: - case coding_HEVAG: - return 28; - case coding_PSX_cfg: - case coding_PSX_pivotal: - return (vgmstream->interleave_block_size - 0x01) * 2; /* size 0x01 header */ - - case coding_EA_XA: - case coding_EA_XA_int: - case coding_EA_XA_V2: - case coding_MAXIS_XA: - return 28; - case coding_EA_XAS_V0: - return 32; - case coding_EA_XAS_V1: - return 128; - - case coding_MSADPCM: - return (vgmstream->frame_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; - case coding_MSADPCM_int: - case coding_MSADPCM_ck: - return (vgmstream->frame_size - 0x07)*2 + 2; - case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ - return vgmstream->ws_output_size; - case coding_AICA: - return 1; - case coding_AICA_int: - return 2; - case coding_ASKA: - return (0x40-0x04*vgmstream->channels) * 2 / vgmstream->channels; - case coding_NXAP: - return (0x40-0x04) * 2; - case coding_NDS_PROCYON: - return 30; - case coding_L5_555: - return 32; - case coding_LSF: - return 54; - -#ifdef VGM_USE_G7221 - case coding_G7221C: - return 32000/50; /* Siren7: 16000/50 */ -#endif -#ifdef VGM_USE_G719 - case coding_G719: - return 48000/50; -#endif -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: - return 0; -#endif - case coding_MTAF: - return 128*2; - case coding_MTA2: - return 128*2; - case coding_MC3: - return 10; - case coding_FADPCM: - return 256; /* (0x8c - 0xc) * 2 */ - case coding_ASF: - return 32; /* (0x11 - 0x1) * 2 */ - case coding_DSA: - return 14; /* (0x08 - 0x1) * 2 */ - case coding_XMD: - return (vgmstream->interleave_block_size - 0x06)*2 + 2; - case coding_PTADPCM: - return (vgmstream->interleave_block_size - 0x05)*2 + 2; - case coding_UBI_ADPCM: - return 0; /* varies per mode */ - case coding_IMUSE: - return 0; /* varies per frame */ - case coding_EA_MT: - return 0; /* 432, but variable in looped files */ - case coding_CIRCUS_VQ: - return 0; - case coding_RELIC: - return 0; /* 512 */ - case coding_CRI_HCA: - return 0; /* 1024 - delay/padding (which can be bigger than 1024) */ -#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - case coding_MP4_AAC: - return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame; -#endif -#ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: - return 2048 - ((maiatrac3plus_codec_data*)vgmstream->codec_data)->samples_discard; -#endif -#ifdef VGM_USE_ATRAC9 - case coding_ATRAC9: - return 0; /* varies with config data, usually 256 or 1024 */ -#endif -#ifdef VGM_USE_CELT - case coding_CELT_FSB: - return 0; /* 512? */ -#endif - default: - return 0; - } -} - -/* Get the number of bytes of a single frame (smallest self-contained byte group, 1/N channels) */ -int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { - switch (vgmstream->coding_type) { - case coding_CRI_ADX: - case coding_CRI_ADX_fixed: - case coding_CRI_ADX_exp: - case coding_CRI_ADX_enc_8: - case coding_CRI_ADX_enc_9: - return vgmstream->interleave_block_size; - - case coding_NGC_DSP: - return 0x08; - case coding_NGC_DSP_subint: - return 0x08 * vgmstream->channels; - case coding_NGC_AFC: - case coding_VADPCM: - return 0x09; - case coding_NGC_DTK: - return 0x20; - case coding_G721: - return 0; - - case coding_PCM16LE: - case coding_PCM16BE: - case coding_PCM16_int: - return 0x02; - case coding_PCM8: - case coding_PCM8_int: - case coding_PCM8_U: - case coding_PCM8_U_int: - case coding_PCM8_SB: - case coding_ULAW: - case coding_ULAW_int: - case coding_ALAW: - return 0x01; - case coding_PCMFLOAT: - return 0x04; - - case coding_SDX2: - case coding_SDX2_int: - case coding_CBD2: - case coding_DERF: - case coding_NWA: - case coding_SASSC: - case coding_CIRCUS_ADPCM: - return 0x01; - - case coding_PCM4: - case coding_PCM4_U: - case coding_IMA: - case coding_IMA_int: - case coding_DVI_IMA: - case coding_DVI_IMA_int: - case coding_3DS_IMA: - case coding_WV6_IMA: - case coding_ALP_IMA: - case coding_FFTA2_IMA: - case coding_BLITZ_IMA: - case coding_PCFX: - case coding_OKI16: - case coding_OKI4S: - case coding_MTF_IMA: - return 0x01; - case coding_MS_IMA: - case coding_RAD_IMA: - case coding_NDS_IMA: - case coding_DAT4_IMA: - case coding_REF_IMA: - return vgmstream->interleave_block_size; - case coding_AWC_IMA: - return 0x800; - case coding_RAD_IMA_mono: - return 0x14; - case coding_SNDS_IMA: - case coding_OTNS_IMA: - return 0; //todo: 0x01? - case coding_UBI_IMA: /* variable (PCM then IMA) */ - return 0; - case coding_XBOX_IMA: - //todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs - return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48; - case coding_XBOX_IMA_int: - case coding_WWISE_IMA: - return 0x24; - case coding_XBOX_IMA_mch: - case coding_FSB_IMA: - return 0x24 * vgmstream->channels; - case coding_APPLE_IMA4: - return 0x22; - case coding_H4M_IMA: - return 0x00; /* variable (block-controlled) */ - - case coding_XA: - return 0x80; - case coding_PSX: - case coding_PSX_badflags: - case coding_HEVAG: - return 0x10; - case coding_PSX_cfg: - case coding_PSX_pivotal: - return vgmstream->interleave_block_size; - - case coding_EA_XA: - return 0x1E; - case coding_EA_XA_int: - return 0x0F; - case coding_MAXIS_XA: - return 0x0F*vgmstream->channels; - case coding_EA_XA_V2: - return 0; /* variable (ADPCM frames of 0x0f or PCM frames of 0x3d) */ - case coding_EA_XAS_V0: - return 0xF+0x02+0x02; - case coding_EA_XAS_V1: - return 0x4c*vgmstream->channels; - - case coding_MSADPCM: - case coding_MSADPCM_int: - case coding_MSADPCM_ck: - return vgmstream->frame_size; - case coding_WS: - return vgmstream->current_block_size; - case coding_AICA: - case coding_AICA_int: - return 0x01; - case coding_ASKA: - case coding_NXAP: - return 0x40; - case coding_NDS_PROCYON: - return 0x10; - case coding_L5_555: - return 0x12; - case coding_LSF: - return 0x1C; - -#ifdef VGM_USE_G7221 - case coding_G7221C: -#endif -#ifdef VGM_USE_G719 - case coding_G719: -#endif -#ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: -#endif -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: -#endif - case coding_MTAF: - return vgmstream->interleave_block_size; - case coding_MTA2: - return 0x90; - case coding_MC3: - return 0x04; - case coding_FADPCM: - return 0x8c; - case coding_ASF: - return 0x11; - case coding_DSA: - return 0x08; - case coding_XMD: - return vgmstream->interleave_block_size; - case coding_PTADPCM: - return vgmstream->interleave_block_size; - case coding_UBI_ADPCM: - return 0; /* varies per mode? */ - case coding_IMUSE: - return 0; /* varies per frame */ - case coding_EA_MT: - return 0; /* variable (frames of bit counts or PCM frames) */ -#ifdef VGM_USE_ATRAC9 - case coding_ATRAC9: - return 0; /* varies with config data, usually 0x100-200 */ -#endif -#ifdef VGM_USE_CELT - case coding_CELT_FSB: - return 0; /* varies, usually 0x80-100 */ -#endif - default: /* Vorbis, MPEG, ACM, etc */ - return 0; - } -} - -/* In NDS IMA the frame size is the block size, so the last one is short */ -int get_vgmstream_samples_per_shortframe(VGMSTREAM * vgmstream) { - switch (vgmstream->coding_type) { - case coding_NDS_IMA: - return (vgmstream->interleave_last_block_size-4)*2; - default: - return get_vgmstream_samples_per_frame(vgmstream); - } -} -int get_vgmstream_shortframe_size(VGMSTREAM * vgmstream) { - switch (vgmstream->coding_type) { - case coding_NDS_IMA: - return vgmstream->interleave_last_block_size; - default: - return get_vgmstream_frame_size(vgmstream); - } -} - -/* Decode samples into the buffer. Assume that we have written samples_written into the - * buffer already, and we have samples_to_do consecutive samples ahead of us. */ -void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to_do, sample_t * buffer) { - int ch; - - switch (vgmstream->coding_type) { - case coding_CRI_ADX: - case coding_CRI_ADX_exp: - case coding_CRI_ADX_fixed: - case coding_CRI_ADX_enc_8: - case coding_CRI_ADX_enc_9: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_adx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->interleave_block_size, vgmstream->coding_type); - } - break; - case coding_NGC_DSP: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ngc_dsp(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_NGC_DSP_subint: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ngc_dsp_subint(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - ch, vgmstream->interleave_block_size); - } - break; - - case coding_PCM16LE: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm16le(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM16BE: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm16be(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM16_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm16_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->codec_endian); - } - break; - case coding_PCM8: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm8(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM8_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm8_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM8_U: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm8_unsigned(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM8_U_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm8_unsigned_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM8_SB: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm8_sb(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM4: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm4(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do,ch); - } - break; - case coding_PCM4_U: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm4_unsigned(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do,ch); - } - break; - - case coding_ULAW: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ulaw(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_ULAW_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ulaw_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_ALAW: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_alaw(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCMFLOAT: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcmfloat(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->codec_endian); - } - break; - - case coding_NDS_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_nds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_DAT4_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_dat4_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_XBOX_IMA: - case coding_XBOX_IMA_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_XBOX_IMA); - decode_xbox_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, - is_stereo); - } - break; - case coding_XBOX_IMA_mch: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_xbox_ima_mch(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_MS_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ms_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_RAD_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_rad_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_RAD_IMA_mono: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_rad_ima_mono(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_NGC_DTK: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ngc_dtk(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_G721: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_g721(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_NGC_AFC: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ngc_afc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_VADPCM: { - int order = vgmstream->codec_config; - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_vadpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, order); - } - break; - } - case coding_PSX: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_psx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 0, vgmstream->codec_config); - } - break; - case coding_PSX_badflags: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_psx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 1, vgmstream->codec_config); - } - break; - case coding_PSX_cfg: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_psx_configurable(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size, vgmstream->codec_config); - } - break; - case coding_PSX_pivotal: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_psx_pivotal(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->interleave_block_size); - } - break; - case coding_HEVAG: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_hevag(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_XA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_EA_XA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_EA_XA_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xa_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_EA_XA_V2: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xa_v2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_MAXIS_XA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_maxis_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_EA_XAS_V0: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xas_v0(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_EA_XAS_V1: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xas_v1(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; -#ifdef VGM_USE_VORBIS - case coding_OGG_VORBIS: - decode_ogg_vorbis(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); - break; - - case coding_VORBIS_custom: - decode_vorbis_custom(vgmstream, buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); - break; -#endif - case coding_CIRCUS_VQ: - decode_circus_vq(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, samples_to_do, vgmstream->channels); - break; - case coding_RELIC: - decode_relic(&vgmstream->ch[0], vgmstream->codec_data, buffer+samples_written*vgmstream->channels, - samples_to_do); - break; - case coding_CRI_HCA: - decode_hca(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, - samples_to_do); - break; -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: - decode_ffmpeg(vgmstream, - buffer+samples_written*vgmstream->channels,samples_to_do,vgmstream->channels); - break; -#endif -#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - case coding_MP4_AAC: - decode_mp4_aac(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); - break; -#endif - case coding_SDX2: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_sdx2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_SDX2_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_sdx2_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_CBD2: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_cbd2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_CBD2_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_cbd2_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_DERF: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_derf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_CIRCUS_ADPCM: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_circus_adpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - - case coding_IMA: - case coding_IMA_int: - case coding_DVI_IMA: - case coding_DVI_IMA_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_IMA) - || (vgmstream->channels > 1 && vgmstream->coding_type == coding_DVI_IMA); - int is_high_first = vgmstream->coding_type == coding_DVI_IMA || vgmstream->coding_type == coding_DVI_IMA_int; - - decode_standard_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, - is_stereo, is_high_first); - } - break; - case coding_MTF_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - int is_stereo = (vgmstream->channels > 1); - decode_mtf_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, - is_stereo); - } - break; - case coding_3DS_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_3ds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_WV6_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_wv6_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_ALP_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_alp_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_FFTA2_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ffta2_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_BLITZ_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_blitz_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - - case coding_APPLE_IMA4: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_apple_ima4(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_SNDS_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_snds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_OTNS_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_otns_ima(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_FSB_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_fsb_ima(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_WWISE_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_wwise_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_REF_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ref_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_AWC_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_awc_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_UBI_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ubi_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, vgmstream->codec_config); - } - break; - case coding_H4M_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - uint16_t frame_format = (uint16_t)((vgmstream->codec_config >> 8) & 0xFFFF); - - decode_h4m_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, - frame_format); - } - break; - - case coding_WS: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ws(vgmstream,ch,buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - -#ifdef VGM_USE_MPEG - case coding_MPEG_custom: - case coding_MPEG_ealayer3: - case coding_MPEG_layer1: - case coding_MPEG_layer2: - case coding_MPEG_layer3: - decode_mpeg(vgmstream,buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); - break; -#endif -#ifdef VGM_USE_G7221 - case coding_G7221C: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_g7221(vgmstream, buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,samples_to_do, ch); - } - break; -#endif -#ifdef VGM_USE_G719 - case coding_G719: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_g719(vgmstream, buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,samples_to_do, ch); - } - break; -#endif -#ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_at3plus(vgmstream, buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,samples_to_do, ch); - } - break; -#endif -#ifdef VGM_USE_ATRAC9 - case coding_ATRAC9: - decode_atrac9(vgmstream, buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); - break; -#endif -#ifdef VGM_USE_CELT - case coding_CELT_FSB: - decode_celt_fsb(vgmstream, buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); - break; -#endif - case coding_ACM: - decode_acm(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, - samples_to_do, vgmstream->channels); - break; - case coding_NWA: - decode_nwa(((nwa_codec_data*)vgmstream->codec_data)->nwa, - buffer+samples_written*vgmstream->channels, samples_to_do); - break; - case coding_MSADPCM: - case coding_MSADPCM_int: - if (vgmstream->channels == 1 || vgmstream->coding_type == coding_MSADPCM_int) { - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); - } - } - else if (vgmstream->channels == 2) { - decode_msadpcm_stereo(vgmstream,buffer+samples_written*vgmstream->channels, - vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_MSADPCM_ck: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_msadpcm_ck(vgmstream,buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); - } - break; - case coding_AICA: - case coding_AICA_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_AICA); - - decode_aica(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, - is_stereo); - } - break; - case coding_ASKA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_aska(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_NXAP: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_nxap(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_TGC: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_tgc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_NDS_PROCYON: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_nds_procyon(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_L5_555: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_l5_555(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_SASSC: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_sassc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - - break; - case coding_LSF: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_lsf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_MTAF: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_mtaf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); - } - break; - case coding_MTA2: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_mta2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); - } - break; - case coding_MC3: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_mc3(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); - } - break; - case coding_FADPCM: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_fadpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_ASF: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_asf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_DSA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_dsa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_XMD: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_xmd(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->interleave_block_size); - } - break; - case coding_PTADPCM: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ptadpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->interleave_block_size); - } - break; - case coding_PCFX: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcfx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->codec_config); - } - break; - case coding_OKI16: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_oki16(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - - case coding_OKI4S: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_oki4s(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - - case coding_UBI_ADPCM: - decode_ubi_adpcm(vgmstream, buffer+samples_written*vgmstream->channels, samples_to_do); - break; - - case coding_IMUSE: - decode_imuse(vgmstream, buffer+samples_written*vgmstream->channels, samples_to_do); - break; - - case coding_EA_MT: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels, samples_to_do, ch); - } - break; - default: - break; - } -} - -/* Calculate number of consecutive samples to do (taking into account stopping for loop start and end) */ -int vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMSTREAM * vgmstream) { - int samples_to_do; - int samples_left_this_block; - - samples_left_this_block = samples_this_block - vgmstream->samples_into_block; - samples_to_do = samples_left_this_block; - - /* fun loopy crap */ - /* Why did I think this would be any simpler? */ - if (vgmstream->loop_flag) { - /* are we going to hit the loop end during this block? */ - if (vgmstream->current_sample + samples_left_this_block > vgmstream->loop_end_sample) { - /* only do to just before it */ - samples_to_do = vgmstream->loop_end_sample - vgmstream->current_sample; - } - - /* are we going to hit the loop start during this block? */ - if (!vgmstream->hit_loop && vgmstream->current_sample + samples_left_this_block > vgmstream->loop_start_sample) { - /* only do to just before it */ - samples_to_do = vgmstream->loop_start_sample - vgmstream->current_sample; - } - - } - - /* if it's a framed encoding don't do more than one frame */ - if (samples_per_frame > 1 && (vgmstream->samples_into_block % samples_per_frame) + samples_to_do > samples_per_frame) - samples_to_do = samples_per_frame - (vgmstream->samples_into_block % samples_per_frame); - - return samples_to_do; -} - -/* Detect loop start and save values, or detect loop end and restore (loop back). Returns 1 if loop was done. */ -int vgmstream_do_loop(VGMSTREAM * vgmstream) { - /*if (!vgmstream->loop_flag) return 0;*/ - - /* is this the loop end? = new loop, continue from loop_start_sample */ - if (vgmstream->current_sample==vgmstream->loop_end_sample) { - - /* disable looping if target count reached and continue normally - * (only needed with the "play stream end after looping N times" option enabled) */ - vgmstream->loop_count++; - if (vgmstream->loop_target && vgmstream->loop_target == vgmstream->loop_count) { - vgmstream->loop_flag = 0; /* could be improved but works ok, will be restored on resets */ - return 0; - } - - /* against everything I hold sacred, preserve adpcm - * history through loop for certain types */ - if (vgmstream->meta_type == meta_DSP_STD || - vgmstream->meta_type == meta_DSP_RS03 || - vgmstream->meta_type == meta_DSP_CSTR || - vgmstream->coding_type == coding_PSX || - vgmstream->coding_type == coding_PSX_badflags) { - int i; - for (i = 0; i < vgmstream->channels; i++) { - vgmstream->loop_ch[i].adpcm_history1_16 = vgmstream->ch[i].adpcm_history1_16; - vgmstream->loop_ch[i].adpcm_history2_16 = vgmstream->ch[i].adpcm_history2_16; - vgmstream->loop_ch[i].adpcm_history1_32 = vgmstream->ch[i].adpcm_history1_32; - vgmstream->loop_ch[i].adpcm_history2_32 = vgmstream->ch[i].adpcm_history2_32; - } - } - - - /* prepare certain codecs' internal state for looping */ - - if (vgmstream->coding_type == coding_CIRCUS_VQ) { - seek_circus_vq(vgmstream->codec_data, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_RELIC) { - seek_relic(vgmstream->codec_data, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_CRI_HCA) { - loop_hca(vgmstream->codec_data, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_UBI_ADPCM) { - seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_IMUSE) { - seek_imuse(vgmstream->codec_data, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_EA_MT) { - seek_ea_mt(vgmstream, vgmstream->loop_sample); - } - -#ifdef VGM_USE_VORBIS - if (vgmstream->coding_type == coding_OGG_VORBIS) { - seek_ogg_vorbis(vgmstream, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_VORBIS_custom) { - seek_vorbis_custom(vgmstream, vgmstream->loop_sample); - } -#endif - -#ifdef VGM_USE_FFMPEG - if (vgmstream->coding_type == coding_FFmpeg) { - seek_ffmpeg(vgmstream, vgmstream->loop_sample); - } -#endif - -#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - if (vgmstream->coding_type == coding_MP4_AAC) { - seek_mp4_aac(vgmstream, vgmstream->loop_sample); - } -#endif - -#ifdef VGM_USE_MAIATRAC3PLUS - if (vgmstream->coding_type == coding_AT3plus) { - seek_at3plus(vgmstream, vgmstream->loop_sample); - } -#endif - -#ifdef VGM_USE_ATRAC9 - if (vgmstream->coding_type == coding_ATRAC9) { - seek_atrac9(vgmstream, vgmstream->loop_sample); - } -#endif - -#ifdef VGM_USE_CELT - if (vgmstream->coding_type == coding_CELT_FSB) { - seek_celt_fsb(vgmstream, vgmstream->loop_sample); - } -#endif - -#ifdef VGM_USE_MPEG - if (vgmstream->coding_type == coding_MPEG_custom || - vgmstream->coding_type == coding_MPEG_ealayer3 || - vgmstream->coding_type == coding_MPEG_layer1 || - vgmstream->coding_type == coding_MPEG_layer2 || - vgmstream->coding_type == coding_MPEG_layer3) { - seek_mpeg(vgmstream, vgmstream->loop_sample); - } -#endif - - if (vgmstream->coding_type == coding_NWA) { - nwa_codec_data *data = vgmstream->codec_data; - if (data) - seek_nwa(data->nwa, vgmstream->loop_sample); - } - - /* restore! */ - memcpy(vgmstream->ch, vgmstream->loop_ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); - vgmstream->current_sample = vgmstream->loop_sample; - vgmstream->samples_into_block = vgmstream->loop_samples_into_block; - vgmstream->current_block_size = vgmstream->loop_block_size; - vgmstream->current_block_samples = vgmstream->loop_block_samples; - vgmstream->current_block_offset = vgmstream->loop_block_offset; - vgmstream->next_block_offset = vgmstream->loop_next_block_offset; - - return 1; /* looped */ - } - - - /* is this the loop start? */ - if (!vgmstream->hit_loop && vgmstream->current_sample == vgmstream->loop_start_sample) { - /* save! */ - memcpy(vgmstream->loop_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); - vgmstream->loop_sample = vgmstream->current_sample; - vgmstream->loop_samples_into_block = vgmstream->samples_into_block; - vgmstream->loop_block_size = vgmstream->current_block_size; - vgmstream->loop_block_samples = vgmstream->current_block_samples; - vgmstream->loop_block_offset = vgmstream->current_block_offset; - vgmstream->loop_next_block_offset = vgmstream->next_block_offset; - vgmstream->hit_loop = 1; - } - - return 0; /* not looped */ -} +/*******************************************************************************/ +/* MISC */ +/*******************************************************************************/ /* Write a description of the stream into array pointed by desc, which must be length bytes long. * Will always be null-terminated if length > 0 */ -void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { +void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) { #define TEMPSIZE (256+32) char temp[TEMPSIZE]; double time_mm, time_ss, seconds; @@ -2544,14 +1025,26 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { snprintf(temp,TEMPSIZE, "stream name: %s\n", vgmstream->stream_name); concatn(length,desc,temp); } + + if (vgmstream->config_enabled) { + double time_mm, time_ss, seconds; + int32_t samples = vgmstream->pstate.play_duration; + + seconds = (double)samples / vgmstream->sample_rate; + time_mm = (int)(seconds / 60.0); + time_ss = seconds - time_mm * 60.0f; + + snprintf(temp,TEMPSIZE, "play duration: %d samples (%1.0f:%06.3f seconds)\n", samples, time_mm, time_ss); + concatn(length,desc,temp); + } } /* See if there is a second file which may be the second channel, given an already opened mono vgmstream. * If a suitable file is found, open it and change opened_vgmstream to a stereo vgmstream. */ -static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *streamFile, VGMSTREAM*(*init_vgmstream_function)(STREAMFILE *)) { +static void try_dual_file_stereo(VGMSTREAM* opened_vgmstream, STREAMFILE* sf, VGMSTREAM*(*init_vgmstream_function)(STREAMFILE*)) { /* filename search pairs for dual file stereo */ - static const char * const dfs_pairs[][2] = { + static const char* const dfs_pairs[][2] = { {"L","R"}, /* most common in .dsp and .vag */ {"l","r"}, /* same */ {"left","right"}, /* Freaky Flyers (GC) .adp, Velocity (PSP) .vag, Hyper Fighters (Wii) .dsp */ @@ -2563,10 +1056,10 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea {".adpcm","_2.adpcm"}, /* Desire: Remaster Version (Switch) */ }; char new_filename[PATH_LIMIT]; - char * extension; + char* extension; int dfs_pair = -1; /* -1=no stereo, 0=opened_vgmstream is left, 1=opened_vgmstream is right */ - VGMSTREAM *new_vgmstream = NULL; - STREAMFILE *dual_streamFile = NULL; + VGMSTREAM* new_vgmstream = NULL; + STREAMFILE* dual_sf = NULL; int i,j, dfs_pair_count, extension_len, filename_len; if (opened_vgmstream->channels != 1) @@ -2579,7 +1072,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea //todo other layouts work but some stereo codecs do weird things //if (opened_vgmstream->layout != layout_none) return; - get_streamfile_name(streamFile, new_filename, sizeof(new_filename)); + get_streamfile_name(sf, new_filename, sizeof(new_filename)); filename_len = strlen(new_filename); if (filename_len < 2) return; @@ -2594,8 +1087,8 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea dfs_pair_count = (sizeof(dfs_pairs)/sizeof(dfs_pairs[0])); for (i = 0; dfs_pair == -1 && i < dfs_pair_count; i++) { for (j = 0; dfs_pair == -1 && j < 2; j++) { - const char * this_suffix = dfs_pairs[i][j]; - const char * that_suffix = dfs_pairs[i][j^1]; + const char* this_suffix = dfs_pairs[i][j]; + const char* that_suffix = dfs_pairs[i][j^1]; size_t this_suffix_len = strlen(dfs_pairs[i][j]); size_t that_suffix_len = strlen(dfs_pairs[i][j^1]); @@ -2621,11 +1114,11 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea if (dfs_pair != -1) { //VGM_LOG("DFS: try %i: %s\n", dfs_pair, new_filename); /* try to init other channel (new_filename now has the opposite name) */ - dual_streamFile = open_streamfile(streamFile, new_filename); - if (!dual_streamFile) { + dual_sf = open_streamfile(sf, new_filename); + if (!dual_sf) { /* restore filename and keep trying (if found it'll break and init) */ dfs_pair = -1; - get_streamfile_name(streamFile, new_filename, sizeof(new_filename)); + get_streamfile_name(sf, new_filename, sizeof(new_filename)); } } } @@ -2636,8 +1129,8 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea goto fail; //;VGM_LOG("DFS: match %i filename=%s\n", dfs_pair, new_filename); - new_vgmstream = init_vgmstream_function(dual_streamFile); /* use the init function that just worked */ - close_streamfile(dual_streamFile); + new_vgmstream = init_vgmstream_function(dual_sf); /* use the init function that just worked */ + close_streamfile(dual_sf); /* see if we were able to open the file, and if everything matched nicely */ if (!(new_vgmstream && @@ -2668,9 +1161,9 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea /* We seem to have a usable, matching file. Merge in the second channel. */ { - VGMSTREAMCHANNEL * new_chans; - VGMSTREAMCHANNEL * new_loop_chans = NULL; - VGMSTREAMCHANNEL * new_start_chans = NULL; + VGMSTREAMCHANNEL* new_chans; + VGMSTREAMCHANNEL* new_loop_chans = NULL; + VGMSTREAMCHANNEL* new_start_chans = NULL; /* build the channels */ new_chans = calloc(2,sizeof(VGMSTREAMCHANNEL)); @@ -2718,6 +1211,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea /* discard the second VGMSTREAM */ mixing_close(new_vgmstream); + free(new_vgmstream->tmpbuf); free(new_vgmstream->start_vgmstream); free(new_vgmstream); @@ -2731,16 +1225,14 @@ fail: } /* average bitrate helper to get STREAMFILE for a channel, since some codecs may use their own */ -static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * vgmstream, int channel) { +static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* vgmstream, int channel) { if (vgmstream->coding_type == coding_NWA) { - nwa_codec_data *data = vgmstream->codec_data; - return (data && data->nwa) ? data->nwa->file : NULL; + return nwa_get_streamfile(vgmstream->codec_data); } if (vgmstream->coding_type == coding_ACM) { - acm_codec_data *data = vgmstream->codec_data; - return (data && data->handle) ? data->streamfile : NULL; + return acm_get_streamfile(vgmstream->codec_data); } #ifdef VGM_USE_VORBIS @@ -2749,13 +1241,11 @@ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * } #endif if (vgmstream->coding_type == coding_CRI_HCA) { - hca_codec_data *data = vgmstream->codec_data; - return data ? data->streamfile : NULL; + return hca_get_streamfile(vgmstream->codec_data); } #ifdef VGM_USE_FFMPEG if (vgmstream->coding_type == coding_FFmpeg) { - ffmpeg_codec_data *data = vgmstream->codec_data; - return data ? data->streamfile : NULL; + return ffmpeg_get_streamfile(vgmstream->codec_data); } #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) @@ -2773,12 +1263,12 @@ static int get_vgmstream_file_bitrate_from_size(size_t size, int sample_rate, in if (length_samples < 100) return 0; /* ignore stupid bitrates caused by some segments */ return (int)((int64_t)size * 8 * sample_rate / length_samples); } -static int get_vgmstream_file_bitrate_from_streamfile(STREAMFILE * streamfile, int sample_rate, int length_samples) { +static int get_vgmstream_file_bitrate_from_streamfile(STREAMFILE* streamfile, int sample_rate, int length_samples) { if (streamfile == NULL) return 0; return get_vgmstream_file_bitrate_from_size(get_streamfile_size(streamfile), sample_rate, length_samples); } -static int get_vgmstream_file_bitrate_main(VGMSTREAM * vgmstream, STREAMFILE **streamfile_pointers, int *pointers_count, int pointers_max) { +static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, STREAMFILE** streamfile_pointers, int *pointers_count, int pointers_max) { int sub, ch; int bitrate = 0; @@ -2815,18 +1305,18 @@ static int get_vgmstream_file_bitrate_main(VGMSTREAM * vgmstream, STREAMFILE **s int is_unique = 1; for (ch = 0; ch < vgmstream->channels; ch++) { - STREAMFILE * currentFile = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, ch); - if (!currentFile) continue; - get_streamfile_name(currentFile, path_current, sizeof(path_current)); + STREAMFILE* sf_cur = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, ch); + if (!sf_cur) continue; + get_streamfile_name(sf_cur, path_current, sizeof(path_current)); for (sub = 0; sub < *pointers_count; sub++) { - STREAMFILE * compareFile = streamfile_pointers[sub]; - if (!compareFile) continue; - if (currentFile == compareFile) { + STREAMFILE* sf_cmp = streamfile_pointers[sub]; + if (!sf_cmp) continue; + if (sf_cur == sf_cmp) { is_unique = 0; break; } - get_streamfile_name(compareFile, path_compare, sizeof(path_compare)); + get_streamfile_name(sf_cmp, path_compare, sizeof(path_compare)); if (strcmp(path_current, path_compare) == 0) { is_unique = 0; break; @@ -2835,10 +1325,10 @@ static int get_vgmstream_file_bitrate_main(VGMSTREAM * vgmstream, STREAMFILE **s if (is_unique) { if (*pointers_count >= pointers_max) goto fail; - streamfile_pointers[*pointers_count] = currentFile; + streamfile_pointers[*pointers_count] = sf_cur; (*pointers_count)++; - bitrate += get_vgmstream_file_bitrate_from_streamfile(currentFile, vgmstream->sample_rate, vgmstream->num_samples); + bitrate += get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples); } } } @@ -2852,9 +1342,9 @@ fail: * This is the bitrate of the *file*, as opposed to the bitrate of the *codec*, meaning * it counts extra data like block headers and padding. While this can be surprising * sometimes (as it's often higher than common codec bitrates) it isn't wrong per se. */ -int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { +int get_vgmstream_average_bitrate(VGMSTREAM* vgmstream) { const size_t pointers_max = 128; /* arbitrary max, but +100 segments have been observed */ - STREAMFILE *streamfile_pointers[128]; /* list already used streamfiles */ + STREAMFILE* streamfile_pointers[128]; /* list already used streamfiles */ int pointers_count = 0; return get_vgmstream_file_bitrate_main(vgmstream, streamfile_pointers, &pointers_count, pointers_max); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 054d51c75..c44c4643e 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -35,11 +35,7 @@ enum { VGMSTREAM_MAX_NUM_SAMPLES = 1000000000 }; /* no ~5h vgm hopefully */ #endif #ifdef VGM_USE_MPEG -#ifdef __MACOSX__ #include -#else -#include -#endif #endif #ifdef VGM_USE_MP4V2 @@ -74,6 +70,8 @@ enum { VGMSTREAM_MAX_NUM_SAMPLES = 1000000000 }; /* no ~5h vgm hopefully */ /* The encoding type specifies the format the sound data itself takes */ typedef enum { + coding_SILENCE, /* generates silence */ + /* PCM */ coding_PCM16LE, /* little endian 16-bit PCM */ coding_PCM16BE, /* big endian 16-bit PCM */ @@ -150,6 +148,7 @@ typedef enum { coding_UBI_IMA, /* Ubisoft IMA ADPCM */ coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */ coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */ + coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */ coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */ coding_MSADPCM_int, /* Microsoft ADPCM (mono) */ @@ -297,6 +296,7 @@ typedef enum { /* The meta type specifies how we know what we know about the file. * We may know because of a header we read, some of it may have been guessed from filenames, etc. */ typedef enum { + meta_SILENCE, meta_DSP_STD, /* Nintendo standard GC ADPCM (DSP) header */ meta_DSP_CSTR, /* Star Fox Assault "Cstr" */ @@ -357,7 +357,7 @@ typedef enum { meta_PS2_RXWS, /* Sony games (Genji, Okage Shadow King, Arc The Lad Twilight of Spirits) */ meta_RAW_INT, meta_PS2_EXST, /* Shadow of Colossus EXST */ - meta_PS2_SVAG, /* Konami SVAG */ + meta_SVAG_KCET, meta_PS_HEADERLESS, /* headerless PS-ADPCM */ meta_PS2_MIB_MIH, /* MIB File + MIH Header*/ meta_PS2_MIC, /* KOEI MIC File */ @@ -595,7 +595,7 @@ typedef enum { meta_MCA, /* Capcom MCA "MADP" */ meta_XB3D_ADX, /* Xenoblade Chronicles 3D ADX */ meta_HCA, /* CRI HCA */ - meta_PS2_SVAG_SNK, /* SNK PS2 SVAG */ + meta_SVAG_SNK, meta_PS2_VDS_VDM, /* Graffiti Kingdom */ meta_FFMPEG, /* any file supported by FFmpeg */ meta_X360_CXS, /* Eternal Sonata (Xbox 360) */ @@ -742,6 +742,7 @@ typedef enum { meta_KTSR, meta_KAT, meta_PCM_SUCCESS, + meta_ADP_KONAMI, } meta_t; /* standard WAVEFORMATEXTENSIBLE speaker positions */ @@ -787,22 +788,71 @@ typedef enum { } mapping_t; typedef struct { + int config_set; /* some of the mods below are set */ + + /* modifiers */ int play_forever; - int loop_count_set; - double loop_count; - int fade_time_set; - double fade_time; - int fade_delay_set; - double fade_delay; - int ignore_fade; + int ignore_loop; int force_loop; int really_force_loop; - int ignore_loop; + int ignore_fade; + + /* processing */ + double loop_count; + int32_t pad_begin; + int32_t trim_begin; + int32_t body_time; + int32_t trim_end; + double fade_delay; /* not in samples for backwards compatibility */ + double fade_time; + int32_t pad_end; + + double pad_begin_s; + double trim_begin_s; + double body_time_s; + double trim_end_s; + //double fade_delay_s; + //double fade_time_s; + double pad_end_s; + + /* internal flags */ + int pad_begin_set; + int trim_begin_set; + int body_time_set; + int loop_count_set; + int trim_end_set; + int fade_delay_set; + int fade_time_set; + int pad_end_set; + + } play_config_t; + +typedef struct { + int input_channels; + int output_channels; + + int32_t pad_begin_duration; + int32_t pad_begin_left; + int32_t trim_begin_duration; + int32_t trim_begin_left; + int32_t body_duration; + int32_t fade_duration; + int32_t fade_left; + int32_t fade_start; + int32_t pad_end_duration; + //int32_t pad_end_left; + int32_t pad_end_start; + + int32_t play_duration; /* total samples that the stream lasts (after applying all config) */ + int32_t play_position; /* absolute sample where stream is */ +} play_state_t; + + /* info for a single vgmstream channel */ typedef struct { - STREAMFILE * streamfile; /* file used by this channel */ + STREAMFILE* streamfile; /* file used by this channel */ off_t channel_start_offset; /* where data for this channel begins */ off_t offset; /* current location in the file */ @@ -832,8 +882,8 @@ typedef struct { int32_t adpcm_history4_32; }; - double adpcm_history1_double; - double adpcm_history2_double; + //double adpcm_history1_double; + //double adpcm_history2_double; int adpcm_step_index; /* for IMA */ int adpcm_scale; /* for MS ADPCM */ @@ -849,6 +899,7 @@ typedef struct { } VGMSTREAMCHANNEL; + /* main vgmstream info */ typedef struct { /* basic config */ @@ -883,31 +934,25 @@ typedef struct { /* other config */ int allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */ - /* config requests, players must read and honor these values - * (ideally internally would work as a player, but for now player must do it manually) */ - play_config_t config; - /* layout/block state */ size_t full_block_size; /* actual data size of an entire block (ie. may be fixed, include padding/headers, etc) */ - int32_t current_sample; /* number of samples we've passed (for loop detection) */ + int32_t current_sample; /* sample point within the file (for loop detection) */ int32_t samples_into_block; /* number of samples into the current block/interleave/segment/etc */ off_t current_block_offset; /* start of this block (offset of block header) */ size_t current_block_size; /* size in usable bytes of the block we're in now (used to calculate num_samples per block) */ - int32_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */ + int32_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */ off_t next_block_offset; /* offset of header of the next block */ - /* layout/block loop state */ - int32_t loop_sample; /* saved from current_sample (same as loop_start_sample, but more state-like) */ + + /* loop state (saved when loop is hit to restore later) */ + int32_t loop_current_sample; /* saved from current_sample (same as loop_start_sample, but more state-like) */ int32_t loop_samples_into_block;/* saved from samples_into_block */ off_t loop_block_offset; /* saved from current_block_offset */ size_t loop_block_size; /* saved from current_block_size */ - int32_t loop_block_samples; /* saved from current_block_samples */ + int32_t loop_block_samples; /* saved from current_block_samples */ off_t loop_next_block_offset; /* saved from next_block_offset */ + int hit_loop; /* save config when loop is hit, but first time only */ - /* loop state */ - int hit_loop; /* have we seen the loop yet? */ - int loop_count; /* counter of complete loops (1=looped once) */ - int loop_target; /* max loops before continuing with the stream end (loops forever if not set) */ /* decoder config/state */ int codec_endian; /* little/big endian marker; name is left vague but usually means big endian */ @@ -916,248 +961,40 @@ typedef struct { /* main state */ - VGMSTREAMCHANNEL * ch; /* array of channels */ - VGMSTREAMCHANNEL * start_ch; /* shallow copy of channels as they were at the beginning of the stream (for resets) */ - VGMSTREAMCHANNEL * loop_ch; /* shallow copy of channels as they were at the loop point (for loops) */ + VGMSTREAMCHANNEL* ch; /* array of channels */ + VGMSTREAMCHANNEL* start_ch; /* shallow copy of channels as they were at the beginning of the stream (for resets) */ + VGMSTREAMCHANNEL* loop_ch; /* shallow copy of channels as they were at the loop point (for loops) */ void* start_vgmstream; /* shallow copy of the VGMSTREAM as it was at the beginning of the stream (for resets) */ - void * mixing_data; /* state for mixing effects */ + void* mixing_data; /* state for mixing effects */ /* Optional data the codec needs for the whole stream. This is for codecs too * different from vgmstream's structure to be reasonably shoehorned. * Note also that support must be added for resetting, looping and * closing for every codec that uses this, as it will not be handled. */ - void * codec_data; + void* codec_data; /* Same, for special layouts. layout_data + codec_data may exist at the same time. */ - void * layout_data; + void* layout_data; + + + /* play config/state */ + int config_enabled; /* config can be used */ + play_config_t config; /* player config (applied over decoding) */ + play_state_t pstate; /* player state (applied over decoding) */ + int loop_count; /* counter of complete loops (1=looped once) */ + int loop_target; /* max loops before continuing with the stream end (loops forever if not set) */ + sample_t* tmpbuf; /* garbage buffer used for seeking/trimming */ + size_t tmpbuf_size; /* for all channels (samples = tmpbuf_size / channels) */ } VGMSTREAM; -#ifdef VGM_USE_VORBIS - -/* standard Ogg Vorbis */ -typedef struct { - STREAMFILE *streamfile; - ogg_int64_t start; /* file offset where the Ogg starts */ - ogg_int64_t offset; /* virtual offset, from 0 to size */ - ogg_int64_t size; /* virtual size of the Ogg */ - - /* decryption setup */ - void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource); - uint8_t scd_xor; - off_t scd_xor_length; - uint32_t xor_value; - -} ogg_vorbis_io; - -typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data; - - -/* custom Vorbis modes */ -typedef enum { - VORBIS_FSB, /* FMOD FSB: simplified/external setup packets, custom packet headers */ - VORBIS_WWISE, /* Wwise WEM: many variations (custom setup, headers and data) */ - VORBIS_OGL, /* Shin'en OGL: custom packet headers */ - VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */ - VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */ -} vorbis_custom_t; - -/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */ -typedef enum { WWV_HEADER_TRIAD, WWV_FULL_SETUP, WWV_INLINE_CODEBOOKS, WWV_EXTERNAL_CODEBOOKS, WWV_AOTUV603_CODEBOOKS } wwise_setup_t; -typedef enum { WWV_TYPE_8, WWV_TYPE_6, WWV_TYPE_2 } wwise_header_t; -typedef enum { WWV_STANDARD, WWV_MODIFIED } wwise_packet_t; - -typedef struct { - /* to reconstruct init packets */ - int channels; - int sample_rate; - int blocksize_0_exp; - int blocksize_1_exp; - - uint32_t setup_id; /* external setup */ - int big_endian; /* flag */ - - /* Wwise Vorbis config */ - wwise_setup_t setup_type; - wwise_header_t header_type; - wwise_packet_t packet_type; - - /* output (kinda ugly here but to simplify) */ - off_t data_start_offset; - -} vorbis_custom_config; - -/* custom Vorbis without Ogg layer */ -typedef struct { - vorbis_info vi; /* stream settings */ - vorbis_comment vc; /* stream comments */ - vorbis_dsp_state vd; /* decoder global state */ - vorbis_block vb; /* decoder local state */ - ogg_packet op; /* fake packet for internal use */ - - uint8_t * buffer; /* internal raw data buffer */ - size_t buffer_size; - - size_t samples_to_discard; /* for looping purposes */ - int samples_full; /* flag, samples available in vorbis buffers */ - - vorbis_custom_t type; /* Vorbis subtype */ - vorbis_custom_config config; /* config depending on the mode */ - - /* Wwise Vorbis: saved data to reconstruct modified packets */ - uint8_t mode_blockflag[64+1]; /* max 6b+1; flags 'n stuff */ - int mode_bits; /* bits to store mode_number */ - uint8_t prev_blockflag; /* blockflag in the last decoded packet */ - /* Ogg-style Vorbis: packet within a page */ - int current_packet; - /* reference for page/blocks */ - off_t block_offset; - size_t block_size; - - int prev_block_samples; /* count for optimization */ - -} vorbis_custom_codec_data; -#endif - - -#ifdef VGM_USE_MPEG -/* Custom MPEG modes, mostly differing in the data layout */ -typedef enum { - MPEG_STANDARD, /* 1 stream */ - MPEG_AHX, /* 1 stream with false frame headers */ - MPEG_XVAG, /* N streams of fixed interleave (frame-aligned, several data-frames of fixed size) */ - MPEG_FSB, /* N streams of 1 data-frame+padding (=interleave) */ - MPEG_P3D, /* N streams of fixed interleave (not frame-aligned) */ - MPEG_SCD, /* N streams of fixed interleave (not frame-aligned) */ - MPEG_EA, /* 1 stream (maybe N streams in absolute offsets?) */ - MPEG_EAL31, /* EALayer3 v1 (SCHl), custom frames with v1 header */ - MPEG_EAL31b, /* EALayer3 v1 (SNS), custom frames with v1 header + minor changes */ - MPEG_EAL32P, /* EALayer3 v2 "PCM", custom frames with v2 header + bigger PCM blocks? */ - MPEG_EAL32S, /* EALayer3 v2 "Spike", custom frames with v2 header + smaller PCM blocks? */ - MPEG_LYN, /* N streams of fixed interleave */ - MPEG_AWC, /* N streams in block layout (music) or absolute offsets (sfx) */ - MPEG_EAMP3 /* custom frame header + MPEG frame + PCM blocks */ -} mpeg_custom_t; - -/* config for the above modes */ -typedef struct { - int channels; /* max channels */ - int fsb_padding; /* fsb padding mode */ - int chunk_size; /* size of a data portion */ - int data_size; /* playable size */ - int interleave; /* size of stream interleave */ - int encryption; /* encryption mode */ - int big_endian; - int skip_samples; - /* for AHX */ - int cri_type; - uint16_t cri_key1; - uint16_t cri_key2; - uint16_t cri_key3; -} mpeg_custom_config; - -/* represents a single MPEG stream */ -typedef struct { - /* per stream as sometimes mpg123 must be fed in passes if data is big enough (ex. EALayer3 multichannel) */ - uint8_t *buffer; /* raw data buffer */ - size_t buffer_size; - size_t bytes_in_buffer; - int buffer_full; /* raw buffer has been filled */ - int buffer_used; /* raw buffer has been fed to the decoder */ - mpg123_handle *m; /* MPEG decoder */ - - uint8_t *output_buffer; /* decoded samples from this stream (in bytes for mpg123) */ - size_t output_buffer_size; - size_t samples_filled; /* data in the buffer (in samples) */ - size_t samples_used; /* data extracted from the buffer */ - - size_t current_size_count; /* data read (if the parser needs to know) */ - size_t current_size_target; /* max data, until something happens */ - size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */ - - int channels_per_frame; /* for rare cases that streams don't share this */ -} mpeg_custom_stream; - -typedef struct { - /* regular/single MPEG internals */ - uint8_t *buffer; /* raw data buffer */ - size_t buffer_size; - size_t bytes_in_buffer; - int buffer_full; /* raw buffer has been filled */ - int buffer_used; /* raw buffer has been fed to the decoder */ - mpg123_handle *m; /* MPEG decoder */ - struct mpg123_frameinfo mi; /* start info, so it's available even when resetting */ - - /* for internal use */ - int channels_per_frame; - int samples_per_frame; - /* for some calcs */ - int bitrate_per_frame; - int sample_rate_per_frame; - - /* custom MPEG internals */ - int custom; /* flag */ - mpeg_custom_t type; /* mpeg subtype */ - mpeg_custom_config config; /* config depending on the mode */ - - size_t default_buffer_size; - mpeg_custom_stream **streams; /* array of MPEG streams (ex. 2ch+2ch) */ - size_t streams_size; - - size_t skip_samples; /* base encoder delay */ - size_t samples_to_discard; /* for custom mpeg looping */ - -} mpeg_codec_data; -#endif - -#ifdef VGM_USE_G7221 -typedef struct g7221_codec_data g7221_codec_data; -#endif - -#ifdef VGM_USE_G719 -typedef struct { - sample_t buffer[960]; - void *handle; -} g719_codec_data; -#endif - -#ifdef VGM_USE_MAIATRAC3PLUS -typedef struct { - sample_t *buffer; - int channels; - int samples_discard; - void *handle; -} maiatrac3plus_codec_data; -#endif - -#ifdef VGM_USE_ATRAC9 -/* ATRAC9 config */ -typedef struct { - int channels; /* to detect weird multichannel */ - uint32_t config_data; /* ATRAC9 config header */ - int encoder_delay; /* initial samples to discard */ -} atrac9_config; -typedef struct atrac9_codec_data atrac9_codec_data; -#endif - -#ifdef VGM_USE_CELT -typedef enum { CELT_0_06_1,CELT_0_11_0} celt_lib_t; -typedef struct celt_codec_data celt_codec_data; -#endif - -/* libacm interface */ -typedef struct { - STREAMFILE *streamfile; - void *handle; - void *io_config; -} acm_codec_data; /* for files made of "continuous" segments, one per section of a song (using a complete sub-VGMSTREAM) */ typedef struct { int segment_count; - VGMSTREAM **segments; + VGMSTREAM** segments; int current_segment; - sample_t *buffer; + sample_t* buffer; int input_channels; /* internal buffer channels */ int output_channels; /* resulting channels (after mixing, if applied) */ } segmented_layout_data; @@ -1165,39 +1002,27 @@ typedef struct { /* for files made of "parallel" layers, one per group of channels (using a complete sub-VGMSTREAM) */ typedef struct { int layer_count; - VGMSTREAM **layers; - sample_t *buffer; + VGMSTREAM** layers; + sample_t* buffer; int input_channels; /* internal buffer channels */ int output_channels; /* resulting channels (after mixing, if applied) */ + int external_looping; /* don't loop using per-layer loops, but layout's own looping */ } layered_layout_data; -/* for compressed NWA */ + + +/* libacm interface */ typedef struct { - NWAData *nwa; -} nwa_codec_data; - -typedef struct relic_codec_data relic_codec_data; - -typedef struct { - STREAMFILE *streamfile; - clHCA_stInfo info; - - signed short *sample_buffer; - size_t samples_filled; - size_t samples_consumed; - size_t samples_to_discard; - - void* data_buffer; - - unsigned int current_block; - + STREAMFILE* streamfile; void* handle; -} hca_codec_data; + void* io_config; +} acm_codec_data; + #ifdef VGM_USE_FFMPEG typedef struct { /*** IO internals ***/ - STREAMFILE *streamfile; + STREAMFILE* streamfile; uint64_t start; // absolute start within the streamfile uint64_t offset; // absolute offset within the streamfile @@ -1253,7 +1078,7 @@ typedef struct { #ifdef VGM_USE_MP4V2 typedef struct { - STREAMFILE *streamfile; + STREAMFILE* streamfile; uint64_t start; uint64_t offset; uint64_t size; @@ -1271,87 +1096,47 @@ typedef struct { INT_PCM sample_buffer[( (6) * (2048)*4 )]; } mp4_aac_codec_data; #endif -#endif +#endif //VGM_USE_MP4V2 -typedef struct ubi_adpcm_codec_data ubi_adpcm_codec_data; - -typedef struct ea_mt_codec_data ea_mt_codec_data; - - -#if 0 -//possible future public/opaque API - -/* define standard C param call and name mangling (to avoid __stdcall / .defs) */ -#define VGMSTREAM_CALL __cdecl //needed? - -/* define external function types (during compilation) */ -#if defined(VGMSTREAM_EXPORT) - #define VGMSTREAM_API __declspec(dllexport) /* when exporting/creating vgmstream DLL */ -#elif defined(VGMSTREAM_IMPORT) - #define VGMSTREAM_API __declspec(dllimport) /* when importing/linking vgmstream DLL */ -#else - #define VGMSTREAM_API /* nothing, internal/default */ -#endif - -//VGMSTREAM_API void VGMSTREAM_CALL vgmstream_function(void); - -//info for opaque VGMSTREAM -typedef struct { - const int channels; - const int sample_rate; - const int num_samples; - const int loop_start_sample; - const int loop_end_sample; - const int loop_flag; - const int num_streams; - const int current_sample; - const int average_bitrate; -} VGMSTREAM_INFO; -void vgmstream_get_info(VGMSTREAM* vgmstream, VGMSTREAM_INFO *vgmstream_info); - -//or maybe -enum vgmstream_value_t { VGMSTREAM_CHANNELS, VGMSTREAM_CURRENT_SAMPLE, ... }; -int vgmstream_get_info(VGMSTREAM* vgmstream, vgmstream_value_t type); -// or -int vgmstream_get_current_sample(VGMSTREAM* vgmstream); - -#endif /* -------------------------------------------------------------------------*/ /* vgmstream "public" API */ /* -------------------------------------------------------------------------*/ /* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */ -VGMSTREAM * init_vgmstream(const char * const filename); +VGMSTREAM* init_vgmstream(const char* const filename); /* init with custom IO via streamfile */ -VGMSTREAM * init_vgmstream_from_STREAMFILE(STREAMFILE *streamFile); +VGMSTREAM* init_vgmstream_from_STREAMFILE(STREAMFILE* sf); /* reset a VGMSTREAM to start of stream */ -void reset_vgmstream(VGMSTREAM * vgmstream); +void reset_vgmstream(VGMSTREAM* vgmstream); /* close an open vgmstream */ -void close_vgmstream(VGMSTREAM * vgmstream); +void close_vgmstream(VGMSTREAM* vgmstream); /* calculate the number of samples to be played based on looping parameters */ -int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM * vgmstream); +int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM* vgmstream); -/* Decode data into sample buffer */ -void render_vgmstream(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +/* Decode data into sample buffer. Returns < sample_count on stream end */ +int render_vgmstream(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); + +/* Seek to sample position (next render starts from that point). Use only after config is set (vgmstream_apply_config) */ +void seek_vgmstream(VGMSTREAM* vgmstream, int32_t seek_sample); /* Write a description of the stream into array pointed by desc, which must be length bytes long. * Will always be null-terminated if length > 0 */ -void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length); +void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length); /* Return the average bitrate in bps of all unique files contained within this stream. */ -int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream); +int get_vgmstream_average_bitrate(VGMSTREAM* vgmstream); /* List supported formats and return elements in the list, for plugins that need to know. * The list disables some common formats that may conflict (.wav, .ogg, etc). */ -const char ** vgmstream_get_formats(size_t * size); +const char** vgmstream_get_formats(size_t* size); /* same, but for common-but-disabled formats in the above list. */ -const char ** vgmstream_get_common_formats(size_t * size); +const char** vgmstream_get_common_formats(size_t* size); /* Force enable/disable internal looping. Should be done before playing anything (or after reset), * and not all codecs support arbitrary loop values ATM. */ @@ -1368,37 +1153,20 @@ int vgmstream_is_virtual_filename(const char* filename); /* -------------------------------------------------------------------------*/ /* Allocate initial memory for the VGMSTREAM */ -VGMSTREAM * allocate_vgmstream(int channel_count, int looped); +VGMSTREAM* allocate_vgmstream(int channel_count, int looped); /* Prepare the VGMSTREAM's initial state once parsed and ready, but before playing. */ -void setup_vgmstream(VGMSTREAM * vgmstream); - -/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */ -int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream); -/* Get the number of bytes of a single frame (smallest self-contained byte group, 1/N channels) */ -int get_vgmstream_frame_size(VGMSTREAM * vgmstream); -/* In NDS IMA the frame size is the block size, so the last one is short */ -int get_vgmstream_samples_per_shortframe(VGMSTREAM * vgmstream); -int get_vgmstream_shortframe_size(VGMSTREAM * vgmstream); - -/* Decode samples into the buffer. Assume that we have written samples_written into the - * buffer already, and we have samples_to_do consecutive samples ahead of us. */ -void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to_do, sample_t * buffer); - -/* Calculate number of consecutive samples to do (taking into account stopping for loop start and end) */ -int vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMSTREAM * vgmstream); - -/* Detect loop start and save values, or detect loop end and restore (loop back). Returns 1 if loop was done. */ -int vgmstream_do_loop(VGMSTREAM * vgmstream); +void setup_vgmstream(VGMSTREAM* vgmstream); /* Open the stream for reading at offset (taking into account layouts, channels and so on). * Returns 0 on failure */ -int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset); -int vgmstream_open_stream_bf(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset, int force_multibuffer); +int vgmstream_open_stream(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t start_offset); +int vgmstream_open_stream_bf(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t start_offset, int force_multibuffer); /* Get description info */ -void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t out_size); -void get_vgmstream_layout_description(VGMSTREAM *vgmstream, char *out, size_t out_size); -void get_vgmstream_meta_description(VGMSTREAM *vgmstream, char *out, size_t out_size); +void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size); +void get_vgmstream_layout_description(VGMSTREAM* vgmstream, char* out, size_t out_size); +void get_vgmstream_meta_description(VGMSTREAM* vgmstream, char* out, size_t out_size); +void setup_state_vgmstream(VGMSTREAM* vgmstream); #endif