/*
	N-Rage`s Dinput8 Plugin
    (C) 2002, 2006  Norbert Wladyka

	Author`s Email: norbert.wladyka@chello.at
	Website: http://go.to/nrage


    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "commonIncludes.h"
#include <windows.h>
#include <Commctrl.h>
#include <stdio.h>
#include "NRagePluginV2.h"
#include "DirectInput.h"
#include "XInputController.h"
#include "FileAccess.h"
#include "PakIO.h"
#include "Interface.h"
#include "International.h"
#include "Version.h"

// Prototypes //
BOOL CALLBACK ControllerTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK ControlsTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK XControlsTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );	// Xinput Control main proc. --tecnicors
BOOL CALLBACK DevicesTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK ModifierTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK ControllerPakTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK PakProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK MemPakProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK RumblePakProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK TransferPakProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK ShortcutsTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
BOOL CALLBACK FoldersDialogProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
LRESULT CALLBACK BlockerProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
BOOL InitOverlay();
HWND MakeOverlay();

// BOOL CALLBACK EnumGetKeyDesc( LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef ); // nope, sorry. --rabid
bool GetButtonID( LPDWORD ButtonID, const BYTE bIndex, const BYTE bButtonSet );
bool GetButtonText( const BUTTON& btnButton, LPTSTR Buffer);
DWORD ScanDevices( LPDWORD lpdwCounter, LPBUTTON pButton );

void DeleteControllerSettings( int indexController );
void GetCurrentConfiguration();
void UpdateControllerStructures();

// global Variables //
INTERFACEVALUES *g_ivConfig = NULL;					// this structure holds all GUI-Data; it points to a valid struct only if the config window is open
LPDIRECTINPUTDEVICE8 g_pConfigDevice = NULL;		// one device handle for current FF device; between messages so it needs to be persistent
LPDIRECTINPUTEFFECT  g_pConfigEffect = NULL;		// FF-effect handle
HWND g_hMainDialog = NULL;							// handle of base-dialog

// Main dialog control handler
BOOL CALLBACK MainDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	static HWND hTabControl;
	HWND hDlgItem;
	long i,j;

	switch(uMsg)
	{
	case WM_INITDIALOG:
		{
			DebugWriteA("Config interface: Open\n");
			g_ivConfig = (INTERFACEVALUES*)P_malloc( sizeof(INTERFACEVALUES) ); // TODO: CHECK THIS MALLOC!
			ZeroMemory( g_ivConfig, sizeof(INTERFACEVALUES) );

			CopyMemory( &g_ivConfig->Shortcuts, &g_scShortcuts ,sizeof(SHORTCUTS) );
			for( i = 0; i < 4; ++i )
				DeleteControllerSettings( i );

			EnterCriticalSection( &g_critical );	// block because the InitiateControllers code may still be running.
			if( !g_strEmuInfo.fInitialisedPlugin )
			{
				LoadConfigFromINI();
			}

			GetCurrentConfiguration();

			LeaveCriticalSection( &g_critical );


			g_hMainDialog = hDlg;

			// Display Version
			{
				TCHAR tszBuffer[DEFAULT_BUFFER], tszMsg[DEFAULT_BUFFER / 2];

				LoadString( g_hResourceDLL, IDS_VERSTRING, tszBuffer, DEFAULT_BUFFER / 2);
				_stprintf(tszMsg, tszBuffer, VER_FILE_VERSION_STR);
				SetDlgItemText( hDlg, IDC_VERSIONSTRING, tszMsg );
			}


			// Tab - Control //
			hTabControl = NULL;
			hDlgItem = GetDlgItem( hDlg, IDC_UPPERTAB );

			TCITEM tcItem;
			tcItem.mask = TCIF_TEXT;

			TCHAR tszText[DEFAULT_BUFFER], tszBuff[DEFAULT_BUFFER];
			tcItem.pszText = tszText;

			LoadString( g_hResourceDLL, IDS_TAB_CONTROLLER, tszBuff, DEFAULT_BUFFER );

			for (int i = 0; i < 4; i++ )
			{
				wsprintf( tszText, tszBuff, i + 1 );
				TabCtrl_InsertItem( hDlgItem, i, &tcItem );
			}

			LoadString( g_hResourceDLL, IDS_TAB_SHORTCUTS, tszText, DEFAULT_BUFFER );
			TabCtrl_InsertItem( hDlgItem, TAB_SHORTCUTS, &tcItem );
			// Tab - Control  End //

			g_ivConfig->ChosenTab = TAB_CONTROLLER1;
			TabCtrl_SetCurSel( GetDlgItem( hDlg, IDC_UPPERTAB), g_ivConfig->ChosenTab );
			NMHDR nmInit;
			nmInit.hwndFrom = hDlgItem;
			nmInit.idFrom = IDC_UPPERTAB;
			nmInit.code = TCN_SELCHANGE;
			MainDlgProc( hDlg, WM_NOTIFY, IDC_UPPERTAB, (LPARAM)&nmInit );

			// we can't click "Use" without saving when not emulating, because the emu resets controls before loading a ROM
			if( !g_bRunning )
				EnableWindow( GetDlgItem( hDlg, IDUSE ), FALSE );

#ifndef _UNICODE
			EnableWindow( GetDlgItem( hDlg, IDC_LANGUAGE), FALSE );
#endif // #ifdef _UNICODE
			InitOverlay();
		}

		// call routine to show content( recursive call )
		MainDlgProc( hDlg, WM_USER_UPDATE, 0, 0 );
		return TRUE; // the system sets focus to one control element

	case WM_NOTIFY:
		hDlgItem = ((LPNMHDR)lParam)->hwndFrom;
		i = ((LPNMHDR)lParam)->code;
		j = ((LPNMHDR)lParam)->idFrom;

		switch( j )
		{
		case IDC_UPPERTAB:
			if( i == TCN_SELCHANGE )
			{
				i = g_ivConfig->ChosenTab;
				g_ivConfig->ChosenTab = TabCtrl_GetCurSel( hDlgItem );

				if( hTabControl )
				{
					if(	(( i == TAB_CONTROLLER1 ) || ( i == TAB_CONTROLLER2 ) || ( i == TAB_CONTROLLER3 ) || ( i == TAB_CONTROLLER4 )) &&
						(( g_ivConfig->ChosenTab == TAB_CONTROLLER1 ) || ( g_ivConfig->ChosenTab == TAB_CONTROLLER2 ) || ( g_ivConfig->ChosenTab == TAB_CONTROLLER3 ) || ( g_ivConfig->ChosenTab == TAB_CONTROLLER4 )))
					{
						MainDlgProc( hDlg, WM_USER_UPDATE, 0, 0 );
						return TRUE;
					}
					else
						DestroyWindow( hTabControl );
				}

				switch( g_ivConfig->ChosenTab )
				{
				case TAB_CONTROLLER1:
				case TAB_CONTROLLER2:
				case TAB_CONTROLLER3:
				case TAB_CONTROLLER4:
					hTabControl = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_CONTROLLER ), hDlg, ControllerTabProc );
					break;
				case TAB_SHORTCUTS:
					hTabControl = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_SHORTCUT ), hDlg, ShortcutsTabProc );
					break;
				default:
					hTabControl = NULL;
					return TRUE;
				}

				RECT rectWindow,
					rectTab, rectMain; // need to know the position of the calling tab window relative to its parent

				GetWindowRect( hDlg, &rectMain );
				GetWindowRect( hDlgItem, &rectTab );

				GetClientRect( hDlgItem, &rectWindow );
				TabCtrl_AdjustRect( hDlgItem, FALSE, &rectWindow );

				// second, third param: plus Tabwindow's position relative to main
				MoveWindow( hTabControl, (rectTab.left - rectMain.left), (rectTab.top - rectMain.top), rectWindow.right - rectWindow.left, rectWindow.bottom - rectWindow.top, FALSE );
				ShowWindow( hTabControl, SW_SHOW );
			}
			return TRUE;
		default:
			return FALSE;
		}

	case WM_COMMAND:
		hDlgItem = GetDlgItem( hDlg, LOWORD(wParam) );

		switch( LOWORD(wParam) )
		{
		case IDSAVE:
			if( hTabControl )
			{
				DestroyWindow( hTabControl );
				hTabControl = NULL;
			}
			DebugWriteA("Config interface: Save\n");
			StoreConfigToINI();
			// NO BREAK-- this is intentional
		case IDUSE:
			if( hTabControl )
			{
				DestroyWindow( hTabControl );
				hTabControl = NULL;
			}
			DebugWriteA("Config interface: Use\n");
			CopyMemory( &g_scShortcuts ,&g_ivConfig->Shortcuts, sizeof(SHORTCUTS) );

			UpdateControllerStructures();

			EndDialog( hDlg, TRUE );
			return TRUE;
		case IDCANCEL:
			if( hTabControl )
			{
				DestroyWindow( hTabControl );
				hTabControl = NULL;
			}
			EndDialog( hDlg, FALSE );
			return TRUE;

		case IDC_LANGUAGE:
			if( HIWORD(wParam) == CBN_SELCHANGE )
			{
				i = SendMessage( hDlgItem, CB_GETCURSEL, 0, 0 );
				g_ivConfig->Language = (LANGID) SendMessage( hDlgItem, CB_GETITEMDATA, i, 0 );
			}
			return TRUE;

		case IDC_STOREINDB:
		case IDC_UPDATEDB:
		case IDC_ERASEFROMDB:
		case IDC_SAVEPROFILE:
		case IDC_LOADPROFILE:
			// we don't have handlers for these, so fall through to FALSE
		default:
			return FALSE;
		}

	case WM_CLOSE:
		if( hTabControl )
		{
			DestroyWindow( hTabControl );
			hTabControl = NULL;
		}
		EndDialog( hDlg, FALSE );
		return TRUE;

	case WM_DESTROY:
		if( hTabControl )
		{
			DestroyWindow( hTabControl );
			hTabControl = NULL;
		}
		EnterCriticalSection(&g_critical);
		ReleaseEffect( g_pConfigEffect );
		ReleaseDevice( g_pConfigDevice );

		for( i = 0; i < 4; ++i )
		{
			freePakData( &g_ivConfig->Controllers[i] );
			freeModifiers( &g_ivConfig->Controllers[i] );
		}
		P_free( g_ivConfig );
		g_ivConfig = NULL;
		DebugWriteA("Config interface: Closed\n");
		LeaveCriticalSection(&g_critical);
		return TRUE;

	case WM_USER_UPDATE:
#ifdef _UNICODE
		// filling DropDownlist with languages
		hDlgItem = GetDlgItem( hDlg, IDC_LANGUAGE );
		SendMessage (hDlgItem, CB_RESETCONTENT, 0, 0);
		
		{
			TCHAR szBuffer[MAX_PATH];
			TCHAR * pSlash;
			WIN32_FIND_DATA fData;
			long lLangFound = 0; // the index matching our current language

			VerLanguageName(0x0009, szBuffer, DEFAULT_BUFFER); // English (default resource)
			j = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)szBuffer );
			SendMessage( hDlgItem, CB_SETITEMDATA, j, 0 );

			GetModuleFileName( NULL, szBuffer, MAX_PATH );
			pSlash = _tcsrchr( szBuffer, '\\' );
			_tcscpy(pSlash + 1, _T("NRage-Language-*.dll"));

			// Search for any file available called NRage-Language-XXXX.dll (where X is numbers)
			HANDLE fSearch = FindFirstFile(szBuffer, &fData);
			
			while ( fSearch != INVALID_HANDLE_VALUE )
			{
				DWORD dwLangID = 0;
				if (_stscanf(fData.cFileName, _T("NRage-Language-%u.dll"), &dwLangID))
				{
					VerLanguageName(dwLangID, szBuffer, DEFAULT_BUFFER);
					j = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)szBuffer );
					SendMessage( hDlgItem, CB_SETITEMDATA, j, dwLangID );
					if (dwLangID == g_ivConfig->Language)
						lLangFound = j;
				}
				if (!FindNextFile(fSearch, &fData))
					fSearch = INVALID_HANDLE_VALUE;
			}
			FindClose(fSearch);

			SendMessage( hDlgItem, CB_SETCURSEL, lLangFound, 0 ); // set combo box selection
		}
		// DropDownlist End
#else
		hDlgItem = GetDlgItem( hDlg, IDC_LANGUAGE );
		SendMessage(hDlgItem, CB_RESETCONTENT, 0, 0);
		j = SendMessage(hDlgItem, CB_ADDSTRING, 0, (LPARAM) _T("Language selection disabled"));
		SendMessage( hDlgItem, CB_SETITEMDATA, j, 0 );
		SendMessage(hDlgItem, CB_SETCURSEL, 0, 0);
#endif // #ifdef _UNICODE
		// call child-dialog(s) to update their content as well
		if( hTabControl )
			SendMessage( hTabControl, WM_USER_UPDATE, 0, 0 );
		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
//	return TRUE; //msg got processed
}

BOOL CALLBACK ControllerTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	static HWND hTabControl;

	CONTROLLER *pcController = &g_ivConfig->Controllers[g_ivConfig->ChosenTab];;
	HWND hDlgItem;
	long i,j;

	switch(uMsg)
	{
	case WM_INITDIALOG:
		hTabControl = NULL;

		// Tab - Control //
		hDlgItem = GetDlgItem( hDlg, IDC_CONTROLLERTAB );

		TCITEM tcItem;
		tcItem.mask = TCIF_TEXT;

		TCHAR tszText[DEFAULT_BUFFER];
		tcItem.pszText = tszText;

		LoadString( g_hResourceDLL, IDS_TAB_CONTROLS, tszText, DEFAULT_BUFFER );
		TabCtrl_InsertItem( hDlgItem, TAB_CONTROLS, &tcItem );
		LoadString( g_hResourceDLL, IDS_TAB_DEVICES, tszText, DEFAULT_BUFFER );
		TabCtrl_InsertItem( hDlgItem, TAB_DEVICES, &tcItem );
		LoadString( g_hResourceDLL, IDS_TAB_MODIFIERS, tszText, DEFAULT_BUFFER );
		TabCtrl_InsertItem( hDlgItem, TAB_MODIFIERS, &tcItem );
		LoadString( g_hResourceDLL, IDS_TAB_CONTROLLERPAK, tszText, DEFAULT_BUFFER );
		TabCtrl_InsertItem( hDlgItem, TAB_PAK, &tcItem );

		NMHDR nmInit;
		nmInit.hwndFrom = hDlgItem;
		nmInit.idFrom = IDC_CONTROLLERTAB;
		nmInit.code = TCN_SELCHANGE;

		// initiate Tab-Display
		ControllerTabProc( hDlg, WM_NOTIFY, IDC_CONTROLLERTAB, (LPARAM)&nmInit );
		// Tab - Control End //

		// call routine to show content( recursive call )
		ControllerTabProc( hDlg, WM_USER_UPDATE, 0, 0 );


		return FALSE; // don't give it focus

	case WM_NOTIFY:
		hDlgItem = ((LPNMHDR)lParam)->hwndFrom;
		i = ((LPNMHDR)lParam)->code;
		j = ((LPNMHDR)lParam)->idFrom;

		switch( j )
		{
			case IDC_CONTROLLERTAB:
				if( i == TCN_SELCHANGE )
				{
					if( hTabControl )
						DestroyWindow( hTabControl );

					i = TabCtrl_GetCurSel( hDlgItem );

					switch( i )
					{
					case TAB_CONTROLS:
						if( pcController->fXInput)	// added to show the xinput controller config tab --tecnicors
							hTabControl = CreateDialog ( g_hResourceDLL, MAKEINTRESOURCE( IDD_XCONTROLS ), hDlg, XControlsTabProc );
						else
							hTabControl = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_CONTROLS ), hDlg, ControlsTabProc );
						break;
					case TAB_DEVICES:
						hTabControl = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_DEVICES ), hDlg, DevicesTabProc );
						break;
					case TAB_MODIFIERS:
						hTabControl = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_MODIFIER ), hDlg, ModifierTabProc );
						break;
					case TAB_PAK:
						hTabControl = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_CONTROLLERPAK ), hDlg, ControllerPakTabProc );
						break;
					default:
						hTabControl = NULL;
						return TRUE;
					}
					hDlgItem = GetDlgItem( hDlg, IDC_CONTROLLERTAB );

					RECT rectWindow,
						rectTab, rectMain; // need to know the position of the calling tab window relative to its parent

					GetWindowRect( hDlg, &rectMain );
					GetWindowRect( hDlgItem, &rectTab );

					GetClientRect( hDlgItem, &rectWindow );
					TabCtrl_AdjustRect( hDlgItem, FALSE, &rectWindow );

					MoveWindow( hTabControl, rectWindow.left + (rectTab.left - rectMain.left), rectWindow.top + (rectTab.top - rectMain.top), rectWindow.right - rectWindow.left, rectWindow.bottom - rectWindow.top, FALSE );
					ShowWindow( hTabControl, SW_SHOW );
					return TRUE;
				}
				else
					return FALSE;
			default:
				return FALSE;
		}

	case WM_COMMAND:
		hDlgItem = GetDlgItem( hDlg, LOWORD(wParam) );

		switch( LOWORD(wParam) )
		{
			TCHAR tszTitle[DEFAULT_BUFFER], tszText[DEFAULT_BUFFER];
			case IDC_PLUGGED:
				pcController->fPlugged = ( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED );
				return TRUE;
			case IDC_CLEARCONTROLLER:
				LoadString( g_hResourceDLL, IDS_DLG_CLEAR, tszText, DEFAULT_BUFFER );
				LoadString( g_hResourceDLL, IDS_DLG_CLEAR_TITLE, tszTitle, DEFAULT_BUFFER );
				if( MessageBox( hDlg, tszText, tszTitle, MB_OKCANCEL | MB_ICONWARNING ) == IDOK )
				{
					DeleteControllerSettings( g_ivConfig->ChosenTab );
					ControllerTabProc( hDlg, WM_USER_UPDATE, 0, 0 );
				}
				return TRUE;
			case IDC_SETDEFAULT:
				LoadString( g_hResourceDLL, IDS_DLG_CONTROLRESTORE, tszText, DEFAULT_BUFFER );
				LoadString( g_hResourceDLL, IDS_DLG_CONTROLRESTORE_TITLE, tszTitle, DEFAULT_BUFFER );
				if( MessageBox( hDlg, tszText, tszTitle, MB_OKCANCEL | MB_ICONWARNING ) == IDOK )
				{
					DeleteControllerSettings( g_ivConfig->ChosenTab );
					LoadProfileFromResource( g_ivConfig->ChosenTab, true);
					*(g_ivConfig->FFDevices[g_ivConfig->ChosenTab].szProductName) = '\0'; // default profile has no FF device
					g_ivConfig->FFDevices[g_ivConfig->ChosenTab].bProductCounter = 0;
					ControllerTabProc( hDlg, WM_USER_UPDATE, 0, 0 );
				}
				return TRUE;

			case IDC_SAVEPROFILE:
				{
					TCHAR szFilename[MAX_PATH+1] = _T("");
					if( BrowseFile( hDlg, szFilename, BF_PROFILE, BF_SAVE ))
					{
						FILE * fFile = _tfopen( szFilename, _T("wS") );
						if (fFile)
						{
							fprintf(fFile, "@" STRING_PROFILEVERSION "\n\n");
							FormatProfileBlock( fFile, g_ivConfig->ChosenTab );

							fclose(fFile);
						}
						else
							WarningMessage( IDS_ERR_PROFWRITE, MB_OK );
					}
				}
				return TRUE;

			case IDC_LOADPROFILE:
				{
					TCHAR szFilename[MAX_PATH+1] = TEXT( "" );
					if( BrowseFile( hDlg, szFilename, BF_PROFILE, BF_LOAD ))
					{
						DebugWrite(_T("Config interface: Load shortcuts file: %s\n"), szFilename);
						if( LoadProfileFile( szFilename, g_ivConfig->ChosenTab, g_ivConfig->FFDevices[g_ivConfig->ChosenTab].szProductName, &g_ivConfig->FFDevices[g_ivConfig->ChosenTab].bProductCounter ))
							ControllerTabProc( hDlg, WM_USER_UPDATE, 0, 0 );
						else
							WarningMessage( IDS_ERR_PROFREAD, MB_OK );

					}
				}
				return TRUE;
				
			case IDC_XINPUT_ENABLER:	// change to xinput config --tecnicors
				pcController->fXInput = ( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED );
				if( hTabControl )
						DestroyWindow( hTabControl );
				if( pcController->fXInput )
					hTabControl = CreateDialog ( g_hResourceDLL, MAKEINTRESOURCE( IDD_XCONTROLS ), hDlg, XControlsTabProc );
				else
					hTabControl = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_CONTROLS ), hDlg, ControlsTabProc );
				{
					hDlgItem = GetDlgItem( hDlg, IDC_CONTROLLERTAB );

					RECT rectWindow,
						rectTab, rectMain; // need to know the position of the calling tab window relative to its parent

					GetWindowRect( hDlg, &rectMain );
					GetWindowRect( hDlgItem, &rectTab );

					GetClientRect( hDlgItem, &rectWindow );
					TabCtrl_AdjustRect( hDlgItem, FALSE, &rectWindow );

					MoveWindow( hTabControl, rectWindow.left + (rectTab.left - rectMain.left), rectWindow.top + (rectTab.top - rectMain.top), rectWindow.right - rectWindow.left, rectWindow.bottom - rectWindow.top, FALSE );
					ShowWindow( hTabControl, SW_SHOW );
				}
				return TRUE;	// END IDC_XINPUT_ENABLER

			default:
				return FALSE;
		}

	case WM_USER_UPDATE:
		// for this dialog
		CheckDlgButton( hDlg, IDC_PLUGGED, pcController->fPlugged ? BST_CHECKED : BST_UNCHECKED );
		CheckDlgButton( hDlg, IDC_XINPUT_ENABLER, pcController->fXInput ? BST_CHECKED : BST_UNCHECKED );

		if( hTabControl )
			DestroyWindow( hTabControl );

		if( pcController->fXInput )
			hTabControl = CreateDialog ( g_hResourceDLL, MAKEINTRESOURCE( IDD_XCONTROLS ), hDlg, XControlsTabProc );
		else
			hTabControl = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_CONTROLS ), hDlg, ControlsTabProc );

		{
			hDlgItem = GetDlgItem( hDlg, IDC_CONTROLLERTAB );

			RECT rectWindow,
				rectTab, rectMain; // need to know the position of the calling tab window relative to its parent

			GetWindowRect( hDlg, &rectMain );
			GetWindowRect( hDlgItem, &rectTab );

			GetClientRect( hDlgItem, &rectWindow );
			TabCtrl_AdjustRect( hDlgItem, FALSE, &rectWindow );

			MoveWindow( hTabControl, rectWindow.left + (rectTab.left - rectMain.left), rectWindow.top + (rectTab.top - rectMain.top), rectWindow.right - rectWindow.left, rectWindow.bottom - rectWindow.top, FALSE );
			ShowWindow( hTabControl, SW_SHOW );
		}

		// call child-dialog(s) to update their content as well
		if( hTabControl )
			SendMessage( hTabControl, WM_USER_UPDATE, 0, 0 );
		return TRUE;
	default:
		return FALSE; //false means the msg didn't got processed
	}
}

// "Controls" tab
BOOL CALLBACK ControlsTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	static LPBUTTON aButtons = NULL;	// should point to g_ivConfig->Controllers[g_ivConfig->ChosenTab].aButton array
	static DWORD dwButtonID[3];			// 0:ID of PushButton	1:ID of TextWindow	2:offset in aButtons struct
	static DWORD dwCounter;
	static bool bScanRunning;
	static HWND hFocus = NULL;
	static HWND hBlocker = NULL;

	TCHAR szBuffer[40], szTemp[40];
	HWND hDlgItem;
	long i;

	switch(uMsg)
	{
	case WM_INITDIALOG:
		// SetTicks on TrackBar
		hDlgItem = GetDlgItem( hDlg, IDC_CTRRANGE );
		SendMessage( hDlgItem, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG( 0, 100 ));
		SendMessage( hDlgItem, TBM_SETTICFREQ, (WPARAM) 10, 0 );
		SendMessage( hDlgItem, TBM_SETPAGESIZE, (WPARAM) 0, 1 );

		// SetTicks on RapidFire Bar
		hDlgItem = GetDlgItem( hDlg, IDC_RAPIDFIRERATE );
		SendMessage( hDlgItem, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG( 0, 32 ));
		SendMessage( hDlgItem, TBM_SETTICFREQ, (WPARAM) 1, 0 );
		SendMessage( hDlgItem, TBM_SETPAGESIZE, (WPARAM) 0, 1 );

		// call routine to show content( recursive call )
		ControlsTabProc( hDlg, WM_USER_UPDATE, 0, 0 );
		return FALSE; // don't give it focus

	case WM_COMMAND:
		hDlgItem = GetDlgItem( hDlg, LOWORD(wParam) );

		switch( LOWORD(wParam) )
		{
		case IDC_N64RANGE:
			g_ivConfig->Controllers[g_ivConfig->ChosenTab].fRealN64Range = ( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED );
			return TRUE;

		case IDC_RAPIDFIREENABLE:
			g_ivConfig->Controllers[g_ivConfig->ChosenTab].bRapidFireEnabled = (IsDlgButtonChecked( hDlg, LOWORD(wParam)) == BST_CHECKED);
			return TRUE;

		case IDC_CONFIG1:
		case IDC_CONFIG2:
		case IDC_CONFIG3:
			if( HIWORD(wParam) == BN_CLICKED )
			{
				if( LOWORD(wParam) == IDC_CONFIG1 )
					g_ivConfig->Controllers[g_ivConfig->ChosenTab].bAxisSet = 0;
				else if( LOWORD(wParam) == IDC_CONFIG2 )
					g_ivConfig->Controllers[g_ivConfig->ChosenTab].bAxisSet = 1;
				else if( LOWORD(wParam) == IDC_CONFIG3 )
					g_ivConfig->Controllers[g_ivConfig->ChosenTab].bAxisSet = 2;

				ControlsTabProc( hDlg, WM_USER_UPDATE, 1, 0 );
			}
			return TRUE;

		case IDC_LEFTTRIGGER:
		case IDC_RIGHTTRIGGER:
		case IDC_ZTRIGGER:
		case IDC_DUP:
		case IDC_DLEFT:
		case IDC_DRIGHT:
		case IDC_DDOWN:
		case IDC_AUP:
		case IDC_ALEFT:
		case IDC_ARIGHT:
		case IDC_ADOWN:
		case IDC_CUP:
		case IDC_CLEFT:
		case IDC_CRIGHT:
		case IDC_CDOWN:
		case IDC_ABUTTON:
		case IDC_BBUTTON:
		case IDC_SBUTTON: // any of these cases means user wants to assign a button
			if( bScanRunning )
			{	; // do nothing
/*				bScanRunning = false;
				KillTimer( hDlg, TIMER_BUTTON );
				GetButtonText( aButtons[dwButtonID[2]], szBuffer );
				SendMessage( GetDlgItem( hDlg, dwButtonID[1] ), WM_SETTEXT , 0, (LPARAM)szBuffer ); */
			}
			else if( HIWORD(wParam) == BN_CLICKED )
			{
				EnterCriticalSection(&g_critical);
				dwButtonID[0] = LOWORD(wParam);
				dwCounter = 0;
				GetButtonID( dwButtonID, 0, BSET_CONTROLS );
				if( dwButtonID[2] >= PF_APADR )
					dwButtonID[2] += g_ivConfig->Controllers[g_ivConfig->ChosenTab].bAxisSet * 4;
				if (hFocus == NULL)
					hFocus = SetFocus( NULL );
				hBlocker = MakeOverlay();

				SetTimer( hDlg, TIMER_BUTTON, INTERVAL_BUTTON, NULL );
				bScanRunning = true;
				LeaveCriticalSection(&g_critical);
			}
			return TRUE;

		default:
			return FALSE;
		}

	case WM_TIMER: // when assigning buttons, this gets called every 20ms (or value in INTERVAL_BUTTON)
		if( wParam == TIMER_BUTTON && bScanRunning )
		{
			BUTTON newButton;

			i = ScanDevices( &dwCounter, &newButton);
			if( i || dwCounter > 500 )
			{
				bScanRunning = false;
				KillTimer( hDlg, TIMER_BUTTON );

				EnterCriticalSection(&g_critical);
				if( i == SC_SCANESCAPE ) // Scan aborted
					ZeroMemory(&aButtons[dwButtonID[2]], sizeof(BUTTON)); //aButtons[dwButtonID[2]].dwButton = 0;
				else if( i == SC_SCANSUCCEED  ) // Got a key, mouseclick, joybutton, or axis
					aButtons[dwButtonID[2]] = newButton;
				DestroyWindow( hBlocker );

				LeaveCriticalSection(&g_critical);

				GetButtonText( aButtons[dwButtonID[2]], szBuffer );
				SendMessage( GetDlgItem( hDlg, dwButtonID[1] ), WM_SETTEXT , 0, (LPARAM)szBuffer );
				if( hFocus != NULL )
				{
					SetFocus( hFocus );
					hFocus = NULL;
				}
			}
			else
			{
				if(( dwCounter % 50 ) == 0 )
				{
					TCHAR tszText[DEFAULT_BUFFER];

					LoadString( g_hResourceDLL, IDS_C_POLLING, tszText, DEFAULT_BUFFER );
					wsprintf( szBuffer, tszText, 10 - dwCounter / 50 );
					SendMessage( GetDlgItem( hDlg, dwButtonID[1] ), WM_SETTEXT , 0, (LPARAM)szBuffer );
				}
				++dwCounter;
			}
			return TRUE;
		}
		else
			return FALSE;

	case WM_HSCROLL: // TrackBars
	case WM_VSCROLL:
		switch ( GetWindowLong( (HWND)lParam, GWL_ID ) )
		{
		case IDC_CTRRANGE:
			TCHAR tszText[DEFAULT_BUFFER];

			LoadString( g_hResourceDLL, IDS_C_RANGE, tszText, DEFAULT_BUFFER );
			g_ivConfig->Controllers[g_ivConfig->ChosenTab].bStickRange = (BYTE)SendMessage( (HWND)lParam, TBM_GETPOS, 0, 0 );
			wsprintf( szBuffer, tszText, g_ivConfig->Controllers[g_ivConfig->ChosenTab].bStickRange );
			SendMessage( GetDlgItem( hDlg, IDT_RANGE ), WM_SETTEXT , 0, (LPARAM)szBuffer );
			return TRUE;
		case IDC_RAPIDFIRERATE:
			g_ivConfig->Controllers[g_ivConfig->ChosenTab].bRapidFireRate = 33 - (BYTE)SendMessage( (HWND)lParam, TBM_GETPOS, 0, 0 );
			return TRUE;
		default:
			return FALSE;
		}

	case WM_USER_UPDATE:
		bScanRunning = false;
		KillTimer( hDlg, TIMER_BUTTON );
		EnterCriticalSection(&g_critical);
		aButtons = g_ivConfig->Controllers[g_ivConfig->ChosenTab].aButton;
		LeaveCriticalSection(&g_critical);

		if( wParam == 0 )
		{
			CheckDlgButton( hDlg, IDC_N64RANGE, g_ivConfig->Controllers[g_ivConfig->ChosenTab].fRealN64Range ? BST_CHECKED : BST_UNCHECKED );
			CheckDlgButton( hDlg, IDC_RAPIDFIREENABLE, g_ivConfig->Controllers[g_ivConfig->ChosenTab].bRapidFireEnabled ? BST_CHECKED : BST_UNCHECKED );
			CheckDlgButton( hDlg, IDC_CONFIG1, ( g_ivConfig->Controllers[g_ivConfig->ChosenTab].bAxisSet == 0 ) ? BST_CHECKED : BST_UNCHECKED );
			CheckDlgButton( hDlg, IDC_CONFIG2, ( g_ivConfig->Controllers[g_ivConfig->ChosenTab].bAxisSet == 1 ) ? BST_CHECKED : BST_UNCHECKED );
			CheckDlgButton( hDlg, IDC_CONFIG3, ( g_ivConfig->Controllers[g_ivConfig->ChosenTab].bAxisSet == 2 ) ? BST_CHECKED : BST_UNCHECKED );

			SendMessage( GetDlgItem( hDlg, IDC_CTRRANGE ), TBM_SETPOS, TRUE, g_ivConfig->Controllers[g_ivConfig->ChosenTab].bStickRange );
			LoadString( g_hResourceDLL, IDS_C_RANGE, szTemp, 40 );
			wsprintf( szBuffer, szTemp, g_ivConfig->Controllers[g_ivConfig->ChosenTab].bStickRange );
			SendMessage( GetDlgItem( hDlg, IDT_RANGE ), WM_SETTEXT , 0, (LPARAM)szBuffer );
			SendMessage( GetDlgItem( hDlg, IDC_RAPIDFIRERATE ), TBM_SETPOS, TRUE, 33 - g_ivConfig->Controllers[g_ivConfig->ChosenTab].bRapidFireRate );

			i = 0;
		}
		else
			i = PF_APADR;

		for( ;i < 18; ++i )
		{
			DWORD dwIDs[3];
			dwIDs[2] = i;
			if( !GetButtonID( dwIDs, 2, BSET_CONTROLS ))
				continue;

			if( dwIDs[2] >= PF_APADR )
				dwIDs[2] += g_ivConfig->Controllers[g_ivConfig->ChosenTab].bAxisSet * 4;

			GetButtonText( aButtons[dwIDs[2]], szBuffer );
			SendMessage( GetDlgItem( hDlg, dwIDs[1] ), WM_SETTEXT , 0, (LPARAM)szBuffer );
		}
		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
