Merge pull request #117 from bnnm/streams-plugins

Streams, plugins
This commit is contained in:
Christopher Snowhill 2017-08-13 21:38:30 -07:00 committed by GitHub
commit 001b1b9d57
39 changed files with 2425 additions and 1764 deletions

View File

@ -2,13 +2,12 @@
#define _FOO_FILETYPES_H_
#define DECLARE_MULTIPLE_FILE_TYPE(NAME,EXTENSION) \
#define VGMSTREAM_DECLARE_FILE_TYPE(NAME,EXTENSION) \
namespace { \
static input_file_type_impl g_filetype_instance_##EXTENSION(NAME,"*." #EXTENSION ,true); \
static input_file_type_impl g_filetype_instance_##EXTENSION(NAME" Audio File (*."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
@ -16,345 +15,346 @@
// these are declared statically, and if anyone has a better idea i'd like to hear it - josh.
DECLARE_MULTIPLE_FILE_TYPE("04SW Audio File (*.04SW)", 04sw);
DECLARE_MULTIPLE_FILE_TYPE("2DX9 Audio File (*.2DX9)", 2dx9);
DECLARE_MULTIPLE_FILE_TYPE("2PFS Audio File (*.2PFS)", 2pfs);
VGMSTREAM_DECLARE_FILE_TYPE("04SW", 04sw);
VGMSTREAM_DECLARE_FILE_TYPE("2DX9", 2dx9);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("AA3", aa3);
VGMSTREAM_DECLARE_FILE_TYPE("AAAP", aaap);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("ACE", ace);
VGMSTREAM_DECLARE_FILE_TYPE("ACM", acm);
VGMSTREAM_DECLARE_FILE_TYPE("ADM", adm);
VGMSTREAM_DECLARE_FILE_TYPE("ADP", adp);
VGMSTREAM_DECLARE_FILE_TYPE("ADPCM", adpcm);
VGMSTREAM_DECLARE_FILE_TYPE("ADS", ads);
VGMSTREAM_DECLARE_FILE_TYPE("ADX", adx);
VGMSTREAM_DECLARE_FILE_TYPE("AFC", afc);
VGMSTREAM_DECLARE_FILE_TYPE("AGSC", agsc);
VGMSTREAM_DECLARE_FILE_TYPE("AHX", ahx);
VGMSTREAM_DECLARE_FILE_TYPE("AIFC", aifc);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("AIX", aix);
VGMSTREAM_DECLARE_FILE_TYPE("AKB", akb);
VGMSTREAM_DECLARE_FILE_TYPE("AMTS", amts);
VGMSTREAM_DECLARE_FILE_TYPE("AS4", as4);
VGMSTREAM_DECLARE_FILE_TYPE("ASD", asd);
VGMSTREAM_DECLARE_FILE_TYPE("ASF", asf);
VGMSTREAM_DECLARE_FILE_TYPE("ASR", asr);
VGMSTREAM_DECLARE_FILE_TYPE("ASS", ass);
VGMSTREAM_DECLARE_FILE_TYPE("AST", ast);
VGMSTREAM_DECLARE_FILE_TYPE("ATRAC3plus", at3);
VGMSTREAM_DECLARE_FILE_TYPE("AUD", aud);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("B1S", b1s);
VGMSTREAM_DECLARE_FILE_TYPE("BAF", baf);
VGMSTREAM_DECLARE_FILE_TYPE("BAKA", baka);
VGMSTREAM_DECLARE_FILE_TYPE("BAR", bar);
VGMSTREAM_DECLARE_FILE_TYPE("BCSTM", bcstm);
VGMSTREAM_DECLARE_FILE_TYPE("BCWAV", bcwav);
VGMSTREAM_DECLARE_FILE_TYPE("BDSP", bdsp);
VGMSTREAM_DECLARE_FILE_TYPE("BFSTM", bfstm);
VGMSTREAM_DECLARE_FILE_TYPE("BFWAV", bfwav);
VGMSTREAM_DECLARE_FILE_TYPE("BFWAVNSMBU", bfwavnsmbu);
VGMSTREAM_DECLARE_FILE_TYPE("BG00", bg00);
VGMSTREAM_DECLARE_FILE_TYPE("BGM", bgm);
VGMSTREAM_DECLARE_FILE_TYPE("BGW", bgw);
VGMSTREAM_DECLARE_FILE_TYPE("BH2PCM", bh2pcm);
VGMSTREAM_DECLARE_FILE_TYPE("BIK", bik);
VGMSTREAM_DECLARE_FILE_TYPE("BIKA", bika);
VGMSTREAM_DECLARE_FILE_TYPE("BIK2", bik2);
VGMSTREAM_DECLARE_FILE_TYPE("BIK2A", bik2a);
VGMSTREAM_DECLARE_FILE_TYPE("BK2", bk2);
VGMSTREAM_DECLARE_FILE_TYPE("BK2A", bk2a);
VGMSTREAM_DECLARE_FILE_TYPE("BMDX", bmdx);
VGMSTREAM_DECLARE_FILE_TYPE("BMS", bms);
VGMSTREAM_DECLARE_FILE_TYPE("KLBS", bnk);
VGMSTREAM_DECLARE_FILE_TYPE("BNS", bns);
VGMSTREAM_DECLARE_FILE_TYPE("BNSF", bnsf);
VGMSTREAM_DECLARE_FILE_TYPE("BO2", bo2);
VGMSTREAM_DECLARE_FILE_TYPE("BRSTM", brstm);
VGMSTREAM_DECLARE_FILE_TYPE("BRSTMSPM", brstmspm);
VGMSTREAM_DECLARE_FILE_TYPE("BTSND", btsnd);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("CAF", caf);
VGMSTREAM_DECLARE_FILE_TYPE("CAPDSP", capdsp);
VGMSTREAM_DECLARE_FILE_TYPE("CBD2", cbd2);
VGMSTREAM_DECLARE_FILE_TYPE("CCC", ccc);
VGMSTREAM_DECLARE_FILE_TYPE("CFN", cfn);
VGMSTREAM_DECLARE_FILE_TYPE("CKD", ckd);
VGMSTREAM_DECLARE_FILE_TYPE("CNK", cnk);
VGMSTREAM_DECLARE_FILE_TYPE("CPS", cps);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("DBM", dbm);
VGMSTREAM_DECLARE_FILE_TYPE("DCS", dcs);
VGMSTREAM_DECLARE_FILE_TYPE("DDSP", ddsp);
VGMSTREAM_DECLARE_FILE_TYPE("DE2", de2);
VGMSTREAM_DECLARE_FILE_TYPE("DMSG", dmsg);
VGMSTREAM_DECLARE_FILE_TYPE("DSP", dsp);
VGMSTREAM_DECLARE_FILE_TYPE("DSPW", dspw);
VGMSTREAM_DECLARE_FILE_TYPE("DTK", dtk);
VGMSTREAM_DECLARE_FILE_TYPE("DVI", dvi);
VGMSTREAM_DECLARE_FILE_TYPE("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("EXA Audio File (*.EXA)", exa);
VGMSTREAM_DECLARE_FILE_TYPE("EAM", eam);
VGMSTREAM_DECLARE_FILE_TYPE("EMFF", emff);
VGMSTREAM_DECLARE_FILE_TYPE("ENTH", enth);
VGMSTREAM_DECLARE_FILE_TYPE("EXA", exa);
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);
VGMSTREAM_DECLARE_FILE_TYPE("FAG", fag);
VGMSTREAM_DECLARE_FILE_TYPE("FFW", ffw);
VGMSTREAM_DECLARE_FILE_TYPE("FILP", filp);
VGMSTREAM_DECLARE_FILE_TYPE("FSB", fsb);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("G1L", g1l);
VGMSTREAM_DECLARE_FILE_TYPE("GBTS", gbts);
VGMSTREAM_DECLARE_FILE_TYPE("GCA", gca);
VGMSTREAM_DECLARE_FILE_TYPE("GCM", gcm);
VGMSTREAM_DECLARE_FILE_TYPE("GCUB", gcub);
VGMSTREAM_DECLARE_FILE_TYPE("GCW", gcw);
VGMSTREAM_DECLARE_FILE_TYPE("GENH", genh);
VGMSTREAM_DECLARE_FILE_TYPE("GMS", gms);
VGMSTREAM_DECLARE_FILE_TYPE("GSB", gsb);
VGMSTREAM_DECLARE_FILE_TYPE("GSB", 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);
VGMSTREAM_DECLARE_FILE_TYPE("HCA", hca);
VGMSTREAM_DECLARE_FILE_TYPE("HGC1", hgc1);
VGMSTREAM_DECLARE_FILE_TYPE("HIS", his);
VGMSTREAM_DECLARE_FILE_TYPE("HLWAV", hlwav);
VGMSTREAM_DECLARE_FILE_TYPE("HALPST", hps);
VGMSTREAM_DECLARE_FILE_TYPE("HSF", hsf);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("IAB", iab);
VGMSTREAM_DECLARE_FILE_TYPE("IADP", iadp);
VGMSTREAM_DECLARE_FILE_TYPE("IDSP", idsp);
VGMSTREAM_DECLARE_FILE_TYPE("IDVI", idvi);
VGMSTREAM_DECLARE_FILE_TYPE("IKM", ikm);
VGMSTREAM_DECLARE_FILE_TYPE("ILD", ild);
VGMSTREAM_DECLARE_FILE_TYPE("INT", int);
VGMSTREAM_DECLARE_FILE_TYPE("ISD", isd);
VGMSTREAM_DECLARE_FILE_TYPE("ISWS", isws);
VGMSTREAM_DECLARE_FILE_TYPE("IVAUD", ivaud);
VGMSTREAM_DECLARE_FILE_TYPE("IVAG", ivag);
VGMSTREAM_DECLARE_FILE_TYPE("IVB", ivb);
DECLARE_MULTIPLE_FILE_TYPE("JOE Audio File (*.JOE)", joe);
DECLARE_MULTIPLE_FILE_TYPE("JSTM Audio File (*.JSTM)", jstm);
VGMSTREAM_DECLARE_FILE_TYPE("JOE", joe);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("KCES", kces);
VGMSTREAM_DECLARE_FILE_TYPE("KCEY", kcey);
VGMSTREAM_DECLARE_FILE_TYPE("KHV", khv);
VGMSTREAM_DECLARE_FILE_TYPE("KOVS", kovs);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("LAAC", laac);
VGMSTREAM_DECLARE_FILE_TYPE("LEG", leg);
VGMSTREAM_DECLARE_FILE_TYPE("LMP4", lmp4);
VGMSTREAM_DECLARE_FILE_TYPE("LOGG", logg);
VGMSTREAM_DECLARE_FILE_TYPE("LPCM", lpcm);
VGMSTREAM_DECLARE_FILE_TYPE("LPS", lps);
VGMSTREAM_DECLARE_FILE_TYPE("LSF", lsf);
VGMSTREAM_DECLARE_FILE_TYPE("LSTM", lstm);
VGMSTREAM_DECLARE_FILE_TYPE("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("MED Audio File (*.MED)", med);
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);
VGMSTREAM_DECLARE_FILE_TYPE("MATX", matx);
VGMSTREAM_DECLARE_FILE_TYPE("MC3", mc3);
VGMSTREAM_DECLARE_FILE_TYPE("MCA", mca);
VGMSTREAM_DECLARE_FILE_TYPE("MCG", mcg);
VGMSTREAM_DECLARE_FILE_TYPE("MDS", mds);
VGMSTREAM_DECLARE_FILE_TYPE("MDSP", mdsp);
VGMSTREAM_DECLARE_FILE_TYPE("MED", med);
VGMSTREAM_DECLARE_FILE_TYPE("MI4", mi4);
VGMSTREAM_DECLARE_FILE_TYPE("MIB", mib);
VGMSTREAM_DECLARE_FILE_TYPE("MIC", mic);
VGMSTREAM_DECLARE_FILE_TYPE("MIHB", mihb);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("MPDSP", mpdsp);
VGMSTREAM_DECLARE_FILE_TYPE("MPDS", mpds);
VGMSTREAM_DECLARE_FILE_TYPE("MSA", msa);
VGMSTREAM_DECLARE_FILE_TYPE("MSF", msf);
VGMSTREAM_DECLARE_FILE_TYPE("MSS", mss);
VGMSTREAM_DECLARE_FILE_TYPE("MSVP", msvp);
VGMSTREAM_DECLARE_FILE_TYPE("MTA2", mta2);
VGMSTREAM_DECLARE_FILE_TYPE("MTAF", mtaf);
VGMSTREAM_DECLARE_FILE_TYPE("MUS", mus);
VGMSTREAM_DECLARE_FILE_TYPE("MUSC", musc);
VGMSTREAM_DECLARE_FILE_TYPE("MUSX", musx);
VGMSTREAM_DECLARE_FILE_TYPE("MWV", mwv);
VGMSTREAM_DECLARE_FILE_TYPE("MxSt", mxst);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("NDP", ndp);
VGMSTREAM_DECLARE_FILE_TYPE("NGCA", ngca);
VGMSTREAM_DECLARE_FILE_TYPE("NPS", nps);
VGMSTREAM_DECLARE_FILE_TYPE("NPSF", npsf);
VGMSTREAM_DECLARE_FILE_TYPE("NUS3BANK", nus3bank);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("OGL", ogl);
VGMSTREAM_DECLARE_FILE_TYPE("OMA", oma);
VGMSTREAM_DECLARE_FILE_TYPE("OMU", omu);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("P2BT", p2bt);
VGMSTREAM_DECLARE_FILE_TYPE("P3D", p3d);
VGMSTREAM_DECLARE_FILE_TYPE("PAST", past);
VGMSTREAM_DECLARE_FILE_TYPE("PCM", pcm);
VGMSTREAM_DECLARE_FILE_TYPE("PDT", pdt);
VGMSTREAM_DECLARE_FILE_TYPE("PNB", pnb);
VGMSTREAM_DECLARE_FILE_TYPE("PONA", pona);
VGMSTREAM_DECLARE_FILE_TYPE("POS", pos);
VGMSTREAM_DECLARE_FILE_TYPE("PS2STM", ps2stm);
VGMSTREAM_DECLARE_FILE_TYPE("PSH", psh);
VGMSTREAM_DECLARE_FILE_TYPE("PSND", psnd);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("RAK", rak);
VGMSTREAM_DECLARE_FILE_TYPE("RAS", ras);
VGMSTREAM_DECLARE_FILE_TYPE("RAW", raw);
VGMSTREAM_DECLARE_FILE_TYPE("RKV", rkv);
VGMSTREAM_DECLARE_FILE_TYPE("RND", rnd);
VGMSTREAM_DECLARE_FILE_TYPE("RRDS", rrds);
VGMSTREAM_DECLARE_FILE_TYPE("RSD", rsd);
VGMSTREAM_DECLARE_FILE_TYPE("RSF", rsf);
VGMSTREAM_DECLARE_FILE_TYPE("RSTM", rstm);
VGMSTREAM_DECLARE_FILE_TYPE("RVWS", rvws);
VGMSTREAM_DECLARE_FILE_TYPE("RWAR", rwar);
VGMSTREAM_DECLARE_FILE_TYPE("RWAV", rwav);
VGMSTREAM_DECLARE_FILE_TYPE("RWS", rws);
VGMSTREAM_DECLARE_FILE_TYPE("RWSD", rwsd);
VGMSTREAM_DECLARE_FILE_TYPE("RWX", rwx);
VGMSTREAM_DECLARE_FILE_TYPE("RXWS", 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);
VGMSTREAM_DECLARE_FILE_TYPE("S14", s14);
VGMSTREAM_DECLARE_FILE_TYPE("SAB", sab);
VGMSTREAM_DECLARE_FILE_TYPE("SAD", sad);
VGMSTREAM_DECLARE_FILE_TYPE("SAP", sap);
VGMSTREAM_DECLARE_FILE_TYPE("SC", sc);
VGMSTREAM_DECLARE_FILE_TYPE("SCD", scd);
VGMSTREAM_DECLARE_FILE_TYPE("SCK", sck);
VGMSTREAM_DECLARE_FILE_TYPE("SD9", sd9);
VGMSTREAM_DECLARE_FILE_TYPE("SDT", sdt);
VGMSTREAM_DECLARE_FILE_TYPE("SEG", seg);
VGMSTREAM_DECLARE_FILE_TYPE("SF0", sf0);
VGMSTREAM_DECLARE_FILE_TYPE("SFL", sfl);
VGMSTREAM_DECLARE_FILE_TYPE("SFS", sfs);
VGMSTREAM_DECLARE_FILE_TYPE("SFX", sfx);
VGMSTREAM_DECLARE_FILE_TYPE("SGB", sgb);
VGMSTREAM_DECLARE_FILE_TYPE("SGD", sgd);
VGMSTREAM_DECLARE_FILE_TYPE("SGX", sgx);
VGMSTREAM_DECLARE_FILE_TYPE("SL3", sl3);
VGMSTREAM_DECLARE_FILE_TYPE("SLI", sli);
VGMSTREAM_DECLARE_FILE_TYPE("SMP", smp);
VGMSTREAM_DECLARE_FILE_TYPE("SMPL", smpl);
VGMSTREAM_DECLARE_FILE_TYPE("SND", snd);
VGMSTREAM_DECLARE_FILE_TYPE("SNDS", snds);
VGMSTREAM_DECLARE_FILE_TYPE("SNG", sng);
VGMSTREAM_DECLARE_FILE_TYPE("SNS", sns);
VGMSTREAM_DECLARE_FILE_TYPE("SPD", spd);
VGMSTREAM_DECLARE_FILE_TYPE("SPM", spm);
VGMSTREAM_DECLARE_FILE_TYPE("SPS", sps);
VGMSTREAM_DECLARE_FILE_TYPE("SPSD", spsd);
VGMSTREAM_DECLARE_FILE_TYPE("SPW", spw);
VGMSTREAM_DECLARE_FILE_TYPE("SS2", ss2);
VGMSTREAM_DECLARE_FILE_TYPE("SS3", ss3);
VGMSTREAM_DECLARE_FILE_TYPE("SS7", ss7);
VGMSTREAM_DECLARE_FILE_TYPE("SSM", ssm);
VGMSTREAM_DECLARE_FILE_TYPE("SSS", sss);
VGMSTREAM_DECLARE_FILE_TYPE("STER", ster);
VGMSTREAM_DECLARE_FILE_TYPE("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("SX Audio File (*.SX)", sx);
DECLARE_MULTIPLE_FILE_TYPE("SXD Audio File (*.SXD)", sxd);
DECLARE_MULTIPLE_FILE_TYPE("SXD2 Audio File (*.SXD2)", sxd2);
VGMSTREAM_DECLARE_FILE_TYPE("STMA", stma);
VGMSTREAM_DECLARE_FILE_TYPE("STR", str);
VGMSTREAM_DECLARE_FILE_TYPE("STRM", strm);
VGMSTREAM_DECLARE_FILE_TYPE("STS", sts);
VGMSTREAM_DECLARE_FILE_TYPE("STX", stx);
VGMSTREAM_DECLARE_FILE_TYPE("SVAG", svag);
VGMSTREAM_DECLARE_FILE_TYPE("SVS", svs);
VGMSTREAM_DECLARE_FILE_TYPE("SWAG", swag);
VGMSTREAM_DECLARE_FILE_TYPE("SWAV", swav);
VGMSTREAM_DECLARE_FILE_TYPE("SWD", swd);
VGMSTREAM_DECLARE_FILE_TYPE("SX", sx);
VGMSTREAM_DECLARE_FILE_TYPE("SXD", sxd);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("TEC", tec);
VGMSTREAM_DECLARE_FILE_TYPE("THP", thp);
VGMSTREAM_DECLARE_FILE_TYPE("TK1", tk1);
VGMSTREAM_DECLARE_FILE_TYPE("TK5", tk5);
VGMSTREAM_DECLARE_FILE_TYPE("TRA", tra);
VGMSTREAM_DECLARE_FILE_TYPE("TUN", tun);
VGMSTREAM_DECLARE_FILE_TYPE("TYDSP", tydsp);
DECLARE_MULTIPLE_FILE_TYPE("ULW Audio File (*.ULW)", ulw);
DECLARE_MULTIPLE_FILE_TYPE("UM3 Audio File (*.UM3)", um3);
VGMSTREAM_DECLARE_FILE_TYPE("ULW", ulw);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("VAG", vag);
VGMSTREAM_DECLARE_FILE_TYPE("VAS", vas);
VGMSTREAM_DECLARE_FILE_TYPE("VAS", vawx);
VGMSTREAM_DECLARE_FILE_TYPE("VB", vb);
VGMSTREAM_DECLARE_FILE_TYPE("VBK", vbk);
VGMSTREAM_DECLARE_FILE_TYPE("VGS", vgs);
VGMSTREAM_DECLARE_FILE_TYPE("VGV", vgv);
VGMSTREAM_DECLARE_FILE_TYPE("VIG", vig);
VGMSTREAM_DECLARE_FILE_TYPE("VMS", vms);
VGMSTREAM_DECLARE_FILE_TYPE("VOI", voi);
VGMSTREAM_DECLARE_FILE_TYPE("VPK", vpk);
VGMSTREAM_DECLARE_FILE_TYPE("VS", vs);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("WAA", waa);
VGMSTREAM_DECLARE_FILE_TYPE("WAC", wac);
VGMSTREAM_DECLARE_FILE_TYPE("WAD", wad);
VGMSTREAM_DECLARE_FILE_TYPE("WAM", wam);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("WAVM", wavm);
VGMSTREAM_DECLARE_FILE_TYPE("WEM", wem);
VGMSTREAM_DECLARE_FILE_TYPE("WII", wii);
VGMSTREAM_DECLARE_FILE_TYPE("WMUS", wmus);
VGMSTREAM_DECLARE_FILE_TYPE("WP2", wp2);
VGMSTREAM_DECLARE_FILE_TYPE("WPD", wpd);
VGMSTREAM_DECLARE_FILE_TYPE("WSD", wsd);
VGMSTREAM_DECLARE_FILE_TYPE("WSI", wsi);
VGMSTREAM_DECLARE_FILE_TYPE("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);
VGMSTREAM_DECLARE_FILE_TYPE("XA", xa);
VGMSTREAM_DECLARE_FILE_TYPE("XA2", xa2);
VGMSTREAM_DECLARE_FILE_TYPE("XA30", xa30);
VGMSTREAM_DECLARE_FILE_TYPE("XAG", xag);
VGMSTREAM_DECLARE_FILE_TYPE("XAU", xau);
VGMSTREAM_DECLARE_FILE_TYPE("XMA", xma);
VGMSTREAM_DECLARE_FILE_TYPE("XMA2", xma2);
VGMSTREAM_DECLARE_FILE_TYPE("XMU", xmu);
VGMSTREAM_DECLARE_FILE_TYPE("XNB", xnb);
VGMSTREAM_DECLARE_FILE_TYPE("XSF", xsf);
VGMSTREAM_DECLARE_FILE_TYPE("XSS", xss);
VGMSTREAM_DECLARE_FILE_TYPE("XVAG", xvag);
VGMSTREAM_DECLARE_FILE_TYPE("XVAS", xvas);
VGMSTREAM_DECLARE_FILE_TYPE("XWAV", xwav);
VGMSTREAM_DECLARE_FILE_TYPE("XWB", xwb);
VGMSTREAM_DECLARE_FILE_TYPE("XWM", xwm);
VGMSTREAM_DECLARE_FILE_TYPE("XWMA", xwma);
VGMSTREAM_DECLARE_FILE_TYPE("XWS", xws);
VGMSTREAM_DECLARE_FILE_TYPE("XWV", xwv);
DECLARE_MULTIPLE_FILE_TYPE("YDSP Audio File (*.YDSP)", ydsp);
DECLARE_MULTIPLE_FILE_TYPE("YMF Audio File (*.YMF)", ymf);
VGMSTREAM_DECLARE_FILE_TYPE("YDSP", ydsp);
VGMSTREAM_DECLARE_FILE_TYPE("YMF", ymf);
DECLARE_MULTIPLE_FILE_TYPE("ZSD Audio File (*.ZSD)", zsd);
DECLARE_MULTIPLE_FILE_TYPE("ZWDSP Audio File (*.ZWDSP)", zwdsp);
VGMSTREAM_DECLARE_FILE_TYPE("ZSD", zsd);
VGMSTREAM_DECLARE_FILE_TYPE("ZWDSP", zwdsp);
DECLARE_MULTIPLE_FILE_TYPE("VGMSTREAM Audio File (*.VGMSTREAM)", vgmstream);
VGMSTREAM_DECLARE_FILE_TYPE("VGMSTREAM", vgmstream);
#endif /*_FOO_FILETYPES_H_ */

View File

@ -24,6 +24,22 @@ extern "C" {
#define STREAMFILE_IGNORE_EOF 0
/* a STREAMFILE that operates via foobar's file service using a buffer */
typedef struct _FOO_STREAMFILE {
struct _STREAMFILE sf;
abort_callback * p_abort;
service_ptr_t<file> m_file;
char * name;
off_t offset;
size_t validsize;
uint8_t * buffer;
size_t buffersize;
size_t filesize;
} FOO_STREAMFILE;
static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_file,const char * const filename, size_t buffersize, abort_callback * p_abort);
static STREAMFILE * open_foo_streamfile_buffer(const char * const filename, size_t buffersize, abort_callback * p_abort, t_filestats * stats);
static size_t read_the_rest_foo(uint8_t * dest, off_t offset, size_t length, FOO_STREAMFILE * streamfile) {
size_t length_read_total=0;
@ -253,7 +269,7 @@ static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_fil
return &streamfile->sf;
}
STREAMFILE * open_foo_streamfile_buffer(const char * const filename, size_t buffersize, abort_callback * p_abort, t_filestats * stats) {
static STREAMFILE * open_foo_streamfile_buffer(const char * const filename, size_t buffersize, abort_callback * p_abort, t_filestats * stats) {
STREAMFILE *streamFile;
service_ptr_t<file> infile;

View File

@ -41,321 +41,251 @@ extern "C" {
"https://sourceforge.net/projects/vgmstream/ (original)";
/* format detection and VGMSTREAM setup, uses default parameters */
VGMSTREAM * input_vgmstream::init_vgmstream_foo(const char * const filename, abort_callback & p_abort) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *streamFile = open_foo_streamfile(filename, &p_abort, &stats);
if (streamFile) {
vgmstream = init_vgmstream_from_STREAMFILE(streamFile);
close_streamfile(streamFile);
}
return vgmstream;
}
void input_vgmstream::open(service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
currentreason = p_reason;
if(p_path) filename = p_path;
/* KLUDGE */
if ( !pfc::stricmp_ascii( pfc::string_extension( p_path ), "MUS" ) )
{
unsigned char buffer[ 4 ];
if ( p_filehint.is_empty() ) input_open_file_helper( p_filehint, p_path, p_reason, p_abort );
p_filehint->read_object_t( buffer, p_abort );
if ( !memcmp( buffer, "MUS\x1A", 4 ) ) throw exception_io_unsupported_format();
}
switch(p_reason) {
case input_open_decode:
vgmstream = init_vgmstream_foo(p_path, p_abort);
/* were we able to open it? */
if (!vgmstream) {
/* Generate exception if the file is unopenable*/
throw exception_io_data();
return;
}
if (ignore_loop) vgmstream->loop_flag = 0;
/* will we be able to play it? */
if (vgmstream->channels <= 0) {
close_vgmstream(vgmstream);
vgmstream=NULL;
throw exception_io_data();
return;
}
decode_pos_ms = 0;
decode_pos_samples = 0;
paused = 0;
stream_length_samples = get_vgmstream_play_samples(loop_count,fade_seconds,fade_delay_seconds,vgmstream);
fade_samples = (int)(fade_seconds * vgmstream->sample_rate);
break;
case input_open_info_read:
if ( p_filehint.is_empty() ) input_open_file_helper( p_filehint, p_path, p_reason, p_abort );
stats = p_filehint->get_stats( p_abort );
break;
case input_open_info_write:
//cant write...ever
throw exception_io_data();
break;
default:
break;
}
}
void input_vgmstream::get_info(file_info & p_info,abort_callback & p_abort ) {
int length_in_ms=0, channels = 0, samplerate = 0;
int total_samples = -1;
int bitrate = 0;
int loop_start = -1, loop_end = -1;
pfc::string8 description;
pfc::string8_fast temp;
getfileinfo(filename, NULL, &length_in_ms, &total_samples, &loop_start, &loop_end, &samplerate, &channels, &bitrate, description, p_abort);
p_info.info_set_int("samplerate", samplerate);
p_info.info_set_int("channels", channels);
p_info.info_set_int("bitspersample",16);
p_info.info_set("encoding","lossless");
p_info.info_set_bitrate(bitrate / 1000);
if (total_samples > 0)
p_info.info_set_int("stream_total_samples", total_samples);
if (loop_start >= 0 && loop_end >= loop_start)
{
p_info.info_set_int("loop_start", loop_start);
p_info.info_set_int("loop_end", loop_end);
}
t_size pos = description.find_first("encoding: ");
t_size eos;
if (pos != pfc::infinite_size)
{
pos += strlen("encoding: ");
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("codec", temp);
}
pos = description.find_first("layout: ");
if (pos != pfc::infinite_size)
{
pos += strlen("layout: ");
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("layout", temp);
}
pos = description.find_first("interleave: ");
if (pos != pfc::infinite_size)
{
pos += strlen("interleave: ");
eos = description.find_first(' ', pos);
if (eos == pfc::infinite_size) eos = description.length();
temp.set_string(description + pos, eos - pos);
p_info.info_set("interleave", temp);
pos = description.find_first("last block interleave: ", eos);
if (pos != pfc::infinite_size)
{
pos += strlen("last block interleave: ");
eos = description.find_first(' ', pos);
if (eos == pfc::infinite_size) eos = description.length();
temp.set_string(description + pos, eos - pos);
p_info.info_set("interleave last block", temp);
}
}
pos = description.find_first("metadata from: ");
if (pos != pfc::infinite_size)
{
pos += strlen("metadata from: ");
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("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);
}
void input_vgmstream::decode_initialize(unsigned p_flags,abort_callback & p_abort) {
force_ignore_loop = !!(p_flags & input_flag_no_looping);
decode_seek( 0, p_abort );
};
bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort) {
if (!decoding) return false;
int max_buffer_samples = sizeof(sample_buffer)/sizeof(sample_buffer[0])/vgmstream->channels;
int l = 0, samples_to_do = max_buffer_samples, t= 0;
if(vgmstream) {
bool loop_okay = loop_forever && vgmstream->loop_flag && !ignore_loop && !force_ignore_loop;
if (decode_pos_samples+max_buffer_samples>stream_length_samples && !loop_okay)
samples_to_do=stream_length_samples-decode_pos_samples;
else
samples_to_do=max_buffer_samples;
l = (samples_to_do*vgmstream->channels * sizeof(sample_buffer[0]));
if (samples_to_do /*< DECODE_SIZE*/ == 0) {
decoding = false;
return false;
}
t = ((test_length*vgmstream->sample_rate)/1000);
render_vgmstream(sample_buffer,samples_to_do,vgmstream);
/* fade! */
if (vgmstream->loop_flag && fade_samples > 0 && !loop_okay) {
int samples_into_fade = decode_pos_samples - (stream_length_samples - fade_samples);
if (samples_into_fade + samples_to_do > 0) {
int j,k;
for (j=0;j<samples_to_do;j++,samples_into_fade++) {
if (samples_into_fade > 0) {
double fadedness = (double)(fade_samples-samples_into_fade)/fade_samples;
for (k=0;k<vgmstream->channels;k++) {
sample_buffer[j*vgmstream->channels+k] =
(short)(sample_buffer[j*vgmstream->channels+k]*fadedness);
}
}
}
}
}
p_chunk.set_data_fixedpoint((char*)sample_buffer, l, vgmstream->sample_rate, vgmstream->channels, 16, audio_chunk::g_guess_channel_config(vgmstream->channels));
decode_pos_samples+=samples_to_do;
decode_pos_ms=decode_pos_samples*1000LL/vgmstream->sample_rate;
return samples_to_do==max_buffer_samples;
}
return false;
}
void input_vgmstream::decode_seek(double p_seconds,abort_callback & p_abort) {
seek_pos_samples = (int) audio_math::time_to_samples(p_seconds, vgmstream->sample_rate);
int max_buffer_samples = sizeof(sample_buffer)/sizeof(sample_buffer[0])/vgmstream->channels;
bool loop_okay = loop_forever && vgmstream->loop_flag && !ignore_loop && !force_ignore_loop;
int corrected_pos_samples = seek_pos_samples;
// adjust for correct position within loop
if(vgmstream->loop_flag && (vgmstream->loop_end_sample - vgmstream->loop_start_sample) && seek_pos_samples >= vgmstream->loop_end_sample) {
corrected_pos_samples -= vgmstream->loop_start_sample;
corrected_pos_samples %= (vgmstream->loop_end_sample - vgmstream->loop_start_sample);
corrected_pos_samples += vgmstream->loop_start_sample;
}
// Allow for delta seeks forward, by up to the total length of the stream, if the delta is less than the corrected offset
if(decode_pos_samples > corrected_pos_samples && decode_pos_samples <= seek_pos_samples &&
(seek_pos_samples - decode_pos_samples) < stream_length_samples) {
if (corrected_pos_samples > (seek_pos_samples - decode_pos_samples))
corrected_pos_samples = seek_pos_samples;
}
// Reset of backwards seek
else if(corrected_pos_samples < decode_pos_samples) {
reset_vgmstream(vgmstream);
if (ignore_loop) vgmstream->loop_flag = 0;
decode_pos_samples = 0;
}
// seeking overrun = bad
if(corrected_pos_samples > stream_length_samples) corrected_pos_samples = stream_length_samples;
while(decode_pos_samples<corrected_pos_samples) {
int seek_samples = max_buffer_samples;
if((decode_pos_samples+max_buffer_samples>=stream_length_samples) && !loop_okay)
seek_samples=stream_length_samples-seek_pos_samples;
if(decode_pos_samples+max_buffer_samples>seek_pos_samples)
seek_samples=seek_pos_samples-decode_pos_samples;
decode_pos_samples+=seek_samples;
render_vgmstream(sample_buffer,seek_samples,vgmstream);
}
// remove seek loop correction from counter so file ends correctly
decode_pos_samples=seek_pos_samples;
decode_pos_ms=decode_pos_samples*1000LL/vgmstream->sample_rate;
decoding = loop_okay || decode_pos_samples < stream_length_samples;
}
input_vgmstream::input_vgmstream() {
vgmstream = NULL;
paused = 0;
decode_pos_ms = 0;
decode_pos_samples = 0;
stream_length_samples = 0;
fade_samples = 0;
fade_seconds = 10.0f;
fade_delay_seconds = 0.0f;
loop_count = 2.0f;
loop_forever = false;
ignore_loop = 0;
decoding = false;
seek_pos_samples = 0;
load_settings();
vgmstream = NULL;
subsong = 1;
decoding = false;
paused = 0;
decode_pos_ms = 0;
decode_pos_samples = 0;
stream_length_samples = 0;
fade_samples = 0;
seek_pos_samples = 0;
fade_seconds = 10.0f;
fade_delay_seconds = 0.0f;
loop_count = 2.0f;
loop_forever = false;
ignore_loop = 0;
disable_subsongs = true;
load_settings();
}
input_vgmstream::~input_vgmstream() {
if(vgmstream)
close_vgmstream(vgmstream);
vgmstream=NULL;
close_vgmstream(vgmstream);
vgmstream = NULL;
}
void input_vgmstream::open(service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
if (!p_path) { // shouldn't be possible
throw exception_io_data();
return;
}
filename = p_path;
/* KLUDGE */
if ( !pfc::stricmp_ascii( pfc::string_extension(filename), "MUS" ) )
{
unsigned char buffer[ 4 ];
if ( p_filehint.is_empty() ) input_open_file_helper( p_filehint, filename, p_reason, p_abort );
p_filehint->read_object_t( buffer, p_abort );
if ( !memcmp( buffer, "MUS\x1A", 4 ) ) throw exception_io_unsupported_format();
}
// keep file stats around (timestamp, filesize)
if ( p_filehint.is_empty() )
input_open_file_helper( p_filehint, filename, p_reason, p_abort );
stats = p_filehint->get_stats( p_abort );
switch(p_reason) {
case input_open_decode: // prepare to retrieve info and decode
case input_open_info_read: // prepare to retrieve info
setup_vgmstream(p_abort); // must init vgmstream to get subsongs
break;
case input_open_info_write: // prepare to retrieve info and tag
throw exception_io_data();
break;
default: // nothing else should be possible
throw exception_io_data();
break;
}
}
unsigned input_vgmstream::get_subsong_count() {
// if the plugin uses input_factory_t template and returns > 1 here when adding a song to the playlist,
// foobar will automagically "unpack" it by calling decode_initialize/get_info with all subsong indexes.
// There is no need to add any playlist code, only properly handle the subsong index.
if (disable_subsongs)
return 1;
// vgmstream ready as method is valid after open() with any reason
int subsong_count = vgmstream->num_streams;
if (subsong_count == 0)
subsong_count = 1; // most formats don't have subsongs
return subsong_count;
}
t_uint32 input_vgmstream::get_subsong(unsigned p_index) {
return p_index + 1; // translates index (0..N < subsong_count) for vgmstream: 1=first
}
void input_vgmstream::get_info(t_uint32 p_subsong, file_info & p_info, abort_callback & p_abort) {
int length_in_ms=0, channels = 0, samplerate = 0;
int total_samples = -1;
int bitrate = 0;
int loop_start = -1, loop_end = -1;
pfc::string8 description;
pfc::string8_fast temp;
get_subsong_info(p_subsong, NULL, &length_in_ms, &total_samples, &loop_start, &loop_end, &samplerate, &channels, &bitrate, description, p_abort);
p_info.info_set_int("samplerate", samplerate);
p_info.info_set_int("channels", channels);
p_info.info_set_int("bitspersample",16);
p_info.info_set("encoding","lossless");
p_info.info_set_bitrate(bitrate / 1000);
if (total_samples > 0)
p_info.info_set_int("stream_total_samples", total_samples);
if (loop_start >= 0 && loop_end >= loop_start) {
p_info.info_set_int("loop_start", loop_start);
p_info.info_set_int("loop_end", loop_end);
}
p_info.set_length(((double)length_in_ms)/1000);
if (get_description_tag(temp,description,"encoding: ")) p_info.info_set("codec",temp);
if (get_description_tag(temp,description,"layout: ")) p_info.info_set("layout",temp);
if (get_description_tag(temp,description,"interleave: ",' ')) p_info.info_set("interleave",temp);
if (get_description_tag(temp,description,"last block interleave:",' ')) p_info.info_set("interleave_last_block",temp);
if (get_description_tag(temp,description,"block size: ")) p_info.info_set("block_size",temp);
if (get_description_tag(temp,description,"metadata from: ")) p_info.info_set("metadata_source",temp);
if (get_description_tag(temp,description,"stream number: ")) p_info.info_set("stream_number",temp);
if (get_description_tag(temp,description,"stream index: ")) p_info.info_set("stream_index",temp);
if (get_description_tag(temp,description,"stream name: ")) p_info.info_set("stream_name",temp);
}
t_filestats input_vgmstream::get_file_stats(abort_callback & p_abort) {
return stats;
return stats;
}
void input_vgmstream::decode_initialize(t_uint32 p_subsong, unsigned p_flags, abort_callback & p_abort) {
force_ignore_loop = !!(p_flags & input_flag_no_looping);
// if subsong changes recreate vgmstream (subsong is only used on init)
if (subsong != p_subsong) {
subsong = p_subsong;
setup_vgmstream(p_abort);
}
decode_seek( 0, p_abort );
};
bool input_vgmstream::decode_run(audio_chunk & p_chunk,abort_callback & p_abort) {
if (!decoding) return false;
int max_buffer_samples = sizeof(sample_buffer)/sizeof(sample_buffer[0])/vgmstream->channels;
int l = 0, samples_to_do = max_buffer_samples;
if(vgmstream) {
bool loop_okay = loop_forever && vgmstream->loop_flag && !ignore_loop && !force_ignore_loop;
if (decode_pos_samples+max_buffer_samples>stream_length_samples && !loop_okay)
samples_to_do=stream_length_samples-decode_pos_samples;
else
samples_to_do=max_buffer_samples;
l = (samples_to_do*vgmstream->channels * sizeof(sample_buffer[0]));
if (samples_to_do /*< DECODE_SIZE*/ == 0) {
decoding = false;
return false;
}
render_vgmstream(sample_buffer,samples_to_do,vgmstream);
/* fade! */
if (vgmstream->loop_flag && fade_samples > 0 && !loop_okay) {
int samples_into_fade = decode_pos_samples - (stream_length_samples - fade_samples);
if (samples_into_fade + samples_to_do > 0) {
int j,k;
for (j=0;j<samples_to_do;j++,samples_into_fade++) {
if (samples_into_fade > 0) {
double fadedness = (double)(fade_samples-samples_into_fade)/fade_samples;
for (k=0;k<vgmstream->channels;k++) {
sample_buffer[j*vgmstream->channels+k] =
(short)(sample_buffer[j*vgmstream->channels+k]*fadedness);
}
}
}
}
}
p_chunk.set_data_fixedpoint((char*)sample_buffer, l, vgmstream->sample_rate, vgmstream->channels, 16, audio_chunk::g_guess_channel_config(vgmstream->channels));
decode_pos_samples+=samples_to_do;
decode_pos_ms=decode_pos_samples*1000LL/vgmstream->sample_rate;
return samples_to_do==max_buffer_samples;
}
return false;
}
void input_vgmstream::decode_seek(double p_seconds,abort_callback & p_abort) {
seek_pos_samples = (int) audio_math::time_to_samples(p_seconds, vgmstream->sample_rate);
int max_buffer_samples = sizeof(sample_buffer)/sizeof(sample_buffer[0])/vgmstream->channels;
bool loop_okay = loop_forever && vgmstream->loop_flag && !ignore_loop && !force_ignore_loop;
int corrected_pos_samples = seek_pos_samples;
// adjust for correct position within loop
if(vgmstream->loop_flag && (vgmstream->loop_end_sample - vgmstream->loop_start_sample) && seek_pos_samples >= vgmstream->loop_end_sample) {
corrected_pos_samples -= vgmstream->loop_start_sample;
corrected_pos_samples %= (vgmstream->loop_end_sample - vgmstream->loop_start_sample);
corrected_pos_samples += vgmstream->loop_start_sample;
}
// Allow for delta seeks forward, by up to the total length of the stream, if the delta is less than the corrected offset
if(decode_pos_samples > corrected_pos_samples && decode_pos_samples <= seek_pos_samples &&
(seek_pos_samples - decode_pos_samples) < stream_length_samples) {
if (corrected_pos_samples > (seek_pos_samples - decode_pos_samples))
corrected_pos_samples = seek_pos_samples;
}
// Reset of backwards seek
else if(corrected_pos_samples < decode_pos_samples) {
reset_vgmstream(vgmstream);
if (ignore_loop) vgmstream->loop_flag = 0;
decode_pos_samples = 0;
}
// seeking overrun = bad
if(corrected_pos_samples > stream_length_samples) corrected_pos_samples = stream_length_samples;
while(decode_pos_samples<corrected_pos_samples) {
int seek_samples = max_buffer_samples;
if((decode_pos_samples+max_buffer_samples>=stream_length_samples) && !loop_okay)
seek_samples=stream_length_samples-seek_pos_samples;
if(decode_pos_samples+max_buffer_samples>seek_pos_samples)
seek_samples=seek_pos_samples-decode_pos_samples;
decode_pos_samples+=seek_samples;
render_vgmstream(sample_buffer,seek_samples,vgmstream);
}
// remove seek loop correction from counter so file ends correctly
decode_pos_samples=seek_pos_samples;
decode_pos_ms=decode_pos_samples*1000LL/vgmstream->sample_rate;
decoding = loop_okay || decode_pos_samples < stream_length_samples;
}
bool input_vgmstream::decode_can_seek() {return true;}
bool input_vgmstream::decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {
//do we need anything else 'cept the samplrate
return true;
}
bool input_vgmstream::decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) { return true; }
bool input_vgmstream::decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) {return false;}
void input_vgmstream::decode_on_idle(abort_callback & p_abort) {/*m_file->on_idle(p_abort);*/}
void input_vgmstream::retag(const file_info & p_info,abort_callback & p_abort) {};
void input_vgmstream::retag_set_info(t_uint32 p_subsong, const file_info & p_info, abort_callback & p_abort) { /*throw exception_io_data();*/ }
void input_vgmstream::retag_commit(abort_callback & p_abort) { /*throw exception_io_data();*/ }
bool input_vgmstream::g_is_our_content_type(const char * p_content_type) {return false;}
bool input_vgmstream::g_is_our_path(const char * p_path,const char * p_extension) {
@ -375,47 +305,103 @@ bool input_vgmstream::g_is_our_path(const char * p_path,const char * p_extension
}
VGMSTREAM * input_vgmstream::init_vgmstream_foo(t_uint32 p_subsong, const char * const filename, abort_callback & p_abort) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *streamFile = open_foo_streamfile(filename, &p_abort, &stats);
if (streamFile) {
streamFile->stream_index = p_subsong;
vgmstream = init_vgmstream_from_STREAMFILE(streamFile);
close_streamfile(streamFile);
}
return vgmstream;
}
void input_vgmstream::setup_vgmstream(abort_callback & p_abort) {
// close first in case of changing subsongs
if (vgmstream) {
close_vgmstream(vgmstream);
}
// subsong and filename are always defined before this
vgmstream = init_vgmstream_foo(subsong, filename, p_abort);
if (!vgmstream) {
throw exception_io_data();
return;
}
if (ignore_loop)
vgmstream->loop_flag = 0;
decode_pos_ms = 0;
decode_pos_samples = 0;
paused = 0;
stream_length_samples = get_vgmstream_play_samples(loop_count,fade_seconds,fade_delay_seconds,vgmstream);
fade_samples = (int)(fade_seconds * vgmstream->sample_rate);
}
void input_vgmstream::get_subsong_info(t_uint32 p_subsong, char *title, int *length_in_ms, int *total_samples, int *loop_start, int *loop_end, int *sample_rate, int *channels, int *bitrate, pfc::string_base & description, abort_callback & p_abort) {
VGMSTREAM * infostream = NULL;
// reuse current vgmstream if not querying a new subsong
if (subsong != p_subsong) {
infostream = init_vgmstream_foo(p_subsong, filename, p_abort);
} else {
// vgmstream ready as get_info is valid after open() with any reason
infostream = vgmstream;
}
/* retrieve information on this or possibly another file */
void input_vgmstream::getfileinfo(const char *filename, char *title, int *length_in_ms, int *total_samples, int *loop_start, int *loop_end, int *sample_rate, int *channels, int *bitrate, pfc::string_base & description, abort_callback & p_abort) {
VGMSTREAM * infostream;
if (length_in_ms)
{
*length_in_ms=-1000;
if ((infostream=init_vgmstream_foo(filename, p_abort)))
{
*length_in_ms = get_vgmstream_play_samples(loop_count,fade_seconds,fade_delay_seconds,infostream)*1000LL/infostream->sample_rate;
test_length = *length_in_ms;
*sample_rate = infostream->sample_rate;
*channels = infostream->channels;
*total_samples = infostream->num_samples;
*bitrate = get_vgmstream_average_bitrate(infostream);
if (infostream->loop_flag)
{
*loop_start = infostream->loop_start_sample;
*loop_end = infostream->loop_end_sample;
}
if (length_in_ms) {
*length_in_ms = -1000;
if (infostream) {
*length_in_ms = get_vgmstream_play_samples(loop_count,fade_seconds,fade_delay_seconds,infostream)*1000LL/infostream->sample_rate;
*sample_rate = infostream->sample_rate;
*channels = infostream->channels;
*total_samples = infostream->num_samples;
*bitrate = get_vgmstream_average_bitrate(infostream);
if (infostream->loop_flag) {
*loop_start = infostream->loop_start_sample;
*loop_end = infostream->loop_end_sample;
}
char temp[1024];
describe_vgmstream(infostream, temp, 1024);
description = temp;
char temp[1024];
describe_vgmstream(infostream, temp, 1024);
description = temp;
}
}
close_vgmstream(infostream);
infostream=NULL;
}
}
if (title)
{
const char *p=filename+strlen(filename);
while (*p != '\\' && p >= filename) p--;
strcpy(title,++p);
}
if (title) {
const char *p=filename+strlen(filename);
while (*p != '\\' && p >= filename) p--;
strcpy(title,++p);
}
// and only close if was querying a new subsong
if (subsong != p_subsong) {
close_vgmstream(infostream);
infostream = NULL;
}
}
bool input_vgmstream::get_description_tag(pfc::string_base & temp, pfc::string_base & description, const char *tag, char delimiter) {
// extract a "tag" from the description string
t_size pos = description.find_first(tag);
t_size eos;
if (pos != pfc::infinite_size) {
pos += strlen(tag);
eos = description.find_first(delimiter, pos);
if (eos == pfc::infinite_size) eos = description.length();
temp.set_string(description + pos, eos - pos);
return true;
}
return false;
}
static input_singletrack_factory_t<input_vgmstream> g_input_vgmstream_factory;
/* foobar plugin defs */
static input_factory_t<input_vgmstream> g_input_vgmstream_factory;
DECLARE_COMPONENT_VERSION(APP_NAME,PLUGIN_VERSION,PLUGIN_DESCRIPTION);
VALIDATE_COMPONENT_FILENAME("foo_input_vgmstream.dll");

View File

@ -1,78 +1,71 @@
#ifndef _FOO_VGMSTREAM_
#define _FOO_VGMSTREAM_
#define OUTBUF_SIZE 1024 /* Samples */
typedef struct _FOO_STREAMFILE {
struct _STREAMFILE sf;
abort_callback * p_abort;
service_ptr_t<file> m_file;
char * name;
off_t offset;
size_t validsize;
uint8_t * buffer;
size_t buffersize;
size_t filesize;
} FOO_STREAMFILE;
#define OUTBUF_SIZE 1024 /* Samples */
class input_vgmstream {
public:
input_vgmstream();
~input_vgmstream();
public:
input_vgmstream();
~input_vgmstream();
VGMSTREAM * init_vgmstream_foo(const char * const filename, abort_callback & p_abort);
void decode_seek(double p_seconds,abort_callback & p_abort);
bool decode_run(audio_chunk & p_chunk,abort_callback & p_abort);
void decode_initialize(unsigned p_flags,abort_callback & p_abort);
void get_info(file_info & p_info,abort_callback & p_abort);
void open(service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort);
void open(service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort);
static bool g_is_our_path(const char * p_path,const char * p_extension);
static bool g_is_our_content_type(const char * p_content_type);
unsigned get_subsong_count();
t_uint32 get_subsong(unsigned p_index);
void get_info(t_uint32 p_subsong,file_info & p_info,abort_callback & p_abort);
t_filestats get_file_stats(abort_callback & p_abort);
bool decode_can_seek();
bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta);
bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta);
void decode_on_idle(abort_callback & p_abort);
void retag(const file_info & p_info,abort_callback & p_abort);
t_filestats get_file_stats(abort_callback & p_abort);
void decode_initialize(t_uint32 p_subsong,unsigned p_flags,abort_callback & p_abort);
bool decode_run(audio_chunk & p_chunk,abort_callback & p_abort);
void decode_seek(double p_seconds,abort_callback & p_abort);
bool decode_can_seek();
bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta);
bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta);
void decode_on_idle(abort_callback & p_abort);
void retag_set_info(t_uint32 p_subsong,const file_info & p_info,abort_callback & p_abort);
void retag_commit(abort_callback & p_abort);
private:
service_ptr_t<file> m_file;
pfc::string8 filename;
t_input_open_reason currentreason;
VGMSTREAM * vgmstream;
static bool g_is_our_content_type(const char * p_content_type);
static bool g_is_our_path(const char * p_path,const char * p_extension);
bool decoding;
int paused;
int decode_pos_ms;
int decode_pos_samples;
int stream_length_samples;
int fade_samples;
int test_length;
private:
//service_ptr_t<file> m_file;
pfc::string8 filename;
t_filestats stats;
double fade_seconds;
double fade_delay_seconds;
double loop_count;
bool loop_forever;
bool force_ignore_loop;
int ignore_loop;
int seek_pos_samples;
/* state */
VGMSTREAM * vgmstream;
t_uint32 subsong;
t_filestats stats;
bool decoding;
int paused;
int decode_pos_ms;
int decode_pos_samples;
int stream_length_samples;
int fade_samples;
int seek_pos_samples;
short sample_buffer[OUTBUF_SIZE];
short sample_buffer[OUTBUF_SIZE];
void getfileinfo(const char *filename, char *title, int *length_in_ms, int *total_samples, int *loop_start, int *loop_end, int *sample_rate, int *channels, int *bitrate, pfc::string_base & description, abort_callback & p_abort);
void load_settings();
private:
/* config */
double fade_seconds;
double fade_delay_seconds;
double loop_count;
bool loop_forever;
bool force_ignore_loop;
int ignore_loop;
bool disable_subsongs;
/* helpers */
VGMSTREAM * init_vgmstream_foo(t_uint32 p_subsong, const char * const filename, abort_callback & p_abort);
void setup_vgmstream(abort_callback & p_abort);
void load_settings();
void get_subsong_info(t_uint32 p_subsong, char *title, int *length_in_ms, int *total_samples, int *loop_start, int *loop_end, int *sample_rate, int *channels, int *bitrate, pfc::string_base & description, abort_callback & p_abort);
bool get_description_tag(pfc::string_base & temp, pfc::string_base & description, const char *tag, char delimiter = '\n');
};
STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_file,const char * const filename, size_t buffersize, abort_callback * p_abort);
STREAMFILE * open_foo_streamfile_buffer(const char * const filename, size_t buffersize, abort_callback * p_abort, t_filestats * stats);
/* foo_streamfile.cpp */
STREAMFILE * open_foo_streamfile(const char * const filename, abort_callback * p_abort, t_filestats * stats);
#endif /*_FOO_VGMSTREAM_*/

