/*	
	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 <dinput.h>
#include "NRagePluginV2.h"
#include "PakIO.h"
#include "DirectInput.h"
#include "XInputController.h"
#include <math.h>

// ProtoTypes //
HRESULT AcquireDevice( LPDIRECTINPUTDEVICE8 lpDirectInputDevice );

// global Variables //
LPDIRECTINPUT8  g_pDIHandle = NULL;													// Base DirectInput8-Handle

//LPDIRECTINPUTDEVICE8 g_apInputDevice[6] = { NULL, NULL, NULL, NULL, NULL, NULL };	// array of Handles for devices
																					// 0:Keyboard, 1:Mouse, the rest are FF gamepads

// BYTE g_acKeystate[256];		// use g_sysKeyboard.stateAs.rgbButtons instead
// DIMOUSESTATE2 g_msMouseState = { 0, 0, 0 };											// Store our mouse state data between reads (because every time we read data, it resets the device data)
		// moved to g_sysMouse.stateAs...

// Update device data tables (so we only have to poll and read the devices once).  This is called by GetKeys and ReadController.
void GetDeviceDatas()
{
	HRESULT hr;

/*	if( g_sysKeyboard.didHandle )
	{
		hr = g_sysKeyboard.didHandle->Poll();
		if( FAILED( hr ))
			AcquireDevice( g_sysKeyboard.didHandle ); // we'll try again next time

		hr = g_sysKeyboard.didHandle->GetDeviceState( sizeof(g_sysKeyboard.stateAs.rgbButtons), &g_sysKeyboard.stateAs.rgbButtons );

		if( FAILED( hr ))
			ZeroMemory( g_sysKeyboard.stateAs.rgbButtons, sizeof(g_sysKeyboard.stateAs.rgbButtons) );
	} */

	if( g_sysMouse.didHandle )
	{
		hr = g_sysMouse.didHandle->Poll();

		if( FAILED( hr ))
			AcquireDevice( g_sysMouse.didHandle ); // we'll try again next time

		hr = g_sysMouse.didHandle->GetDeviceState( sizeof(DIMOUSESTATE2), &g_sysMouse.stateAs.mouseState );

		if( FAILED( hr ))
			ZeroMemory( &g_sysMouse.stateAs.mouseState, sizeof(DIMOUSESTATE2) );
	}

	// need to just poll every damn device we're using
	for( int i = 0; i < g_nDevices; i++ )
	{
		if( g_devList[i].didHandle )
		{
			if( FAILED( g_devList[i].didHandle->Poll() ))
				AcquireDevice( g_devList[i].didHandle ); // we'll try again next time
		
			switch (LOBYTE(g_devList[i].dwDevType))
			{
			case DI8DEVTYPE_KEYBOARD:
				hr = g_devList[i].didHandle->GetDeviceState( sizeof(g_devList[i].stateAs.rgbButtons), g_devList[i].stateAs.rgbButtons );
				break;
			case DI8DEVTYPE_MOUSE:
				hr = g_devList[i].didHandle->GetDeviceState( sizeof(g_devList[i].stateAs.mouseState), &g_devList[i].stateAs.mouseState );
				break;
			default:
				hr = g_devList[i].didHandle->GetDeviceState( sizeof(g_devList[i].stateAs.joyState), &g_devList[i].stateAs.joyState );
			}
		}
		else
			hr = DIERR_NOTACQUIRED;

		if( hr == DIERR_NOTACQUIRED ) // changed this because in the rare condition that we lose input between polling and GetDeviceState we don't want to reset our current controls --rabid
		{
			ZeroMemory( &g_devList[i].stateAs.joyState, sizeof(DEVICE::INPUTSTATE));
			if (g_devList[i].dwDevType != DI8DEVTYPE_KEYBOARD && g_devList[i].dwDevType != DI8DEVTYPE_MOUSE)
				FillMemory( g_devList[i].stateAs.joyState.rgdwPOV, sizeof(g_devList[i].stateAs.joyState.rgdwPOV), 0xFF ); // g_devList[i].stateAs.joyState.rgdwPOV = -1; // -1 is neutral
		}	
	}
}

// hacked up piece of shit, but it works
inline bool GetJoyPadPOV( PDWORD dwDegree, BYTE AxeId )
// TRUE if specified Direction is Pressed
{
	if( LOWORD( *dwDegree ) == 0xFFFF )
		return false;

	bool bPressed;

	switch( AxeId )
	{
	case AI_POV_DOWN:
		bPressed = (( *dwDegree >= 18000 - POVANGLETHRESH ) && (*dwDegree <= 18000 + POVANGLETHRESH ));
		break;
	case AI_POV_LEFT:
		bPressed = (( *dwDegree >= 27000 - POVANGLETHRESH ) && (*dwDegree <= 27000 + POVANGLETHRESH ));
		break;
	case AI_POV_RIGHT:
		bPressed = (( *dwDegree >= 9000 - POVANGLETHRESH ) && (*dwDegree <= 9000 + POVANGLETHRESH ));
		break;
	case AI_POV_UP:
		bPressed = (( *dwDegree >= 36000 - POVANGLETHRESH ) || ( *dwDegree <= 0 + POVANGLETHRESH ));
		break;
	default:
		bPressed = false;
	}
	
	return bPressed;
}