//	return TRUE; //msg got processed
}

// XInput controllers tab	--tecnicors
BOOL CALLBACK XControlsTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	LPXCONTROLLER gController = &g_ivConfig->Controllers[g_ivConfig->ChosenTab].xiController;

	switch( uMsg )
	{
	case WM_INITDIALOG:
		for( int i = IDC_XC_A; i <= IDC_XC_START; i++ )
			FillN64ButtonComboBox( hDlg, i );
		for( int i = IDC_XC_DPAD; i <= IDC_XC_RTS; i++ )
			FillN64AnalogComboBox( hDlg, i );
		XControlsTabProc( hDlg, WM_USER_UPDATE, 0, 0 );
		return FALSE;

	case WM_USER_UPDATE:
		{
			TCHAR buffer[MAX_PATH];
			GetDirectory( buffer, DIRECTORY_CONFIG );
			_stprintf_s( buffer, _T("%sXInput Controller %d Config.xcc"), buffer, gController->nControl + 1 );
			FILE *saveFile = _tfopen( buffer, _T("rS") );
			if( saveFile )
			{
				LoadXInputConfigFromFile( saveFile, gController );
				fclose( saveFile );
			}
		}
		if( !ReadXInputControllerKeys( hDlg, gController ))
			for( int i = IDC_XC_A; i <= IDC_XC_RTS; i++ )
				SendDlgItemMessage( hDlg, i, CB_SETCURSEL, 0, ( LPARAM )0 );
		return TRUE;

	case WM_COMMAND:
		switch( LOWORD( wParam ))
		{
		case IDC_XC_USE:
			StoreXInputControllerKeys( hDlg, gController );
			{
				TCHAR buffer[MAX_PATH];
				GetDirectory( buffer, DIRECTORY_CONFIG );
				_stprintf_s( buffer, _T("%sXInput Controller %d Config.xcc"), buffer, gController->nControl + 1 );
				FILE *saveFile = _tfopen( buffer, _T("wS") );
				SaveXInputConfigToFile( saveFile, gController );
				fclose( saveFile );
			}
			return TRUE;
		}
		return FALSE;
	default:
		return FALSE;
	}
}// END Xinput Controller Tab --tecnicors

BOOL CALLBACK DevicesTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	CONTROLLER *pcController = &g_ivConfig->Controllers[g_ivConfig->ChosenTab];
	TCHAR szBuffer[DEFAULT_BUFFER], szTemp[DEFAULT_BUFFER];
	HWND hDlgItem;
	long i;

	switch(uMsg)
	{
	case WM_INITDIALOG:

		// TrackBars
		hDlgItem = GetDlgItem( hDlg, IDC_DEADZONE );

		SendMessage( hDlgItem, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG( 0, 100 ));
		SendMessage( hDlgItem, TBM_SETTICFREQ, (WPARAM) 10, 0 );
		SendMessage( hDlgItem, TBM_SETPAGESIZE, (WPARAM) 0, 1 );

		hDlgItem = GetDlgItem( hDlg, IDC_MSSENSITIVITY_X );

		SendMessage( hDlgItem, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG( 0, 1000 ));
		SendMessage( hDlgItem, TBM_SETPAGESIZE, (WPARAM) 0, 1 );

		hDlgItem = GetDlgItem( hDlg, IDC_MSSENSITIVITY_Y );

		SendMessage( hDlgItem, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG( 0, 1000 ));
		SendMessage( hDlgItem, TBM_SETPAGESIZE, (WPARAM) 0, 1 );
		{
			short sTics[] = { 25, 50, 75, 100, 125, 150, 200, 250, 300, 400, 500, 600, 700, 800, 900 };
			for( i = 0; i < (sizeof(sTics) / sizeof(short)); ++i )
				SendMessage( hDlgItem, TBM_SETTIC, 0, sTics[i] );
		}
		// TrackBars End

		DevicesTabProc( hDlg, WM_USER_UPDATE, 0, 0 ); // setting values

		return FALSE; // don't give it focus

	case WM_COMMAND:
		hDlgItem = GetDlgItem( hDlg, LOWORD(wParam) );

		switch( LOWORD(wParam) )
		{
		case IDC_DEADPANMOUSEX:
			if( LOWORD(wParam) == IDC_DEADPANMOUSEX )
				i = MM_DEAD;
		case IDC_BUFFEREDMOUSEX:
			if( LOWORD(wParam) == IDC_BUFFEREDMOUSEX )
				i = MM_BUFF;
		case IDC_ABSOLUTEMOUSEX:
			if( LOWORD(wParam) == IDC_ABSOLUTEMOUSEX )
				i = MM_ABS;
			if(( HIWORD(wParam) == BN_CLICKED ) &&	( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED ))
				pcController->bMouseMoveX = i;
			return TRUE;

		case IDC_DEADPANMOUSEY:
			if( LOWORD(wParam) == IDC_DEADPANMOUSEY )
				i = MM_DEAD;
		case IDC_BUFFEREDMOUSEY:
			if( LOWORD(wParam) == IDC_BUFFEREDMOUSEY )
				i = MM_BUFF;
		case IDC_ABSOLUTEMOUSEY:
			if( LOWORD(wParam) == IDC_ABSOLUTEMOUSEY )
				i = MM_ABS;
			if(( HIWORD(wParam) == BN_CLICKED ) &&	( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED ))
				pcController->bMouseMoveY = i;
			return TRUE;

		case IDC_ACCELERATEX:
			pcController->fKeyAbsoluteX = ( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED );
			return TRUE;
		case IDC_ACCELERATEY:
			pcController->fKeyAbsoluteY = ( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED );
			return TRUE;

		default:
			return FALSE;
		}

	case WM_HSCROLL: // TrackBars
	case WM_VSCROLL:
		i = GetWindowLong( (HWND)lParam, GWL_ID );
		switch( i )
		{
		case IDC_MSSENSITIVITY_X:
			pcController->wMouseSensitivityX = (WORD)SendMessage( (HWND)lParam, TBM_GETPOS, 0, 0 );
			LoadString( g_hResourceDLL, IDS_D_MSX, szTemp, DEFAULT_BUFFER );
			wsprintf( szBuffer, szTemp, pcController->wMouseSensitivityX );
			SendMessage( GetDlgItem( hDlg, IDT_MSSENSITIVITY_X ), WM_SETTEXT , 0, (LPARAM)szBuffer );
			return TRUE;

		case IDC_MSSENSITIVITY_Y:
			pcController->wMouseSensitivityY = (WORD)SendMessage( (HWND)lParam, TBM_GETPOS, 0, 0 );
			LoadString( g_hResourceDLL, IDS_D_MSY, szTemp, DEFAULT_BUFFER );
			wsprintf( szBuffer, szTemp, pcController->wMouseSensitivityY );
			SendMessage( GetDlgItem( hDlg, IDT_MSSENSITIVITY_Y ), WM_SETTEXT , 0, (LPARAM)szBuffer );
			return TRUE;

		case IDC_DEADZONE:
			pcController->bPadDeadZone = (BYTE)SendMessage( (HWND)lParam, TBM_GETPOS, 0, 0 );
			LoadString( g_hResourceDLL, IDS_D_DEADZONE, szTemp, DEFAULT_BUFFER );
			wsprintf( szBuffer, szTemp, pcController->bPadDeadZone );
			SendMessage( GetDlgItem( hDlg, IDT_DEADZONE ), WM_SETTEXT , 0, (LPARAM)szBuffer );
			return TRUE;
		default:
			return FALSE;
		}

	case WM_USER_UPDATE:
		if( pcController->bMouseMoveX == MM_DEAD )
			CheckDlgButton( hDlg, IDC_DEADPANMOUSEX, BST_CHECKED );
		else
			CheckDlgButton( hDlg, IDC_DEADPANMOUSEX, BST_UNCHECKED );

		if( pcController->bMouseMoveX == MM_BUFF )
			CheckDlgButton( hDlg, IDC_BUFFEREDMOUSEX, BST_CHECKED );
		else
			CheckDlgButton( hDlg, IDC_BUFFEREDMOUSEX, BST_UNCHECKED );

		if( pcController->bMouseMoveX == MM_ABS )
			CheckDlgButton( hDlg, IDC_ABSOLUTEMOUSEX, BST_CHECKED );
		else
			CheckDlgButton( hDlg, IDC_ABSOLUTEMOUSEX, BST_UNCHECKED );

		if( pcController->bMouseMoveY == MM_DEAD )
			CheckDlgButton( hDlg, IDC_DEADPANMOUSEY, BST_CHECKED );
		else
			CheckDlgButton( hDlg, IDC_DEADPANMOUSEY, BST_UNCHECKED );

		if( pcController->bMouseMoveY == MM_BUFF )
			CheckDlgButton( hDlg, IDC_BUFFEREDMOUSEY, BST_CHECKED );
		else
			CheckDlgButton( hDlg, IDC_BUFFEREDMOUSEY, BST_UNCHECKED );

		if( pcController->bMouseMoveY == MM_ABS )
			CheckDlgButton( hDlg, IDC_ABSOLUTEMOUSEY, BST_CHECKED );
		else
			CheckDlgButton( hDlg, IDC_ABSOLUTEMOUSEY, BST_UNCHECKED );

		CheckDlgButton( hDlg, IDC_ACCELERATEX, pcController->fKeyAbsoluteX ? BST_CHECKED : BST_UNCHECKED );
		CheckDlgButton( hDlg, IDC_ACCELERATEY, pcController->fKeyAbsoluteY ? BST_CHECKED : BST_UNCHECKED );

		// TrackBars
		SendMessage( GetDlgItem( hDlg, IDC_DEADZONE ), TBM_SETPOS, TRUE, pcController->bPadDeadZone );
		LoadString( g_hResourceDLL, IDS_D_DEADZONE, szTemp, DEFAULT_BUFFER );
		wsprintf( szBuffer, szTemp, pcController->bPadDeadZone );
		SendMessage( GetDlgItem( hDlg, IDT_DEADZONE ), WM_SETTEXT , 0, (LPARAM)szBuffer );

		SendMessage( GetDlgItem( hDlg, IDC_MSSENSITIVITY_X ), TBM_SETPOS, TRUE, pcController->wMouseSensitivityX );
		LoadString( g_hResourceDLL, IDS_D_MSX, szTemp, DEFAULT_BUFFER );
		wsprintf( szBuffer, szTemp, pcController->wMouseSensitivityX );
		SendMessage( GetDlgItem( hDlg, IDT_MSSENSITIVITY_X ), WM_SETTEXT , 0, (LPARAM)szBuffer );

		SendMessage( GetDlgItem( hDlg, IDC_MSSENSITIVITY_Y ), TBM_SETPOS, TRUE, pcController->wMouseSensitivityY );
		LoadString( g_hResourceDLL, IDS_D_MSY, szTemp, DEFAULT_BUFFER );
		wsprintf( szBuffer, szTemp, pcController->wMouseSensitivityY );
		SendMessage( GetDlgItem( hDlg, IDT_MSSENSITIVITY_Y ), WM_SETTEXT , 0, (LPARAM)szBuffer );
		// TrackBars End
		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
}