View File

@ -14,13 +14,13 @@ Since Ogg Vorbis, MPEG audio, and other formats are now supported, you will
need to have certain DLL files.
In the case of the foobar2000 component they are all bundled for convenience,
or you can get them from here: https://github.com/kode54/vgmstream
(also here: https://f.losno.co/vgmstream-win32-deps.zip, may not be latest).
(also here: https://f.losno.co/vgmstream-win32-deps.zip, may not be latest).
Put libvorbis.dll, libmpg123-0.dll, libg7221_decode.dll, libg719_decode.dll,
at3plusdecoder.dll, avcodec-vgmstream-57.dll, avformat-vgmstream-57.dll, and
avutil-vgmstream-55.dll somewhere Windows can find them.
For Winamp/XMPlay/test.exe this means in the directory with the .exe, or in a
system directory, or other directory in the PATH variable.
system directory, or any other directory in the PATH variable.
--- test.exe ---
Usage: ./test [-o outfile.wav] [-l loop count]
@ -44,6 +44,7 @@ Options:
-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
-s N: select subtream N, if the format supports multiple streams
Typical usage would be:
test -o happy.wav happy.adx
@ -57,13 +58,18 @@ the above instructions for installing the other files needed.
Drop the xmp-vgmstream.dll in your XMPlay plugins directory. Please follow
the above instructions for installing the other files needed.
Note that there are minor differences compared to in_vgmstream. Since XMPlay
supports Winamp plugins you may also use in_vgmstream.dll instead.
Because the XMPlay MP3 decoder incorrectly tries to play some vgmstream exts,
you need to manually fix it by going to options > plugins > input > vgmstream
and in the "priority filetypes" put: ckd,fsb,genh,msf,rak,scd,xvag
and in the "priority filetypes" put: ckd,fsb,genh,msf,p3d,rak,scd,xvag
--- foo_input_vgmstream ---
Every should be installed automatically by the .fb2k-component bundle.
--- Audacious plugin ---
Needs to be manually built. Instructions can be found in the source files.
--- File types supported by this version of vgmstream ---
@ -136,7 +142,6 @@ PS2/PSX ADPCM:
GC/Wii/3DS DSP ADPCM:
- .aaap
- .agsc
- .amts
- .asr
- .bns
- .bo2
@ -227,37 +232,44 @@ IMA ADPCM:
- .idvi (DVI IMA ADPCM)
- .ivaud (IMA ADPCM)
- .myspd (IMA ADPCM)
- .stma (DVI IMA ADPCM)
- .strm (IMA ADPCM)
multi:
- .aifc (SDX2 DPCM, DVI IMA ADPCM)
- .asf, .as4 (8/16 bit PCM, EACS IMA ADPCM)
- .asf/as4 (8/16 bit PCM, EACS IMA ADPCM)
- .ast (GC AFC ADPCM, 16 bit PCM)
- .aud (IMA ADPCM, WS DPCM)
- .aus (PSX ADPCM, Xbox IMA ADPCM)
- .brstm (GC DSP ADPCM, 8/16 bit PCM)
- .emff (PSX APDCM, GC DSP ADPCM)
- .fsb, .wii (PSX ADPCM, GC DSP ADPCM, Xbox IMA ADPCM)
- .fsb/wii (PSX ADPCM, GC DSP ADPCM, Xbox IMA ADPCM, MPEG audio, FSB Vorbis,
MS XMA)
- .genh (lots)
- .txth (lots)
- .msf (PCM, PSX ADPCM, ATRAC3, MP3)
- .musx (PSX ADPCM, Xbox IMA ADPCM, DAT4 IMA ADPCM)
- .nwa (16 bit PCM, NWA DPCM)
- .p3d (Radical ADPCM, Radical MP3, XMA2)
- .psw (PSX ADPCM, GC DSP ADPCM)
- .rwar, .rwav (GC DSP ADPCM, 8/16 bit PCM)
- .rws (PSX ADPCM, XBOX IMA ADPCM, GC DSP ADPCM, 16 bit PCM)
- .rwsd (GC DSP ADPCM, 8/16 bit PCM)
- .rsd (PSX ADPCM, 16 bit PCM, GC DSP ADPCM, Xbox IMA ADPCM, Radical ADPCM)
- .rrds (NDS IMA ADPCM)
- .sad (GC DSP ADPCM, NDS IMA ADPCM, Procyon Studios NDS ADPCM)
- .sgd/sgb/sgx (PSX ADPCM, ATRAC3plus, AC3)
- .sgd/sgb+sgh/sgx (PSX ADPCM, ATRAC3plus, AC3)
- .seg (Xbox IMA ADPCM, PS2 ADPCM)
- .sng, .asf, .str, .eam (EA/XA ADPCM or PSX ADPCM)
- .sng/asf/str/eam/aud (8/16 bit PCM, EA-XA ADPCM, PSX ADPCM, GC DSP ADPCM,
XBOX IMA ADPCM, MPEG audio, EALayer3)
- .strm (NDS IMA ADPCM, 8/16 bit PCM)
- .ss7 (EACS IMA ADPCM, IMA ADPCM)
- .swav (NDS IMA ADPCM, 8/16 bit PCM)
- .xwb (PCM, Xbox IMA ADPCM, MS ADPCM, XMA, XWMA, ATRAC3)
- .wav, .lwav (unsigned 8 bit PCM, 16 bit PCM, GC DSP ADPCM, MS IMA ADPCM)
- .wem (PCM, Wwise Vorbis, Wwise ADPCM, XMA, XWMA, GC DSP ADPCM)
- .xwb+xwh (PCM, PSX ADPCM, ATRAC3)
- .wav/lwav (unsigned 8 bit PCM, 16 bit PCM, GC DSP ADPCM, MS IMA ADPCM,
XBOX IMA ADPCM)
- .wem [lwav/logg/xma] (PCM, Wwise Vorbis, Wwise IMA ADPCM, XMA, XWMA,
GC DSP ADPCM)
etc:
- .2dx9 (MS ADPCM)
@ -269,34 +281,38 @@ etc:
- .ahx (MPEG-2 Layer II)
- .aix (CRI ADX ADPCM)
- .at3 (Sony ATRAC3 / ATRAC3plus)
- .baf (Blur ADPCM)
- .bgw (FFXI PS-like ADPCM)
- .aud (Silicon Knights Vorbis)
- .baf (PSX configurable ADPCM)
- .bgw (PSX configurable ADPCM)
- .bnsf (G.722.1)
- .caf (Apple IMA4 ADPCM)
- .caf (Apple IMA4 ADPCM, others)
- .de2 (MS ADPCM)
- .hca (CRI)
- .hca (CRI High Compression Audio)
- .kcey (EACS IMA ADPCM)
- .lsf (LSF ADPCM)
- .mc3 (Paradigm MC3 ADPCM)
- .mwv (Level-5 0x555 ADPCM)
- .mp4/lmp4 (AAC)
- .msf (PCM, PSX ADPCM, ATRAC3, MP3)
- .mtaf (Konami ADPCM)
- .ogg, .logg (Ogg Vorbis)
- .mta2 (Konami XAS-like ADPCM)
- .mwv (Level-5 0x555 ADPCM)
- .ogg/logg (Ogg Vorbis)
- .ogl (Shin'en Vorbis)
- .p3d (Radical ADPCM)
- .rsf (CCITT G.721 ADPCM)
- .sab (Worms 4 soundpacks)
- .s14/.sss (G.722.1)
- .s14/sss (G.722.1)
- .sc (Activision EXAKT SASSC DPCM)
- .scd (MS ADPCM, MPEG Audio, 16 bit PCM)
- .sd9 (MS ADPCM)
- .smp (MS ADPCM)
- .spw (FFXI PS-like ADPCM)
- .stm renamed .ps2stm (DVI IMA ADPCM)
- .spw (PSX configurable ADPCM)
- .stm/lstm [amts/ps2stm/stma] (16 bit PCM, DVI IMA ADPCM, GC DSP ADPCM)
- .str (SDX2 DPCM)
- .stx (GC AFC ADPCM)
- .ulw (u-Law PCM)
- .um3 (Ogg Vorbis)
- .xa (CD-ROM XA audio)
- .xma (MS WMA Pro)
- .xma (MS XMA/XMA2)
loop assists:
- .mus (playlist for .acm)
@ -306,6 +322,7 @@ loop assists:
other:
- .adxkey (decryption key for .adx, in start/mult/add format)
- .ahxkey (decryption key for .ahx, in start/mult/add format)
- .hcakey (decryption key for .hca, in HCA Decoder format)
- .vgmstream + .pos (FFmpeg formats + loop assist)

View File

@ -42,6 +42,9 @@ int32_t dsp_nibbles_to_samples(int32_t nibbles);
void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing);
void dsp_read_coefs_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing);
void dsp_read_coefs(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be);
void dsp_read_hist_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing);
void dsp_read_hist_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing);
void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be);
/* ngc_dtk_decoder */
void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);