// Fill in button states and axis states for controller indexController, into the struct pdwData.
// pdwData is a pointer to a 4 byte BUTTONS union, if anyone cares
bool GetNControllerInput ( const int indexController, LPDWORD pdwData )
{
	*pdwData = 0;
	WORD w_Buttons = 0;
	// WORD w_Axes = 0;

	LPCONTROLLER pcController = &g_pcControllers[indexController]; // still needs to be here, but not as important --rabid

	bool b_Value;
	long l_Value = 0;

	long lAxisValueX = ZEROVALUE;
	long lAxisValueY = ZEROVALUE;

	// take this info from the N64 controller struct, regardless of input devices
	float d_ModifierX = (float)pcController->bStickRange / 100.0f;
	float d_ModifierY = (float)pcController->bStickRange / 100.0f;

	int i;

	// do N64-Buttons / modifiers
	for (i = 0; i < pcController->nModifiers; i++ )
	{
		BUTTON btnButton = pcController->pModifiers[i].btnButton;

		b_Value = IsBtnPressed( btnButton );

		bool fChangeMod = false;

		if( pcController->pModifiers[i].bModType == MDT_CONFIG )
		{ // Config-Type
			if( pcController->pModifiers[i].fToggle )
			{
				if( b_Value && !btnButton.fPrevPressed)
				{
					pcController->pModifiers[i].fStatus = !pcController->pModifiers[i].fStatus;
					fChangeMod = true;
				}
			}
			else
			{
				if(	b_Value != (bool)(btnButton.fPrevPressed))
					fChangeMod = true;
			}
		}
		else
		{ // Move / Macro Type
			if( pcController->pModifiers[i].fToggle )
			{
				if( b_Value && !btnButton.fPrevPressed )
					pcController->pModifiers[i].fStatus = !pcController->pModifiers[i].fStatus;
				fChangeMod = ( pcController->pModifiers[i].fStatus != 0 );
			}
			else
			{
				fChangeMod = b_Value;
			}
		}

		if( fChangeMod )
		{
			switch( pcController->pModifiers[i].bModType )
			{
			case MDT_MOVE:
			{
				LPMODSPEC_MOVE args = (LPMODSPEC_MOVE)&pcController->pModifiers[i].dwSpecific;
				d_ModifierX *= args->XModification / 100.0f;
				d_ModifierY *= args->YModification / 100.0f;	
			}
				break;
			case MDT_MACRO:
			{
				LPMODSPEC_MACRO args = (LPMODSPEC_MACRO)&pcController->pModifiers[i].dwSpecific;

				if (args->fRapidFire) // w00t! Rapid Fire here
				{
					if ((unsigned) b_Value != btnButton.fPrevPressed) // New macro pressed
					{
						args->fPrevFireState = 0;
						args->fPrevFireState2 = 0;
					}
					if(!args->fPrevFireState) // This round, a firing is needed
					{
						w_Buttons |= args->aButtons;
						if( args->fAnalogRight )
							lAxisValueX += MAXAXISVALUE;
						else if( args->fAnalogLeft )
							lAxisValueX -= MAXAXISVALUE;

						if( args->fAnalogDown )
							lAxisValueY -= MAXAXISVALUE;
						else if( args->fAnalogUp ) // up
								lAxisValueY += MAXAXISVALUE;
					}

					// Ok, update the firing counters here
					if (args->fRapidFireRate) // Do the rapid fire slowly
					{ // Note that this updates State2 before State... Makes a nice slower square-wave type pulse for the update
						args->fPrevFireState2 = (args->fPrevFireState2 + 1) & 1;
						if (!args->fPrevFireState2)
						{
							args->fPrevFireState = (args->fPrevFireState + 1) & 1;
							DebugWriteA("Slow Rapid Fire - Mark 2\n");
						}
					}
					else // Do a fast rapid fire
					{
						args->fPrevFireState = (args->fPrevFireState + 1) & 1;
						DebugWriteA("Fast Rapid Fire\n");
					}
				}
				else
				{
					w_Buttons |= args->aButtons; // Note this: It lets you push buttons as well as the macro buttons
					if( args->fAnalogRight )
						lAxisValueX += MAXAXISVALUE;
					else if( args->fAnalogLeft )
						lAxisValueX -= MAXAXISVALUE;

					if( args->fAnalogDown )
						lAxisValueY -= MAXAXISVALUE;
					else if( args->fAnalogUp ) // up
						lAxisValueY += MAXAXISVALUE;

					args->fPrevFireState = 0;
				}
			}
				break;
			case MDT_CONFIG:
			{
				LPMODSPEC_CONFIG args = (LPMODSPEC_CONFIG)&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;
			}
				break;
			}
		}

		btnButton.fPrevPressed = b_Value;
		pcController->pModifiers[i].btnButton = btnButton;
	} // END N64 MODIFIERS for

	// do N64-Buttons / modifiers
	for( i = 0; i < PF_APADR; i++ )
	{
		BUTTON btnButton = pcController->aButton[i];

		b_Value = IsBtnPressed( btnButton );

		w_Buttons |= (((WORD)b_Value) << i);
	} // END N64 BUTTONS for

	long lDeadZoneValue = pcController->bPadDeadZone * RANGERELATIVE / 100;
	float fDeadZoneRelation	= (float)RANGERELATIVE  / (float)( RANGERELATIVE - lDeadZoneValue );

	// do N64 joystick axes
	for ( i = 0; i < 4; i++ )
	{
		//	0 : right
		//	1 : left
		//	2 : down
		//	3 : up

		bool fNegInput = (( i == 1 ) || ( i == 2 )); // Input has to be negated

		BUTTON btnButton = pcController->aButton[PF_APADR + pcController->bAxisSet * 4 + i];
		LPLONG plRawState = (LPLONG)&btnButton.parentDevice->stateAs.joyState;
		
		switch( btnButton.bBtnType )
		{
		case DT_JOYBUTTON:
			l_Value = MAXAXISVALUE;
			b_Value = ( btnButton.parentDevice->stateAs.joyState.rgbButtons[btnButton.bOffset] & 0x80 ) != 0;
			break;

		case DT_JOYSLIDER:
		case DT_JOYAXE:
			l_Value = plRawState[btnButton.bOffset] - ZEROVALUE;

			if( btnButton.bAxisID ) // negative Range
			{
				fNegInput = !fNegInput;

				b_Value = ( l_Value <= -lDeadZoneValue );
				if( b_Value )
					l_Value = (long) ((float)(l_Value + lDeadZoneValue ) * fDeadZoneRelation );
			}
			else
			{
				b_Value = ( l_Value >= lDeadZoneValue );
				if( b_Value )
					l_Value = (long) ((float)(l_Value - lDeadZoneValue ) * fDeadZoneRelation );
			}	
			break;

		case DT_JOYPOV:
			l_Value = MAXAXISVALUE;
			b_Value = GetJoyPadPOV( (PDWORD)&plRawState[btnButton.bOffset] , btnButton.bAxisID );
			break;

		case DT_KEYBUTTON:
			if( btnButton.parentDevice->stateAs.rgbButtons[btnButton.bOffset] & 0x80 )
			{
				b_Value = true;

				if(( pcController->fKeyAbsoluteX && i < 2 )
					|| ( pcController->fKeyAbsoluteY &&  i > 1 ))
				{
					if( pcController->wAxeBuffer[i] < MAXAXISVALUE )
					{
						l_Value = pcController->wAxeBuffer[i] = min(( pcController->wAxeBuffer[i] + N64DIVIDER*3), MAXAXISVALUE );
					}
					else
						l_Value = MAXAXISVALUE;
				}
				else
				{
					if( pcController->wAxeBuffer[i] < MAXAXISVALUE )
					{
						l_Value = pcController->wAxeBuffer[i] = min(( pcController->wAxeBuffer[i] * 2 + N64DIVIDER*5 ), MAXAXISVALUE );
					}
					else
						l_Value = MAXAXISVALUE;
				}
			}
			else
			{
				if(( pcController->fKeyAbsoluteX && i < 2 )
					|| ( pcController->fKeyAbsoluteY && i > 1 ))
				{
					l_Value = pcController->wAxeBuffer[i];
					b_Value = true;
				}
				else
				{
					if( pcController->wAxeBuffer[i] > N64DIVIDER )
					{
						b_Value = true;
						l_Value = pcController->wAxeBuffer[i] = pcController->wAxeBuffer[i] / 2 ;
					}
					else
						b_Value = false;
				}
			}
			break;

		case DT_MOUSEBUTTON:
			l_Value = MAXAXISVALUE;
			b_Value = ( btnButton.parentDevice->stateAs.mouseState.rgbButtons[btnButton.bOffset] & 0x80 ) != 0;
			break;

		case DT_MOUSEAXE:
			if( i < 2 )
				pcController->wAxeBuffer[i] += plRawState[btnButton.bOffset] * pcController->wMouseSensitivityX * MOUSESCALEVALUE;	// l_Value = btnButton.parentDevice->stateAs.mouseState[btnButton.bOffset];
			else
				pcController->wAxeBuffer[i] += plRawState[btnButton.bOffset] * pcController->wMouseSensitivityY * MOUSESCALEVALUE;	// l_Value = btnButton.parentDevice->stateAs.mouseState[btnButton.bOffset];

			l_Value = pcController->wAxeBuffer[i];

			// wAxeBuffer is positive for axes 0 and 3 if buffer remains, else zero
			// wAxeBuffer is negative for axes 1 and 2 if buffer remains, else zero

			if(( pcController->bMouseMoveX == MM_ABS && i < 2 ) || ( pcController->bMouseMoveY == MM_ABS &&  i > 1 ))
				pcController->wAxeBuffer[i] = min( max( MINAXISVALUE, pcController->wAxeBuffer[i]) , MAXAXISVALUE);
			else if (( pcController->bMouseMoveX == MM_BUFF && i < 2 ) || ( pcController->bMouseMoveY == MM_BUFF &&  i > 1 ))
				pcController->wAxeBuffer[i] = pcController->wAxeBuffer[i] * MOUSEBUFFERDECAY / 100;
			else // "deadpan" mouse
			{
				pcController->wAxeBuffer[i] = 0;
			}

			if( btnButton.bAxisID == AI_AXE_N) // the mouse axis has the '-' flag set
			{
				fNegInput = !fNegInput;

				b_Value = ( l_Value < ZEROVALUE );
			}
			else
			{
				b_Value = ( l_Value > ZEROVALUE );
			}

			break;
		
		case DT_UNASSIGNED:
		default:
			b_Value = false;
		}

		if ( b_Value )
		{
			if ( fNegInput )
				l_Value = -l_Value;
			
			if( i < 2 )
				lAxisValueX += l_Value;
			else
				lAxisValueY += l_Value;
		}
	}

	if( pcController->fKeyboard )
	{
		if( pcController->fKeyAbsoluteX )
		{
			if( pcController->wAxeBuffer[0] > pcController->wAxeBuffer[1] )
			{
				pcController->wAxeBuffer[0] -= pcController->wAxeBuffer[1];
				pcController->wAxeBuffer[1] = 0;
			}
			else
			{
				pcController->wAxeBuffer[1] -= pcController->wAxeBuffer[0];
				pcController->wAxeBuffer[0] = 0;
			}
		}
		if( pcController->fKeyAbsoluteY )
		{
			if( pcController->wAxeBuffer[2] > pcController->wAxeBuffer[3] )
			{
				pcController->wAxeBuffer[2] -= pcController->wAxeBuffer[3];
				pcController->wAxeBuffer[3] = 0;
			}
			else
			{
				pcController->wAxeBuffer[3] -= pcController->wAxeBuffer[2];
				pcController->wAxeBuffer[2] = 0;
			}
		}
	}


	if (pcController->bRapidFireEnabled)
	{
		if (pcController->bRapidFireCounter >= pcController->bRapidFireRate)
		{
			w_Buttons = (w_Buttons & 0xFF1F);
			pcController->bRapidFireCounter = 0;
		}
		else
		{
			pcController->bRapidFireCounter = pcController->bRapidFireCounter + 1;
		}
	}

	if( pcController->fRealN64Range && ( lAxisValueX || lAxisValueY ))
	{
		long lAbsoluteX = ( lAxisValueX > 0 ) ? lAxisValueX : -lAxisValueX;
		long lAbsoluteY = ( lAxisValueY > 0 ) ? lAxisValueY : -lAxisValueY;

		long lRangeX;
		long lRangeY;

		if(	lAbsoluteX > lAbsoluteY )
		{
			lRangeX = MAXAXISVALUE;
			lRangeY = lRangeX * lAbsoluteY / lAbsoluteX;
		}
		else
		{
			lRangeY = MAXAXISVALUE;
			lRangeX = lRangeY * lAbsoluteX / lAbsoluteY;
		}

		// TODO: optimize this --rabid
		double dRangeDiagonal = sqrt((double)(lRangeX * lRangeX + lRangeY * lRangeY));
//		__asm{
//			fld fRangeDiagonal
//			fsqrt
//			fstp fRangeDiagonal
//			fwait
//		}
		double dRel = MAXAXISVALUE / dRangeDiagonal;

		*pdwData = MAKELONG(w_Buttons,
							MAKEWORD(	(BYTE)(min( max( MINAXISVALUE, (long)(lAxisValueX * d_ModifierX * dRel )), MAXAXISVALUE) / N64DIVIDER ),
										(BYTE)(min( max( MINAXISVALUE, (long)(lAxisValueY * d_ModifierY * dRel )), MAXAXISVALUE) / N64DIVIDER )));
	}
	else
	{
		*pdwData = MAKELONG(w_Buttons,
							MAKEWORD(	(BYTE)(min( max( MINAXISVALUE, (long)(lAxisValueX * d_ModifierX )), MAXAXISVALUE) / N64DIVIDER ),
										(BYTE)(min( max( MINAXISVALUE, (long)(lAxisValueY * d_ModifierY )), MAXAXISVALUE) / N64DIVIDER )));
	}

	return true;
}

