/** (c) Nullsoft, Inc.         C O N F I D E N T I A L
** Filename: 
** Project:
** Description:
** Author:
** Created:
**/

#include "Main.h"
#include "language.h"
#include <math.h>
#include "../nu/threadname.h"
#include "resource.h"
#include <tataki/export.h>
#include "api.h"
#include "../nu/threadpool/TimerHandle.hpp"
#include "../nu/AutoWide.h"

int img_w[2] = {400, 100}, img_h[2] = {189, 34};
int about_lastpage;
HWND about_hwnd;
#define M_PI 3.14159265358979323846

static BOOL CALLBACK tipsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
static BOOL CALLBACK whatsnewProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
static BOOL CALLBACK about1Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
static BOOL CALLBACK creditProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
static BOOL CALLBACK translationProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
static LRESULT WINAPI aboutProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);

static unsigned const char sq_table[]=
{0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, 64, 65, 
67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, 90, 91, 93, 94, 
96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, 110, 112, 113, 
114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 128,  
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 
142, 143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 
154, 155, 155, 156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 
165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175,  
176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 183, 184, 185,  
185, 186, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 193, 194, 
195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 203, 203, 
204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, 212, 
212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220,
221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228,
229, 229, 230, 230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 
236, 237, 237, 238, 238, 239, 240, 240, 241, 241, 242, 242, 243, 243, 
244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 
251, 251, 252, 252, 253, 253, 254, 254, 255};

static __inline unsigned long isqrt(unsigned long n)
{
	if (n >= 0x10000)
		if (n >= 0x1000000)
			if (n >= 0x10000000)
				if (n >= 0x40000000) return(sq_table[n >> 24] << 8);
				else return(sq_table[n >> 22] << 7);
			else
				if (n >= 0x4000000) return(sq_table[n >> 20] << 6);
				else return(sq_table[n >> 18] << 5);
		else
			if (n >= 0x100000)
				if (n >= 0x400000) return(sq_table[n >> 16] << 4);
				else return(sq_table[n >> 14] << 3);
			else
				if (n >= 0x40000) return(sq_table[n >> 12] << 2);
				else return(sq_table[n >> 10] << 1);
	else
		if (n >= 0x100)
			if (n >= 0x1000)
				if (n >= 0x4000) return(sq_table[n >> 8]);
				else return(sq_table[n >> 6] >> 1);
			else
				if (n >= 0x400) return(sq_table[n >> 4] >> 2);
				else return(sq_table[n >> 2] >> 3);
		else
			if (n >= 0x10)
				if (n >= 0x40) return(sq_table[n] >> 4);
				else return(sq_table[n << 2] << 5);
			else
				if (n >= 0x4) return(sq_table[n >> 4] << 6);
				else return(sq_table[n >> 6] << 7);
}

void about_dialog(void)
{
	if (about_hwnd)
	{
		SetForegroundWindow(about_hwnd);
		return;
	}

	about_hwnd=(HWND)LPCreateDialogW(IDD_NEWABOUT, hMainWindow, aboutProc);

	// show about window and restore last position as applicable
	POINT pt = {about_rect.left, about_rect.top};
	if (!windowOffScreen(about_hwnd, pt))
		SetWindowPos(about_hwnd, HWND_TOP, about_rect.left, about_rect.top, 0, 0, SWP_NOSIZE | SWP_NOSENDCHANGING | SWP_SHOWWINDOW);
	else
		ShowWindow(about_hwnd, SW_SHOW);
}

static LRESULT WINAPI aboutProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) 
{
	static HWND cur_wnd;
	switch (uMsg)
	{
	case WM_INITDIALOG:
		{
			int t=about_lastpage;
			about_lastpage=-1;
			// make sure not to show the localised page if not under a language pack
			if (t==4 && config_langpack[0]) SendMessageW(hwndDlg, WM_COMMAND, IDC_BUTTON2, 0);
			else if (t==3) SendMessageW(hwndDlg, WM_COMMAND, IDC_BUTTON6, 0);
			else if (t==2) SendMessageW(hwndDlg, WM_COMMAND, IDC_BUTTON5, 0);
			else if (t==1) SendMessageW(hwndDlg, WM_COMMAND, IDC_BUTTON4, 0);
			else
			{
				if (t==4) t = 0;
				SendMessageW(hwndDlg, WM_COMMAND, IDC_BUTTON3, 0);
			}
		}
		return FALSE;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDCANCEL:
		case IDOK:
			DestroyWindow(hwndDlg);
			return FALSE;
		case IDC_BUTTON3:
		case IDC_BUTTON4:
		case IDC_BUTTON5:
		case IDC_BUTTON6:
		case IDC_BUTTON2:
			{
				int id=-1;
				int t = 0;
				void *proc = 0;
				if (LOWORD(wParam)==IDC_BUTTON3) t=0;
				if (LOWORD(wParam)==IDC_BUTTON4) t=1;
				if (LOWORD(wParam)==IDC_BUTTON5) t=2;
				if (LOWORD(wParam)==IDC_BUTTON6) t=3;
				if (LOWORD(wParam)==IDC_BUTTON2) t=4;
				if (t == about_lastpage) return 0;
				about_lastpage=t;
				switch (t)
				{
					case 0: id=IDD_NEWABOUT1;	proc=about1Proc; break;
					case 1: id=IDD_NEWABOUT2;	proc=creditProc; break;
					case 2: id=IDD_NEWABOUT3;	proc=tipsProc; break;
					case 3: id=IDD_NEWABOUT4;	proc=whatsnewProc; break;
					case 4: id=IDD_ABOUT_TRANSLATION; proc=translationProc; break;
				}

				if (IsWindow(cur_wnd)) 
				{
					DestroyWindow(cur_wnd); 
					cur_wnd=0;
				}

				if (id != -1)
				{
					cur_wnd = CreateDialogW(language_pack_instance, MAKEINTRESOURCEW(id), hwndDlg, (DLGPROC)proc);
					// handles cases where there's no localised info page in a language pack / under en-us
					if(!IsWindow(cur_wnd)) cur_wnd = LPCreateDialogW(id, hwndDlg, (DLGPROC)proc);

					{
						RECT r;
						GetWindowRect(GetDlgItem(hwndDlg,IDC_RECT),&r);
						ScreenToClient(hwndDlg,(LPPOINT)&r);
						SetWindowPos(cur_wnd,0,r.left,r.top,0,0,SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
					}
					ShowWindow(cur_wnd,SW_SHOWNA);
					{
						RECT r,r2;
						GetWindowRect(GetDlgItem(hwndDlg,IDC_BUTTON3),&r);
						GetWindowRect(GetDlgItem(hwndDlg,IDC_BUTTON6),&r2);
						r.right=r2.right;
						ScreenToClient(hwndDlg,(LPPOINT)&r);
						ScreenToClient(hwndDlg,((LPPOINT)&r)+1);
						InvalidateRect(hwndDlg,&r,FALSE);
					}
				}
			}
			return FALSE;
		}
		break;

	case WM_DESTROY:
		GetWindowRect(hwndDlg, &about_rect);
		if (IsWindow(cur_wnd)) DestroyWindow(cur_wnd); 
		cur_wnd=0;
		about_hwnd=NULL;
		break;
	}

	if (uMsg == WM_DRAWITEM)
	{
		DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT *)lParam;
		if (di->CtlType == ODT_BUTTON) 
		{
			wchar_t wt[123] = {0};
			int sel=0;
			RECT r;
			GetDlgItemTextW(hwndDlg,wParam,wt,123);

			if (di->CtlID==IDC_BUTTON3 && about_lastpage==0) sel=1;
			else if (di->CtlID==IDC_BUTTON4 && about_lastpage==1) sel=1;
			else if (di->CtlID==IDC_BUTTON5 && about_lastpage==2) sel=1;
			else if (di->CtlID==IDC_BUTTON6 && about_lastpage==3) sel=1;
			else if (di->CtlID==IDC_BUTTON2 && about_lastpage==4) sel=1;

			if (di->CtlID != IDC_BUTTON6)
			{
				MoveToEx(di->hDC,di->rcItem.right-1,di->rcItem.top,NULL);
				LineTo(di->hDC,di->rcItem.right-1,di->rcItem.bottom);
			}

			// draw text
			if (!sel && (di->itemState & (ODS_SELECTED)))
				SetTextColor(di->hDC,RGB(0,40,255));
			r=di->rcItem;
			DrawTextW(di->hDC,wt,-1,&r,DT_VCENTER|DT_SINGLELINE|DT_CENTER);
			if (sel)
			{
				r=di->rcItem;
				r.left+=2;
				SetBkMode(di->hDC,TRANSPARENT);
				DrawTextW(di->hDC,wt,-1,&r,DT_VCENTER|DT_SINGLELINE|DT_CENTER);
			}
		}	
	}
	return 0;
}