View File

@ -100,9 +100,7 @@ size_t dsp_bytes_to_samples(size_t bytes, int channels) {
}
/**
* reads DSP coefs built in the streamfile
*/
/* reads DSP coefs built in the streamfile */
void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
dsp_read_coefs(vgmstream, streamFile, offset, spacing, 1);
}
@ -121,3 +119,23 @@ void dsp_read_coefs(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset,
}
}
/* reads DSP initial hist built in the streamfile */
void dsp_read_hist_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
dsp_read_hist(vgmstream, streamFile, offset, spacing, 1);
}
void dsp_read_hist_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) {
dsp_read_hist(vgmstream, streamFile, offset, spacing, 0);
}
void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be) {
int ch;
/* get ADPCM hist */
for (ch=0; ch < vgmstream->channels; ch++) {
vgmstream->ch[ch].adpcm_history1_16 = be ?
read_16bitBE(offset + ch*spacing + 0*2, streamFile) :
read_16bitLE(offset + ch*spacing + 0*2, streamFile);;
vgmstream->ch[ch].adpcm_history2_16 = be ?
read_16bitBE(offset + ch*spacing + 1*2, streamFile) :
read_16bitLE(offset + ch*spacing + 1*2, streamFile);;
}
}

View File

@ -32,7 +32,7 @@ static const char* extension_list[] = {
//"aiff", //common
"aix",
"akb",
"amts",
"amts", //fake extension (to be removed)
"as4",
"asd",
"asf",
@ -147,14 +147,15 @@ static const char* extension_list[] = {
"kovs",
"kraw",
"laac", //fake extension, for tri-Ace/FFmpeg
"laac", //fake extension, for AAC (tri-Ace/FFmpeg)
"leg",
"lmp4", //fake extension, for looping
"logg", //fake extension, for looping
"lmp4", //fake extension, for MP4s
"logg", //fake extension, for OGGs
"lpcm",
"lps",
"lsf",
"lwav", //fake extension, for looping
"lstm", //fake extension, for STMs
"lwav", //fake extension, for WAVs
"matx",
"mc3",
@ -187,7 +188,7 @@ static const char* extension_list[] = {
"ndp",
"ngca",
"nps",
"npsf",
"npsf", //fake extension (to be removed)
"nus3bank",
"nwa",
@ -206,7 +207,7 @@ static const char* extension_list[] = {
"pnb",
"pona",
"pos",
"ps2stm",
"ps2stm", //fake extension (to be removed)
"psh",
"psnd",
"psw",
@ -266,7 +267,7 @@ static const char* extension_list[] = {
"ster",
"sth",
//"stm", //common
"stma",
"stma", //fake extension (to be removed)
"str",
"strm",
"sts",
@ -660,7 +661,6 @@ static const meta_info meta_info_list[] = {
{meta_PS2_TEC, "assumed TECMO badflagged stream by .tec extension"},
{meta_XBOX_WVS, "Metal Arms WVS Header (XBOX)"},
{meta_NGC_WVS, "Metal Arms WVS Header (GameCube)"},
{meta_XBOX_STMA, "Midnight Club 2 STMA Header"},
{meta_XBOX_MATX, "assumed Matrix file by .matx extension"},
{meta_DE2, "gurumin .de2 with embedded funky RIFF"},
{meta_VS, "Men in Black VS Header"},
@ -762,7 +762,6 @@ static const meta_info meta_info_list[] = {
{meta_WII_WAS, "WAS (iSWS) DSP header"},
{meta_XBOX_HLWAV, "Half Life 2 bgm header"},
{meta_STX, "Nintendo .stx header"},
{meta_PS2_STM, "Red Dead Revolver .stm (.ps2stm)"},
{meta_MYSPD, "U-Sing .myspd header"},
{meta_HIS, "Her Interactive Sound header"},
{meta_PS2_AST, "KOEI AST header"},
@ -874,6 +873,7 @@ static const meta_info meta_info_list[] = {
{meta_EA_BNK, "Electronic Arts BNK header"},
{meta_SK_AUD, "Silicon Knights AUD header"},
{meta_AHX, "CRI AHX header"},
{meta_STM, "Angel Studios/Rockstar San Diego STMA header"},
#ifdef VGM_USE_VORBIS
{meta_OGG_VORBIS, "Ogg Vorbis"},

View File

@ -878,10 +878,6 @@
RelativePath=".\meta\ps2_ster.c"
>
</File>
<File
RelativePath=".\meta\ps2_stm.c"
>
</File>
<File
RelativePath=".\meta\ps2_str.c"
>
@ -1102,6 +1098,10 @@
RelativePath=".\meta\ss_stream.c"
>
</File>
<File
RelativePath=".\meta\stm.c"
>
</File>
<File
RelativePath=".\meta\str_asr.c"
>
@ -1242,10 +1242,6 @@
RelativePath=".\meta\xbox_ims.c"
>
</File>
<File
RelativePath=".\meta\xbox_stma.c"
>
</File>
<File
RelativePath=".\meta\xbox_wavm.c"
>

View File

@ -328,7 +328,6 @@
<ClCompile Include="meta\ps2_snd.c" />
<ClCompile Include="meta\ps2_sps.c" />
<ClCompile Include="meta\ps2_ster.c" />
<ClCompile Include="meta\ps2_stm.c" />
<ClCompile Include="meta\ps2_str.c" />
<ClCompile Include="meta\ps2_svag.c" />
<ClCompile Include="meta\ps2_svag_snk.c" />
@ -374,6 +373,7 @@
<ClCompile Include="meta\sli.c" />
<ClCompile Include="meta\spt_spd.c" />
<ClCompile Include="meta\ss_stream.c" />
<ClCompile Include="meta\stm.c" />
<ClCompile Include="meta\str_asr.c" />
<ClCompile Include="meta\str_snds.c" />
<ClCompile Include="meta\stx.c" />
@ -400,7 +400,6 @@
<ClCompile Include="meta\xau.c" />
<ClCompile Include="meta\xbox_hlwav.c" />
<ClCompile Include="meta\xbox_ims.c" />
<ClCompile Include="meta\xbox_stma.c" />
<ClCompile Include="meta\xbox_wavm.c" />
<ClCompile Include="meta\xbox_xmu.c" />
<ClCompile Include="meta\xbox_xvas.c" />

View File

@ -526,9 +526,6 @@
<ClCompile Include="meta\ps2_ster.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_stm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_str.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -664,6 +661,9 @@
<ClCompile Include="meta\ss_stream.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\stm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\str_asr.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -748,9 +748,6 @@
<ClCompile Include="meta\xbox_ims.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\xbox_stma.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\xbox_wavm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

View File

@ -122,7 +122,7 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
int i, bnk_version;
int target_stream = 0, total_streams;
int total_streams, target_stream = streamFile->stream_index;
/* check extension */

View File

@ -92,7 +92,10 @@ typedef struct {
/* extra */
uint32_t hdrsize;
uint32_t shdrsize_min;
meta_t meta_type;
off_t name_offset;
size_t name_size;
} FSB_HEADER;
/* ********************************************************************************** */
@ -116,12 +119,12 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
off_t start_offset;
size_t custom_data_offset;
int loop_flag = 0;
int target_stream = 0;
int target_stream = streamFile->stream_index;
FSB_HEADER fsbh;
/* check extensions */
if ( !check_extensions(streamFile, "fsb,wii") )
/* check extensions (.bnk = Hard Corps Uprising PS3) */
if ( !check_extensions(streamFile, "fsb,wii,bnk") )
goto fail;
/* check header */
@ -144,7 +147,8 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
{
off_t s_off = offset+fsbh.hdrsize;
/* 0x00:name(len=0x20) */
fsbh.name_offset = s_off;
fsbh.name_size = 0x20;
fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile);
fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile);
fsbh.deffreq = read_32bitLE(s_off+0x28,streamFile);
@ -209,9 +213,11 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
off_t s_off = offset + fsbh.hdrsize;
off_t d_off = offset + fsbh.hdrsize + fsbh.shdrsize;
/* find target_stream data offset, reading each header */
for(i=1; i <= fsbh.numsamples; i++) {
/* 0x00:size 0x02:name(len=size) */
/* find target_stream header (variable sized) */
for(i = 0; i < fsbh.numsamples; i++) {
size_t stream_header_size = (uint16_t)read_16bitLE(s_off+0x00,streamFile);
fsbh.name_offset = s_off+0x02;
fsbh.name_size = 0x20-0x02;
fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile);
fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile);
fsbh.loopstart = read_32bitLE(s_off+0x28,streamFile);
@ -221,20 +227,19 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
/* 0x38:defvol 0x3a:defpan 0x3c:defpri */
fsbh.numchannels = read_16bitLE(s_off+0x3e,streamFile);
/* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsbh.varpan */
/* FSB3/4: 0x50:extended_data size_32bits (not always given) */
if (target_stream == i) /* d_off found */
if (i+1 == target_stream) /* d_off found */
break;
s_off += fsbh.shdrsize_min; /* default size */
if (fsbh.version == FMOD_FSB_VERSION_4_0) {
uint32_t extended_data = read_32bitLE(s_off+0x48,streamFile); /* +0x50:extended_data of size_32bits */
if (extended_data > fsbh.shdrsize_min)
s_off += extended_data;
}
s_off += stream_header_size;
d_off += fsbh.lengthcompressedbytes; /* there is no offset so manually count */
//d_off += d_off % 0x30; /*todo some formats need padding, not sure when/how */
/* IMAs streams have weird end padding (maybe: FSB3=no padding, FSB4=always padding) */
if ((fsbh.mode & FSOUND_IMAADPCM) && (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4)) {
if (d_off % 0x20)
d_off += 0x20 - (d_off % 0x20);
}
}
if (i > fsbh.numsamples) goto fail; /* not found */
@ -243,19 +248,13 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
}
}
#if 0
/* XOR encryption for some FSB4, though the flag is only seen after decrypting */
if (fsbh.flags & FMOD_FSB_SOURCE_ENCRYPTED) {
VGM_LOG("FSB ENCRYPTED found\n");
goto fail;
}
VGM_ASSERT(fsbh.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n");
/* sometimes there is garbage at the end or missing bytes due to improper demuxing */
if (fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize != streamFile->get_size(streamFile) - offset) {
VGM_LOG("FSB wrong head/datasize found\n");
goto fail;
}
#endif
VGM_ASSERT(fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize != streamFile->get_size(streamFile) - offset,
"FSB wrong head/datasize found\n");
/* Loops by default unless disabled (sometimes may add FSOUND_LOOP_NORMAL). Often streams
* repeat over and over (some tracks that shouldn't do this based on the flags, no real way to identify them). */
@ -273,6 +272,9 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
vgmstream->loop_end_sample = fsbh.loopend;
vgmstream->num_streams = fsbh.numsamples;
vgmstream->meta_type = fsbh.meta_type;
if (fsbh.name_offset)
read_string(vgmstream->stream_name,fsbh.name_size+1, fsbh.name_offset,streamFile);
/* parse format */
if (fsbh.mode & FSOUND_MPEG) {
@ -320,8 +322,8 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
vgmstream->interleave_block_size = 0x10;
}
else if (fsbh.mode & FSOUND_XMA) {
/* FSB4: Xbox360 Armored Core V, Hard Corps, Halo Anniversary */
#if defined(VGM_USE_FFMPEG)
/* FSB4: Xbox360 Armored Core V, Hard Corps, Halo Anniversary */
ffmpeg_codec_data *ffmpeg_data = NULL;
uint8_t buf[FAKE_RIFF_BUFFER_SIZE];
size_t bytes, block_size, block_count;
@ -368,7 +370,8 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x1;
} else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
}
else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
/* sometimes FSOUND_STEREO/FSOUND_MONO is not set (ex. Dead Space iOS),
* or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */
if (fsbh.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) {

View File

@ -5,17 +5,16 @@
/* FSB5 - FMOD Studio multiplatform format */
VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t StartOffset = 0;
off_t StartOffset = 0, NameOffset = 0;
off_t SampleHeaderStart = 0, DSPInfoStart = 0;
size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0;
uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0;
int LoopFlag = 0, ChannelCount = 0, SampleRate = 0, CodingID;
int TotalStreams, TargetStream = 0;
int TotalStreams, TargetStream = streamFile->stream_index;
uint32_t VorbisSetupId = 0;
int i;
/* check extension, case insensitive */
if (!check_extensions(streamFile,"fsb")) goto fail;
@ -163,16 +162,19 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
/* continue searching */
SampleHeaderStart += StreamHeaderLength;
}
/* target stream not found*/
if (!StartOffset || !StreamSize) goto fail;
/* get stream name */
if (NameTableLength) {
NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetStream-1),streamFile);
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ChannelCount,LoopFlag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->sample_rate = SampleRate;
vgmstream->num_streams = TotalStreams;
vgmstream->num_samples = NumSamples;
@ -181,7 +183,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = LoopEnd;
}
vgmstream->meta_type = meta_FSB5;
if (NameOffset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, NameOffset,streamFile);
/* parse codec */
switch (CodingID) {
case 0x00: /* FMOD_SOUND_FORMAT_NONE */
goto fail;
@ -306,7 +312,6 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,StartOffset))
goto fail;

View File

@ -9,7 +9,7 @@ static VGMSTREAM * init_vgmstream_kt_wiibgm_offset(STREAMFILE *streamFile, off_t
* It probably makes more sense to extract it externally, it's here mainly for Hyrule Warriors */
VGMSTREAM * init_vgmstream_kt_g1l(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int type, num_streams, target_stream = 1;
int type, num_streams, target_stream = streamFile->stream_index;
off_t stream_offset;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;

View File

@ -6,7 +6,7 @@ typedef enum { XMA2 } gtd_codec;
/* GTD - found in Knights Contract (X360, PS3), Valhalla Knights 3 (PSV) */
VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, chunk_offset;
off_t start_offset, chunk_offset, stpr_offset, name_offset = 0;
size_t data_size, chunk_size;
int loop_flag, channel_count, sample_rate;
int num_samples, loop_start_sample, loop_end_sample;
@ -32,14 +32,22 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
start_offset = read_32bitBE(0x58,streamFile); /* always 0x800 */
data_size = read_32bitBE(0x5c,streamFile);
/* 0x40(18): null, 0x60(4): header size (0x70), 0x64(4): seek table size again, 0x68(8): null */
/* 0x70: seek table; then a "STPR" chunk with the file ID and filename */
/* 0x34(18): null, 0x54(4): seek table offset, 0x58(4): seek table size, 0x5c(8): null, 0x64: seek table */
stpr_offset = read_32bitBE(chunk_offset+0x54,streamFile) + read_32bitBE(chunk_offset+0x58,streamFile);;
if (read_32bitBE(stpr_offset,streamFile) == 0x53545052) { /* "STPR" */
name_offset = stpr_offset + 0xB8; /* there are offsets fields but seems to work */
}
codec = XMA2;
}
else {
/* there are PSV (LE, ATRAC9) and PS3 (MSF inside?) variations, somewhat-but-not-quite similar
* (contain the "STPR" chunk but the rest is mostly different) */
/* there are PSV (LE, ATRAC9 data) and PS3 (MSF inside?) variations, somewhat-but-not-quite similar */
/* for PSV: */
/* 0x0c: data_size, 0x10: channles, 0x14: sample rate, 0x18-0x2c: fixed and unknown values */
/* 0x2c: STPR chunk, with name_offset at + 0xE8 */
goto fail;
}
@ -53,6 +61,8 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->meta_type = meta_GTD;
if (name_offset) //encoding is Shift-Jis in some PSV files
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
switch(codec) {
#ifdef VGM_USE_FFMPEG

View File

@ -88,10 +88,6 @@ VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_amts(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xbox_stma(STREAMFILE *streamFile);
#ifdef VGM_USE_VORBIS
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile);
@ -462,8 +458,6 @@ VGMSTREAM * init_vgmstream_xbox_hlwav(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_stx(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_stm(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_myspd(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_his(STREAMFILE* streamFile);
@ -686,4 +680,6 @@ VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_sk_aud(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_stm(STREAMFILE * streamFile);
#endif /*_META_H*/

View File

@ -920,122 +920,6 @@ fail:
return NULL;
}
/* AMTS - .amts files */
VGMSTREAM * init_vgmstream_amts(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
off_t interleave;
int channel_count;
struct dsp_header ch0_header,ch1_header;
int i;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("amts",filename_extension(filename))) goto fail;
/* check header magic */
if (read_32bitBE(0x0,streamFile) != 0x414D5453) goto fail; /* "sadb" */
channel_count=read_32bitBE(0x14,streamFile);
start_offset = 0x800;
interleave = read_32bitBE(0x08,streamFile);
if (read_dsp_header(&ch0_header, 0x20, streamFile)) goto fail;
/* check initial predictor/scale */
if (ch0_header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile))
goto fail;
if(channel_count==2) {
if (read_dsp_header(&ch1_header, 0x80, streamFile)) goto fail;
if (ch1_header.initial_ps != (uint8_t)read_8bit(start_offset+interleave,streamFile))
goto fail;
/* check type==0 and gain==0 */
if (ch0_header.format || ch0_header.gain ||
ch1_header.format || ch1_header.gain)
goto fail;
/* check for agreement */
if (
ch0_header.sample_count != ch1_header.sample_count ||
ch0_header.nibble_count != ch1_header.nibble_count ||
ch0_header.sample_rate != ch1_header.sample_rate ||
ch0_header.loop_flag != ch1_header.loop_flag ||
ch0_header.loop_start_offset != ch1_header.loop_start_offset ||
ch0_header.loop_end_offset != ch1_header.loop_end_offset
) goto fail;
if (ch0_header.loop_flag) {
off_t loop_off;
/* check loop predictor/scale */
loop_off = ch0_header.loop_start_offset/16*8;
loop_off = (loop_off/interleave*interleave*2) + (loop_off%interleave);
if (ch0_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile))
goto fail;
if (ch1_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off+interleave,streamFile))
goto fail;
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,ch0_header.loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = ch0_header.sample_count;
vgmstream->sample_rate = ch0_header.sample_rate;
/* TODO: adjust for interleave? */
vgmstream->loop_start_sample = dsp_nibbles_to_samples(
ch0_header.loop_start_offset);
vgmstream->loop_end_sample = dsp_nibbles_to_samples(
ch0_header.loop_end_offset)+1;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
vgmstream->meta_type = meta_DSP_AMTS;
/* coeffs */
for (i=0;i<16;i++) {
vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i];
vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i];
}
/* initial history */
/* always 0 that I've ever seen, but for completeness... */
vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1;
vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2;
vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if(channel_count==2) {
vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1;
vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2;
vgmstream->ch[1].streamfile = vgmstream->ch[0].streamfile;
}
if (!vgmstream->ch[0].streamfile) goto fail;
/* open the file for reading */
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+i*interleave;
}
return vgmstream;
fail:
/* clean up anything we may have opened */
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}
/* .wsi as found in Alone in the Dark for Wii */
/* These appear to be standard .dsp, but interleaved in a blocked format */

View File

@ -4,7 +4,7 @@
/* P3D - from Radical's Prototype 1/2 (PC/PS3/X360) */
VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, parse_offset;
off_t start_offset, parse_offset, name_offset = 0;
size_t header_size, file_size, data_size;
int loop_flag = 0, channel_count, sample_rate, codec;
int i, name_count, text_len, block_size = 0, block_count = 0, num_samples;
@ -49,9 +49,12 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
if (name_count != 2 && name_count != 3) goto fail; /* 2: Prototype1, 3: Prototype2 */
parse_offset += 4;
for (i = 0; i < 2; i++) { /* skip names */
text_len = read_32bit(parse_offset,streamFile);
parse_offset += 4 + text_len + 1;
/* skip names */
for (i = 0; i < 2; i++) {
if (!name_offset)
name_offset = parse_offset + 4;
text_len = read_32bit(parse_offset,streamFile) + 1; /* null-terminated */
parse_offset += 4 + text_len;
}
/* info count? */
@ -65,8 +68,8 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
/* extra "Music" string in Prototype 2 */
if (name_count == 3) {
text_len = read_32bit(parse_offset,streamFile);
parse_offset += 4 + text_len + 1;
text_len = read_32bit(parse_offset,streamFile) + 1; /* null-terminated */
parse_offset += 4 + text_len;
}
@ -129,6 +132,8 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->meta_type = meta_P3D;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
/* codec init */
switch(codec) {

View File

@ -1,77 +1,49 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* NPFS - found in Namco PS2/PSP games:
* Tekken 5, Ace Combat 5, Yumeria, Venus & Braves (.nps), Ridge Racer PSP, etc
*/
/* NPFS - found in Namco PS2/PSP games (Tekken 5, Ace Combat 5, Yumeria, Venus & Braves, Ridge Racer PSP) */
VGMSTREAM * init_vgmstream_ps2_npsf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag=0;
int channel_count;
off_t start_offset;
int i;
int loop_flag, channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("npsf",filename_extension(filename)) &&
strcasecmp("nps",filename_extension(filename)))
/* check extension, case insensitive (should be .nps as per Venus & Braves data files) */
if ( !check_extensions(streamFile,"nps,npsf"))
goto fail;
/* check NPSF Header */
if (read_32bitBE(0x00,streamFile) != 0x4E505346)
if (read_32bitBE(0x00,streamFile) != 0x4E505346) /* "NPSF" */
goto fail;
/* check loop */
loop_flag = (read_32bitLE(0x14,streamFile)!=0xFFFFFFFF);
channel_count=read_32bitLE(0x0C,streamFile);
loop_flag = (read_32bitLE(0x14,streamFile) != 0xFFFFFFFF);
channel_count = read_32bitLE(0x0C,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = read_32bitLE(0x0C,streamFile);
vgmstream->sample_rate = read_32bitLE(0x18,streamFile);
/* Check for Compression Scheme */
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitLE(0x08,streamFile)*28/16;
/* Get loop point values */
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x08,streamFile), 1); /* single channel data */
if(vgmstream->loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile);
vgmstream->loop_end_sample = read_32bitLE(0x08,streamFile)*28/16;
vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(0x08,streamFile), 1);
}
vgmstream->interleave_block_size = read_32bitLE(0x04,streamFile)/2;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x04,streamFile) / 2;
vgmstream->meta_type = meta_PS2_NPSF;
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, 0x34,streamFile);
start_offset = (off_t)read_32bitLE(0x10,streamFile);
if (vgmstream->channels == 1) {
vgmstream->layout_type = layout_none;
} else {
vgmstream->layout_type = layout_interleave;
}
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x8000);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
(off_t)(start_offset+vgmstream->interleave_block_size*i);
}
}
/* open the file for reading */
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;
}

