mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-17 23:36:41 +01:00
commit
001b1b9d57
@ -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_ */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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_*/
|
||||
|
61
readme.txt
61
readme.txt
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"},
|
||||
|
@ -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"
|
||||
>
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -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 */
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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*/
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
@ -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) )
|
||||
|
@ -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;
|
||||
|
@ -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) */
|
||||
|
@ -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
118
src/meta/stm.c
Normal 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;
|
||||
}
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
331
src/meta/xwb.c
331
src/meta/xwb.c
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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? */
|
||||
|
21
test/test.c
21
test/test.c
@ -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]);
|
||||
|
@ -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
56
winamp/ipc_pe.h
Normal 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
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user