/* Window Proc for the 'keyboard shorcuts' tab of about screen */
static BOOL CALLBACK tipsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
{
	static HGLOBAL hResource=0;
	static DWORD hResourceSize=0;
	switch (uMsg)
	{
	case WM_INITDIALOG:
		{
			char *b = NULL, *p = 0, *op = 0;
			DWORD size = 0;
			if (!hResource)
			{
				hResource = langManager->LoadResourceFromFileW(language_pack_instance, hMainInstance,TEXT("TEXT"), TEXT("TIPSTEXT"),&size);
				hResourceSize = size;
			}
			p = (char*)hResource;
			if (p && (op = strstr(p, "!!End")))  // if there's "!!End" in the resource, than copy everything before it
			{
				b = (char*)GlobalAlloc(GPTR, op-p+1);
				memcpy(b, p, op-p);
				b[op-p] = 0;
			} else {
				b = (char*)GlobalAlloc(GPTR, hResourceSize+1);
				memcpy(b, p, hResourceSize);
				b[hResourceSize] = 0;
			}

			SetDlgItemTextA(hwndDlg, IDC_TIPS, (b ? b : p)); // send it to the text control to display
			if (b) GlobalFree(b);
		}
		break;

	case WM_COMMAND:
		if(LOWORD(wParam) == IDCANCEL)
		{
			DestroyWindow(about_hwnd);
		}
		break;
	}

	if (FALSE != IsDirectMouseWheelMessage(uMsg))
	{
		HWND textWindow;
		RECT windowRect;
		textWindow = GetDlgItem(hwndDlg, IDC_TIPS);
		if (NULL != textWindow && 
			FALSE != GetClientRect(textWindow, &windowRect))
		{
			POINT pt;
			POINTSTOPOINT(pt, lParam);
			
			MapWindowPoints(HWND_DESKTOP, textWindow, &pt, 1);
			if (FALSE != PtInRect(&windowRect, pt))
			{			
				SendMessageW(textWindow, WM_MOUSEWHEEL, wParam, lParam);
				SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, TRUE);
			}
			return TRUE;
		}
	}
	return 0;
}

WNDPROC whatedproc = 0;
static LRESULT CALLBACK whatseditproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if(uMsg == WM_KEYDOWN)
	{
		if(wParam == 'F' && (GetAsyncKeyState(VK_CONTROL)&0x8000) && !(GetAsyncKeyState(VK_SHIFT)&0x8000))
		{
			if(IsWindowEnabled(GetDlgItem(GetParent(hwndDlg), IDC_ABOUT_SEARCH)))
			{
				SendMessageW(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(IDC_ABOUT_SEARCH,0), 0);
			}
		}
	}
	return CallWindowProcW(whatedproc,hwndDlg,uMsg,wParam,lParam);
}

#define WHATSNEW_BUFFER_SIZE 262144
static wchar_t* ver_buf;

void GetWhatsNewFromFile(FILE *fp)
{
	bool utf8=false, utf16=false;
	unsigned char BOM[3] = {0, 0, 0};
	if (fread(BOM, 3, 1, fp) == 1 && BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF)
		utf8 = true;
	else
	{
		fseek(fp, 0, SEEK_SET);
		if (fread(BOM, 2, 1, fp) == 1 && BOM[0] == 0xFF && BOM[1] == 0xFE)
			utf16=true;
		else
			fseek(fp, 0, SEEK_SET);
	}

	if (utf16)
	{
		wchar_t buffer[WHATSNEW_BUFFER_SIZE]={0}, *p=buffer;
		for (;;)
		{
			fgetws(p,1024,fp);
			if (feof(fp)) break;
			if (p[wcslen(p)-1]==L'\n')
				p[wcslen(p)-1]=0;
			StringCchCatW(p,WHATSNEW_BUFFER_SIZE,L"\r\n");
			p=p+wcslen(p);
			if (p-buffer > WHATSNEW_BUFFER_SIZE) break;
		}

		ver_buf = wcsdup(buffer);
	}
	else
	{
		char buffer[WHATSNEW_BUFFER_SIZE]={0},*p=buffer;
		for (;;)
		{
			fgets(p,1024,fp);
			if (feof(fp)) break;
			if (p[lstrlenA(p)-1]=='\n')
				p[lstrlenA(p)-1]=0;
			StringCchCatA(p,WHATSNEW_BUFFER_SIZE,"\r\n");
			p=p+lstrlenA(p);
			if (p-buffer > WHATSNEW_BUFFER_SIZE) break;
		}

		if (utf8)
		{
			ver_buf = AutoWideDup(buffer, CP_UTF8);
		}
		else
		{
			ver_buf = AutoWideDup(buffer);
		}
	}
}

