Added lightgun support to input manager

This commit is contained in:
ergo720 2021-11-13 22:00:01 +01:00
parent 2c1f5bd430
commit c658777645
4 changed files with 151 additions and 16 deletions

View File

@ -132,6 +132,7 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
}
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK):
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
@ -379,6 +380,11 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
m_Mtx.unlock();
return has_changed;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
has_changed = UpdateInputLightgun(dev, buffer, direction, port, port_str);
m_Mtx.unlock();
return has_changed;
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
has_changed = UpdateInputSBC(dev, buffer, direction, port, port_str);
m_Mtx.unlock();
@ -394,7 +400,6 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
m_Mtx.unlock();
return has_changed;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE):
EmuLog(LOG_LEVEL::ERROR2, "An unsupported device is attached at port %d! The device was %s",
@ -472,6 +477,117 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, v
return true;
}
bool InputDeviceManager::UpdateInputLightgun(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction, int Port_num, const std::string &Port)
{
std::map<int, InputDevice::IoControl *> bindings = Device->GetBindings(Port);
assert(bindings.size() == static_cast<size_t>(dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN)]));
// NOTE: the output state is not supported
if (Direction == DIRECTION_IN) {
if (!Device->UpdateInput()) {
return false;
}
// We change the toggle buttons only when a press -> release input transaction is completed
// 0 -> Turbo left
// 1 -> Turbo right
XpadInput *in_buf = reinterpret_cast<XpadInput *>(static_cast<uint8_t *>(Buffer) + XID_PACKET_HEADER);
uint8_t last_turbo = g_devs[Port_num].info.ligthgun.turbo;
for (int i = 14, j = 0; i < 16; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint8_t curr_state = static_cast<uint8_t>(!!state);
if ((~curr_state) & ((g_devs[Port_num].info.ligthgun.last_turbo_state >> j) & 1)) {
if (j == 0) {
if (g_devs[Port_num].info.ligthgun.turbo != 2) {
g_devs[Port_num].info.ligthgun.turbo += 1;
}
}
else {
if (g_devs[Port_num].info.ligthgun.turbo != 0) {
g_devs[Port_num].info.ligthgun.turbo -= 1;
}
}
}
(g_devs[Port_num].info.ligthgun.last_turbo_state &= ~(1 << j)) |= (curr_state << j);
}
in_buf->wButtons = XINPUT_LIGHTGUN_ONSCREEN;
for (int i = 0; i < 6; i++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
if (state) {
in_buf->wButtons |= (1 << i);
}
else {
in_buf->wButtons &= ~(1 << i);
}
}
if (g_devs[Port_num].info.ligthgun.turbo) {
ControlState trigger_state = (bindings[6] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[6])->GetState() : 0.0;
if (trigger_state) {
// Turbo mode 1
in_buf->bAnalogButtons[0] ^= 0xFF;
int start_idx = 7;
if (g_devs[Port_num].info.ligthgun.turbo == 2) {
// Turbo mode 2
start_idx = 8;
++g_devs[Port_num].info.ligthgun.turbo_delay;
if (last_turbo != g_devs[Port_num].info.ligthgun.turbo) {
g_devs[Port_num].info.ligthgun.turbo_delay = 0;
}
if (g_devs[Port_num].info.ligthgun.turbo_delay == LIGHTGUN_GRIP_DELAY) {
in_buf->bAnalogButtons[1] ^= 0xFF;
g_devs[Port_num].info.ligthgun.turbo_delay = 0;
}
}
for (int i = start_idx, j = start_idx - 6; i < 10; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
}
}
else {
// Turbo active but trigger not pressed
in_buf->bAnalogButtons[0] = 0;
for (int i = 7, j = 1; i < 10; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
}
}
}
else {
// Turbo mode 0 (no turbo)
for (int i = 6, j = 0; i < 10; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
}
}
in_buf->bAnalogButtons[4] = 0;
in_buf->bAnalogButtons[5] = 0;
in_buf->bAnalogButtons[6] = 0;
in_buf->bAnalogButtons[7] = 0;
for (int i = 10; i < 14; i += 2) {
ControlState state_plus = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
ControlState state_minus = (bindings[i + 1] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i + 1])->GetState() : 0.0;
ControlState state = state_plus ? state_plus * 0x7FFF : state_minus ? -state_minus * 0x8000 : 0.0;
switch (i)
{
case 10:
in_buf->sThumbLX = static_cast<int16_t>(state) + g_devs[Port_num].info.ligthgun.offset_x;
break;
case 12:
in_buf->sThumbLY = static_cast<int16_t>(state) + g_devs[Port_num].info.ligthgun.offset_y;
break;
}
}
in_buf->sThumbRX = in_buf->sThumbRY = 0;
}
return true;
}
bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port_num, const std::string &Port)
{
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings(Port);
@ -495,7 +611,6 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
// 8 -> TunerDial Down
// 9 -> GearLever Up
// 10 -> GearLever Down
static uint16_t last_in_state[XBOX_NUM_PORTS] = { 0, 0, 0, 0 };
SBCInput *in_buf = reinterpret_cast<SBCInput *>(static_cast<uint8_t *>(Buffer) + XID_PACKET_HEADER);
for (int i = 0; i < 4; i++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
@ -511,10 +626,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
for (int i = 4, j = 0; i < 6; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
in_buf->wButtons[0] ^= (1 << i);
}
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
}
for (int i = 6; i < 34; i++) {
@ -531,10 +646,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
for (int i = 34, j = 2; i < 39; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
in_buf->wButtons[2] ^= (1 << (i % 16));
}
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
}
for (int i = 39; i < 49; i += 2) {
@ -593,7 +708,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
for (int i = 52, j = 7; i < 56; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
switch (i)
{
case 52:
@ -621,7 +736,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
break;
}
}
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
}
}

