2024-08-26 09:58:44 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#include <shlwapi.h>
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <xinput.h>
|
|
|
|
|
|
|
|
#include "board/io4.h"
|
|
|
|
|
|
|
|
#include "hook/table.h"
|
|
|
|
#include "util/dprintf.h"
|
|
|
|
#include "util/lib.h"
|
|
|
|
|
|
|
|
#include "swdchook/config.h"
|
|
|
|
#include "swdchook/zinput.h"
|
|
|
|
|
|
|
|
static struct zinput_config zinput_config;
|
|
|
|
static bool zinput_hook_initted;
|
|
|
|
static bool zinput_controller_init = false;
|
|
|
|
|
|
|
|
static HRESULT init_mmf(void);
|
|
|
|
|
|
|
|
static HANDLE mmf;
|
|
|
|
static uint16_t* swdc_gamebtn;
|
|
|
|
|
|
|
|
/* Hooked functions */
|
|
|
|
DWORD WINAPI hook_XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState);
|
|
|
|
DWORD WINAPI hook_XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration);
|
|
|
|
// Not needed for now?
|
|
|
|
DWORD WINAPI hook_XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities);
|
|
|
|
|
|
|
|
// Yup SEGA imports XInput functions via ordinal. FUN!
|
|
|
|
static struct hook_symbol zinput_hook_syms[] = {
|
|
|
|
{
|
|
|
|
.name = "XInputGetState",
|
|
|
|
.ordinal = 0x0002,
|
|
|
|
.patch = hook_XInputGetState,
|
|
|
|
.link = NULL
|
|
|
|
}, {
|
|
|
|
.name = "XInputSetState",
|
|
|
|
.ordinal = 0x0003,
|
|
|
|
.patch = hook_XInputSetState,
|
|
|
|
.link = NULL
|
|
|
|
}, {
|
|
|
|
// Not needed for now?
|
|
|
|
.name = "XInputGetCapabilities",
|
|
|
|
.patch = hook_XInputGetCapabilities,
|
|
|
|
.link = NULL
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
void zinput_hook_init(struct zinput_config *cfg)
|
|
|
|
{
|
|
|
|
wchar_t *module_path;
|
|
|
|
wchar_t *file_name;
|
|
|
|
|
|
|
|
assert(cfg != NULL);
|
|
|
|
|
|
|
|
if (!cfg->enable) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zinput_hook_initted) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
module_path = module_file_name(NULL);
|
|
|
|
|
|
|
|
if (module_path != NULL) {
|
|
|
|
file_name = PathFindFileNameW(module_path);
|
|
|
|
|
|
|
|
free(module_path);
|
|
|
|
module_path = NULL;
|
|
|
|
|
|
|
|
_wcslwr(file_name);
|
|
|
|
|
|
|
|
if (wcsstr(file_name, L"amdaemon") != NULL) {
|
|
|
|
// dprintf("Executable filename contains 'amdaemon', disabling zinput\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hook_table_apply(
|
|
|
|
NULL,
|
|
|
|
"XINPUT1_3.dll",
|
|
|
|
zinput_hook_syms,
|
|
|
|
_countof(zinput_hook_syms));
|
|
|
|
|
|
|
|
if (FAILED(init_mmf())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
zinput_hook_initted = true;
|
|
|
|
|
|
|
|
dprintf("ZInput: Hooking built-in XInput support\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool zinput_connect_controller(bool enable) {
|
|
|
|
zinput_controller_init = enable;
|
|
|
|
dprintf("zinput_connect_controller\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT init_mmf(void) {
|
|
|
|
// Create or open memory-mapped file
|
|
|
|
mmf = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 2, "SWDCButton");
|
|
|
|
if (mmf == NULL) {
|
|
|
|
return S_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Map the memory-mapped file
|
|
|
|
swdc_gamebtn = (uint16_t*)MapViewOfFile(mmf, FILE_MAP_ALL_ACCESS, 0, 0, 2);
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD WINAPI hook_XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities) {
|
|
|
|
// dprintf("ZInput: XInputGetCapabilities hook hit\n");
|
|
|
|
|
|
|
|
if (!zinput_controller_init) {
|
|
|
|
zinput_connect_controller(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dwFlags > XINPUT_FLAG_GAMEPAD) {
|
|
|
|
return ERROR_BAD_ARGUMENTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zinput_controller_init && dwUserIndex == 0) {
|
|
|
|
pCapabilities->Flags = XINPUT_CAPS_VOICE_SUPPORTED;
|
|
|
|
pCapabilities->Type = XINPUT_DEVTYPE_GAMEPAD;
|
|
|
|
pCapabilities->SubType = XINPUT_DEVSUBTYPE_GAMEPAD;
|
|
|
|
|
|
|
|
pCapabilities->Gamepad.wButtons = 0xF3FF;
|
|
|
|
|
|
|
|
pCapabilities->Gamepad.bLeftTrigger = 0xFF;
|
|
|
|
pCapabilities->Gamepad.bRightTrigger = 0xFF;
|
|
|
|
|
|
|
|
pCapabilities->Gamepad.sThumbLX = (SHORT)0xFFC0;
|
|
|
|
pCapabilities->Gamepad.sThumbLY = (SHORT)0xFFC0;
|
|
|
|
pCapabilities->Gamepad.sThumbRX = (SHORT)0xFFC0;
|
|
|
|
pCapabilities->Gamepad.sThumbRY = (SHORT)0xFFC0;
|
|
|
|
|
|
|
|
pCapabilities->Vibration.wLeftMotorSpeed = 0xFF;
|
|
|
|
pCapabilities->Vibration.wRightMotorSpeed = 0xFF;
|
|
|
|
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
} else {
|
|
|
|
return ERROR_DEVICE_NOT_CONNECTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD WINAPI hook_XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState) {
|
|
|
|
// dprintf("ZInput: XInputGetState hook hit\n");
|
|
|
|
|
|
|
|
if (!zinput_controller_init) {
|
|
|
|
zinput_connect_controller(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zinput_controller_init && dwUserIndex == 0) {
|
|
|
|
XINPUT_GAMEPAD gamepad_state = {0};
|
|
|
|
gamepad_state.wButtons = 0;
|
|
|
|
|
|
|
|
/* Read filemapping for for the gamebtns */
|
|
|
|
|
|
|
|
if (*swdc_gamebtn & SWDC_IO_GAMEBTN_STEERING_PADDLE_LEFT) {
|
|
|
|
gamepad_state.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*swdc_gamebtn & SWDC_IO_GAMEBTN_STEERING_PADDLE_RIGHT) {
|
|
|
|
gamepad_state.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*swdc_gamebtn & SWDC_IO_GAMEBTN_STEERING_BLUE) {
|
|
|
|
gamepad_state.wButtons |= XINPUT_GAMEPAD_X;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*swdc_gamebtn & SWDC_IO_GAMEBTN_STEERING_RED) {
|
|
|
|
gamepad_state.wButtons |= XINPUT_GAMEPAD_B;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*swdc_gamebtn & SWDC_IO_GAMEBTN_STEERING_GREEN) {
|
|
|
|
gamepad_state.wButtons |= XINPUT_GAMEPAD_A;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*swdc_gamebtn & SWDC_IO_GAMEBTN_STEERING_YELLOW) {
|
|
|
|
gamepad_state.wButtons |= XINPUT_GAMEPAD_Y;
|
|
|
|
}
|
|
|
|
if (pState->dwPacketNumber == UINT_MAX)
|
|
|
|
pState->dwPacketNumber = 0;
|
|
|
|
else
|
|
|
|
pState->dwPacketNumber++;
|
|
|
|
|
|
|
|
pState->Gamepad = gamepad_state;
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
} else {
|
|
|
|
return ERROR_DEVICE_NOT_CONNECTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD WINAPI hook_XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) {
|
|
|
|
// dprintf("ZInput: XInputSetState hook hit\n");
|
|
|
|
|
|
|
|
if (!zinput_controller_init) {
|
|
|
|
zinput_connect_controller(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zinput_controller_init && dwUserIndex == 0) {
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
} else {
|
|
|
|
return ERROR_DEVICE_NOT_CONNECTED;
|
|
|
|
}
|
|
|
|
}
|