View File

@ -6,14 +6,14 @@
VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
off_t start_offset, chunk_offset;
size_t data_size;
off_t start_offset, chunk_offset, name_offset = 0;
size_t data_size, chunk_size;
int loop_flag = 0, channel_count, is_separate, type, sample_rate;
int32_t loop_start, loop_end;
int target_stream = 0, total_streams;
int total_streams, target_stream = streamFile->stream_index;
/* check extensions */
/* .xws: header and data, .xwh+xwb: header + data */
/* .xws: header and data, .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */
if (!check_extensions(streamFile,"xws,xwb")) goto fail;
is_separate = check_extensions(streamFile,"xwb");
@ -35,14 +35,16 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
/* file size (just the .xwh/xws) */
if (read_32bitLE(0x04,streamHeader)+0x10 != get_streamfile_size(streamHeader))
goto fail;
/* 0x08(4): version/type? (0x200), 0x0C: null */
/* 0x08(4): version (0x100/0x200), 0x0C: null */
/* typical chunks: FORM, FTXT, MARK, BODY (for .xws) */
if (read_32bitBE(0x10,streamHeader) != 0x464F524D) /* "FORM", main header (always first) */
goto fail;
/* 0x04: chunk size (-0x10), 0x08 version/type? (0x100), 0x0c: null */
chunk_size = read_32bitLE(0x10+0x04,streamHeader); /* size - 0x10 */
/* 0x08 version (0x100), 0x0c: null */
chunk_offset = 0x20;
/* check multi-streams */
total_streams = read_32bitLE(chunk_offset+0x00,streamHeader);
if (target_stream == 0) target_stream = 1;
@ -92,6 +94,14 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
start_offset = data_offset + stream_offset;
}
/* get stream name (always follows FORM) */
if (read_32bitBE(0x10+0x10 + chunk_size,streamHeader) == 0x46545854) { /* "FTXT" */
chunk_offset = 0x10+0x10 + chunk_size + 0x10;
if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_streams) {
name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x04,streamHeader);
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
@ -100,6 +110,8 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_PS2_RXWS;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
switch (type) {
case 0x00: /* PS-ADPCM */
@ -107,9 +119,9 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
vgmstream->num_samples = ps_bytes_to_samples(loop_end, channel_count); //todo (read_32bitLE(0x38,streamFile)*28/16)/2;
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count); //todo read_32bitLE(0x3C,streamFile)/16*14;
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channel_count); //todo read_32bitLE(0x38,streamFile)/16*14;
vgmstream->num_samples = ps_bytes_to_samples(loop_end, channel_count);
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count);
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channel_count);
break;
case 0x01: /* PCM */

View File

@ -1,78 +0,0 @@
#include "meta.h"
#include "../util.h"
/* STM: Red Dead Revolver */
VGMSTREAM * init_vgmstream_ps2_stm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("ps2stm",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x0,streamFile) != 0x53544d41) goto fail;
if (read_32bitBE(0x4,streamFile) != 0x6b690000) goto fail;
/* check bps */
if (read_32bitLE(0x10,streamFile) != 4) goto fail;
loop_flag = read_32bitLE(0x20,streamFile);
channel_count = read_32bitLE(0x14,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x800;
vgmstream->sample_rate = (uint16_t)read_32bitLE(0xc,streamFile);
vgmstream->coding_type = coding_DVI_IMA_int;
vgmstream->num_samples = read_32bitLE(0x18,streamFile);
//vgmstream->interleave_block_size = read_32bitLE(0x8,streamFile) / channel_count;
vgmstream->interleave_block_size = 0x40;
if(1 < channel_count)
{
/* not sure if this is right ... */
vgmstream->layout_type = layout_interleave;
} else {
vgmstream->layout_type = layout_none;
}
vgmstream->meta_type = meta_PS2_STM;
if(loop_flag) {
vgmstream->loop_start_sample=read_32bitLE(0x24,streamFile);
vgmstream->loop_end_sample=vgmstream->num_samples;
}
/* 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;
}

View File

@ -212,6 +212,9 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) {
vgmstream->loop_end_sample += (int32_t)(loopEnd%(interleave*channel_count))/16*28;
}
/* always, but can be null or used as special string */
read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile);
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )

View File

