NJBDevice.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. #include "NJBDevice.h"
  2. #include "../nu/AutoWide.h"
  3. #include "../nu/AutoChar.h"
  4. HWND CreateDummyWindow();
  5. extern HWND mainMessageWindow;
  6. static __int64 fileSize(wchar_t * filename)
  7. {
  8. WIN32_FIND_DATA f={0};
  9. HANDLE h = FindFirstFileW(filename,&f);
  10. if(h == INVALID_HANDLE_VALUE) return -1;
  11. FindClose(h);
  12. ULARGE_INTEGER i;
  13. i.HighPart = f.nFileSizeHigh;
  14. i.LowPart = f.nFileSizeLow;
  15. return i.QuadPart;
  16. }
  17. static void FillSongFromMeta(BYTE * buf,Song * song) {
  18. BYTE * ptr = buf;
  19. short count = 0;
  20. short type = 0;
  21. short NameLen = 0;
  22. long DataLen = 0;
  23. long lData;
  24. memcpy(&count, ptr, sizeof(short));
  25. ptr += sizeof(short);
  26. for(int i=0; i<count; i++)
  27. {
  28. memcpy(&type, ptr, sizeof(short));
  29. ptr += sizeof(short);
  30. memcpy(&NameLen, ptr, sizeof(short));
  31. ptr += sizeof(short);
  32. memcpy(&DataLen, ptr, sizeof(long));
  33. ptr += sizeof(long);
  34. char itemname[MAX_PATH] = {0};
  35. memcpy(itemname, ptr, NameLen);
  36. itemname[NameLen]=0;
  37. ptr += NameLen;
  38. if(type == 1) { // binary
  39. memcpy(&lData, ptr, min(DataLen,4));
  40. if (!_stricmp(itemname,LENGTH)) song->length = lData * 1000;
  41. else if (!_stricmp(itemname,FILESIZE)) song->size = lData;
  42. else if (!_stricmp(itemname,TRACKNUM)) song->track = lData;
  43. else if (!_stricmp(itemname,YEAR)) song->year = lData;
  44. else if (!_stricmp(itemname,TRACKID)) song->trackid = lData;
  45. } else if(type == 2) { // unicode
  46. if (!_stricmp(itemname,TITLE)) lstrcpyn(song->title,(WCHAR*)ptr,min((DataLen+2)/2,fieldlen));
  47. else if (!_stricmp(itemname,ARTIST)) lstrcpyn(song->artist,(WCHAR*)ptr,min((DataLen+2)/2,fieldlen));
  48. else if (!_stricmp(itemname,ALBUM)) lstrcpyn(song->album,(WCHAR*)ptr,min((DataLen+2)/2,fieldlen));
  49. else if (!_stricmp(itemname,GENRE)) lstrcpyn(song->genre,(WCHAR*)ptr,min((DataLen+2)/2,fieldlen));
  50. } else if(type == 0) { // ASCII
  51. if (!_stricmp(itemname,CODEC)) {
  52. int l=min(sizeof(song->codec)-1,DataLen);
  53. memcpy(song->codec,ptr,l);
  54. song->codec[l]=0;
  55. }
  56. }
  57. ptr += DataLen;
  58. }
  59. }
  60. static bool GetSong(DAPSDK_ID * item, long id, Song * song) {
  61. long size;
  62. if(m_pCTJukebox2->GetItemAttribute(id,(IUnknown*)item,0,&size,NULL) != S_OK) return false;
  63. BYTE * buf = (BYTE*)calloc(size,sizeof(BYTE));
  64. if(!buf) return false;
  65. if(m_pCTJukebox2->GetItemAttribute(id,(IUnknown*)item,size,&size,(IUnknown*)buf) != S_OK) { free(buf); return false; }
  66. FillSongFromMeta(buf,song);
  67. free(buf);
  68. return true;
  69. }
  70. static int song_sortfunc(const void *elem1, const void *elem2) {
  71. Song *a=(Song *)*(void **)elem1;
  72. Song *b=(Song *)*(void **)elem2;
  73. return a->trackid - b->trackid;
  74. }
  75. static Song *BinaryChopFind(int id,Playlist * mpl) {
  76. Song s;
  77. s.trackid=id;
  78. Song * d = &s;
  79. Song ** ret = (Song**)bsearch(&d,mpl->songs.GetAll(),mpl->songs.GetSize(),sizeof(void*),song_sortfunc);
  80. return ret?*ret:NULL;
  81. }
  82. static bool GetPlaylist(long id, DAPSDK_ID * item,Playlist * pl, Playlist * mpl)
  83. {
  84. pl->dirty=false;
  85. pl->plid = item->lID;
  86. lstrcpyn(pl->name,item->bstrName,fieldlen);
  87. SysFreeString(item->bstrName);
  88. DAPSDK_ID song;
  89. HRESULT hr = m_pCTJukebox2->FindFirstItem(id,(IUnknown*)item,(IUnknown*)&song);
  90. while(hr == S_OK) {
  91. Song * s = BinaryChopFind(song.lID,mpl);
  92. if(s) pl->songs.Add(s);
  93. hr = m_pCTJukebox2->FindNextItem(id,(IUnknown*)item,(IUnknown*)&song);
  94. }
  95. return true;
  96. }
  97. NJBDevice::NJBDevice(long id) : transcoder(NULL)
  98. {
  99. InitializeCriticalSection(&csRevTransfer);
  100. InitializeCriticalSection(&csTransfer);
  101. devices.Add(this);
  102. pmpDeviceLoading load;
  103. load.dev = this;
  104. load.UpdateCaption = NULL;
  105. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)&load,PMP_IPC_DEVICELOADING);
  106. if(load.UpdateCaption) {
  107. load.UpdateCaption(WASABI_API_LNGSTRINGW(IDS_NJB_LOADING),load.context);
  108. }
  109. this->id = id;
  110. transferQueueLength = 0;
  111. messageWindow = CreateDummyWindow();
  112. m_pCTJukebox2->SetCallbackWindow2(id,(long)messageWindow);
  113. BYTE * ptr = NULL;
  114. if(m_pCTJukebox2->GetDeviceProperties(id,kDeviceSerialNumberValue,(IUnknown*)ptr) == S_OK) {
  115. memcpy(serial,ptr,16);
  116. //free(ptr);
  117. }
  118. DAPSDK_ID item,parent,root;
  119. Playlist * mpl = new Playlist;
  120. BSTR name=NULL;
  121. m_pCTJukebox2->GetDeviceProperties(id,kDeviceNameString,(IUnknown*)&name);
  122. lstrcpyn(mpl->name,name?name:L"Creative Jukebox",fieldlen);
  123. SysFreeString(name);
  124. // search for tracks...
  125. parent.lID = ALLTRACKSKEY;
  126. parent.lType = kAudioTrackType;
  127. HRESULT hr = m_pCTJukebox2->FindFirstItem(id,(IUnknown*)&parent,(IUnknown*)&item);
  128. while(hr == S_OK) {
  129. // add track
  130. Song * song = new Song;
  131. if(GetSong(&item,id,song)) {
  132. mpl->songs.Add(song);
  133. song->trackid = item.lID;
  134. }
  135. else delete song;
  136. hr = m_pCTJukebox2->FindNextItem(id,(IUnknown*)&parent,(IUnknown*)&item);
  137. }
  138. qsort(mpl->songs.GetAll(),mpl->songs.GetSize(),sizeof(void*),song_sortfunc); // sort the master playlist by trackid, so we can find stuff later using binary chop
  139. playlists.Add(mpl);
  140. // search for playlists...
  141. hr = m_pCTJukebox2->FindFirstRootItem(id,(IUnknown*)&root);
  142. while(hr == S_OK) {
  143. if(_wcsicmp(root.bstrName,L"PLAY LISTS")==0) {
  144. playlistRoot.bstrName = L"PLAY LISTS";
  145. playlistRoot.lID = root.lID;
  146. playlistRoot.lType = root.lType;
  147. HRESULT hr = m_pCTJukebox2->FindFirstParentItem(id,(IUnknown*)&root,(IUnknown*)&parent);
  148. while(hr == S_OK) {
  149. Playlist * pl = new Playlist;
  150. if(GetPlaylist(id,&parent,pl,mpl)) playlists.Add(pl);
  151. else delete pl;
  152. hr = m_pCTJukebox2->FindNextParentItem(id,(IUnknown*)&root,(IUnknown*)&parent);
  153. }
  154. }
  155. SysFreeString(root.bstrName);
  156. hr = m_pCTJukebox2->FindNextRootItem(id,(IUnknown*)&root);
  157. }
  158. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICECONNECTED);
  159. //transcoder = NULL;
  160. transcoder = (Transcoder*)SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_GET_TRANSCODER);
  161. if(transcoder) {
  162. transcoder->AddAcceptableFormat(L"mp3");
  163. //transcoder->AddAcceptableFormat(L"wav");
  164. transcoder->AddAcceptableFormat(L"wma");
  165. }
  166. }
  167. NJBDevice::~NJBDevice()
  168. {
  169. m_pCTJukebox2->SetCallbackWindow2(id, (long)mainMessageWindow);
  170. DestroyWindow(messageWindow);
  171. messageWindow = NULL;
  172. Playlist * mpl = (Playlist *)playlists.Get(0);
  173. int l = mpl->songs.GetSize();
  174. for(int i=0; i<l; i++) delete (Song *)mpl->songs.Get(i);
  175. l = playlists.GetSize();
  176. for(int i=0; i<l; i++) delete (Playlist *)playlists.Get(i);
  177. for(int i=0; i<devices.GetSize(); i++) if(devices.Get(i) == this) { devices.Del(i); break; }
  178. DeleteCriticalSection(&csRevTransfer);
  179. DeleteCriticalSection(&csTransfer);
  180. if(transcoder) SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(WPARAM)transcoder,PMP_IPC_RELEASE_TRANSCODER);
  181. }
  182. __int64 NJBDevice::getDeviceCapacityAvailable() {
  183. DAPSDK_STORAGE_INFO s;
  184. ULARGE_INTEGER ret;
  185. m_pCTJukebox2->GetDeviceProperties(id,kStorageInfoStruct,(IUnknown*)&s);
  186. ret.LowPart = s.freeL;
  187. ret.HighPart = s.freeH;
  188. return ret.QuadPart;
  189. }
  190. __int64 NJBDevice::getDeviceCapacityTotal() {
  191. DAPSDK_STORAGE_INFO s;
  192. ULARGE_INTEGER ret;
  193. m_pCTJukebox2->GetDeviceProperties(id,kStorageInfoStruct,(IUnknown*)&s);
  194. ret.LowPart = s.totalL;
  195. ret.HighPart = s.totalH;
  196. return ret.QuadPart;
  197. }
  198. void NJBDevice::Eject() {
  199. Close();
  200. }
  201. void NJBDevice::Close()
  202. {
  203. commitChanges();
  204. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICEDISCONNECTED);
  205. if(devices.GetSize() == 1)
  206. m_pCTJukebox2->SetCallbackWindow2(0,(long)mainMessageWindow);
  207. delete this;
  208. }
  209. static BYTE * setAttrib(BYTE * ptr,short type, char * name, BYTE * data, int datalen) {
  210. short namelen = (short)strlen(name);
  211. memcpy(ptr,&type,2); ptr += 2;
  212. memcpy(ptr,&namelen,2); ptr += 2;
  213. memcpy(ptr,&datalen,4); ptr += 4;
  214. memcpy(ptr,name,namelen); ptr += namelen;
  215. memcpy(ptr,data,datalen); ptr += datalen;
  216. return ptr;
  217. }
  218. static BYTE * makeMetaFromItemRecord(itemRecordW * item, wchar_t * file, long * size) {
  219. char codec[4]="WAV";
  220. wchar_t * ext = wcsrchr(file,L'.') + 1;
  221. if(!_wcsicmp(ext,L"mp3")) strncpy(codec,"MP3",3);
  222. else if(!_wcsicmp(ext,L"wma")) strncpy(codec,"WMA",3);
  223. if(!item->album) item->album = _wcsdup(L"");
  224. if(!item->artist) item->artist = _wcsdup(L"");
  225. if(!item->title) item->title = _wcsdup(L"");
  226. *size = (long)(2/*count*/+2/*type*/+6/*namelen+datalen*/+strlen(TITLE)+2*wcslen(item->title)
  227. +2+6+strlen(ALBUM)+2*wcslen(item->album)
  228. +2+6+strlen(ARTIST)+2*wcslen(item->artist)
  229. +2+6+strlen(CODEC)+strlen(codec)
  230. +2+6+strlen(FILESIZE)+sizeof(long)
  231. +2+6+strlen(LENGTH)+sizeof(long));
  232. int count = 6;
  233. if (item->year > 0 ){
  234. *size+=2+6+(long)strlen(YEAR)+sizeof(short);
  235. count++;
  236. }
  237. if (item->genre) {
  238. *size+=(long)(2+6+strlen(GENRE)+2*wcslen(item->genre));
  239. count++;
  240. }
  241. if (item->track>0) {
  242. *size+= (long)(2+6+strlen(TRACKNUM)+sizeof(short));
  243. count++;
  244. }
  245. BYTE *buf = (BYTE*)calloc(1,*size);
  246. BYTE *ptr = buf;
  247. memcpy(ptr, &count, sizeof(short));
  248. ptr += sizeof(short);
  249. ptr = setAttrib(ptr,2,TITLE,(BYTE*)((wchar_t*)(item->title)),(int)wcslen(item->title)*2);
  250. ptr = setAttrib(ptr,2,ARTIST,(BYTE*)((wchar_t*)(item->artist)), (int)wcslen(item->artist)*2);
  251. ptr = setAttrib(ptr,2,ALBUM,(BYTE*)((wchar_t*)(item->album)), (int)wcslen(item->album)*2);
  252. if(item->genre) ptr = setAttrib(ptr,2,GENRE,(BYTE*)((wchar_t*)(item->genre)), (int)wcslen(item->genre)*2);
  253. short v = item->track;
  254. if(item->track>0) ptr = setAttrib(ptr,1,TRACKNUM,(BYTE*)&v,2);
  255. v = item->year;
  256. if(item->year>0) ptr = setAttrib(ptr,1,YEAR,(BYTE*)&v,2);
  257. ptr = setAttrib(ptr,0,CODEC,(BYTE*)codec,(int)strlen(codec));
  258. ptr = setAttrib(ptr,1,LENGTH,(BYTE*)&item->length,4);
  259. __int64 filesize = fileSize(file);
  260. ptr = setAttrib(ptr,1,FILESIZE,(BYTE*)&filesize,4);
  261. return buf;
  262. }
  263. BOOL NJBDevice::WindowMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  264. switch(uMsg) {
  265. case WM_USER: // start add item
  266. if(transferItem.status == 0) {
  267. long size;
  268. BYTE * buf = makeMetaFromItemRecord(const_cast<itemRecordW *>(transferItem.track),transferItem.file,&size);
  269. HRESULT hr = m_pCTJukebox2->AddItem(id,kAudioTrackType,SysAllocString(transferItem.file),size,(IUnknown*)buf);
  270. transferItem.meta = buf;
  271. if(hr != S_OK) this->WindowMessage(hwnd,WM_DAPSDK_ADDITEM_COMPLETE,-1,0);
  272. }
  273. break;
  274. case WM_DAPSDK_ADDITEM_PROGRESS:
  275. {
  276. wchar_t buf[100] = {0};
  277. wsprintf(buf,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING_PERCENT),(int)wParam);
  278. transferItem.callback(transferItem.callbackContext,buf);
  279. }
  280. break;
  281. case WM_DAPSDK_ADDITEM_COMPLETE:
  282. if(wParam == 0) {
  283. transferItem.callback(transferItem.callbackContext,WASABI_API_LNGSTRINGW(IDS_DONE));
  284. Song * song = new Song;
  285. song->trackid = (int)lParam;
  286. FillSongFromMeta(transferItem.meta,song);
  287. song->track = transferItem.track->track;
  288. song->year = transferItem.track->year;
  289. *transferItem.songid = (songid_t)song;
  290. }
  291. else {
  292. transferItem.callback(transferItem.callbackContext,WASABI_API_LNGSTRINGW(IDS_ERROR));
  293. }
  294. transferItem.status = (wParam==0?1:2);
  295. free(transferItem.meta);
  296. break;
  297. case WM_USER+1: // start get item
  298. if(revTransferItem.status == 0) {
  299. Song * song = (Song*)*revTransferItem.songid;
  300. DAPSDK_ID item = {song->trackid,kAudioTrackType,song->title};
  301. // memory allocated by SysAllocString is freed by COM (why, i don't know)
  302. HRESULT hr = m_pCTJukebox2->GetItem(id,SysAllocString(revTransferItem.file),(IUnknown*)&item);
  303. if(hr != S_OK) WindowMessage(hwnd,WM_DAPSDK_GETITEM_COMPLETE,-1,0);
  304. }
  305. break;
  306. case WM_DAPSDK_GETITEM_PROGRESS:
  307. {
  308. wchar_t buf[100] = {0};
  309. wsprintf(buf,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING_PERCENT),(int)wParam);
  310. revTransferItem.callback(revTransferItem.callbackContext,buf);
  311. }
  312. break;
  313. case WM_DAPSDK_GETITEM_COMPLETE:
  314. revTransferItem.callback(revTransferItem.callbackContext,
  315. WASABI_API_LNGSTRINGW((wParam==0?IDS_DONE:IDS_ERROR)));
  316. revTransferItem.status = (wParam==0?1:2);
  317. break;
  318. }
  319. return 0;
  320. }
  321. //p75
  322. int NJBDevice::transferTrackToDevice(const itemRecordW * track,void * callbackContext,void (*callback)(void * callbackContext, wchar_t * status),songid_t * songid,int * killswitch) {
  323. wchar_t file[2048] = {0};
  324. wcsncpy(file,track->filename,2048);
  325. bool deletefile = false;
  326. if(transcoder) if(transcoder->ShouldTranscode(file)) {
  327. wchar_t newfile[MAX_PATH] = {0};
  328. wchar_t ext[10] = {0};
  329. transcoder->CanTranscode(file,ext);
  330. transcoder->GetTempFilePath(ext,newfile);
  331. if(transcoder->TranscodeFile(file,newfile,killswitch,callback,callbackContext)) return -1;
  332. wcsncpy(file,newfile,2048);
  333. deletefile=true;
  334. }
  335. EnterCriticalSection(&csTransfer);
  336. callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING));
  337. transferItem.file = file;
  338. transferItem.callback = callback;
  339. transferItem.callbackContext = callbackContext;
  340. transferItem.status=0; // in progress
  341. transferItem.killswitch = killswitch;
  342. transferItem.songid = songid;
  343. transferItem.track = track;
  344. //now start the transfer
  345. PostMessage(messageWindow,WM_USER,0,0);
  346. while(transferItem.status==0) Sleep(10); // wait for transfer
  347. // transfer completed
  348. int ret = transferItem.status==1?0:-1;
  349. LeaveCriticalSection(&csTransfer);
  350. if(deletefile) _wunlink(file);
  351. return ret;
  352. }
  353. int NJBDevice::trackAddedToTransferQueue(const itemRecordW * track) {
  354. __int64 l;
  355. if(transcoder && transcoder->ShouldTranscode(track->filename)) {
  356. int k = transcoder->CanTranscode(track->filename);
  357. if(k == -1) return -2;
  358. if(k == 0) l = fileSize(track->filename);
  359. else l = (__int64)k;
  360. } else {
  361. wchar_t * ext = wcsrchr(track->filename,L'.');
  362. if(!ext) return -2;
  363. if(_wcsicmp(ext,L".mp3") && _wcsicmp(ext,L".wma") && _wcsicmp(ext,L".wav")) return -2;
  364. l = fileSize(track->filename);
  365. }
  366. if(transferQueueLength + l + 1000000 > getDeviceCapacityAvailable())
  367. return -1;
  368. else {
  369. transferQueueLength += l;
  370. return 0;
  371. }
  372. }
  373. void NJBDevice::trackRemovedFromTransferQueue(const itemRecordW * track) {
  374. __int64 l = (__int64)fileSize(track->filename);
  375. if(transcoder && transcoder->ShouldTranscode(track->filename)) {
  376. int k = transcoder->CanTranscode(track->filename);
  377. if(k != -1 && k != 0) l = (__int64)k;
  378. }
  379. transferQueueLength -= l;
  380. }
  381. __int64 NJBDevice::getTrackSizeOnDevice(const itemRecordW * track) {
  382. if(transcoder && transcoder->ShouldTranscode(track->filename)) {
  383. int k = transcoder->CanTranscode(track->filename);
  384. if(k != -1 && k != 0) return k;
  385. }
  386. wchar_t * ext = wcsrchr(track->filename,L'.');
  387. if(!ext) return 0;
  388. if(_wcsicmp(ext,L".mp3") && _wcsicmp(ext,L".wma") && _wcsicmp(ext,L".wav")) return 0;
  389. return fileSize(track->filename);
  390. }
  391. void NJBDevice::deleteTrack(songid_t songid) {
  392. Song * s = (Song*)songid;
  393. for(int i=0; i<playlists.GetSize(); i++) {
  394. Playlist * pl = (Playlist *)playlists.Get(i);
  395. int l = pl->songs.GetSize();
  396. while(l-- > 0) if(pl->songs.Get(l) == (void*)s) { pl->songs.Del(l); if(i>0) pl->dirty=true; }
  397. }
  398. DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
  399. m_pCTJukebox2->DeleteItem(id,(IUnknown*)&item);
  400. delete s;
  401. }
  402. void NJBDevice::commitChanges() {
  403. for(int i=1; i<playlists.GetSize(); i++) {
  404. Playlist * pl = (Playlist *)playlists.Get(i);
  405. if(pl->dirty) {
  406. pl->dirty = false;
  407. DAPSDK_ID parentold = {pl->plid,kPlaylistType,pl->name};
  408. m_pCTJukebox2->DeleteParentItem(id,(IUnknown*)&parentold);
  409. DAPSDK_ID parent = {0,kPlaylistType,_wcsdup(pl->name)};
  410. m_pCTJukebox2->AddParentItem(id,(IUnknown*)&playlistRoot,(IUnknown*)&parent);
  411. pl->plid = parent.lID;
  412. long l = pl->songs.GetSize();
  413. DAPSDK_ID * list = (DAPSDK_ID *)calloc(sizeof(DAPSDK_ID),l);
  414. for(int j=0; j<l; j++) {
  415. Song * s = (Song*)pl->songs.Get(j);
  416. if(s) {
  417. list[j].lID = s->trackid;
  418. list[j].lType = kAudioTrackType;
  419. list[j].bstrName = SysAllocString(s->title);
  420. }
  421. }
  422. m_pCTJukebox2->AddItemsToParentItem(id,(IUnknown*)&parent,l,(IUnknown*)list);
  423. free(list);
  424. }
  425. }
  426. }
  427. int NJBDevice::getPlaylistCount() {
  428. return playlists.GetSize();
  429. }
  430. void NJBDevice::getPlaylistName(int playlistnumber, wchar_t * buf, int len) {
  431. Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
  432. lstrcpyn(buf,pl->name,len);
  433. }
  434. int NJBDevice::getPlaylistLength(int playlistnumber) {
  435. Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
  436. return pl->songs.GetSize();
  437. }
  438. songid_t NJBDevice::getPlaylistTrack(int playlistnumber,int songnum) {
  439. Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
  440. return (songid_t) pl->songs.Get(songnum);
  441. }
  442. void NJBDevice::setPlaylistName(int playlistnumber, const wchar_t * buf) {
  443. Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
  444. lstrcpyn(pl->name,buf,fieldlen);
  445. DAPSDK_ID item = {pl->plid,kPlaylistType,pl->name};
  446. BSTR name = SysAllocString(buf);
  447. m_pCTJukebox2->RenameParentItem(id,(IUnknown*)&item, name);
  448. SysFreeString(name);
  449. }
  450. void NJBDevice::playlistSwapItems(int playlistnumber, int posA, int posB) {
  451. Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
  452. void * a = pl->songs.Get(posA);
  453. void * b = pl->songs.Get(posB);
  454. pl->songs.Set(posA,b);
  455. pl->songs.Set(posB,a);
  456. pl->dirty = true;
  457. }
  458. static int sortby;
  459. #define RETIFNZ(v) if ((v)!=0) return v;
  460. #define STRCMP_NULLOK _wcsicmp
  461. static int sortFunc(const void *elem1, const void *elem2)
  462. {
  463. int use_by = sortby;
  464. Song *a=(Song *)*(void **)elem1;
  465. Song *b=(Song *)*(void **)elem2;
  466. // this might be too slow, but it'd be nice
  467. int x;
  468. for (x = 0; x < 5; x ++)
  469. {
  470. if (use_by == SORTBY_TITLE) // title -> artist -> album -> disc -> track
  471. {
  472. int v=STRCMP_NULLOK(a->title,b->title);
  473. RETIFNZ(v)
  474. use_by=SORTBY_ARTIST;
  475. }
  476. else if (use_by == SORTBY_ARTIST) // artist -> album -> disc -> track -> title
  477. {
  478. int v=STRCMP_NULLOK(a->artist,b->artist);
  479. RETIFNZ(v)
  480. use_by=SORTBY_ALBUM;
  481. }
  482. else if (use_by == SORTBY_ALBUM) // album -> disc -> track -> title -> artist
  483. {
  484. int v=STRCMP_NULLOK(a->album,b->album);
  485. RETIFNZ(v)
  486. use_by=SORTBY_DISCNUM;
  487. }
  488. else if (use_by == SORTBY_TRACKNUM) // track -> title -> artist -> album -> disc
  489. {
  490. int v1=a->track;
  491. int v2=b->track;
  492. if (v1<0)v1=0;
  493. if (v2<0)v2=0;
  494. RETIFNZ(v1-v2)
  495. use_by=SORTBY_TITLE;
  496. }
  497. else if (use_by == SORTBY_GENRE) // genre -> artist -> album -> disc -> track
  498. {
  499. int v=STRCMP_NULLOK(a->genre,b->genre);
  500. RETIFNZ(v)
  501. use_by=SORTBY_ARTIST;
  502. }
  503. else break; // no sort order?
  504. }
  505. return 0;
  506. }
  507. #undef RETIFNZ
  508. #undef STRCMP_NULLOK
  509. void NJBDevice::sortPlaylist(int playlistnumber, int sortBy) {
  510. sortby = sortBy;
  511. Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
  512. qsort(pl->songs.GetAll(),pl->songs.GetSize(),sizeof(void*),sortFunc);
  513. pl->dirty=true;
  514. }
  515. void NJBDevice::addTrackToPlaylist(int playlistnumber, songid_t songid) {
  516. Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
  517. pl->songs.Add((void*)songid);
  518. pl->dirty = true;
  519. }
  520. void NJBDevice::removeTrackFromPlaylist(int playlistnumber, int songnum) {
  521. Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
  522. pl->songs.Del(songnum);
  523. pl->dirty = true;
  524. }
  525. void NJBDevice::deletePlaylist(int playlistnumber) {
  526. Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
  527. DAPSDK_ID parent = {pl->plid,kPlaylistType,pl->name};
  528. m_pCTJukebox2->DeleteParentItem(id,(IUnknown*)&parent);
  529. playlists.Del(playlistnumber);
  530. delete pl;
  531. }
  532. int NJBDevice::newPlaylist(const wchar_t * name) {
  533. Playlist * pl = new Playlist;
  534. pl->dirty = false;
  535. lstrcpyn(pl->name,name,fieldlen);
  536. DAPSDK_ID parent = {0,kPlaylistType,pl->name};
  537. m_pCTJukebox2->AddParentItem(id,(IUnknown*)&playlistRoot,(IUnknown*)&parent);
  538. pl->plid = parent.lID;
  539. playlists.Add(pl);
  540. return playlists.GetSize() - 1;
  541. }
  542. void NJBDevice::getTrackArtist(songid_t songid, wchar_t * buf, int len) {lstrcpyn(buf,((Song*)songid)->artist,len);}
  543. void NJBDevice::getTrackAlbum(songid_t songid, wchar_t * buf, int len) {lstrcpyn(buf,((Song*)songid)->album,len);}
  544. void NJBDevice::getTrackTitle(songid_t songid, wchar_t * buf, int len) {lstrcpyn(buf,((Song*)songid)->title,len);}
  545. void NJBDevice::getTrackGenre(songid_t songid, wchar_t * buf, int len) {lstrcpyn(buf,((Song*)songid)->genre,len);}
  546. int NJBDevice::getTrackTrackNum(songid_t songid) {return ((Song*)songid)->track;}
  547. int NJBDevice::getTrackDiscNum(songid_t songid) {return -1;}
  548. int NJBDevice::getTrackYear(songid_t songid) {return ((Song*)songid)->year;}
  549. __int64 NJBDevice::getTrackSize(songid_t songid) {return ((Song*)songid)->size;}
  550. int NJBDevice::getTrackLength(songid_t songid) {return ((Song*)songid)->length;}
  551. int NJBDevice::getTrackBitrate(songid_t songid) {return -1;}
  552. int NJBDevice::getTrackPlayCount(songid_t songid) {return -1;}
  553. int NJBDevice::getTrackRating(songid_t songid) {return -1;}
  554. __time64_t NJBDevice::getTrackLastPlayed(songid_t songid) {return -1;}
  555. __time64_t NJBDevice::getTrackLastUpdated(songid_t songid) {return -1;}
  556. void NJBDevice::getTrackExtraInfo(songid_t songid, const wchar_t * field, wchar_t * buf, int len) {
  557. if(!wcscmp(field,L"ext")) {
  558. Song * s = (Song *)songid;
  559. lstrcpyn(buf,(wchar_t*)AutoWide(s->codec),len);
  560. wchar_t * p = buf;
  561. while(p && *p) { *p=towlower(*p); p++; }
  562. }
  563. }
  564. void NJBDevice::setTrackArtist(songid_t songid, const wchar_t * value) {
  565. Song * s = (Song *)songid;
  566. lstrcpyn(s->artist,value,fieldlen);
  567. DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
  568. m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"ARTIST",2,(long)wcslen(value)*2+2,(IUnknown*)value);
  569. }
  570. void NJBDevice::setTrackAlbum(songid_t songid, const wchar_t * value) {
  571. Song * s = (Song *)songid;
  572. lstrcpyn(s->album,value,fieldlen);
  573. DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
  574. m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"ALBUM",2, (long)wcslen(value)*2+2,(IUnknown*)value);
  575. }
  576. void NJBDevice::setTrackTitle(songid_t songid, const wchar_t * value) {
  577. Song * s = (Song *)songid;
  578. lstrcpyn(s->title,value,fieldlen);
  579. DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
  580. m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"TITLE",2, (long)wcslen(value)*2+2,(IUnknown*)value);
  581. }
  582. void NJBDevice::setTrackGenre(songid_t songid, const wchar_t * value) {
  583. Song * s = (Song *)songid;
  584. lstrcpyn(s->genre,value,fieldlen);
  585. DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
  586. m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"GENRE",2, (long)wcslen(value)*2+2,(IUnknown*)value);
  587. }
  588. void NJBDevice::setTrackTrackNum(songid_t songid, int value) {
  589. Song * s = (Song *)songid;
  590. s->track = value;
  591. DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
  592. m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"TRACK NUM",1,sizeof(short),(IUnknown*)&value);
  593. }
  594. void NJBDevice::setTrackYear(songid_t songid, int value) {
  595. Song * s = (Song *)songid;
  596. s->year = value;
  597. DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
  598. m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"YEAR",1,sizeof(short),(IUnknown*)&value);
  599. }
  600. int NJBDevice::copyToHardDrive(songid_t s,wchar_t * path,void * callbackContext,void (*callback)(void * callbackContext, wchar_t * status),int * killswitch) {
  601. EnterCriticalSection(&csRevTransfer);
  602. callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING));
  603. Song * song = (Song*)s;
  604. wcscat(path,L".");
  605. wcscat(path,AutoWide(song->codec));
  606. wchar_t *p = wcsrchr(path,L'.');
  607. while(p && *p) { *p = towlower(*p); p++; }
  608. revTransferItem.callback = callback;
  609. revTransferItem.callbackContext = callbackContext;
  610. revTransferItem.killswitch = killswitch;
  611. revTransferItem.songid = &s;
  612. revTransferItem.file = path;
  613. revTransferItem.status = 0;
  614. PostMessage(messageWindow,WM_USER+1,0,0);
  615. while(revTransferItem.status==0) Sleep(10); // wait for transfer
  616. int ret = revTransferItem.status==1?0:-1;
  617. LeaveCriticalSection(&csRevTransfer);
  618. return ret;
  619. }
  620. intptr_t NJBDevice::extraActions(intptr_t param1, intptr_t param2, intptr_t param3,intptr_t param4) {
  621. switch(param1) {
  622. case DEVICE_SET_ICON:
  623. {
  624. MLTREEIMAGE * i = (MLTREEIMAGE*)param2;
  625. i->hinst = plugin.hDllInstance;
  626. i->resourceId = IDR_ZEN_ICON;
  627. }
  628. break;
  629. case DEVICE_SUPPORTED_METADATA:
  630. return 0x3ef;
  631. }
  632. return 0;
  633. }