static std::map<int,wchar_t*> versions;
/* Window Proc for the 'version history' tab of about screen */
static BOOL CALLBACK whatsnewProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static UINT fm_msg;
	static FINDREPLACEW find;
	static wchar_t fromstring[256];
	static HWND finder;

	if(uMsg == fm_msg)
	{
		LPFINDREPLACEW lpFindReplace = (LPFINDREPLACEW) lParam;
		if(lpFindReplace->Flags & FR_FINDNEXT)
		{
			int len = GetWindowTextLengthW(GetDlgItem(hwndDlg,IDC_TIPS))+1,
				flen = lstrlenW(lpFindReplace->lpstrFindWhat), start = 0, end = 0;
			wchar_t *buffer = (wchar_t*)calloc((len+1),sizeof(wchar_t)),
					*search = (wchar_t*)calloc((flen+2),sizeof(wchar_t)),
					*result = 0;

			lstrcpynW(search,lpFindReplace->lpstrFindWhat,flen+1);
			SendDlgItemMessageW(hwndDlg,IDC_TIPS,EM_GETSEL,(WPARAM)&start,(LPARAM)&end);

			GetDlgItemTextW(hwndDlg,IDC_TIPS,buffer,len);

			// handles the match case option
			if(!(lpFindReplace->Flags & FR_MATCHCASE))
			{
				CharUpperBuffW(buffer, len);
				CharUpperBuffW(search, flen+1);
			}

			if((result = wcsstr(buffer+end,search)))
			{
				SendDlgItemMessage(hwndDlg,IDC_TIPS,EM_SETSEL,result-buffer,(result-buffer)+flen);
				SendDlgItemMessage(hwndDlg,IDC_TIPS,EM_SCROLLCARET,0,0);
			}
			else
			{
				MessageBoxW(finder,getStringW(IDS_NO_MATCHES_FOUND,NULL,0),L"Winamp",MB_ICONINFORMATION);
			}

			free(buffer);
			free(search);
		}
	}

	switch (uMsg) {
		case WM_INITDIALOG:
			{
				wchar_t fn[MAX_PATH] = {0};
				FILE *fp = NULL;
				int last_add = 0;

				whatedproc = (WNDPROC)SetWindowLongPtrW(GetDlgItem(hwndDlg,IDC_TIPS),GWLP_WNDPROC,(LONG_PTR)whatseditproc);

				SendDlgItemMessage(hwndDlg, IDC_TIPS, EM_SETLIMITTEXT, 0, 0);

				// attempt to pull a localised whatsnew.txt
				// before reverting to the english default
				if (lang_directory[0])
				{
					lstrcpynW(fn,lang_directory,MAX_PATH);
				}
				else
				{
					GetModuleFileNameW(NULL,fn,MAX_PATH);
					PathRemoveFileSpecW(fn);
				}
				PathAppendW(fn, L"whatsnew.txt");

				// we don't set the buffer position so that it all works on the fallback code
				// and make sure there's a selection no matter what happens with other things
				SendDlgItemMessageW(hwndDlg,IDC_VER_COMBO,CB_ADDSTRING,0,(LPARAM)getStringW(IDS_SHOW_ALL_HISTORY,NULL,0));
				SendDlgItemMessageW(hwndDlg,IDC_VER_COMBO,CB_SETCURSEL,0,0);

				versions.clear();
				fp = _wfopen(fn,L"rb");

				// if there isn't a localised whatsnew.txt then revert to the old ways
				if(!fp)
				{
					GetModuleFileNameW(NULL,fn,MAX_PATH);
					PathRemoveFileSpecW(fn);
					PathAppendW(fn,L"whatsnew.txt");
					fp = _wfopen(fn,L"rb");
				}

				if (fp)
				{
					GetWhatsNewFromFile(fp);
					fclose(fp);
					wchar_t* p = ver_buf;
					while(p && *p)
					{
						// populate the version combobox
						if(!wcsncmp(p,L"Winamp 5.",9))
						{
							wchar_t* pp = p, ver[64] = {0};
							while(pp && *pp && *pp != L'\r') pp = CharNextW(pp);
							// need to make sure that we've actually found a valid part otherwise
							// just skip over things and don't fill in the combobox (attempted exploit)
							if(pp && *pp && *pp == L'\r'){
								pp = CharNextW(pp);
								// make sure that we keep within the buffer size as some people have
								// tried to make a buffer overflow vulnerability with this on XP SP3
								lstrcpynW(ver,p,(pp-p<64?pp-p:64));
								wchar_t* v = ver, *vn = 0, *vne = 0;
								while(v && *v && *v != L'\t') v = CharNextW(v);
								if(v && *v == L'\t'){
									vn = vne = CharNextW(v);
									*v = 0;
									if(vn && *vn == L'[') vn = CharNextW(vn);
									while(vne && *vne && *vne != L']') vne = CharNextW(vne);
									if(vne && *vne == L']') *vne = 0;
								}
								last_add = SendDlgItemMessageW(hwndDlg,IDC_VER_COMBO,CB_ADDSTRING,0,(LPARAM)ver);
								SendDlgItemMessageW(hwndDlg,IDC_VER_COMBO,CB_SETITEMDATA,last_add,(LPARAM)p);
								versions[last_add] = wcsdup(vn);
							}
						}
						p = CharNextW(p);
					}

					// reset the selection to the last view and force an update to that
					// would be better to do it straight away on load but this ensures all synchs up
					SendDlgItemMessage(hwndDlg,IDC_VER_COMBO,CB_SETCURSEL,_r_i("whtsnewlp",1),0);
					SendMessageW(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_VER_COMBO,CBN_SELCHANGE),(LPARAM)GetDlgItem(hwndDlg,IDC_VER_COMBO));
				}
				else
				{
					EnableWindow(GetDlgItem(hwndDlg,IDC_VER_COMBO),0);
					EnableWindow(GetDlgItem(hwndDlg,IDC_ABOUT_SEARCH),0);
					SetDlgItemTextW(hwndDlg,IDC_TIPS,getStringW(IDS_WHATSNEW_FAIL,ver_buf,WHATSNEW_BUFFER_SIZE));
				}
			}
			return FALSE;

		case WM_COMMAND:
			if(LOWORD(wParam) == IDC_VER_COMBO && HIWORD(wParam) == CBN_SELCHANGE)
			{
				int cur = SendMessageW((HWND)lParam,CB_GETCURSEL,0,0);
				wchar_t* position = (wchar_t*)SendMessageW((HWND)lParam,CB_GETITEMDATA,cur,0);
				if(cur != CB_ERR && position){
					wchar_t* p = position, *out_buf = (wchar_t*)calloc(WHATSNEW_BUFFER_SIZE,sizeof(wchar_t)), *out = out_buf;
					while(p && *p)
					{
						if(*p == L'\t')
						{
							if(*CharNextW(p) == L'[')
							{
								wchar_t* pb = p;
								while(pb && *pb && *pb != L']')
								{
									pb = CharNextW(pb);
								}
								if(pb && *pb == L']') p = CharNextW(pb);
							}
						}

						// look for 2 empty lines to indicate end of version block
						if(*p == L'\r')
						{
							wchar_t* n = CharNextW(p);
							if(n && *n == L'\n')
							{
								n = CharNextW(n);
								if(n && *n == L'\r')
								{
									break;
								}
							}
						}
						*out = *p;
						p = CharNextW(p);
						out = CharNextW(out);
					}
					*out = 0;

					wchar_t released[128] = {0};
					if(cur == 1 || cur > 1 && versions[cur] != 0)
						StringCchPrintfW(released,128,getStringW(IDS_RELEASED,NULL,0), (cur == 1 ? AutoWide(__DATE__) : versions[cur]));
					else
						released[0] = 0;
					SendMessageW(GetDlgItem(hwndDlg,IDC_RELEASED),WM_SETTEXT,0,(LPARAM)released);
					SendMessageW(GetDlgItem(hwndDlg,IDC_TIPS),WM_SETTEXT,0,(LPARAM)out_buf);
					free(out_buf);
				}
				else{
					SendMessageW(GetDlgItem(hwndDlg,IDC_RELEASED),WM_SETTEXT,0,(LPARAM)L"");
					SendMessageW(GetDlgItem(hwndDlg,IDC_TIPS),WM_SETTEXT,0,(LPARAM)ver_buf);
				}
			}
			else if(LOWORD(wParam) == IDC_ABOUT_SEARCH)
			{
				if(!IsWindow(finder))
				{
					if(!fm_msg) fm_msg = RegisterWindowMessageW(FINDMSGSTRINGW);

					find.lStructSize = sizeof(find);
					find.hwndOwner = hwndDlg;
					find.Flags = FR_DOWN|FR_HIDEWHOLEWORD|FR_HIDEUPDOWN;
					find.lpstrFindWhat = fromstring;
					find.wFindWhatLen = ARRAYSIZE(fromstring);
					finder = FindTextW(&find);
					ShowWindow(finder,SW_SHOW);
				}
				else
				{
					SetActiveWindow(finder);
				}
			}

			else if(LOWORD(wParam) == IDCANCEL)
			{
				DestroyWindow(about_hwnd);
			}
			break;

		case WM_DESTROY:
			free(ver_buf);
			ver_buf = 0;

			//for (int i=0; i!=versions.size(); i++)
			//	free(versions[i]);
			for (auto& version : versions)
			{
				if (version.second)
				{
					free(version.second);
				}
			}

			versions.clear();
			_w_i("whtsnewlp",SendDlgItemMessage(hwndDlg,IDC_VER_COMBO,CB_GETCURSEL,0,0));
			if(IsWindow(finder)) DestroyWindow(finder);
			break;
	} 

	if (FALSE != IsDirectMouseWheelMessage(uMsg))
	{
		HWND textWindow;
		RECT windowRect;
		textWindow = GetDlgItem(hwndDlg, IDC_TIPS);
		if (NULL != textWindow && 
			FALSE != GetClientRect(textWindow, &windowRect))
		{
			POINT pt;
			POINTSTOPOINT(pt, lParam);
			
			MapWindowPoints(HWND_DESKTOP, textWindow, &pt, 1);
			if (FALSE != PtInRect(&windowRect, pt))
			{			
				SendMessageW(textWindow, WM_MOUSEWHEEL, wParam, lParam);
				SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, TRUE);
			}
			return TRUE;
		}
	}
	return 0;
}

