aboutsummaryrefslogtreecommitdiff
path: root/Src/xspf
diff options
context:
space:
mode:
Diffstat (limited to 'Src/xspf')
-rw-r--r--Src/xspf/XSPFHandler.cpp74
-rw-r--r--Src/xspf/XSPFHandler.h23
-rw-r--r--Src/xspf/XSPFHandlerFactory.cpp56
-rw-r--r--Src/xspf/XSPFHandlerFactory.h19
-rw-r--r--Src/xspf/XSPFLoader.cpp461
-rw-r--r--Src/xspf/XSPFLoader.h16
-rw-r--r--Src/xspf/api__xspf.h21
-rw-r--r--Src/xspf/main.cpp71
-rw-r--r--Src/xspf/resource.h16
-rw-r--r--Src/xspf/version.rc239
-rw-r--r--Src/xspf/xspf.rc86
-rw-r--r--Src/xspf/xspf.sln30
-rw-r--r--Src/xspf/xspf.vcxproj278
-rw-r--r--Src/xspf/xspf.vcxproj.filters53
14 files changed, 1243 insertions, 0 deletions
diff --git a/Src/xspf/XSPFHandler.cpp b/Src/xspf/XSPFHandler.cpp
new file mode 100644
index 00000000..72c0927e
--- /dev/null
+++ b/Src/xspf/XSPFHandler.cpp
@@ -0,0 +1,74 @@
+#include "XSPFHandler.h"
+#include "XSPFLoader.h"
+#include "api__xspf.h"
+#include "resource.h"
+
+const wchar_t *XSPFHandler::EnumerateExtensions(size_t n)
+{
+ switch(n)
+ {
+ case 0:
+ return L"xspf";
+ default:
+ return 0;
+ }
+}
+
+const char *XSPFHandler::EnumerateMIMETypes(size_t n)
+{
+ switch(n)
+ {
+ case 0:
+ return "application/xspf+xml";
+ default:
+ return 0;
+ }
+}
+
+const wchar_t *XSPFHandler::GetName()
+{
+ static wchar_t xspfpl[64];
+ // no point re-loading this all of the time since it won't change once we've been loaded
+ return (!xspfpl[0]?WASABI_API_LNGSTRINGW_BUF(IDS_XSPF_PLAYLIST,xspfpl,64):xspfpl);
+}
+
+// returns SUCCESS and FAILED, so be careful ...
+int XSPFHandler::SupportedFilename(const wchar_t *filename)
+{
+ size_t filenameLength = wcslen(filename);
+ size_t extensionLength = wcslen(L".xspf");
+ if (filenameLength < extensionLength) return SVC_PLAYLISTHANDLER_FAILED; // too short
+ if (!_wcsicmp(filename + filenameLength - extensionLength, L".xspf"))
+ return SVC_PLAYLISTHANDLER_SUCCESS;
+ else
+ return SVC_PLAYLISTHANDLER_FAILED;
+}
+
+int XSPFHandler::SupportedMIMEType(const char *type)
+{
+ if (!strcmp(type, "application/xspf+xml"))
+ return SVC_PLAYLISTHANDLER_SUCCESS;
+ else
+ return SVC_PLAYLISTHANDLER_FAILED;
+}
+
+ifc_playlistloader *XSPFHandler::CreateLoader(const wchar_t *filename)
+{
+ return new XSPFLoader;
+}
+
+void XSPFHandler::ReleaseLoader(ifc_playlistloader *loader)
+{
+ delete static_cast<XSPFLoader *>(loader);
+}
+
+// Define the dispatch table
+#define CBCLASS XSPFHandler
+START_DISPATCH;
+CB(SVC_PLAYLISTHANDLER_ENUMEXTENSIONS, EnumerateExtensions)
+CB(SVC_PLAYLISTHANDLER_ENUMMIMETYPES, EnumerateMIMETypes)
+CB(SVC_PLAYLISTHANDLER_SUPPORTFILENAME, SupportedFilename)
+CB(SVC_PLAYLISTHANDLER_CREATELOADER, CreateLoader)
+VCB(SVC_PLAYLISTHANDLER_RELEASELOADER, ReleaseLoader)
+CB(SVC_PLAYLISTHANDLER_GETNAME, GetName)
+END_DISPATCH; \ No newline at end of file
diff --git a/Src/xspf/XSPFHandler.h b/Src/xspf/XSPFHandler.h
new file mode 100644
index 00000000..bcfa949b
--- /dev/null
+++ b/Src/xspf/XSPFHandler.h
@@ -0,0 +1,23 @@
+#ifndef NULLSOFT_XSPF_XSPFHANDLER_H
+#define NULLSOFT_XSPF_XSPFHANDLER_H
+
+#include "../playlist/svc_playlisthandler.h"
+// the "Playlist Handler" is responsible for describing all the capabilities of the playlist format to Winamp
+// It is a singleton class (for each playlist type)
+// besides informational functions, it contains factory methods for creating playlist loaders and writers
+
+class XSPFHandler : public svc_playlisthandler
+{
+public:
+ const wchar_t *EnumerateExtensions(size_t n); // returns 0 when it's done
+ const char *EnumerateMIMETypes(size_t n); // returns 0 when it's done, returns char * to match HTTP specs
+ const wchar_t *GetName(); // returns a name suitable for display to user of this playlist form (e.g. PLS Playlist)
+ int SupportedFilename(const wchar_t *filename); // returns SUCCESS and FAILED, so be careful ...
+ int SupportedMIMEType(const char *filename); // returns SUCCESS and FAILED, so be careful ...
+ ifc_playlistloader *CreateLoader(const wchar_t *filename);
+ void ReleaseLoader(ifc_playlistloader *loader);
+
+protected:
+ RECVS_DISPATCH; // boiler-plate code for the dispatch-table
+};
+#endif \ No newline at end of file
diff --git a/Src/xspf/XSPFHandlerFactory.cpp b/Src/xspf/XSPFHandlerFactory.cpp
new file mode 100644
index 00000000..f1a17d3a
--- /dev/null
+++ b/Src/xspf/XSPFHandlerFactory.cpp
@@ -0,0 +1,56 @@
+#include "XSPFHandlerFactory.h"
+#include "XSPFHandler.h"
+/*
+ This is the GUID for our service factory
+ don't re-use this.
+ make your own guid with guidgen.exe
+ lives somewhere like C:\program files\Microsoft Visual Studio .NET 2003\Common7\Tools\Bin
+*/
+
+// {51D17273-566F-4fa9-AFE0-1345C65B8B1B}
+static const GUID XSPFHandlerGUID =
+{ 0x51d17273, 0x566f, 0x4fa9, { 0xaf, 0xe0, 0x13, 0x45, 0xc6, 0x5b, 0x8b, 0x1b } };
+
+
+// our playlist handler.
+static XSPFHandler xspfHandler;
+
+FOURCC XSPFHandlerFactory::GetServiceType()
+{
+ return svc_playlisthandler::getServiceType();
+}
+
+const char *XSPFHandlerFactory::GetServiceName()
+{
+ return "XSPF Playlist Loader";
+}
+
+GUID XSPFHandlerFactory::GetGuid()
+{
+ return XSPFHandlerGUID;
+}
+
+void *XSPFHandlerFactory::GetInterface(int global_lock)
+{
+ // xspfHandler is a singleton object, so we can just return a pointer to it
+ // depending on what kind of service you are making, you might have to
+ // 'new' an object and return that instead (and then free it in ReleaseInterface)
+ return &xspfHandler;
+}
+
+int XSPFHandlerFactory::ReleaseInterface(void *ifc)
+{
+ // no-op because we returned a singleton (see above)
+ return 1;
+}
+
+// Define the dispatch table
+#define CBCLASS XSPFHandlerFactory
+START_DISPATCH;
+CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
+CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
+CB(WASERVICEFACTORY_GETGUID, GetGuid)
+CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
+CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/xspf/XSPFHandlerFactory.h b/Src/xspf/XSPFHandlerFactory.h
new file mode 100644
index 00000000..e65332e2
--- /dev/null
+++ b/Src/xspf/XSPFHandlerFactory.h
@@ -0,0 +1,19 @@
+#ifndef NULLSOFT_XSPF_XSPFHANDLERFACTORY_H
+#define NULLSOFT_XSPF_XSPFHANDLERFACTORY_H
+
+#include <api/service/waservicefactory.h>
+
+class XSPFHandlerFactory : public waServiceFactory
+{
+public:
+ FOURCC GetServiceType();
+ const char *GetServiceName();
+ GUID GetGuid();
+ void *GetInterface(int global_lock = TRUE);
+ int ReleaseInterface(void *ifc);
+
+protected:
+ RECVS_DISPATCH; // all Wasabi objects implementing a Dispatchable interface require this
+};
+
+#endif \ No newline at end of file
diff --git a/Src/xspf/XSPFLoader.cpp b/Src/xspf/XSPFLoader.cpp
new file mode 100644
index 00000000..5caad890
--- /dev/null
+++ b/Src/xspf/XSPFLoader.cpp
@@ -0,0 +1,461 @@
+#include "XSPFLoader.h"
+#include "../xml/obj_xml.h"
+#include "../xml/ifc_xmlreadercallback.h"
+#include "api__xspf.h"
+#include "../winamp/wa_ipc.h"
+#include <api/service/waservicefactory.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+
+// tries to retrieve the media library API (if it's not already retrieved
+bool HasMediaLibraryAPI()
+{
+ // TODO: should critical section this
+ if (!AGAVE_API_MLDB)
+ {
+ waServiceFactory *mldbFactory = WASABI_API_SVC->service_getServiceByGuid(mldbApiGuid);
+ if (mldbFactory)
+ AGAVE_API_MLDB = (api_mldb *)mldbFactory->getInterface(); // retrieve a pointer to the API object
+ }
+ return !!AGAVE_API_MLDB;
+}
+
+static bool HasTagzAPI()
+{
+ // TODO: should critical section this
+ if (!AGAVE_API_TAGZ)
+ {
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(tagzGUID);
+ if (sf)
+ AGAVE_API_TAGZ = (api_tagz *)sf->getInterface(); // retrieve a pointer to the API object
+ }
+ return !!AGAVE_API_TAGZ;
+}
+
+const wchar_t* ATFString()
+{
+ // TODO: should critical section this
+ if (!WASABI_API_APP)
+ {
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid);
+ if (sf)
+ WASABI_API_APP = (api_application *)sf->getInterface(); // retrieve a pointer to the API object
+ }
+ if (WASABI_API_APP)
+ {
+ const wchar_t *atf = WASABI_API_APP->getATFString();
+ if (atf && *atf) return atf;
+ }
+ return L"%artist% - %title%";
+}
+
+// go check out XSPFLoader::Load before decyphering this class
+class XSPFLoaderCallback : public ifc_xmlreadercallback
+{
+public:
+ XSPFLoaderCallback(ifc_playlistloadercallback *_playlist);
+ ~XSPFLoaderCallback();
+ void xmlReaderOnStartElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params);
+ void xmlReaderOnEndElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag);
+ void xmlReaderOnCharacterDataCallback(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str);
+
+protected:
+ RECVS_DISPATCH;
+
+private:
+ enum
+ {
+ ELEMENT_NONE=-1,
+ ELEMENT_LOCATION=0,
+ ELEMENT_IDENTIFIER,
+ ELEMENT_TITLE,
+ ELEMENT_CREATOR,
+ ELEMENT_ALBUM,
+ ELEMENT_TRACKNUM,
+ NUM_ELEMENTS,
+ };
+ wchar_t *elements[NUM_ELEMENTS];
+ int element;
+ ifc_playlistloadercallback *playlist;
+};
+
+XSPFLoaderCallback::XSPFLoaderCallback(ifc_playlistloadercallback *_playlist)
+{
+ element = ELEMENT_NONE;
+ for (int i=0;i<NUM_ELEMENTS;i++)
+ elements[i]=0;
+
+ playlist = _playlist;
+}
+
+XSPFLoaderCallback::~XSPFLoaderCallback()
+{
+ for (int i=0;i<NUM_ELEMENTS;i++)
+ {
+ free(elements[i]);
+ elements[i]=0;
+ }
+}
+
+// this gets called for every opening tag
+// xmlpath is the full XML path up to this point, delimited with \f (form feed)
+// xmltag is the actual opening tag
+// params is an object which lets you query for the attribtues in the tag
+void XSPFLoaderCallback::xmlReaderOnStartElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params)
+{
+ if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\flocation"))
+ element=ELEMENT_LOCATION;
+ else if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\fidentifier"))
+ element=ELEMENT_IDENTIFIER;
+ else if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\ftitle"))
+ element=ELEMENT_TITLE;
+ else if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\fcreator"))
+ element=ELEMENT_CREATOR;
+ else if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\falbum"))
+ element=ELEMENT_ALBUM;
+ else if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\ftracknum"))
+ element=ELEMENT_TRACKNUM;
+ else
+ element=ELEMENT_NONE;
+}
+
+static void QueryStringAdd(const wchar_t *value, wchar_t *&query, size_t &query_len)
+{
+ if (!value || !*value) return;
+ while (value && *value && query_len > 3) {
+ if (*value == L'%') { *query++ = L'%'; *query++ = L'%'; query_len-=2;}
+ else if (*value == L'\"') { *query++ = L'%'; *query++ = L'2'; *query++ = L'2'; query_len-=3;}
+ else if (*value == L'\'') { *query++ = L'%'; *query++ = L'2'; *query++ = L'7'; query_len-=3;}
+ else if (*value == L'[') { *query++ = L'%'; *query++ = L'5'; *query++ = L'B'; query_len-=3;}
+ else if (*value == L']') { *query++ = L'%'; *query++ = L'5'; *query++ = L'D'; query_len-=3;}
+ else if (*value == L'(') { *query++ = L'%'; *query++ = L'2'; *query++ = L'8'; query_len-=3;}
+ else if (*value == L')') { *query++ = L'%'; *query++ = L'2'; *query++ = L'9'; query_len-=3;}
+ else if (*value == L'#') { *query++ = L'%'; *query++ = L'2'; *query++ = L'3'; query_len-=3;}
+ else { *query++ = *value; query_len--; }
+ value++;
+ }
+ *query = 0;
+}
+
+class ItemRecordTagProvider : public ifc_tagprovider
+{
+public:
+ ItemRecordTagProvider(itemRecordW *item) : t(item) {}
+ wchar_t *GetTag(const wchar_t *name, ifc_tagparams *parameters);
+ void FreeTag(wchar_t *Tag);
+protected:
+ RECVS_DISPATCH;
+private:
+ itemRecordW *t;
+};
+
+#define CBCLASS ItemRecordTagProvider
+START_DISPATCH;
+CB(IFC_TAGPROVIDER_GET_TAG, GetTag);
+VCB(IFC_TAGPROVIDER_FREE_TAG, FreeTag);
+END_DISPATCH;
+#undef CBCLASS
+
+wchar_t *ItemRecordTagProvider::GetTag(const wchar_t *tag, ifc_tagparams *parameters)
+{
+ bool copy=false;
+ wchar_t buf[128]={0};
+ wchar_t *value = NULL;
+
+ if (!_wcsicmp(tag, L"artist")) value = t->artist;
+ else if (!_wcsicmp(tag, L"album")) value = t->album;
+ else if (!_wcsicmp(tag, L"filename")) value = t->filename;
+ else if (!_wcsicmp(tag, L"title")) value = t->title;
+ else if (!_wcsicmp(tag, L"year"))
+ {
+ if (t->year > 0)
+ {
+ StringCchPrintfW(buf, 128, L"%04d", t->year);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"genre")) value = t->genre;
+ else if (!_wcsicmp(tag, L"comment")) value = t->comment;
+ else if (!_wcsicmp(tag, L"tracknumber") || !_wcsicmp(tag, L"track"))
+ {
+ if (t->track > 0)
+ {
+ if (t->tracks > 0)
+ StringCchPrintfW(buf, 128, L"%02d/%02d", t->track, t->tracks);
+ else
+ StringCchPrintfW(buf, 128, L"%02d", t->track);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"disc"))
+ {
+ if (t->disc > 0)
+ {
+ if (t->discs > 0)
+ StringCchPrintfW(buf, 128, L"%d/%d", t->disc, t->discs);
+ else
+ StringCchPrintfW(buf, 128, L"%d", t->disc);
+
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"rating"))
+ {
+ if (t->rating > 0)
+ {
+ StringCchPrintfW(buf, 128, L"%d", t->rating);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"playcount"))
+ {
+ if (t->playcount > 0)
+ {
+ StringCchPrintfW(buf, 128, L"%d", t->playcount);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"bitrate"))
+ {
+ if (t->bitrate > 0)
+ {
+ StringCchPrintfW(buf, 128, L"%d", t->bitrate);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"bpm"))
+ {
+ if (t->bpm > 0)
+ {
+ StringCchPrintfW(buf, 128, L"%d", t->bpm);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"albumartist")) value = t->albumartist;
+ else if (!_wcsicmp(tag, L"publisher")) value = t->publisher;
+ else if (!_wcsicmp(tag, L"composer")) value = t->composer;
+ else if (!_wcsicmp(tag, L"replaygain_album_gain")) value = t->replaygain_album_gain;
+ else if (!_wcsicmp(tag, L"replaygain_track_gain")) value = t->replaygain_track_gain;
+ else if (!_wcsicmp(tag, L"GracenoteFileID"))
+ {
+ copy=true;
+ value = getRecordExtendedItem(t, L"GracenoteFileID");
+ }
+ else if (!_wcsicmp(tag, L"GracenoteExtData"))
+ {
+ copy=true;
+ value = getRecordExtendedItem(t, L"GracenoteExtData");
+ }
+ else
+ return 0;
+
+ if (!value)
+ return 0;
+ else
+ {
+ if (copy || value == buf)
+ return AGAVE_API_MLDB->DuplicateString(value);
+ else
+ {
+ AGAVE_API_MLDB->RetainString(value);
+ return value;
+ }
+ }
+}
+
+void ItemRecordTagProvider::FreeTag(wchar_t *tag)
+{
+ AGAVE_API_MLDB->ReleaseString(tag);
+}
+
+// this gets called for every closing tag
+void XSPFLoaderCallback::xmlReaderOnEndElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag)
+{
+ if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack"))
+ {
+ // end of track info
+ if (elements[ELEMENT_LOCATION] // if we have a location
+ && (PathIsURL(elements[ELEMENT_LOCATION]) // and it's a URL
+ || GetFileAttributes(elements[ELEMENT_LOCATION]) != INVALID_FILE_ATTRIBUTES)) // or a file that exists
+ {
+ // location field seemed OK so go ahead and add
+ // TODO try using the length if available...
+ playlist->OnFile(elements[ELEMENT_LOCATION], 0, -1, 0);
+ }
+ else if (HasMediaLibraryAPI()) // we don't have a good location so let's hit the media library
+ {
+ // let's build a query out of what we have
+ bool needs_and=false;
+ wchar_t query[2048]={0};
+ wchar_t *query_end=query;
+ size_t query_size=2048;
+
+ struct {
+ const wchar_t *field_name;
+ int field_value;
+ } fields[] =
+ {
+ {L"artist", ELEMENT_CREATOR},
+ {L"title", ELEMENT_TITLE},
+ };
+ for(size_t i=0;i!=sizeof(fields)/sizeof(fields[0]);i++)
+ {
+ if (elements[fields[i].field_value])
+ {
+ if (needs_and)
+ StringCchCopyEx(query_end, query_size, L" AND ", &query_end, &query_size, 0);
+ StringCchPrintfEx(query_end, query_size, &query_end, &query_size, 0, L"(%s == \"", fields[i].field_name);
+ QueryStringAdd(elements[fields[i].field_value], query_end, query_size);
+ StringCchCopyEx(query_end, query_size, L"\")", &query_end, &query_size, 0);
+ needs_and=true;
+ }
+ }
+
+ if (query[0])
+ {
+ itemRecordListW *results = AGAVE_API_MLDB->QueryLimit(query, 1);
+ if (results && results->Size >= 1 && results->Items[0].filename)
+ {
+ if (HasTagzAPI())
+ {
+ wchar_t title[2048]={0};
+ ItemRecordTagProvider provider(&results->Items[0]);
+ AGAVE_API_TAGZ->format(ATFString(), title, 2048, &provider, 0);
+ int length = -1;
+ if (results->Items[0].length > 0)
+ length=results->Items[0].length*1000;
+ playlist->OnFile(results->Items[0].filename, title, length, 0);
+ }
+ else
+ {
+ int length = -1;
+ if (results->Items[0].length > 0)
+ length=results->Items[0].length*1000;
+ playlist->OnFile(results->Items[0].filename, 0, length, 0);
+ }
+ }
+ AGAVE_API_MLDB->FreeRecordList(results);
+ }
+ }
+ for (int i=0;i<NUM_ELEMENTS;i++)
+ {
+ free(elements[i]);
+ elements[i]=0;
+ }
+ }
+ else if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\flocation"))
+ element=ELEMENT_NONE;
+ else if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\fidentifier"))
+ element=ELEMENT_NONE;
+ else if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\ftitle"))
+ element=ELEMENT_NONE;
+ else if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\fcreator"))
+ element=ELEMENT_NONE;
+ else if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\falbum"))
+ element=ELEMENT_NONE;
+ else if (!wcscmp(xmlpath, L"playlist\ftrackList\ftrack\ftracknum"))
+ element=ELEMENT_NONE;
+}
+
+void XSPFLoaderCallback::xmlReaderOnCharacterDataCallback(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str)
+{
+ if (element!=ELEMENT_NONE)
+ {
+ // this certainly isn't the most memory efficient way to do this but it'll work for now :)
+ if (elements[element] == 0)
+ {
+ elements[element] = wcsdup(str);
+ }
+ else
+ {
+ size_t elementlen = wcslen(elements[element]);
+ size_t incominglen = wcslen(str);
+
+ wchar_t *&newstr = elements[element], *oldstr = newstr;
+ newstr = (wchar_t *)realloc(newstr, sizeof(wchar_t)*(elementlen+incominglen+1));
+ if (newstr != NULL)
+ {
+ memcpy(newstr+elementlen, str, incominglen*sizeof(wchar_t));
+ newstr[elementlen+incominglen] = 0;
+ }
+ }
+ }
+}
+
+#define CBCLASS XSPFLoaderCallback
+START_DISPATCH;
+VCB(ONSTARTELEMENT, xmlReaderOnStartElementCallback)
+VCB(ONENDELEMENT, xmlReaderOnEndElementCallback)
+VCB(ONCHARDATA, xmlReaderOnCharacterDataCallback)
+END_DISPATCH;
+#undef CBCLASS
+
+/* ---------------------------- */
+static int LoadFile(obj_xml *parser, const wchar_t *filename)
+{
+ HANDLE file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
+
+ if (file == INVALID_HANDLE_VALUE)
+ return IFC_PLAYLISTLOADER_FAILED;
+
+ while (true)
+ {
+ char data[1024] = {0};
+ DWORD bytesRead = 0;
+ if (ReadFile(file, data, 1024, &bytesRead, NULL) && bytesRead)
+ {
+ if (parser->xmlreader_feed(data, bytesRead) != API_XML_SUCCESS)
+ {
+ CloseHandle(file);
+ return IFC_PLAYLISTLOADER_FAILED;
+ }
+ }
+ else
+ break;
+ }
+
+ CloseHandle(file);
+ if (parser->xmlreader_feed(0, 0) != API_XML_SUCCESS)
+ return IFC_PLAYLISTLOADER_FAILED;
+
+ return IFC_PLAYLISTLOADER_SUCCESS;
+}
+
+// this get called by Winamp
+// you a method in the passed playlist loader callback object
+// for each item in the playlist
+int XSPFLoader::Load(const wchar_t *filename, ifc_playlistloadercallback *playlist)
+{
+ // first thing we'll need is an XML parser
+ // we'll have to do the wasabi dance to get it
+
+ // first, get the service factory for creating XML objects
+ waServiceFactory *xmlFactory = WASABI_API_SVC->service_getServiceByGuid(obj_xmlGUID);
+ if (xmlFactory)
+ {
+ obj_xml *parser = (obj_xml *)xmlFactory->getInterface(); // create an XML parser
+ if (parser)
+ {
+ // ok now we can get down to biz'nes
+ XSPFLoaderCallback xmlCallback(playlist);
+ parser->xmlreader_registerCallback(L"*", &xmlCallback);
+ parser->xmlreader_setCaseSensitive();
+ parser->xmlreader_open();
+ int ret = LoadFile(parser, filename);
+ parser->xmlreader_unregisterCallback(&xmlCallback);
+ parser->xmlreader_close();
+ xmlFactory->releaseInterface(parser); // destroy the XML parser via the service factory
+
+ return ret;
+ }
+ }
+
+ return IFC_PLAYLISTLOADER_FAILED;
+}
+
+// Define the dispatch table
+#define CBCLASS XSPFLoader
+START_DISPATCH;
+CB(IFC_PLAYLISTLOADER_LOAD, Load)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/xspf/XSPFLoader.h b/Src/xspf/XSPFLoader.h
new file mode 100644
index 00000000..04e2230f
--- /dev/null
+++ b/Src/xspf/XSPFLoader.h
@@ -0,0 +1,16 @@
+#ifndef NULLSOFT_XSPF_XSPFLOADER_H
+#define NULLSOFT_XSPF_XSPFLOADER_H
+
+#include "../playlist/ifc_playlistloader.h"
+
+// this is the class that actually loads the playlist.
+// everything up to this point (component, handler, handler factory) was just administrative
+class XSPFLoader : public ifc_playlistloader
+{
+public:
+ int Load(const wchar_t *filename, ifc_playlistloadercallback *playlist);
+
+protected:
+ RECVS_DISPATCH; // boiler-plate code for our dispatch table
+};
+#endif \ No newline at end of file
diff --git a/Src/xspf/api__xspf.h b/Src/xspf/api__xspf.h
new file mode 100644
index 00000000..8fa5cb6f
--- /dev/null
+++ b/Src/xspf/api__xspf.h
@@ -0,0 +1,21 @@
+#ifndef NULLSOFT_XSPF_API_H
+#define NULLSOFT_XSPF_API_H
+
+// Service Manager
+#include "api/service/api_service.h"
+
+// Media Library API
+#include "../Plugins/Library/ml_local/api_mldb.h"
+extern api_mldb *mldbApi;
+#define AGAVE_API_MLDB mldbApi
+
+#include "../Agave/Language/api_language.h"
+
+#include "../tagz/api_tagz.h"
+extern api_tagz *tagzApi;
+#define AGAVE_API_TAGZ tagzApi
+
+#include <api/application/api_application.h>
+#define WASABI_API_APP applicationApi
+
+#endif \ No newline at end of file
diff --git a/Src/xspf/main.cpp b/Src/xspf/main.cpp
new file mode 100644
index 00000000..ab0001f5
--- /dev/null
+++ b/Src/xspf/main.cpp
@@ -0,0 +1,71 @@
+#include "../Agave/Component/ifc_wa5component.h"
+#include "api__xspf.h" // services we'll used are extern'd here
+#include "XSPFHandlerFactory.h"
+
+// declarations for the pointers to services we'll need
+// by convention, these follow the format WASABI_API_* or AGAVE_API_*
+// you are free to name it however you want, but this provides good consistency
+api_service *WASABI_API_SVC = 0; // the wasabi service manager (our gateway to other services)
+api_mldb *AGAVE_API_MLDB = 0; // media library API. we'll retrieve this the first time we need it, because it will load after us
+api_tagz *AGAVE_API_TAGZ = 0;
+api_application *WASABI_API_APP = 0;
+// wasabi based services for localisation support
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+// our service factories that we're going to register
+static XSPFHandlerFactory xspfHandlerFactory;
+
+// here is the implementation of our component
+// this class contains the methods called during initialization and shutdown
+// to register/deregister our component's services
+class XSPFComponent : public ifc_wa5component
+{
+public:
+ void RegisterServices(api_service *service); // this is where the party is
+ void DeregisterServices(api_service *service);
+
+protected:
+ RECVS_DISPATCH; // some boiler-plate code to implement our dispatch table
+};
+
+static HINSTANCE GetMyInstance()
+{
+ MEMORY_BASIC_INFORMATION mbi = {0};
+ if(VirtualQuery(GetMyInstance, &mbi, sizeof(mbi)))
+ return (HINSTANCE)mbi.AllocationBase;
+ return NULL;
+}
+
+void XSPFComponent::RegisterServices(api_service *service)
+{
+ WASABI_API_SVC = service; // we get passed the service manager and we need to save the pointer
+ WASABI_API_SVC->service_register(&xspfHandlerFactory);
+
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
+ if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(GetMyInstance(),playlistLangGUID);
+}
+
+void XSPFComponent::DeregisterServices(api_service *service)
+{
+ WASABI_API_SVC->service_deregister(&xspfHandlerFactory);
+}
+
+static XSPFComponent xspfComponent;
+// Winamp calls this function after it LoadLibrary's your W5S
+// you need to turn a pointer to your component.
+extern "C" __declspec(dllexport) ifc_wa5component *GetWinamp5SystemComponent()
+{
+ return &xspfComponent;
+}
+
+// some boiler-plate code to implement the dispatch table for our component
+#define CBCLASS XSPFComponent
+START_DISPATCH;
+VCB(API_WA5COMPONENT_REGISTERSERVICES, RegisterServices)
+VCB(API_WA5COMPONENT_DEREEGISTERSERVICES, DeregisterServices)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/xspf/resource.h b/Src/xspf/resource.h
new file mode 100644
index 00000000..1249067c
--- /dev/null
+++ b/Src/xspf/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by xspf.rc
+//
+#define IDS_XSPF_PLAYLIST 8
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/xspf/version.rc2 b/Src/xspf/version.rc2
new file mode 100644
index 00000000..f27fc594
--- /dev/null
+++ b/Src/xspf/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION WINAMP_PRODUCTVER
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp 5.x System Component"
+ VALUE "FileVersion", STR_WINAMP_PRODUCTVER
+ VALUE "InternalName", "xspf.w5s"
+ VALUE "LegalCopyright", "Copyright © 2007-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "xspf.w5s"
+ VALUE "ProductName", "Winamp XML Shareable Playlist Format Support"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/Src/xspf/xspf.rc b/Src/xspf/xspf.rc
new file mode 100644
index 00000000..967c05c4
--- /dev/null
+++ b/Src/xspf/xspf.rc
@@ -0,0 +1,86 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_XSPF_PLAYLIST "XML Shareable Playlist Format"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/xspf/xspf.sln b/Src/xspf/xspf.sln
new file mode 100644
index 00000000..6d3f3b58
--- /dev/null
+++ b/Src/xspf/xspf.sln
@@ -0,0 +1,30 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29424.173
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xspf", "xspf.vcxproj", "{3FDDC715-EC20-4FD6-B723-92795736BDD5}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3FDDC715-EC20-4FD6-B723-92795736BDD5}.Debug|Win32.ActiveCfg = Debug|Win32
+ {3FDDC715-EC20-4FD6-B723-92795736BDD5}.Debug|Win32.Build.0 = Debug|Win32
+ {3FDDC715-EC20-4FD6-B723-92795736BDD5}.Debug|x64.ActiveCfg = Debug|x64
+ {3FDDC715-EC20-4FD6-B723-92795736BDD5}.Debug|x64.Build.0 = Debug|x64
+ {3FDDC715-EC20-4FD6-B723-92795736BDD5}.Release|Win32.ActiveCfg = Release|Win32
+ {3FDDC715-EC20-4FD6-B723-92795736BDD5}.Release|Win32.Build.0 = Release|Win32
+ {3FDDC715-EC20-4FD6-B723-92795736BDD5}.Release|x64.ActiveCfg = Release|x64
+ {3FDDC715-EC20-4FD6-B723-92795736BDD5}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {F492E8CB-F967-49DF-9CBE-D69862846DD6}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/xspf/xspf.vcxproj b/Src/xspf/xspf.vcxproj
new file mode 100644
index 00000000..14b3e137
--- /dev/null
+++ b/Src/xspf/xspf.vcxproj
@@ -0,0 +1,278 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{3FDDC715-EC20-4FD6-B723-92795736BDD5}</ProjectGuid>
+ <RootNamespace>xspf</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetExt>.w5s</TargetExt>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetExt>.w5s</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetExt>.w5s</TargetExt>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetExt>.w5s</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../replicant;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;XSPF_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(ProjectDir)x86_Debug\$(ProjectName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../replicant;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;XSPF_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(ProjectDir)x64_Debug\$(ProjectName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <ProfileGuidedDatabase>$(ProjectDir)x64_Debug\$(TargetName).pgd</ProfileGuidedDatabase>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>../replicant;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;XSPF_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(ProjectDir)x86_Release\$(ProjectName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <ProfileGuidedDatabase>$(ProjectDir)x86_Release\$(TargetName).pgd</ProfileGuidedDatabase>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>../replicant;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;XSPF_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(ProjectDir)x64_Release\$(ProjectName).lib</ImportLibrary>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <ManifestFile>$(ProjectDir)x64_Release\$(TargetName)$(TargetExt).intermediate.manifest</ManifestFile>
+ <ProfileGuidedDatabase>$(ProjectDir)x64_Release\$(TargetName).pgd</ProfileGuidedDatabase>
+ <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\Plugins\General\gen_ml\ml_lib.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="XSPFHandler.cpp" />
+ <ClCompile Include="XSPFHandlerFactory.cpp" />
+ <ClCompile Include="XSPFLoader.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__xspf.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="XSPFHandler.h" />
+ <ClInclude Include="XSPFHandlerFactory.h" />
+ <ClInclude Include="XSPFLoader.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="xspf.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/xspf/xspf.vcxproj.filters b/Src/xspf/xspf.vcxproj.filters
new file mode 100644
index 00000000..b5aea129
--- /dev/null
+++ b/Src/xspf/xspf.vcxproj.filters
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="XSPFHandler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="XSPFHandlerFactory.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="XSPFLoader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Plugins\General\gen_ml\ml_lib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="XSPFLoader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="XSPFHandlerFactory.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="XSPFHandler.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="api__xspf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{85782fd0-d621-46ed-84af-21d7dc5cbb60}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{4d951680-f48e-4040-9d03-dbafac1e7cb1}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{0a439138-f363-4152-aaaf-5e20c98a37cd}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="xspf.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file