BOOL CALLBACK MoveModifierDialog( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	HWND hDlgItem;
	long i,j;
	DWORD dwValue;
	TCHAR szBuffer[DEFAULT_BUFFER], szTemp[DEFAULT_BUFFER];

	switch(uMsg)
	{
	case WM_INITDIALOG:
		// TrackBars
		hDlgItem = GetDlgItem( hDlg, IDC_XMODIFIER );

		SendMessage( hDlgItem, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG( 0, 500 ));
		SendMessage( hDlgItem, TBM_SETPAGESIZE, (WPARAM) 0, 1 );
		{
			short sTics[] = { 25, 50, 75, 100, 125, 150, 200, 250, 300, 400, 500 };
			for( i = 0; i < (sizeof(sTics) / sizeof(short)); ++i )
				SendMessage( hDlgItem, TBM_SETTIC, 0, sTics[i] );
		}

		hDlgItem = GetDlgItem( hDlg, IDC_YMODIFIER );

		SendMessage( hDlgItem, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG( 0, 500 ));
		SendMessage( hDlgItem, TBM_SETPAGESIZE, (WPARAM) 0, 1 );
		{
			short sTics[] = { 25, 50, 75, 100, 125, 150, 200, 250, 300, 400, 500 };
			for( i = 0; i < (sizeof(sTics) / sizeof(short)); ++i )
				SendMessage( hDlgItem, TBM_SETTIC, 0, sTics[i] );
		}
		return FALSE; // don't give it focus

	case WM_HSCROLL: // TrackBars
	case WM_VSCROLL:
		switch (GetWindowLong( (HWND)lParam, GWL_ID ))
		{
		case IDC_XMODIFIER:
			i = SendMessage( (HWND)lParam, TBM_GETPOS, 0, 0 );
			LoadString( g_hResourceDLL, IDS_M_MOVEVALUE, szTemp, DEFAULT_BUFFER );
			wsprintf( szBuffer, szTemp, i );
			SendMessage( GetDlgItem( hDlg, IDT_XMODIFIER ), WM_SETTEXT , 0, (LPARAM)szBuffer );
			return TRUE;
		case IDC_YMODIFIER:
			i = SendMessage( (HWND)lParam, TBM_GETPOS, 0, 0 );
			LoadString( g_hResourceDLL, IDS_M_MOVEVALUE, szTemp, DEFAULT_BUFFER );
			wsprintf( szBuffer, szTemp, i );
			SendMessage( GetDlgItem( hDlg, IDT_YMODIFIER ), WM_SETTEXT , 0, (LPARAM)szBuffer );
			return TRUE;
		default:
			return FALSE;
		}

	case WM_USER_UPDATE:
		if( wParam == MDT_MOVE )
			dwValue = (DWORD)lParam;
		else
			dwValue = MAKELONG( 100, 100 );

		i = (short)(dwValue & 0x0000FFFF);

		if( i < 0 )
		{
			i = -i;
			CheckDlgButton( hDlg, IDC_XNEGATE, BST_CHECKED );
		}
		else
			CheckDlgButton( hDlg, IDC_XNEGATE, BST_UNCHECKED );
		SendMessage( GetDlgItem( hDlg, IDC_XMODIFIER ), TBM_SETPOS, TRUE, i );
		LoadString( g_hResourceDLL, IDS_M_MOVEVALUE, szTemp, DEFAULT_BUFFER );
		wsprintf( szBuffer, szTemp, i );
		SendMessage( GetDlgItem( hDlg, IDT_XMODIFIER ), WM_SETTEXT , 0, (LPARAM)szBuffer );

		i = (short)((dwValue >> 16) & 0x0000FFFF);

		if( i < 0 )
		{
			i = -i;
			CheckDlgButton( hDlg, IDC_YNEGATE, BST_CHECKED );
		}
		else
			CheckDlgButton( hDlg, IDC_YNEGATE, BST_UNCHECKED );
		SendMessage( GetDlgItem( hDlg, IDC_YMODIFIER ), TBM_SETPOS, TRUE, i );
		LoadString( g_hResourceDLL, IDS_M_MOVEVALUE, szTemp, DEFAULT_BUFFER );
		wsprintf( szBuffer, szTemp, i );
		SendMessage( GetDlgItem( hDlg, IDT_YMODIFIER ), WM_SETTEXT , 0, (LPARAM)szBuffer );
		return TRUE;

	case WM_USER_READVALUES:
		i = SendMessage( GetDlgItem( hDlg, IDC_XMODIFIER ), TBM_GETPOS, 0, 0 );
		if( IsDlgButtonChecked( hDlg, IDC_XNEGATE ) == BST_CHECKED )
			i = -i;

		j = SendMessage( GetDlgItem( hDlg, IDC_YMODIFIER ), TBM_GETPOS, 0, 0 );
		if( IsDlgButtonChecked( hDlg, IDC_YNEGATE ) == BST_CHECKED )
			j = -j;

		*(DWORD*)wParam = MAKELONG( (short)i, (short)j );
		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
}

BOOL CALLBACK MacroModifierDialog( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	long i;
	DWORD dwValue;

	const DWORD aButtons[] ={	IDC_DRIGHT	, IDC_DLEFT	, IDC_DDOWN		, IDC_DUP,
						IDC_SBUTTON	, IDC_ZTRIG	, IDC_BBUTTON	, IDC_ABUTTON,
						IDC_CRIGHT	, IDC_CLEFT	, IDC_CDOWN		, IDC_CUP,
						IDC_RTRIG	, IDC_LTRIG	, 0				, 0,
						IDC_ARIGHT	, IDC_ALEFT	, IDC_ADOWN		, IDC_AUP,
						IDC_RAPIDFIREMODE		, IDC_RAPIDFIREMODERATE };

	switch(uMsg)
	{
	case WM_INITDIALOG:
		return FALSE; // don't give it focus

	case WM_USER_UPDATE:
		if( wParam == MDT_MACRO )
			dwValue = (DWORD)lParam;
		else
			dwValue = 0;

		i = sizeof(aButtons) / sizeof(aButtons[0]) - 1;

		while( i >= 0 )
		{
			if( aButtons[i] )
			{
				if( dwValue & ( 1 << i ) )
					CheckDlgButton( hDlg, aButtons[i], BST_CHECKED );
				else
					CheckDlgButton( hDlg, aButtons[i], BST_UNCHECKED );
			}

			i--;
		}
		return TRUE;

	case WM_USER_READVALUES:
		dwValue = 0;

		i = sizeof(aButtons) / sizeof(aButtons[0]) - 1;
		while( i >= 0 )
		{
			if( aButtons[i] && ( IsDlgButtonChecked( hDlg, aButtons[i] ) == BST_CHECKED ))
				dwValue = dwValue | ( 1 << i );

			i--;
		}

		*(DWORD*)wParam = dwValue;
		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
}

BOOL CALLBACK ConfigModifierDialog( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	DWORD dwValue;

	const DWORD aButtons[] ={	IDC_CONFIG1		, 0x000001,
						IDC_CONFIG2		, 0x000003,
						IDC_CONFIG3		, 0x000005,
						IDC_CONFIGCYCLE	, 0x0000FF,
						IDC_MOUSEX		, 0x000100,
						IDC_MOUSEY		, 0x000200,
						IDC_KEYX		, 0x010000,
						IDC_KEYY		, 0x020000 };

	int i;

	switch(uMsg)
	{
	case WM_INITDIALOG:
		return FALSE; // don't give it focus

	case WM_COMMAND:
		switch( LOWORD(wParam) )
		{
		case IDC_CONFIG1:
		case IDC_CONFIG2:
		case IDC_CONFIG3:
		case IDC_CONFIGCYCLE:
			if(( HIWORD(wParam) == BN_CLICKED ) || ( HIWORD(wParam) == BN_DBLCLK ))
			{
				EnterCriticalSection(&g_critical);	// has a possibility of affecting the buttons we're writing to
				bool bCheck = ( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED ) ? false : true;
				CheckDlgButton( hDlg, IDC_CONFIG1, BST_UNCHECKED );
				CheckDlgButton( hDlg, IDC_CONFIG2, BST_UNCHECKED );
				CheckDlgButton( hDlg, IDC_CONFIG3, BST_UNCHECKED );
				CheckDlgButton( hDlg, IDC_CONFIGCYCLE, BST_UNCHECKED );
				if( bCheck )
					CheckDlgButton( hDlg, LOWORD(wParam), BST_CHECKED );
				LeaveCriticalSection(&g_critical);
			}
			return TRUE;
		default:
			return FALSE;
		}

	case WM_USER_UPDATE:
		if( wParam == MDT_CONFIG )
			dwValue = (DWORD)lParam;
		else
			dwValue = 0;
		if( dwValue & 0x01 )
		{
			BYTE bConfig = (BYTE)((dwValue >> 1) & 0x7F);
			if( bConfig >= PF_AXESETS )
			{
				CheckDlgButton( hDlg, IDC_CONFIG1, BST_UNCHECKED );
				CheckDlgButton( hDlg, IDC_CONFIG2, BST_UNCHECKED );
				CheckDlgButton( hDlg, IDC_CONFIG3, BST_UNCHECKED );
				CheckDlgButton( hDlg, IDC_CONFIGCYCLE, BST_CHECKED );
			}
			else
			{
				i = PF_AXESETS - 1;
				while( i >= 0 )
				{
					if( bConfig == i )
						CheckDlgButton( hDlg, aButtons[i*2], BST_CHECKED );
					else
						CheckDlgButton( hDlg, aButtons[i*2], BST_UNCHECKED );

					i--;
				}
			}
		}

		i = sizeof(aButtons) / sizeof(aButtons[0]) - 2;
		while( i >= 8 )
		{
			if(( dwValue & aButtons[i+1] ) == aButtons[i+1] )
				CheckDlgButton( hDlg, aButtons[i], BST_CHECKED );
			else
				CheckDlgButton( hDlg, aButtons[i], BST_UNCHECKED );

			i -= 2;
		}
		return TRUE;

	case WM_USER_READVALUES:
		dwValue = 0;

		i = sizeof(aButtons) / sizeof(aButtons[0]) - 2;
		while( i >= 0 )
		{
			if( IsDlgButtonChecked( hDlg, aButtons[i] ) == BST_CHECKED )
				dwValue |= aButtons[i+1];
			i -= 2;
		}

		*(DWORD*)wParam = dwValue;
		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
}

void ModDescription( HWND hListView, int iEntry, const LPMODIFIER pModifier )
{
	TCHAR szBuffer[DEFAULT_BUFFER];
	const UINT iModTypes[] = { IDS_M_TAB_NONE, IDS_M_TAB_MOVE, IDS_M_TAB_MACRO, IDS_M_TAB_CONFIG };
	TCHAR pszModTypes[4][16];

	for (int i = 0; i < ARRAYSIZE(iModTypes); i++ )
		LoadString( g_hResourceDLL, iModTypes[i], pszModTypes[i], ARRAYSIZE(pszModTypes[i]) );

	GetButtonText( pModifier->btnButton, szBuffer );

	ListView_SetItemText( hListView, iEntry, 0,szBuffer );

	switch( pModifier->bModType )
	{
	case MDT_MOVE:
		ListView_SetItemText( hListView, iEntry, 1, pszModTypes[1] );
		wsprintf( szBuffer, _T("X:%i%% / Y:%i%%"), (short)(pModifier->dwSpecific & 0x0000FFFF), (short)((pModifier->dwSpecific >> 16) & 0x0000FFFF));
		break;
	case MDT_MACRO:
		ListView_SetItemText( hListView, iEntry, 1, pszModTypes[2] );
		szBuffer[0] = '\0';
		{
			bool bGotKey = false;
			DWORD dwValue = pModifier->dwSpecific;
			const TCHAR *apszButtons[] ={	_T("Dp->"), _T("Dp<-"), _T("Dp\\/"), _T("Dp/\\"), _T("St"), _T("Z"), _T("B"), _T("A"),
									_T("Cb->"), _T("Cb<-"), _T("Cb\\/"), _T("Cb/\\"), _T("R"), _T("L"), NULL, NULL,
									_T("As->"), _T("As<-"), _T("As\\/"), _T("As/\\"), _T("(<Rf>)") };

			int i = sizeof(apszButtons) / sizeof(apszButtons[0]) - 1;

			while( i >= 0 )
			{
				if( apszButtons[i] && ( dwValue & ( 1 << i ) ))
				{
					if( bGotKey )
						lstrcat( szBuffer, _T(" ") );
					else
						bGotKey = true;

					lstrcat( szBuffer, apszButtons[i] );
				}

				i--;
			}
		}
		break;
	case MDT_CONFIG:
		ListView_SetItemText( hListView, iEntry, 1, pszModTypes[3] );
		szBuffer[0] = '\0';
		{
			DWORD dwValue = pModifier->dwSpecific;
			bool bGotKey = false;

			if( dwValue & 0x1 )
			{
				lstrcat( szBuffer, _T("C-") );
				if((( dwValue >> 1 ) & 0x7F ) < PF_AXESETS )
					wsprintf( &szBuffer[lstrlen(szBuffer)], _T("%i"), (( dwValue >> 1 ) & 0x7F ));
				else
					lstrcat( szBuffer, _T("Sw") );
				bGotKey = true;
			}

			if( dwValue & 0x300 )
			{
				if( bGotKey )
					lstrcat( szBuffer, _T(" ") );
				else
					bGotKey = true;

				lstrcat( szBuffer, _T("Ms-") );
				if( dwValue & 0x100 )
					lstrcat( szBuffer, _T("X") );
				if( dwValue & 0x200 )
					lstrcat( szBuffer, _T("Y") );
			}

			if( dwValue & 0x30000 )
			{
				if( bGotKey )
					lstrcat( szBuffer, _T(" ") );
				else
					bGotKey = true;

				lstrcat( szBuffer, _T("Kb-") );
				if( dwValue & 0x10000 )
					lstrcat( szBuffer, _T("X") );
				if( dwValue & 0x20000 )
					lstrcat( szBuffer, _T("Y") );
			}
		}
		break;

	case MDT_NONE:
	default:
		ListView_SetItemText( hListView, iEntry, 1, pszModTypes[0] );
		szBuffer[0] = '\0';
	}
	ListView_SetItemText( hListView, iEntry, 2, szBuffer );
}

BOOL CALLBACK ModifierTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	static BUTTON storage;
	static bool bScanRunning;
	static DWORD dwCounter;
	static HWND hFocus = NULL, hBlocker;
	static int iSelectedMod;
	static BYTE bDisplayedProbs;
	static HWND hModProperties;
	static LPCONTROLLER pcController;

	const UINT iModTypes[] = { IDS_M_NONE, IDS_M_MOVE, IDS_M_MACRO, IDS_M_CONFIG };
	TCHAR pszModTypes[4][16];

	for (int j = 0; j < ARRAYSIZE(iModTypes); j++ )
		LoadString( g_hResourceDLL, iModTypes[j], pszModTypes[j], ARRAYSIZE(pszModTypes[j]) );

	TCHAR szBuffer[40];
	HWND hDlgItem;
	long i;
	BYTE bByte;

	switch(uMsg)
	{
	case WM_INITDIALOG:
		bScanRunning = false;
		hModProperties = NULL;
		bDisplayedProbs = MDT_NONE;
		// List View
		hDlgItem = GetDlgItem( hDlg, IDC_MODIFIERLIST );

		LVCOLUMN lvColumn;

		lvColumn.mask = LVCFMT_CENTER | LVCF_WIDTH | LVCF_TEXT;
		lvColumn.fmt = LVCFMT_LEFT;
		lvColumn.cx = 100;
		LoadString( g_hResourceDLL, IDS_M_ASSIGNED, szBuffer, 40 );
		lvColumn.pszText = szBuffer;
		ListView_InsertColumn( hDlgItem, 0, &lvColumn );

		lvColumn.fmt = LVCFMT_CENTER | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
		lvColumn.cx = 43;
		lvColumn.iSubItem = 1;
		LoadString( g_hResourceDLL, IDS_M_TYPE, szBuffer, 40 );
		lvColumn.pszText = szBuffer;
		ListView_InsertColumn( hDlgItem, 1, &lvColumn );

		lvColumn.cx = 128;
		lvColumn.iSubItem = 2;
		LoadString( g_hResourceDLL, IDS_M_PARAM, szBuffer, 40 );
		lvColumn.pszText = szBuffer;
		ListView_InsertColumn( hDlgItem, 2, &lvColumn );

		ListView_SetExtendedListViewStyle( hDlgItem, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP );
		// ListView End

		// DropDown
		hDlgItem = GetDlgItem( hDlg, IDC_MODTYP );

		i = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)pszModTypes[0] );
		SendMessage( hDlgItem, CB_SETITEMDATA, i, MDT_NONE );

		i = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)pszModTypes[1] );
		SendMessage( hDlgItem, CB_SETITEMDATA, i, MDT_MOVE );

		i = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)pszModTypes[2] );
		SendMessage( hDlgItem, CB_SETITEMDATA, i, MDT_MACRO );

		i = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)pszModTypes[3] );
		SendMessage( hDlgItem, CB_SETITEMDATA, i, MDT_CONFIG );
		// DropDown End

		ZeroMemory(&storage, sizeof(BUTTON));

		ModifierTabProc( hDlg, WM_USER_UPDATE, 0, 0 ); // setting values

		return FALSE; // don't give it focus

	case WM_COMMAND:
		hDlgItem = GetDlgItem( hDlg, LOWORD(wParam) );
		switch( LOWORD(wParam) )
		{
		case IDC_NEWMODIFIER:
			if( pcController->nModifiers < MAX_MODIFIERS )
			{
				EnterCriticalSection(&g_critical);
				pcController->nModifiers++;
				pcController->pModifiers = (MODIFIER*)P_realloc( pcController->pModifiers, sizeof(MODIFIER) * pcController->nModifiers );

				ZeroMemory( &pcController->pModifiers[pcController->nModifiers - 1], sizeof(MODIFIER) );
				iSelectedMod = pcController->nModifiers - 1;
				LeaveCriticalSection(&g_critical);

				ModifierTabProc( hDlg, WM_USER_UPDATE, 0, 1 );
			}
			return TRUE;

		case IDC_KILLMODIFIER:
			hDlgItem = GetDlgItem( hDlg, IDC_MODIFIERLIST );

			if( ListView_GetSelectedCount( hDlgItem ) > 0 )
			{
				EnterCriticalSection(&g_critical);
				for( i = ListView_GetItemCount( hDlgItem ) - 1; i >= 0; i-- )
				{
					if( ListView_GetItemState( hDlgItem, i, LVIS_SELECTED ))
					{
						MoveMemory( &pcController->pModifiers[i], &pcController->pModifiers[i+1], sizeof(MODIFIER) * ( pcController->nModifiers - 1 - i ));
						pcController->nModifiers--;
					}
				}
				pcController->pModifiers = (MODIFIER*)P_realloc( pcController->pModifiers, sizeof(MODIFIER) * pcController->nModifiers );
				LeaveCriticalSection(&g_critical);
				ModifierTabProc( hDlg, WM_USER_UPDATE, 0, 0 );
			}
			return TRUE;

		case IDC_ASSIGNMOD:
			EnterCriticalSection(&g_critical);
			hDlgItem = GetDlgItem( hDlg, IDC_MODIFIERLIST );
			if( bScanRunning )
			{
/*				bScanRunning = false;
				KillTimer( hDlg, TIMER_BUTTON );
				BUTTON btnButton;
				btnButton = storage; // btnButton.dwButton = GetWindowLong( hDlgItem, GWL_USERDATA );
				GetButtonText( btnButton, szBuffer );
				SendMessage( GetDlgItem( hDlg, IDT_ASSIGNMOD ), WM_SETTEXT , 0, (LPARAM)szBuffer ); */ ;
			}
			else if( HIWORD(wParam) == BN_CLICKED && ListView_GetItemCount( hDlgItem ))
			{
				dwCounter = 0;
				if (hFocus == NULL)
					hFocus = SetFocus( NULL );
				hBlocker = MakeOverlay();

				SetTimer( hDlg, TIMER_BUTTON, INTERVAL_BUTTON, NULL );
				bScanRunning = true;
			}
			LeaveCriticalSection(&g_critical);
			return TRUE;

		case IDC_MODTYP:
			if( HIWORD (wParam) == CBN_SELCHANGE )
			{
				ModifierTabProc( hDlg, WM_USER_UPDATE, 1, 1 );
			}
			return TRUE;

		case IDC_APPCHANGES:
			// ModType
			EnterCriticalSection(&g_critical);
			hDlgItem = GetDlgItem( hDlg, IDC_MODTYP );
			i = SendMessage( hDlgItem, CB_GETCURSEL, 0, 0 );
			pcController->pModifiers[iSelectedMod].bModType = (BYTE)SendMessage( hDlgItem, CB_GETITEMDATA, i, 0 );

			// Toggle
			pcController->pModifiers[iSelectedMod].fToggle = ( IsDlgButtonChecked( hDlg, IDC_TOGGLE ) == BST_CHECKED );
			// Status
			pcController->pModifiers[iSelectedMod].fStatus = ( IsDlgButtonChecked( hDlg, IDC_STATE ) == BST_CHECKED );

			// Specific Data
			if( hModProperties )
				SendMessage( hModProperties, WM_USER_READVALUES, (WPARAM)&pcController->pModifiers[iSelectedMod].dwSpecific, 0 );
			else
				pcController->pModifiers[iSelectedMod].dwSpecific = 0;

			// ModButton
			pcController->pModifiers[iSelectedMod].btnButton = storage;
			LeaveCriticalSection(&g_critical);

			ModDescription( GetDlgItem( hDlg, IDC_MODIFIERLIST ), iSelectedMod, &pcController->pModifiers[iSelectedMod] );

			return TRUE;
		case IDC_RESET:
			ModifierTabProc( hDlg, WM_USER_UPDATE, 1, 0 );
			return TRUE;

		case IDC_TOGGLE:
			EnableWindow( GetDlgItem( hDlg, IDC_STATE ), ( IsDlgButtonChecked( hDlg, IDC_TOGGLE ) == BST_CHECKED ) ? TRUE : FALSE );
			return TRUE;
		case IDC_STATE:
			return TRUE;

		default:
			return FALSE;
		}

	case WM_TIMER: // when assigning modifiers, this gets called every 20ms (or value in INTERVAL_BUTTON)
		if( wParam == TIMER_BUTTON && bScanRunning )
		{
			BUTTON newButton;
			i = ScanDevices( &dwCounter, &newButton );
			if( i || dwCounter > 500 )
			{
				bScanRunning = false;
				KillTimer( hDlg, TIMER_BUTTON );

				hDlgItem = GetDlgItem( hDlg, IDC_ASSIGNMOD );

				EnterCriticalSection(&g_critical);
				if( i == SC_SCANESCAPE ) // Got an escape char from keyboard; cancel
					ZeroMemory(&storage, sizeof(BUTTON));
				else if( i == SC_SCANSUCCEED  ) // Got a button or axis
					storage = newButton;
				newButton = storage;
				DestroyWindow( hBlocker );

				LeaveCriticalSection(&g_critical);

				GetButtonText( newButton, szBuffer );
				SendMessage( GetDlgItem( hDlg, IDT_ASSIGNMOD ), WM_SETTEXT , 0, (LPARAM)szBuffer );
				if( hFocus != NULL )
				{
					SetFocus( hFocus );
					hFocus = NULL;
				}
			}
			else
			{
				if(( dwCounter % 50 ) == 0 )
				{
					TCHAR tszText[DEFAULT_BUFFER];

					LoadString( g_hResourceDLL, IDS_C_POLLING, tszText, DEFAULT_BUFFER );
					wsprintf( szBuffer, tszText, 10 - dwCounter / 50 );
					SendMessage( GetDlgItem( hDlg, IDT_ASSIGNMOD ), WM_SETTEXT , 0, (LPARAM)szBuffer );
				}
				++dwCounter;
			}
			return TRUE;
		}
		else
			return FALSE;

	case WM_NOTIFY:
		switch( ((LPNMHDR)lParam)->idFrom )
		{
		case IDC_MODIFIERLIST:
			switch( ((LPNMHDR)lParam)->code )
			{
			case LVN_ITEMCHANGED:
				ModifierTabProc( hDlg, WM_USER_UPDATE, 1, 0 );
				return TRUE;
			default:
				return FALSE;
			}
		default:
			return FALSE;
		}

	case WM_USER_UPDATE:
		pcController = &g_ivConfig->Controllers[g_ivConfig->ChosenTab];
		hDlgItem = GetDlgItem( hDlg, IDC_MODIFIERLIST );

		if( wParam == 0 )
		{
			// Update Modifier List
			ListView_DeleteAllItems( hDlgItem );
			if( lParam == 0 )
				iSelectedMod = -1;

			LVITEM lvItem;
			lvItem.mask = LVIF_TEXT | LVIF_PARAM;
			lvItem.iItem = 0;
			lvItem.iSubItem = 0;
			lvItem.pszText = _T("");

			ListView_SetItemCount( hDlgItem, pcController->nModifiers );
			for( lvItem.lParam = 0; lvItem.lParam < pcController->nModifiers; ++lvItem.lParam )
			{
				lvItem.iItem = lvItem.lParam;
				i = ListView_InsertItem( hDlgItem, &lvItem );

				ModDescription( hDlgItem, i, &pcController->pModifiers[lvItem.lParam] );
			}
			if( iSelectedMod >= 0 && iSelectedMod < ListView_GetItemCount( hDlgItem ))

				ListView_SetItemState( hDlgItem, iSelectedMod, LVIS_SELECTED, LVIS_SELECTED )
			else
				iSelectedMod = -1;

		}
		else
		{
			// Get selected Modifier
			iSelectedMod = -1;
			if( ListView_GetSelectedCount( hDlgItem ) > 0 )
			{
				for( i = ListView_GetItemCount( hDlgItem ) - 1; i >= 0; i-- )
				{
					if( ListView_GetItemState( hDlgItem, i, LVIS_SELECTED ))
					{
						iSelectedMod = i;
						i = -1;
					}
				}
			}
		}

		hDlgItem = GetDlgItem( hDlg, IDC_MODTYP );

		if( lParam == 0 )
		{
			if( iSelectedMod >= 0 )
			{	// a mod is selected
				EnableWindow( GetDlgItem( hDlg, IDC_ASSIGNMOD ), TRUE );
				EnableWindow( GetDlgItem( hDlg, IDT_ASSIGNMOD ), TRUE );
				EnableWindow( GetDlgItem( hDlg, IDC_TOGGLE ), TRUE );
				EnableWindow( hDlgItem, TRUE );
				EnableWindow( GetDlgItem( hDlg, IDT_MODTYP ), TRUE );
				EnableWindow( GetDlgItem( hDlg, IDC_APPCHANGES ), TRUE );
				EnableWindow( GetDlgItem( hDlg, IDC_RESET ), TRUE );

				for( i = SendMessage( hDlgItem, CB_GETCOUNT, 0, 0 )-1; i >= 0; i-- )
				{ // looking which Mod-Typ
					bByte = (BYTE)SendMessage( hDlgItem, CB_GETITEMDATA, i, 0 );
					if( pcController->pModifiers[iSelectedMod].bModType == bByte )
					{
						SendMessage( hDlgItem, CB_SETCURSEL, i, 0 );
						i = -10;
					}
				}
				if( i > -5 )
					SendMessage( hDlgItem, CB_SETCURSEL, 0, 0 );

				CheckDlgButton( hDlg, IDC_TOGGLE, pcController->pModifiers[iSelectedMod].fToggle ? BST_CHECKED : BST_UNCHECKED );
				EnableWindow( GetDlgItem( hDlg, IDC_STATE ), pcController->pModifiers[iSelectedMod].fToggle ? TRUE : FALSE );
				CheckDlgButton( hDlg, IDC_STATE, pcController->pModifiers[iSelectedMod].fStatus ? BST_CHECKED : BST_UNCHECKED );


				storage = pcController->pModifiers[iSelectedMod].btnButton;
				GetButtonText( pcController->pModifiers[iSelectedMod].btnButton, szBuffer );
				SendMessage( GetDlgItem( hDlg, IDT_ASSIGNMOD ), WM_SETTEXT , 0, (LPARAM)szBuffer );
			}
			else
			{
				EnableWindow( GetDlgItem( hDlg, IDC_ASSIGNMOD ), FALSE );
				EnterCriticalSection(&g_critical);
				ZeroMemory(&storage, sizeof(BUTTON));
				LeaveCriticalSection(&g_critical);
				SendMessage( GetDlgItem( hDlg, IDT_ASSIGNMOD ), WM_SETTEXT , 0, (LPARAM) _T("") );
				EnableWindow( GetDlgItem( hDlg, IDT_ASSIGNMOD ), FALSE );

				SendMessage( hDlgItem, CB_SETCURSEL, 0, 0 );
				EnableWindow( hDlgItem, FALSE );
				EnableWindow( GetDlgItem( hDlg, IDT_MODTYP ), FALSE );

				EnableWindow( GetDlgItem( hDlg, IDC_TOGGLE ), FALSE );
				CheckDlgButton( hDlg, IDC_TOGGLE , BST_UNCHECKED );
				EnableWindow( GetDlgItem( hDlg, IDC_STATE ), FALSE );
				CheckDlgButton( hDlg, IDC_STATE , BST_UNCHECKED );
				EnableWindow( GetDlgItem( hDlg, IDC_APPCHANGES ), FALSE );
				EnableWindow( GetDlgItem( hDlg, IDC_RESET ), FALSE );
			}
		}

		i = SendMessage( hDlgItem, CB_GETCURSEL, 0, 0 );
		bByte = (BYTE)SendMessage( hDlgItem, CB_GETITEMDATA, i, 0 );


		if(( bByte == bDisplayedProbs ) && hModProperties )
			SendMessage( hModProperties, WM_USER_UPDATE, pcController->pModifiers[iSelectedMod].bModType, pcController->pModifiers[iSelectedMod].dwSpecific );
		else
		{
			if( hModProperties )
				DestroyWindow( hModProperties );

			switch( bByte )
			{
			case MDT_MOVE:
				hModProperties = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_MOD_MOVE ), hDlg, MoveModifierDialog );
				break;
			case MDT_MACRO:
				hModProperties = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_MOD_MACRO ), hDlg, MacroModifierDialog );
				break;
			case MDT_CONFIG:
				hModProperties = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_MOD_CONFIG ), hDlg, ConfigModifierDialog );
				break;
			case MDT_NONE:
			default:
				hModProperties = NULL;
			}

			hDlgItem = GetDlgItem( hDlg, IDC_PROPWINDOW );

			if( hModProperties )
			{
				RECT rectProp, rectMain; // need to know the position of the calling tab window relative to its parent

				GetWindowRect( hDlg, &rectMain );
				GetWindowRect( hDlgItem, &rectProp );

				MoveWindow( hModProperties, rectProp.left - rectMain.left, rectProp.top - rectMain.top, rectProp.right - rectProp.left, rectProp.bottom - rectProp.top, FALSE );
				SendMessage( hModProperties, WM_USER_UPDATE, pcController->pModifiers[iSelectedMod].bModType, pcController->pModifiers[iSelectedMod].dwSpecific );
				ShowWindow( hModProperties, SW_SHOW );
			}
			bDisplayedProbs = bByte;
		}
		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
}

