ASDevice.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  1. #include "ASDevice.h"
  2. #include <vector>
  3. static void removebadchars(wchar_t *s) {
  4. while (s && *s)
  5. {
  6. if (*s == L'?' || *s == L'/' || *s == L'\\' || *s == L':' || *s == L'*' || *s == L'\"' || *s == L'<' || *s == L'>' || *s == L'|')
  7. *s = L'_';
  8. s = CharNextW(s);
  9. }
  10. }
  11. Playlist::Playlist(const wchar_t * path, LPCE_FIND_DATA f) {
  12. _snwprintf(fn,MAX_PATH,L"%s\\%s",path,f->cFileName);
  13. wchar_t * ext = wcsrchr(f->cFileName,L'.');
  14. if(ext) *ext=0;
  15. lstrcpyn(name,f->cFileName,fieldlen);
  16. }
  17. Playlist::Playlist(const wchar_t * name0) {
  18. lstrcpyn(name,name0,fieldlen);
  19. fn[0]=0;
  20. }
  21. #define ASSIGNLARGE(r,h,l) {ULARGE_INTEGER li; li.HighPart = h; li.LowPart = l; r=li.QuadPart;}
  22. Song::Song(const wchar_t * path0, LPCE_FIND_DATA f, bool video) : track(-1), video(video) {
  23. artist[0]=album[0]=title[0]=fn[0]=0;
  24. ASSIGNLARGE(size,f->nFileSizeHigh,f->nFileSizeLow);
  25. // first, fill in artist and album
  26. wchar_t *path = _wcsdup(path0);
  27. wchar_t *a = wcsrchr(path,L'\\');
  28. if(a && a-1 != path) {
  29. lstrcpyn(album,a+1,fieldlen);
  30. *a=0;
  31. a = wcsrchr(path,L'\\');
  32. if(a && a-1 != path) lstrcpyn(artist,a+1,fieldlen);
  33. }
  34. // now parse out the title
  35. _snwprintf(fn,MAX_PATH,L"%s\\%s",path0,f->cFileName);
  36. wchar_t * ext = wcsrchr(f->cFileName,L'.');
  37. if(ext) *ext=0;
  38. wchar_t * p = f->cFileName;
  39. if(memcmp(artist,p,wcslen(artist)*sizeof(wchar_t))==0) p+=wcslen(artist);
  40. while(p && *p && (*p==L'.' || *p==L'_' || *p==L'-' || *p==L' ')) p++;
  41. track = wcstoul(p,&p,10);
  42. while(p && *p && (*p==L'.' || *p==L'_' || *p==L'-' || *p==L' ')) p++;
  43. lstrcpyn(title,p,fieldlen);
  44. if(title[0]==0) lstrcpyn(title,f->cFileName,fieldlen);
  45. free(path);
  46. }
  47. Song::Song() : video(false) {}
  48. void ASDevice::Find(const wchar_t * path) {
  49. wchar_t fpath[MAX_PATH] = {0};
  50. wsprintf(fpath,L"%s\\*",path);
  51. CE_FIND_DATA f = {0};
  52. HANDLE h = pISession->CeFindFirstFile(fpath,&f);
  53. if(h == INVALID_HANDLE_VALUE) return;
  54. do {
  55. if(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  56. wchar_t path2[MAX_PATH] = {0};
  57. wsprintf(path2,L"%s\\%s",path,f.cFileName);
  58. Find(path2);
  59. }
  60. else FoundFile(path,&f);
  61. } while(pISession->CeFindNextFile(h,&f));
  62. pISession->CeFindClose(h);
  63. }
  64. void ASDevice::FoundFile(const wchar_t * path, LPCE_FIND_DATA f) {
  65. wchar_t * ext = wcsrchr(f->cFileName,L'.');
  66. if(!_wcsicmp(ext,L".mp3") || !_wcsicmp(ext,L".wma"))
  67. playlists[0]->songs.push_back(new Song(path,f,false));
  68. if(!_wcsicmp(ext,L".avi") || !_wcsicmp(ext,L".wmv") || !_wcsicmp(ext,L".asf") || !_wcsicmp(ext,L".mpg") || !_wcsicmp(ext,L".mpeg"))
  69. playlists[0]->songs.push_back(new Song(path,f,true));
  70. else if(!_wcsicmp(ext,L".asx"))
  71. playlists.push_back(new Playlist(path,f));
  72. }
  73. void fixTagsForXML(wchar_t* dest, const wchar_t *cstr, const int len)
  74. {
  75. int tindex = 0;
  76. wchar_t *temp = (wchar_t*)calloc(len, sizeof(wchar_t));
  77. for(int i=0;i<len && tindex<len;i++)
  78. {
  79. switch(cstr[i])
  80. {
  81. case(L'&'):
  82. if(tindex < len-5)
  83. {
  84. temp[tindex++] = '&';
  85. temp[tindex++] = 'a';
  86. temp[tindex++] = 'm';
  87. temp[tindex++] = 'p';
  88. temp[tindex] = ';';
  89. }
  90. else temp[tindex] = ' '; //no room
  91. break;
  92. case(L'<'):
  93. {
  94. if(tindex < len-4)
  95. {
  96. temp[tindex++] = '&';
  97. temp[tindex++] = 'l';
  98. temp[tindex++] = 't';
  99. temp[tindex] = ';';
  100. }
  101. else temp[tindex] = ' '; //no room
  102. break;
  103. }
  104. case(L'>'):
  105. {
  106. if(tindex < len-4)
  107. {
  108. temp[tindex++] = '&';
  109. temp[tindex++] = 'g';
  110. temp[tindex++] = 't';
  111. temp[tindex] = ';';
  112. }
  113. else temp[tindex] = ' '; //no room
  114. break;
  115. }
  116. case(L'\"'):
  117. {
  118. if(tindex < len-4)
  119. {
  120. temp[tindex++] = '&';
  121. temp[tindex++] = 'q';
  122. temp[tindex++] = 'u';
  123. temp[tindex++] = 'o';
  124. temp[tindex++] = 't';
  125. temp[tindex] = ';';
  126. }
  127. else temp[tindex] = ' '; //no room
  128. break;
  129. }
  130. case(L'\''):
  131. {
  132. if(tindex < len-4)
  133. {
  134. temp[tindex++] = '&';
  135. temp[tindex++] = 'a';
  136. temp[tindex++] = 'p';
  137. temp[tindex++] = 'o';
  138. temp[tindex++] = 's';
  139. temp[tindex] = ';';
  140. }
  141. else temp[tindex] = ' '; //no room
  142. break;
  143. }
  144. default:
  145. {
  146. temp[tindex] = cstr[i];
  147. break;
  148. }
  149. }
  150. if(cstr[i] == 0) break;
  151. tindex++;
  152. }
  153. wcsncpy(dest, temp, len);
  154. free(temp);
  155. }
  156. void ASDevice::WritePlaylist(Playlist * pl) {
  157. #define CePutws(x,h) pISession->CeWriteFile(h,x,(DWORD)wcslen(x)*sizeof(wchar_t),&w,NULL)
  158. #define CePuts(x,h) pISession->CeWriteFile(h,x,(DWORD)strlen(x)*sizeof(char),&w,NULL)
  159. HANDLE h = pISession->CeCreateFile(pl->fn,GENERIC_WRITE,FILE_SHARE_WRITE,NULL,TRUNCATE_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  160. if(h == INVALID_HANDLE_VALUE) return;
  161. std::vector<Song*> *songs = &pl->songs;
  162. int l=(int)songs->size();
  163. DWORD w;
  164. CePuts("<asx version=\"3.0\">\r\n",h);
  165. for(int j=0; j<l; j++) {
  166. CePuts("<entry><ref href=\"",h);
  167. wchar_t safe[fieldlen*2] = {0};
  168. fixTagsForXML(safe,songs->at(j)->fn,sizeof(safe)/sizeof(wchar_t));
  169. AutoChar fn(safe);
  170. CePuts(fn,h);
  171. CePuts("\"/></entry>\r\n",h);
  172. }
  173. CePuts("</asx>",h);
  174. pISession->CeCloseHandle(h);
  175. #undef CePutws
  176. #undef CePuts
  177. }
  178. struct mplSearch { bool operator()(Song*& a,Song*& b) { return _wcsicmp(a->fn,b->fn)<0; } };
  179. struct mplSearch2 { bool operator()(Song*& a,Song* b) { return _wcsicmp(a->fn,b->fn)<0; } };
  180. waServiceFactory *parserFactory;
  181. class plread : public ifc_xmlreadercallback {
  182. public:
  183. Playlist * pl;
  184. Playlist * mpl;
  185. plread(Playlist * pl,Playlist * mpl) : pl(pl),mpl(mpl) {}
  186. void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) {
  187. if(!wcscmp(xmlpath,L"ASX\fENTRY\fREF")) {
  188. const wchar_t* path = params->getItemValue(L"HREF");
  189. int l= (int)mpl->songs.size();
  190. Song s;
  191. lstrcpyn(s.fn,path,MAX_PATH);
  192. std::vector<Song*>::iterator p = std::lower_bound(mpl->songs.begin(),mpl->songs.end(),&s,mplSearch2());
  193. int f = (int)(p - mpl->songs.begin());
  194. if(f >= 0 && f < (int)mpl->songs.size()) {
  195. Song * found = mpl->songs[f];
  196. if(!_wcsicmp(found->fn,s.fn)) pl->songs.push_back(found);
  197. }
  198. }
  199. }
  200. RECVS_DISPATCH;
  201. };
  202. #define CBCLASS plread
  203. START_DISPATCH;
  204. VCB(ONSTARTELEMENT, StartTag)
  205. END_DISPATCH;
  206. #undef CBCLASS
  207. void ASDevice::ReadPlaylist(Playlist * pl) {
  208. if(!parserFactory) return;
  209. obj_xml * parser = (obj_xml *)parserFactory->getInterface();
  210. if(!parser) return;
  211. HANDLE h = pISession->CeCreateFile(pl->fn,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
  212. if(h == INVALID_HANDLE_VALUE) { parserFactory->releaseInterface(parser); return; }
  213. plread cb(pl,playlists[0]);
  214. parser->xmlreader_open();
  215. parser->xmlreader_registerCallback(L"ASX\fENTRY\f*",&cb);
  216. for(;;) {
  217. char buf[32768] = {0};
  218. DWORD read = 0;
  219. pISession->CeReadFile(h,buf,sizeof(buf),&read,NULL);
  220. if(read == 0) break;
  221. parser->xmlreader_feed(buf,read);
  222. }
  223. parserFactory->releaseInterface(parser);
  224. pISession->CeCloseHandle(h);
  225. }
  226. static void findStorageCard(IRAPISession *pISession,wchar_t *storageCard) {
  227. ULARGE_INTEGER fa={0},rootTotal={0},ft={0};
  228. pISession->CeGetDiskFreeSpaceEx(L"\\",&fa,&rootTotal,&ft);
  229. wchar_t *fpath = L"\\*";
  230. CE_FIND_DATA f;
  231. HANDLE h = pISession->CeFindFirstFile(fpath,&f);
  232. if(h == INVALID_HANDLE_VALUE) return;
  233. do {
  234. if(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY/* && wcscmp(f.cFileName,L"Storage Card")*/) {
  235. ULARGE_INTEGER folderTotal={0};
  236. wchar_t path[MAX_PATH] = L"\\";
  237. wcscat(path,f.cFileName);
  238. pISession->CeGetDiskFreeSpaceEx(path,&fa,&folderTotal,&ft);
  239. if(folderTotal.QuadPart > rootTotal.QuadPart) {
  240. rootTotal = folderTotal;
  241. wcsncpy(storageCard,path,MAX_PATH);
  242. }
  243. }
  244. } while(pISession->CeFindNextFile(h,&f));
  245. pISession->CeFindClose(h);
  246. }
  247. ASDevice::ASDevice(IRAPIDevice *pIDevice,IRAPISession *pISession) : pIDevice(pIDevice), pISession(pISession), transferQueueSize(0) {
  248. pIDevice->AddRef();
  249. pIDevice->GetDeviceInfo(&devInfo);
  250. pmpDeviceLoading load={this,0};
  251. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)&load,PMP_IPC_DEVICELOADING);
  252. if(load.UpdateCaption) {
  253. wchar_t buf[200]=L"";
  254. wsprintf(buf,WASABI_API_LNGSTRINGW(IDS_LOADING),devInfo.bstrName);
  255. load.UpdateCaption(buf,load.context);
  256. }
  257. //find where playlists and music are stored...
  258. wchar_t storageCard[MAX_PATH]=L"";
  259. findStorageCard(pISession,storageCard);
  260. wsprintf(musicFolder,L"%s\\Music",storageCard);
  261. wsprintf(videoFolder,L"%s\\My Documents\\My Videos",storageCard);
  262. wsprintf(playlistFolder,L"%s\\Playlists",storageCard);
  263. wcsncpy(playlistFormat,L".asx",16);
  264. // default values found. Fill in real values
  265. {
  266. wchar_t inifile[MAX_PATH] = {0};
  267. const char * iniDirectory = (const char*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETINIDIRECTORY);
  268. wchar_t name[256] = {0};
  269. lstrcpyn(name,devInfo.bstrName,256);
  270. removebadchars(name);
  271. wsprintf(inifile,L"%s\\Plugins\\ml\\ml_pmp_device_%s.ini",(wchar_t*)AutoWide(iniDirectory),name);
  272. wchar_t * def = _wcsdup(musicFolder);
  273. GetPrivateProfileString(L"pmp_activesync",L"musicfolder",def,musicFolder,MAX_PATH,inifile);
  274. free(def); def = _wcsdup(videoFolder);
  275. GetPrivateProfileString(L"pmp_activesync",L"videofolder",def,videoFolder,MAX_PATH,inifile);
  276. free(def); def = _wcsdup(playlistFolder);
  277. GetPrivateProfileString(L"pmp_activesync",L"playlistfolder",def,playlistFolder,MAX_PATH,inifile);
  278. free(def);
  279. }
  280. playlists.push_back(new Playlist(devInfo.bstrName));
  281. Find(musicFolder);
  282. Find(videoFolder);
  283. Find(playlistFolder);
  284. std::sort(playlists[0]->songs.begin(),playlists[0]->songs.end(),mplSearch());
  285. parserFactory = plugin.service->service_getServiceByGuid(obj_xmlGUID);
  286. for(unsigned int i=1; i<playlists.size(); i++)
  287. ReadPlaylist(playlists[i]);
  288. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICECONNECTED);
  289. transcoder = (Transcoder*)SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_GET_TRANSCODER);
  290. transcoder->AddAcceptableFormat(L"mp3");
  291. transcoder->AddAcceptableFormat(L"wma");
  292. transcoder->AddAcceptableFormat(L"wmv");
  293. transcoder->AddAcceptableFormat(L"avi");
  294. transcoder->AddAcceptableFormat(L"asf");
  295. transcoder->AddAcceptableFormat(L"mpeg");
  296. transcoder->AddAcceptableFormat(L"mpg");
  297. }
  298. ASDevice::~ASDevice()
  299. {
  300. Playlist *mpl = playlists[ 0 ];
  301. unsigned int l = (unsigned int)mpl->songs.size();
  302. for ( unsigned int i = 0; i < l; i++ )
  303. {
  304. delete mpl->songs[ i ];
  305. }
  306. for ( unsigned int j = 0; j < playlists.size(); j++ )
  307. {
  308. delete playlists[ j ];
  309. }
  310. for ( int k = 0; k < devices.size(); k++ )
  311. {
  312. if ( devices[ k ] == this )
  313. {
  314. devices.erase( devices.begin() + k );
  315. k--;
  316. }
  317. }
  318. pIDevice->Release();
  319. pISession->Release();
  320. SysFreeString( devInfo.bstrName );
  321. SysFreeString( devInfo.bstrPlatform );
  322. SendMessage( plugin.hwndPortablesParent, WM_PMP_IPC, (WPARAM)transcoder, PMP_IPC_RELEASE_TRANSCODER );
  323. }
  324. __int64 ASDevice::getDeviceCapacityAvailable() {
  325. if(devInfo.dwOsVersionMajor >= 5) {
  326. ULARGE_INTEGER fa={0},t={0},ft={0};
  327. pISession->CeGetDiskFreeSpaceEx(musicFolder,&fa,&t,&ft);
  328. return fa.QuadPart;
  329. } else {
  330. STORE_INFORMATION s;
  331. pISession->CeGetStoreInformation(&s);
  332. return s.dwFreeSize;
  333. }
  334. }
  335. __int64 ASDevice::getDeviceCapacityTotal() {
  336. if(devInfo.dwOsVersionMajor >= 5) {
  337. ULARGE_INTEGER fa={0},t={0},ft={0};
  338. pISession->CeGetDiskFreeSpaceEx(musicFolder,&fa,&t,&ft);
  339. return t.QuadPart;
  340. } else {
  341. STORE_INFORMATION s;
  342. pISession->CeGetStoreInformation(&s);
  343. return s.dwStoreSize;
  344. }
  345. }
  346. void ASDevice::Eject() {
  347. ejectedDevice *e = (ejectedDevice *)calloc(1, sizeof(ejectedDevice));
  348. e->id = devInfo.DeviceId;
  349. e->marked = true;
  350. ejected.push_back(e);
  351. Close();
  352. }
  353. void ASDevice::Close() {
  354. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICEDISCONNECTED);
  355. delete this;
  356. }
  357. int ASDevice::transferTrackToDevice(const itemRecordW * track,void * callbackContext,void (*callback)(void * callbackContext, wchar_t * status),songid_t * songid,int * killswitch) {
  358. wchar_t ext[10]={0};
  359. wchar_t file[2048]={0};
  360. wcsncpy(file,track->filename,2048);
  361. {wchar_t * e = wcsrchr(file,L'.'); if(e) wcsncpy(ext,e+1,10);}
  362. bool deletefile = false;
  363. if(transcoder->ShouldTranscode(file)) {
  364. wchar_t newfile[MAX_PATH] = {0};
  365. transcoder->CanTranscode(file,ext);
  366. transcoder->GetTempFilePath(ext,newfile);
  367. if(transcoder->TranscodeFile(file,newfile,killswitch,callback,callbackContext)) return -1;
  368. wcsncpy(file,newfile,2048);
  369. deletefile=true;
  370. }
  371. callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING));
  372. bool video = !_wcsicmp(ext,L"wmv") || !_wcsicmp(ext,L"avi");
  373. int len = (int)(wcslen(musicFolder)+wcslen(track->artist)+wcslen(track->album)+wcslen(track->title)+100);
  374. wchar_t *path = (wchar_t*)calloc(len, sizeof(wchar_t));
  375. wchar_t *artist = _wcsdup(track->artist);
  376. wchar_t *album = _wcsdup(track->album);
  377. wchar_t *title = _wcsdup(track->title);
  378. removebadchars(artist);
  379. removebadchars(album);
  380. removebadchars(title);
  381. if(video) {
  382. wcsncpy(path,videoFolder,len);
  383. pISession->CeCreateDirectory(path,NULL);
  384. wsprintf(path+wcslen(path),L"\\%s - %s.%s",artist,title,ext);
  385. } else {
  386. wcsncpy(path,musicFolder,len);
  387. pISession->CeCreateDirectory(path,NULL);
  388. wcscat(path,L"\\");
  389. wcscat(path,artist);
  390. pISession->CeCreateDirectory(path,NULL);
  391. wcscat(path,L"\\");
  392. wcscat(path,album);
  393. pISession->CeCreateDirectory(path,NULL);
  394. wsprintf(path+wcslen(path),L"\\%02d - %s.%s",track->track,title,ext);
  395. }
  396. free(artist); free(album); free(title);
  397. FILE *f = _wfopen(file,L"rb");
  398. if(!f) { callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_CANNOT_OPEN_LOCAL_FILE)); return -1; }
  399. HANDLE h = pISession->CeCreateFile(path,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
  400. if(h == INVALID_HANDLE_VALUE) {
  401. callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_CANNOT_OPEN_FILE_ON_DEVICE));
  402. fclose(f);
  403. return -1;
  404. }
  405. fseek(f,0,2);
  406. int error=0;
  407. int size = ftell(f);
  408. int pc = size/100;
  409. fseek(f,0,0);
  410. int written=0,lastupdate=0;
  411. for(;;) {
  412. char buf[32768] = {0};
  413. int l = (int)fread(buf,1,sizeof(buf),f);
  414. if(!l) break;
  415. DWORD wl=0;
  416. pISession->CeWriteFile(h,buf,l,&wl,NULL);
  417. if(wl != l) {
  418. callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_ERROR_WRITING_FILE));
  419. error=1;
  420. break;
  421. }
  422. written += l;
  423. if(written - lastupdate > pc) {
  424. lastupdate = written;
  425. wchar_t buf[100] = {0};
  426. wsprintf(buf,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING_PERCENT),written/(size/100));
  427. callback(callbackContext,buf);
  428. }
  429. }
  430. fclose(f);
  431. pISession->CeCloseHandle(h);
  432. if(deletefile) _wunlink(file);
  433. if(!error) {
  434. callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_DONE));
  435. Song * s = new Song;
  436. lstrcpyn(s->fn,path,MAX_PATH);
  437. lstrcpyn(s->album,track->album,fieldlen);
  438. lstrcpyn(s->artist,track->artist,fieldlen);
  439. lstrcpyn(s->title,track->title,fieldlen);
  440. s->track = track->track;
  441. s->size = size;
  442. s->video = video;
  443. *songid = (songid_t)s;
  444. }
  445. free(path);
  446. return error?-1:0;
  447. }
  448. static __int64 fileSize(wchar_t * filename)
  449. {
  450. WIN32_FIND_DATA f={0};
  451. HANDLE h = FindFirstFileW(filename,&f);
  452. if(h == INVALID_HANDLE_VALUE) return -1;
  453. FindClose(h);
  454. ULARGE_INTEGER i;
  455. i.HighPart = f.nFileSizeHigh;
  456. i.LowPart = f.nFileSizeLow;
  457. return i.QuadPart;
  458. }
  459. static bool extentionSupported(wchar_t * ext) {
  460. if(!ext) return false;
  461. bool supported=false;
  462. if(!_wcsicmp(ext,L".mp3") || !_wcsicmp(ext,L".wma")) supported=true;
  463. if(!_wcsicmp(ext,L".avi") || !_wcsicmp(ext,L".wmv")) supported=true;
  464. return supported;
  465. }
  466. int ASDevice::trackAddedToTransferQueue(const itemRecordW * track) {
  467. __int64 s = getTrackSizeOnDevice(track);
  468. if(!s) return -2;
  469. __int64 avail = getDeviceCapacityAvailable();
  470. __int64 cmp = transferQueueSize;
  471. cmp += s;
  472. if(cmp > avail) return -1;
  473. else {
  474. transferQueueSize += s;
  475. return 0;
  476. }
  477. }
  478. void ASDevice::trackRemovedFromTransferQueue(const itemRecordW * track) {
  479. transferQueueSize -= getTrackSizeOnDevice(track);
  480. }
  481. __int64 ASDevice::getTrackSizeOnDevice(const itemRecordW * track) {
  482. if(transcoder->ShouldTranscode(track->filename)) {
  483. int k = transcoder->CanTranscode(track->filename);
  484. if(k != -1 && k != 0) return k;
  485. }
  486. wchar_t * ext = wcsrchr(track->filename,L'.');
  487. if(!extentionSupported(ext)) return 0;
  488. return fileSize(track->filename);
  489. }
  490. void ASDevice::deleteTrack(songid_t songid) {
  491. Song * song = (Song*)songid;
  492. if(!pISession->CeDeleteFile(song->fn)) return;
  493. for(unsigned int i=0; i<playlists.size(); i++)
  494. {
  495. unsigned int l = (unsigned int)playlists[i]->songs.size();
  496. for(int j=0; j<l; j++)
  497. {
  498. if(playlists[i]->songs[j] == song)
  499. {
  500. playlists[i]->songs.erase(playlists[i]->songs.begin() + j);
  501. j--;
  502. l--;
  503. playlists[i]->dirty=true;
  504. }
  505. }
  506. }
  507. delete song;
  508. }
  509. void ASDevice::commitChanges() {
  510. for(unsigned int i=1; i<playlists.size(); i++) if(playlists[i]->dirty) { WritePlaylist(playlists[i]); playlists[i]->dirty=false; }
  511. }
  512. int ASDevice::getPlaylistCount() { return (int)playlists.size(); }
  513. void ASDevice::getPlaylistName(int playlistnumber, wchar_t * buf, int len) { lstrcpyn(buf,playlists[playlistnumber]->name,len); }
  514. int ASDevice::getPlaylistLength(int playlistnumber) { return (int)playlists[playlistnumber]->songs.size(); }
  515. songid_t ASDevice::getPlaylistTrack(int playlistnumber,int songnum) { return (songid_t)playlists[playlistnumber]->songs[songnum]; }
  516. void ASDevice::setPlaylistName(int playlistnumber, const wchar_t * buf) {
  517. Playlist * pl = playlists[playlistnumber];
  518. lstrcpyn(pl->name,buf,fieldlen);
  519. wchar_t * oldname = _wcsdup(pl->fn);
  520. wchar_t * name = _wcsdup(buf);
  521. removebadchars(name);
  522. wsprintf(pl->fn,L"%s\\%s.%s",playlistFolder,name,playlistFormat);
  523. free(name);
  524. pISession->CeMoveFile(oldname,pl->fn);
  525. free(oldname);
  526. }
  527. void ASDevice::playlistSwapItems(int playlistnumber, int posA, int posB) {
  528. std::vector<Song*> &songs = playlists[playlistnumber]->songs;
  529. Song * a = songs[posA];
  530. Song * b = songs[posB];
  531. songs[posA] = b;
  532. songs[posB] = a;
  533. playlists[playlistnumber]->dirty=true;
  534. }
  535. #define CMPFIELDS(x) { int v = lstrcmpi(a->x,b->x); if(v) return v<0; }
  536. #define CMPINTFIELDS(x) { int v = a->x-b->x; if(v) return v<0; }
  537. typedef struct PlaylistItemSort {
  538. int use_by;
  539. bool operator()(Song*& a,Song*& b) {
  540. int x;
  541. for (x = 0; x < 4; x ++)
  542. {
  543. if (use_by == SORTBY_TITLE) // title -> artist -> album -> disc -> track
  544. {
  545. CMPFIELDS(title);
  546. use_by=SORTBY_ARTIST;
  547. }
  548. else if (use_by == SORTBY_ARTIST) // artist -> album -> disc -> track -> title
  549. {
  550. CMPFIELDS(artist);
  551. use_by=SORTBY_ALBUM;
  552. }
  553. else if (use_by == SORTBY_ALBUM) // album -> disc -> track -> title -> artist
  554. {
  555. CMPFIELDS(album);
  556. use_by=SORTBY_TRACKNUM;
  557. }
  558. else if (use_by == SORTBY_TRACKNUM) // track -> title -> artist -> album -> disc
  559. {
  560. CMPINTFIELDS(track);
  561. use_by=SORTBY_TITLE;
  562. }
  563. else break; // no sort order?
  564. }
  565. return false;
  566. }
  567. } PlaylistItemSort;
  568. #undef CMPFIELDS
  569. #undef CMPINTFIELDS
  570. void ASDevice::sortPlaylist(int playlistnumber, int sortBy) {
  571. PlaylistItemSort sort;
  572. sort.use_by = sortBy;
  573. std::sort(playlists[playlistnumber]->songs.begin(),playlists[playlistnumber]->songs.end(),sort);
  574. playlists[playlistnumber]->dirty=true;
  575. }
  576. void ASDevice::addTrackToPlaylist(int playlistnumber, songid_t songid){
  577. playlists[playlistnumber]->songs.push_back((Song*)songid);
  578. playlists[playlistnumber]->dirty=true;
  579. }
  580. void ASDevice::removeTrackFromPlaylist(int playlistnumber, int songnum) {
  581. playlists[playlistnumber]->songs.erase(playlists[playlistnumber]->songs.begin() + songnum);
  582. playlists[playlistnumber]->dirty=true;
  583. }
  584. void ASDevice::deletePlaylist(int playlistnumber) {
  585. pISession->CeDeleteFile(playlists[playlistnumber]->fn);
  586. delete playlists[playlistnumber];
  587. playlists.erase(playlists.begin() + playlistnumber);
  588. }
  589. int ASDevice::newPlaylist(const wchar_t * name0) {
  590. pISession->CeCreateDirectory(playlistFolder,NULL);
  591. Playlist* pl = new Playlist(name0);
  592. wchar_t * name = _wcsdup(name0);
  593. removebadchars(name);
  594. wsprintf(pl->fn,L"%s\\%s.%s",playlistFolder,name,playlistFormat);
  595. free(name);
  596. pl->dirty=true;
  597. playlists.push_back(pl);
  598. return (int)playlists.size()-1;
  599. }
  600. void ASDevice::getTrackArtist(songid_t songid, wchar_t * buf, int len) { lstrcpyn(buf,((Song*)songid)->artist,len); }
  601. void ASDevice::getTrackAlbum(songid_t songid, wchar_t * buf, int len) { lstrcpyn(buf,((Song*)songid)->album,len); }
  602. void ASDevice::getTrackTitle(songid_t songid, wchar_t * buf, int len) { lstrcpyn(buf,((Song*)songid)->title,len); }
  603. int ASDevice::getTrackTrackNum(songid_t songid) { return ((Song*)songid)->track; }
  604. __int64 ASDevice::getTrackSize(songid_t songid) { return ((Song*)songid)->size; }
  605. void ASDevice::getTrackExtraInfo(songid_t songid, const wchar_t * field, wchar_t * buf, int len) {
  606. if(!wcscmp(field,FIELD_EXTENSION)) {
  607. Song * s = (Song *)songid;
  608. wchar_t * ext = wcsrchr(s->fn,L'.');
  609. if(ext) { ext++; lstrcpyn(buf,ext,len); }
  610. }
  611. }
  612. int ASDevice::copyToHardDrive(songid_t song,wchar_t * path,void * callbackContext,void (*callback)(void * callbackContext, wchar_t * status),int * killswitch) {
  613. Song * s = (Song*)song;
  614. wchar_t * ext = wcsrchr(s->fn,L'.');
  615. if(ext && wcslen(ext)<10) wcscat(path,ext);
  616. callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING));
  617. FILE * f = _wfopen(path,L"wb");
  618. if(!f) {
  619. callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_CANNOT_OPEN_DESTINATION));
  620. return -1;
  621. }
  622. HANDLE h = pISession->CeCreateFile(s->fn,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
  623. if(h == INVALID_HANDLE_VALUE) {
  624. callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_CANNOT_OPEN_FILE_ON_DEVICE));
  625. fclose(f);
  626. return -1;
  627. }
  628. int error=0;
  629. int pc = (int)(s->size/100);
  630. int written=0,lastupdate=0;
  631. for(;;) {
  632. char buf[32768] = {0};
  633. DWORD read=0;
  634. pISession->CeReadFile(h,buf,sizeof(buf),&read,NULL);
  635. if(!read) break;
  636. int wr = (int)fwrite(buf,1,read,f);
  637. if(wr != read) {
  638. callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_ERROR_WRITING_FILE));
  639. error=1;
  640. break;
  641. }
  642. written += read;
  643. if(written - lastupdate > pc) {
  644. lastupdate = written;
  645. wchar_t buf[100] = {0};
  646. wsprintf(buf,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING),written/(s->size/100));
  647. callback(callbackContext,buf);
  648. }
  649. }
  650. pISession->CeCloseHandle(h);
  651. fclose(f);
  652. if(!error) {
  653. callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_DONE));
  654. return 0;
  655. } else return -1;
  656. }
  657. static BOOL CALLBACK config_dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
  658. intptr_t ASDevice::extraActions(intptr_t param1, intptr_t param2, intptr_t param3,intptr_t param4) {
  659. switch(param1) {
  660. case DEVICE_SET_ICON:
  661. {
  662. MLTREEIMAGE * i = (MLTREEIMAGE*)param2;
  663. i->hinst = plugin.hDllInstance;
  664. i->resourceId = IDR_ACTIVESYNC_ICON;
  665. }
  666. break;
  667. case DEVICE_SUPPORTED_METADATA: return 0x8f;
  668. case DEVICE_DOES_NOT_SUPPORT_EDITING_METADATA: return 1;
  669. case DEVICE_GET_PREFS_DIALOG:
  670. if(param3 == 0) {
  671. pref_tab * p = (pref_tab *)param2;
  672. p->hinst = WASABI_API_LNG_HINST;
  673. p->dlg_proc = (DLGPROC)config_dialogProc;
  674. p->res_id = IDD_CONFIG;
  675. lstrcpyn(p->title,WASABI_API_LNGSTRINGW(IDS_ADVANCED),100);
  676. }
  677. break;
  678. case DEVICE_SUPPORTS_VIDEO:
  679. return 1;
  680. }
  681. return 0;
  682. }
  683. static BOOL CALLBACK config_dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) {
  684. static ASDevice * dev;
  685. switch(uMsg) {
  686. case WM_INITDIALOG:
  687. {
  688. prefsParam* p = (prefsParam*)lParam;
  689. dev = (ASDevice*)p->dev;
  690. p->config_tab_init(hwndDlg,p->parent);
  691. SetDlgItemText(hwndDlg,IDC_FOLDER_MUSIC,dev->musicFolder);
  692. SetDlgItemText(hwndDlg,IDC_FOLDER_VIDEO,dev->videoFolder);
  693. SetDlgItemText(hwndDlg,IDC_FOLDER_PLAYLIST,dev->playlistFolder);
  694. }
  695. break;
  696. case WM_DESTROY:
  697. {
  698. GetDlgItemText(hwndDlg,IDC_FOLDER_MUSIC,dev->musicFolder,MAX_PATH);
  699. GetDlgItemText(hwndDlg,IDC_FOLDER_VIDEO,dev->videoFolder,MAX_PATH);
  700. GetDlgItemText(hwndDlg,IDC_FOLDER_PLAYLIST,dev->playlistFolder,MAX_PATH);
  701. wchar_t *inifile = (wchar_t*)SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)dev,PMP_IPC_GET_INI_FILE);
  702. #if 1
  703. wchar_t inifil[MAX_PATH] = {0};
  704. if(!inifile) {
  705. inifile=inifil;
  706. const char * iniDirectory = (const char*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETINIDIRECTORY);
  707. wchar_t name[256] = {0};
  708. lstrcpyn(name,dev->devInfo.bstrName,256);
  709. removebadchars(name);
  710. wsprintf(inifile,L"%s\\Plugins\\ml\\ml_pmp_device_%s.ini",(wchar_t*)AutoWide(iniDirectory),name);
  711. }
  712. #endif
  713. if(inifile) {
  714. WritePrivateProfileString(L"pmp_activesync",L"musicfolder",dev->musicFolder,inifile);
  715. WritePrivateProfileString(L"pmp_activesync",L"videofolder",dev->videoFolder,inifile);
  716. WritePrivateProfileString(L"pmp_activesync",L"playlistfolder",dev->playlistFolder,inifile);
  717. }
  718. }
  719. break;
  720. case WM_COMMAND:
  721. switch(LOWORD(wParam)) {
  722. case IDC_RESCAN:
  723. config_dialogProc(hwndDlg,WM_DESTROY,0,0);
  724. dev->Close();
  725. break;
  726. }
  727. break;
  728. }
  729. return 0;
  730. }