@ -9,11 +9,12 @@ static off_t get_rws_string_size(off_t off, STREAMFILE *streamFile);
/* RWS - RenderWare Stream (from games using RenderWare Audio middleware) */
VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, off, coefs_offset = 0, stream_offset = 0;
off_t start_offset, off, coefs_offset = 0, stream_offset = 0, name_offset = 0;
int loop_flag = 0, channel_count = 0, codec = 0, sample_rate = 0;
size_t file_size, header_size, data_size, stream_size = 0, block_size_max = 0, block_size = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int i, total_segments, total_streams, target_stream = 0;
int i, total_segments;
int total_streams, target_stream = streamFile->stream_index;
if (!check_extensions(streamFile,"rws"))
@ -73,7 +74,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
/* get block_size for our target stream and from all streams, to skip their blocks during decode */
for (i = 0; i < total_streams; i++) { /* get block_sizes */
block_size_max += read_32bit(off+0x10 + 0x28*i,streamFile); /* includes padding and can be different per stream */
if (target_stream-1 == i) {
if (i+1 == target_stream) {
block_size = read_32bit(off+0x20 + 0x28*i,streamFile); /* actual size */
stream_offset = read_32bit(off+0x24 + 0x28*i,streamFile); /* within data */
}
@ -81,27 +82,40 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
off += 0x28 * total_streams;
/* get stream config: 0x0c(1): bits per sample, others: ? */
for (i = 0; i < total_streams; i++) {/* size depends on codec so we must parse it */
sample_rate = read_32bit(off+0x00, streamFile);
//unk_size = read_32bit(off+0x08, streamFile); /* segment size? loop-related? */
channel_count = read_8bit(off+0x0d, streamFile);
codec = read_32bitBE(off+0x1c, streamFile); /* uuid of 128b but first 32b is enough */
for (i = 0; i < total_streams; i++) { /* size depends on codec so we must parse it */
int prev_codec = 0;
if (i+1 == target_stream) {
sample_rate = read_32bit(off+0x00, streamFile);
//unk_size = read_32bit(off+0x08, streamFile); /* segment size? loop-related? */
channel_count = read_8bit(off+0x0d, streamFile);
codec = read_32bitBE(off+0x1c, streamFile); /* uuid of 128b but first 32b is enough */
}
prev_codec = read_32bitBE(off+0x1c, streamFile);
off += 0x2c;
if (codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */
if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */
/* 0x00: approx num samples? 0x04: approx size/loop related? (can be 0) */
coefs_offset = off + 0x1c;
if (i+1 == target_stream) {
coefs_offset = off + 0x1c;
}
off += 0x60;
}
if (total_streams > 1) /* multitracks have an unknown field */
off += 0x04;
if (i == target_stream-1)
break;
off += 0x04; /* padding/garbage */
}
/* next is 0x14 * streams = ?(4) + uuid? (header ends), rest is garbage/padding until chunk end (may contain strings and weird stuff) */
/* skip uuid? per stream */
off += 0x10 * total_streams;
/* get stream name */
for (i = 0; i < total_streams; i++) {
if (i+1 == target_stream) {
name_offset = off;
}
off += get_rws_string_size(off, streamFile);
}
/* rest is padding/garbage until chunk end (may contain strings and weird stuff) */
start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset; /* usually 0x800 but not always */
@ -110,9 +124,11 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_RWS;
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_RWS;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
vgmstream->layout_type = layout_rws_blocked;
vgmstream->current_block_size = block_size / vgmstream->channels;

View File

@ -8,13 +8,13 @@
VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
off_t start_offset, data_offset, chunk_offset;
off_t start_offset, data_offset, chunk_offset, name_offset = 0;
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;
int total_streams, target_stream = streamFile->stream_index;
/* check extension, case insensitive */
@ -48,7 +48,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
}
/* typical chunks: WAVE, NAME (strings), RGND, SEQD (related to SFX), WSUR, WMKR, BUSS */
/* typical chunks: WAVE, RGND, NAME (strings for WAVE or RGND), SEQD (related to SFX), WSUR, WMKR, BUSS */
/* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */
if (is_sgx) { /* position after chunk+size */
if (read_32bitBE(0x10,streamHeader) != 0x57415645) goto fail; /* "WAVE" */
@ -69,7 +69,8 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
chunk_offset += 0x08 + 0x38 * (target_stream-1); /* position in target header*/
/* 0x00 ? (00/01/02) */
/* 0x04 sometimes global offset to wave_name */
if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */
name_offset = read_32bitLE(chunk_offset+0x04,streamHeader);
type = read_8bit(chunk_offset+0x08,streamHeader);
channels = read_8bit(chunk_offset+0x09,streamHeader);
/* 0x0a null */
@ -97,6 +98,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
start_offset = data_offset + stream_offset;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
@ -107,6 +109,8 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_SGXD;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
/* 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) */

View File

@ -53,7 +53,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
int headers_entries;
int32_t loop_start, loop_end;
int target_stream = 1; /* N=Nth stream, 0=auto (first) */
int target_stream = streamFile->stream_index;
int loop_flag = 0, channel_count, codec_id;
int aux_chunk_count;

118
src/meta/stm.c Normal file
View File

@ -0,0 +1,118 @@
#include "meta.h"
#include "../coding/coding.h"
/* STM - from Angel Studios/Rockstar San Diego games (Red Dead Revolver, Midnight Club 2, TransWorld Surf) */
VGMSTREAM * init_vgmstream_stm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channel_count;
int big_endian, bps, interleave, data_size, loop_start = 0, loop_end = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
/* check extension, case insensitive
* .stm is the real ext but common, rename to .lstm or .stma/amts/ps2stm (older) */
if ( !check_extensions(streamFile,"stm,lstm,stma,amts,ps2stm"))
goto fail;
/* check header */
if ((read_32bitBE(0x00,streamFile) != 0x53544d41) && /* "SMTA" (LE) */
(read_32bitBE(0x00,streamFile) != 0x414D5453)) /* "AMTS" (BE) */
goto fail;
/* 0x04: revision (696F/696B/696A/6969) */
big_endian = (read_32bitBE(0x00,streamFile) == 0x414D5453);
if (big_endian) {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
}
start_offset = 0x800;
interleave = read_32bit(0x08,streamFile);
bps = read_32bit(0x10,streamFile);
channel_count = read_32bit(0x14,streamFile);
data_size = read_32bit(0x18,streamFile);
loop_end = read_32bit(0x1c,streamFile); /* absolute offset */
if (data_size + start_offset != get_streamfile_size(streamFile)) goto fail;
if (big_endian) {
/* GC AMTS have a regular DSP header beyond 0x20, just use what we need, no point on validating all fields */
loop_flag = read_16bit(0x2c,streamFile);
}
else {
/* PS2/Xbox STMA have either loop info or padding beyond 0x20 */
if (read_32bit(0x20,streamFile) == 1) { /* may contain 0xCCCCCCCC garbage */
loop_flag = 1;
loop_start = read_32bit(0x24,streamFile);
/* 0x28 (32b * ch): loop start hist+step per channel */ //todo setup
}
#if 0
/* this works for some files that do full repeats, but also repeats many SFX */
else if (data_size != loop_end && data_size != loop_end - 0x100) { /* data_size vs adjusted loop_end */
loop_flag = 1;
}
#endif
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bit(0xc,streamFile);
vgmstream->meta_type = meta_STM;
vgmstream->layout_type = (channel_count > 1) ? layout_interleave : layout_none;
switch(bps) {
case 4:
if (big_endian) { /* GC DSP ADPCM (TransWorld Surf GC) */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->interleave_block_size = interleave;
vgmstream->num_samples = read_32bit(0x20,streamFile);
vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bit(0x30,streamFile));
vgmstream->loop_end_sample = dsp_nibbles_to_samples(read_32bit(0x34,streamFile))+1;
dsp_read_coefs_be(vgmstream, streamFile, 0x3c, 0x60);
dsp_read_hist_be(vgmstream, streamFile, 0x60, 0x60);
}
else { /* DVI IMA ADPCM (Red Dead Revolver, Midnight Club 2) */
vgmstream->coding_type = coding_DVI_IMA_int;
/* 'interleave not' reliable, strange values and rarely needs 0x80 */
vgmstream->interleave_block_size = (interleave == 0xc000) ? 0x80 : 0x40;
vgmstream->num_samples = ima_bytes_to_samples(data_size, vgmstream->channels);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = ima_bytes_to_samples(loop_end - start_offset, vgmstream->channels);
}
break;
case 16: /* PCM (Spy Hunter 2 PS2, rare) */
vgmstream->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE;
vgmstream->interleave_block_size = 0x02; /* interleave not always reliable */
vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, bps);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end - start_offset, vgmstream->channels, bps);
break;
default:
VGM_LOG("STM: unknown bps %i\n", bps);
goto fail;
}
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -6,12 +6,12 @@
VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
off_t start_offset, chunk_offset, first_offset = 0x60;
off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0;
int is_separate;
int loop_flag, channels, type;
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
int target_stream = 0, total_streams;
int total_streams, target_stream = streamFile->stream_index;
/* check extension, case insensitive */
@ -85,6 +85,20 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
start_offset = data_offset + stream_offset;
}
/* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */
if (is_separate && find_chunk_le(streamHeader, 0x4E414D45,first_offset,0, &chunk_offset,NULL)) { /* "NAME" */
/* table: relative offset (32b) + hash? (32b) + cue index (32b) */
int i;
int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /*can be more than streams */
for (i = 0; i < num_entries; i++) {
uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader);
if (index+1 == target_stream) {
name_offset = chunk_offset+0x08 + 0x00 + i*0x0c + read_32bitLE(chunk_offset+0x08 + 0x00 + i*0x0c,streamHeader);
break;
}
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
@ -96,7 +110,8 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_SXD;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
switch (type) {
case 0x01: /* HEVAG */

View File

@ -3,6 +3,8 @@
#include "../layout/layout.h"
#include "../util.h"
#define TXTH_LINE_MAX 0x2000
/* known TXTH types */
typedef enum {
PSX = 0, /* PSX ADPCM */
@ -390,13 +392,6 @@ fail:
static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth) {
off_t off = 0;
off_t file_size = get_streamfile_size(streamText);
#ifndef LINE_MAX /* some platforms define this via limits.h */
#if defined(_MSC_VER) && (_MSC_VER < 1900)
enum { LINE_MAX = 0x2000 };
#else
const size_t LINE_MAX = 0x2000; /* arbitrary max */
#endif
#endif
txth->data_size = get_streamfile_size(streamFile); /* for later use */
@ -406,15 +401,15 @@ static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_hea
/* read lines */
while (off < file_size) {
char line[LINE_MAX];
char key[LINE_MAX];
char val[LINE_MAX]; /* at least as big as a line to avoid overflows (I hope) */
char line[TXTH_LINE_MAX];
char key[TXTH_LINE_MAX];
char val[TXTH_LINE_MAX]; /* at least as big as a line to avoid overflows (I hope) */
int ok;
off_t line_start = off, line_end = 0;
line[0] = key[0] = val[0] = 0;
/* find line end */
while (line_end == 0 && off - line_start < LINE_MAX) {
while (line_end == 0 && off - line_start < TXTH_LINE_MAX) {
char c = (char)read_8bit(off, streamText);
if (c == '\n')
line_end = off;

View File

@ -1,64 +0,0 @@
#include "meta.h"
#include "../util.h"
/* STMA
STMA (found in Midnight Club 2)
*/
VGMSTREAM * init_vgmstream_xbox_stma(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag=0;
int channel_count;
int i;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("stma",filename_extension(filename))) goto fail;
if(read_32bitBE(0x0,streamFile)!=0x53544D41)
goto fail;
loop_flag = ((read_32bitLE(0x20,streamFile)==1) || (read_32bitLE(0x18,streamFile)>read_32bitLE(0x1C,streamFile)));
/* Seems that the loop flag is not allways well defined */
/* Some of the tracks should loop, but without flag set */
channel_count=read_32bitLE(0x14,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x0C,streamFile);
vgmstream->coding_type = coding_DVI_IMA_int;
vgmstream->num_samples = read_32bitLE(0x18,streamFile)*2/vgmstream->channels;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size=0x40;
vgmstream->meta_type = meta_XBOX_STMA;
if(loop_flag) {
vgmstream->loop_start_sample=read_32bitLE(0x24,streamFile);
vgmstream->loop_end_sample=vgmstream->num_samples;
}
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,36);
vgmstream->ch[i].offset = 0x800+(i*vgmstream->interleave_block_size);
if (!vgmstream->ch[i].streamfile) goto fail;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -63,13 +63,15 @@ typedef struct {
uint32_t loop_end_sample;
} xwb_header;
static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_header * xwb, STREAMFILE *streamFile);
/* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */
VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, off, suboff;
xwb_header xwb;
int target_stream = 0;
int target_stream = streamFile->stream_index;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
@ -336,6 +338,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = xwb.loop_end_sample;
vgmstream->num_streams = xwb.streams;
vgmstream->meta_type = meta_XWB;
get_xsb_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_stream, &xwb, streamFile);
switch(xwb.codec) {
case PCM:
@ -462,3 +465,329 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
/* ****************************************************************************** */
/* XSB parsing from xwb_split (mostly untouched), could be improved */
#define XSB_XACT1_MAX 11
#define XSB_XACT2_MAX 41
/**
* XWB contain stream info (channels, loop, data etc), often from multiple streams.
* XSBs contain info about how to play sounds (volume, pitch, name, etc) from XWBs (music or SFX).
* We only need to parse the XSB for the stream names.
*/
typedef struct {
int sound_count;
} xsb_wavebank;
typedef struct {
int stream_index; /* stream id in the xwb (doesn't need to match xsb sound order) */
int wavebank; /* xwb id, if the xsb has multiple wavebanks */
off_t name_index; /* name order */
off_t name_offset; /* global offset to the name string */
off_t sound_offset; /* global offset to the xsb sound */
off_t unk_index; /* some kind of number up to sound_count or 0xffff */
} xsb_sound;
typedef struct {
/* XSB header info */
xsb_sound * xsb_sounds; /* array of sounds info from the xsb, simplified */
xsb_wavebank * xsb_wavebanks; /* array of wavebank info from the xsb, simplified */
off_t xsb_sounds_offset;
size_t xsb_sounds_count;
size_t xsb_simple_sounds_offset; /* sound cues */
size_t xsb_simple_sounds_count;
size_t xsb_complex_sounds_offset;
size_t xsb_complex_sounds_count;
size_t xsb_wavebanks_count;
off_t xsb_nameoffsets_offset;
} xsb_header;
/* try to find the stream name in a companion XSB file, a comically complex cue format. */
static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_header * xwb, STREAMFILE *streamXwb) {
STREAMFILE *streamFile;
int i,j, start_sound, cfg__start_sound = 0, cfg__selected_wavebank = 0;
int xsb_version;
off_t off, suboff, name_offset = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
xsb_header xsb;
memset(&xsb,0,sizeof(xsb_header)); /* before any "fail"! */
streamFile = open_stream_ext(streamXwb, "xsb");
if (!streamFile) goto fail;
//todo try common names (xwb and xsb often are named slightly differently using a common convention)
/* check header */
if ((read_32bitBE(0x00,streamFile) != 0x5344424B) && /* "SDBK" (LE) */
(read_32bitBE(0x00,streamFile) != 0x4B424453)) /* "KBDS" (BE) */
goto fail;
if (read_32bitBE(0x00,streamFile) == 0x5344424B) { /* SDBK */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
} else {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
}
/* read main header (SoundBankHeader) */
xsb_version = read_16bit(0x04, streamFile);
if ((xwb->version <= XACT1_1_MAX && xsb_version > XSB_XACT1_MAX) || (xwb->version <= XACT2_2_MAX && xsb_version > XSB_XACT2_MAX)) {
VGM_LOG("XSB: xsb and xwb are from different XACT versions (xsb v%i vs xwb v%i)", xsb_version, xwb->version);
goto fail;
}
off = 0;
if (xsb_version <= XSB_XACT1_MAX) {
xsb.xsb_wavebanks_count = 1; //read_8bit(0x22, streamFile);
xsb.xsb_sounds_count = read_16bit(0x1e, streamFile);//@ 0x1a? 0x1c?
//xsb.xsb_names_size = 0;
//xsb.xsb_names_offset = 0;
xsb.xsb_nameoffsets_offset = 0;
xsb.xsb_sounds_offset = 0x38;
} else if (xsb_version <= XSB_XACT2_MAX) {
xsb.xsb_simple_sounds_count = read_16bit(0x09, streamFile);
xsb.xsb_complex_sounds_count = read_16bit(0x0B, streamFile);
xsb.xsb_wavebanks_count = read_8bit(0x11, streamFile);
xsb.xsb_sounds_count = read_16bit(0x12, streamFile);
//0x14: 16b unk
//xsb.xsb_names_size = read_32bit(0x16, streamFile);
xsb.xsb_simple_sounds_offset = read_32bit(0x1a, streamFile);
xsb.xsb_complex_sounds_offset = read_32bit(0x1e, streamFile); //todo 0x1e?
//xsb.xsb_names_offset = read_32bit(0x22, streamFile);
xsb.xsb_nameoffsets_offset = read_32bit(0x3a, streamFile);
xsb.xsb_sounds_offset = read_32bit(0x3e, streamFile);
} else {
xsb.xsb_simple_sounds_count = read_16bit(0x13, streamFile);
xsb.xsb_complex_sounds_count = read_16bit(0x15, streamFile);
xsb.xsb_wavebanks_count = read_8bit(0x1b, streamFile);
xsb.xsb_sounds_count = read_16bit(0x1c, streamFile);
//xsb.xsb_names_size = read_32bit(0x1e, streamFile);
xsb.xsb_simple_sounds_offset = read_32bit(0x22, streamFile);
xsb.xsb_complex_sounds_offset = read_32bit(0x26, streamFile);
//xsb.xsb_names_offset = read_32bit(0x2a, streamFile);
xsb.xsb_nameoffsets_offset = read_32bit(0x42, streamFile);
xsb.xsb_sounds_offset = read_32bit(0x46, streamFile);
}
VGM_ASSERT(xsb.xsb_sounds_count < xwb->streams,
"XSB: number of streams in xsb lower than xwb (xsb %i vs xwb %i)", xsb.xsb_sounds_count, xwb->streams);
VGM_ASSERT(xsb.xsb_simple_sounds_count + xsb.xsb_complex_sounds_count != xsb.xsb_sounds_count,
"XSB: number of xsb sounds doesn't match simple + complex sounds (simple %i, complex %i, total %i)", xsb.xsb_simple_sounds_count, xsb.xsb_complex_sounds_count, xsb.xsb_sounds_count);
/* init stuff */
xsb.xsb_sounds = calloc(xsb.xsb_sounds_count, sizeof(xsb_sound));
if (!xsb.xsb_sounds) goto fail;
xsb.xsb_wavebanks = calloc(xsb.xsb_wavebanks_count, sizeof(xsb_wavebank));
if (!xsb.xsb_wavebanks) goto fail;
/* The following is a bizarre soup of flags, tables, offsets to offsets and stuff, just to get the actual name.
* info: https://wiki.multimedia.cx/index.php/XACT */
/* parse xsb sounds */
off = xsb.xsb_sounds_offset;
for (i = 0; i < xsb.xsb_sounds_count; i++) {
xsb_sound *s = &(xsb.xsb_sounds[i]);
uint32_t flag;
size_t size;
if (xsb_version <= XSB_XACT1_MAX) {
/* The format seems constant */
flag = read_8bit(off+0x00, streamFile);
size = 0x14;
if (flag != 0x01) {
VGM_LOG("XSB: xsb flag 0x%x at offset 0x%08lx not implemented", flag, off);
goto fail;
}
s->wavebank = 0; //read_8bit(off+suboff + 0x02, streamFile);
s->stream_index = read_16bit(off+0x02, streamFile);
s->sound_offset = off;
s->name_offset = read_16bit(off+0x04, streamFile);
}
else {
/* Each XSB sound has a variable size and somewhere inside is the stream/wavebank index.
* Various flags control the sound layout, but I can't make sense of them so quick hack instead */
flag = read_8bit(off+0x00, streamFile);
//0x01 16b unk, 0x03: 8b unk 04: 16b unk, 06: 8b unk
size = read_16bit(off+0x07, streamFile);
if (!(flag & 0x01)) { /* simple sound */
suboff = 0x09;
} else { /* complex sound */
/* not very exact but seems to work */
if (flag==0x01 || flag==0x03 || flag==0x05 || flag==0x07) {
if (size == 0x49) { //grotesque hack for Eschatos (these flags are way too complex)
suboff = 0x23;
} else if (size % 2 == 1 && read_16bit(off+size-0x2, streamFile)!=0) {
suboff = size - 0x08 - 0x07; //7 unk bytes at the end
} else {
suboff = size - 0x08;
}
} else {
VGM_LOG("XSB: xsb flag 0x%x at offset 0x%08lx not implemented", flag, off);
goto fail;
}
}
s->stream_index = read_16bit(off+suboff + 0x00, streamFile);
s->wavebank = read_8bit(off+suboff + 0x02, streamFile);
s->sound_offset = off;
}
if (s->wavebank+1 > xsb.xsb_wavebanks_count) {
VGM_LOG("XSB: unknown xsb wavebank id %i at offset 0x%lx", s->wavebank, off);
goto fail;
}
xsb.xsb_wavebanks[s->wavebank].sound_count += 1;
off += size;
}
/* parse name offsets */
if (xsb_version > XSB_XACT1_MAX) {
/* "cue" name order: first simple sounds, then complex sounds
* Both aren't ordered like the sound entries, instead use a global offset to the entry
*
* ex. of a possible XSB:
* name 1 = simple sound 1 > sound entry 2 (points to xwb stream 4): stream 4 uses name 1
* name 2 = simple sound 2 > sound entry 1 (points to xwb stream 1): stream 1 uses name 2
* name 3 = complex sound 1 > sound entry 3 (points to xwb stream 3): stream 3 uses name 3
* name 4 = complex sound 2 > sound entry 4 (points to xwb stream 2): stream 2 uses name 4
*
* Multiple cues can point to the same sound entry but we only use the first name (meaning some won't be used) */
off_t n_off = xsb.xsb_nameoffsets_offset;
off = xsb.xsb_simple_sounds_offset;
for (i = 0; i < xsb.xsb_simple_sounds_count; i++) {
off_t sound_offset = read_32bit(off + 0x01, streamFile);
off += 0x05;
/* find sound by offset */
for (j = 0; j < xsb.xsb_sounds_count; j++) {
xsb_sound *s = &(xsb.xsb_sounds[j]);;
/* update with the current name offset */
if (!s->name_offset && sound_offset == s->sound_offset) {
s->name_offset = read_32bit(n_off + 0x00, streamFile);
s->unk_index = read_16bit(n_off + 0x04, streamFile);
n_off += 0x06;
break;
}
}
}
off = xsb.xsb_complex_sounds_offset;
for (i = 0; i < xsb.xsb_complex_sounds_count; i++) {
off_t sound_offset = read_32bit(off + 0x01, streamFile);
off += 0x0f;
/* find sound by offset */
for (j = 0; j < xsb.xsb_sounds_count; j++) {
xsb_sound *s = &(xsb.xsb_sounds[j]);;
/* update with the current name offset */
if (!s->name_offset && sound_offset == s->sound_offset) {
s->name_offset = read_32bit(n_off + 0x00, streamFile);
s->unk_index = read_16bit(n_off + 0x04, streamFile);
n_off += 0x06;
break;
}
}
}
}
// todo: it's possible to find the wavebank using the name
/* try to find correct wavebank, in cases of multiple */
if (!cfg__selected_wavebank) {
for (i = 0; i < xsb.xsb_wavebanks_count; i++) {
xsb_wavebank *w = &(xsb.xsb_wavebanks[i]);
//CHECK_EXIT(w->sound_count == 0, "ERROR: xsb wavebank %i has no sounds", i); //Ikaruga PC
if (w->sound_count == xwb->streams) {
if (!cfg__selected_wavebank) {
VGM_LOG("XSB: multiple xsb wavebanks with the same number of sounds, use -w to specify one of the wavebanks");
goto fail;
}
cfg__selected_wavebank = i+1;
}
}
}
/* banks with different number of sounds but only one wavebank, just select the first */
if (!cfg__selected_wavebank && xsb.xsb_wavebanks_count==1) {
cfg__selected_wavebank = 1;
}
if (!cfg__selected_wavebank) {
VGM_LOG("XSB: multiple xsb wavebanks but autodetect didn't work");
goto fail;
}
if (xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count == 0) {
VGM_LOG("XSB: xsb selected wavebank %i has no sounds", cfg__selected_wavebank);
goto fail;
}
if (cfg__start_sound) {
if (xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count - (cfg__start_sound-1) < xwb->streams) {
VGM_LOG("XSB: starting sound too high (max in selected wavebank is %i)", xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count - xwb->streams + 1);
goto fail;
}
} else {
/*
if (!cfg->ignore_names_not_found)
CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count > xwb->streams_count, "ERROR: number of streams in xsb wavebank bigger than xwb (xsb %i vs xwb %i), use -s to specify (1=first)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count);
if (!cfg->ignore_names_not_found)
CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count < xwb->streams_count, "ERROR: number of streams in xsb wavebank lower than xwb (xsb %i vs xwb %i), use -n to ignore (some names won't be extracted)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count);
*/
//if (!cfg->ignore_names_not_found)
// CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count != xwb->streams_count, "ERROR: number of streams in xsb wavebank different than xwb (xsb %i vs xwb %i), use -s to specify (1=first)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count);
}
/* *************************** */
start_sound = cfg__start_sound ? cfg__start_sound-1 : 0;
/* get name offset */
for (i = start_sound; i < xsb.xsb_sounds_count; i++) {
xsb_sound *s = &(xsb.xsb_sounds[i]);
VGM_LOG("wa=%i, sel=%i, si=%i vs ti=%i\n", s->wavebank, cfg__selected_wavebank, s->stream_index, target_stream);
if (s->wavebank == cfg__selected_wavebank-1
&& s->stream_index == target_stream-1){
name_offset = s->name_offset;
break;
}
}
if (name_offset)
read_string(buf,maxsize, name_offset,streamFile);
//return; /* no return, let free */
fail:
free(xsb.xsb_sounds);
free(xsb.xsb_wavebanks);
return;
}

View File

@ -12,7 +12,7 @@
#define STREAMFILE_IGNORE_EOF 0
/* buffered file reader */
/* a STREAMFILE that operates via standard IO using a buffer */
typedef struct {
STREAMFILE sf; /* callbacks */
FILE * infile; /* actual FILE */
@ -31,7 +31,8 @@ typedef struct {
#endif
} STDIOSTREAMFILE;
static STREAMFILE * open_stdio_streamfile_buffer_by_FILE(FILE *infile,const char * const filename, size_t buffersize);
static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize);
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize);
static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOSTREAMFILE * streamfile) {
size_t length_read_total=0;
@ -211,12 +212,13 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil
if (!filename)
return NULL;
// if same name, duplicate the file pointer we already have open
if (!strcmp(streamFile->name,filename)) {
if (((newfd = dup(fileno(streamFile->infile))) >= 0) &&
(newfile = fdopen( newfd, "rb" )))
{
newstreamFile = open_stdio_streamfile_buffer_by_FILE(newfile,filename,buffersize);
newstreamFile = open_stdio_streamfile_buffer_by_file(newfile,filename,buffersize);
if (newstreamFile) {
return newstreamFile;
}
@ -228,7 +230,7 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil
return open_stdio_streamfile_buffer(filename,buffersize);
}
static STREAMFILE * open_stdio_streamfile_buffer_by_FILE(FILE *infile,const char * const filename, size_t buffersize) {
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize) {
uint8_t * buffer;
STDIOSTREAMFILE * streamfile;
@ -269,14 +271,14 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_FILE(FILE *infile,const char
return &streamfile->sf;
}
STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize) {
static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize) {
FILE * infile;
STREAMFILE *streamFile;
infile = fopen(filename,"rb");
if (!infile) return NULL;
streamFile = open_stdio_streamfile_buffer_by_FILE(infile,filename,buffersize);
streamFile = open_stdio_streamfile_buffer_by_file(infile,filename,buffersize);
if (!streamFile) {
fclose(infile);
}
@ -284,6 +286,18 @@ STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t bu
return streamFile;
}
STREAMFILE * open_stdio_streamfile(const char * filename) {
return open_stdio_streamfile_buffer(filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
}
STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename) {
return open_stdio_streamfile_buffer_by_file(file,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
}
/* **************************************************** */
/* Read a line into dst. The source files are MS-DOS style,
* separated (not terminated) by CRLF. Return 1 if the full line was
* retrieved (if it could fit in dst), 0 otherwise. In any case the result
@ -344,6 +358,28 @@ size_t get_streamfile_dos_line(int dst_length, char * dst, off_t offset,
}
/* reads a c-string, up to maxsize or NULL, returning size. buf is optional. */
int read_string(char * buf, size_t maxsize, off_t offset, STREAMFILE *streamFile) {
int i;
for (i=0; i < maxsize; i++) {
char c = read_8bit(offset + i, streamFile);
if (buf) buf[i] = c;
if (c == '\0')
return i;
if (i+1 == maxsize) { /* null at maxsize and don't validate (expected to be garbage) */
if (buf) buf[i] = '\0';
return maxsize;
}
if (c < 0x20 || c > 0xA5)
goto fail;
}
fail:
if (buf) buf[0] = '\0';
return 0;
}
/**
* Opens an stream using the base streamFile name plus a new extension (ex. for headers in a separate file)
*/

View File

@ -42,34 +42,44 @@
#endif
#endif
/* struct representing a file with callbacks. Code should use STREAMFILEs and not std C functions
* to do file operations, as plugins may need to provide their own callbacks. */
typedef struct _STREAMFILE {
size_t (*read)(struct _STREAMFILE *,uint8_t * dest, off_t offset, size_t length);
size_t (*get_size)(struct _STREAMFILE *);
off_t (*get_offset)(struct _STREAMFILE *);
// for dual-file support
/* for dual-file support */
void (*get_name)(struct _STREAMFILE *,char *name,size_t length);
// for when the "name" is encoded specially, this is the actual user
// visible name
/* for when the "name" is encoded specially, this is the actual user visible name */
void (*get_realname)(struct _STREAMFILE *,char *name,size_t length);
struct _STREAMFILE * (*open)(struct _STREAMFILE *,const char * const filename,size_t buffersize);
void (*close)(struct _STREAMFILE *);
#ifdef PROFILE_STREAMFILE
size_t (*get_bytes_read)(struct _STREAMFILE *);
int (*get_error_count)(struct _STREAMFILE *);
#endif
/* Substream selection for files with multiple streams. Manually used in metas if supported.
* Not ideal here, but it's the simplest way to pass to all init_vgmstream_x functions. */
int stream_index; /* 0=default/auto (first), 1=first, N=Nth */
} STREAMFILE;
/* create a STREAMFILE from path */
STREAMFILE * open_stdio_streamfile(const char * filename);
/* create a STREAMFILE from pre-opened file path */
STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename);
/* close a file, destroy the STREAMFILE object */
static inline void close_streamfile(STREAMFILE * streamfile) {
streamfile->close(streamfile);
}
/* read from a file
*
* returns number of bytes read
*/
/* read from a file, returns number of bytes read */
static inline size_t read_streamfile(uint8_t * dest, off_t offset, size_t length, STREAMFILE * streamfile) {
return streamfile->read(streamfile,dest,offset,length);
}
@ -132,26 +142,15 @@ static inline int8_t read_8bit(off_t offset, STREAMFILE * streamfile) {
return buf[0];
}
/* open file with a set buffer size, create a STREAMFILE object
*
* Returns pointer to new STREAMFILE or NULL if open failed
*/
STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize);
/* open file with a default buffer size, create a STREAMFILE object
*
* Returns pointer to new STREAMFILE or NULL if open failed
*/
static inline STREAMFILE * open_stdio_streamfile(const char * const filename) {
return open_stdio_streamfile_buffer(filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
}
/* various STREAMFILE helpers functions */
size_t get_streamfile_dos_line(int dst_length, char * dst, off_t offset, STREAMFILE * infile, int *line_done_ptr);
STREAMFILE * open_stream_ext(STREAMFILE *streamFile, const char * ext);
int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile);
int read_string(char * buf, size_t bufsize, off_t offset, STREAMFILE *streamFile);
int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile);
int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile);
int check_extensions(STREAMFILE *streamFile, const char * cmp_exts);