BOOL CALLBACK ControllerPakTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	static HWND hPakWindow;
	static bool bAdaptoidInList;
	static BYTE bCurrentPak;
	HWND hDlgItem;
	long i,j;
	BYTE bByte;
	TCHAR tszMsg[DEFAULT_BUFFER];

	switch(uMsg)
	{
	case WM_INITDIALOG:
		hPakWindow = NULL;
		bAdaptoidInList = false;
		bCurrentPak = (BYTE)(-1);

		// DropDown-List;
		hDlgItem = GetDlgItem( hDlg, IDC_PAKTYPE );
		LoadString( g_hResourceDLL, IDS_P_NONE, tszMsg, DEFAULT_BUFFER );
		i = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)tszMsg );
		SendMessage( hDlgItem, CB_SETITEMDATA, i, PAK_NONE );

		LoadString( g_hResourceDLL, IDS_P_MEMPAK, tszMsg, DEFAULT_BUFFER );
		i = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)tszMsg );
		SendMessage( hDlgItem, CB_SETITEMDATA, i, PAK_MEM );

		LoadString( g_hResourceDLL, IDS_P_RUMBLEPAK, tszMsg, DEFAULT_BUFFER );
		i = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)tszMsg );
		SendMessage( hDlgItem, CB_SETITEMDATA, i, PAK_RUMBLE );

#ifdef V_TRANSFERPAK
		LoadString( g_hResourceDLL, IDS_P_TRANSFERPAK, tszMsg, DEFAULT_BUFFER );
		i = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)tszMsg );
		SendMessage( hDlgItem, CB_SETITEMDATA, i, PAK_TRANSFER );
#pragma message( "Transferpak enabled in Interface" )
#endif
#ifdef V_VOICEPAK
		LoadString( g_hResourceDLL, IDS_P_VOICEPAK, tszMsg, DEFAULT_BUFFER );
		i = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)tszMsg );
		SendMessage( hDlgItem, CB_SETITEMDATA, i, PAK_VOICE );
#pragma message( "Voicepak enabled in Interface" )
#endif

		ControllerPakTabProc( hDlg, WM_USER_UPDATE, 0, 0 );
		return FALSE; // don't give it focus

	case WM_COMMAND:
		hDlgItem = GetDlgItem( hDlg, LOWORD(wParam) );

		switch( LOWORD(wParam) )
		{
		case IDC_PAKTYPE:
			if( HIWORD (wParam) == CBN_SELCHANGE )
			{
				i = SendMessage( hDlgItem, CB_GETCURSEL, 0, 0 );
				g_ivConfig->Controllers[g_ivConfig->ChosenTab].PakType = SendMessage( hDlgItem, CB_GETITEMDATA, i, 0 );
				ControllerPakTabProc( hDlg, WM_USER_UPDATE, 0, 0 );
			}
			return TRUE;
		case IDC_RAWMODE:
			g_ivConfig->Controllers[g_ivConfig->ChosenTab].fRawData = ( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED );
			ControllerPakTabProc( hDlg, WM_USER_UPDATE, 0, 0 );
			return TRUE;

		default:
			return FALSE;
		}

	case WM_USER_UPDATE:
		CheckDlgButton( hDlg, IDC_RAWMODE, g_ivConfig->Controllers[g_ivConfig->ChosenTab].fRawData ? BST_CHECKED : BST_UNCHECKED );
		//Set Dropdownlist
		hDlgItem = GetDlgItem( hDlg, IDC_PAKTYPE );

		if(	g_ivConfig->Controllers[g_ivConfig->ChosenTab].fRawData &&
			!lstrcmp( g_ivConfig->FFDevices[g_ivConfig->ChosenTab].szProductName, _T(STRING_ADAPTOID) ) &&
			!bAdaptoidInList )
		{
			// add Adaptoid Pak to list
			LoadString( g_hResourceDLL, IDS_P_ADAPTOIDPAK, tszMsg, DEFAULT_BUFFER );
			i = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)tszMsg );
			SendMessage( hDlgItem, CB_SETITEMDATA, i, PAK_ADAPTOID );
			bAdaptoidInList = true;
		}

		if(	bAdaptoidInList &&
			( lstrcmp( g_ivConfig->FFDevices[g_ivConfig->ChosenTab].szProductName, _T(STRING_ADAPTOID) ) ||
			  !g_ivConfig->Controllers[g_ivConfig->ChosenTab].fRawData ))
		{
			// remove Adaptoid Pak from list
			i = SendMessage( hDlgItem, CB_GETCOUNT, 0, 0 ) - 1;
			while(( i >= 0 ))
			{
				j = SendMessage( hDlgItem, CB_GETITEMDATA, i, 0 );
				if( j == PAK_ADAPTOID )
				{
					SendMessage( hDlgItem, CB_DELETESTRING, i, 0 );
					i = -1;
				}
				i--;
			}
			bAdaptoidInList = false;
		}


		i = SendMessage( hDlgItem, CB_GETCOUNT, 0, 0 ) - 1;

		while(( i >= 0 ))
		{
			bByte = (BYTE)SendMessage( hDlgItem, CB_GETITEMDATA, i, 0 );
			if( g_ivConfig->Controllers[g_ivConfig->ChosenTab].PakType == bByte )
			{
				SendMessage( hDlgItem, CB_SETCURSEL, i, 0 );
				i = -10;
			}
			else
				i--;
		}
		if( i != -10 )
			SendMessage( hDlgItem, CB_SETCURSEL, 0, 0 );

		// Update Pak-Display
		if( g_ivConfig->Controllers[g_ivConfig->ChosenTab].fRawData )
		{
			i = SendMessage( GetDlgItem( hDlg, IDC_PAKTYPE ), CB_GETCURSEL, 0, 0 );
			bByte = (BYTE)SendMessage( hDlgItem, CB_GETITEMDATA, i, 0 );
		}
		else
			bByte = PAK_NONRAW;


		if( bByte == bCurrentPak && hPakWindow )
			SendMessage( hPakWindow, WM_USER_UPDATE, 0, 0 );
		else
		{
			if( hPakWindow )
				DestroyWindow( hPakWindow );

			hDlgItem = GetDlgItem( hDlg, IDC_PAKAREA );

			switch( bByte )
			{
			case PAK_MEM:
				hPakWindow = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_PAK_MEMPAK ), hDlg, MemPakProc );
				break;
			case PAK_RUMBLE:
				hPakWindow = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_PAK_RUMBLE ), hDlg, RumblePakProc );
				break;
			case PAK_TRANSFER:
				hPakWindow = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_PAK_TRANSFER ), hDlg, TransferPakProc );
				break;
			case PAK_ADAPTOID:
				hPakWindow = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_PAK_ADAPTOID ), hDlg, PakProc );
				break;

			case PAK_VOICE:
			case PAK_NONRAW:
			case PAK_NONE:
			default:
				hPakWindow = CreateDialog( g_hResourceDLL, MAKEINTRESOURCE( IDD_PAK_TEXT ), hDlg, PakProc );

			}
			if( hPakWindow )
			{
				RECT rectProp, rectMain; // need to know the position of the calling tab window relative to its parent

				GetWindowRect( hDlg, &rectMain );
				GetWindowRect( hDlgItem, &rectProp );

				MoveWindow( hPakWindow, rectProp.left - rectMain.left, rectProp.top - rectMain.top, rectProp.right - rectProp.left, rectProp.bottom - rectProp.top, FALSE );
				ShowWindow( hPakWindow, SW_SHOW );
			}
			bCurrentPak = bByte;
		}
		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
}

BOOL CALLBACK PakProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	TCHAR tszLineOne[DEFAULT_BUFFER] = _T(""), tszLineTwo[DEFAULT_BUFFER] = _T("");
	TCHAR *pszDescription[2] = { tszLineOne, tszLineTwo };
	bool bRAW;

	switch(uMsg)
	{
	case WM_INITDIALOG:
		PakProc( hDlg, WM_USER_UPDATE, 0, 0 );
		return FALSE; // don't give it focus

	case WM_USER_UPDATE:
		bRAW = g_ivConfig->Controllers[g_ivConfig->ChosenTab].fRawData ? 1 : 0;

		switch( g_ivConfig->Controllers[g_ivConfig->ChosenTab].PakType )
		{
		case PAK_MEM:
			LoadString( g_hResourceDLL, IDS_P_MEM_NORAW, pszDescription[0], DEFAULT_BUFFER );
			break;
		case PAK_RUMBLE:
			LoadString( g_hResourceDLL, IDS_P_RUMBLE_NORAW, pszDescription[0], DEFAULT_BUFFER );
			break;
		case PAK_TRANSFER:
			LoadString( g_hResourceDLL, IDS_P_TRANSFER_NORAW, pszDescription[0], DEFAULT_BUFFER );
			break;
		case PAK_VOICE:
			LoadString( g_hResourceDLL, IDS_P_VOICE_RAW, pszDescription[1], DEFAULT_BUFFER );
			pszDescription[0] = pszDescription[1];
			break;
		case PAK_ADAPTOID:
			LoadString( g_hResourceDLL, IDS_P_ADAPTOID_NORAW, pszDescription[0], DEFAULT_BUFFER );
			break;
		case PAK_NONE:
		default:
			LoadString( g_hResourceDLL, IDS_P_NONE_RAWNORAW, pszDescription[1], DEFAULT_BUFFER );
			pszDescription[0] = pszDescription[1];
		}
		SendMessage( GetDlgItem( hDlg, IDT_PAKDESC ), WM_SETTEXT, 0, (LPARAM)pszDescription[bRAW] );

		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
}