View File

@ -54,6 +54,8 @@
#define XID_PACKET_HEADER 2
#define LIGHTGUN_GRIP_DELAY 30
extern int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)];
inline XBOX_INPUT_DEVICE input_support_list[] = {
@ -157,13 +159,16 @@ struct XidSBCOutput {
#pragma pack()
struct LightGunOffsets {
xbox::short_xt x;
xbox::short_xt y;
struct LightGunData {
xbox::short_xt offset_x;
xbox::short_xt offset_y;
uint8_t last_turbo_state;
uint8_t turbo_delay;
uint8_t turbo;
};
union ExtraData {
LightGunOffsets offsets;
struct SbcData {
uint16_t last_in_state;
};
union InputBuff {
@ -181,7 +186,11 @@ struct DeviceInfo {
uint8_t ucFeedbackSize; // feedback size in bytes, does not include FeedbackHeader
uint32_t dwPacketNumber; // increases by one when the input state changes
InputBuff buff; // input buffer
ExtraData data; // device-specific additional data
// device-specific additional data
union {
LightGunData ligthgun;
SbcData sbc;
};
};
struct DeviceState {
@ -230,6 +239,8 @@ public:
private:
// update input for an xbox controller
bool UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, const std::string &Port);
// update input for a lightgun
bool UpdateInputLightgun(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction, int Port_num, const std::string &Port);
// update input for a Steel Battalion controller
bool UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port_num, const std::string &Port);
// update input for a passthrough xbox device

View File

@ -264,6 +264,9 @@ void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type,
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_LIGHTGUN;
dev->info.ucInputStateSize = sizeof(XpadInput);
dev->info.ucFeedbackSize = sizeof(XpadOutput);
dev->info.ligthgun.offset_x = dev->info.ligthgun.offset_x = 0;
dev->info.ligthgun.last_turbo_state = dev->info.ligthgun.turbo = 0;
dev->info.ligthgun.turbo_delay = 0;
break;
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
@ -277,6 +280,7 @@ void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type,
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
dev->info.ucInputStateSize = sizeof(SBCInput);
dev->info.ucFeedbackSize = sizeof(SBCOutput);
dev->info.sbc.last_in_state = 0;
if (type == to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER)) {
dev->type = XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER;
char dev_name[50];
@ -864,8 +868,8 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XInputSetLightgunCalibration)
int port = static_cast<DeviceState *>(hDevice)->port_idx;
if (g_devs[port].info.hHandle == hDevice && !g_devs[port].bPendingRemoval) {
if (g_devs[port].type == XBOX_INPUT_DEVICE::LIGHTGUN) {
g_devs[port].info.data.offsets.x = pCalibrationOffsets->wCenterX;
g_devs[port].info.data.offsets.y = pCalibrationOffsets->wCenterY;
g_devs[port].info.ligthgun.offset_x = pCalibrationOffsets->wCenterX;
g_devs[port].info.ligthgun.offset_y = pCalibrationOffsets->wCenterY;
ret = ERROR_SUCCESS;
}
else {

View File

@ -294,6 +294,11 @@ typedef struct _XINPUT_LIGHTGUN_CALIBRATION_OFFSETS
}
XINPUT_LIGHTGUN_CALIBRATION_OFFSETS, *PXINPUT_LIGHTGUN_CALIBRATION_OFFSETS;
// ******************************************************************
// * Lightgun-specific flags
// ******************************************************************
#define XINPUT_LIGHTGUN_ONSCREEN 0x2000
// ******************************************************************
// * RTL_HEAP_PARAMETERS
// ******************************************************************