bool InitDirectInput( HWND hWnd )
{
	if( g_hDirectInputDLL == NULL )
		g_hDirectInputDLL = LoadLibrary( _T( "dinput8.dll" ));
	if( g_hDirectInputDLL == NULL )
	{
		ErrorMessage(IDS_ERR_DINOTFOUND, 0, false);
	}
	else if( !g_pDIHandle ) // is NULL if not yet initialized
	{
		HRESULT (WINAPI *lpGetDIHandle)( HINSTANCE, DWORD, REFIID, LPVOID*, LPUNKNOWN ) = NULL;
		lpGetDIHandle = (HRESULT (WINAPI *)( HINSTANCE, DWORD, REFIID, LPVOID*, LPUNKNOWN ))GetProcAddress( g_hDirectInputDLL, "DirectInput8Create" );

		if( lpGetDIHandle != NULL )
		{
			HRESULT hr;
			hr = lpGetDIHandle( g_strEmuInfo.hinst, DIRECTINPUT_VERSION, 
								IID_IDirectInput8, (LPVOID*)&g_pDIHandle, NULL );
			if( FAILED( hr ))
			{
				ErrorMessage(IDS_ERR_DICREATE, 0, false);
				g_pDIHandle = NULL;
				FreeLibrary( g_hDirectInputDLL );
				g_hDirectInputDLL = NULL;
			}
		}
	}

	return (g_pDIHandle != NULL);
}