static volatile int aboutThread_kill, aboutThread_mode;
static HPALETTE m_haboutpal;

/* This does the 'zooming' effect on the image in the 'winamp' tab of the about screen */
class AboutContext
{
public:
	bool Init(HWND _hwndDlg);
	bool Tick();
	void Quit();

private:
	int mode;
	static int m_wt,m_wait;
	static int a;
	HDC m_hdc;
	HBITMAP m_hbm,m_oldhbm;

	int m_pitch,m_height;
	char *m_source,*m_dib;
	int m_wmul[200];
	RECT r;
	HBITMAP m_imgbm, m_imgoldbm;
	HDC m_imgdc;

	struct
	{
		BITMAPINFO bmi;
		RGBQUAD more_bmiColors[256];
		LPVOID data;
	} m_bitmap;
	int c,use_palette;
	HWND hwndDlg;
};

int AboutContext::m_wt = 0;
int AboutContext::m_wait = 0;
int AboutContext::a = 0;
bool AboutContext::Init(HWND _hwndDlg)
{
	hwndDlg = _hwndDlg;
	mode=(GetAsyncKeyState(VK_SHIFT)&0x8000);
	GetClientRect(GetDlgItem(hwndDlg,IDC_ABOUTIMG),&r);
	HDC hdc=GetWindowDC(hwndDlg);
	if (!hdc) 
		return false;
	use_palette = (GetDeviceCaps(hdc,RASTERCAPS)&RC_PALETTE)?1:0;
	m_hdc = CreateCompatibleDC(NULL);
	m_imgdc = CreateCompatibleDC(NULL);
	ReleaseDC(hwndDlg,hdc);

	if (!m_imgdc) 
		return false;

	m_imgbm= (HBITMAP)LoadImage(hMainInstance,MAKEINTRESOURCE((!aboutThread_mode ? IDB_SPLASH : IDB_PLATE)),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
	m_imgoldbm=(HBITMAP)SelectObject(m_imgdc,m_imgbm);
	m_source=(char *)GlobalAlloc(GPTR,img_w[aboutThread_mode]*img_h[aboutThread_mode]);

	if (m_imgbm && m_source) 
	{
		memset(&m_bitmap, 0, sizeof(m_bitmap));
		m_bitmap.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		m_bitmap.bmi.bmiHeader.biPlanes = 1;
		m_bitmap.bmi.bmiHeader.biBitCount = 8;
		m_bitmap.bmi.bmiHeader.biCompression = BI_RGB;
		m_bitmap.bmi.bmiHeader.biSizeImage = 0;
		m_bitmap.bmi.bmiHeader.biClrUsed = 128;
		m_bitmap.bmi.bmiHeader.biClrImportant = 128;
		m_bitmap.bmi.bmiHeader.biWidth = img_w[aboutThread_mode];
		m_bitmap.bmi.bmiHeader.biHeight = -img_h[aboutThread_mode];
		m_bitmap.bmi.bmiHeader.biSizeImage = img_w[aboutThread_mode]*img_h[aboutThread_mode];

		GetDIBits(m_imgdc,m_imgbm,0,img_h[aboutThread_mode],m_source,(BITMAPINFO *)&m_bitmap,DIB_RGB_COLORS);
		GetDIBColorTable(m_imgdc,0,256,m_bitmap.bmi.bmiColors);

		SelectObject(m_imgdc, m_imgoldbm);
		DeleteDC(m_imgdc);
		DeleteObject(m_imgbm);
	}
	else
	{
		DeleteDC(m_imgdc);
		if (m_imgbm) DeleteObject(m_imgbm);
		if (m_source) GlobalFree(m_source);
		return false;
	}

	if (use_palette) 
	{
		struct
		{
			LOGPALETTE logpal;
			PALETTEENTRY palentries[255];
		} palette;
		palette.logpal.palVersion = 0x300;
		palette.logpal.palNumEntries = 128;

		for (c = 0; c < 128; c ++) 
		{
			palette.logpal.palPalEntry[c].peRed = m_bitmap.bmi.bmiColors[c].rgbRed;
			palette.logpal.palPalEntry[c].peGreen = m_bitmap.bmi.bmiColors[c].rgbGreen;
			palette.logpal.palPalEntry[c].peBlue = m_bitmap.bmi.bmiColors[c].rgbBlue;
			palette.logpal.palPalEntry[c].peFlags = 0;
		}
		m_haboutpal = CreatePalette((LOGPALETTE *)&palette.logpal);
	}

	m_pitch=((r.right-r.left+3)&~3);
	m_height=r.bottom-r.top;
	m_bitmap.bmi.bmiHeader.biSizeImage = 0;
	m_bitmap.bmi.bmiHeader.biClrUsed = 0;
	m_bitmap.bmi.bmiHeader.biClrImportant = 0;
	m_bitmap.bmi.bmiHeader.biWidth = m_pitch;
	m_bitmap.bmi.bmiHeader.biHeight = -m_height;
	m_bitmap.bmi.bmiHeader.biSizeImage = m_pitch*m_height;
	m_hbm = CreateDIBSection(m_hdc,&m_bitmap.bmi,DIB_RGB_COLORS, &m_bitmap.data, NULL, 0);

	if (!m_hbm)
	{
		if (m_imgbm) DeleteObject(m_imgbm);
		if (m_source) GlobalFree(m_source);
		return 0;
	} 
	m_oldhbm = (HBITMAP) SelectObject(m_hdc, m_hbm);
	m_dib=(char*)m_bitmap.data;
	{
		for (int x = 0; x < img_h[aboutThread_mode]; x ++)
			m_wmul[x]=x*img_w[aboutThread_mode];
	}

	return true;
}


bool AboutContext::Tick()
{
	if (aboutThread_kill)
		return false;

	int tab[512] = {0};
	//		Sleep(50);
	{
		int offs;
		int x;
		double max_d=sqrt((double)((m_pitch*m_pitch+m_height*m_height))/4.0f);
		//int imax_d2=(int)(255.0/max_d*256.0);
		int imax_d=(int)max_d;
		double dpos;

		int thiswt=m_wt;
		if (thiswt > 63) thiswt = 126-thiswt;

		dpos=1.0+sin(thiswt*M_PI/32.0);

		if (dpos < 1.0) dpos=0.5+dpos/2.0;
		if (thiswt < 32) offs=thiswt*24;
		else offs=(64-thiswt)*24;       
		if (imax_d > 512) return false;

		for (x = 0; x < imax_d; x ++)
		{
			tab[x]=(int) (pow(sin(x/(max_d-1)),dpos)*1.7*256.0*max_d/(x+1)) - offs;
			if (tab[x]<0)tab[x]=-tab[x];
		}

		if (m_wt == 0)
		{
			if (m_wait++>=40)
			{
				m_wt++;
			}
		}
		else
		{
			m_wait=0;
			m_wt++;
		}
		m_wt&=127;
	}

	{
		int xsc=((!aboutThread_mode ? img_w[0]*180 : img_w[1]*360))/m_pitch;
		int ysc=((!aboutThread_mode ? img_h[0]*200 : img_h[1]*500))/m_height;
		int w2=m_pitch/2;
		int h2=m_height/2;
		char *out=m_dib;
		int y;
		if (m_wt)
		{
			a=!a;
			// weird interlace shit
			if (a && mode)
			{
				out+=m_pitch;
			}
			for (y = -h2+(mode?a:0); y < h2; y ++)
			{
				int x2=w2*w2+w2+y*y+256;
				int dx2=-2*w2;
				int yysc=y*ysc;
				int xxsc=-w2*xsc;
				int x=m_pitch;
				while (x--)
				{
					int qd=tab[isqrt(x2)];
					int ow,oh;
					x2+=dx2;
					dx2+=2;
					ow = img_w[aboutThread_mode]/2 + (qd*xxsc)/65536;
					xxsc+=xsc;
					oh = img_h[aboutThread_mode]/2 + (qd*yysc)/65536;

					if (ow < 0) ow=0;
					else if (ow >= img_w[aboutThread_mode]) ow=img_w[aboutThread_mode]-1;
					if (oh < 0) oh=0;
					else if (oh >= img_h[aboutThread_mode]) oh=img_h[aboutThread_mode]-1;

					*out++=m_source[ow+m_wmul[oh]];
				}

				// weird interlace shit
				if (mode) 
				{
					y++;
					out+=m_pitch;
				}
			}
		}
		else // copy, with optional rescale
		{
			int x;
			int skipw=0;
			int skiph=0;

			int dxp,dyp,xp,yp;

			skiph=(m_height-img_h[aboutThread_mode])/2;
			skipw=(m_pitch-img_w[aboutThread_mode])/2;

			if (skipw <0) skipw=0;
			if (skiph <0) skiph=0;

			dxp=(img_w[aboutThread_mode]<<16)/(m_pitch-skipw*2);
			dyp=(img_h[aboutThread_mode]<<16)/(m_height-skiph*2);
			yp=0;
			for (y = 0; y < m_height; y ++)
			{
				if (y < skiph || y >= m_height - skiph)
				{
					memset(out,0,m_pitch);
					out+=m_pitch;
				}
				else
				{
					char *in=m_source+(yp>>16)*img_w[aboutThread_mode];
					xp=0;
					for (x = 0; x < m_pitch; x ++)
					{
						if (x < skipw || x >=m_pitch-skipw)
						{
							*out++=0;
						}
						else
						{
							*out++=in[xp>>16];
							xp+=dxp;
						}
					}
					yp+=dyp;
				}
			}
		}
	}

	{
		HWND hwnd=GetDlgItem(hwndDlg,IDC_ABOUTIMG);
		if (hwnd)
		{
			HDC h=GetDC(hwnd);
			if (h)
			{
				if (m_haboutpal)
				{
					SelectPalette(h,m_haboutpal,FALSE);
					RealizePalette(h);
				}
				BitBlt(h,0,0,m_pitch,m_height,m_hdc,0,0,SRCCOPY);
				ReleaseDC(hwnd,h);
			}
		}
	}

	return true;
}

void AboutContext::Quit()
{
	if (m_hbm)
	{
		if (m_hdc) SelectObject(m_hdc, m_oldhbm);
		DeleteObject(m_hbm);
		m_hbm = NULL;
		m_oldhbm=NULL;
	}
	if (m_haboutpal) 
	{
		DeleteObject(m_haboutpal);
		m_haboutpal = NULL;
	}
	if (m_hdc) 
	{
		DeleteDC(m_hdc);
		m_hdc=NULL;
	}
	if (m_source)
	{
		GlobalFree((HGLOBAL)m_source);
		m_source=NULL;
	}
}

static int AboutTickThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id)
{
	AboutContext *context = (AboutContext *)user_data;
	TimerHandle t(handle);
	if (context->Tick())
	{
		t.Wait(50);
		return 0;
	}
	else
	{
		context->Quit();
		delete context;
		HANDLE event = (HANDLE)id;
		SetEvent(event);
		WASABI_API_THREADPOOL->RemoveHandle(0, handle);
		t.Close();
		return 0;
	}
}