View File

@ -47,7 +47,6 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_ps2_rxws,
init_vgmstream_ps2_rxw,
init_vgmstream_ps2_int,
init_vgmstream_ngc_dsp_stm,
init_vgmstream_ps2_exst,
init_vgmstream_ps2_svag,
init_vgmstream_ps2_mib,
@ -86,7 +85,6 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_ws_aud,
init_vgmstream_ahx,
init_vgmstream_ivb,
init_vgmstream_amts,
init_vgmstream_svs,
init_vgmstream_riff,
init_vgmstream_rifx,
@ -139,7 +137,6 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_ngc_wvs,
init_vgmstream_dc_str,
init_vgmstream_dc_str_v2,
init_vgmstream_xbox_stma,
init_vgmstream_xbox_matx,
init_vgmstream_de2,
init_vgmstream_vs,
@ -252,7 +249,6 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_pona_psx,
init_vgmstream_xbox_hlwav,
init_vgmstream_stx,
init_vgmstream_ps2_stm,
init_vgmstream_myspd,
init_vgmstream_his,
init_vgmstream_ps2_ast,
@ -369,6 +365,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_ea_bnk,
init_vgmstream_ea_schl_fixed,
init_vgmstream_sk_aud,
init_vgmstream_stm,
init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG
@ -378,7 +375,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
/* internal version with all parameters */
VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) {
static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) {
int i, fcns_size;
if (!streamFile)
@ -446,6 +443,9 @@ VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) {
}
#endif
/* save info */
vgmstream->stream_index = streamFile->stream_index;
/* save start things so we can restart for seeking */
/* copy the channels */
memcpy(vgmstream->start_ch,vgmstream->ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels);
@ -2052,10 +2052,23 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
/* only interesting if more than one */
if (vgmstream->num_streams > 1) {
snprintf(temp,TEMPSIZE,
"\nnumber of streams: %d",
"\nstream number: %d",
vgmstream->num_streams);
concatn(length,desc,temp);
}
if (vgmstream->num_streams > 1 && vgmstream->stream_index > 0) {
snprintf(temp,TEMPSIZE,
"\nstream index: %d",
vgmstream->stream_index);
concatn(length,desc,temp);
}
if (vgmstream->stream_name[0] != '\0') {
snprintf(temp,TEMPSIZE,
"\nstream name: %s",
vgmstream->stream_name);
concatn(length,desc,temp);
}
}
/* filename search pairs for dual file stereo */

View File