// release a DirectInput device.  We don't need it anymore.
void ReleaseDevice( LPDIRECTINPUTDEVICE8 &lpDirectInputDevice )
{
	if( lpDirectInputDevice != NULL)
	{
		lpDirectInputDevice->Unacquire();
		lpDirectInputDevice->Release();
		lpDirectInputDevice = NULL;
	}
	return;
}

// release a DirectInput effect.  We don't need it anymore.
void ReleaseEffect( LPDIRECTINPUTEFFECT &lpDirectEffect )
{
	if( lpDirectEffect != NULL)
	{
		// should unload the effect on release, I hope
		lpDirectEffect->Release();
		lpDirectEffect = NULL;
	}
	return;
}

// release our DirectInput effects and devices, our DirectInput handle, and then unload dinput library
void FreeDirectInput ()
{
	int i;
	// release effects
	for( i = 0; i < ARRAYSIZE( g_apdiEffect ); ++i )
		ReleaseEffect( g_apdiEffect[i] );
	ZeroMemory( g_apdiEffect, sizeof(g_apdiEffect) );

	// release FF devices
	for( i = 0; i << ARRAYSIZE(g_apFFDevice); ++i )
		ReleaseDevice( g_apFFDevice[i] );
	ZeroMemory( g_apFFDevice, sizeof(g_apFFDevice) );

	// release normal devices
	for( i = 0; i < g_nDevices; i++ )
		ReleaseDevice( g_devList[i].didHandle );
	ZeroMemory( g_devList, sizeof(g_devList) );
	g_nDevices = 0;

	// release mouse device
	ReleaseDevice( g_sysMouse.didHandle );
	ZeroMemory( &g_sysMouse, sizeof(g_sysMouse) );

    // Release any DirectInput handles.
	if( g_pDIHandle != NULL )
	{
		g_pDIHandle->Release();
		g_pDIHandle = NULL;
	}
	// Unload the library.
	if( g_hDirectInputDLL != NULL )
	{
		FreeLibrary( g_hDirectInputDLL );
		g_hDirectInputDLL = NULL;
	}
	return;
}

// Acquire our device.  Our device might get unacquired for many many reasons, and we need to be able to reacquire it to get input again.
// We use this a LOT.
inline HRESULT AcquireDevice( LPDIRECTINPUTDEVICE8 lpDirectInputDevice )
{
	HRESULT hResult = lpDirectInputDevice->Acquire();
	while( hResult == DIERR_INPUTLOST )
		hResult = lpDirectInputDevice->Acquire();
	if( SUCCEEDED( hResult ))
		lpDirectInputDevice->Poll();
	return hResult;
}

// Called by the DirectInput enumerator for each FF device. What kind of force feedback effects does this device support?  We'll store it in pvRef.
BOOL CALLBACK EnumGetEffectTypes( LPCDIEFFECTINFO pdei, LPVOID pvRef )
{
	BYTE bFFType = *(LPBYTE)pvRef;
	bFFType |= ( pdei->dwEffType & DIEFT_CONSTANTFORCE )	? RUMBLE_CONSTANT : 0;
	bFFType |= ( pdei->dwEffType & DIEFT_RAMPFORCE )		? RUMBLE_RAMP : 0;
	bFFType |= ( pdei->dwEffType & DIEFT_CONDITION )		? RUMBLE_CONDITION : 0;
	bFFType |= ( pdei->dwEffType & DIEFT_PERIODIC )			? RUMBLE_PERIODIC : 0;
	bFFType |= ( pdei->dwEffType & DIEFT_CUSTOMFORCE )		? RUMBLE_CUSTOM : 0;
	*(WORD*)pvRef = bFFType;
	return DIENUM_CONTINUE;
}

