diff --git a/src/common/input/InputManager.cpp b/src/common/input/InputManager.cpp index edf4ba7ad..84931408b 100644 --- a/src/common/input/InputManager.cpp +++ b/src/common/input/InputManager.cpp @@ -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& Device, v return true; } +bool InputDeviceManager::UpdateInputLightgun(std::shared_ptr &Device, void *Buffer, int Direction, int Port_num, const std::string &Port) +{ + std::map bindings = Device->GetBindings(Port); + assert(bindings.size() == static_cast(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(static_cast(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(bindings[i])->GetState() : 0.0; + uint8_t curr_state = static_cast(!!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(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(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(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(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(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(bindings[i])->GetState() : 0.0; + ControlState state_minus = (bindings[i + 1] != nullptr) ? dynamic_cast(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(state) + g_devs[Port_num].info.ligthgun.offset_x; + break; + + case 12: + in_buf->sThumbLY = static_cast(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& Device, void* Buffer, int Direction, int Port_num, const std::string &Port) { std::map bindings = Device->GetBindings(Port); @@ -495,7 +611,6 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr& 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(static_cast(Buffer) + XID_PACKET_HEADER); for (int i = 0; i < 4; i++) { ControlState state = (bindings[i] != nullptr) ? dynamic_cast(bindings[i])->GetState() : 0.0; @@ -511,10 +626,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr& Device, vo for (int i = 4, j = 0; i < 6; i++, j++) { ControlState state = (bindings[i] != nullptr) ? dynamic_cast(bindings[i])->GetState() : 0.0; uint16_t curr_in_state = static_cast(!!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& Device, vo for (int i = 34, j = 2; i < 39; i++, j++) { ControlState state = (bindings[i] != nullptr) ? dynamic_cast(bindings[i])->GetState() : 0.0; uint16_t curr_in_state = static_cast(!!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& Device, vo for (int i = 52, j = 7; i < 56; i++, j++) { ControlState state = (bindings[i] != nullptr) ? dynamic_cast(bindings[i])->GetState() : 0.0; uint16_t curr_in_state = static_cast(!!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& 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); } } diff --git a/src/common/input/InputManager.h b/src/common/input/InputManager.h index 628cbcf7d..3ddfde46e 100644 --- a/src/common/input/InputManager.h +++ b/src/common/input/InputManager.h @@ -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& Device, void* Buffer, int Direction, const std::string &Port); + // update input for a lightgun + bool UpdateInputLightgun(std::shared_ptr &Device, void *Buffer, int Direction, int Port_num, const std::string &Port); // update input for a Steel Battalion controller bool UpdateInputSBC(std::shared_ptr& Device, void* Buffer, int Direction, int Port_num, const std::string &Port); // update input for a passthrough xbox device diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index ee4aaf8b6..b49f4c387 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -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(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 { diff --git a/src/core/hle/XAPI/Xapi.h b/src/core/hle/XAPI/Xapi.h index 6be07729d..ef15e5914 100644 --- a/src/core/hle/XAPI/Xapi.h +++ b/src/core/hle/XAPI/Xapi.h @@ -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 // ******************************************************************