mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-12-19 20:35:54 +01:00
599 lines
14 KiB
C++
599 lines
14 KiB
C++
#include "main.h"
|
|
#include "./api__ml_online.h"
|
|
#include "./config.h"
|
|
#include "./navigation.h"
|
|
#include "./resource.h"
|
|
#include "./preferences.h"
|
|
#include "./serviceHelper.h"
|
|
|
|
#include "../../General/gen_ml/ml.h"
|
|
#include "../../General/gen_ml/ml_ipc_0313.h"
|
|
|
|
#include "../nu/MediaLibraryInterface.h"
|
|
#include "../nu/AutoChar.h"
|
|
#include "../nu/ns_wc.h"
|
|
#include "../nu/AutoWide.h"
|
|
#include <vector>
|
|
#include "../nu/nonewthrow.c"
|
|
#include "../nu/ConfigCOM.h"
|
|
|
|
#include "BufferCache.h"
|
|
#include "OMCOM.h"
|
|
#include "JNetCom.h" // for buffer_map
|
|
|
|
#include <shlwapi.h>
|
|
#include <strsafe.h>
|
|
|
|
|
|
static int Plugin_Init();
|
|
static void Plugin_Quit();
|
|
static INT_PTR Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3);
|
|
|
|
static Navigation *navigation = NULL;
|
|
static std::vector<PLUGINUNLOADCALLBACK> *unloadCallbacks = NULL;
|
|
|
|
C_Config *g_config=NULL;
|
|
|
|
extern "C" winampMediaLibraryPlugin plugin =
|
|
{
|
|
MLHDR_VER,
|
|
"nullsoft(ml_online.dll)",
|
|
Plugin_Init,
|
|
Plugin_Quit,
|
|
Plugin_MessageProc,
|
|
0,
|
|
0,
|
|
0,
|
|
};
|
|
|
|
|
|
HINSTANCE Plugin_GetInstance(void)
|
|
{
|
|
return plugin.hDllInstance;
|
|
}
|
|
|
|
HWND Plugin_GetWinamp(void)
|
|
{
|
|
return plugin.hwndWinampParent;
|
|
}
|
|
|
|
HWND Plugin_GetLibrary(void)
|
|
{
|
|
return plugin.hwndLibraryParent;
|
|
}
|
|
|
|
HRESULT Plugin_GetNavigation(Navigation **instance)
|
|
{
|
|
if(NULL == instance) return E_POINTER;
|
|
|
|
if (NULL == navigation)
|
|
{
|
|
*instance = NULL;
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
*instance = navigation;
|
|
navigation->AddRef();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
typedef struct __PLUGINTIMERREC
|
|
{
|
|
UINT_PTR id;
|
|
PLUGINTIMERPROC callback;
|
|
ULONG_PTR data;
|
|
} PLUGINTIMERREC;
|
|
|
|
typedef std::vector<PLUGINTIMERREC> PluginTimerList;
|
|
|
|
static void CALLBACK Plugin_TimerProcDispath(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD elapsedMs)
|
|
{
|
|
HWND hLibrary = Plugin_GetLibrary();
|
|
if (NULL != hLibrary && FALSE != IsWindow(hLibrary))
|
|
{
|
|
PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
|
|
if (NULL != list)
|
|
{
|
|
size_t index = list->size();
|
|
while(index--)
|
|
{
|
|
PLUGINTIMERREC *rec = &list->at(index);
|
|
if (rec->id == eventId)
|
|
{
|
|
rec->callback(eventId, elapsedMs, rec->data);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
KillTimer(hwnd, eventId);
|
|
}
|
|
|
|
UINT_PTR Plugin_SetTimer(UINT elapseMs, PLUGINTIMERPROC callback, ULONG_PTR data)
|
|
{
|
|
HWND hLibrary = Plugin_GetLibrary();
|
|
if (NULL == hLibrary || FALSE == IsWindow(hLibrary))
|
|
return 0;
|
|
|
|
if (GetCurrentThreadId() != GetWindowThreadProcessId(hLibrary, NULL))
|
|
return 0;
|
|
|
|
if (NULL == callback)
|
|
return 0;
|
|
|
|
PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
|
|
if (NULL == list)
|
|
{
|
|
list = new PluginTimerList();
|
|
if (NULL == list) return 0;
|
|
if (0 == SetProp(hLibrary, L"OnlineMediaTimerData", list))
|
|
{
|
|
delete(list);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
PLUGINTIMERREC rec;
|
|
rec.data = data;
|
|
rec.callback = callback;
|
|
rec.id = SetTimer(NULL, NULL, elapseMs, Plugin_TimerProcDispath);
|
|
if (0 == rec.id)
|
|
{
|
|
if (0 == list->size())
|
|
{
|
|
RemoveProp(hLibrary, L"OnlineMediaTimerData");
|
|
delete(list);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
list->push_back(rec);
|
|
return rec.id;
|
|
}
|
|
|
|
void Plugin_KillTimer(UINT_PTR eventId)
|
|
{
|
|
KillTimer(NULL, eventId);
|
|
|
|
HWND hLibrary = Plugin_GetLibrary();
|
|
if (NULL == hLibrary || FALSE == IsWindow(hLibrary))
|
|
return;
|
|
|
|
PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
|
|
if (NULL == list) return;
|
|
|
|
size_t index = list->size();
|
|
while(index--)
|
|
{
|
|
if (list->at(index).id == eventId)
|
|
{
|
|
list->erase(list->begin() + index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (0 == list->size())
|
|
{
|
|
RemoveProp(hLibrary, L"OnlineMediaTimerData");
|
|
delete(list);
|
|
}
|
|
}
|
|
|
|
static void Plugin_UninitializeTimer()
|
|
{
|
|
HWND hLibrary = Plugin_GetLibrary();
|
|
if (NULL == hLibrary || FALSE == IsWindow(hLibrary))
|
|
return;
|
|
|
|
PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
|
|
RemoveProp(hLibrary, L"OnlineMediaTimerData");
|
|
if (NULL == list) return;
|
|
|
|
size_t index = list->size();
|
|
while(index--)
|
|
{
|
|
KillTimer(NULL, list->at(index).id);
|
|
}
|
|
|
|
delete(list);
|
|
}
|
|
|
|
|
|
wchar_t g_w_cachedir[2048] = {0};
|
|
int winampVersion=0;
|
|
|
|
OMCOM omCOM;
|
|
|
|
URLMap urlMap;
|
|
MetadataMap metadataMap;
|
|
Nullsoft::Utility::LockGuard urlMapGuard;
|
|
|
|
void LoadCacheItem( wchar_t *path )
|
|
{
|
|
FILECACHETYPE cachefile = {0};
|
|
unsigned long size = 0;
|
|
HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) return;
|
|
ReadFile(hFile, &cachefile, sizeof(FILECACHETYPE),&size, NULL);
|
|
if ( size == sizeof(FILECACHETYPE ))
|
|
{
|
|
size = 0;
|
|
time_t now = time(NULL);
|
|
//read the header, validate
|
|
if ( cachefile.version == FILECACHEVERSION )
|
|
{
|
|
if ( now < cachefile.expires )
|
|
{
|
|
char *url = (char *)calloc((size_t)cachefile.urllen, sizeof(char));
|
|
if (url)
|
|
{
|
|
size = 0;
|
|
ReadFile(hFile, url, (DWORD)cachefile.urllen, &size, NULL);
|
|
if ( cachefile.urllen == size ) // we read it ok!
|
|
{
|
|
char tempbuf[16384] = {0};
|
|
Buffer_GrowBuf *newbuffer = new Buffer_GrowBuf;
|
|
INT64 readin=0;
|
|
newbuffer->expire_time = (time_t)cachefile.expires;
|
|
while ( readin != cachefile.datalen )
|
|
{
|
|
DWORD toread=(DWORD)cachefile.datalen - (DWORD)readin;
|
|
if ( toread > 16384 ) toread=16384;
|
|
size = 0;
|
|
int success = ReadFile(hFile, &tempbuf, toread , &size, NULL);
|
|
if ( success )
|
|
{
|
|
if ( size )
|
|
{
|
|
newbuffer->add(tempbuf,(int)size);
|
|
readin += size;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if ( readin != cachefile.datalen )
|
|
{
|
|
delete newbuffer;
|
|
}
|
|
else
|
|
{
|
|
buffer_map[(wchar_t *)AutoWide(url)]=newbuffer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
free(url);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CloseHandle(hFile);
|
|
DeleteFile(path);
|
|
}
|
|
|
|
void LoadCache()
|
|
{
|
|
WIN32_FIND_DATA FindFileData = {0};
|
|
HANDLE hFind;
|
|
wchar_t searchs[2048] = {0};
|
|
|
|
buffer_map.clear();
|
|
|
|
StringCchPrintf(searchs, 2048, L"%s\\*.w5x",g_w_cachedir);
|
|
hFind = FindFirstFile(searchs, &FindFileData);
|
|
if ( hFind != INVALID_HANDLE_VALUE )
|
|
{
|
|
do
|
|
{
|
|
wchar_t activefile[2048] = {0};
|
|
StringCchPrintf(activefile, 2048, L"%s\\%s",g_w_cachedir,FindFileData.cFileName);
|
|
LoadCacheItem(activefile);
|
|
} while ( FindNextFile(hFind, &FindFileData) );
|
|
FindClose(hFind);
|
|
}
|
|
}
|
|
|
|
void SaveCache()
|
|
{
|
|
BufferMap::iterator buffer_it;
|
|
DWORD start=0xABBACAFE;
|
|
for(buffer_it = buffer_map.begin();buffer_it != buffer_map.end(); buffer_it++)
|
|
{
|
|
time_t now = time(NULL);
|
|
if ( buffer_it->second->expire_time > now )
|
|
{
|
|
wchar_t filename[2048] = {0};
|
|
FILECACHETYPE cachefile;
|
|
HANDLE hFile;
|
|
INT64 size=0;
|
|
memset((void *)&cachefile,0,sizeof(FILECACHETYPE));
|
|
cachefile.version = FILECACHEVERSION;
|
|
cachefile.expires = buffer_it->second->expire_time;
|
|
AutoChar charUrl(buffer_it->first.c_str());
|
|
cachefile.urllen = strlen(charUrl)+1;
|
|
cachefile.datalen = buffer_it->second->getlen()+1;
|
|
|
|
StringCchPrintf(filename, 2048, L"%s\\%08X.w5x",g_w_cachedir,start++);
|
|
hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
WriteFile(hFile, &cachefile, sizeof(FILECACHETYPE),(LPDWORD)&size,NULL);
|
|
if ( size == sizeof(FILECACHETYPE) )
|
|
{
|
|
char blank[2]="\0";
|
|
size = 0; WriteFile(hFile, (char *)charUrl ,(DWORD)cachefile.urllen, (LPDWORD)&size, NULL);
|
|
size = 0; WriteFile(hFile, buffer_it->second->get() , (DWORD)buffer_it->second->getlen(), (LPDWORD)&size, NULL);
|
|
size = 0; WriteFile(hFile, blank , 1, (LPDWORD)&size, NULL);
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(hFile);
|
|
hFile=NULL;
|
|
DeleteFile(filename);
|
|
}
|
|
}
|
|
if (hFile)
|
|
{
|
|
CloseHandle(hFile);
|
|
hFile=NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void initConfigCache()
|
|
{
|
|
wchar_t iniFileName[2048] = {0};
|
|
mediaLibrary.BuildPath(L"Plugins\\ml", iniFileName, 2048);
|
|
CreateDirectory(iniFileName, NULL);
|
|
mediaLibrary.BuildPath(L"Plugins\\ml\\cache", g_w_cachedir, 2048);
|
|
CreateDirectory(g_w_cachedir, NULL);
|
|
mediaLibrary.BuildPath(L"Plugins\\ml\\ml_online.ini", iniFileName, 2048);
|
|
AutoChar charFn(iniFileName);
|
|
g_config = new C_Config(AutoChar(iniFileName));
|
|
|
|
int x = g_config->ReadInt("maxbandwidth", MAXBANDWIDTH );
|
|
g_config->WriteInt("maxbandwidth",x);
|
|
|
|
x = g_config->ReadInt("minbandwidth",1);
|
|
g_config->WriteInt("minbandwidth",x);
|
|
|
|
LoadCache();
|
|
}
|
|
|
|
static void Plugin_ExecuteOpenOnce()
|
|
{
|
|
CHAR szBuffer[128] = {0};
|
|
INT cchLen = Config_ReadStr("Navigation", "openOnce", NULL, szBuffer, ARRAYSIZE(szBuffer));
|
|
if (0 != cchLen)
|
|
{
|
|
UINT serviceId;
|
|
if (FALSE != StrToIntExA(szBuffer, STIF_SUPPORT_HEX, (INT*)&serviceId))
|
|
{
|
|
|
|
cchLen = Config_ReadStr("Navigation", "openOnceMode", NULL, szBuffer, ARRAYSIZE(szBuffer));
|
|
UINT showMode;
|
|
if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "popup", -1, szBuffer, cchLen))
|
|
showMode = SHOWMODE_POPUP;
|
|
else if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "ensureVisible", -1, szBuffer, cchLen))
|
|
showMode = SHOWMODE_ENSUREVISIBLE;
|
|
else
|
|
showMode = SHOWMODE_NORMAL;
|
|
|
|
ServiceHelper_ShowService(serviceId, showMode);
|
|
}
|
|
|
|
Config_WriteStr("Navigation", "openOnce", NULL);
|
|
Config_WriteStr("Navigation", "openOnceMode", NULL);
|
|
}
|
|
}
|
|
|
|
static int Plugin_Init()
|
|
{
|
|
if (FAILED(WasabiApi_Initialize(plugin.hDllInstance, plugin.service)))
|
|
return 1;
|
|
|
|
if (FAILED(WasabiApi_LoadDefaults()) ||
|
|
NULL == OMBROWSERMNGR ||
|
|
NULL == OMSERVICEMNGR ||
|
|
NULL == OMUTILITY)
|
|
{
|
|
WasabiApi_Release();
|
|
return 2;
|
|
}
|
|
|
|
ServiceHelper_Initialize();
|
|
|
|
if (NULL != WASABI_API_LNG)
|
|
{
|
|
static wchar_t szDescription[256];
|
|
StringCchPrintf(szDescription, ARRAYSIZE(szDescription),
|
|
WASABI_API_LNGSTRINGW(IDS_PLUGIN_NAME),
|
|
PLUGIN_VERSION_MAJOR, PLUGIN_VERSION_MINOR);
|
|
plugin.description = (char*)szDescription;
|
|
}
|
|
|
|
mediaLibrary.library = plugin.hwndLibraryParent;
|
|
mediaLibrary.winamp = plugin.hwndWinampParent;
|
|
mediaLibrary.instance = plugin.hDllInstance;
|
|
|
|
winampVersion = mediaLibrary.GetWinampVersion();
|
|
|
|
omCOM.Publish();
|
|
|
|
Preferences_Register();
|
|
|
|
if (NULL == navigation)
|
|
{
|
|
if (FAILED(Navigation::CreateInstance(&navigation)))
|
|
{
|
|
navigation = NULL;
|
|
|
|
if (NULL != unloadCallbacks)
|
|
{
|
|
size_t index = unloadCallbacks->size();
|
|
while(index--)
|
|
unloadCallbacks->at(index)();
|
|
delete(unloadCallbacks);
|
|
}
|
|
|
|
Preferences_Unregister();
|
|
WasabiApi_Release();
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
initConfigCache();
|
|
|
|
Plugin_ExecuteOpenOnce();
|
|
return ML_INIT_SUCCESS;
|
|
}
|
|
|
|
static void Plugin_Quit()
|
|
{
|
|
SaveCache();
|
|
buffer_map.clear();
|
|
|
|
Plugin_UninitializeTimer();
|
|
|
|
if (NULL != navigation)
|
|
{
|
|
navigation->Finish();
|
|
navigation->Release();
|
|
navigation = NULL;
|
|
}
|
|
|
|
if (NULL != unloadCallbacks)
|
|
{
|
|
size_t index = unloadCallbacks->size();
|
|
while(index--)
|
|
unloadCallbacks->at(index)();
|
|
delete(unloadCallbacks);
|
|
unloadCallbacks = NULL;
|
|
}
|
|
|
|
Preferences_Unregister();
|
|
|
|
WasabiApi_Release();
|
|
}
|
|
|
|
static INT_PTR TitleHook(waHookTitleStructW *hookTitle)
|
|
{
|
|
if (NULL == hookTitle ||
|
|
NULL == hookTitle->filename)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Nullsoft::Utility::AutoLock lock(urlMapGuard);
|
|
// this is kinda slow but AOL Videos is so underused anyway that this map won't fill up much
|
|
URLMap::iterator itr;
|
|
for (itr=urlMap.begin();itr!=urlMap.end();itr++)
|
|
{
|
|
if (!_wcsnicmp(hookTitle->filename, itr->url.c_str(), itr->url_wcslen))
|
|
{
|
|
if (NULL != hookTitle->title)
|
|
StringCchCopy(hookTitle->title, 2048, itr->title.c_str());
|
|
|
|
hookTitle->length = itr->length;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static INT_PTR MetadataHook(extendedFileInfoStructW *hookMetadata)
|
|
{
|
|
if (NULL == hookMetadata ||
|
|
NULL == hookMetadata->filename ||
|
|
NULL == hookMetadata->metadata)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Nullsoft::Utility::AutoLock lock(urlMapGuard);
|
|
// this is kinda slow but AOL Videos is so underused anyway that this map won't fill up much
|
|
MetadataMap::iterator itr;
|
|
|
|
for (itr=metadataMap.begin();itr!=metadataMap.end();itr++)
|
|
{
|
|
if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, hookMetadata->filename, -1, itr->url.c_str(), - 1) &&
|
|
CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, hookMetadata->metadata, -1, itr->tag.c_str(), - 1))
|
|
{
|
|
StringCchCopy(hookMetadata->ret, hookMetadata->retlen, itr->metadata.c_str());
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static INT_PTR Plugin_MessageProc(int msg, INT_PTR param1, INT_PTR param2, INT_PTR param3)
|
|
{
|
|
INT_PTR result = 0;
|
|
if (NULL != navigation &&
|
|
FALSE != navigation->ProcessMessage(msg, param1, param2, param3, &result))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
switch (msg)
|
|
{
|
|
case ML_IPC_HOOKTITLEW: return TitleHook((waHookTitleStructW *)param1);
|
|
case ML_IPC_HOOKEXTINFOW: return MetadataHook((extendedFileInfoStructW *)param1);
|
|
case ML_MSG_CONFIG: Preferences_Show(); return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void Plugin_RegisterUnloadCallback(PLUGINUNLOADCALLBACK callback)
|
|
{
|
|
if (NULL == unloadCallbacks)
|
|
{
|
|
unloadCallbacks = new std::vector<PLUGINUNLOADCALLBACK>();
|
|
if (NULL == unloadCallbacks)
|
|
return;
|
|
}
|
|
unloadCallbacks->push_back(callback);
|
|
}
|
|
|
|
|
|
extern "C" __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()
|
|
{
|
|
return &plugin;
|
|
}
|
|
|
|
#if 0
|
|
extern "C" __declspec( dllexport ) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) {
|
|
|
|
// prompt to remove our settings with default as no (just incase)
|
|
/*if(MessageBoxA(hwndDlg,"Do you also want to remove the saved settings for this plugin?",
|
|
plugin.description,MB_YESNO|MB_DEFBUTTON2) == IDYES)
|
|
{
|
|
WritePrivateProfileString("ml_rg", 0, 0, iniFile);
|
|
}*/
|
|
|
|
// also attempt to remove the ReplayGainAnalysis.dll so everything is kept cleaner
|
|
/*char path[MAX_PATH] = {0};
|
|
GetModuleFileName(hDllInst, path, MAX_PATH);
|
|
PathRemoveFileSpec(path);
|
|
PathAppend(path, "ReplayGainAnalysis.dll");
|
|
// if we get a handle then try to lower the handle count so we can delete
|
|
HINSTANCE rgLib = GetModuleHandle(path);
|
|
if(rgLib)
|
|
FreeLibrary(rgLib);
|
|
DeleteFile(path);*/
|
|
|
|
// allow an on-the-fly removal (since we've got to be with a compatible client build)
|
|
return ML_PLUGIN_UNINSTALL_NOW;
|
|
}
|
|
#endif
|