// Called by the DirectInput enumerator for each attached DI device.  We use it to make a list of devices.
// EnumMakeDeviceList has been rewritten. --rabid
BOOL CALLBACK EnumMakeDeviceList( LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef )
{
	if( IsXInputDevice( &lpddi->guidProduct ) )		// Check if is XInput device --tecnicors
        return DIENUM_CONTINUE;

	if (IsEqualGUID(g_sysMouse.guidInstance, lpddi->guidInstance))
		return DIENUM_CONTINUE;

	for (int i = 0; i < g_nDevices; i++)
		if (IsEqualGUID(g_devList[i].guidInstance, lpddi->guidInstance))
			return ( g_nDevices < ARRAYSIZE(g_devList) ) ? DIENUM_CONTINUE : DIENUM_STOP;

	if (g_nDevices < ARRAYSIZE(g_devList)) // our buffer isn't full yet and the device doesn't already exist in our table
	{
		lstrcpyn( g_devList[g_nDevices].szProductName, lpddi->tszProductName, MAX_PATH );
		g_devList[g_nDevices].dwDevType = lpddi->dwDevType;
		g_devList[g_nDevices].guidInstance = lpddi->guidInstance;

		g_devList[g_nDevices].bProductCounter = 0; // counting similar devices
		for( int i = 0; i < g_nDevices; ++i )
		{
			if( !lstrcmp( lpddi->tszProductName, g_devList[i].szProductName ))
			{
				if( g_devList[g_nDevices].bProductCounter == 0 )
				{
					g_devList[g_nDevices].bProductCounter = 2;
					if( g_devList[i].bProductCounter == 0 )
						g_devList[i].bProductCounter = 1;
				}
				else
					g_devList[g_nDevices].bProductCounter++; // give em instance numbers
			}
		}
		if( !lstrcmp( lpddi->tszProductName, TEXT( STRING_ADAPTOID )))
			g_devList[g_nDevices].bEffType = RUMBLE_DIRECT;
		else
			g_devList[g_nDevices].bEffType = RUMBLE_NONE;

		if ( GetInputDevice(g_strEmuInfo.hMainWindow, g_devList[g_nDevices].didHandle, lpddi->guidInstance, lpddi->dwDevType, DIB_DEVICE) )
		{
			g_devList[g_nDevices].didHandle->EnumEffects( EnumGetEffectTypes, &g_devList[g_nDevices].bEffType, DIEFT_ALL );		
			g_nDevices++;
		}
		else
			ZeroMemory(&g_devList[g_nDevices], sizeof(DEVICE));
	}

	return ( g_nDevices < ARRAYSIZE(g_devList) ) ? DIENUM_CONTINUE : DIENUM_STOP;
}

// Called by an enumerator in GetInputDevice to determine if a device is attached at all.
BOOL CALLBACK EnumIsDeviceAvailable( LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef )
{
	if( lpddi->guidInstance == *(GUID*)((LPVOID*)pvRef)[0] )
	{
		*(bool*)((LPVOID*)pvRef)[1] = true;
		return DIENUM_STOP;
	}

	return DIENUM_CONTINUE;
}

// Called by an axis enumerator in GetInputDevice.  Set the min and max range so we can pass the rec'd value right to the emulator.
BOOL CALLBACK EnumSetObjectsAxis( LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef)
{
	LPDIRECTINPUTDEVICE8 lpDirectInputDevice = (LPDIRECTINPUTDEVICE8)pvRef;
	DIPROPRANGE diprg; 

	diprg.diph.dwSize       = sizeof(DIPROPRANGE);
	diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
	diprg.diph.dwHow        = DIPH_BYID;
	diprg.diph.dwObj        = lpddoi->dwType;
	diprg.lMin              = MINAXISVALUE;
	diprg.lMax              = MAXAXISVALUE;

	lpDirectInputDevice->SetProperty(DIPROP_RANGE, &diprg.diph); // HACK: Usually works, but not all devices support setting range.

	return DIENUM_CONTINUE;
}

// **if passed an existing DirectInputDevice which matches gGuid
//		unacquires it, and sets its cooperative level (reinitialize)
// **if the existing device does not match the passed gGuid, the existing device is released
// **if no device was passed or gGuid did not match
//		searches for the controller matching gGuid in connected and available devices
//		creates a DirectInputDevice
//		sets its data format
//		sets its cooperative level
//		for joysticks, calls EnumSetObjectsAxis for each axis
// GetInputDevice always leaves the returned device in an UNACQUIRED state.
bool GetInputDevice( HWND hWnd, LPDIRECTINPUTDEVICE8 &lpDirectInputDevice, GUID gGuid, DWORD dwDevType, DWORD dwCooperativeLevel )
{
	DebugWriteA("GetInputDevice: gGuid is {%08.8lX-%04.4hX-%04.4hX-%02.2X%02.2X-%02.2X%02.2X%02.2X%02.2X%02.2X%02.2X}\n", gGuid.Data1, gGuid.Data2, gGuid.Data3, gGuid.Data4[0], gGuid.Data4[1], gGuid.Data4[2], gGuid.Data4[3], gGuid.Data4[4], gGuid.Data4[5], gGuid.Data4[6], gGuid.Data4[7]);
	if( lpDirectInputDevice != NULL)
	{
		DIDEVICEINSTANCE didDev;
		didDev.dwSize = sizeof(DIDEVICEINSTANCE);
		lpDirectInputDevice->GetDeviceInfo( &didDev );

		if( didDev.guidInstance == gGuid )
		{	// we've already gotten this device; unacquire it and initialize
			DebugWriteA("GetInputDevice: already created, attempting to reinit\n");
			lpDirectInputDevice->Unacquire();
			lpDirectInputDevice->SetCooperativeLevel( hWnd, dwCooperativeLevel );
			return true;
		}
		else
			ReleaseDevice( lpDirectInputDevice );
	}

	HRESULT hResult;
	
	LPCDIDATAFORMAT ppDiDataFormat = NULL;
	bool Success = false;

	switch( LOBYTE(dwDevType) )
	{
	case DI8DEVTYPE_KEYBOARD:
		ppDiDataFormat = &c_dfDIKeyboard;
		break;

	case DI8DEVTYPE_MOUSE:
		ppDiDataFormat = &c_dfDIMouse2;
		break;

	//case DI8DEVTYPE_GAMEPAD:
	//case DI8DEVTYPE_JOYSTICK:
	//case DI8DEVTYPE_DRIVING:
	//case DI8DEVTYPE_1STPERSON:
	//case DI8DEVTYPE_FLIGHT:
	default: // assume everything else is a gamepad; probably not the best idea but it works
		ppDiDataFormat = &c_dfDIJoystick;
		break;
	}

	bool bDeviceAvailable = false;
		
	VOID* aRef[2] = { &gGuid, &bDeviceAvailable };
		
	// for each available device in our dwDevType category, run EnumIsDeviceAvailable with params "aRef"
	g_pDIHandle->EnumDevices( DI8DEVCLASS_ALL, EnumIsDeviceAvailable, (LPVOID)aRef, DIEDFL_ATTACHEDONLY );
		
	if( !bDeviceAvailable )
	{
		DebugWriteA("GetInputDevice: Device does not appear available\n");
		return false;
	}
		
	hResult = g_pDIHandle->CreateDevice( gGuid, &lpDirectInputDevice, NULL );
	
	if( SUCCEEDED( hResult ))
	{
		hResult = lpDirectInputDevice->SetDataFormat( ppDiDataFormat );
		hResult = lpDirectInputDevice->SetCooperativeLevel( hWnd, dwCooperativeLevel );
		
		Success = SUCCEEDED( hResult );
		if (!Success)
		{
			DebugWriteA("GetInputDevice: SetCooperativeLevel failed\n");
		}
	}
	else
		DebugWriteA("GetInputDevice: CreateDevice failed\n");

	if( Success && ( ppDiDataFormat == &c_dfDIJoystick ))
		lpDirectInputDevice->EnumObjects( EnumSetObjectsAxis, lpDirectInputDevice, DIDFT_AXIS );

	return Success;
}