static int AboutThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id)
{
	AboutContext *context = new AboutContext;
	if (context->Init((HWND)user_data))
	{
		TimerHandle t;
		WASABI_API_THREADPOOL->AddHandle(0, t, AboutTickThreadPoolFunc, context, id, 0);
		t.Wait(50);
	}
	else
	{
		delete context;
		HANDLE event = (HANDLE)id;
		SetEvent(event);
	}
	return 0;
}
/* Window Proc for the 'winamp' tab of the about screen */
static INT_PTR CALLBACK about1EggProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
{
	switch(uMsg)
	{
	case WM_INITDIALOG:
		{
#if 0
			static char ascii[]="\n\n  ______  ____  ___/\\  _/\\_ _____  dro  _____   <3  ______  /\\____   _______\n\
													\\_    \\/    \\/     \\_\\__/_\\    \\___  _\\  _ \\___  _\\     \\/      \\__\\  _   \\_\n\
													/    /     /     /     /          \\/   |/     \\/    /  /  /     /   |/    /\n\
  													/    /  /  /     /     /     /     /           /    /     /     /     ____/\n\
  													\\______/\\________\\____/\\____/\\_____\\____/\\_____\\____\\____/\\____/\\____/\n\
  													_________________________________________________________________  _________\n\
 													(_________________________________________________________________\\\\\\_WINAMP_)\n\n\n\
													supplied by deadbeef\n\n\
													cracked by rOn\n\n\
													32kb cool intro by lone";

			int i=0;
			FILE *fh;
			fh=fopen("c:\\blah.c","wt");
			for(i=0;ascii[i];i++)
			{
				ascii[i]^=0x65;
				fprintf(fh,"0x%x,",ascii[i]);
				if((i&31)==31) fprintf(fh,"\n");
			}
			fclose(fh);
#else
			static char ascii[]={
					0x6f,0x6f,0x6f,0x45,0x45,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x45,0x45,0x3a,0x3a,0x3a,0x3a,0x45,0x45,0x3a,0x3a,0x3a,0x4a,0x39,0x45,0x45,0x3a,0x4a,0x39,0x3a,0x45,
					0x3a,0x3a,0x3a,0x3a,0x3a,0x45,0x45,0x1,0x17,0xa,0x45,0x45,0x3a,0x3a,0x3a,0x3a,0x3a,0x45,0x45,0x45,0x59,0x56,0x45,0x45,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x45,0x45,
					0x4a,0x39,0x3a,0x3a,0x3a,0x3a,0x45,0x45,0x45,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x6f,0x45,0x45,0x39,0x3a,0x45,0x45,0x45,0x45,0x39,0x4a,0x45,0x45,0x45,0x45,0x39,
					0x4a,0x45,0x45,0x45,0x45,0x45,0x39,0x3a,0x39,0x3a,0x3a,0x4a,0x3a,0x39,0x45,0x45,0x45,0x45,0x39,0x3a,0x3a,0x3a,0x45,0x45,0x3a,0x39,0x45,0x45,0x3a,0x45,0x39,0x3a,
					0x3a,0x3a,0x45,0x45,0x3a,0x39,0x45,0x45,0x45,0x45,0x45,0x39,0x4a,0x45,0x45,0x45,0x45,0x45,0x45,0x39,0x3a,0x3a,0x39,0x45,0x45,0x3a,0x45,0x45,0x45,0x39,0x3a,0x6f,
					0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x45,
					0x45,0x45,0x45,0x45,0x45,0x39,0x4a,0x45,0x45,0x45,0x19,0x4a,0x45,0x45,0x45,0x45,0x45,0x39,0x4a,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x4a,0x45,0x45,0x4a,0x45,0x45,
					0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x19,0x4a,0x45,0x45,0x45,0x45,0x4a,0x6f,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x4a,0x45,0x45,0x4a,0x45,0x45,0x45,
					0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,
					0x4a,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x45,0x4a,0x45,0x45,0x45,0x45,0x45,0x3a,0x3a,0x3a,0x3a,0x4a,0x6f,0x45,0x45,0x39,
					0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x4a,0x39,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x39,0x3a,0x3a,0x3a,0x3a,0x4a,0x39,0x3a,0x3a,0x3a,0x3a,0x4a,0x39,0x3a,0x3a,0x3a,
					0x3a,0x3a,0x39,0x3a,0x3a,0x3a,0x3a,0x4a,0x39,0x3a,0x3a,0x3a,0x3a,0x3a,0x39,0x3a,0x3a,0x3a,0x3a,0x39,0x3a,0x3a,0x3a,0x3a,0x4a,0x39,0x3a,0x3a,0x3a,0x3a,0x4a,0x39,
					0x3a,0x3a,0x3a,0x3a,0x4a,0x6f,0x45,0x45,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,
					0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,
					0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x45,0x45,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x6f,0x45,0x4d,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,
					0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,
					0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x39,0x39,0x39,0x3a,0x32,0x2c,0x2b,0x24,
					0x28,0x35,0x3a,0x4c,0x6f,0x6f,0x6f,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,
					0x45,0x45,0x45,0x16,0x10,0x15,0x15,0x9,0xc,0x0,0x1,0x45,0x7,0x1c,0x45,0x1,0x0,0x4,0x1,0x7,0x0,0x0,0x3,0x6f,0x6f,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,
					0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x6,0x17,0x4,0x6,0xe,0x0,0x1,0x45,0x7,0x1c,0x45,
					0x17,0x2a,0xb,0x6f,0x6f,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,
					0x56,0x57,0xe,0x7,0x45,0x6,0xa,0xa,0x9,0x45,0xc,0xb,0x11,0x17,0xa,0x45,0x7,0x1c,0x45,0x9,0xa,0xb,0x0,
			};
			static int dexored=0;
			if(!dexored)
			{
				int i=0;
				for(i=0;i<sizeof(ascii);i++) ascii[i]^=0x65;
				dexored=1;
			}
#endif
			SetDlgItemTextA(hwndDlg,IDC_ASCII,ascii);
		}
		return 1;
	case WM_LBUTTONDBLCLK:
		{
			EndDialog(hwndDlg,0);
			ShowWindow(GetDlgItem(GetParent(hwndDlg),IDC_ABOUTIMG),SW_NORMAL);
		}
		break;
	}
	return 0;
}

