Add multi controller support for Xinput gamepads

This commit is contained in:
jackchentwkh 2018-04-29 23:43:33 +08:00
parent 7e21e8bb80
commit 3838ae7318
3 changed files with 72 additions and 34 deletions

View File

@ -51,17 +51,38 @@
static XINPUT_STATE g_Controller; static XINPUT_STATE g_Controller;
static BOOL g_bXInputInitialized = FALSE; static BOOL g_bXInputInitialized = FALSE;
//
//
//
DWORD XTL::XInputGamepad_Connected(void)
{
DWORD dwResult;
DWORD gamepad_connected = 0;
for (DWORD i = 0; i< 4; i++)
{
ZeroMemory(&g_Controller, sizeof(XINPUT_STATE));
// query each port for gamepad state
dwResult = XInputGetState(i, &g_Controller);
//success means gamepad is connected
if (dwResult == ERROR_SUCCESS)
{
gamepad_connected++;
}
}
return gamepad_connected;
}
// ****************************************************************** // ******************************************************************
// * patch: XInputPCPoll // * patch: XInputPCPoll
// ****************************************************************** // ******************************************************************
void XTL::EmuXInputPCPoll( XTL::PXINPUT_STATE Controller ) void XTL::EmuXInputPCPoll( DWORD dwPort,XTL::PXINPUT_STATE Controller )
{ {
// //
// Get the PC's XInput values // Get the PC's XInput values
// //
if( XInputGetState( 0, &g_Controller ) != ERROR_SUCCESS ) if( XInputGetState( dwPort, &g_Controller ) != ERROR_SUCCESS )
return; return;
//Packet# must be updated to trigger the xbe processing the input state. //Packet# must be updated to trigger the xbe processing the input state.
@ -140,7 +161,7 @@ void XTL::EmuXInputPCPoll( XTL::PXINPUT_STATE Controller )
// ****************************************************************** // ******************************************************************
// * Native implementation of XInputSetState // * Native implementation of XInputSetState
// ****************************************************************** // ******************************************************************
void XTL::EmuXInputSetState(XTL::PXINPUT_FEEDBACK Feedback) void XTL::EmuXInputSetState(DWORD dwPort, XTL::PXINPUT_FEEDBACK Feedback)
{ {
XINPUT_VIBRATION FrameVibration = XINPUT_VIBRATION FrameVibration =
{ {
@ -151,5 +172,5 @@ void XTL::EmuXInputSetState(XTL::PXINPUT_FEEDBACK Feedback)
// //
// Set the PC XInput state // Set the PC XInput state
XInputSetState(0, &FrameVibration); XInputSetState(dwPort, &FrameVibration);
} }

View File

@ -34,14 +34,17 @@
#ifndef EMUXINPUT_H #ifndef EMUXINPUT_H
#define EMUXINPUT_H #define EMUXINPUT_H
//query the total connected xinput gamepad
DWORD XInputGamepad_Connected(void);
// ****************************************************************** // ******************************************************************
// * patch: XInputPCPoll // * patch: XInputPCPoll
// ****************************************************************** // ******************************************************************
void EmuXInputPCPoll( XTL::PXINPUT_STATE Controller ); void EmuXInputPCPoll( DWORD dwPort, XTL::PXINPUT_STATE Controller );
// ****************************************************************** // ******************************************************************
// * Native implementation of XInputSetState // * Native implementation of XInputSetState
// ****************************************************************** // ******************************************************************
void EmuXInputSetState(XTL::PXINPUT_FEEDBACK Feedback); void EmuXInputSetState(DWORD dwPort, XTL::PXINPUT_FEEDBACK Feedback);
#endif #endif

View File

@ -68,9 +68,9 @@ XTL::PXPP_DEVICE_TYPE gDeviceType_Gamepad = nullptr;
#include "EmuXTL.h" #include "EmuXTL.h"
XTL::POLLING_PARAMETERS_HANDLE g_pph; XTL::POLLING_PARAMETERS_HANDLE g_pph[4];
XTL::XINPUT_POLLING_PARAMETERS g_pp; XTL::XINPUT_POLLING_PARAMETERS g_pp[4];
DWORD total_xinput_gamepad = 0;
void SetupXboxDeviceTypes() void SetupXboxDeviceTypes()
{ {
@ -165,6 +165,18 @@ VOID WINAPI XTL::EMUPATCH(XInitDevices)
{ {
g_hInputHandle[v] = 0; g_hInputHandle[v] = 0;
} }
if (g_XInputEnabled)
{
//query the total connected xinput gamepad.
total_xinput_gamepad = XInputGamepad_Connected();
}
else
{
//using keyboard, we set the gamd pad count to 1
total_xinput_gamepad = 1;
}
} }
bool TitleIsJSRF() bool TitleIsJSRF()
@ -239,10 +251,14 @@ DWORD WINAPI XTL::EMUPATCH(XGetDevices)
DeviceType->ChangeConnected = 0; DeviceType->ChangeConnected = 0;
DeviceType->PreviousConnected = DeviceType->CurrentConnected; DeviceType->PreviousConnected = DeviceType->CurrentConnected;
// If this is a gamepad, and no gamepad was previously detected, connect one // If this is for getting gamepad devices, and no gamepad was previously detected, connect one
if (DeviceType == gDeviceType_Gamepad && DeviceType->CurrentConnected == 0) { if (DeviceType == gDeviceType_Gamepad && DeviceType->CurrentConnected == 0) {
ret = DeviceType->CurrentConnected = 1; for (int i = 0; i < total_xinput_gamepad; i++)
DeviceType->ChangeConnected = 1; {
DeviceType->CurrentConnected |= 1<<i;
}
DeviceType->ChangeConnected = DeviceType->CurrentConnected ;
ret = DeviceType->CurrentConnected;
} }
// JSRF Hack: Don't set the ChangeConnected flag. Without this, JSRF hard crashes // JSRF Hack: Don't set the ChangeConnected flag. Without this, JSRF hard crashes
@ -339,16 +355,16 @@ HANDLE WINAPI XTL::EMUPATCH(XInputOpen)
LOG_FUNC_END; LOG_FUNC_END;
POLLING_PARAMETERS_HANDLE *pph = 0; POLLING_PARAMETERS_HANDLE *pph = 0;
//return pph handle only for port 0, this prevents some title from polling input state with port other than 0 thus fix the no input issue such as RalliSport //rever back to return handle for port 0~3, this is for multi controller support.
if(dwPort >= 0 && (dwPort <= 0)) if(dwPort >= 0 && (dwPort <= total_xinput_gamepad))
{ {
if(g_hInputHandle[dwPort] == 0) if(g_hInputHandle[dwPort] == 0)
{ {
pph = (POLLING_PARAMETERS_HANDLE*) &g_pph; // new POLLING_PARAMETERS_HANDLE(); pph = (POLLING_PARAMETERS_HANDLE*) &g_pph[dwPort]; // new POLLING_PARAMETERS_HANDLE();
if(pPollingParameters != NULL) if(pPollingParameters != NULL)
{ {
pph->pPollingParameters = (XINPUT_POLLING_PARAMETERS*) &g_pp; // new XINPUT_POLLING_PARAMETERS(); pph->pPollingParameters = (XINPUT_POLLING_PARAMETERS*) &g_pp[dwPort]; // new XINPUT_POLLING_PARAMETERS();
memcpy(pph->pPollingParameters, pPollingParameters, sizeof(XINPUT_POLLING_PARAMETERS)); memcpy(pph->pPollingParameters, pPollingParameters, sizeof(XINPUT_POLLING_PARAMETERS));
} }
@ -367,7 +383,7 @@ HANDLE WINAPI XTL::EMUPATCH(XInputOpen)
{ {
if(pph->pPollingParameters == 0) if(pph->pPollingParameters == 0)
{ {
pph->pPollingParameters = (XINPUT_POLLING_PARAMETERS*) &g_pp; // new XINPUT_POLLING_PARAMETERS(); pph->pPollingParameters = (XINPUT_POLLING_PARAMETERS*) &g_pp[dwPort]; // new XINPUT_POLLING_PARAMETERS();
} }
memcpy(pph->pPollingParameters, pPollingParameters, sizeof(XINPUT_POLLING_PARAMETERS)); memcpy(pph->pPollingParameters, pPollingParameters, sizeof(XINPUT_POLLING_PARAMETERS));
@ -404,8 +420,10 @@ VOID WINAPI XTL::EMUPATCH(XInputClose)
LOG_FUNC_ONE_ARG(hDevice); LOG_FUNC_ONE_ARG(hDevice);
POLLING_PARAMETERS_HANDLE *pph = (POLLING_PARAMETERS_HANDLE*)hDevice; POLLING_PARAMETERS_HANDLE *pph = (POLLING_PARAMETERS_HANDLE*)hDevice;
DWORD dwPort = pph->dwPort;
//NULL out the input handle corresponds to port.
g_hInputHandle[dwPort] = 0;
/* no longer necessary
if(pph != NULL) if(pph != NULL)
{ {
int v; int v;
@ -420,15 +438,16 @@ VOID WINAPI XTL::EMUPATCH(XInputClose)
g_pXInputSetStateStatus[v].dwLatency = 0; g_pXInputSetStateStatus[v].dwLatency = 0;
} }
} }
/*
if(pph->pPollingParameters != NULL) if(pph->pPollingParameters != NULL)
{ {
delete pph->pPollingParameters; delete pph->pPollingParameters;
} }
delete pph; delete pph;
*/
} }
//*/
} }
// ****************************************************************** // ******************************************************************
@ -506,8 +525,8 @@ DWORD WINAPI XTL::EMUPATCH(XInputGetCapabilities)
if(pph != NULL) if(pph != NULL)
{ {
DWORD dwPort = pph->dwPort; DWORD dwPort = pph->dwPort;
//return gamepad capabilities for port 0~3.
if(dwPort == 0) if(dwPort >= 0 && dwPort<=total_xinput_gamepad)
{ {
pCapabilities->SubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD; pCapabilities->SubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
pCapabilities->In.Gamepad = {}; pCapabilities->In.Gamepad = {};
@ -556,20 +575,18 @@ DWORD WINAPI XTL::EMUPATCH(XInputGetState)
DWORD dwPort = pph->dwPort; DWORD dwPort = pph->dwPort;
if((dwPort >= 0) && (dwPort <= 3)) if((dwPort >= 0) && (dwPort <= total_xinput_gamepad))
{ {
DbgPrintf("XAPI: EmuXInputGetState(): dwPort = %d\n", dwPort ); DbgPrintf("XAPI: EmuXInputGetState(): dwPort = %d\n", dwPort );
if(dwPort == 0) //for xinput, we query the state corresponds to port.
{
if (g_XInputEnabled) { if (g_XInputEnabled) {
EmuXInputPCPoll(pState); EmuXInputPCPoll(dwPort,pState);
} else { } else {
EmuDInputPoll(pState); EmuDInputPoll(pState);
} }
ret = ERROR_SUCCESS; ret = ERROR_SUCCESS;
}
} }
} }
else else
@ -607,7 +624,7 @@ DWORD WINAPI XTL::EMUPATCH(XInputSetState)
// //
bool found = false; bool found = false;
for(v=0;v<XINPUT_SETSTATE_SLOTS;v++) for(v=0;v<XINPUT_SETSTATE_SLOTS;v++)
{ {
if(g_pXInputSetStateStatus[v].hDevice == hDevice) if(g_pXInputSetStateStatus[v].hDevice == hDevice)
@ -652,12 +669,9 @@ DWORD WINAPI XTL::EMUPATCH(XInputSetState)
} }
} }
if (pph->dwPort == 0) if (g_XInputEnabled)
{ {
if (g_XInputEnabled) XTL::EmuXInputSetState(pph->dwPort, pFeedback);
{
XTL::EmuXInputSetState(pFeedback);
}
} }
} }