// How many force feedback axes (motors) does our device have?  We want to rumble them all.
BOOL CALLBACK EnumCountFFAxes( LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pnAxes )
{
	*(DWORD*)pnAxes += 1;
	return DIENUM_CONTINUE;
}

// Create a force feedback effect handle and downloads the effect.  Parameters are self-explanatory.
bool CreateEffectHandle( HWND hWnd, LPDIRECTINPUTDEVICE8 lpDirectInputDevice, LPDIRECTINPUTEFFECT &pDIEffect, BYTE bRumbleTyp, long lStrength )
{
	if( pDIEffect )
		ReleaseEffect( pDIEffect );

	if( !lpDirectInputDevice || bRumbleTyp == RUMBLE_DIRECT )
		return false;	

	DWORD nAxes = 0;
	DWORD rgdwAxes[] = { DIJOFS_X, DIJOFS_Y };

	HRESULT hResult;

	// count the FF - axes of the joystick
	lpDirectInputDevice->EnumObjects( EnumCountFFAxes, &nAxes, DIDFT_FFACTUATOR | DIDFT_AXIS );

	if( nAxes == 0 )
		return false;
	nAxes = min( nAxes, 2 );


	// Must be unaquired for setting stuff like Co-op Level
	hResult = lpDirectInputDevice->Unacquire();
	//FF Requires EXCLUSIVE LEVEL, took me hours to find the reason why it wasnt working
	hResult = lpDirectInputDevice->SetCooperativeLevel( hWnd, DIB_FF );

	// fail if we can't set coop level --rabid
	if (hResult != DI_OK)
	{
		DebugWriteA("CreateEffectHandle: couldn't set coop level: %08X\n", hResult);
		return false;
	}

    // Since we will be playing force feedback effects, we should disable the
    // auto-centering spring.
	DIPROPDWORD dipdw;
    dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    dipdw.diph.dwObj        = 0;
    dipdw.diph.dwHow        = DIPH_DEVICE;
    dipdw.dwData            = FALSE;
	
	hResult = lpDirectInputDevice->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph );

	long rglDirection[] = { 1, 1 };
	LPGUID EffectGuid;
    DIEFFECT eff;
    ZeroMemory( &eff, sizeof(eff) );

	eff.dwSize                  = sizeof(DIEFFECT);
	eff.dwFlags                 = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
	eff.dwGain                  = lStrength * 100;
    eff.dwTriggerButton         = DIEB_NOTRIGGER;
    eff.dwTriggerRepeatInterval = 0;
    eff.cAxes                   = nAxes; //Number of Axes
    eff.rgdwAxes                = rgdwAxes;
    eff.rglDirection            = rglDirection;
    eff.lpEnvelope              = NULL;
	eff.dwStartDelay            = 0;

	DICONSTANTFORCE cf;
	DIRAMPFORCE rf;
	DIPERIODIC pf;

	switch( bRumbleTyp )
	{
	case RUMBLE_CONSTANT:
		EffectGuid = (GUID*)&GUID_ConstantForce;

		eff.dwDuration              = 150000; // microseconds
		eff.dwSamplePeriod          = 0;
		eff.cbTypeSpecificParams    = sizeof(DICONSTANTFORCE);
		eff.lpvTypeSpecificParams   = &cf;

		cf.lMagnitude = 10000;
		break;
	case RUMBLE_RAMP:
		EffectGuid = (GUID*)&GUID_RampForce;
		
		eff.dwDuration              = 300000; // microseconds
		eff.dwSamplePeriod          = 0;
		eff.cbTypeSpecificParams    = sizeof(DIRAMPFORCE);
		eff.lpvTypeSpecificParams   = &rf;

		rf.lStart = 10000;
		rf.lEnd = 2000;
		break;
	
	case RUMBLE_CONDITION:
	case RUMBLE_PERIODIC:
		EffectGuid = (GUID*)&GUID_Sine;

		eff.dwDuration              = 150000; // microseconds
		eff.dwSamplePeriod          = 0;
		eff.cbTypeSpecificParams    = sizeof(DIPERIODIC);
		eff.lpvTypeSpecificParams   = &pf;

		pf.dwMagnitude = 10000;
		pf.lOffset = 0;
		pf.dwPhase = 0;
		pf.dwPeriod = 2000;
		break;

	case RUMBLE_NONE:
	case RUMBLE_CUSTOM:
	default:
		return false;
	}

	hResult = lpDirectInputDevice->CreateEffect( *EffectGuid, &eff, &pDIEffect, NULL );

	if (hResult == DI_OK)
	{
		hResult = lpDirectInputDevice->Acquire();
		hResult = pDIEffect->Download();
	}
	else
	{
		DebugWriteA("CreateEffectHandle: didn't CreateEffect: %08X\n", hResult);
	}
	return SUCCEEDED( hResult );
}