/* Window proc for the about screen (i.e. this one handles the tab changes */
static BOOL CALLBACK about1Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
{
	static HANDLE hThread;
	static HWND egg_wnd;
	if (uMsg == WM_INITDIALOG)
	{
		if (!hThread)
		{
			aboutThread_kill=0;
			aboutThread_mode=0;
			hThread = CreateEvent(NULL, TRUE, FALSE, NULL);
			WASABI_API_THREADPOOL->RunFunction(0, AboutThreadPoolFunc, (void *)hwndDlg, (intptr_t)hThread, 0);
		}
		{
			wchar_t buf[2048] = {0}, buf2[2048] = {0}, buf3[256] = {0},
					*t1 = 0, *t2 = 0, *t3 = 0;
			GetDlgItemTextW(hwndDlg,IDC_ABOUTTEXT,buf,ARRAYSIZE(buf));
			StringCchPrintfW(buf2,2048,buf,(buf3[0] ? buf3 : (t1 = AutoWideDup(app_version_string))),
							 (t2 = AutoWideDup(APP_VERSION_PLATFORM)), (t3 = AutoWideDup(app_date)));
			SetDlgItemTextW(hwndDlg, IDC_ABOUTTEXT, buf2);
			link_startsubclass(hwndDlg, IDC_WINAMPLINK);
			if (t1) free(t1);
			if (t2) free(t2);
			if (t3) free(t3);
		}
		return TRUE;
	}
	if (uMsg == WM_QUERYNEWPALETTE)
	{
		if (m_haboutpal) 
		{
			HDC hdc = GetWindowDC(hwndDlg);
			SelectPalette(hdc,m_haboutpal,FALSE);
			RealizePalette(hdc);
			InvalidateRect(hwndDlg,NULL,FALSE);
			ReleaseDC(hwndDlg,hdc);
			return 1;
		}
		return 0;
	}
	if (uMsg == WM_PALETTECHANGED)
	{
		if (m_haboutpal)
		{
			HDC hdc = GetWindowDC(hwndDlg);
			SelectPalette(hdc,m_haboutpal,FALSE);
			RealizePalette(hdc);
			UpdateColors(hdc);
			ReleaseDC(hwndDlg,hdc);
			return 1;
		}
		return 0;
	}
	if (uMsg == WM_DESTROY)
	{
		if (hThread)
		{
			aboutThread_kill=1;
			WaitForSingleObject(hThread,INFINITE);
			CloseHandle(hThread);
			hThread=NULL;
		}
	}
	if (uMsg == WM_COMMAND)
	{
		switch (LOWORD(wParam))
		{
			case IDC_WINAMPLINK:
			{
				wchar_t homepage[1024] = {0};
				if (config_langpack[0])
				{
					getStringW(IDS_LOCALIZED_HOMEPAGE, homepage, 1024);
				}
				if (homepage[0])
					myOpenURL(hwndDlg, homepage);
				else
					myOpenURL(hwndDlg,L"http://www.winamp.com/");
			}
			return 0;
		}
	}
	if (uMsg == WM_LBUTTONDBLCLK)
	{
		if ((GetAsyncKeyState(VK_SHIFT)&0x8000) && !(GetAsyncKeyState(VK_CONTROL)&0x8000))
		{
			ShowWindow(GetDlgItem(hwndDlg,IDC_ABOUTIMG),SW_HIDE);
			if (!IsWindow(egg_wnd))
			{
				egg_wnd = LPCreateDialogW(IDD_NEWABOUT1EGG,hwndDlg,about1EggProc);
			}
			ShowWindow(egg_wnd, SW_SHOW);
		}
		else
		{
			if ((GetAsyncKeyState(VK_CONTROL)&0x8000) && !(GetAsyncKeyState(VK_SHIFT)&0x8000))
			{
				if (hThread)
				{
					aboutThread_kill=1;
					WaitForSingleObject(hThread,INFINITE);
					CloseHandle(hThread);
					hThread=NULL;
					aboutThread_kill=0;
					aboutThread_mode=!aboutThread_mode;
					hThread = CreateEvent(NULL, TRUE, FALSE, NULL);
					WASABI_API_THREADPOOL->RunFunction(0, AboutThreadPoolFunc, (void *)hwndDlg, (intptr_t)hThread, 0);
				}
			}
			ShowWindow(GetDlgItem(hwndDlg,IDC_ABOUTIMG),SW_SHOW);
		}
	}
	link_handledraw(hwndDlg,uMsg,wParam,lParam);
	return 0;
}

