winamp/Src/Plugins/Library/ml_local/bgscan.cpp
2024-09-24 14:54:57 +02:00

940 lines
24 KiB
C++

#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, &params->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, &params->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, &params->found, &params->total, &params->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);
}
}