mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-16 03:23:19 +01:00
commit
3103029cca
355
fb2k/foo_filetypes.h
Normal file
355
fb2k/foo_filetypes.h
Normal file
@ -0,0 +1,355 @@
|
||||
#ifndef _FOO_FILETYPES_H_
|
||||
#define _FOO_FILETYPES_H_
|
||||
|
||||
|
||||
#define DECLARE_MULTIPLE_FILE_TYPE(NAME,EXTENSION) \
|
||||
namespace { \
|
||||
static input_file_type_impl g_filetype_instance_##EXTENSION(NAME,"*." #EXTENSION ,true); \
|
||||
static service_factory_single_ref_t<input_file_type_impl> g_filetype_service##EXTENSION(g_filetype_instance_##EXTENSION); \
|
||||
}
|
||||
|
||||
|
||||
// Registered file types, to associate an extension with foobar2000 in Windows.
|
||||
// Accepted file types go in formats.c and are checked in input_vgmstream::g_is_our_path.
|
||||
// Both lists don't need to match, formats.c is what really matters
|
||||
// (ie. we won't need to associate every single vgmstream format).
|
||||
|
||||
// these are declared statically, and if anyone has a better idea i'd like to hear it - josh.
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("2DX9 Audio File (*.2DX9)", 2dx9);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("2PFS Audio File (*.2PFS)", 2pfs);
|
||||
|
||||
//"aac", //common, also tri-Ace's
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AA3 Audio File (*.AA3)", aa3);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AAAP Audio File (*.AAAP)", aaap);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AAX Audio File (*.AAX)", aax);
|
||||
//"ac3", //FFmpeg, not parsed //common?
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ACE Audio File (*.ACE)", ace);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ACM Audio File (*.ACM)", acm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ADM Audio File (*.ADM)", adm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ADP Audio File (*.ADP)", adp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ADPCM Audio File (*.ADPCM)", adpcm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ADS Audio File (*.ADS)", ads);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ADX Audio File (*.ADX)", adx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AFC Audio File (*.AFC)", afc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AGSC Audio File (*.AGSC)", agsc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AHX Audio File (*.AHX)", ahx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AIFC Audio File (*.AIFC)", aifc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AIFCL Audio File (*.AIFCL)", aifcl);
|
||||
//"aiff", //common
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AIX Audio File (*.AIX)", aix);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AKB Audio File (*.AKB)", akb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AMTS Audio File (*.AMTS)", amts);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AS4 Audio File (*.AS4)", as4);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ASD Audio File (*.ASD)", asd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ASF Audio File (*.ASF)", asf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ASR Audio File (*.ASR)", asr);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ASS Audio File (*.ASS)", ass);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AST Audio File (*.AST)", ast);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ATRAC3plus Audio File (*.AT3)", at3);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AUD Audio File (*.AUD)", aud);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AUS Audio File (*.AUS)", aus);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("B1S Audio File (*.B1S)", b1s);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BAF Audio File (*.BAF)", baf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BAKA Audio File (*.BAKA)", baka);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BAR Audio File (*.BAR)", bar);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BCSTM Audio File (*.BCSTM)", bcstm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BCWAV Audio File (*.BCWAV)", bcwav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BDSP Audio File (*.BDSP)", bdsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BFSTM Audio File (*.BFSTM)", bfstm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BFWAV Audio File (*.BFWAV)", bfwav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BFWAV Audio File [2] (*.BFWAV)", bfwavnsmbu);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BG00 Audio File (*.BG00)", bg00);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BGM Audio File (*.BGM)", bgm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BGW Audio File (*.BGW)", bgw);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BH2PCM Audio File (*.BH2PCM)", bh2pcm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BIK Audio File (*.BIK)", bik);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BIKA Audio File (*.BIKA)", bika);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BIK2 Audio File (*.BIK2)", bik2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BIK2A Audio File (*.BIK2A)", bik2a);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BK2 Audio File (*.BK2)", bk2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BK2A Audio File (*.BK2A)", bk2a);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BMDX Audio File (*.BMDX)", bmdx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BMS Audio File (*.BMS)", bms);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KLBS Audio File (*.BNK)", bnk);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BNS Audio File (*.BNS)", bns);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BNSF Audio File (*.BNSF)", bnsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BO2 Audio File (*.BO2)", bo2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BRSTM Audio File (*.BRSTM)", brstm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BRSTM Audio File [2] (*.BRSTM)", brstmspm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BTSND Audio File (*.BTSND)", btsnd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BVG Audio File (*.BVG)", bvg);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CAF Audio File (*.CAF)", caf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CAPDSP Audio File (*.CAPDSP)", capdsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CBD2 Audio File (*.CBD2)", cbd2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CCC Audio File (*.CCC)", ccc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CFN Audio File (*.CFN)", cfn);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CKD Audio File (*.CKD)", ckd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CNK Audio File (*.CNK)", cnk);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CPS Audio File (*.CPS)", cps);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CXS Audio File (*.CXS)", cxs);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DBM Audio File (*.DBM)", dbm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DCS Audio File (*.DCS)", dcs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DDSP Audio File (*.DDSP)", ddsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DE2 Audio File (*.DE2)", de2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DMSG Audio File (*.DMSG)", dmsg);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DSP Audio File (*.DSP)", dsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DSPW Audio File (*.DSPW)", dspw);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DTK Audio File (*.DTK)", dtk);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DVI Audio File (*.DVI)", dvi);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DXH Audio File (*.DXH)", dxh);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("EAM Audio File (*.EAM)", eam);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("EMFF Audio File (*.EMFF)", emff);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ENTH Audio File (*.ENTH)", enth);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("FAG Audio File (*.FAG)", fag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("FFW Audio File (*.FFW)", ffw);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("FILP Audio File (*.FILP)", filp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("FSB Audio File (*.FSB)", fsb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("FWAV Audio File (*.FWAV)", fwav);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("G1L Audio File (*.G1L)", g1l);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GBTS Audio File (*.GBTS)", gbts);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GCA Audio File (*.GCA)", gca);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GCM Audio File (*.GCM)", gcm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GCUB Audio File (*.GCUB)", gcub);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GCW Audio File (*.GCW)", gcw);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GENH Audio File (*.GENH)", genh);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GMS Audio File (*.GMS)", gms);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GSB Audio File (*.GSB)", gsb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GSB Audio File (*.GTD)", gtd);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HCA Audio File (*.HCA)", hca);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HGC1 Audio File (*.HGC1)", hgc1);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HIS Audio File (*.HIS)", his);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HLWAV Audio File (*.HLWAV)", hlwav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HALPST Audio File (*.HPS)", hps);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HSF Audio File (*.HSF)", hsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HWAS Audio File (*.HWAS)", hwas);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IAB Audio File (*.IAB)", iab);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IADP Audio File (*.IADP)", iadp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IDSP Audio File (*.IDSP)", idsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IDVI Audio File (*.IDVI)", idvi);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IKM Audio File (*.IKM)", ikm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ILD Audio File (*.ILD)", ild);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("INT Audio File (*.INT)", int);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ISD Audio File (*.ISD)", isd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ISWS Audio File (*.ISWS)", isws);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IVAUD Audio File (*.IVAUD)", ivaud);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IVAG Audio File (*.IVAG)", ivag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IVB Audio File (*.IVB)", ivb);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("JOE Audio File (*.JOE)", joe);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("JSTM Audio File (*.JSTM)", jstm);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KCES Audio File (*.KCES)", kces);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KCEY Audio File (*.KCEY)", kcey);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KHV Audio File (*.KHV)", khv);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KOVS Audio File (*.KOVS)", kovs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KRAW Audio File (*.KRAW)", kraw);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LAAC Audio File (*.LAAC)", laac);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LEG Audio File (*.LEG)", leg);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LMP4 Audio File (*.LMP4)", lmp4);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LOGG Audio File (*.LOGG)", logg);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LPCM Audio File (*.LPCM)", lpcm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LPS Audio File (*.LPS)", lps);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LSF Audio File (*.LSF)", lsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LWAV Audio File (*.LWAV)", lwav);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MATX Audio File (*.MATX)", matx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MC3 Audio File (*.MC3)", mc3);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MCA Audio File (*.MCA)", mca);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MCG Audio File (*.MCG)", mcg);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MDS Audio File (*.MDS)", mds);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MDSP Audio File (*.MDSP)", mdsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MI4 Audio File (*.MI4)", mi4);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MIB Audio File (*.MIB)", mib);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MIC Audio File (*.MIC)", mic);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MIHB Audio File (*.MIHB)", mihb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MNSTR Audio File (*.MNSTR)", mnstr);
|
||||
//"mp4", //common
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MPDSP Audio File (*.MPDSP)", mpdsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MPDS Audio File (*.MPDS)", mpds);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MSA Audio File (*.MSA)", msa);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MSF Audio File (*.MSF)", msf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MSS Audio File (*.MSS)", mss);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MSVP Audio File (*.MSVP)", msvp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MTA2 Audio File (*.MTA2)", mta2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MTAF Audio File (*.MTAF)", mtaf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MUS Playlist File (*.MUS)", mus);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MUSC Audio File (*.MUSC)", musc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MUSX Audio File (*.MUSX)", musx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MWV Audio File (*.MWV)", mwv);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MxSt Audio File (*.MxSt)", mxst);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MYSPD Audio File (*.MYSPD)", myspd);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("NDP Audio File (*.NDP)", ndp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("NGCA Audio File (*.NGCA)", ngca);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("NPS Audio File (*.NPS)", nps);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("NPSF Audio File (*.NPSF)", npsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("NUS3BANK Audio File (*.NUS3BANK)", nus3bank);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("NWA Audio File (*.NWA)", nwa);
|
||||
|
||||
//"ogg", //common
|
||||
DECLARE_MULTIPLE_FILE_TYPE("OGL Audio File (*.OGL)", ogl);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("OMA Audio File (*.OMA)", oma);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("OMU Audio File (*.OMU)", omu);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("OTM Audio File (*.OTM)", otm);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("P2BT Audio File (*.P2BT)", p2bt);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("P3D Audio File (*.P3D)", p3d);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PAST Audio File (*.PAST)", past);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PCM Audio File (*.PCM)", pcm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PDT Audio File (*.PDT)", pdt);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PNB Audio File (*.PNB)", pnb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PONA Audio File (*.PONA)", pona);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("POS Audio File (*.POS)", pos);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2STM Audio File (*.PS2STM)", ps2stm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PSH Audio File (*.PSH)", psh);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PSND Audio File (*.PSND)", psnd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PSW Audio File (*.PSW)", psw);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RAK Audio File (*.RAK)", rak);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RAS Audio File (*.RAS)", ras);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RAW Audio File (*.RAW)", raw);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RKV Audio File (*.RKV)", rkv);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RND Audio File (*.RND)", rnd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RRDS Audio File (*.RRDS)", rrds);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RSD Audio File (*.RSD)", rsd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RSF Audio File (*.RSF)", rsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RSTM Audio File (*.RSTM)", rstm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RVWS Audio File (*.RSTM)", rvws);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RWAR Audio File (*.RWSD)", rwar);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RWAV Audio File (*.RWAV)", rwav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RWS Audio File (*.RWS)", rws);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RWSD Audio File (*.RWSD)", rwsd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RWX Audio File (*.RWX)", rwx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RXWS File (*.RXW)", rxw);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("S14 Audio File (*.S14)", s14);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SAB Audio File (*.SAB)", sab);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SAD Audio File (*.SAD)", sad);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SAP Audio File (*.SAP)", sap);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SC Audio File (*.SC)", sc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SCD Audio File (*.SCD)", scd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SCK Audio File (*.SCK)", sck);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SD9 Audio File (*.SD9)", sd9);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SDT Audio File (*.SDT)", sdt);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SEG Audio File (*.SEG)", seg);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SF0 Audio File (*.SF0)", sf0);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SFL Audio File (*.SFL)", sfl);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SFS Audio File (*.SFS)", sfs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SFX Audio File (*.SFX)", sfx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SGB Audio File (*.SGB)", sgb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SGD Audio File (*.SGD)", sgd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SGX Audio File (*.SGX)", sgx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SL3 Audio File (*.SL3)", sl3);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SLI Audio File (*.SLI)", sli);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SMP Audio File (*.SMP)", smp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SMPL Audio File (*.SMPL)", smpl);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SND Audio File (*.SND)", snd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SNDS Audio File (*.SNDS)", snds);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SNG Audio File (*.SNG)", sng);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SNS Audio File (*.SNS)", sns);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SPD Audio File (*.SPD)", spd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SPM Audio File (*.SPM)", spm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SPS Audio File (*.SPS)", sps);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SPSD Audio File (*.SPSD)", spsd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SPW Audio File (*.SPW)", spw);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SS2 Audio File (*.SS2)", ss2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SS3 Audio File (*.SS3)", ss3);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SS7 Audio File (*.SS7)", ss7);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SSM Audio File (*.SSM)", ssm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SSS Audio File (*.SSS)", sss);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STER Audio File (*.STER)", ster);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STH Audio File (*.STH)", sth);
|
||||
//"stm", //common
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STMA Audio File (*.STMA)", stma);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STR Audio File (*.STR)", str);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STRM Audio File (*.STRM)", strm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STS Audio File (*.STS)", sts);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STX Audio File (*.STX)", stx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SVAG Audio File (*.SVAG)", svag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SVS Audio File (*.SVS)", svs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SWAG Audio File (*.SWAG)", swag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SWAV Audio File (*.SWAV)", swav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SWD Audio File (*.SWD)", swd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SXD Audio File (*.SXD)", sxd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SXD2 Audio File (*.SXD2)", sxd2);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("TEC Audio File (*.TEC)", tec);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("THP Audio File (*.THP)", thp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("TK1 Audio File (*.TK1)", tk1);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("TK5 Audio File (*.TK5)", tk5);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("TRA Audio File (*.TRA)", tra);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("TUN Audio File (*.TUN)", tun);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("TYDSP Audio File (*.TYDSP)", tydsp);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("UM3 Audio File (*.UM3)", um3);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VAG Audio File (*.VAG)", vag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VAS Audio File (*.VAS)", vas);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VAS Audio File (*.VAS)", vawx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VB Audio File (*.VB)", vb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VBK Audio File (*.VBK)", vbk);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VGS Audio File (*.VGS)", vgs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VGV Audio File (*.VGV)", vgv);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VIG Audio File (*.VIG)", vig);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VMS Audio File (*.VMS)", vms);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VOI Audio File (*.VOI)", voi);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VPK Audio File (*.VPK)", vpk);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VS Audio File (*.VS)", vs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VSF Audio File (*.VSF)", vsf);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAA Audio File (*.WAA)", waa);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAC Audio File (*.WAC)", wac);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAD Audio File (*.WAD)", wad);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAM Audio File (*.WAM)", wam);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAS Audio File (*.WAS)", was);
|
||||
//"wav", //common
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAVM Audio File (*.WAVM)", wavm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WEM Audio File (*.WEM)", wem);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WII Audio File (*.WII)", wii);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WMUS Audio File (*.WMUS)", wmus);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WP2 Audio File (*.WP2)", wp2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WPD Audio File (*.WPD)", wpd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WSD Audio File (*.WSD)", wsd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WSI Audio File (*.WSI)", wsi);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WVS Audio File (*.WVS)", wvs);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XA File (*.XA)", xa);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XA2 Audio File (*.XA2)", xa2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XA30 Audio File (*.XA30)", xa30);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XAG Audio File (*.XAG)", xag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XAU Audio File (*.XAU)", xau);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XMA Audio File (*.XMA)", xma);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XMA2 Audio File (*.XMA2)", xma2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XMU Audio File (*.XMU)", xmu);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XNB Audio File (*.XNB)", xnb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XSF Audio File (*.XSF)", xsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XSS Audio File (*.XSS)", xss);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XVAG Audio File (*.XVAG)", xvag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XVAS Audio File (*.XVAS)", xvas);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XWAV Audio File (*.XWAV)", xwav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XWB Audio File (*.XWB)", xwb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XWM Audio File (*.XWM)", xwm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XWMA Audio File (*.XWMA)", xwma);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XWS Audio File (*.XWS)", xws);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XWV Audio File (*.XWV)", xwv);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("YDSP Audio File (*.YDSP)", ydsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("YMF Audio File (*.YMF)", ymf);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ZSD Audio File (*.ZSD)", zsd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ZWDSP Audio File (*.ZWDSP)", zwdsp);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VGMSTREAM Audio File (*.VGMSTREAM)", vgmstream);
|
||||
|
||||
|
||||
#endif /*_FOO_FILETYPES_H_ */
|
@ -162,6 +162,7 @@
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="foo_filetypes.h" />
|
||||
<ClInclude Include="foo_prefs.h" />
|
||||
<ClInclude Include="foo_vgmstream.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
|
@ -21,6 +21,9 @@
|
||||
<ClInclude Include="foo_prefs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="foo_filetypes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -20,6 +20,7 @@ extern "C" {
|
||||
#include "../src/vgmstream.h"
|
||||
}
|
||||
#include "foo_vgmstream.h"
|
||||
#include "foo_filetypes.h"
|
||||
|
||||
#ifndef VERSION
|
||||
#include "../version.h"
|
||||
@ -187,6 +188,26 @@ void input_vgmstream::get_info(file_info & p_info,abort_callback & p_abort ) {
|
||||
p_info.info_set("metadata source", temp);
|
||||
}
|
||||
|
||||
pos = description.find_first("number of streams: ");
|
||||
if (pos != pfc::infinite_size)
|
||||
{
|
||||
pos += strlen("number of streams: ");
|
||||
eos = description.find_first('\n', pos);
|
||||
if (eos == pfc::infinite_size) eos = description.length();
|
||||
temp.set_string(description + pos, eos - pos);
|
||||
p_info.info_set("number of streams", temp);
|
||||
}
|
||||
|
||||
pos = description.find_first("block size: ");
|
||||
if (pos != pfc::infinite_size)
|
||||
{
|
||||
pos += strlen("block size: ");
|
||||
eos = description.find_first('\n', pos);
|
||||
if (eos == pfc::infinite_size) eos = description.length();
|
||||
temp.set_string(description + pos, eos - pos);
|
||||
p_info.info_set("block size", temp);
|
||||
}
|
||||
|
||||
p_info.set_length(((double)length_in_ms)/1000);
|
||||
}
|
||||
|
||||
@ -398,311 +419,3 @@ static input_singletrack_factory_t<input_vgmstream> g_input_vgmstream_factory;
|
||||
|
||||
DECLARE_COMPONENT_VERSION(APP_NAME,PLUGIN_VERSION,PLUGIN_DESCRIPTION);
|
||||
VALIDATE_COMPONENT_FILENAME("foo_input_vgmstream.dll");
|
||||
|
||||
// Registered file types, to associate an extension with foobar2000 in Windows.
|
||||
// Accepted types go in input_vgmstream::g_is_our_path; both lists don't need to match.
|
||||
// todo do we really want to associate every single vgmstream format?
|
||||
//
|
||||
// these are declared statically, and if anyone has a better idea i'd like to hear it - josh.
|
||||
DECLARE_MULTIPLE_FILE_TYPE("2DX9 Audio File (*.2DX9)", 2dx9);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("2PFS Audio File (*.2PFS)", 2pfs);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AAAP Audio File (*.AAAP)", aaap);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AAX Audio File (*.AAX)", aax);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ACM Audio File (*.ACM)", acm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ADPCM Audio File (*.ADPCM)", adpcm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ADM Audio File (*.ADM)", adm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ADP Audio File (*.ADP)", adp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2 ADS Audio File (*.ADS)", ads);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ADX Audio File (*.ADX)", adx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AFC Audio File (*.AFC)", afc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AGSC Audio File (*.AGSC)", agsc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AHX Audio File (*.AHX)", ahx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AIFC Audio File (*.AIFC)", aifc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AIX Audio File (*.AIX)", aix);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AKB Audio File (*.AKB)", akb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AMTS Audio File (*.AMTS)", amts);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AS4 Audio File (*.AS4)", as4);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ASD Audio File (*.ASD)", asd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ASF Audio File (*.ASF)", asf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AST Audio File (*.AST)", ast);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ASR Audio File (*.ASR)", asr);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ASS Audio File (*.ASS)", ass);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ATRAC3plus Audio File (*.AT3)", at3);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AUD Audio File (*.AUD)", aud);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("AUS Audio File (*.AUS)", aus);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BAKA Audio File (*.BAKA)", baka);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BAF Audio File (*.BAF)", baf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BAR Audio File (*.BAR)", bar);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BCSTM Audio File (*.BCSTM)", bcstm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BCWAV Audio File (*.BCWAV)", bcwav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BFSTM Audio File (*.BFSTM)", bfstm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BFWAV Audio File (*.BFWAV)", bfwav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BFWAV Audio File [2] (*.BFWAV)", bfwavnsmbu);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BG00 Audio File (*.BG00)", bg00);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BGW Audio File (*.BGW)", bgw);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BH2PCM Audio File (*.BH2PCM)", bh2pcm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BIK Audio File (*.BIK)", bik);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BIKA Audio File (*.BIKA)", bika);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BIK2 Audio File (*.BIK2)", bik2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BIK2A Audio File (*.BIK2A)", bik2a);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BK2 Audio File (*.BK2)", bk2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BK2A Audio File (*.BK2A)", bk2a);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BMDX Audio File (*.BMDX)", bmdx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BMS Audio File (*.BMS)", bms);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KLBS Audio File (*.BNK)", bnk);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BNS Audio File (*.BNS)", bns);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BNSF Audio File (*.BNSF)", bnsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BO2 Audio File (*.BO2)", bo2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BRSTM Audio File (*.BRSTM)", brstm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BRSTM Audio File [2] (*.BRSTM)", brstmspm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BTSND Audio File (*.BTSND)", btsnd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("BVG Audio File (*.BVG)", bvg);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CAF Audio File (*.CAF)", caf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CAPDSP Audio File (*.CAPDSP)", capdsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CBD2 Audio File (*.CBD2)", cbd2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CCC Audio File (*.CCC)", ccc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CFN Audio File (*.CFN)", cfn);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CKD Audio File (*.CKD)", ckd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CNK Audio File (*.CNK)", cnk);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("CPS Audio File (*.CPS)", cps);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DCS Audio File (*.DCS)", dcs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DE2 Audio File (*.DE2)", de2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DDSP Audio File (*.DDSP)", ddsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DMSG Audio File (*.DMSG)", dmsg);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DSP Audio File (*.DSP)", dsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DSPW Audio File (*.DSPW)", dspw);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DTK Audio File (*.DTK)", dtk);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DVI Audio File (*.DVI)", dvi);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("DXH Audio File (*.DXH)", dxh);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("EAM Audio File (*.EAM)", eam);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("EMFF Audio File (*.EMFF)", emff);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ENTH Audio File (*.ENTH)", enth);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("FAG Audio File (*.FAG)", fag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("FILP Audio File (*.FILP)", filp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("FSB Audio File (*.FSB)", fsb);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("G1L Audio File (*.G1L)", g1l);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GBTS Audio File (*.GBTS)", gbts);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GCA Audio File (*.GCA)", gca);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GCM Audio File (*.GCM)", gcm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GCUB Audio File (*.GCUB)", gcub);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GCW Audio File (*.GCW)", gcw);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GENH Audio File (*.GENH)", genh);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GMS Audio File (*.GMS)", gms);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("GSB Audio File (*.GSB)", gsb);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HCA Audio File (*.HCA)", hca);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HGC1 Audio File (*.HGC1)", hgc1);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HIS Audio File (*.HIS)", his);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HLWAV Audio File (*.HLWAV)", hlwav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HALPST Audio File (*.HPS)", hps);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HSF Audio File (*.HSF)", hsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("HWAS Audio File (*.HWAS)", hwas);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IDSP Audio File (*.IDSP)", idsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IDVI Audio File (*.IDVI)", idvi);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IKM Audio File (*.IKM)", ikm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ILD Audio File (*.ILD)", ild);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2 RAW Interleaved PCM (*.INT)", int);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ISD Audio File (*.ISD)", isd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ISWS Audio File (*.ISWS)", isws);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IVAUD Audio File (*.IVAUD)", ivaud);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IVAG Audio File (*.IVAG)", ivag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("IVB Audio File (*.IVB)", ivb);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("JOE Audio File (*.JOE)", joe);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("JSTM Audio File (*.JSTM)", jstm);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KCES Audio File (*.KCES)", kces);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KCEY Audio File (*.KCEY)", kcey);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KHV Audio File (*.KHV)", khv);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KOVS Audio File (*.KOVS)", kovs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("KRAW Audio File (*.KRAW)", kraw);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LEG Audio File (*.LEG)", leg);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LOGG Audio File (*.LOGG)", logg);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LMP4 Audio File (*.LMP4)", lmp4);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LPCM Audio File (*.LPCM)", lpcm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LPS Audio File (*.LPS)", lps);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LSF Audio File (*.LSF)", lsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("LWAV Audio File (*.LWAV)", lwav);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MATX Audio File (*.MATX)", matx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MC3 Audio File (*.MC3)", mc3);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MCA Audio File (*.MCA)", mca);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MCG Audio File (*.MCG)", mcg);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MDS Audio File (*.MDS)", mds);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MDSP Audio File (*.MDSP)", mdsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2 MI4 Audio File (*.MI4)", mi4);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2 MIB Audio File (*.MIB)", mib);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2 MIC Audio File (*.MIC)", mic);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MIHB Audio File (*.MIHB)", mihb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MNSTR Audio File (*.MNSTR)", mnstr);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MPDSP Audio File (*.MPDSP)", mpdsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MPDS Audio File (*.MPDS)", mpds);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MSA Audio File (*.MSA)", msa);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MSF Audio File (*.MSF)", msf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MSS Audio File (*.MSS)", mss);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MSVP Audio File (*.MSVP)", msvp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MTAF Audio File (*.MTAF)", mtaf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MUS Playlist File (*.MUS)", mus);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MUSC Audio File (*.MUSC)", musc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MUSX Audio File (*.MUSX)", musx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MWV Audio File (*.MWV)", mwv);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MxSt Audio File (*.MxSt)", mxst);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("MYSPD Audio File (*.MYSPD)", myspd);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("NDP Audio File (*.NDP)", ndp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("NGCA Audio File (*.NGCA)", ngca);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2 NPSF Audio File (*.NPSF)", npsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("NUS3BANK Audio File (*.NUS3BANK)", nus3bank);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("NWA Audio File (*.NWA)", nwa);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("OGL Audio File (*.OGL)", ogl);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("OMU Audio File (*.OMU)", omu);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("OTM Audio File (*.OTM)", otm);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("P2BT Audio File (*.P2BT)", p2bt);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("P3D Audio File (*.P3D)", p3d);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PAST Audio File (*.PAST)", past);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PCM Audio File (*.PCM)", pcm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PDT Audio File (*.PDT)", pdt);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PNB Audio File (*.PNB)", pnb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PONA Audio File (*.PONA)", pona);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("POS Audio File (*.POS)", pos);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2STM Audio File (*.PS2STM)", ps2stm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PSH Audio File (*.PSH)", psh);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PSND Audio File (*.PSND)", psnd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PSW Audio File (*.PSW)", psw);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RAK Audio File (*.RAK)", rak);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RAS Audio File (*.RAS)", ras);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RAW Audio File (*.RAW)", raw);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RKV Audio File (*.RKV)", rkv);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RND Audio File (*.RND)", rnd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RRDS Audio File (*.RRDS)", rrds);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RSD Audio File (*.RSD)", rsd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RSF Audio File (*.RSF)", rsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RSTM Audio File (*.RSTM)", rstm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RVWS Audio File (*.RSTM)", rvws);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RWAR Audio File (*.RWSD)", rwar);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RWAV Audio File (*.RWAV)", rwav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RWS Audio File (*.RWS)", rws);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RWSD Audio File (*.RWSD)", rwsd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("RWX Audio File (*.RWX)", rwx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2 RXWS File (*.RXW)", rxw);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("S14 Audio File (*.S14)", s14);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SAB Audio File (*.SAB)", sab);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SAD Audio File (*.SAD)", sad);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SAP Audio File (*.SAP)", sap);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SC Audio File (*.SC)", sc);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SCD Audio File (*.SCD)", scd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SCK Audio File (*.SCK)", sck);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SD9 Audio File (*.SD9)", sd9);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SDT Audio File (*.SDT)", sdt);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SEG Audio File (*.SEG)", seg);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SF0 Audio File (*.SF0)", sf0);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SFL Audio File (*.SFL)", sfl);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SFS Audio File (*.SFS)", sfs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SFX Audio File (*.SFX)", sfx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SGB Audio File (*.SGB)", sgb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SGD Audio File (*.SGD)", sgd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SGX Audio File (*.SGX)", sgx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SL3 Audio File (*.SL3)", sl3);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SLI Audio File (*.SLI)", sli);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SMP Audio File (*.SMP)", smp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SMPL Audio File (*.SMPL)", smpl);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SND Audio File (*.SND)", snd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SNDS Audio File (*.SNDS)", snds);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SNG Audio File (*.SNG)", sng);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SNS Audio File (*.SNS)", sns);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SPD Audio File (*.SPD)", spd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SPM Audio File (*.SPM)", spm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SPS Audio File (*.SPS)", sps);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SPSD Audio File (*.SPSD)", spsd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SPW Audio File (*.SPW)", spw);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2 SS2 Audio File (*.SS2)", ss2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SS3 Audio File (*.SS3)", ss3);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SS7 Audio File (*.SS7)", ss7);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SSM Audio File (*.SSM)", ssm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SSS Audio File (*.SSS)", sss);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STER Audio File (*.STER)", ster);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STMA Audio File (*.STMA)", stma);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STR Audio File (*.STR)", str);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STRM Audio File (*.STRM)", strm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2 EXST Audio File (*.STS)", sts);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("STX Audio File (*.STX)", stx);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PS2 SVAG Audio File (*.SVAG)", svag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SVS Audio File (*.SVS)", svs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SWAV Audio File (*.SWAV)", swav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SWD Audio File (*.SWD)", swd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SXD Audio File (*.SXD)", sxd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("SXD2 Audio File (*.SXD2)", sxd2);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("TEC Audio File (*.TEC)", tec);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("THP Audio File (*.THP)", thp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("TK1 Audio File (*.TK1)", tk1);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("TK5 Audio File (*.TK5)", tk5);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("TUN Audio File (*.TUN)", tun);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("TYDSP Audio File (*.TYDSP)", tydsp);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("UM3 Audio File (*.UM3)", um3);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VAG Audio File (*.VAG)", vag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VAS Audio File (*.VAS)", vas);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VB Audio File (*.VB)", vb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VBK Audio File (*.VBK)", vbk);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VGS Audio File (*.VGS)", vgs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VGV Audio File (*.VGV)", vgv);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VIG Audio File (*.VIG)", vig);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VMS Audio File (*.VMS)", vms);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VOI Audio File (*.VOI)", voi);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VPK Audio File (*.VPK)", vpk);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VS Audio File (*.VS)", vs);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("VSF Audio File (*.VSF)", vsf);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAA Audio File (*.WAA)", waa);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAC Audio File (*.WAC)", wac);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAD Audio File (*.WAD)", wad);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAM Audio File (*.WAM)", wam);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAVM Audio File (*.WAVM)", wavm);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WAS Audio File (*.WAS)", was);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WEM Audio File (*.WEM)", wem);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WII Audio File (*.WII)", wii);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WMUS Audio File (*.WMUS)", wmus);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WP2 Audio File (*.WP2)", wp2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WPD Audio File (*.WPD)", wpd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WSD Audio File (*.WSD)", wsd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WSI Audio File (*.WSI)", wsi);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("WVS Audio File (*.WVS)", wvs);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("PSX CD-XA File (*.XA)", xa);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XA2 Audio File (*.XA2)", xa2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XA30 Audio File (*.XA30)", xa30);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XAU Audio File (*.XAU)", xau);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XMA Audio File (*.XMA)", xma);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XMA2 Audio File (*.XMA2)", xma2);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XMU Audio File (*.XMU)", xmu);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XNB Audio File (*.XNB)", xnb);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XSF Audio File (*.XSF)", xsf);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XSS Audio File (*.XSS)", xss);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XVAG Audio File (*.XVAG)", xvag);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XVAS Audio File (*.XVAS)", xvas);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XWAV Audio File (*.XWAV)", xwav);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("XWB Audio File (*.XWB)", xwb);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("YDSP Audio File (*.YDSP)", ydsp);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("YMF Audio File (*.YMF)", ymf);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ZSD Audio File (*.ZSD)", zsd);
|
||||
DECLARE_MULTIPLE_FILE_TYPE("ZWDSP Audio File (*.ZWDSP)", zwdsp);
|
||||
|
||||
DECLARE_MULTIPLE_FILE_TYPE("vgmstream Audio File (*.VGMSTREAM)", vgmstream);
|
||||
|
@ -75,10 +75,4 @@ STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_file,const
|
||||
STREAMFILE * open_foo_streamfile_buffer(const char * const filename, size_t buffersize, abort_callback * p_abort, t_filestats * stats);
|
||||
STREAMFILE * open_foo_streamfile(const char * const filename, abort_callback * p_abort, t_filestats * stats);
|
||||
|
||||
|
||||
#define DECLARE_MULTIPLE_FILE_TYPE(NAME,EXTENSION) \
|
||||
namespace { static input_file_type_impl g_filetype_instance_##EXTENSION(NAME,"*." #EXTENSION ,true); \
|
||||
static service_factory_single_ref_t<input_file_type_impl> g_filetype_service##EXTENSION(g_filetype_instance_##EXTENSION); }
|
||||
|
||||
|
||||
#endif
|
||||
#endif /*_FOO_VGMSTREAM_*/
|
||||
|
@ -43,6 +43,7 @@ Options:
|
||||
-E: force end-to-end looping even if file has real loop points
|
||||
-r outfile2.wav: output a second time after resetting
|
||||
-2 N: only output the Nth (first is 0) set of stereo channels
|
||||
-F: don't fade after N loops and play the rest of the stream
|
||||
|
||||
Typical usage would be:
|
||||
test -o happy.wav happy.adx
|
||||
|
@ -114,6 +114,9 @@ void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
|
||||
/* mtaf_decoder */
|
||||
void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int channels);
|
||||
|
||||
/* mta2_decoder */
|
||||
void decode_mta2(VGMSTREAMCHANNEL * stream, sample * 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);
|
||||
|
||||
@ -201,9 +204,13 @@ void free_at3plus(maiatrac3plus_codec_data *data);
|
||||
|
||||
#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);
|
||||
|
||||
void decode_ffmpeg(VGMSTREAM *stream, sample * 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 ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
|
||||
#endif
|
||||
|
@ -1,7 +1,34 @@
|
||||
#include "../vgmstream.h"
|
||||
#include "coding.h"
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
/* internal sizes, can be any value */
|
||||
#define FFMPEG_DEFAULT_BUFFER_SIZE 2048
|
||||
#define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024
|
||||
|
||||
|
||||
static volatile int g_ffmpeg_initialized = 0;
|
||||
|
||||
|
||||
/* ******************************************** */
|
||||
/* INTERNAL UTILS */
|
||||
/* ******************************************** */
|
||||
|
||||
/* Global FFmpeg init */
|
||||
static void g_init_ffmpeg() {
|
||||
if (g_ffmpeg_initialized == 1) {
|
||||
while (g_ffmpeg_initialized < 2); /* active wait for lack of a better way */
|
||||
}
|
||||
else if (g_ffmpeg_initialized == 0) {
|
||||
g_ffmpeg_initialized = 1;
|
||||
av_log_set_flags(AV_LOG_SKIP_REPEATED);
|
||||
av_log_set_level(AV_LOG_ERROR);
|
||||
av_register_all();
|
||||
g_ffmpeg_initialized = 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* converts codec's samples (can be in any format, ex. Ogg's float32) to PCM16 */
|
||||
static void convert_audio(sample *outbuf, const uint8_t *inbuf, int sampleCount, int bitsPerSample, int floatingPoint) {
|
||||
int s;
|
||||
switch (bitsPerSample) {
|
||||
@ -59,15 +86,390 @@ static void convert_audio(sample *outbuf, const uint8_t *inbuf, int sampleCount,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special patching for FFmpeg's buggy seek code.
|
||||
*
|
||||
* To seek with avformat_seek_file/av_seek_frame, FFmpeg's demuxers can implement read_seek2 (newest API)
|
||||
* or read_seek (older API), with various search modes. If none are available it will use seek_frame_generic,
|
||||
* which manually reads frame by frame until the selected timestamp. However, the prev frame will be consumed
|
||||
* (so after seeking to 0 next av_read_frame will actually give the second frame and so on).
|
||||
*
|
||||
* Fortunately seek_frame_generic can use an index to find the correct position. This function reads the
|
||||
* 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) {
|
||||
int ret, ts_index, found_first = 0;
|
||||
int64_t ts = 0;
|
||||
int64_t pos = 0; /* offset */
|
||||
int size = 0; /* coded size */
|
||||
int distance = 0; /* always? */
|
||||
|
||||
AVStream * stream;
|
||||
AVPacket * pkt;
|
||||
|
||||
stream = data->formatCtx->streams[data->streamIndex];
|
||||
pkt = data->lastReadPacket;
|
||||
|
||||
/* read_seek shouldn't need this index, but direct access to FFmpeg's internals is no good */
|
||||
/* if (data->formatCtx->iformat->read_seek || data->formatCtx->iformat->read_seek2)
|
||||
return 0; */
|
||||
|
||||
/* some formats already have a proper index (e.g. M4A) */
|
||||
ts_index = av_index_search_timestamp(stream, ts, AVSEEK_FLAG_ANY);
|
||||
if (ts_index>=0)
|
||||
goto test_seek;
|
||||
|
||||
|
||||
/* find the first + second packets to get pos/size */
|
||||
while (1) {
|
||||
av_packet_unref(pkt);
|
||||
ret = av_read_frame(data->formatCtx, pkt);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (pkt->stream_index != data->streamIndex)
|
||||
continue; /* ignore non-selected streams */
|
||||
|
||||
if (!found_first) { /* first found */
|
||||
found_first = 1;
|
||||
pos = pkt->pos;
|
||||
ts = pkt->dts;
|
||||
continue;
|
||||
} else { /* second found */
|
||||
size = pkt->pos - pos; /* coded, pkt->size is decoded size */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_first)
|
||||
goto fail;
|
||||
|
||||
/* in rare cases there is only one packet */
|
||||
/* if (size == 0) { size = data_end - pos; } */ /* no easy way to know, ignore (most formats don's need size) */
|
||||
|
||||
/* some formats (XMA1) don't seem to have packet.dts, pretend it's 0 */
|
||||
if (ts == INT64_MIN)
|
||||
ts = 0;
|
||||
|
||||
/* Some streams start with negative DTS (observed in Ogg). For Ogg seeking to negative or 0 doesn't alter the output.
|
||||
* It does seem seeking before decoding alters a bunch of (inaudible) +-1 lower bytes though. */
|
||||
VGM_ASSERT(ts != 0, "FFMPEG: negative start_ts (%li)\n", (long)ts);
|
||||
if (ts != 0)
|
||||
ts = 0;
|
||||
|
||||
/* add index 0 */
|
||||
ret = av_add_index_entry(stream, pos, ts, size, distance, AVINDEX_KEYFRAME);
|
||||
if ( ret < 0 )
|
||||
return ret;
|
||||
|
||||
|
||||
test_seek:
|
||||
/* seek to 0 test / move back to beginning, since we just consumed packets */
|
||||
ret = avformat_seek_file(data->formatCtx, data->streamIndex, ts, ts, ts, AVSEEK_FLAG_ANY);
|
||||
if ( ret < 0 )
|
||||
return ret; /* we can't even reset_vgmstream the file */
|
||||
|
||||
avcodec_flush_buffers(data->codecCtx);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* ******************************************** */
|
||||
/* AVIO CALLBACKS */
|
||||
/* ******************************************** */
|
||||
|
||||
/* AVIO callback: read stream, skipping external headers if needed */
|
||||
static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) {
|
||||
ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque;
|
||||
uint64_t offset = data->offset;
|
||||
int max_to_copy = 0;
|
||||
int ret;
|
||||
|
||||
if (data->header_insert_block) {
|
||||
if (offset < data->header_size) {
|
||||
max_to_copy = (int)(data->header_size - offset);
|
||||
if (max_to_copy > buf_size) {
|
||||
max_to_copy = buf_size;
|
||||
}
|
||||
memcpy(buf, data->header_insert_block + offset, max_to_copy);
|
||||
buf += max_to_copy;
|
||||
buf_size -= max_to_copy;
|
||||
offset += max_to_copy;
|
||||
if (!buf_size) {
|
||||
data->offset = offset;
|
||||
return max_to_copy;
|
||||
}
|
||||
}
|
||||
offset -= data->header_size;
|
||||
}
|
||||
|
||||
/* when "fake" size is smaller than "real" size we need to make sure bytes_read (ret) is clamped;
|
||||
* it confuses FFmpeg in rare cases (STREAMFILE may have valid data after size) */
|
||||
if (offset + buf_size > data->size + data->header_size) {
|
||||
buf_size = data->size - offset; /* header "read" is manually inserted later */
|
||||
}
|
||||
|
||||
ret = read_streamfile(buf, offset + data->start, buf_size, data->streamfile);
|
||||
if (ret > 0) {
|
||||
offset += ret;
|
||||
if (data->header_insert_block) {
|
||||
ret += max_to_copy;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->header_insert_block) {
|
||||
offset += data->header_size;
|
||||
}
|
||||
|
||||
data->offset = offset;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* AVIO callback: write stream not needed */
|
||||
static int ffmpeg_write(void *opaque, uint8_t *buf, int buf_size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* AVIO callback: seek stream, skipping external headers if needed */
|
||||
static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
|
||||
ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque;
|
||||
int ret = 0;
|
||||
|
||||
if (whence & AVSEEK_SIZE) {
|
||||
return data->size + data->header_size;
|
||||
}
|
||||
whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE);
|
||||
/* false offsets, on reads data->start will be added */
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
offset += data->offset;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
offset += data->size;
|
||||
if (data->header_insert_block)
|
||||
offset += data->header_size;
|
||||
break;
|
||||
}
|
||||
|
||||
/* clamp offset; fseek returns 0 when offset > size, too */
|
||||
if (offset > data->size + data->header_size) {
|
||||
offset = data->size + data->header_size;
|
||||
}
|
||||
|
||||
data->offset = offset;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* ******************************************** */
|
||||
/* MAIN INIT/DECODER */
|
||||
/* ******************************************** */
|
||||
|
||||
/**
|
||||
* Manually init FFmpeg, from an offset.
|
||||
* Used if the stream has internal data recognized by FFmpeg.
|
||||
*/
|
||||
ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) {
|
||||
return init_ffmpeg_header_offset(streamFile, NULL, 0, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually init FFmpeg, from a fake header / offset.
|
||||
*
|
||||
* Takes a fake header, to trick FFmpeg into demuxing/decoding the stream.
|
||||
* This header will be seamlessly inserted before 'start' offset, and total filesize will be 'header_size' + 'size'.
|
||||
* The header buffer will be copied and memory-managed internally.
|
||||
*/
|
||||
ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) {
|
||||
char filename[PATH_LIMIT];
|
||||
ffmpeg_codec_data * data;
|
||||
int errcode, i;
|
||||
|
||||
int streamIndex, streamCount;
|
||||
AVStream *stream;
|
||||
AVCodecParameters *codecPar;
|
||||
|
||||
AVRational tb;
|
||||
|
||||
|
||||
/* basic setup */
|
||||
g_init_ffmpeg();
|
||||
|
||||
data = ( ffmpeg_codec_data * ) calloc(1, sizeof(ffmpeg_codec_data));
|
||||
if (!data) return NULL;
|
||||
|
||||
streamFile->get_name( streamFile, filename, sizeof(filename) );
|
||||
|
||||
data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!data->streamfile) goto fail;
|
||||
|
||||
data->start = start;
|
||||
data->size = size;
|
||||
|
||||
|
||||
/* insert fake header to trick FFmpeg into demuxing/decoding the stream */
|
||||
if (header_size > 0) {
|
||||
data->header_size = header_size;
|
||||
data->header_insert_block = av_memdup(header, header_size);
|
||||
if (!data->header_insert_block) goto fail;
|
||||
}
|
||||
|
||||
/* setup IO, attempt to autodetect format and gather some info */
|
||||
data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE);
|
||||
if (!data->buffer) goto fail;
|
||||
|
||||
data->ioCtx = avio_alloc_context(data->buffer, FFMPEG_DEFAULT_IO_BUFFER_SIZE, 0, data, ffmpeg_read, ffmpeg_write, ffmpeg_seek);
|
||||
if (!data->ioCtx) goto fail;
|
||||
|
||||
data->formatCtx = avformat_alloc_context();
|
||||
if (!data->formatCtx) goto fail;
|
||||
|
||||
data->formatCtx->pb = data->ioCtx;
|
||||
|
||||
if ((errcode = avformat_open_input(&data->formatCtx, "", NULL, NULL)) < 0) goto fail; /* autodetect */
|
||||
|
||||
if ((errcode = avformat_find_stream_info(data->formatCtx, NULL)) < 0) goto fail;
|
||||
|
||||
|
||||
/* find valid audio stream inside */
|
||||
streamIndex = -1;
|
||||
streamCount = 0; /* audio streams only */
|
||||
|
||||
for (i = 0; i < data->formatCtx->nb_streams; ++i) {
|
||||
stream = data->formatCtx->streams[i];
|
||||
codecPar = stream->codecpar;
|
||||
if (streamIndex < 0 && codecPar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
streamIndex = i; /* select first audio stream found */
|
||||
} else {
|
||||
stream->discard = AVDISCARD_ALL; /* disable demuxing unneded streams */
|
||||
}
|
||||
if (codecPar->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
streamCount++;
|
||||
}
|
||||
|
||||
if (streamIndex < 0) goto fail;
|
||||
|
||||
data->streamIndex = streamIndex;
|
||||
stream = data->formatCtx->streams[streamIndex];
|
||||
data->streamCount = streamCount;
|
||||
|
||||
|
||||
/* prepare codec and frame/packet buffers */
|
||||
data->codecCtx = avcodec_alloc_context3(NULL);
|
||||
if (!data->codecCtx) goto fail;
|
||||
|
||||
if ((errcode = avcodec_parameters_to_context(data->codecCtx, codecPar)) < 0) goto fail;
|
||||
|
||||
av_codec_set_pkt_timebase(data->codecCtx, stream->time_base);
|
||||
|
||||
data->codec = avcodec_find_decoder(data->codecCtx->codec_id);
|
||||
if (!data->codec) goto fail;
|
||||
|
||||
if ((errcode = avcodec_open2(data->codecCtx, data->codec, NULL)) < 0) goto fail;
|
||||
|
||||
data->lastDecodedFrame = av_frame_alloc();
|
||||
if (!data->lastDecodedFrame) goto fail;
|
||||
av_frame_unref(data->lastDecodedFrame);
|
||||
|
||||
data->lastReadPacket = malloc(sizeof(AVPacket));
|
||||
if (!data->lastReadPacket) goto fail;
|
||||
av_new_packet(data->lastReadPacket, 0);
|
||||
|
||||
data->readNextPacket = 1;
|
||||
data->bytesConsumedFromDecodedFrame = INT_MAX;
|
||||
|
||||
|
||||
/* other setup */
|
||||
data->sampleRate = data->codecCtx->sample_rate;
|
||||
data->channels = data->codecCtx->channels;
|
||||
data->floatingPoint = 0;
|
||||
|
||||
switch (data->codecCtx->sample_fmt) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
case AV_SAMPLE_FMT_U8P:
|
||||
data->bitsPerSample = 8;
|
||||
break;
|
||||
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
data->bitsPerSample = 16;
|
||||
break;
|
||||
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
data->bitsPerSample = 32;
|
||||
break;
|
||||
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
data->bitsPerSample = 32;
|
||||
data->floatingPoint = 1;
|
||||
break;
|
||||
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
data->bitsPerSample = 64;
|
||||
data->floatingPoint = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->bitrate = (int)(data->codecCtx->bit_rate);
|
||||
data->endOfStream = 0;
|
||||
data->endOfAudio = 0;
|
||||
|
||||
/* try to guess frames/samples (duration isn't always set) */
|
||||
tb.num = 1; tb.den = data->codecCtx->sample_rate;
|
||||
data->totalSamples = av_rescale_q(stream->duration, stream->time_base, tb);
|
||||
if (data->totalSamples < 0)
|
||||
data->totalSamples = 0; /* caller must consider this */
|
||||
|
||||
data->blockAlign = data->codecCtx->block_align;
|
||||
data->frameSize = data->codecCtx->frame_size;
|
||||
if(data->frameSize == 0) /* some formats don't set frame_size but can get on request, and vice versa */
|
||||
data->frameSize = av_get_audio_frame_duration(data->codecCtx,0);
|
||||
|
||||
/* setup decode buffer */
|
||||
data->sampleBufferBlock = FFMPEG_DEFAULT_BUFFER_SIZE;
|
||||
data->sampleBuffer = av_malloc( data->sampleBufferBlock * (data->bitsPerSample / 8) * data->channels );
|
||||
if (!data->sampleBuffer)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* setup decent seeking for faulty formats */
|
||||
errcode = init_seek(data);
|
||||
if (errcode < 0) goto fail;
|
||||
|
||||
/* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc)
|
||||
* get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */
|
||||
if (stream->start_skip_samples) /* samples to skip in the first packet */
|
||||
data->skipSamples = stream->start_skip_samples;
|
||||
else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */
|
||||
data->skipSamples = stream->skip_samples;
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_ffmpeg(data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* decode samples of any kind of FFmpeg format */
|
||||
void decode_ffmpeg(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
|
||||
|
||||
int bytesPerSample;
|
||||
int bytesPerFrame;
|
||||
int frameSize;
|
||||
|
||||
int bytesToRead;
|
||||
int bytesRead;
|
||||
int bytesPerSample, bytesPerFrame, frameSize;
|
||||
int bytesToRead, bytesRead;
|
||||
|
||||
uint8_t *targetBuf;
|
||||
|
||||
@ -78,10 +480,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do,
|
||||
|
||||
int bytesConsumedFromDecodedFrame;
|
||||
|
||||
int readNextPacket;
|
||||
int endOfStream;
|
||||
int endOfAudio;
|
||||
|
||||
int readNextPacket, endOfStream, endOfAudio;
|
||||
int framesReadNow;
|
||||
|
||||
|
||||
@ -114,12 +513,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do,
|
||||
|
||||
/* keep reading and decoding packets until the requested number of samples (in bytes) */
|
||||
while (bytesRead < bytesToRead) {
|
||||
int planeSize;
|
||||
int planar;
|
||||
int dataSize;
|
||||
int toConsume;
|
||||
int errcode;
|
||||
|
||||
int planeSize, planar, dataSize, toConsume, errcode;
|
||||
|
||||
/* size of previous frame */
|
||||
dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels, lastDecodedFrame->nb_samples, codecCtx->sample_fmt, 1);
|
||||
@ -231,10 +625,10 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do,
|
||||
end:
|
||||
framesReadNow = bytesRead / frameSize;
|
||||
|
||||
// Convert the audio
|
||||
/* Convert the audio */
|
||||
convert_audio(outbuf, data->sampleBuffer, framesReadNow * channels, data->bitsPerSample, data->floatingPoint);
|
||||
|
||||
// Output the state back to the structure
|
||||
/* Output the state back to the structure */
|
||||
data->bytesConsumedFromDecodedFrame = bytesConsumedFromDecodedFrame;
|
||||
data->readNextPacket = readNextPacket;
|
||||
data->endOfStream = endOfStream;
|
||||
@ -242,6 +636,10 @@ end:
|
||||
}
|
||||
|
||||
|
||||
/* ******************************************** */
|
||||
/* UTILS */
|
||||
/* ******************************************** */
|
||||
|
||||
void reset_ffmpeg(VGMSTREAM *vgmstream) {
|
||||
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
|
||||
|
||||
@ -268,7 +666,6 @@ void reset_ffmpeg(VGMSTREAM *vgmstream) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
|
||||
int64_t ts;
|
||||
@ -297,6 +694,55 @@ void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
}
|
||||
}
|
||||
|
||||
void free_ffmpeg(ffmpeg_codec_data *data) {
|
||||
if (data == NULL)
|
||||
return;
|
||||
|
||||
if (data->lastReadPacket) {
|
||||
av_packet_unref(data->lastReadPacket);
|
||||
free(data->lastReadPacket);
|
||||
data->lastReadPacket = NULL;
|
||||
}
|
||||
if (data->lastDecodedFrame) {
|
||||
av_free(data->lastDecodedFrame);
|
||||
data->lastDecodedFrame = NULL;
|
||||
}
|
||||
if (data->codecCtx) {
|
||||
avcodec_close(data->codecCtx);
|
||||
avcodec_free_context(&(data->codecCtx));
|
||||
data->codecCtx = NULL;
|
||||
}
|
||||
if (data->formatCtx) {
|
||||
avformat_close_input(&(data->formatCtx));
|
||||
data->formatCtx = NULL;
|
||||
}
|
||||
if (data->ioCtx) {
|
||||
// buffer passed in is occasionally freed and replaced.
|
||||
// the replacement must be freed as well.
|
||||
data->buffer = data->ioCtx->buffer;
|
||||
av_free(data->ioCtx);
|
||||
data->ioCtx = NULL;
|
||||
}
|
||||
if (data->buffer) {
|
||||
av_free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
}
|
||||
if (data->sampleBuffer) {
|
||||
av_free(data->sampleBuffer);
|
||||
data->sampleBuffer = NULL;
|
||||
}
|
||||
if (data->header_insert_block) {
|
||||
av_free(data->header_insert_block);
|
||||
data->header_insert_block = NULL;
|
||||
}
|
||||
if (data->streamfile) {
|
||||
close_streamfile(data->streamfile);
|
||||
data->streamfile = NULL;
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of samples to skip at the beginning of the stream, needed by some "gapless" formats.
|
||||
* (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc to "set up" the decoder).
|
||||
|
186
src/coding/mta2_decoder.c
Normal file
186
src/coding/mta2_decoder.c
Normal file
@ -0,0 +1,186 @@
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* MTA2 (EA XAS variant?) decoder based on:
|
||||
* - MGS Developer Wiki: https://www.mgsdevwiki.com/wiki/index.php/MTA2_(Codec) [codec by daemon1]
|
||||
* - Solid4 tools: https://github.com/GHzGangster/Drebin
|
||||
*
|
||||
* MTA2 layout:
|
||||
* - data is divided into N tracks of 0x10 header + 0x90 frame per track channel, forming N streams
|
||||
* ex: 8ch: track0 4ch + track1 4ch + track0 4ch + track1 4ch ...; or 2ch = 1ch track0 + 1ch track1
|
||||
* * up to 16 possible tracks, but max seen is 3 (ex. track0=sneaking, track1=action, track2=ambience)
|
||||
* - each ch frame is divided into 4 headers + 4 vertical groups with nibbles (0x4*4 + 0x20*4)
|
||||
* ex. group1 is 0x04(4) + 0x14(4) + 0x24(4) + 0x34(4) ... (vertically maybe for paralelism?)
|
||||
* - in case of "macroblock" layout, there are also headers before N tracks (like other MGS games)
|
||||
*
|
||||
* Due to this vertical layout and multiple hist/indexes, it decodes everything in a block between calls
|
||||
* but discards unwanted data, instead of trying to skip to the target nibble. Meaning no need to save hist, and
|
||||
* expects samples_to_do to be block_samples at most (could be simplified, I guess).
|
||||
*
|
||||
* Because of how the macroblock/track and stream's offset per channel work, they are supported by
|
||||
* autodetecting and skipping when needed (ideally should keep a special layout/count, but this is simpler).
|
||||
*/
|
||||
|
||||
static const int c1[8] = { /* mod table 1 */
|
||||
0, 240, 460, 392, 488, 460, 460, 240
|
||||
};
|
||||
static const int c2[8] = { /* mod table 2 */
|
||||
0, 0, -208, -220, -240, -240, -220, -104
|
||||
};
|
||||
static const int c3[32] = { /* shift table */
|
||||
256, 335, 438, 573, 749, 979, 1281, 1675,
|
||||
2190, 2864, 3746, 4898, 6406, 8377, 10955, 14327,
|
||||
18736, 24503, 32043, 41905, 54802, 71668, 93724, 122568,
|
||||
160290, 209620, 274133, 358500, 468831, 613119, 801811, 1048576
|
||||
};
|
||||
|
||||
/* expands nibble */
|
||||
static short calculate_output(int nibble, short smp1, short smp2, int mod, int sh) {
|
||||
int output;
|
||||
if (nibble > 7) /* sign extend */
|
||||
nibble = nibble - 16;
|
||||
|
||||
output = (smp1 * c1[mod] + smp2 * c2[mod] + (nibble * c3[sh]) + 128) >> 8;
|
||||
output = clamp16(output);
|
||||
return (short)output;
|
||||
}
|
||||
|
||||
|
||||
/* autodetect and skip "macroblocks" */
|
||||
static void mta2_block_update(VGMSTREAMCHANNEL * stream) {
|
||||
int block_type, block_size, block_tracks, repeat = 1;
|
||||
|
||||
/* may need to skip N empty blocks */
|
||||
do {
|
||||
block_type = read_32bitBE(stream->offset + 0x00, stream->streamfile);
|
||||
block_size = read_32bitBE(stream->offset + 0x04, stream->streamfile); /* including this header */
|
||||
/* 0x08: always null */
|
||||
block_tracks = read_32bitBE(stream->offset + 0x0c, stream->streamfile); /* total tracks of variable size (can be 0) */
|
||||
|
||||
/* 0x10001: music, 0x20001: sfx?, 0xf0: loop control (goes at the end) */
|
||||
if (block_type != 0x00010001 && block_type != 0x00020001 && block_type != 0x000000F0)
|
||||
return; /* not a block */
|
||||
|
||||
/* frame=010001+00/etc can be mistaken as block_type, do extra checks */
|
||||
{
|
||||
int i, track_channels = 0;
|
||||
uint16_t channel_layout = (block_size >> 16);
|
||||
uint16_t track_size = (block_size & 0xFFFF);
|
||||
|
||||
/* has chanel layout == may be a track */
|
||||
if (channel_layout > 0 && channel_layout <= 0xFF) {
|
||||
for (i = 0; i < 8; i++) {
|
||||
if ((channel_layout >> i) & 0x01)
|
||||
track_channels++;
|
||||
}
|
||||
if (track_channels*0x90 == track_size)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */
|
||||
VGM_LOG("MTA2: bad block @ %08lx\n", stream->offset);
|
||||
stream->offset += 0x10;
|
||||
repeat = 0;
|
||||
}
|
||||
else if (block_tracks == 0) { /* empty block (common), keep repeating */
|
||||
stream->offset += block_size;
|
||||
}
|
||||
else { /* normal block, position into next track header */
|
||||
stream->offset += 0x10;
|
||||
repeat = 0;
|
||||
}
|
||||
} while (repeat);
|
||||
}
|
||||
|
||||
/* decodes a block for a channel, skipping macroblocks/tracks if needed */
|
||||
void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0;
|
||||
int i, group, row, col;
|
||||
int num_track = 0, channel_layout, track_channels = 0, track_channel;
|
||||
|
||||
|
||||
/* block/track skip */
|
||||
do {
|
||||
/* autodetect and skip macroblock header */
|
||||
mta2_block_update(stream);
|
||||
|
||||
/* parse track header (0x10) and skip tracks that our current channel doesn't belong to */
|
||||
num_track = read_8bit(stream->offset+0x00,stream->streamfile); /* 0=first */
|
||||
/* 0x01(3): num_frame (0=first), 0x04(1): 0? */
|
||||
channel_layout = read_8bit(stream->offset+0x05,stream->streamfile); /* bitmask, see mta2.c */
|
||||
frame_size = read_16bitBE(stream->offset+0x06,stream->streamfile); /* not including this header */
|
||||
/* 0x08(8): null */
|
||||
|
||||
if (num_track < 0)
|
||||
break; /* EOF: whatever */
|
||||
|
||||
track_channels = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if ((channel_layout >> i) & 0x01)
|
||||
track_channels++;
|
||||
}
|
||||
|
||||
/* assumes tracks channels are divided evenly in all tracks (ex. not 2ch + 1ch + 1ch) */
|
||||
if (channel / track_channels == num_track)
|
||||
break; /* channel belongs to this track */
|
||||
|
||||
/* keep looping for our track */
|
||||
stream->offset += 0x10 + frame_size;
|
||||
}
|
||||
while (1);
|
||||
|
||||
track_channel = channel % track_channels;
|
||||
channel_block_samples = (0x80*2);
|
||||
channel_first_sample = first_sample % (0x80*2);
|
||||
|
||||
|
||||
/* parse channel frame (header 0x04*4 + data 0x20*4) */
|
||||
for (group = 0; group < 4; group++) {
|
||||
short smp2, smp1, mod, sh, output;
|
||||
int group_header = read_32bitBE(stream->offset + 0x10 + track_channel*0x90 + group*0x4, stream->streamfile);
|
||||
smp2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */
|
||||
smp1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */
|
||||
mod = (group_header >> 5) & 0x7; /* mid 3b */
|
||||
sh = group_header & 0x1f; /* lower 5b */
|
||||
|
||||
/* write header samples (skips the last 2 group nibbles), like Drebin's decoder
|
||||
* last 2 nibbles and next 2 header hist should match though */
|
||||
if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = smp2;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = smp1;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
|
||||
for (row = 0; row < 8; row++) {
|
||||
for (col = 0; col < 4*2; col++) {
|
||||
uint8_t nibbles = read_8bit(stream->offset + 0x10 + 0x10 + track_channel*0x90 + group*0x4 + row*0x10 + col/2, stream->streamfile);
|
||||
int nibble_shift = (!(col&1) ? 4 : 0); /* upper first */
|
||||
output = calculate_output((nibbles >> nibble_shift) & 0xf, smp1, smp2, mod, sh);
|
||||
|
||||
/* ignore last 2 nibbles (uses first 2 header samples) */
|
||||
if (row < 7 || col < 3*2) {
|
||||
if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
|
||||
outbuf[samples_done * channelspacing] = output;
|
||||
samples_done++;
|
||||
}
|
||||
sample_count++;
|
||||
}
|
||||
|
||||
smp2 = smp1;
|
||||
smp1 = output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* block fully done */
|
||||
if (channel_first_sample + samples_done == channel_block_samples) {
|
||||
stream->offset += 0x10 + frame_size;
|
||||
}
|
||||
}
|
@ -1,142 +1,152 @@
|
||||
//#include <stdlib.h>
|
||||
//#include <stdio.h>
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
#define MTAF_BLOCK_SUPPORT 0
|
||||
#define MTAF_BLOCK_SUPPORT
|
||||
|
||||
// A hybrid of IMA and Yamaha ADPCM found in Metal Gear Solid 3
|
||||
// Thanks to X_Tra (http://metalgear.in/) for pointing me to the step size table.
|
||||
|
||||
int index_table[16] = {
|
||||
/* A hybrid of IMA and Yamaha ADPCM found in Metal Gear Solid 3
|
||||
* Thanks to X_Tra (http://metalgear.in/) for pointing me to the step size table.
|
||||
*
|
||||
* Layout: N tracks of 0x10 header + 0x80*2 (always 2ch; multichannels uses 4ch = 2ch track0 + 2ch track1 xN)
|
||||
* "macroblocks" support is not really needed as the extractors should remove them but they are
|
||||
* autodetected and skipped if found (ideally should keep a special layout/count, but this is simpler).
|
||||
*/
|
||||
|
||||
static const int index_table[16] = {
|
||||
-1, -1, -1, -1, 2, 4, 6, 8,
|
||||
-1, -1, -1, -1, 2, 4, 6, 8
|
||||
};
|
||||
|
||||
static int16_t step_size[32][16] = {
|
||||
{ 1, 5, 9, 13, 16, 20, 24, 28,
|
||||
-1, -5, -9, -13, -16, -20, -24, -28, },
|
||||
{ 2, 6, 11, 15, 20, 24, 29, 33,
|
||||
-2, -6, -11, -15, -20, -24, -29, -33, },
|
||||
{ 2, 7, 13, 18, 23, 28, 34, 39,
|
||||
-2, -7, -13, -18, -23, -28, -34, -39, },
|
||||
{ 3, 9, 15, 21, 28, 34, 40, 46,
|
||||
-3, -9, -15, -21, -28, -34, -40, -46, },
|
||||
{ 3, 11, 18, 26, 33, 41, 48, 56,
|
||||
-3, -11, -18, -26, -33, -41, -48, -56, },
|
||||
{ 4, 13, 22, 31, 40, 49, 58, 67,
|
||||
-4, -13, -22, -31, -40, -49, -58, -67, },
|
||||
{ 5, 16, 26, 37, 48, 59, 69, 80,
|
||||
-5, -16, -26, -37, -48, -59, -69, -80, },
|
||||
{ 6, 19, 31, 44, 57, 70, 82, 95,
|
||||
-6, -19, -31, -44, -57, -70, -82, -95, },
|
||||
{ 7, 22, 38, 53, 68, 83, 99, 114,
|
||||
-7, -22, -38, -53, -68, -83, -99, -114, },
|
||||
{ 9, 27, 45, 63, 81, 99, 117, 135,
|
||||
-9, -27, -45, -63, -81, -99, -117, -135, },
|
||||
{ 10, 32, 53, 75, 96, 118, 139, 161,
|
||||
-10, -32, -53, -75, -96, -118, -139, -161, },
|
||||
{ 12, 38, 64, 90, 115, 141, 167, 193,
|
||||
-12, -38, -64, -90, -115, -141, -167, -193, },
|
||||
{ 15, 45, 76, 106, 137, 167, 198, 228,
|
||||
-15, -45, -76, -106, -137, -167, -198, -228, },
|
||||
{ 18, 54, 91, 127, 164, 200, 237, 273,
|
||||
-18, -54, -91, -127, -164, -200, -237, -273, },
|
||||
{ 21, 65, 108, 152, 195, 239, 282, 326,
|
||||
-21, -65, -108, -152, -195, -239, -282, -326, },
|
||||
{ 25, 77, 129, 181, 232, 284, 336, 388,
|
||||
-25, -77, -129, -181, -232, -284, -336, -388, },
|
||||
{ 30, 92, 153, 215, 276, 338, 399, 461,
|
||||
-30, -92, -153, -215, -276, -338, -399, -461, },
|
||||
{ 36, 109, 183, 256, 329, 402, 476, 549,
|
||||
-36, -109, -183, -256, -329, -402, -476, -549, },
|
||||
{ 43, 130, 218, 305, 392, 479, 567, 654,
|
||||
-43, -130, -218, -305, -392, -479, -567, -654, },
|
||||
{ 52, 156, 260, 364, 468, 572, 676, 780,
|
||||
-52, -156, -260, -364, -468, -572, -676, -780, },
|
||||
{ 62, 186, 310, 434, 558, 682, 806, 930,
|
||||
-62, -186, -310, -434, -558, -682, -806, -930, },
|
||||
{ 73, 221, 368, 516, 663, 811, 958, 1106,
|
||||
-73, -221, -368, -516, -663, -811, -958, -1106, },
|
||||
{ 87, 263, 439, 615, 790, 966, 1142, 1318,
|
||||
-87, -263, -439, -615, -790, -966, -1142, -1318, },
|
||||
{ 104, 314, 523, 733, 942, 1152, 1361, 1571,
|
||||
-104, -314, -523, -733, -942, -1152, -1361, -1571, },
|
||||
{ 124, 374, 623, 873, 1122, 1372, 1621, 1871,
|
||||
-124, -374, -623, -873, -1122, -1372, -1621, -1871, },
|
||||
{ 148, 445, 743, 1040, 1337, 1634, 1932, 2229,
|
||||
-148, -445, -743, -1040, -1337, -1634, -1932, -2229, },
|
||||
{ 177, 531, 885, 1239, 1593, 1947, 2301, 2655,
|
||||
-177, -531, -885, -1239, -1593, -1947, -2301, -2655, },
|
||||
{ 210, 632, 1053, 1475, 1896, 2318, 2739, 3161,
|
||||
-210, -632, -1053, -1475, -1896, -2318, -2739, -3161, },
|
||||
{ 251, 753, 1255, 1757, 2260, 2762, 3264, 3766,
|
||||
-251, -753, -1255, -1757, -2260, -2762, -3264, -3766, },
|
||||
{ 299, 897, 1495, 2093, 2692, 3290, 3888, 4486,
|
||||
-299, -897, -1495, -2093, -2692, -3290, -3888, -4486, },
|
||||
{ 356, 1068, 1781, 2493, 3206, 3918, 4631, 5343,
|
||||
-356, -1068, -1781, -2493, -3206, -3918, -4631, -5343, },
|
||||
{ 424, 1273, 2121, 2970, 3819, 4668, 5516, 6365,
|
||||
-424, -1273, -2121, -2970, -3819, -4668, -5516, -6365, },
|
||||
static const int16_t step_size[32][16] = {
|
||||
{ 1, 5, 9, 13, 16, 20, 24, 28,
|
||||
-1, -5, -9, -13, -16, -20, -24, -28, },
|
||||
{ 2, 6, 11, 15, 20, 24, 29, 33,
|
||||
-2, -6, -11, -15, -20, -24, -29, -33, },
|
||||
{ 2, 7, 13, 18, 23, 28, 34, 39,
|
||||
-2, -7, -13, -18, -23, -28, -34, -39, },
|
||||
{ 3, 9, 15, 21, 28, 34, 40, 46,
|
||||
-3, -9, -15, -21, -28, -34, -40, -46, },
|
||||
{ 3, 11, 18, 26, 33, 41, 48, 56,
|
||||
-3, -11, -18, -26, -33, -41, -48, -56, },
|
||||
{ 4, 13, 22, 31, 40, 49, 58, 67,
|
||||
-4, -13, -22, -31, -40, -49, -58, -67, },
|
||||
{ 5, 16, 26, 37, 48, 59, 69, 80,
|
||||
-5, -16, -26, -37, -48, -59, -69, -80, },
|
||||
{ 6, 19, 31, 44, 57, 70, 82, 95,
|
||||
-6, -19, -31, -44, -57, -70, -82, -95, },
|
||||
{ 7, 22, 38, 53, 68, 83, 99, 114,
|
||||
-7, -22, -38, -53, -68, -83, -99, -114, },
|
||||
{ 9, 27, 45, 63, 81, 99, 117, 135,
|
||||
-9, -27, -45, -63, -81, -99, -117, -135, },
|
||||
{ 10, 32, 53, 75, 96, 118, 139, 161,
|
||||
-10, -32, -53, -75, -96, -118, -139, -161, },
|
||||
{ 12, 38, 64, 90, 115, 141, 167, 193,
|
||||
-12, -38, -64, -90, -115, -141, -167, -193, },
|
||||
{ 15, 45, 76, 106, 137, 167, 198, 228,
|
||||
-15, -45, -76, -106, -137, -167, -198, -228, },
|
||||
{ 18, 54, 91, 127, 164, 200, 237, 273,
|
||||
-18, -54, -91, -127, -164, -200, -237, -273, },
|
||||
{ 21, 65, 108, 152, 195, 239, 282, 326,
|
||||
-21, -65, -108, -152, -195, -239, -282, -326, },
|
||||
{ 25, 77, 129, 181, 232, 284, 336, 388,
|
||||
-25, -77, -129, -181, -232, -284, -336, -388, },
|
||||
{ 30, 92, 153, 215, 276, 338, 399, 461,
|
||||
-30, -92, -153, -215, -276, -338, -399, -461, },
|
||||
{ 36, 109, 183, 256, 329, 402, 476, 549,
|
||||
-36, -109, -183, -256, -329, -402, -476, -549, },
|
||||
{ 43, 130, 218, 305, 392, 479, 567, 654,
|
||||
-43, -130, -218, -305, -392, -479, -567, -654, },
|
||||
{ 52, 156, 260, 364, 468, 572, 676, 780,
|
||||
-52, -156, -260, -364, -468, -572, -676, -780, },
|
||||
{ 62, 186, 310, 434, 558, 682, 806, 930,
|
||||
-62, -186, -310, -434, -558, -682, -806, -930, },
|
||||
{ 73, 221, 368, 516, 663, 811, 958, 1106,
|
||||
-73, -221, -368, -516, -663, -811, -958, -1106, },
|
||||
{ 87, 263, 439, 615, 790, 966, 1142, 1318,
|
||||
-87, -263, -439, -615, -790, -966, -1142, -1318, },
|
||||
{ 104, 314, 523, 733, 942, 1152, 1361, 1571,
|
||||
-104, -314, -523, -733, -942, -1152, -1361, -1571, },
|
||||
{ 124, 374, 623, 873, 1122, 1372, 1621, 1871,
|
||||
-124, -374, -623, -873, -1122, -1372, -1621, -1871, },
|
||||
{ 148, 445, 743, 1040, 1337, 1634, 1932, 2229,
|
||||
-148, -445, -743, -1040, -1337, -1634, -1932, -2229, },
|
||||
{ 177, 531, 885, 1239, 1593, 1947, 2301, 2655,
|
||||
-177, -531, -885, -1239, -1593, -1947, -2301, -2655, },
|
||||
{ 210, 632, 1053, 1475, 1896, 2318, 2739, 3161,
|
||||
-210, -632, -1053, -1475, -1896, -2318, -2739, -3161, },
|
||||
{ 251, 753, 1255, 1757, 2260, 2762, 3264, 3766,
|
||||
-251, -753, -1255, -1757, -2260, -2762, -3264, -3766, },
|
||||
{ 299, 897, 1495, 2093, 2692, 3290, 3888, 4486,
|
||||
-299, -897, -1495, -2093, -2692, -3290, -3888, -4486, },
|
||||
{ 356, 1068, 1781, 2493, 3206, 3918, 4631, 5343,
|
||||
-356, -1068, -1781, -2493, -3206, -3918, -4631, -5343, },
|
||||
{ 424, 1273, 2121, 2970, 3819, 4668, 5516, 6365,
|
||||
-424, -1273, -2121, -2970, -3819, -4668, -5516, -6365, },
|
||||
};
|
||||
|
||||
#ifdef MTAF_BLOCK_SUPPORT
|
||||
/* autodetect and skip "macroblocks" */
|
||||
static void mtaf_block_update(VGMSTREAMCHANNEL * stream) {
|
||||
int block_type, block_size, block_empty, block_tracks, repeat = 1;
|
||||
|
||||
do {
|
||||
block_type = read_32bitLE(stream->offset+0x00, stream->streamfile);
|
||||
block_size = read_32bitLE(stream->offset+0x04, stream->streamfile); /* including this header */
|
||||
block_empty = read_32bitLE(stream->offset+0x08, stream->streamfile); /* always 0 */
|
||||
block_tracks = read_32bitLE(stream->offset+0x0c, stream->streamfile); /* total tracks of 0x110 (can be 0)*/
|
||||
|
||||
/* 0x110001: music (type 0x11=adpcm), 0xf0: loop control (goes at the end) */
|
||||
if ((block_type != 0x00110001 && block_type != 0x000000F0) || block_empty != 0)
|
||||
return; /* not a block */
|
||||
|
||||
/* track=001100+01 could be mistaken as block_type, do extra checks */
|
||||
{
|
||||
int track = read_8bit(stream->offset+0x10, stream->streamfile);
|
||||
if (track != 0 && track != 1)
|
||||
return; /* if this is a block, next header should be from track 0/1 */
|
||||
if (block_tracks > 0 && (block_size-0x10) != block_tracks*0x110)
|
||||
return; /* wrong expected size */
|
||||
}
|
||||
|
||||
if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */
|
||||
VGM_LOG("MTAF: bad block @ %08lx\n", stream->offset);
|
||||
stream->offset += 0x10;
|
||||
repeat = 0;
|
||||
}
|
||||
else if (block_tracks == 0) { /* empty block (common), keep repeating */
|
||||
stream->offset += block_size;
|
||||
}
|
||||
else { /* normal block, position into next track header */
|
||||
stream->offset += 0x10;
|
||||
repeat = 0;
|
||||
}
|
||||
|
||||
} while(repeat);
|
||||
}
|
||||
#endif
|
||||
|
||||
void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int channels) {
|
||||
int32_t sample_count;
|
||||
off_t cur_off = stream->offset;
|
||||
int i;
|
||||
int c = channel%2; /* global channel to stream channel */
|
||||
int c = channel%2; /* global channel to track channel */
|
||||
int32_t hist = stream->adpcm_history1_16;
|
||||
int32_t step_idx = stream->adpcm_step_index;
|
||||
uint8_t byte = 0;
|
||||
|
||||
|
||||
#if MTAF_BLOCK_SUPPORT
|
||||
{
|
||||
/* "macroblock" support (layout/mtaf_block.c) was removed since the extractor now produces clean files;
|
||||
* this a hack to skip those blocks, left as a reminder (not well tested) */
|
||||
int unk, size, empty, frames, repeat = 1;
|
||||
do {
|
||||
unk = read_32bitLE(cur_off+0x00, stream->streamfile); /* always BE 0x01001100? */
|
||||
size = read_32bitLE(cur_off+0x04, stream->streamfile); /* block size */
|
||||
empty = read_32bitLE(cur_off+0x08, stream->streamfile); /* always 0? */
|
||||
frames = read_32bitLE(cur_off+0x0c, stream->streamfile); /* total frames of 0x110 */
|
||||
if (unk == 0x00110001 && empty == 0 && size > 0) {
|
||||
if (frames == 0) {
|
||||
stream->offset += size; /* full skip */
|
||||
} else if ((size-0x10) == frames*0x110) {
|
||||
stream->offset += 0x10; /* header skip */
|
||||
repeat = 0;
|
||||
}
|
||||
cur_off = stream->offset;
|
||||
}
|
||||
else {
|
||||
repeat = 0;
|
||||
}
|
||||
|
||||
} while(repeat);
|
||||
}
|
||||
#endif
|
||||
#ifdef MTAF_BLOCK_SUPPORT
|
||||
/* autodetect and skip macroblock header */
|
||||
mtaf_block_update(stream);
|
||||
#endif
|
||||
|
||||
/* read header when we hit a new track every 0x100 samples */
|
||||
first_sample = first_sample % 0x100;
|
||||
|
||||
/* read header when we hit a new frame every 0x100 samples */
|
||||
if (first_sample == 0) {
|
||||
int32_t init_idx, init_hist;
|
||||
|
||||
/* 0x10 header: owner stream, frame count, step-L, step-R, hist-L, hist-R */
|
||||
/* uint32_t stream = read_8bit(cur_off+0+c*2, stream->streamfile); */ /* 0=first */
|
||||
/* uint24_t frames = (uint24_t)read_16bitLE(cur_off+1, stream->streamfile); */ /* 1=first */
|
||||
init_idx = read_16bitLE(cur_off+4+c*2, stream->streamfile); /* step-L/R */
|
||||
init_hist = read_16bitLE(cur_off+4+4+c*4, stream->streamfile); /* hist-L/R: hist 16bit + empty 16bit */
|
||||
|
||||
VGM_ASSERT( read_16bitLE(cur_off+4+4+2+c*4, stream->streamfile) != 0,
|
||||
"init_hist not 16bit at 0x%lx, ch=%d\n", cur_off, c);
|
||||
VGM_ASSERT( init_idx < 0 || init_idx > 31,
|
||||
"init_idx out of range at 0x%lx, ch=%d\n", cur_off, c);
|
||||
VGM_ASSERT( step_idx != init_idx,
|
||||
"step_idx does not match init_idx at 0x%lx, step=%d, init=%d\n",cur_off,step_idx, init_idx);
|
||||
/* 0x10 header: track (8b, 0=first), track count (24b, 1=first), step-L, step-R, hist-L, hist-R */
|
||||
int32_t init_idx = read_16bitLE(stream->offset+4+0+c*2, stream->streamfile); /* step-L/R */
|
||||
int32_t init_hist = read_16bitLE(stream->offset+4+4+c*4, stream->streamfile); /* hist-L/R: hist 16bit + empty 16bit */
|
||||
|
||||
VGM_ASSERT(init_idx < 0 || init_idx > 31, "MTAF: bad header idx @ 0x%lx\n", stream->offset);
|
||||
/* avoid index out of range in corrupt files */
|
||||
if (init_idx < 0) {
|
||||
init_idx = 0;
|
||||
@ -149,18 +159,12 @@ void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
|
||||
}
|
||||
|
||||
|
||||
/* skip to nibble */
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t nibble;
|
||||
|
||||
if (i%2 != 1) { /* low nibble first */
|
||||
byte = read_8bit(cur_off + 0x10 + 0x80*c + i/2, stream->streamfile);
|
||||
nibble = byte & 0x0f;
|
||||
} else { /* high nibble last */
|
||||
nibble = byte >> 4;
|
||||
}
|
||||
uint8_t byte = read_8bit(stream->offset + 0x10 + 0x80*c + i/2, stream->streamfile);
|
||||
uint8_t nibble = (byte >> (!(i&1)?0:4)) & 0xf; /* lower first */
|
||||
|
||||
hist = clamp16(hist+step_size[step_idx][nibble]);
|
||||
|
||||
outbuf[sample_count] = hist;
|
||||
|
||||
step_idx += index_table[nibble];
|
||||
@ -169,10 +173,9 @@ void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
|
||||
} else if (step_idx > 31) {
|
||||
step_idx = 31;
|
||||
}
|
||||
} /* end sample loop */
|
||||
}
|
||||
|
||||
// update state
|
||||
/* update state */
|
||||
stream->adpcm_step_index = step_idx;
|
||||
stream->adpcm_history1_16 = hist;
|
||||
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ static const char* extension_list[] = {
|
||||
"bfwav",
|
||||
"bfwavnsmbu",
|
||||
"bg00",
|
||||
"bgm",
|
||||
"bgw",
|
||||
"bh2pcm",
|
||||
"bik",
|
||||
@ -82,6 +83,7 @@ static const char* extension_list[] = {
|
||||
"cps",
|
||||
"cxs",
|
||||
|
||||
"dbm",
|
||||
"dcs",
|
||||
"ddsp",
|
||||
"de2",
|
||||
@ -143,6 +145,7 @@ static const char* extension_list[] = {
|
||||
"kovs",
|
||||
"kraw",
|
||||
|
||||
"laac", //fake extension, for tri-Ace/FFmpeg
|
||||
"leg",
|
||||
"lmp4", //fake extension, for looping
|
||||
"logg", //fake extension, for looping
|
||||
@ -156,6 +159,7 @@ static const char* extension_list[] = {
|
||||
"mca",
|
||||
"mcg",
|
||||
"mds",
|
||||
"mdsp",
|
||||
"mi4",
|
||||
"mib",
|
||||
"mic",
|
||||
@ -168,6 +172,7 @@ static const char* extension_list[] = {
|
||||
"msf",
|
||||
"mss",
|
||||
"msvp",
|
||||
"mta2",
|
||||
"mtaf",
|
||||
"mus",
|
||||
"musc",
|
||||
@ -288,11 +293,9 @@ static const char* extension_list[] = {
|
||||
"vgs",
|
||||
"vgv",
|
||||
"vig",
|
||||
|
||||
"vds",
|
||||
"vdm",
|
||||
"vms",
|
||||
"vms",
|
||||
"voi",
|
||||
"vpk",
|
||||
"vs",
|
||||
@ -446,6 +449,7 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_SASSC, "Activision / EXAKT SASSC 8-bit DPCM"},
|
||||
{coding_LSF, "lsf 4-bit ADPCM"},
|
||||
{coding_MTAF, "Konami MTAF 4-bit ADPCM"},
|
||||
{coding_MTA2, "Konami MTA2 4-bit ADPCM"},
|
||||
{coding_MC3, "Paradigm MC3 3-bit ADPCM"},
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
@ -791,7 +795,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_DSP_CABELAS, "Cabelas games dsp header"},
|
||||
{meta_PS2_LPCM, "LPCM header"},
|
||||
{meta_PS2_VMS, "VMS Header"},
|
||||
{meta_PS2_XAU, "XAU Header"},
|
||||
{meta_XAU, "XPEC XAU header"},
|
||||
{meta_GH3_BAR, "Guitar Hero III Mobile .bar"},
|
||||
{meta_FFW, "Freedom Fighters BGM header"},
|
||||
{meta_DSP_DSPW, "DSPW dsp header"},
|
||||
@ -861,6 +865,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_GTD, "GTD/GHS header"},
|
||||
{meta_TA_AAC_X360, "tri-Ace AAC (X360) header"},
|
||||
{meta_TA_AAC_PS3, "tri-Ace AAC (PS3) header"},
|
||||
{meta_PS3_MTA2, "Konami MTA2 header"},
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{meta_OGG_VORBIS, "Ogg Vorbis"},
|
||||
|
@ -946,10 +946,6 @@
|
||||
RelativePath=".\meta\ps2_xa30.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps2_xau.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps3_cps.c"
|
||||
>
|
||||
@ -966,6 +962,10 @@
|
||||
RelativePath=".\meta\ps3_msf.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps3_mta2.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps3_past.c"
|
||||
>
|
||||
@ -1202,6 +1202,10 @@
|
||||
RelativePath=".\meta\x360_tra.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xau.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xbox_hlwav.c"
|
||||
>
|
||||
@ -1358,6 +1362,10 @@
|
||||
RelativePath=".\coding\mtaf_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\mta2_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\nds_procyon_decoder.c"
|
||||
>
|
||||
|
@ -129,6 +129,7 @@
|
||||
<ClCompile Include="coding\lsf_decoder.c" />
|
||||
<ClCompile Include="coding\mp4_aac_decoder.c" />
|
||||
<ClCompile Include="coding\mtaf_decoder.c" />
|
||||
<ClCompile Include="coding\mta2_decoder.c" />
|
||||
<ClCompile Include="layout\ps2_iab_blocked.c" />
|
||||
<ClCompile Include="layout\ps2_strlr_blocked.c" />
|
||||
<ClCompile Include="layout\scd_int_layout.c" />
|
||||
@ -340,9 +341,9 @@
|
||||
<ClCompile Include="meta\ps2_wb.c" />
|
||||
<ClCompile Include="meta\ps2_xa2.c" />
|
||||
<ClCompile Include="meta\ps2_xa30.c" />
|
||||
<ClCompile Include="meta\ps2_xau.c" />
|
||||
<ClCompile Include="meta\ps3_cps.c" />
|
||||
<ClCompile Include="meta\ps3_msf.c" />
|
||||
<ClCompile Include="meta\ps3_mta2.c" />
|
||||
<ClCompile Include="meta\ps3_xvag.c" />
|
||||
<ClCompile Include="meta\psx_cdxa.c" />
|
||||
<ClCompile Include="meta\psx_fag.c" />
|
||||
@ -389,6 +390,7 @@
|
||||
<ClCompile Include="meta\ws_aud.c" />
|
||||
<ClCompile Include="meta\wvs.c" />
|
||||
<ClCompile Include="meta\wwise.c" />
|
||||
<ClCompile Include="meta\xau.c" />
|
||||
<ClCompile Include="meta\xbox_hlwav.c" />
|
||||
<ClCompile Include="meta\xbox_ims.c" />
|
||||
<ClCompile Include="meta\xbox_stma.c" />
|
||||
|
@ -571,15 +571,15 @@
|
||||
<ClCompile Include="meta\ps2_xa30.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_xau.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps3_cps.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps3_msf.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps3_mta2.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps3_xvag.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -724,6 +724,9 @@
|
||||
<ClCompile Include="meta\x360_pasx.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xau.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xbox_hlwav.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1009,6 +1012,9 @@
|
||||
<ClCompile Include="coding\mtaf_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\mta2_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\tun.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "../vgmstream.h"
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
/* AKB (AAC only) - found in SQEX iOS games */
|
||||
|
@ -1,41 +1,11 @@
|
||||
#include "../vgmstream.h"
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
/* internal sizes, can be any value */
|
||||
#define FFMPEG_DEFAULT_BUFFER_SIZE 2048
|
||||
#define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024
|
||||
|
||||
static int init_seek(ffmpeg_codec_data * data);
|
||||
|
||||
|
||||
static volatile int g_ffmpeg_initialized = 0;
|
||||
|
||||
/*
|
||||
* Global FFmpeg init
|
||||
*/
|
||||
static void g_init_ffmpeg()
|
||||
{
|
||||
if (g_ffmpeg_initialized == 1)
|
||||
{
|
||||
while (g_ffmpeg_initialized < 2);
|
||||
}
|
||||
else if (g_ffmpeg_initialized == 0)
|
||||
{
|
||||
g_ffmpeg_initialized = 1;
|
||||
av_log_set_flags(AV_LOG_SKIP_REPEATED);
|
||||
av_log_set_level(AV_LOG_ERROR);
|
||||
av_register_all();
|
||||
g_ffmpeg_initialized = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generic init FFmpeg and vgmstream for any file supported by FFmpeg.
|
||||
* Always called by vgmstream when trying to identify the file type (if the player allows it).
|
||||
* Called by vgmstream when trying to identify the file type (if the player allows it).
|
||||
*/
|
||||
VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile) {
|
||||
return init_vgmstream_ffmpeg_offset( streamFile, 0, streamFile->get_size(streamFile) );
|
||||
@ -105,436 +75,4 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* AVIO callback: read stream, skipping external headers if needed
|
||||
*/
|
||||
static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size)
|
||||
{
|
||||
ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque;
|
||||
uint64_t offset = data->offset;
|
||||
int max_to_copy = 0;
|
||||
int ret;
|
||||
|
||||
if (data->header_insert_block) {
|
||||
if (offset < data->header_size) {
|
||||
max_to_copy = (int)(data->header_size - offset);
|
||||
if (max_to_copy > buf_size) {
|
||||
max_to_copy = buf_size;
|
||||
}
|
||||
memcpy(buf, data->header_insert_block + offset, max_to_copy);
|
||||
buf += max_to_copy;
|
||||
buf_size -= max_to_copy;
|
||||
offset += max_to_copy;
|
||||
if (!buf_size) {
|
||||
data->offset = offset;
|
||||
return max_to_copy;
|
||||
}
|
||||
}
|
||||
offset -= data->header_size;
|
||||
}
|
||||
|
||||
/* when "fake" size is smaller than "real" size we need to make sure bytes_read (ret) is clamped;
|
||||
* it confuses FFmpeg in rare cases (STREAMFILE may have valid data after size) */
|
||||
if (offset + buf_size > data->size + data->header_size) {
|
||||
buf_size = data->size - offset; /* header "read" is manually inserted later */
|
||||
}
|
||||
|
||||
ret = read_streamfile(buf, offset + data->start, buf_size, data->streamfile);
|
||||
if (ret > 0) {
|
||||
offset += ret;
|
||||
if (data->header_insert_block) {
|
||||
ret += max_to_copy;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->header_insert_block) {
|
||||
offset += data->header_size;
|
||||
}
|
||||
|
||||
data->offset = offset;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* AVIO callback: write stream not needed
|
||||
*/
|
||||
static int ffmpeg_write(void *opaque, uint8_t *buf, int buf_size)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* AVIO callback: seek stream, skipping external headers if needed
|
||||
*/
|
||||
static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence)
|
||||
{
|
||||
ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque;
|
||||
int ret = 0;
|
||||
|
||||
if (whence & AVSEEK_SIZE) {
|
||||
return data->size + data->header_size;
|
||||
}
|
||||
whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE);
|
||||
/* false offsets, on reads data->start will be added */
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
offset += data->offset;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
offset += data->size;
|
||||
if (data->header_insert_block)
|
||||
offset += data->header_size;
|
||||
break;
|
||||
}
|
||||
|
||||
/* clamp offset; fseek returns 0 when offset > size, too */
|
||||
if (offset > data->size + data->header_size) {
|
||||
offset = data->size + data->header_size;
|
||||
}
|
||||
|
||||
data->offset = offset;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Manually init FFmpeg, from an offset.
|
||||
* Can be used if the stream has an extra header over data recognized by FFmpeg.
|
||||
*/
|
||||
ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) {
|
||||
return init_ffmpeg_header_offset(streamFile, NULL, 0, start, size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Manually init FFmpeg, from a fake header / offset.
|
||||
*
|
||||
* Can take a fake header, to trick FFmpeg into demuxing/decoding the stream.
|
||||
* This header will be seamlessly inserted before 'start' offset, and total filesize will be 'header_size' + 'size'.
|
||||
* The header buffer will be copied and memory-managed internally.
|
||||
*/
|
||||
ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) {
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
ffmpeg_codec_data * data;
|
||||
|
||||
int errcode, i;
|
||||
|
||||
int streamIndex, streamCount;
|
||||
AVStream *stream;
|
||||
AVCodecParameters *codecPar;
|
||||
|
||||
AVRational tb;
|
||||
|
||||
|
||||
/* basic setup */
|
||||
g_init_ffmpeg();
|
||||
|
||||
data = ( ffmpeg_codec_data * ) calloc(1, sizeof(ffmpeg_codec_data));
|
||||
if (!data) return NULL;
|
||||
|
||||
streamFile->get_name( streamFile, filename, sizeof(filename) );
|
||||
|
||||
data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!data->streamfile) goto fail;
|
||||
|
||||
data->start = start;
|
||||
data->size = size;
|
||||
|
||||
|
||||
/* insert fake header to trick FFmpeg into demuxing/decoding the stream */
|
||||
if (header_size > 0) {
|
||||
data->header_size = header_size;
|
||||
data->header_insert_block = av_memdup(header, header_size);
|
||||
if (!data->header_insert_block) goto fail;
|
||||
}
|
||||
|
||||
/* setup IO, attempt to autodetect format and gather some info */
|
||||
data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE);
|
||||
if (!data->buffer) goto fail;
|
||||
|
||||
data->ioCtx = avio_alloc_context(data->buffer, FFMPEG_DEFAULT_IO_BUFFER_SIZE, 0, data, ffmpeg_read, ffmpeg_write, ffmpeg_seek);
|
||||
if (!data->ioCtx) goto fail;
|
||||
|
||||
data->formatCtx = avformat_alloc_context();
|
||||
if (!data->formatCtx) goto fail;
|
||||
|
||||
data->formatCtx->pb = data->ioCtx;
|
||||
|
||||
if ((errcode = avformat_open_input(&data->formatCtx, "", NULL, NULL)) < 0) goto fail; /* autodetect */
|
||||
|
||||
if ((errcode = avformat_find_stream_info(data->formatCtx, NULL)) < 0) goto fail;
|
||||
|
||||
|
||||
/* find valid audio stream inside */
|
||||
streamIndex = -1;
|
||||
streamCount = 0; /* audio streams only */
|
||||
|
||||
for (i = 0; i < data->formatCtx->nb_streams; ++i) {
|
||||
stream = data->formatCtx->streams[i];
|
||||
codecPar = stream->codecpar;
|
||||
if (streamIndex < 0 && codecPar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
streamIndex = i; /* select first audio stream found */
|
||||
} else {
|
||||
stream->discard = AVDISCARD_ALL; /* disable demuxing unneded streams */
|
||||
}
|
||||
if (codecPar->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
streamCount++;
|
||||
}
|
||||
|
||||
if (streamIndex < 0) goto fail;
|
||||
|
||||
data->streamIndex = streamIndex;
|
||||
stream = data->formatCtx->streams[streamIndex];
|
||||
data->streamCount = streamCount;
|
||||
|
||||
|
||||
/* prepare codec and frame/packet buffers */
|
||||
data->codecCtx = avcodec_alloc_context3(NULL);
|
||||
if (!data->codecCtx) goto fail;
|
||||
|
||||
if ((errcode = avcodec_parameters_to_context(data->codecCtx, codecPar)) < 0) goto fail;
|
||||
|
||||
av_codec_set_pkt_timebase(data->codecCtx, stream->time_base);
|
||||
|
||||
data->codec = avcodec_find_decoder(data->codecCtx->codec_id);
|
||||
if (!data->codec) goto fail;
|
||||
|
||||
if ((errcode = avcodec_open2(data->codecCtx, data->codec, NULL)) < 0) goto fail;
|
||||
|
||||
data->lastDecodedFrame = av_frame_alloc();
|
||||
if (!data->lastDecodedFrame) goto fail;
|
||||
av_frame_unref(data->lastDecodedFrame);
|
||||
|
||||
data->lastReadPacket = malloc(sizeof(AVPacket));
|
||||
if (!data->lastReadPacket) goto fail;
|
||||
av_new_packet(data->lastReadPacket, 0);
|
||||
|
||||
data->readNextPacket = 1;
|
||||
data->bytesConsumedFromDecodedFrame = INT_MAX;
|
||||
|
||||
|
||||
/* other setup */
|
||||
data->sampleRate = data->codecCtx->sample_rate;
|
||||
data->channels = data->codecCtx->channels;
|
||||
data->floatingPoint = 0;
|
||||
|
||||
switch (data->codecCtx->sample_fmt) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
case AV_SAMPLE_FMT_U8P:
|
||||
data->bitsPerSample = 8;
|
||||
break;
|
||||
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
data->bitsPerSample = 16;
|
||||
break;
|
||||
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
data->bitsPerSample = 32;
|
||||
break;
|
||||
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
data->bitsPerSample = 32;
|
||||
data->floatingPoint = 1;
|
||||
break;
|
||||
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
data->bitsPerSample = 64;
|
||||
data->floatingPoint = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->bitrate = (int)(data->codecCtx->bit_rate);
|
||||
data->endOfStream = 0;
|
||||
data->endOfAudio = 0;
|
||||
|
||||
/* try to guess frames/samples (duration isn't always set) */
|
||||
tb.num = 1; tb.den = data->codecCtx->sample_rate;
|
||||
data->totalSamples = av_rescale_q(stream->duration, stream->time_base, tb);
|
||||
if (data->totalSamples < 0)
|
||||
data->totalSamples = 0; /* caller must consider this */
|
||||
|
||||
data->blockAlign = data->codecCtx->block_align;
|
||||
data->frameSize = data->codecCtx->frame_size;
|
||||
if(data->frameSize == 0) /* some formats don't set frame_size but can get on request, and vice versa */
|
||||
data->frameSize = av_get_audio_frame_duration(data->codecCtx,0);
|
||||
|
||||
/* setup decode buffer */
|
||||
data->sampleBufferBlock = FFMPEG_DEFAULT_BUFFER_SIZE;
|
||||
data->sampleBuffer = av_malloc( data->sampleBufferBlock * (data->bitsPerSample / 8) * data->channels );
|
||||
if (!data->sampleBuffer)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* setup decent seeking for faulty formats */
|
||||
errcode = init_seek(data);
|
||||
if (errcode < 0) goto fail;
|
||||
|
||||
/* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc)
|
||||
* get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */
|
||||
if (stream->start_skip_samples) /* samples to skip in the first packet */
|
||||
data->skipSamples = stream->start_skip_samples;
|
||||
else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */
|
||||
data->skipSamples = stream->skip_samples;
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_ffmpeg(data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Special patching for FFmpeg's buggy seek code.
|
||||
*
|
||||
* To seek with avformat_seek_file/av_seek_frame, FFmpeg's demuxers can implement read_seek2 (newest API)
|
||||
* or read_seek (older API), with various search modes. If none are available it will use seek_frame_generic,
|
||||
* which manually reads frame by frame until the selected timestamp. However, the prev frame will be consumed
|
||||
* (so after seeking to 0 next av_read_frame will actually give the second frame and so on).
|
||||
*
|
||||
* Fortunately seek_frame_generic can use an index to find the correct position. This function reads the
|
||||
* 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) {
|
||||
int ret, ts_index, found_first = 0;
|
||||
int64_t ts = 0;
|
||||
int64_t pos = 0; /* offset */
|
||||
int size = 0; /* coded size */
|
||||
int distance = 0; /* always? */
|
||||
|
||||
AVStream * stream;
|
||||
AVPacket * pkt;
|
||||
|
||||
stream = data->formatCtx->streams[data->streamIndex];
|
||||
pkt = data->lastReadPacket;
|
||||
|
||||
/* read_seek shouldn't need this index, but direct access to FFmpeg's internals is no good */
|
||||
/* if (data->formatCtx->iformat->read_seek || data->formatCtx->iformat->read_seek2)
|
||||
return 0; */
|
||||
|
||||
/* some formats already have a proper index (e.g. M4A) */
|
||||
ts_index = av_index_search_timestamp(stream, ts, AVSEEK_FLAG_ANY);
|
||||
if (ts_index>=0)
|
||||
goto test_seek;
|
||||
|
||||
|
||||
/* find the first + second packets to get pos/size */
|
||||
while (1) {
|
||||
av_packet_unref(pkt);
|
||||
ret = av_read_frame(data->formatCtx, pkt);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (pkt->stream_index != data->streamIndex)
|
||||
continue; /* ignore non-selected streams */
|
||||
|
||||
if (!found_first) { /* first found */
|
||||
found_first = 1;
|
||||
pos = pkt->pos;
|
||||
ts = pkt->dts;
|
||||
continue;
|
||||
} else { /* second found */
|
||||
size = pkt->pos - pos; /* coded, pkt->size is decoded size */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_first)
|
||||
goto fail;
|
||||
|
||||
/* in rare cases there is only one packet */
|
||||
/* if (size == 0) { size = data_end - pos; } */ /* no easy way to know, ignore (most formats don's need size) */
|
||||
|
||||
/* some formats (XMA1) don't seem to have packet.dts, pretend it's 0 */
|
||||
if (ts == INT64_MIN)
|
||||
ts = 0;
|
||||
|
||||
/* Some streams start with negative DTS (observed in Ogg). For Ogg seeking to negative or 0 doesn't alter the output.
|
||||
* It does seem seeking before decoding alters a bunch of (inaudible) +-1 lower bytes though. */
|
||||
VGM_ASSERT(ts != 0, "FFMPEG: negative start_ts (%li)\n", (long)ts);
|
||||
if (ts != 0)
|
||||
ts = 0;
|
||||
|
||||
/* add index 0 */
|
||||
ret = av_add_index_entry(stream, pos, ts, size, distance, AVINDEX_KEYFRAME);
|
||||
if ( ret < 0 )
|
||||
return ret;
|
||||
|
||||
|
||||
test_seek:
|
||||
/* seek to 0 test / move back to beginning, since we just consumed packets */
|
||||
ret = avformat_seek_file(data->formatCtx, data->streamIndex, ts, ts, ts, AVSEEK_FLAG_ANY);
|
||||
if ( ret < 0 )
|
||||
return ret; /* we can't even reset_vgmstream the file */
|
||||
|
||||
avcodec_flush_buffers(data->codecCtx);
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void free_ffmpeg(ffmpeg_codec_data *data) {
|
||||
if (data == NULL)
|
||||
return;
|
||||
|
||||
if (data->lastReadPacket) {
|
||||
av_packet_unref(data->lastReadPacket);
|
||||
free(data->lastReadPacket);
|
||||
data->lastReadPacket = NULL;
|
||||
}
|
||||
if (data->lastDecodedFrame) {
|
||||
av_free(data->lastDecodedFrame);
|
||||
data->lastDecodedFrame = NULL;
|
||||
}
|
||||
if (data->codecCtx) {
|
||||
avcodec_close(data->codecCtx);
|
||||
avcodec_free_context(&(data->codecCtx));
|
||||
data->codecCtx = NULL;
|
||||
}
|
||||
if (data->formatCtx) {
|
||||
avformat_close_input(&(data->formatCtx));
|
||||
data->formatCtx = NULL;
|
||||
}
|
||||
if (data->ioCtx) {
|
||||
// buffer passed in is occasionally freed and replaced.
|
||||
// the replacement must be freed as well.
|
||||
data->buffer = data->ioCtx->buffer;
|
||||
av_free(data->ioCtx);
|
||||
data->ioCtx = NULL;
|
||||
}
|
||||
if (data->buffer) {
|
||||
av_free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
}
|
||||
if (data->sampleBuffer) {
|
||||
av_free(data->sampleBuffer);
|
||||
data->sampleBuffer = NULL;
|
||||
}
|
||||
if (data->header_insert_block) {
|
||||
av_free(data->header_insert_block);
|
||||
data->header_insert_block = NULL;
|
||||
}
|
||||
if (data->streamfile) {
|
||||
close_streamfile(data->streamfile);
|
||||
data->streamfile = NULL;
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
#endif
|
||||
|
@ -19,7 +19,7 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x47485320) /* "GHS " */
|
||||
goto fail;
|
||||
VGM_LOG("1");
|
||||
|
||||
/* header type, not formally specified */
|
||||
if (read_32bitBE(0x04,streamFile) == 1 && read_16bitBE(0x0C,streamFile) == 0x0166) { /* XMA2 */
|
||||
/* 0x08(4): seek table size */
|
||||
@ -55,8 +55,8 @@ VGM_LOG("1");
|
||||
vgmstream->meta_type = meta_GTD;
|
||||
|
||||
switch(codec) {
|
||||
case XMA2: {
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case XMA2: {
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes;
|
||||
|
||||
|
@ -121,21 +121,14 @@ VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile);
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
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);
|
||||
|
||||
void free_ffmpeg(ffmpeg_codec_data *data);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
|
||||
|
||||
VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE * streamFile);
|
||||
#endif
|
||||
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
VGMSTREAM * init_vgmstream_mp4_aac(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
|
||||
|
||||
VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile);
|
||||
@ -541,7 +534,7 @@ VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_vms(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_xau(STREAMFILE* streamFile);
|
||||
VGMSTREAM * init_vgmstream_xau(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_gh3_bar(STREAMFILE* streamFile);
|
||||
|
||||
@ -677,4 +670,6 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ta_aac_x360(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "../vgmstream.h"
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
#ifdef VGM_USE_MP4V2
|
||||
void* mp4_file_open( const char* name, MP4FileMode mode )
|
||||
|
@ -1,91 +1,62 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* AST */
|
||||
/* AST - from Koei and Marvelous games (same internal dev?) */
|
||||
VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, variant_type;
|
||||
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
int variant_type;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("ast",filename_extension(filename))) goto fail;
|
||||
/* check extension */
|
||||
if (!check_extensions(streamFile,"ast")) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x41535400) /* "AST\0" */
|
||||
goto fail;
|
||||
|
||||
/* determine variant */
|
||||
if (read_32bitBE(0x10,streamFile) == 0)
|
||||
{
|
||||
variant_type = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
variant_type = 2;
|
||||
}
|
||||
|
||||
/* determine variant (after 0x10 is garbage/code data in type 1 until 0x800, but consistent in all songs) */
|
||||
if (read_32bitBE(0x10,streamFile) == 0x00000000 || read_32bitBE(0x10,streamFile) == 0x20002000) {
|
||||
variant_type = 1; /* Koei: P.T.O. IV (0x00000000), Naval Ops: Warship Gunner (0x20002000) */
|
||||
channel_count = 2;
|
||||
}
|
||||
else {
|
||||
variant_type = 2; /* Marvelous: Katekyoo Hitman Reborn! Dream Hyper Battle!, Binchou-tan: Shiawasegoyomi */
|
||||
channel_count = read_32bitLE(0x0C,streamFile);
|
||||
}
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = 2;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
if (variant_type == 1)
|
||||
{
|
||||
start_offset = 0x800;
|
||||
channel_count = 2;
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitLE(0x04,streamFile);
|
||||
vgmstream->num_samples = (read_32bitLE(0x0C,streamFile)-start_offset)*28/16/channel_count;
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x08,streamFile);
|
||||
loop_flag = 0;
|
||||
}
|
||||
else if (variant_type == 2)
|
||||
{
|
||||
start_offset = 0x100;
|
||||
channel_count = read_32bitLE(0x0C,streamFile);
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
|
||||
vgmstream->num_samples = (read_32bitLE(0x04,streamFile)-start_offset)*28/16/channel_count;
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fill in the vital statistics */
|
||||
if (variant_type == 1) {
|
||||
start_offset = 0x800;
|
||||
vgmstream->sample_rate = read_32bitLE(0x04,streamFile);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x0C,streamFile)-start_offset,channel_count);
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x08,streamFile);
|
||||
}
|
||||
else if (variant_type == 2) {
|
||||
start_offset = 0x100;
|
||||
vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile)-start_offset,channel_count);
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile);
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->meta_type = meta_PS2_AST;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->meta_type = meta_PS2_AST;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset+
|
||||
vgmstream->interleave_block_size*i;
|
||||
|
||||
}
|
||||
}
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,182 +1,94 @@
|
||||
//#include <stdlib.h>
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* MTAF (Metal Gear Solid 3: Snake Eater) */
|
||||
|
||||
/* MTAF - found in Metal Gear Solid 3: Snake Eater (Subsistence and HD too) */
|
||||
VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
int32_t loop_start, loop_end;
|
||||
|
||||
int stream_count;
|
||||
int loop_flag = 1;
|
||||
int channel_count;
|
||||
int32_t loop_start;
|
||||
int32_t loop_end;
|
||||
|
||||
int i;
|
||||
/* check extension */
|
||||
if ( !check_extensions(streamFile,"mtaf"))
|
||||
goto fail;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("mtaf",filename_extension(filename))) goto fail;
|
||||
/* base header */
|
||||
if (read_32bitBE(0x00, streamFile) != 0x4d544146) /* "MTAF" */
|
||||
goto fail;
|
||||
/* 0x04(4): pseudo file size (close but smaller) */
|
||||
/* 0x08(4): version? (0), 0x0c(20): null, 0x30(32): some kind of id or config? */
|
||||
|
||||
/* check header */
|
||||
|
||||
// master MTAF header (mostly useless)
|
||||
/* HEAD chunk */
|
||||
if (read_32bitBE(0x40, streamFile) != 0x48454144) /* "HEAD" */
|
||||
goto fail;
|
||||
if (read_32bitLE(0x44, streamFile) != 0xB0) /* HEAD size */
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0, streamFile) != 0x4d544146) // "MTAF"
|
||||
{
|
||||
//fprintf(stderr, "no MTAF header at 0x%08lx\n", cur_off);
|
||||
|
||||
/* 0x48(4): null, 0x4c: usually channel count (sometimes 0x10 with 2ch), 0x50(4): 0x7F (vol?), 0x54(2): 0x40 (pan?) */
|
||||
channel_count = 2 * read_8bit(0x61, streamFile); /* 0x60(4): full block size (0x110 * channels), but this works */
|
||||
/* 0x70(4): ? (00/05/07), 0x80 .. 0xf8: null */
|
||||
|
||||
loop_start = read_32bitLE(0x58, streamFile);
|
||||
loop_end = read_32bitLE(0x5c, streamFile);
|
||||
loop_flag = (loop_start != loop_end);
|
||||
|
||||
/* check loop points vs frame counts */
|
||||
if (loop_start/0x100 != read_32bitLE(0x64, streamFile) ||
|
||||
loop_end /0x100 != read_32bitLE(0x68, streamFile) ) {
|
||||
VGM_LOG("MTAF: wrong loop points\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//const uint32_t pseudo_size = readint32(&mtaf_header_buf[4]);
|
||||
/* TRKP chunks (x16) */
|
||||
/* just seem to contain pan/vol stuff (0x7f/0x40), one TRKP with data per channel and the rest fixed values */
|
||||
|
||||
// check the rest is clear
|
||||
for (i = 0x8; i < 0x20; i++)
|
||||
{
|
||||
if (read_8bit(i, streamFile) != 0)
|
||||
{
|
||||
//fprintf(stderr, "unexpected nonzero in MTAF header at 0x%08lx\n", cur_off+i);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
// ignore the rest for now
|
||||
|
||||
// HEAD chunk header
|
||||
|
||||
if (read_32bitBE(0x40, streamFile) != 0x48454144) // "HEAD"
|
||||
{
|
||||
//fprintf(stderr, "no HEAD chunk at 0x%08lx\n", cur_off);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t mtaf_head_chunk_size = read_32bitLE(0x44, streamFile);
|
||||
if (mtaf_head_chunk_size != 0xB0)
|
||||
{
|
||||
//fprintf(stderr, "unexpected size for MTAF header at 0x%08lx\n", cur_off);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
stream_count = read_8bit(0x61, streamFile);
|
||||
|
||||
// check some standard stuff
|
||||
if ( 0 != read_32bitLE(0x48, streamFile) ||
|
||||
0x7F != read_32bitLE(0x50, streamFile) ||
|
||||
0x40 != read_32bitLE(0x54, streamFile) ||
|
||||
0 != read_16bitLE(0x62, streamFile) ||
|
||||
0 != read_32bitLE(0x6c, streamFile)) // ||
|
||||
//5 != readint32(&mtaf_header_buf[0x68])) ||
|
||||
//(dc.streams==3 ? 12:0) != readint32(&mtaf_header_buf[0x7c]))
|
||||
{
|
||||
//fprintf(stderr, "unexpected header values at 0x%08lx\n", cur_off);
|
||||
/* DATA chunk */
|
||||
if (read_32bitBE(0x7f8, streamFile) != 0x44415441) /* "DATA" */
|
||||
goto fail;
|
||||
}
|
||||
/* 0x7fc: data size (without blocks in case of blocked layout) */
|
||||
|
||||
// 0 streams should be impossible
|
||||
if (stream_count == 0)
|
||||
{
|
||||
//fprintf(stderr, "0 streams at 0x%08lx\n", cur_off);
|
||||
goto fail;
|
||||
}
|
||||
/* without blocks it should start with 0x00000100 ("frame 1 from track 0") */
|
||||
//is_blocked = read_32bitLE(0x800,streamFile) != 0x00000100 && read_32bitLE(0x810,streamFile) == 0x00000100;
|
||||
|
||||
// check the other stream count indicator
|
||||
if (stream_count*0x10 != read_8bit(0x60, streamFile))
|
||||
{
|
||||
//fprintf(stderr, "secondary stream count mismatch at 0x%08lx\n", cur_off);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// maybe this is how to compute channels per stream?
|
||||
// check total channel count
|
||||
if (2*stream_count != read_32bitLE(0x4c, streamFile))
|
||||
{
|
||||
//fprintf(stderr, "total channel count does not match stream count at 0x%08lx\n", cur_off);
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
// check loop points as frame counts
|
||||
if (read_32bitLE(0x64, streamFile) != read_32bitLE(0x58, streamFile)/0x100 ||
|
||||
read_32bitLE(0x68, streamFile) != read_32bitLE(0x5c, streamFile)/0x100)
|
||||
{
|
||||
//fprintf(stderr, "loop frame count mismatch at 0x%lx\n", cur_off);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// check that rest is clear
|
||||
for (i = 0x78; i < 0xf8; i++)
|
||||
{
|
||||
if (read_8bit(i, streamFile) != 0)
|
||||
{
|
||||
//fprintf(stderr, "unexpected nonzero in HEAD chunk at 0x%lx\n", cur_off+i);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
// check TRKP chunks
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
if (read_32bitBE(0xf8+0x70*i, streamFile) != 0x54524b50 || // "TRKP"
|
||||
read_32bitLE(0xf8+0x70*i+4, streamFile) != 0x68)
|
||||
{
|
||||
//fprintf(stderr, "missing or unusual TRKP chunk #%d at 0x%lx\n", i, cur_off);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
// check for grand finale, DATA
|
||||
if (read_32bitBE(0x7f8, streamFile) != 0x44415441) // "DATA"
|
||||
{
|
||||
//fprintf(stderr, "missing DATA header at 0x%lx\n", cur_off);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
start_offset = 0x800;
|
||||
|
||||
// seems to always be the case
|
||||
channel_count = 2 * stream_count;
|
||||
|
||||
loop_start = read_32bitLE(0x58, streamFile);
|
||||
loop_end = read_32bitLE(0x5c, streamFile);
|
||||
if (loop_start == loop_end) loop_flag = 0;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
// a guess
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = 48000;
|
||||
vgmstream->coding_type = coding_MTAF;
|
||||
vgmstream->num_samples = read_32bitLE(0x5c, streamFile);
|
||||
|
||||
vgmstream->sample_rate = 48000; /* always */
|
||||
vgmstream->num_samples = loop_end;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->interleave_block_size = 0x110/2;
|
||||
|
||||
vgmstream->coding_type = coding_MTAF;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x110/2; /* kinda hacky for MTAF track layout */
|
||||
vgmstream->meta_type = meta_PS2_MTAF;
|
||||
|
||||
//const uint32_t pseudo_data_size = readint32(&mtaf_header_buf[4]);
|
||||
|
||||
// TODO: first block
|
||||
/* open the file for reading, in a specific way */
|
||||
{
|
||||
int i;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
/* open the file for reading */
|
||||
for (i = 0; i < channel_count; i++) {
|
||||
STREAMFILE * file = streamFile->open(streamFile,filename,vgmstream->interleave_block_size);
|
||||
if (!file) goto fail;
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset = start_offset + vgmstream->interleave_block_size*2*(i/2);
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
for (i = 0; i < channel_count; i++) {
|
||||
STREAMFILE * file = streamFile->open(streamFile,filename,vgmstream->interleave_block_size);
|
||||
if (!file) goto fail;
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset = start_offset + vgmstream->interleave_block_size*2*(i/2);
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -23,6 +23,10 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
/* don't hijack Sonic & Sega All Stars Racing X360 (xma) */
|
||||
if (read_32bitBE(0x00,streamFile) == 0x52494646) /* "RIFF"*/
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = 2;
|
||||
|
||||
|
@ -1,67 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* XAU (Spectral Force Chronicle [SLPM-65967]) */
|
||||
VGMSTREAM * init_vgmstream_ps2_xau(STREAMFILE *streamFile)
|
||||
{
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("xau",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x58415500)
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = read_8bit(0x18,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = 0x800;
|
||||
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitBE(0x50, streamFile);
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->num_samples = ((read_32bitBE(0x4C, streamFile) * channel_count)/ 16 / channel_count * 28);
|
||||
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x8000;
|
||||
vgmstream->meta_type = meta_PS2_XAU;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
|
||||
file = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!file) goto fail;
|
||||
|
||||
for (i=0;i<channel_count;i++)
|
||||
{
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset + vgmstream->interleave_block_size * i;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -34,7 +34,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
|
||||
|
||||
/* byte flags, not in MSFv1 or v2
|
||||
* 0x01/02/04/08: loop marker 0/1/2/3
|
||||
* 0x10: "resample" loop option (may be active with no 0x01 flag set)
|
||||
* 0x10: resample options (force 44/48khz)
|
||||
* 0x20: VBR MP3
|
||||
* 0x40: joint stereo MP3 (apparently interleaved stereo for other formats)
|
||||
* 0x80+: (none/reserved) */
|
||||
@ -104,17 +104,19 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
|
||||
case 0x6: { /* ATRAC3 high (132 kbps, frame size 192) */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
uint8_t buf[100];
|
||||
int32_t bytes, block_size, encoder_delay, joint_stereo, max_samples;
|
||||
int32_t bytes, block_size, encoder_delay, joint_stereo;
|
||||
|
||||
block_size = (codec_id==4 ? 0x60 : (codec_id==5 ? 0x98 : 0xC0)) * vgmstream->channels;
|
||||
encoder_delay = 0x0; //todo MSF encoder delay (around 440-450*2)
|
||||
max_samples = atrac3_bytes_to_samples(data_size, block_size);
|
||||
joint_stereo = codec_id==4; /* interleaved joint stereo (ch must be even) */
|
||||
joint_stereo = (codec_id==4); /* interleaved joint stereo (ch must be even) */
|
||||
|
||||
/* MSF skip samples: from tests with MSEnc and real files (ex. TTT2 eddy.msf v43, v01 demos) seems like 1162 is consistent.
|
||||
* Atelier Rorona bt_normal01 needs it to properly skip the beginning garbage but usually doesn't matter.
|
||||
* (note that encoder may add a fade-in with looping/resampling enabled but should be skipped) */
|
||||
encoder_delay = 1162;
|
||||
vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_size) - encoder_delay;
|
||||
if (vgmstream->sample_rate==0xFFFFFFFF) /* some MSFv1 (Digi World SP) */
|
||||
vgmstream->sample_rate = 44100;//voice tracks seems to use 44khz, not sure about other tracks
|
||||
|
||||
/* make a fake riff so FFmpeg can parse the ATRAC3 */
|
||||
bytes = ffmpeg_make_riff_atrac3(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
@ -124,10 +126,16 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = max_samples;
|
||||
|
||||
/* manually set skip_samples if FFmpeg didn't do it */
|
||||
if (ffmpeg_data->skipSamples <= 0) {
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, encoder_delay);
|
||||
}
|
||||
|
||||
/* MSF loop/sample values are offsets so trickier to adjust the skip_samples but this seems correct */
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_size);
|
||||
vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_size);
|
||||
vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_size) /* - encoder_delay*/;
|
||||
vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_size) - encoder_delay;
|
||||
}
|
||||
|
||||
break;
|
||||
|
104
src/meta/ps3_mta2.c
Normal file
104
src/meta/ps3_mta2.c
Normal file
@ -0,0 +1,104 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
|
||||
/* MTA2 - found in Metal Gear Solid 4 */
|
||||
VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t header_offset, start_offset;
|
||||
int loop_flag, channel_count, sample_rate; //block_offset;
|
||||
int32_t loop_start, loop_end;
|
||||
|
||||
|
||||
/* check extension */
|
||||
/* .mta2: normal file, .bgm: mta2 with block layout, .dbm: iPod metadata + block layout mta2 */
|
||||
if ( !check_extensions(streamFile,"mta2,bgm,dbm"))
|
||||
goto fail;
|
||||
|
||||
/* base header (everything is very similar to MGS3's MTAF but BE) */
|
||||
if (read_32bitBE(0x00,streamFile) == 0x4d544132) { /* "MTA2" (.mta) */
|
||||
//block_offset = 0;
|
||||
header_offset = 0x00;
|
||||
} else if (read_32bitBE(0x20,streamFile) == 0x4d544132) { /* "MTA2" @ 0x20 (.bgm) */
|
||||
//block_offset = 0x10;
|
||||
header_offset = 0x20;
|
||||
} else if (read_32bitBE(0x00, streamFile) == 0x444C424D
|
||||
&& read_32bitBE(0x820,streamFile) == 0x4d544132) { /* "DLBM" + "MTA2" @ 0x820 (.dbm) */
|
||||
//block_offset = 0x810;
|
||||
header_offset = 0x820;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
/* 0x04(4): file size -4-4 (not including block headers in case of block layout) */
|
||||
/* 0x08(4): version? (1), 0x0c(52): null */
|
||||
|
||||
|
||||
/* HEAD chunk */
|
||||
if (read_32bitBE(header_offset+0x40, streamFile) != 0x48454144) /* "HEAD" */
|
||||
goto fail;
|
||||
if (read_32bitBE(header_offset+0x44, streamFile) != 0xB0) /* HEAD size */
|
||||
goto fail;
|
||||
|
||||
|
||||
|
||||
/* 0x48(4): null, 0x4c: ? (0x10), 0x50(4): 0x7F (vol?), 0x54(2): 0x40 (pan?) */
|
||||
channel_count = read_16bitBE(header_offset+0x56, streamFile); /* counting all tracks */
|
||||
/* 0x60(4): full block size (0x110 * channels), indirectly channels_per_track = channels / (block_size / 0x110) */
|
||||
/* 0x80 .. 0xf8: null */
|
||||
|
||||
loop_start = read_32bitBE(header_offset+0x58, streamFile);
|
||||
loop_end = read_32bitBE(header_offset+0x5c, streamFile);
|
||||
loop_flag = (loop_start != loop_end); /* also flag possibly @ 0x73 */
|
||||
#if 0
|
||||
/* those values look like some kind of loop offsets */
|
||||
if (loop_start/0x100 != read_32bitBE(header_offset+0x68, streamFile) ||
|
||||
loop_end /0x100 != read_32bitBE(header_offset+0x6C, streamFile) ) {
|
||||
VGM_LOG("MTA2: wrong loop points\n");
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
sample_rate = read_32bitBE(header_offset+0x7c, streamFile);
|
||||
if (sample_rate) { /* sample rate in 32b float (WHY?) typically 48000.0 */
|
||||
float sample_float;
|
||||
memcpy(&sample_float, &sample_rate, 4);
|
||||
sample_rate = (int)sample_float;
|
||||
} else { /* default when not specified (most of the time) */
|
||||
sample_rate = 48000;
|
||||
}
|
||||
|
||||
|
||||
/* TRKP chunks (x16) */
|
||||
/* just seem to contain pan/vol stuff (0x7f/0x40), TRKP per track (sometimes +1 main track?) */
|
||||
/* there is channel layout bitmask @ 0x0f (ex. 1ch = 0x04, 3ch = 0x07, 4ch = 0x33, 6ch = 0x3f), surely:
|
||||
* FRONT_L = 0x01, FRONT_R = 0x02, FRONT_M = 0x04, BACK_L = 0x08, BACK_R = 0x10, BACK_M = 0x20 */
|
||||
|
||||
/* DATA chunk */
|
||||
if (read_32bitBE(header_offset+0x7f8, streamFile) != 0x44415441) // "DATA"
|
||||
goto fail;
|
||||
/* 0x7fc: data size (without blocks in case of blocked layout) */
|
||||
|
||||
start_offset = header_offset + 0x800;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = loop_end;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->coding_type = coding_MTA2;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_PS3_MTA2;
|
||||
|
||||
/* open the file for reading */
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -174,7 +174,6 @@ int read_fmt(int big_endian,
|
||||
case 0xFFFE: /* WAVEFORMATEXTENSIBLE / ATRAC3plus */
|
||||
#endif /* defined */
|
||||
fmt->coding_type = coding_FFmpeg;
|
||||
fmt->block_size = 2048;
|
||||
fmt->interleave = 0;
|
||||
break;
|
||||
#endif /* VGM_USE_FFMPEG */
|
||||
@ -408,10 +407,20 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
|
||||
sample_count = ffmpeg_data->totalSamples; /* fact_sample_count */
|
||||
/* the encoder introduces some garbage (usually silent) samples to skip before the stream
|
||||
* loop values include the skip samples but fact_sample_count doesn't; add them back to fix some edge loops */
|
||||
if (fact_sample_skip > 0)
|
||||
sample_count += fact_sample_skip;
|
||||
|
||||
if (at3) {
|
||||
/* the encoder introduces some garbage (not always silent) samples to skip before the stream */
|
||||
/* manually set skip_samples if FFmpeg didn't do it */
|
||||
if (ffmpeg_data->skipSamples <= 0) {
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, fact_sample_skip);
|
||||
}
|
||||
|
||||
/* RIFF loop/sample values are absolute (with skip samples), adjust */
|
||||
if (loop_flag) {
|
||||
loop_start_offset -= ffmpeg_data->skipSamples;
|
||||
loop_end_offset -= ffmpeg_data->skipSamples;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
135
src/meta/sgxd.c
135
src/meta/sgxd.c
@ -1,49 +1,28 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* utils to fix AT3 looping */
|
||||
typedef struct {
|
||||
int32_t fact_samples;
|
||||
int32_t loop_start_sample;
|
||||
int32_t loop_end_sample;
|
||||
int32_t skip_samples;
|
||||
} at3_riff_info;
|
||||
static int get_at3_riff_info(at3_riff_info* info, STREAMFILE *streamFile, int32_t offset);
|
||||
|
||||
|
||||
/* Sony's SGB+SGH / SGD / SGX (variations of the same format)
|
||||
* PS3: Genji (SGX only), Folklore, Afrika, Tokyo Jungle
|
||||
* PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2
|
||||
*
|
||||
* Contains header + chunks, usually:
|
||||
* WAVE: stream(s) header of ADPCM, AC3, ATRAC3plus, etc
|
||||
* NAME: stream name(s)
|
||||
* WSUR, WMRK, BUSS: unknown
|
||||
* RGND, SEQD: unknown (related to SE)
|
||||
* Then data, containing the original header if applicable (ex. AT3 RIFF).
|
||||
* The SGXD header has priority over it (ex. some ATRAC3plus files have 48000 while the data RIFF 44100)
|
||||
*/
|
||||
/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX), found in:
|
||||
* PS3: Genji, Folklore, Afrika (Short VAG), Tokyo Jungle
|
||||
* PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2, Pathwork Heroes */
|
||||
VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamHeader = NULL;
|
||||
|
||||
off_t start_offset, data_offset, chunk_offset;
|
||||
size_t data_size;
|
||||
|
||||
int is_sgx, is_sgb;
|
||||
int loop_flag, channels, type;
|
||||
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
|
||||
|
||||
int target_stream = 0, total_streams;
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
/* .sgx: header+data (Genji), .sgd: header+data, .sgh/sgd: header/data */
|
||||
if (!check_extensions(streamFile,"sgx,sgd,sgb"))
|
||||
goto fail;
|
||||
is_sgx = check_extensions(streamFile,"sgx");
|
||||
is_sgb = check_extensions(streamFile,"sgb");
|
||||
//is_sgd = check_extensions(streamFile,"sgd");
|
||||
|
||||
/* SGB+SGH: use SGH as header; otherwise use the current file as header */
|
||||
if (is_sgb) {
|
||||
@ -69,14 +48,15 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
|
||||
/* typical chunks: WAVE, NAME (strings), RGND, SEQD (related to SFX), WSUR, WMKR, BUSS */
|
||||
/* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */
|
||||
/* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */
|
||||
if (is_sgx) { /* position after chunk+size */
|
||||
if (read_32bitBE(0x10,streamHeader) != 0x57415645) goto fail; /* "WAVE" */
|
||||
chunk_offset = 0x18;
|
||||
} else {
|
||||
if (!find_chunk_le(streamHeader, 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_streams = read_32bitLE(chunk_offset+0x04,streamHeader);
|
||||
@ -107,7 +87,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
data_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */
|
||||
|
||||
if (is_sgx) {
|
||||
stream_offset = 0x0; /* TODO unknown (not seen multi SGX) */
|
||||
stream_offset = 0x0;
|
||||
} else{
|
||||
stream_offset = read_32bitLE(chunk_offset+0x30,streamHeader);
|
||||
}
|
||||
@ -128,41 +108,53 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
vgmstream->num_streams = total_streams;
|
||||
vgmstream->meta_type = meta_SGXD;
|
||||
|
||||
/* needs -1 to match RIFF AT3's loop chunk
|
||||
* (maybe SGXD = "loop before this sample" rather than "loop after this sample" as expected by vgmstream) */
|
||||
if (vgmstream->loop_end_sample > 0)
|
||||
vgmstream->loop_end_sample -= 1;
|
||||
|
||||
switch (type) {
|
||||
case 0x03: /* PSX ADPCM */
|
||||
case 0x03: /* PS-ADPCM */
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
if (is_sgx || is_sgb) {
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
} else { //todo this only seems to happen with SFX
|
||||
} else { /* this only seems to happen with SFX */
|
||||
vgmstream->interleave_block_size = data_size;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x04: { /* ATRAC3plus */
|
||||
at3_riff_info info;
|
||||
ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
|
||||
case 0x04: { /* ATRAC3plus */
|
||||
ffmpeg_codec_data *ffmpeg_data;
|
||||
|
||||
/* internally has a RIFF header; but the SGXD header / sample rate has priority over it (may not match) */
|
||||
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;
|
||||
|
||||
/* manually fix looping due to FFmpeg bugs */
|
||||
if (loop_flag && get_at3_riff_info(&info, streamFile, start_offset)) {
|
||||
if (vgmstream->num_samples == info.fact_samples) { /* use if looks normal */
|
||||
/* todo use "skip samples"; for now we just use absolute loop values */
|
||||
vgmstream->loop_start_sample = info.loop_start_sample;
|
||||
vgmstream->loop_end_sample = info.loop_end_sample;
|
||||
vgmstream->num_samples += info.skip_samples; /* to ensure it always reaches loop_end */
|
||||
/* manually read skip_samples if FFmpeg didn't do it */
|
||||
if (ffmpeg_data->skipSamples <= 0) {
|
||||
off_t chunk_offset;
|
||||
size_t chunk_size, fact_skip_samples = 0;
|
||||
if (!find_chunk_le(streamFile, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) /* find "fact" */
|
||||
goto fail;
|
||||
if (chunk_size == 0x8) {
|
||||
fact_skip_samples = read_32bitLE(chunk_offset+0x4, streamFile);
|
||||
} else if (chunk_size == 0xc) {
|
||||
fact_skip_samples = read_32bitLE(chunk_offset+0x8, streamFile);
|
||||
}
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, fact_skip_samples);
|
||||
}
|
||||
/* SGXD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case 0x05: /* Short VAG ADPCM */
|
||||
case 0x05: /* Short PS-ADPCM */
|
||||
vgmstream->coding_type = coding_PSX_cfg;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x4;
|
||||
@ -170,13 +162,23 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x06: { /* AC3 */
|
||||
ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
|
||||
case 0x06: { /* AC3 */
|
||||
ffmpeg_codec_data *ffmpeg_data;
|
||||
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
/* SGXD loop/sample values are relative (without skip samples), no need to adjust */
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@ -197,50 +199,3 @@ fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* AT3 RIFF headers have a "skip samples at the beginning" value that the decoder should use,
|
||||
* and absolute loop values. However the SGXD header loop values assume those samples are skipped.
|
||||
*
|
||||
* FFmpeg doesn't support/export this, so we have to manually get the absolute values to fix looping.
|
||||
*/
|
||||
static int get_at3_riff_info(at3_riff_info* info, STREAMFILE *streamFile, int32_t offset) {
|
||||
off_t chunk_offset;
|
||||
size_t chunk_size;
|
||||
|
||||
memset(info, 0, sizeof(at3_riff_info));
|
||||
|
||||
if (read_32bitBE(offset+0x0,streamFile)!=0x52494646 /* "RIFF" */
|
||||
&& read_32bitBE(offset+0x8,streamFile)!=0x57415645 ) /* "WAVE" */
|
||||
goto fail;
|
||||
|
||||
/*"smpl"*/
|
||||
if (!find_chunk_le(streamFile, 0x736D706C,offset+0xc,0, &chunk_offset,&chunk_size)) goto fail;
|
||||
if (read_32bitLE(chunk_offset+0x1C, streamFile)==0
|
||||
|| read_32bitLE(chunk_offset+0x24+0x4, streamFile)!=0 )
|
||||
goto fail;
|
||||
info->loop_start_sample = read_32bitLE(chunk_offset+0x1C+0x8+0x8, streamFile);
|
||||
info->loop_end_sample = read_32bitLE(chunk_offset+0x1C+0x8+0xc,streamFile);
|
||||
|
||||
/*"fact"*/
|
||||
if (!find_chunk_le(streamFile, 0x66616374,offset+0xc,0, &chunk_offset,&chunk_size)) goto fail;
|
||||
if (chunk_size == 0x8) {
|
||||
info->fact_samples = read_32bitLE(chunk_offset+0x0, streamFile);
|
||||
info->skip_samples = read_32bitLE(chunk_offset+0x4, streamFile);
|
||||
} else if (chunk_size == 0xc) {
|
||||
info->fact_samples = read_32bitLE(chunk_offset+0x0, streamFile);
|
||||
info->skip_samples = read_32bitLE(chunk_offset+0x8, streamFile);
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* found */
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
/* not found */
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* Square-Enix SCD (FF XIII, XIV) */
|
||||
|
||||
@ -358,7 +357,6 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
int32_t bytes;
|
||||
|
||||
/* post_meta_offset+0x00: fmt0x166 header (BE), post_meta_offset+0x34: seek table */
|
||||
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, post_meta_offset,0x34, stream_size, streamFile, 1);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
@ -378,30 +376,33 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
/* ATRAC3plus */ /* Lord of Arcana (PSP) */
|
||||
{
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
off_t chunk_offset;
|
||||
size_t chunk_size, fact_sample_skip = 0;
|
||||
|
||||
/* full riff header at start_offset/post_meta_offset (same) */
|
||||
/* full RIFF header at start_offset/post_meta_offset (same) */
|
||||
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size);
|
||||
if (!ffmpeg_data) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = ffmpeg_data->totalSamples;
|
||||
vgmstream->num_samples = ffmpeg_data->totalSamples; /* fact samples */
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
/* manually find encoder_delay to adjust samples since it's not properly used by FFmpeg */
|
||||
if (!find_chunk_le(streamFile, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) goto fail; /*"fact"*/
|
||||
if (chunk_size == 0x8) {
|
||||
fact_sample_skip = read_32bitLE(chunk_offset+0x4, streamFile);
|
||||
} else if (chunk_size == 0xc) {
|
||||
fact_sample_skip = read_32bitLE(chunk_offset+0x8, streamFile);
|
||||
/* manually read skip_samples if FFmpeg didn't do it */
|
||||
if (ffmpeg_data->skipSamples <= 0) {
|
||||
off_t chunk_offset;
|
||||
size_t chunk_size, fact_skip_samples = 0;
|
||||
if (!find_chunk_le(streamFile, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) /* find "fact" */
|
||||
goto fail;
|
||||
if (chunk_size == 0x8) {
|
||||
fact_skip_samples = read_32bitLE(chunk_offset+0x4, streamFile);
|
||||
} else if (chunk_size == 0xc) {
|
||||
fact_skip_samples = read_32bitLE(chunk_offset+0x8, streamFile);
|
||||
}
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, fact_skip_samples);
|
||||
}
|
||||
vgmstream->num_samples += fact_sample_skip;
|
||||
vgmstream->loop_start_sample += fact_sample_skip;
|
||||
vgmstream->loop_end_sample += fact_sample_skip;
|
||||
/* SCD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
80
src/meta/xau.c
Normal file
80
src/meta/xau.c
Normal file
@ -0,0 +1,80 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XAU - XPEC Entertainment sound format (Beat Down PS2/Xbox, Spectral Force Chronicle [SLPM-65967]) */
|
||||
VGMSTREAM * init_vgmstream_xau(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, type, loop_start, loop_end;
|
||||
|
||||
|
||||
/* check extension */
|
||||
if (!check_extensions(streamFile, "xau"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x58415500) /* "XAU\0" "*/
|
||||
goto fail;
|
||||
if (read_32bitLE(0x08,streamFile) != 0x40) /* header start */
|
||||
goto fail;
|
||||
|
||||
/* 0x04: version? (0x100) */
|
||||
type = read_32bitBE(0x0c, streamFile);
|
||||
loop_start = read_32bitLE(0x10, streamFile);
|
||||
loop_end = read_32bitLE(0x14, streamFile);
|
||||
loop_flag = (loop_end > 0);
|
||||
|
||||
channel_count = read_8bit(0x18,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->meta_type = meta_XAU;
|
||||
|
||||
/* miniheader over a common header with some tweaks, so we'll simplify parsing */
|
||||
switch(type) {
|
||||
case 0x50533200: /* "PS2\0" */
|
||||
if (read_32bitBE(0x40,streamFile) != 0x56414770) goto fail; /* "VAGp" */
|
||||
|
||||
start_offset = 0x800;
|
||||
vgmstream->sample_rate = read_32bitBE(0x50, streamFile);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(read_32bitBE(0x4C,streamFile) * channel_count, channel_count);
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x8000;
|
||||
|
||||
break;
|
||||
case 0x58420000: /* "XB\0\0" */
|
||||
if (read_32bitBE(0x40,streamFile) != 0x52494646) goto fail; /* "RIFF" */
|
||||
|
||||
start_offset = 0x70;
|
||||
vgmstream->sample_rate = read_32bitLE(0x58, streamFile);
|
||||
vgmstream->num_samples = ms_ima_bytes_to_samples(read_32bitLE(0x6c, streamFile), read_16bitLE(0x60, streamFile), channel_count);
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
/* there is also a "smpl" chunk at the end, same as loop_start/end */
|
||||
|
||||
vgmstream->coding_type = coding_XBOX;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -11,8 +11,8 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
/* .xma2: Skullgirls, .nps: Beautiful Katamari (renamed .xma) */
|
||||
if ( !check_extensions(streamFile, "xma,xma2,nps") )
|
||||
/* .xma2: Skullgirls, .nps: Beautiful Katamari (renamed .xma), .str: Sonic & Sega All Stars Racing */
|
||||
if ( !check_extensions(streamFile, "xma,xma2,nps,str") )
|
||||
goto fail;
|
||||
|
||||
{
|
||||
|
@ -14,7 +14,8 @@
|
||||
#define XACT2_1_MAX 38 /* Prey (v38) */ // v39 too?
|
||||
#define XACT2_2_MAX 41 /* Blue Dragon (v40) */
|
||||
#define XACT3_0_MAX 46 /* Ninja Blade (t43 v42), Persona 4 Ultimax NESSICA (t45 v43) */
|
||||
#define XACT_TECHLAND 0x10000 /* Sniper Ghost Warrior, Nail'd (PS3/X360) */
|
||||
#define XACT_TECHLAND 0x10000 /* Sniper Ghost Warrior, Nail'd (PS3/X360), equivalent to XACT3_0 */
|
||||
#define XACT_CRACKDOWN 0x87 /* Crackdown 1, equivalent to XACT2_2 */
|
||||
|
||||
static const int wma_avg_bps_index[7] = {
|
||||
12000, 24000, 4000, 6000, 8000, 20000, 2500
|
||||
@ -92,6 +93,10 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
/* read main header (WAVEBANKHEADER) */
|
||||
xwb.version = read_32bit(0x04, streamFile); /* XACT3: 0x04=tool version, 0x08=header version */
|
||||
|
||||
/* Crackdown 1 X360, essentially XACT2 but may have split header in some cases */
|
||||
if (xwb.version == XACT_CRACKDOWN)
|
||||
xwb.version = XACT2_2_MAX;
|
||||
|
||||
/* read segment offsets (SEGIDX) */
|
||||
if (xwb.version <= XACT1_0_MAX) {
|
||||
xwb.streams = read_32bit(0x0c, streamFile);
|
||||
@ -306,7 +311,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
xwb.loop_end_sample = msd.loop_end_sample;
|
||||
|
||||
// todo fix properly (XWB loop_start/end seem to count padding samples while XMA1 RIFF doesn't)
|
||||
//this doesn't seem ok because can fall within 0 to 512 (ie.- first frame)
|
||||
//this doesn't seem ok because can fall within 0 to 512 (ie.- first frame, 384)
|
||||
//if (xwb.loop_start_sample) xwb.loop_start_sample -= 512;
|
||||
//if (xwb.loop_end_sample) xwb.loop_end_sample -= 512;
|
||||
|
||||
@ -314,6 +319,11 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
// (in rare cases this causes a glitch in FFmpeg since it has a bug where it's missing some samples)
|
||||
xwb.num_samples += 64 + 512;
|
||||
}
|
||||
else if ((xwb.codec == XMA1 || xwb.codec == XMA2) && xwb.loop_flag) {
|
||||
/* seems to be needed by some edge cases, ex. Crackdown */
|
||||
//add padding, see above
|
||||
xwb.num_samples += 64 + 512;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
|
@ -292,7 +292,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_ps2_lpcm,
|
||||
init_vgmstream_dsp_bdsp,
|
||||
init_vgmstream_ps2_vms,
|
||||
init_vgmstream_ps2_xau,
|
||||
init_vgmstream_xau,
|
||||
init_vgmstream_gh3_bar,
|
||||
init_vgmstream_ffw,
|
||||
init_vgmstream_dsp_dspw,
|
||||
@ -360,6 +360,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_rsd6xma,
|
||||
init_vgmstream_ta_aac_x360,
|
||||
init_vgmstream_ta_aac_ps3,
|
||||
init_vgmstream_ps3_mta2,
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
init_vgmstream_mp4_aac_ffmpeg,
|
||||
@ -868,12 +869,31 @@ 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) {
|
||||
return vgmstream->loop_start_sample+(vgmstream->loop_end_sample-vgmstream->loop_start_sample)*looptimes+(fadedelayseconds+fadeseconds)*vgmstream->sample_rate;
|
||||
} else return vgmstream->num_samples;
|
||||
if (fadeseconds < 0) { /* a bit hack-y to avoid signature change */
|
||||
/* Continue playing the file normally after looping, instead of fading.
|
||||
* Most files cut abruply after the loop, but some do have proper endings.
|
||||
* With looptimes = 1 this option should give the same output vs loop disabled */
|
||||
int loop_count = (int)looptimes; /* no half loops allowed */
|
||||
vgmstream->loop_target = loop_count;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* decode data into sample buffer */
|
||||
void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) {
|
||||
switch (vgmstream->layout_type) {
|
||||
case layout_interleave:
|
||||
@ -1065,6 +1085,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
return 54;
|
||||
case coding_MTAF:
|
||||
return 0x80*2;
|
||||
case coding_MTA2:
|
||||
return 0x80*2;
|
||||
case coding_MC3:
|
||||
return 10;
|
||||
case coding_CRI_HCA:
|
||||
@ -1191,6 +1213,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
case coding_MSADPCM:
|
||||
case coding_MTAF:
|
||||
return vgmstream->interleave_block_size;
|
||||
case coding_MTA2:
|
||||
return 0x90;
|
||||
case coding_MC3:
|
||||
return 4;
|
||||
default:
|
||||
@ -1735,6 +1759,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
chan, vgmstream->channels);
|
||||
}
|
||||
break;
|
||||
case coding_MTA2:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_mta2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
|
||||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do,
|
||||
chan);
|
||||
}
|
||||
break;
|
||||
case coding_MC3:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_mc3(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
|
||||
@ -1776,13 +1807,21 @@ int vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMST
|
||||
return samples_to_do;
|
||||
}
|
||||
|
||||
/* return 1 if we just looped */
|
||||
/* loop if end sample is reached, and return 1 if we did loop */
|
||||
int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
||||
/*if (vgmstream->loop_flag) return 0;*/
|
||||
/*if (!vgmstream->loop_flag) return 0;*/
|
||||
|
||||
/* is this the loop end? */
|
||||
/* 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 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* against everything I hold sacred, preserve adpcm
|
||||
* history through loop for certain types */
|
||||
if (vgmstream->meta_type == meta_DSP_STD ||
|
||||
|
@ -134,6 +134,7 @@ typedef enum {
|
||||
coding_SASSC, /* Activision EXAKT SASSC DPCM */
|
||||
coding_LSF, /* lsf ADPCM (Fastlane Street Racing iPhone)*/
|
||||
coding_MTAF, /* Konami MTAF ADPCM (IMA-derived) */
|
||||
coding_MTA2, /* Konami MTA2 ADPCM */
|
||||
coding_MC3, /* Paradigm MC3 3-bit ADPCM */
|
||||
|
||||
/* others */
|
||||
@ -548,7 +549,7 @@ typedef enum {
|
||||
meta_PS2_LPCM, /* Ah! My Goddess */
|
||||
meta_DSP_BDSP, /* Ah! My Goddess */
|
||||
meta_PS2_VMS, /* Autobahn Raser - Police Madness */
|
||||
meta_PS2_XAU, /* Spectral Force Chronicle */
|
||||
meta_XAU, /* XPEC Entertainment (Beat Down (PS2 Xbox), Spectral Force Chronicle (PS2)) */
|
||||
meta_GH3_BAR, /* Guitar Hero III Mobile .bar */
|
||||
meta_FFW, /* Freedom Fighters [NGC] */
|
||||
meta_DSP_DSPW, /* Sengoku Basara 3 [WII] */
|
||||
@ -615,6 +616,7 @@ typedef enum {
|
||||
meta_GTD, /* Knights Contract (X360/PS3), Valhalla Knights 3 (PSV) */
|
||||
meta_TA_AAC_X360, /* tri-ace AAC (Star Ocean 4, End of Eternity, Infinite Undiscovery) */
|
||||
meta_TA_AAC_PS3, /* tri-ace AAC (Star Ocean International, Resonance of Fate) */
|
||||
meta_PS3_MTA2, /* Metal Gear Solid 4 MTA2 */
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
meta_OGG_VORBIS, /* Ogg Vorbis */
|
||||
@ -726,8 +728,6 @@ typedef struct {
|
||||
off_t next_block_offset; /* offset of header of the next block */
|
||||
int block_count; /* count of "semi" block in total block */
|
||||
|
||||
int hit_loop; /* have we seen the loop yet? */
|
||||
|
||||
/* loop layout (saved values) */
|
||||
int32_t loop_sample; /* saved from current_sample, should be loop_start_sample... */
|
||||
int32_t loop_samples_into_block;/* saved from samples_into_block */
|
||||
@ -735,6 +735,12 @@ typedef struct {
|
||||
size_t loop_block_size; /* saved from current_block_size */
|
||||
off_t loop_next_block_offset; /* saved from next_block_offset */
|
||||
|
||||
/* loop internals */
|
||||
int hit_loop; /* have we seen the loop yet? */
|
||||
/* counters for "loop + play end of the stream instead of fading" (not used/needed otherwise) */
|
||||
int loop_count; /* number of complete loops (1=looped once) */
|
||||
int loop_target; /* max loops before continuing with the stream end */
|
||||
|
||||
/* decoder specific */
|
||||
int codec_endian; /* little/big endian marker; name is left vague but usually means big endian */
|
||||
|
||||
|
34
test/test.c
34
test/test.c
@ -36,7 +36,7 @@ void usage(const char * name) {
|
||||
"Options:\n"
|
||||
" -o outfile.wav: name of output .wav file, default is dump.wav\n"
|
||||
" -l loop count: loop count, default 2.0\n"
|
||||
" -f fade time: fade time (seconds), default 10.0\n"
|
||||
" -f fade time: fade time (seconds) after N loops, default 10.0\n"
|
||||
" -d fade delay: fade delay (seconds, default 0.0\n"
|
||||
" -i: ignore looping information and play the whole stream once\n"
|
||||
" -p: output to stdout (for piping into another program)\n"
|
||||
@ -46,11 +46,12 @@ void usage(const char * name) {
|
||||
" -x: decode and print adxencd command line to encode as ADX\n"
|
||||
" -g: decode and print oggenc command line to encode as OGG\n"
|
||||
" -b: decode and print batch variable commands\n"
|
||||
" -L: append a smpl chunk and create a looping wav\n"
|
||||
" -L: append a smpl chunk and create a looping wav\n"
|
||||
" -e: force end-to-end looping\n"
|
||||
" -E: force end-to-end looping even if file has real loop points\n"
|
||||
" -r outfile2.wav: output a second time after resetting\n"
|
||||
" -2 N: only output the Nth (first is 0) set of stereo channels\n"
|
||||
" -F: don't fade after N loops and play the rest of the stream\n"
|
||||
,name);
|
||||
}
|
||||
|
||||
@ -73,15 +74,16 @@ int main(int argc, char ** argv) {
|
||||
int metaonly = 0;
|
||||
int adxencd = 0;
|
||||
int oggenc = 0;
|
||||
int lwav = 0;
|
||||
int lwav = 0;
|
||||
int batchvar = 0;
|
||||
int only_stereo = -1;
|
||||
double loop_count = 2.0;
|
||||
double fade_seconds = 10.0;
|
||||
double fade_delay_seconds = 0.0;
|
||||
int32_t bytecount;
|
||||
int fade_ignore = 0;
|
||||
int32_t bytecount;
|
||||
|
||||
while ((opt = getopt(argc, argv, "o:l:f:d:ipPcmxeLEr:gb2:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "o:l:f:d:ipPcmxeLEFr:gb2:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'o':
|
||||
outfilename = optarg;
|
||||
@ -127,14 +129,17 @@ int main(int argc, char ** argv) {
|
||||
really_force_loop = 1;
|
||||
break;
|
||||
case 'L':
|
||||
lwav = 1;
|
||||
break;
|
||||
lwav = 1;
|
||||
break;
|
||||
case 'r':
|
||||
reset_outfilename = optarg;
|
||||
break;
|
||||
case '2':
|
||||
only_stereo = atoi(optarg);
|
||||
break;
|
||||
case 'F':
|
||||
fade_ignore = 1;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
@ -263,6 +268,11 @@ int main(int argc, char ** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* signal ignore fade for get_vgmstream_play_samples */
|
||||
if (loop_count && fade_ignore) {
|
||||
fade_seconds = -1.0;
|
||||
}
|
||||
|
||||
len = get_vgmstream_play_samples(loop_count,fade_seconds,fade_delay_seconds,s);
|
||||
if (!play && !adxencd && !oggenc && !batchvar) printf("samples to play: %d (%.4lf seconds)\n",len,(double)len/s->sample_rate);
|
||||
fade_samples = fade_seconds * s->sample_rate;
|
||||
@ -273,10 +283,10 @@ int main(int argc, char ** argv) {
|
||||
} else {
|
||||
make_wav_header((uint8_t*)buf, len, s->sample_rate, s->channels);
|
||||
}
|
||||
if (lwav && s->loop_flag) { // Adding space for smpl chunk at end
|
||||
if (lwav && s->loop_flag) { // Adding space for smpl chunk at end
|
||||
bytecount = get_32bitLE((uint8_t*)buf + 4);
|
||||
put_32bitLE((uint8_t*)buf + 4, bytecount + 0x44);
|
||||
}
|
||||
}
|
||||
fwrite(buf,1,0x2c,outfile);
|
||||
|
||||
/* decode forever */
|
||||
@ -325,9 +335,9 @@ int main(int argc, char ** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (lwav && s->loop_flag) { // Writing smpl chuck
|
||||
make_smpl_chunk((uint8_t*)buf, s->loop_start_sample, s->loop_end_sample);
|
||||
fwrite(buf,1,0x44,outfile);
|
||||
if (lwav && s->loop_flag) { // Writing smpl chuck
|
||||
make_smpl_chunk((uint8_t*)buf, s->loop_start_sample, s->loop_end_sample);
|
||||
fwrite(buf,1,0x44,outfile);
|
||||
}
|
||||
fclose(outfile); outfile = NULL;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user