// Counts how many of each type of button assignment (keyboard, mouse, gamepad)
DWORD CountControllerStructDevs( CONTROLLER *pController )
{
	BYTE bMouse = 0, bKeyboard = 0, bGamePad = 0;
	BYTE bDType;

	bool fModifier = ( pController->pModifiers != NULL );
	int i = ( fModifier ) ? pController->nModifiers : ARRAYSIZE( pController->aButton );
	i--;

	for( ; i >= 0 || fModifier; --i )
	{
		if( i < 0 )
		{
			i = ARRAYSIZE( pController->aButton ) - 1;
			fModifier = false;
		}

		bDType = ( fModifier )	? pController->pModifiers[i].btnButton.bBtnType
								: pController->aButton[i].bBtnType;

		switch( bDType )
		{
		case DT_JOYBUTTON:
		case DT_JOYAXE:
		case DT_JOYSLIDER:
		case DT_JOYPOV:
			++bGamePad;
			break;

		case DT_KEYBUTTON:
			++bKeyboard;
			break;

		case DT_MOUSEBUTTON:
		case DT_MOUSEAXE:
			++bMouse;
			break;
		}
	}

	pController->fGamePad	= bGamePad != 0;
	pController->fKeyboard	= bKeyboard != 0;
	pController->fMouse		= bMouse != 0;

	return MAKELONG( MAKEWORD( bGamePad, bMouse ), MAKEWORD( bKeyboard, ( bMouse + bKeyboard + bGamePad )));
}


// PrepareInputDevices rewritten --rabid
bool PrepareInputDevices()
{
	bool fKeyboard = false;
	bool fMouse = false;
	bool fGamePad = false;

	for( int i = 0; i < ARRAYSIZE( g_pcControllers ); ++i )
	{
		fGamePad = false;
		if( g_pcControllers[i].fPlugged )
		{
			CountControllerStructDevs( &g_pcControllers[i] );

			fKeyboard = g_pcControllers[i].fKeyboard != 0;
			fMouse = g_pcControllers[i].fMouse != 0;
			fGamePad = ( g_pcControllers[i].fGamePad != 0); // we'll assume for now that there's a gamepad to go with those buttons
		}

		ReleaseEffect( g_apdiEffect[i] );
		if( g_pcControllers[i].guidFFDevice != GUID_NULL && GetInputDevice( g_strEmuInfo.hMainWindow, g_apFFDevice[i], g_pcControllers[i].guidFFDevice, DI8DEVTYPE_JOYSTICK, DIB_FF )) // not necessarily a joystick type device, but we don't use the data anyway
		{
			DIDEVICEINSTANCE diDev;
			diDev.dwSize = sizeof( DIDEVICEINSTANCE );

			g_apFFDevice[i]->GetDeviceInfo( &diDev );

			if( !lstrcmp( diDev.tszProductName, _T( STRING_ADAPTOID )))
			{
				g_pcControllers[i].fIsAdaptoid = true;
				DebugWriteA("FF device on controller %d is of type Adaptoid\n", i+1);
			}
			else
			{
				g_pcControllers[i].fIsAdaptoid = false;
			}

			if ( CreateEffectHandle( i, g_pcControllers[i].bRumbleTyp, g_pcControllers[i].bRumbleStrength ) )
			{
				AcquireDevice( g_apFFDevice[i] );
				DebugWriteA("Got FF device %d\n", i);
			}
			else
				DebugWriteA("Couldn't get FF device: CreateEffectHandle failed!\n");
		}
		else
		{
			g_apFFDevice[i] = NULL;
			DebugWriteA("Didn't get FF device %d\n", i);
		}
	}

	if( fMouse )
	{
		if( !g_sysMouse.didHandle )
		{
			if( GetInputDevice( g_strEmuInfo.hMainWindow, g_sysMouse.didHandle, GUID_SysMouse, DI8DEVTYPE_MOUSE, g_bExclusiveMouse ? DIB_MOUSE : DIB_KEYBOARD ))
			{
				AcquireDevice( g_sysMouse.didHandle );
			}
		}
	}
	else
	{
		g_bExclusiveMouse = false;
	}

	return true;
}

bool IsAdaptoidCommandSupported( LPDIRECTINPUTDEVICE8 lpDirectInputDevice, DWORD cmd )
{
    DIEFFESCAPE esc;
    DWORD inbuf, outbuf;
    HRESULT hr;

	esc.dwSize = sizeof(esc);
	esc.dwCommand = ADAPT_TEST;   // command to determine if a command is supported
	esc.lpvInBuffer = &inbuf;
	esc.cbInBuffer = 4;
	esc.lpvOutBuffer = &outbuf;   
	esc.cbOutBuffer = 4;
	inbuf = cmd;                  // command that we are asking is supported
	outbuf = 0;                   

	hr = lpDirectInputDevice->Escape(&esc);

    return( SUCCEEDED(hr) && esc.cbOutBuffer == 4 && outbuf == 0xB0CAB0CA );
}

#ifdef _DEBUG
// Direct Adaptoid debugging stuff.
void _debugAd( LPCSTR szMessage, HRESULT res )
{
	LPCSTR suc = (SUCCEEDED(res)) ? "OK" : "FAILED";

	DebugWriteA( "%s: %s (RC:%08X)\n", szMessage, suc, res );
}
#endif // #ifdef _DEBUG

HRESULT DirectRumbleCommand( LPDIRECTINPUTDEVICE8 lpDirectInputDevice, DWORD cmd )
{
    DIEFFESCAPE esc;

    esc.dwSize = sizeof(esc);
    esc.dwCommand = ADAPT_RUMBLE;   // send rumble command
    esc.lpvInBuffer = &cmd;  // 1=go, 0=stop
    esc.cbInBuffer = 4;
    esc.lpvOutBuffer = NULL;
    esc.cbOutBuffer = 0;

	HRESULT hr = lpDirectInputDevice->Escape(&esc);

#ifdef _DEBUG
	_debugAd( "Direct Adaptoid RumbleCommand", hr );
#endif // #ifdef _DEBUG

    return hr;
}

