project64/Source/nragev20/DirectInput.cpp

1313 lines
44 KiB
C++
Raw Normal View History

2020-02-09 11:36:49 +00:00
/*
N-Rage`s Dinput8 Plugin
(C) 2002, 2006 Norbert Wladyka
2016-01-27 09:11:59 +00:00
Author`s Email: norbert.wladyka@chello.at
Website: http://go.to/nrage
2016-01-27 09:11:59 +00:00
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.
2016-01-27 09:11:59 +00:00
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.
2016-01-27 09:11:59 +00:00
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
2016-01-27 09:11:59 +00:00
*/
#include <algorithm>
#include <math.h>
2016-01-27 09:11:59 +00:00
2020-02-10 09:57:07 +00:00
#include <initguid.h>
#include <cguid.h>
2016-01-27 09:11:59 +00:00
#include <dinput.h>
#include "commonIncludes.h"
#include "DirectInput.h"
2016-01-27 09:11:59 +00:00
#include "NRagePluginV2.h"
#include "PakIO.h"
#include "XInputController.h"
using std::min;
using std::max;
2016-01-27 09:11:59 +00:00
// Prototypes
2016-01-27 09:11:59 +00:00
HRESULT AcquireDevice( LPDIRECTINPUTDEVICE8 lpDirectInputDevice );
// Global variables
2020-02-09 11:36:49 +00:00
LPDIRECTINPUT8 g_pDIHandle = NULL; // Base DirectInput8-Handle
2016-01-27 09:11:59 +00:00
//LPDIRECTINPUTDEVICE8 g_apInputDevice[6] = { NULL, NULL, NULL, NULL, NULL, NULL }; // Array of handles for devices
// 0:keyboard, 1:mouse, the rest are force feedback gamepads
2016-01-27 09:11:59 +00:00
// BYTE g_acKeystate[256]; // Use g_sysKeyboard.stateAs.rgbButtons instead
2020-02-09 11:36:49 +00:00
// 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...
2016-01-27 09:11:59 +00:00
// Update device data tables (so we only have to poll and read the devices once). This is called by GetKeys and ReadController.
void GetDeviceDatas()
{
2020-02-09 11:36:49 +00:00
HRESULT hr;
/* if( g_sysKeyboard.didHandle )
{
hr = g_sysKeyboard.didHandle->Poll();
if( FAILED( hr ))
AcquireDevice( g_sysKeyboard.didHandle ); // We'll try again next time
2020-02-09 11:36:49 +00:00
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
2020-02-09 11:36:49 +00:00
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 device we're using
2020-02-09 11:36:49 +00:00
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
2020-02-09 11:36:49 +00:00
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 (comment by rabid)
2020-02-09 11:36:49 +00:00
{
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
}
}
2016-01-27 09:11:59 +00:00
}
// Hacked up, but it works
2016-01-27 09:11:59 +00:00
inline bool GetJoyPadPOV( PDWORD dwDegree, BYTE AxeId )
// True if specified direction is pressed
2016-01-27 09:11:59 +00:00
{
2020-02-09 11:36:49 +00:00
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;
2016-01-27 09:11:59 +00:00
}
// 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 matters to anybody
2016-01-27 09:11:59 +00:00
bool GetNControllerInput ( const int indexController, LPDWORD pdwData )
{
2020-02-09 11:36:49 +00:00
*pdwData = 0;
WORD w_Buttons = 0;
// WORD w_Axes = 0;
LPCONTROLLER pcController = &g_pcControllers[indexController]; // Still needs to be here, but not as important (comment by rabid)
2020-02-09 11:36:49 +00:00
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
2020-02-09 11:36:49 +00:00
float d_ModifierX = (float)pcController->bStickRange / 100.0f;
float d_ModifierY = (float)pcController->bStickRange / 100.0f;
int i;
// Do N64-Buttons / modifiers
2020-02-09 11:36:49 +00:00
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
2020-02-09 11:36:49 +00:00
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
2020-02-09 11:36:49 +00:00
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) // Rapid fire here
2020-02-09 11:36:49 +00:00
{
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
2020-02-09 11:36:49 +00:00
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
2020-02-09 11:36:49 +00:00
args->fPrevFireState2 = (args->fPrevFireState2 + 1) & 1;
if (!args->fPrevFireState2)
{
args->fPrevFireState = (args->fPrevFireState + 1) & 1;
DebugWriteA("Slow rapid fire - Mark 2\n");
2020-02-09 11:36:49 +00:00
}
}
else // Do a fast rapid fire
{
args->fPrevFireState = (args->fPrevFireState + 1) & 1;
DebugWriteA("Fast rapid fire\n");
2020-02-09 11:36:49 +00:00
}
}
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
2020-02-09 11:36:49 +00:00
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
2020-02-09 11:36:49 +00:00
// Do N64 buttons / modifiers
2020-02-09 11:36:49 +00:00
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
2020-02-09 11:36:49 +00:00
long lDeadZoneValue = pcController->bPadDeadZone * RANGERELATIVE / 100;
float fDeadZoneRelation = (float)RANGERELATIVE / (float)( RANGERELATIVE - lDeadZoneValue );
// Do N64 joystick axes
2020-02-09 11:36:49 +00:00
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
2020-02-09 11:36:49 +00:00
{
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<long>(( pcController->wAxeBuffer[i] + N64DIVIDER*3), MAXAXISVALUE );
}
else
l_Value = MAXAXISVALUE;
}
else
{
if( pcController->wAxeBuffer[i] < MAXAXISVALUE )
{
l_Value = pcController->wAxeBuffer[i] = min<long>(( 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<long>( max<long>( 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
2020-02-09 11:36:49 +00:00
{
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: We should optimize this (comment by rabid)
2020-02-09 11:36:49 +00:00
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<long>( max<long>( MINAXISVALUE, (long)(lAxisValueX * d_ModifierX * dRel )), MAXAXISVALUE) / N64DIVIDER ),
(BYTE)(min<long>( max<long>( MINAXISVALUE, (long)(lAxisValueY * d_ModifierY * dRel )), MAXAXISVALUE) / N64DIVIDER )));
}
else
{
*pdwData = MAKELONG(w_Buttons,
MAKEWORD( (BYTE)(min<long>( max<long>( MINAXISVALUE, (long)(lAxisValueX * d_ModifierX )), MAXAXISVALUE) / N64DIVIDER ),
(BYTE)(min<long>( max<long>( MINAXISVALUE, (long)(lAxisValueY * d_ModifierY )), MAXAXISVALUE) / N64DIVIDER )));
}
return true;
2016-01-27 09:11:59 +00:00
}
bool InitDirectInput( HWND hWnd )
{
2020-02-09 11:36:49 +00:00
if( g_hDirectInputDLL == NULL )
g_hDirectInputDLL = LoadLibrary( _T( "dinput8.dll" ));
if( g_hDirectInputDLL == NULL )
{
ErrorMessage(IDS_ERR_DINOTFOUND, 0, false);
}
else if( !g_pDIHandle ) // This is NULL if not yet initialized
2020-02-09 11:36:49 +00:00
{
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);
2016-01-27 09:11:59 +00:00
}
// Release a DirectInput device since we don't need it anymore
2016-01-27 09:11:59 +00:00
void ReleaseDevice( LPDIRECTINPUTDEVICE8 &lpDirectInputDevice )
{
2020-02-09 11:36:49 +00:00
if( lpDirectInputDevice != NULL)
{
lpDirectInputDevice->Unacquire();
lpDirectInputDevice->Release();
lpDirectInputDevice = NULL;
}
return;
2016-01-27 09:11:59 +00:00
}
// Release a DirectInput device since we don't need it anymore
2016-01-27 09:11:59 +00:00
void ReleaseEffect( LPDIRECTINPUTEFFECT &lpDirectEffect )
{
2020-02-09 11:36:49 +00:00
if( lpDirectEffect != NULL)
{
// We should unload the effect on release
2020-02-09 11:36:49 +00:00
lpDirectEffect->Release();
lpDirectEffect = NULL;
}
return;
2016-01-27 09:11:59 +00:00
}
// Release our DirectInput effects and devices, our DirectInput handle, and then unload DInput library
2016-01-27 09:11:59 +00:00
void FreeDirectInput ()
{
2020-02-09 11:36:49 +00:00
int i;
// Release effects
2020-02-09 11:36:49 +00:00
for( i = 0; i < ARRAYSIZE( g_apdiEffect ); ++i )
ReleaseEffect( g_apdiEffect[i] );
ZeroMemory( g_apdiEffect, sizeof(g_apdiEffect) );
// Release force feedback devices
2020-02-09 11:36:49 +00:00
for( i = 0; i << ARRAYSIZE(g_apFFDevice); ++i )
ReleaseDevice( g_apFFDevice[i] );
ZeroMemory( g_apFFDevice, sizeof(g_apFFDevice) );
// Release normal devices
2020-02-09 11:36:49 +00:00
for( i = 0; i < g_nDevices; i++ )
ReleaseDevice( g_devList[i].didHandle );
ZeroMemory( g_devList, sizeof(g_devList) );
g_nDevices = 0;
// Release mouse device
2020-02-09 11:36:49 +00:00
ReleaseDevice( g_sysMouse.didHandle );
ZeroMemory( &g_sysMouse, sizeof(g_sysMouse) );
2016-01-27 09:11:59 +00:00
// Release any DirectInput handles
2020-02-09 11:36:49 +00:00
if( g_pDIHandle != NULL )
{
g_pDIHandle->Release();
g_pDIHandle = NULL;
}
// Unload the library
2020-02-09 11:36:49 +00:00
if( g_hDirectInputDLL != NULL )
{
FreeLibrary( g_hDirectInputDLL );
g_hDirectInputDLL = NULL;
}
return;
2016-01-27 09:11:59 +00:00
}
// 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.
2016-01-27 09:11:59 +00:00
// We use this a LOT.
inline HRESULT AcquireDevice( LPDIRECTINPUTDEVICE8 lpDirectInputDevice )
{
2020-02-09 11:36:49 +00:00
HRESULT hResult = lpDirectInputDevice->Acquire();
while( hResult == DIERR_INPUTLOST )
hResult = lpDirectInputDevice->Acquire();
if( SUCCEEDED( hResult ))
lpDirectInputDevice->Poll();
return hResult;
2016-01-27 09:11:59 +00:00
}
// Called by the DirectInput enumerator for each force feedback device. What kind of force feedback effects does this device support? We'll store it in pvRef.
2016-01-27 09:11:59 +00:00
BOOL CALLBACK EnumGetEffectTypes( LPCDIEFFECTINFO pdei, LPVOID pvRef )
{
2020-02-09 11:36:49 +00:00
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;
2016-01-27 09:11:59 +00:00
}
// Called by the DirectInput enumerator for each attached DI device. We use it to make a list of devices.
// EnumMakeDeviceList has been rewritten (comment by rabid)
2016-01-27 09:11:59 +00:00
BOOL CALLBACK EnumMakeDeviceList( LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef )
{
2020-02-09 11:36:49 +00:00
switch (GET_DIDEVICE_TYPE(lpddi->dwDevType)) {
// We don't need to do anything with these generic devices
2020-02-09 11:36:49 +00:00
case DI8DEVTYPE_DEVICE:
return DIENUM_CONTINUE;
break;
// These are potential XInput controllers, check them
2020-02-09 11:36:49 +00:00
case DI8DEVTYPE_GAMEPAD:
case DI8DEVTYPE_DRIVING:
case DI8DEVTYPE_JOYSTICK:
case DI8DEVTYPE_FLIGHT:
if (IsXInputDevice(&lpddi->guidProduct)) // Check if is XInput device (comment by tecnicors)
2020-02-09 11:36:49 +00:00
return DIENUM_CONTINUE;
break;
// For all other devices, continue on
2020-02-09 11:36:49 +00:00
default:
break;
}
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
2020-02-09 11:36:49 +00:00
{
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
2020-02-09 11:36:49 +00:00
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 them instance numbers
2020-02-09 11:36:49 +00:00
}
}
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;
2016-01-27 09:11:59 +00:00
}
// Called by an enumerator in GetInputDevice to determine if a device is attached at all
2016-01-27 09:11:59 +00:00
BOOL CALLBACK EnumIsDeviceAvailable( LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef )
{
2020-02-09 11:36:49 +00:00
if( lpddi->guidInstance == *(GUID*)((LPVOID*)pvRef)[0] )
{
*(bool*)((LPVOID*)pvRef)[1] = true;
return DIENUM_STOP;
}
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
return DIENUM_CONTINUE;
2016-01-27 09:11:59 +00:00
}
// Called by an axis enumerator in GetInputDevice. Set the minimum and maximum range so we can pass the recorded value right to the emulator
2016-01-27 09:11:59 +00:00
BOOL CALLBACK EnumSetObjectsAxis( LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef)
{
2020-02-09 11:36:49 +00:00
LPDIRECTINPUTDEVICE8 lpDirectInputDevice = (LPDIRECTINPUTDEVICE8)pvRef;
DIPROPRANGE diprg;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
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;
2016-01-27 09:11:59 +00:00
lpDirectInputDevice->SetProperty(DIPROP_RANGE, &diprg.diph); // HACK: Usually works, but not all devices support setting range
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
return DIENUM_CONTINUE;
2016-01-27 09:11:59 +00:00
}
/*
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 it 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.
*/
2016-01-27 09:11:59 +00:00
bool GetInputDevice( HWND hWnd, LPDIRECTINPUTDEVICE8 &lpDirectInputDevice, GUID gGuid, DWORD dwDevType, DWORD dwCooperativeLevel )
{
2020-02-09 11:36:49 +00:00
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 reinitialize\n");
2020-02-09 11:36:49 +00:00
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 controller; probably not the best idea but it works
2020-02-09 11:36:49 +00:00
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"
2020-02-09 11:36:49 +00:00
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;
2016-01-27 09:11:59 +00:00
}
// How many force feedback axes (motors) does our device have? We want to rumble them all.
BOOL CALLBACK EnumCountFFAxes( LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pnAxes )
{
2020-02-09 11:36:49 +00:00
*(DWORD*)pnAxes += 1;
return DIENUM_CONTINUE;
2016-01-27 09:11:59 +00:00
}
// 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 )
{
2020-02-09 11:36:49 +00:00
if( pDIEffect )
ReleaseEffect( pDIEffect );
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
if( !lpDirectInputDevice || bRumbleTyp == RUMBLE_DIRECT )
return false;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
DWORD nAxes = 0;
DWORD rgdwAxes[] = { DIJOFS_X, DIJOFS_Y };
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
HRESULT hResult;
2016-01-27 09:11:59 +00:00
// Count the force feedback - axes of the joystick
2020-02-09 11:36:49 +00:00
lpDirectInputDevice->EnumObjects( EnumCountFFAxes, &nAxes, DIDFT_FFACTUATOR | DIDFT_AXIS );
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
if( nAxes == 0 )
return false;
nAxes = min<long>( nAxes, 2 );
2016-01-27 09:11:59 +00:00
// Must be unaquired for setting stuff like co-op level
2020-02-09 11:36:49 +00:00
hResult = lpDirectInputDevice->Unacquire();
// Force feedback Requires EXCLUSIVE LEVEL, took me hours to find the reason why it wasn't working
2020-02-09 11:36:49 +00:00
hResult = lpDirectInputDevice->SetCooperativeLevel( hWnd, DIB_FF );
2016-01-27 09:11:59 +00:00
// Fail if we can't set coop level (comment by rabid)
2020-02-09 11:36:49 +00:00
if (hResult != DI_OK)
{
DebugWriteA("CreateEffectHandle: couldn't set coop level: %08X\n", hResult);
return false;
}
2016-01-27 09:11:59 +00:00
// Since we will be playing force feedback effects, we should disable the
// auto-centering spring.
2020-02-09 11:36:49 +00:00
DIPROPDWORD dipdw;
2016-01-27 09:11:59 +00:00
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = FALSE;
2020-02-09 11:36:49 +00:00
hResult = lpDirectInputDevice->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph );
long rglDirection[] = { 1, 1 };
LPGUID EffectGuid;
2016-01-27 09:11:59 +00:00
DIEFFECT eff;
ZeroMemory( &eff, sizeof(eff) );
2020-02-09 11:36:49 +00:00
eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.dwGain = lStrength * 100;
2016-01-27 09:11:59 +00:00
eff.dwTriggerButton = DIEB_NOTRIGGER;
eff.dwTriggerRepeatInterval = 0;
eff.cAxes = nAxes; // Number of axes
2016-01-27 09:11:59 +00:00
eff.rgdwAxes = rgdwAxes;
eff.rglDirection = rglDirection;
eff.lpEnvelope = NULL;
2020-02-09 11:36:49 +00:00
eff.dwStartDelay = 0;
DICONSTANTFORCE cf;
DIRAMPFORCE rf;
DIPERIODIC pf;
switch( bRumbleTyp )
{
case RUMBLE_CONSTANT:
EffectGuid = (GUID*)&GUID_ConstantForce;
eff.dwDuration = 150000; // Microseconds
2020-02-09 11:36:49 +00:00
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
2020-02-09 11:36:49 +00:00
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
2020-02-09 11:36:49 +00:00
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 );
2016-01-27 09:11:59 +00:00
}
// Counts how many of each type of button assignment (keyboard, mouse, controller)
2016-01-27 09:11:59 +00:00
DWORD CountControllerStructDevs( CONTROLLER *pController )
{
2020-02-09 11:36:49 +00:00
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 )));
2016-01-27 09:11:59 +00:00
}
// PrepareInputDevices rewritten (comment by rabid)
2016-01-27 09:11:59 +00:00
bool PrepareInputDevices()
{
2020-02-09 11:36:49 +00:00
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 controller to go with those buttons
2020-02-09 11:36:49 +00:00
}
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
2020-02-09 11:36:49 +00:00
{
DIDEVICEINSTANCE diDev;
diDev.dwSize = sizeof( DIDEVICEINSTANCE );
g_apFFDevice[i]->GetDeviceInfo( &diDev );
if( !lstrcmp( diDev.tszProductName, _T( STRING_ADAPTOID )))
{
g_pcControllers[i].fIsAdaptoid = true;
DebugWriteA("Force feedback device on controller %d is of type Adaptoid\n", i+1);
2020-02-09 11:36:49 +00:00
}
else
{
g_pcControllers[i].fIsAdaptoid = false;
}
if ( CreateEffectHandle( i, g_pcControllers[i].bRumbleTyp, g_pcControllers[i].bRumbleStrength ) )
{
AcquireDevice( g_apFFDevice[i] );
DebugWriteA("Got force feedback device %d\n", i);
2020-02-09 11:36:49 +00:00
}
else
DebugWriteA("Couldn't get force feedback device: CreateEffectHandle failed!\n");
2020-02-09 11:36:49 +00:00
}
else
{
g_apFFDevice[i] = NULL;
DebugWriteA("Didn't get force feedback device %d\n", i);
2020-02-09 11:36:49 +00:00
}
}
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;
2016-01-27 09:11:59 +00:00
}
bool IsAdaptoidCommandSupported( LPDIRECTINPUTDEVICE8 lpDirectInputDevice, DWORD cmd )
{
DIEFFESCAPE esc;
DWORD inbuf, outbuf;
HRESULT hr;
2020-02-09 11:36:49 +00:00
esc.dwSize = sizeof(esc);
esc.dwCommand = ADAPT_TEST; // Command to determine if a command is supported
2020-02-09 11:36:49 +00:00
esc.lpvInBuffer = &inbuf;
esc.cbInBuffer = 4;
esc.lpvOutBuffer = &outbuf;
esc.cbOutBuffer = 4;
inbuf = cmd; // Command that we are asking is supported
2020-02-09 11:36:49 +00:00
outbuf = 0;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
hr = lpDirectInputDevice->Escape(&esc);
2016-01-27 09:11:59 +00:00
return( SUCCEEDED(hr) && esc.cbOutBuffer == 4 && outbuf == 0xB0CAB0CA );
}
#ifdef _DEBUG
// Direct Adaptoid debugging stuff
2016-01-27 09:11:59 +00:00
void _debugAd( LPCSTR szMessage, HRESULT res )
{
2020-02-09 11:36:49 +00:00
LPCSTR suc = (SUCCEEDED(res)) ? "OK" : "FAILED";
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
DebugWriteA( "%s: %s (RC:%08X)\n", szMessage, suc, res );
2016-01-27 09:11:59 +00:00
}
#endif // #ifdef _DEBUG
HRESULT DirectRumbleCommand( LPDIRECTINPUTDEVICE8 lpDirectInputDevice, DWORD cmd )
{
DIEFFESCAPE esc;
esc.dwSize = sizeof(esc);
esc.dwCommand = ADAPT_RUMBLE; // Send rumble command
2016-01-27 09:11:59 +00:00
esc.lpvInBuffer = &cmd; // 1=go, 0=stop
esc.cbInBuffer = 4;
esc.lpvOutBuffer = NULL;
esc.cbOutBuffer = 0;
2020-02-09 11:36:49 +00:00
HRESULT hr = lpDirectInputDevice->Escape(&esc);
2016-01-27 09:11:59 +00:00
#ifdef _DEBUG
2020-02-09 11:36:49 +00:00
_debugAd( "Direct Adaptoid RumbleCommand", hr );
2016-01-27 09:11:59 +00:00
#endif // #ifdef _DEBUG
return hr;
}
HRESULT InitializeAdaptoid( LPDIRECTINPUTDEVICE8 lpDirectInputDevice, LPBYTE status )
{
DIEFFESCAPE esc;
esc.dwSize = sizeof(esc);
esc.dwCommand = ADAPT_INIT; // Initialize pak
2016-01-27 09:11:59 +00:00
esc.lpvInBuffer = NULL;
esc.cbInBuffer = 0;
2020-02-09 11:36:49 +00:00
esc.lpvOutBuffer = status;
2016-01-27 09:11:59 +00:00
esc.cbOutBuffer = 1;
2020-02-09 11:36:49 +00:00
HRESULT hr = lpDirectInputDevice->Escape(&esc);
2016-01-27 09:11:59 +00:00
#ifdef _DEBUG
2020-02-09 11:36:49 +00:00
_debugAd( "Direct Adaptoid InitPak", hr );
2016-01-27 09:11:59 +00:00
#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;
2020-02-09 11:36:49 +00:00
esc.lpvOutBuffer = data;
2016-01-27 09:11:59 +00:00
esc.cbOutBuffer = 32;
2020-02-09 11:36:49 +00:00
HRESULT hr = lpDirectInputDevice->Escape(&esc);
2016-01-27 09:11:59 +00:00
#ifdef _DEBUG
2020-02-09 11:36:49 +00:00
LPCSTR suc = (SUCCEEDED(hr)) ? "OK" : "FAILED";
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
DebugWriteA( "Direct Adaptoid ReadPak(Addr:%04X): %s (RC:%08X)\n", addr, suc, hr );
2016-01-27 09:11:59 +00:00
#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;
2020-02-09 11:36:49 +00:00
esc.lpvOutBuffer = NULL;
2016-01-27 09:11:59 +00:00
esc.cbOutBuffer = 0;
2020-02-09 11:36:49 +00:00
HRESULT hr = lpDirectInputDevice->Escape(&esc);
2016-01-27 09:11:59 +00:00
#ifdef _DEBUG
2020-02-09 11:36:49 +00:00
LPCSTR suc = (SUCCEEDED(hr)) ? "OK" : "FAILED";
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
DebugWriteA( "Direct Adaptoid WritePak(Addr:%04X): %s (RC:%08X)\n", addr, suc, hr );
2016-01-27 09:11:59 +00:00
#endif // #ifdef _DEBUG
return hr;
}
BYTE GetAdaptoidStatus( LPDIRECTINPUTDEVICE8 lpDirectInputDevice )
{
2020-02-09 11:36:49 +00:00
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;
2016-01-27 09:11:59 +00:00
}
// Fill the handle for g_sysMouse properly
void InitMouse()
{
2020-02-09 11:36:49 +00:00
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;
2016-01-27 09:11:59 +00:00
}
// Treat btnButton as a b_Value, and return whether it is pressed or not
2016-01-27 09:11:59 +00:00
bool IsBtnPressed(BUTTON btnButton)
{
2020-02-09 11:36:49 +00:00
long l_Value;
LPLONG plRawState = (LPLONG)&btnButton.parentDevice->stateAs.joyState;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
switch ( btnButton.bBtnType )
{
case DT_JOYBUTTON:
return ( btnButton.parentDevice->stateAs.joyState.rgbButtons[btnButton.bOffset] & 0x80 ) != 0;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
case DT_JOYSLIDER:
case DT_JOYAXE:
l_Value = plRawState[btnButton.bOffset] - ZEROVALUE;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
if ( btnButton.bAxisID )
return ( l_Value <= -ABSTHRESHOLD );
else
return ( l_Value >= ABSTHRESHOLD );
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
case DT_JOYPOV:
return GetJoyPadPOV( (PDWORD)&plRawState[btnButton.bOffset] , btnButton.bAxisID );
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
case DT_KEYBUTTON:
return ( btnButton.parentDevice->stateAs.rgbButtons[btnButton.bOffset] & 0x80 ) != 0;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
case DT_MOUSEBUTTON:
return ( btnButton.parentDevice->stateAs.mouseState.rgbButtons[btnButton.bOffset] & 0x80 ) != 0;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
case DT_MOUSEAXE:
l_Value = MOUSEMOVE; // AKA lvalue is button threshold
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
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;
2016-01-27 09:11:59 +00:00
2020-02-09 11:36:49 +00:00
case DT_UNASSIGNED:
default:
return false;
}
2016-01-27 09:11:59 +00:00
}