BOOL CALLBACK MemPakProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	static TCHAR *pszMemPakFile;
	static WORD wMemPakState;
	static int iSelectedNote;
	TCHAR szBuffer[MAX_PATH+1], szTemp[MAX_PATH+1];

	HWND hDlgItem;
	long i,j;

	switch(uMsg)
	{
	case WM_INITDIALOG:
		if( g_bRunning )
		{
			EnableWindow( GetDlgItem( hDlg, IDC_CHGDIR ), FALSE );
			LoadString( g_hResourceDLL, IDS_P_MEM_NOCHANGE, szBuffer, MAX_PATH + 1 );
			SendMessage( GetDlgItem( hDlg, IDC_CHGDIR ), WM_SETTEXT, 0, (LPARAM)szBuffer );
		}
		iSelectedNote = -1;
		// Browser
		hDlgItem = GetDlgItem( hDlg, IDC_MEMPAKBROWSER );

		LVCOLUMN lvColumn;

		lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
		lvColumn.fmt = LVCFMT_LEFT;
		lvColumn.cx = 147;
		LoadString( g_hResourceDLL, IDS_P_MEM_NAME, szBuffer, MAX_PATH + 1 );
		lvColumn.pszText = szBuffer;
		ListView_InsertColumn( hDlgItem, 0, &lvColumn );

		lvColumn.fmt = LVCFMT_CENTER | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;

		lvColumn.cx = 70;
		lvColumn.iSubItem = 1;
		LoadString( g_hResourceDLL, IDS_P_MEM_REGION, szBuffer, MAX_PATH + 1 );
		lvColumn.pszText = szBuffer;
		ListView_InsertColumn( hDlgItem, 1, &lvColumn );

		lvColumn.cx = 50;
		lvColumn.iSubItem = 2;
		LoadString( g_hResourceDLL, IDS_P_MEM_BLOCKS, szBuffer, MAX_PATH + 1 );
		lvColumn.pszText = szBuffer;
		ListView_InsertColumn( hDlgItem, 2, &lvColumn );

		ListView_SetExtendedListViewStyle( hDlgItem, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP );


		MemPakProc( hDlg, WM_USER_UPDATE, 0, 0 ); // setting values
		return FALSE; // don't give it focus

	case WM_COMMAND:
		hDlgItem = GetDlgItem( hDlg, LOWORD(wParam) );
		switch( LOWORD(wParam) )
		{
			TCHAR tszTitle[DEFAULT_BUFFER], tszText[DEFAULT_BUFFER];

		case IDC_MEMPAKLIST:
			if( HIWORD(wParam) == LBN_SELCHANGE )
			{
				i = SendMessage( hDlgItem, LB_GETCURSEL, 0, 0 );
				SendMessage( hDlgItem, LB_GETTEXT, i, (LPARAM)pszMemPakFile );
				MemPakProc( hDlg, WM_USER_UPDATE, 1, 0 );
			}
			return TRUE;
		case IDC_BROWSE:
			lstrcpyn( szBuffer, pszMemPakFile, ARRAYSIZE(szBuffer)  );
			if( BrowseFile( hDlg, szBuffer, BF_MEMPAK, BF_LOAD ))
			{
				if( !CheckFileExists( szBuffer ) )
				{
					BYTE aMemPak[PAK_MEM_SIZE];

					FormatMemPak( aMemPak );
					WriteMemPakFile( szBuffer, aMemPak, true );
				}
				if( CheckFileExists( szBuffer ) )
				{
					lstrcpyn( pszMemPakFile, szBuffer, MAX_PATH );
					MemPakProc( hDlg, WM_USER_UPDATE, 0, 0 );
				}
			}
			return TRUE;
		case IDC_CHGDIR:
			if( DialogBox( g_hResourceDLL, MAKEINTRESOURCE( IDD_FOLDERS ), hDlg, FoldersDialogProc ) == TRUE )
				MemPakProc( hDlg, WM_USER_UPDATE, 0, 0 );
			return TRUE;

		case IDC_DELMEMPAK:
			LoadString( g_hResourceDLL, IDS_DLG_DELETEPAK, tszText, DEFAULT_BUFFER );
			LoadString( g_hResourceDLL, IDS_DLG_MSG_TITLE, tszTitle, DEFAULT_BUFFER );
			if( MessageBox( hDlg, tszText, tszTitle, MB_OKCANCEL | MB_ICONQUESTION ) == IDOK )
			{
				GetAbsoluteFileName( szBuffer, pszMemPakFile, DIRECTORY_MEMPAK );
				DeleteFile( szBuffer );
				pszMemPakFile[0] = '\0';
				MemPakProc( hDlg, WM_USER_UPDATE, 0, 0 );
			}
			return TRUE;

		case IDC_FORMATMEMPAK:
			LoadString( g_hResourceDLL, IDS_DLG_FORMATPAK, tszText, DEFAULT_BUFFER );
			LoadString( g_hResourceDLL, IDS_DLG_MSG_TITLE, tszTitle, DEFAULT_BUFFER );
			if( MessageBox( hDlg, tszText, tszTitle, MB_OKCANCEL | MB_ICONQUESTION ) == IDOK )
			{
				GetAbsoluteFileName( szBuffer, pszMemPakFile, DIRECTORY_MEMPAK );
				BYTE aMemPak[PAK_MEM_SIZE];

				FormatMemPak( aMemPak );
				WriteMemPakFile( szBuffer, aMemPak, false );
				MemPakProc( hDlg, WM_USER_UPDATE, 1, 0 );
			}
			return TRUE;
		case IDC_SAVENOTE:
			{
				BYTE aMemPak[PAK_MEM_SIZE];
				if( g_bRunning && ( HIWORD(wMemPakState) == MPAK_INUSE )
					&& g_pcControllers[g_ivConfig->ChosenTab].pPakData
					&& ( *(BYTE*)g_pcControllers[g_ivConfig->ChosenTab].pPakData == PAK_MEM ))
				{
					EnterCriticalSection( &g_critical );
					CopyMemory( aMemPak, ((MEMPAK*)g_pcControllers[g_ivConfig->ChosenTab].pPakData)->aMemPakData, PAK_MEM_SIZE );
					LeaveCriticalSection( &g_critical );
				}
				else
				{
					GetAbsoluteFileName( szBuffer, pszMemPakFile, DIRECTORY_MEMPAK );
					ReadMemPakFile( szBuffer, aMemPak, false );
				}

				int iChars = TranslateNotes( &aMemPak[0x300 + iSelectedNote * 32 + 0x10], szBuffer, 16 );

				if( TranslateNotes( &aMemPak[0x300 + iSelectedNote * 32 + 0x0C], &szBuffer[iChars + 1], 1 ) )
					szBuffer[iChars] = _T('_');

				if( BrowseFile( hDlg, szBuffer, BF_NOTE, BF_SAVE ))
				{
					TCHAR szAbsoluteMemPak[MAX_PATH+1];
					GetAbsoluteFileName( szAbsoluteMemPak, pszMemPakFile, DIRECTORY_MEMPAK );

					SaveNoteFileA( aMemPak, iSelectedNote, szBuffer );
				}
			}
			return TRUE;
		case IDC_INSERTNOTE:
			{
				GetAbsoluteFileName( szBuffer, pszMemPakFile, DIRECTORY_MEMPAK );
				BYTE aMemPak[PAK_MEM_SIZE];
				ReadMemPakFile( szBuffer, aMemPak, false );
				szBuffer[0] = '\0';

				if( BrowseFile( hDlg, szBuffer, BF_NOTE, BF_LOAD ))
				{
					if( InsertNoteFile( aMemPak, szBuffer ) )
					{
						GetAbsoluteFileName( szBuffer, pszMemPakFile, DIRECTORY_MEMPAK );
						WriteMemPakFile( szBuffer, aMemPak, false );
						MemPakProc( hDlg, WM_USER_UPDATE, 1, 0 );
					}
				}
			}
			return TRUE;
		case IDC_DELETENOTE:
			LoadString( g_hResourceDLL, IDS_DLG_DELETENOTE, tszText, DEFAULT_BUFFER );
			LoadString( g_hResourceDLL, IDS_DLG_MSG_TITLE, tszTitle, DEFAULT_BUFFER );
			if( MessageBox( hDlg, tszText, tszTitle, MB_OKCANCEL | MB_ICONQUESTION ) == IDOK )
			{
				GetAbsoluteFileName( szBuffer, pszMemPakFile, DIRECTORY_MEMPAK );
				BYTE aMemPak[PAK_MEM_SIZE];
				ReadMemPakFile( szBuffer, aMemPak, false );
				if( RemoveNote( aMemPak, iSelectedNote ) )
				{
					WriteMemPakFile( szBuffer, aMemPak, false );
					MemPakProc( hDlg, WM_USER_UPDATE, 1, 0 );
				}
			}
			return TRUE;
		default:
			return FALSE;
		}

	case WM_NOTIFY:
		switch( ((LPNMHDR)lParam)->idFrom )
		{
		case IDC_MEMPAKBROWSER:
			switch( ((LPNMHDR)lParam)->code )
			{
			case LVN_ITEMCHANGED:
				if( ((LPNMLISTVIEW)lParam)->uNewState & LVIS_SELECTED )
				{
					iSelectedNote = ((LPNMLISTVIEW)lParam)->lParam;
					if( HIBYTE(wMemPakState) & MPAK_READABLE )
						EnableWindow( GetDlgItem( hDlg, IDC_SAVENOTE ), TRUE );
					if( HIBYTE(wMemPakState) & MPAK_WRITEABLE )
						EnableWindow( GetDlgItem( hDlg, IDC_DELETENOTE ), TRUE );
				}
				else
				{
					iSelectedNote = -1;
					EnableWindow( GetDlgItem( hDlg, IDC_SAVENOTE ), FALSE );
					EnableWindow( GetDlgItem( hDlg, IDC_DELETENOTE ), FALSE );
				}
				return TRUE;
			default:
				return FALSE;
			}
		default:
			return FALSE;
		}

	case WM_USER_UPDATE:
		pszMemPakFile = g_ivConfig->Controllers[g_ivConfig->ChosenTab].szMempakFile;
		iSelectedNote = -1;

		// Mempak List Window
		if( wParam == 0 && lParam == 0 )
		{
			hDlgItem = GetDlgItem( hDlg, IDC_MEMPAKLIST );
			SendMessage( hDlgItem, LB_RESETCONTENT, 0, 0 );
			SendFilestoList( hDlgItem, FILIST_MEM );

			if( pszMemPakFile[1] == ':' || ( pszMemPakFile[1] == '\\' && pszMemPakFile[0] == '\\' ))
			{
				GetDirectory( szBuffer, DIRECTORY_MEMPAK );
				i = lstrlen( szBuffer );
				if( !_tcsncmp( szBuffer, pszMemPakFile, i ))
				{
					lstrcpyn( szBuffer, &pszMemPakFile[i], MAX_PATH );
					lstrcpyn( pszMemPakFile, szBuffer, MAX_PATH );
				}

			}

			j = -1;
			if( !( pszMemPakFile[1] == ':' || ( pszMemPakFile[1] == '\\' && pszMemPakFile[0] == '\\' )) )
			{
				i = SendMessage( hDlgItem, LB_FINDSTRINGEXACT, (WPARAM)(-1), (LPARAM)pszMemPakFile );
				if( i != LB_ERR )
					j = i;
			}
			SendMessage( hDlgItem, LB_SETCURSEL, j, 0 );
		}
		// Mempak List Window End

		// MamPak Full Path+Name
		GetAbsoluteFileName( szBuffer, pszMemPakFile, DIRECTORY_MEMPAK );

		// MemPak Browser
		ListView_DeleteAllItems( GetDlgItem( hDlg, IDC_MEMPAKBROWSER ));
		if(	(!lstrcmpi( &szBuffer[lstrlen(szBuffer)-4], _T(".mpk") ))
			|| (!lstrcmpi( &szBuffer[lstrlen(szBuffer)-4], _T(".n64") )))
		{
			BYTE aMemPakHeader[0x500];

			bool bMemPakUsed = false;
			// first, if we're running emulation we need to make sure we haven't selected a file that's currently in use 
			if (g_bRunning)
			{
				TCHAR szMemPakFile[MAX_PATH+1];
				for( i = 0; i < 4; ++i )
				{
					if( g_pcControllers[i].pPakData && ( *(BYTE*)g_pcControllers[i].pPakData == PAK_MEM ) && !((LPMEMPAK)g_pcControllers[i].pPakData)->fReadonly )
					{
						GetAbsoluteFileName( szMemPakFile, g_pcControllers[i].szMempakFile, DIRECTORY_MEMPAK );
						if( !lstrcmp( szMemPakFile, szBuffer ))
						{
							// grab the file info from memory instead of the file... but keep in mind we can't do anything dangerous with it
							EnterCriticalSection( &g_critical );
							wMemPakState = ShowMemPakContent( ((MEMPAK*)g_pcControllers[i].pPakData)->aMemPakData, GetDlgItem( hDlg, IDC_MEMPAKBROWSER ));
							LeaveCriticalSection( &g_critical );
							if (HIBYTE(wMemPakState) == MPAK_OK)
							{
								LoadString( g_hResourceDLL, IDS_P_MEM_INUSE, szTemp, MAX_PATH + 1 );
								wsprintf( szBuffer, szTemp, LOBYTE(wMemPakState) );
								wMemPakState = MAKEWORD( MPAK_READABLE, MPAK_INUSE );
							}
							bMemPakUsed = true;
						}
					}
				}
			}

			if( !bMemPakUsed )
			{
				HANDLE hFile;
				hFile = CreateFile( szBuffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
				if ( hFile != INVALID_HANDLE_VALUE )
				{
					DWORD dwFileSize = GetFileSize( hFile, NULL );
					DWORD dwCorrectFileSize = PAK_MEM_SIZE;

					TCHAR *pcPoint = _tcsrchr( szBuffer, _T('.') );
					if( !lstrcmpi( pcPoint, _T(".n64") ) )
					{
						SetFilePointer( hFile, PAK_MEM_DEXOFFSET, NULL, FILE_BEGIN );
						dwCorrectFileSize += PAK_MEM_DEXOFFSET;
					}
					else
						SetFilePointer( hFile, 0L, NULL, FILE_BEGIN );

					if( dwFileSize > ( dwCorrectFileSize - 0x7500 ))
					{
						DWORD dwBytesRead;

						ReadFile( hFile, aMemPakHeader, 0x500, &dwBytesRead, NULL);
						ListView_DeleteAllItems( GetDlgItem( hDlg, IDC_MEMPAKBROWSER ));
						wMemPakState = ShowMemPakContent( aMemPakHeader, GetDlgItem( hDlg, IDC_MEMPAKBROWSER ));
					}
					else
						wMemPakState = MAKEWORD( 0, MPAK_DAMAGED );

					if( HIBYTE( wMemPakState ) == MPAK_OK &&
						dwFileSize != dwCorrectFileSize )
						wMemPakState = MAKEWORD( LOBYTE( wMemPakState ), MPAK_WRONGSIZE );

					CloseHandle( hFile );
				}
				else
					wMemPakState = MAKEWORD( 0, MPAK_ERROR );
			}
		}
		else
			wMemPakState = MAKEWORD( 0, MPAK_NOSELECTION );

		switch( HIBYTE(wMemPakState) )
		{
		case MPAK_OK:
			LoadString( g_hResourceDLL, IDS_P_MEM_BLOCKSFREE, szTemp, MAX_PATH + 1 );
			wsprintf( szBuffer, szTemp, LOBYTE(wMemPakState) );
			break;
		case MPAK_INUSE:
			// text field already set
			break;
		case MPAK_WRONGSIZE:
			LoadString( g_hResourceDLL, IDS_P_MEM_WRONGSIZE, szTemp, MAX_PATH + 1 );
			wsprintf( szBuffer, szTemp, LOBYTE(wMemPakState) );
			break;
		case MPAK_NOSELECTION:
			LoadString( g_hResourceDLL, IDS_P_MEM_NONESELECTED, szBuffer, MAX_PATH + 1 );
			break;
		case MPAK_DAMAGED:
			LoadString( g_hResourceDLL, IDS_P_MEM_DAMAGED, szBuffer, MAX_PATH + 1 );
			break;

		case MPAK_ERROR:
		default:
			{
				LoadString( g_hResourceDLL, IDS_P_MEM_ERROR, szTemp, MAX_PATH + 1 );
				DWORD dwError = GetLastError();
				DWORD dwLength = wsprintf( szBuffer, szTemp, dwError );
				FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError , 0, &szBuffer[dwLength], ARRAYSIZE(szBuffer) - dwLength - 1, NULL );
			}
			break;
		}
		SendMessage( GetDlgItem( hDlg, IDT_MEMPAKSTATE ), WM_SETTEXT, 0, (LPARAM)szBuffer );

		if( HIBYTE(wMemPakState) & MPAK_FORMATABLE )
		{
			EnableWindow( GetDlgItem( hDlg, IDC_FORMATMEMPAK ), TRUE );
			EnableWindow( GetDlgItem( hDlg, IDC_DELMEMPAK ), TRUE );
		}
		else
		{
			EnableWindow( GetDlgItem( hDlg, IDC_FORMATMEMPAK ), FALSE );
			EnableWindow( GetDlgItem( hDlg, IDC_DELMEMPAK ), FALSE );
		}

		if( HIBYTE(wMemPakState) & MPAK_WRITEABLE )
			EnableWindow( GetDlgItem( hDlg, IDC_INSERTNOTE ), TRUE );
		else
			EnableWindow( GetDlgItem( hDlg, IDC_INSERTNOTE ), FALSE );

		EnableWindow( GetDlgItem( hDlg, IDC_SAVENOTE ), FALSE );
		EnableWindow( GetDlgItem( hDlg, IDC_DELETENOTE ), FALSE );

		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
}

BOOL CALLBACK RumblePakProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	static BOOL bNewRumbleTyp = true;
	IFDEVICE *pifDevice = &g_ivConfig->FFDevices[g_ivConfig->ChosenTab];
	CONTROLLER *pcController = &g_ivConfig->Controllers[g_ivConfig->ChosenTab];
	TCHAR szBuffer[DEFAULT_BUFFER], szTemp[DEFAULT_BUFFER];
	HWND hDlgItem;
	long i = 0,j;

	switch(uMsg)
	{
	case WM_INITDIALOG:
		hDlgItem = GetDlgItem( hDlg, IDC_RUMBLESTRENGTH );

		SendMessage( hDlgItem, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG( 0, 100 ));
		SendMessage( hDlgItem, TBM_SETTICFREQ, (WPARAM) 10, 0 );
		SendMessage( hDlgItem, TBM_SETPAGESIZE, (WPARAM) 0, 1 );

		RumblePakProc( hDlg, WM_USER_UPDATE, 0, 0 );
		return FALSE; // don't give it focus

	case WM_COMMAND:
		hDlgItem = GetDlgItem( hDlg, LOWORD(wParam) );

		switch( LOWORD(wParam) )
		{
		case IDC_CTRDEVICE:
			if( HIWORD(wParam) == CBN_SELCHANGE )
			{
				i = SendMessage( hDlgItem, CB_GETCURSEL, 0, 0 );
				j = SendMessage( hDlgItem, CB_GETITEMDATA, i, 0 );

				EnterCriticalSection(&g_critical);
				if( j == -1 )
				{
					pifDevice->bProductCounter = 0;
					pifDevice->szProductName[0] = '\0';
				}
				else
				{
					pifDevice->bProductCounter = g_devList[j].bProductCounter;
					lstrcpyn( pifDevice->szProductName, g_devList[j].szProductName, sizeof(pifDevice->szProductName) / sizeof(TCHAR) );
				}
				LeaveCriticalSection(&g_critical);

				RumblePakProc( hDlg, WM_USER_UPDATE, 0, 0 ); // en/disabling RumbleOptions
			}
			return TRUE;

		// the following three cases use fallthrough assignment
		case IDC_RUMBLE1:
			if( LOWORD(wParam) == IDC_RUMBLE1 )
				i = RUMBLE_EFF1;
		case IDC_RUMBLE2:
			if( LOWORD(wParam) == IDC_RUMBLE2 )
				i = RUMBLE_EFF2;
		case IDC_RUMBLE3:
			if( LOWORD(wParam) == IDC_RUMBLE3 )
				i = RUMBLE_EFF3;
			if(( HIWORD(wParam) == BN_CLICKED ) &&
				( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED ))
			{
				pcController->bRumbleTyp = (BYTE)i;
				bNewRumbleTyp = true;
			}
			return TRUE;

		case IDC_VISUALRUMBLE:
			pcController->fVisualRumble = ( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED );
			return TRUE;

		case IDC_RUMBLETEST:
			if ( !g_pConfigEffect || bNewRumbleTyp )
			{
				EnterCriticalSection(&g_critical);
				ReleaseEffect( g_pConfigEffect );
				if ( !CreateEffectHandle(g_hMainDialog, g_pConfigDevice, g_pConfigEffect, pcController->bRumbleTyp, pcController->bRumbleStrength) )
				{
					DebugWriteA("Interface: CreateEffectHandle failed\n");
				}
				bNewRumbleTyp = false;
				LeaveCriticalSection(&g_critical);
			}
			g_pConfigDevice->Acquire();
			{
				HRESULT hRslt = g_pConfigEffect->Start(1, 0);
				if (hRslt != DI_OK )
				{
					switch (hRslt)
					{
					case DIERR_INCOMPLETEEFFECT:
						DebugWriteA("Test Rumble: DIError: incomplete effect.\n");
						break;
					case DIERR_INVALIDPARAM:
						DebugWriteA("Test Rumble: DIError: invalid param.\n");
						break;
					case DIERR_NOTEXCLUSIVEACQUIRED:
						DebugWriteA("Test Rumble: DIError: not exclusive acquired.\n");
						break;
					case DIERR_NOTINITIALIZED:
						DebugWriteA("Test Rumble: DIError: not initialized.\n");
						break;
					case DIERR_UNSUPPORTED:
						DebugWriteA("Test Rumble: DIError: unsupported.\n");
						break;
					default:
						DebugWriteA("Test Rumble: DIError: undocumented: %lX\n", hRslt);
					}
				}
				else
					DebugWriteA("Test Rumble: OK\n");
			}
			return TRUE;

		default:
			return FALSE;
		}

	case WM_HSCROLL: // TrackBars
	case WM_VSCROLL:
		i = GetWindowLong( (HWND)lParam, GWL_ID );
		switch( i )
		{
		case IDC_RUMBLESTRENGTH:
			pcController->bRumbleStrength = (BYTE)SendMessage( (HWND)lParam, TBM_GETPOS, 0, 0 );
			LoadString( g_hResourceDLL, IDS_D_RUMBLESTR, szTemp, DEFAULT_BUFFER );
 			wsprintf( szBuffer, szTemp, pcController->bRumbleStrength );
			SendMessage( GetDlgItem( hDlg, IDT_RUMBLESTRENGTH ), WM_SETTEXT , 0, (LPARAM)szBuffer );
			bNewRumbleTyp = true;
			return TRUE;
		default:
			return FALSE;
		}

	case WM_USER_UPDATE:
		// filling DropDownlist with devices
		hDlgItem = GetDlgItem( hDlg, IDC_CTRDEVICE );
		SendMessage (hDlgItem, CB_RESETCONTENT, 0, 0); // HACK: yeah this isn't the best way to do this, but it works.
		LoadString( g_hResourceDLL, IDS_P_R_NODEVICE, szBuffer, DEFAULT_BUFFER );
		j = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)szBuffer );
		SendMessage( hDlgItem, CB_SETITEMDATA, j, -1 );
		for( i = 0;  i < g_nDevices; i++ )
		{
			bool bMatch = false;
			// if device is not already set as a FF device in some other tab
			for( int m = 0; m < 4; m++)
			{
				if( g_ivConfig->ChosenTab != m)
					if (g_devList[i].bProductCounter == g_ivConfig->FFDevices[m].bProductCounter && !lstrcmp( g_devList[i].szProductName, g_ivConfig->FFDevices[m].szProductName ) )
						bMatch = true;
			}

			if (!bMatch && g_devList[i].bEffType != 0 )
			{
				if( g_devList[i].bProductCounter == 0 )
					lstrcpyn( szBuffer, g_devList[i].szProductName, ARRAYSIZE(szBuffer) );
				else
					wsprintf( szBuffer, _T("%s %i"), g_devList[i].szProductName, g_devList[i].bProductCounter );

				j = SendMessage( hDlgItem, CB_ADDSTRING, 0, (LPARAM)szBuffer );
				SendMessage( hDlgItem, CB_SETITEMDATA, j, i );
			}
		}
		// DropDownlist End

		EnterCriticalSection(&g_critical);
		j = FindDeviceinList( pifDevice->szProductName, pifDevice->bProductCounter, true );
		hDlgItem = GetDlgItem( hDlg, IDC_CTRDEVICE );
		ReleaseEffect( g_pConfigEffect );
		ReleaseDevice( g_pConfigDevice );
		if( j == -1 || !g_devList[j].bEffType)
		{
			SendMessage( hDlgItem, CB_SETCURSEL, 0, 0 ); // set "None"
			EnableWindow( GetDlgItem( hDlg, IDC_RUMBLE1 ), FALSE );
			EnableWindow( GetDlgItem( hDlg, IDC_RUMBLE2 ), FALSE );
			EnableWindow( GetDlgItem( hDlg, IDC_RUMBLE3 ), FALSE );
			EnableWindow( GetDlgItem( hDlg, IDC_RUMBLESTRENGTH ), FALSE );
			EnableWindow( GetDlgItem( hDlg, IDT_RUMBLESTRENGTH ), FALSE );
			EnableWindow( GetDlgItem( hDlg, IDC_RUMBLETEST ), FALSE );
		}
		else
		{
			if (! GetInputDevice( g_hMainDialog, g_pConfigDevice, g_devList[j].guidInstance, g_devList[j].dwDevType, DIB_FF ) )
			{
				g_pConfigDevice = NULL;
				DebugWriteA("Could not GetInputDevice in user update, RumblePakProc.\n");
			}
			else
			{
				DebugWriteA("GetInputDevice in RumblePakProc: OK\n");
			}

			// DropDownList
			if( g_devList[j].bProductCounter == 0 )
				i = SendMessage( hDlgItem, CB_FINDSTRINGEXACT, (WPARAM)(-1), (LPARAM)g_devList[j].szProductName );
			else
			{
				wsprintf( szBuffer, _T("%s %i"), g_devList[j].szProductName, g_devList[j].bProductCounter );
				i = SendMessage( hDlgItem, CB_FINDSTRINGEXACT, (WPARAM)(-1), (LPARAM)szBuffer ); // search index of Device-String
			}

			SendMessage( hDlgItem, CB_SETCURSEL, i, 0 ); // select the right string

			if( g_devList[j].bEffType & RUMBLE_EFF1 )
				EnableWindow( GetDlgItem( hDlg, IDC_RUMBLE1 ), TRUE );
			else
				EnableWindow( GetDlgItem( hDlg, IDC_RUMBLE1 ), FALSE );
			if( g_devList[j].bEffType & RUMBLE_EFF2 )
				EnableWindow( GetDlgItem( hDlg, IDC_RUMBLE2 ), TRUE );
			else
				EnableWindow( GetDlgItem( hDlg, IDC_RUMBLE2 ), FALSE );
			if( g_devList[j].bEffType & RUMBLE_EFF3 )
				EnableWindow( GetDlgItem( hDlg, IDC_RUMBLE3 ), TRUE );
			else
				EnableWindow( GetDlgItem( hDlg, IDC_RUMBLE3 ), FALSE );

			if(	( g_devList[j].bEffType & RUMBLE_EFF1 ) ||
				( g_devList[j].bEffType & RUMBLE_EFF2 ))
			{
				EnableWindow( GetDlgItem( hDlg, IDC_RUMBLESTRENGTH ), TRUE );
				EnableWindow( GetDlgItem( hDlg, IDT_RUMBLESTRENGTH ), TRUE );
			}
			else
			{
				EnableWindow( GetDlgItem( hDlg, IDC_RUMBLESTRENGTH ), FALSE );
				EnableWindow( GetDlgItem( hDlg, IDT_RUMBLESTRENGTH ), FALSE );
			}

			if( g_devList[j].bEffType != RUMBLE_NONE )
			{
				// if (!CreateEffectHandle(g_hMainDialog, g_pConfigDevice, g_pConfigEffect, pcController->bRumbleTyp, pcController->bRumbleStrength) )
				// {
				//	DebugWriteA("Interface: CreateEffectHandle failed\n");
				// }
				bNewRumbleTyp = true;
				EnableWindow( GetDlgItem( hDlg, IDC_RUMBLETEST ), TRUE );
			}
			else
				EnableWindow( GetDlgItem( hDlg, IDC_RUMBLETEST ), FALSE );

		}
		LeaveCriticalSection(&g_critical);

		if( pcController->bRumbleTyp == RUMBLE_EFF1 )
			CheckDlgButton( hDlg, IDC_RUMBLE1, BST_CHECKED );
		else
			CheckDlgButton( hDlg, IDC_RUMBLE1, BST_UNCHECKED );

		if( pcController->bRumbleTyp == RUMBLE_EFF2 )
			CheckDlgButton( hDlg, IDC_RUMBLE2, BST_CHECKED );
		else
			CheckDlgButton( hDlg, IDC_RUMBLE2, BST_UNCHECKED );

		if( pcController->bRumbleTyp == RUMBLE_EFF3 )
			CheckDlgButton( hDlg, IDC_RUMBLE3, BST_CHECKED );
		else
			CheckDlgButton( hDlg, IDC_RUMBLE3, BST_UNCHECKED );

		CheckDlgButton( hDlg, IDC_VISUALRUMBLE, pcController->fVisualRumble ? BST_CHECKED : BST_UNCHECKED );

		// TrackBars
		SendMessage( GetDlgItem( hDlg, IDC_RUMBLESTRENGTH ), TBM_SETPOS, TRUE, pcController->bRumbleStrength );
		LoadString( g_hResourceDLL, IDS_D_RUMBLESTR, szTemp, DEFAULT_BUFFER );
		wsprintf( szBuffer, szTemp, pcController->bRumbleStrength );
		SendMessage( GetDlgItem( hDlg, IDT_RUMBLESTRENGTH ), WM_SETTEXT , 0, (LPARAM)szBuffer );

		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
}

