From 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d Mon Sep 17 00:00:00 2001 From: Jef Date: Tue, 24 Sep 2024 14:54:57 +0200 Subject: Initial community commit --- Src/xspf/XSPFHandler.cpp | 74 +++++++ Src/xspf/XSPFHandler.h | 23 ++ Src/xspf/XSPFHandlerFactory.cpp | 56 +++++ Src/xspf/XSPFHandlerFactory.h | 19 ++ Src/xspf/XSPFLoader.cpp | 461 ++++++++++++++++++++++++++++++++++++++++ Src/xspf/XSPFLoader.h | 16 ++ Src/xspf/api__xspf.h | 21 ++ Src/xspf/main.cpp | 71 +++++++ Src/xspf/resource.h | 16 ++ Src/xspf/version.rc2 | 39 ++++ Src/xspf/xspf.rc | 86 ++++++++ Src/xspf/xspf.sln | 30 +++ Src/xspf/xspf.vcxproj | 278 ++++++++++++++++++++++++ Src/xspf/xspf.vcxproj.filters | 53 +++++ 14 files changed, 1243 insertions(+) create mode 100644 Src/xspf/XSPFHandler.cpp create mode 100644 Src/xspf/XSPFHandler.h create mode 100644 Src/xspf/XSPFHandlerFactory.cpp create mode 100644 Src/xspf/XSPFHandlerFactory.h create mode 100644 Src/xspf/XSPFLoader.cpp create mode 100644 Src/xspf/XSPFLoader.h create mode 100644 Src/xspf/api__xspf.h create mode 100644 Src/xspf/main.cpp create mode 100644 Src/xspf/resource.h create mode 100644 Src/xspf/version.rc2 create mode 100644 Src/xspf/xspf.rc create mode 100644 Src/xspf/xspf.sln create mode 100644 Src/xspf/xspf.vcxproj create mode 100644 Src/xspf/xspf.vcxproj.filters (limited to 'Src/xspf') 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(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 + +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 +#include +#include + +// 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 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;ixmlreader_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 +#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(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 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {3FDDC715-EC20-4FD6-B723-92795736BDD5} + xspf + Win32Proj + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + .w5s + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + .w5s + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + .w5s + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + .w5s + + + false + + + Debug + x86-windows-static-md + + + x86-windows-static-md + Debug + + + x86-windows-static-md + + + x86-windows-static-md + + + + Disabled + ../replicant;../Wasabi;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;XSPF_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + true + MultiThreadedDebugDLL + true + true + Level3 + ProgramDatabase + 4996;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + Windows + false + $(ProjectDir)x86_Debug\$(ProjectName).lib + MachineX86 + false + shlwapi.lib;%(AdditionalDependencies) + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\' + + + + + Disabled + ../replicant;../Wasabi;%(AdditionalIncludeDirectories) + WIN64;_DEBUG;_WINDOWS;_USRDLL;XSPF_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + false + EnableFastChecks + true + MultiThreadedDebugDLL + true + true + Level3 + ProgramDatabase + 4996;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + Windows + false + $(ProjectDir)x64_Debug\$(ProjectName).lib + false + $(ProjectDir)x64_Debug\$(TargetName).pgd + shlwapi.lib;%(AdditionalDependencies) + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\' + + + + + MinSpace + Size + true + ../replicant;../Wasabi;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;XSPF_EXPORTS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + Level3 + None + 4996;%(DisableSpecificWarnings) + Speed + $(IntDir)$(TargetName).pdb + + + false + $(IntDir)$(TargetName).pdb + Windows + true + true + false + $(ProjectDir)x86_Release\$(ProjectName).lib + MachineX86 + $(OutDir)$(TargetName)$(TargetExt) + $(ProjectDir)x86_Release\$(TargetName).pgd + false + shlwapi.lib;%(AdditionalDependencies) + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\' + + + + + MinSpace + Size + true + ../replicant;../Wasabi;%(AdditionalIncludeDirectories) + WIN64;NDEBUG;_WINDOWS;_USRDLL;XSPF_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + Level3 + None + 4996;%(DisableSpecificWarnings) + Speed + $(IntDir)$(TargetName).pdb + + + false + $(IntDir)$(TargetName).pdb + Windows + true + true + false + $(ProjectDir)x64_Release\$(ProjectName).lib + $(OutDir)$(TargetName)$(TargetExt) + $(ProjectDir)x64_Release\$(TargetName)$(TargetExt).intermediate.manifest + $(ProjectDir)x64_Release\$(TargetName).pgd + false + shlwapi.lib;%(AdditionalDependencies) + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\' + + + + + + + + + + + + + + + + + + + + + + {3e0bfa8a-b86a-42e9-a33f-ec294f823f7f} + + + + + + \ 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 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + {85782fd0-d621-46ed-84af-21d7dc5cbb60} + + + {4d951680-f48e-4040-9d03-dbafac1e7cb1} + + + {0a439138-f363-4152-aaaf-5e20c98a37cd} + + + + + Ressource Files + + + \ No newline at end of file -- cgit