aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Library/ml_nowplaying
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Library/ml_nowplaying')
-rw-r--r--Src/Plugins/Library/ml_nowplaying/common.cpp147
-rw-r--r--Src/Plugins/Library/ml_nowplaying/common.h58
-rw-r--r--Src/Plugins/Library/ml_nowplaying/external.cpp110
-rw-r--r--Src/Plugins/Library/ml_nowplaying/external.h44
-rw-r--r--Src/Plugins/Library/ml_nowplaying/handler.cpp47
-rw-r--r--Src/Plugins/Library/ml_nowplaying/handler.h19
-rw-r--r--Src/Plugins/Library/ml_nowplaying/local_menu.cpp68
-rw-r--r--Src/Plugins/Library/ml_nowplaying/local_menu.h15
-rw-r--r--Src/Plugins/Library/ml_nowplaying/main.cpp160
-rw-r--r--Src/Plugins/Library/ml_nowplaying/main.h22
-rw-r--r--Src/Plugins/Library/ml_nowplaying/ml_nowplaying.rc99
-rw-r--r--Src/Plugins/Library/ml_nowplaying/ml_nowplaying.sln30
-rw-r--r--Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj280
-rw-r--r--Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj.filters107
-rw-r--r--Src/Plugins/Library/ml_nowplaying/navigation.cpp684
-rw-r--r--Src/Plugins/Library/ml_nowplaying/navigation.h27
-rw-r--r--Src/Plugins/Library/ml_nowplaying/png.rc7
-rw-r--r--Src/Plugins/Library/ml_nowplaying/resource.h23
-rw-r--r--Src/Plugins/Library/ml_nowplaying/resources/serviceIcon.pngbin0 -> 290 bytes
-rw-r--r--Src/Plugins/Library/ml_nowplaying/service.cpp171
-rw-r--r--Src/Plugins/Library/ml_nowplaying/service.h53
-rw-r--r--Src/Plugins/Library/ml_nowplaying/version.rc239
-rw-r--r--Src/Plugins/Library/ml_nowplaying/wasabi.cpp82
-rw-r--r--Src/Plugins/Library/ml_nowplaying/wasabi.h42
-rw-r--r--Src/Plugins/Library/ml_nowplaying/wasabiCallback.cpp90
-rw-r--r--Src/Plugins/Library/ml_nowplaying/wasabiCallback.h42
26 files changed, 2466 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_nowplaying/common.cpp b/Src/Plugins/Library/ml_nowplaying/common.cpp
new file mode 100644
index 00000000..f6d2f03c
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/common.cpp
@@ -0,0 +1,147 @@
+#include "./common.h"
+#include "./wasabi.h"
+
+#include <strsafe.h>
+
+LPWSTR Plugin_MallocString(size_t cchLen)
+{
+ return (LPWSTR)calloc(cchLen, sizeof(WCHAR));
+}
+
+void Plugin_FreeString(LPWSTR pszString)
+{
+ if (NULL != pszString)
+ {
+ free(pszString);
+ }
+}
+
+LPWSTR Plugin_ReAllocString(LPWSTR pszString, size_t cchLen)
+{
+ return (LPWSTR)realloc(pszString, sizeof(WCHAR) * cchLen);
+}
+
+LPWSTR Plugin_CopyString(LPCWSTR pszSource)
+{
+ if (NULL == pszSource)
+ return NULL;
+
+ INT cchSource = lstrlenW(pszSource) + 1;
+
+ LPWSTR copy = Plugin_MallocString(cchSource);
+ if (NULL != copy)
+ {
+ CopyMemory(copy, pszSource, sizeof(WCHAR) * cchSource);
+ }
+ return copy;
+}
+
+LPSTR Plugin_MallocAnsiString(size_t cchLen)
+{
+ return (LPSTR)calloc(cchLen, sizeof(CHAR));
+}
+
+LPSTR Plugin_CopyAnsiString(LPCSTR pszSource)
+{
+ if (NULL == pszSource)
+ return NULL;
+
+ INT cchSource = lstrlenA(pszSource) + 1;
+
+ LPSTR copy = Plugin_MallocAnsiString(cchSource);
+ if (NULL != copy)
+ {
+ CopyMemory(copy, pszSource, sizeof(CHAR) * cchSource);
+ }
+ return copy;
+
+}
+void Plugin_FreeAnsiString(LPSTR pszString)
+{
+ Plugin_FreeString((LPWSTR)pszString);
+}
+
+LPSTR Plugin_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar)
+{
+ INT cchBuffer = WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar);
+ if (0 == cchBuffer) return NULL;
+
+ LPSTR buffer = Plugin_MallocAnsiString(cchBuffer);
+ if (NULL == buffer) return NULL;
+
+ if (0 == WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, buffer, cchBuffer, lpDefaultChar, lpUsedDefaultChar))
+ {
+ Plugin_FreeAnsiString(buffer);
+ return NULL;
+ }
+ return buffer;
+}
+
+LPWSTR Plugin_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte)
+{
+ if (NULL == lpMultiByteStr) return NULL;
+ INT cchBuffer = MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0);
+ if (NULL == cchBuffer) return NULL;
+
+ if (cbMultiByte > 0) cchBuffer++;
+
+ LPWSTR buffer = Plugin_MallocString(cchBuffer);
+ if (NULL == buffer) return NULL;
+
+ if (0 == MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, buffer, cchBuffer))
+ {
+ Plugin_FreeString(buffer);
+ return NULL;
+ }
+
+ if (cbMultiByte > 0)
+ {
+ buffer[cchBuffer - 1] = L'\0';
+ }
+ return buffer;
+}
+
+
+LPWSTR Plugin_DuplicateResString(LPCWSTR pszResource)
+{
+ return (IS_INTRESOURCE(pszResource)) ?
+ (LPWSTR)pszResource :
+ Plugin_CopyString(pszResource);
+}
+
+void Plugin_FreeResString(LPWSTR pszResource)
+{
+ if (!IS_INTRESOURCE(pszResource))
+ Plugin_FreeString(pszResource);
+}
+
+HRESULT Plugin_CopyResString(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszString)
+{
+ if (NULL == pszBuffer)
+ return E_INVALIDARG;
+
+ HRESULT hr = S_OK;
+
+ if (NULL == pszString)
+ {
+ pszBuffer[0] = L'\0';
+ }
+ else if (IS_INTRESOURCE(pszString))
+ {
+ if (NULL == WASABI_API_LNG)
+ hr = E_FAIL;
+ else
+ WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszString, pszBuffer, cchBufferMax);
+ }
+ else
+ {
+ hr = StringCchCopy(pszBuffer, cchBufferMax, pszString);
+ }
+ return hr;
+}
+
+void Plugin_SafeRelease(IUnknown *pUnk)
+{
+ if (NULL != pUnk)
+ pUnk->Release();
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/common.h b/Src/Plugins/Library/ml_nowplaying/common.h
new file mode 100644
index 00000000..8e93665d
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/common.h
@@ -0,0 +1,58 @@
+#ifndef NULLSOFT_NOWPLAYING_PLUGIN_COMMON_HEADER
+#define NULLSOFT_NOWPLAYING_PLUGIN_COMMON_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "../nu/trace.h"
+
+#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(blah) (sizeof(blah)/sizeof(*blah))
+#endif
+
+#ifndef LONGX86
+#ifdef _WIN64
+ #define LONGX86 LONG_PTR
+#else /*_WIN64*/
+ #define LONGX86 LONG
+#endif /*_WIN64*/
+#endif // LONGX86
+
+#ifdef __cplusplus
+ #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) ::SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam))
+#else
+ #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam))
+#endif // __cplusplus
+
+#define SENDMLIPC(__hwndML, __ipcMsgId, __param) SENDMSG((__hwndML), WM_ML_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId))
+#define SENDWAIPC(__hwndWA, __ipcMsgId, __param) SENDMSG((__hwndWA), WM_WA_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId))
+
+#define MSGRESULT(__hwnd, __result) { SetWindowLongPtrW((__hwnd), DWL_MSGRESULT, ((LONGX86)(LONG_PTR)(__result))); return TRUE; }
+
+#define SENDCMD(__hwnd, __ctrlId, __eventId, __hctrl) (SENDMSG((__hwnd), WM_COMMAND, MAKEWPARAM(__ctrlId, __eventId), (LPARAM)(__hctrl)))
+
+
+LPWSTR Plugin_MallocString(size_t cchLen);
+LPWSTR Plugin_ReAllocString(LPWSTR pszString, size_t cchLen);
+void Plugin_FreeString(LPWSTR pszString);
+LPWSTR Plugin_CopyString(LPCWSTR pszSource);
+
+LPSTR Plugin_MallocAnsiString(size_t cchLen);
+LPSTR Plugin_CopyAnsiString(LPCSTR pszSource);
+void Plugin_FreeAnsiString(LPSTR pszString);
+
+LPWSTR Plugin_DuplicateResString(LPCWSTR pszResource);
+void Plugin_FreeResString(LPWSTR pszResource);
+HRESULT Plugin_CopyResString(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszString);
+
+LPSTR Plugin_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar);
+LPWSTR Plugin_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte);
+
+void Plugin_SafeRelease(IUnknown *pUnk);
+
+
+#endif //NULLSOFT_NOWPLAYING_PLUGIN_COMMON_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/external.cpp b/Src/Plugins/Library/ml_nowplaying/external.cpp
new file mode 100644
index 00000000..add50723
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/external.cpp
@@ -0,0 +1,110 @@
+#include "main.h"
+#include "./resource.h"
+#include "./external.h"
+
+ExternalDispatch::ExternalDispatch()
+ : ref(1)
+{
+}
+
+ExternalDispatch::~ExternalDispatch()
+{
+}
+
+HRESULT ExternalDispatch::CreateInstance(ExternalDispatch **instance)
+{
+ if (NULL == instance) return E_POINTER;
+
+ *instance = new ExternalDispatch();
+ if (NULL == *instance) return E_OUTOFMEMORY;
+
+ return S_OK;
+}
+
+LPCWSTR ExternalDispatch::GetName()
+{
+ return L"NowPlaying";
+}
+
+
+ULONG ExternalDispatch::AddRef(void)
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+
+ULONG ExternalDispatch::Release(void)
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+STDMETHODIMP ExternalDispatch::QueryInterface(REFIID riid, void **ppvObject)
+{
+ if (NULL == ppvObject) return E_POINTER;
+
+ if (IsEqualIID(riid, IID_IDispatch))
+ *ppvObject = static_cast<IDispatch*>(this);
+ else if (IsEqualIID(riid, IID_IUnknown))
+ *ppvObject = static_cast<IUnknown*>(this);
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+HRESULT ExternalDispatch::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
+{
+ bool unknowns = false;
+ for (unsigned int i = 0;i != cNames;i++)
+ {
+ if (_wcsicmp(rgszNames[i], L"hidden") == 0)
+ rgdispid[i] = DISPATCH_HIDDEN;
+ else
+ {
+ rgdispid[i] = DISPID_UNKNOWN;
+ unknowns = true;
+ }
+ }
+ if (unknowns)
+ return DISP_E_UNKNOWNNAME;
+ else
+ return S_OK;
+}
+
+HRESULT ExternalDispatch::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ExternalDispatch::GetTypeInfoCount(unsigned int FAR * pctinfo)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ExternalDispatch::Invoke(DISPID dispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
+{
+ switch(dispId)
+ {
+ case DISPATCH_HIDDEN:
+ if (NULL != pvarResult)
+ {
+ HWND hLibrary = Plugin_GetLibrary();
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_BOOL;
+ V_BOOL(pvarResult) = (NULL == hLibrary || FALSE == SENDMLIPC(hLibrary, ML_IPC_IS_VISIBLE, 0));
+ }
+ return S_OK;
+ }
+ return DISP_E_MEMBERNOTFOUND;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/external.h b/Src/Plugins/Library/ml_nowplaying/external.h
new file mode 100644
index 00000000..fe9e4a9e
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/external.h
@@ -0,0 +1,44 @@
+#ifndef NULLSOFT_NOWPLAYING_PLUGIN_EXTERNAL_HEADER
+#define NULLSOFT_NOWPLAYING_PLUGIN_EXTERNAL_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+class ExternalDispatch : public IDispatch
+{
+
+public:
+ typedef enum
+ {
+ DISPATCH_HIDDEN = 777,
+ } DispatchCodes;
+
+protected:
+ ExternalDispatch();
+ ~ExternalDispatch();
+
+public:
+ static HRESULT CreateInstance(ExternalDispatch **instance);
+ static LPCWSTR GetName();
+
+public:
+ STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+
+ // *** IDispatch Methods ***
+ STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);
+ STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);
+ STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);
+ STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);
+
+protected:
+ ULONG ref;
+
+};
+
+
+#endif //NULLSOFT_NOWPLAYING_PLUGIN_EXTERNAL_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/handler.cpp b/Src/Plugins/Library/ml_nowplaying/handler.cpp
new file mode 100644
index 00000000..2d47a94e
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/handler.cpp
@@ -0,0 +1,47 @@
+#include "main.h"
+#include "service.h"
+#include "navigation.h"
+#include "handler.h"
+#include "../Agave/URIHandler/svc_urihandler.h"
+#include <api/service/waservicefactory.h>
+#include "api.h"
+#include "../ml_online/config.h"
+#include "../replicant/nu/Autowide.h"
+
+int NowPlayingURIHandler::ProcessFilename(const wchar_t *filename)
+{
+ if (!_wcsnicmp(filename, L"winamp://Now Playing", 20) || !_wcsnicmp(filename, L"winamp://Now%20Playing", 22))
+ {
+ size_t index = 0;
+ if (filename[12] == L' ')
+ index = 20;
+ else
+ index = 22;
+
+ wchar_t fullUrl[1024] = L"http://client.winamp.com/nowplaying";
+ lstrcpynW(fullUrl, AutoWide(g_config->ReadString("nowplayingurl", "http://client.winamp.com/nowplaying")), ARRAYSIZE(fullUrl));
+
+ if (filename[index] != 0)
+ {
+ StringCchCatW(fullUrl, 1024, filename + index);
+ }
+ Navigation_ShowService(SERVICE_ID, fullUrl, NAVFLAG_FORCEACTIVE | NAVFLAG_ENSUREMLVISIBLE | NAVFLAG_ENSUREITEMVISIBLE);
+ return HANDLED_EXCLUSIVE;
+ }
+ return NOT_HANDLED;
+}
+
+int NowPlayingURIHandler::IsMine(const wchar_t *filename)
+{
+ if (!_wcsnicmp(filename, L"winamp://Now Playing", 20 ) || !_wcsnicmp(filename, L"winamp://Now%20Playing", 22))
+ return HANDLED;
+ else
+ return NOT_HANDLED;
+}
+
+#define CBCLASS NowPlayingURIHandler
+START_DISPATCH;
+CB(PROCESSFILENAME, ProcessFilename);
+CB(ISMINE, IsMine);
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/handler.h b/Src/Plugins/Library/ml_nowplaying/handler.h
new file mode 100644
index 00000000..d5021422
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/handler.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "../Agave/URIHandler/svc_urihandler.h"
+
+// {7A8BAF83-3995-4550-B15B-12D297A9220E}
+static const GUID ml_nowplaying_uri_handler =
+{ 0x7a8baf83, 0x3995, 0x4550, { 0xb1, 0x5b, 0x12, 0xd2, 0x97, 0xa9, 0x22, 0xe } };
+
+class NowPlayingURIHandler : public svc_urihandler
+{
+public:
+ static const char *getServiceName() { return "Now Playing URI Handler"; }
+ static GUID getServiceGuid() { return ml_nowplaying_uri_handler; }
+ int ProcessFilename(const wchar_t *filename);
+ int IsMine(const wchar_t *filename); // just like ProcessFilename but don't actually process
+
+protected:
+ RECVS_DISPATCH;
+}; \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/local_menu.cpp b/Src/Plugins/Library/ml_nowplaying/local_menu.cpp
new file mode 100644
index 00000000..33d3da7f
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/local_menu.cpp
@@ -0,0 +1,68 @@
+#include "main.h"
+#include "./local_menu.h"
+#include "./wasabi.h"
+#include "./resource.h"
+#include "./navigation.h"
+#include "../gen_ml/ml_ipc_0313.h"
+#include "../nu/menuHelpers.h"
+
+#define SUBMENU_NAVIGATIONCONTEXT 0
+
+static HMENU Menu_GetNavigationContext(HMENU baseMenu)
+{
+ HMENU hMenu = GetSubMenu(baseMenu, SUBMENU_NAVIGATIONCONTEXT);
+ if (NULL == hMenu) return NULL;
+
+ hMenu = MenuHelper_DuplcateMenu(hMenu);
+ if (NULL == hMenu) return NULL;
+
+ HNAVITEM hActive = Navigation_GetActive(NULL);
+ if (NULL != hActive)
+ {
+ EnableMenuItem(hMenu, ID_NAVIGATION_OPEN, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED);
+ }
+ else
+ {
+ EnableMenuItem(hMenu, ID_NAVIGATION_OPEN, MF_BYCOMMAND | MF_ENABLED);
+ SetMenuDefaultItem(hMenu, ID_NAVIGATION_OPEN, FALSE);
+ }
+
+ return hMenu;
+}
+
+HMENU Menu_GetMenu(UINT menuKind)
+{
+ HMENU baseMenu = WASABI_API_LOADMENUW(IDR_CONTEXTMENU);
+ if (NULL == baseMenu)
+ return NULL;
+
+ switch(menuKind)
+ {
+ case MENU_NAVIGATIONCONTEXT:
+ {
+ HMENU menu = Menu_GetNavigationContext(baseMenu);
+ if (!GetModuleHandle(L"ml_online.dll"))
+ {
+ if (DeleteMenu(menu, ID_PLUGIN_PREFERENCES, MF_BYCOMMAND))
+ {
+ DeleteMenu(menu, 2, MF_BYPOSITION);
+ }
+ }
+ return menu;
+ }
+ }
+
+ return NULL;
+}
+
+BOOL Menu_ReleaseMenu(HMENU hMenu, UINT menuKind)
+{
+ if (NULL == hMenu) return FALSE;
+
+ switch(menuKind)
+ {
+ case MENU_NAVIGATIONCONTEXT:
+ return DestroyMenu(hMenu);
+ }
+ return FALSE;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/local_menu.h b/Src/Plugins/Library/ml_nowplaying/local_menu.h
new file mode 100644
index 00000000..bd33edcc
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/local_menu.h
@@ -0,0 +1,15 @@
+#ifndef NULLSOFT_NOWPLAYING_PLUGIN_MENU_HEADER
+#define NULLSOFT_NOWPLAYING_PLUGIN_MENU_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+#define MENU_NAVIGATIONCONTEXT 0
+
+HMENU Menu_GetMenu(UINT menuKind);
+BOOL Menu_ReleaseMenu(HMENU hMenu, UINT menuKind);
+
+#endif //NULLSOFT_NOWPLAYING_PLUGIN_MENU_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/main.cpp b/Src/Plugins/Library/ml_nowplaying/main.cpp
new file mode 100644
index 00000000..4b5f2944
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/main.cpp
@@ -0,0 +1,160 @@
+#include "main.h"
+#include "./navigation.h"
+#include "./wasabi.h"
+#include "./resource.h"
+#include "./external.h"
+#include "./wasabiCallback.h"
+#include "../nu/MediaLibraryInterface.h"
+#include "../replicant/nu/AutoChar.h"
+#include "../winamp/wa_ipc.h"
+#include "handler.h"
+#include "../nu/Singleton.h"
+#include "../ml_online/config.h"
+#include <strsafe.h>
+
+static NowPlayingURIHandler uri_handler;
+static SingletonServiceFactory<svc_urihandler, NowPlayingURIHandler> uri_handler_factory;
+
+static DWORD externalCookie = 0;
+static SysCallback *wasabiCallback = NULL;
+C_Config *g_config = NULL;
+
+static INT Plugin_Init(void);
+static void Plugin_Quit(void);
+static INT_PTR Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3);
+
+EXTERN_C winampMediaLibraryPlugin plugin =
+{
+ MLHDR_VER,
+ "nullsoft(ml_nowplaying.dll)",
+ Plugin_Init,
+ Plugin_Quit,
+ Plugin_MessageProc,
+ 0,
+ 0,
+ 0,
+};
+
+HINSTANCE Plugin_GetInstance(void)
+{
+ return plugin.hDllInstance;
+}
+
+HWND Plugin_GetWinamp(void)
+{
+ return plugin.hwndWinampParent;
+}
+
+HWND Plugin_GetLibrary(void)
+{
+ return plugin.hwndLibraryParent;
+}
+
+void initConfigCache()
+{
+ wchar_t iniFileName[2048] = {0};
+ mediaLibrary.BuildPath(L"Plugins\\ml", iniFileName, 2048);
+ CreateDirectory(iniFileName, NULL);
+ mediaLibrary.BuildPath(L"Plugins\\ml\\ml_online.ini", iniFileName, 2048);
+ AutoChar charFn(iniFileName);
+ g_config = new C_Config(AutoChar(iniFileName));
+}
+
+static INT Plugin_Init(void)
+{
+ if (!WasabiApi_Initialize(Plugin_GetInstance()))
+ return 1;
+
+ if (NULL == OMBROWSERMNGR)
+ {
+ WasabiApi_Release();
+ return 2;
+ }
+
+ mediaLibrary.library = plugin.hwndLibraryParent;
+ mediaLibrary.winamp = plugin.hwndWinampParent;
+ mediaLibrary.instance = plugin.hDllInstance;
+
+ initConfigCache();
+
+ if (NULL != WASABI_API_LNG)
+ {
+ static wchar_t szDescription[256];
+ StringCchPrintf(szDescription, ARRAYSIZE(szDescription), WASABI_API_LNGSTRINGW(IDS_PLUGIN_NAME),
+ PLUGIN_VERSION_MAJOR, PLUGIN_VERSION_MINOR);
+ plugin.description = (char*)szDescription;
+ }
+
+ ExternalDispatch *externalDispatch;
+ if (SUCCEEDED(ExternalDispatch::CreateInstance(&externalDispatch)))
+ {
+ DispatchInfo dispatchInfo;
+ dispatchInfo.id = 0;
+ dispatchInfo.name =(LPWSTR)externalDispatch->GetName();
+ dispatchInfo.dispatch = externalDispatch;
+
+ if (0 == SENDWAIPC(Plugin_GetWinamp(), IPC_ADD_DISPATCH_OBJECT, (WPARAM)&dispatchInfo))
+ externalCookie = dispatchInfo.id;
+
+ externalDispatch->Release();
+ }
+
+ if(NULL != WASABI_API_SYSCB && NULL == wasabiCallback &&
+ SUCCEEDED(WasabiCallback::CreateInstance((WasabiCallback**)&wasabiCallback)))
+ {
+ WASABI_API_SYSCB->syscb_registerCallback(wasabiCallback, 0);
+ for (;;)
+ {
+ SysCallback *callback = WASABI_API_SYSCB->syscb_enum(SysCallback::BROWSER, 0);
+ if (NULL == callback || callback == wasabiCallback)
+ {
+ if (NULL != callback)
+ callback->Release();
+ break;
+ }
+
+ WASABI_API_SYSCB->syscb_deregisterCallback(callback);
+ WASABI_API_SYSCB->syscb_registerCallback(callback, 0);
+ callback->Release();
+ }
+ }
+
+ uri_handler_factory.Register(plugin.service, &uri_handler);
+ Navigation_Initialize();
+
+ return 0;
+}
+
+static void Plugin_Quit(void)
+{
+ if (NULL != wasabiCallback)
+ {
+ if (NULL != WASABI_API_SYSCB)
+ WASABI_API_SYSCB->syscb_deregisterCallback(wasabiCallback);
+ wasabiCallback->Release();
+ wasabiCallback = NULL;
+ }
+
+ if (0 != externalCookie)
+ {
+ HWND hWinamp = Plugin_GetWinamp();
+ SENDWAIPC(hWinamp, IPC_REMOVE_DISPATCH_OBJECT, (WPARAM)externalCookie);
+ externalCookie = 0;
+ }
+ uri_handler_factory.Deregister(plugin.service);
+ WasabiApi_Release();
+}
+
+static INT_PTR Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3)
+{
+ INT_PTR result = 0;
+ if (FALSE != Navigation_ProcessMessage(msg, param1, param2, param3, &result))
+ return result;
+
+ return FALSE;
+}
+
+EXTERN_C __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()
+{
+ return &plugin;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/main.h b/Src/Plugins/Library/ml_nowplaying/main.h
new file mode 100644
index 00000000..2141525e
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/main.h
@@ -0,0 +1,22 @@
+#ifndef NULLSOFT_NOWPLAYING_PLUGIN_MAIN_HEADER
+#define NULLSOFT_NOWPLAYING_PLUGIN_MAIN_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "../gen_ml/ml.h"
+#include "./common.h"
+
+#define PLUGIN_VERSION_MAJOR 4
+#define PLUGIN_VERSION_MINOR 1
+
+HINSTANCE Plugin_GetInstance(void);
+HWND Plugin_GetWinamp(void);
+HWND Plugin_GetLibrary(void);
+
+#include "../ml_online/config.h"
+extern C_Config *g_config;
+
+#endif //NULLSOFT_NOWPLAYING_PLUGIN_MAIN_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.rc b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.rc
new file mode 100644
index 00000000..fea60a30
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.rc
@@ -0,0 +1,99 @@
+// 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
+
+#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
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_CONTEXTMENU MENU
+BEGIN
+ POPUP "Navigation"
+ BEGIN
+ MENUITEM "&Open", ID_NAVIGATION_OPEN
+ MENUITEM "Open in &New Window", ID_NAVIGATION_OPENNEWWINDOW
+ MENUITEM SEPARATOR
+ MENUITEM "&Preferences", 40006
+ MENUITEM SEPARATOR
+ MENUITEM "Help", ID_NAVIGATION_HELP
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_PLUGIN_NAME "Nullsoft Now Playing v%d.%02d"
+ 65535 "{7F31F590-6602-45c9-B3F8-F61AE05BD1D3}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_SERVICE_NAME "Now Playing"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.sln b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.sln
new file mode 100644
index 00000000..cf52a91e
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.sln
@@ -0,0 +1,30 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29613.14
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ml_nowplaying", "ml_nowplaying.vcxproj", "{CB59733D-8D69-4DC4-AB67-C831DAE5A80F}"
+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
+ {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Debug|Win32.Build.0 = Debug|Win32
+ {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Debug|x64.ActiveCfg = Debug|x64
+ {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Debug|x64.Build.0 = Debug|x64
+ {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Release|Win32.ActiveCfg = Release|Win32
+ {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Release|Win32.Build.0 = Release|Win32
+ {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Release|x64.ActiveCfg = Release|x64
+ {CB59733D-8D69-4DC4-AB67-C831DAE5A80F}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {0DF91B23-5CCB-4CD1-8A07-03B96BB9324E}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj
new file mode 100644
index 00000000..0c9effee
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj
@@ -0,0 +1,280 @@
+<?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>{CB59733D-8D69-4DC4-AB67-C831DAE5A80F}</ProjectGuid>
+ <RootNamespace>ml_nowplaying</RootNamespace>
+ <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>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>true</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>..\..\..\external_dependencies\vcpkg</VcpkgInstalledDir>
+ <VcpkgUseStatic>true</VcpkgUseStatic>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>..\..\..\external_dependencies\vcpkg</VcpkgInstalledDir>
+ <VcpkgUseStatic>true</VcpkgUseStatic>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>..\..\..\external_dependencies\vcpkg</VcpkgInstalledDir>
+ <VcpkgUseStatic>true</VcpkgUseStatic>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>..\..\..\external_dependencies\vcpkg</VcpkgInstalledDir>
+ <VcpkgUseStatic>true</VcpkgUseStatic>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;TEST_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName).dll</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\
+xcopy /Y /D $(OutDir)*.pdb ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;TEST_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName).dll</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\
+xcopy /Y /D $(OutDir)*.pdb ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_WIN32_WINNT=0x601;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName).dll</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_WIN32_WINNT=0x601;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName).dll</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\General\gen_ml\menu.cpp" />
+ <ClCompile Include="..\ml_online\config.cpp" />
+ <ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp" />
+ <ClCompile Include="..\..\..\nu\menuHelpers.cpp" />
+ <ClCompile Include="..\..\..\nu\trace.cpp" />
+ <ClCompile Include="common.cpp" />
+ <ClCompile Include="external.cpp" />
+ <ClCompile Include="handler.cpp" />
+ <ClCompile Include="local_menu.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="navigation.cpp" />
+ <ClCompile Include="service.cpp" />
+ <ClCompile Include="wasabi.cpp" />
+ <ClCompile Include="wasabiCallback.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\General\gen_ml\menu.h" />
+ <ClInclude Include="..\ml_online\config.h" />
+ <ClInclude Include="..\..\..\nu\MediaLibraryInterface.h" />
+ <ClInclude Include="common.h" />
+ <ClInclude Include="external.h" />
+ <ClInclude Include="handler.h" />
+ <ClInclude Include="local_menu.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="navigation.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="service.h" />
+ <ClInclude Include="wasabi.h" />
+ <ClInclude Include="wasabiCallback.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="ml_nowplaying.rc" />
+ <ResourceCompile Include="png.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\omBrowser\omBrowser.vcxproj">
+ <Project>{fc74db95-5008-4d22-9147-1c052f43cdd3}</Project>
+ </ProjectReference>
+ <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/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj.filters b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj.filters
new file mode 100644
index 00000000..046d44c4
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj.filters
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="common.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\ml_online\config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="external.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="handler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="local_menu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nu\MediaLibraryInterface.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\gen_ml\menu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nu\menuHelpers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="navigation.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="service.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nu\trace.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wasabi.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wasabiCallback.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="common.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\ml_online\config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="external.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="handler.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="local_menu.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\nu\MediaLibraryInterface.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\gen_ml\menu.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="navigation.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="service.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wasabi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wasabiCallback.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="png.rc">
+ <Filter>Header Files</Filter>
+ </ResourceCompile>
+ <ResourceCompile Include="ml_nowplaying.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{a1f12bb1-c2c0-49d3-b74a-5f8bbac3dee6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{8fef1ee3-d193-47aa-872e-843f5178bcc5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{f17e7245-8101-4787-b60f-874085ddb8a7}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/navigation.cpp b/Src/Plugins/Library/ml_nowplaying/navigation.cpp
new file mode 100644
index 00000000..8f662db5
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/navigation.cpp
@@ -0,0 +1,684 @@
+#include "main.h"
+#include "./navigation.h"
+#include "./resource.h"
+#include "./wasabi.h"
+#include "./service.h"
+#include "../omBrowser/browserView.h"
+#include "../winamp/wa_ipc.h"
+#include "../replicant/nu/Autowide.h"
+#include "../gen_ml/ml_ipc_0313.h"
+#include "./local_menu.h"
+#include "../gen_ml/menu.h"
+#include <strsafe.h>
+
+#define NAVITEM_PREFIX L"nowplaying_svc_"
+
+#define E_NAVITEM_UNKNOWN E_NOINTERFACE
+
+typedef struct __NAVENUMRESULT
+{
+ HNAVITEM hItem;
+ OmService *service;
+ UINT serviceId;
+ LPCWSTR pszPrefix;
+ INT cchPrefix;
+ HWND hLibrary;
+ NAVITEM itemInfo;
+ WCHAR szBuffer[256];
+} NAVENUMRESULT;
+
+typedef struct __FORCEURLDATA
+{
+ UINT serviceId;
+ LPWSTR url;
+} FORCEURLDATA;
+
+#define FORCEURLPROP L"MLNOWPLAYING_FORCEURL"
+
+static void Navigation_RemoveForceUrl()
+{
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return;
+
+ FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP);
+ RemoveProp(hLibrary, FORCEURLPROP);
+ if (NULL != data)
+ {
+ Plugin_FreeString(data->url);
+ free(data);
+ }
+}
+
+static HRESULT Navigation_SetForceUrl(UINT serviceId, LPCWSTR pszUrl)
+{
+ if (NULL == pszUrl) return E_INVALIDARG;
+
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return E_FAIL;
+
+ FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP);
+
+ if (NULL != data)
+ {
+ Plugin_FreeString(data->url);
+ if (data->serviceId != serviceId)
+ {
+ free(data);
+ data = NULL;
+ }
+ }
+
+ if (NULL == data)
+ {
+ data = (FORCEURLDATA*)calloc(1, sizeof(FORCEURLDATA));
+ if (NULL == data) return E_OUTOFMEMORY;
+ data->serviceId = serviceId;
+ }
+
+ data->url = Plugin_CopyString(pszUrl);
+ if (NULL == data->url || FALSE == SetProp(hLibrary, FORCEURLPROP, data))
+ {
+ Navigation_RemoveForceUrl();
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+static HRESULT Navigation_GetForceUrl(UINT serviceId, const wchar_t **ppszUrl)
+{
+ if (NULL == ppszUrl) return E_POINTER;
+ *ppszUrl = NULL;
+
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return E_FAIL;
+
+ FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP);
+
+ if (NULL == data || data->serviceId != serviceId)
+ return E_NOINTERFACE;
+
+ *ppszUrl = data->url;
+ return S_OK;
+}
+
+static INT Navigation_GetIconIndex(LPCWSTR pszImage)
+{
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return -1;
+
+ HMLIMGLST hmlilNavigation = MLNavCtrl_GetImageList(hLibrary);
+ if (NULL == hmlilNavigation) return -1;
+
+ MLIMAGESOURCE mlis;
+ ZeroMemory(&mlis, sizeof(mlis));
+ mlis.cbSize = sizeof(mlis);
+ mlis.hInst = NULL;
+ mlis.bpp = 24;
+ mlis.lpszName = pszImage;
+ mlis.type = SRC_TYPE_PNG;
+ mlis.flags = ISF_FORCE_BPP | ISF_PREMULTIPLY | ISF_LOADFROMFILE;
+
+ MLIMAGELISTITEM item;
+ ZeroMemory(&item, sizeof(item));
+ item.cbSize = sizeof(item);
+ item.hmlil = hmlilNavigation;
+ item.filterUID = MLIF_FILTER3_UID;
+ item.pmlImgSource = &mlis;
+
+ return MLImageList_Add(hLibrary, &item);
+}
+
+static HNAVITEM Navigation_CreateItem(HWND hLibrary, HNAVITEM hParent, OmService *service)
+{
+ if (NULL == hLibrary || NULL == service)
+ return NULL;
+
+ WCHAR szName[256] = {0}, szInvariant[64] = {0};
+ if (FAILED(service->GetName(szName, ARRAYSIZE(szName))))
+ return NULL;
+
+ if (FAILED(StringCchPrintf(szInvariant, ARRAYSIZE(szInvariant), NAVITEM_PREFIX L"%u", service->GetId())))
+ return NULL;
+
+ NAVINSERTSTRUCT nis = {0};
+ nis.hInsertAfter = NULL;
+ nis.hParent = hParent;
+
+ WCHAR szIcon[512] = {0};
+ INT iIcon = (SUCCEEDED(service->GetIcon(szIcon, ARRAYSIZE(szIcon)))) ?
+ Navigation_GetIconIndex(szIcon) : -1;
+
+ nis.item.cbSize = sizeof(NAVITEM);
+ nis.item.mask = NIMF_TEXT | NIMF_STYLE | NIMF_TEXTINVARIANT | NIMF_PARAM;
+ if (-1 != iIcon)
+ nis.item.mask |= (NIMF_IMAGE | NIMF_IMAGESEL);
+
+ nis.item.id = 0;
+ nis.item.pszText = szName;
+ nis.item.pszInvariant = szInvariant;
+ nis.item.style = NIS_ALLOWCHILDMOVE;
+ nis.item.styleMask = nis.item.style;
+ nis.item.lParam = (LPARAM)service;
+ nis.item.iImage = iIcon;
+ nis.item.iSelectedImage = iIcon;
+
+ HNAVITEM hItem = MLNavCtrl_InsertItem(hLibrary, &nis);
+ if (NULL != hItem)
+ service->AddRef();
+
+ return hItem;
+}
+
+static HNAVITEM Navigation_GetMessageItem(INT msg, INT_PTR param1)
+{
+ HWND hLibrary = Plugin_GetLibrary();
+ HNAVITEM hItem = (msg < ML_MSG_NAVIGATION_FIRST) ? MLNavCtrl_FindItemById(hLibrary, param1) : (HNAVITEM)param1;
+ return hItem;
+}
+
+static HRESULT Navigation_GetService(HWND hLibrary, HNAVITEM hItem, OmService **service)
+{
+ WCHAR szBuffer[64] = {0};
+
+ if (NULL == service) return E_POINTER;
+ *service = NULL;
+
+ if (NULL == hLibrary || NULL == hItem) return E_INVALIDARG;
+
+ NAVITEM itemInfo = {0};
+ itemInfo.cbSize = sizeof(NAVITEM);
+ itemInfo.hItem = hItem;
+ itemInfo.pszInvariant = szBuffer;
+ itemInfo.cchInvariantMax = ARRAYSIZE(szBuffer);
+ itemInfo.mask = NIMF_PARAM | NIMF_TEXTINVARIANT;
+
+ if (FALSE == MLNavItem_GetInfo(hLibrary, &itemInfo))
+ return E_FAIL;
+
+ INT cchInvariant = lstrlen(szBuffer);
+ INT cchPrefix = ARRAYSIZE(NAVITEM_PREFIX) - 1;
+ if (cchInvariant <= cchPrefix ||
+ CSTR_EQUAL != CompareString(CSTR_INVARIANT, 0, NAVITEM_PREFIX, cchPrefix, szBuffer, cchPrefix))
+ {
+ return E_NAVITEM_UNKNOWN;
+ }
+
+ *service = (OmService*)itemInfo.lParam;
+ (*service)->AddRef();
+ return S_OK;
+}
+
+
+static BOOL CALLBACK Navigation_ItemEnumerator(HNAVITEM hItem, LPARAM param)
+{
+ if (NULL == hItem) return TRUE;
+ NAVENUMRESULT *result = (NAVENUMRESULT*)param;
+ if (NULL == result) return FALSE;
+
+ result->itemInfo .hItem = hItem;
+ if (FALSE != MLNavItem_GetInfo(result->hLibrary, &result->itemInfo) &&
+ CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, result->itemInfo.pszInvariant, result->cchPrefix,
+ result->pszPrefix, result->cchPrefix))
+ {
+ OmService *service = (OmService*)result->itemInfo.lParam;
+ if (NULL != service && service->GetId() == result->serviceId)
+ {
+ result->hItem = hItem;
+ result->service = service;
+ service->AddRef();
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static HRESULT Navigation_CreateView(HNAVITEM hItem, HWND hParent, HWND *hView)
+{
+ if (NULL == hView) return E_POINTER;
+ *hView = NULL;
+
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ if (NULL == hItem || NULL == hParent) return E_INVALIDARG;
+
+ HRESULT hr;
+
+ OmService *service = NULL;
+ hr = Navigation_GetService(hLibrary, hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ if (NULL == OMBROWSERMNGR)
+ hr = E_UNEXPECTED;
+
+ if (SUCCEEDED(hr))
+ {
+ hr = OMBROWSERMNGR->Initialize(NULL, Plugin_GetWinamp());
+ if (SUCCEEDED(hr))
+ {
+ LPCWSTR forceUrl;
+ if (FAILED(Navigation_GetForceUrl(service->GetId(), &forceUrl)))
+ forceUrl = NULL;
+
+ hr = OMBROWSERMNGR->CreateView(service, hParent, forceUrl, 0, hView);
+ Navigation_RemoveForceUrl();
+ }
+ }
+
+ wchar_t nowplayingurl[1024] = {0};
+ // May 2022 - this service url is dead and would need either fixing up or replacing
+ lstrcpynW(nowplayingurl, AutoWide(g_config->ReadString("nowplayingurl", "http://client.winamp.com/nowplaying")), ARRAYSIZE(nowplayingurl));
+ service->SetUrl(nowplayingurl[0] ? nowplayingurl : SERVICE_HOMEURL);
+ service->Release();
+ }
+ return hr;
+}
+
+static BOOL Navigation_GetViewRect(RECT *rect)
+{
+ if (NULL == rect) return FALSE;
+
+ HWND hWinamp = Plugin_GetWinamp();
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hWinamp || NULL == hLibrary)
+ return FALSE;
+
+ HWND hFrame = (HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0);
+ if (NULL == hFrame)
+ hFrame = hLibrary;
+
+ return GetWindowRect(hFrame, rect);
+}
+
+static HRESULT Navigation_CreatePopup(HNAVITEM hItem, HWND *hWindow)
+{
+ if (NULL == hWindow) return E_POINTER;
+ *hWindow = NULL;
+
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ if (NULL == hItem) return E_INVALIDARG;
+
+ HRESULT hr;
+
+ OmService *service;
+ hr = Navigation_GetService(hLibrary, hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ HWND hWinamp = Plugin_GetWinamp();
+
+ if (NULL == OMBROWSERMNGR)
+ hr = E_UNEXPECTED;
+
+ if (SUCCEEDED(hr))
+ {
+ hr = OMBROWSERMNGR->Initialize(NULL, hWinamp);
+ if (SUCCEEDED(hr))
+ {
+ RECT rect;
+ if (FALSE == Navigation_GetViewRect(&rect))
+ hr = E_FAIL;
+
+ if (SUCCEEDED(hr))
+ {
+ rect.left += 16;
+ rect.top += 16;
+
+ hr = OMBROWSERMNGR->CreatePopup(service, rect.left, rect.top,
+ rect.right - rect.left, rect.bottom - rect.top, hWinamp, NULL, 0, hWindow);
+ }
+ }
+ }
+
+ service->Release();
+ }
+
+ return hr;
+}
+
+
+static void Navigation_OnDestroy()
+{
+ Navigation_RemoveForceUrl();
+
+ if (NULL != OMBROWSERMNGR)
+ {
+ OMBROWSERMNGR->Finish();
+ }
+}
+
+static void Navigation_OpenPreferences()
+{
+ winampMediaLibraryPlugin *(*gp)();
+ gp = (winampMediaLibraryPlugin * (__cdecl *)(void))GetProcAddress(GetModuleHandle(L"ml_online.dll"), "winampGetMediaLibraryPlugin");
+ if (gp)
+ {
+ winampMediaLibraryPlugin *mlplugin = gp();
+ if (mlplugin && (mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER))
+ {
+ mlplugin->MessageProc(ML_MSG_CONFIG, 0, 0, 0);
+ }
+ else
+ SendMessage(Plugin_GetWinamp(), WM_WA_IPC, (WPARAM)-1, IPC_OPENPREFSTOPAGE);
+ }
+ else
+ SendMessage(Plugin_GetWinamp(), WM_WA_IPC, (WPARAM)-1, IPC_OPENPREFSTOPAGE);
+}
+
+static HRESULT Navigation_ShowContextMenu(HNAVITEM hItem, HWND hHost, POINTS pts)
+{
+ if (NULL == hItem || NULL == hHost)
+ return E_INVALIDARG;
+
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ HRESULT hr;
+
+ OmService *service;
+ hr = Navigation_GetService(hLibrary, hItem, &service);
+ if (FAILED(hr)) return hr;
+
+ POINT pt;
+ POINTSTOPOINT(pt, pts);
+ if (-1 == pt.x || -1 == pt.y)
+ {
+ NAVITEMGETRECT itemRect;
+ itemRect.fItem = FALSE;
+ itemRect.hItem = hItem;
+ if (MLNavItem_GetRect(hLibrary, &itemRect))
+ {
+ MapWindowPoints(hHost, HWND_DESKTOP, (POINT*)&itemRect.rc, 2);
+ pt.x = itemRect.rc.left + 2;
+ pt.y = itemRect.rc.top + 2;
+ }
+ }
+
+ HMENU hMenu = Menu_GetMenu(MENU_NAVIGATIONCONTEXT);
+ if (NULL != hMenu)
+ {
+ INT commandId = Menu_TrackPopup(hLibrary, hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
+ pt.x, pt.y, hHost, NULL);
+
+ Menu_ReleaseMenu(hMenu, MENU_NAVIGATIONCONTEXT);
+
+ switch(commandId)
+ {
+ case ID_NAVIGATION_OPEN:
+ MLNavItem_Select(hLibrary, hItem);
+ break;
+
+ case ID_NAVIGATION_OPENNEWWINDOW:
+ {
+ HWND hWindow;
+ if (SUCCEEDED(Navigation_CreatePopup(hItem, &hWindow)))
+ {
+ ShowWindow(hWindow, SW_SHOWNORMAL);
+ }
+ }
+ break;
+ case ID_NAVIGATION_HELP:
+ SENDWAIPC(Plugin_GetWinamp(), IPC_OPEN_URL, L"https://help.winamp.com/hc/articles/8105304048660-The-Winamp-Media-Library");
+ break;
+
+ case ID_PLUGIN_PREFERENCES:
+ Navigation_OpenPreferences();
+ break;
+ }
+ }
+
+ service->Release();
+
+ return hr;
+}
+
+BOOL Navigation_Initialize(void)
+{
+ OmService *service;
+ HWND hLibrary = Plugin_GetLibrary();
+
+ MLNavCtrl_BeginUpdate(hLibrary, NUF_LOCK_TOP);
+
+ if (SUCCEEDED(OmService::CreateInstance(&service)))
+ {
+ HNAVITEM hParent = NULL;
+ Navigation_CreateItem(hLibrary, hParent, service);
+ service->Release();
+ }
+
+ MLNavCtrl_EndUpdate(hLibrary);
+
+ return TRUE;
+}
+
+static void Navigation_OnDeleteItem(HNAVITEM hItem)
+{
+ if (NULL == hItem) return;
+
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return;
+
+ OmService *service;
+ if (SUCCEEDED(Navigation_GetService(hLibrary, hItem, &service)))
+ {
+
+ NAVITEM itemInfo;
+ itemInfo.cbSize = sizeof(NAVITEM);
+ itemInfo.hItem = hItem;
+ itemInfo.mask = NIMF_PARAM;
+ itemInfo.lParam = 0L;
+ MLNavItem_SetInfo(hLibrary, &itemInfo);
+
+ service->Release(); // create
+ service->Release(); // Navigation_GetService
+ }
+}
+
+BOOL Navigation_ProcessMessage(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3, INT_PTR *result)
+{
+ if (msg == ML_MSG_NO_CONFIG)
+ {
+ if (!GetModuleHandle(L"ml_online.dll"))
+ {
+ *result = TRUE;
+ return TRUE;
+ }
+ }
+ else if (msg == ML_MSG_CONFIG)
+ {
+ Navigation_OpenPreferences();
+ *result = TRUE;
+ return TRUE;
+ }
+
+ if (msg < ML_MSG_TREE_BEGIN || msg > ML_MSG_TREE_END)
+ return FALSE;
+
+ switch(msg)
+ {
+ case ML_MSG_TREE_ONCREATEVIEW:
+ {
+ HWND hView;
+ HNAVITEM hItem = Navigation_GetMessageItem(msg, param1);
+ HRESULT hr = Navigation_CreateView(hItem, (HWND)param2, &hView);
+ if (SUCCEEDED(hr))
+ {
+ *result = (INT_PTR)hView;
+ return TRUE;
+ }
+ }
+ break;
+
+ case ML_MSG_NAVIGATION_ONDESTROY:
+ Navigation_OnDestroy();
+ break;
+
+ case ML_MSG_NAVIGATION_CONTEXTMENU:
+ {
+ HNAVITEM hItem = Navigation_GetMessageItem(msg, param1);
+ HRESULT hr = Navigation_ShowContextMenu(hItem, (HWND)param2, MAKEPOINTS(param3));
+ if (SUCCEEDED(hr))
+ {
+ *result = TRUE;
+ return TRUE;
+ }
+ }
+ break;
+
+ case ML_MSG_NAVIGATION_ONDELETE:
+ {
+ HNAVITEM hItem = Navigation_GetMessageItem(msg, param1);
+ Navigation_OnDeleteItem(hItem);
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+HNAVITEM Navigation_FindService(UINT serviceId, OmService **serviceOut)
+{
+ NAVENUMRESULT result;
+ result.hItem = NULL;
+ result.service = NULL;
+
+ result.serviceId = serviceId;
+ result.pszPrefix = NAVITEM_PREFIX;
+ result.cchPrefix = lstrlen(result.pszPrefix);
+
+ result.hLibrary = Plugin_GetLibrary();
+ result.itemInfo.cbSize = sizeof(result.itemInfo);
+ result.itemInfo.mask = NIMF_TEXTINVARIANT | NIMF_PARAM;
+ result.itemInfo.cchInvariantMax = ARRAYSIZE(result.szBuffer);
+ result.itemInfo.pszInvariant = result.szBuffer;
+
+ NAVCTRLENUMPARAMS param;
+ param.enumProc = Navigation_ItemEnumerator;
+ param.hItemStart = NULL;
+ param.lParam = (LPARAM)&result;
+
+ if (NULL != result.hLibrary)
+ MLNavCtrl_EnumItems(result.hLibrary, &param);
+
+ if (NULL != serviceOut)
+ *serviceOut = result.service;
+ else if (NULL != result.service)
+ result.service->Release();
+
+ return result.hItem;
+}
+
+HRESULT Navigation_ShowService(UINT serviceId, LPCWSTR pszUrl, UINT navFlags)
+{
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary)
+ return E_FAIL;
+
+ OmService *service;
+ HNAVITEM hItem = Navigation_FindService(serviceId, &service);
+ if (NULL == hItem)
+ return E_FAIL;
+
+ OmService *activeService;
+ HWND hView = Navigation_GetActiveView(&activeService);
+ if (NULL == hView || activeService->GetId() != service->GetId())
+ {
+ hView = NULL;
+ activeService = NULL;
+ }
+
+ HRESULT hr = S_OK;
+
+ if (NULL != hView)
+ {
+ if (NULL == pszUrl && 0 != (NAVFLAG_FORCEACTIVE & navFlags))
+ pszUrl = NAVIGATE_HOME;
+
+ if (NULL != pszUrl && FALSE == BrowserView_Navigate(hView, pszUrl, TRUE))
+ hr = E_FAIL;
+ }
+ else
+ {
+ if (NULL != pszUrl)
+ hr = Navigation_SetForceUrl(serviceId, pszUrl);
+ else
+ Navigation_RemoveForceUrl();
+
+ if (SUCCEEDED(hr) && FALSE == MLNavItem_Select(hLibrary, hItem))
+ {
+ Navigation_RemoveForceUrl();
+ hr = E_FAIL;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (0 != (NAVFLAG_ENSUREITEMVISIBLE & navFlags))
+ MLNavItem_EnsureVisible(hLibrary, hItem);
+
+ if (0 != (NAVFLAG_ENSUREMLVISIBLE & navFlags))
+ SENDMLIPC(hLibrary, ML_IPC_ENSURE_VISIBLE, 0L);
+ }
+
+ service->Release();
+ if (NULL != activeService)
+ activeService->Release();
+
+ return hr;
+
+}
+HNAVITEM Navigation_GetActive(OmService **serviceOut)
+{
+ HWND hLibrary = Plugin_GetLibrary();
+
+ OmService *service;
+ HNAVITEM hActive = (NULL != hLibrary) ? MLNavCtrl_GetSelection(hLibrary) : NULL;
+ if (NULL == hActive || FAILED(Navigation_GetService(hLibrary, hActive, &service)))
+ {
+ hActive = NULL;
+ service = NULL;
+ }
+
+ if (NULL != serviceOut)
+ *serviceOut = service;
+
+ return hActive;
+}
+
+HWND Navigation_GetActiveView(OmService **serviceOut)
+{
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary)
+ {
+ if (NULL != serviceOut) *serviceOut = NULL;
+ return NULL;
+ }
+
+
+ HWND hView =((HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0));
+ if (NULL != hView)
+ {
+ WCHAR szBuffer[128] = {0};
+ if (!GetClassName(hView, szBuffer, ARRAYSIZE(szBuffer)) || CSTR_EQUAL != CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, szBuffer, -1, L"Nullsoft_omBrowserView", -1))
+ hView = NULL;
+ }
+
+ OmService *service;
+ HNAVITEM hActive = (NULL != hLibrary) ? MLNavCtrl_GetSelection(hLibrary) : NULL;
+ if (NULL == hView || FALSE == BrowserView_GetService(hView, &service))
+ {
+ hView = NULL;
+ service = NULL;
+ }
+
+ if (NULL != serviceOut)
+ *serviceOut = service;
+ else if (NULL != service)
+ service->Release();
+
+ return hView;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/navigation.h b/Src/Plugins/Library/ml_nowplaying/navigation.h
new file mode 100644
index 00000000..d37264db
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/navigation.h
@@ -0,0 +1,27 @@
+#ifndef NULLSOFT_NOWPLAYING_PLUGIN_NAVIGATION_HEADER
+#define NULLSOFT_NOWPLAYING_PLUGIN_NAVIGATION_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+typedef LPVOID HNAVITEM;
+class OmService;
+
+
+BOOL Navigation_Initialize(void);
+BOOL Navigation_ProcessMessage(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3, INT_PTR *result);
+
+#define NAVFLAG_NORMAL 0x0000
+#define NAVFLAG_ENSUREITEMVISIBLE 0x0001
+#define NAVFLAG_ENSUREMLVISIBLE 0x0002
+#define NAVFLAG_FORCEACTIVE 0x0004
+
+HRESULT Navigation_ShowService(UINT serviceId, LPCWSTR pszUrl, UINT navFlags);
+HNAVITEM Navigation_FindService(UINT serviceId, OmService **serviceOut);
+HNAVITEM Navigation_GetActive(OmService **serviceOut);
+HWND Navigation_GetActiveView(OmService **serviceOut);
+
+#endif //NULLSOFT_NOWPLAYING_PLUGIN_NAVIGATION_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/png.rc b/Src/Plugins/Library/ml_nowplaying/png.rc
new file mode 100644
index 00000000..ac25fbdd
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/png.rc
@@ -0,0 +1,7 @@
+#include "resource.h"
+/////////////////////////////////////////////////////////////////////////////
+//
+// Data
+//
+IDR_SERVICE_ICON RCDATA
+".\\resources\\serviceIcon.png" \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/resource.h b/Src/Plugins/Library/ml_nowplaying/resource.h
new file mode 100644
index 00000000..74da0171
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/resource.h
@@ -0,0 +1,23 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ml_nowplaying.rc
+//
+#define IDS_SERVICE_NAME 1
+#define IDR_CONTEXTMENU 101
+#define IDR_SERVICE_ICON 20000
+#define ID_NAVIGATION_OPENNEWWINDOW 40000
+#define ID_NAVIGATION_OPEN 40001
+#define ID_NAVIGATION_HELP 40005
+#define ID_PLUGIN_PREFERENCES 40006
+#define IDS_PLUGIN_NAME 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 105
+#define _APS_NEXT_COMMAND_VALUE 40007
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Library/ml_nowplaying/resources/serviceIcon.png b/Src/Plugins/Library/ml_nowplaying/resources/serviceIcon.png
new file mode 100644
index 00000000..13d47f3b
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/resources/serviceIcon.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_nowplaying/service.cpp b/Src/Plugins/Library/ml_nowplaying/service.cpp
new file mode 100644
index 00000000..5d37ec21
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/service.cpp
@@ -0,0 +1,171 @@
+#include "main.h"
+#include "./service.h"
+#include "./wasabi.h"
+#include "./resource.h"
+#include "../replicant/nu/Autowide.h"
+#include "../winamp/wa_ipc.h"
+#include <strsafe.h>
+
+#define IS_INVALIDISPATCH(__disp) (((IDispatch *)1) == (__disp) || NULL == (__disp))
+
+OmService::OmService(UINT nId)
+ : ref(1), id(nId), name(NULL), url(NULL), icon(NULL)
+{
+}
+
+OmService::~OmService()
+{
+ Plugin_FreeResString(name);
+ Plugin_FreeString(url);
+ Plugin_FreeResString(icon);
+}
+
+HRESULT OmService::CreateInstance(OmService **instance)
+{
+ if (NULL == instance) return E_POINTER;
+ *instance = NULL;
+
+ OmService *service = new OmService(SERVICE_ID);
+ if (NULL == service) return E_OUTOFMEMORY;
+
+ wchar_t nowplayingurl[1024] = {0};
+ lstrcpynW(nowplayingurl, AutoWide(g_config->ReadString("nowplayingurl","")), ARRAYSIZE(nowplayingurl));
+
+ service->SetName(MAKEINTRESOURCE(IDS_SERVICE_NAME));
+ service->SetUrl((nowplayingurl[0] ? nowplayingurl : SERVICE_HOMEURL));
+ service->SetIcon(MAKEINTRESOURCE(IDR_SERVICE_ICON));
+
+ *instance = service;
+ return S_OK;
+}
+
+size_t OmService::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+size_t OmService::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+int OmService::QueryInterface(GUID interface_guid, void **object)
+{
+ if (NULL == object) return E_POINTER;
+
+ if (IsEqualIID(interface_guid, IFC_OmService))
+ *object = static_cast<ifc_omservice*>(this);
+ else
+ {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+
+ if (NULL == *object)
+ return E_UNEXPECTED;
+
+ AddRef();
+ return S_OK;
+}
+
+unsigned int OmService::GetId()
+{
+ return id;
+}
+
+HRESULT OmService::GetName(wchar_t *pszBuffer, int cchBufferMax)
+{
+ return Plugin_CopyResString(pszBuffer, cchBufferMax, name);
+}
+
+HRESULT OmService::GetUrl(wchar_t *pszBuffer, int cchBufferMax)
+{
+ return StringCchCopyEx(pszBuffer, cchBufferMax, url, NULL, NULL, STRSAFE_IGNORE_NULLS);
+}
+
+HRESULT OmService::GetIcon(wchar_t *pszBuffer, int cchBufferMax)
+{
+ if (NULL != icon && IS_INTRESOURCE(icon))
+ {
+ WCHAR szPath[2*MAX_PATH] = {0};
+ if (0 == GetModuleFileName(Plugin_GetInstance(), szPath, ARRAYSIZE(szPath)))
+ return E_FAIL;
+
+ return StringCchPrintf(pszBuffer, cchBufferMax, L"res://%s/#%d/#%d", szPath, RT_RCDATA, icon);
+ }
+
+ return StringCchCopyEx(pszBuffer, cchBufferMax, icon, NULL, NULL, STRSAFE_IGNORE_NULLS);
+}
+
+HRESULT OmService::GetExternal(IDispatch **ppDispatch)
+{
+ if (NULL == ppDispatch)
+ return E_POINTER;
+
+ *ppDispatch = NULL;
+
+ HWND hWinamp = Plugin_GetWinamp();
+ if (NULL == hWinamp)
+ return E_UNEXPECTED;
+
+ // So far we do not use JSAPI2 in nowplaying
+ // // try JSAPI2 first
+ // WCHAR szBuffer[64] = {0};
+ // if (SUCCEEDED(StringCchPrintfW(szBuffer, ARRAYSIZE(szBuffer), L"%u", id)))
+ // *ppDispatch = (IDispatch*)SENDWAIPC(hWinamp, IPC_JSAPI2_GET_DISPATCH_OBJECT, (WPARAM)szBuffer);
+
+ if (IS_INVALIDISPATCH(*ppDispatch))
+ { // try JSAPI1
+ *ppDispatch = (IDispatch*)SENDWAIPC(hWinamp, IPC_GET_DISPATCH_OBJECT, 0);
+ if (IS_INVALIDISPATCH(*ppDispatch))
+ { // Fail
+ *ppDispatch = NULL;
+ return E_FAIL;
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT OmService::SetName(LPCWSTR pszName)
+{
+ Plugin_FreeResString(name);
+ name = Plugin_DuplicateResString(pszName);
+ return S_OK;
+}
+
+HRESULT OmService::SetUrl(LPCWSTR pszUrl)
+{
+ Plugin_FreeString(url);
+ url = Plugin_CopyString(pszUrl);
+ return S_OK;
+}
+
+HRESULT OmService::SetIcon(LPCWSTR pszIcon)
+{
+ Plugin_FreeResString(icon);
+ icon = Plugin_DuplicateResString(pszIcon);
+ return S_OK;
+}
+
+#define CBCLASS OmService
+START_DISPATCH;
+CB(ADDREF, AddRef)
+CB(RELEASE, Release)
+CB(QUERYINTERFACE, QueryInterface)
+CB(API_GETID, GetId)
+CB(API_GETNAME, GetName)
+CB(API_GETURL, GetUrl)
+CB(API_GETICON, GetIcon)
+CB(API_GETEXTERNAL, GetExternal)
+END_DISPATCH;
+#undef CBCLASS
+
+
diff --git a/Src/Plugins/Library/ml_nowplaying/service.h b/Src/Plugins/Library/ml_nowplaying/service.h
new file mode 100644
index 00000000..292a4628
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/service.h
@@ -0,0 +1,53 @@
+#ifndef NULLSOFT_NOWPLAYING_PLUGIN_SERVICE_HEADER
+#define NULLSOFT_NOWPLAYING_PLUGIN_SERVICE_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include <ifc_omservice.h>
+
+#define SERVICE_ID 101
+#define SERVICE_HOMEURL L"http://client.winamp.com/nowplaying?v=5.9&icid=navigationtree"
+
+class OmService : public ifc_omservice
+{
+
+protected:
+ OmService(UINT nId);
+ ~OmService();
+
+public:
+ static HRESULT CreateInstance(OmService **instance);
+
+public:
+ /* Dispatchable */
+ size_t AddRef();
+ size_t Release();
+ int QueryInterface(GUID interface_guid, void **object);
+
+ /* ifc_omservice */
+ unsigned int GetId();
+ HRESULT GetName(wchar_t *pszBuffer, int cchBufferMax);
+ HRESULT GetUrl(wchar_t *pszBuffer, int cchBufferMax);
+ HRESULT GetExternal(IDispatch **ppDispatch);
+ HRESULT GetIcon(wchar_t *pszBuffer, int cchBufferMax);
+
+public:
+ HRESULT SetName(LPCWSTR pszName);
+ HRESULT SetUrl(LPCWSTR pszUrl);
+ HRESULT SetIcon(LPCWSTR pszIcon);
+
+protected:
+ RECVS_DISPATCH;
+
+protected:
+ ULONG ref;
+ UINT id;
+ LPWSTR name;
+ LPWSTR url;
+ LPWSTR icon;
+};
+
+#endif //NULLSOFT_NOWPLAYING_PLUGIN_SERVICE_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/version.rc2 b/Src/Plugins/Library/ml_nowplaying/version.rc2
new file mode 100644
index 00000000..47a11f64
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 4,0,1,0
+ 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 Media Library Plug-in"
+ VALUE "FileVersion", "4,0,1,0"
+ VALUE "InternalName", "Nullsoft Nowplaying"
+ VALUE "LegalCopyright", "Copyright © 2003-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "ml_nowplaying.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/Src/Plugins/Library/ml_nowplaying/wasabi.cpp b/Src/Plugins/Library/ml_nowplaying/wasabi.cpp
new file mode 100644
index 00000000..7a071143
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/wasabi.cpp
@@ -0,0 +1,82 @@
+#include "main.h"
+#include "./wasabi.h"
+#include <api/service/waservicefactory.h>
+
+static ULONG wasabiRef = 0;
+
+api_application *WASABI_API_APP = NULL;
+api_language *WASABI_API_LNG = NULL;
+JSAPI2::api_security *AGAVE_API_JSAPI2_SECURITY = NULL;
+obj_ombrowser *browserManager = NULL;
+api_syscb *WASABI_API_SYSCB = NULL;
+ifc_omutility *omUtility = NULL;
+
+HINSTANCE WASABI_API_LNG_HINST = NULL;
+HINSTANCE WASABI_API_ORIG_HINST = NULL;
+EXTERN_C winampMediaLibraryPlugin plugin;
+
+void *Wasabi_QueryInterface(REFGUID interfaceGuid)
+{
+ waServiceFactory *serviceFactory = plugin.service->service_getServiceByGuid(interfaceGuid);
+ return (NULL != serviceFactory) ? serviceFactory->getInterface() : NULL;
+}
+
+void Wasabi_ReleaseInterface(REFGUID interfaceGuid, void *pInstance)
+{
+ waServiceFactory *serviceFactory = plugin.service->service_getServiceByGuid(interfaceGuid);
+ if (NULL != serviceFactory) serviceFactory->releaseInterface(pInstance);
+}
+
+void Wasabi_SafeRelease(Dispatchable *pDisp)
+{
+ if (NULL != pDisp)
+ pDisp->Release();
+}
+
+BOOL WasabiApi_Initialize(HINSTANCE hInstance)
+{
+ WASABI_API_APP = QueryWasabiInterface(api_application, applicationApiServiceGuid);
+ WASABI_API_SYSCB = QueryWasabiInterface(api_syscb, syscbApiServiceGuid);
+ WASABI_API_LNG = QueryWasabiInterface(api_language, languageApiGUID);
+ AGAVE_API_JSAPI2_SECURITY = QueryWasabiInterface(JSAPI2::api_security, JSAPI2::api_securityGUID);
+ OMBROWSERMNGR = QueryWasabiInterface(obj_ombrowser, OBJ_OmBrowser);
+ OMUTILITY = QueryWasabiInterface(ifc_omutility, IFC_OmUtility);
+
+ if (NULL != WASABI_API_LNG)
+ WASABI_API_START_LANG(hInstance, MlNowPlayingLangGUID);
+
+ WasabiApi_AddRef();
+ return TRUE;
+}
+
+static void WasabiApi_Uninitialize()
+{
+ ReleaseWasabiInterface(applicationApiServiceGuid, WASABI_API_APP);
+ ReleaseWasabiInterface(syscbApiServiceGuid, WASABI_API_SYSCB);
+ ReleaseWasabiInterface(languageApiGUID, WASABI_API_LNG);
+ ReleaseWasabiInterface(JSAPI2::api_securityGUID, AGAVE_API_JSAPI2_SECURITY);
+ ReleaseWasabiInterface(OBJ_OmBrowser, OMBROWSERMNGR);
+ ReleaseWasabiInterface(IFC_OmUtility, OMUTILITY);
+
+ WASABI_API_APP = NULL;
+ WASABI_API_LNG = NULL;
+ AGAVE_API_JSAPI2_SECURITY = NULL;
+}
+
+ULONG WasabiApi_AddRef()
+{
+ return InterlockedIncrement((LONG*)&wasabiRef);
+}
+
+ULONG WasabiApi_Release()
+{
+ if (0 == wasabiRef)
+ return wasabiRef;
+
+ LONG r = InterlockedDecrement((LONG*)&wasabiRef);
+ if (0 == r)
+ {
+ WasabiApi_Uninitialize();
+ }
+ return r;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/wasabi.h b/Src/Plugins/Library/ml_nowplaying/wasabi.h
new file mode 100644
index 00000000..72d17983
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/wasabi.h
@@ -0,0 +1,42 @@
+#ifndef NULLSOFT_NOWPLAYING_PLUGIN_WASABI_HEADER
+#define NULLSOFT_NOWPLAYING_PLUGIN_WASABI_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+#include <api/application/api_application.h>
+#define WASABI_API_APP applicationApi
+
+#include "../Agave/Language/api_language.h"
+
+#include "../Winamp/JSAPI2_api_security.h"
+extern JSAPI2::api_security *jsapi2_securityApi;
+#define AGAVE_API_JSAPI2_SECURITY jsapi2_securityApi
+
+#include <obj_ombrowser.h>
+extern obj_ombrowser *browserManager;
+#define OMBROWSERMNGR browserManager
+
+#include <api/syscb/api_syscb.h>
+#define WASABI_API_SYSCB sysCallbackApi
+
+#include <ifc_omutility.h>
+extern ifc_omutility *omUtility;
+#define OMUTILITY omUtility
+
+BOOL WasabiApi_Initialize(HINSTANCE hInstance);
+ULONG WasabiApi_AddRef(void);
+ULONG WasabiApi_Release(void);
+
+void *Wasabi_QueryInterface(REFGUID interfaceGuid);
+void Wasabi_ReleaseInterface(REFGUID interfaceGuid, void *pInstance);
+
+#define QueryWasabiInterface(__interfaceType, __interfaceGuid) ((##__interfaceType*)Wasabi_QueryInterface(__interfaceGuid))
+#define ReleaseWasabiInterface(__interfaceGuid, __interfaceInstance) (Wasabi_ReleaseInterface((__interfaceGuid), (__interfaceInstance)))
+
+void Wasabi_SafeRelease(Dispatchable *pDisp);
+
+#endif // NULLSOFT_NOWPLAYING_PLUGIN_WASABI_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/wasabiCallback.cpp b/Src/Plugins/Library/ml_nowplaying/wasabiCallback.cpp
new file mode 100644
index 00000000..54e9d3a0
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/wasabiCallback.cpp
@@ -0,0 +1,90 @@
+#include "main.h"
+#include "./wasabiCallback.h"
+#include "./navigation.h"
+#include "./service.h"
+#include "../replicant/nu/Autowide.h"
+
+WasabiCallback::WasabiCallback()
+ : ref(1)
+{
+}
+
+WasabiCallback::~WasabiCallback()
+{
+}
+
+HRESULT WasabiCallback::CreateInstance(WasabiCallback **instance)
+{
+ if (NULL == instance) return E_POINTER;
+
+ *instance = new WasabiCallback();
+ if (NULL == *instance) return E_OUTOFMEMORY;
+
+ return S_OK;
+}
+
+size_t WasabiCallback::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+size_t WasabiCallback::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+int WasabiCallback::QueryInterface(GUID interface_guid, void **object)
+{
+ return 0;
+}
+
+FOURCC WasabiCallback::GetEventType()
+{
+ return SysCallback::BROWSER;
+}
+
+int WasabiCallback::Notify(int msg, intptr_t param1, intptr_t param2)
+{
+ switch (msg)
+ {
+ case BrowserCallback::ONOPENURL:
+ return OpenURL(reinterpret_cast<const wchar_t*>(param1), reinterpret_cast<bool *>(param2));
+ }
+ return 0;
+}
+
+int WasabiCallback::OpenURL(const wchar_t *url, bool *override)
+{
+ WCHAR szTemplate[1024] = L"http://client.winamp.com/nowplaying";
+ INT cchTemplate = ARRAYSIZE(szTemplate) - 1;
+ lstrcpynW(szTemplate, AutoWide(g_config->ReadString("nowplayingurl", "http://client.winamp.com/nowplaying")), ARRAYSIZE(szTemplate));
+
+ if (NULL != url &&
+ CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, url, cchTemplate, szTemplate, cchTemplate))
+ {
+ if (SUCCEEDED(Navigation_ShowService(SERVICE_ID, url,
+ NAVFLAG_FORCEACTIVE | NAVFLAG_ENSUREMLVISIBLE | NAVFLAG_ENSUREITEMVISIBLE)))
+ {
+ *override = true;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#define CBCLASS WasabiCallback
+START_DISPATCH;
+ CB(ADDREF, AddRef);
+ CB(RELEASE, Release);
+ CB(QUERYINTERFACE, QueryInterface);
+ CB(SYSCALLBACK_GETEVENTTYPE, GetEventType);
+ CB(SYSCALLBACK_NOTIFY, Notify);
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_nowplaying/wasabiCallback.h b/Src/Plugins/Library/ml_nowplaying/wasabiCallback.h
new file mode 100644
index 00000000..3add0b9b
--- /dev/null
+++ b/Src/Plugins/Library/ml_nowplaying/wasabiCallback.h
@@ -0,0 +1,42 @@
+#ifndef NULLSOFT_NOWPLAYING_PLUGIN_WASABI_CALLBACK_HEADER
+#define NULLSOFT_NOWPLAYING_PLUGIN_WASABI_CALLBACK_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <api/syscb/callbacks/syscb.h>
+#include <api/syscb/callbacks/browsercb.h>
+
+class WasabiCallback : public SysCallback
+{
+protected:
+ WasabiCallback();
+ ~WasabiCallback();
+
+public:
+ static HRESULT CreateInstance(WasabiCallback **instance);
+
+public:
+ /*** Dispatchable ***/
+ size_t AddRef();
+ size_t Release();
+ int QueryInterface(GUID interface_guid, void **object);
+
+ /*** SysCallback ***/
+ FOURCC GetEventType();
+ int Notify(int msg, intptr_t param1 = 0, intptr_t param2 = 0);
+
+protected:
+ // set *override = true to prevent the URL from being opened
+ // leave it alone otherwise (in case someone else wanted to override it)
+ int OpenURL(const wchar_t *url, bool *override);
+
+protected:
+ RECVS_DISPATCH;
+
+protected:
+ ULONG ref;
+};
+
+#endif //NULLSOFT_NOWPLAYING_PLUGIN_WASABI_CALLBACK_HEADER \ No newline at end of file