diff options
Diffstat (limited to 'Src/Plugins/Library/ml_local/bgscan.cpp')
-rw-r--r-- | Src/Plugins/Library/ml_local/bgscan.cpp | 940 |
1 files changed, 940 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_local/bgscan.cpp b/Src/Plugins/Library/ml_local/bgscan.cpp new file mode 100644 index 00000000..8b4d9bdc --- /dev/null +++ b/Src/Plugins/Library/ml_local/bgscan.cpp @@ -0,0 +1,940 @@ +#include "main.h" +#include "resource.h" +#include "api_mldb.h" +#include "../Winamp/strutil.h" + +enum { + STATUS_SEARCHING, + STATUS_GETINFO, + STATUS_DONE, +}; + +extern HWND g_bgrescan_status_hwnd; +extern nde_scanner_t m_media_scanner; + +#define MAX_RECURSE_DEPTH 32 +/* Event handles */ +static HANDLE scan_killswitch=0; +static HANDLE scan_cancel=0; +static HANDLE scan_cancel_complete=0; +/* Thread handle */ +static HANDLE scan_thread=0; +/* extension list */ +static wchar_t *scan_extlist=0; + +static void SyncTable() +{ + EnterCriticalSection(&g_db_cs); + NDE_Table_Sync(g_table); + g_table_dirty=0; + LeaveCriticalSection(&g_db_cs); +} + +static bool ScanCancelled(HANDLE *events, int count) +{ + // make sure no one cancelled us + DWORD eventFired=WaitForMultipleObjectsEx(count, events, FALSE, 0, TRUE); + if (eventFired >= WAIT_OBJECT_0 && eventFired < (WAIT_OBJECT_0+count)) + return true; + + return false; +} + +static bool SupportedType(const wchar_t *ext) +{ + if (!scan_extlist) + scan_extlist=(wchar_t*)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GET_EXTLISTW); + + // dunno how this would happen but should verify + if (!scan_extlist || scan_extlist == (wchar_t *)1) + return false; + + const wchar_t *a = scan_extlist; + while (a && *a) + { + if (CompareStringW(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, a, -1, ext, -1) == CSTR_EQUAL) + { + return true; + } + a+=wcslen(a)+1; + } + return false; +} + +static void CountFolder(const wchar_t *folder, int recurse, HANDLE cancelswitch, volatile int *found) +{ + wchar_t filespec[MAX_PATH] = {0}; + PathCombineW(filespec, folder, L"*.*"); + + WIN32_FIND_DATAW findData = {0}; + + HANDLE h = FindFirstFileW(filespec, &findData); + if (h != INVALID_HANDLE_VALUE) + { + if (IsWindow(g_bgrescan_status_hwnd)) + { + wchar_t status[150+MAX_PATH] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_DIR, status, 150); + + const wchar_t *p=folder+wcslen(folder); + while (p > folder && *p != '\\') p--; + p--; + while (p >= folder && *p != '\\') p--; + + StringCbCatW(status, sizeof(status), ++p); + + SetWindowTextW(g_bgrescan_status_hwnd,status); + } + + HANDLE events[2] = { scan_killswitch, cancelswitch}; + do + { + // make sure no one cancelled us + if (ScanCancelled(events, 2)) + break; + + /* if it's a directory (And not either of the two special dirs */ + if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + && lstrcmpiW(findData.cFileName, L".") + && lstrcmpiW(findData.cFileName, L"..")) + { + if (recurse && recurse < MAX_RECURSE_DEPTH) + { + PathCombineW(filespec, folder, findData.cFileName); + CountFolder(filespec, recurse+1, cancelswitch, found); // add 1 so we can verify recurse depth + } + } + + if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + wchar_t *ext=extensionW(findData.cFileName); + if (ext && ext[0] && SupportedType(ext)) + { + PathCombineW(filespec, folder, findData.cFileName); + if (IsWindow(g_bgrescan_status_hwnd)) + { + wchar_t b[150+MAX_PATH] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, b, 150); + StringCbCatW(b, sizeof(b), filespec); + SetWindowTextW(g_bgrescan_status_hwnd,b); + } + if (found) + (*found)++; + } + } + + } while (FindNextFileW(h, &findData)); + FindClose(h); + } + else if (!(GetFileAttributesW(folder) & FILE_ATTRIBUTE_DIRECTORY)) + { + if (IsWindow(g_bgrescan_status_hwnd)) + { + wchar_t b[150+MAX_PATH] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, b, 150); + StringCbCatW(b, sizeof(b), folder); + SetWindowTextW(g_bgrescan_status_hwnd,b); + } + if (found) + (*found)++; + } +} + +static void ScanFolder(const wchar_t *folder, int recurse, int metadata, int guessmode, HANDLE cancelswitch, volatile int *scanned) +{ + if ((unsigned long)folder < 65536) return; + + wchar_t filespec[MAX_PATH] = {0}; + wchar_t status[150+MAX_PATH] = {0}; + PathCombineW(filespec, folder, L"*.*"); + + WIN32_FIND_DATAW findData = {0}; + + HANDLE h = FindFirstFileW(filespec, &findData); + if (h != INVALID_HANDLE_VALUE) + { + if (IsWindow(g_bgrescan_status_hwnd)) + { + WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_DIR, status, 150); + + const wchar_t *p=folder+wcslen(folder); + while (p > folder && *p != '\\') p--; + p--; + while (p >= folder && *p != '\\') p--; + + StringCbCatW(status, sizeof(status), ++p); + + SetWindowTextW(g_bgrescan_status_hwnd,status); + } + + HANDLE events[2] = { scan_killswitch, cancelswitch}; + do + { + // make sure no one cancelled us + if (ScanCancelled(events, 2)) + break; + + /* if it's a directory (And not either of the two special dirs */ + if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + && lstrcmpiW(findData.cFileName, L".") + && lstrcmpiW(findData.cFileName, L"..")) + { + if (recurse && recurse < MAX_RECURSE_DEPTH) + { + PathCombineW(filespec, folder, findData.cFileName); + ScanFolder(filespec, recurse+1, metadata, guessmode, cancelswitch, scanned); // add 1 so we can verify recurse depth + } + } + + if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + wchar_t *ext=extensionW(findData.cFileName); + if (ext && ext[0] && SupportedType(ext)) + { + PathCombineW(filespec, folder, findData.cFileName); + if (IsWindow(g_bgrescan_status_hwnd)) + { + WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, status, 150); + StringCbCatW(status, sizeof(status), filespec); + SetWindowTextW(g_bgrescan_status_hwnd,status); + } + addFileToDb(filespec, 0, metadata, guessmode); + if (scanned) + (*scanned)++; + } + } + } while (FindNextFileW(h, &findData)); + FindClose(h); + } + else if (!(GetFileAttributesW(folder) & FILE_ATTRIBUTE_DIRECTORY)) + { + if (IsWindow(g_bgrescan_status_hwnd)) + { + WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, status, 150); + StringCbCatW(status, sizeof(status), folder); + SetWindowTextW(g_bgrescan_status_hwnd,status); + } + addFileToDb(folder, 0, metadata, guessmode); + if (scanned) + (*scanned)++; + } +} + +static DWORD CALLBACK ScanThreadProc(LPVOID param) +{ + /* sit and run APCs until we get signalled to die */ + HANDLE events[2] = { scan_killswitch, scan_cancel}; + int eventFired; + do + { + eventFired=WaitForMultipleObjectsEx(2, events, FALSE, INFINITE, TRUE); + switch(eventFired) + { + case WAIT_OBJECT_0+1: // cancel event + ResetEvent(scan_cancel); + SetEvent(scan_cancel_complete); + break; + } + } + while (eventFired != WAIT_OBJECT_0); + + if (scan_extlist && scan_extlist != (wchar_t *)1) + GlobalFree((HGLOBAL)scan_extlist); + scan_extlist=0; + return 0; +} + +static bool ScanCreateThread() +{ + if (!scan_thread) + { + /* create events */ + scan_killswitch = CreateEvent(NULL, TRUE, FALSE, NULL); + scan_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); + scan_cancel_complete = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset event + + /* start thread */ + scan_thread = CreateThread(NULL, 0, ScanThreadProc, 0, 0, 0); + } + + return !!scan_thread; +} + +void Scan_Cancel() +{ + HWND old = g_bgrescan_status_hwnd; // clear g_bgrescan_status_hwnd so that we don't deadlock when the BG thread calls SetWindowText + g_bgrescan_status_hwnd = 0; + if (scan_cancel) + SignalObjectAndWait(scan_cancel, scan_cancel_complete, INFINITE, FALSE); + g_bgrescan_status_hwnd = old; +} + +void Scan_Kill() +{ + HWND old = g_bgrescan_status_hwnd; // clear g_bgrescan_status_hwnd so that we don't deadlock when the BG thread calls SetWindowText + g_bgrescan_status_hwnd = 0; + if (scan_thread) + SignalObjectAndWait(scan_killswitch, scan_thread, INFINITE, FALSE); + g_bgrescan_status_hwnd = old; +} + +/* --------------- +* Scan_ScanFolder +* --------------- +*/ + +struct ScanFolderParams +{ + ScanFolderParams(const wchar_t *_path, int _guess, int _meta, int _recurse) + { + path = _wcsdup(_path); + guess = _guess >= 0 ? _guess : g_config->ReadInt(L"guessmode",0);; + meta = _meta >= 0 ? _meta : g_config->ReadInt(L"usemetadata",1); + recurse = _recurse; + } + ~ScanFolderParams() + { + free(path); + } + wchar_t *path; + int guess; + int meta; + int recurse; +}; + +static VOID CALLBACK ScanFolderAPC(ULONG_PTR param) +{ + // clear extension list to get latest config + if (scan_extlist && scan_extlist != (wchar_t *)1) + GlobalFree((HGLOBAL)scan_extlist); + scan_extlist = 0; + + ScanFolderParams *params = (ScanFolderParams *)param; + ScanFolder(params->path, params->recurse, params->meta, params->guess, scan_cancel, 0); + SyncTable(); + delete params; +} + +void Scan_ScanFolderBackground(const wchar_t *path, int guess, int meta, int recurse) +{ + if (ScanCreateThread()) + { + ScanFolderParams *params = new ScanFolderParams(path, guess, meta, recurse); + if (QueueUserAPC(ScanFolderAPC, scan_thread, (ULONG_PTR)params) == 0) + delete params; + } +} + +/* --------------- +* Scan_ScanFolders +* --------------- +*/ + +struct ScanFoldersParams +{ + ScanFoldersParams(wchar_t **_path, size_t _count, int *_guess, int *_meta, int *_recurse) + { + path = _path; + count = _count; + guess = _guess; + meta = _meta; + recurse = _recurse; + found = 0; + scanned = 0; + cancel_switch = CreateEvent(NULL, TRUE, FALSE, NULL); + status = STATUS_SEARCHING; + ui = 0; + in_timer = 0; + + } + ~ScanFoldersParams() + { + for (size_t i=0;i!=count;i++) + free(path[i]); + free(path); + free(guess); + free(meta); + free(recurse); + CloseHandle(cancel_switch); + } + wchar_t **path; + size_t count; + int *guess; + int *meta; + int *recurse; + volatile int found; + volatile int scanned; + volatile int status; + int in_timer; + HANDLE cancel_switch; + HWND ui; +}; + +static VOID CALLBACK ScanFoldersAPC(ULONG_PTR param) +{ + // clear extension list to get latest config + if (scan_extlist && scan_extlist != (wchar_t *)1) + GlobalFree((HGLOBAL)scan_extlist); + scan_extlist = 0; + + ScanFoldersParams *params = (ScanFoldersParams *)param; + HANDLE events[2] = { scan_killswitch, params->cancel_switch}; + + for (size_t i=0;i!=params->count;i++) + { + if (ScanCancelled(events, 2)) + break; + + CountFolder(params->path[i], params->recurse[i], params->cancel_switch, ¶ms->found); + } + + params->status = STATUS_GETINFO; + + for (size_t i=0;i!=params->count;i++) + { + if (ScanCancelled(events, 2)) + break; + + int guess = params->guess[i] >= 0 ? params->guess[i] : g_config->ReadInt(L"guessmode",0);; + int meta = params->meta[i] >= 0 ? params->meta[i] : g_config->ReadInt(L"usemetadata",1); + ScanFolder(params->path[i], params->recurse[i], meta, guess, params->cancel_switch, ¶ms->scanned); + } + + params->status = STATUS_DONE; + PostMessage(params->ui, WM_APP, 0, 0); +} + +static INT_PTR CALLBACK ScanFileUI(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: + { + SetDlgItemTextW(hwndDlg,IDC_STATUS,WASABI_API_LNGSTRINGW(IDS_INITIALIZING)); + + ScanFoldersParams *params = (ScanFoldersParams *)lParam; + params->ui = hwndDlg; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); + if (QueueUserAPC(ScanFoldersAPC, scan_thread, (ULONG_PTR)lParam) == 0) + EndDialog(hwndDlg, 0); + else + { + SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETRANGE,0,MAKELPARAM(0, 100)); + SetTimer(hwndDlg,0x123,300,NULL); + } + + // show window and restore last position as applicable + POINT pt = {g_config->ReadInt(L"scan_x", -1), g_config->ReadInt(L"scan_y", -1)}; + if (!windowOffScreen(hwndDlg, pt)) + SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING); + } + break; + + case WM_TIMER: + { + ScanFoldersParams *params = (ScanFoldersParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if (params->in_timer) break; + params->in_timer++; + + if(params->status==STATUS_SEARCHING) + { + wchar_t tmp[512] = {0}; + StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_SEARCHING_X_FILES_FOUND), params->found); + SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp); + } + else if(params->status==STATUS_GETINFO) + { + wchar_t tmp[512] = {0}; + int perc=params->found?(params->scanned*100/params->found):0; + StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_GETTING_INFO_FROM_FILES_PERCENT),perc); + SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp); + SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETPOS,perc,0); + } + params->in_timer--; + } + break; + + case WM_APP: + { + KillTimer(hwndDlg,0x123); + SyncTable(); + EndDialog(hwndDlg,0); + } + break; + + case WM_COMMAND: + if (LOWORD(wParam)==IDCANCEL) + { + ScanFoldersParams *params = (ScanFoldersParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + SetEvent(params->cancel_switch); + } + break; + + case WM_DESTROY: + { + RECT scan_rect = {0}; + GetWindowRect(hwndDlg, &scan_rect); + g_config->WriteInt(L"scan_x", scan_rect.left); + g_config->WriteInt(L"scan_y", scan_rect.top); + + KillTimer(hwndDlg,0x123); + ScanFoldersParams *params = (ScanFoldersParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); + delete params; + return FALSE; + } + } + return FALSE; +} + +/* When you call this function, it will own the memory and release it with free() */ +void Scan_ScanFolders(HWND parent, size_t count, wchar_t **paths, int *guess, int *meta, int *recurse) +{ + openDb(); + + if (g_table && ScanCreateThread()) + { + ScanFoldersParams *params = new ScanFoldersParams(paths, count, guess, meta, recurse); + WASABI_API_LNG->LDialogBoxParamW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID), + GetModuleHandleW(L"winamp.exe"), IDD_ADDSTUFF, + parent, (DLGPROC)ScanFileUI, (LPARAM)params); + PostMessage(plugin.hwndWinampParent,WM_WA_IPC,NDE_Table_GetRecordsCount(g_table),IPC_STATS_LIBRARY_ITEMCNT); + } + else + { + for (size_t i=0;i!=count;i++) + free(paths[i]); + free(paths); + } +} + +void Scan_ScanFolder(HWND parent, const wchar_t *path, int guess, int meta, int recurse) +{ + // kind of a hack ... + if (ScanCreateThread()) + { + wchar_t **paths = (wchar_t **)calloc(1, sizeof(wchar_t*)); + int *guesses = (int *)calloc(1, sizeof(int)); + int *metas = (int *)calloc(1, sizeof(int)); + int *recs = (int *)calloc(1, sizeof(int)); + *guesses = guess; + *metas = meta; + *recs = recurse; + paths[0] = _wcsdup(path); + Scan_ScanFolders(parent, 1, paths, guesses, metas, recs); + } +} + +static VOID CALLBACK BackgroundScanAPC(ULONG_PTR param) +{ + openDb(); + if (!g_table) + return; + + HANDLE events[2] = { scan_killswitch, scan_cancel}; + + // clear extension list to get latest config + if (scan_extlist && scan_extlist != (wchar_t *)1) + GlobalFree((HGLOBAL)scan_extlist); + scan_extlist = 0; + + // read list from config + UINT codePage = CP_ACP; + char scandirlist[65536] = {0}; + if (!g_config->ReadString("scandirlist", 0, scandirlist, 65536)) + { + g_config->ReadString("scandirlist_utf8","", scandirlist, 65536); + codePage = CP_UTF8; + } + + AutoWide s1(scandirlist, codePage); + size_t len = wcslen(s1)+2; + wchar_t *s =(wchar_t*)calloc(len, sizeof(wchar_t)); + if (s) + { + lstrcpynW(s, s1, len); + s[wcslen(s)+1]=0; + + wchar_t *p=s; + while (p && *p == L'|') p++; + + while ((p=wcsstr(p,L"|"))) + { + *p++=0; + while (p && *p == L'|') p++; + } + p=s; + + // iterate through list + while (p && *p && !ScanCancelled(events, 2)) + { + while (p && *p == L'|') p++; + + int use_metadata=g_config->ReadInt(L"usemetadata",1); + int guess_mode=g_config->ReadInt(L"guessmode",0); + int recurse=1; + if (*p == L'<' && wcsstr(p,L">")) + { + p++; + while (p && *p != L'>') + { + // <MmSs>can prefix directory + // M=metadata use override + // m=no metadata + // S=smart guessing + // s=stupid guessing + if (*p == L'M') use_metadata=1; + else if (*p == L'm') use_metadata=0; + else if (*p == L'S') guess_mode=0; + else if (*p == L's') guess_mode=1; + else if (*p == L'r') recurse=0; + else if (*p == L'g') guess_mode=2; + p++; + } + p++; + } + ScanFolder(p, recurse, use_metadata, guess_mode, scan_cancel, 0); + p+=wcslen(p)+1; + } + + free(s); + } + + /* Remove missing files */ + if (!ScanCancelled(events, 2) && g_config->ReadInt(L"bgrescan_compact",1)) + { + EnterCriticalSection(&g_db_cs); + nde_scanner_t scanner = NDE_Table_CreateScanner(g_table); + NDE_Scanner_Query(scanner, L""); + NDE_Scanner_First(scanner); +again: + nde_field_t f=NDE_Scanner_GetFieldByID(scanner, MAINTABLE_ID_FILENAME); + wchar_t *gs=0; + if (f) + { + gs = NDE_StringField_GetString(f); + ndestring_retain(gs); + if (GetFileAttributesW(gs) != INVALID_FILE_ATTRIBUTES) + { + NDE_Scanner_Next(scanner); + } + else + { + // Issue wasabi callback for pre removal + WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)gs, 0); + + NDE_Scanner_Delete(scanner); + NDE_Scanner_Post(scanner); + + // Issue wasabi callback for pre removal + WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)gs, 0); + } + } + LeaveCriticalSection(&g_db_cs); + + if (f) // done checking for unused files + { + if (IsWindow(g_bgrescan_status_hwnd)) + { + wchar_t b[150+MAX_PATH] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_CHECKING_FOR_FILE, b, 150); + StringCbCatW(b, sizeof(b), PathFindFileNameW(gs)); + SetWindowTextW(g_bgrescan_status_hwnd,b); + } + ndestring_release(gs); + gs=0; + if (!ScanCancelled(events, 2)) + { + EnterCriticalSection(&g_db_cs); + goto again; + } + } + + EnterCriticalSection(&g_db_cs); + NDE_Table_DestroyScanner(g_table, scanner); + LeaveCriticalSection(&g_db_cs); + + if (IsWindow(g_bgrescan_status_hwnd)) + { + wchar_t b[150] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_COMPACTING, b, 150); + SetWindowTextW(g_bgrescan_status_hwnd,b); + } + } + + // TODO: hmmm, safe to do on separate thread? + EnterCriticalSection(&g_db_cs); + if (!ScanCancelled(events, 2)) + { + wchar_t *last_query = NULL; + if (m_media_scanner) + { + const wchar_t *lq = NDE_Scanner_GetLastQuery(m_media_scanner); + if (lq) last_query = _wcsdup(lq); + NDE_Table_DestroyScanner(g_table, m_media_scanner); + } + NDE_Table_Sync(g_table); // this is currently b0rk3d -- fucko :) + NDE_Table_Compact(g_table); + g_table_dirty=0; + if (m_media_scanner) + { + m_media_scanner=NDE_Table_CreateScanner(g_table); + if (last_query != NULL) + { + NDE_Scanner_Query(m_media_scanner, last_query); + free(last_query); + } + } + + PostMessage(plugin.hwndWinampParent,WM_WA_IPC,NDE_Table_GetRecordsCount(g_table),IPC_STATS_LIBRARY_ITEMCNT); + } + else + { + NDE_Table_Sync(g_table); // this is currently b0rk3d -- fucko :) + g_table_dirty=0; + } + LeaveCriticalSection(&g_db_cs); + if (IsWindow(g_bgrescan_status_hwnd)) + SetWindowTextW(g_bgrescan_status_hwnd,L""); + g_bgscan_last_rescan = time(NULL); + g_bgscan_scanning = 0; +} + +void Scan_BackgroundScan() +{ + if (ScanCreateThread()) + { + g_bgrescan_force = 0; + g_bgscan_last_rescan = time(NULL); + g_bgscan_scanning = 1; + + QueueUserAPC(BackgroundScanAPC, scan_thread, (ULONG_PTR)0); + + } +} + +static void RemoveFiles(HANDLE cancelswitch, volatile int *found, volatile int *count, volatile int *scanned) +{ + // TODO: benski> we might need to keep the database lock the whole time. need to think it thru + EnterCriticalSection(&g_db_cs); + nde_scanner_t myscanner=NDE_Table_CreateScanner(g_table); + NDE_Scanner_Query(myscanner, L""); + NDE_Scanner_First(myscanner); + *found=0; + *scanned=0; + *count=NDE_Table_GetRecordsCount(g_table); + LeaveCriticalSection(&g_db_cs); + + HANDLE events[2] = { scan_killswitch, cancelswitch}; + + bool fileRemoved = false; + wchar_t *filename; + while (!ScanCancelled(events, 2)) + { + EnterCriticalSection(&g_db_cs); + if (!NDE_Scanner_BOF(myscanner) && !NDE_Scanner_EOF(myscanner)) + { + nde_field_t f= NDE_Scanner_GetFieldByID(myscanner, MAINTABLE_ID_FILENAME); + if (f) + { + (*scanned)++; + + filename = (NDE_StringField_GetString(f)); + + if (GetFileAttributesW(NDE_StringField_GetString(f)) != INVALID_FILE_ATTRIBUTES) + { + NDE_Scanner_Next(myscanner); + } + else + { + // Issue wasabi callback for pre removal + WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)filename, 0); + + //remove file + NDE_Scanner_Delete(myscanner); + NDE_Scanner_Post(myscanner); + (*found)++; + fileRemoved = true; + } + } + else + { + //remove file + NDE_Scanner_Delete(myscanner); + NDE_Scanner_Post(myscanner); + (*found)++; + fileRemoved = false; + } + } + else // last file + { + LeaveCriticalSection(&g_db_cs); + break; + } + LeaveCriticalSection(&g_db_cs); + + if (fileRemoved) + { + // Issue wasabi callback for pre removal + WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)filename, 0); + fileRemoved = false; + } + } + EnterCriticalSection(&g_db_cs); + NDE_Table_DestroyScanner(g_table, myscanner); // important that we delete the scanner BEFORE + myscanner=0; + + wchar_t *last_query = NULL; + if (m_media_scanner) + { + const wchar_t *lq = NDE_Scanner_GetLastQuery(m_media_scanner); + if (lq) last_query = _wcsdup(lq); + NDE_Table_DestroyScanner(g_table, m_media_scanner); + } + NDE_Table_Sync(g_table); // this is currently b0rk3d -- fucko :) + NDE_Table_Compact(g_table); + g_table_dirty=0; + if (m_media_scanner) + { + m_media_scanner=NDE_Table_CreateScanner(g_table); + if (last_query != NULL) + { + NDE_Scanner_Query(m_media_scanner, last_query); + free(last_query); + } + } + LeaveCriticalSection(&g_db_cs); +} + +struct RemoveFilesParams +{ + RemoveFilesParams() + { + found = 0; + scanned = 0; + total = 0; + cancel_switch = CreateEvent(NULL, TRUE, FALSE, NULL); + ui = 0; + in_timer = 0; + } + ~RemoveFilesParams() + { + + CloseHandle(cancel_switch); + } + volatile int found; + volatile int scanned; + volatile int total; + + int in_timer; + HANDLE cancel_switch; + HWND ui; +}; + +static VOID CALLBACK RemoveFilesAPC(ULONG_PTR param) +{ + RemoveFilesParams *params = (RemoveFilesParams *)param; + + RemoveFiles(params->cancel_switch, ¶ms->found, ¶ms->total, ¶ms->scanned); + PostMessage(params->ui, WM_APP, 0, 0); +} + +static INT_PTR CALLBACK RemoveFilesUI(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: + { + SetWindowTextW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_REMOVING_FILES_NOT_EXISTING)); + SetDlgItemTextW(hwndDlg,IDC_STATUS,WASABI_API_LNGSTRINGW(IDS_INITIALIZING)); + + RemoveFilesParams *params = (RemoveFilesParams *)lParam; + params->ui = hwndDlg; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); + if (QueueUserAPC(RemoveFilesAPC, scan_thread, (ULONG_PTR)lParam) == 0) + EndDialog(hwndDlg, 0); + else + { + SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETRANGE,0,MAKELPARAM(0, 100)); + SetTimer(hwndDlg,0x123,300,NULL); + } + + // show window and restore last position as applicable + POINT pt = {g_config->ReadInt(L"scan_x", -1), g_config->ReadInt(L"scan_y", -1)}; + if (!windowOffScreen(hwndDlg, pt)) + SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING); + } + break; + + case WM_TIMER: + { + RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if (params->in_timer) break; + params->in_timer++; + + if(params->total) + { + wchar_t tmp[512] = {0}; + int perc=(params->scanned*100/(params->total?params->total:1)); + StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_SCANNING_X_OF_X_X_REMOVED),params->scanned,params->total,params->found); + SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp); + SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETPOS,perc,0); + } + + params->in_timer--; + } + break; + + case WM_APP: + { + RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + KillTimer(hwndDlg,0x123); + + wchar_t tmp[512] = {0}; + int perc=(params->scanned*100/(params->total?params->total:1)); + StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_SCANNED_X_FILES_X_REMOVED),params->total,params->found); + SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp); + SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETPOS,perc,0); + + SyncTable(); + EndDialog(hwndDlg,0); + } + break; + + case WM_COMMAND: + if (LOWORD(wParam)==IDCANCEL) + { + RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + SetEvent(params->cancel_switch); + } + break; + + case WM_DESTROY: + { + RECT scan_rect = {0}; + GetWindowRect(hwndDlg, &scan_rect); + g_config->WriteInt(L"scan_x", scan_rect.left); + g_config->WriteInt(L"scan_y", scan_rect.top); + + KillTimer(hwndDlg,0x123); + RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); + delete params; + } + return FALSE; + } + return FALSE; +} + +void Scan_RemoveFiles(HWND parent) +{ + openDb(); + if (g_table && ScanCreateThread()) + { + RemoveFilesParams *params = new RemoveFilesParams(); + WASABI_API_LNG->LDialogBoxParamW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID), + GetModuleHandleW(L"winamp.exe"), IDD_ADDSTUFF, + parent, (DLGPROC)RemoveFilesUI, (LPARAM)params); + PostMessage(plugin.hwndWinampParent,WM_WA_IPC,NDE_Table_GetRecordsCount(g_table),IPC_STATS_LIBRARY_ITEMCNT); + } +}
\ No newline at end of file |