1
0

bgscan.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  1. #include "main.h"
  2. #include "resource.h"
  3. #include "api_mldb.h"
  4. #include "../Winamp/strutil.h"
  5. enum {
  6. STATUS_SEARCHING,
  7. STATUS_GETINFO,
  8. STATUS_DONE,
  9. };
  10. extern HWND g_bgrescan_status_hwnd;
  11. extern nde_scanner_t m_media_scanner;
  12. #define MAX_RECURSE_DEPTH 32
  13. /* Event handles */
  14. static HANDLE scan_killswitch=0;
  15. static HANDLE scan_cancel=0;
  16. static HANDLE scan_cancel_complete=0;
  17. /* Thread handle */
  18. static HANDLE scan_thread=0;
  19. /* extension list */
  20. static wchar_t *scan_extlist=0;
  21. static void SyncTable()
  22. {
  23. EnterCriticalSection(&g_db_cs);
  24. NDE_Table_Sync(g_table);
  25. g_table_dirty=0;
  26. LeaveCriticalSection(&g_db_cs);
  27. }
  28. static bool ScanCancelled(HANDLE *events, int count)
  29. {
  30. // make sure no one cancelled us
  31. DWORD eventFired=WaitForMultipleObjectsEx(count, events, FALSE, 0, TRUE);
  32. if (eventFired >= WAIT_OBJECT_0 && eventFired < (WAIT_OBJECT_0+count))
  33. return true;
  34. return false;
  35. }
  36. static bool SupportedType(const wchar_t *ext)
  37. {
  38. if (!scan_extlist)
  39. scan_extlist=(wchar_t*)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GET_EXTLISTW);
  40. // dunno how this would happen but should verify
  41. if (!scan_extlist || scan_extlist == (wchar_t *)1)
  42. return false;
  43. const wchar_t *a = scan_extlist;
  44. while (a && *a)
  45. {
  46. if (CompareStringW(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, a, -1, ext, -1) == CSTR_EQUAL)
  47. {
  48. return true;
  49. }
  50. a+=wcslen(a)+1;
  51. }
  52. return false;
  53. }
  54. static void CountFolder(const wchar_t *folder, int recurse, HANDLE cancelswitch, volatile int *found)
  55. {
  56. wchar_t filespec[MAX_PATH] = {0};
  57. PathCombineW(filespec, folder, L"*.*");
  58. WIN32_FIND_DATAW findData = {0};
  59. HANDLE h = FindFirstFileW(filespec, &findData);
  60. if (h != INVALID_HANDLE_VALUE)
  61. {
  62. if (IsWindow(g_bgrescan_status_hwnd))
  63. {
  64. wchar_t status[150+MAX_PATH] = {0};
  65. WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_DIR, status, 150);
  66. const wchar_t *p=folder+wcslen(folder);
  67. while (p > folder && *p != '\\') p--;
  68. p--;
  69. while (p >= folder && *p != '\\') p--;
  70. StringCbCatW(status, sizeof(status), ++p);
  71. SetWindowTextW(g_bgrescan_status_hwnd,status);
  72. }
  73. HANDLE events[2] = { scan_killswitch, cancelswitch};
  74. do
  75. {
  76. // make sure no one cancelled us
  77. if (ScanCancelled(events, 2))
  78. break;
  79. /* if it's a directory (And not either of the two special dirs */
  80. if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  81. && lstrcmpiW(findData.cFileName, L".")
  82. && lstrcmpiW(findData.cFileName, L".."))
  83. {
  84. if (recurse && recurse < MAX_RECURSE_DEPTH)
  85. {
  86. PathCombineW(filespec, folder, findData.cFileName);
  87. CountFolder(filespec, recurse+1, cancelswitch, found); // add 1 so we can verify recurse depth
  88. }
  89. }
  90. if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  91. {
  92. wchar_t *ext=extensionW(findData.cFileName);
  93. if (ext && ext[0] && SupportedType(ext))
  94. {
  95. PathCombineW(filespec, folder, findData.cFileName);
  96. if (IsWindow(g_bgrescan_status_hwnd))
  97. {
  98. wchar_t b[150+MAX_PATH] = {0};
  99. WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, b, 150);
  100. StringCbCatW(b, sizeof(b), filespec);
  101. SetWindowTextW(g_bgrescan_status_hwnd,b);
  102. }
  103. if (found)
  104. (*found)++;
  105. }
  106. }
  107. } while (FindNextFileW(h, &findData));
  108. FindClose(h);
  109. }
  110. else if (!(GetFileAttributesW(folder) & FILE_ATTRIBUTE_DIRECTORY))
  111. {
  112. if (IsWindow(g_bgrescan_status_hwnd))
  113. {
  114. wchar_t b[150+MAX_PATH] = {0};
  115. WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, b, 150);
  116. StringCbCatW(b, sizeof(b), folder);
  117. SetWindowTextW(g_bgrescan_status_hwnd,b);
  118. }
  119. if (found)
  120. (*found)++;
  121. }
  122. }
  123. static void ScanFolder(const wchar_t *folder, int recurse, int metadata, int guessmode, HANDLE cancelswitch, volatile int *scanned)
  124. {
  125. if ((unsigned long)folder < 65536) return;
  126. wchar_t filespec[MAX_PATH] = {0};
  127. wchar_t status[150+MAX_PATH] = {0};
  128. PathCombineW(filespec, folder, L"*.*");
  129. WIN32_FIND_DATAW findData = {0};
  130. HANDLE h = FindFirstFileW(filespec, &findData);
  131. if (h != INVALID_HANDLE_VALUE)
  132. {
  133. if (IsWindow(g_bgrescan_status_hwnd))
  134. {
  135. WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_DIR, status, 150);
  136. const wchar_t *p=folder+wcslen(folder);
  137. while (p > folder && *p != '\\') p--;
  138. p--;
  139. while (p >= folder && *p != '\\') p--;
  140. StringCbCatW(status, sizeof(status), ++p);
  141. SetWindowTextW(g_bgrescan_status_hwnd,status);
  142. }
  143. HANDLE events[2] = { scan_killswitch, cancelswitch};
  144. do
  145. {
  146. // make sure no one cancelled us
  147. if (ScanCancelled(events, 2))
  148. break;
  149. /* if it's a directory (And not either of the two special dirs */
  150. if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  151. && lstrcmpiW(findData.cFileName, L".")
  152. && lstrcmpiW(findData.cFileName, L".."))
  153. {
  154. if (recurse && recurse < MAX_RECURSE_DEPTH)
  155. {
  156. PathCombineW(filespec, folder, findData.cFileName);
  157. ScanFolder(filespec, recurse+1, metadata, guessmode, cancelswitch, scanned); // add 1 so we can verify recurse depth
  158. }
  159. }
  160. if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  161. {
  162. wchar_t *ext=extensionW(findData.cFileName);
  163. if (ext && ext[0] && SupportedType(ext))
  164. {
  165. PathCombineW(filespec, folder, findData.cFileName);
  166. if (IsWindow(g_bgrescan_status_hwnd))
  167. {
  168. WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, status, 150);
  169. StringCbCatW(status, sizeof(status), filespec);
  170. SetWindowTextW(g_bgrescan_status_hwnd,status);
  171. }
  172. addFileToDb(filespec, 0, metadata, guessmode);
  173. if (scanned)
  174. (*scanned)++;
  175. }
  176. }
  177. } while (FindNextFileW(h, &findData));
  178. FindClose(h);
  179. }
  180. else if (!(GetFileAttributesW(folder) & FILE_ATTRIBUTE_DIRECTORY))
  181. {
  182. if (IsWindow(g_bgrescan_status_hwnd))
  183. {
  184. WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, status, 150);
  185. StringCbCatW(status, sizeof(status), folder);
  186. SetWindowTextW(g_bgrescan_status_hwnd,status);
  187. }
  188. addFileToDb(folder, 0, metadata, guessmode);
  189. if (scanned)
  190. (*scanned)++;
  191. }
  192. }
  193. static DWORD CALLBACK ScanThreadProc(LPVOID param)
  194. {
  195. /* sit and run APCs until we get signalled to die */
  196. HANDLE events[2] = { scan_killswitch, scan_cancel};
  197. int eventFired;
  198. do
  199. {
  200. eventFired=WaitForMultipleObjectsEx(2, events, FALSE, INFINITE, TRUE);
  201. switch(eventFired)
  202. {
  203. case WAIT_OBJECT_0+1: // cancel event
  204. ResetEvent(scan_cancel);
  205. SetEvent(scan_cancel_complete);
  206. break;
  207. }
  208. }
  209. while (eventFired != WAIT_OBJECT_0);
  210. if (scan_extlist && scan_extlist != (wchar_t *)1)
  211. GlobalFree((HGLOBAL)scan_extlist);
  212. scan_extlist=0;
  213. return 0;
  214. }
  215. static bool ScanCreateThread()
  216. {
  217. if (!scan_thread)
  218. {
  219. /* create events */
  220. scan_killswitch = CreateEvent(NULL, TRUE, FALSE, NULL);
  221. scan_cancel = CreateEvent(NULL, TRUE, FALSE, NULL);
  222. scan_cancel_complete = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset event
  223. /* start thread */
  224. scan_thread = CreateThread(NULL, 0, ScanThreadProc, 0, 0, 0);
  225. }
  226. return !!scan_thread;
  227. }
  228. void Scan_Cancel()
  229. {
  230. HWND old = g_bgrescan_status_hwnd; // clear g_bgrescan_status_hwnd so that we don't deadlock when the BG thread calls SetWindowText
  231. g_bgrescan_status_hwnd = 0;
  232. if (scan_cancel)
  233. SignalObjectAndWait(scan_cancel, scan_cancel_complete, INFINITE, FALSE);
  234. g_bgrescan_status_hwnd = old;
  235. }
  236. void Scan_Kill()
  237. {
  238. HWND old = g_bgrescan_status_hwnd; // clear g_bgrescan_status_hwnd so that we don't deadlock when the BG thread calls SetWindowText
  239. g_bgrescan_status_hwnd = 0;
  240. if (scan_thread)
  241. SignalObjectAndWait(scan_killswitch, scan_thread, INFINITE, FALSE);
  242. g_bgrescan_status_hwnd = old;
  243. }
  244. /* ---------------
  245. * Scan_ScanFolder
  246. * ---------------
  247. */
  248. struct ScanFolderParams
  249. {
  250. ScanFolderParams(const wchar_t *_path, int _guess, int _meta, int _recurse)
  251. {
  252. path = _wcsdup(_path);
  253. guess = _guess >= 0 ? _guess : g_config->ReadInt(L"guessmode",0);;
  254. meta = _meta >= 0 ? _meta : g_config->ReadInt(L"usemetadata",1);
  255. recurse = _recurse;
  256. }
  257. ~ScanFolderParams()
  258. {
  259. free(path);
  260. }
  261. wchar_t *path;
  262. int guess;
  263. int meta;
  264. int recurse;
  265. };
  266. static VOID CALLBACK ScanFolderAPC(ULONG_PTR param)
  267. {
  268. // clear extension list to get latest config
  269. if (scan_extlist && scan_extlist != (wchar_t *)1)
  270. GlobalFree((HGLOBAL)scan_extlist);
  271. scan_extlist = 0;
  272. ScanFolderParams *params = (ScanFolderParams *)param;
  273. ScanFolder(params->path, params->recurse, params->meta, params->guess, scan_cancel, 0);
  274. SyncTable();
  275. delete params;
  276. }
  277. void Scan_ScanFolderBackground(const wchar_t *path, int guess, int meta, int recurse)
  278. {
  279. if (ScanCreateThread())
  280. {
  281. ScanFolderParams *params = new ScanFolderParams(path, guess, meta, recurse);
  282. if (QueueUserAPC(ScanFolderAPC, scan_thread, (ULONG_PTR)params) == 0)
  283. delete params;
  284. }
  285. }
  286. /* ---------------
  287. * Scan_ScanFolders
  288. * ---------------
  289. */
  290. struct ScanFoldersParams
  291. {
  292. ScanFoldersParams(wchar_t **_path, size_t _count, int *_guess, int *_meta, int *_recurse)
  293. {
  294. path = _path;
  295. count = _count;
  296. guess = _guess;
  297. meta = _meta;
  298. recurse = _recurse;
  299. found = 0;
  300. scanned = 0;
  301. cancel_switch = CreateEvent(NULL, TRUE, FALSE, NULL);
  302. status = STATUS_SEARCHING;
  303. ui = 0;
  304. in_timer = 0;
  305. }
  306. ~ScanFoldersParams()
  307. {
  308. for (size_t i=0;i!=count;i++)
  309. free(path[i]);
  310. free(path);
  311. free(guess);
  312. free(meta);
  313. free(recurse);
  314. CloseHandle(cancel_switch);
  315. }
  316. wchar_t **path;
  317. size_t count;
  318. int *guess;
  319. int *meta;
  320. int *recurse;
  321. volatile int found;
  322. volatile int scanned;
  323. volatile int status;
  324. int in_timer;
  325. HANDLE cancel_switch;
  326. HWND ui;
  327. };
  328. static VOID CALLBACK ScanFoldersAPC(ULONG_PTR param)
  329. {
  330. // clear extension list to get latest config
  331. if (scan_extlist && scan_extlist != (wchar_t *)1)
  332. GlobalFree((HGLOBAL)scan_extlist);
  333. scan_extlist = 0;
  334. ScanFoldersParams *params = (ScanFoldersParams *)param;
  335. HANDLE events[2] = { scan_killswitch, params->cancel_switch};
  336. for (size_t i=0;i!=params->count;i++)
  337. {
  338. if (ScanCancelled(events, 2))
  339. break;
  340. CountFolder(params->path[i], params->recurse[i], params->cancel_switch, &params->found);
  341. }
  342. params->status = STATUS_GETINFO;
  343. for (size_t i=0;i!=params->count;i++)
  344. {
  345. if (ScanCancelled(events, 2))
  346. break;
  347. int guess = params->guess[i] >= 0 ? params->guess[i] : g_config->ReadInt(L"guessmode",0);;
  348. int meta = params->meta[i] >= 0 ? params->meta[i] : g_config->ReadInt(L"usemetadata",1);
  349. ScanFolder(params->path[i], params->recurse[i], meta, guess, params->cancel_switch, &params->scanned);
  350. }
  351. params->status = STATUS_DONE;
  352. PostMessage(params->ui, WM_APP, 0, 0);
  353. }
  354. static INT_PTR CALLBACK ScanFileUI(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
  355. {
  356. switch(uMsg)
  357. {
  358. case WM_INITDIALOG:
  359. {
  360. SetDlgItemTextW(hwndDlg,IDC_STATUS,WASABI_API_LNGSTRINGW(IDS_INITIALIZING));
  361. ScanFoldersParams *params = (ScanFoldersParams *)lParam;
  362. params->ui = hwndDlg;
  363. SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
  364. if (QueueUserAPC(ScanFoldersAPC, scan_thread, (ULONG_PTR)lParam) == 0)
  365. EndDialog(hwndDlg, 0);
  366. else
  367. {
  368. SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETRANGE,0,MAKELPARAM(0, 100));
  369. SetTimer(hwndDlg,0x123,300,NULL);
  370. }
  371. // show window and restore last position as applicable
  372. POINT pt = {g_config->ReadInt(L"scan_x", -1), g_config->ReadInt(L"scan_y", -1)};
  373. if (!windowOffScreen(hwndDlg, pt))
  374. SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING);
  375. }
  376. break;
  377. case WM_TIMER:
  378. {
  379. ScanFoldersParams *params = (ScanFoldersParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  380. if (params->in_timer) break;
  381. params->in_timer++;
  382. if(params->status==STATUS_SEARCHING)
  383. {
  384. wchar_t tmp[512] = {0};
  385. StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_SEARCHING_X_FILES_FOUND), params->found);
  386. SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp);
  387. }
  388. else if(params->status==STATUS_GETINFO)
  389. {
  390. wchar_t tmp[512] = {0};
  391. int perc=params->found?(params->scanned*100/params->found):0;
  392. StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_GETTING_INFO_FROM_FILES_PERCENT),perc);
  393. SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp);
  394. SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETPOS,perc,0);
  395. }
  396. params->in_timer--;
  397. }
  398. break;
  399. case WM_APP:
  400. {
  401. KillTimer(hwndDlg,0x123);
  402. SyncTable();
  403. EndDialog(hwndDlg,0);
  404. }
  405. break;
  406. case WM_COMMAND:
  407. if (LOWORD(wParam)==IDCANCEL)
  408. {
  409. ScanFoldersParams *params = (ScanFoldersParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  410. SetEvent(params->cancel_switch);
  411. }
  412. break;
  413. case WM_DESTROY:
  414. {
  415. RECT scan_rect = {0};
  416. GetWindowRect(hwndDlg, &scan_rect);
  417. g_config->WriteInt(L"scan_x", scan_rect.left);
  418. g_config->WriteInt(L"scan_y", scan_rect.top);
  419. KillTimer(hwndDlg,0x123);
  420. ScanFoldersParams *params = (ScanFoldersParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  421. SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
  422. delete params;
  423. return FALSE;
  424. }
  425. }
  426. return FALSE;
  427. }
  428. /* When you call this function, it will own the memory and release it with free() */
  429. void Scan_ScanFolders(HWND parent, size_t count, wchar_t **paths, int *guess, int *meta, int *recurse)
  430. {
  431. openDb();
  432. if (g_table && ScanCreateThread())
  433. {
  434. ScanFoldersParams *params = new ScanFoldersParams(paths, count, guess, meta, recurse);
  435. WASABI_API_LNG->LDialogBoxParamW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID),
  436. GetModuleHandleW(L"winamp.exe"), IDD_ADDSTUFF,
  437. parent, (DLGPROC)ScanFileUI, (LPARAM)params);
  438. PostMessage(plugin.hwndWinampParent,WM_WA_IPC,NDE_Table_GetRecordsCount(g_table),IPC_STATS_LIBRARY_ITEMCNT);
  439. }
  440. else
  441. {
  442. for (size_t i=0;i!=count;i++)
  443. free(paths[i]);
  444. free(paths);
  445. }
  446. }
  447. void Scan_ScanFolder(HWND parent, const wchar_t *path, int guess, int meta, int recurse)
  448. {
  449. // kind of a hack ...
  450. if (ScanCreateThread())
  451. {
  452. wchar_t **paths = (wchar_t **)calloc(1, sizeof(wchar_t*));
  453. int *guesses = (int *)calloc(1, sizeof(int));
  454. int *metas = (int *)calloc(1, sizeof(int));
  455. int *recs = (int *)calloc(1, sizeof(int));
  456. *guesses = guess;
  457. *metas = meta;
  458. *recs = recurse;
  459. paths[0] = _wcsdup(path);
  460. Scan_ScanFolders(parent, 1, paths, guesses, metas, recs);
  461. }
  462. }
  463. static VOID CALLBACK BackgroundScanAPC(ULONG_PTR param)
  464. {
  465. openDb();
  466. if (!g_table)
  467. return;
  468. HANDLE events[2] = { scan_killswitch, scan_cancel};
  469. // clear extension list to get latest config
  470. if (scan_extlist && scan_extlist != (wchar_t *)1)
  471. GlobalFree((HGLOBAL)scan_extlist);
  472. scan_extlist = 0;
  473. // read list from config
  474. UINT codePage = CP_ACP;
  475. char scandirlist[65536] = {0};
  476. if (!g_config->ReadString("scandirlist", 0, scandirlist, 65536))
  477. {
  478. g_config->ReadString("scandirlist_utf8","", scandirlist, 65536);
  479. codePage = CP_UTF8;
  480. }
  481. AutoWide s1(scandirlist, codePage);
  482. size_t len = wcslen(s1)+2;
  483. wchar_t *s =(wchar_t*)calloc(len, sizeof(wchar_t));
  484. if (s)
  485. {
  486. lstrcpynW(s, s1, len);
  487. s[wcslen(s)+1]=0;
  488. wchar_t *p=s;
  489. while (p && *p == L'|') p++;
  490. while ((p=wcsstr(p,L"|")))
  491. {
  492. *p++=0;
  493. while (p && *p == L'|') p++;
  494. }
  495. p=s;
  496. // iterate through list
  497. while (p && *p && !ScanCancelled(events, 2))
  498. {
  499. while (p && *p == L'|') p++;
  500. int use_metadata=g_config->ReadInt(L"usemetadata",1);
  501. int guess_mode=g_config->ReadInt(L"guessmode",0);
  502. int recurse=1;
  503. if (*p == L'<' && wcsstr(p,L">"))
  504. {
  505. p++;
  506. while (p && *p != L'>')
  507. {
  508. // <MmSs>can prefix directory
  509. // M=metadata use override
  510. // m=no metadata
  511. // S=smart guessing
  512. // s=stupid guessing
  513. if (*p == L'M') use_metadata=1;
  514. else if (*p == L'm') use_metadata=0;
  515. else if (*p == L'S') guess_mode=0;
  516. else if (*p == L's') guess_mode=1;
  517. else if (*p == L'r') recurse=0;
  518. else if (*p == L'g') guess_mode=2;
  519. p++;
  520. }
  521. p++;
  522. }
  523. ScanFolder(p, recurse, use_metadata, guess_mode, scan_cancel, 0);
  524. p+=wcslen(p)+1;
  525. }
  526. free(s);
  527. }
  528. /* Remove missing files */
  529. if (!ScanCancelled(events, 2) && g_config->ReadInt(L"bgrescan_compact",1))
  530. {
  531. EnterCriticalSection(&g_db_cs);
  532. nde_scanner_t scanner = NDE_Table_CreateScanner(g_table);
  533. NDE_Scanner_Query(scanner, L"");
  534. NDE_Scanner_First(scanner);
  535. again:
  536. nde_field_t f=NDE_Scanner_GetFieldByID(scanner, MAINTABLE_ID_FILENAME);
  537. wchar_t *gs=0;
  538. if (f)
  539. {
  540. gs = NDE_StringField_GetString(f);
  541. ndestring_retain(gs);
  542. if (GetFileAttributesW(gs) != INVALID_FILE_ATTRIBUTES)
  543. {
  544. NDE_Scanner_Next(scanner);
  545. }
  546. else
  547. {
  548. // Issue wasabi callback for pre removal
  549. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)gs, 0);
  550. NDE_Scanner_Delete(scanner);
  551. NDE_Scanner_Post(scanner);
  552. // Issue wasabi callback for pre removal
  553. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)gs, 0);
  554. }
  555. }
  556. LeaveCriticalSection(&g_db_cs);
  557. if (f) // done checking for unused files
  558. {
  559. if (IsWindow(g_bgrescan_status_hwnd))
  560. {
  561. wchar_t b[150+MAX_PATH] = {0};
  562. WASABI_API_LNGSTRINGW_BUF(IDS_CHECKING_FOR_FILE, b, 150);
  563. StringCbCatW(b, sizeof(b), PathFindFileNameW(gs));
  564. SetWindowTextW(g_bgrescan_status_hwnd,b);
  565. }
  566. ndestring_release(gs);
  567. gs=0;
  568. if (!ScanCancelled(events, 2))
  569. {
  570. EnterCriticalSection(&g_db_cs);
  571. goto again;
  572. }
  573. }
  574. EnterCriticalSection(&g_db_cs);
  575. NDE_Table_DestroyScanner(g_table, scanner);
  576. LeaveCriticalSection(&g_db_cs);
  577. if (IsWindow(g_bgrescan_status_hwnd))
  578. {
  579. wchar_t b[150] = {0};
  580. WASABI_API_LNGSTRINGW_BUF(IDS_COMPACTING, b, 150);
  581. SetWindowTextW(g_bgrescan_status_hwnd,b);
  582. }
  583. }
  584. // TODO: hmmm, safe to do on separate thread?
  585. EnterCriticalSection(&g_db_cs);
  586. if (!ScanCancelled(events, 2))
  587. {
  588. wchar_t *last_query = NULL;
  589. if (m_media_scanner)
  590. {
  591. const wchar_t *lq = NDE_Scanner_GetLastQuery(m_media_scanner);
  592. if (lq) last_query = _wcsdup(lq);
  593. NDE_Table_DestroyScanner(g_table, m_media_scanner);
  594. }
  595. NDE_Table_Sync(g_table); // this is currently b0rk3d -- fucko :)
  596. NDE_Table_Compact(g_table);
  597. g_table_dirty=0;
  598. if (m_media_scanner)
  599. {
  600. m_media_scanner=NDE_Table_CreateScanner(g_table);
  601. if (last_query != NULL)
  602. {
  603. NDE_Scanner_Query(m_media_scanner, last_query);
  604. free(last_query);
  605. }
  606. }
  607. PostMessage(plugin.hwndWinampParent,WM_WA_IPC,NDE_Table_GetRecordsCount(g_table),IPC_STATS_LIBRARY_ITEMCNT);
  608. }
  609. else
  610. {
  611. NDE_Table_Sync(g_table); // this is currently b0rk3d -- fucko :)
  612. g_table_dirty=0;
  613. }
  614. LeaveCriticalSection(&g_db_cs);
  615. if (IsWindow(g_bgrescan_status_hwnd))
  616. SetWindowTextW(g_bgrescan_status_hwnd,L"");
  617. g_bgscan_last_rescan = time(NULL);
  618. g_bgscan_scanning = 0;
  619. }
  620. void Scan_BackgroundScan()
  621. {
  622. if (ScanCreateThread())
  623. {
  624. g_bgrescan_force = 0;
  625. g_bgscan_last_rescan = time(NULL);
  626. g_bgscan_scanning = 1;
  627. QueueUserAPC(BackgroundScanAPC, scan_thread, (ULONG_PTR)0);
  628. }
  629. }
  630. static void RemoveFiles(HANDLE cancelswitch, volatile int *found, volatile int *count, volatile int *scanned)
  631. {
  632. // TODO: benski> we might need to keep the database lock the whole time. need to think it thru
  633. EnterCriticalSection(&g_db_cs);
  634. nde_scanner_t myscanner=NDE_Table_CreateScanner(g_table);
  635. NDE_Scanner_Query(myscanner, L"");
  636. NDE_Scanner_First(myscanner);
  637. *found=0;
  638. *scanned=0;
  639. *count=NDE_Table_GetRecordsCount(g_table);
  640. LeaveCriticalSection(&g_db_cs);
  641. HANDLE events[2] = { scan_killswitch, cancelswitch};
  642. bool fileRemoved = false;
  643. wchar_t *filename;
  644. while (!ScanCancelled(events, 2))
  645. {
  646. EnterCriticalSection(&g_db_cs);
  647. if (!NDE_Scanner_BOF(myscanner) && !NDE_Scanner_EOF(myscanner))
  648. {
  649. nde_field_t f= NDE_Scanner_GetFieldByID(myscanner, MAINTABLE_ID_FILENAME);
  650. if (f)
  651. {
  652. (*scanned)++;
  653. filename = (NDE_StringField_GetString(f));
  654. if (GetFileAttributesW(NDE_StringField_GetString(f)) != INVALID_FILE_ATTRIBUTES)
  655. {
  656. NDE_Scanner_Next(myscanner);
  657. }
  658. else
  659. {
  660. // Issue wasabi callback for pre removal
  661. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)filename, 0);
  662. //remove file
  663. NDE_Scanner_Delete(myscanner);
  664. NDE_Scanner_Post(myscanner);
  665. (*found)++;
  666. fileRemoved = true;
  667. }
  668. }
  669. else
  670. {
  671. //remove file
  672. NDE_Scanner_Delete(myscanner);
  673. NDE_Scanner_Post(myscanner);
  674. (*found)++;
  675. fileRemoved = false;
  676. }
  677. }
  678. else // last file
  679. {
  680. LeaveCriticalSection(&g_db_cs);
  681. break;
  682. }
  683. LeaveCriticalSection(&g_db_cs);
  684. if (fileRemoved)
  685. {
  686. // Issue wasabi callback for pre removal
  687. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)filename, 0);
  688. fileRemoved = false;
  689. }
  690. }
  691. EnterCriticalSection(&g_db_cs);
  692. NDE_Table_DestroyScanner(g_table, myscanner); // important that we delete the scanner BEFORE
  693. myscanner=0;
  694. wchar_t *last_query = NULL;
  695. if (m_media_scanner)
  696. {
  697. const wchar_t *lq = NDE_Scanner_GetLastQuery(m_media_scanner);
  698. if (lq) last_query = _wcsdup(lq);
  699. NDE_Table_DestroyScanner(g_table, m_media_scanner);
  700. }
  701. NDE_Table_Sync(g_table); // this is currently b0rk3d -- fucko :)
  702. NDE_Table_Compact(g_table);
  703. g_table_dirty=0;
  704. if (m_media_scanner)
  705. {
  706. m_media_scanner=NDE_Table_CreateScanner(g_table);
  707. if (last_query != NULL)
  708. {
  709. NDE_Scanner_Query(m_media_scanner, last_query);
  710. free(last_query);
  711. }
  712. }
  713. LeaveCriticalSection(&g_db_cs);
  714. }
  715. struct RemoveFilesParams
  716. {
  717. RemoveFilesParams()
  718. {
  719. found = 0;
  720. scanned = 0;
  721. total = 0;
  722. cancel_switch = CreateEvent(NULL, TRUE, FALSE, NULL);
  723. ui = 0;
  724. in_timer = 0;
  725. }
  726. ~RemoveFilesParams()
  727. {
  728. CloseHandle(cancel_switch);
  729. }
  730. volatile int found;
  731. volatile int scanned;
  732. volatile int total;
  733. int in_timer;
  734. HANDLE cancel_switch;
  735. HWND ui;
  736. };
  737. static VOID CALLBACK RemoveFilesAPC(ULONG_PTR param)
  738. {
  739. RemoveFilesParams *params = (RemoveFilesParams *)param;
  740. RemoveFiles(params->cancel_switch, &params->found, &params->total, &params->scanned);
  741. PostMessage(params->ui, WM_APP, 0, 0);
  742. }
  743. static INT_PTR CALLBACK RemoveFilesUI(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
  744. {
  745. switch(uMsg)
  746. {
  747. case WM_INITDIALOG:
  748. {
  749. SetWindowTextW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_REMOVING_FILES_NOT_EXISTING));
  750. SetDlgItemTextW(hwndDlg,IDC_STATUS,WASABI_API_LNGSTRINGW(IDS_INITIALIZING));
  751. RemoveFilesParams *params = (RemoveFilesParams *)lParam;
  752. params->ui = hwndDlg;
  753. SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
  754. if (QueueUserAPC(RemoveFilesAPC, scan_thread, (ULONG_PTR)lParam) == 0)
  755. EndDialog(hwndDlg, 0);
  756. else
  757. {
  758. SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETRANGE,0,MAKELPARAM(0, 100));
  759. SetTimer(hwndDlg,0x123,300,NULL);
  760. }
  761. // show window and restore last position as applicable
  762. POINT pt = {g_config->ReadInt(L"scan_x", -1), g_config->ReadInt(L"scan_y", -1)};
  763. if (!windowOffScreen(hwndDlg, pt))
  764. SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING);
  765. }
  766. break;
  767. case WM_TIMER:
  768. {
  769. RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  770. if (params->in_timer) break;
  771. params->in_timer++;
  772. if(params->total)
  773. {
  774. wchar_t tmp[512] = {0};
  775. int perc=(params->scanned*100/(params->total?params->total:1));
  776. StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_SCANNING_X_OF_X_X_REMOVED),params->scanned,params->total,params->found);
  777. SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp);
  778. SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETPOS,perc,0);
  779. }
  780. params->in_timer--;
  781. }
  782. break;
  783. case WM_APP:
  784. {
  785. RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  786. KillTimer(hwndDlg,0x123);
  787. wchar_t tmp[512] = {0};
  788. int perc=(params->scanned*100/(params->total?params->total:1));
  789. StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_SCANNED_X_FILES_X_REMOVED),params->total,params->found);
  790. SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp);
  791. SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETPOS,perc,0);
  792. SyncTable();
  793. EndDialog(hwndDlg,0);
  794. }
  795. break;
  796. case WM_COMMAND:
  797. if (LOWORD(wParam)==IDCANCEL)
  798. {
  799. RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  800. SetEvent(params->cancel_switch);
  801. }
  802. break;
  803. case WM_DESTROY:
  804. {
  805. RECT scan_rect = {0};
  806. GetWindowRect(hwndDlg, &scan_rect);
  807. g_config->WriteInt(L"scan_x", scan_rect.left);
  808. g_config->WriteInt(L"scan_y", scan_rect.top);
  809. KillTimer(hwndDlg,0x123);
  810. RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  811. SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
  812. delete params;
  813. }
  814. return FALSE;
  815. }
  816. return FALSE;
  817. }
  818. void Scan_RemoveFiles(HWND parent)
  819. {
  820. openDb();
  821. if (g_table && ScanCreateThread())
  822. {
  823. RemoveFilesParams *params = new RemoveFilesParams();
  824. WASABI_API_LNG->LDialogBoxParamW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID),
  825. GetModuleHandleW(L"winamp.exe"), IDD_ADDSTUFF,
  826. parent, (DLGPROC)RemoveFilesUI, (LPARAM)params);
  827. PostMessage(plugin.hwndWinampParent,WM_WA_IPC,NDE_Table_GetRecordsCount(g_table),IPC_STATS_LIBRARY_ITEMCNT);
  828. }
  829. }