BOOL CALLBACK TransferPakProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	static LPTSTR pszGBRomFile = NULL;
	static LPTSTR pszGBSaveFile = NULL;
	TCHAR tszMsg[DEFAULT_BUFFER];

	switch(uMsg)
	{
	case WM_INITDIALOG:
		if( g_bRunning )
		{
			EnableWindow( GetDlgItem( hDlg, IDC_CHGDIR ), FALSE );
			LoadString( g_hResourceDLL, IDS_P_TRANS_NOCHANGE, tszMsg, DEFAULT_BUFFER );
			SendMessage( GetDlgItem( hDlg, IDC_CHGDIR ), WM_SETTEXT, 0, (LPARAM)tszMsg );
		}

		TransferPakProc( hDlg, WM_USER_UPDATE, 0, 0 ); // setting values
		return FALSE; // don't give it focus

	case WM_COMMAND:
		switch( LOWORD(wParam) )
		{
		case IDC_GBROM_EDIT:
			if( HIWORD(wParam) == EN_CHANGE )
				GetDlgItemText( hDlg, IDC_GBROM_EDIT, pszGBRomFile, ARRAYSIZE(g_ivConfig->Controllers->szTransferRom) );
			return TRUE;

		case IDC_GBSAVE_EDIT:
			if( HIWORD(wParam) == EN_CHANGE )
				GetDlgItemText( hDlg, IDC_GBSAVE_EDIT, pszGBSaveFile, ARRAYSIZE(g_ivConfig->Controllers->szTransferSave) );
			return TRUE;

		case IDC_GBROM_BROWSE:
			{
				TCHAR szBuffer[MAX_PATH+1];
				GetAbsoluteFileName( szBuffer, pszGBRomFile, DIRECTORY_GBROMS );
				if( BrowseFile( hDlg, szBuffer, BF_GBROM, BF_LOAD ))
				{
					lstrcpyn( pszGBRomFile, szBuffer, ARRAYSIZE(g_ivConfig->Controllers->szTransferRom) );
					TransferPakProc( hDlg, WM_USER_UPDATE, 0, 0 );
				}
			}
			return TRUE;

		case IDC_GBSAVE_BROWSE:
			{
				TCHAR szBuffer[MAX_PATH+1];
				GetAbsoluteFileName( szBuffer, pszGBSaveFile, DIRECTORY_GBSAVES );
				if( BrowseFile( hDlg, szBuffer, BF_GBSAVE, BF_LOAD ))
				{
					lstrcpyn( pszGBSaveFile, szBuffer, ARRAYSIZE(g_ivConfig->Controllers->szTransferSave) );
					TransferPakProc( hDlg, WM_USER_UPDATE, 0, 0 );
				}
			}
			return TRUE;

		case IDC_CHGDIR:
			if( DialogBox( g_hResourceDLL, MAKEINTRESOURCE( IDD_FOLDERS ), hDlg, FoldersDialogProc ) == TRUE )
				TransferPakProc( hDlg, WM_USER_UPDATE, 0, 0 );
			return TRUE;

		default:
			return FALSE;
		}

	case WM_USER_UPDATE:
		pszGBRomFile = g_ivConfig->Controllers[g_ivConfig->ChosenTab].szTransferRom;
		pszGBSaveFile = g_ivConfig->Controllers[g_ivConfig->ChosenTab].szTransferSave;

		SetDlgItemText( hDlg, IDC_GBROM_EDIT, pszGBRomFile );
		SetDlgItemText( hDlg, IDC_GBSAVE_EDIT, pszGBSaveFile );
		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
}

BOOL CALLBACK ShortcutsTabProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	static bool bScanRunning;
	static DWORD dwButtonID[3];
	static DWORD dwCounter;
	static HWND hFocus = NULL;
	static int iPlayer = 5;		// HACK: player "5" is obviously out of bounds, so indicates no control has been set

	long i;
	static HWND hBlocker = NULL;

	TCHAR szBuffer[40];

	switch(uMsg)
	{
	case WM_INITDIALOG:
		bScanRunning = false;
		iPlayer = 5;

		ShortcutsTabProc( hDlg, WM_USER_UPDATE, 0, 0 ); // setting values
		return FALSE; // don't give it focus

	case WM_COMMAND:
		switch( LOWORD(wParam) )
		{
		case IDC_SHOWMESSAGES:
			g_ivConfig->fDisplayShortPop = ( IsDlgButtonChecked( hDlg, LOWORD(wParam) ) == BST_CHECKED );
			return TRUE;
		case IDC_LOCKMOUSE:
			iPlayer = -1;

		case IDC_SETNOPAK_P1:
		case IDC_SETMEMPAK_P1:
		case IDC_SETRUMBLEPAK_P1:
		case IDC_SETTRANSFERPAK_P1:
		case IDC_SETADAPTOIDPAK_P1:
		case IDC_SWMEMRUMBLE_P1:
		case IDC_SWMEMADAPTOID_P1:
			if (iPlayer > -1)
				iPlayer = 0;

		case IDC_SETNOPAK_P2:
		case IDC_SETMEMPAK_P2:
		case IDC_SETRUMBLEPAK_P2:
		case IDC_SETTRANSFERPAK_P2:
		case IDC_SETADAPTOIDPAK_P2:
		case IDC_SWMEMRUMBLE_P2:
		case IDC_SWMEMADAPTOID_P2:
			if (iPlayer > 0)
				iPlayer = 1;

		case IDC_SETNOPAK_P3:
		case IDC_SETMEMPAK_P3:
		case IDC_SETRUMBLEPAK_P3:
		case IDC_SETTRANSFERPAK_P3:
		case IDC_SETADAPTOIDPAK_P3:
		case IDC_SWMEMRUMBLE_P3:
		case IDC_SWMEMADAPTOID_P3:
			if (iPlayer > 1)
				iPlayer = 2;

		case IDC_SETNOPAK_P4:
		case IDC_SETMEMPAK_P4:
		case IDC_SETRUMBLEPAK_P4:
		case IDC_SETTRANSFERPAK_P4:
		case IDC_SETADAPTOIDPAK_P4:
		case IDC_SWMEMRUMBLE_P4:
		case IDC_SWMEMADAPTOID_P4:
			if (iPlayer > 2)
				iPlayer = 3;

			if( bScanRunning )
			{
/*				bScanRunning = false;
				KillTimer( hDlg, TIMER_BUTTON );
				if( ((LPBYTE)&g_ivConfig->Shortcuts)[dwButtonID[2]] == VK_ESCAPE )
					lstrcpyn( szKeyname, "Unassigned", sizeof(szKeyname) );
				else
					GetKeyNameText(( MapVirtualKey( ((LPBYTE)&g_ivConfig->Shortcuts)[dwButtonID[2]], 0 ) << 16 ), szKeyname, sizeof(szKeyname) );

				SendMessage( GetDlgItem( hDlg, dwButtonID[1] ), WM_SETTEXT, 0, (LPARAM)szKeyname ); */; // do nothing!
			}
			if( HIWORD(wParam) == BN_CLICKED )
			{
				EnterCriticalSection(&g_critical);
				dwButtonID[0] = LOWORD(wParam);
				dwCounter = 0;
				GetButtonID( dwButtonID, 0, BSET_SHORTCUTS );
				if (hFocus == NULL)
					hFocus = SetFocus( NULL );
				hBlocker = MakeOverlay();

				SetTimer( hDlg, TIMER_BUTTON, INTERVAL_BUTTON, NULL );
				bScanRunning = true;
				LeaveCriticalSection(&g_critical);
			}
			return TRUE;

		case IDC_SETDEFAULTSC:
			{
				TCHAR tszTitle[DEFAULT_BUFFER], tszMsg[DEFAULT_BUFFER];

				LoadString( g_hResourceDLL, IDS_DLG_SHORTCUTRESTORE, tszMsg, DEFAULT_BUFFER );
				LoadString( g_hResourceDLL, IDS_DLG_CONTROLRESTORE_TITLE, tszTitle, DEFAULT_BUFFER );

				if( MessageBox( hDlg, tszMsg, tszTitle, MB_OKCANCEL | MB_ICONWARNING ) == IDOK )
				{
					LoadShortcutsFromResource(true);
					ShortcutsTabProc( hDlg, WM_USER_UPDATE, 0, 0 );
				}
			}
			return TRUE;

		case IDC_SAVESHORTCUTS:
			{
				TCHAR szFilename[MAX_PATH+1] = _T( "" );
				if( BrowseFile( hDlg, szFilename, BF_SHORTCUTS, BF_SAVE ))
				{
					FILE * fFile = _tfopen( szFilename, _T("wS") );
					if (fFile)
					{
						fprintf(fFile, "@" STRING_PROFILEVERSION "\n\n");
						FormatShortcutsBlock( fFile, false );

						fclose(fFile);
					}
					else
						WarningMessage( IDS_ERR_PROFWRITE, MB_OK );
				}
			}
			return TRUE;

		case IDC_LOADSHORTCUTS:
			{
				TCHAR szFilename[MAX_PATH+1] = TEXT( "" );
				if( BrowseFile( hDlg, szFilename, BF_SHORTCUTS, BF_LOAD ))
				{
					DebugWrite(_T("Config interface: Load shortcuts file: %s\n"), szFilename);
					if( LoadShortcutsFile( szFilename ))
						ShortcutsTabProc( hDlg, WM_USER_UPDATE, 0, 0 );
					else
						WarningMessage( IDS_ERR_SHORTREAD, MB_OK );

				}
			}
			return TRUE;

		default:
			return FALSE;
		}

	case WM_TIMER: // when assigning shortcuts, this gets called every 20ms (or value in INTERVAL_BUTTON)
		if( wParam == TIMER_BUTTON && bScanRunning )
		{
			BUTTON newButton;

			i = ScanDevices( &dwCounter, &newButton);
			if( i || dwCounter > 500 )
			{
				bScanRunning = false;
				KillTimer( hDlg, TIMER_BUTTON );

				EnterCriticalSection(&g_critical);
				if( i == SC_SCANESCAPE ) // Scan aborted
				{
					if (iPlayer == -1)
						ZeroMemory(&g_ivConfig->Shortcuts.bMouseLock, sizeof(BUTTON) );
					else
						ZeroMemory(&g_ivConfig->Shortcuts.Player[iPlayer].aButtons[dwButtonID[2] % SC_TOTAL], sizeof(BUTTON));
				}
				else if( i == SC_SCANSUCCEED  ) // Got a key, mouseclick, joybutton, or axis
					if (iPlayer == -1)
						g_ivConfig->Shortcuts.bMouseLock = newButton;
					else
						g_ivConfig->Shortcuts.Player[iPlayer].aButtons[dwButtonID[2] % SC_TOTAL] = newButton;
				DestroyWindow( hBlocker );


				if (iPlayer == -1)
					GetButtonText( g_ivConfig->Shortcuts.bMouseLock, szBuffer );
				else
					GetButtonText( g_ivConfig->Shortcuts.Player[iPlayer].aButtons[dwButtonID[2] % SC_TOTAL], szBuffer );
				iPlayer = 5;		// reset invalid player value
				LeaveCriticalSection(&g_critical);

				SendMessage( GetDlgItem( hDlg, dwButtonID[1] ), WM_SETTEXT , 0, (LPARAM)szBuffer );
				if( hFocus != NULL )
				{
					SetFocus( hFocus );
					hFocus = NULL;
				}
			}
			else
			{
				if(( dwCounter % 50 ) == 0 )
				{
					TCHAR tszText[DEFAULT_BUFFER];

					LoadString( g_hResourceDLL, IDS_C_POLLING, tszText, DEFAULT_BUFFER );
					wsprintf( szBuffer, tszText, 10 - dwCounter / 50 );
					SendMessage( GetDlgItem( hDlg, dwButtonID[1] ), WM_SETTEXT , 0, (LPARAM)szBuffer );
				}
				++dwCounter;
			}
			return TRUE;
		}
		else
			return FALSE;

	case WM_USER_UPDATE:
		bScanRunning = false;
		KillTimer( hDlg, TIMER_BUTTON );

		for ( i = 0; i < 4; i++ ) // controllers
			for( int j = 0; j < SC_TOTAL * 4; j++ ) // shortcuts
			{
				DWORD aIDs[3];
				aIDs[2] = i * SC_TOTAL + j;
				if( !GetButtonID( aIDs, 2, BSET_SHORTCUTS ))
					continue; // for not implemented Buttons, otherwise the behaviour is unsafe

				GetButtonText( g_ivConfig->Shortcuts.Player[i].aButtons[j], szBuffer );
				SendMessage( GetDlgItem( hDlg, aIDs[1] ), WM_SETTEXT , 0, (LPARAM)szBuffer );
			}

		DWORD aIDs[3];
		aIDs[2] = (DWORD)(-1);
		if( GetButtonID( aIDs, 2, BSET_SHORTCUTS ) )
		{
			GetButtonText( g_ivConfig->Shortcuts.bMouseLock, szBuffer );
			SendMessage( GetDlgItem( hDlg, aIDs[1] ), WM_SETTEXT , 0, (LPARAM)szBuffer );
		}

		CheckDlgButton( hDlg, IDC_SHOWMESSAGES, g_ivConfig->fDisplayShortPop ? BST_CHECKED : BST_UNCHECKED );
		return TRUE;

	default:
		return FALSE; //false means the msg didn't got processed
	}
}