static volatile int creditThread_kill;
static HPALETTE m_hcreditspal;

/* this thread handles the 3d credits rendering (this one does some busywork and housecleaning, actual rendering done in creditsrend.c) */
class CreditsContext
{
public:
	bool Init(HWND _hwndDlg);
	bool Tick(int &wait_time);
	void Quit();

private:	
	HDC m_hdc;
	HBITMAP m_hbm,m_oldhbm;
	int m_pitch,m_height;
	char *m_dib;
char pal[768];
	struct
	{
		BITMAPINFO bmi;
		RGBQUAD more_bmiColors[255];
	} m_bitmap;
	int c,use_palette;
	HWND hwndDlg;
};

bool CreditsContext::Init(HWND _hwndDlg)
{
	hwndDlg = _hwndDlg;
	Tataki::Init(WASABI_API_SVC);
	m_hcreditspal = 0;
	m_hdc = 0;
	m_hbm = 0;

	RECT r={0,};
	memset(&m_bitmap, 0, sizeof(m_bitmap));

	GetWindowRect(hwndDlg,&r);
	r.right=r.right-r.left;
	r.bottom=r.bottom-r.top;
		HDC hdc=GetWindowDC(hwndDlg);
		if (!hdc) 
			return false;
		use_palette = (GetDeviceCaps(hdc,RASTERCAPS)&RC_PALETTE)?1:0;
		m_hdc = CreateCompatibleDC(NULL);
		ReleaseDC(hwndDlg,hdc);

		m_pitch=((r.right+3)&~3);
	m_height=r.bottom;
	if (m_pitch < 4) m_pitch=4;
	if (m_height < 4) m_height=4;
	//  if (m_pitch > GetSystemMetrics(SM_CXSCREEN)) m_pitch=GetSystemMetrics(SM_CXSCREEN);
	//  if (m_height > GetSystemMetrics(SM_CYSCREEN)) m_height=GetSystemMetrics(SM_CYSCREEN);
	render_init(m_pitch,m_height,pal);
	
	{
		struct 
		{
			LOGPALETTE logpal;
			PALETTEENTRY palentries[255];
		} palette;
		palette.logpal.palVersion = 0x300;
		palette.logpal.palNumEntries = 220;
		for (c = 0; c < 256; c ++) 
		{
			palette.logpal.palPalEntry[c].peRed = pal[c*3];
			palette.logpal.palPalEntry[c].peGreen = pal[c*3+1];
			palette.logpal.palPalEntry[c].peBlue = pal[c*3+2];
			palette.logpal.palPalEntry[c].peFlags = PC_NOCOLLAPSE;
			m_bitmap.bmi.bmiColors[c].rgbRed=pal[c*3];
			m_bitmap.bmi.bmiColors[c].rgbGreen=pal[c*3+1];
			m_bitmap.bmi.bmiColors[c].rgbBlue=pal[c*3+2];
			m_bitmap.bmi.bmiColors[c].rgbReserved=0;
		}
		if (use_palette) 
			m_hcreditspal = CreatePalette((LOGPALETTE *)&palette.logpal);
	}
		

	m_bitmap.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	m_bitmap.bmi.bmiHeader.biPlanes = 1;
	m_bitmap.bmi.bmiHeader.biBitCount = 8;
	m_bitmap.bmi.bmiHeader.biCompression = BI_RGB;
	m_bitmap.bmi.bmiHeader.biSizeImage = 0;
	m_bitmap.bmi.bmiHeader.biClrImportant = 0;
	m_bitmap.bmi.bmiHeader.biClrUsed = 256;
	m_bitmap.bmi.bmiHeader.biWidth = m_pitch;
	m_bitmap.bmi.bmiHeader.biHeight = -m_height;
	m_bitmap.bmi.bmiHeader.biSizeImage = m_pitch*m_height;
	m_hbm = CreateDIBSection(m_hdc,&m_bitmap.bmi,DIB_RGB_COLORS, (void**)&m_dib, NULL, 0);
	m_oldhbm = (HBITMAP) SelectObject(m_hdc, m_hbm);
	return (m_hbm && m_dib);
}

void CreditsContext::Quit()
{
		if (m_hbm)
	{
		if (m_hdc) SelectObject(m_hdc, m_oldhbm);
		DeleteObject(m_hbm);
		m_hbm = NULL;
		m_oldhbm=NULL;
	}
	if (m_hcreditspal) 
	{
		DeleteObject(m_hcreditspal);
		m_hcreditspal = NULL;
	}
	if (m_hdc) 
	{
		DeleteDC(m_hdc);
		m_hdc=NULL;
	}
	render_quit();
	Tataki::Quit();
}

bool CreditsContext::Tick(int &wait_time)
{
	if (creditThread_kill)
		return false;

		unsigned int thist=GetTickCount();
		render_render((unsigned char*)m_dib, m_hdc);
		{
			HDC h=GetDC(hwndDlg);
			if (h)
			{
				if (m_hcreditspal)
				{
					SelectPalette(h,m_hcreditspal,FALSE);
					RealizePalette(h);
				}
				BitBlt(h,0,0,m_pitch,m_height,m_hdc,0,0,SRCCOPY);
				ReleaseDC(hwndDlg,h);
			}
		}
		thist=GetTickCount()-thist;
		if (thist > 28) thist=28;
		wait_time = 30-thist;
		return true;
}

