| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719 | #define PLUGIN_VER L"1.81"#define FORCED_REBUILD_VERSION 1	// When changing this be sure to update 'forcedRebuildVersion' logic if no breaking changes are introduced in the new version#include "api__ml_plg.h"#include "../../General/gen_ml/ml.h"#include "resource.h"#include "main.h"#include "../winamp/wa_ipc.h"#include "../winamp/ipc_pe.h"#include "../nu/MediaLibraryInterface.h"#include "../nu/AutoChar.h"#include "../nu/ns_wc.h"#include <api/service/waservicefactory.h>#include "playlist.h"#include "../Agave/Language/api_language.h"#include "resource.h"//#include "mldbcallbacks.h"#include "../../General/gen_ml/menufucker.h"#include "../nu/ServiceWatcher.h"//#include "api_playlist_generator.h"#include "../nu/Singleton.h"#include "PlaylistGeneratorApi.h"//#include "wacmldbcallbacks.h"#include <strsafe.h> // make sure this always gets #include'd last// For the playlist generator APIstatic PlaylistGeneratorAPI playlistGeneratorAPI;static SingletonServiceFactory<api_playlist_generator, PlaylistGeneratorAPI> playlistGeneratorFactory;api_threadpool *WASABI_API_THREADPOOL = 0;api_application *WASABI_API_APP = 0;api_playlistmanager *AGAVE_API_PLAYLISTMGR = 0;api_language *WASABI_API_LNG = 0;api_config *AGAVE_API_CONFIG=0;api_gracenote *AGAVE_API_GRACENOTE=0;api_decodefile *AGAVE_API_DECODE=0;api_metadata *AGAVE_API_METADATA=0;api_mldb *AGAVE_API_MLDB = 0;api_playlists *AGAVE_API_PLAYLISTS = 0;api_stats *AGAVE_API_STATS = 0;HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;api_syscb *WASABI_API_SYSCB=0;class MLDBWatcher : public ServiceWatcherSingle{public:	void OnDeregister()	{		StopScan();	}};static MLDBWatcher mldbWatcher;extern winampMediaLibraryPlugin plugin;template <class api_T>void ServiceBuild(api_T *&api_t, GUID factoryGUID_t){	if (plugin.service)	{		waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);		if (factory)			api_t = reinterpret_cast<api_T *>( factory->getInterface() );	}}template <class api_T>void ServiceRelease(api_T *&api_t, GUID factoryGUID_t){	if (plugin.service && api_t)	{		waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);		if (factory)			factory->releaseInterface(api_t);	}	api_t = NULL;}static HWND winampPlaylist;static int ML_IPC_MENUFUCKER_BUILD;static int ML_IPC_MENUFUCKER_RESULT;bool pluginEnabled = true;int scanMode = 0;						// 0 = not inited, 1 = on start, 2 = on useint plLengthType = PL_ITEMS;			// 0 = not inited, 1 = items (# of), 2 = length (minutes), 3 = size (kilobytes)//int plLength = 20;					// Length of playlist, used for either # of items or for minutes target of the playlistint plItems = 20;						// Number of desired items in the playlistint plMinutes = 60;						// Number of desired minutes in the playlistint plMegabytes = 650;					// Size of the desired playlist in kilobytesint multipleArtists = FALSE;			// Generate tracks from the same artists in a single playlistint multipleAlbums = FALSE;				// Generate tracks from the same albums in a single playlistint useSeed = TRUE;						// Put the seed track into the generated playlistint useMLQuery = FALSE;					// Use a custom query against the media library database to post process the resultswchar_t mlQuery[MAX_ML_QUERY_SIZE] = {0};	// Storage for the custom queryint forcedRebuildVersion = 0;			// Stores the ID of a forced rebuild when upgrading, 0 is never reset, 1 reset with ml_plg rewrite, 2+ and on are reserved for future scenariosThreadID *plg_thread=0;					// Thread ID for the single gracenote thread that we always make API calls on.bool reset_db_flag = false;				// Flag that gets set whenever the DB needs to be reset before a scan.bool run_full_scan_flag = true;			// Flag that gets set whenever there are media library changes so step 4 (pass 2) can be rerun for any changed filesvolatile bool run_pass2_flag = false;	// Flag that gets set whenever there are media library changes so step 4 (pass 2) can be rerun for any changed filesvoid WriteIntToIni(const char *key, const int value){	char buf[32] = {0};	_itoa(value, buf, 10);	WritePrivateProfileStringA("ml_plg", key, buf, mediaLibrary.GetWinampIni());}// BE CAREFULL! Using this could potentially internationalize floats on some versions of windows eg. '1,6' instead of '1.6'void WriteFloatToIni(const char *key, const float value){	char buf[32] = {0};	StringCchPrintfA(buf, 32, "%.2f", value);	WritePrivateProfileStringA("ml_plg", key, buf, mediaLibrary.GetWinampIni());}int Init(){	mediaLibrary.library = plugin.hwndLibraryParent;	mediaLibrary.winamp = plugin.hwndWinampParent;	mediaLibrary.instance = plugin.hDllInstance;	ServiceBuild(WASABI_API_SYSCB, syscbApiServiceGuid);	ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);	ServiceBuild(AGAVE_API_PLAYLISTMGR, api_playlistmanagerGUID);	ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);	ServiceBuild(AGAVE_API_DECODE, decodeFileGUID);	ServiceBuild(AGAVE_API_GRACENOTE, gracenoteApiGUID);	ServiceBuild(AGAVE_API_METADATA, api_metadataGUID);	ServiceBuild(AGAVE_API_PLAYLISTS, api_playlistsGUID);	ServiceBuild(AGAVE_API_STATS, AnonymousStatsGUID);	ServiceBuild(WASABI_API_LNG, languageApiGUID);	ServiceBuild(WASABI_API_THREADPOOL, ThreadPoolGUID);	playlistGeneratorFactory.Register(plugin.service, &playlistGeneratorAPI);	if (WASABI_API_THREADPOOL)		plg_thread = WASABI_API_THREADPOOL->ReserveThread(api_threadpool::FLAG_REQUIRE_COM_STA);	if (!plg_thread)		return ML_INIT_FAILURE; // if we weren't able to get a thread from the threadpool, bail out	// no guarantee that AGAVE_API_MLDB will be available yet, so we'll start a watcher for it	mldbWatcher.WatchWith(plugin.service);	mldbWatcher.WatchFor(&AGAVE_API_MLDB, mldbApiGuid);	WASABI_API_SYSCB->syscb_registerCallback(&mldbWatcher);	// need to have this initialised before we try to do anything with localisation features	WASABI_API_START_LANG(plugin.hDllInstance,MlPlgLangGUID);	ML_IPC_MENUFUCKER_BUILD = (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"menufucker_build", IPC_REGISTER_WINAMP_IPCMESSAGE);	ML_IPC_MENUFUCKER_RESULT = (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"menufucker_result", IPC_REGISTER_WINAMP_IPCMESSAGE);	winampPlaylist = (HWND)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,IPC_GETWND_PE,IPC_GETWND);	static wchar_t szDescription[256];	StringCchPrintf(szDescription, ARRAYSIZE(szDescription),					WASABI_API_LNGSTRINGW(IDS_NULLSOFT_PLAYLIST_GENERATOR), PLUGIN_VER);	plugin.description = (char*)szDescription;	// Load variables from winamp.ini	scanMode = GetPrivateProfileInt(L"ml_plg", L"scanmode", 0, mediaLibrary.GetWinampIniW());	pluginEnabled = GetPrivateProfileInt(L"ml_plg", L"enable", 1, mediaLibrary.GetWinampIniW())!=0;	multipleArtists = GetPrivateProfileInt(L"ml_plg", L"multipleArtists", multipleArtists, mediaLibrary.GetWinampIniW());	multipleAlbums = GetPrivateProfileInt(L"ml_plg", L"multipleAlbums", multipleAlbums, mediaLibrary.GetWinampIniW());		plLengthType = GetPrivateProfileInt(L"ml_plg", L"plLengthType", plLengthType, mediaLibrary.GetWinampIniW());	plItems = GetPrivateProfileInt(L"ml_plg", L"plItems", plItems, mediaLibrary.GetWinampIniW());	plMinutes = GetPrivateProfileInt(L"ml_plg", L"plMinutes", plMinutes, mediaLibrary.GetWinampIniW());	plMegabytes = GetPrivateProfileInt(L"ml_plg", L"plMegabytes", plMegabytes, mediaLibrary.GetWinampIniW());	useSeed = GetPrivateProfileInt(L"ml_plg", L"useSeed", useSeed, mediaLibrary.GetWinampIniW());	useMLQuery = GetPrivateProfileInt(L"ml_plg", L"useMLQuery", useMLQuery, mediaLibrary.GetWinampIniW());			char temp[MAX_ML_QUERY_SIZE] = {0};	GetPrivateProfileStringA("ml_plg", "mlQuery", DEFAULT_ML_QUERY, temp, sizeof(temp), mediaLibrary.GetWinampIni());	MultiByteToWideCharSZ(CP_UTF8, 0, temp, -1, mlQuery, sizeof(mlQuery)/sizeof(mlQuery[0]));	//GetPrivateProfileStringA("ml_plg", "forcedRebuildVersion", "", temp, sizeof(temp), mediaLibrary.GetWinampIni());	//forcedRebuildVersion = (float)atof(temp);	forcedRebuildVersion = GetPrivateProfileIntA("ml_plg","forcedRebuildVersion", forcedRebuildVersion, mediaLibrary.GetWinampIni());		// Here we check if the person is upgrading from the old ml_plg, if that value is less than our current version then we need to force a rebuild	if (forcedRebuildVersion < FORCED_REBUILD_VERSION/*atof(PLUGIN_VER)*/)		// NOTE: Hard code this to a version if no breaking changes were made	{																			// Otherwise there will be a forced reset every time version is incremented		reset_db_flag = true;		//ResetDBOnThread(true);	}	forcedRebuildVersion = FORCED_REBUILD_VERSION;								//(float)atof(PLUGIN_VER);	if(scanMode == 1)									// If scanmode is set to rescan on winamp launch		WASABI_API_CREATEDIALOGPARAMW(IDD_NAG, plugin.hwndWinampParent, BGScanProcedure, 1); // 1 means silent!	return ML_INIT_SUCCESS;}void Quit(){	StopScan();	HANDLE wait_event = CreateEvent(NULL, FALSE, FALSE, 0);	WASABI_API_THREADPOOL->RunFunction(plg_thread, ShutdownScanner, (void *)wait_event, 0, api_threadpool::FLAG_REQUIRE_COM_STA);	WaitForSingleObject(wait_event, INFINITE);		mldbWatcher.StopWatching();	WASABI_API_THREADPOOL->ReleaseThread(plg_thread);	ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);	ServiceRelease(AGAVE_API_PLAYLISTMGR, api_playlistmanagerGUID);	ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);	ServiceRelease(AGAVE_API_DECODE, decodeFileGUID);	ServiceRelease(AGAVE_API_GRACENOTE, gracenoteApiGUID);	ServiceRelease(AGAVE_API_METADATA, api_metadataGUID);	ServiceRelease(AGAVE_API_MLDB, mldbApiGuid);	ServiceRelease(AGAVE_API_PLAYLISTS, api_playlistsGUID);	ServiceRelease(AGAVE_API_STATS, AnonymousStatsGUID);	ServiceRelease(WASABI_API_THREADPOOL, ThreadPoolGUID);	playlistGeneratorFactory.Deregister(plugin.service);	//WASABI_API_SYSCB->syscb_deregisterCallback(&IDscanner);}static void FixAmps(wchar_t *str, size_t len){	size_t realSize = 0;	size_t extra = 0;	wchar_t *itr = str;	while (itr && *itr)	{		if (*itr == L'&')			extra++;		itr++;		realSize++;	}	extra = min(len - (realSize + 1), extra);	while (extra)	{		str[extra+realSize] = str[realSize];		if (str[realSize] == L'&')		{			extra--;			str[extra+realSize] = L'&';		}		realSize--;	}}static void FixStrForMenu(wchar_t *str, size_t len){	FixAmps(str,len);}// Triggered once some seed tracks are selected and added by the userHWND SongsSelected(void){	// I know this function is a one-liner but it may not be the case forever	//WASABI_API_CREATEDIALOG(IDD_GENERATE, GetDesktopWindow(), GenerateProcedure);	return WASABI_API_CREATEDIALOGW(IDD_GENERATE, plugin.hwndLibraryParent, GenerateProcedure);}// Display the warning message that the current file is not in ML so it cannot be used as a seed trackvoid NotInMLWarning(const wchar_t *filename){	wchar_t message[MAX_PATH + 256] = {0};	StringCchPrintfW(message, MAX_PATH + 256, WASABI_API_LNGSTRINGW(IDS_CANT_USE_SEED), filename);	MessageBoxW(plugin.hwndLibraryParent, message, (LPWSTR)plugin.description, MB_OK| MB_ICONINFORMATION);}void MultipleInstancesWarning(void){	MessageBoxW(plugin.hwndLibraryParent, WASABI_API_LNGSTRINGW(IDS_THERE_CAN_BE_ONLY_ONE), (LPWSTR)plugin.description, MB_OK | MB_ICONINFORMATION);}// Add seed tracks from main media library viewint AddSeedTracks(menufucker_t *mf){	const int count = mf->extinf.mediaview.items->Size;	int position = ListView_GetNextItem(mf->extinf.mediaview.list, -1, LVNI_SELECTED);		// Start the search from -1 so that we dont ignore the 0th selection	while (position >= 0 && position < count)	{		wchar_t winamp_title[MAX_TITLE_SIZE] = {0};		itemRecordW *item = &mf->extinf.mediaview.items->Items[position];		if (item)		{			GetTitleFormattingML(item->filename, item, winamp_title, MAX_TITLE_SIZE);			seedPlaylist.AppendWithInfo(item->filename, winamp_title, item->length * 1000, item->filesize * 1024);			AGAVE_API_MLDB->FreeRecord(item);		}		position = ListView_GetNextItem(mf->extinf.mediaview.list, position, LVNI_SELECTED);	}	return true;}// Add seed tracks from a media library playlistint AddSeedTracksMlPlaylist(menufucker_t *mf){	int position = ListView_GetNextItem(mf->extinf.mlplaylist.list, -1, LVNI_SELECTED);		// Start the search from -1 so that we dont ignore the 0th selection	while (position >= 0)	{		wchar_t filename[MAX_PATH] = {0};		mf->extinf.mlplaylist.pl->GetItem(position, filename, MAX_PATH);		itemRecordW *item = AGAVE_API_MLDB->GetFile(filename);		if (item)		{			wchar_t winamp_title[MAX_TITLE_SIZE] = {0};			GetTitleFormattingML(item->filename, item, winamp_title, MAX_TITLE_SIZE);			seedPlaylist.AppendWithInfo(item->filename, winamp_title, item->length * 1000, item->filesize * 1024);			AGAVE_API_MLDB->FreeRecord(item);		}		position = ListView_GetNextItem(mf->extinf.mlplaylist.list, position, LVNI_SELECTED);	}	return true;}// Add tracks from the winamp playlistint AddSeedTracksPlaylist(menufucker_t *mf, int first_selection){	bool isSuccess = true;	int position = first_selection;	while (position >= 0)	{		wchar_t winamp_title[MAX_TITLE_SIZE] = {0};		fileinfoW inf={0};		inf.index = position;		SendMessage(winampPlaylist,WM_WA_IPC,IPC_PE_GETINDEXINFOW_INPROC,(LPARAM)&inf);		itemRecordW *item = AGAVE_API_MLDB->GetFile(inf.file);		if (item)		{			GetTitleFormattingML(inf.file, item, winamp_title, MAX_TITLE_SIZE);			seedPlaylist.AppendWithInfo(item->filename, winamp_title, item->length * 1000, item->filesize * 1024);			AGAVE_API_MLDB->FreeRecord(item);		}		else		{			NotInMLWarning(inf.file);			// Popup to warn that its not in the ML		}		position = (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, position, IPC_PLAYLIST_GET_NEXT_SELECTED);	}	if (seedPlaylist.GetNumItems() == 0)		isSuccess = false;	return isSuccess;}// Add a single seed track from the now playing song tickerint AddSeedTrack(const wchar_t *filename){	bool isSuccess = true;	if (filename)	{		wchar_t winamp_title[MAX_TITLE_SIZE] = {0};		itemRecordW *item = AGAVE_API_MLDB->GetFile(filename);		if (item)		{			GetTitleFormattingML(filename, item, winamp_title, MAX_TITLE_SIZE);			seedPlaylist.AppendWithInfo(filename, winamp_title, item->length * 1000, item->filesize * 1024);			AGAVE_API_MLDB->FreeRecord(item);		}		else		{			NotInMLWarning(filename);			// Popup to warn that its not in the ML		}	}	else	{		NotInMLWarning(filename);			// Popup to warn that its not in the ML	}	if (seedPlaylist.GetNumItems() == 0)		isSuccess = false;	return isSuccess;}void WriteSettingsToIni(HWND hwndDlg){	/*char buf[32] = {0};		StringCchPrintfA(buf, 32, "%d", plLengthType);	WritePrivateProfileStringA("ml_plg","plLengthType",buf,mediaLibrary.GetWinampIni());*/	WriteIntToIni("plLengthType", plLengthType);	WriteIntToIni("plItems", plItems);	WriteIntToIni("plMinutes", plMinutes);	WriteIntToIni("plMegabytes", plMegabytes);	WriteIntToIni("forcedRebuildVersion", forcedRebuildVersion);	//WriteFloatToIni("forcedRebuildVersion", forcedRebuildVersion);	WriteIntToIni("multipleArtists", multipleArtists);	WriteIntToIni("multipleAlbums", multipleAlbums);	WriteIntToIni("useSeed", useSeed);	WriteIntToIni("useMLQuery", useMLQuery);		/*multipleArtists = IsDlgButtonChecked(hwndDlg,IDC_CHECK_MULTIPLE_ARTISTS);	WritePrivateProfileStringA("ml_plg","multipleArtists",multipleArtists?"1":"0",mediaLibrary.GetWinampIni());	multipleAlbums = IsDlgButtonChecked(hwndDlg,IDC_CHECK_MULTIPLE_ALBUMS);	WritePrivateProfileStringA("ml_plg","multipleAlbums",multipleAlbums?"1":"0",mediaLibrary.GetWinampIni());	useSeed = IsDlgButtonChecked(hwndDlg,IDC_CHECK_USE_SEED);	WritePrivateProfileStringA("ml_plg","useSeed",useSeed?"1":"0",mediaLibrary.GetWinampIni());	useMLQuery = IsDlgButtonChecked(hwndDlg,IDC_CHECK_ML_QUERY);	WritePrivateProfileStringA("ml_plg","useMLQuery", useMLQuery ? "1" : "0",mediaLibrary.GetWinampIni());*/	//WritePrivateProfileStringW(L"ml_plg",L"mlQuery", mlQuery ,mediaLibrary.GetWinampIniW());	WritePrivateProfileStringA("ml_plg", "mlQuery", AutoChar(mlQuery, CP_UTF8), mediaLibrary.GetWinampIni());}static bool IsInternetAvailable(){	return !!SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_INETAVAILABLE);}INT_PTR MessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3){	static int mymenuid=0;	if(message_type == ML_IPC_MENUFUCKER_BUILD && pluginEnabled && IsInternetAvailable())	{		menufucker_t* mf = (menufucker_t*)param1;		wchar_t str[100] = {0}, str2[64] = {0};		MENUITEMINFOW mii =		{			sizeof(MENUITEMINFOW),			MIIM_TYPE | MIIM_ID,			MFT_STRING,			MFS_ENABLED,			(UINT)mf->nextidx,			0		};		mymenuid = mf->nextidx;		mf->nextidx++;		if(mf->type == MENU_MEDIAVIEW)		{			int n = ListView_GetSelectionMark(mf->extinf.mediaview.list);			if(n == -1)			{				mymenuid=0;				return 0;			}			itemRecordW * ice = &mf->extinf.mediaview.items->Items[n];			if(!ice->title || !ice->title[0])			{				mymenuid=0;				return 0;			}			int len = lstrlenW(ice->title);			if (len > 39)			{				StringCchPrintfW(str2, 40, L"%.36s...", ice->title);				StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), str2);			}			else			{				StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), ice->title);			}			FixStrForMenu(str,100);			mii.dwTypeData = str;			mii.cch = (UINT)wcslen(str);			if(!InsertMenuItem(mf->menu,0xdeadbeef,FALSE,&mii))			{				InsertMenuItem(mf->menu,40012,FALSE,&mii);				mii.wID = 0xdeadbeef;				mii.fType = MFT_SEPARATOR;				InsertMenuItem(mf->menu,40012,FALSE,&mii);			}		}		else if(mf->type == MENU_MLPLAYLIST)		{			int n = ListView_GetSelectionMark(mf->extinf.mlplaylist.list);			if(n == -1)			{				mymenuid=0;				return 0;			}			wchar_t filename[MAX_PATH] = {0}, title[75] = {0};			mf->extinf.mlplaylist.pl->GetItem(n,filename,MAX_PATH);			AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"title", title, 75);			if(!title[0])			{				mymenuid=0;				return 0;			}			int len = lstrlenW(title);			if (len > 39)			{				StringCchPrintfW(str2, 40, L"%.36s...", title);				StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), str2);			}			else			{				StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), title);			}			FixStrForMenu(str,100);			mii.dwTypeData = str;			mii.cch = (UINT)wcslen(str);			InsertMenuItem(mf->menu,3,TRUE,&mii);			mii.wID = 0xdeadc0de;			mii.fType = MFT_SEPARATOR;			InsertMenuItem(mf->menu,3,TRUE,&mii);		}		else if(mf->type == MENU_PLAYLIST)		{			int n = (int)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,-1,IPC_PLAYLIST_GET_NEXT_SELECTED);			if(n == -1) {				mymenuid=0;				return 0;			}			fileinfoW inf={0};			inf.index = n;			SendMessage(winampPlaylist,WM_WA_IPC,IPC_PE_GETINDEXINFOW_INPROC,(LPARAM)&inf);			wchar_t title[75] = {0};			AGAVE_API_METADATA->GetExtendedFileInfo(inf.file, L"title", title, 75);			if(!title[0])			{				mymenuid=0;				return 0;			}			int len = lstrlenW(title);			if (len > 39)			{				StringCchPrintfW(str2, 40, L"%.36s...", title);				StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), str2);			}			else			{				StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), title);			}			FixStrForMenu(str,100);			mii.dwTypeData = str;			mii.cch = (UINT)wcslen(str);			InsertMenuItem(mf->menu,40470/*40208*/,FALSE,&mii);			mii.wID = 0xdeadc0de;			mii.fType = MFT_SEPARATOR;			InsertMenuItem(mf->menu,40470/*40208*/,FALSE,&mii);		}		else if (mf->type == MENU_SONGTICKER)		{			wchar_t * file = (wchar_t*)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GET_PLAYING_FILENAME);			wchar_t title[75] = {0};			AGAVE_API_METADATA->GetExtendedFileInfo(file, L"title", title, 75);			if(!title[0])			{				mymenuid=0;				return 0;			}			int len = lstrlenW(title);			if (len > 39)			{				StringCchPrintfW(str2, 40, L"%.36s...", title);				StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), str2);			}			else			{				StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), title);			}			FixStrForMenu(str,100);			mii.dwTypeData = str;			mii.cch = (UINT)wcslen(str);			InsertMenuItem(mf->menu,0,TRUE,&mii);		}	}	else if(message_type == ML_IPC_MENUFUCKER_RESULT && mymenuid != 0 && pluginEnabled)	{		menufucker_t* mf = (menufucker_t*)param1;		DeleteMenu(mf->menu,mymenuid,MF_BYCOMMAND);		if(mf->type == MENU_PLAYLIST || mf->type == MENU_MLPLAYLIST) DeleteMenu(mf->menu,0xdeadc0de,MF_BYCOMMAND);		if(param2 == mymenuid && mymenuid != 0)		{			if(mf->type == MENU_MEDIAVIEW)			// Main Media Library View			{				int n = ListView_GetSelectionMark(mf->extinf.mediaview.list);				if(n == -1)				{					mymenuid=0;					return 0;				}				if (hwndDlgCurrent)					// Warn if trying to open two seperate playlist generators					MultipleInstancesWarning();				else				{					if (AddSeedTracks(mf))		// Make sure that we added the seed tracks successfully						SongsSelected();				}			}			else if(mf->type == MENU_MLPLAYLIST)	// Media library playlist view			{				// Check to see if anything is selected				int n = ListView_GetSelectionMark(mf->extinf.mlplaylist.list);				if(n == -1)				{					mymenuid=0;					return 0;				}				if (hwndDlgCurrent)					// Warn if trying to open two seperate playlist generators					MultipleInstancesWarning();				else				{					if (AddSeedTracksMlPlaylist(mf))	// Make sure that we added the seed tracks successfully						SongsSelected();				}			}			else if(mf->type == MENU_PLAYLIST)		// Main window playlist			{				// Check to see if anything is selected				int n = (int)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,-1,IPC_PLAYLIST_GET_NEXT_SELECTED);				if(n == -1)				{					mymenuid=0;					return 0;				}				if (hwndDlgCurrent)					// Warn if trying to open two seperate playlist generators					MultipleInstancesWarning();				else				{					if (AddSeedTracksPlaylist(mf, n))	// Make sure that we added the seed tracks successfully						SongsSelected();				}			}			else if(mf->type == MENU_SONGTICKER)	// Current playing track in the song ticker			{				wchar_t * file = (wchar_t*)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GET_PLAYING_FILENAME);				if (file)				{					if (hwndDlgCurrent)				// Warn if trying to open two seperate playlist generators						MultipleInstancesWarning();					else					{						if (AddSeedTrack(file))	// Make sure that we added the seed tracks successfully							SongsSelected();					}				}			}		}		mymenuid=0;	}	else switch (message_type)	{	case ML_MSG_CONFIG:	{		HWND parent = (HWND)param1;		WASABI_API_DIALOGBOXW(IDD_PREFS, parent, PrefsProcedure);		return TRUE;	}	break;	}	return 0;}extern "C" winampMediaLibraryPlugin plugin ={	MLHDR_VER,	"nullsoft(ml_plg.dll)", // name filled in later	Init,	Quit,	MessageProc,	0,	0,	0,};extern "C"{	__declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()	{		return &plugin;	}	__declspec( dllexport ) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) {		// prompt to remove our settings with default as no (just incase)		static wchar_t title[256];		StringCchPrintf(title, ARRAYSIZE(title),						WASABI_API_LNGSTRINGW(IDS_NULLSOFT_PLAYLIST_GENERATOR), PLUGIN_VER);		if(MessageBoxW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_DO_YOU_ALSO_WANT_TO_REMOVE_SETTINGS),					   title,MB_YESNO|MB_DEFBUTTON2) == IDYES)		{			WritePrivateProfileStringW(L"ml_plg",0,0,mediaLibrary.GetWinampIniW());		}		// allow an on-the-fly removal (since we've got to be with a compatible client build)		return ML_PLUGIN_UNINSTALL_NOW;	}};
 |