HRESULT InitializeAdaptoid( LPDIRECTINPUTDEVICE8 lpDirectInputDevice, LPBYTE status )
{
    DIEFFESCAPE esc;

    esc.dwSize = sizeof(esc);
    esc.dwCommand = ADAPT_INIT;   // Initialize Pak
    esc.lpvInBuffer = NULL;
    esc.cbInBuffer = 0;
    esc.lpvOutBuffer = status;   
    esc.cbOutBuffer = 1;

	HRESULT hr = lpDirectInputDevice->Escape(&esc);

#ifdef _DEBUG
	_debugAd( "Direct Adaptoid InitPak", hr );
#endif // #ifdef _DEBUG

    return hr;
}

HRESULT ReadAdaptoidPak( LPDIRECTINPUTDEVICE8 lpDirectInputDevice, DWORD addr, LPBYTE data )
{
    DIEFFESCAPE esc;

    esc.dwSize = sizeof(esc);
    esc.dwCommand = ADAPT_READPAK;   // Read 32 bytes from pak
    esc.lpvInBuffer = &addr;
    esc.cbInBuffer = 4;
    esc.lpvOutBuffer = data;   
    esc.cbOutBuffer = 32;

	HRESULT hr = lpDirectInputDevice->Escape(&esc);

#ifdef _DEBUG
	LPCSTR suc = (SUCCEEDED(hr)) ? "OK" : "FAILED";

	DebugWriteA( "Direct Adaptoid ReadPak(Addr:%04X): %s (RC:%08X)\n", addr, suc, hr );
#endif // #ifdef _DEBUG

    return hr;
}

HRESULT WriteAdaptoidPak( LPDIRECTINPUTDEVICE8 lpDirectInputDevice, DWORD addr, LPBYTE data )
{
    DIEFFESCAPE esc;
    struct
    {
        DWORD addr;
        BYTE data[32];
    } buf;

    buf.addr = addr;
    CopyMemory( buf.data, data, 32 );

    esc.dwSize = sizeof(esc);
    esc.dwCommand = ADAPT_WRITEPAK;   // Write 32 bytes to pak
    esc.lpvInBuffer = &buf;
    esc.cbInBuffer = 36;
    esc.lpvOutBuffer = NULL;   
    esc.cbOutBuffer = 0;
	
	HRESULT hr = lpDirectInputDevice->Escape(&esc);

#ifdef _DEBUG
	LPCSTR suc = (SUCCEEDED(hr)) ? "OK" : "FAILED";

	DebugWriteA( "Direct Adaptoid WritePak(Addr:%04X): %s (RC:%08X)\n", addr, suc, hr );
#endif // #ifdef _DEBUG

    return hr;
}

BYTE GetAdaptoidStatus( LPDIRECTINPUTDEVICE8 lpDirectInputDevice )
{
	HRESULT hr;
	BYTE bStatus = 0;
	hr = InitializeAdaptoid( lpDirectInputDevice, &bStatus );

	int iRetrys = 10;
	while( FAILED( hr ) && iRetrys > 0 )
	{
		Sleep( 5 );
		hr = AcquireDevice( lpDirectInputDevice );
		hr = InitializeAdaptoid( lpDirectInputDevice, &bStatus );
		iRetrys--;
	}

	return (SUCCEEDED(hr)) ? bStatus : RD_NOPLUGIN | RD_NOTINITIALIZED;
}

// Fill the handle for g_sysMouse properly
void InitMouse()
{
	if (GetInputDevice( g_strEmuInfo.hMainWindow, g_sysMouse.didHandle, GUID_SysMouse, DI8DEVTYPE_MOUSE, DIB_KEYBOARD ))
	{
		g_sysMouse.guidInstance = GUID_SysMouse;
		g_sysMouse.dwDevType = DI8DEVTYPE_MOUSE;
		_tcsncpy(g_sysMouse.szProductName, STRING_GUID_SYSMOUSE, ARRAYSIZE(g_sysMouse.szProductName));
	}
	else
		g_sysMouse.didHandle = NULL;
}

// treat btnButton as a b_Value, and return whether it is pressed or not
bool IsBtnPressed(BUTTON btnButton)
{
	long l_Value;
	LPLONG plRawState = (LPLONG)&btnButton.parentDevice->stateAs.joyState;

	switch ( btnButton.bBtnType )
	{
	case DT_JOYBUTTON:
		return ( btnButton.parentDevice->stateAs.joyState.rgbButtons[btnButton.bOffset] & 0x80 ) != 0;

	case DT_JOYSLIDER:
	case DT_JOYAXE:
		l_Value = plRawState[btnButton.bOffset] - ZEROVALUE;

		if ( btnButton.bAxisID )
			return ( l_Value <= -ABSTHRESHOLD );
		else
			return ( l_Value >= ABSTHRESHOLD );

	case DT_JOYPOV:
		return GetJoyPadPOV( (PDWORD)&plRawState[btnButton.bOffset] , btnButton.bAxisID );

	case DT_KEYBUTTON:
		return ( btnButton.parentDevice->stateAs.rgbButtons[btnButton.bOffset] & 0x80 ) != 0;

	case DT_MOUSEBUTTON:
		return ( btnButton.parentDevice->stateAs.mouseState.rgbButtons[btnButton.bOffset] & 0x80 ) != 0;

	case DT_MOUSEAXE:
		l_Value = MOUSEMOVE; // a.k.a. lvalue is button threshold

		if( btnButton.bAxisID )
			return ( ((LPLONG)(&btnButton.parentDevice->stateAs.mouseState))[btnButton.bOffset] < -l_Value );
		else
			return ( ((LPLONG)(&btnButton.parentDevice->stateAs.mouseState))[btnButton.bOffset] > l_Value );
		break;

	case DT_UNASSIGNED:
	default:
		return false;
	}
}