@ -6,6 +6,7 @@
#define _VGMSTREAM_H
enum { PATH_LIMIT = 32768 };
enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */
#include "streamfile.h"
@ -264,7 +265,6 @@ typedef enum {
meta_DSP_STR, /* Conan .str files */
meta_DSP_SADB, /* .sad */
meta_DSP_WSI, /* .wsi */
meta_DSP_AMTS, /* .amts */
meta_DSP_WII_IDSP, /* .gcm with IDSP header */
meta_DSP_WII_MUS, /* .mus */
meta_DSP_WII_WSD, /* Phantom Brave (WII) */
@ -436,7 +436,6 @@ typedef enum {
meta_ADS, /* Gauntlet Dark Legends (GC) */
meta_PS2_SPS, /* Ape Escape 2 */
meta_PS2_XA2_RRP, /* RC Revenge Pro */
meta_PS2_STM, /* Red Dead Revolver .stm, renamed .ps2stm */
meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */
meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */
@ -444,7 +443,6 @@ typedef enum {
meta_XBOX_RIFF, /* XBOX RIFF/WAVE File */
meta_XBOX_WVS, /* XBOX WVS */
meta_NGC_WVS, /* Metal Arms - Glitch in the System */
meta_XBOX_STMA, /* XBOX STMA */
meta_XBOX_MATX, /* XBOX MATX */
meta_XBOX_XMU, /* XBOX XMU */
meta_XBOX_XVAS, /* XBOX VAS */
@ -616,6 +614,7 @@ typedef enum {
meta_TXTH, /* generic text header */
meta_SK_AUD, /* Silicon Knights .AUD (Eternal Darkness GC) */
meta_AHX, /* CRI AHX header */
meta_STM, /* Angel Studios/Rockstar San Diego Games */
#ifdef VGM_USE_VORBIS
meta_OGG_VORBIS, /* Ogg Vorbis */
@ -698,7 +697,11 @@ typedef struct {
coding_t coding_type; /* type of encoding */
layout_t layout_type; /* type of layout for data */
meta_t meta_type; /* how we know the metadata */
int num_streams; /* info only, for a few multi-stream formats (0=not set/one, 1=one stream) */
/* streams (info only) */
int num_streams; /* for multi-stream formats (0=not set/one, 1=one stream) */
int stream_index; /* current stream */
char stream_name[STREAM_NAME_SIZE]; /* name of the current stream, if the file stores it and it's filled */
/* looping */
int loop_flag; /* is this stream looped? */

View File

@ -52,11 +52,12 @@ void usage(const char * name) {
" -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"
" -s N: select subtream N, if the format supports multiple streams\n"
,name);
}
int main(int argc, char ** argv) {
VGMSTREAM * s;
VGMSTREAM * s = NULL;
sample * buf = NULL;
int32_t len;
int32_t fade_samples;
@ -77,13 +78,14 @@ int main(int argc, char ** argv) {
int lwav = 0;
int batchvar = 0;
int only_stereo = -1;
int stream_index = 0;
double loop_count = 2.0;
double fade_seconds = 10.0;
double fade_delay_seconds = 0.0;
int fade_ignore = 0;
int32_t bytecount;
while ((opt = getopt(argc, argv, "o:l:f:d:ipPcmxeLEFr:gb2:")) != -1) {
while ((opt = getopt(argc, argv, "o:l:f:d:ipPcmxeLEFr:gb2:s:")) != -1) {
switch (opt) {
case 'o':
outfilename = optarg;
@ -140,6 +142,9 @@ int main(int argc, char ** argv) {
case 'F':
fade_ignore = 1;
break;
case 's':
stream_index = atoi(optarg);
break;
default:
usage(argv[0]);
return 1;
@ -182,7 +187,17 @@ int main(int argc, char ** argv) {
return 1;
}
s = init_vgmstream(argv[optind]);
/* manually init streamfile to pass the stream index */
{
//s = init_vgmstream(argv[optind]);
STREAMFILE *streamFile = open_stdio_streamfile(argv[optind]);
if (streamFile) {
streamFile->stream_index = stream_index;
s = init_vgmstream_from_STREAMFILE(streamFile);
close_streamfile(streamFile);
}
}
if (!s) {
fprintf(stderr,"failed opening %s\n",argv[optind]);

View File

@ -2,9 +2,11 @@
* vgmstream for Winamp
*/
/* Winamp uses wchar_t filenames when this is on, so extra steps are needed.
* To open unicode filenames it needs to use _wfopen, inside a WA_STREAMFILE to pass around */
//#define UNICODE_INPUT_PLUGIN
/* Normally Winamp opens unicode files by their DOS 8.3 name. #define this to use wchar_t filenames,
* which must be opened with _wfopen in a WINAMP_STREAMFILE (needed for dual files like .pos).
* Only for Winamp paths, other parts would need #define UNICODE for Windows. */
#define UNICODE_INPUT_PLUGIN
#ifdef _MSC_VER
#define _CRT_SECURE_NO_DEPRECATE
@ -21,6 +23,7 @@
#include "../src/vgmstream.h"
#include "in2.h"
#include "wa_ipc.h"
#include "ipc_pe.h"
#include "resource.h"
@ -31,17 +34,13 @@
#define VERSION "(unknown version)"
#endif
#define APP_NAME "vgmstream plugin"
#define PLUGIN_DESCRIPTION "vgmstream plugin " VERSION " " __DATE__
#define INI_NAME "plugin.ini"
/* ************************************* */
/* post when playback stops */
#define WM_WA_MPEG_EOF WM_USER+2
In_Module input_module; /* the input module, declared at the bottom of this file */
DWORD WINAPI __stdcall decode(void *arg);
/* config */
#define CONFIG_APP_NAME "vgmstream plugin"
#define CONFIG_INI_NAME "plugin.ini"
#define DEFAULT_FADE_SECONDS "10.00"
#define DEFAULT_FADE_DELAY_SECONDS "0.00"
@ -60,8 +59,11 @@ DWORD WINAPI __stdcall decode(void *arg);
char *priority_strings[] = {"Idle","Lowest","Below Normal","Normal","Above Normal","Highest (not recommended)","Time Critical (not recommended)"};
int priority_values[] = {THREAD_PRIORITY_IDLE,THREAD_PRIORITY_LOWEST,THREAD_PRIORITY_BELOW_NORMAL,THREAD_PRIORITY_NORMAL,THREAD_PRIORITY_ABOVE_NORMAL,THREAD_PRIORITY_HIGHEST,THREAD_PRIORITY_TIME_CRITICAL};
#define WINAMP_MAX_PATH 32768 /* originally 260+1 */
in_char lastfn[WINAMP_MAX_PATH] = {0}; /* name of the currently playing file */
/* ************************************* */
/* plugin main (declared at the bottom of this file) */
In_Module input_module;
DWORD WINAPI __stdcall decode(void *arg);
/* Winamp Play extension list, needed to accept/play and associate extensions in Windows */
#define EXTENSION_LIST_SIZE VGM_EXTENSION_LIST_CHAR_SIZE * 6
@ -89,7 +91,195 @@ int decode_pos_samples = 0;
int stream_length_samples = 0;
int fade_samples = 0;
/* ***************************************** */
in_char lastfn[PATH_LIMIT] = {0}; /* name of the currently playing file */
/* ************************************* */
//todo safe ops
#ifdef UNICODE_INPUT_PLUGIN
#define wa_strcpy wcscpy
#define wa_strncpy wcsncpy
#define wa_strlen wcslen
#else
#define wa_strcpy strcpy
#define wa_strncpy strncpy
#define wa_strlen strlen
#endif
/* converts from utf16 to utf8 (if unicode is active) */
static void wa_wchar_to_char(char *dst, size_t dstsize, const in_char *wsrc) {
#ifdef UNICODE_INPUT_PLUGIN
/* converto to UTF8 codepage, default separate bytes, source wstr, wstr lenght, */
//int size_needed = WideCharToMultiByte(CP_UTF8,0, src,-1, NULL,0, NULL, NULL);
WideCharToMultiByte(CP_UTF8,0, wsrc,-1, dst,dstsize, NULL, NULL);
#else
strcpy(dst,wsrc);
#endif
}
/* converts from utf8 to utf16 (if unicode is active) */
static void wa_char_to_wchar(in_char *wdst, size_t wdstsize, const char *src) {
#ifdef UNICODE_INPUT_PLUGIN
//int size_needed = MultiByteToWideChar(CP_UTF8,0, src,-1, NULL,0);
MultiByteToWideChar(CP_UTF8,0, src,-1, wdst,wdstsize);
#else
strcpy(wdst,src);
#endif
}
/* opens a utf16 (unicode) path */
static FILE* wa_fopen(const in_char *wpath) {
#ifdef UNICODE_INPUT_PLUGIN
return _wfopen(wpath,L"rb");
#else
return fopen(wpath,"rb");
#endif
}
/* dupes a utf16 (unicode) file */
static FILE* wa_fdopen(int fd) {
#ifdef UNICODE_INPUT_PLUGIN
return _wfdopen(fd,L"rb");
#else
return fdopen(fd,"rb");
#endif
}
/* a STREAMFILE that operates via STDIOSTREAMFILE but handles Winamp's unicode (in_char) paths */
typedef struct _WINAMP_STREAMFILE {
STREAMFILE sf;
STREAMFILE *stdiosf;
FILE *infile_ref; /* pointer to the infile in stdiosf */
} WINAMP_STREAMFILE;
static STREAMFILE *open_winamp_streamfile_by_file(FILE *infile, const char * path);
static STREAMFILE *open_winamp_streamfile_by_wpath(const in_char *wpath);
static size_t wasf_read(WINAMP_STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) {
return streamfile->stdiosf->read(streamfile->stdiosf,dest,offset,length);
}
static off_t wasf_get_size(WINAMP_STREAMFILE *streamfile) {
return streamfile->stdiosf->get_size(streamfile->stdiosf);
}
static off_t wasf_get_offset(WINAMP_STREAMFILE *streamfile) {
return streamfile->stdiosf->get_offset(streamfile->stdiosf);
}
static void wasf_get_name(WINAMP_STREAMFILE *streamfile, char *buffer, size_t length) {
return streamfile->stdiosf->get_name(streamfile->stdiosf, buffer, length);
}
static void wasf_get_realname(WINAMP_STREAMFILE *streamfile, char *buffer, size_t length) {
return streamfile->stdiosf->get_realname(streamfile->stdiosf, buffer, length);
}
static STREAMFILE *wasf_open(WINAMP_STREAMFILE *streamFile, const char *const filename, size_t buffersize) {
int newfd;
FILE *newfile;
STREAMFILE *newstreamFile;
in_char wpath[PATH_LIMIT];
char name[PATH_LIMIT];
if (!filename)
return NULL;
/* if same name, duplicate the file pointer we already have open */ //unsure if all this is needed
streamFile->stdiosf->get_name(streamFile->stdiosf, name, PATH_LIMIT);
if (!strcmp(name,filename)) {
if (((newfd = dup(fileno(streamFile->infile_ref))) >= 0) &&
(newfile = wa_fdopen(newfd)))
{
newstreamFile = open_winamp_streamfile_by_file(newfile,filename);
if (newstreamFile) {
return newstreamFile;
}
// failure, close it and try the default path (which will probably fail a second time)
fclose(newfile);
}
}
/* STREAMFILEs carry char/UTF8 names, convert to wchar for Winamp */
wa_char_to_wchar(wpath,PATH_LIMIT, filename);
return open_winamp_streamfile_by_wpath(wpath);
}
static void wasf_close(WINAMP_STREAMFILE *streamfile) {
/* closes infile_ref + frees in the internal STDIOSTREAMFILE (fclose for wchar is not needed) */
streamfile->stdiosf->close(streamfile->stdiosf);
free(streamfile); /* and the current struct */
}
static STREAMFILE *open_winamp_streamfile_by_file(FILE *infile, const char * path) {
WINAMP_STREAMFILE *streamfile = NULL;
STREAMFILE *stdiosf = NULL;
streamfile = calloc(1,sizeof(WINAMP_STREAMFILE));
if (!streamfile) goto fail;
stdiosf = open_stdio_streamfile_by_file(infile,path);
if (!stdiosf) goto fail;
streamfile->sf.read = (void*)wasf_read;
streamfile->sf.get_size = (void*)wasf_get_size;
streamfile->sf.get_offset = (void*)wasf_get_offset;
streamfile->sf.get_name = (void*)wasf_get_name;
streamfile->sf.get_realname = (void*)wasf_get_realname;
streamfile->sf.open = (void*)wasf_open;
streamfile->sf.close = (void*)wasf_close;
streamfile->stdiosf = stdiosf;
streamfile->infile_ref = infile;
return &streamfile->sf; /* pointer to STREAMFILE start = rest of the custom data follows */
fail:
close_streamfile(stdiosf);
free(streamfile);
return NULL;
}
static STREAMFILE *open_winamp_streamfile_by_wpath(const in_char *wpath) {
FILE *infile = NULL;
STREAMFILE *streamFile;
char path[PATH_LIMIT];
/* open a FILE from a Winamp (possibly UTF-16) path */
infile = wa_fopen(wpath);
if (!infile) return NULL;
/* convert to UTF-8 if needed for internal use */
wa_wchar_to_char(path,PATH_LIMIT, wpath);
streamFile = open_winamp_streamfile_by_file(infile,path);
if (!streamFile) {
fclose(infile);
}
return streamFile;
}
/* opens vgmstream for winamp */
static VGMSTREAM* init_vgmstream_winamp(const in_char *fn) {
VGMSTREAM * vgmstream = NULL;
//return init_vgmstream(fn);
/* manually init streamfile to pass the stream index */
STREAMFILE *streamFile = open_winamp_streamfile_by_wpath(fn); //open_stdio_streamfile(fn);
if (streamFile) {
vgmstream = init_vgmstream_from_STREAMFILE(streamFile);
close_streamfile(streamFile);
}
return vgmstream;
}
/* ************************************* */
/* Winamp INI reader */
static void GetINIFileName(char * iniFile) {
@ -99,21 +289,21 @@ static void GetINIFileName(char * iniFile) {
if(IsWindow(input_module.hMainWindow) && SendMessage(input_module.hMainWindow, WM_WA_IPC,0,IPC_GETVERSION) >= 0x5000) {
char * iniDir = (char *)SendMessage(input_module.hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORY);
strncpy(iniFile, iniDir, WINAMP_MAX_PATH);
strncpy(iniFile, iniDir, PATH_LIMIT);
strncat(iniFile, "\\Plugins\\", WINAMP_MAX_PATH);
strncat(iniFile, "\\Plugins\\", PATH_LIMIT);
/* can't be certain that \Plugins already exists in the user dir */
CreateDirectory(iniFile,NULL);
strncat(iniFile, INI_NAME, WINAMP_MAX_PATH);
strncat(iniFile, CONFIG_INI_NAME, PATH_LIMIT);
}
else {
char * lastSlash;
GetModuleFileName(NULL, iniFile, WINAMP_MAX_PATH);
GetModuleFileName(NULL, iniFile, PATH_LIMIT);
lastSlash = strrchr(iniFile, '\\');
*(lastSlash + 1) = 0;
strncat(iniFile, "Plugins\\" INI_NAME,WINAMP_MAX_PATH);
strncat(iniFile, "Plugins\\" CONFIG_INI_NAME,PATH_LIMIT);
}
}
@ -175,25 +365,17 @@ static void build_extension_list() {
/* unicode utils */
static void copy_title(in_char * dst, int dst_size, const in_char * src) {
#ifdef UNICODE_INPUT_PLUGIN
in_char *p = (in_char*)src + wcslen(src); /* find end */
in_char *p = (in_char*)src + wa_strlen(src); /* find end */
while (*p != '\\' && p >= src) /* and find last "\" */
p--;
p++;
wcscpy(dst,p); /* copy filename only */
#else
in_char *p = (in_char*)src + strlen(src); /* find end */
while (*p != '\\' && p >= src) /* and find last "\" */
p--;
p++;
strcpy(dst,p); /* copy filename only */
#endif
wa_strcpy(dst,p); /* copy filename only */
}
/* ***************************************** */
/* about dialog */
void about(HWND hwndParent) {
void winamp_About(HWND hwndParent) {
MessageBox(hwndParent,
PLUGIN_DESCRIPTION "\n"
"by hcs, FastElbja, manakoAT, bxaimc, snakemeat, soneek, kode54, bnnm and many others\n"
@ -206,116 +388,108 @@ void about(HWND hwndParent) {
}
/* called at program init */
void init() {
char iniFile[WINAMP_MAX_PATH];
void winamp_Init() {
char iniFile[PATH_LIMIT];
char buf[256];
int consumed;
GetINIFileName(iniFile);
thread_priority=GetPrivateProfileInt(APP_NAME,THREAD_PRIORITY_INI_ENTRY,DEFAULT_THREAD_PRIORITY,iniFile);
thread_priority = GetPrivateProfileInt(CONFIG_APP_NAME,THREAD_PRIORITY_INI_ENTRY,DEFAULT_THREAD_PRIORITY,iniFile);
if (thread_priority < 0 || thread_priority > 6) {
sprintf(buf,"%d",DEFAULT_THREAD_PRIORITY);
WritePrivateProfileString(APP_NAME,THREAD_PRIORITY_INI_ENTRY,buf,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,THREAD_PRIORITY_INI_ENTRY,buf,iniFile);
thread_priority = DEFAULT_THREAD_PRIORITY;
}
GetPrivateProfileString(APP_NAME,FADE_SECONDS_INI_ENTRY,DEFAULT_FADE_SECONDS,buf,sizeof(buf),iniFile);
GetPrivateProfileString(CONFIG_APP_NAME,FADE_SECONDS_INI_ENTRY,DEFAULT_FADE_SECONDS,buf,sizeof(buf),iniFile);
if (sscanf(buf,"%lf%n",&fade_seconds,&consumed)<1 || consumed!=strlen(buf) || fade_seconds < 0) {
WritePrivateProfileString(APP_NAME,FADE_SECONDS_INI_ENTRY,DEFAULT_FADE_SECONDS,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,FADE_SECONDS_INI_ENTRY,DEFAULT_FADE_SECONDS,iniFile);
sscanf(DEFAULT_FADE_SECONDS,"%lf",&fade_seconds);
}
GetPrivateProfileString(APP_NAME,FADE_DELAY_SECONDS_INI_ENTRY,DEFAULT_FADE_DELAY_SECONDS,buf,sizeof(buf),iniFile);
GetPrivateProfileString(CONFIG_APP_NAME,FADE_DELAY_SECONDS_INI_ENTRY,DEFAULT_FADE_DELAY_SECONDS,buf,sizeof(buf),iniFile);
if (sscanf(buf,"%lf%n",&fade_delay_seconds,&consumed)<1 || consumed!=strlen(buf)) {
WritePrivateProfileString(APP_NAME,FADE_DELAY_SECONDS_INI_ENTRY,DEFAULT_FADE_DELAY_SECONDS,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,FADE_DELAY_SECONDS_INI_ENTRY,DEFAULT_FADE_DELAY_SECONDS,iniFile);
sscanf(DEFAULT_FADE_DELAY_SECONDS,"%lf",&fade_delay_seconds);
}
GetPrivateProfileString(APP_NAME,LOOP_COUNT_INI_ENTRY,DEFAULT_LOOP_COUNT,buf,sizeof(buf),iniFile);
GetPrivateProfileString(CONFIG_APP_NAME,LOOP_COUNT_INI_ENTRY,DEFAULT_LOOP_COUNT,buf,sizeof(buf),iniFile);
if (sscanf(buf,"%lf%n",&loop_count,&consumed)!=1 || consumed!=strlen(buf) || loop_count < 0) {
WritePrivateProfileString(APP_NAME,LOOP_COUNT_INI_ENTRY,DEFAULT_LOOP_COUNT,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,LOOP_COUNT_INI_ENTRY,DEFAULT_LOOP_COUNT,iniFile);
sscanf(DEFAULT_LOOP_COUNT,"%lf",&loop_count);
}
loop_forever=GetPrivateProfileInt(APP_NAME,LOOP_FOREVER_INI_ENTRY,DEFAULT_LOOP_FOREVER,iniFile);
ignore_loop=GetPrivateProfileInt(APP_NAME,IGNORE_LOOP_INI_ENTRY,DEFAULT_IGNORE_LOOP,iniFile);
loop_forever = GetPrivateProfileInt(CONFIG_APP_NAME,LOOP_FOREVER_INI_ENTRY,DEFAULT_LOOP_FOREVER,iniFile);
ignore_loop = GetPrivateProfileInt(CONFIG_APP_NAME,IGNORE_LOOP_INI_ENTRY,DEFAULT_IGNORE_LOOP,iniFile);
if (loop_forever && ignore_loop) {
sprintf(buf,"%d",DEFAULT_LOOP_FOREVER);
WritePrivateProfileString(APP_NAME,LOOP_FOREVER_INI_ENTRY,buf,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,LOOP_FOREVER_INI_ENTRY,buf,iniFile);
loop_forever = DEFAULT_LOOP_FOREVER;
sprintf(buf,"%d",DEFAULT_IGNORE_LOOP);
WritePrivateProfileString(APP_NAME,IGNORE_LOOP_INI_ENTRY,buf,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,IGNORE_LOOP_INI_ENTRY,buf,iniFile);
ignore_loop = DEFAULT_IGNORE_LOOP;
}
/* dynamically make a list of supported extensions */
build_extension_list();
}
/* called at program quit */
void quit() {
void winamp_Quit() {
}
/* called before extension checks, to allow detection of mms://, etc */
int isourfile(const in_char *fn) {
int winamp_IsOurFile(const in_char *fn) {
return 0; /* we don't recognize protocols */
}
/* request to start playing a file */
int play(const in_char *fn) {
int winamp_Play(const in_char *fn) {
int max_latency;
/* don't lose a pointer! */
if (vgmstream) {
/* TODO: this should either pop up an error box or close the file */
return 1; /* error */
}
if (vgmstream)
return 1; // TODO: this should either pop up an error box or close the file
/* open the stream, set up */
vgmstream = init_vgmstream(fn);
/* were we able to open it? */
if (!vgmstream) {
/* open the stream */
vgmstream = init_vgmstream_winamp(fn);
if (!vgmstream)
return 1;
}
if (ignore_loop) vgmstream->loop_flag = 0;
/* will we be able to play it? */
if (vgmstream->channels <= 0) {
close_vgmstream(vgmstream);
vgmstream=NULL;
return 1; /* error */
}
/* Remember that name, friends! */
strncpy(lastfn,fn,WINAMP_MAX_PATH);
/* config */
if (ignore_loop)
vgmstream->loop_flag = 0;
/* save original name */
wa_strncpy(lastfn,fn,PATH_LIMIT);
/* open the output plugin */
max_latency = input_module.outMod->Open(vgmstream->sample_rate,vgmstream->channels,
16, 0, 0);
/* were we able to open it? */
max_latency = input_module.outMod->Open(vgmstream->sample_rate,vgmstream->channels, 16, 0, 0);
if (max_latency < 0) {
close_vgmstream(vgmstream);
vgmstream=NULL;
return 1; /* error */
vgmstream = NULL;
return 1;
}
/* Set info display */
/* TODO: actual bitrate */
/* set info display */ //TODO: actual bitrate
input_module.SetInfo(get_vgmstream_average_bitrate(vgmstream)/1000,vgmstream->sample_rate/1000,vgmstream->channels,1);
/* setup visualization */
input_module.SAVSAInit(max_latency,vgmstream->sample_rate);
input_module.VSASetInfo(vgmstream->sample_rate,vgmstream->channels);
/* reset internals */
decode_abort = 0;
seek_needed_samples = -1;
decode_pos_ms = 0;
decode_pos_samples = 0;
paused = 0;
stream_length_samples = get_vgmstream_play_samples(loop_count,fade_seconds,fade_delay_seconds,vgmstream);
fade_samples = (int)(fade_seconds * vgmstream->sample_rate);
/* start */
decode_thread_handle = CreateThread(
NULL, /* handle cannot be inherited */
0, /* stack size, 0=default */
@ -330,129 +504,141 @@ int play(const in_char *fn) {
}
/* pause stream */
void pause() {
paused=1;
void winamp_Pause() {
paused = 1;
input_module.outMod->Pause(1);
}
/* unpause stream */
void unpause() {
paused=0;
void winamp_UnPause() {
paused = 0;
input_module.outMod->Pause(0);
}
/* ispaused? return 1 if paused, 0 if not */
int ispaused() {
/* return 1 if paused, 0 if not */
int winamp_IsPaused() {
return paused;
}
/* stop (unload) stream */
void stop() {
void winamp_Stop() {
if (decode_thread_handle != INVALID_HANDLE_VALUE) {
decode_abort=1;
decode_abort = 1;
/* arbitrary wait length */
if (WaitForSingleObject(decode_thread_handle,1000) == WAIT_TIMEOUT) {
/* TODO: error? */
TerminateThread(decode_thread_handle,0);
TerminateThread(decode_thread_handle,0); // TODO: error?
}
CloseHandle(decode_thread_handle);
decode_thread_handle = INVALID_HANDLE_VALUE;
}
if (vgmstream) {
close_vgmstream(vgmstream);
vgmstream=NULL;
}
close_vgmstream(vgmstream);
vgmstream = NULL;
input_module.outMod->Close();
input_module.SAVSADeInit();
}
/* get length in ms */
int getlength() {
return stream_length_samples*1000LL/vgmstream->sample_rate;
int winamp_GetLength() {
return stream_length_samples * 1000LL / vgmstream->sample_rate;
}
/* current output time in ms */
int getoutputtime() {
return decode_pos_ms+(input_module.outMod->GetOutputTime()-input_module.outMod->GetWrittenTime());
int winamp_GetOutputTime() {
return decode_pos_ms + (input_module.outMod->GetOutputTime()-input_module.outMod->GetWrittenTime());
}
/* seeks to point in stream (in ms) */
void setoutputtime(int t) {
void winamp_SetOutputTime(int time_in_ms) {
if (vgmstream)
seek_needed_samples = (long long)t * vgmstream->sample_rate / 1000LL;
seek_needed_samples = (long long)time_in_ms * vgmstream->sample_rate / 1000LL;
}
/* pass these commands through */
void setvolume(int volume) {
void winamp_SetVolume(int volume) {
input_module.outMod->SetVolume(volume);
}
void setpan(int pan) {
void winamp_SetPan(int pan) {
input_module.outMod->SetPan(pan);
}
/* display information */
int infoDlg(const in_char *fn, HWND hwnd) {
VGMSTREAM * infostream = NULL;
/* display info box (ALT+3) */
int winamp_InfoBox(const in_char *fn, HWND hwnd) {
char description[1024] = {0};
concatn(sizeof(description),description,PLUGIN_DESCRIPTION "\n\n");
if (!fn || !*fn) {
if (!vgmstream) return 0;
/* no filename = current playing file */
if (!vgmstream)
return 0;
describe_vgmstream(vgmstream,description,sizeof(description));
} else {
infostream = init_vgmstream((char*)fn);
if (!infostream) return 0;
}
else {
/* some other file in playlist given by filename */
VGMSTREAM * infostream = NULL;
infostream = init_vgmstream_winamp(fn);
if (!infostream)
return 0;
describe_vgmstream(infostream,description,sizeof(description));
close_vgmstream(infostream);
infostream=NULL;
infostream = NULL;
}
MessageBox(hwnd,description,"Stream info",MB_OK);
return 0;
}
/* retrieve information on this or possibly another file */
void getfileinfo(const in_char *filename, in_char *title, int *length_in_ms) {
/* retrieve title (playlist name) and time on the current or other file in the playlist */
void winamp_GetFileInfo(const in_char *fn, in_char *title, int *length_in_ms) {
if (!fn || !*fn) {
/* no filename = current playing file */
if (!filename || !*filename) /* no filename = use currently playing file */
{
if (!vgmstream)
return;
if (length_in_ms)
*length_in_ms = getlength();
if (title) {
copy_title(title,GETFILEINFO_TITLE_LENGTH, lastfn);
}
}
else /* some other file */
{
VGMSTREAM * infostream;
if (length_in_ms) {
*length_in_ms=-1000;
*length_in_ms = winamp_GetLength();
}
}
else {
/* some other file in playlist given by filename */
VGMSTREAM * infostream = NULL;
if ((infostream=init_vgmstream(filename))) {
*length_in_ms = get_vgmstream_play_samples(loop_count,fade_seconds,fade_delay_seconds,infostream)*1000LL/infostream->sample_rate;
infostream = init_vgmstream_winamp(fn);
close_vgmstream(infostream);
infostream=NULL;
if (title) {
copy_title(title,GETFILEINFO_TITLE_LENGTH, fn);
}
if (length_in_ms) {
*length_in_ms = -1000;
if (infostream) {
int num_samples = get_vgmstream_play_samples(loop_count,fade_seconds,fade_delay_seconds,infostream);
*length_in_ms = num_samples * 1000LL /infostream->sample_rate;
}
}
if (title) {
copy_title(title,GETFILEINFO_TITLE_LENGTH, filename);
}
close_vgmstream(infostream);
infostream = NULL;
}
}
/* eq stuff */
void eq_set(int on, char data[10], int preamp) {
/* nothin' */
void winamp_EQSet(int on, char data[10], int preamp) {
}
/* the decode thread */
@ -461,14 +647,14 @@ DWORD WINAPI __stdcall decode(void *arg) {
int max_buffer_samples = sizeof(sample_buffer)/sizeof(sample_buffer[0])/2/vgmstream->channels;
while (!decode_abort) {
int samples_to_do;
int l;
if (decode_pos_samples+max_buffer_samples>stream_length_samples && (!loop_forever || !vgmstream->loop_flag))
samples_to_do=stream_length_samples-decode_pos_samples;
if (decode_pos_samples + max_buffer_samples > stream_length_samples
&& (!loop_forever || !vgmstream->loop_flag))
samples_to_do = stream_length_samples - decode_pos_samples;
else
samples_to_do=max_buffer_samples;
samples_to_do = max_buffer_samples;
/* play 'till the end of this seek, or note if we're done seeking */
if (seek_needed_samples != -1) {
@ -476,29 +662,29 @@ DWORD WINAPI __stdcall decode(void *arg) {
if (seek_needed_samples < decode_pos_samples) {
reset_vgmstream(vgmstream);
if (ignore_loop) vgmstream->loop_flag = 0;
if (ignore_loop)
vgmstream->loop_flag = 0;
decode_pos_samples = 0;
decode_pos_ms = 0;
}
if (decode_pos_samples < seek_needed_samples) {
samples_to_do=seek_needed_samples-decode_pos_samples;
if (samples_to_do>max_buffer_samples) samples_to_do=max_buffer_samples;
samples_to_do = seek_needed_samples-decode_pos_samples;
if (samples_to_do > max_buffer_samples)
samples_to_do = max_buffer_samples;
} else
seek_needed_samples = -1;
input_module.outMod->Flush((int)decode_pos_ms);
}
l = (samples_to_do*vgmstream->channels*2)<<(input_module.dsp_isactive()?1:0);
l = (samples_to_do*vgmstream->channels*2) << (input_module.dsp_isactive()?1:0);
if (samples_to_do == 0) {
input_module.outMod->CanWrite(); /* ? */
if (!input_module.outMod->IsPlaying()) {
PostMessage(input_module.hMainWindow, /* message dest */
WM_WA_MPEG_EOF, /* message id */
0,0); /* no parameters */
PostMessage(input_module.hMainWindow, WM_WA_MPEG_EOF, 0,0); /* end */
return 0;
}
Sleep(10);
@ -506,8 +692,8 @@ DWORD WINAPI __stdcall decode(void *arg) {
else if (seek_needed_samples != -1) {
render_vgmstream(sample_buffer,samples_to_do,vgmstream);
decode_pos_samples+=samples_to_do;
decode_pos_ms=decode_pos_samples*1000LL/vgmstream->sample_rate;
decode_pos_samples += samples_to_do;
decode_pos_ms = decode_pos_samples * 1000LL / vgmstream->sample_rate;
}
else if (input_module.outMod->CanWrite() >= l) {
/* let vgmstream do its thing */
@ -518,10 +704,10 @@ DWORD WINAPI __stdcall decode(void *arg) {
int samples_into_fade = decode_pos_samples - (stream_length_samples - fade_samples);
if (samples_into_fade + samples_to_do > 0) {
int j,k;
for (j=0;j<samples_to_do;j++,samples_into_fade++) {
for (j=0; j < samples_to_do; j++, samples_into_fade++) {
if (samples_into_fade > 0) {
double fadedness = (double)(fade_samples-samples_into_fade)/fade_samples;
for (k=0;k<vgmstream->channels;k++) {
for (k=0; k < vgmstream->channels; k++) {
sample_buffer[j*vgmstream->channels+k] =
(short)(sample_buffer[j*vgmstream->channels+k]*fadedness);
}
@ -532,22 +718,25 @@ DWORD WINAPI __stdcall decode(void *arg) {
input_module.SAAddPCMData((char*)sample_buffer,vgmstream->channels,16,decode_pos_ms);
input_module.VSAAddPCMData((char*)sample_buffer,vgmstream->channels,16,decode_pos_ms);
decode_pos_samples+=samples_to_do;
decode_pos_ms=decode_pos_samples*1000LL/vgmstream->sample_rate;
decode_pos_samples += samples_to_do;
decode_pos_ms = decode_pos_samples*1000LL/vgmstream->sample_rate;
if (input_module.dsp_isactive())
l =input_module.dsp_dosamples(sample_buffer,samples_to_do,16,vgmstream->channels,vgmstream->sample_rate) *
2 * vgmstream->channels;
l = input_module.dsp_dosamples(sample_buffer,samples_to_do,16,vgmstream->channels,vgmstream->sample_rate) * 2 * vgmstream->channels;
input_module.outMod->Write((char*)sample_buffer,l);
} /* if we can write enough */
else Sleep(20);
else {
Sleep(20);
}
} /* main loop */
return 0;
}
/* config dialog handler */
INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
char buf[256];
char iniFile[WINAMP_MAX_PATH];
char iniFile[PATH_LIMIT];
static int mypri;
HANDLE hSlider;
@ -630,27 +819,27 @@ INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
thread_priority=mypri;
sprintf(buf,"%d",thread_priority);
WritePrivateProfileString(APP_NAME,THREAD_PRIORITY_INI_ENTRY,buf,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,THREAD_PRIORITY_INI_ENTRY,buf,iniFile);
fade_seconds = temp_fade_seconds;
sprintf(buf,"%.2lf",fade_seconds);
WritePrivateProfileString(APP_NAME,FADE_SECONDS_INI_ENTRY,buf,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,FADE_SECONDS_INI_ENTRY,buf,iniFile);
fade_delay_seconds = temp_fade_delay_seconds;
sprintf(buf,"%.2lf",fade_delay_seconds);
WritePrivateProfileString(APP_NAME,FADE_DELAY_SECONDS_INI_ENTRY,buf,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,FADE_DELAY_SECONDS_INI_ENTRY,buf,iniFile);
loop_count = temp_loop_count;
sprintf(buf,"%.2lf",loop_count);
WritePrivateProfileString(APP_NAME,LOOP_COUNT_INI_ENTRY,buf,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,LOOP_COUNT_INI_ENTRY,buf,iniFile);
loop_forever = (IsDlgButtonChecked(hDlg,IDC_LOOP_FOREVER) == BST_CHECKED);
sprintf(buf,"%d",loop_forever);
WritePrivateProfileString(APP_NAME,LOOP_FOREVER_INI_ENTRY,buf,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,LOOP_FOREVER_INI_ENTRY,buf,iniFile);
ignore_loop = (IsDlgButtonChecked(hDlg,IDC_IGNORE_LOOP) == BST_CHECKED);
sprintf(buf,"%d",ignore_loop);
WritePrivateProfileString(APP_NAME,IGNORE_LOOP_INI_ENTRY,buf,iniFile);
WritePrivateProfileString(CONFIG_APP_NAME,IGNORE_LOOP_INI_ENTRY,buf,iniFile);
}
EndDialog(hDlg,TRUE);
@ -696,7 +885,7 @@ INT_PTR CALLBACK configDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
}
/* configuration dialog */
void config(HWND hwndParent) {
void winamp_Config(HWND hwndParent) {
/* defined in resource.rc */
DialogBox(input_module.hDllInstance, (const char *)IDD_CONFIG, hwndParent, configDlgProc);
}
@ -707,33 +896,33 @@ void config(HWND hwndParent) {
In_Module input_module = {
IN_VER,
PLUGIN_DESCRIPTION,
0, /* hMainWindow */
0, /* hDllInstance */
0, /* hMainWindow (filled in by Winamp) */
0, /* hDllInstance (filled in by Winamp) */
working_extension_list,
1, /* is_seekable */
1, /* uses output */
config,
about,
init,
quit,
getfileinfo,
infoDlg,
isourfile,
play,
pause,
unpause,
ispaused,
stop,
getlength,
getoutputtime,
setoutputtime,
setvolume,
setpan,
0,0,0,0,0,0,0,0,0, // vis stuff
0,0, // dsp
eq_set,
NULL, // setinfo
0 // out_mod
1, /* is_seekable flag */
1, /* UsesOutputPlug flag */
winamp_Config,
winamp_About,
winamp_Init,
winamp_Quit,
winamp_GetFileInfo,
winamp_InfoBox,
winamp_IsOurFile,
winamp_Play,
winamp_Pause,
winamp_UnPause,
winamp_IsPaused,
winamp_Stop,
winamp_GetLength,
winamp_GetOutputTime,
winamp_SetOutputTime,
winamp_SetVolume,
winamp_SetPan,
0,0,0,0,0,0,0,0,0, /* vis stuff */
0,0, /* dsp stuff */
winamp_EQSet,
NULL, /* SetInfo */
0 /* outMod */
};
__declspec( dllexport ) In_Module * winampGetInModule2() {

56
winamp/ipc_pe.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef __IPC_PE_H
#define __IPC_PE_H
#define IPC_PE_GETCURINDEX 100 // returns current idx
#define IPC_PE_GETINDEXTOTAL 101 // returns number of items
#define IPC_PE_GETINDEXINFO 102 // (copydata) lpData is of type callbackinfo, callback is called with copydata/fileinfo structure and msg IPC_PE_GETINDEXINFORESULT
#define IPC_PE_GETINDEXINFORESULT 103 // callback message for IPC_PE_GETINDEXINFO
#define IPC_PE_DELETEINDEX 104 // lParam = index
#define IPC_PE_SWAPINDEX 105 // (lParam & 0xFFFF0000) >> 16 = from, (lParam & 0xFFFF) = to
#define IPC_PE_INSERTFILENAME 106 // (copydata) lpData is of type fileinfo
#define IPC_PE_GETDIRTY 107 // returns 1 if the playlist changed since the last IPC_PE_SETCLEAN
#define IPC_PE_SETCLEAN 108 // resets the dirty flag until next modification
#define IPC_PE_GETIDXFROMPOINT 109 // pass a point parm, return a playlist index
#define IPC_PE_SAVEEND 110 // pass index to save from
#define IPC_PE_RESTOREEND 111 // no parm
#define IPC_PE_GETNEXTSELECTED 112 // same as IPC_PLAYLIST_GET_NEXT_SELECTED for the main window
#define IPC_PE_GETSELECTEDCOUNT 113
#define IPC_PE_INSERTFILENAMEW 114 // (copydata) lpData is of type fileinfoW
#define IPC_PE_GETINDEXINFO_TITLE 115 // like IPC_PE_GETINDEXINFO, but writes the title to char file[MAX_PATH] instead of filename
#define IPC_PE_GETINDEXINFORESULT_TITLE 116 // callback message for IPC_PE_GETINDEXINFO
typedef struct {
char file[MAX_PATH];
int index;
} fileinfo;
typedef struct {
wchar_t file[MAX_PATH];
int index;
} fileinfoW;
typedef struct {
HWND callback;
int index;
} callbackinfo;
// the following messages are in_process ONLY
#define IPC_PE_GETINDEXTITLE 200 // lParam = pointer to fileinfo2 struct
#define IPC_PE_GETINDEXTITLEW 201 // lParam = pointer to fileinfo2W struct
#define IPC_PE_GETINDEXINFO_INPROC 202 // lParam = pointer to fileinfo struct
#define IPC_PE_GETINDEXINFOW_INPROC 203 // lParam = pointer to fileinfoW struct
typedef struct {
int fileindex;
char filetitle[256];
char filelength[16];
} fileinfo2;
typedef struct
{
int fileindex;
wchar_t filetitle[256];
wchar_t filelength[16];
} fileinfo2W;
#endif

View File

@ -23,147 +23,207 @@
#define VERSION "(unknown version)"
#endif
/* ************************************* */
/* XMPlay extension list, only needed to associate extensions in Windows */
/* todo: as of v3.8.2.17, any more than ~1000 will crash XMplay's file list screen (but not using the non-native Winamp plugin...) */
#define EXTENSION_LIST_SIZE 1000 /*VGM_EXTENSION_LIST_CHAR_SIZE * 2*/
#define XMPLAY_MAX_PATH 32768
/* XMPlay function library */
static XMPFUNC_IN *xmpfin;
static XMPFUNC_MISC *xmpfmisc;
static XMPFUNC_FILE *xmpffile;
/* ************************************* */
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
typedef struct _XMPSTREAMFILE {
STREAMFILE sf;
XMPFILE file;
off_t offset;
char name[PATH_LIMIT];
} XMPSTREAMFILE;
/* plugin config */
double fade_seconds = 10.0;
double fade_delay_seconds = 10.0;
double loop_count = 2.0;
int disable_subsongs = 1;
static void xmpsf_seek(XMPSTREAMFILE *this, off_t offset) {
if (xmpffile->Seek(this->file, offset))
this->offset = offset;
else
this->offset = xmpffile->Tell(this->file);
}
static off_t xmpsf_get_size(XMPSTREAMFILE *this)
{
return xmpffile->GetSize(this->file);
}
static off_t xmpsf_get_offset(XMPSTREAMFILE *this)
{
return xmpffile->Tell(this->file);
}
static void xmpsf_get_name(XMPSTREAMFILE *this, char *buffer, size_t length)
{
strncpy(buffer, this->name, length);
buffer[length - 1] = '\0';
}
static size_t xmpsf_read(XMPSTREAMFILE *this, uint8_t *dest, off_t offset, size_t length)
{
size_t read;
if (this->offset != offset)
xmpsf_seek(this, offset);
read = xmpffile->Read(this->file, dest, length);
if (read > 0)
this->offset += read;
return read;
}
static void xmpsf_close(XMPSTREAMFILE *this)
{
// The line below is what Causes this Plugin to Crash. Credits to Ian Luck to Finding this issue.
// This closes the internal XMPFILE, which must be done by XMPlay instead.
// However vgmtream sometimes opens its own files, so it may be leaking handles.
//xmpffile->Close(this->file);
free(this);
}
static STREAMFILE *xmpsf_create_from_path(const char *path);
static STREAMFILE *xmpsf_open(XMPSTREAMFILE *this, const char *const filename, size_t buffersize)
{
if (!filename) return NULL;
return xmpsf_create_from_path(filename);
}
static STREAMFILE *xmpsf_create(XMPFILE file, const char *path)
{
XMPSTREAMFILE *streamfile = malloc(sizeof(XMPSTREAMFILE));
if (!streamfile) return NULL;
memset(streamfile, 0, sizeof(XMPSTREAMFILE));
streamfile->sf.read = (void*)xmpsf_read;
streamfile->sf.get_size = (void*)xmpsf_get_size;
streamfile->sf.get_offset = (void*)xmpsf_get_offset;
streamfile->sf.get_name = (void*)xmpsf_get_name;
streamfile->sf.get_realname = (void*)xmpsf_get_name;
streamfile->sf.open = (void*)xmpsf_open;
streamfile->sf.close = (void*)xmpsf_close;
streamfile->file = file;
streamfile->offset = 0;
strncpy(streamfile->name, path, sizeof(streamfile->name));
return &streamfile->sf;
}
STREAMFILE *xmpsf_create_from_path(const char *path)
{
XMPFILE file = xmpffile->Open(path);
if (!file)
return NULL;
return xmpsf_create(file, path);
}
// Probably not needed at all.
//VGMSTREAM *init_vgmstream_from_path(const char *path)
//{
// STREAMFILE *sf;
// VGMSTREAM *vgm;
//
// sf = xmpsf_create_from_path(path);
// if (!sf) return NULL;
//
// vgm = init_vgmstream_from_STREAMFILE(sf);
// if (!vgm) goto err1;
//
// return vgm;
//
//err1:
// xmpsf_close((XMPSTREAMFILE *)sf);
// return NULL;
//}
VGMSTREAM *init_vgmstream_from_xmpfile(XMPFILE file, const char *path)
{
STREAMFILE *sf;
VGMSTREAM *vgm;
sf = xmpsf_create(file, path);
if (!sf) return NULL;
vgm = init_vgmstream_from_STREAMFILE(sf);
if (!vgm) goto err1;
return vgm;
err1:
xmpsf_close((XMPSTREAMFILE *)sf);
return NULL;
}
/* ************************************* */
/* internal state */
/* plugin state */
VGMSTREAM * vgmstream = NULL;
int32_t totalFrames, framesDone, framesLength, framesFade;
int framesDone, framesLength;
int stream_length_samples = 0;
int fade_samples = 0;
#define APP_NAME "vgmstream plugin" //unused?
int current_subsong = 0;
//XMPFILE current_file = NULL;
//char current_fn[XMPLAY_MAX_PATH] = {0};
void __stdcall XMP_About(HWND hwParent) {
MessageBox(hwParent,
static int shownerror = 0; /* init error */
/* ************************************* */
/* a STREAMFILE that operates via XMPlay's XMPFUNC_FILE+XMPFILE */
typedef struct _XMPLAY_STREAMFILE {
STREAMFILE sf; /* callbacks */
XMPFILE infile; /* actual FILE */
char name[PATH_LIMIT];
off_t offset; /* current offset */
int internal_xmpfile; /* infile was not supplied externally and can be closed */
} XMPLAY_STREAMFILE;
static STREAMFILE *open_xmplay_streamfile_by_xmpfile(XMPFILE file, const char *path, int internal);
static size_t xmpsf_read(XMPLAY_STREAMFILE *this, uint8_t *dest, off_t offset, size_t length) {
size_t read;
if (this->offset != offset) {
if (xmpffile->Seek(this->infile, offset))
this->offset = offset;
else
this->offset = xmpffile->Tell(this->infile);
}
read = xmpffile->Read(this->infile, dest, length);
if (read > 0)
this->offset += read;
return read;
}
static off_t xmpsf_get_size(XMPLAY_STREAMFILE *this) {
return xmpffile->GetSize(this->infile);
}
static off_t xmpsf_get_offset(XMPLAY_STREAMFILE *this) {
return xmpffile->Tell(this->infile);
}
static void xmpsf_get_name(XMPLAY_STREAMFILE *this, char *buffer, size_t length) {
strncpy(buffer, this->name, length);
buffer[length-1] = '\0';
}
static STREAMFILE *xmpsf_open(XMPLAY_STREAMFILE *this, const char *const filename, size_t buffersize) {
XMPFILE newfile;
if (!filename)
return NULL;
newfile = xmpffile->Open(filename);
if (!newfile) return NULL;
return open_xmplay_streamfile_by_xmpfile(newfile, filename, 1); /* internal XMPFILE */
}
static void xmpsf_close(XMPLAY_STREAMFILE *this) {
/* Close XMPFILE, but only if we opened it (ex. for subfiles inside metas).
* Otherwise must be left open as other parts of XMPlay need it and would crash. */
if (this->internal_xmpfile) {
xmpffile->Close(this->infile);
}
free(this);
}
static STREAMFILE *open_xmplay_streamfile_by_xmpfile(XMPFILE infile, const char *path, int internal) {
XMPLAY_STREAMFILE *streamfile = calloc(1,sizeof(XMPLAY_STREAMFILE));
if (!streamfile) return NULL;
streamfile->sf.read = (void*)xmpsf_read;
streamfile->sf.get_size = (void*)xmpsf_get_size;
streamfile->sf.get_offset = (void*)xmpsf_get_offset;
streamfile->sf.get_name = (void*)xmpsf_get_name;
streamfile->sf.get_realname = (void*)xmpsf_get_name;
streamfile->sf.open = (void*)xmpsf_open;
streamfile->sf.close = (void*)xmpsf_close;
streamfile->infile = infile;
streamfile->offset = 0;
strncpy(streamfile->name, path, sizeof(streamfile->name));
streamfile->internal_xmpfile = internal;
return &streamfile->sf; /* pointer to STREAMFILE start = rest of the custom data follows */
}
VGMSTREAM *init_vgmstream_xmplay(XMPFILE file, const char *path, int subsong) {
STREAMFILE *streamfile = NULL;
VGMSTREAM *vgmstream = NULL;
streamfile = open_xmplay_streamfile_by_xmpfile(file, path, 0); /* external XMPFILE */
if (!streamfile) return NULL;
streamfile->stream_index = subsong;
vgmstream = init_vgmstream_from_STREAMFILE(streamfile);
if (!vgmstream) goto fail;
return vgmstream;
fail:
xmpsf_close((XMPLAY_STREAMFILE *)streamfile);
return NULL;
}
/* ************************************* */
#if 0
/* get the tags as an array of "key\0value\0", NULL-terminated */
static char *get_tags(VGMSTREAM * infostream) {
char *tags;
size_t tag_number = 20; // ?
tags = (char*)xmpfmisc->Alloc(tag_number+1);
for (...) {
...
}
tags[tag_number]=0; // terminating NULL
return tags; /* assuming XMPlay free()s this, since it Alloc()s it */
}
#endif
/* Adds ext to XMPlay's extension list. */
static int add_extension(int length, char * dst, const char * ext) {
int ext_len;
int i;
if (length <= 1)
return 0;
ext_len = strlen(ext);
/* check if end reached or not enough room to add */
if (ext_len+2 > length-2) {
dst[0]='\0';
return 0;
}
/* copy new extension + null terminate */
for (i=0; i < ext_len; i++)
dst[i] = ext[i];
dst[i]='/';
dst[i+1]='\0';
return i+1;
}
/* Creates XMPlay's extension list, a single string with 2 nulls.
* Extensions must be in this format: "Description\0extension1/.../extensionN" */
static void build_extension_list() {
const char ** ext_list;
int ext_list_len;
int i, written;
written = sprintf(working_extension_list, "%s%c", "vgmstream files",'\0');
ext_list = vgmstream_get_formats();
ext_list_len = vgmstream_get_formats_length();
for (i=0; i < ext_list_len; i++) {
written += add_extension(EXTENSION_LIST_SIZE-written, working_extension_list + written, ext_list[i]);
}
working_extension_list[written-1] = '\0'; /* remove last "/" */
}
/* ************************************* */
/* info for the "about" button in plugin options */
void WINAPI xmplay_About(HWND win) {
MessageBox(win,
"vgmstream plugin " VERSION " " __DATE__ "\n"
"by hcs, FastElbja, manakoAT, bxaimc, snakemeat, soneek, kode54, bnnm and many others\n"
"\n"
@ -174,177 +234,113 @@ void __stdcall XMP_About(HWND hwParent) {
,"about xmp-vgmstream",MB_OK);
}
void __stdcall XMP_Close() {
close_vgmstream(vgmstream);
vgmstream = NULL;
#if 0
/* present config options to user (OPTIONAL) */
void WINAPI xmplay_Config(HWND win) {
/* defined in resource.rc */
DialogBox(input_module.hDllInstance, (const char *)IDD_CONFIG, win, configDlgProc);
}
#endif
/* quick check if a file is playable by this plugin */
BOOL WINAPI xmplay_CheckFile(const char *filename, XMPFILE file) {
VGMSTREAM* infostream = NULL;
if (file)
infostream = init_vgmstream_xmplay(file, filename, 0);
else
infostream = init_vgmstream(filename); //TODO: unicode problems?
if (!infostream)
return FALSE;
close_vgmstream(infostream);
return TRUE;
}
BOOL __stdcall XMP_CheckFile(const char *filename, XMPFILE file) {
VGMSTREAM* thisvgmstream;
int ret;
if (file) thisvgmstream = init_vgmstream_from_xmpfile(file, filename);
else thisvgmstream = init_vgmstream(filename);
/* update info from a file, returning the number of subsongs */
DWORD WINAPI xmplay_GetFileInfo(const char *filename, XMPFILE file, float **length, char **tags) {
VGMSTREAM* infostream;
int subsong_count;
if (!thisvgmstream) ret = FALSE;
else { ret = TRUE; close_vgmstream(thisvgmstream); }
if (file)
infostream = init_vgmstream_xmplay(file, filename, 0);
else
infostream = init_vgmstream(filename); //TODO: unicode problems?
if (!infostream)
return 0;
return ret;
if (length && infostream->sample_rate) {
int stream_length_samples = get_vgmstream_play_samples(loop_count, fade_seconds, fade_delay_seconds, infostream);
float *lens = (float*)xmpfmisc->Alloc(sizeof(float));
lens[0] = (float)stream_length_samples / (float)infostream->sample_rate;
*length = lens;
}
subsong_count = infostream->num_streams;
if (disable_subsongs || subsong_count == 0)
subsong_count = 1;
close_vgmstream(infostream);
return subsong_count;
}
DWORD __stdcall XMP_GetFileInfo(const char *filename, XMPFILE file, float **length, char **tags)
{
VGMSTREAM* thisvgmstream;
if (file) thisvgmstream = init_vgmstream_from_xmpfile(file, filename);
else thisvgmstream = init_vgmstream(filename);
if (!thisvgmstream) return 0;
/* open a file for playback, returning: 0=failed, 1=success, 2=success and XMPlay can close the file */
DWORD WINAPI xmplay_Open(const char *filename, XMPFILE file) {
if (file)
vgmstream = init_vgmstream_xmplay(file, filename, current_subsong+1);
else
vgmstream = init_vgmstream(filename);
if (!vgmstream)
return 0;
if (length && thisvgmstream->sample_rate)
{
int totalFrames = get_vgmstream_play_samples(2.0, 10.0, 10.0, thisvgmstream);
float *lens = (float*)xmpfmisc->Alloc(sizeof(float));
lens[0] = (float)totalFrames / (float)thisvgmstream->sample_rate;
*length = lens;
}
framesDone = 0;
stream_length_samples = get_vgmstream_play_samples(loop_count, fade_seconds, fade_delay_seconds, vgmstream);
fade_samples = (int)(fade_seconds * vgmstream->sample_rate);
framesLength = stream_length_samples - fade_samples;
close_vgmstream(thisvgmstream);
//strncpy(current_fn,filename,XMPLAY_MAX_PATH);
//current_file = file;
//current_subsong = 0;
return 1;
if (stream_length_samples) {
float length = (float)stream_length_samples / (float)vgmstream->sample_rate;
xmpfin->SetLength(length, TRUE);
}
return 1;
}
DWORD __stdcall XMP_Open(const char *filename, XMPFILE file) {
if (file) vgmstream = init_vgmstream_from_xmpfile(file, filename);
else vgmstream = init_vgmstream(filename);
if (!vgmstream) return 0;
totalFrames = get_vgmstream_play_samples(2.0, 10.0, 10.0, vgmstream);
framesDone = 0;
framesFade = vgmstream->sample_rate * 10;
framesLength = totalFrames - framesFade;
if (totalFrames)
{
float length = (float)totalFrames / (float)vgmstream->sample_rate;
xmpfin->SetLength(length, TRUE);
}
return 1;
/* close the playback file */
void WINAPI xmplay_Close() {
close_vgmstream(vgmstream);
vgmstream = NULL;
}
DWORD __stdcall XMP_Process(float* buffer, DWORD bufsize) {
INT16 buf[1024];
UINT32 i, j, todo, done;
BOOL doLoop = xmpfin->GetLooping();
float *sbuf = buffer;
UINT32 samplesTodo;
bufsize /= vgmstream->channels;
samplesTodo = doLoop ? bufsize : totalFrames - framesDone;
if (samplesTodo > bufsize)
samplesTodo = bufsize;
done = 0;
while (done < samplesTodo) {
todo = 1024 / vgmstream->channels;
if (todo > samplesTodo - done)
todo = samplesTodo - done;
render_vgmstream(buf, todo, vgmstream);
for (i = 0, j = todo * vgmstream->channels; i < j; ++i)
{
*sbuf++ = buf[i] * 1.0f / 32768.0f;
}
done += todo;
}
sbuf = buffer;
if (!doLoop && framesDone + done > framesLength)
{
long fadeStart = (framesLength > framesDone) ? framesLength : framesDone;
long fadeEnd = (framesDone + done) > totalFrames ? totalFrames : (framesDone + done);
long fadePos;
float fadeScale = (float)(totalFrames - fadeStart) / framesFade;
float fadeStep = 1.0f / framesFade;
sbuf += (fadeStart - framesDone) * vgmstream->channels;
j = vgmstream->channels;
for (fadePos = fadeStart; fadePos < fadeEnd; ++fadePos)
{
for (i = 0; i < j; ++i)
{
sbuf[i] = sbuf[i] * fadeScale;
}
sbuf += j;
fadeScale -= fadeStep;
if (fadeScale <= 0.0f) break;
}
done = (int)(fadePos - framesDone);
}
framesDone += done;
return done * vgmstream->channels;
/* set the sample format */
void WINAPI xmplay_SetFormat(XMPFORMAT *form) {
form->res = 16 / 8; /* PCM 16 */
form->chan = vgmstream->channels;
form->rate = vgmstream->sample_rate;
}
void __stdcall XMP_SetFormat(XMPFORMAT *form) {
form->res = 16 / 8;
form->chan = vgmstream->channels;
form->rate = vgmstream->sample_rate;
}
char * __stdcall XMP_GetTags()
{
return NULL;
}
double __stdcall XMP_GetGranularity()
{
return 0.001;
}
double __stdcall XMP_SetPosition(DWORD pos) {
double cpos = (double)framesDone / (double)vgmstream->sample_rate;
double time = pos * XMP_GetGranularity();
if (time < cpos)
{
reset_vgmstream(vgmstream);
cpos = 0.0;
}
while (cpos < time)
{
INT16 buffer[1024];
long max_sample_count = 1024 / vgmstream->channels;
long samples_to_skip = (long)((time - cpos) * vgmstream->sample_rate);
if (samples_to_skip > max_sample_count)
samples_to_skip = max_sample_count;
if (!samples_to_skip)
break;
render_vgmstream(buffer, (int)samples_to_skip, vgmstream);
cpos += (double)samples_to_skip / (double)vgmstream->sample_rate;
}
framesDone = (int32_t)(cpos * vgmstream->sample_rate);
return cpos;
/* get tags, return NULL to delay title update (OPTIONAL) */
char * WINAPI xmplay_GetTags() {
return NULL; //get_tags(vgmstream);
}
/* main panel info text (short file info) */
void __stdcall XMP_GetInfoText(char* format, char* length) {
void WINAPI xmplay_GetInfoText(char* format, char* length) {
if (!format)
return;
return;
sprintf(format,"vgmstream");
/* length is the file time */
sprintf(format,"vgmstream");
/* length is the file time */
}
/* info for the "General" window/tab (buf is ~40K) */
void __stdcall XMP_GetGeneralInfo(char* buf) {
void WINAPI xmplay_GetGeneralInfo(char* buf) {
int i;
char description[1024];
@ -378,121 +374,225 @@ void __stdcall XMP_GetGeneralInfo(char* buf) {
sprintf(buf,"vgmstream\t\r%s\r", description);
}
/* get the seeking granularity in seconds */
double WINAPI xmplay_GetGranularity() {
return 0.001; /* can seek in milliseconds */
}
static int add_extension(int length, char * dst, const char * src);
static void build_extension_list();
/* seek to a position (in granularity units), return new position or -1 = failed */
double WINAPI xmplay_SetPosition(DWORD pos) {
double cpos = (double)framesDone / (double)vgmstream->sample_rate;
double time = pos * xmplay_GetGranularity();
/* XMPlay extension list, only needed to associate extensions in Windows */
/* todo: as of v3.8.2.17, any more than ~1000 will crash XMplay's file list screen (but not using the non-native Winamp plugin...) */
#define EXTENSION_LIST_SIZE 1000 /*VGM_EXTENSION_LIST_CHAR_SIZE * 2*/
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
#if 0
/* set a subsong */
if (!disable_subsongs && (pos & XMPIN_POS_SUBSONG)) {
int new_subsong = LOWORD(pos);
/* plugin defs, see xmpin.h */
XMPIN vgmstream_intf = {
XMPIN_FLAG_CANSTREAM,
"vgmstream for XMPlay",
working_extension_list,
XMP_About,
NULL,//XMP_Config
XMP_CheckFile,
XMP_GetFileInfo,
XMP_Open,
XMP_Close,
NULL,
XMP_SetFormat,
XMP_GetTags, //(OPTIONAL) --actually mandatory
XMP_GetInfoText,
XMP_GetGeneralInfo,
NULL,//GetMessage - text for the "Message" tab window/tab (OPTIONAL)
XMP_SetPosition,
XMP_GetGranularity,
NULL,
XMP_Process,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
/* "single subsong mode (don't show info on other subsongs)" */
if (pos & XMPIN_POS_SUBSONG1) {
// ???
}
if (new_subsong && new_subsong != current_subsong) { /* todo implicit? */
if (current_file)
return -1;
vgmstream = init_vgmstream_xmplay(current_file, current_fn, current_subsong+1);
if (!vgmstream) return -1;
current_subsong = new_subsong;
return 0.0;
}
}
#endif
if (time < cpos) {
reset_vgmstream(vgmstream);
cpos = 0.0;
}
while (cpos < time) {
INT16 buffer[1024];
long max_sample_count = 1024 / vgmstream->channels;
long samples_to_skip = (long)((time - cpos) * vgmstream->sample_rate);
if (samples_to_skip > max_sample_count)
samples_to_skip = max_sample_count;
if (!samples_to_skip)
break;
render_vgmstream(buffer, (int)samples_to_skip, vgmstream);
cpos += (double)samples_to_skip / (double)vgmstream->sample_rate;
}
framesDone = (int32_t)(cpos * vgmstream->sample_rate);
return cpos;
}
/* decode some sample data */
DWORD WINAPI xmplay_Process(float* buf, DWORD bufsize) {
INT16 sample_buffer[1024];
UINT32 i, j, todo, done;
BOOL doLoop = xmpfin->GetLooping();
float *sbuf = buf;
UINT32 samplesTodo;
bufsize /= vgmstream->channels;
samplesTodo = doLoop ? bufsize : stream_length_samples - framesDone;
if (samplesTodo > bufsize)
samplesTodo = bufsize;
/* decode */
done = 0;
while (done < samplesTodo) {
todo = 1024 / vgmstream->channels;
if (todo > samplesTodo - done)
todo = samplesTodo - done;
render_vgmstream(sample_buffer, todo, vgmstream);
for (i = 0, j = todo * vgmstream->channels; i < j; ++i) {
*sbuf++ = sample_buffer[i] * 1.0f / 32768.0f;
}
done += todo;
}
sbuf = buf;
/* fade */
if (!doLoop && framesDone + done > framesLength) {
long fadeStart = (framesLength > framesDone) ? framesLength : framesDone;
long fadeEnd = (framesDone + done) > stream_length_samples ? stream_length_samples : (framesDone + done);
long fadePos;
float fadeScale = (float)(stream_length_samples - fadeStart) / fade_samples;
float fadeStep = 1.0f / fade_samples;
sbuf += (fadeStart - framesDone) * vgmstream->channels;
j = vgmstream->channels;
for (fadePos = fadeStart; fadePos < fadeEnd; ++fadePos) {
for (i = 0; i < j; ++i) {
sbuf[i] = sbuf[i] * fadeScale;
}
sbuf += j;
fadeScale -= fadeStep;
if (fadeScale <= 0.0f)
break;
}
done = (int)(fadePos - framesDone);
}
framesDone += done;
return done * vgmstream->channels;
}
static DWORD WINAPI xmplay_GetSubSongs(float *length) {
int subsong_count;
if (!vgmstream)
return 0;
subsong_count = vgmstream->num_streams;
if (disable_subsongs || subsong_count == 0)
subsong_count = 1;
/* get times for all subsongs */
//todo request updating playlist update every subsong change instead?
{
int stream_length_samples;
/* not good for vgmstream as would mean re-parsing many times */
//int i;
//for (i = 0; i < subsong_count; i++) {
// float subsong_length = ...
// *length += subsong_length;
//}
/* simply use the current length */ //todo just use 0?
stream_length_samples = get_vgmstream_play_samples(loop_count, fade_seconds, fade_delay_seconds, vgmstream);
*length = (float)stream_length_samples / (float)vgmstream->sample_rate;
}
return subsong_count;
}
/* *********************************** */
/* main plugin def, see xmpin.h */
XMPIN vgmstream_xmpin = {
XMPIN_FLAG_CANSTREAM,
"vgmstream for XMPlay",
working_extension_list,
xmplay_About,
NULL,//XMP_Config
xmplay_CheckFile,
xmplay_GetFileInfo,
xmplay_Open,
xmplay_Close,
NULL,
xmplay_SetFormat,
xmplay_GetTags, //(OPTIONAL) --actually mandatory
xmplay_GetInfoText,
xmplay_GetGeneralInfo,
NULL,//GetMessage - text for the "Message" tab window/tab (OPTIONAL)
xmplay_SetPosition,
xmplay_GetGranularity,
NULL,
xmplay_Process,
NULL,
NULL,
xmplay_GetSubSongs,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
static int shownerror = 0;
/* get the plugin's XMPIN interface */
__declspec(dllexport) XMPIN* WINAPI XMPIN_GetInterface(UINT32 face, InterfaceProc faceproc) {
if (face != XMPIN_FACE) {
// unsupported version
if (face < XMPIN_FACE && !shownerror) {
MessageBox(0,
"The xmp-vgmstream plugin requires XMPlay 3.8 or above.\n\n"
"Please update at:\n"
"http://www.un4seen.com/xmplay.html\n"
"http://www.un4seen.com/stuff/xmplay.exe", 0, MB_ICONEXCLAMATION);
shownerror = 1;
}
return NULL;
}
__declspec(dllexport) XMPIN* __stdcall XMPIN_GetInterface(UINT32 face, InterfaceProc faceproc)
{
if (face != XMPIN_FACE)
{ // unsupported version
if (face<XMPIN_FACE && !shownerror)
{
//Replaced the message box below with a better one.
//MessageBox(0, "The XMP-vgmstream plugin requires XMPlay 3.8 or above", 0, MB_ICONEXCLAMATION);
MessageBox(0, "The xmp-vgmstream plugin requires XMPlay 3.8 or above. Please update at.\n\n http://www.un4seen.com/xmplay.html \n or at\n http://www.un4seen.com/stuff/xmplay.exe.", 0, MB_ICONEXCLAMATION);
shownerror = 1;
}
return NULL;
}
xmpfin = (XMPFUNC_IN*)faceproc(XMPFUNC_IN_FACE);
xmpfmisc = (XMPFUNC_MISC*)faceproc(XMPFUNC_MISC_FACE);
xmpffile = (XMPFUNC_FILE*)faceproc(XMPFUNC_FILE_FACE);
xmpfin = (XMPFUNC_IN*)faceproc(XMPFUNC_IN_FACE);
xmpfmisc = (XMPFUNC_MISC*)faceproc(XMPFUNC_MISC_FACE);
xmpffile = (XMPFUNC_FILE*)faceproc(XMPFUNC_FILE_FACE);
build_extension_list();
return &vgmstream_intf;
return &vgmstream_xmpin;
}
/**
* Creates XMPlay's extension list, a single string with 2 nulls.
* Extensions must be in this format: "Description\0extension1/.../extensionN"
*/
static void build_extension_list() {
const char ** ext_list;
int ext_list_len;
int i, written;
written = sprintf(working_extension_list, "%s%c", "vgmstream files",'\0');
ext_list = vgmstream_get_formats();
ext_list_len = vgmstream_get_formats_length();
for (i=0; i < ext_list_len; i++) {
written += add_extension(EXTENSION_LIST_SIZE-written, working_extension_list + written, ext_list[i]);
#if 0
// needed?
BOOL WINAPI DllMain(HINSTANCE hDLL, DWORD reason, LPVOID reserved) {
switch (reason) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hDLL);
break;
}
working_extension_list[written-1] = '\0'; /* remove last "/" */
}
/**
* Adds ext to XMPlay's extension list.
*/
static int add_extension(int length, char * dst, const char * ext) {
int ext_len;
int i;
if (length <= 1)
return 0;
ext_len = strlen(ext);
/* check if end reached or not enough room to add */
if (ext_len+2 > length-2) {
dst[0]='\0';
return 0;
}
/* copy new extension + null terminate */
for (i=0; i < ext_len; i++)
dst[i] = ext[i];
dst[i]='/';
dst[i+1]='\0';
return i+1;
return TRUE;
}
#endif