aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Portable/pmp_wifi
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Portable/pmp_wifi')
-rw-r--r--Src/Plugins/Portable/pmp_wifi/ConnectActivity.cpp53
-rw-r--r--Src/Plugins/Portable/pmp_wifi/ConnectActivity.h16
-rw-r--r--Src/Plugins/Portable/pmp_wifi/InfoDownloader.cpp201
-rw-r--r--Src/Plugins/Portable/pmp_wifi/InfoDownloader.h51
-rw-r--r--Src/Plugins/Portable/pmp_wifi/ListenServer.cpp147
-rw-r--r--Src/Plugins/Portable/pmp_wifi/Pair.cpp87
-rw-r--r--Src/Plugins/Portable/pmp_wifi/Pair.h25
-rw-r--r--Src/Plugins/Portable/pmp_wifi/PlaylistSync.cpp150
-rw-r--r--Src/Plugins/Portable/pmp_wifi/PlaylistSync.h9
-rw-r--r--Src/Plugins/Portable/pmp_wifi/RenameDownloader.cpp39
-rw-r--r--Src/Plugins/Portable/pmp_wifi/RenameDownloader.h3
-rw-r--r--Src/Plugins/Portable/pmp_wifi/SongDownloader.cpp88
-rw-r--r--Src/Plugins/Portable/pmp_wifi/SongDownloader.h24
-rw-r--r--Src/Plugins/Portable/pmp_wifi/SongListDownloader.cpp178
-rw-r--r--Src/Plugins/Portable/pmp_wifi/SongListDownloader.h58
-rw-r--r--Src/Plugins/Portable/pmp_wifi/WifiDevice.cpp269
-rw-r--r--Src/Plugins/Portable/pmp_wifi/WifiDevice.h64
-rw-r--r--Src/Plugins/Portable/pmp_wifi/WifiPlaylist.cpp104
-rw-r--r--Src/Plugins/Portable/pmp_wifi/WifiPlaylist.h36
-rw-r--r--Src/Plugins/Portable/pmp_wifi/XMLString.cpp49
-rw-r--r--Src/Plugins/Portable/pmp_wifi/XMLString.h28
-rw-r--r--Src/Plugins/Portable/pmp_wifi/api.cpp72
-rw-r--r--Src/Plugins/Portable/pmp_wifi/api.h44
-rw-r--r--Src/Plugins/Portable/pmp_wifi/device.cpp797
-rw-r--r--Src/Plugins/Portable/pmp_wifi/device.h127
-rw-r--r--Src/Plugins/Portable/pmp_wifi/images.cpp72
-rw-r--r--Src/Plugins/Portable/pmp_wifi/images.h5
-rw-r--r--Src/Plugins/Portable/pmp_wifi/main.cpp263
-rw-r--r--Src/Plugins/Portable/pmp_wifi/main.h30
-rw-r--r--Src/Plugins/Portable/pmp_wifi/modelInfo.cpp168
-rw-r--r--Src/Plugins/Portable/pmp_wifi/modelInfo.h22
-rw-r--r--Src/Plugins/Portable/pmp_wifi/pmp_wifi.rc126
-rw-r--r--Src/Plugins/Portable/pmp_wifi/pmp_wifi.sln83
-rw-r--r--Src/Plugins/Portable/pmp_wifi/pmp_wifi.vcxproj341
-rw-r--r--Src/Plugins/Portable/pmp_wifi/pmp_wifi.vcxproj.filters214
-rw-r--r--Src/Plugins/Portable/pmp_wifi/post.cpp518
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resource.h65
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/attach.pngbin0 -> 788 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/attach16.pngbin0 -> 239 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/generic_android.pngbin0 -> 5721 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/generic_drive_wifi_16.pngbin0 -> 242 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo.pngbin0 -> 4177 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo_16.pngbin0 -> 162 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g.pngbin0 -> 2305 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g_16.pngbin0 -> 159 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/htc_hd2.pngbin0 -> 1861 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/htc_hd2_16.pngbin0 -> 162 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/htc_incredible.pngbin0 -> 2484 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/htc_incredible_16.pngbin0 -> 182 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/htc_nexus_one_16.pngbin0 -> 164 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/motorola_droid.pngbin0 -> 2662 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_16.pngbin0 -> 156 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x.pngbin0 -> 2319 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x_16.pngbin0 -> 169 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/nexus_one.pngbin0 -> 4053 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/resources/wifi.pngbin0 -> 599 bytes
-rw-r--r--Src/Plugins/Portable/pmp_wifi/version.rc239
57 files changed, 4665 insertions, 0 deletions
diff --git a/Src/Plugins/Portable/pmp_wifi/ConnectActivity.cpp b/Src/Plugins/Portable/pmp_wifi/ConnectActivity.cpp
new file mode 100644
index 00000000..37e94da4
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/ConnectActivity.cpp
@@ -0,0 +1,53 @@
+#include "ConnectActivity.h"
+#include "resource.h"
+#include "api.h"
+#include <strsafe.h>
+
+BOOL ConnectActivity::GetActive()
+{
+ return TRUE;
+}
+
+BOOL ConnectActivity::GetCancelable()
+{
+ return FALSE;
+}
+
+HRESULT ConnectActivity::GetProgress(unsigned int *percentCompleted)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT ConnectActivity::GetDisplayName(wchar_t *buffer, size_t bufferMax)
+{
+ if (NULL == buffer)
+ return E_POINTER;
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_ACTIVITY_CONNECT, buffer, bufferMax);
+ return S_OK;
+}
+
+HRESULT ConnectActivity::GetStatus(wchar_t *buffer, size_t bufferMax)
+{
+ if (NULL == buffer)
+ return E_POINTER;
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_ACTIVITY_CONNECT_DESC, buffer, bufferMax);
+ return S_OK;
+}
+
+HRESULT ConnectActivity::Cancel(HWND hostWindow)
+{
+ return E_NOTIMPL;
+}
+
+#define CBCLASS ConnectActivity
+START_DISPATCH;
+CB(API_GETACTIVE, GetActive);
+CB(API_GETCANCELABLE, GetCancelable);
+CB(API_GETPROGRESS, GetProgress);
+CB(API_GETDISPLAYNAME, GetDisplayName);
+CB(API_GETSTATUS, GetStatus);
+CB(API_CANCEL, Cancel);
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/ConnectActivity.h b/Src/Plugins/Portable/pmp_wifi/ConnectActivity.h
new file mode 100644
index 00000000..620dc6ab
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/ConnectActivity.h
@@ -0,0 +1,16 @@
+#pragma once
+#include "../devices/ifc_deviceactivity.h"
+
+class ConnectActivity : public ifc_deviceactivity
+{
+public:
+ BOOL GetActive();
+ BOOL GetCancelable();
+ HRESULT GetProgress(unsigned int *percentCompleted);
+ HRESULT GetDisplayName(wchar_t *buffer, size_t bufferMax);
+ HRESULT GetStatus(wchar_t *buffer, size_t bufferMax);
+ HRESULT Cancel(HWND hostWindow);
+
+protected:
+ RECVS_DISPATCH;
+}; \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/InfoDownloader.cpp b/Src/Plugins/Portable/pmp_wifi/InfoDownloader.cpp
new file mode 100644
index 00000000..86bd941e
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/InfoDownloader.cpp
@@ -0,0 +1,201 @@
+#include "InfoDownloader.h"
+
+#include "api.h"
+#include "main.h"
+#include "images.h"
+#include "InfoDownloader.h"
+#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
+#include <api/service/waServiceFactory.h>
+#include <strsafe.h>
+
+void OnInfoDownloadDone(InfoDownloader *_info);
+
+void DeviceInfo_Init(DeviceInfo *device_info)
+{
+ device_info->id=0;
+ device_info->modelInfo=NULL;
+ device_info->total_space=0;
+ device_info->used_space=0;
+ device_info->model[0]=0;
+ device_info->manufacturer[0]=0;
+ device_info->name[0]=0;
+ device_info->product[0]=0;
+}
+
+void DeviceInfo_Copy(DeviceInfo *dest, const DeviceInfo *source)
+{
+ dest->id=source->id;
+ dest->modelInfo=source->modelInfo;
+ dest->total_space=source->total_space;
+ dest->used_space=source->used_space;
+ StringCbCopy(dest->model, sizeof(dest->model), source->model);
+ StringCbCopy(dest->manufacturer, sizeof(dest->manufacturer), source->manufacturer);
+ StringCbCopy(dest->name, sizeof(dest->name), source->name);
+ StringCbCopy(dest->product, sizeof(dest->product), source->product);
+}
+
+
+DeviceXML::DeviceXML()
+{
+ DeviceInfo_Init(&device_info);
+}
+
+InfoXML::InfoXML(obj_xml *parser) : parser(parser)
+{
+ parser->xmlreader_registerCallback(L"info\fdevice", this);
+ parser->xmlreader_registerCallback(L"info\fspace", this);
+}
+
+InfoXML::~InfoXML()
+{
+ parser->xmlreader_unregisterCallback(this);
+}
+
+void DeviceXML::xmlReaderOnStartElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params)
+{
+ if (!wcscmp(xmltag, L"device"))
+ {
+ const wchar_t *value = params->getItemValue(L"id");
+ if (value)
+ device_info.id = _wcstoui64(value, 0, 16);
+
+ value = params->getItemValue(L"manufacturer");
+ if (value)
+ StringCbCopyW(device_info.manufacturer, sizeof(device_info.manufacturer), value);
+
+ value = params->getItemValue(L"model");
+ if (value)
+ StringCbCopyW(device_info.model, sizeof(device_info.model), value);
+
+ value = params->getItemValue(L"name");
+ if (value)
+ StringCbCopyW(device_info.name, sizeof(device_info.name), value);
+
+ value = params->getItemValue(L"product");
+ if (value)
+ StringCbCopyW(device_info.product, sizeof(device_info.product), value);
+
+ device_info.modelInfo = FindModelInfo(device_info.manufacturer, device_info.model, FALSE);
+ }
+ else if (!wcscmp(xmltag, L"space"))
+ {
+ const wchar_t *value = params->getItemValue(L"total");
+ if (value)
+ {
+ device_info.total_space = _wtoi64(value);
+ }
+
+ value = params->getItemValue(L"used");
+ if (value)
+ {
+ device_info.used_space = _wtoi64(value);
+ }
+ }
+}
+
+WifiDevice *InfoXML::CreateDevice(uint64_t device_id_check, const char *root_url)
+{
+ if (device_info.id == device_id_check)
+ {
+ return new WifiDevice(root_url, &device_info);
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------------------------------------------ */
+
+InfoDownloader::InfoDownloader(const char *_root_url, uint64_t id, Wasabi2::nx_string_t usn) : id(id)
+{
+ this->usn = NXStringRetain(usn);
+ root_url = strdup(_root_url);
+ done=0;
+ waServiceFactory *parserFactory = plugin.service->service_getServiceByGuid(obj_xmlGUID);
+ if (parserFactory)
+ parser = (obj_xml *)parserFactory->getInterface();
+
+ parser->xmlreader_setCaseSensitive();
+ info = new InfoXML(parser);
+ parser->xmlreader_open();
+}
+
+InfoDownloader::~InfoDownloader()
+{
+ NXStringRelease(usn);
+ waServiceFactory *parserFactory = plugin.service->service_getServiceByGuid(obj_xmlGUID);
+ if (parserFactory)
+ {
+ delete info;
+ parser->xmlreader_close();
+ parserFactory->releaseInterface(parser);
+ }
+ free(root_url);
+}
+
+void InfoDownloader::OnInit(DownloadToken token)
+{
+ if (done)
+ {
+ WAC_API_DOWNLOADMANAGER->CancelDownload(token);
+ return;
+ }
+
+ api_httpreceiver *jnet = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
+ if (jnet)
+ {
+ jnet->AddHeaderValue("X-Winamp-ID", winamp_id_str);
+ jnet->AddHeaderValue("X-Winamp-Name", winamp_name);
+ }
+}
+
+void InfoDownloader::OnData(DownloadToken token, void *data, size_t datalen)
+{
+ if (done)
+ {
+ WAC_API_DOWNLOADMANAGER->CancelDownload(token);
+ return;
+ }
+
+ if (parser->xmlreader_feed(data, datalen) != OBJ_XML_SUCCESS)
+ {
+ WAC_API_DOWNLOADMANAGER->CancelDownload(token);
+ }
+}
+
+void InfoDownloader::OnCancel(DownloadToken token)
+{
+ done=2;
+ OnInfoDownloadDone(this);
+}
+
+void InfoDownloader::OnError(DownloadToken token, int error)
+{
+ // TODO
+ done=2;
+ OnInfoDownloadDone(this);
+}
+
+void InfoDownloader::OnFinish(DownloadToken token)
+{
+ if (!done)
+ {
+ parser->xmlreader_feed(0, 0);
+ }
+
+ done=1;
+ OnInfoDownloadDone(this);
+ }
+
+bool InfoDownloader::Done(WifiDevice **out_device)
+{
+ if (done != 1)
+ return false;
+
+ *out_device = info->CreateDevice(id, root_url);
+
+ return true;
+}
+
+void InfoDownloader::Cancel()
+{
+ done=2;
+} \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/InfoDownloader.h b/Src/Plugins/Portable/pmp_wifi/InfoDownloader.h
new file mode 100644
index 00000000..648d1976
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/InfoDownloader.h
@@ -0,0 +1,51 @@
+#pragma once
+#include "device.h"
+#include "../xml/obj_xml.h"
+#include "../Components/wac_downloadManager/DownloadCallbackT.h"
+#include "WifiDevice.h"
+#include "../xml/ifc_xmlreadercallbackT.h"
+#include "main.h"
+namespace Wasabi2
+{
+#include "nx/nxstring.h"
+}
+
+class DeviceXML : public ifc_xmlreadercallbackT<DeviceXML>
+{
+public:
+ DeviceXML();
+ void xmlReaderOnStartElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params);
+ DeviceInfo device_info;
+};
+
+class InfoXML : public DeviceXML
+{
+public:
+ InfoXML(obj_xml *parser);
+ ~InfoXML();
+ WifiDevice *CreateDevice(uint64_t device_id_check, const char *root_url);
+private:
+ obj_xml *parser;
+};
+
+class InfoDownloader : public DownloadCallbackT<InfoDownloader>
+{
+public:
+ InfoDownloader(const char *root_url, uint64_t id, Wasabi2::nx_string_t usn);
+ ~InfoDownloader();
+ void OnInit(DownloadToken token);
+ void OnData(DownloadToken token, void *data, size_t datalen);
+ void OnCancel(DownloadToken token);
+ void OnError(DownloadToken token, int error);
+ void OnFinish(DownloadToken token);
+ bool Done(WifiDevice **device);
+
+ void Cancel();
+ uint64_t id;
+ Wasabi2::nx_string_t usn;
+private:
+ char *root_url;
+ obj_xml *parser;
+ InfoXML *info;
+ volatile int done; // 1 for successfully done, 2 for cancelled/error
+};
diff --git a/Src/Plugins/Portable/pmp_wifi/ListenServer.cpp b/Src/Plugins/Portable/pmp_wifi/ListenServer.cpp
new file mode 100644
index 00000000..297b0d9e
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/ListenServer.cpp
@@ -0,0 +1,147 @@
+#include "api.h"
+#include "..\..\..\replicant\jnetlib\multicastlisten.h"
+#include "jnetlib/util.h"
+#include "InfoDownloader.h"
+#include "nu/AutoChar.h"
+#include <vector>
+#include "nu/AutoLock.h"
+
+namespace Wasabi2
+{
+ #include "ssdp/cb_ssdp.h"
+}
+
+#include <windows.h>
+
+static nu::LockGuard connections_guard;
+
+struct Connection
+{
+ Connection()
+ {
+ usn = 0;
+ location = 0;
+ device = 0;
+ downloader = 0;
+ }
+
+ Wasabi2::nx_string_t usn;
+ Wasabi2::nx_uri_t location;
+ WifiDevice *device;
+ InfoDownloader *downloader;
+};
+
+typedef std::vector<Connection> Connections;
+static Connections connections;
+static volatile int killswitch;
+
+static Connection &FindUSN(Wasabi2::nx_string_t usn)
+{
+ for (size_t i=0;i<connections.size();i++)
+ {
+ if (!Wasabi2::NXStringKeywordCompare(connections[i].usn, usn))
+ {
+ return connections[i];
+ }
+ }
+
+ Connection dummy;
+ dummy.usn = NXStringRetain(usn);
+ connections.push_back(dummy);
+
+ return connections[connections.size()-1];
+}
+
+class WAFAListener : public Wasabi2::cb_ssdp
+{
+ void WASABICALL SSDPCallback_OnServiceConnected(Wasabi2::nx_uri_t location, Wasabi2::nx_string_t type, Wasabi2::nx_string_t usn);
+ void WASABICALL SSDPCallback_OnServiceDisconnected(Wasabi2::nx_string_t usn);
+};
+
+void WAFAListener::SSDPCallback_OnServiceConnected(Wasabi2::nx_uri_t location, Wasabi2::nx_string_t type, Wasabi2::nx_string_t usn)
+{
+ if (!Wasabi2::NXStringKeywordCompareWithCString(type, "urn:nullsoft.com:device:Android:1"))
+ {
+ nu::AutoLock auto_lock(connections_guard);
+ Connection &connection = FindUSN(usn);
+ connection.location = Wasabi2::NXURIRetain(location);
+
+ uint64_t id = _wcstoui64(usn->string, 0, 16);
+ AutoChar cached_location(location->string);
+ InfoDownloader *downloader = new InfoDownloader(cached_location, id, usn);
+ connection.downloader = downloader;
+ WAC_API_DOWNLOADMANAGER->DownloadEx(cached_location, downloader, api_downloadManager::DOWNLOADEX_CALLBACK);
+ }
+}
+
+void WAFAListener::SSDPCallback_OnServiceDisconnected(Wasabi2::nx_string_t usn)
+{
+ nu::AutoLock auto_lock(connections_guard);
+ Connection &connection = FindUSN(usn);
+ if (connection.device)
+ {
+ connection.device->OnDisconnect();
+ connection.device->Release();
+ connection.device = 0;
+ }
+
+ if (connection.downloader)
+ {
+ connection.downloader->Cancel();
+ connection.downloader->Release();
+ connection.downloader = 0;
+ }
+}
+
+void OnInfoDownloadDone(InfoDownloader *_info)
+{
+ nu::AutoLock auto_lock(connections_guard);
+ Connection &connection = FindUSN(_info->usn);
+ WifiDevice *device = 0;
+ if (connection.downloader && connection.downloader->Done(&device))
+ {
+ connection.device = device;
+ connection.downloader->Release();
+ connection.downloader = 0;
+ }
+ else
+ {
+ if (connection.location)
+ {
+ // requeue download request, TODO: but might want to wait a bit
+ AutoChar cached_location(connection.location->string);
+ WAC_API_DOWNLOADMANAGER->DownloadEx(cached_location, connection.downloader, api_downloadManager::DOWNLOADEX_CALLBACK);
+ }
+ }
+}
+
+static WAFAListener wafa_ssdp_callback;
+
+void StartListenServer()
+{
+ if (REPLICANT_API_SSDP)
+ {
+ REPLICANT_API_SSDP->RegisterCallback(&wafa_ssdp_callback);
+ }
+}
+
+void StopListenServer()
+{
+ nu::AutoLock auto_lock(connections_guard);
+ for (size_t i=0;i<connections.size();i++)
+ {
+ NXStringRelease(connections[i].usn);
+ NXURIRelease(connections[i].location);
+
+ if (connections[i].downloader)
+ {
+ connections[i].downloader->Cancel();
+ connections[i].downloader->Release();
+ }
+
+ if (connections[i].device)
+ connections[i].device->Release();
+ }
+
+ connections.clear();
+} \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/Pair.cpp b/Src/Plugins/Portable/pmp_wifi/Pair.cpp
new file mode 100644
index 00000000..6eac0e0d
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/Pair.cpp
@@ -0,0 +1,87 @@
+#include "Pair.h"
+#include "api.h"
+#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
+#include "main.h"
+#include "WifiDevice.h"
+#include <strsafe.h>
+
+PairDownloader::PairDownloader(WifiDevice *device) : device(device)
+{
+ device->AddRef();
+}
+
+PairDownloader::~PairDownloader()
+{
+}
+
+void PairDownloader::OnInit(DownloadToken token)
+{
+ api_httpreceiver *jnet = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
+ if (jnet)
+ {
+ jnet->AddHeaderValue("X-Winamp-ID", winamp_id_str);
+ jnet->AddHeaderValue("X-Winamp-Name", winamp_name);
+ }
+}
+
+void PairDownloader::OnData(DownloadToken token, void *data, size_t datalen)
+{
+}
+
+void PairDownloader::OnCancel(DownloadToken token)
+{
+ device->OnConnectionFailed();
+ device->Release();
+ Release();
+}
+
+void PairDownloader::OnError(DownloadToken token, int error)
+{
+ api_httpreceiver *jnet = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
+ if (jnet)
+ {
+ jnet->getreplycode();
+ }
+ device->OnConnectionFailed();
+ device->Release();
+ Release();
+}
+
+void PairDownloader::OnFinish(DownloadToken token)
+{
+ api_httpreceiver *jnet = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
+ if (jnet)
+ {
+ if (jnet->getreplycode() == 202)
+ {
+ device->OnPaired();
+ device->Release();
+ Release();
+ return;
+ }
+ }
+ device->OnConnectionFailed();
+ device->Release();
+ Release();
+}
+
+bool IsPaired(uint64_t id)
+{
+ wchar_t pair_name[64] = {0};
+ StringCbPrintfW(pair_name, sizeof(pair_name), L"%016I64x", id);
+ if (GetPrivateProfileInt(L"pairs", pair_name, 0, inifile) == 0)
+ return false;
+
+ return true;
+}
+
+void SetPaired(uint64_t id, bool status)
+{
+ wchar_t pair_name[64] = {0};
+ StringCbPrintfW(pair_name, sizeof(pair_name), L"%016I64x", id);
+ if (status)
+ WritePrivateProfileString(L"pairs", pair_name, L"1", inifile);
+ else
+ WritePrivateProfileString(L"pairs", pair_name, L"0", inifile);
+
+} \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/Pair.h b/Src/Plugins/Portable/pmp_wifi/Pair.h
new file mode 100644
index 00000000..ac2e5de8
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/Pair.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "../nu/refcount.h"
+#include "../Components/wac_downloadManager/DownloadCallbackT.h"
+
+class WifiDevice;
+
+class PairDownloader : public DownloadCallbackT<PairDownloader>
+{
+public:
+ PairDownloader(WifiDevice *device);
+ ~PairDownloader();
+
+ void OnInit(DownloadToken token);
+ void OnData(DownloadToken token, void *data, size_t datalen);
+ void OnCancel(DownloadToken token);
+ void OnError(DownloadToken token, int error);
+ void OnFinish(DownloadToken token);
+
+private:
+ WifiDevice *device;
+};
+
+bool IsPaired(uint64_t id);
+void SetPaired(uint64_t id, bool status); \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/PlaylistSync.cpp b/Src/Plugins/Portable/pmp_wifi/PlaylistSync.cpp
new file mode 100644
index 00000000..ae0ccffa
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/PlaylistSync.cpp
@@ -0,0 +1,150 @@
+#include "PlaylistSync.h"
+#include "api.h"
+#include "../nu/AutoUrl.h"
+#include "main.h"
+#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
+#include <strsafe.h>
+
+/* classes and utility functions to notifying the device of playlist modifications */
+class SimpleCallback : public ifc_downloadManagerCallback
+{
+public:
+ void OnInit(DownloadToken token)
+ {
+ api_httpreceiver *jnet = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
+ if (jnet)
+ {
+ jnet->AddHeaderValue("X-Winamp-ID", winamp_id_str);
+ jnet->AddHeaderValue("X-Winamp-Name", winamp_name);
+ }
+ }
+
+ RECVS_DISPATCH;
+};
+
+#define CBCLASS SimpleCallback
+START_DISPATCH;
+VCB(IFC_DOWNLOADMANAGERCALLBACK_ONINIT, OnInit)
+END_DISPATCH;
+#undef CBCLASS
+
+class NewPlaylistCallback : public ifc_downloadManagerCallback
+{
+public:
+ NewPlaylistCallback()
+ {
+ event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ location=0;
+ }
+
+ ~NewPlaylistCallback()
+ {
+ CloseHandle(event);
+ free(location);
+ }
+
+ void OnInit(DownloadToken token)
+ {
+ api_httpreceiver *jnet = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
+ if (jnet)
+ {
+ jnet->AddHeaderValue("X-Winamp-ID", winamp_id_str);
+ jnet->AddHeaderValue("X-Winamp-Name", winamp_name);
+ }
+ }
+ void OnCancel(DownloadToken token)
+ {
+ SetEvent(event);
+ }
+ void OnError(DownloadToken token, int error)
+ {
+ SetEvent(event);
+ }
+ void OnFinish(DownloadToken token)
+ {
+ api_httpreceiver *jnet = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
+ if (jnet)
+ {
+ const char *jnet_location = jnet->getheader("Location");
+ if (jnet_location)
+ location = strdup(jnet_location);
+ }
+ SetEvent(event);
+ }
+ const char *Wait()
+ {
+ WaitForSingleObject(event, INFINITE);
+ return location;
+ }
+ HANDLE event;
+ char *location;
+ RECVS_DISPATCH;
+};
+
+#define CBCLASS NewPlaylistCallback
+START_DISPATCH;
+VCB(IFC_DOWNLOADMANAGERCALLBACK_ONINIT, OnInit)
+VCB(IFC_DOWNLOADMANAGERCALLBACK_ONCANCEL, OnCancel)
+VCB(IFC_DOWNLOADMANAGERCALLBACK_ONERROR, OnInit)
+VCB(IFC_DOWNLOADMANAGERCALLBACK_ONFINISH, OnFinish)
+END_DISPATCH;
+#undef CBCLASS
+
+static SimpleCallback simple_callback;
+void Sync_AddToPlaylist(const char *root_url, const wchar_t *playlist_id, const wchar_t *song_id)
+{
+ if ( WAC_API_DOWNLOADMANAGER )
+ {
+ char url[1024] = {0};
+ StringCbPrintfA(url, sizeof(url), "%s/playlist?action=add&id=%s&songid=%s", root_url, AutoUrl(playlist_id), AutoUrl(song_id));
+ WAC_API_DOWNLOADMANAGER->DownloadEx(url, &simple_callback, api_downloadManager::DOWNLOADEX_BUFFER);
+ }
+}
+
+void Sync_RemoveFromPlaylist(const char *root_url, const wchar_t *playlist_id, const wchar_t *song_id)
+{
+ if ( WAC_API_DOWNLOADMANAGER )
+ {
+ char url[1024] = {0};
+ StringCbPrintfA(url, sizeof(url), "%s/playlist?action=remove&id=%s&songid=%s", root_url, AutoUrl(playlist_id), AutoUrl(song_id));
+ WAC_API_DOWNLOADMANAGER->DownloadEx(url, &simple_callback, api_downloadManager::DOWNLOADEX_BUFFER);
+ }
+}
+
+void Sync_DeletePlaylist(const char *root_url, const wchar_t *playlist_id)
+{
+ if ( WAC_API_DOWNLOADMANAGER )
+ {
+ char url[1024] = {0};
+ StringCbPrintfA(url, sizeof(url), "%s/playlist?action=delete&id=%s", root_url, AutoUrl(playlist_id));
+ WAC_API_DOWNLOADMANAGER->DownloadEx(url, &simple_callback, api_downloadManager::DOWNLOADEX_BUFFER);
+ }
+}
+
+WifiPlaylist *Sync_NewPlaylist(const char *root_url, const wchar_t *playlist_name)
+{
+ if ( WAC_API_DOWNLOADMANAGER )
+ {
+ NewPlaylistCallback new_playlist_callback;
+ char url[1024] = {0};
+ StringCbPrintfA(url, sizeof(url), "%s/playlist?action=new&name=%s", root_url, AutoUrl(playlist_name));
+ WAC_API_DOWNLOADMANAGER->DownloadEx(url, &new_playlist_callback, api_downloadManager::DOWNLOADEX_BUFFER);
+ const char *playlist_id = new_playlist_callback.Wait();
+ if (playlist_id)
+ {
+ return new WifiPlaylist(playlist_id, playlist_name);
+ }
+ }
+
+ return 0;
+}
+
+void Sync_RenamePlaylist(const char *root_url, const wchar_t *playlist_id, const wchar_t *playlist_name)
+{
+ if ( WAC_API_DOWNLOADMANAGER )
+ {
+ char url[ 1024 ] = { 0 };
+ StringCbPrintfA( url, sizeof( url ), "%s/playlist?action=rename&id=%s&name=%s", root_url, AutoUrl( playlist_id ), AutoUrl( playlist_name ) );
+ WAC_API_DOWNLOADMANAGER->DownloadEx( url, &simple_callback, api_downloadManager::DOWNLOADEX_BUFFER );
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/PlaylistSync.h b/Src/Plugins/Portable/pmp_wifi/PlaylistSync.h
new file mode 100644
index 00000000..5e716faa
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/PlaylistSync.h
@@ -0,0 +1,9 @@
+#pragma once
+#include "WifiPlaylist.h"
+/* classes and utility functions to notifying the device of playlist modifications */
+
+void Sync_AddToPlaylist(const char *root_url, const wchar_t *playlist_id, const wchar_t *song_id);
+void Sync_RemoveFromPlaylist(const char *root_url, const wchar_t *playlist_id, const wchar_t *song_id);
+void Sync_DeletePlaylist(const char *root_url, const wchar_t *playlist_id);
+WifiPlaylist *Sync_NewPlaylist(const char *root_url, const wchar_t *playlist_name);
+void Sync_RenamePlaylist(const char *root_url, const wchar_t *playlist_id, const wchar_t *playlist_name); \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/RenameDownloader.cpp b/Src/Plugins/Portable/pmp_wifi/RenameDownloader.cpp
new file mode 100644
index 00000000..4ace323f
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/RenameDownloader.cpp
@@ -0,0 +1,39 @@
+#include "RenameDownloader.h"
+#include "api.h"
+#include "../nu/AutoUrl.h"
+#include "main.h"
+#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
+#include <strsafe.h>
+
+class PingCallback : public ifc_downloadManagerCallback
+{
+public:
+ void OnInit(DownloadToken token)
+ {
+ api_httpreceiver *jnet = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
+ if (jnet)
+ {
+ jnet->AddHeaderValue("X-Winamp-ID", winamp_id_str);
+ jnet->AddHeaderValue("X-Winamp-Name", winamp_name);
+ }
+ }
+
+ RECVS_DISPATCH;
+};
+
+#define CBCLASS PingCallback
+START_DISPATCH;
+VCB(IFC_DOWNLOADMANAGERCALLBACK_ONINIT, OnInit)
+END_DISPATCH;
+#undef CBCLASS
+
+static PingCallback ping_callback;
+void RenameDevice(const char *root_url, const wchar_t *new_name)
+{
+ if ( WAC_API_DOWNLOADMANAGER )
+ {
+ char url[1024] = {0};
+ StringCbPrintfA(url, sizeof(url), "%s/set?nick=%s", root_url, AutoUrl(new_name));
+ WAC_API_DOWNLOADMANAGER->DownloadEx(url, &ping_callback, api_downloadManager::DOWNLOADEX_BUFFER);
+ }
+}
diff --git a/Src/Plugins/Portable/pmp_wifi/RenameDownloader.h b/Src/Plugins/Portable/pmp_wifi/RenameDownloader.h
new file mode 100644
index 00000000..fe5b78cd
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/RenameDownloader.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void RenameDevice(const char *root_url, const wchar_t *new_name);
diff --git a/Src/Plugins/Portable/pmp_wifi/SongDownloader.cpp b/Src/Plugins/Portable/pmp_wifi/SongDownloader.cpp
new file mode 100644
index 00000000..f3003715
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/SongDownloader.cpp
@@ -0,0 +1,88 @@
+#include "api.h"
+#include "SongDownloader.h"
+#include "main.h"
+#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
+#include <strsafe.h>
+
+SongDownloader::SongDownloader(const wchar_t *filename, HANDLE done_event, void (*callback)(void *callbackContext, wchar_t *status), void *context)
+: done_event(done_event), callback(callback), context(context)
+{
+ hFile = CreateFile(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
+ content_length=0;
+ bytes_downloaded=0;
+}
+
+SongDownloader::~SongDownloader()
+{
+ if (hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+}
+
+void SongDownloader::OnInit(DownloadToken token)
+{
+ api_httpreceiver *jnet = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
+ if (jnet)
+ {
+ jnet->AddHeaderValue("X-Winamp-ID", winamp_id_str);
+ jnet->AddHeaderValue("X-Winamp-Name", winamp_name);
+ }
+}
+
+void SongDownloader::OnData(DownloadToken token, void *data, size_t datalen)
+{
+ if (!content_length)
+ {
+ api_httpreceiver *jnet = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
+ if (jnet)
+ {
+ const char *header = jnet->getheader("content-length");
+ if (header)
+ content_length = _strtoui64(header, 0, 10);
+ }
+ }
+ DWORD written = 0;
+ WriteFile(hFile, data, (DWORD)datalen, &written, 0);
+ bytes_downloaded+=written;
+ if (content_length && callback)
+ {
+ wchar_t status[128] = {0};
+ StringCbPrintf(status, sizeof(status), L"Transferring (%d%%)", (int)(100ULL * bytes_downloaded / content_length));
+ callback(context,status);
+ }
+}
+
+void SongDownloader::OnCancel(DownloadToken token)
+{
+ wchar_t status[128] = {0};
+
+ // TODO: lang pack
+ StringCbCopy(status, sizeof(status), L"Cancelled");
+ callback(context,status);
+
+ SetEvent(done_event);
+ this->Release();
+}
+
+void SongDownloader::OnError(DownloadToken token, int error)
+{
+ wchar_t status[128] = {0};
+
+ // TODO: lang pack
+ StringCbCopy(status, sizeof(status), L"Failed");
+ callback(context,status);
+
+ SetEvent(done_event);
+ this->Release();
+}
+
+void SongDownloader::OnFinish(DownloadToken token)
+{
+ wchar_t status[128] = {0};
+
+ // TODO: lang pack
+ StringCbCopy(status, sizeof(status), L"Done");
+ callback(context,status);
+
+ SetEvent(done_event);
+ this->Release();
+}
diff --git a/Src/Plugins/Portable/pmp_wifi/SongDownloader.h b/Src/Plugins/Portable/pmp_wifi/SongDownloader.h
new file mode 100644
index 00000000..947415f6
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/SongDownloader.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "../Components/wac_downloadManager/DownloadCallbackT.h"
+
+class SongDownloader : public DownloadCallbackT<SongDownloader>
+{
+public:
+ SongDownloader(const wchar_t *filename, HANDLE done_event, void (*callback)(void *callbackContext, wchar_t *status), void *context);
+ ~SongDownloader();
+
+ void OnInit(DownloadToken token);
+ void OnData(DownloadToken token, void *data, size_t datalen);
+ void OnCancel(DownloadToken token);
+ void OnError(DownloadToken token, int error);
+ void OnFinish(DownloadToken token);
+
+private:
+ void (*callback)(void *callbackContext, wchar_t *status);
+ void *context;
+ HANDLE hFile, done_event;
+ uint64_t content_length;
+ uint64_t bytes_downloaded;
+
+};
diff --git a/Src/Plugins/Portable/pmp_wifi/SongListDownloader.cpp b/Src/Plugins/Portable/pmp_wifi/SongListDownloader.cpp
new file mode 100644
index 00000000..7dbcc5e9
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/SongListDownloader.cpp
@@ -0,0 +1,178 @@
+#include "api.h"
+#include "main.h"
+#include "images.h"
+#include "SongListDownloader.h"
+#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
+#include <api/service/waServiceFactory.h>
+#include <strsafe.h>
+
+WifiXML::WifiXML(obj_xml *parser) : parser(parser)
+{
+ wifi_track = 0;
+ wifi_playlist = 0;
+
+ parser->xmlreader_registerCallback(L"items", this);
+ parser->xmlreader_registerCallback(L"items\fdevice", &info_xml);
+ parser->xmlreader_registerCallback(L"items\fspace", &info_xml);
+ parser->xmlreader_registerCallback(L"items\fitem", this);
+ parser->xmlreader_registerCallback(L"items\fplaylist", this);
+ parser->xmlreader_registerCallback(L"items\fplaylist\fitem", this);
+
+ parser->xmlreader_registerCallback(L"items\fitem\fartist", &artist);
+ parser->xmlreader_registerCallback(L"items\fitem\falbum", &album);
+ parser->xmlreader_registerCallback(L"items\fitem\fcomposer", &composer);
+ parser->xmlreader_registerCallback(L"items\fitem\fduration", &duration);
+ parser->xmlreader_registerCallback(L"items\fitem\ftrack", &track);
+ parser->xmlreader_registerCallback(L"items\fitem\fyear", &year);
+ parser->xmlreader_registerCallback(L"items\fitem\fsize", &size);
+ parser->xmlreader_registerCallback(L"items\fitem\ftitle", &title);
+ parser->xmlreader_registerCallback(L"items\fitem\fmime", &mime_type);
+
+ parser->xmlreader_registerCallback(L"items\fplaylist\fitem\fartist", &artist);
+ parser->xmlreader_registerCallback(L"items\fplaylist\fitem\falbum", &album);
+ parser->xmlreader_registerCallback(L"items\fplaylist\fitem\fcomposer", &composer);
+ parser->xmlreader_registerCallback(L"items\fplaylist\fitem\fduration", &duration);
+ parser->xmlreader_registerCallback(L"items\fplaylist\fitem\ftrack", &track);
+ parser->xmlreader_registerCallback(L"items\fplaylist\fitem\fyear", &year);
+ parser->xmlreader_registerCallback(L"items\fplaylist\fitem\fsize", &size);
+ parser->xmlreader_registerCallback(L"items\fplaylist\fitem\ftitle", &title);
+ parser->xmlreader_registerCallback(L"items\fplaylist\fitem\fmime", &mime_type);
+ parser->xmlreader_registerCallback(L"items\fplaylist\fitem\fmodified", &modified);
+}
+
+WifiXML::~WifiXML()
+{
+ parser->xmlreader_unregisterCallback(this);
+ parser->xmlreader_unregisterCallback(&info_xml);
+
+ parser->xmlreader_unregisterCallback(&artist);
+ parser->xmlreader_unregisterCallback(&album);
+ parser->xmlreader_unregisterCallback(&composer);
+ parser->xmlreader_unregisterCallback(&duration);
+ parser->xmlreader_unregisterCallback(&track);
+ parser->xmlreader_unregisterCallback(&year);
+ parser->xmlreader_unregisterCallback(&size);
+ parser->xmlreader_unregisterCallback(&title);
+ parser->xmlreader_unregisterCallback(&mime_type);
+ parser->xmlreader_unregisterCallback(&modified);
+}
+
+void WifiXML::xmlReaderOnStartElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params)
+{
+ if (!wcscmp(xmltag, L"item"))
+ {
+ const wchar_t *value = params->getItemValue(L"id");
+ if (value)
+ {
+ wifi_track = new WifiTrack;
+ wifi_track->id = _wcsdup(value);
+ }
+ }
+ else if (!wcscmp(xmltag, L"playlist"))
+ {
+ const wchar_t *value = params->getItemValue(L"id");
+ const wchar_t *name = params->getItemValue(L"name");
+ if (value && name)
+ {
+ wifi_playlist = new WifiPlaylist;
+ wifi_playlist->id = _wcsdup(value);
+ wifi_playlist->name = _wcsdup(name);
+ }
+ }
+}
+
+void WifiXML::xmlReaderOnEndElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag)
+{
+ if (!wcscmp(xmltag, L"item") && wifi_track)
+ {
+ wifi_track->artist = _wcsdup(artist.GetString()); artist.Reset();
+ wifi_track->album = _wcsdup(album.GetString()); album.Reset();
+ wifi_track->composer = _wcsdup(composer.GetString()); composer.Reset();
+ wifi_track->duration = _wtoi(duration.GetString()); duration.Reset();
+ wifi_track->track = _wtoi(track.GetString()); track.Reset();
+ wifi_track->year = _wtoi(year.GetString()); year.Reset();
+ wifi_track->size = _wtoi(size.GetString()); size.Reset();
+ wifi_track->title = _wcsdup(title.GetString()); title.Reset();
+ wifi_track->mime_type = _wcsdup(mime_type.GetString()); mime_type.Reset();
+ wifi_track->last_updated = _wtoi64(modified.GetString()); modified.Reset();
+ if (wifi_playlist)
+ wifi_playlist->tracks.push_back(wifi_track);
+ else
+ tracks.push_back(wifi_track);
+ wifi_track=0;
+ }
+ else if (!wcscmp(xmltag, L"playlist") && wifi_playlist)
+ {
+ playlists.push_back(wifi_playlist);
+ wifi_playlist = 0;
+ }
+}
+
+/* ------------------------------------------------------------------------------------------------------------ */
+
+SongListDownloader::SongListDownloader(const char *root_url, WifiDevice *wifi_device) : root_url(root_url), wifi_device(wifi_device)
+{
+ device=0;
+ wifi_device->AddRef();
+ waServiceFactory *parserFactory = plugin.service->service_getServiceByGuid(obj_xmlGUID);
+ if (parserFactory)
+ parser = (obj_xml *)parserFactory->getInterface();
+
+ parser->xmlreader_setCaseSensitive();
+ wifi = new WifiXML(parser);
+ parser->xmlreader_open();
+}
+
+SongListDownloader::~SongListDownloader()
+{
+ waServiceFactory *parserFactory = plugin.service->service_getServiceByGuid(obj_xmlGUID);
+ if (parserFactory)
+ {
+ delete wifi;
+ parser->xmlreader_close();
+ parserFactory->releaseInterface(parser);
+ }
+}
+
+void SongListDownloader::OnInit(DownloadToken token)
+{
+ api_httpreceiver *jnet = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
+ if (jnet)
+ {
+ jnet->AddHeaderValue("X-Winamp-ID", winamp_id_str);
+ jnet->AddHeaderValue("X-Winamp-Name", winamp_name);
+ }
+}
+
+void SongListDownloader::OnData(DownloadToken token, void *data, size_t datalen)
+{
+ if (parser->xmlreader_feed(data, datalen) != OBJ_XML_SUCCESS)
+ {
+ WAC_API_DOWNLOADMANAGER->CancelDownload(token);
+ }
+}
+
+void SongListDownloader::OnCancel(DownloadToken token)
+{
+ wifi_device->OnConnectionFailed();
+ wifi_device->Release();
+ this->Release();
+}
+
+void SongListDownloader::OnError(DownloadToken token, int error)
+{
+ wifi_device->OnConnectionFailed();
+ wifi_device->Release();
+ this->Release();
+}
+
+void SongListDownloader::OnFinish(DownloadToken token)
+{
+ parser->xmlreader_feed(0, 0);
+ device = new TemplateDevice(wifi_device, root_url, &wifi->info_xml.device_info, &wifi->tracks, &wifi->playlists);
+ wifi_device->OnConnected(device);
+ wifi_device->Release();
+ PostMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)device,PMP_IPC_DEVICECONNECTED);
+
+ this->Release();
+}
diff --git a/Src/Plugins/Portable/pmp_wifi/SongListDownloader.h b/Src/Plugins/Portable/pmp_wifi/SongListDownloader.h
new file mode 100644
index 00000000..e5e9b350
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/SongListDownloader.h
@@ -0,0 +1,58 @@
+#pragma once
+#include "device.h"
+#include "../xml/obj_xml.h"
+#include "XMLString.h"
+#include "../Components/wac_downloadManager/DownloadCallbackT.h"
+#include "WifiDevice.h"
+#include "../xml/ifc_xmlreadercallbackT.h"
+#include "main.h"
+#include "InfoDownloader.h" // for InfoXML
+
+class WifiXML : public ifc_xmlreadercallbackT<WifiXML>
+{
+public:
+ WifiXML(obj_xml *parser);
+ ~WifiXML();
+
+public:
+ void xmlReaderOnStartElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params);
+ void xmlReaderOnEndElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag);
+
+ DeviceXML info_xml;
+ TemplateDevice::PlaylistsList playlists;
+ TemplateDevice::TrackList tracks;
+private:
+ obj_xml *parser;
+ TemplateDevice *device;
+ XMLString artist;
+ XMLString album;
+ XMLString composer;
+ XMLString duration;
+ XMLString track;
+ XMLString year;
+ XMLString size;
+ XMLString title;
+ XMLString mime_type;
+ XMLString modified;
+ WifiTrack *wifi_track;
+ WifiPlaylist *wifi_playlist;
+};
+
+class SongListDownloader : public DownloadCallbackT<SongListDownloader>
+{
+public:
+ SongListDownloader(const char *root_url, WifiDevice *wifi_device);
+ ~SongListDownloader();
+ void OnInit(DownloadToken token);
+ void OnData(DownloadToken token, void *data, size_t datalen);
+ void OnCancel(DownloadToken token);
+ void OnError(DownloadToken token, int error);
+ void OnFinish(DownloadToken token);
+
+private:
+ obj_xml *parser;
+ WifiXML *wifi;
+ TemplateDevice *device;
+ WifiDevice *wifi_device;
+ const char *root_url;
+};
diff --git a/Src/Plugins/Portable/pmp_wifi/WifiDevice.cpp b/Src/Plugins/Portable/pmp_wifi/WifiDevice.cpp
new file mode 100644
index 00000000..0b97b96e
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/WifiDevice.cpp
@@ -0,0 +1,269 @@
+#include "WifiDevice.h"
+#include "api.h"
+#include "device.h"
+#include "nu/ns_wc.h"
+#include "resource.h"
+#include "Pair.h"
+#include "images.h"
+#include "modelInfo.h"
+#include "SongListDownloader.h"
+#include <strsafe.h>
+
+WifiDevice::WifiDevice(const char *root_url, const DeviceInfo *in_device_info)
+: url(strdup(root_url))
+{
+ DeviceInfo_Copy(&device_info, in_device_info);
+ InitializeCriticalSection(&register_lock);
+ dead=0;
+ connect_active=false;
+ pmp_device=0;
+ StringCbPrintfA(id_string, sizeof(id_string), "%016I64x", device_info.id);
+ if (IsPaired(device_info.id))
+ {
+ char full_url[256] = {0};
+ StringCbPrintfA(full_url, sizeof(full_url), "%s/library", url);
+ WAC_API_DOWNLOADMANAGER->DownloadEx(full_url, new SongListDownloader(url, this), api_downloadManager::DOWNLOADEX_CALLBACK);
+ }
+ else
+ {
+ ifc_device *device = this;
+ AGAVE_API_DEVICEMANAGER->DeviceRegister(&device, 1);
+ }
+}
+
+WifiDevice::~WifiDevice()
+{
+ DeleteCriticalSection(&register_lock);
+}
+
+/* ifc_device stuff */
+int WifiDevice::QueryInterface(GUID interface_guid, void **object)
+{
+ if (interface_guid == IFC_Device)
+ {
+ AddRef();
+ *object = (ifc_device *)this;
+ return 0;
+ }
+ return 1;
+}
+
+const char *WifiDevice::GetName()
+{
+ return id_string;
+}
+
+HRESULT WifiDevice::GetDisplayName(wchar_t *buffer, size_t bufferSize)
+{
+ StringCchCopyW(buffer, bufferSize, device_info.name);
+ return 0;
+}
+
+const char *WifiDevice::GetType()
+{
+ return "portable";
+}
+
+const char *WifiDevice::GetConnection()
+{
+ return "wifi";
+}
+
+extern ifc_devicesupportedcommandenum *command_enum;
+extern ifc_devicesupportedcommandstore *command_store;
+extern ifc_deviceeventmanager *device_event_manager;
+
+HRESULT WifiDevice::EnumerateCommands(ifc_devicesupportedcommandenum **enumerator, DeviceCommandContext context)
+{
+ if (connect_active)
+ return E_NOTIMPL;
+
+ return command_store->Enumerate(enumerator);
+}
+
+HRESULT WifiDevice::SendCommand(const char *command, HWND hostWindow, ULONG_PTR param)
+{
+ if (!strcmp(command, "attach"))
+ {
+ return Attach(hostWindow);
+ }
+
+ return 0;
+}
+
+BOOL WifiDevice::GetAttached()
+{
+ return FALSE;
+}
+
+HRESULT WifiDevice::Attach(HWND hostWindow)
+{
+ if (!connect_active)
+ {
+ connect_active = true;
+ device_event_manager->Notify_ActivityStarted(this, &connect_activity);
+
+ char full_url[256] = {0};
+ StringCbPrintfA(full_url, sizeof(full_url), "%s/pair", url);
+ WAC_API_DOWNLOADMANAGER->DownloadEx(full_url, new PairDownloader(this), api_downloadManager::DOWNLOADEX_CALLBACK);
+ }
+
+ return S_OK;
+}
+
+HRESULT WifiDevice::Detach(HWND hostWindow)
+{
+ return S_OK;
+}
+
+HRESULT WifiDevice::Advise(ifc_deviceevent *handler)
+{
+ return device_event_manager->Advise(handler);
+}
+
+HRESULT WifiDevice::Unadvise(ifc_deviceevent *handler)
+{
+ return device_event_manager->Unadvise(handler);
+}
+
+HRESULT WifiDevice::GetIcon(wchar_t *buffer, size_t bufferSize, int width, int height)
+{
+ return ModelInfo_GetIconPath(device_info.modelInfo, width, height, buffer, bufferSize, TRUE);
+}
+
+void WifiDevice::OnPaired()
+{
+ char full_url[256] = {0};
+ StringCbPrintfA(full_url, sizeof(full_url), "%s/library", url);
+ WAC_API_DOWNLOADMANAGER->DownloadEx(full_url, new SongListDownloader(url, this), api_downloadManager::DOWNLOADEX_CALLBACK);
+ SetPaired(device_info.id, true);
+}
+
+void WifiDevice::OnConnected(TemplateDevice *device)
+{
+ EnterCriticalSection(&register_lock);
+ pmp_device = device;
+ connect_active = false;
+ device_event_manager->Notify_ActivityFinished(this, &connect_activity);
+ AGAVE_API_DEVICEMANAGER->DeviceUnregister(id_string);
+ // if we disconnected/timed out on the listen server while connecting, go ahead and close the device out
+ if (dead && pmp_device)
+ {
+ pmp_device->CloseAsync();
+ pmp_device = 0;
+ }
+ LeaveCriticalSection(&register_lock);
+}
+
+void WifiDevice::OnDisconnect()
+{
+ // TODO: might actually need a crit sec here
+ EnterCriticalSection(&register_lock);
+ dead=1;
+ if (pmp_device)
+ {
+ pmp_device->CloseAsync();
+ pmp_device = 0;
+ }
+ else
+ {
+ AGAVE_API_DEVICEMANAGER->DeviceUnregister(id_string);
+ }
+ LeaveCriticalSection(&register_lock);
+}
+
+void WifiDevice::OnConnectionFailed()
+{
+ EnterCriticalSection(&register_lock);
+ delete pmp_device;
+ pmp_device = 0;
+ ifc_device *device = NULL;
+ bool device_exist = false;
+
+ // see if we're already registered (e.g. we started in unpaired state)
+ if (AGAVE_API_DEVICEMANAGER->DeviceFind(id_string, &device) == S_OK)
+ {
+ if (device == this)
+ device_exist = true;
+
+ device->Release();
+ }
+
+ if (device_exist)
+ { // if we are, then notify about activity being done
+ connect_active = false;
+ device_event_manager->Notify_ActivityFinished(this, &connect_activity);
+ }
+ else if (!dead)
+ { // if we weren't registered, we thought we were paired but failed
+ device = this;
+ AGAVE_API_DEVICEMANAGER->DeviceRegister(&device, 1);
+ }
+
+
+ LeaveCriticalSection(&register_lock);
+}
+
+HRESULT WifiDevice::GetActivity(ifc_deviceactivity **activity)
+{
+ if (connect_active)
+ {
+ *activity = &connect_activity;
+ return S_OK;
+ }
+ else
+ {
+ return E_FAIL;
+ }
+}
+
+HRESULT WifiDevice::GetTotalSpace(uint64_t *size)
+{
+#if 0
+ if (device_info.total_space)
+ {
+ *size = device_info.total_space;
+ return S_OK;
+ }
+#endif
+ return E_NOTIMPL;
+}
+
+HRESULT WifiDevice::GetUsedSpace(uint64_t *size)
+{
+#if 0
+ if (device_info.used_space)
+ {
+ *size = device_info.used_space;
+ return S_OK;
+ }
+#endif
+ return E_NOTIMPL;
+}
+
+HRESULT WifiDevice::GetModel(wchar_t *buffer, size_t bufferSize)
+{
+ return ModelInfo_CopyDisplayName(device_info.modelInfo, buffer, bufferSize);
+}
+
+#define CBCLASS WifiDevice
+START_DISPATCH;
+CB(QUERYINTERFACE, QueryInterface);
+CB(API_GETNAME, GetName);
+CB(API_GETICON, GetIcon);
+CB(API_GETDISPLAYNAME, GetDisplayName);
+CB(API_GETTOTALSPACE, GetTotalSpace);
+CB(API_GETUSEDSPACE, GetUsedSpace);
+CB(API_GETTYPE, GetType);
+CB(API_GETCONNECTION, GetConnection);
+CB(API_ENUMERATECOMMANDS, EnumerateCommands);
+CB(API_SENDCOMMAND, SendCommand);
+CB(API_GETATTACHED, GetAttached);
+CB(API_ATTACH, Attach);
+CB(API_DETACH, Detach);
+CB(API_GETACTIVITY, GetActivity);
+CB(API_ADVISE, Advise);
+CB(API_UNADVISE, Unadvise);
+CB(API_GETMODEL, GetModel);
+REFERENCE_COUNTED;
+END_DISPATCH;
diff --git a/Src/Plugins/Portable/pmp_wifi/WifiDevice.h b/Src/Plugins/Portable/pmp_wifi/WifiDevice.h
new file mode 100644
index 00000000..a7065c6b
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/WifiDevice.h
@@ -0,0 +1,64 @@
+#pragma once
+#include <bfc/platform/types.h>
+#include "../devices/ifc_device.h"
+#include "device.h"
+#include "../nu/refcount.h"
+#include "ConnectActivity.h"
+#include "main.h"
+/* this one inherits from ifc_device (not Device from ml_pmp) and is used to manage
+ attaching/detaching, etc from the device view */
+class TemplateDevice;
+class WifiDevice : public Countable<ifc_device>
+{
+public:
+ WifiDevice(const char *root_url, const DeviceInfo *device_info);
+ ~WifiDevice();
+ /* --- ifc_device interface --- */
+ int QueryInterface(GUID interface_guid, void **object);
+ HRESULT GetDisplayName(wchar_t *buffer, size_t bufferSize);
+ const char *GetName();
+ HRESULT GetIcon(wchar_t *buffer, size_t bufferSize, int width, int height);
+ const char *GetType();
+ const char *GetConnection();
+
+ BOOL GetHidden();
+
+ HRESULT GetTotalSpace(uint64_t *size);
+ HRESULT GetUsedSpace(uint64_t *size);
+
+ BOOL GetAttached();
+ HRESULT Attach(HWND hostWindow);
+ HRESULT Detach(HWND hostWindow);
+
+ HRESULT EnumerateCommands(ifc_devicesupportedcommandenum **enumerator, DeviceCommandContext context);
+ HRESULT SendCommand(const char *command, HWND hostWindow, ULONG_PTR param);
+ HRESULT GetActiveCommand(char *buffer, size_t bufferSize);
+ HRESULT CancelCommand(const char *command, HWND hostWindow);
+ HRESULT GetCommandFlags(const char *command, DeviceCommandFlags *flags);
+
+ HRESULT Advise(ifc_deviceevent *handler);
+ HRESULT Unadvise(ifc_deviceevent *handler);
+
+ HWND CreateView(HWND parentWindow);
+ void SetNavigationItem(void *navigationItem);
+
+ void OnPaired();
+ void OnConnected(TemplateDevice *device);
+ void OnConnectionFailed();
+ void OnDisconnect();
+ HRESULT GetModel(wchar_t *buffer, size_t bufferSize);
+
+ HRESULT GetActivity(ifc_deviceactivity **activity);
+
+ REFERENCE_COUNT_IMPLEMENTATION;
+private:
+ DeviceInfo device_info;
+ RECVS_DISPATCH;
+ char id_string[32];
+ char *url;
+ TemplateDevice *pmp_device;
+ ConnectActivity connect_activity;
+ bool connect_active;
+ volatile int dead;
+ CRITICAL_SECTION register_lock;
+}; \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/WifiPlaylist.cpp b/Src/Plugins/Portable/pmp_wifi/WifiPlaylist.cpp
new file mode 100644
index 00000000..fc0e3264
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/WifiPlaylist.cpp
@@ -0,0 +1,104 @@
+#include "WifiPlaylist.h"
+#include "api.h"
+#include "nu/AutoWide.h"
+
+/* ---- WifiTrack ---- */
+WifiTrack::WifiTrack()
+{
+ last_updated=0;
+ id=0;
+ artist=0;
+ album=0;
+ composer=0;
+ duration=0;
+ track=0;
+ year=0;
+ size=0;
+ title=0;
+ mime_type=0;
+}
+
+WifiTrack::WifiTrack(const char *id, const itemRecordW *record, const wchar_t *filename)
+{
+ this->id=AutoWideDup(id);
+ artist=_wcsdup(record->artist);
+ album=_wcsdup(record->album);
+ composer=_wcsdup(record->composer);
+ duration=record->length;
+ track=record->track;
+ year=record->year;
+ size=record->filesize;
+ title=_wcsdup(record->title);
+ wchar_t mime[128] = {0};
+ if (AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"mime", mime, 128) && mime[0])
+ {
+ mime_type=_wcsdup(mime);
+ }
+ else
+ {
+ mime_type=0;
+ }
+
+ last_updated=record->lastupd;
+}
+
+WifiTrack::WifiTrack(const WifiTrack &copy)
+{
+ id=_wcsdup(copy.id);
+ artist=_wcsdup(copy.artist);
+ album=_wcsdup(copy.album);
+ composer=_wcsdup(copy.composer);
+ duration=copy.duration;
+ track=copy.track;
+ year=copy.year;
+ size=copy.size;
+ title=_wcsdup(copy.title);
+ mime_type=_wcsdup(copy.mime_type);
+ last_updated=copy.last_updated;
+}
+
+WifiTrack::~WifiTrack()
+{
+ free(id);
+ free(artist);
+ free(album);
+ free(composer);
+ free(title);
+ free(mime_type);
+}
+
+
+/* ---- WifiPlaylist ---- */
+
+WifiPlaylist::WifiPlaylist()
+{
+ id=0;
+ name=0;
+}
+
+WifiPlaylist::WifiPlaylist(const char *id, const wchar_t *name)
+{
+ this->id = AutoWideDup(id);
+ this->name = _wcsdup(name);
+}
+
+WifiPlaylist::~WifiPlaylist()
+{
+ free(id);
+ free(name);
+ //tracks.deleteAll();
+ for (auto obj : tracks)
+ {
+ delete obj;
+ }
+ tracks.clear();
+}
+
+void WifiPlaylist::SetName(const wchar_t *new_name)
+{
+ if (name != new_name)
+ {
+ free(name);
+ name = _wcsdup(new_name);
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/WifiPlaylist.h b/Src/Plugins/Portable/pmp_wifi/WifiPlaylist.h
new file mode 100644
index 00000000..600428a5
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/WifiPlaylist.h
@@ -0,0 +1,36 @@
+#pragma once
+#include "../../General/gen_ml/ml.h" // for itemRecordW
+#include <vector>
+
+struct WifiTrack
+{
+ WifiTrack();
+ WifiTrack(const WifiTrack &copy);
+ ~WifiTrack();
+ WifiTrack(const char *id, const itemRecordW *record, const wchar_t *filename);
+ wchar_t *id;
+ wchar_t *artist;
+ wchar_t *album;
+ wchar_t *composer;
+ int duration;
+ int track;
+ int year;
+ int size;
+ wchar_t *title;
+ wchar_t *mime_type;
+ __time64_t last_updated;
+ wchar_t *ext;
+};
+
+class WifiPlaylist
+{
+public:
+ WifiPlaylist();
+ WifiPlaylist(const char *id, const wchar_t *name);
+ ~WifiPlaylist();
+ void SetName(const wchar_t *new_name);
+ typedef std::vector<WifiTrack*> TrackList;
+ TrackList tracks;
+ wchar_t *id;
+ wchar_t *name;
+}; \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/XMLString.cpp b/Src/Plugins/Portable/pmp_wifi/XMLString.cpp
new file mode 100644
index 00000000..a5d77b86
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/XMLString.cpp
@@ -0,0 +1,49 @@
+/** (c) Nullsoft, Inc. C O N F I D E N T I A L
+ ** Filename:
+ ** Project:
+ ** Description:
+ ** Author: Ben Allison benski@nullsoft.com
+ ** Created:
+ **/
+
+#include "XMLString.h"
+#include <strsafe.h>
+
+XMLString::XMLString()
+{
+ data[0]=0;
+}
+
+void XMLString::Reset()
+{
+ data[0]=0;
+}
+
+const wchar_t *XMLString::GetString()
+{
+ return data;
+}
+
+void XMLString::StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params)
+{
+ data[0]=0;
+}
+
+
+void XMLString::TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str)
+{
+ StringCchCatW(data, XMLSTRING_SIZE, str);
+}
+
+
+void XMLString::ManualSet(const wchar_t *string)
+{
+StringCchCatW(data, XMLSTRING_SIZE, string);
+}
+
+#define CBCLASS XMLString
+START_DISPATCH;
+VCB(ONSTARTELEMENT, StartTag)
+VCB(ONCHARDATA, TextHandler)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/XMLString.h b/Src/Plugins/Portable/pmp_wifi/XMLString.h
new file mode 100644
index 00000000..f4599022
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/XMLString.h
@@ -0,0 +1,28 @@
+#ifndef NULLSOFT_WINAMP_XMLSTRING_H
+#define NULLSOFT_WINAMP_XMLSTRING_H
+
+#include "../xml/ifc_xmlreadercallback.h"
+/*
+this one is an xml callback that just saves the last encountered string
+*/
+
+#define XMLSTRING_SIZE 1024
+class XMLString : public ifc_xmlreadercallback
+{
+public:
+ XMLString();
+ void Reset();
+ const wchar_t *GetString();
+ void ManualSet(const wchar_t *string);
+private:
+ /* XML callbacks */
+ void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params);
+ void EndTag(const wchar_t *xmlpath, const wchar_t *xmltag);
+ void TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str);
+
+ wchar_t data[XMLSTRING_SIZE]; // for now, we'll make it dynamic later
+
+ RECVS_DISPATCH;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/api.cpp b/Src/Plugins/Portable/pmp_wifi/api.cpp
new file mode 100644
index 00000000..84a80717
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/api.cpp
@@ -0,0 +1,72 @@
+#include "../../Library/ml_pmp/pmp.h"
+#include "api.h"
+#include <api/service/waservicefactory.h>
+
+api_albumart *AGAVE_API_ALBUMART=0;
+api_language *WASABI_API_LNG = 0;
+api_application *WASABI_API_APP = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+api_downloadManager *WAC_API_DOWNLOADMANAGER =0;
+api_config *AGAVE_API_CONFIG=0;
+api_devicemanager *AGAVE_API_DEVICEMANAGER = 0;
+api_metadata *AGAVE_API_METADATA=0;
+api_memmgr *WASABI_API_MEMMGR=0;
+Wasabi2::api_service *WASABI2_API_SVC=0;
+
+Wasabi2::api_ssdp *REPLICANT_API_SSDP=0;
+extern PMPDevicePlugin plugin;
+
+template <class api_T>
+void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
+{
+ if (plugin.service)
+ {
+ waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ api_t = reinterpret_cast<api_T *>( factory->getInterface() );
+ }
+}
+
+template <class api_T>
+void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
+{
+ if (plugin.service && api_t)
+ {
+ waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ factory->releaseInterface(api_t);
+ }
+ api_t = NULL;
+}
+
+void WasabiInit()
+{
+ ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
+ ServiceBuild(WASABI_API_LNG, languageApiGUID);
+ ServiceBuild( WAC_API_DOWNLOADMANAGER, DownloadManagerGUID);
+ ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
+ ServiceBuild(AGAVE_API_DEVICEMANAGER, DeviceManagerGUID);
+ ServiceBuild(AGAVE_API_ALBUMART, albumArtGUID);
+ ServiceBuild(AGAVE_API_METADATA, api_metadataGUID);
+ ServiceBuild(WASABI_API_MEMMGR, memMgrApiServiceGuid);
+ ServiceBuild(WASABI2_API_SVC, Wasabi2::api_service::GetServiceGUID());
+ // need to have this initialised before we try to do anything with localisation features
+ // TODO: WASABI_API_START_LANG(plugin.hDllInstance,InAviLangGUID);
+ if (WASABI2_API_SVC)
+ WASABI2_API_SVC->GetService(&REPLICANT_API_SSDP);
+}
+
+void WasabiQuit()
+{
+ ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);
+ ServiceRelease(WASABI_API_LNG, languageApiGUID);
+ ServiceRelease( WAC_API_DOWNLOADMANAGER, DownloadManagerGUID);
+ ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
+ ServiceRelease(AGAVE_API_DEVICEMANAGER, DeviceManagerGUID);
+ ServiceRelease(AGAVE_API_ALBUMART, albumArtGUID);
+ ServiceRelease(AGAVE_API_METADATA, api_metadataGUID);
+ ServiceRelease(WASABI_API_MEMMGR, memMgrApiServiceGuid);
+ ServiceRelease(WASABI2_API_SVC, Wasabi2::api_service::GetServiceGUID());
+ if (REPLICANT_API_SSDP)
+ REPLICANT_API_SSDP->Release();
+} \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/api.h b/Src/Plugins/Portable/pmp_wifi/api.h
new file mode 100644
index 00000000..9dcf8c20
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/api.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "../Agave/Language/api_language.h"
+
+#include <api/application/api_application.h>
+extern api_application *applicationApi;
+#define WASABI_API_APP applicationApi
+
+#include "../Components/wac_downloadManager/wac_downloadManager_api.h"
+
+#include "../Agave/Config/api_config.h"
+extern api_config *config;
+#define AGAVE_API_CONFIG config
+
+#include "../devices/api_devicemanager.h"
+extern api_devicemanager *deviceManagerApi;
+#define AGAVE_API_DEVICEMANAGER deviceManagerApi
+
+#include "../Agave/Metadata/api_metadata.h"
+extern api_metadata *metadataApi;
+#define AGAVE_API_METADATA metadataApi
+
+
+#include "../Agave/AlbumArt/api_albumart.h"
+extern api_albumart *albumArtApi;
+#define AGAVE_API_ALBUMART albumArtApi
+
+
+#include <api/memmgr/api_memmgr.h>
+extern api_memmgr *memmgr;
+#define WASABI_API_MEMMGR memmgr
+
+
+namespace Wasabi2
+{
+#include "service/api_service.h"
+#include "ssdp/api_ssdp.h"
+}
+
+extern Wasabi2::api_service *WASABI2_API_SVC;
+extern Wasabi2::api_ssdp *REPLICANT_API_SSDP;
+
+void WasabiInit();
+void WasabiQuit();
diff --git a/Src/Plugins/Portable/pmp_wifi/device.cpp b/Src/Plugins/Portable/pmp_wifi/device.cpp
new file mode 100644
index 00000000..c0acd3c2
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/device.cpp
@@ -0,0 +1,797 @@
+#include "main.h"
+#include "device.h"
+#include "XMLString.h"
+#include "api.h"
+#include "../xml/obj_xml.h"
+#include "../xml/ifc_xmlreaderparams.h"
+#include <api/service/waServiceFactory.h>
+#include "SongListDownloader.h"
+#include "SongDownloader.h"
+#include "RenameDownloader.h"
+#include "resource.h"
+#include "PlaylistSync.h"
+#include "nu/AutoWide.h"
+#include "images.h"
+#include <mmsystem.h> // for mmioFOURCC
+#include <strsafe.h>
+#include <shlwapi.h>
+
+TemplateDevice::TemplateDevice(WifiDevice *device, const char *root_url, DeviceInfo *in_device_info, TrackList *track_list, PlaylistsList *playlists_list)
+: url(strdup(root_url))
+{
+ DeviceInfo_Copy(&device_info, in_device_info);
+ //tracks.own(*track_list);
+ for (auto track : tracks)
+ {
+ delete track;
+ }
+ tracks.clear();
+ tracks.assign(track_list->begin(), track_list->end());
+ track_list->clear();
+
+
+ //playlists.own(*playlists_list);
+ for (auto playlist : playlists)
+ {
+ delete playlist;
+ }
+ playlists.clear();
+ playlists.assign(playlists_list->begin(), playlists_list->end());
+ playlists_list->clear();
+
+ transcoder=0;
+ transferQueueLength=0;
+
+ transcoder = (Transcoder*)SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(WPARAM)this,PMP_IPC_GET_TRANSCODER);
+ if(transcoder)
+ {
+ transcoder->AddAcceptableFormat(L"m4a");
+ transcoder->AddAcceptableFormat(L"mp3");
+ transcoder->AddAcceptableFormat(L"wav");
+ transcoder->AddAcceptableFormat(L"m4v");
+ transcoder->AddAcceptableFormat(L"mp4");
+ transcoder->AddAcceptableFormat(L"avi");
+ transcoder->AddAcceptableFormat(L"3gp");
+ transcoder->AddAcceptableFormat(L"mid");
+ transcoder->AddAcceptableFormat(L"ogg");
+ }
+}
+
+TemplateDevice::~TemplateDevice()
+{
+ free(url);
+
+ //tracks.deleteAll();
+ for (auto track : tracks)
+ {
+ delete track;
+ }
+ tracks.clear();
+
+ //playlists.deleteAll();
+ for (auto playlist : playlists)
+ {
+ delete playlist;
+ }
+ playlists.clear();
+
+ if (transcoder)
+ SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(WPARAM)transcoder,PMP_IPC_RELEASE_TRANSCODER);
+ transcoder=0;
+
+}
+
+__int64 TemplateDevice::getDeviceCapacityAvailable() // in bytes
+{
+ return device_info.total_space - device_info.used_space;
+}
+
+__int64 TemplateDevice::getDeviceCapacityTotal()
+{
+ return device_info.total_space;
+}
+
+void TemplateDevice::Eject()
+{
+ SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICEDISCONNECTED);
+}
+
+void TemplateDevice::Close()
+{
+ SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICEDISCONNECTED);
+}
+
+void TemplateDevice::CloseAsync()
+{
+ PostMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICEDISCONNECTED);
+}
+
+int PostFile(const char *url, const wchar_t *filename, const itemRecordW *track, obj_xml *parser, int *killswitch,
+ void (*callback)(void *callbackContext, wchar_t *status), void *context, char *new_item_id, size_t new_item_id_len);
+
+int PostAlbumArt(const char *url, const itemRecordW *track, obj_xml *parser, int *killswitch, void (*callback)(void *callbackContext, wchar_t *status), void *context);
+
+
+static int64_t FileSize64(const wchar_t * filename)
+{
+ WIN32_FIND_DATA f={0};
+ HANDLE h = FindFirstFileW(filename,&f);
+ if(h == INVALID_HANDLE_VALUE) return -1;
+ FindClose(h);
+ ULARGE_INTEGER i;
+ i.HighPart = f.nFileSizeHigh;
+ i.LowPart = f.nFileSizeLow;
+ return i.QuadPart;
+}
+
+// return 0 for success, -1 for failed or cancelled
+int TemplateDevice::transferTrackToDevice(const itemRecordW *track, // the track to transfer
+ void * callbackContext, //pass this to the callback
+ void (*callback)(void *callbackContext, wchar_t *status), // call this every so often so the GUI can be updated. Including when finished!
+ songid_t * songid, // fill in the songid when you are finished
+ int * killswitch // if this gets set to anything other than zero, the transfer has been cancelled by the user
+ )
+{
+ wchar_t newfile[MAX_PATH] = {0};
+ wchar_t *filename = track->filename;
+ bool delete_file = false;
+ if(transcoder && transcoder->ShouldTranscode(track->filename))
+ {
+ wchar_t ext[10] = {0};
+ int r = transcoder->CanTranscode(track->filename, ext, track->length);
+ if(r != 0 && r != -1)
+ {
+ transcoder->GetTempFilePath(ext,newfile);
+ if(transcoder->TranscodeFile(track->filename,newfile,killswitch,callback,callbackContext)) return -1;
+ filename = newfile;
+ delete_file=true;
+ }
+ }
+
+ char new_item_id[512] = {0};
+ char upload_url[555] = {0};
+ StringCbPrintfA(upload_url, sizeof(upload_url), "%s/upload", url);
+ if (PostFile(upload_url, filename, track, 0, killswitch, callback, callbackContext, new_item_id, 512) == 0 && new_item_id[0])
+ {
+ StringCbPrintfA(upload_url, sizeof(upload_url), "%s/albumart/%s", url, new_item_id);
+ PostAlbumArt(upload_url, track, 0, killswitch, callback, callbackContext);
+ callback(callbackContext, WASABI_API_LNGSTRINGW(IDS_COMPLETED));
+ WifiTrack *new_track = new WifiTrack(new_item_id, track, filename);
+ *songid = (songid_t)new_track;
+ device_info.used_space += FileSize64(filename); // TODO: count album art also. or re-query for device info
+ if (delete_file)
+ DeleteFile(filename);
+ return 0;
+ }
+ else
+ {
+ callback(callbackContext, L"Failed");
+ if (delete_file)
+ DeleteFile(filename);
+ return -1;
+ }
+}
+
+
+int TemplateDevice::trackAddedToTransferQueue(const itemRecordW *track)
+{
+ // return 0 to accept, -1 for "not enough space", -2 for "incorrect format"
+ __int64 l;
+ if(transcoder && transcoder->ShouldTranscode(track->filename))
+ {
+ int k = transcoder->CanTranscode(track->filename, 0, track->length);
+ if(k == -1) return -2;
+ if(k == 0) l = (__int64)FileSize64(track->filename);
+ else l = (__int64)k;
+ }
+ else
+ {
+ l = FileSize64(track->filename);
+ }
+ int64_t avail = getDeviceCapacityAvailable();
+ int64_t cmp = transferQueueLength;
+ cmp += l;
+ cmp += 3000000LL;
+
+ if(cmp > avail)
+ return -1;
+ else
+ {
+ transferQueueLength += l;
+ return 0;
+ }
+}
+
+void TemplateDevice::trackRemovedFromTransferQueue(const itemRecordW *track)
+{
+ int64_t l = FileSize64(track->filename);
+ if(transcoder && transcoder->ShouldTranscode(track->filename))
+ {
+ int k = transcoder->CanTranscode(track->filename, 0, track->length);
+ if(k != -1 && k != 0) l = (__int64)k;
+ }
+ transferQueueLength -= l;
+
+}
+
+// return the amount of space that will be taken up on the device by the track (once it has been tranferred)
+// or 0 for incompatable. This is usually the filesize, unless you are transcoding. An estimate is acceptable.
+__int64 TemplateDevice::getTrackSizeOnDevice(const itemRecordW *track)
+{
+ if(transcoder && transcoder->ShouldTranscode(track->filename))
+ {
+ int k = transcoder->CanTranscode(track->filename, 0, track->length);
+ if(k != -1 && k != 0) return k;
+ }
+ return track->filesize;
+}
+
+int HTTP_Delete(const char *url);
+void TemplateDevice::deleteTrack(songid_t songid)
+{
+ // physically remove from device. Be sure to remove it from all the playlists!
+ WifiTrack *track = (WifiTrack *)songid;
+ char delete_url[1024] = {0};
+ StringCbPrintfA(delete_url, sizeof(delete_url), "%s/file/%S", url, track->id);
+ HTTP_Delete(delete_url);
+again1:
+ for (WifiPlaylist::TrackList::iterator itr2=tracks.begin(); itr2 != tracks.end(); itr2++)
+ {
+ WifiTrack *trackitr = *itr2;
+ if (!wcscmp(trackitr->id, track->id))
+ {
+ tracks.erase(itr2);
+ if (trackitr != track)
+ delete trackitr;
+ goto again1; // iterator was invalidated
+ }
+ }
+
+ for (PlaylistsList::iterator itr=playlists.begin();itr!=playlists.end();itr++)
+ {
+ WifiPlaylist *playlist = *itr;
+again2:
+ for (WifiPlaylist::TrackList::iterator itr2=playlist->tracks.begin(); itr2 != playlist->tracks.end(); itr2++)
+ {
+ WifiTrack *trackitr = *itr2;
+ if (!wcscmp(trackitr->id, track->id))
+ {
+ playlist->tracks.erase(itr2);
+ if (trackitr != track)
+ delete trackitr;
+ goto again2; // iterator was invalidated
+ }
+ }
+ }
+ delete track;
+}
+
+
+void TemplateDevice::commitChanges()
+{
+ // optional. Will be called at a good time to save changes
+}
+
+int TemplateDevice::getPlaylistCount()
+{
+ // always at least 1. playlistnumber 0 is the Master Playlist containing all tracks.
+ return 1 + (int)playlists.size();
+}
+
+// PlaylistName(0) should return the name of the device.
+void TemplateDevice::getPlaylistName(int playlistnumber, wchar_t *buf, int len)
+{
+ if (playlistnumber == 0)
+ {
+ StringCchCopy(buf, len, device_info.name);
+ }
+ else
+ {
+ WifiPlaylist *playlist = playlists[playlistnumber-1];
+ StringCchCopy(buf, len, playlist->name);
+ }
+
+}
+int TemplateDevice::getPlaylistLength(int playlistnumber)
+{
+ if (playlistnumber == 0)
+ {
+ size_t size = tracks.size();
+ return (int)size;
+ }
+ else
+ {
+ WifiPlaylist *playlist = playlists[playlistnumber-1];
+ size_t size = playlist->tracks.size();
+ return (int)size;
+ }
+}
+
+songid_t TemplateDevice::getPlaylistTrack(int playlistnumber,int songnum)
+{
+ if (playlistnumber == 0)
+ {
+ WifiTrack *track = tracks[songnum];
+ return (songid_t)track;
+ }
+ else
+ {
+ WifiPlaylist *playlist = playlists[playlistnumber-1];
+ WifiTrack *track = playlist->tracks[songnum];
+ return (songid_t)track;
+ }
+
+}
+
+void TemplateDevice::setPlaylistName(int playlistnumber, const wchar_t *buf)
+{
+ if (playlistnumber == 0) // playlist 0 is the device itself
+ {
+ RenameDevice(url, buf);
+ StringCbCopy(device_info.name, sizeof(device_info.name), buf);
+ }
+ else
+ {
+ WifiPlaylist *playlist = playlists[playlistnumber-1];
+ playlist->SetName(buf);
+ Sync_RenamePlaylist(url, playlist->id, buf);
+ }
+}
+
+void TemplateDevice::playlistSwapItems(int playlistnumber, int posA, int posB)
+{
+ // swap the songs at position posA and posB
+ // TODO: implement
+}
+
+void TemplateDevice::sortPlaylist(int playlistnumber, int sortBy)
+{
+ // TODO: implement
+}
+
+void TemplateDevice::addTrackToPlaylist(int playlistnumber, songid_t songid)
+{
+ // adds songid to the end of the playlist
+ WifiTrack *track = (WifiTrack *)songid;
+ if (playlistnumber == 0)
+ {
+ tracks.push_back(track);
+ }
+ else
+ {
+ playlists[playlistnumber - 1]->tracks.push_back(new WifiTrack(*track));
+ Sync_AddToPlaylist(url, playlists[playlistnumber-1]->id, track->id);
+ }
+
+}
+
+void TemplateDevice::removeTrackFromPlaylist(int playlistnumber, int songnum)
+{
+ //where songnum is the position of the track in the playlist
+ if (playlistnumber == 0)
+ {
+ tracks.erase(tracks.begin() + songnum);
+ }
+ else
+ {
+ WifiPlaylist *playlist = playlists[playlistnumber-1];
+ WifiTrack *track = playlist->tracks[songnum];
+ Sync_RemoveFromPlaylist(url, playlist->id, track->id);
+ }
+}
+
+void TemplateDevice::deletePlaylist(int playlistnumber)
+{
+ if (playlistnumber == 0)
+ {
+ }
+ else
+ {
+ WifiPlaylist *playlist = playlists[playlistnumber-1];
+ Sync_DeletePlaylist(url, playlist->id);
+ playlists.erase(playlists.begin() + playlistnumber-1);
+ }
+}
+
+int TemplateDevice::newPlaylist(const wchar_t *name)
+{
+ // create empty playlist, returns playlistnumber. -1 for failed.
+ WifiPlaylist *new_playlist = Sync_NewPlaylist(url, name);
+ if (new_playlist)
+ {
+ playlists.push_back(new_playlist);
+ return (int)playlists.size();
+ }
+ return -1;
+}
+
+void TemplateDevice::getTrackArtist(songid_t songid, wchar_t *buf, int len)
+{
+ WifiTrack *track = (WifiTrack *)songid;
+ StringCchCopy(buf, len, track->artist);
+}
+
+void TemplateDevice::getTrackAlbum(songid_t songid, wchar_t *buf, int len)
+{
+ WifiTrack *track = (WifiTrack *)songid;
+ StringCchCopy(buf, len, track->album);
+}
+
+void TemplateDevice::getTrackTitle(songid_t songid, wchar_t *buf, int len)
+{
+ WifiTrack *track = (WifiTrack *)songid;
+ StringCchCopy(buf, len, track->title);
+}
+
+int TemplateDevice::getTrackTrackNum(songid_t songid)
+{
+ WifiTrack *track = (WifiTrack *)songid;
+ return track->track;
+}
+int TemplateDevice::getTrackDiscNum(songid_t songid)
+{
+ // TODO: implement
+ return 0;
+}
+void TemplateDevice::getTrackGenre(songid_t songid, wchar_t * buf, int len)
+{
+ buf[0]=0;
+}
+
+int TemplateDevice::getTrackYear(songid_t songid)
+{
+ WifiTrack *track = (WifiTrack *)songid;
+ return track->year;
+}
+
+__int64 TemplateDevice::getTrackSize(songid_t songid)
+{
+ WifiTrack *track = (WifiTrack *)songid;
+ return track->size;
+}
+
+int TemplateDevice::getTrackLength(songid_t songid)
+{
+ WifiTrack *track = (WifiTrack *)songid;
+ return track->duration;
+}
+
+int TemplateDevice::getTrackBitrate(songid_t songid)
+{
+ return 128;
+}
+
+int TemplateDevice::getTrackPlayCount(songid_t songid)
+{
+ return 0;
+}
+
+int TemplateDevice::getTrackRating(songid_t songid)
+{
+ return 0;
+}
+
+__time64_t TemplateDevice::getTrackLastPlayed(songid_t songid)
+{
+ return 0;
+}
+
+__time64_t TemplateDevice::getTrackLastUpdated(songid_t songid)
+{
+ WifiTrack *track = (WifiTrack *)songid;
+ return track->last_updated;
+}
+
+void TemplateDevice::getTrackAlbumArtist(songid_t songid, wchar_t *buf, int len)
+{
+ buf[0]=0;
+}
+
+void TemplateDevice::getTrackPublisher(songid_t songid, wchar_t *buf, int len)
+{
+ buf[0]=0;
+}
+
+void TemplateDevice::getTrackComposer(songid_t songid, wchar_t *buf, int len)
+{
+ WifiTrack *track = (WifiTrack *)songid;
+ StringCchCopy(buf, len, track->composer);
+}
+
+int TemplateDevice::getTrackType(songid_t songid)
+{
+ return 0;
+}
+void TemplateDevice::getTrackExtraInfo(songid_t songid, const wchar_t *field, wchar_t *buf, int len)
+{
+ // TODO: implement
+ //optional
+}
+
+// feel free to ignore any you don't support
+void TemplateDevice::setTrackArtist(songid_t songid, const wchar_t *value)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackAlbum(songid_t songid, const wchar_t *value)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackTitle(songid_t songid, const wchar_t *value)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackTrackNum(songid_t songid, int value)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackDiscNum(songid_t songid, int value)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackGenre(songid_t songid, const wchar_t *value)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackYear(songid_t songid, int year)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackPlayCount(songid_t songid, int value)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackRating(songid_t songid, int value)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackLastPlayed(songid_t songid, __time64_t value)
+{
+ // TODO: implement
+
+} // in unix time format
+void TemplateDevice::setTrackLastUpdated(songid_t songid, __time64_t value)
+{
+ // TODO: implement
+
+} // in unix time format
+void TemplateDevice::setTrackAlbumArtist(songid_t songid, const wchar_t *value)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackPublisher(songid_t songid, const wchar_t *value)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackComposer(songid_t songid, const wchar_t *value)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setTrackExtraInfo(songid_t songid, const wchar_t *field, const wchar_t *value)
+{
+ // TODO: implement
+
+} //optional
+
+bool TemplateDevice::playTracks(songid_t * songidList, int listLength, int startPlaybackAt, bool enqueue)
+{
+ if(!enqueue) //clear playlist
+ {
+ SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_DELETE);
+ }
+
+ for(int i=0; i<listLength; i++)
+ {
+ WifiTrack*curSong = (WifiTrack *)songidList[i];
+
+ if (curSong)
+ {
+ wchar_t fn[1024] = {0};
+
+ if (curSong->mime_type && !_wcsicmp(curSong->mime_type, L"audio/mp4"))
+ StringCbPrintf(fn, sizeof(fn), L"%S/file/%s?=.m4a", url, curSong->id);
+ else if (curSong->mime_type && !_wcsicmp(curSong->mime_type, L"audio/x-ms-wma"))
+ StringCbPrintf(fn, sizeof(fn), L"%S/file/%s?=.wma", url, curSong->id);
+ else if (curSong->mime_type && (!_wcsicmp(curSong->mime_type, L"application/ogg") || !_wcsicmp(curSong->mime_type, L"audio/ogg")))
+ StringCbPrintf(fn, sizeof(fn), L"%S/file/%s?=.ogg", url, curSong->id);
+ else
+ StringCbPrintf(fn, sizeof(fn), L"%S/file/%s", url, curSong->id);
+ enqueueFileWithMetaStructW s={0};
+ s.filename = fn;
+ s.title = _wcsdup(curSong->title);
+ s.ext = NULL;
+ s.length = curSong->duration/1000;
+
+ SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW);
+ }
+ else
+ {
+ //char titleStr[32];
+ //MessageBoxA(plugin.hwndWinampParent,WASABI_API_LNGSTRING(IDS_CANNOT_OPEN_FILE),
+ // WASABI_API_LNGSTRING_BUF(IDS_ERROR,titleStr,32),0);
+ }
+ }
+
+ if(!enqueue)
+ {
+ //play item startPlaybackAt
+ SendMessage(plugin.hwndWinampParent,WM_WA_IPC,startPlaybackAt,IPC_SETPLAYLISTPOS);
+ SendMessage(plugin.hwndWinampParent,WM_COMMAND,40047,0); //stop
+ SendMessage(plugin.hwndWinampParent,WM_COMMAND,40045,0); //play
+ }
+ return true;
+}
+
+static const intptr_t encoder_blacklist[] =
+{
+ mmioFOURCC('W','M','A',' '),
+ mmioFOURCC('A','A','C','H'),
+ mmioFOURCC('A','A','C','P'),
+ mmioFOURCC('A','A','C','r'),
+ mmioFOURCC('F','L','A','C'),
+ mmioFOURCC('M','P','2',' '),
+ mmioFOURCC('A','D','T','S'),
+};
+
+
+intptr_t TemplateDevice::extraActions(intptr_t param1, intptr_t param2, intptr_t param3,intptr_t param4)
+{
+ switch(param1)
+ {
+ case DEVICE_SET_ICON: // icons
+ {
+ MLTREEIMAGE * i = (MLTREEIMAGE*)param2;
+ const ModelInfo *modelInfo;
+ i->hinst = plugin.hDllInstance;
+
+ modelInfo = device_info.modelInfo;
+ if (NULL == modelInfo || NULL == modelInfo->smallIcon)
+ {
+ modelInfo = GetDefaultModelInfo();
+ if (NULL == modelInfo)
+ break;
+ }
+
+ i->resourceId = (int)(intptr_t)modelInfo->smallIcon;
+ }
+ break;
+ case DEVICE_CAN_RENAME_DEVICE:
+ return 1;
+ case DEVICE_GET_ICON:
+ ModelInfo_GetIconPath(device_info.modelInfo, (int)param2, (int)param3, (wchar_t*)param4, 260, TRUE);
+ break;
+ case DEVICE_GET_CONNECTION_TYPE:
+ {
+ const char **type = (const char **)param2;
+ *type = "WiFi";
+ return 1;
+ }
+ case DEVICE_SUPPORTS_PODCASTS:
+ return 1; // we don't support podcasts
+ case DEVICE_GET_MODEL:
+ ModelInfo_CopyDisplayName(device_info.modelInfo, (wchar_t*)param2, param3);
+ return 1;
+ case DEVICE_SUPPORTED_METADATA:
+ {
+ intptr_t supported = SUPPORTS_ARTIST | SUPPORTS_ALBUM | SUPPORTS_TITLE | SUPPORTS_TRACKNUM /*| SUPPORTS_DISCNUM | SUPPORTS_GENRE */|
+ SUPPORTS_YEAR | SUPPORTS_SIZE | SUPPORTS_LENGTH /*| SUPPORTS_BITRATE */| SUPPORTS_LASTUPDATED /*| SUPPORTS_ALBUMARTIST */|
+ SUPPORTS_COMPOSER /*| SUPPORTS_PUBLISHER | SUPPORTS_ALBUMART*/;
+ return supported;
+ }
+ break;
+ case DEVICE_VETO_ENCODER:
+ {
+ for (size_t i=0;i<sizeof(encoder_blacklist)/sizeof(*encoder_blacklist);i++)
+ {
+ if (param2 == encoder_blacklist[i])
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+
+ // TODO: implement more
+ return 0;
+}
+
+bool TemplateDevice::copyToHardDriveSupported()
+{
+ return true;
+}
+
+__int64 TemplateDevice::songSizeOnHardDrive(songid_t song)
+{
+ WifiTrack *track = (WifiTrack *)song;
+ return track->size;
+}
+
+int TemplateDevice::copyToHardDrive(songid_t song, // the song to copy
+ wchar_t * path, // path to copy to, in the form "c:\directory\song". The directory will already be created, you must append ".mp3" or whatever to this string! (there is space for at least 10 new characters).
+ void * callbackContext, //pass this to the callback
+ void (*callback)(void * callbackContext, wchar_t * status), // call this every so often so the GUI can be updated. Including when finished!
+ int * killswitch // if this gets set to anything other than zero, the transfer has been cancelled by the user
+ )
+{
+ WifiTrack *track = (WifiTrack *)song;
+ char download_url[1024] = {0};
+ StringCbPrintfA(download_url, sizeof(download_url), "%s/file/%S", url, track->id);
+ HANDLE event = CreateEvent(0, FALSE, FALSE, 0);
+
+ if (!_wcsicmp(track->mime_type, L"audio/mpeg"))
+ wcsncat(path, L".mp3", MAX_PATH);
+ else if (!_wcsicmp(track->mime_type, L"audio/mp4"))
+ wcsncat(path, L".m4a", MAX_PATH);
+ else if (!_wcsicmp(track->mime_type, L"audio/x-ms-wma"))
+ wcsncat(path, L".wma", MAX_PATH);
+ else if (!_wcsicmp(track->mime_type, L"application/ogg") || !_wcsicmp(track->mime_type, L"audio/ogg") )
+ wcsncat(path, L".ogg", MAX_PATH);
+ // TODO: more
+
+ SongDownloader *song_downloader = new SongDownloader(path, event, callback, callbackContext);
+ song_downloader->AddRef();
+ WAC_API_DOWNLOADMANAGER->DownloadEx(download_url, song_downloader, api_downloadManager::DOWNLOADEX_CALLBACK);
+ WaitForSingleObject(event, INFINITE);
+ song_downloader->Release();
+ return 0; // TODO: check error code
+}
+
+// art functions
+void TemplateDevice::setArt(songid_t songid, void *buf, int w, int h)
+{
+ //buf is in format ARGB32*
+ // TODO: implement
+
+}
+
+pmpart_t TemplateDevice::getArt(songid_t songid)
+{
+ // TODO: implement
+ return 0;
+}
+
+void TemplateDevice::releaseArt(pmpart_t art)
+{
+ // TODO: implement
+
+}
+int TemplateDevice::drawArt(pmpart_t art, HDC dc, int x, int y, int w, int h)
+{
+ // TODO: implement
+ return 0;
+}
+
+void TemplateDevice::getArtNaturalSize(pmpart_t art, int *w, int *h)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::setArtNaturalSize(pmpart_t art, int w, int h)
+{
+ // TODO: implement
+
+}
+void TemplateDevice::getArtData(pmpart_t art, void* data)
+{
+ // data ARGB32* is at natural size
+ // TODO: implement
+}
+
+bool TemplateDevice::artIsEqual(pmpart_t a, pmpart_t b)
+{
+ // TODO: implement
+ return false;
+}
+
diff --git a/Src/Plugins/Portable/pmp_wifi/device.h b/Src/Plugins/Portable/pmp_wifi/device.h
new file mode 100644
index 00000000..12a82087
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/device.h
@@ -0,0 +1,127 @@
+#pragma once
+#include "../../Library/ml_pmp/pmp.h"
+#include "../../Library/ml_pmp/transcoder.h"
+#include <vector>
+#include "WifiDevice.h"
+#include "WifiPlaylist.h"
+#include "main.h"
+#include <bfc/platform/types.h>
+
+class WifiDevice;
+class TemplateDevice : public Device
+{
+public:
+ typedef std::vector<WifiTrack*> TrackList;
+ typedef std::vector<WifiPlaylist*> PlaylistsList;
+ TemplateDevice(WifiDevice *device, const char *root_url, DeviceInfo *device_info, TemplateDevice::TrackList *track_list, TemplateDevice::PlaylistsList *playlists_list);
+ ~TemplateDevice();
+ virtual __int64 getDeviceCapacityAvailable(); // in bytes
+ virtual __int64 getDeviceCapacityTotal(); // in bytes
+
+ virtual void Eject(); // if you ejected successfully, you MUST call PMP_IPC_DEVICEDISCONNECTED and delete this;
+ virtual void Close(); // save any changes, and call PMP_IPC_DEVICEDISCONNECTED AND delete this;
+ void CloseAsync();
+
+ // return 0 for success, -1 for failed or cancelled
+ virtual int transferTrackToDevice(const itemRecordW * track, // the track to transfer
+ void * callbackContext, //pass this to the callback
+ void (*callback)(void *callbackContext, wchar_t *status), // call this every so often so the GUI can be updated. Including when finished!
+ songid_t * songid, // fill in the songid when you are finished
+ int * killswitch // if this gets set to anything other than zero, the transfer has been cancelled by the user
+ );
+ virtual int trackAddedToTransferQueue(const itemRecordW *track); // return 0 to accept, -1 for "not enough space", -2 for "incorrect format"
+ virtual void trackRemovedFromTransferQueue(const itemRecordW *track);
+
+ // return the amount of space that will be taken up on the device by the track (once it has been tranferred)
+ // or 0 for incompatable. This is usually the filesize, unless you are transcoding. An estimate is acceptable.
+ virtual __int64 getTrackSizeOnDevice(const itemRecordW *track);
+
+ virtual void deleteTrack(songid_t songid); // physically remove from device. Be sure to remove it from all the playlists!
+
+ virtual void commitChanges(); // optional. Will be called at a good time to save changes
+
+ virtual int getPlaylistCount(); // always at least 1. playlistnumber 0 is the Master Playlist containing all tracks.
+ // PlaylistName(0) should return the name of the device.
+ virtual void getPlaylistName(int playlistnumber, wchar_t *buf, int len);
+ virtual int getPlaylistLength(int playlistnumber);
+ virtual songid_t getPlaylistTrack(int playlistnumber,int songnum); // returns a songid
+
+ virtual void setPlaylistName(int playlistnumber, const wchar_t *buf); // with playlistnumber==0, set the name of the device.
+ virtual void playlistSwapItems(int playlistnumber, int posA, int posB); // swap the songs at position posA and posB
+ virtual void sortPlaylist(int playlistnumber, int sortBy);
+ virtual void addTrackToPlaylist(int playlistnumber, songid_t songid); // adds songid to the end of the playlist
+ virtual void removeTrackFromPlaylist(int playlistnumber, int songnum); //where songnum is the position of the track in the playlist
+
+ virtual void deletePlaylist(int playlistnumber);
+ virtual int newPlaylist(const wchar_t *name); // create empty playlist, returns playlistnumber. -1 for failed.
+
+ virtual void getTrackArtist(songid_t songid, wchar_t *buf, int len);
+ virtual void getTrackAlbum(songid_t songid, wchar_t *buf, int len);
+ virtual void getTrackTitle(songid_t songid, wchar_t *buf, int len);
+ virtual int getTrackTrackNum(songid_t songid);
+ virtual int getTrackDiscNum(songid_t songid);
+ virtual void getTrackGenre(songid_t songid, wchar_t * buf, int len);
+ virtual int getTrackYear(songid_t songid);
+ virtual __int64 getTrackSize(songid_t songid); // in bytes
+ virtual int getTrackLength(songid_t songid); // in millisecs
+ virtual int getTrackBitrate(songid_t songid); // in kbps
+ virtual int getTrackPlayCount(songid_t songid);
+ virtual int getTrackRating(songid_t songid); //0-5
+ virtual __time64_t getTrackLastPlayed(songid_t songid); // in unix time format
+ virtual __time64_t getTrackLastUpdated(songid_t songid); // in unix time format
+ virtual void getTrackAlbumArtist(songid_t songid, wchar_t *buf, int len);
+ virtual void getTrackPublisher(songid_t songid, wchar_t *buf, int len);
+ virtual void getTrackComposer(songid_t songid, wchar_t *buf, int len);
+ virtual int getTrackType(songid_t songid);
+ virtual void getTrackExtraInfo(songid_t songid, const wchar_t *field, wchar_t *buf, int len) ; //optional
+
+ // feel free to ignore any you don't support
+ virtual void setTrackArtist(songid_t songid, const wchar_t *value);
+ virtual void setTrackAlbum(songid_t songid, const wchar_t *value);
+ virtual void setTrackTitle(songid_t songid, const wchar_t *value);
+ virtual void setTrackTrackNum(songid_t songid, int value);
+ virtual void setTrackDiscNum(songid_t songid, int value);
+ virtual void setTrackGenre(songid_t songid, const wchar_t *value);
+ virtual void setTrackYear(songid_t songid, int year);
+ virtual void setTrackPlayCount(songid_t songid, int value);
+ virtual void setTrackRating(songid_t songid, int value);
+ virtual void setTrackLastPlayed(songid_t songid, __time64_t value); // in unix time format
+ virtual void setTrackLastUpdated(songid_t songid, __time64_t value); // in unix time format
+ virtual void setTrackAlbumArtist(songid_t songid, const wchar_t *value);
+ virtual void setTrackPublisher(songid_t songid, const wchar_t *value);
+ virtual void setTrackComposer(songid_t songid, const wchar_t *value);
+ virtual void setTrackExtraInfo(songid_t songid, const wchar_t *field, const wchar_t *value) ; //optional
+
+ virtual bool playTracks(songid_t * songidList, int listLength, int startPlaybackAt, bool enqueue); // return false if unsupported
+
+ virtual intptr_t extraActions(intptr_t param1, intptr_t param2, intptr_t param3,intptr_t param4);
+
+ virtual bool copyToHardDriveSupported();
+
+ virtual __int64 songSizeOnHardDrive(songid_t song); // how big a song will be when copied back. Return -1 for not supported.
+
+ virtual int copyToHardDrive(songid_t song, // the song to copy
+ wchar_t * path, // path to copy to, in the form "c:\directory\song". The directory will already be created, you must append ".mp3" or whatever to this string! (there is space for at least 10 new characters).
+ void * callbackContext, //pass this to the callback
+ void (*callback)(void * callbackContext, wchar_t * status), // call this every so often so the GUI can be updated. Including when finished!
+ int * killswitch // if this gets set to anything other than zero, the transfer has been cancelled by the user
+ ); // -1 for failed/not supported. 0 for success.
+
+ // art functions
+ virtual void setArt(songid_t songid, void *buf, int w, int h); //buf is in format ARGB32*
+ virtual pmpart_t getArt(songid_t songid);
+ virtual void releaseArt(pmpart_t art);
+ virtual int drawArt(pmpart_t art, HDC dc, int x, int y, int w, int h);
+ virtual void getArtNaturalSize(pmpart_t art, int *w, int *h);
+ virtual void setArtNaturalSize(pmpart_t art, int w, int h);
+ virtual void getArtData(pmpart_t art, void* data); // data ARGB32* is at natural size
+ virtual bool artIsEqual(pmpart_t a, pmpart_t b);
+
+ PlaylistsList playlists;
+ TrackList tracks;
+ DeviceInfo device_info;
+ char *url;
+ int image16, image160;
+ Transcoder *transcoder;
+ int64_t transferQueueLength;
+}; \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/images.cpp b/Src/Plugins/Portable/pmp_wifi/images.cpp
new file mode 100644
index 00000000..6badb929
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/images.cpp
@@ -0,0 +1,72 @@
+#include "main.h"
+#include "images.h"
+#include "resource.h"
+#include <strsafe.h>
+
+static int small_images[] = { IDB_GENERIC_16, IDB_EVO_16, IDB_INCREDIBLE_16, IDB_NEXUSONE_16, IDB_DROID_16 };
+static int large_images[] = { IDB_GENERIC_160, IDB_EVO_160, IDB_INCREDIBLE_160, IDB_NEXUSONE_160, IDB_DROID_160 };
+int GetImageIndex(const wchar_t *manufacturer, const wchar_t *model)
+{
+ if (!wcscmp(manufacturer, L"HTC"))
+ {
+ if (!wcscmp(model, L"PC36100")) // evo
+ {
+ return 1;
+ }
+ else if (!wcscmp(model, L"ADR6300")) // incredible
+ {
+ return 2;
+ }
+ else if (!wcscmp(model, L"Nexus One"))
+ {
+ return 3;
+ }
+ }
+ else if (!wcscmp(manufacturer, L"motorola"))
+ {
+ if (!wcscmp(model, L"DROID2"))
+ {
+ return 4;
+ }
+ }
+
+ return 0;
+}
+
+void GetImagePath(int image_index, int width, int height, wchar_t *path, size_t path_cch)
+{
+ if (image_index < 0)
+ {
+ path[0]=0;
+ return;
+ }
+
+ if (width <= 16 && height <= 16)
+ {
+ if (image_index >= sizeof(small_images)/sizeof(small_images[0]))
+ {
+ path[0]=0;
+ return;
+ }
+ int resource = small_images[image_index];
+ FormatResProtocol(MAKEINTRESOURCE(resource), L"PNG", path, path_cch);
+ }
+ else
+ {
+ if (image_index >= sizeof(large_images)/sizeof(large_images[0]))
+ {
+ path[0]=0;
+ return;
+ }
+ int resource = large_images[image_index];
+ FormatResProtocol(MAKEINTRESOURCE(resource), L"PNG", path, path_cch);
+ }
+}
+
+int GetSmallImageID(int image_index)
+{
+ if (image_index < 0 || image_index >= sizeof(small_images)/sizeof(small_images[0]))
+ return IDB_GENERIC_16;
+
+ return small_images[image_index];
+} \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/images.h b/Src/Plugins/Portable/pmp_wifi/images.h
new file mode 100644
index 00000000..39fd9e13
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/images.h
@@ -0,0 +1,5 @@
+#pragma once
+
+int GetImageIndex(const wchar_t *manufacturer, const wchar_t *model);
+void GetImagePath(int image_index, int width, int height, wchar_t *path, size_t path_cch);
+int GetSmallImageID(int image_index); \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/main.cpp b/Src/Plugins/Portable/pmp_wifi/main.cpp
new file mode 100644
index 00000000..6df15a76
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/main.cpp
@@ -0,0 +1,263 @@
+#include "../../Library/ml_pmp/pmp.h"
+#include "../Winamp/wa_ipc.h"
+#include "device.h"
+#include "api.h"
+#include "main.h"
+#include "nu/ns_wc.h"
+#include "resource.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define PLUGIN_VERSION L"1.56"
+int winampVersion = 0;
+ifc_devicesupportedcommandenum *command_enum=0;
+ifc_devicesupportedcommandstore *command_store=0;
+ifc_deviceeventmanager *device_event_manager;
+char winamp_name[260] = {0};
+char winamp_id_str[40] = {0};
+wchar_t inifile[MAX_PATH] = {0};
+GUID winamp_id = GUID_NULL;
+static int Init();
+static void Quit();
+static intptr_t MessageProc(int msg, intptr_t param1, intptr_t param2, intptr_t param3);
+
+PMPDevicePlugin plugin = {PMPHDR_VER,0,Init,Quit,MessageProc};
+void StartListenServer();
+
+
+BOOL FormatResProtocol(const wchar_t *resourceName, const wchar_t *resourceType, wchar_t *buffer, size_t bufferMax)
+{
+ unsigned long filenameLength;
+
+ if (NULL == resourceName)
+ return FALSE;
+
+ if (FAILED(StringCchCopyExW(buffer, bufferMax, L"res://", &buffer, &bufferMax, 0)))
+ return FALSE;
+
+ filenameLength = GetModuleFileNameW(plugin.hDllInstance, buffer, (DWORD)bufferMax);
+ if (0 == filenameLength || bufferMax == filenameLength)
+ return FALSE;
+
+ buffer += filenameLength;
+ bufferMax -= filenameLength;
+
+ if (NULL != resourceType)
+ {
+ if (FALSE != IS_INTRESOURCE(resourceType))
+ {
+ if (FAILED(StringCchPrintfExW(buffer, bufferMax, &buffer, &bufferMax, 0, L"/#%d", (int)(INT_PTR)resourceType)))
+ return FALSE;
+ }
+ else
+ {
+ if (FAILED(StringCchPrintfExW(buffer, bufferMax, &buffer, &bufferMax, 0, L"/%s", resourceType)))
+ return FALSE;
+ }
+ }
+
+ if (FALSE != IS_INTRESOURCE(resourceName))
+ {
+ if (FAILED(StringCchPrintfExW(buffer, bufferMax, &buffer, &bufferMax, 0, L"/#%d", (int)(INT_PTR)resourceName)))
+ return FALSE;
+ }
+ else
+ {
+ if (FAILED(StringCchPrintfExW(buffer, bufferMax, &buffer, &bufferMax, 0, L"/%s", resourceName)))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+class WifiDeviceConnection : public ifc_deviceconnection
+{
+public:
+ WifiDeviceConnection()
+ {
+ }
+ const char *GetName()
+ {
+ return "wifi";
+ }
+
+ HRESULT GetIcon(wchar_t *buffer, size_t bufferMax, int width, int height)
+ {
+ if(FALSE == FormatResProtocol(MAKEINTRESOURCE(IDB_WIFI), L"PNG", buffer, bufferMax))
+ return E_FAIL;
+
+ return S_OK;
+ }
+
+ HRESULT GetDisplayName(wchar_t *buffer, size_t bufferMax)
+ {
+ if (NULL == buffer)
+ return E_POINTER;
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_CONNECTION_WIFI, buffer, bufferMax);
+ return S_OK;
+ }
+protected:
+
+#define CBCLASS WifiDeviceConnection
+ START_DISPATCH_INLINE;
+ CB(API_GETNAME, GetName);
+ CB(API_GETICON, GetIcon);
+ CB(API_GETDISPLAYNAME, GetDisplayName);
+ END_DISPATCH;
+#undef CBCLASS
+};
+
+class AttachCommand : public ifc_devicecommand
+{
+public:
+ const char *GetName()
+ {
+ return "attach";
+ }
+
+ HRESULT GetIcon(wchar_t *buffer, size_t bufferMax, int width, int height)
+ {
+ int resourceId;
+
+ if (width <= 16 && height <= 16)
+ resourceId = IDB_ATTACH_16;
+ else
+ resourceId = IDB_ATTACH;
+
+ if(FALSE == FormatResProtocol(MAKEINTRESOURCE(resourceId), L"PNG", buffer, bufferMax))
+ return E_FAIL;
+
+ return S_OK;
+ }
+
+ HRESULT GetDisplayName(wchar_t *buffer, size_t bufferMax)
+ {
+ if (NULL == buffer)
+ return E_POINTER;
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_CMD_ATTACH, buffer, bufferMax);
+ return S_OK;
+ }
+
+ HRESULT GetDescription(wchar_t *buffer, size_t bufferMax)
+ {
+ if (NULL == buffer)
+ return E_POINTER;
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_CMD_ATTACH_DESC, buffer, bufferMax);
+ return S_OK;
+ }
+
+#define CBCLASS AttachCommand
+ START_DISPATCH_INLINE;
+ CB(API_GETNAME, GetName);
+ CB(API_GETICON, GetIcon);
+ CB(API_GETDISPLAYNAME, GetDisplayName);
+ CB(API_GETDESCRIPTION, GetDescription);
+ END_DISPATCH;
+#undef CBCLASS
+};
+
+class DeviceCommand : public Countable<ifc_devicesupportedcommand>
+{
+public:
+ DeviceCommand(const char *name, DeviceCommandFlags flags);
+
+public:
+ const char *GetName();
+ HRESULT GetFlags(DeviceCommandFlags *flags);
+ REFERENCE_COUNT_IMPLEMENTATION;
+
+public:
+ const char *name;
+ DeviceCommandFlags flags;
+RECVS_DISPATCH;
+};
+
+
+static AttachCommand attach_command;
+static WifiDeviceConnection wifi_connection;
+static int Init()
+{
+ winampVersion = (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETVERSION);
+ WasabiInit();
+
+ if (!AGAVE_API_DEVICEMANAGER)
+ return 1;
+ WASABI_API_APP->GetUserID(&winamp_id);
+ StringCbPrintfA(winamp_id_str, sizeof(winamp_id_str), "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", (int)winamp_id.Data1, (int)winamp_id.Data2, (int)winamp_id.Data3, (int)winamp_id.Data4[0], (int)winamp_id.Data4[1], (int)winamp_id.Data4[2], (int)winamp_id.Data4[3], (int)winamp_id.Data4[4], (int)winamp_id.Data4[5], (int)winamp_id.Data4[6], (int)winamp_id.Data4[7] );
+
+ wchar_t user_name[128] = {0};
+ wchar_t computer_name[128] = {0};
+ DWORD buffer_size_user = 128, buffer_size_computer=128;
+ if (GetUserNameW(user_name, &buffer_size_user) && GetComputerNameW(computer_name, &buffer_size_computer))
+ {
+ wchar_t winamp_name_utf16[260] = {0};
+ StringCbPrintfW(winamp_name_utf16, sizeof(winamp_name_utf16), L"%s (%s)", user_name, computer_name);
+ WideCharToMultiByteSZ(CP_UTF8, 0, winamp_name_utf16, -1, winamp_name, sizeof(winamp_name), 0, 0);
+ }
+ else
+ StringCbCopyA(winamp_name, sizeof(winamp_name), "Winamp");
+
+ const wchar_t *settings_path = WASABI_API_APP->path_getUserSettingsPath();
+ PathCombineW(inifile, settings_path, L"Plugins\\ml\\pmp_wifi.ini");
+
+ // need to have this initialized before we try to do anything with localization features
+ WASABI_API_START_LANG(plugin.hDllInstance,PmpWifiLangGUID);
+
+ static wchar_t szDescription[256];
+ StringCbPrintfW(szDescription, sizeof(szDescription),
+ WASABI_API_LNGSTRINGW(IDS_NULLSOFT_WIFI_DEVICE_PLUGIN), PLUGIN_VERSION);
+ plugin.description = szDescription;
+
+ if (AGAVE_API_DEVICEMANAGER)
+ {
+ ifc_devicecommand *command = &attach_command;
+ AGAVE_API_DEVICEMANAGER->CommandRegister(&command, 1);
+
+ ifc_deviceconnection *connection = &wifi_connection;
+ AGAVE_API_DEVICEMANAGER->ConnectionRegister(&connection, 1);
+
+
+ AGAVE_API_DEVICEMANAGER->CreateSupportedCommandStore(&command_store);
+ command_store->Add("attach", DeviceCommandFlag_Primary);
+
+ AGAVE_API_DEVICEMANAGER->CreateDeviceEventManager(&device_event_manager);
+ }
+ //AGAVE_API_DEVICEMANAGER->CreateSupportedCommandEnum(&command, 1, &command_enum);
+ /* TODO: Use this if your device shows up as a normal drive
+ SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(WPARAM)autoDetectCallback,PMP_IPC_ENUM_ACTIVE_DRIVES);
+ */
+ StartListenServer();
+ return 0;
+}
+
+static void Quit()
+{
+ StopListenServer();
+ WasabiQuit();
+}
+
+
+
+static intptr_t MessageProc(int msg, intptr_t param1, intptr_t param2, intptr_t param3)
+{
+ switch(msg) {
+ case PMP_DEVICECHANGE:
+ // TODO: Implement
+ return 0;
+ case PMP_NO_CONFIG:
+ return TRUE;
+ case PMP_CONFIG:
+ // TODO: Implement (Egg: changed from 1 to 0, for now)
+ return 0;
+ }
+ return 0;
+}
+
+extern "C" __declspec(dllexport) PMPDevicePlugin *winampGetPMPDevicePlugin()
+{
+ return &plugin;
+} \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/main.h b/Src/Plugins/Portable/pmp_wifi/main.h
new file mode 100644
index 00000000..b571a777
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/main.h
@@ -0,0 +1,30 @@
+#pragma once
+#include "../../Library/ml_pmp/pmp.h"
+#include "../Winamp/wa_ipc.h"
+#include <bfc/platform/types.h>
+#include "modelInfo.h"
+extern PMPDevicePlugin plugin;
+extern int winampVersion;
+extern GUID winamp_id;
+extern char winamp_id_str[40];
+extern char winamp_name[260];
+extern wchar_t inifile[MAX_PATH];
+void StopListenServer();
+BOOL FormatResProtocol(const wchar_t *resourceName, const wchar_t *resourceType, wchar_t *buffer, size_t bufferMax);
+
+
+// result from <device> XML data structure
+struct DeviceInfo
+{
+ uint64_t total_space, used_space;
+ uint64_t id;
+
+ wchar_t manufacturer[128];
+ wchar_t model[128];
+ wchar_t name[128];
+ wchar_t product[128];
+ const ModelInfo *modelInfo;
+};
+
+extern "C" void DeviceInfo_Init(DeviceInfo *device_info);
+extern "C" void DeviceInfo_Copy(DeviceInfo *dest, const DeviceInfo *source); \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/modelInfo.cpp b/Src/Plugins/Portable/pmp_wifi/modelInfo.cpp
new file mode 100644
index 00000000..72720c5c
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/modelInfo.cpp
@@ -0,0 +1,168 @@
+#include "main.h"
+#include "./modelInfo.h"
+#include "./api.h"
+#include "./resource.h"
+
+#include <strsafe.h>
+
+typedef struct ManufacturerInfo
+{
+ const wchar_t *name;
+ const ModelInfo *records;
+ size_t count;
+} ManufacturerInfo;
+
+#define MANUFACTURER(_name, _model_table)\
+{ (_name), (_model_table), ARRAYSIZE(_model_table) }
+
+#define MODEL(_name, _title_res_id, _small_icon_res_id, _large_icon_res_id)\
+{ (_name), MAKEINTRESOURCE(_title_res_id), MAKEINTRESOURCE(_small_icon_res_id), MAKEINTRESOURCE(_large_icon_res_id)}
+
+
+const struct ModelInfo HtcModelTable[] =
+{
+ MODEL(L"PC36100", IDS_DEVICE_MODEL_HTC_EVO, IDB_EVO_16, IDB_EVO_160),
+ MODEL(L"ADR6300", IDS_DEVICE_MODEL_HTC_INCREDIBLE, IDB_INCREDIBLE_16, IDB_INCREDIBLE_160),
+ MODEL(L"Nexus One", IDS_DEVICE_MODEL_HTC_NEXUS_ONE, IDB_NEXUSONE_16, IDB_NEXUSONE_160),
+ MODEL(L"Desire", IDS_DEVICE_MODEL_HTC_DESIRE, IDB_HTC_DESIRE_16, IDB_HTC_DESIRE_160),
+ MODEL(L"Wildfire", IDS_DEVICE_MODEL_HTC_WILDFIRE, 0, 0),
+ MODEL(L"Hero", IDS_DEVICE_MODEL_HTC_HERO, 0, 0),
+};
+
+const struct ModelInfo MotorolaModelTable[] =
+{
+ MODEL(L"Droid", IDS_DEVICE_MODEL_MOTOROLA_DROID, IDB_DROID_16, IDB_DROID_160),
+ MODEL(L"DROID2", IDS_DEVICE_MODEL_MOTOROLA_DROID2, IDB_DROID_16, IDB_DROID_160),
+ MODEL(L"DROIDX", IDS_DEVICE_MODEL_MOTOROLA_DROIDX, IDB_DROIDX_16, IDB_DROIDX_160),
+ MODEL(L"Milestone", IDS_DEVICE_MODEL_MOTOROLA_MILESTONE, IDB_DROID_16, IDB_DROID_160),
+ MODEL(L"DROIDX2", IDS_DEVICE_MODEL_MOTOROLA_DROIDX2, IDB_DROID_16, IDB_DROID_160),
+};
+
+const struct ModelInfo SamsungModelTable[] =
+{
+ MODEL(L"GT I9000", IDS_DEVICE_MODEL_SAMSUNG_GALAXY_S, 0, 0),
+};
+
+const static ManufacturerInfo ManufacturerTable[] =
+{
+ MANUFACTURER(L"HTC", HtcModelTable),
+ MANUFACTURER(L"motorola", MotorolaModelTable),
+ MANUFACTURER(L"samsung", SamsungModelTable),
+};
+
+const static ModelInfo defaultModel = { L"", L"", MAKEINTRESOURCE(IDB_GENERIC_16), MAKEINTRESOURCE(IDB_GENERIC_160)};
+
+const ModelInfo *
+FindModelInfo(const wchar_t *manufacturer, const wchar_t *model, BOOL allowDefault)
+{
+ if (NULL != manufacturer &&
+ NULL != model)
+ {
+ const ManufacturerInfo *manufacturerInfo;
+ size_t i;
+ unsigned long lcid;
+
+ lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+
+ for (i = 0; i < ARRAYSIZE(ManufacturerTable); i++)
+ {
+ manufacturerInfo = &ManufacturerTable[i];
+ if (CSTR_EQUAL == CompareString(lcid, NORM_IGNORECASE, manufacturerInfo->name, -1, manufacturer, -1))
+ {
+ for (i = 0; i < manufacturerInfo->count; i++)
+ {
+ const ModelInfo *modelInfo = &manufacturerInfo->records[i];
+ if (CSTR_EQUAL == CompareString(lcid, NORM_IGNORECASE, modelInfo->name, -1, model, -1))
+ {
+ return modelInfo;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if (FALSE != allowDefault)
+ return &defaultModel;
+
+ return NULL;
+}
+
+const ModelInfo*
+GetDefaultModelInfo()
+{
+ return &defaultModel;
+}
+
+HRESULT ModelInfo_CopyDisplayName(const ModelInfo *modelInfo, wchar_t *buffer, size_t bufferMax)
+{
+ if (NULL == modelInfo)
+ return E_INVALIDARG;
+
+ if (NULL == buffer)
+ return E_POINTER;
+
+ if (NULL == modelInfo->displayName)
+ return E_UNEXPECTED;
+
+
+ if (FALSE != IS_INTRESOURCE(modelInfo->displayName))
+ {
+ WASABI_API_LNGSTRINGW_BUF((int)(intptr_t)modelInfo->displayName, buffer, bufferMax);
+ return S_OK;
+ }
+
+ if (FAILED(StringCchCopy(buffer, bufferMax, modelInfo->displayName)))
+ return E_FAIL;
+
+ return S_OK;
+}
+
+const wchar_t *ModelInfo_GetIconName(const ModelInfo *modelInfo, int width, int height, BOOL allowDefault)
+{
+ if (NULL == modelInfo)
+ {
+ if (FALSE == allowDefault)
+ return NULL;
+
+ modelInfo = GetDefaultModelInfo();
+ if (NULL == modelInfo)
+ return NULL;
+ }
+
+ if (width <= 16 && height <= 16)
+ {
+ if (NULL == modelInfo->smallIcon &&
+ FALSE != allowDefault)
+ {
+ modelInfo = GetDefaultModelInfo();
+ if (NULL == modelInfo)
+ return NULL;
+ }
+ return modelInfo->smallIcon;
+ }
+
+ if (NULL == modelInfo->largeIcon &&
+ FALSE != allowDefault)
+ {
+ modelInfo = GetDefaultModelInfo();
+ if (NULL == modelInfo)
+ return NULL;
+ }
+
+ return modelInfo->largeIcon;
+}
+
+HRESULT ModelInfo_GetIconPath(const ModelInfo *modelInfo, int width, int height, wchar_t *buffer, size_t bufferMax, BOOL allowDefault)
+{
+ const wchar_t *iconName;
+
+ iconName = ModelInfo_GetIconName(modelInfo, width, height, allowDefault);
+ if (NULL == iconName)
+ return E_FAIL;
+
+ if (FALSE == FormatResProtocol(iconName, L"PNG", buffer, bufferMax))
+ return E_FAIL;
+
+ return S_OK;
+} \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/modelInfo.h b/Src/Plugins/Portable/pmp_wifi/modelInfo.h
new file mode 100644
index 00000000..6e3d3c53
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/modelInfo.h
@@ -0,0 +1,22 @@
+#pragma once
+
+typedef struct ModelInfo
+{
+ const wchar_t *name;
+ const wchar_t *displayName;
+ const wchar_t *smallIcon;
+ const wchar_t *largeIcon;
+} ModelInfo;
+
+
+const ModelInfo *GetDefaultModelInfo();
+const ModelInfo *FindModelInfo(const wchar_t *manufacturer, const wchar_t *model, BOOL allowDefault);
+
+/* helpers*/
+HRESULT ModelInfo_CopyDisplayName(const ModelInfo *modelInfo, wchar_t *buffer, size_t bufferMax);
+
+const wchar_t *ModelInfo_GetIconName(const ModelInfo *modelInfo, int width, int height, BOOL allowDefault);
+
+HRESULT ModelInfo_GetIconPath(const ModelInfo *modelInfo, int width, int height, wchar_t *buffer, size_t bufferMax, BOOL allowDefault);
+
+
diff --git a/Src/Plugins/Portable/pmp_wifi/pmp_wifi.rc b/Src/Plugins/Portable/pmp_wifi/pmp_wifi.rc
new file mode 100644
index 00000000..36b963e6
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/pmp_wifi.rc
@@ -0,0 +1,126 @@
+// 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
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// PNG
+//
+
+IDB_GENERIC_16 PNG "resources\\generic_drive_wifi_16.png"
+IDB_HTC_DESIRE_16 PNG "resources\\htc_desire_passion_bravo_16.png"
+IDB_EVO_16 PNG "resources\\htc_evo_4g_16.png"
+IDB_NEXUSONE_16 PNG "resources\\htc_nexus_one_16.png"
+IDB_DROID_16 PNG "resources\\motorola_droid_16.png"
+IDB_GENERIC_160 PNG "resources\\generic_android.png"
+IDB_HTC_DESIRE_160 PNG "resources\\htc_desire_passion_bravo.png"
+IDB_EVO_160 PNG "resources\\htc_evo_4g.png"
+IDB_DROID_160 PNG "resources\\motorola_droid.png"
+IDB_WIFI PNG "resources\\wifi.png"
+IDB_ATTACH PNG "resources\\attach.png"
+IDB_ATTACH_16 PNG "resources\\attach16.png"
+IDB_INCREDIBLE_160 PNG "resources\\htc_incredible.png"
+IDB_INCREDIBLE_16 PNG "resources\\htc_incredible_16.png"
+IDB_NEXUSONE_160 PNG "resources\\nexus_one.png"
+IDB_DROIDX_160 PNG "resources\\motorola_droid_x.png"
+IDB_DROIDX_16 PNG "resources\\motorola_droid_x_16.png"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_WIFI_DEVICE_PLUGIN "Nullsoft Wi-Fi Device Plug-in v%s"
+ 65535 "{3066887B-CA40-4683-897F-4416FE349D7E}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_ACTIVITY_CONNECT "Connecting..."
+ IDS_ACTIVITY_CONNECT_DESC "Connecting device."
+ IDS_DEVICE_CONNECTION_WIFI "Wi-Fi"
+ IDS_DEVICE_CMD_ATTACH "&Attach"
+ IDS_DEVICE_CMD_ATTACH_DESC "Attach device to Winamp"
+ IDS_DEVICE_MODEL_HTC_EVO "HTC Evo 4G"
+ IDS_DEVICE_MODEL_HTC_INCREDIBLE "DROID INCREDIBLE by HTC"
+ IDS_DEVICE_MODEL_HTC_NEXUS_ONE "Google Nexus One"
+ IDS_DEVICE_MODEL_MOTOROLA_DROID2 "DROID 2 by Motorola"
+ IDS_DEVICE_MODEL_MOTOROLA_DROIDX "DROID X by Motorola"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_DEVICE_MODEL_MOTOROLA_DROID "DROID by Motorola"
+ IDS_DEVICE_MODEL_HTC_DESIRE "HTC Desire"
+ IDS_DEVICE_MODEL_SAMSUNG_GALAXY_S "Samsung Galaxy S"
+ IDS_DEVICE_MODEL_MOTOROLA_MILESTONE "Motorola Milestone"
+ IDS_DEVICE_MODEL_HTC_WILDFIRE "HTC Wildfire"
+ IDS_DEVICE_MODEL_HTC_HERO "HTC Hero"
+ IDS_CONNECTING "Connecting"
+ IDS_UPLOADING "Uploading (%d%%)"
+ IDS_COMPLETED "Completed"
+ IDS_DEVICE_MODEL_MOTOROLA_DROIDX2 "DROID X2 by Motorola"
+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/Portable/pmp_wifi/pmp_wifi.sln b/Src/Plugins/Portable/pmp_wifi/pmp_wifi.sln
new file mode 100644
index 00000000..965cd264
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/pmp_wifi.sln
@@ -0,0 +1,83 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pmp_wifi", "pmp_wifi.vcxproj", "{DD76E337-88AD-402E-ACC7-E33510A5AE43}"
+ ProjectSection(ProjectDependencies) = postProject
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27} = {57C90706-B25D-4ACA-9B33-95CDB2427C27}
+ {44AEBB50-1331-4F2E-8AEC-56C82DE16C11} = {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}
+ {E105A0A2-7391-47C5-86AC-718003524C3D} = {E105A0A2-7391-47C5-86AC-718003524C3D}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nx", "..\replicant\nx\nx.vcxproj", "{57C90706-B25D-4ACA-9B33-95CDB2427C27}"
+ ProjectSection(ProjectDependencies) = postProject
+ {44AEBB50-1331-4F2E-8AEC-56C82DE16C11} = {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\replicant\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "..\replicant\jnetlib\jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}"
+ ProjectSection(ProjectDependencies) = postProject
+ {44AEBB50-1331-4F2E-8AEC-56C82DE16C11} = {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\replicant\zlib\zlib.vcxproj", "{44AEBB50-1331-4F2E-8AEC-56C82DE16C11}"
+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
+ {DD76E337-88AD-402E-ACC7-E33510A5AE43}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DD76E337-88AD-402E-ACC7-E33510A5AE43}.Debug|Win32.Build.0 = Debug|Win32
+ {DD76E337-88AD-402E-ACC7-E33510A5AE43}.Debug|x64.ActiveCfg = Debug|x64
+ {DD76E337-88AD-402E-ACC7-E33510A5AE43}.Debug|x64.Build.0 = Debug|x64
+ {DD76E337-88AD-402E-ACC7-E33510A5AE43}.Release|Win32.ActiveCfg = Release|Win32
+ {DD76E337-88AD-402E-ACC7-E33510A5AE43}.Release|Win32.Build.0 = Release|Win32
+ {DD76E337-88AD-402E-ACC7-E33510A5AE43}.Release|x64.ActiveCfg = Release|x64
+ {DD76E337-88AD-402E-ACC7-E33510A5AE43}.Release|x64.Build.0 = Release|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.ActiveCfg = Debug|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.Build.0 = Debug|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.ActiveCfg = Debug|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.Build.0 = Debug|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.ActiveCfg = Release|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.Build.0 = Release|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.ActiveCfg = Release|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.Build.0 = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64
+ {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|Win32.ActiveCfg = Debug|Win32
+ {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|Win32.Build.0 = Debug|Win32
+ {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|x64.ActiveCfg = Debug|x64
+ {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|x64.Build.0 = Debug|x64
+ {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|Win32.ActiveCfg = Release|Win32
+ {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|Win32.Build.0 = Release|Win32
+ {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|x64.ActiveCfg = Release|x64
+ {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C4E799C1-027B-487B-8E1A-31F2D26A1AFE}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Portable/pmp_wifi/pmp_wifi.vcxproj b/Src/Plugins/Portable/pmp_wifi/pmp_wifi.vcxproj
new file mode 100644
index 00000000..a85823eb
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/pmp_wifi.vcxproj
@@ -0,0 +1,341 @@
+<?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>{DD76E337-88AD-402E-ACC7-E33510A5AE43}</ProjectGuid>
+ <RootNamespace>pmp_wifi</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" 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>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|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>
+ <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>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;pmp_wifi_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>ws2_32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;pmp_wifi_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>ws2_32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;pmp_wifi_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4018;4995;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>ws2_32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <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>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;pmp_wifi_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4018;4995;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>ws2_32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <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>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\replicant\jnetlib\asyncdns.cpp" />
+ <ClCompile Include="..\..\..\replicant\jnetlib\multicastlisten.cpp" />
+ <ClCompile Include="..\..\..\replicant\jnetlib\udpconnection.cpp" />
+ <ClCompile Include="..\..\..\replicant\jnetlib\util.cpp" />
+ <ClCompile Include="..\..\..\replicant\nu\RingBuffer.cpp" />
+ <ClCompile Include="api.cpp" />
+ <ClCompile Include="ConnectActivity.cpp" />
+ <ClCompile Include="device.cpp" />
+ <ClCompile Include="images.cpp" />
+ <ClCompile Include="InfoDownloader.cpp" />
+ <ClCompile Include="ListenServer.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="modelInfo.cpp" />
+ <ClCompile Include="Pair.cpp" />
+ <ClCompile Include="PlaylistSync.cpp" />
+ <ClCompile Include="post.cpp" />
+ <ClCompile Include="RenameDownloader.cpp" />
+ <ClCompile Include="SongDownloader.cpp" />
+ <ClCompile Include="SongListDownloader.cpp" />
+ <ClCompile Include="WifiDevice.cpp" />
+ <ClCompile Include="WifiPlaylist.cpp" />
+ <ClCompile Include="XMLString.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\replicant\jnetlib\asyncdns.h" />
+ <ClInclude Include="..\..\..\replicant\jnetlib\multicastlisten.h" />
+ <ClInclude Include="..\..\..\replicant\jnetlib\udpconnection.h" />
+ <ClInclude Include="..\..\..\replicant\jnetlib\util.h" />
+ <ClInclude Include="..\..\Library\ml_pmp\pmp.h" />
+ <ClInclude Include="..\..\..\replicant\nu\RingBuffer.h" />
+ <ClInclude Include="api.h" />
+ <ClInclude Include="ConnectActivity.h" />
+ <ClInclude Include="device.h" />
+ <ClInclude Include="InfoDownloader.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="modelInfo.h" />
+ <ClInclude Include="Pair.h" />
+ <ClInclude Include="PlaylistSync.h" />
+ <ClInclude Include="RenameDownloader.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="SongDownloader.h" />
+ <ClInclude Include="SongListDownloader.h" />
+ <ClInclude Include="WifiDevice.h" />
+ <ClInclude Include="WifiPlaylist.h" />
+ <ClInclude Include="XMLString.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="attach.png" />
+ <Image Include="attach16.png" />
+ <Image Include="generic_android.png" />
+ <Image Include="generic_drive_wifi_16.png" />
+ <Image Include="htc_desire_passion_bravo.png" />
+ <Image Include="htc_desire_passion_bravo_16.png" />
+ <Image Include="htc_evo_4g.png" />
+ <Image Include="htc_evo_4g_16.png" />
+ <Image Include="htc_hd2.png" />
+ <Image Include="htc_hd2_16.png" />
+ <Image Include="htc_incredible.png" />
+ <Image Include="htc_incredible_16.png" />
+ <Image Include="htc_nexus_one_16.png" />
+ <Image Include="motorola_droid.png" />
+ <Image Include="motorola_droid_16.png" />
+ <Image Include="motorola_droid_x.png" />
+ <Image Include="motorola_droid_x_16.png" />
+ <Image Include="nexus_one.png" />
+ <Image Include="wifi.png" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="pmp_wifi.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\General\gen_ml\gen_ml.vcxproj">
+ <Project>{9b212232-4908-49d2-8d6d-96555b1f701e}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Library\ml_pmp\ml_pmp.vcxproj">
+ <Project>{c524f141-87ca-491b-91d8-920248c9066e}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\replicant\nu\nu.vcxproj">
+ <Project>{f1f5cd60-0d5b-4cea-9eeb-2f87ff9aa915}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\replicant\nx\nx.vcxproj">
+ <Project>{57c90706-b25d-4aca-9b33-95cdb2427c27}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\Wasabi\bfc\bfc.vcxproj">
+ <Project>{d0ec862e-dddd-4f4f-934f-b75dc9062dc1}</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/Portable/pmp_wifi/pmp_wifi.vcxproj.filters b/Src/Plugins/Portable/pmp_wifi/pmp_wifi.vcxproj.filters
new file mode 100644
index 00000000..ebb2023c
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/pmp_wifi.vcxproj.filters
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="api.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ConnectActivity.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="device.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="images.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="InfoDownloader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ListenServer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="modelInfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Pair.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="XMLString.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WifiPlaylist.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WifiDevice.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SongListDownloader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SongDownloader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RenameDownloader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="post.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PlaylistSync.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\replicant\jnetlib\asyncdns.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\replicant\jnetlib\multicastlisten.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\replicant\nu\RingBuffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\replicant\jnetlib\udpconnection.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\replicant\jnetlib\util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ConnectActivity.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="device.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="InfoDownloader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="modelInfo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Pair.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="PlaylistSync.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RenameDownloader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SongDownloader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SongListDownloader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WifiDevice.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WifiPlaylist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="XMLString.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\replicant\jnetlib\asyncdns.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\replicant\jnetlib\multicastlisten.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Library\ml_pmp\pmp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\replicant\nu\RingBuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\replicant\jnetlib\udpconnection.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\replicant\jnetlib\util.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="attach.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="attach16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="generic_android.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="generic_drive_wifi_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="htc_desire_passion_bravo.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="htc_desire_passion_bravo_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="htc_evo_4g.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="htc_evo_4g_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="htc_hd2.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="htc_hd2_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="htc_incredible.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="htc_incredible_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="htc_nexus_one_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="motorola_droid.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="motorola_droid_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="motorola_droid_x.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="motorola_droid_x_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="nexus_one.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="wifi.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{0b5eaaf3-d36a-4b37-b20f-50ebc6c35a6f}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{2f4cb2e1-34f6-44e9-bb88-ff40ce0f6e61}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{92dbab76-3096-4992-8074-7eabc2673267}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Image Files">
+ <UniqueIdentifier>{075a31ea-0a24-40e9-b4da-cc17b11a8459}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="pmp_wifi.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_wifi/post.cpp b/Src/Plugins/Portable/pmp_wifi/post.cpp
new file mode 100644
index 00000000..3eb93dc2
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/post.cpp
@@ -0,0 +1,518 @@
+#include "main.h"
+#include "api.h"
+#include "resource.h"
+#include "../xml/obj_xml.h"
+#include "nu/AutoChar.h"
+#include "../nu/AutoUrl.h"
+#include "../nu/AutoHeader.h"
+#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
+#include "../agave/albumart/svc_albumartprovider.h"
+#include <api/service/waservicefactory.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+
+
+static const GUID internetConfigGroupGUID =
+{
+ 0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c }
+};
+
+
+#define USER_AGENT_SIZE (10 /*User-Agent*/ + 2 /*: */ + 6 /*Winamp*/ + 1 /*/*/ + 1 /*5*/ + 3/*.21*/ + 1 /*Null*/)
+static void SetUserAgent(api_httpreceiver *http)
+{
+
+ char user_agent[USER_AGENT_SIZE] = {0};
+ int bigVer = ((winampVersion & 0x0000FF00) >> 12);
+ int smallVer = ((winampVersion & 0x000000FF));
+ StringCchPrintfA(user_agent, USER_AGENT_SIZE, "User-Agent: Winamp/%01x.%02x", bigVer, smallVer);
+ http->addheader(user_agent);
+}
+
+
+#define HTTP_BUFFER_SIZE 8192
+#define POST_BUFFER_SIZE (128*1024)
+
+
+int PostFile(const char *base_url, const wchar_t *filename, const itemRecordW *track, obj_xml *parser, int *killswitch,
+ void (*callback)(void *callbackContext, wchar_t *status), void *context, char *new_item_id, size_t new_item_id_len)
+{
+ //if (!parser)
+// return 1;
+ bool first=true;
+ char url[2048] = {0};
+ char *p_url=url;
+ size_t url_cch=sizeof(url)/sizeof(*url);
+ FILE *f = _wfopen(filename, L"rb");
+ if (!f)
+ return 1;
+ api_httpreceiver *http = 0;
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
+ if (sf)
+ http = (api_httpreceiver *)sf->getInterface();
+
+ if (!http)
+ return 1;
+
+ int use_proxy = 1;
+ bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
+ if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3)))
+ use_proxy = 0;
+
+ const wchar_t *proxy = use_proxy?AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0):0;
+
+ fseek(f, 0, SEEK_END);
+
+ size_t clen = ftell(f);
+ size_t transferred=0;
+ size_t total_clen = clen;
+ fseek(f, 0, SEEK_SET);
+
+ http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, (proxy && proxy[0]) ? (const char *)AutoChar(proxy) : NULL);
+ http->set_sendbufsize(POST_BUFFER_SIZE);
+ SetUserAgent(http);
+
+ char data[POST_BUFFER_SIZE] = {0};
+
+ StringCbCopyExA(p_url, url_cch, base_url, &p_url, &url_cch, 0);
+
+ StringCbPrintfA(data, sizeof(data), "Content-Length: %u", clen);
+ http->addheader(data);
+// http->addheader("Content-Type: application/octet-stream");
+
+ /* send metadata */
+ if (track->artist && track->artist[0])
+ {
+ if (first)
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?artist=%s", AutoUrl(track->artist));
+ else
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&artist=%s", AutoUrl(track->artist));
+ first=false;
+ }
+
+ if (track->title && track->title[0])
+ {
+ if (first)
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?title=%s", AutoUrl(track->title));
+ else
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&title=%s", AutoUrl(track->title));
+ first=false;
+ }
+
+ if (track->album && track->album[0])
+ {
+ if (first)
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?album=%s", AutoUrl(track->album));
+ else
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&album=%s", AutoUrl(track->album));
+ first=false;
+ }
+
+ if (track->composer && track->composer[0])
+ {
+ if (first)
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?composer=%s", AutoUrl(track->composer));
+ else
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&composer=%s", AutoUrl(track->composer));
+ first=false;
+ }
+
+ if (track->albumartist && track->albumartist[0])
+ {
+ if (first)
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?albumartist=%s", AutoUrl(track->albumartist));
+ else
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&albumartist=%s", AutoUrl(track->albumartist));
+ first=false;
+ }
+
+ if (track->genre && track->genre[0])
+ {
+ if (first)
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?genre=%s", AutoUrl(track->genre));
+ else
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&genre=%s", AutoUrl(track->genre));
+ first=false;
+ }
+
+ if (track->track > 0)
+ {
+ if (first)
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?track=%d", track->track);
+ else
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&track=%d", track->track);
+ first=false;
+ }
+
+ const wchar_t *ext = PathFindExtension(filename);
+ if (ext && ext[0])
+ {
+ if (ext[0] == '.') ext++;
+ if (ext[0])
+ {
+ if (first)
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?extension=%s", AutoUrl(ext));
+ else
+ StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&extension=%s", AutoUrl(ext));
+ first=false;
+ }
+ }
+
+
+ wchar_t mime_type[128] = {0};
+ if (AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"mime", mime_type, 128) == 1 && mime_type[0])
+ {
+ http->AddHeaderValue("Content-Type", AutoHeader(mime_type));
+ }
+
+ http->AddHeaderValue("X-Winamp-ID", winamp_id_str);
+ http->AddHeaderValue("X-Winamp-Name", winamp_name);
+
+ http->AddHeaderValue("Expect", "100-continue");
+ /* connect */
+ callback(context, WASABI_API_LNGSTRINGW(IDS_CONNECTING));
+ http->connect(url, 0, "POST");
+
+ // spin and wait for a 100 response
+ for (;;)
+ {
+ Sleep(55);
+ if (*killswitch)
+ goto connection_failed;
+ int ret = http->run();
+ if (ret != HTTPRECEIVER_RUN_OK) // connection failed or closed
+ goto connection_failed;
+
+ int reply_code = http->getreplycode();
+ if (reply_code == 100)
+ break;
+ else if (reply_code)
+ goto connection_failed;
+ }
+
+
+ /* POST the data */
+ api_connection *connection = http->GetConnection();
+ if (connection)
+ {
+ if (http->run() == -1)
+ goto connection_failed;
+
+ while (clen)
+ {
+ int percent = MulDiv(100, (int)transferred, (int)total_clen);
+ wchar_t msg[128] = {0};
+ StringCbPrintfW(msg, sizeof(msg), WASABI_API_LNGSTRINGW(IDS_UPLOADING), percent);
+ callback(context, msg);
+ if (*killswitch)
+ goto connection_failed;
+ if (http->run() == -1)
+ goto connection_failed;
+
+ int connection_state = connection->get_state();
+ if (connection_state == CONNECTION_STATE_CLOSED || connection_state == CONNECTION_STATE_ERROR)
+ goto connection_failed;
+
+ size_t lengthToSend = min(clen, connection->GetSendBytesAvailable());
+ lengthToSend = min(lengthToSend, sizeof(data));
+
+ if (lengthToSend)
+ {
+ int bytes_read = (int)fread(data, 1, lengthToSend, f);
+ connection->send(data, bytes_read);
+ clen-=bytes_read;
+ transferred+=bytes_read;
+ }
+ else
+ {
+ Sleep(10);
+ }
+ }
+ int x;
+ while (x = (int)connection->GetSendBytesInQueue())
+ {
+ int connection_state = connection->get_state();
+ if (connection_state == CONNECTION_STATE_CLOSED || connection_state == CONNECTION_STATE_ERROR)
+ goto connection_failed;
+ Sleep(10);
+ if (*killswitch)
+ goto connection_failed;
+ if (http->run() == -1)
+ goto connection_failed;
+
+ }
+ }
+ fclose(f);
+ f=0;
+
+ /* retrieve reply */
+ int ret;
+ do
+ {
+ Sleep(55);
+ ret = http->run();
+ if (ret == -1) // connection failed
+ break;
+
+ // ---- check our reply code ----
+ int status = http->get_status();
+ switch (status)
+ {
+ case HTTPRECEIVER_STATUS_CONNECTING:
+ case HTTPRECEIVER_STATUS_READING_HEADERS:
+ break;
+
+ case HTTPRECEIVER_STATUS_READING_CONTENT:
+ {
+ const char *location = http->getheader("Location");
+ if (location)
+ StringCchCopyA(new_item_id, new_item_id_len, location);
+ else
+ new_item_id[0]=0;
+
+ sf->releaseInterface(http);
+ return 0;
+ }
+ break;
+ case HTTPRECEIVER_STATUS_ERROR:
+ default:
+ sf->releaseInterface(http);
+ return 1;
+ }
+ }
+ while (ret == HTTPRECEIVER_RUN_OK);
+
+
+connection_failed:
+ if (f)
+ fclose(f);
+ sf->releaseInterface(http);
+ return 1;
+}
+
+
+
+int PostAlbumArt(const char *url, const itemRecordW *track, obj_xml *parser, int *killswitch, void (*callback)(void *callbackContext, wchar_t *status), void *context)
+{
+
+ void *artData=0;
+ size_t datalen=0;
+ wchar_t *mimeType=0;
+ if (AGAVE_API_ALBUMART->GetAlbumArtData(track->filename, L"cover", &artData, &datalen, &mimeType) != ALBUMART_SUCCESS)
+ return 1;
+
+ api_httpreceiver *http = 0;
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
+ if (sf)
+ http = (api_httpreceiver *)sf->getInterface();
+
+ if (!http)
+ return 1;
+
+ int use_proxy = 1;
+ bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
+ if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3)))
+ use_proxy = 0;
+
+ const wchar_t *proxy = use_proxy?AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0):0;
+
+ uint8_t *artDataPtr=(uint8_t *)artData;
+ size_t clen = datalen;
+ size_t transferred=0;
+ size_t total_clen = datalen;
+
+ http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, (proxy && proxy[0]) ? (const char *)AutoChar(proxy) : NULL);
+ http->set_sendbufsize(POST_BUFFER_SIZE);
+ SetUserAgent(http);
+
+ char data[POST_BUFFER_SIZE] = {0};
+
+ StringCbPrintfA(data, sizeof(data), "Content-Length: %u", datalen);
+ http->addheader(data);
+ if (mimeType)
+ {
+ StringCbPrintfA(data, sizeof(data), "Content-Type: %s", AutoHeader(mimeType));
+ http->addheader(data);
+ }
+
+ http->AddHeaderValue("X-Winamp-ID", winamp_id_str);
+ http->AddHeaderValue("X-Winamp-Name", winamp_name);
+
+ /* connect */
+ http->AddHeaderValue("Expect", "100-continue");
+ callback(context, WASABI_API_LNGSTRINGW(IDS_CONNECTING));
+ http->connect(url, 0, "POST");
+
+ // spin and wait for a 100 response
+ for (;;)
+ {
+ Sleep(55);
+ int ret = http->run();
+ if (ret != HTTPRECEIVER_RUN_OK) // connection failed or closed
+ goto connection_failed;
+
+ if (*killswitch)
+ goto connection_failed;
+ int reply_code = http->getreplycode();
+ if (reply_code == 100)
+ break;
+ else if (reply_code)
+ goto connection_failed;
+ }
+
+ /* POST the data */
+ api_connection *connection = http->GetConnection();
+ if (connection)
+ {
+ if (http->run() == -1)
+ goto connection_failed;
+
+ while (clen)
+ {
+ int percent = MulDiv(100, (int)transferred, (int)total_clen);
+ wchar_t msg[128] = {0};
+ StringCbPrintfW(msg, sizeof(msg), L"Uploading Album Art (%d%%)", percent);
+ callback(context, msg);
+ if (*killswitch)
+ goto connection_failed;
+ if (http->run() == -1)
+ goto connection_failed;
+
+ int connection_state = connection->get_state();
+ if (connection_state == CONNECTION_STATE_CLOSED || connection_state == CONNECTION_STATE_ERROR)
+ goto connection_failed;
+
+ size_t lengthToSend = min(clen, connection->GetSendBytesAvailable());
+
+ if (lengthToSend)
+ {
+ connection->send(artDataPtr, (int)lengthToSend);
+ artDataPtr += lengthToSend;
+ clen-=lengthToSend;
+ transferred+=lengthToSend;
+ }
+ else
+ {
+ Sleep(10);
+ }
+ }
+ int x;
+ while (x = (int)connection->GetSendBytesInQueue())
+ {
+ int connection_state = connection->get_state();
+ if (connection_state == CONNECTION_STATE_CLOSED || connection_state == CONNECTION_STATE_ERROR)
+ goto connection_failed;
+ Sleep(10);
+ if (*killswitch)
+ goto connection_failed;
+ if (http->run() == -1)
+ goto connection_failed;
+
+ }
+ }
+
+ /* retrieve reply */
+ int ret;
+ do
+ {
+ Sleep(55);
+ ret = http->run();
+ if (ret == -1) // connection failed
+ break;
+
+ // ---- check our reply code ----
+ int status = http->get_status();
+ switch (status)
+ {
+ case HTTPRECEIVER_STATUS_CONNECTING:
+ case HTTPRECEIVER_STATUS_READING_HEADERS:
+ break;
+
+ case HTTPRECEIVER_STATUS_READING_CONTENT:
+ {
+ sf->releaseInterface(http);
+ WASABI_API_MEMMGR->sysFree(artData);
+ WASABI_API_MEMMGR->sysFree(mimeType);
+ return 0;
+ }
+ break;
+ case HTTPRECEIVER_STATUS_ERROR:
+ default:
+ sf->releaseInterface(http);
+ WASABI_API_MEMMGR->sysFree(artData);
+ WASABI_API_MEMMGR->sysFree(mimeType);
+ return 1;
+ }
+ }
+ while (ret == HTTPRECEIVER_RUN_OK);
+
+
+connection_failed:
+ WASABI_API_MEMMGR->sysFree(artData);
+ WASABI_API_MEMMGR->sysFree(mimeType);
+ sf->releaseInterface(http);
+ return 1;
+}
+
+
+int HTTP_Delete(const char *url)
+{
+ api_httpreceiver *http = 0;
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
+ if (sf)
+ http = (api_httpreceiver *)sf->getInterface();
+
+ if (!http)
+ return 1;
+
+ int use_proxy = 1;
+ bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
+ if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3)))
+ use_proxy = 0;
+
+ const wchar_t *proxy = use_proxy?AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0):0;
+
+ http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, (proxy && proxy[0]) ? (const char *)AutoChar(proxy) : NULL);
+ SetUserAgent(http);
+
+ http->AddHeaderValue("X-Winamp-ID", winamp_id_str);
+ http->AddHeaderValue("X-Winamp-Name", winamp_name);
+
+ /* connect */
+ http->connect(url, 0, "DELETE");
+
+ /* retrieve reply */
+ int ret;
+ do
+ {
+ Sleep(55);
+ ret = http->run();
+ if (ret == -1) // connection failed
+ break;
+
+ // ---- check our reply code ----
+ int status = http->get_status();
+ switch (status)
+ {
+ case HTTPRECEIVER_STATUS_CONNECTING:
+ case HTTPRECEIVER_STATUS_READING_HEADERS:
+ break;
+
+ case HTTPRECEIVER_STATUS_READING_CONTENT:
+ {
+ sf->releaseInterface(http);
+ return 0;
+ }
+ break;
+ case HTTPRECEIVER_STATUS_ERROR:
+ default:
+ sf->releaseInterface(http);
+ return 1;
+ }
+ }
+ while (ret == HTTPRECEIVER_RUN_OK);
+
+ sf->releaseInterface(http);
+ return 1;
+}
+
diff --git a/Src/Plugins/Portable/pmp_wifi/resource.h b/Src/Plugins/Portable/pmp_wifi/resource.h
new file mode 100644
index 00000000..dadd58d0
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resource.h
@@ -0,0 +1,65 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by pmp_wifi.rc
+//
+#define IDB_GENERIC_16 101
+#define IDB_PNG2 102
+#define IDB_HTC_DESIRE_16 102
+#define IDB_PNG3 103
+#define IDB_EVO16 103
+#define IDB_EVO_16 103
+#define IDB_PNG5 105
+#define IDB_NEXUSONE_16 105
+#define IDB_PNG6 106
+#define IDB_DROID_16 106
+#define IDB_GENERIC_160 107
+#define IDB_PNG7 108
+#define IDB_HTC_DESIRE_160 108
+#define IDB_PNG8 109
+#define IDB_EVO_160 109
+#define IDB_PNG10 111
+#define IDB_DROID_160 111
+#define IDB_PNG1 112
+#define IDB_WIFI 112
+#define IDB_ATTACH 113
+#define IDB_PNG11 114
+#define IDB_ATTACH_16 114
+#define IDB_PNG4 115
+#define IDB_INCREDIBLE_160 115
+#define IDB_INCREDIBLE_16 116
+#define IDB_NEXUSONE_160 117
+#define IDS_ACTIVITY_CONNECT 118
+#define IDS_ACTIVITY_CONNECT_DESC 119
+#define IDS_DEVICE_CONNECTION_WIFI 120
+#define IDS_DEVICE_CMD_ATTACH 121
+#define IDS_DEVICE_CMD_ATTACH_DESC 122
+#define IDS_DEVICE_MODEL_HTC_EVO 123
+#define IDS_DEVICE_MODEL_HTC_INCREDIBLE 124
+#define IDS_DEVICE_MODEL_HTC_NEXUS_ONE 125
+#define IDS_DEVICE_MODEL_MOTOROLA_DROID2 126
+#define IDS_DEVICE_MODEL_MOTOROLA_DROIDX 127
+#define IDS_DEVICE_MODEL_MOTOROLA_DROID 128
+#define IDS_DEVICE_MODEL_HTC_DESIRE 129
+#define IDS_DEVICE_MODEL_SAMSUNG_GALAXY_S 130
+#define IDB_DROIDX_160 130
+#define IDS_DEVICE_MODEL_MOTOROLA_MILESTONE 131
+#define IDB_PNG12 131
+#define IDB_DROIDX_16 131
+#define IDS_DEVICE_MODEL_HTC_WILDFIRE 132
+#define IDS_DEVICE_MODEL_HTC_HERO 133
+#define IDS_CONNECTING 134
+#define IDS_UPLOADING 135
+#define IDS_COMPLETED 136
+#define IDS_DEVICE_MODEL_MOTOROLA_DROIDX2 137
+#define IDS_NULLSOFT_WIFI_DEVICE_PLUGIN 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 139
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/attach.png b/Src/Plugins/Portable/pmp_wifi/resources/attach.png
new file mode 100644
index 00000000..bb645908
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/attach.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/attach16.png b/Src/Plugins/Portable/pmp_wifi/resources/attach16.png
new file mode 100644
index 00000000..64f2e081
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/attach16.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/generic_android.png b/Src/Plugins/Portable/pmp_wifi/resources/generic_android.png
new file mode 100644
index 00000000..bafdb3ce
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/generic_android.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/generic_drive_wifi_16.png b/Src/Plugins/Portable/pmp_wifi/resources/generic_drive_wifi_16.png
new file mode 100644
index 00000000..68f46c81
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/generic_drive_wifi_16.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo.png
new file mode 100644
index 00000000..6125b39a
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo_16.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo_16.png
new file mode 100644
index 00000000..58ed7340
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo_16.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g.png
new file mode 100644
index 00000000..511c24fe
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g_16.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g_16.png
new file mode 100644
index 00000000..d3f5c451
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g_16.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2.png
new file mode 100644
index 00000000..fd1fb50d
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2_16.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2_16.png
new file mode 100644
index 00000000..3b30603f
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2_16.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible.png
new file mode 100644
index 00000000..dbe2ed94
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible_16.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible_16.png
new file mode 100644
index 00000000..394ada27
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible_16.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_nexus_one_16.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_nexus_one_16.png
new file mode 100644
index 00000000..04728360
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_nexus_one_16.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid.png b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid.png
new file mode 100644
index 00000000..14a90e8c
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_16.png b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_16.png
new file mode 100644
index 00000000..87bbd832
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_16.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x.png b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x.png
new file mode 100644
index 00000000..bc1d71c7
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x_16.png b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x_16.png
new file mode 100644
index 00000000..6febb3d2
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x_16.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/nexus_one.png b/Src/Plugins/Portable/pmp_wifi/resources/nexus_one.png
new file mode 100644
index 00000000..439542c1
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/nexus_one.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/resources/wifi.png b/Src/Plugins/Portable/pmp_wifi/resources/wifi.png
new file mode 100644
index 00000000..682ced55
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/resources/wifi.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_wifi/version.rc2 b/Src/Plugins/Portable/pmp_wifi/version.rc2
new file mode 100644
index 00000000..aaf33d2e
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_wifi/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,56,0,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 Portable Device Plug-in"
+ VALUE "FileVersion", "1,56,0,0"
+ VALUE "InternalName", "Nullsoft Wi-Fi Device"
+ VALUE "LegalCopyright", "Copyright © 2010-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "pmp_wifi.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END