main.cpp 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454
  1. #define PLUGIN_NAME "Nullsoft Portable Music Player Support"
  2. #define PLUGIN_VERSION L"2.25"
  3. #include "main.h"
  4. #include <windows.h>
  5. #include <windowsx.h>
  6. #include <stdio.h>
  7. #include <shlobj.h>
  8. #include <shlwapi.h>
  9. #include "..\..\General\gen_ml/ml.h"
  10. #include "..\..\General\gen_ml/itemlist.h"
  11. #include "../winamp/wa_ipc.h"
  12. #include "nu/ns_wc.h"
  13. #include "..\..\General\gen_hotkeys/wa_hotkeys.h"
  14. #include "resource1.h"
  15. #include "pmp.h"
  16. #include "DeviceView.h"
  17. #include "pluginloader.h"
  18. #include "nu/AutoWide.h"
  19. #include "api__ml_pmp.h"
  20. #include "transcoder_imp.h"
  21. #include <api/service/waservicefactory.h>
  22. #include "config.h"
  23. #include "tataki/export.h"
  24. #include "nu/ServiceWatcher.h"
  25. #include "..\..\General\gen_ml/ml_ipc_0313.h"
  26. #include "mt19937ar.h"
  27. #include "./local_menu.h"
  28. #include "pmpdevice.h"
  29. #include "IconStore.h"
  30. #include "../replicant/nx/nxstring.h"
  31. #include <strsafe.h>
  32. #include "../nu/MediaLibraryInterface.h"
  33. #include <vector>
  34. #define MAINTREEID 5002
  35. #define PROGRESSTIMERID 1
  36. static int init();
  37. static void quit();
  38. static INT_PTR PluginMessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3);
  39. LRESULT CALLBACK DeviceMsgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  40. extern INT_PTR CALLBACK pmp_devices_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
  41. extern void UpdateDevicesListView(bool softupdate);
  42. INT_PTR CALLBACK global_config_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
  43. INT_PTR CALLBACK config_dlgproc_plugins(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
  44. void UpdateTransfersListView(bool softUpdate, CopyInst * item=NULL);
  45. static void CALLBACK ProgressTimerTickCb(HWND hwnd, UINT uMsg, UINT_PTR eventId, unsigned long elapsed);
  46. extern "C" winampMediaLibraryPlugin plugin =
  47. {
  48. MLHDR_VER,
  49. "nullsoft(ml_pmp.dll)",
  50. init,
  51. quit,
  52. PluginMessageProc,
  53. 0,
  54. 0,
  55. 0,
  56. };
  57. C_ItemList devices;
  58. C_ItemList loadingDevices;
  59. extern HNAVITEM cloudQueueTreeItem;
  60. static HNAVITEM navigationRoot = NULL;
  61. static ATOM viewAtom = 0;
  62. int groupBtn = 1, customAllowed = 0, enqueuedef = 0;
  63. extern HWND hwndMediaView;
  64. extern DeviceView * currentViewedDevice;
  65. HMENU m_context_menus = NULL, m_context_menus2 = NULL;
  66. int prefsPageLoaded = 0, profile = 0;
  67. prefsDlgRecW prefsPage;
  68. prefsDlgRecW pluginsPrefsPage;
  69. C_Config * global_config;
  70. C_Config * gen_mlconfig;
  71. UINT genhotkeys_add_ipc;
  72. HANDLE hMainThread;
  73. UINT_PTR mainTreeHandle;
  74. extern HINSTANCE cloud_hinst;
  75. extern int IPC_GET_CLOUD_HINST, IPC_LIBRARY_PLAYLISTS_REFRESH;
  76. void deviceConnected(Device * dev);
  77. void deviceDisconnected(Device * dev);
  78. void deviceLoading(pmpDeviceLoading * load);
  79. void deviceNameChanged(Device * dev);
  80. //extern CRITICAL_SECTION listTransfersLock;
  81. CRITICAL_SECTION csenumdrives;
  82. api_playlists *AGAVE_API_PLAYLISTS = 0;
  83. api_playlistmanager *AGAVE_API_PLAYLISTMANAGER = 0;
  84. api_mldb *AGAVE_API_MLDB = 0;
  85. api_memmgr *WASABI_API_MEMMGR = 0;
  86. api_syscb *WASABI_API_SYSCB = 0;
  87. api_application *WASABI_API_APP = 0;
  88. api_podcasts *AGAVE_API_PODCASTS = 0;
  89. api_albumart *AGAVE_API_ALBUMART = 0;
  90. api_stats *AGAVE_API_STATS = 0;
  91. api_threadpool *WASABI_API_THREADPOOL = 0;
  92. api_devicemanager *AGAVE_API_DEVICEMANAGER = 0;
  93. api_metadata *AGAVE_API_METADATA = 0;
  94. // wasabi based services for localisation support
  95. api_language *WASABI_API_LNG = 0;
  96. HINSTANCE WASABI_API_LNG_HINST = 0;
  97. HINSTANCE WASABI_API_ORIG_HINST = 0;
  98. static const GUID pngLoaderGUID =
  99. { 0x5e04fb28, 0x53f5, 0x4032, { 0xbd, 0x29, 0x3, 0x2b, 0x87, 0xec, 0x37, 0x25 } };
  100. static svc_imageLoader *wasabiPngLoader = NULL;
  101. HWND mainMessageWindow = 0;
  102. static bool classRegistered=0;
  103. HWND CreateDummyWindow()
  104. {
  105. if (!classRegistered)
  106. {
  107. WNDCLASSW wc = {0, };
  108. wc.style = 0;
  109. wc.lpfnWndProc = DeviceMsgProc;
  110. wc.hInstance = plugin.hDllInstance;
  111. wc.hIcon = 0;
  112. wc.hCursor = NULL;
  113. wc.lpszClassName = L"ml_pmp_window";
  114. if (!RegisterClassW(&wc))
  115. return 0;
  116. classRegistered = true;
  117. }
  118. HWND dummy = CreateWindowW(L"ml_pmp_window", L"ml_pmp_window", 0, 0, 0, 0, 0, NULL, NULL, plugin.hDllInstance, NULL);
  119. return dummy;
  120. }
  121. genHotkeysAddStruct hksync = { 0, HKF_UNICODE_NAME, WM_USER, 0, 0, "ml_pmp_sync", 0 };
  122. genHotkeysAddStruct hkautofill = { 0, HKF_UNICODE_NAME, WM_USER + 1, 0, 0, "ml_pmp_autofill", 0 };
  123. genHotkeysAddStruct hkeject = { 0, HKF_UNICODE_NAME, WM_USER + 2, 0, 0, "ml_pmp_eject", 0 };
  124. template <class api_T>
  125. void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
  126. {
  127. if (plugin.service)
  128. {
  129. waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
  130. if (factory)
  131. api_t = reinterpret_cast<api_T *>( factory->getInterface() );
  132. }
  133. }
  134. template <class api_T>
  135. void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
  136. {
  137. if (plugin.service && api_t)
  138. {
  139. waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
  140. if (factory)
  141. factory->releaseInterface(api_t);
  142. }
  143. api_t = NULL;
  144. }
  145. HNAVITEM NavigationItem_Find(HNAVITEM root, const wchar_t *name, BOOL allow_root = 0)
  146. {
  147. NAVCTRLFINDPARAMS find;
  148. HNAVITEM item;
  149. if (NULL == name)
  150. return NULL;
  151. if (NULL == plugin.hwndLibraryParent)
  152. return NULL;
  153. find.pszName = (wchar_t*)name;
  154. find.cchLength = -1;
  155. find.compFlags = NICF_INVARIANT;
  156. find.fFullNameSearch = FALSE;
  157. item = MLNavCtrl_FindItemByName(plugin.hwndLibraryParent, &find);
  158. if (NULL == item)
  159. return NULL;
  160. if (!allow_root)
  161. {
  162. // if allowed then we can look for root level items which
  163. // is really for getting 'cloud' devices to another group
  164. if (NULL != root &&
  165. root != MLNavItem_GetParent(plugin.hwndLibraryParent, item))
  166. {
  167. item = NULL;
  168. }
  169. }
  170. return item;
  171. }
  172. HNAVITEM GetNavigationRoot(BOOL forceCreate)
  173. {
  174. if (NULL == navigationRoot &&
  175. FALSE != forceCreate)
  176. {
  177. NAVINSERTSTRUCT nis = {0};
  178. wchar_t buffer[512] = {0};
  179. WASABI_API_LNGSTRINGW_BUF(IDS_PORTABLES, buffer, ARRAYSIZE(buffer));
  180. nis.hParent = NULL;
  181. nis.hInsertAfter = NCI_LAST;
  182. nis.item.cbSize = sizeof(NAVITEM);
  183. nis.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_STYLE | NIMF_ITEMID | NIMF_PARAM;
  184. nis.item.id = MAINTREEID;
  185. nis.item.pszInvariant = L"Portables";
  186. nis.item.style = NIS_HASCHILDREN;
  187. nis.item.pszText = buffer;
  188. nis.item.lParam = -1;
  189. navigationRoot = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis);
  190. if (NULL != navigationRoot)
  191. {
  192. SetTimer(mainMessageWindow, PROGRESSTIMERID, 250, ProgressTimerTickCb);
  193. }
  194. }
  195. return navigationRoot;
  196. }
  197. static HNAVITEM GetNavigationItemFromMessage(int msg, INT_PTR param)
  198. {
  199. return (msg < ML_MSG_NAVIGATION_FIRST) ?
  200. MLNavCtrl_FindItemById(plugin.hwndLibraryParent, param) :
  201. (HNAVITEM)param;
  202. }
  203. void Menu_ConvertRatingMenuStar(HMENU menu, UINT menu_id)
  204. {
  205. MENUITEMINFOW mi = {sizeof(mi), MIIM_DATA | MIIM_TYPE, MFT_STRING};
  206. wchar_t rateBuf[32], *rateStr = rateBuf;
  207. mi.dwTypeData = rateBuf;
  208. mi.cch = 32;
  209. if(GetMenuItemInfoW(menu, menu_id, FALSE, &mi))
  210. {
  211. while(rateStr && *rateStr)
  212. {
  213. if(*rateStr == L'*') *rateStr = L'\u2605';
  214. rateStr=CharNextW(rateStr);
  215. }
  216. SetMenuItemInfoW(menu, menu_id, FALSE, &mi);
  217. }
  218. }
  219. static ServiceWatcher serviceWatcher;
  220. static int init()
  221. {
  222. // if there are no pmp_*.dll then no reason to load
  223. if (!testForDevPlugins())
  224. return ML_INIT_FAILURE;
  225. genrand_int31 = (int (*)())SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_RANDFUNC);
  226. if (0 == viewAtom)
  227. {
  228. viewAtom = GlobalAddAtomW(L"WinampPortableMediaView");
  229. if (0 == viewAtom)
  230. return 2;
  231. }
  232. TranscoderImp::init();
  233. InitializeCriticalSection(&csenumdrives);
  234. Tataki::Init(plugin.service);
  235. ServiceBuild( AGAVE_API_PLAYLISTS, api_playlistsGUID );
  236. ServiceBuild( AGAVE_API_PLAYLISTMANAGER, api_playlistmanagerGUID );
  237. ServiceBuild( WASABI_API_SYSCB, syscbApiServiceGuid );
  238. ServiceBuild( WASABI_API_APP, applicationApiServiceGuid );
  239. ServiceBuild( AGAVE_API_STATS, AnonymousStatsGUID );
  240. ServiceBuild( WASABI_API_THREADPOOL, ThreadPoolGUID );
  241. ServiceBuild( AGAVE_API_DEVICEMANAGER, DeviceManagerGUID );
  242. ServiceBuild( AGAVE_API_ALBUMART, albumArtGUID );
  243. ServiceBuild( AGAVE_API_METADATA, api_metadataGUID );
  244. // loader so that we can get the localisation service api for use
  245. ServiceBuild( WASABI_API_LNG, languageApiGUID );
  246. ServiceBuild( WASABI_API_MEMMGR, memMgrApiServiceGuid );
  247. // no guarantee that AGAVE_API_MLDB will be available yet, so we'll start a watcher for it
  248. serviceWatcher.WatchWith( plugin.service );
  249. serviceWatcher.WatchFor( &AGAVE_API_MLDB, mldbApiGuid );
  250. serviceWatcher.WatchFor( &AGAVE_API_PODCASTS, api_podcastsGUID );
  251. WASABI_API_SYSCB->syscb_registerCallback( &serviceWatcher );
  252. mediaLibrary.library = plugin.hwndLibraryParent;
  253. mediaLibrary.winamp = plugin.hwndWinampParent;
  254. mediaLibrary.instance = plugin.hDllInstance;
  255. mediaLibrary.GetIniDirectory();
  256. mediaLibrary.GetIniDirectoryW();
  257. // need to have this initialised before we try to do anything with localisation features
  258. WASABI_API_START_LANG(plugin.hDllInstance,MlPMPLangGUID);
  259. static wchar_t szDescription[256];
  260. StringCbPrintfW(szDescription, ARRAYSIZE(szDescription), WASABI_API_LNGSTRINGW(IDS_NULLSOFT_PMP_SUPPORT), PLUGIN_VERSION);
  261. plugin.description = (char*)szDescription;
  262. hMainThread = GetCurrentThread();
  263. //InitializeCriticalSection(&listTransfersLock);
  264. global_config = new C_Config( (wchar_t *)mediaLibrary.GetWinampIniW() );
  265. profile = global_config->ReadInt( L"profile", 0, L"Winamp" );
  266. gen_mlconfig = new C_Config( (wchar_t *)SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETMLINIFILEW ), L"gen_ml_config" );
  267. m_context_menus = WASABI_API_LOADMENU( IDR_CONTEXTMENUS );
  268. m_context_menus2 = WASABI_API_LOADMENU( IDR_CONTEXTMENUS );
  269. HMENU rate_hmenu = GetSubMenu(GetSubMenu(m_context_menus,0),7);
  270. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_5);
  271. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_4);
  272. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_3);
  273. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_2);
  274. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_1);
  275. rate_hmenu = GetSubMenu(GetSubMenu(m_context_menus,1),4);
  276. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_5);
  277. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_4);
  278. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_3);
  279. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_2);
  280. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_1);
  281. rate_hmenu = GetSubMenu(GetSubMenu(m_context_menus,2),7);
  282. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_5);
  283. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_4);
  284. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_3);
  285. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_2);
  286. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_1);
  287. rate_hmenu = GetSubMenu(GetSubMenu(m_context_menus,7),5);
  288. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_5);
  289. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_4);
  290. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_3);
  291. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_2);
  292. Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATE_1);
  293. //subclass winamp window
  294. mainMessageWindow = CreateDummyWindow();
  295. prefsPage.hInst = WASABI_API_LNG_HINST;
  296. prefsPage.dlgID = IDD_CONFIG_GLOBAL;
  297. prefsPage.name = _wcsdup( WASABI_API_LNGSTRINGW( IDS_PORTABLES ) );
  298. prefsPage.where = 6;
  299. prefsPage.proc = global_config_dlgproc;
  300. pluginsPrefsPage.hInst = WASABI_API_LNG_HINST;
  301. pluginsPrefsPage.dlgID = IDD_CONFIG_PLUGINS;
  302. pluginsPrefsPage.name = prefsPage.name;
  303. pluginsPrefsPage.where = 1;
  304. pluginsPrefsPage.proc = config_dlgproc_plugins;
  305. // only insert the portables page if we've actually loaded a pmp_*.dll
  306. int count = 0;
  307. if(loadDevPlugins(&count) && count > 0)
  308. {
  309. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(intptr_t)&pluginsPrefsPage,IPC_ADD_PREFS_DLGW);
  310. }
  311. else if (!count)
  312. {
  313. // and if there are none, then cleanup and also notify ml_devices.dll to
  314. // shut-down as no need for it to keep running if there's not going to be
  315. // anything else running (as unlikely we'd have use for it without ml_pmp
  316. quit();
  317. winampMediaLibraryPlugin *(*gp)();
  318. gp = (winampMediaLibraryPlugin * (__cdecl *)(void))GetProcAddress(GetModuleHandleW(L"ml_devices.dll"), "winampGetMediaLibraryPlugin");
  319. if (gp)
  320. {
  321. winampMediaLibraryPlugin *mlplugin = gp();
  322. if (mlplugin && (mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER))
  323. {
  324. mlplugin->quit();
  325. }
  326. }
  327. return ML_INIT_FAILURE;
  328. }
  329. // we've got pmp_*.dll to load, so lets start the fun...
  330. Devices_Init();
  331. if (AGAVE_API_DEVICEMANAGER)
  332. {
  333. SetTimer(mainMessageWindow, PROGRESSTIMERID, 250, ProgressTimerTickCb);
  334. }
  335. else if (!global_config->ReadInt(L"HideRoot",0))
  336. {
  337. GetNavigationRoot(TRUE);
  338. }
  339. SetTimer(mainMessageWindow,COMMITTIMERID,5000,NULL);
  340. IPC_LIBRARY_PLAYLISTS_REFRESH = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"ml_playlist_refresh", IPC_REGISTER_WINAMP_IPCMESSAGE);
  341. IPC_GET_CLOUD_HINST = (INT)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"WinampCloud", IPC_REGISTER_WINAMP_IPCMESSAGE);
  342. cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST);
  343. // set up hotkeys...
  344. genhotkeys_add_ipc = SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&"GenHotkeysAdd",IPC_REGISTER_WINAMP_IPCMESSAGE);
  345. hksync.wnd = hkautofill.wnd = hkeject.wnd = mainMessageWindow;
  346. hksync.name = (char*)_wcsdup(WASABI_API_LNGSTRINGW(IDS_GHK_SYNC_PORTABLE_DEVICE));
  347. PostMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&hksync,genhotkeys_add_ipc);
  348. hkautofill.name = (char*)_wcsdup(WASABI_API_LNGSTRINGW(IDS_GHK_AUTOFILL_PORTABLE_DEVICE));
  349. PostMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&hkautofill,genhotkeys_add_ipc);
  350. hkeject.name = (char*)_wcsdup(WASABI_API_LNGSTRINGW(IDS_GHK_EJECT_PORTABLE_DEVICE));
  351. PostMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&hkeject,genhotkeys_add_ipc);
  352. return ML_INIT_SUCCESS;
  353. }
  354. bool quitting=false;
  355. static void quit()
  356. {
  357. // trigger transfer kill
  358. int i = devices.GetSize();
  359. if (i > 0)
  360. {
  361. while(i-- > 0)
  362. {
  363. DeviceView * device = ((DeviceView*)devices.Get(i));
  364. if (device) device->threadKillswitch = 1;
  365. }
  366. }
  367. quitting = true;
  368. KillTimer(mainMessageWindow, COMMITTIMERID);
  369. DeviceMsgProc(NULL, WM_TIMER, COMMITTIMERID, 0);
  370. i = devices.GetSize();
  371. if (i > 0)
  372. {
  373. while(i-- > 0)
  374. {
  375. DeviceView * device = ((DeviceView*)devices.Get(i));
  376. if (device) device->dev->Close();
  377. }
  378. }
  379. unloadDevPlugins();
  380. stopServer();
  381. HWND f = mainMessageWindow;
  382. mainMessageWindow = 0;
  383. DestroyWindow(f);
  384. delete global_config;
  385. WASABI_API_SYSCB->syscb_deregisterCallback(&serviceWatcher);
  386. serviceWatcher.Clear();
  387. ServiceRelease( AGAVE_API_PLAYLISTS, api_playlistsGUID );
  388. ServiceRelease( AGAVE_API_PLAYLISTMANAGER, api_playlistmanagerGUID );
  389. ServiceRelease( WASABI_API_SYSCB, syscbApiServiceGuid );
  390. ServiceRelease( WASABI_API_APP, applicationApiServiceGuid );
  391. ServiceRelease( WASABI_API_LNG, languageApiGUID );
  392. ServiceRelease( WASABI_API_MEMMGR, memMgrApiServiceGuid );
  393. ServiceRelease( AGAVE_API_MLDB, mldbApiGuid );
  394. ServiceRelease( AGAVE_API_PODCASTS, api_podcastsGUID );
  395. ServiceRelease( AGAVE_API_STATS, AnonymousStatsGUID );
  396. ServiceRelease( AGAVE_API_DEVICEMANAGER, DeviceManagerGUID );
  397. ServiceRelease( AGAVE_API_ALBUMART, albumArtGUID );
  398. ServiceRelease( AGAVE_API_METADATA, api_metadataGUID );
  399. if (NULL != wasabiPngLoader)
  400. {
  401. ServiceRelease(wasabiPngLoader, pngLoaderGUID);
  402. wasabiPngLoader = NULL;
  403. }
  404. DeleteCriticalSection(&csenumdrives);
  405. TranscoderImp::quit();
  406. delete gen_mlconfig;
  407. Tataki::Quit();
  408. ServiceRelease(WASABI_API_THREADPOOL, ThreadPoolGUID);
  409. if (0 != viewAtom)
  410. {
  411. GlobalDeleteAtom(viewAtom);
  412. viewAtom = 0;
  413. }
  414. }
  415. typedef struct { ULONG_PTR param; void * proc; int state; CRITICAL_SECTION lock;} spc ;
  416. static VOID CALLBACK spc_caller(ULONG_PTR dwParam) {
  417. spc * s = (spc*)dwParam;
  418. if(!s) return;
  419. EnterCriticalSection(&s->lock);
  420. if(s->state == -1) { LeaveCriticalSection(&s->lock); DeleteCriticalSection(&s->lock); free(s); return; }
  421. s->state = 2;
  422. void (CALLBACK *p)(ULONG_PTR dwParam);
  423. p = (void (CALLBACK *)(ULONG_PTR dwParam))s->proc;
  424. if(p) p(s->param);
  425. s->state=1;
  426. LeaveCriticalSection(&s->lock);
  427. }
  428. static int spc_GetState(spc *s)
  429. {
  430. EnterCriticalSection(&s->lock);
  431. int state = s->state;
  432. LeaveCriticalSection(&s->lock);
  433. return state;
  434. }
  435. // p must be of type void (CALLBACK *)(ULONG_PTR dwParam). Returns 0 for success.
  436. int SynchronousProcedureCall(void * p, ULONG_PTR dwParam) {
  437. if(!p) return 1;
  438. spc * s = (spc*)calloc(1, sizeof(spc));
  439. InitializeCriticalSection(&s->lock);
  440. s->param = dwParam;
  441. s->proc = p;
  442. s->state = 0;
  443. #if 1 // _WIN32_WINNT >= 0x0400
  444. if(!QueueUserAPC(spc_caller,hMainThread,(ULONG_PTR)s)) { DeleteCriticalSection(&s->lock); free(s); return 1; } //failed
  445. #else
  446. if(!mainMessageWindow) { DeleteCriticalSection(&s->lock); free(s); return 1; } else PostMessage(mainMessageWindow,WM_USER+3,(WPARAM)spc_caller,(LPARAM)s);
  447. #endif
  448. int i=0;
  449. while (spc_GetState(s) != 1)
  450. {
  451. if (SleepEx(10,TRUE) == 0)
  452. i++;
  453. if(i >= 100)
  454. {
  455. EnterCriticalSection(&s->lock);
  456. s->state = -1;
  457. LeaveCriticalSection(&s->lock);
  458. return 1;
  459. }
  460. }
  461. DeleteCriticalSection(&s->lock);
  462. free(s);
  463. return 0;
  464. }
  465. static VOID CALLBACK getTranscoder(ULONG_PTR dwParam)
  466. {
  467. C_Config * conf = global_config;
  468. for(int i=0; i<devices.GetSize(); i++)
  469. {
  470. DeviceView * d = (DeviceView *)devices.Get(i);
  471. if(d->dev == *(Device **)dwParam) { conf = d->config; break; }
  472. }
  473. Transcoder ** t = (Transcoder**)dwParam;
  474. *t = new TranscoderImp(plugin.hwndWinampParent,plugin.hDllInstance,conf, *(Device **)dwParam);
  475. }
  476. static void CALLBACK ProgressTimerTickCb(HWND hwnd, UINT uMsg, UINT_PTR eventId, unsigned long elapsed)
  477. {
  478. wchar_t *buf = (wchar_t*)calloc(sizeof(wchar_t), 256);
  479. NAVITEM itemInfo = {0};
  480. HNAVITEM rootItem = GetNavigationRoot(FALSE);
  481. if (!AGAVE_API_DEVICEMANAGER && NULL == rootItem)
  482. {
  483. KillTimer(hwnd, eventId);
  484. if (buf) free(buf);
  485. return;
  486. }
  487. // TODO need to get this working for a merged instance...
  488. int num = 0,total = 0, percent = 0;
  489. for(int i=0; i<devices.GetSize(); i++)
  490. {
  491. DeviceView * dev = (DeviceView*)devices.Get(i);
  492. LinkedQueue * txQueue = getTransferQueue(dev);
  493. LinkedQueue * finishedTX = getFinishedTransferQueue(dev);
  494. int txProgress = getTransferProgress(dev);
  495. int size = (txQueue ? txQueue->GetSize() : 0);
  496. int device_num = (100 * size) - txProgress;
  497. num += device_num;
  498. int device_total = 100 * size + 100 * (finishedTX ? finishedTX->GetSize() : 0);
  499. total += device_total;
  500. percent = (0 != device_total) ? (((device_total - device_num) * 100) / device_total) : -1;
  501. // TODO need to do something to handle it sticking on 100%
  502. if (dev->queueTreeItem)
  503. {
  504. itemInfo.cbSize = sizeof(itemInfo);
  505. itemInfo.hItem = dev->queueTreeItem;
  506. itemInfo.mask = NIMF_PARAM;
  507. if (MLNavItem_GetInfo(plugin.hwndLibraryParent, &itemInfo) && itemInfo.lParam != percent)
  508. {
  509. if (-1 == percent)
  510. WASABI_API_LNGSTRINGW_BUF(IDS_TRANSFERS,buf, 256);
  511. else
  512. StringCbPrintfW(buf, 256, WASABI_API_LNGSTRINGW(IDS_TRANSFERS_PERCENT), percent);
  513. itemInfo.mask |= NIMF_TEXT;
  514. itemInfo.pszText = buf;
  515. itemInfo.lParam = percent;
  516. itemInfo.cchTextMax = -1;
  517. MLNavItem_SetInfo(plugin.hwndLibraryParent, &itemInfo);
  518. }
  519. }
  520. dev->UpdateActivityState();
  521. }
  522. if (!rootItem)
  523. {
  524. if (buf) free(buf);
  525. return;
  526. }
  527. percent = (0 != total) ? (((total-num)*100)/total) : -1;
  528. itemInfo.cbSize = sizeof(itemInfo);
  529. itemInfo.hItem = rootItem;
  530. itemInfo.mask = NIMF_PARAM;
  531. if (FALSE == MLNavItem_GetInfo(plugin.hwndLibraryParent, &itemInfo))
  532. {
  533. if (buf) free(buf);
  534. return;
  535. }
  536. if (itemInfo.lParam != percent)
  537. {
  538. if (-1 == percent)
  539. WASABI_API_LNGSTRINGW_BUF(IDS_PORTABLES,buf,256);
  540. else
  541. StringCbPrintfW(buf, sizeof(buf), WASABI_API_LNGSTRINGW(IDS_PORTABLES_PERCENT),percent);
  542. itemInfo.mask |= NIMF_TEXT;
  543. itemInfo.pszText = buf;
  544. itemInfo.lParam = percent;
  545. itemInfo.cchTextMax = -1;
  546. MLNavItem_SetInfo(plugin.hwndLibraryParent, &itemInfo);
  547. }
  548. if (buf) free(buf);
  549. }
  550. extern LRESULT CALLBACK TranscodeMsgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  551. static void EnumDrives(ENUM_DRIVES_CALLBACK callback)
  552. {
  553. DWORD drives=GetLogicalDrives();
  554. wchar_t drivestr[4] = L"X:\\";
  555. for(int i=2;i<25;i++) if(drives&(1<<i))
  556. {
  557. EnterCriticalSection(&csenumdrives);
  558. UINT olderrmode=SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  559. drivestr[0] = L'A'+i;
  560. callback(drivestr[0],GetDriveTypeW(drivestr));
  561. SetErrorMode(olderrmode);
  562. LeaveCriticalSection(&csenumdrives);
  563. }
  564. }
  565. LRESULT CALLBACK DeviceMsgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  566. {
  567. if(uMsg == WM_WA_IPC && hwnd != mainMessageWindow) return TranscodeMsgProc(hwnd,uMsg,wParam,lParam);
  568. switch(uMsg)
  569. {
  570. case WM_USER: // hotkey: sync
  571. if(devices.GetSize() > 0)
  572. {
  573. if (!((DeviceView *)devices.Get(0))->isCloudDevice)
  574. ((DeviceView *)devices.Get(0))->Sync();
  575. else
  576. ((DeviceView *)devices.Get(0))->CloudSync();
  577. }
  578. break;
  579. case WM_USER+1: // hotkey: autofill
  580. if(devices.GetSize() > 0)
  581. ((DeviceView *)devices.Get(0))->Autofill();
  582. break;
  583. case WM_USER+2: // hotkey: eject
  584. if(devices.GetSize() > 0)
  585. ((DeviceView *)devices.Get(0))->Eject();
  586. break;
  587. case WM_USER+3:
  588. {
  589. void (CALLBACK *p)(ULONG_PTR dwParam);
  590. p = (void (CALLBACK *)(ULONG_PTR dwParam))wParam;
  591. p((ULONG_PTR)lParam);
  592. }
  593. break;
  594. case WM_USER+4: // refreshes cloud views (re-checks if we've moved away)
  595. if (IsWindow(hwndMediaView) && currentViewedDevice && currentViewedDevice->isCloudDevice)
  596. {
  597. PostMessage(plugin.hwndLibraryParent, WM_USER + 30, 0, 0);
  598. }
  599. break;
  600. case WM_USER+5: // removes cloud devices from a cloud sources logout...
  601. {
  602. for (int i = devices.GetSize() - 1; i > -1; --i)
  603. {
  604. DeviceView * dev = (DeviceView *)devices.Get(i);
  605. if (dev->isCloudDevice)
  606. dev->dev->Close();
  607. }
  608. break;
  609. }
  610. case WM_PMP_IPC:
  611. {
  612. switch(lParam)
  613. {
  614. case PMP_IPC_DEVICECONNECTED:
  615. deviceConnected((Device*)wParam);
  616. break;
  617. case PMP_IPC_DEVICEDISCONNECTED:
  618. deviceDisconnected((Device*)wParam);
  619. break;
  620. case PMP_IPC_DEVICELOADING:
  621. deviceLoading((pmpDeviceLoading *)wParam);
  622. break;
  623. case PMP_IPC_DEVICENAMECHANGED:
  624. deviceNameChanged((Device*)wParam);
  625. break;
  626. case PMP_IPC_DEVICECLOUDTRANSFER:
  627. {
  628. int ret = 0;
  629. cloudDeviceTransfer * transfer = (cloudDeviceTransfer *)wParam;
  630. if (transfer)
  631. {
  632. for (int i = 0; i < devices.GetSize(); i++)
  633. {
  634. DeviceView * dev = (DeviceView *)devices.Get(i);
  635. if (dev->dev->extraActions(DEVICE_IS_CLOUD_TX_DEVICE, (intptr_t)transfer->device_token, 0, 0))
  636. {
  637. ret = dev->TransferFromML(ML_TYPE_FILENAMESW, (void *)transfer->filenames, 0, 1);
  638. }
  639. }
  640. }
  641. return ret;
  642. }
  643. case PMP_IPC_GETCLOUDTRANSFERS:
  644. {
  645. typedef std::vector<wchar_t*> CloudFiles;
  646. CloudFiles *pending = (CloudFiles *)wParam;
  647. if (pending)
  648. {
  649. cloudTransferQueue.lock();
  650. // TODO de-dupe incase going to multiple devices...
  651. for(int i = 0; i < cloudTransferQueue.GetSize(); i++)
  652. {
  653. pending->push_back(((CopyInst *)cloudTransferQueue.Get(i))->sourceFile);
  654. }
  655. cloudTransferQueue.unlock();
  656. return pending->size();
  657. }
  658. return 0;
  659. }
  660. case PMP_IPC_GET_TRANSCODER:
  661. {
  662. void * t = (void*)wParam;
  663. getTranscoder((ULONG_PTR)&t);
  664. if (t == (void*)wParam) return 0;
  665. return (LRESULT)t;
  666. }
  667. case PMP_IPC_RELEASE_TRANSCODER:
  668. delete ((TranscoderImp *)wParam);
  669. break;
  670. case PMP_IPC_ENUM_ACTIVE_DRIVES:
  671. {
  672. if (wParam == 0)
  673. return (LRESULT)EnumDrives;
  674. else
  675. EnumDrives((ENUM_DRIVES_CALLBACK)wParam);
  676. }
  677. break;
  678. case PMP_IPC_GET_INI_FILE:
  679. for (int i = 0; i < devices.GetSize(); i++)
  680. {
  681. DeviceView * dev = (DeviceView *)devices.Get(i);
  682. if(dev->dev == (Device*)wParam) return (intptr_t)dev->config->GetIniFile();
  683. }
  684. break;
  685. case PMP_IPC_GET_PREFS_VIEW:
  686. {
  687. pmpDevicePrefsView *view = (pmpDevicePrefsView *)wParam;
  688. if (view)
  689. {
  690. for (int i = 0; i < devices.GetSize(); i++)
  691. {
  692. DeviceView * dev = (DeviceView *)devices.Get(i);
  693. if (!lstrcmpA(dev->GetName(), view->dev_name))
  694. {
  695. return (LRESULT)OnSelChanged(view->parent, view->parent, dev);
  696. }
  697. }
  698. }
  699. return 0;
  700. }
  701. }
  702. }
  703. break;
  704. case WM_DEVICECHANGE:
  705. {
  706. int r = wmDeviceChange(wParam,lParam);
  707. if (r) return r;
  708. }
  709. break;
  710. case WM_TIMER:
  711. switch(wParam)
  712. {
  713. case COMMITTIMERID:
  714. {
  715. for(int i=0; i<devices.GetSize(); i++)
  716. {
  717. DeviceView * d = (DeviceView *)devices.Get(i);
  718. LinkedQueue * txQueue = getTransferQueue(d);
  719. if (txQueue)
  720. {
  721. if(d->commitNeeded && !txQueue->GetSize())
  722. {
  723. d->commitNeeded = false;
  724. d->dev->commitChanges();
  725. }
  726. }
  727. }
  728. }
  729. break;
  730. }
  731. break;
  732. }
  733. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  734. }
  735. void UpdateLoadingCaption(wchar_t * caption, void * context)
  736. {
  737. NAVITEM itemInfo = {0};
  738. itemInfo.hItem = (HNAVITEM)context;
  739. if (NULL == itemInfo.hItem)
  740. return;
  741. itemInfo.cbSize = sizeof(itemInfo);
  742. itemInfo.pszText = caption;
  743. itemInfo.cchTextMax = -1;
  744. itemInfo.mask = NIMF_TEXT;
  745. MLNavItem_SetInfo(plugin.hwndLibraryParent, &itemInfo);
  746. }
  747. void deviceLoading(pmpDeviceLoading * load)
  748. {
  749. if (!AGAVE_API_DEVICEMANAGER)
  750. {
  751. wchar_t buffer[256] = {0};
  752. NAVINSERTSTRUCT nis = {0};
  753. MLTREEIMAGE devIcon;
  754. devIcon.resourceId = IDR_DEVICE_ICON;
  755. devIcon.hinst = plugin.hDllInstance;
  756. if(load->dev)
  757. load->dev->extraActions(DEVICE_SET_ICON,(intptr_t)&devIcon,0,0);
  758. nis.hParent = GetNavigationRoot(TRUE);
  759. nis.hInsertAfter = NCI_LAST;
  760. nis.item.cbSize = sizeof(NAVITEM);
  761. nis.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_STYLE | NIMF_IMAGE | NIMF_IMAGESEL;
  762. nis.item.pszText = WASABI_API_LNGSTRINGW_BUF(IDS_LOADING, buffer, ARRAYSIZE(buffer));
  763. nis.item.pszInvariant = L"device_loading";
  764. nis.item.style = NIS_ITALIC;
  765. nis.item.iImage = icon_store.GetResourceIcon(devIcon.hinst, (LPCWSTR)MAKEINTRESOURCE(devIcon.resourceId));
  766. nis.item.iSelectedImage = nis.item.iImage;
  767. load->context = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis);
  768. load->UpdateCaption = UpdateLoadingCaption;
  769. loadingDevices.Add(load);
  770. }
  771. }
  772. void finishDeviceLoading(Device * dev)
  773. {
  774. for(int i=0; i < loadingDevices.GetSize(); i++)
  775. {
  776. pmpDeviceLoading * l = (pmpDeviceLoading *)loadingDevices.Get(i);
  777. if(l->dev == dev) {
  778. loadingDevices.Del(i);
  779. HNAVITEM treeItem = (HNAVITEM)l->context;
  780. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent,treeItem);
  781. return;
  782. }
  783. }
  784. }
  785. void deviceConnected(Device * dev)
  786. {
  787. if (!AGAVE_API_DEVICEMANAGER)
  788. GetNavigationRoot(TRUE);
  789. Device * oldDev = dev;
  790. finishDeviceLoading(oldDev);
  791. if(!devices.GetSize()) startServer();
  792. DeviceView *pmp_device = new DeviceView(dev);
  793. devices.Add(pmp_device);
  794. UpdateDevicesListView(false);
  795. }
  796. void deviceDisconnected(Device * dev)
  797. {
  798. finishDeviceLoading(dev);
  799. int cloud_count = 0;
  800. for(int i = 0; i < devices.GetSize(); i++)
  801. {
  802. DeviceView * d = (DeviceView *)devices.Get(i);
  803. if(d->dev == dev)
  804. {
  805. d->threadKillswitch = 1;
  806. d->transferContext.WaitForKill();
  807. devices.Del(i);
  808. d->Unregister();
  809. d->Release();
  810. }
  811. else
  812. {
  813. // to keep the 'cloud library' node on the correct expanded state
  814. // we need to adjust the cloud sources count due to 'all sources'
  815. if (_strnicmp(d->GetName(), "all_sources", 11) && d->isCloudDevice)
  816. {
  817. cloud_count += 1;
  818. }
  819. }
  820. }
  821. // if we're only showing a single device, then we can just disable everything else
  822. if (cloud_count == 0)
  823. {
  824. HNAVITEM cloud_transfers = NavigationItem_Find(0, L"cloud_transfers", TRUE);
  825. if (cloud_transfers != NULL)
  826. {
  827. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, cloud_transfers);
  828. cloudQueueTreeItem = NULL;
  829. }
  830. cloud_transfers = NavigationItem_Find(0, L"cloud_add_sources", TRUE);
  831. if (cloud_transfers != NULL)
  832. {
  833. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, cloud_transfers);
  834. }
  835. cloud_transfers = NavigationItem_Find(0, L"cloud_byos", TRUE);
  836. if (cloud_transfers != NULL)
  837. {
  838. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, cloud_transfers);
  839. }
  840. HNAVITEM cloud = NavigationItem_Find(0, L"cloud_sources", TRUE);
  841. if (cloud != NULL)
  842. {
  843. NAVITEM item = {0};
  844. item.cbSize = sizeof(NAVITEM);
  845. item.mask = NIMF_IMAGE | NIMF_IMAGESEL;
  846. item.hItem = cloud;
  847. item.iImage = item.iSelectedImage = mediaLibrary.AddTreeImageBmp(IDB_TREEITEM_CLOUD);
  848. MLNavItem_SetInfo(plugin.hwndLibraryParent, &item);
  849. }
  850. }
  851. UpdateDevicesListView(false);
  852. if(!devices.GetSize() && !loadingDevices.GetSize() && global_config->ReadInt(L"HideRoot",0))
  853. {
  854. HNAVITEM rootItem = GetNavigationRoot(FALSE);
  855. if (NULL != rootItem)
  856. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, rootItem);
  857. }
  858. if(!devices.GetSize()) stopServer();
  859. }
  860. void deviceNameChanged(Device * dev)
  861. {
  862. finishDeviceLoading(dev);
  863. for(int i=0; i < devices.GetSize(); i++)
  864. {
  865. DeviceView * d = (DeviceView *)devices.Get(i);
  866. if(d->dev == dev)
  867. {
  868. wchar_t *buffer = (wchar_t*)calloc(sizeof(wchar_t),128);
  869. if (buffer)
  870. {
  871. dev->getPlaylistName(0, buffer, 128);
  872. UpdateLoadingCaption(buffer, d->treeItem);
  873. d->SetDisplayName(buffer, 1);
  874. free(buffer);
  875. }
  876. break;
  877. }
  878. }
  879. }
  880. svc_imageLoader *GetPngLoaderService()
  881. {
  882. if (NULL == wasabiPngLoader)
  883. {
  884. if (NULL == WASABI_API_MEMMGR)
  885. return NULL;
  886. ServiceBuild(wasabiPngLoader, pngLoaderGUID);
  887. }
  888. return wasabiPngLoader;
  889. }
  890. BOOL FormatResProtocol(const wchar_t *resourceName, const wchar_t *resourceType, wchar_t *buffer, size_t bufferMax)
  891. {
  892. unsigned long filenameLength;
  893. if (NULL == resourceName)
  894. return FALSE;
  895. if (FAILED(StringCchCopyExW(buffer, bufferMax, L"res://", &buffer, &bufferMax, 0)))
  896. return FALSE;
  897. filenameLength = GetModuleFileNameW(plugin.hDllInstance, buffer, bufferMax);
  898. if (0 == filenameLength || bufferMax == filenameLength)
  899. return FALSE;
  900. buffer += filenameLength;
  901. bufferMax -= filenameLength;
  902. if (NULL != resourceType)
  903. {
  904. if (FALSE != IS_INTRESOURCE(resourceType))
  905. {
  906. if (FAILED(StringCchPrintfExW(buffer, bufferMax, &buffer, &bufferMax, 0, L"/#%d", (int)(INT_PTR)resourceType)))
  907. return FALSE;
  908. }
  909. else
  910. {
  911. if (FAILED(StringCchPrintfExW(buffer, bufferMax, &buffer, &bufferMax, 0, L"/%s", resourceType)))
  912. return FALSE;
  913. }
  914. }
  915. if (FALSE != IS_INTRESOURCE(resourceName))
  916. {
  917. if (FAILED(StringCchPrintfExW(buffer, bufferMax, &buffer, &bufferMax, 0, L"/#%d", (int)(INT_PTR)resourceName)))
  918. return FALSE;
  919. }
  920. else
  921. {
  922. if (FAILED(StringCchPrintfExW(buffer, bufferMax, &buffer, &bufferMax, 0, L"/%s", resourceName)))
  923. return FALSE;
  924. }
  925. return TRUE;
  926. }
  927. BOOL
  928. CenterWindow(HWND window, HWND centerWindow)
  929. {
  930. RECT centerRect, windowRect;
  931. long x, y;
  932. if (NULL == window ||
  933. FALSE == GetWindowRect(window, &windowRect))
  934. {
  935. return FALSE;
  936. }
  937. if (CENTER_OVER_ML_VIEW == centerWindow)
  938. {
  939. centerWindow = (HWND)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_GETCURRENTVIEW, 0);
  940. if (NULL == centerWindow || FALSE == IsWindowVisible(centerWindow))
  941. centerWindow = CENTER_OVER_ML;
  942. }
  943. if (CENTER_OVER_ML == centerWindow)
  944. {
  945. centerWindow = plugin.hwndLibraryParent;
  946. if (NULL == centerWindow || FALSE == IsWindowVisible(centerWindow))
  947. centerWindow = CENTER_OVER_WINAMP;
  948. }
  949. if (CENTER_OVER_WINAMP == centerWindow)
  950. {
  951. centerWindow = (HWND)SENDWAIPC(plugin.hwndWinampParent, IPC_GETDIALOGBOXPARENT, 0);
  952. if (FALSE == IsChild(centerWindow, plugin.hwndLibraryParent))
  953. centerWindow = NULL;
  954. }
  955. if (NULL == centerWindow ||
  956. FALSE == IsWindowVisible(centerWindow) ||
  957. FALSE == GetWindowRect(centerWindow, &centerRect))
  958. {
  959. HMONITOR monitor;
  960. MONITORINFO monitorInfo;
  961. monitor = MonitorFromWindow(plugin.hwndWinampParent, MONITOR_DEFAULTTONEAREST);
  962. monitorInfo.cbSize = sizeof(monitorInfo);
  963. if (NULL == monitor ||
  964. FALSE == GetMonitorInfo(monitor, &monitorInfo) ||
  965. FALSE == CopyRect(&centerRect, &monitorInfo.rcWork))
  966. {
  967. CopyRect(&centerRect, &windowRect);
  968. }
  969. }
  970. x = centerRect.left + ((centerRect.right - centerRect.left)- (windowRect.right - windowRect.left))/2;
  971. y = centerRect.top + ((centerRect.bottom - centerRect.top)- (windowRect.bottom - windowRect.top))/2;
  972. if (x == windowRect.left && y == windowRect.top)
  973. return FALSE;
  974. return SetWindowPos(window, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
  975. }
  976. ATOM
  977. GetViewAtom()
  978. {
  979. return viewAtom;
  980. }
  981. void *
  982. GetViewData(HWND hwnd)
  983. {
  984. return GetPropW(hwnd, (const wchar_t*)MAKEINTATOM(viewAtom));
  985. }
  986. BOOL
  987. SetViewData(HWND hwnd, void *data)
  988. {
  989. return SetPropW(hwnd, (const wchar_t*)MAKEINTATOM(viewAtom), data);
  990. }
  991. void *
  992. RemoveViewData(HWND hwnd)
  993. {
  994. return RemovePropW(hwnd, (const wchar_t*)MAKEINTATOM(viewAtom));
  995. }
  996. static void URL_GetParameter(const wchar_t *param_start, char *dest, size_t dest_len)
  997. {
  998. if (!param_start)
  999. {
  1000. dest[0]=0;
  1001. return;
  1002. }
  1003. while (param_start && *param_start++ != '=');
  1004. while (param_start && *param_start && dest_len > 1 && *param_start != L'&')
  1005. {
  1006. if (*param_start == '+')
  1007. {
  1008. param_start++;
  1009. *dest++=' ';
  1010. }
  1011. else if (*param_start == L'%' && param_start[1] != L'%' && param_start[1])
  1012. {
  1013. int a=0;
  1014. int b=0;
  1015. for ( b = 0; b < 2; b ++)
  1016. {
  1017. int r=param_start[1+b];
  1018. if (r>='0'&&r<='9') r-='0';
  1019. else if (r>='a'&&r<='z') r-='a'-10;
  1020. else if (r>='A'&&r<='Z') r-='A'-10;
  1021. else break;
  1022. a*=16;
  1023. a+=r;
  1024. }
  1025. if (b < 2)
  1026. {
  1027. *dest++=(char)*param_start++;
  1028. }
  1029. else
  1030. {
  1031. *dest++=a;
  1032. param_start += 3;}
  1033. }
  1034. else
  1035. {
  1036. *dest++=(char)*param_start++;
  1037. }
  1038. dest_len--;
  1039. }
  1040. *dest = 0;
  1041. }
  1042. // this only works for one-character parameters
  1043. static const wchar_t *GetParameterStart(const wchar_t *url, wchar_t param)
  1044. {
  1045. wchar_t lookup[4] = { L'&', param, L'=', 0 };
  1046. lookup[1] = param;
  1047. const wchar_t *val = wcsstr(url, lookup);
  1048. if (!val)
  1049. {
  1050. lookup[0] = L'?';
  1051. val = wcsstr(url, lookup);
  1052. }
  1053. return val;
  1054. }
  1055. extern int serverPort;
  1056. static INT_PTR PluginMessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3)
  1057. {
  1058. int i;
  1059. if (message_type == ML_IPC_HOOKEXTINFOW)
  1060. {
  1061. extendedFileInfoStructW *hookMetadata = (extendedFileInfoStructW *)param1;
  1062. if (hookMetadata->filename && !wcsncmp(hookMetadata->filename, L"http://127.0.0.1:", 17) && _wtoi(hookMetadata->filename + 17) == serverPort)
  1063. {
  1064. if (!_wcsicmp(hookMetadata->metadata, L"artist"))
  1065. {
  1066. char metadata[1024] = {0};
  1067. URL_GetParameter(GetParameterStart(hookMetadata->filename, 'a'), metadata, 1024);
  1068. MultiByteToWideCharSZ(CP_UTF8, 0, metadata, -1, hookMetadata->ret, hookMetadata->retlen);
  1069. return 1;
  1070. }
  1071. else if (!_wcsicmp(hookMetadata->metadata, L"album"))
  1072. {
  1073. char metadata[1024] = {0};
  1074. URL_GetParameter(GetParameterStart(hookMetadata->filename, 'l'), metadata, 1024);
  1075. MultiByteToWideCharSZ(CP_UTF8, 0, metadata, -1, hookMetadata->ret, hookMetadata->retlen);
  1076. return 1;
  1077. }
  1078. else if (!_wcsicmp(hookMetadata->metadata, L"title"))
  1079. {
  1080. char metadata[1024] = {0};
  1081. URL_GetParameter(GetParameterStart(hookMetadata->filename, 't'), metadata, 1024);
  1082. MultiByteToWideCharSZ(CP_UTF8, 0, metadata, -1, hookMetadata->ret, hookMetadata->retlen);
  1083. return 1;
  1084. }
  1085. }
  1086. }
  1087. for(i=0; i < devices.GetSize(); i++)
  1088. {
  1089. DeviceView *deviceView;
  1090. deviceView = (DeviceView *)devices.Get(i);
  1091. if (NULL != deviceView)
  1092. {
  1093. INT_PTR a= deviceView->MessageProc(message_type,param1,param2,param3);
  1094. if(0 != a)
  1095. return a;
  1096. }
  1097. }
  1098. if (message_type >= ML_MSG_TREE_BEGIN &&
  1099. message_type <= ML_MSG_TREE_END)
  1100. {
  1101. HNAVITEM item, rootItem;
  1102. item = GetNavigationItemFromMessage(message_type, param1);
  1103. rootItem = GetNavigationRoot(FALSE);
  1104. if(message_type == ML_MSG_TREE_ONCREATEVIEW)
  1105. {
  1106. for(i=0; i < loadingDevices.GetSize(); i++)
  1107. {
  1108. pmpDeviceLoading * l = (pmpDeviceLoading *)loadingDevices.Get(i);
  1109. if(NULL != l->context)
  1110. {
  1111. if (((HNAVITEM)l->context) == item)
  1112. {
  1113. HNAVITEM parentItem;
  1114. parentItem = MLNavItem_GetParent(plugin.hwndLibraryParent, item);
  1115. if (NULL == parentItem)
  1116. parentItem = rootItem;
  1117. PostMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)parentItem, ML_IPC_NAVITEM_SELECT);
  1118. return 0;
  1119. }
  1120. }
  1121. }
  1122. }
  1123. if(NULL != item && item == rootItem)
  1124. {
  1125. switch (message_type)
  1126. {
  1127. case ML_MSG_TREE_ONCREATEVIEW:
  1128. return (INT_PTR)WASABI_API_CREATEDIALOGW(IDD_VIEW_PMP_DEVICES,(HWND)param2,pmp_devices_dlgproc);
  1129. case ML_MSG_TREE_ONCLICK:
  1130. switch(param2)
  1131. {
  1132. case ML_ACTION_RCLICK:
  1133. {
  1134. HMENU menu = GetSubMenu(m_context_menus,6);
  1135. int hideRoot = global_config->ReadInt(L"HideRoot",0);
  1136. CheckMenuItem(menu,ID_MAINTREEROOT_AUTOHIDEROOT,hideRoot?MF_CHECKED:MF_UNCHECKED);
  1137. POINT p;
  1138. GetCursorPos(&p);
  1139. int r = Menu_TrackSkinnedPopup(menu,TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTBUTTON|TPM_NONOTIFY,p.x,p.y,plugin.hwndLibraryParent,NULL);
  1140. switch(r)
  1141. {
  1142. case ID_MAINTREEROOT_AUTOHIDEROOT:
  1143. hideRoot = hideRoot?0:1;
  1144. global_config->WriteInt(L"HideRoot",hideRoot);
  1145. if(hideRoot && devices.GetSize() == 0)
  1146. {
  1147. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, rootItem);
  1148. }
  1149. break;
  1150. case ID_MAINTREEROOT_PREFERENCES:
  1151. SENDWAIPC(plugin.hwndWinampParent, IPC_OPENPREFSTOPAGE, &pluginsPrefsPage);
  1152. break;
  1153. case ID_MAINTREEROOT_HELP:
  1154. SENDWAIPC(plugin.hwndWinampParent, IPC_OPEN_URL, L"https://help.winamp.com/hc/articles/8106455294612-Winamp-Portables-Guide");
  1155. break;
  1156. }
  1157. }
  1158. break;
  1159. case ML_ACTION_DBLCLICK:
  1160. break;
  1161. case ML_ACTION_ENTER:
  1162. break;
  1163. }
  1164. break;
  1165. case ML_MSG_TREE_ONDROPTARGET:
  1166. break;
  1167. case ML_MSG_TREE_ONDRAG:
  1168. break;
  1169. case ML_MSG_TREE_ONDROP:
  1170. break;
  1171. case ML_MSG_NAVIGATION_ONDELETE:
  1172. navigationRoot = NULL;
  1173. KillTimer(mainMessageWindow, PROGRESSTIMERID);
  1174. return TRUE;
  1175. }
  1176. }
  1177. }
  1178. else if (message_type == ML_MSG_NO_CONFIG)
  1179. {
  1180. if(prefsPage._id == 0)
  1181. return TRUE;
  1182. }
  1183. else if (message_type == ML_MSG_CONFIG)
  1184. {
  1185. if(prefsPage._id == 0) return 0;
  1186. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, prefsPage._id, IPC_OPENPREFSTOPAGE);
  1187. }
  1188. else if (message_type == ML_MSG_NOTOKTOQUIT)
  1189. {
  1190. // see if we have any transfers in progress and if so then prompt on what to do...
  1191. bool transfers = false;
  1192. for (int i = 0; i < devices.GetSize(); i++)
  1193. {
  1194. DeviceView * d = (DeviceView *)devices.Get(i);
  1195. LinkedQueue * txQueue = getTransferQueue(d);
  1196. if(txQueue && txQueue->GetSize() > 0)
  1197. {
  1198. transfers = true;
  1199. break;
  1200. }
  1201. }
  1202. if (transfers)
  1203. {
  1204. wchar_t titleStr[32] = {0};
  1205. if (MessageBoxW(plugin.hwndLibraryParent, WASABI_API_LNGSTRINGW(IDS_CANCEL_TRANSFERS_AND_QUIT),
  1206. WASABI_API_LNGSTRINGW_BUF(IDS_CONFIRM_QUIT,titleStr,32), MB_YESNO | MB_ICONQUESTION) == IDNO)
  1207. return TRUE;
  1208. }
  1209. return FALSE;
  1210. }
  1211. else if(message_type == ML_MSG_ONSENDTOBUILD)
  1212. {
  1213. if (param1 == ML_TYPE_ITEMRECORDLIST || param1 == ML_TYPE_ITEMRECORDLISTW ||
  1214. param1 == ML_TYPE_FILENAMES || param1 == ML_TYPE_FILENAMESW ||
  1215. param1 == ML_TYPE_PLAYLISTS || param1 == ML_TYPE_PLAYLIST)
  1216. {
  1217. if (gen_mlconfig->ReadInt(L"pmp_send_to", DEFAULT_PMP_SEND_TO))
  1218. {
  1219. for(int m = 0, mode = 0; m < 2; m++, mode++)
  1220. {
  1221. int added = 0;
  1222. for(i = 0; i < devices.GetSize(); i++)
  1223. {
  1224. DeviceView *deviceView;
  1225. deviceView = (DeviceView *)devices.Get(i);
  1226. if (NULL != deviceView)
  1227. {
  1228. if (deviceView->dev->extraActions(DEVICE_SENDTO_UNSUPPORTED, 0, 0, 0) == 0)
  1229. {
  1230. wchar_t buffer[128] = {0};
  1231. deviceView->dev->getPlaylistName(0, buffer, 128);
  1232. if (buffer[0])
  1233. {
  1234. // TODO - this is to block true playlists from appearing on the sendto
  1235. // for cloud playlists handling - remove this when we can do more
  1236. // than just uploading the playlist blob without the actual files
  1237. if (deviceView->isCloudDevice && param3 == ML_TYPE_PLAYLIST) continue;
  1238. if (!deviceView->isCloudDevice == mode)
  1239. {
  1240. if (!added)
  1241. {
  1242. mediaLibrary.BranchSendTo(param2);
  1243. added = 1;
  1244. }
  1245. mediaLibrary.AddToBranchSendTo(buffer, param2, reinterpret_cast<INT_PTR>(deviceView));
  1246. }
  1247. }
  1248. }
  1249. }
  1250. }
  1251. if (added)
  1252. {
  1253. mediaLibrary.EndBranchSendTo(WASABI_API_LNGSTRINGW((!m ? IDS_SENDTO_CLOUD : IDS_SENDTO_DEVICES)), param2);
  1254. }
  1255. }
  1256. }
  1257. }
  1258. }
  1259. else if (message_type == ML_MSG_VIEW_PLAY_ENQUEUE_CHANGE)
  1260. {
  1261. enqueuedef = param1;
  1262. groupBtn = param2;
  1263. if (IsWindow(hwndMediaView))
  1264. PostMessage(hwndMediaView, WM_APP + 104, param1, param2);
  1265. return 0;
  1266. }
  1267. return 0;
  1268. }
  1269. extern "C" {
  1270. __declspec( dllexport ) winampMediaLibraryPlugin * winampGetMediaLibraryPlugin()
  1271. {
  1272. return &plugin;
  1273. }
  1274. __declspec( dllexport ) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) {
  1275. // cleanup the ml tree so the portables root isn't left
  1276. HNAVITEM rootItem = GetNavigationRoot(FALSE);
  1277. if(NULL != rootItem)
  1278. MLNavCtrl_DeleteItem(plugin.hwndLibraryParent, rootItem);
  1279. // prompt to remove our settings with default as no (just incase)
  1280. wchar_t title[256] = {0};
  1281. StringCbPrintfW(title, ARRAYSIZE(title), WASABI_API_LNGSTRINGW(IDS_NULLSOFT_PMP_SUPPORT), PLUGIN_VERSION);
  1282. if(MessageBoxW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_DO_YOU_ALSO_WANT_TO_REMOVE_SETTINGS),
  1283. title,MB_YESNO|MB_DEFBUTTON2) == IDYES)
  1284. {
  1285. global_config->WriteString(0,0);
  1286. }
  1287. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(intptr_t)&pluginsPrefsPage,IPC_REMOVE_PREFS_DLG);
  1288. // allow an on-the-fly removal (since we've got to be with a compatible client build)
  1289. return ML_PLUGIN_UNINSTALL_NOW;
  1290. }
  1291. };