static int CreditsTickThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id)
{
	CreditsContext *context = (CreditsContext *)user_data;
	TimerHandle t(handle);
	int wait_time=30;
	if (context->Tick(wait_time))
	{
		t.Wait(wait_time);
		return 0;
	}
	else
	{
		context->Quit();
		delete context;
		HANDLE event = (HANDLE)id;
		SetEvent(event);
		WASABI_API_THREADPOOL->RemoveHandle(0, handle);
		t.Close();
		return 0;
	}
}

static int CreditThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id)
{
	CreditsContext *context = new CreditsContext;
	if (context->Init((HWND)user_data))
	{
		TimerHandle t;
		WASABI_API_THREADPOOL->AddHandle(0, t, CreditsTickThreadPoolFunc, context, id, api_threadpool::FLAG_LONG_EXECUTION);
		t.Wait(30);
	}
	else
	{
		delete context;
		HANDLE event = (HANDLE)id;
		SetEvent(event);
	}
	return 0;
}

/* Window Proc for the 'credits' tab of the about screen */
static BOOL CALLBACK creditProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
{
	static int g_fullscreen=0;
	static RECT rsave;
	static HWND hOldParent;
	static int oldstyle;
	static HANDLE hThread;
	if (uMsg == WM_LBUTTONDBLCLK)
	{
		render_togglecredits();
		return 0;
	}
	if (uMsg == WM_KEYDOWN && g_fullscreen)
	{
		if (wParam == VK_ESCAPE)
		{
			PostMessageW(hwndDlg,WM_LBUTTONDOWN,0,0);
		}
		else PostMessageW(hMainWindow,uMsg,wParam,lParam);
	}
	if ((uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDBLCLK) && g_fullscreen)
	{
		if (hThread)
		{
			creditThread_kill=1;
			WaitForSingleObject(hThread,INFINITE);
			CloseHandle(hThread);
			hThread = 0;
			if (g_fullscreen)
			{
				RECT r=rsave;
				ScreenToClient(hOldParent,(LPPOINT)&r);
				ScreenToClient(hOldParent,(LPPOINT)&r+1);
				SetWindowLong(hwndDlg,GWL_STYLE,oldstyle);
				SetParent(hwndDlg,hOldParent);
				SetWindowPos(hwndDlg,0,r.left,r.top,r.right-r.left,r.bottom-r.top,SWP_NOZORDER);
			}
		}
		g_fullscreen=0;
		creditThread_kill=0;
		hThread = CreateEvent(NULL, TRUE, FALSE, NULL);
		WASABI_API_THREADPOOL->RunFunction(0, CreditThreadPoolFunc, (void *)hwndDlg, (intptr_t)hThread, api_threadpool::FLAG_LONG_EXECUTION);
	}
	else if (uMsg == WM_RBUTTONDBLCLK && !g_fullscreen)
	{
		RECT r;
		if (hThread)
		{
			creditThread_kill=1;
			WaitForSingleObject(hThread,INFINITE);
			CloseHandle(hThread);
			hThread = 0;
		}
		g_fullscreen=1;
		GetWindowRect(hwndDlg,&rsave);
		oldstyle=GetWindowLongW(hwndDlg,GWL_STYLE);
		hOldParent=SetParent(hwndDlg,NULL);

		SetWindowLong(hwndDlg,GWL_STYLE,WS_POPUP|WS_VISIBLE);

		getViewport(&r,hwndDlg,1,NULL);

		SetWindowPos(hwndDlg, HWND_TOPMOST, r.left, r.top, r.right, r.bottom, SWP_DRAWFRAME);
		creditThread_kill=0;
		hThread = CreateEvent(NULL, TRUE, FALSE, NULL);
		WASABI_API_THREADPOOL->RunFunction(0, CreditThreadPoolFunc, (void *)hwndDlg, (intptr_t)hThread, api_threadpool::FLAG_LONG_EXECUTION);
		// go fullscreen
	}
	if (uMsg == WM_INITDIALOG)
	{
		if (!hThread)
		{
			g_fullscreen=0;
			creditThread_kill=0;
			hThread = CreateEvent(NULL, TRUE, FALSE, NULL);
			WASABI_API_THREADPOOL->RunFunction(0, CreditThreadPoolFunc, (void *)hwndDlg, (intptr_t)hThread, api_threadpool::FLAG_LONG_EXECUTION);
		}
		return TRUE;
	}
	if (uMsg == WM_QUERYNEWPALETTE)
	{
		if (m_hcreditspal) 
		{
			HDC hdc = GetWindowDC(hwndDlg);
			SelectPalette(hdc,m_hcreditspal,FALSE);
			RealizePalette(hdc);
			InvalidateRect(hwndDlg,NULL,FALSE);
			ReleaseDC(hwndDlg,hdc);
			return 1;
		}
		return 0;
	}
	if (uMsg == WM_PALETTECHANGED)
	{
		if (m_hcreditspal)
		{
			HDC hdc = GetWindowDC(hwndDlg);
			SelectPalette(hdc,m_hcreditspal,FALSE);
			RealizePalette(hdc);
			UpdateColors(hdc);
			ReleaseDC(hwndDlg,hdc);
			return 1;
		}
		return 0;
	}
	if (uMsg == WM_DESTROY)
	{
		if (hThread)
		{
			creditThread_kill=1;
			WaitForSingleObject(hThread,INFINITE);
			CloseHandle(hThread);
			hThread=NULL;
		}
	}
	return 0;
}

static BOOL CALLBACK translationProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_INITDIALOG:
		{
			// only attempt set it to show a hand cursor if it's an ownerdraw button
			if(GetWindowLongPtrW(GetDlgItem(hwndDlg, IDC_AUTHOR_HOMEPAGE), GWL_STYLE) & BS_OWNERDRAW)
			{
				link_startsubclass(hwndDlg, IDC_AUTHOR_HOMEPAGE);
			}
			if(GetWindowLongPtrW(GetDlgItem(hwndDlg, IDC_AUTHOR_HOMEPAGE2), GWL_STYLE) & BS_OWNERDRAW)
			{
				link_startsubclass(hwndDlg, IDC_AUTHOR_HOMEPAGE2);
			}
		}
		break;

	case WM_COMMAND:
		if(LOWORD(wParam) == IDCANCEL)
		{
			DestroyWindow(about_hwnd);
		}
		else if (LOWORD(wParam) == IDC_AUTHOR_HOMEPAGE)
		{
			wchar_t homepage[1024] = {0};
			getStringW(IDS_AUTHOR_HOMEPAGE, homepage, 1024);
			myOpenURL(hwndDlg, homepage);
		}
		else if (LOWORD(wParam) == IDC_AUTHOR_HOMEPAGE2)
		{
			wchar_t homepage[1024] = {0};
			getStringW(IDS_AUTHOR_HOMEPAGE2, homepage, 1024);
			myOpenURL(hwndDlg, homepage);
		}
		break;
	} 
	link_handledraw(hwndDlg,uMsg,wParam,lParam);
	return 0;
}