BOOL CALLBACK FoldersDialogProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	long i,j;
	TCHAR szBuffer[MAX_PATH+1];
	TCHAR szApplication[MAX_PATH+1],
		  *pcSlash;

	switch(uMsg)
	{
	case WM_INITDIALOG:

#if defined(HIDEUNIMPLEMENTED) && !defined(V_TRANSFERPAK)
		{
			RECT rRect, rGBSave;
			GetWindowRect( GetDlgItem( hDlg, IDC_GBSAVEPANEL ), &rGBSave );
			GetWindowRect( GetDlgItem( hDlg, IDC_MEMPAKPANEL ), &rRect );
			int iShift = rRect.bottom - rGBSave.bottom;

			WINDOWPLACEMENT pos;
			pos.length = sizeof(WINDOWPLACEMENT);

			GetWindowPlacement( GetDlgItem( hDlg, IDOK ), &pos );
			pos.rcNormalPosition.top += iShift;
			pos.rcNormalPosition.bottom += iShift;
			SetWindowPlacement( GetDlgItem( hDlg, IDOK ), &pos );

			GetWindowPlacement( GetDlgItem( hDlg, IDCANCEL ), &pos );
			pos.rcNormalPosition.top += iShift;
			pos.rcNormalPosition.bottom += iShift;
			SetWindowPlacement( GetDlgItem( hDlg, IDCANCEL ), &pos );

			GetWindowRect( hDlg, &rRect );
			rRect.bottom += iShift;

			SetWindowPos( hDlg, NULL, rRect.left, rRect.top, rRect.right-rRect.left, rRect.bottom-rRect.top, SWP_NOMOVE | SWP_NOZORDER );

			int aChilds[] = {	IDC_GBROMPANEL, IDC_GBROM_REL, IDC_GBROM_REL_EDIT, IDC_GBROM_BROWSE_REL, IDC_GBROM_ABS,
								IDC_GBROM_ABS_EDIT, IDC_GBROM_BROWSE_ABS, IDC_GBSAVEPANEL, IDC_GBSAVE_SAME, IDC_GBSAVE_REL,
								IDC_GBSAVE_REL_EDIT, IDC_GBSAVE_BROWSE_REL, IDC_GBSAVE_ABS, IDC_GBSAVE_ABS_EDIT, IDC_GBSAVE_BROWSE_ABS };

			for( int i = 0; i < sizeof(aChilds)/sizeof(int); ++i )
				DestroyWindow( GetDlgItem( hDlg, aChilds[i] ));
		}
#pragma message( "unimplemented Features from the Folderdialog will be removed" )
#endif // #ifdef HIDEUNIMPLEMENTED

		GetDirectory( szApplication, DIRECTORY_GBROMS );
		GetDirectory( szBuffer, DIRECTORY_GBSAVES );
		j = lstrcmp( szApplication, szBuffer );

		GetDirectory( szApplication, DIRECTORY_APPLICATION );
		i = lstrlen( szApplication );

		// MemPak Directory
		lstrcpyn(szBuffer, g_aszDefFolders[DIRECTORY_MEMPAK], MAX_PATH);

		if( szBuffer[0] != 0 && ( szBuffer[1] == ':' || ( szBuffer[1] == '\\' &&  szBuffer[0] == '\\' )))
		{
			CheckDlgButton( hDlg, IDC_MEMPAK_ABS, BST_CHECKED );

			GetDirectory( szBuffer, DIRECTORY_MEMPAK );
			if( !_tcsncmp( szBuffer, szApplication, i ))
			{
				TCHAR *pcSub = &szBuffer[i];
				pcSlash = _tcsrchr( pcSub, _T('\\') );
				if( pcSlash && ( pcSlash[1] == '\0' )) *pcSlash = '\0';
				SendMessage( GetDlgItem( hDlg, IDC_MEMPAK_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)pcSub );
			}
			else
				SendMessage( GetDlgItem( hDlg, IDC_MEMPAK_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)STRING_DEF_MEMPAKFILE );
		}
		else
		{
			CheckDlgButton( hDlg, IDC_MEMPAK_REL, BST_CHECKED );

			if( szBuffer[0] != 0 )
				SendMessage( GetDlgItem( hDlg, IDC_MEMPAK_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
			else
				SendMessage( GetDlgItem( hDlg, IDC_MEMPAK_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)STRING_DEF_MEMPAKFILE );
		}

		GetDirectory( szBuffer, DIRECTORY_MEMPAK );
		pcSlash = _tcsrchr( szBuffer, '\\' );
		if( pcSlash && ( pcSlash[1] == '\0' )) *pcSlash = '\0';
		SendMessage( GetDlgItem( hDlg, IDC_MEMPAK_ABS_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );

		// GBRom Directory
		lstrcpyn(szBuffer, g_aszDefFolders[DIRECTORY_GBROMS], MAX_PATH);

		if( szBuffer[0] != 0 && ( szBuffer[1] == _T(':') || ( szBuffer[1] == _T('\\') &&  szBuffer[0] == _T('\\') )))
		{
			CheckDlgButton( hDlg, IDC_GBROM_ABS, BST_CHECKED );

			GetDirectory( szBuffer, DIRECTORY_GBROMS );
			if( !_tcsncmp( szBuffer, szApplication, i ))
			{
				TCHAR *pcSub = &szBuffer[i];
				pcSlash = _tcsrchr( pcSub, _T('\\') );
				if( pcSlash && ( pcSlash[1] == '\0' )) *pcSlash = '\0';
				SendMessage( GetDlgItem( hDlg, IDC_GBROM_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
			}
			else
				SendMessage( GetDlgItem( hDlg, IDC_GBROM_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)STRING_DEF_GBROMFILE );
		}
		else
		{
			CheckDlgButton( hDlg, IDC_GBROM_REL, BST_CHECKED );

			if( szBuffer[0] != 0 )
				SendMessage( GetDlgItem( hDlg, IDC_GBROM_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
			else
				SendMessage( GetDlgItem( hDlg, IDC_GBROM_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)STRING_DEF_GBROMFILE );
		}

		GetDirectory( szBuffer, DIRECTORY_GBROMS );
		pcSlash = _tcsrchr( szBuffer, _T('\\') );
		if( pcSlash && ( pcSlash[1] == _T('\0') )) *pcSlash = '\0';
		SendMessage( GetDlgItem( hDlg, IDC_GBROM_ABS_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );

		// GBSave Directory
		lstrcpyn(szBuffer, g_aszDefFolders[DIRECTORY_GBSAVES], MAX_PATH);

		if( !j )
		{
			CheckDlgButton( hDlg, IDC_GBSAVE_SAME, BST_CHECKED );
			FoldersDialogProc( hDlg, WM_COMMAND, (WPARAM)MAKELONG( IDC_GBSAVE_SAME, BN_CLICKED ), (LPARAM)GetDlgItem( hDlg, IDC_GBSAVE_SAME ));
			SendMessage( GetDlgItem( hDlg, IDC_GBROM_REL_EDIT ), WM_GETTEXT, sizeof(szBuffer), (LPARAM)szBuffer );
			SendMessage( GetDlgItem( hDlg, IDC_GBSAVE_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
		}
		else
		{
			if( szBuffer[0] != 0 && ( szBuffer[1] == _T(':') || ( szBuffer[1] == _T('\\') &&  szBuffer[0] == _T('\\') )))
			{
				CheckDlgButton( hDlg, IDC_GBSAVE_ABS, BST_CHECKED );

				GetDirectory( szBuffer, DIRECTORY_GBSAVES );
				if( !_tcsncmp( szBuffer, szApplication, i ))
				{
					TCHAR *pcSub = &szBuffer[i];
					pcSlash = _tcsrchr( pcSub, '\\' );
					if( pcSlash && ( pcSlash[1] == '\0' )) *pcSlash = '\0';
					SendMessage( GetDlgItem( hDlg, IDC_GBSAVE_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
				}
				else
					SendMessage( GetDlgItem( hDlg, IDC_GBSAVE_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)STRING_DEF_GBROMSAVE );
			}
			else
			{
				CheckDlgButton( hDlg, IDC_GBSAVE_REL, BST_CHECKED );

				if( szBuffer[0] != 0 )
					SendMessage( GetDlgItem( hDlg, IDC_GBSAVE_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
				else
					SendMessage( GetDlgItem( hDlg, IDC_GBSAVE_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)STRING_DEF_GBROMSAVE );
			}
		}

		GetDirectory( szBuffer, DIRECTORY_GBSAVES );
		pcSlash = _tcsrchr( szBuffer, _T('\\') );
		if( pcSlash && ( pcSlash[1] == _T('\0') )) *pcSlash = '\0';
		SendMessage( GetDlgItem( hDlg, IDC_GBSAVE_ABS_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );

		return FALSE; // don't give it focus

	case WM_COMMAND:
		switch( LOWORD(wParam) )
		{
		case IDC_GBSAVE_ABS:
		case IDC_GBSAVE_REL:
			EnableWindow( GetDlgItem( hDlg, IDC_GBSAVE_REL_EDIT ), TRUE );
			EnableWindow( GetDlgItem( hDlg, IDC_GBSAVE_ABS_EDIT ), TRUE );
			EnableWindow( GetDlgItem( hDlg, IDC_GBSAVE_BROWSE_REL ), TRUE );
			EnableWindow( GetDlgItem( hDlg, IDC_GBSAVE_BROWSE_ABS ), TRUE );
			return TRUE;

		case IDC_GBSAVE_SAME:
			EnableWindow( GetDlgItem( hDlg, IDC_GBSAVE_REL_EDIT ), FALSE );
			EnableWindow( GetDlgItem( hDlg, IDC_GBSAVE_ABS_EDIT ), FALSE );
			EnableWindow( GetDlgItem( hDlg, IDC_GBSAVE_BROWSE_REL ), FALSE );
			EnableWindow( GetDlgItem( hDlg, IDC_GBSAVE_BROWSE_ABS ), FALSE );
			return TRUE;

		case IDC_MEMPAK_BROWSE_ABS:
			szBuffer[0] = '\0';
			if( BrowseFolders( hDlg, NULL, szBuffer ))
				SendMessage( GetDlgItem( hDlg, IDC_MEMPAK_ABS_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
			return TRUE;
		case IDC_GBROM_BROWSE_ABS:
			szBuffer[0] = '\0';
			if( BrowseFolders( hDlg, NULL, szBuffer ))
				SendMessage( GetDlgItem( hDlg, IDC_GBROM_ABS_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
			return TRUE;
		case IDC_GBSAVE_BROWSE_ABS:
			szBuffer[0] = '\0';
			if( BrowseFolders( hDlg, NULL, szBuffer ))
				SendMessage( GetDlgItem( hDlg, IDC_GBSAVE_ABS_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
			return TRUE;
		case IDC_MEMPAK_BROWSE_REL:
			GetDirectory( szApplication, DIRECTORY_APPLICATION );
			lstrcpyn( szBuffer, szApplication, ARRAYSIZE(szBuffer) );
			if( BrowseFolders( hDlg, NULL, szBuffer ))
			{
				i = lstrlen( szApplication );
				lstrcpyn( szBuffer, &szBuffer[i], ARRAYSIZE(szBuffer) ); // HACK: The lstrcpyn function has an undefined behavior if source and destination buffers overlap.
				SendMessage( GetDlgItem( hDlg, IDC_MEMPAK_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
			}
			return TRUE;

		case IDC_GBROM_BROWSE_REL:
			GetDirectory( szApplication, DIRECTORY_APPLICATION );
			lstrcpyn( szBuffer, szApplication, ARRAYSIZE(szBuffer) );
			if( BrowseFolders( hDlg, NULL, szBuffer ))
			{
				i = lstrlen( szApplication );
				lstrcpyn( szBuffer, &szBuffer[i], ARRAYSIZE(szBuffer) ); // HACK: The lstrcpyn function has an undefined behavior if source and destination buffers overlap.
				SendMessage( GetDlgItem( hDlg, IDC_GBROM_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
			}
			return TRUE;

		case IDC_GBSAVE_BROWSE_REL:
			GetDirectory( szApplication, DIRECTORY_APPLICATION );
			lstrcpyn( szBuffer, szApplication, ARRAYSIZE(szBuffer) );
			if( BrowseFolders( hDlg, NULL, szBuffer ))
			{
				i = lstrlen( szApplication );
				lstrcpyn( szBuffer, &szBuffer[i], ARRAYSIZE(szBuffer) ); // HACK: The lstrcpyn function has an undefined behavior if source and destination buffers overlap.
				SendMessage( GetDlgItem( hDlg, IDC_GBSAVE_REL_EDIT ), WM_SETTEXT, 0, (LPARAM)szBuffer );
			}
			return TRUE;

		case IDOK:
			if( IsDlgButtonChecked( hDlg, IDC_MEMPAK_ABS ) == BST_CHECKED )
			{
				SendMessage( GetDlgItem( hDlg, IDC_MEMPAK_ABS_EDIT ), WM_GETTEXT, ARRAYSIZE(szApplication), (LPARAM)szApplication );
				GetFullPathName( szApplication, ARRAYSIZE(szBuffer), szBuffer, &pcSlash );
			}
			else
				SendMessage( GetDlgItem( hDlg, IDC_MEMPAK_REL_EDIT ), WM_GETTEXT, ARRAYSIZE(szBuffer), (LPARAM)szBuffer );
			pcSlash = _tcsrchr( szBuffer, '\\' );
			if( pcSlash && ( pcSlash[1] == '\0' ))
				*pcSlash = '\0';

			if( !lstrcmp( szBuffer, STRING_DEF_MEMPAKFILE ))
				g_aszDefFolders[DIRECTORY_MEMPAK][0] = 0;
			else
				lstrcpyn(g_aszDefFolders[DIRECTORY_MEMPAK], szBuffer, MAX_PATH);

			if( IsDlgButtonChecked( hDlg, IDC_GBROM_ABS ) == BST_CHECKED )
			{
				SendMessage( GetDlgItem( hDlg, IDC_GBROM_ABS_EDIT ), WM_GETTEXT, ARRAYSIZE(szApplication), (LPARAM)szApplication );
				GetFullPathName( szApplication, ARRAYSIZE(szBuffer), szBuffer, &pcSlash );
			}
			else
				SendMessage( GetDlgItem( hDlg, IDC_GBROM_REL_EDIT ), WM_GETTEXT, ARRAYSIZE(szBuffer), (LPARAM)szBuffer );
			pcSlash = _tcsrchr( szBuffer, '\\' );
			if( pcSlash && ( pcSlash[1] == '\0' )) *pcSlash = '\0';

			if( !lstrcmp( szBuffer, STRING_DEF_GBROMFILE ))
				g_aszDefFolders[DIRECTORY_GBROMS][0] = 0;
			else
				lstrcpyn(g_aszDefFolders[DIRECTORY_GBROMS], szBuffer, MAX_PATH);

			if( !IsDlgButtonChecked( hDlg, IDC_GBSAVE_SAME ) == BST_CHECKED )
			{
				if( IsDlgButtonChecked( hDlg, IDC_GBSAVE_ABS ) == BST_CHECKED )
				{
					SendMessage( GetDlgItem( hDlg, IDC_GBSAVE_ABS_EDIT ), WM_GETTEXT, ARRAYSIZE(szApplication), (LPARAM)szApplication );
					GetFullPathName( szApplication, ARRAYSIZE(szBuffer), szBuffer, &pcSlash );
				}
				else
					SendMessage( GetDlgItem( hDlg, IDC_GBSAVE_REL_EDIT ), WM_GETTEXT, ARRAYSIZE(szBuffer), (LPARAM)szBuffer );
			}

			pcSlash = _tcsrchr( szBuffer, _T('\\') );
			if( pcSlash && ( pcSlash[1] == '\0' )) *pcSlash = '\0';

			if( !lstrcmp( szBuffer, STRING_DEF_GBROMSAVE ))
				g_aszDefFolders[DIRECTORY_GBSAVES][0] = 0;
			else
				lstrcpyn(g_aszDefFolders[DIRECTORY_GBSAVES], szBuffer, MAX_PATH);

			EndDialog( hDlg, TRUE );
			return TRUE;

		case IDCANCEL:
			EndDialog( hDlg, FALSE );
			return TRUE;

		default:
			return FALSE;	// unhandled WM_COMMAND
		}
	default:
		return FALSE; //false means the msg didn't got processed
	}
}

///////////////////////////////////////////////////////////////////////////////
// A wonderful n squared algorithm to store the key names in a string... what for???
// called by EnumObjects in MainDlgProcess to enumerate the keys on the keyboard...?
/* BOOL CALLBACK EnumGetKeyDesc( LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef )
{
	// REMOVED //
} // I am putting this function out of its misery before it causes more grief. --rabid */


// ButtonID is where we store an array of 3 DWORDs: ID of pushbutton, ID of text window, and an offset in the button-structure controls
// bIndex tells us what information to copy, but in a confusing way
// bButtonSet tells us whether we want the data from the Controls array or the Shortcuts array below
// This function is confusing, but not much I can do to fix it now. I'm sorry. --rabid
bool GetButtonID( LPDWORD ButtonID, const BYTE bIndex, const BYTE bButtonSet )
{
	// TODO: make these const, read from a resource or a define, or something... I don't know
	LPDWORD ButtonTable = NULL;
	int nEntries = 0;

							//	ID of PushButton  ID of TextWindow	  place in Button-structure
	const DWORD Controls[][3] = {	{ IDC_DRIGHT		, IDT_DRIGHT		, PF_DPADR },		// Controls
							{ IDC_DLEFT			, IDT_DLEFT			, PF_DPADL },
							{ IDC_DDOWN			, IDT_DDOWN			, PF_DPADD },
							{ IDC_DUP			, IDT_DUP			, PF_DPADU },
							{ IDC_SBUTTON		, IDT_SBUTTON		, PF_START },
							{ IDC_ZTRIGGER		, IDT_ZTRIGGER		, PF_TRIGGERZ },
							{ IDC_BBUTTON		, IDT_BBUTTON		, PF_BBUTTON },
							{ IDC_ABUTTON		, IDT_ABUTTON		, PF_ABUTTON },
							{ IDC_CRIGHT		, IDT_CRIGHT		, PF_CBUTTONR },
							{ IDC_CLEFT			, IDT_CLEFT			, PF_CBUTTONL },
							{ IDC_CDOWN			, IDT_CDOWN			, PF_CBUTTOND },
							{ IDC_CUP			, IDT_CUP			, PF_CBUTTONU },
							{ IDC_RIGHTTRIGGER	, IDT_RIGHTTRIGGER	, PF_TRIGGERR },
							{ IDC_LEFTTRIGGER	, IDT_LEFTTRIGGER	, PF_TRIGGERL },
							{ IDC_ARIGHT		, IDT_ARIGHT		, PF_APADR },
							{ IDC_ALEFT			, IDT_ALEFT			, PF_APADL },
							{ IDC_ADOWN			, IDT_ADOWN			, PF_APADD },
							{ IDC_AUP			, IDT_AUP			, PF_APADU }	};

	const DWORD Shortcuts[][3]= {
							{ IDC_SETNOPAK_P1		, IDC_SETNOPAK_P1		, SC_NOPAK },
							{ IDC_SETMEMPAK_P1		, IDC_SETMEMPAK_P1		, SC_MEMPAK },
							{ IDC_SETRUMBLEPAK_P1	, IDC_SETRUMBLEPAK_P1	, SC_RUMBPAK },
							{ IDC_SETTRANSFERPAK_P1	, IDC_SETTRANSFERPAK_P1	, SC_TRANSPAK },
							{ IDC_SETADAPTOIDPAK_P1	, IDC_SETADAPTOIDPAK_P1	, SC_ADAPTPAK },
							{ IDC_SWMEMRUMBLE_P1	, IDC_SWMEMRUMBLE_P1	, SC_SWMEMRUMB },
							{ IDC_SWMEMADAPTOID_P1	, IDC_SWMEMADAPTOID_P1	, SC_SWMEMADAPT },

							{ IDC_SETNOPAK_P2		, IDC_SETNOPAK_P2		, SC_NOPAK + SC_TOTAL },
							{ IDC_SETMEMPAK_P2		, IDC_SETMEMPAK_P2		, SC_MEMPAK + SC_TOTAL },
							{ IDC_SETRUMBLEPAK_P2	, IDC_SETRUMBLEPAK_P2	, SC_RUMBPAK + SC_TOTAL },
							{ IDC_SETTRANSFERPAK_P2	, IDC_SETTRANSFERPAK_P2	, SC_TRANSPAK + SC_TOTAL },
							{ IDC_SETADAPTOIDPAK_P2	, IDC_SETADAPTOIDPAK_P2	, SC_ADAPTPAK + SC_TOTAL },
							{ IDC_SWMEMRUMBLE_P2	, IDC_SWMEMRUMBLE_P2	, SC_SWMEMRUMB + SC_TOTAL },
							{ IDC_SWMEMADAPTOID_P2	, IDC_SWMEMADAPTOID_P2	, SC_SWMEMADAPT + SC_TOTAL },

							{ IDC_SETNOPAK_P3		, IDC_SETNOPAK_P3		, SC_NOPAK + SC_TOTAL * 2 },
							{ IDC_SETMEMPAK_P3		, IDC_SETMEMPAK_P3		, SC_MEMPAK + SC_TOTAL * 2 },
							{ IDC_SETRUMBLEPAK_P3	, IDC_SETRUMBLEPAK_P3	, SC_RUMBPAK + SC_TOTAL * 2 },
							{ IDC_SETTRANSFERPAK_P3	, IDC_SETTRANSFERPAK_P3	, SC_TRANSPAK + SC_TOTAL * 2 },
							{ IDC_SETADAPTOIDPAK_P3	, IDC_SETADAPTOIDPAK_P3	, SC_ADAPTPAK + SC_TOTAL * 2 },
							{ IDC_SWMEMRUMBLE_P3	, IDC_SWMEMRUMBLE_P3	, SC_SWMEMRUMB + SC_TOTAL * 2 },
							{ IDC_SWMEMADAPTOID_P3	, IDC_SWMEMADAPTOID_P3	, SC_SWMEMADAPT + SC_TOTAL * 2 },

							{ IDC_SETNOPAK_P4		, IDC_SETNOPAK_P4		, SC_NOPAK + SC_TOTAL * 3 },
							{ IDC_SETMEMPAK_P4		, IDC_SETMEMPAK_P4		, SC_MEMPAK + SC_TOTAL * 3 },
							{ IDC_SETRUMBLEPAK_P4	, IDC_SETRUMBLEPAK_P4	, SC_RUMBPAK + SC_TOTAL * 3 },
							{ IDC_SETTRANSFERPAK_P4	, IDC_SETTRANSFERPAK_P4	, SC_TRANSPAK + SC_TOTAL * 3 },
							{ IDC_SETADAPTOIDPAK_P4	, IDC_SETADAPTOIDPAK_P4	, SC_ADAPTPAK + SC_TOTAL * 3 },
							{ IDC_SWMEMRUMBLE_P4	, IDC_SWMEMRUMBLE_P4	, SC_SWMEMRUMB + SC_TOTAL * 3 },
							{ IDC_SWMEMADAPTOID_P4	, IDC_SWMEMADAPTOID_P4	, SC_SWMEMADAPT + SC_TOTAL * 3 },

							{ IDC_LOCKMOUSE			, IDC_LOCKMOUSE			, (DWORD)(-1) }
								};


	if( bButtonSet == BSET_CONTROLS )
	{
		ButtonTable = (LPDWORD)Controls; // HACK: what an ugly solution, using a cast like that
		nEntries = ARRAYSIZE( Controls );
	}

	if( bButtonSet == BSET_SHORTCUTS )
	{
		ButtonTable = (LPDWORD)Shortcuts;
		nEntries = ARRAYSIZE( Shortcuts );
	}

	bool bReturn = false;

	// TODO: fix this! just use 2 dimensional arrays for crying out loud!
	if( ButtonTable != NULL )
	{
		nEntries *= 3;
		for( int i = 0; i < nEntries; i+=3 )
		{
			if( ButtonTable[i+bIndex] == ButtonID[bIndex] )
			{
				ButtonID[0] = ButtonTable[i+0];
				ButtonID[1] = ButtonTable[i+1];
				ButtonID[2] = ButtonTable[i+2];

				bReturn = true;
				break;
			}
		}
	}

	return bReturn;
}

// a text-munging routine to spit out what PC control was assigned to a button
bool GetButtonText( const BUTTON& btnButton, LPTSTR Buffer )
{
	const UINT iDevice[] ={	IDS_BUTTON_UNASSIGNED,
						IDS_C_GAMEPAD,
						IDS_C_KEYBOARD,
						IDS_C_MOUSE };
	const UINT iGamepad[] ={ IDS_C_XAXIS,
						IDS_C_YAXIS,
						IDS_C_ZAXIS,
						IDS_C_XROT,
						IDS_C_YROT,
						IDS_C_ZROT,
						IDS_C_SLIDER,
						IDS_C_SLIDER,
						IDS_C_POV,
						IDS_C_POV,
						IDS_C_POV,
						IDS_C_POV,
						IDS_C_BUTTON };
	const UINT iMouse[] = {	IDS_C_XAXIS,
						IDS_C_YAXIS,
						IDS_C_WHEEL,
						IDS_C_BUTTON };
	const LPTSTR AxeID[] = {	TEXT( " +" ),
						TEXT( " -" ),
						TEXT( " /\\" ),
						TEXT( " >" ),
						TEXT( " \\/" ),
						TEXT( " <" ) };
	TCHAR a[16], b[16], buff[16]; // gotta allocate the space, stack is easier than P_malloc
	TCHAR Btn[6];
	LPTSTR Text[] = {a, b, NULL };
	bool bReturn = true;
	DIDEVICEOBJECTINSTANCE didoi;
	didoi.dwSize = sizeof(DIDEVICEOBJECTINSTANCE);

	switch ( btnButton.bBtnType )
	{
	case DT_JOYBUTTON:
		LoadString( g_hResourceDLL, iDevice[1], Text[0], 16 );
		LoadString( g_hResourceDLL, iGamepad[12], Text[1], 16 );
		Text[2] = Btn;
		wsprintf( Btn, TEXT( "%02u" ), btnButton.bOffset );
		break;
	case DT_JOYAXE:
		LoadString( g_hResourceDLL, iDevice[1], Text[0], 16 );
		LoadString( g_hResourceDLL, iGamepad[btnButton.bOffset], Text[1], 16 );
		Text[2] = AxeID[btnButton.bAxisID];
		break;
	case DT_JOYSLIDER:
		LoadString( g_hResourceDLL, iDevice[1], Text[0], 16 );
		LoadString( g_hResourceDLL, iGamepad[btnButton.bOffset], buff, 16 );
		wsprintf(Text[1], buff, btnButton.bOffset - 5 );
		Text[2] = AxeID[btnButton.bAxisID];
	case DT_JOYPOV:
		LoadString( g_hResourceDLL, iDevice[1], Text[0], 16 );
		LoadString( g_hResourceDLL, iGamepad[btnButton.bOffset], buff, 16 );
		wsprintf(Text[1], buff, btnButton.bOffset - 7 );
		Text[2] = AxeID[2 + btnButton.bAxisID];
		break;
	case DT_KEYBUTTON:
		LoadString( g_hResourceDLL, iDevice[2], Text[0], 16 );
		//TODO: this is great! can we do this for all of them?
		if (btnButton.parentDevice->didHandle->GetObjectInfo(&didoi, btnButton.bOffset, DIPH_BYOFFSET) == DI_OK)
			Text[1] = didoi.tszName;
		else
		LoadString( g_hResourceDLL, IDS_C_UNKNOWN, Text[1], 16 );
		Text[2] = TEXT( "" );
		break;
	case DT_MOUSEBUTTON:
		LoadString( g_hResourceDLL, iDevice[3], Text[0], 16 );
		LoadString( g_hResourceDLL, iMouse[3], Text[1], 16 );
		Text[2] = Btn;
		wsprintf( Btn, TEXT( "%02u" ), btnButton.bOffset );
		break;
	case DT_MOUSEAXE:
		LoadString( g_hResourceDLL, iDevice[3], Text[0], 16 );
		LoadString( g_hResourceDLL, iMouse[btnButton.bOffset], Text[1], 16 );
		Text[2] = AxeID[btnButton.bAxisID];
		break;
	case DT_UNASSIGNED:
	default:
		LoadString( g_hResourceDLL, iDevice[0], Text[0], 16 );
		Text[1] = Text[2] = TEXT( "" );
		bReturn = false;
	}

	wsprintf( Buffer, TEXT( "%s%s%s" ), Text[0], Text[1], Text[2] );
	return ( bReturn );
}

// called when scanning devices to assign N64 buttons
DWORD ScanKeyboard( LPDEVICE lpDevice, LPDWORD lpdwCounter, LPBUTTON pButton )
{
	HRESULT hr;
	BYTE cKeys[256];

	hr = lpDevice->didHandle->GetDeviceState( sizeof( cKeys ), (LPVOID)&cKeys );
	if ( FAILED(hr) )
	{
			lpDevice->didHandle->Acquire();
			return FALSE;
	}

	int iGotKey = FALSE;
	int i = 0;

	for( i = 0; i < ARRAYSIZE( cKeys ); ++i )
	{
		if (( cKeys[i] & 0x80 ) )
		{
			if( i == DIK_ESCAPE )
				iGotKey = SC_SCANESCAPE;
			else
			{
				iGotKey = SC_SCANSUCCEED;
				pButton->bBtnType = (BYTE)DT_KEYBUTTON;
				pButton->bAxisID = (BYTE)0;
				pButton->bOffset = (BYTE)i;
				pButton->parentDevice = lpDevice;
			}
		}
	}
	return iGotKey;
}

// called when scanning devices to assign N64 buttons
DWORD ScanMouse( LPDEVICE lpDevice, LPDWORD lpdwCounter, LPBUTTON pButton )
{
	static BYTE rgbInitButtons[8];
	static bool bFirstScan = true;
	DIMOUSESTATE2 dm_Current;
	HRESULT hr;

	if ( *lpdwCounter == 0 )
		bFirstScan = true;

	hr = lpDevice->didHandle->GetDeviceState( sizeof(DIMOUSESTATE2), &dm_Current );
	if ( FAILED(hr) )
	{
		lpDevice->didHandle->Acquire();
		return FALSE;
	}

	if ( bFirstScan )
	{
		CopyMemory( rgbInitButtons, dm_Current.rgbButtons, sizeof(rgbInitButtons));
		bFirstScan = false;
		lpDevice->didHandle->GetDeviceState( sizeof(DIMOUSESTATE2), &dm_Current );
	}

	int iGotKey = FALSE;
	int i = 0;
	BYTE bAxeDirection = 0;
	long Mouse[] ={		FIELD_OFFSET( DIMOUSESTATE2, lX ) / sizeof(long),
						FIELD_OFFSET( DIMOUSESTATE2, lY ) / sizeof(long),
						FIELD_OFFSET( DIMOUSESTATE2, lZ ) / sizeof(long) };

	long lValue;

	for( i = 0; i < ARRAYSIZE(Mouse); ++i )
	{
		lValue =  ((long*)&dm_Current)[Mouse[i]];

		if( lValue > MOUSE_THRESHOLD )
		{
			iGotKey = SC_SCANSUCCEED;
			bAxeDirection = AI_AXE_P;
		}
		if( lValue < -MOUSE_THRESHOLD )
		{
			iGotKey = SC_SCANSUCCEED;
			bAxeDirection = AI_AXE_N;
		}
		if( iGotKey )
			break;
	}

	if( iGotKey == SC_SCANSUCCEED )
	{
		pButton->bBtnType = (BYTE)DT_MOUSEAXE;
		pButton->bAxisID = (BYTE)bAxeDirection;
		pButton->bOffset = (BYTE)Mouse[i];
		pButton->parentDevice = lpDevice;
	}
	else
	{
		for( i = 0; i < ARRAYSIZE( dm_Current.rgbButtons ); ++i )
		{
			if(( dm_Current.rgbButtons[i] != rgbInitButtons[i] ) && ( dm_Current.rgbButtons[i] & 0x80 ))
			{
				iGotKey = SC_SCANSUCCEED;
				pButton->bBtnType = (BYTE)DT_MOUSEBUTTON;
				pButton->bAxisID = (BYTE)0;
				pButton->bOffset = (BYTE)i;
				pButton->parentDevice = lpDevice;
				break;
			}
		}
	}
	CopyMemory( rgbInitButtons, dm_Current.rgbButtons, sizeof(rgbInitButtons));

	return iGotKey;
}

// called when scanning devices to assign N64 buttons
// tries to read any possible button presses or axes from lpDirectInputDevice; called by ScanDevices
DWORD ScanGamePad ( LPDEVICE lpDevice, LPDWORD lpdwCounter, LPBUTTON pButton, int iDeviceNumber )
{
	static DIJOYSTATE dj_Initial[MAX_DEVICES];
	static bool bFirstScan = true;
	HRESULT hr;

	if ( *lpdwCounter == 0 )
		bFirstScan = true;

	hr = lpDevice->didHandle->GetDeviceState( sizeof(DIJOYSTATE), &lpDevice->stateAs.joyState );
	if ( FAILED(hr) )
	{
		hr = lpDevice->didHandle->Acquire();
		return FALSE;
	}

	if ( bFirstScan )
	{
		dj_Initial[iDeviceNumber] = lpDevice->stateAs.joyState;
		bFirstScan = false;
		return FALSE; // initial scan done; gotta wait until next time
	}

	long lAxePos = ZEROVALUE + ( RANGERELATIVE * CONFIGTHRESHOLD / 100 );
	long lAxeNeg = ZEROVALUE - ( RANGERELATIVE * CONFIGTHRESHOLD / 100 );
	long lValue;

	int iGotKey = FALSE;
	BYTE bAxeDirection = 0;

	int JoyPad[][2] ={	{ FIELD_OFFSET( DIJOYSTATE, lX ) / sizeof(long)	, DT_JOYAXE },
						{ FIELD_OFFSET( DIJOYSTATE, lY ) / sizeof(long)	, DT_JOYAXE },
						{ FIELD_OFFSET( DIJOYSTATE, lZ ) / sizeof(long)	, DT_JOYAXE },
						{ FIELD_OFFSET( DIJOYSTATE, lRx ) / sizeof(long)	, DT_JOYAXE },
						{ FIELD_OFFSET( DIJOYSTATE, lRy ) / sizeof(long)	, DT_JOYAXE },
						{ FIELD_OFFSET( DIJOYSTATE, lRz ) / sizeof(long)	, DT_JOYAXE },
						{ FIELD_OFFSET( DIJOYSTATE, rglSlider[0] ) / sizeof(long)	, DT_JOYSLIDER },
						{ FIELD_OFFSET( DIJOYSTATE, rglSlider[1] ) / sizeof(long)	, DT_JOYSLIDER },
						{ FIELD_OFFSET( DIJOYSTATE, rgdwPOV[0] ) / sizeof(long)	, DT_JOYPOV },
						{ FIELD_OFFSET( DIJOYSTATE, rgdwPOV[1] ) / sizeof(long)	, DT_JOYPOV },
						{ FIELD_OFFSET( DIJOYSTATE, rgdwPOV[2] ) / sizeof(long)	, DT_JOYPOV },
						{ FIELD_OFFSET( DIJOYSTATE, rgdwPOV[3] ) / sizeof(long)	, DT_JOYPOV }	};

	int i;

	for( i = 0; i < ARRAYSIZE( JoyPad ); ++i )
	{
		lValue =  ((long*)&lpDevice->stateAs.joyState)[JoyPad[i][0]];
		if( lValue != ((long*)&(dj_Initial[iDeviceNumber]))[JoyPad[i][0]] )
		{
			if(( JoyPad[i][1] == DT_JOYAXE ) || ( JoyPad[i][1] == DT_JOYSLIDER ))
			{
				if( lValue - ((long*)&(dj_Initial[iDeviceNumber]))[JoyPad[i][0]] > lAxePos )
				{
					iGotKey = SC_SCANSUCCEED;
					bAxeDirection = AI_AXE_P;
					break;
				}
				else if( lValue - ((long*)&(dj_Initial[iDeviceNumber]))[JoyPad[i][0]] < lAxeNeg )
				{
					iGotKey = SC_SCANSUCCEED;
					bAxeDirection = AI_AXE_N;
					break;
				}
			}
			if( JoyPad[i][1] == DT_JOYPOV && LOWORD( lValue ) != 0xFFFF )
			{
				iGotKey = SC_SCANSUCCEED;
				if (( lValue > 31500 ) || ( lValue < 4500 ))
					bAxeDirection = AI_POV_UP;
				if (( lValue > 4500 ) && ( lValue < 13500 ))
					bAxeDirection = AI_POV_RIGHT;
				if (( lValue > 13500 ) && ( lValue < 22500 ))
					bAxeDirection = AI_POV_DOWN;
				if (( lValue > 22500 ) && ( lValue < 31500 ))
					bAxeDirection = AI_POV_LEFT;
				break;
			}
		}
	}

	if( iGotKey == SC_SCANSUCCEED )
	{
		pButton->bBtnType = (BYTE)JoyPad[i][1];
		pButton->bAxisID = (BYTE)bAxeDirection;
		pButton->bOffset = (BYTE)JoyPad[i][0];
		pButton->parentDevice = lpDevice;
	}
	else
	{
		for( i = 0; i < ARRAYSIZE( lpDevice->stateAs.joyState.rgbButtons ); ++i )
		{
			if (( lpDevice->stateAs.joyState.rgbButtons[i] & 0x80 ))
			{
				iGotKey = SC_SCANSUCCEED;
				pButton->bBtnType = (BYTE)DT_JOYBUTTON;
				pButton->bAxisID = (BYTE)0;
				pButton->bOffset = (BYTE)i;
				pButton->parentDevice = lpDevice;
				break;
			}
		}
	}

	return iGotKey;
}

// called when we assign a control to an N64 button
// returns FALSE if no keyscans recorded, SC_SCANESCAPE if escape pressed on keyboard (to abort), or SC_SUCCEED for a good read
// *apDirectInputDevices is an array of only 2 devices; sys keyboard, sys mouse
DWORD ScanDevices( LPDWORD lpdwCounter, LPBUTTON pButton )
{
	// Do ALL our polling first-- this ensures consistency (i.e. every device always gets polled at same interval)
/*	if( g_sysKeyboard.didHandle )
	{
 		if( FAILED(g_sysKeyboard.didHandle->Poll()))
			g_sysKeyboard.didHandle->Acquire();
	} */
	if( g_sysMouse.didHandle )
	{
		if( FAILED(g_sysMouse.didHandle->Poll()))
			g_sysMouse.didHandle->Acquire();
	}
	for (int i = 0; i < g_nDevices; i++)
		if (g_devList[i].didHandle)
			if (FAILED(g_devList[i].didHandle->Poll()))
				g_devList[i].didHandle->Acquire();

	// While we have devices to scan,
	// if it's a keyboard, ScanKeyboard; if it's a mouse, ScanMouse;
	// otherwise ScanGamePad.

	DWORD dwReturn = FALSE;

//	if( !dwReturn && g_sysKeyboard.didHandle )
//		dwReturn = ScanKeyboard( &g_sysKeyboard, lpdwCounter, pButton );

	if( !dwReturn && g_sysMouse.didHandle )
		dwReturn = ScanMouse( &g_sysMouse, lpdwCounter, pButton );

	// ScanGamePad on ALL remaining devices
	for (int i = 0; !dwReturn && i < g_nDevices; i++)
		if (g_devList[i].didHandle)
		{
			switch (LOBYTE(g_devList[i].dwDevType))
			{
			case DI8DEVTYPE_KEYBOARD:
				dwReturn = ScanKeyboard( &g_devList[i], lpdwCounter, pButton );
				break;
			case DI8DEVTYPE_MOUSE:
				dwReturn = ScanMouse( &g_devList[i], lpdwCounter, pButton );
				break;
			default:
				dwReturn = ScanGamePad( &g_devList[i], lpdwCounter, pButton, i );
			}
		}

	return dwReturn;
}

// sets controller defaults; called when user clicks on "Clear Controller" or "Default Config"
// also called before loading stuff from the INI or from profile files, in case something isn't defined
// DEFAULTS TO NOT PLUGGED.  Make sure you "plug" the first controller manually.
void SetControllerDefaults( LPCONTROLLER pcController )
{
	freePakData( pcController );
	freeModifiers( pcController );
	ZeroMemory( pcController, sizeof(CONTROLLER) );

	pcController->fRawData =			true;
	pcController->fRealN64Range =		true;
	pcController->bRapidFireEnabled =	false;
	pcController->bRapidFireRate =		3; // Set default rapid fire rate here
	pcController->bStickRange =			DEFAULT_STICKRANGE;
	pcController->bPadDeadZone =		DEFAULT_DEADZONE;
	pcController->bRumbleTyp =			DEFAULT_RUMBLETYP;
	pcController->bRumbleStrength =		DEFAULT_RUMBLESTRENGTH;
	pcController->wMouseSensitivityX =	DEFAULT_MOUSESENSIVITY;
	pcController->wMouseSensitivityY =	DEFAULT_MOUSESENSIVITY;
	pcController->PakType =				DEFAULT_PAKTYPE;
	pcController->bMouseMoveX =			DEFAULT_MOUSEMOVE;
	pcController->bMouseMoveY =			DEFAULT_MOUSEMOVE;
}

void DeleteControllerSettings( int indexController )
{
	if( !g_ivConfig )
		return;

	SetControllerDefaults( &g_ivConfig->Controllers[indexController] );
	g_ivConfig->FFDevices[indexController].bProductCounter =		0;
	g_ivConfig->FFDevices[indexController].szProductName[0] =		_T('\0');

	return;
}

// SetModifier activates certain "always on" Config modifiers after they've been inserted or loaded
void SetModifier( LPCONTROLLER pcController )
{
	for( int i = 0; i < pcController->nModifiers; i++ )
	{
		if( pcController->pModifiers[i].bModType == MDT_CONFIG && pcController->pModifiers[i].fStatus )
		{
			MODSPEC_CONFIG args;
			args.dwValue = pcController->pModifiers[i].dwSpecific;
			if( args.fChangeAnalogConfig )
			{
				BYTE bConfig = (BYTE)args.fAnalogStickMode;
				if( bConfig < PF_AXESETS )
					pcController->bAxisSet = bConfig;
				else
				{
					if( pcController->bAxisSet == PF_AXESETS-1 )
						pcController->bAxisSet = 0;
					else
						++pcController->bAxisSet;
				}
			}
			if( args.fChangeMouseXAxis )
				if (pcController->bMouseMoveX == MM_BUFF)
					pcController->bMouseMoveX = MM_ABS;
				else if (pcController->bMouseMoveX == MM_ABS)
					pcController->bMouseMoveX = MM_BUFF;
			if( args.fChangeMouseYAxis )
				if (pcController->bMouseMoveY == MM_BUFF)
					pcController->bMouseMoveY = MM_ABS;
				else if (pcController->bMouseMoveY == MM_ABS)
					pcController->bMouseMoveY = MM_BUFF;

			if( args.fChangeKeyboardXAxis )
				pcController->fKeyAbsoluteX = !pcController->fKeyAbsoluteX;
			if( args.fChangeKeyboardYAxis )
				pcController->fKeyAbsoluteY = !pcController->fKeyAbsoluteY;
		}
	}
}

// Copies over the existing g_pcControllers' idea of configuration over to the interface.
void GetCurrentConfiguration()
{
	EnterCriticalSection( &g_critical );

	g_ivConfig->Language = g_strEmuInfo.Language;
	g_ivConfig->fDisplayShortPop = g_strEmuInfo.fDisplayShortPop;

	LPCONTROLLER pcController;
	for( int i = 0; i < 4; i++ )
	{
		pcController = &g_ivConfig->Controllers[i];
		CopyMemory( pcController, &g_pcControllers[i], sizeof(CONTROLLER));

		if( pcController->nModifiers > 0 )
		{
			pcController->pModifiers = (LPMODIFIER)P_malloc( sizeof(MODIFIER) * pcController->nModifiers );
			CopyMemory( pcController->pModifiers, g_pcControllers[i].pModifiers, sizeof(MODIFIER) * pcController->nModifiers );
			SetModifier( pcController );
		}
		else
			pcController->pModifiers = NULL;

		pcController->pPakData = NULL;

		for( int iDevice = 0; iDevice < ARRAYSIZE(g_devList) && g_devList[iDevice].dwDevType; ++iDevice )
		{
			if(	g_devList[iDevice].guidInstance == pcController->guidFFDevice )
			{
				g_ivConfig->FFDevices[i].bProductCounter = g_devList[iDevice].bProductCounter;
				lstrcpyn( g_ivConfig->FFDevices[i].szProductName, g_devList[iDevice].szProductName, MAX_PATH );
				break;
			}
		}
	}

	LeaveCriticalSection( &g_critical );
	return;
}

// Copies over the interface's idea of configuration over to g_pcControllers.
void UpdateControllerStructures()
{
	// bDebug = g_ivConfig->bAutoConfig;
	EnterCriticalSection( &g_critical );

	g_iFirstController = -1;

#ifdef _UNICODE
	// might as well use this time to copy over language info and open the new DLL as well... --rabid
	g_strEmuInfo.Language = g_ivConfig->Language;
	if (g_hResourceDLL != g_strEmuInfo.hinst)
		FreeLibrary(g_hResourceDLL);
	g_hResourceDLL = LoadLanguageDLL(g_strEmuInfo.Language);
	if( g_hResourceDLL == NULL )
	{
		g_strEmuInfo.Language = 0;
		g_hResourceDLL = g_strEmuInfo.hinst;
		DebugWriteA("couldn't load language DLL, falling back to defaults\n");
	}
#endif // #ifdef _UNICODE

	g_strEmuInfo.fDisplayShortPop = g_ivConfig->fDisplayShortPop;

	LPCONTROLLER pcController;
	for( int i = 3; i >= 0; i-- )
	{
		pcController = &g_ivConfig->Controllers[i];

		SaveControllerPak( i );
		CloseControllerPak( i );
//		freePakData( &g_pcControllers[i] );	// called already by CloseControllerPak
		freeModifiers( &g_pcControllers[i] );

		CopyMemory( &g_pcControllers[i], pcController, sizeof(CONTROLLER));
		g_pcControllers[i].pPakData = NULL;
		g_pcControllers[i].pModifiers = NULL;

		if( g_pcControllers[i].nModifiers > 0 )
		{
			g_pcControllers[i].pModifiers = (LPMODIFIER)P_malloc( sizeof(MODIFIER) * g_pcControllers[i].nModifiers );
			CopyMemory( g_pcControllers[i].pModifiers, pcController->pModifiers, sizeof(MODIFIER) * g_pcControllers[i].nModifiers );
			SetModifier( &g_pcControllers[i] );
		}


		int iDevice = FindDeviceinList( g_ivConfig->FFDevices[i].szProductName, g_ivConfig->FFDevices[i].bProductCounter, true );
		if( iDevice != -1 && g_devList[iDevice].bEffType )
		{
			g_pcControllers[i].guidFFDevice = g_devList[iDevice].guidInstance;
		}
		else
		{
			g_pcControllers[i].guidFFDevice = GUID_NULL;
		}

		g_pcControllers[i].fPakCRCError = false;
		g_pcControllers[i].fPakInitialized = false;

		if (g_pcControllers[i].fPlugged)
			g_iFirstController = i;
	}
	LeaveCriticalSection( &g_critical );
	return;
}

LRESULT CALLBACK BlockerProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;

	switch (msg)
	{
		case WM_CREATE:
			return 0;
		case WM_PAINT:
			BeginPaint(hwnd, &ps);
			EndPaint(hwnd, &ps);
			return 0;
		default:
			return DefWindowProc(hwnd, msg, wParam, lParam);
	}
//	return 0;
}

BOOL InitOverlay(void)
{
	// registers our overlay class

    WNDCLASS wc;

    // Fill in the window class structure with parameters
    // that describe the main window.

    wc.style = 0;
    wc.lpfnWndProc = BlockerProc;     // window procedure
    wc.cbClsExtra = 0;                // no extra class memory
    wc.cbWndExtra = 0;                // no extra window memory
    wc.hInstance = g_strEmuInfo.hinst;
    wc.hIcon = 0;
    wc.hCursor = NULL;
    wc.hbrBackground = NULL;
    wc.lpszMenuName =  NULL;
    wc.lpszClassName = _T("BlockerClass");  // name of window class

    // Register the window class.

    return RegisterClass(&wc);
}

HWND MakeOverlay()
{
	HWND hwnd;
	RECT size;

	GetWindowRect(g_hMainDialog, &size);

	// Create the main window.

	hwnd = CreateWindowEx(
		WS_EX_TOPMOST | WS_EX_TRANSPARENT,
		_T("BlockerClass"),
		_T("Blocker"),
		WS_POPUP,
		size.left,				// horizontal position
		size.top,				// vertical position
		size.right - size.left,	// width
		size.bottom - size.top,	// height
		g_hMainDialog,			// owner window
		(HMENU) NULL,			// menu
		g_strEmuInfo.hinst,		// handle to application instance
		(LPVOID) NULL			// window-creation data
	);
	if (!hwnd)
		return NULL;

	// Show the window (necessary to block input)
	ShowWindow(hwnd, SW_SHOWNOACTIVATE);
	return hwnd;
}
