1
0

iPodDevice.cpp 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795
  1. #include "iPodDevice.h"
  2. //#include <assert.h>
  3. #include "..\..\General\gen_ml/itemlist.h"
  4. #include "../nu/AutoWide.h"
  5. #include "../nu/AutoChar.h"
  6. #include "../Winamp/wa_ipc.h"
  7. #include <math.h>
  8. #include "../Agave/Language/api_language.h"
  9. #include "api.h"
  10. #include <tataki/bitmap/bitmap.h>
  11. #include <tataki/canvas/bltcanvas.h>
  12. #include "iPodSD.h"
  13. #include <strsafe.h>
  14. #include <shlwapi.h>
  15. //#include "../nu/combobox.h"
  16. // needed to query for replaygain stuff
  17. static const GUID playbackConfigGroupGUID =
  18. { 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
  19. extern PMPDevicePlugin plugin;
  20. extern time_t mactime_to_wintime (const unsigned long mactime);
  21. extern unsigned long wintime_to_mactime (const __time64_t time);
  22. extern wchar_t* UTF8_to_UTF16(char *str);
  23. extern BOOL EjectVolume(TCHAR cDriveLetter);
  24. extern std::vector<iPodDevice*> iPods;
  25. static __int64 fileSize(const wchar_t * filename);
  26. iPodDevice::iPodDevice(char deviceDrive)
  27. {
  28. fwid=0;
  29. info=0;
  30. artdb=0;
  31. gapscanner=0;
  32. transcoder=0;
  33. image16 = 0;
  34. image160 = 0;
  35. drive = deviceDrive;
  36. driveW = ((int)(drive - 'A')) + L'A';
  37. transferQueueLength=0;
  38. db=NULL;
  39. srand(GetTickCount());
  40. dirnum = rand() % 20;
  41. {
  42. wchar_t artwork[] = {driveW,L":\\iPod_Control\\Artwork"};
  43. _wmkdir(artwork);
  44. wchar_t device[] = {driveW,L":\\iPod_Control\\Device"};
  45. _wmkdir(device);
  46. }
  47. iPods.push_back(this);
  48. pmpDeviceLoading load;
  49. load.dev = this;
  50. load.UpdateCaption = NULL;
  51. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)&load,PMP_IPC_DEVICELOADING);
  52. if(load.UpdateCaption)
  53. {
  54. load.UpdateCaption(WASABI_API_LNGSTRINGW(IDS_IPOD_LOADING),load.context);
  55. }
  56. info = GetiPodInfo(driveW);
  57. // Get the artwork formats that are supported
  58. if(info && info->numberOfSupportedFormats >0)
  59. {
  60. // formats are already available, read from the sysinfo xml
  61. // just use them
  62. for (int i=0; i<info->numberOfSupportedFormats; i++)
  63. {
  64. thumbs.push_back(&info->supportedArtworkFormats[i]);
  65. }
  66. }
  67. else
  68. {
  69. // revert to the static list of supported artwork formats
  70. const ArtworkFormat* art = GetArtworkFormats(info);
  71. if(art) for(int i=0; art[i].type != THUMB_INVALID; i++)
  72. {
  73. if(art[i].type >= THUMB_COVER_SMALL && art[i].type <= THUMB_COVER_LARGE)
  74. thumbs.push_back(&art[i]);
  75. }
  76. }
  77. if(!info || parseiTunesDB(thumbs.size()!=0) < 0)
  78. {
  79. //iPods.eraseObject(this);
  80. auto it = std::find(iPods.begin(), iPods.end(), this);
  81. if (it != iPods.end())
  82. {
  83. iPods.erase(it);
  84. }
  85. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICEDISCONNECTED);
  86. delete this;
  87. return;
  88. }
  89. image16 = info->image16;
  90. image160 = info->image160;
  91. db->mhsdplaylists->mhlp->SortPlaylists();
  92. int n = db->mhsdplaylists->mhlp->GetChildrenCount();
  93. for(int i=0; i<n; i++)
  94. playlists.Add(db->mhsdplaylists->mhlp->GetPlaylist(i));
  95. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICECONNECTED);
  96. transcoder = (Transcoder*)SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(WPARAM)this,PMP_IPC_GET_TRANSCODER);
  97. if(transcoder)
  98. {
  99. transcoder->AddAcceptableFormat(mmioFOURCC('M','4','A',' '));
  100. transcoder->AddAcceptableFormat(L"mp3");
  101. //transcoder->AddAcceptableFormat(L"wav");
  102. transcoder->AddAcceptableFormat(L"m4v");
  103. transcoder->AddAcceptableFormat(L"m4b");
  104. transcoder->AddAcceptableFormat(L"aa\0\0");
  105. transcoder->AddAcceptableFormat(L"mp4");
  106. }
  107. if (info->fwid)
  108. {
  109. fwid = (uint8_t *)malloc(8);
  110. memcpy(fwid, info->fwid, 8);
  111. }
  112. }
  113. iPodDevice::~iPodDevice() {
  114. if(gapscanner) SendMessage(gapscanner,WM_CLOSE,0,0);
  115. if(db) delete db; db=NULL;
  116. char lockPath[] = {drive, ":\\iPod_Control\\iTunes\\iTunesLock"};
  117. _unlink(lockPath);
  118. if(transcoder) SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(WPARAM)transcoder,PMP_IPC_RELEASE_TRANSCODER);
  119. delete info;
  120. info=0;
  121. free(fwid);
  122. }
  123. static unsigned char * readFile(char * path, int &len) {
  124. FILE * f = fopen(path,"rb");
  125. if(!f) return 0;
  126. fseek(f,0,2); //seek to end
  127. int l = ftell(f); //length of file
  128. unsigned char * data = (unsigned char *)malloc(l);
  129. if(!data)
  130. {
  131. fclose(f);
  132. return 0;
  133. }
  134. fseek(f,0,0);
  135. if(fread(data,1,l,f) != l) { fclose(f); free(data); return 0; }
  136. fclose(f);
  137. len = l;
  138. return data;
  139. }
  140. static unsigned char * readFile(char * path) {
  141. int l=0;
  142. return readFile(path,l);
  143. }
  144. static HANDLE iTunesLock(char drive) { // returns false for unable to aquire lock.
  145. char lockPath[] = {drive, ":\\iPod_Control\\iTunes\\iTunesLock"};
  146. HANDLE h=CreateFileA(lockPath,GENERIC_READ,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  147. if(h == INVALID_HANDLE_VALUE) return h;
  148. while(!LockFile(h,0,0,0,0)) Sleep(50);
  149. return h;
  150. }
  151. static void iTunesUnlock(HANDLE h) {
  152. UnlockFile(h,0,0,0,0);
  153. CloseHandle(h);
  154. }
  155. int iPodDevice::parseiTunesDB(bool parseArt) {
  156. HANDLE hLock = iTunesLock(drive);
  157. if(hLock == INVALID_HANDLE_VALUE) return -1;
  158. char dbPath[] = "x:\\iPod_Control\\iTunes\\iTunesDB";
  159. dbPath[0]=drive;
  160. unsigned char * data = readFile(dbPath);
  161. if(data==0) {
  162. iTunesUnlock(hLock);
  163. return -1;
  164. }
  165. db = new iPod_mhbd;
  166. int ret = db->parse(data);
  167. free(data);
  168. bool changed=false;
  169. char playcounts[] = "x:\\iPod_Control\\iTunes\\Play Counts";
  170. playcounts[0]=drive;
  171. data = readFile(playcounts);
  172. if(data) {
  173. iPod_mhdp * mhdp = new iPod_mhdp;
  174. int l = db->mhsdsongs->mhlt->GetChildrenCount();
  175. if(mhdp->parse(data) == l) {
  176. changed=true;
  177. for(int i=0; i<l; i++) {
  178. PCEntry p = mhdp->GetPlayCount(i);
  179. iPod_mhit * mhit = db->mhsdsongs->mhlt->GetTrack(i);
  180. if(!mhit) continue;
  181. mhit->bookmarktime = p.bookmarktime;
  182. mhit->lastplayedtime = p.lastplayedtime;
  183. mhit->stars = (unsigned char)p.stars;
  184. mhit->playcount = p.playcount;
  185. mhit->skipcount = p.skipcount;
  186. mhit->skippedtime = p.skippedtime;
  187. }
  188. }
  189. delete mhdp;
  190. free(data);
  191. }
  192. _unlink(playcounts);
  193. db->mhsdplaylists->mhlp->GetPlaylist(0)->mhit = &db->mhsdsongs->mhlt->mhit;
  194. char otg[] = "x:\\iPod_Control\\iTunes\\OTGPlaylistInfo";
  195. otg[0]=drive;
  196. data = readFile(otg);
  197. if(data) {
  198. iPod_mhpo * mhpo = new iPod_mhpo;
  199. mhpo->parse(data);
  200. mhpo->CreatePlaylistFromOTG(db,L"On The Go");
  201. changed=true;
  202. delete mhpo;
  203. free(data);
  204. }
  205. _unlink(otg);
  206. iTunesUnlock(hLock);
  207. if(changed) writeiTunesDB();
  208. if(parseArt) {
  209. char dbPath[] = "x:\\iPod_Control\\Artwork\\ArtworkDB";
  210. dbPath[0]=drive;
  211. int l=0;
  212. unsigned char * data = readFile(dbPath,l);
  213. bool createNew=false;
  214. if(data) {
  215. artdb = new ArtDB();
  216. int r = artdb->parse(data,l,driveW);
  217. if(r<0) {
  218. delete artdb;
  219. artdb=NULL;
  220. }
  221. free(data);
  222. } else createNew=true;
  223. if(createNew) {
  224. char dir[] = {drive,":\\iPod_Control\\Artwork"};
  225. CreateDirectoryA(dir,NULL);
  226. artdb = new ArtDB();
  227. artdb->makeEmptyDB(driveW);
  228. }
  229. }
  230. return ret;
  231. }
  232. extern bool ParseSysInfoXML(wchar_t drive_letter, char * xml, int xmllen);
  233. static unsigned char *GetFwId(wchar_t drive, unsigned char *fwid)
  234. {
  235. char xml[65536] = {0};
  236. if(!ParseSysInfoXML(drive, xml, sizeof(xml)/sizeof(char))) return NULL;
  237. char *p = strstr(xml,"<key>FireWireGUID</key>");
  238. if(!p) return 0;
  239. p = strstr(p,"<string>");
  240. if(!p) return 0;
  241. p += strlen("<string>");
  242. for(int i=0; i<8 && *p; i++) {
  243. char num[3]={0,0,0};
  244. num[0] = *(p++);
  245. num[1] = *(p++);
  246. fwid[i] = (uint8_t)strtoul(num,NULL,16);
  247. }
  248. return fwid;
  249. }
  250. int iPodDevice::writeiTunesDB()
  251. {
  252. char dbPath[] = "x:\\iPod_Control\\iTunes\\iTunesDB"; dbPath[0]=drive;
  253. char dbPathOld[] = "x:\\iPod_Control\\iTunes\\iTunesDB.old_mlpmp"; dbPathOld[0]=drive;
  254. char dbPathNew[] = "x:\\iPod_Control\\iTunes\\iTunesDB.new_mlpmp"; dbPathNew[0]=drive;
  255. if(!db) return -1;
  256. HANDLE hLock = iTunesLock(drive);
  257. if(hLock == INVALID_HANDLE_VALUE) return -1;
  258. uint32_t allocate = (uint32_t)fileSize(AutoWide(dbPath));
  259. int incr = 10000000;
  260. bool done=false;
  261. int i=0;
  262. int ret=0;
  263. unsigned char * data;
  264. while(!done)
  265. {
  266. if(i++ > 10)
  267. {
  268. iTunesUnlock(hLock);
  269. return -1;
  270. }
  271. allocate += incr;
  272. data = (unsigned char*)malloc(allocate);
  273. if(!data) return -1; //what else can we do?
  274. // TODO: i'd like to cut this but it seems to still be causing problems to parse it from XML
  275. unsigned char fwid[8]={0};
  276. GetFwId(driveW, fwid);
  277. #ifdef _DEBUG
  278. if (memcmp(fwid, this->fwid, 8) || memcmp(fwid, info->fwid, 8))
  279. {
  280. DebugBreak();
  281. }
  282. #endif
  283. int len = db->write(data,allocate, fwid);
  284. if(len > 0) {
  285. _unlink(dbPathOld);
  286. _unlink(dbPathNew);
  287. FILE * f = fopen(dbPathNew,"wb");
  288. if(!f) {
  289. iTunesUnlock(hLock);
  290. return -1;
  291. }
  292. fwrite(data,1,len,f);
  293. fclose(f);
  294. rename(dbPath,dbPathOld);
  295. _unlink(dbPath);
  296. rename(dbPathNew,dbPath);
  297. done=true;
  298. ret=len;
  299. } else free(data);
  300. }
  301. if(data)
  302. {
  303. if (info->shadow_db_version == 1)
  304. {
  305. iTunesSD1 sd;
  306. int l = sd.write(&db->mhsdsongs->mhlt->mhit, data,allocate);
  307. if(l>0)
  308. {
  309. char dbPath[] = "x:\\iPod_Control\\iTunes\\iTunesSD"; dbPath[0]=drive;
  310. FILE * f = fopen(dbPath,"wb");
  311. if(f) {
  312. fwrite(data,1,l,f);
  313. fclose(f);
  314. }
  315. }
  316. }
  317. else if (info->shadow_db_version == 2)
  318. {
  319. iTunesSD2 sd;
  320. int l = sd.write(db->mhsdsongs->mhlt, db->mhsdplaylists->mhlp, data,allocate);
  321. if(l>0)
  322. {
  323. char dbPath[] = "x:\\iPod_Control\\iTunes\\iTunesSD"; dbPath[0]=drive;
  324. FILE * f = fopen(dbPath,"wb");
  325. if(f) {
  326. fwrite(data,1,l,f);
  327. fclose(f);
  328. }
  329. }
  330. }
  331. }
  332. if(artdb && data) {
  333. int l = artdb->write(data,allocate);
  334. if(l>0) {
  335. char dbPath[] = "x:\\iPod_Control\\Artwork\\ArtworkDB"; dbPath[0]=drive;
  336. FILE * f = fopen(dbPath,"wb");
  337. if(f) {
  338. fwrite(data,1,l,f);
  339. fclose(f);
  340. }
  341. }
  342. }
  343. iTunesUnlock(hLock);
  344. if(data) free(data);
  345. return ret;
  346. }
  347. __int64 iPodDevice::getDeviceCapacityAvailable() {
  348. ULARGE_INTEGER tfree={0,}, total={0,}, freeb={0,};
  349. wchar_t path[4]=L"x:\\";
  350. path[0]=drive;
  351. GetDiskFreeSpaceEx(path, &tfree, &total, &freeb);
  352. return freeb.QuadPart;
  353. }
  354. __int64 iPodDevice::getDeviceCapacityTotal() {
  355. ULARGE_INTEGER tfree={0,}, total={0,}, freeb={0,};
  356. wchar_t path[4]=L"x:\\";
  357. path[0]=drive;
  358. GetDiskFreeSpaceEx(path, &tfree, &total, &freeb);
  359. return total.QuadPart;
  360. }
  361. void iPodDevice::Close()
  362. {
  363. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICEDISCONNECTED);
  364. //writeiTunesDB();
  365. //iPods.eraseObject(this);
  366. auto it = std::find(iPods.begin(), iPods.end(), this);
  367. if (it != iPods.end())
  368. {
  369. iPods.erase(it);
  370. }
  371. delete this;
  372. }
  373. void iPodDevice::Eject()
  374. {
  375. //iPods.eraseObject(this);
  376. auto it = std::find(iPods.begin(), iPods.end(), this);
  377. if (it != iPods.end())
  378. {
  379. iPods.erase(it);
  380. }
  381. if(EjectVolume(drive))
  382. {
  383. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICEDISCONNECTED);
  384. delete this;
  385. }
  386. else
  387. {
  388. wchar_t titleStr[32] = {0};
  389. iPods.push_back(this);
  390. MessageBox(plugin.hwndLibraryParent,WASABI_API_LNGSTRINGW(IDS_FAILED_TO_EJECT_IPOD),
  391. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,titleStr,32),0);
  392. }
  393. }
  394. extern int CopyFile(const wchar_t * infile, const wchar_t * outfile, void * callbackContext, void (*callback)(void * callbackContext, wchar_t * status), int * killswitch);
  395. static __int64 fileSize(const wchar_t * filename)
  396. {
  397. WIN32_FIND_DATA f={0};
  398. HANDLE h = FindFirstFileW(filename,&f);
  399. if(h == INVALID_HANDLE_VALUE) return -1;
  400. FindClose(h);
  401. ULARGE_INTEGER i;
  402. i.HighPart = f.nFileSizeHigh;
  403. i.LowPart = f.nFileSizeLow;
  404. return i.QuadPart;
  405. }
  406. void GetFileInfo(const wchar_t * file, const wchar_t * metadata, wchar_t * buf, int len) {
  407. buf[0]=0;
  408. extendedFileInfoStructW m = {file,metadata,buf,(size_t)len};
  409. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&m,IPC_GET_EXTENDED_FILE_INFOW);
  410. }
  411. __int64 GetFileInfoInt64(wchar_t * file, wchar_t * metadata, BOOL *w=NULL) {
  412. wchar_t buf[100]=L"";
  413. GetFileInfo(file,metadata,buf,100);
  414. if(w && buf[0]==0) {(*w) = 0; return 0;}
  415. return _wtoi64(buf);
  416. }
  417. int GetFileInfoInt(wchar_t * file, wchar_t * metadata, BOOL *w=NULL) {
  418. wchar_t buf[100]=L"";
  419. GetFileInfo(file,metadata,buf,100);
  420. if(w && buf[0]==0) {(*w) = 0; return 0;}
  421. return _wtoi(buf);
  422. }
  423. int iPodDevice::transferTrackToDevice(const itemRecordW *track,void * callbackContext,void (*callback)(void * callbackContext, wchar_t * status),songid_t * songid,int * killswitch)
  424. {
  425. bool transcodefile = false;
  426. wchar_t outfile[2048] = {0};
  427. wchar_t infile[MAX_PATH] = {0};
  428. StringCchCopy(infile, MAX_PATH, track->filename);
  429. bool nocopy=false;
  430. iPod_mhit * mhit = db->mhsdsongs->mhlt->NewTrack();
  431. dirnum = (dirnum + 1) % 20;
  432. // create the output filename and directory
  433. wchar_t ext[10]=L"";
  434. const wchar_t *e = wcsrchr(infile,L'.');
  435. if(e)
  436. StringCbCopyW(ext, sizeof(ext), e);
  437. if(transcoder && transcoder->ShouldTranscode(infile)) {
  438. int r = transcoder->CanTranscode(infile,ext, track->length);
  439. if(r != 0 && r != -1) transcodefile = true;
  440. }
  441. bool video = !_wcsicmp(ext,L".m4v");
  442. if(!_wcsicmp(ext,L".mp4")) {
  443. wchar_t buf[100]=L"0";
  444. extendedFileInfoStructW m = {infile,L"type",buf,100};
  445. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&m,IPC_GET_EXTENDED_FILE_INFOW);
  446. if(!wcscmp(buf,L"1")) { video=true; wcsncpy(ext,L".m4v",10); }
  447. else wcsncpy(ext,L".m4a",10);
  448. }
  449. // and the location in the ipod naming scheme
  450. if (infile[0] == drive && infile[1] && infile[1] == L':')
  451. {
  452. // file already on the ipod? add it directly
  453. StringCbCopy(outfile, sizeof(outfile), infile);
  454. nocopy=true;
  455. }
  456. else
  457. {
  458. StringCbPrintf(outfile,sizeof(outfile),L"%c:\\iPod_Control\\Music\\F%02d\\",(wchar_t)drive,dirnum);
  459. CreateDirectory(outfile,NULL);
  460. StringCbPrintf(outfile,sizeof(outfile),L"%c:\\iPod_Control\\Music\\F%02d\\w%05d%s",(wchar_t)drive,dirnum,mhit->id,ext);
  461. }
  462. wchar_t location[2048] = {0};
  463. StringCbCopy(location, sizeof(location), outfile+2);
  464. int i=0;
  465. while(location[i] != 0) { if(location[i]==L'\\') location[i]=L':'; i++; }
  466. {
  467. wchar_t buf[100]=L"";
  468. int which = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0);
  469. extendedFileInfoStructW m = {infile,which?L"replaygain_album_gain":L"replaygain_track_gain",buf,100};
  470. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&m,IPC_GET_EXTENDED_FILE_INFOW);
  471. if(buf[0]) {
  472. double gain = _wtof(&buf[buf[0]==L'+'?1:0]);
  473. mhit->soundcheck = (unsigned long)(1000.0 * pow(10.0,-0.1*gain));
  474. }
  475. }
  476. // fill in the new MHIT (track item) with our metadata
  477. mhit->AddString(MHOD_TITLE)->SetString(track->title);
  478. mhit->AddString(MHOD_LOCATION)->SetString(location);
  479. mhit->AddString(MHOD_ALBUM)->SetString(track->album);
  480. mhit->AddString(MHOD_ARTIST)->SetString(track->artist);
  481. mhit->AddString(MHOD_GENRE)->SetString(track->genre);
  482. mhit->AddString(MHOD_COMMENT)->SetString(track->comment);
  483. mhit->AddString(MHOD_ALBUMARTIST)->SetString(track->albumartist);
  484. mhit->AddString(MHOD_COMPOSER)->SetString(track->composer);
  485. mhit->length = (track->length>0)?track->length*1000:0;
  486. mhit->year = (track->year>0)?track->year:0;
  487. mhit->tracknum = (track->track>0)?track->track:0;
  488. mhit->totaltracks = (track->tracks>0)?track->tracks:0;
  489. mhit->stars = (unsigned char)(mhit->app_rating = track->rating);
  490. mhit->playcount = mhit->playcount2 = track->playcount;
  491. mhit->lastplayedtime = wintime_to_mactime(track->lastplay);
  492. mhit->lastmodifiedtime = wintime_to_mactime(track->lastupd);
  493. mhit->compilation = track->albumartist && !_wcsicmp(track->albumartist, L"various artists");
  494. mhit->samplerate = 44100; // TODO: benski> we could query this from the input plugin, but we'd have to be careful with HE-AAC
  495. mhit->samplerate2 = 44100.0f;
  496. mhit->mediatype = video?0x02:0x01;
  497. mhit->movie_flag = video?1:0;
  498. mhit->cdnum = (track->disc>0)?track->disc:0;
  499. mhit->totalcds = (track->discs>0)?track->discs:0;
  500. mhit->BPM=(track->bpm>0)?track->bpm:0;
  501. wchar_t *pubdate = getRecordExtendedItem(track,L"podcastpubdate");
  502. if(pubdate && *pubdate) mhit->releasedtime=wintime_to_mactime(_wtoi(pubdate));
  503. // copy the file over
  504. int r;
  505. if(transcodefile)
  506. r = transcoder->TranscodeFile(infile,outfile,killswitch,callback,callbackContext);
  507. else if (!nocopy)
  508. r = CopyFile(infile,outfile,callbackContext,callback,killswitch);
  509. else
  510. {
  511. if (callback)
  512. {
  513. wchar_t langtemp[100] = {0};
  514. callback(callbackContext, WASABI_API_LNGSTRINGW_BUF(IDS_DONE, langtemp, 100));
  515. }
  516. r=0;
  517. }
  518. if(r == 0)
  519. {
  520. StringCbCopyW(ext, sizeof(ext), wcsrchr(outfile,L'.'));
  521. if (!_wcsicmp(ext, L".m4a") || !_wcsicmp(ext, L".mp4"))
  522. {
  523. mhit->vbr = 0;
  524. mhit->type = 0;
  525. mhit->unk14 = 51;
  526. mhit->filetype = FILETYPE_M4A;
  527. }
  528. else if (!_wcsicmp(ext, L".mp3"))
  529. {
  530. mhit->type = 1;
  531. mhit->unk27 = 1;
  532. mhit->unk14 = 12;
  533. mhit->AddString(MHOD_FILETYPE)->SetString(L"MPEG audio file");
  534. mhit->filetype = FILETYPE_MP3;
  535. }
  536. else if (!_wcsicmp(ext, L".wav"))
  537. {
  538. mhit->filetype = FILETYPE_WAV;
  539. }
  540. mhit->samplecount = GetFileInfoInt64(outfile,L"numsamples");
  541. mhit->pregap = (unsigned long)GetFileInfoInt64(outfile,L"pregap");
  542. mhit->postgap = (unsigned long)GetFileInfoInt64(outfile,L"postgap");
  543. mhit->gaplessData = (unsigned long)GetFileInfoInt64(outfile,L"endoffset");
  544. mhit->trackgapless = 1;
  545. mhit->size = (unsigned long)fileSize(outfile);
  546. if (!transcodefile && track->bitrate > 0)
  547. mhit->bitrate = track->bitrate;
  548. else
  549. {
  550. mhit->bitrate = (unsigned long)GetFileInfoInt64(outfile,L"bitrate");
  551. if (!mhit->bitrate)
  552. {
  553. if (track->length > 0)
  554. mhit->bitrate = (mhit->size / track->length)/125;
  555. else
  556. mhit->bitrate = 128;
  557. }
  558. }
  559. *songid = (songid_t)mhit;
  560. }
  561. else
  562. {
  563. DeleteFileW(outfile);
  564. delete mhit;
  565. }
  566. return r;
  567. }
  568. int iPodDevice::trackAddedToTransferQueue(const itemRecordW *track) {
  569. __int64 l;
  570. if(transcoder && transcoder->ShouldTranscode(track->filename)) {
  571. int k = transcoder->CanTranscode(track->filename, 0, track->length);
  572. if(k == -1) return -2;
  573. if(k == 0) l = (__int64)fileSize(track->filename);
  574. else l = (__int64)k;
  575. } else {
  576. wchar_t * ext = wcsrchr(track->filename,L'.');
  577. if(!ext) return -2;
  578. if(_wcsicmp(ext,L".mp3") && _wcsicmp(ext,L".wav") && _wcsicmp(ext,L".m4a") &&
  579. _wcsicmp(ext,L".m4b") && _wcsicmp(ext,L".aa") && _wcsicmp(ext,L".m4v") &&
  580. _wcsicmp(ext,L".mp4")) return -2;
  581. l = (__int64)fileSize(track->filename);
  582. }
  583. __int64 avail = getDeviceCapacityAvailable();
  584. __int64 cmp = transferQueueLength;
  585. cmp += l;
  586. cmp += (__int64)3000000;
  587. if(cmp > avail)
  588. return -1;
  589. else {
  590. transferQueueLength += l;
  591. return 0;
  592. }
  593. }
  594. void iPodDevice::trackRemovedFromTransferQueue(const itemRecordW *track) {
  595. __int64 l = (__int64)fileSize(track->filename);
  596. if(transcoder && transcoder->ShouldTranscode(track->filename)) {
  597. int k = transcoder->CanTranscode(track->filename, 0, track->length);
  598. if(k != -1 && k != 0) l = (__int64)k;
  599. }
  600. transferQueueLength -= l;
  601. }
  602. __int64 iPodDevice::getTrackSizeOnDevice(const itemRecordW *track) {
  603. if(transcoder && transcoder->ShouldTranscode(track->filename)) {
  604. int k = transcoder->CanTranscode(track->filename, 0, track->length);
  605. if(k != -1 && k != 0) return k;
  606. }
  607. wchar_t * ext = wcsrchr(track->filename,'.');
  608. if(!ext) return 0;
  609. if(_wcsicmp(ext,L".mp3") && _wcsicmp(ext,L".wav") && _wcsicmp(ext,L".m4a") &&
  610. _wcsicmp(ext,L".m4b") && _wcsicmp(ext,L".aa") && _wcsicmp(ext,L".m4v") &&
  611. _wcsicmp(ext,L"mp4")) return 0;
  612. return fileSize(track->filename);
  613. }
  614. void iPodDevice::deleteTrack(songid_t songid) {
  615. iPod_mhit * mhit = (iPod_mhit *)songid;
  616. iPod_mhod * mhod = mhit->FindString(MHOD_LOCATION);
  617. if(!mhod) return;
  618. wchar_t * t = mhod->str;
  619. // change ':' to '\\;
  620. wchar_t * p = t;
  621. int l = wcslen(t);
  622. for(int j=0; j<l; j++) if(*(p++)==L':') *(p-1)=L'\\';
  623. // add drive onto front
  624. wchar_t file[2048] = L"x:\\";
  625. file[0] = driveW;
  626. wcscat(file,t);
  627. //check this file isn't playing...
  628. wchar_t* curPlaying = (wchar_t*)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GET_PLAYING_FILENAME);
  629. if(curPlaying && !_wcsicmp(curPlaying,file)) SendMessage(plugin.hwndWinampParent,WM_COMMAND,40047,0); //stop
  630. //delete :)
  631. // benski> we might have a file that has been deleted from the disk but not the DB
  632. if(!DeleteFileW(file) // check for file failure
  633. && GetFileAttributes(file) != INVALID_FILE_ATTRIBUTES) // but only fail if the file actually exists
  634. return;
  635. setArt(songid,NULL,0,0);
  636. l = playlists.GetSize();
  637. for(int i=0; i<l; i++) {
  638. iPod_mhyp * mhyp = ((iPod_mhyp*)playlists.Get(i)); //->DeletePlaylistEntryByID(mhit->id);
  639. for(unsigned int j=0; j<mhyp->GetMhipChildrenCount(); j++) {
  640. if(mhyp->GetPlaylistEntry(j)->songindex == mhit->id) mhyp->DeletePlaylistEntry(j);
  641. }
  642. }
  643. db->mhsdsongs->mhlt->DeleteTrackByID(mhit->id);
  644. }
  645. int iPodDevice::getPlaylistCount() {
  646. return playlists.GetSize();
  647. }
  648. static void readStringMHOD(iPod_mhod * mhod, wchar_t * buf, int len) {
  649. if(mhod) lstrcpyn(buf,mhod->str,len);
  650. else buf[0]=0;
  651. }
  652. static void setStringMHOD(iPod_mhod * mhod, const wchar_t *buf) {
  653. mhod->SetString(buf);
  654. }
  655. void iPodDevice::getPlaylistName(int playlistnumber, wchar_t * buf, int len) {
  656. iPod_mhod * name = ((iPod_mhyp*)playlists.Get(playlistnumber))->FindString(MHOD_TITLE);
  657. readStringMHOD(name,buf,len);
  658. }
  659. int iPodDevice::getPlaylistLength(int playlistnumber) {
  660. return ((iPod_mhyp*)playlists.Get(playlistnumber))->GetMhipChildrenCount();
  661. }
  662. static iPod_mhit blank;
  663. songid_t iPodDevice::getPlaylistTrack(int playlistnumber,int songnum) {
  664. int idx = ((iPod_mhyp*)playlists.Get(playlistnumber))->GetPlaylistEntry(songnum)->songindex;
  665. iPod_mhlt::mhit_map_t::const_iterator f = db->mhsdsongs->mhlt->mhit.find(idx);
  666. if(f != db->mhsdsongs->mhlt->mhit.end() && idx) return (songid_t)f->second;
  667. else {
  668. iPod_mhip* m = ((iPod_mhyp*)playlists.Get(playlistnumber))->GetPlaylistEntry(songnum);
  669. blank.DeleteString(4);
  670. if(m->podcastgroupflag && m->mhod[0]) {
  671. iPod_mhod * mh = blank.AddString(4);
  672. mh->SetString(m->mhod[0]->str);
  673. }
  674. return (songid_t)&blank;
  675. }
  676. }
  677. void iPodDevice::setPlaylistName(int playlistnumber, const wchar_t *buf) {
  678. iPod_mhod * name = ((iPod_mhyp*)playlists.Get(playlistnumber))->FindString(MHOD_TITLE);
  679. if(!name) name = ((iPod_mhyp*)playlists.Get(playlistnumber))->AddString(MHOD_TITLE);
  680. setStringMHOD(name,buf);
  681. if(playlistnumber == 0) {
  682. wchar_t volumename[12] = {0};
  683. const wchar_t * p = buf;
  684. for(int i=0; i<11;) {
  685. if(*p!=L' ' && *p!=L'\t') volumename[i++]=*p;
  686. if(*(p++)==0) break;
  687. }
  688. volumename[11]=0;
  689. char root[] = {drive,":\\"};
  690. SetVolumeLabel(AutoWide(root),volumename);
  691. }
  692. }
  693. void iPodDevice::playlistSwapItems(int playlistnumber, int posA, int posB) {
  694. iPod_mhyp * p = ((iPod_mhyp*)playlists.Get(playlistnumber));
  695. iPod_mhip * a = p->mhip.at(posA);
  696. iPod_mhip * b = p->mhip.at(posB);
  697. if(a && b) {
  698. p->mhip[posA] = b;
  699. p->mhip[posB] = a;
  700. }
  701. }
  702. static iPod_mhyp * sortpl;
  703. static int sortby;
  704. static iPod_mhbd * sortdb;
  705. #define CMPFIELDS(x) { int v=0; iPod_mhod * am = a->FindString(x); iPod_mhod * bm = b->FindString(x); if(am && bm) v = lstrcmpi(am->str,bm->str); else if(am != bm) v = am==NULL?-1:1; if(v!=0) return v<0; }
  706. #define CMPINTFIELDS(x,y) { int v = x-y; if(v!=0) return v<0; }
  707. struct PlaylistItemSort {
  708. bool operator()(iPod_mhip*& ap,iPod_mhip*& bp) {
  709. int use_by = sortby;
  710. iPod_mhit * a = sortdb->mhsdsongs->mhlt->mhit.find(ap->songindex)->second;
  711. iPod_mhit * b = sortdb->mhsdsongs->mhlt->mhit.find(bp->songindex)->second;
  712. // this might be too slow, but it'd be nice
  713. int x;
  714. for (x = 0; x < 5; x ++)
  715. {
  716. if (use_by == SORTBY_TITLE) // title -> artist -> album -> disc -> track
  717. {
  718. CMPFIELDS(MHOD_TITLE);
  719. use_by=SORTBY_ARTIST;
  720. }
  721. else if (use_by == SORTBY_ARTIST) // artist -> album -> disc -> track -> title
  722. {
  723. CMPFIELDS(MHOD_ARTIST);
  724. use_by=SORTBY_ALBUM;
  725. }
  726. else if (use_by == SORTBY_ALBUM) // album -> disc -> track -> title -> artist
  727. {
  728. CMPFIELDS(MHOD_ALBUM);
  729. use_by=SORTBY_DISCNUM;
  730. }
  731. else if (use_by == SORTBY_DISCNUM) // disc -> track -> title -> artist -> album
  732. {
  733. CMPINTFIELDS(a->cdnum,b->cdnum);
  734. use_by=SORTBY_TRACKNUM;
  735. }
  736. else if (use_by == SORTBY_TRACKNUM) // track -> title -> artist -> album -> disc
  737. {
  738. CMPINTFIELDS(a->tracknum,b->tracknum);
  739. use_by=SORTBY_TITLE;
  740. }
  741. else if (use_by == SORTBY_GENRE) // genre -> artist -> album -> disc -> track
  742. {
  743. CMPFIELDS(MHOD_GENRE);
  744. use_by=SORTBY_ARTIST;
  745. }
  746. else if (use_by == SORTBY_PLAYCOUNT) // size -> artist -> album -> disc -> track
  747. {
  748. CMPINTFIELDS(a->playcount,b->playcount);
  749. use_by=SORTBY_ARTIST;
  750. }
  751. else if (use_by == SORTBY_RATING) // size -> artist -> album -> disc -> track
  752. {
  753. CMPINTFIELDS(a->stars,b->stars);
  754. use_by=SORTBY_ARTIST;
  755. }
  756. else if (use_by == SORTBY_LASTPLAYED)
  757. {
  758. double t = difftime(a->lastplayedtime,b->lastplayedtime);
  759. if(t != 0) return t>0;
  760. use_by=SORTBY_ARTIST;
  761. }
  762. else break; // no sort order?
  763. }
  764. return false;
  765. }
  766. };
  767. #undef CMPFIELDS
  768. #undef CMPINTFIELDS
  769. void iPodDevice::sortPlaylist(int playlistnumber, int sortby0) {
  770. sortpl = ((iPod_mhyp*)playlists.Get(playlistnumber));
  771. sortby = sortby0;
  772. sortdb = db;
  773. std::sort(sortpl->mhip.begin(),sortpl->mhip.end(),PlaylistItemSort());
  774. }
  775. void iPodDevice::addTrackToPlaylist(int playlistnumber, songid_t songid)
  776. {
  777. iPod_mhit *mhit = (iPod_mhit *)songid;
  778. if (playlistnumber == 0)
  779. {
  780. db->mhsdsongs->mhlt->AddTrack(mhit);
  781. db->mhsdplaylists->mhlp->GetDefaultPlaylist()->AddPlaylistEntry(NULL, mhit->id);
  782. }
  783. else
  784. {
  785. ((iPod_mhyp*)playlists.Get(playlistnumber))->AddPlaylistEntry(NULL, mhit->id);
  786. }
  787. }
  788. void iPodDevice::removeTrackFromPlaylist(int playlistnumber, int songnum) {
  789. ((iPod_mhyp*)playlists.Get(playlistnumber))->DeletePlaylistEntry(songnum);
  790. }
  791. void iPodDevice::deletePlaylist(int playlistnumber) {
  792. iPod_mhyp* p = ((iPod_mhyp*)playlists.Get(playlistnumber));
  793. playlists.Del(playlistnumber);
  794. db->mhsdplaylists->mhlp->DeletePlaylistByID(p->playlistID);
  795. }
  796. int iPodDevice::newPlaylist(const wchar_t *name) {
  797. iPod_mhyp * p = db->mhsdplaylists->mhlp->AddPlaylist();
  798. playlists.Add(p);
  799. int ret = db->mhsdplaylists->mhlp->GetChildrenCount() - 1;
  800. setPlaylistName(ret,name);
  801. db->mhsdplaylists->mhlp->SortPlaylists();
  802. return ret;
  803. }
  804. void iPodDevice::getTrackArtist(songid_t songid, wchar_t * buf, int len) {
  805. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_ARTIST);
  806. readStringMHOD(mhod,buf,len);
  807. }
  808. void iPodDevice::getTrackAlbum(songid_t songid, wchar_t * buf, int len) {
  809. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_ALBUM);
  810. readStringMHOD(mhod,buf,len);
  811. }
  812. void iPodDevice::getTrackTitle(songid_t songid, wchar_t * buf, int len) {
  813. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_TITLE);
  814. readStringMHOD(mhod,buf,len);
  815. }
  816. void iPodDevice::getTrackGenre(songid_t songid, wchar_t * buf, int len) {
  817. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_GENRE);
  818. readStringMHOD(mhod,buf,len);
  819. }
  820. int iPodDevice::getTrackTrackNum(songid_t songid) {
  821. return ((iPod_mhit *)songid)->tracknum;
  822. }
  823. int iPodDevice::getTrackDiscNum(songid_t songid) {
  824. return ((iPod_mhit *)songid)->cdnum;
  825. }
  826. int iPodDevice::getTrackYear(songid_t songid) {
  827. return (int)(((iPod_mhit *)songid)->year);
  828. }
  829. __int64 iPodDevice::getTrackSize(songid_t songid) {
  830. return ((iPod_mhit *)songid)->size;
  831. }
  832. int iPodDevice::getTrackLength(songid_t songid) {
  833. return ((iPod_mhit *)songid)->length;
  834. }
  835. int iPodDevice::getTrackBitrate(songid_t songid) {
  836. return ((iPod_mhit *)songid)->bitrate;
  837. }
  838. int iPodDevice::getTrackPlayCount(songid_t songid) {
  839. return ((iPod_mhit *)songid)->playcount;
  840. }
  841. int iPodDevice::getTrackRating(songid_t songid) {
  842. return ((iPod_mhit *)songid)->stars / 20;
  843. }
  844. __time64_t iPodDevice::getTrackLastPlayed(songid_t songid) {
  845. return mactime_to_wintime(((iPod_mhit *)songid)->lastplayedtime);
  846. }
  847. __time64_t iPodDevice::getTrackLastUpdated(songid_t songid) {
  848. return mactime_to_wintime(((iPod_mhit *)songid)->lastmodifiedtime);
  849. }
  850. void iPodDevice::getTrackAlbumArtist(songid_t songid, wchar_t * buf, int len) {
  851. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_ALBUMARTIST);
  852. readStringMHOD(mhod,buf,len);
  853. if(!mhod) getTrackArtist(songid,buf,len);
  854. }
  855. void iPodDevice::getTrackComposer(songid_t songid, wchar_t * buf, int len) {
  856. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_COMPOSER);
  857. readStringMHOD(mhod,buf,len);
  858. }
  859. int iPodDevice::getTrackType(songid_t songid) {
  860. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_LOCATION);
  861. if(!mhod) return 0;
  862. wchar_t * ext = wcsrchr(mhod->str,L'.');
  863. if(!ext) return 0;
  864. if(!_wcsicmp(ext,L".m4v")) return 1;
  865. return 0;
  866. }
  867. void iPodDevice::getTrackExtraInfo(songid_t songid, const wchar_t *field, wchar_t * buf, int len) {
  868. if(!wcscmp(field,FIELD_EXTENSION)) {
  869. wchar_t buf2[1024]=L"";
  870. getFilename(buf2,1024,songid);
  871. wchar_t * ext = wcsrchr(buf2,L'.');
  872. if(ext) { ext++; lstrcpyn(buf,ext,len); }
  873. }
  874. }
  875. void iPodDevice::setTrackArtist(songid_t songid, const wchar_t *value) {
  876. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_ARTIST);
  877. if(!mhod) mhod = ((iPod_mhit *)songid)->AddString(MHOD_ARTIST);
  878. setStringMHOD(mhod,value);
  879. }
  880. void iPodDevice::setTrackAlbum(songid_t songid, const wchar_t *value) {
  881. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_ALBUM);
  882. if(!mhod) mhod = ((iPod_mhit *)songid)->AddString(MHOD_ALBUM);
  883. setStringMHOD(mhod,value);
  884. }
  885. void iPodDevice::setTrackTitle(songid_t songid, const wchar_t *value) {
  886. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_TITLE);
  887. if(!mhod) mhod = ((iPod_mhit *)songid)->AddString(MHOD_TITLE);
  888. setStringMHOD(mhod,value);
  889. }
  890. void iPodDevice::setTrackTrackNum(songid_t songid, int value) {
  891. ((iPod_mhit *)songid)->tracknum = value;
  892. }
  893. void iPodDevice::setTrackDiscNum(songid_t songid, int value) {
  894. ((iPod_mhit *)songid)->cdnum = value;
  895. }
  896. void iPodDevice::setTrackGenre(songid_t songid, const wchar_t *value) {
  897. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_GENRE);
  898. if(!mhod) mhod = ((iPod_mhit *)songid)->AddString(MHOD_GENRE);
  899. setStringMHOD(mhod,value);
  900. }
  901. void iPodDevice::setTrackYear(songid_t songid, int value) {
  902. ((iPod_mhit *)songid)->year = (unsigned long)value;
  903. }
  904. void iPodDevice::setTrackPlayCount(songid_t songid, int value) {
  905. ((iPod_mhit *)songid)->playcount = value;
  906. }
  907. void iPodDevice::setTrackRating(songid_t songid, int value) {
  908. ((iPod_mhit *)songid)->app_rating = ((iPod_mhit *)songid)->stars = value*20;
  909. }
  910. void iPodDevice::setTrackLastPlayed(songid_t songid, __time64_t value) {
  911. ((iPod_mhit *)songid)->lastplayedtime = wintime_to_mactime(value);
  912. }
  913. void iPodDevice::setTrackLastUpdated(songid_t songid, __time64_t value) {
  914. ((iPod_mhit *)songid)->lastmodifiedtime = wintime_to_mactime(value);
  915. }
  916. void iPodDevice::setTrackAlbumArtist(songid_t songid, const wchar_t *value) {
  917. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_ALBUMARTIST);
  918. if(!mhod) mhod = ((iPod_mhit *)songid)->AddString(MHOD_ALBUMARTIST);
  919. setStringMHOD(mhod,value);
  920. }
  921. void iPodDevice::setTrackComposer(songid_t songid, const wchar_t *value) {
  922. iPod_mhod * mhod = ((iPod_mhit *)songid)->FindString(MHOD_COMPOSER);
  923. if(!mhod) mhod = ((iPod_mhit *)songid)->AddString(MHOD_COMPOSER);
  924. setStringMHOD(mhod,value);
  925. }
  926. void iPodDevice::getFilename(char * buf, int len, songid_t song) {
  927. iPod_mhod * mhod = ((iPod_mhit *)song)->FindString(MHOD_LOCATION);
  928. if(!mhod) {buf[0]=0; return;}
  929. char * filename = UTF16_to_char(mhod->str,mhod->length);
  930. char * p = filename;
  931. buf[0] = drive;
  932. buf[1] = ':';
  933. int j=2;
  934. while(p && *p && j < len-1) { if(*p==':') buf[j]='\\'; else buf[j]=*p; p++; j++; }
  935. buf[j]=0;
  936. free(filename);
  937. }
  938. void iPodDevice::getFilename(wchar_t * buf, int len, songid_t song) {
  939. iPod_mhod * mhod = ((iPod_mhit *)song)->FindString(MHOD_LOCATION);
  940. if(!mhod) {buf[0]=0; return;}
  941. wchar_t * filename = mhod->str;
  942. wchar_t * p = filename;
  943. buf[0] = drive;
  944. buf[1] = L':';
  945. int j=2;
  946. while(p && *p && j < len-1) { if(*p==L':') buf[j]=L'\\'; else buf[j]=*p; p++; j++; }
  947. buf[j]=0;
  948. }
  949. typedef struct { songid_t song; Device * dev; const wchar_t * filename; } tagItem;
  950. static wchar_t * tagFunc(const wchar_t * tag, void * p) { //return 0 if not found, -1 for empty tag
  951. tagItem * s = (tagItem *)p;
  952. int len = 2048;
  953. wchar_t * buf = (wchar_t *)malloc(sizeof(wchar_t)*len);
  954. if (!_wcsicmp(tag, L"artist")) s->dev->getTrackArtist(s->song,buf,len);
  955. else if (!_wcsicmp(tag, L"album")) s->dev->getTrackAlbum(s->song,buf,len);
  956. else if (!_wcsicmp(tag, L"title")) s->dev->getTrackTitle(s->song,buf,len);
  957. else if (!_wcsicmp(tag, L"genre")) s->dev->getTrackGenre(s->song,buf,len);
  958. else if (!_wcsicmp(tag, L"year"))
  959. {
  960. int year = s->dev->getTrackYear(s->song);
  961. if (year>0)
  962. StringCchPrintf(buf,len,L"%d",year);
  963. else
  964. buf[0]=0;
  965. }
  966. else if (!_wcsicmp(tag, L"tracknumber") || !_wcsicmp(tag, L"track"))
  967. {
  968. int track = s->dev->getTrackTrackNum(s->song);
  969. if (track>0)
  970. StringCchPrintf(buf,len,L"%d",track);
  971. else
  972. buf[0]=0;
  973. }
  974. else if (!_wcsicmp(tag, L"discnumber"))
  975. {
  976. int disc = s->dev->getTrackDiscNum(s->song);
  977. if (disc>0)
  978. StringCchPrintf(buf,len,L"%d",disc);
  979. else
  980. buf[0]=0;
  981. }
  982. else if (!_wcsicmp(tag, L"bitrate"))
  983. {
  984. int bitrate = s->dev->getTrackBitrate(s->song);
  985. if (bitrate>0)
  986. StringCchPrintf(buf,len,L"%d",bitrate);
  987. else
  988. buf[0]=0;
  989. }
  990. else if (!_wcsicmp(tag, L"filename")) lstrcpyn(buf,s->filename,len);
  991. else buf[0]=0;
  992. return buf;
  993. }
  994. static void tagFreeFunc(wchar_t *tag, void *p) { if(tag) free(tag); }
  995. void getTitle(Device * dev, songid_t song, const wchar_t * filename, wchar_t * buf, int len) {
  996. buf[0]=0; buf[len-1]=0;
  997. tagItem item = {song,dev,filename};
  998. waFormatTitleExtended fmt={filename,0,NULL,&item,buf,len,tagFunc,tagFreeFunc};
  999. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&fmt, IPC_FORMAT_TITLE_EXTENDED);
  1000. }
  1001. bool iPodDevice::playTracks(songid_t * songidList, int listLength, int startPlaybackAt, bool enqueue) {
  1002. //char buf[2048]="";
  1003. wchar_t wbuf[2048]=L"";
  1004. if(!enqueue) { //clear playlist
  1005. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_DELETE);
  1006. /*int l=SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_PE_GETINDEXTOTAL);
  1007. while(l>=0) SendMessage(plugin.hwndWinampParent,WM_WA_IPC,--l,IPC_PE_DELETEINDEX);*/
  1008. }
  1009. for(int i=0; i<listLength; i++) {
  1010. getFilename(wbuf,2048,songidList[i]);
  1011. //strcpy(buf,AutoChar(wbuf));
  1012. wchar_t title[2048] = {0};
  1013. getTitle(this,songidList[i],wbuf,title,2048);
  1014. /*enqueueFileWithMetaStruct s={buf,strdup(AutoChar(title)),getTrackLength(songidList[i])/1000};
  1015. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILE);
  1016. free((void*)s.title);*/
  1017. enqueueFileWithMetaStructW s={wbuf,_wcsdup(title),PathFindExtensionW(wbuf),getTrackLength(songidList[i]) / 1000};
  1018. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW);
  1019. free((void*)s.title);
  1020. }
  1021. if(!enqueue) { //play item startPlaybackAt
  1022. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,startPlaybackAt,IPC_SETPLAYLISTPOS);
  1023. SendMessage(plugin.hwndWinampParent,WM_COMMAND,40047,0); //stop
  1024. SendMessage(plugin.hwndWinampParent,WM_COMMAND,40045,0); //play
  1025. }
  1026. return true;
  1027. }
  1028. int iPodDevice::copyToHardDrive(songid_t song, // the song to copy
  1029. wchar_t * path, // path to copy to, in the form "c:\directory\song". The directory will already be created, you must append ".mp3" or whatever to this string! (there is space for at least 10 new characters).
  1030. void * callbackContext, //pass this to the callback
  1031. void (*callback)(void * callbackContext, wchar_t * status), // call this every so often so the GUI can be updated. Including when finished!
  1032. int * killswitch // if this gets set to anything other than zero, the transfer has been cancelled by the user
  1033. ) // -1 for failed/not supported. 0 for success.
  1034. {
  1035. wchar_t fn[2048] = {0}; // song filename on ipod
  1036. getFilename(fn,2048,song);
  1037. wchar_t * ext = wcsrchr(fn,L'.');
  1038. if(!ext) { callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_INVALID_TRACK)); return -1; }
  1039. wcscat(path,ext);
  1040. return CopyFile(fn,path,callbackContext,callback,killswitch);
  1041. }
  1042. // art functions
  1043. static void fileputinhole(const wchar_t* file, unsigned int pos, int len, void* newdata) {
  1044. __int64 fs = fileSize(file);
  1045. int open_flags = OPEN_EXISTING;
  1046. if(fs <= 0) open_flags = CREATE_NEW;
  1047. HANDLE hw = CreateFile(file,GENERIC_WRITE | GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,open_flags,0,NULL);
  1048. if(hw == INVALID_HANDLE_VALUE) return;
  1049. SetFilePointer(hw,pos,NULL,FILE_BEGIN);
  1050. DWORD written=0;
  1051. WriteFile(hw,newdata,len,&written,NULL);
  1052. CloseHandle(hw);
  1053. }
  1054. ArtDataObject * makeThumbMetadata(const ArtworkFormat * thumb, wchar_t drive, Image * image, ArtDB *artdb) {
  1055. wchar_t file[MAX_PATH] = {0};
  1056. wsprintfW(file,L"%c:\\iPod_Control\\Artwork\\F%04d_1.ithmb",drive,thumb->correlation_id);
  1057. bool found=false;
  1058. ArtFile *f=NULL;
  1059. for(size_t i=0; i < artdb->fileListDS->fileList->files.size(); i++) {
  1060. f = artdb->fileListDS->fileList->files[i];
  1061. if(f->corrid == thumb->correlation_id) { found=true; break; }
  1062. }
  1063. if(!found) {
  1064. f = new ArtFile;
  1065. f->corrid = thumb->correlation_id;
  1066. f->imagesize = image->get16BitSize(thumb->row_align, thumb->image_align);
  1067. artdb->fileListDS->fileList->files.push_back(f);
  1068. f->file = _wcsdup(file);
  1069. }
  1070. ArtDataObject * ms = new ArtDataObject;
  1071. ms->type=2;
  1072. ms->image = new ArtImageName;
  1073. ms->image->corrid = thumb->correlation_id;
  1074. ms->image->imgw = thumb->width;
  1075. ms->image->imgh = thumb->height;
  1076. ms->image->imagesize = image->get16BitSize(thumb->row_align, thumb->image_align);
  1077. //__int64 fs = fileSize(file);
  1078. //ms->image->ithmboffset = fs>0?fs:0;
  1079. ms->image->ithmboffset = f->getNextHole(ms->image->imagesize);
  1080. wchar_t buf[100] = {0};
  1081. StringCchPrintf(buf,100,L":F%04d_1.ithmb",thumb->correlation_id);
  1082. ms->image->filename = new ArtDataObject;
  1083. ms->image->filename->type=3;
  1084. ms->image->filename->SetString(buf);
  1085. unsigned short *data = (unsigned short *)calloc(ms->image->imagesize,1);
  1086. image->exportToRGB565((RGB565*)data, thumb->format, thumb->row_align, thumb->image_align);
  1087. fileputinhole(file,ms->image->ithmboffset,ms->image->imagesize,data);
  1088. //writeDataToThumb(file,data,thumb->width * thumb->height);
  1089. free(data);
  1090. f->images.push_back(new ArtFileImage(ms->image->ithmboffset,ms->image->imagesize,1));
  1091. f->sortImages();
  1092. return ms;
  1093. }
  1094. void GetTempFilePath(wchar_t *path) {
  1095. wchar_t dir[MAX_PATH] = {0};
  1096. GetTempPath(MAX_PATH,dir);
  1097. GetTempFileName(dir,L"ml_pmp",0,path);
  1098. }
  1099. static void fileclosehole(const wchar_t* file, unsigned int pos, int len) {
  1100. HANDLE hw = CreateFile(file,GENERIC_WRITE | GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
  1101. if(hw == INVALID_HANDLE_VALUE) return;
  1102. HANDLE hr = CreateFile(file,GENERIC_WRITE | GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
  1103. if(hr == INVALID_HANDLE_VALUE) { CloseHandle(hw); return; }
  1104. SetFilePointer(hw,pos,NULL,FILE_BEGIN);
  1105. SetFilePointer(hr,pos+len,NULL,FILE_BEGIN);
  1106. DWORD fs = GetFileSize(hw,NULL);
  1107. if(pos == 0 && len == fs) { CloseHandle(hr); CloseHandle(hw); _wunlink(file); return; }
  1108. unsigned int p = pos;
  1109. while(1) {
  1110. BYTE buf[65536] = {0};
  1111. DWORD read=0;
  1112. ReadFile(hr,buf,sizeof(buf),&read,NULL);
  1113. if(!read) break;
  1114. DWORD written=0;
  1115. WriteFile(hw,buf,read,&written,NULL);
  1116. if(!written) break;
  1117. p+=read;
  1118. if(p>=fs) break;
  1119. }
  1120. SetFilePointer(hw,fs - len,NULL,FILE_BEGIN);
  1121. SetEndOfFile(hw);
  1122. CloseHandle(hr);
  1123. CloseHandle(hw);
  1124. }
  1125. static bool replaceart(songid_t songid, wchar_t driveW, ArtDB *artdb, std::vector<const ArtworkFormat*> * thumbs, std::vector<Image*> * images) {
  1126. //return false;
  1127. __int64 dbid = ((iPod_mhit*)songid)->dbid;
  1128. int done=0;
  1129. ArtImageList::ArtImageMapIterator art = artdb->imageListDS->imageList->images.find(dbid);
  1130. if(art != artdb->imageListDS->imageList->images.end() && art->second) { // replace old art
  1131. for(size_t i=0; i!=art->second->dataobjs.size(); i++) if(art->second->dataobjs[i]->image) {
  1132. ArtImageName * in = art->second->dataobjs[i]->image;
  1133. for(size_t j=0; j!=thumbs->size(); j++)
  1134. {
  1135. if(in->corrid == thumbs->at(j)->correlation_id) {
  1136. wchar_t file[MAX_PATH] = {0};
  1137. wsprintfW(file,L"%c:\\iPod_Control\\Artwork\\F%04d_1.ithmb",driveW,in->corrid);
  1138. int size = images->at(j)->get16BitSize(thumbs->at(j)->row_align, thumbs->at(j)->image_align);
  1139. if(size == in->imagesize) {
  1140. unsigned short *data = (unsigned short *)malloc(size);
  1141. images->at(j)->exportToRGB565((RGB565*)data, thumbs->at(j)->format, thumbs->at(j)->row_align, thumbs->at(j)->image_align);
  1142. fileputinhole(file,in->ithmboffset,in->imagesize,data);
  1143. free(data);
  1144. done++;
  1145. }
  1146. }
  1147. }
  1148. }
  1149. }
  1150. return (done == thumbs->size());
  1151. }
  1152. void iPodDevice::setArt(songid_t songid, void *bits, int w, int h) { //buf is in format ARGB32*
  1153. if(!artdb || !thumbs.size()) return; // art not supported
  1154. iPod_mhit * mhit = (iPod_mhit *)songid;
  1155. if(bits == NULL || w == 0 || h == 0) { // remove art
  1156. ArtImageList::ArtImageMapIterator arti = artdb->imageListDS->imageList->images.find(mhit->dbid);
  1157. if(arti == artdb->imageListDS->imageList->images.end() || !arti->second) return;
  1158. ArtImage * art = arti->second;
  1159. for(auto j = art->dataobjs.begin(); j!=art->dataobjs.end(); j++) {
  1160. ArtImageName *n = (*j)->image;
  1161. if(n)
  1162. {
  1163. ArtFile * f = artdb->fileListDS->fileList->getFile(n->corrid);
  1164. if(f)
  1165. {
  1166. bool found=false;
  1167. for(size_t i=0; i!=f->images.size(); i++)
  1168. {
  1169. if(!found && f->images[i]->start == n->ithmboffset && --f->images[i]->refcount==0)
  1170. {
  1171. delete f->images[i];
  1172. f->images.erase(f->images.begin() + i);
  1173. i--;
  1174. found=true;
  1175. }
  1176. }
  1177. }
  1178. }
  1179. }
  1180. mhit->mhii_link = 0;
  1181. mhit->artworkcount = 0;
  1182. mhit->hasArtwork = 0;
  1183. artdb->imageListDS->imageList->images.erase(arti);
  1184. delete art;
  1185. } else {
  1186. //setArt(songid,NULL,0,0); // clear old art first
  1187. HQSkinBitmap albumart((ARGB32*)bits, w, h); // wrap image into a bitmap object (no copying done)
  1188. std::vector<Image*> images;
  1189. for(size_t i=0; i!=thumbs.size(); i++) {
  1190. BltCanvas canvas(thumbs[i]->width,thumbs[i]->height);
  1191. albumart.stretch(&canvas, 0, 0, thumbs[i]->width,thumbs[i]->height);
  1192. images.push_back(new Image((ARGB32 *)canvas.getBits(), thumbs[i]->width,thumbs[i]->height));
  1193. }
  1194. if(!replaceart(songid,driveW,artdb,&thumbs,&images)) {
  1195. setArt(songid,NULL,0,0);
  1196. ArtImage * artimg = new ArtImage();
  1197. artimg->songid = mhit->dbid;
  1198. artimg->id = artdb->nextid++;
  1199. artimg->srcImageSize = w*h*4;//0; //fileSize(infile);
  1200. //artimg->srcImageSize = mhit->unk45 = rand();
  1201. mhit->artworksize = 0;
  1202. for(size_t i=0; i!=thumbs.size(); i++)
  1203. {
  1204. artimg->dataobjs.push_back(makeThumbMetadata(thumbs[i],driveW,images[i],artdb));
  1205. mhit->artworksize += thumbs[i]->width * thumbs[i]->height * sizeof(short);
  1206. }
  1207. artdb->imageListDS->imageList->images.insert(ArtImageList::ArtImageMapPair(artimg->songid,artimg));
  1208. mhit->artworkcount = 1;//thumbs.size();
  1209. mhit->hasArtwork = 1;//thumbs.size();
  1210. mhit->mhii_link = artimg->id;
  1211. }
  1212. //images.deleteAll();
  1213. for (auto image : images)
  1214. {
  1215. delete image;
  1216. }
  1217. images.clear();
  1218. }
  1219. }
  1220. class ipodart_t {
  1221. public:
  1222. ipodart_t(ArtImageName *in, wchar_t driveW,int w, int h, const ArtworkFormat* format): w(w),h(h),image(0),error(0),resized(0),format(format) {
  1223. wsprintf(fn,L"%c:\\iPod_Control\\Artwork",driveW);
  1224. wchar_t *p = fn+wcslen(fn);
  1225. in->filename->GetString(p,MAX_PATH - (p - fn));
  1226. while(p && *p) {if(*p == L':') *p=L'\\'; p++;}
  1227. offset = in->ithmboffset;
  1228. }
  1229. ~ipodart_t() { if(image) delete image; }
  1230. Image * GetImage() {
  1231. if(image || error) return image;
  1232. int size = Image::get16BitSize(w,h,format->row_align, format->image_align);
  1233. RGB565 * r = (RGB565*)calloc(size,1);
  1234. if(!r) { return 0; error=1; }
  1235. FILE *f = _wfopen(fn,L"rb");
  1236. if(!f) { free(r); error=1; return 0; }
  1237. fseek(f,offset,0);
  1238. if(fread(r,size,1,f) != 1) { free(r); fclose(f); error=1; return 0; }
  1239. fclose(f);
  1240. image = new Image(r,w,h,format->format,format->row_align, format->image_align);
  1241. free(r);
  1242. return image;
  1243. }
  1244. Image * RegetImage() {
  1245. if(image) delete image; image=0;
  1246. return GetImage();
  1247. }
  1248. int GetError() {return error;}
  1249. int getHeight(){if(image) return image->getHeight(); else return h;}
  1250. int getWidth() {if(image) return image->getWidth(); else return w;}
  1251. int resized;
  1252. void Resize(int neww, int newh)
  1253. {
  1254. HQSkinBitmap temp(image->getData(), image->getWidth(), image->getHeight()); // wrap into a SkinBitmap (no copying involved)
  1255. BltCanvas newImage(neww,newh);
  1256. temp.stretch(&newImage, 0, 0, neww, newh);
  1257. delete image;
  1258. image = new Image((ARGB32 *)newImage.getBits(), neww, newh);
  1259. resized=1;
  1260. }
  1261. private:
  1262. wchar_t fn[MAX_PATH];
  1263. int offset;
  1264. int w,h;
  1265. Image * image;
  1266. int error;
  1267. const ArtworkFormat* format;
  1268. };
  1269. pmpart_t iPodDevice::getArt(songid_t songid) {
  1270. if(!artdb) return 0;
  1271. __int64 dbid = ((iPod_mhit*)songid)->dbid;
  1272. ArtImageList::ArtImageMapIterator art = artdb->imageListDS->imageList->images.find(dbid);
  1273. if(art == artdb->imageListDS->imageList->images.end() || !art->second) return 0;
  1274. int l = art->second->dataobjs.size();
  1275. ArtImageName * in=0;
  1276. int w=0,h=0;
  1277. const ArtworkFormat * format=0;
  1278. for(int i=0; i<l; i++) {
  1279. if(art->second->dataobjs[i]->image && (w < art->second->dataobjs[i]->image->imgw || h < art->second->dataobjs[i]->image->imgh)) {
  1280. in = art->second->dataobjs[i]->image;
  1281. w = in->imgw;
  1282. h = in->imgh;
  1283. for(size_t i=0; i < thumbs.size(); i++)
  1284. {
  1285. const ArtworkFormat *f = thumbs.at(i);
  1286. if(f->width == w && f->height == h)
  1287. format = f;
  1288. }
  1289. }
  1290. }
  1291. if(!in || !format) return 0;
  1292. return (pmpart_t)new ipodart_t(in,driveW,w,h,format);
  1293. }
  1294. void iPodDevice::releaseArt(pmpart_t art) {
  1295. if(!art) return;
  1296. ipodart_t *image = (ipodart_t *)art;
  1297. delete image;
  1298. }
  1299. int iPodDevice::drawArt(pmpart_t art, HDC dc, int x, int y, int w, int h) {
  1300. Image *image = ((ipodart_t*)art)->GetImage();
  1301. if(!image) return 0;
  1302. HQSkinBitmap temp(image->getData(), image->getWidth(), image->getHeight()); // wrap into a SkinBitmap (no copying involved)
  1303. DCCanvas canvas(dc);
  1304. temp.stretch(&canvas,x,y,w,h);
  1305. return 1;
  1306. }
  1307. void iPodDevice::getArtNaturalSize(pmpart_t art, int *w, int *h){
  1308. ipodart_t *image = (ipodart_t*)art;
  1309. if(!image) return;
  1310. *h = image->getHeight();
  1311. *w = image->getWidth();
  1312. }
  1313. void iPodDevice::setArtNaturalSize(pmpart_t art, int w, int h){
  1314. Image *image = ((ipodart_t*)art)->GetImage();
  1315. if(!image) return;
  1316. if(w == image->getWidth() && h == image->getHeight()) return;
  1317. if(((ipodart_t*)art)->resized) {
  1318. image = ((ipodart_t*)art)->RegetImage();
  1319. if(!image) return;
  1320. }
  1321. ((ipodart_t*)art)->Resize(w, h);
  1322. }
  1323. void iPodDevice::getArtData(pmpart_t art, void* data){ // data ARGB32* is at natural size
  1324. Image *image = ((ipodart_t*)art)->GetImage();
  1325. if(!image) return;
  1326. image->exportToARGB32((ARGB32*)data);
  1327. }
  1328. bool iPodDevice::artIsEqual(pmpart_t at, pmpart_t bt) {
  1329. if(at == bt) return true;
  1330. if(!at || !bt) return false;
  1331. if(((ipodart_t*)at)->getWidth() != ((ipodart_t*)bt)->getWidth()) return false;
  1332. if(((ipodart_t*)at)->getHeight() != ((ipodart_t*)bt)->getHeight()) return false;
  1333. Image *a = ((ipodart_t*)at)->RegetImage();
  1334. Image *b = ((ipodart_t*)bt)->RegetImage();
  1335. if(!a && !b) return true;
  1336. if(!a || !b) return false;
  1337. if(a->getWidth() != b->getWidth()) return false;
  1338. if(b->getHeight() != b->getHeight()) return false;
  1339. return memcmp(a->getData(),b->getData(),a->getWidth()*a->getHeight()*sizeof(ARGB32)) == 0;
  1340. }
  1341. static INT_PTR CALLBACK gapscan_dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) {
  1342. static iPodDevice * dev;
  1343. static int i;
  1344. switch(uMsg) {
  1345. case WM_INITDIALOG:
  1346. SetWindowPos(hwndDlg,HWND_TOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
  1347. dev = (iPodDevice*)lParam;
  1348. i=0;
  1349. dev->gapscanner = hwndDlg;
  1350. SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETRANGE32,0,dev->getPlaylistLength(0));
  1351. SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETPOS,0,0);
  1352. SetTimer(hwndDlg,1,100,NULL);
  1353. break;
  1354. case WM_TIMER:
  1355. if(wParam == 1) {
  1356. KillTimer(hwndDlg,1);
  1357. int l = dev->getPlaylistLength(0);
  1358. int j=0;
  1359. for(;;) {
  1360. if(i >= l) return gapscan_dialogProc(hwndDlg,WM_CLOSE,0,0);
  1361. iPod_mhit * mhit = (iPod_mhit *)dev->getPlaylistTrack(0,i++);
  1362. if(!mhit->trackgapless && !mhit->gaplessData) {
  1363. wchar_t artist[50] = {0}, title[50] = {0}, buf[200] = {0};
  1364. dev->getTrackArtist((songid_t)mhit,artist,50);
  1365. dev->getTrackTitle((songid_t)mhit,title,50);
  1366. StringCchPrintf(buf,200,L"%d/%d: %s - %s",i+1,l,artist,title);
  1367. SetDlgItemText(hwndDlg,IDC_CAPTION,buf);
  1368. wchar_t infile[MAX_PATH]=L"";
  1369. dev->getFilename(infile,MAX_PATH,(songid_t)mhit);
  1370. BOOL worked=TRUE;
  1371. mhit->samplecount = GetFileInfoInt64(infile,L"numsamples",&worked);
  1372. mhit->pregap = (unsigned long)GetFileInfoInt64(infile,L"pregap",&worked);
  1373. mhit->postgap = (unsigned long)GetFileInfoInt64(infile,L"postgap",&worked);
  1374. mhit->gaplessData = (unsigned long)GetFileInfoInt64(infile,L"endoffset",&worked);
  1375. mhit->trackgapless = worked?1:0;
  1376. j++;
  1377. } else if(!(i%23)) SetDlgItemText(hwndDlg,IDC_CAPTION,WASABI_API_LNGSTRINGW(IDS_SCANNING));
  1378. if(j > 3 || !(i % 50)) {
  1379. SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETPOS,i,0);
  1380. SetTimer(hwndDlg,1,25,NULL);
  1381. return 0;
  1382. }
  1383. }
  1384. }
  1385. break;
  1386. case WM_CLOSE:
  1387. dev->writeiTunesDB();
  1388. EndDialog(hwndDlg,0);
  1389. dev->gapscanner = NULL;
  1390. break;
  1391. case WM_DESTROY:
  1392. dev->gapscanner = NULL;
  1393. break;
  1394. case WM_COMMAND:
  1395. switch(LOWORD(wParam)) {
  1396. case IDCANCEL:
  1397. gapscan_dialogProc(hwndDlg,WM_CLOSE,0,0);
  1398. break;
  1399. case IDC_BG:
  1400. ShowWindow(hwndDlg,SW_HIDE);
  1401. break;
  1402. }
  1403. break;
  1404. }
  1405. return 0;
  1406. }
  1407. static INT_PTR CALLBACK config_dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) {
  1408. static iPodDevice * dev;
  1409. switch(uMsg) {
  1410. case WM_INITDIALOG:
  1411. {
  1412. prefsParam* p = (prefsParam*)lParam;
  1413. p->config_tab_init(hwndDlg,p->parent);
  1414. dev = (iPodDevice*)p->dev;
  1415. if(dev->artdb && dev->thumbs.size()) {
  1416. wchar_t inifile[] = {dev->driveW,L":\\iPod_Control\\iTunes\\ml_pmp.ini"};
  1417. ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC_ARTGROUP),SW_SHOWNA);
  1418. ShowWindow(GetDlgItem(hwndDlg,IDC_CHECK_USEART),SW_SHOWNA);
  1419. CheckDlgButton(hwndDlg,IDC_CHECK_USEART,GetPrivateProfileInt(L"ml_pmp",L"albumart",1,inifile));
  1420. //ShowWindow(GetDlgItem(hwndDlg,IDC_COMBO_ARTMODE),SW_SHOWNA);
  1421. /*ComboBox combo(hwndDlg,IDC_COMBO_USEART);
  1422. combo.AddString(L"Add to all tracks");
  1423. combo.AddString(L"Only add to the first track in an album");
  1424. combo.AddString(L"Don't add to any tracks");
  1425. */
  1426. }
  1427. }
  1428. break;
  1429. case WM_COMMAND:
  1430. switch(LOWORD(wParam)) {
  1431. case IDC_SCAN:
  1432. if(dev->gapscanner) ShowWindow(dev->gapscanner,SW_SHOW);
  1433. else WASABI_API_DIALOGBOXPARAM(IDD_GAPSCAN,NULL,gapscan_dialogProc,(LPARAM)dev);
  1434. break;
  1435. case IDC_CHECK_USEART:
  1436. wchar_t inifile[] = {dev->driveW,L":\\iPod_Control\\iTunes\\ml_pmp.ini"}, s[32] = {0};
  1437. StringCchPrintf(s, 32, L"%d", (IsDlgButtonChecked(hwndDlg, IDC_CHECK_USEART)==BST_CHECKED));
  1438. WritePrivateProfileString(L"ml_pmp", L"albumart", s, inifile);
  1439. break;
  1440. }
  1441. break;
  1442. }
  1443. return 0;
  1444. }
  1445. static const intptr_t encoder_blacklist[] =
  1446. {
  1447. mmioFOURCC('W','M','A',' '),
  1448. mmioFOURCC('A','A','C','H'),
  1449. mmioFOURCC('A','A','C','P'),
  1450. mmioFOURCC('A','A','C','r'),
  1451. mmioFOURCC('F','L','A','C'),
  1452. mmioFOURCC('O','G','G',' '),
  1453. mmioFOURCC('M','P','2',' '),
  1454. mmioFOURCC('M','4','A','H'),
  1455. mmioFOURCC('M','4','A','+'),
  1456. mmioFOURCC('A','D','T','S'),
  1457. };
  1458. intptr_t iPodDevice::extraActions(intptr_t param1, intptr_t param2, intptr_t param3,intptr_t param4) {
  1459. switch(param1) {
  1460. case DEVICE_SET_ICON: // icons
  1461. {
  1462. MLTREEIMAGE * i = (MLTREEIMAGE*)param2;
  1463. i->hinst = plugin.hDllInstance;
  1464. i->resourceId = image16;
  1465. }
  1466. break;
  1467. case DEVICE_GET_ICON:
  1468. {
  1469. if (param2 <= 16 && param3 <= 16)
  1470. {
  1471. // TODO: get the name of the DLL at load time
  1472. StringCchPrintfW((wchar_t *)param4, 260, L"res://%s/PNG/#%u", L"pmp_ipod.dll", image16);
  1473. }
  1474. else
  1475. {
  1476. // TODO: get the name of the DLL at load time
  1477. StringCchPrintfW((wchar_t *)param4, 260, L"res://%s/PNG/#%u", L"pmp_ipod.dll", image160);
  1478. }
  1479. }
  1480. break;
  1481. case DEVICE_SUPPORTED_METADATA:
  1482. return 0xffff | (artdb?SUPPORTS_ALBUMART:0);
  1483. case DEVICE_CAN_RENAME_DEVICE:
  1484. return 1;
  1485. case DEVICE_GET_INI_FILE:
  1486. {
  1487. wchar_t inifile[] = {driveW,L":\\iPod_Control\\iTunes\\ml_pmp.ini"};
  1488. wcsncpy((wchar_t*)param2,inifile,MAX_PATH);
  1489. }
  1490. break;
  1491. case DEVICE_GET_PREFS_DIALOG:
  1492. if(param3 == 0) {
  1493. pref_tab * p = (pref_tab *)param2;
  1494. p->hinst = WASABI_API_LNG_HINST;
  1495. p->dlg_proc = config_dialogProc;
  1496. p->res_id = IDD_CONFIG;
  1497. WASABI_API_LNGSTRINGW_BUF(IDS_ADVANCED,p->title,100);
  1498. }
  1499. break;
  1500. case DEVICE_REFRESH:
  1501. {
  1502. char drive = this->drive;
  1503. //iPods.eraseObject(this);
  1504. auto it = std::find(iPods.begin(), iPods.end(), this);
  1505. if (it != iPods.end())
  1506. {
  1507. iPods.erase(it);
  1508. }
  1509. SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICEDISCONNECTED);
  1510. delete this;
  1511. new iPodDevice(drive);
  1512. }
  1513. break;
  1514. case DEVICE_ADDPODCASTGROUP:
  1515. {
  1516. int pos = param3;
  1517. wchar_t * name = (wchar_t *)param4;
  1518. iPod_mhyp* pl = (iPod_mhyp*)playlists.Get(param2);
  1519. pl->podcastflag=1;
  1520. iPod_mhip * mhip = new iPod_mhip();
  1521. mhip->podcastgroupflag=256;
  1522. mhip->podcastgroupref=0;
  1523. iPod_mhod * d = new iPod_mhod();
  1524. d->SetString(name);
  1525. d->type=1;
  1526. mhip->mhod.push_back(d);
  1527. pl->mhip.insert(pl->mhip.begin()+pos,mhip);
  1528. }
  1529. break;
  1530. case DEVICE_ADDPODCASTGROUP_FINISH:
  1531. {
  1532. iPod_mhyp* pl = (iPod_mhyp*)playlists.Get(param2);
  1533. pl->numLibraryMHODs=0x18;
  1534. int groupref=0;
  1535. for(size_t i=0; i < pl->mhip.size(); i++) {
  1536. iPod_mhip * m = pl->mhip[i];
  1537. m->groupid = i+1000000;
  1538. if(m->podcastgroupflag & 256) groupref = m->groupid;
  1539. else m->podcastgroupref = groupref;
  1540. }
  1541. }
  1542. break;
  1543. case DEVICE_SUPPORTS_VIDEO:
  1544. return 1;
  1545. case DEVICE_VETO_ENCODER:
  1546. {
  1547. for (size_t i=0;i<sizeof(encoder_blacklist)/sizeof(*encoder_blacklist);i++)
  1548. {
  1549. // TODO: check device info XML for aacPlus support
  1550. if (param2 == encoder_blacklist[i])
  1551. return 1;
  1552. }
  1553. }
  1554. return 0;
  1555. case DEVICE_GET_MODEL:
  1556. {
  1557. wchar_t *model_buffer = (wchar_t *)param2;
  1558. unsigned int cch = (unsigned int)param3;
  1559. if (!info)
  1560. {
  1561. StringCchCopyW(model_buffer, cch, L"Apple iPod");
  1562. }
  1563. else switch(info->family_id)
  1564. {
  1565. default:
  1566. StringCchCopyW(model_buffer, cch, L"Apple iPod");
  1567. break;
  1568. case 3:
  1569. StringCchCopyW(model_buffer, cch, L"Apple iPod Mini");
  1570. break;
  1571. case 4:
  1572. StringCchCopyW(model_buffer, cch, L"Apple iPod 4G");
  1573. break;
  1574. case 5:
  1575. StringCchCopyW(model_buffer, cch, L"Apple iPod Photo");
  1576. break;
  1577. case 6:
  1578. StringCchCopyW(model_buffer, cch, L"Apple iPod 5G");
  1579. break;
  1580. case 7:
  1581. StringCchCopyW(model_buffer, cch, L"Apple iPod Nano 1G");
  1582. break;
  1583. case 9:
  1584. StringCchCopyW(model_buffer, cch, L"Apple iPod Nano 2G");
  1585. break;
  1586. case 11:
  1587. StringCchCopyW(model_buffer, cch, L"Apple iPod Classic");
  1588. break;
  1589. case 12:
  1590. StringCchCopyW(model_buffer, cch, L"Apple iPod Fat Nano");
  1591. break;
  1592. case 15:
  1593. StringCchCopyW(model_buffer, cch, L"Apple iPod Nano4G");
  1594. break;
  1595. case 128:
  1596. StringCchCopyW(model_buffer, cch, L"Apple iPod Shuffle");
  1597. break;
  1598. case 130:
  1599. StringCchCopyW(model_buffer, cch, L"Apple iPod Shuffle 2G");
  1600. break;
  1601. case 132:
  1602. StringCchCopyW(model_buffer, cch, L"Apple iPod Shuffle 3G");
  1603. break;
  1604. }
  1605. }
  1606. return 1;
  1607. }
  1608. return 0;
  1609. }