diff options
Diffstat (limited to 'Src/Plugins/Portable/pmp_wifi')
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(®ister_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(®ister_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(®ister_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(®ister_lock); +} + +void WifiDevice::OnDisconnect() +{ + // TODO: might actually need a crit sec here + EnterCriticalSection(®ister_lock); + dead=1; + if (pmp_device) + { + pmp_device->CloseAsync(); + pmp_device = 0; + } + else + { + AGAVE_API_DEVICEMANAGER->DeviceUnregister(id_string); + } + LeaveCriticalSection(®ister_lock); +} + +void WifiDevice::OnConnectionFailed() +{ + EnterCriticalSection(®ister_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(®ister_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 ©) +{ + 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 ©); + ~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 Binary files differnew file mode 100644 index 00000000..bb645908 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/attach.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/attach16.png b/Src/Plugins/Portable/pmp_wifi/resources/attach16.png Binary files differnew file mode 100644 index 00000000..64f2e081 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/attach16.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/generic_android.png b/Src/Plugins/Portable/pmp_wifi/resources/generic_android.png Binary files differnew file mode 100644 index 00000000..bafdb3ce --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/generic_android.png 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 Binary files differnew file mode 100644 index 00000000..68f46c81 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/generic_drive_wifi_16.png 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 Binary files differnew file mode 100644 index 00000000..6125b39a --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo.png 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 Binary files differnew file mode 100644 index 00000000..58ed7340 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo_16.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g.png Binary files differnew file mode 100644 index 00000000..511c24fe --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g.png 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 Binary files differnew file mode 100644 index 00000000..d3f5c451 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g_16.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2.png Binary files differnew file mode 100644 index 00000000..fd1fb50d --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2_16.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2_16.png Binary files differnew file mode 100644 index 00000000..3b30603f --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_hd2_16.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible.png Binary files differnew file mode 100644 index 00000000..dbe2ed94 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible_16.png b/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible_16.png Binary files differnew file mode 100644 index 00000000..394ada27 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_incredible_16.png 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 Binary files differnew file mode 100644 index 00000000..04728360 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/htc_nexus_one_16.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid.png b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid.png Binary files differnew file mode 100644 index 00000000..14a90e8c --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_16.png b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_16.png Binary files differnew file mode 100644 index 00000000..87bbd832 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_16.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x.png b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x.png Binary files differnew file mode 100644 index 00000000..bc1d71c7 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x.png 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 Binary files differnew file mode 100644 index 00000000..6febb3d2 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x_16.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/nexus_one.png b/Src/Plugins/Portable/pmp_wifi/resources/nexus_one.png Binary files differnew file mode 100644 index 00000000..439542c1 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/nexus_one.png diff --git a/Src/Plugins/Portable/pmp_wifi/resources/wifi.png b/Src/Plugins/Portable/pmp_wifi/resources/wifi.png Binary files differnew file mode 100644 index 00000000..682ced55 --- /dev/null +++ b/Src/Plugins/Portable/pmp_wifi/resources/wifi.png 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 |