From 2e9546a20aebced34fb064d38f81b5e9f60ef64d Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Wed, 16 Jun 2021 16:47:08 +0200 Subject: [PATCH] Added support to MUs to input manager --- import/XbSymbolDatabase | 2 +- src/common/input/InputDevice.cpp | 56 ++ src/common/input/InputDevice.h | 17 +- src/common/input/InputManager.cpp | 296 +++++----- src/common/input/InputManager.h | 88 +-- src/common/input/SdlJoystick.cpp | 10 +- src/common/input/SdlJoystick.h | 6 + src/core/hle/XAPI/Xapi.cpp | 532 ++++++++++++------ src/gui/DlgInputConfig.cpp | 13 +- .../controllers/DlgDukeControllerConfig.cpp | 2 +- src/gui/controllers/DlgSBControllerConfig.cpp | 2 +- 11 files changed, 629 insertions(+), 395 deletions(-) diff --git a/import/XbSymbolDatabase b/import/XbSymbolDatabase index c2fe7365a..9a411fea5 160000 --- a/import/XbSymbolDatabase +++ b/import/XbSymbolDatabase @@ -1 +1 @@ -Subproject commit c2fe7365adae51ffabd9fe318ecdfaeac2cbe097 +Subproject commit 9a411fea58d97f4abe49e08893c57d6d9ff7f666 diff --git a/src/common/input/InputDevice.cpp b/src/common/input/InputDevice.cpp index f00ec285d..dd39cdeb1 100644 --- a/src/common/input/InputDevice.cpp +++ b/src/common/input/InputDevice.cpp @@ -35,6 +35,7 @@ #include "InputDevice.h" #include "common\util\CxbxUtil.h" #include +#include std::string GetInputDeviceName(int dev_type) @@ -90,6 +91,33 @@ std::string GetInputDeviceName(int dev_type) return str; } +std::string PortUserFormat(std::string_view port) +{ + int port1, slot; + PortStr2Int(port, &port1, &slot); + ++port1; + if (slot != PORT_INVALID) { + ++slot; + return std::to_string(port1) + "." + std::to_string(slot); + } + else { + return std::to_string(port1); + } +} + +void PortStr2Int(std::string_view port, int *port1, int *slot) +{ + *slot = PORT_INVALID; + auto &ret = std::from_chars(port.data(), port.data() + port.size(), *port1); + assert(ret.ec != std::errc::invalid_argument); + if (ret.ptr != port.data() + port.size()) { + ++ret.ptr; + ret = std::from_chars(ret.ptr, port.data() + port.size(), *slot); + assert(ret.ec != std::errc::invalid_argument); + assert(ret.ptr == port.data() + port.size()); + } +} + // Destructor, delete all inputs/outputs on device destruction InputDevice::~InputDevice() { @@ -130,3 +158,31 @@ const std::vector InputDevice::GetIoControls() }); return vec; } + +void InputDevice::SetPort(std::string_view Port, bool Connect) +{ + if (Connect) { + m_XboxPort.emplace_back(Port); + } + else { + const auto &it = FindPort(Port); + if (it != m_XboxPort.end()) { + m_XboxPort.erase(it); + } + } +} + +bool InputDevice::GetPort(std::string_view Port) const +{ + return FindPort(Port) != m_XboxPort.end() ? true : false; +} + +const auto InputDevice::FindPort(std::string_view Port) const +{ + return std::find_if(m_XboxPort.begin(), m_XboxPort.end(), [Port](std::string_view Port1) { + if (Port1 == Port) { + return true; + } + return false; + }); +} diff --git a/src/common/input/InputDevice.h b/src/common/input/InputDevice.h index c0c8cbcef..1438d61e2 100644 --- a/src/common/input/InputDevice.h +++ b/src/common/input/InputDevice.h @@ -42,9 +42,6 @@ #define PORT_3 2 #define PORT_4 3 -#define PORT_INC(port) ((port) + 1) -#define PORT_DEC(port) ((port) - 1) - #define DIRECTION_IN 0 #define DIRECTION_OUT 1 @@ -75,8 +72,12 @@ inline bool g_bIsTrackingMoMove = false; // Lookup array used to translate a gui port to an xbox usb port and vice versa extern int Gui2XboxPortArray[4]; -// Global function used to retrieve the printable name of a xid type +// Retrieves the printable name of a xid type std::string GetInputDeviceName(int dev_type); +// Converts the port number in the user format +std::string PortUserFormat(std::string_view); +// Extracts port and slot number from a port formatted as a string +void PortStr2Int(std::string_view port, int *port1, int *slot); /* Abstract class which represents a host device usable for input/output */ class InputDevice @@ -124,9 +125,9 @@ public: // sets the ID of this device void SetId(int ID) { m_ID = ID; } // retrieves the port this device is attached to - bool GetPort(int Port) const { return m_XboxPort[Port]; } + bool GetPort(std::string_view Port) const; // sets the port this device is attached to - void SetPort(int Port, bool Connect) { m_XboxPort[Port] = Connect; } + void SetPort(std::string_view Port, bool Connect); protected: @@ -134,6 +135,8 @@ protected: void AddInput(Input* const In); // adds an output control to the device void AddOutput(Output* const Out); + // searches for a port + const auto FindPort(std::string_view Port) const; // indicates that the device has new input data available bool m_bDirty; // lock for the bindings map @@ -159,7 +162,7 @@ private: // all the output controls detected and usable on this device std::vector m_Outputs; // xbox port(s) this device is attached to - bool m_XboxPort[4] = { false, false, false, false }; + std::vector m_XboxPort; // button bindings to the xbox device buttons std::map m_Bindings; }; diff --git a/src/common/input/InputManager.cpp b/src/common/input/InputManager.cpp index 7bab6f1b5..3167d554b 100644 --- a/src/common/input/InputManager.cpp +++ b/src/common/input/InputManager.cpp @@ -47,6 +47,7 @@ #include "EmuShared.h" #include "devices\usb\OHCI.h" #include "core/common/video/RenderBase.hpp" +#include // hle input specific #include "core\hle\XAPI\Xapi.h" @@ -69,7 +70,7 @@ int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)] = { XBOX_CTRL_NUM_BUTTONS, // ARCADE_STICK }; -extern CXBX_CONTROLLER_HOST_BRIDGE g_XboxControllerHostBridge[4]; // hle xinput +void UpdateXppState(DeviceState *dev, XBOX_INPUT_DEVICE type, std::string_view port); InputDeviceManager g_InputDeviceManager; @@ -108,10 +109,41 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd) RefreshDevices(); if (!is_gui) { - UpdateDevices(PORT_1, false); - UpdateDevices(PORT_2, false); - UpdateDevices(PORT_3, false); - UpdateDevices(PORT_4, false); + for (unsigned i = 0; i < 12; ++i) { + g_devs[i].type = XBOX_INPUT_DEVICE::DEVICE_INVALID; + g_devs[i].port = std::to_string(PORT_INVALID); + } + + for (unsigned i = 0; i < 4; ++i) { + int type; + g_EmuShared->GetInputDevTypeSettings(&type, i); + std::string port = std::to_string(i); + if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) { + switch (type) + { + case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): + case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): { + ConstructHleInputDevice(&g_devs[CTRL_OFFSET + i], nullptr, type, port); + for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) { + g_EmuShared->GetInputSlotTypeSettings(&type, i, slot); + if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) { + assert(type == to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT)); + ConstructHleInputDevice(&g_devs[MU_OFFSET + slot], &g_devs[CTRL_OFFSET + i], type, port + std::to_string(slot)); + } + } + } + break; + + case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK): + case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): + ConstructHleInputDevice(&g_devs[CTRL_OFFSET + i], nullptr, type, port); + break; + + default: + assert(0); + } + } + } } RawInput::IgnoreHotplug = false; @@ -188,164 +220,107 @@ void InputDeviceManager::RemoveDevice(std::function Ca } } -void InputDeviceManager::UpdateDevices(int port, bool ack) +void InputDeviceManager::UpdateDevices(std::string_view port, bool ack) { - if (port > PORT_4 || port < PORT_1) { - EmuLog(LOG_LEVEL::WARNING, "Invalid port number. The port was %d", PORT_INC(port)); - return; - } - int type; -#if 0 // lle usb - int usb_port = PORT_DEC(Gui2XboxPortArray[port]); -#else - int usb_port = port; -#endif - g_EmuShared->GetInputDevTypeSettings(&type, port); + DeviceState *dev, *upstream; + int port1, type, slot; + dev = &g_devs[port1]; + PortStr2Int(port, &port1, &slot); + if (slot == PORT_INVALID) { // Port references a device attached to an xbox port + upstream = nullptr; + g_EmuShared->GetInputDevTypeSettings(&type, port1); + } + else { // Port references a device attached to a slot port + assert(dev->type == XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE || + dev->type == XBOX_INPUT_DEVICE::MS_CONTROLLER_S); + upstream = dev; + dev = dev->info.ctrl.slots[slot]; + g_EmuShared->GetInputSlotTypeSettings(&type, port1, slot); + } + + // connect slot + if (dev == nullptr) { + ConnectDevice(&g_devs[MU_OFFSET + port1 + slot], upstream, type, port); + } // connect - if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) && - //g_XidDeviceObjArray[usb_port].xid_dev == nullptr) { lle usb - g_XboxControllerHostBridge[usb_port].XboxType == XBOX_INPUT_DEVICE::DEVICE_INVALID) { - ConnectDevice(port, usb_port, type); + else if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) && + dev->type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { + ConnectDevice(dev, upstream, type, port); } // disconnect else if (type == to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) && - //g_XidDeviceObjArray[usb_port].xid_dev != nullptr) { lle usb - g_XboxControllerHostBridge[usb_port].XboxType != XBOX_INPUT_DEVICE::DEVICE_INVALID) { - DisconnectDevice(port, usb_port, ack); + dev->type != XBOX_INPUT_DEVICE::DEVICE_INVALID) { + // We don't need to check of we need to destroy child devices because the UpdateInputEvent_t message always + // calls us on the entire slot connectivity if the device has slots available + DisconnectDevice(dev, port, ack); } // update bindings else { - auto dev = g_InputDeviceManager.FindDevice(usb_port, 0); - if (dev != nullptr) { - dev->SetPort(usb_port, false); + auto dev1 = g_InputDeviceManager.FindDevice(port); + if (dev1 != nullptr) { + dev1->SetPort(port, false); } if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) { - if (type != to_underlying(g_XboxControllerHostBridge[port].XboxType)) { - // this will happen when the user changes the type of an existing xbox device type connected to a port - if (g_XboxControllerHostBridge[port].bPendingRemoval == false) { - g_XboxControllerHostBridge[port].bPendingRemoval = true; + if (type != to_underlying(dev->type)) { + // This will happen when the user changes the type of an existing xbox device type connected to a port. + // We don't need to check of we need to destroy child devices because the UpdateInputEvent_t message always + // calls us on the entire slot connectivity if the device has slots available + if (dev->bPendingRemoval == false) { + dev->bPendingRemoval = true; return; } else { - DestructHleInputDevice(port); - if (!ConstructHleInputDevice(type, port)) { - return; - } + DestructHleInputDevice(dev); + ConstructHleInputDevice(dev, upstream, type, port); } } - BindHostDevice(port, usb_port, type); + BindHostDevice(type, port); } } } -void InputDeviceManager::ConnectDevice(int port, int usb_port, int type) +void InputDeviceManager::ConnectDevice(DeviceState *dev, DeviceState *upstream, int type, std::string_view port) { -#if 0 // lle usb - switch (type) - { - case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): { - if (ConstructHub(usb_port)) { - if (!ConstructXpadDuke(usb_port)) { - DestructHub(usb_port); - return; - } - g_XidDeviceObjArray[usb_port].xid_type = type; - EmuLog(LOG_LEVEL::INFO, "Attached device %s to port %d", GetInputDeviceName(type).c_str(), PORT_INC(port)); - } - } - break; - - case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): - case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN): - case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL): - case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT): - case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE): - case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): { - EmuLog(LOG_LEVEL::INFO, "%s: device %s is not yet supported", __func__, GetInputDeviceName(type).c_str()); - return; - } - - default: - EmuLog(LOG_LEVEL::WARNING, "Attempted to attach an unknown device type (type was %d)", type); - return; - } -#else - if (!ConstructHleInputDevice(type, port)) { - return; - } - EmuLog(LOG_LEVEL::INFO, "Attached device %s to port %d", GetInputDeviceName(type).c_str(), PORT_INC(port)); -#endif - BindHostDevice(port, usb_port, type); + ConstructHleInputDevice(dev, upstream, type, port); + BindHostDevice(type, port); + EmuLog(LOG_LEVEL::INFO, "Attached device %s to port %d", GetInputDeviceName(type).c_str(), + PortUserFormat(port).c_str()); } -void InputDeviceManager::DisconnectDevice(int port, int usb_port, bool ack) +void InputDeviceManager::DisconnectDevice(DeviceState *dev, std::string_view port, bool ack) { -#if 0 // lle usb - if (g_XidDeviceObjArray[usb_port].xid_dev != nullptr) { - int type = g_XidDeviceObjArray[usb_port].xid_type; - - switch (type) - { - case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): { - if (ack) { - assert(g_HubObjArray[usb_port] != nullptr); - DestructHub(usb_port); - DestructXpadDuke(usb_port); - g_HostController->OHCI_SetRemovalFlag(usb_port, false); - EmuLog(LOG_LEVEL::INFO, "Detached device %s from port %d", GetInputDeviceName(type).c_str(), PORT_INC(port)); - } - else { - g_HostController->OHCI_SetRemovalFlag(usb_port, true); - } - } - break; - - case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): - case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN): - case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL): - case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT): - case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE): - case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): { - EmuLog(LOG_LEVEL::INFO, "%s: device %s is not yet supported", __func__, GetInputDeviceName(type).c_str()); - return; - } - - default: - EmuLog(LOG_LEVEL::WARNING, "Attempted to detach an unknown device type (type was %d)", type); - return; - } -#endif - if (g_XboxControllerHostBridge[port].XboxType != XBOX_INPUT_DEVICE::DEVICE_INVALID) { - if (ack) { - int type = to_underlying(g_XboxControllerHostBridge[port].XboxType); - DestructHleInputDevice(port); - EmuLog(LOG_LEVEL::INFO, "Detached device %s from port %d", GetInputDeviceName(type).c_str(), PORT_INC(port)); - } - else { - g_XboxControllerHostBridge[port].bPendingRemoval = true; - } - auto dev = g_InputDeviceManager.FindDevice(usb_port, 0); - if (dev != nullptr) { - dev->SetPort(usb_port, false); - } + if (ack) { + int type = to_underlying(dev->type); + DestructHleInputDevice(dev); + EmuLog(LOG_LEVEL::INFO, "Detached device %s from port %d", GetInputDeviceName(type).c_str(), PortUserFormat(port).c_str()); } else { - EmuLog(LOG_LEVEL::WARNING, "Attempted to detach a device not attached to the emulated machine"); + dev->bPendingRemoval = true; + } + auto dev1 = g_InputDeviceManager.FindDevice(port); + if (dev1 != nullptr) { + dev1->SetPort(port, false); } } -void InputDeviceManager::BindHostDevice(int port, int usb_port, int type) +void InputDeviceManager::BindHostDevice(int type, std::string_view port) { + if (type == to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT)) { + // MUs don't have any host device bound, so we just return + return; + } + char dev_name[50]; char dev_control_names[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH]; - - g_EmuShared->GetInputDevNameSettings(dev_name, port); - g_EmuShared->GetInputBindingsSettings(dev_control_names, dev_num_buttons[type], port); + int port1, slot; + PortStr2Int(port, &port1, &slot); + g_EmuShared->GetInputDevNameSettings(dev_name, port1); + g_EmuShared->GetInputBindingsSettings(dev_control_names, dev_num_buttons[type], port1); auto dev = FindDevice(std::string(dev_name)); if (dev != nullptr) { - std::vector controls = dev->GetIoControls(); + std::vector controls = dev->GetIoControls(); for (int index = 0; index < dev_num_buttons[type]; index++) { std::string dev_button(dev_control_names[index]); auto it = std::find_if(controls.begin(), controls.end(), [&dev_button](const auto control) { @@ -356,54 +331,53 @@ void InputDeviceManager::BindHostDevice(int port, int usb_port, int type) }); dev->SetBindings(index, (it != controls.end()) ? *it : nullptr); } - dev->SetPort(usb_port, true); + dev->SetPort(port, true); } } -bool InputDeviceManager::UpdateXboxPortInput(int usb_port, void* Buffer, int Direction, int xid_type) +bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int direction, int type) { - assert(Direction == DIRECTION_IN || Direction == DIRECTION_OUT); - assert(xid_type > to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) && - xid_type < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)); + assert(direction == DIRECTION_IN || direction == DIRECTION_OUT); + assert(type > to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) && + type < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)); bool has_changed = false; // First check if ImGui is focus, then ignore any input update occur. // If somebody else is currently holding the lock, we won't wait and instead report no input changes if (!g_renderbase->IsImGuiFocus() && m_Mtx.try_lock()) { - for (auto &dev_ptr : m_Devices) { - if (dev_ptr->GetPort(usb_port)) { - switch (xid_type) + for (auto &dev : m_Devices) { + if (dev->GetPort(std::to_string(port))) { + switch (type) { case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): - case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK): { - has_changed = UpdateInputXpad(dev_ptr, Buffer, Direction); - } - break; + case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK): + has_changed = UpdateInputXpad(dev, buffer, direction); + m_Mtx.unlock(); + return has_changed; - case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): { - has_changed = UpdateInputSBC(dev_ptr, Buffer, Direction, usb_port); - } - break; + case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): + has_changed = UpdateInputSBC(dev, buffer, direction, port); + m_Mtx.unlock(); + return has_changed; + + case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT): + assert(0); + break; case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN): case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL): - case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT): - case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE): { - EmuLog(LOG_LEVEL::WARNING, "An unsupported device is attached at port %d! The device was %s", - Gui2XboxPortArray[usb_port], GetInputDeviceName(xid_type).c_str()); - } - break; + case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE): + EmuLog(LOG_LEVEL::ERROR2, "An unsupported device is attached at port %d! The device was %s", + Gui2XboxPortArray[port], GetInputDeviceName(type).c_str()); + break; - default: { - EmuLog(LOG_LEVEL::WARNING, "An unknown device attached at port %d! The type was %s", - Gui2XboxPortArray[usb_port], GetInputDeviceName(xid_type).c_str()); - } } } } m_Mtx.unlock(); } + return has_changed; } @@ -690,18 +664,12 @@ std::shared_ptr InputDeviceManager::FindDevice(SDL_JoystickID id) c } } -std::shared_ptr InputDeviceManager::FindDevice(int usb_port, int dummy) const +std::shared_ptr InputDeviceManager::FindDevice(std::string_view port) const { - // Ignore dummy, it's just used to overload the function - - if (usb_port == PORT_INVALID) { - return nullptr; - } - std::lock_guard lck(m_Mtx); - auto it = std::find_if(m_Devices.begin(), m_Devices.end(), [usb_port](const auto& Device) { - return Device->GetPort(usb_port); + auto it = std::find_if(m_Devices.begin(), m_Devices.end(), [port](const auto& Device) { + return Device->GetPort(port); }); if (it != m_Devices.end()) { return *it; @@ -762,7 +730,7 @@ void InputDeviceManager::HotplugHandler(bool is_sdl) int type; g_EmuShared->GetInputDevTypeSettings(&type, port); if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) { - BindHostDevice(port, port, type); + BindHostDevice(type, std::to_string(port)); } } } diff --git a/src/common/input/InputManager.h b/src/common/input/InputManager.h index 9b288d8ba..e3ae3c5a0 100644 --- a/src/common/input/InputManager.h +++ b/src/common/input/InputManager.h @@ -40,6 +40,9 @@ #define SLOT_TOP 0 #define SLOT_BOTTOM 1 +#define CTRL_OFFSET 0 +#define MU_OFFSET 4 + extern int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)]; inline XBOX_INPUT_DEVICE input_support_list[] = { @@ -100,36 +103,51 @@ struct SBCOutput { #pragma pack() - -// hle specific input types -typedef struct _CXBX_XINPUT_DEVICE_INFO { - uint8_t ucType; // xbox controller type - uint8_t ucSubType; // xbox controller subtype - uint8_t ucInputStateSize; // xbox controller input state size in bytes, not include dwPacketNumber - uint8_t ucFeedbackSize; // xbox controller feedback size in bytes, not include FeedbackHeader +struct CommonCtrlInfo { + xbox::HANDLE hhandle; // device handle returned by xapi + bool bAutoPoll; // autopoll on/off, as instructed by the title in XInputOpen + bool bAutoPollDefault; // default autopoll value, depending on device type + uint8_t ucType; // xapi type + uint8_t ucSubType; // xapi subtype + uint8_t ucInputStateSize; // input state size in bytes, does not include dwPacketNumber + uint8_t ucFeedbackSize; // feedback size in bytes, does not include FeedbackHeader uint32_t dwPacketNumber; -} -CXBX_XINPUT_DEVICE_INFO, *PCXBX_XINPUT_DEVICE_INFO; - -union CXBX_XINPUT_IN_STATE { - XpadInput Gamepad; - SBCInput SBC; }; -// this structure is for use of tracking the xbox controllers assigned to 4 ports. -typedef struct _CXBX_CONTROLLER_HOST_BRIDGE { - HANDLE hXboxDevice; // xbox device handle to this device, we use the address of this bridge as the handle, only set after opened. cleared after closed. - int XboxPort; // xbox port# for this xbox controller - XBOX_INPUT_DEVICE XboxType; // xbox device type - CXBX_XINPUT_IN_STATE *InState; - bool bPendingRemoval; - bool bSignaled; - bool bIoInProgress; - bool bAutoPoll; // autopoll on/off, as instructed by the title in XInputOpen - bool bAutoPollDefault; // default autopoll value, depending on device type - CXBX_XINPUT_DEVICE_INFO XboxDeviceInfo; -} -CXBX_CONTROLLER_HOST_BRIDGE, *PCXBX_CONTROLLER_HOST_BRIDGE; +struct DeviceState; +struct CtrlInfo { + CommonCtrlInfo common; + XpadInput in_buffer; + DeviceState *slots[XBOX_CTRL_NUM_SLOTS]; +}; + +struct ArcadeCtrlInfo { + CommonCtrlInfo common; + XpadInput in_buffer; +}; + +struct SbcInfo { + CommonCtrlInfo common; + SBCInput in_buffer; +}; + +union DeviceInfo { + CtrlInfo ctrl; + ArcadeCtrlInfo arcade; + SbcInfo sbc; +}; + +struct DeviceState { + DeviceState *upstream; + std::string port; + XBOX_INPUT_DEVICE type; + bool bPendingRemoval; + bool bSignaled; + DeviceInfo info; +}; + +extern DeviceState g_devs[4 + 8]; + class InputDeviceManager { @@ -137,7 +155,7 @@ public: void Initialize(bool is_gui, HWND hwnd); void Shutdown(); // read/write the input/output from/to the device attached to the supplied xbox port - bool UpdateXboxPortInput(int usb_port, void* Buffer, int Direction, int xid_type); + bool UpdateXboxPortInput(int port, void *buffer, int direction, int type); // add the device to the list of availble devices void AddDevice(std::shared_ptr Device); // remove the device from the list of availble devices @@ -151,9 +169,9 @@ public: // find device from its sdl id std::shared_ptr FindDevice(SDL_JoystickID id) const; // find device from its xbox port - std::shared_ptr FindDevice(int usb_port, int dummy) const; + std::shared_ptr FindDevice(std::string_view port) const; // attach/detach guest devices to the emulated machine - void UpdateDevices(int port, bool ack); + void UpdateDevices(std::string_view port, bool ack); // update input options void UpdateOpt(bool is_gui); // device hotplug event handler @@ -166,11 +184,11 @@ private: // update input for a Steel Battalion controller bool UpdateInputSBC(std::shared_ptr& Device, void* Buffer, int Direction, int Port); // bind a host device to an emulated device - void BindHostDevice(int port, int usb_port, int type); + void BindHostDevice(int type, std::string_view port); // connect a device to the emulated machine - void ConnectDevice(int port, int usb_port, int type); + void ConnectDevice(DeviceState *dev, DeviceState *upstream, int type, std::string_view port); // disconnect a device from the emulated machine - void DisconnectDevice(int port, int usb_port, bool ack); + void DisconnectDevice(DeviceState *dev, std::string_view port, bool ack); // all enumerated devices currently detected and supported std::vector> m_Devices; @@ -189,7 +207,7 @@ private: extern InputDeviceManager g_InputDeviceManager; // hle input functions -bool ConstructHleInputDevice(int Type, int Port); -void DestructHleInputDevice(int Port); +void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type, std::string_view port); +void DestructHleInputDevice(DeviceState *dev); #endif diff --git a/src/common/input/SdlJoystick.cpp b/src/common/input/SdlJoystick.cpp index 530686f5f..7c0f22461 100644 --- a/src/common/input/SdlJoystick.cpp +++ b/src/common/input/SdlJoystick.cpp @@ -160,7 +160,15 @@ namespace Sdl else { XInput::GetDeviceChanges(); DInput::GetDeviceChanges(); - g_InputDeviceManager.UpdateDevices(*static_cast(Event.user.data1), false); + std::string port = std::to_string(*static_cast(Event.user.data1)); + int port1, slot; + PortStr2Int(port, &port1, &slot); + if (g_devs[port1].type == XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE || g_devs[port1].type == XBOX_INPUT_DEVICE::MS_CONTROLLER_S) { + // Force an update of the entire slot connectivity of this port + g_InputDeviceManager.UpdateDevices(port + ".0", false); + g_InputDeviceManager.UpdateDevices(port + ".1", false); + } + g_InputDeviceManager.UpdateDevices(port, false); } delete Event.user.data1; diff --git a/src/common/input/SdlJoystick.h b/src/common/input/SdlJoystick.h index b6d18008e..05055f7a9 100644 --- a/src/common/input/SdlJoystick.h +++ b/src/common/input/SdlJoystick.h @@ -43,6 +43,12 @@ namespace Sdl } SDL_INIT_STATUS; + struct UPDATE_INPUT_DATA { + int Port; + int Slot; + bool Opt; + }; + extern uint32_t ExitEvent_t; extern uint32_t PopulateEvent_t; extern uint32_t UpdateInputEvent_t; diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 39d5ca233..b325ebc87 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -43,23 +43,25 @@ #include "Windef.h" #include #include "core\hle\XAPI\Xapi.h" +#include "distorm.h" +#include "mnemonics.h" +#include xbox::PXPP_DEVICE_TYPE g_DeviceType_Gamepad = nullptr; xbox::PXPP_DEVICE_TYPE g_DeviceType_SBC = nullptr; +xbox::PXPP_DEVICE_TYPE g_DeviceType_MU = nullptr; // Flag is unset after initialize devices is done by simulate LLE USB thread. std::atomic g_bIsDevicesInitializing = true; std::atomic g_bIsDevicesEmulating = false; -static CXBX_XINPUT_IN_STATE g_InState[4]; -// Global bridge for xbox controller to host, 4 elements for 4 ports. -CXBX_CONTROLLER_HOST_BRIDGE g_XboxControllerHostBridge[4] = { - { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, &g_InState[0], false, false, false, false, false, { 0, 0, 0, 0, 0 } }, - { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, &g_InState[1], false, false, false, false, false, { 0, 0, 0, 0, 0 } }, - { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, &g_InState[2], false, false, false, false, false, { 0, 0, 0, 0, 0 } }, - { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, &g_InState[3], false, false, false, false, false, { 0, 0, 0, 0, 0 } }, -}; +// Protects access to xpp types +std::atomic g_bXppGuard = false; + +// allocate enough memory for the max number of devices we can support simultaneously +// 4 duke / S / sbc / arcade jpystick (mutually exclusive) + 8 memory units +DeviceState g_devs[4 + 8]; bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) @@ -82,9 +84,15 @@ bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) } break; + case XBOX_INPUT_DEVICE::MEMORY_UNIT: { + if (XppType == g_DeviceType_MU) { + return true; + } + } + break; + case XBOX_INPUT_DEVICE::LIGHT_GUN: case XBOX_INPUT_DEVICE::STEERING_WHEEL: - case XBOX_INPUT_DEVICE::MEMORY_UNIT: case XBOX_INPUT_DEVICE::IR_DONGLE: default: break; @@ -93,15 +101,9 @@ bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) return false; } -bool operator!=(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) -{ - return !(XppType == XidType); -} - -bool ConstructHleInputDevice(int Type, int Port) +void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type, std::string_view port) { g_bIsDevicesEmulating = true; - bool ret = true; if (g_bIsChihiro) { // Don't emulate XID devices during Chihiro Emulation @@ -110,105 +112,153 @@ bool ConstructHleInputDevice(int Type, int Port) } // NOTE: initialize bAutoPollDefault to its default state, which varies depending on the device type - switch (Type) + switch (type) { - case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): { - g_XboxControllerHostBridge[Port].XboxPort = Port; - g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE; - g_XboxControllerHostBridge[Port].bPendingRemoval = false; - g_XboxControllerHostBridge[Port].bSignaled = false; - g_XboxControllerHostBridge[Port].bIoInProgress = false; - g_XboxControllerHostBridge[Port].bAutoPollDefault = true; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_GAMEPAD; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(XpadInput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(XpadOutput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0; - } - break; - - case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): { - g_XboxControllerHostBridge[Port].XboxPort = Port; - g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::MS_CONTROLLER_S; - g_XboxControllerHostBridge[Port].bPendingRemoval = false; - g_XboxControllerHostBridge[Port].bSignaled = false; - g_XboxControllerHostBridge[Port].bIoInProgress = false; - g_XboxControllerHostBridge[Port].bAutoPollDefault = true; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_GAMEPAD; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(XpadInput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(XpadOutput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0; - } - break; - - case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): { - g_XboxControllerHostBridge[Port].XboxPort = Port; - g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER; - g_XboxControllerHostBridge[Port].InState->SBC.ucGearLever = 8; - g_XboxControllerHostBridge[Port].InState->SBC.sAimingX = static_cast(0x7F); - g_XboxControllerHostBridge[Port].InState->SBC.sAimingY = static_cast(0x7F); - g_XboxControllerHostBridge[Port].bPendingRemoval = false; - g_XboxControllerHostBridge[Port].bSignaled = false; - g_XboxControllerHostBridge[Port].bIoInProgress = false; - g_XboxControllerHostBridge[Port].bAutoPollDefault = true; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_STEELBATTALION; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(SBCInput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(SBCOutput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0; - } - break; - - case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK): { - g_XboxControllerHostBridge[Port].XboxPort = Port; - g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::ARCADE_STICK; - g_XboxControllerHostBridge[Port].bPendingRemoval = false; - g_XboxControllerHostBridge[Port].bSignaled = false; - g_XboxControllerHostBridge[Port].bIoInProgress = false; - g_XboxControllerHostBridge[Port].bAutoPollDefault = true; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_GAMEPAD; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_ARCADE_STICK; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(XpadInput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(XpadOutput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0; - } - break; - - case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN): - case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL): - case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT): - case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE): - EmuLog(LOG_LEVEL::INFO, "%s: device %s is not yet supported", __func__, GetInputDeviceName(Type).c_str()); - ret = false; + case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): + dev->upstream = nullptr; + dev->port = port; + dev->type = XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE; + dev->bPendingRemoval = false; + dev->bSignaled = false; + dev->info.ctrl.common.bAutoPollDefault = true; + dev->info.ctrl.common.ucType = XINPUT_DEVTYPE_GAMEPAD; + dev->info.ctrl.common.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD; + dev->info.ctrl.common.ucInputStateSize = sizeof(XpadInput); + dev->info.ctrl.common.ucFeedbackSize = sizeof(XpadOutput); + dev->info.ctrl.common.dwPacketNumber = 0; + dev->info.ctrl.slots[SLOT_TOP] = dev->info.ctrl.slots[SLOT_BOTTOM] = nullptr; break; + case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): + dev->upstream = nullptr; + dev->port = port; + dev->type = XBOX_INPUT_DEVICE::MS_CONTROLLER_S; + dev->bPendingRemoval = false; + dev->bSignaled = false; + dev->info.ctrl.common.bAutoPollDefault = true; + dev->info.ctrl.common.ucType = XINPUT_DEVTYPE_GAMEPAD; + dev->info.ctrl.common.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT; + dev->info.ctrl.common.ucInputStateSize = sizeof(XpadInput); + dev->info.ctrl.common.ucFeedbackSize = sizeof(XpadOutput); + dev->info.ctrl.common.dwPacketNumber = 0; + dev->info.ctrl.slots[SLOT_TOP] = dev->info.ctrl.slots[SLOT_BOTTOM] = nullptr; + break; + + case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): + dev->upstream = nullptr; + dev->port = port; + dev->type = XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER; + dev->bPendingRemoval = false; + dev->bSignaled = false; + dev->info.sbc.in_buffer.ucGearLever = 8; + dev->info.sbc.in_buffer.sAimingX = static_cast(0x7F); + dev->info.sbc.in_buffer.sAimingY = static_cast(0x7F); + dev->info.sbc.common.bAutoPollDefault = true; + dev->info.sbc.common.ucType = XINPUT_DEVTYPE_STEELBATTALION; + dev->info.sbc.common.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT; + dev->info.sbc.common.ucInputStateSize = sizeof(SBCInput); + dev->info.sbc.common.ucFeedbackSize = sizeof(SBCOutput); + dev->info.sbc.common.dwPacketNumber = 0; + break; + + case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK): + dev->upstream = nullptr; + dev->port = port; + dev->type = XBOX_INPUT_DEVICE::ARCADE_STICK; + dev->bPendingRemoval = false; + dev->bSignaled = false; + dev->info.arcade.common.bAutoPollDefault = true; + dev->info.arcade.common.ucType = XINPUT_DEVTYPE_GAMEPAD; + dev->info.arcade.common.ucSubType = XINPUT_DEVSUBTYPE_GC_ARCADE_STICK; + dev->info.arcade.common.ucInputStateSize = sizeof(XpadInput); + dev->info.arcade.common.ucFeedbackSize = sizeof(XpadOutput); + dev->info.arcade.common.dwPacketNumber = 0; + break; + + case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT): { + assert(upstream != nullptr); + dev->upstream = upstream; + dev->port = port; + dev->type = XBOX_INPUT_DEVICE::MEMORY_UNIT; + dev->bPendingRemoval = false; + dev->bSignaled = false; + int port1, slot; + PortStr2Int(port, &port1, &slot); + assert(slot != PORT_INVALID); + dev->upstream->info.ctrl.slots[slot] = dev; + } + break; + default: - EmuLog(LOG_LEVEL::WARNING, "Attempted to attach an unknown device type (type was %d)", Type); - ret = false; + assert(0); } + UpdateXppState(dev, static_cast(type), port); + g_bIsDevicesEmulating = false; - return ret; } -void DestructHleInputDevice(int Port) +void DestructHleInputDevice(DeviceState *dev) { g_bIsDevicesEmulating = true; - g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::DEVICE_INVALID; - g_XboxControllerHostBridge[Port].XboxPort = PORT_INVALID; - while (g_XboxControllerHostBridge[Port].bIoInProgress) {} - g_XboxControllerHostBridge[Port].bPendingRemoval = false; - g_XboxControllerHostBridge[Port].bSignaled = false; - g_XboxControllerHostBridge[Port].bIoInProgress = false; - g_XboxControllerHostBridge[Port].bAutoPollDefault = false; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = 0; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = 0; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = 0; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = 0; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0; - std::memset(&g_InState[Port], 0, sizeof(CXBX_XINPUT_IN_STATE)); + XBOX_INPUT_DEVICE type = dev->type; + std::string port = dev->port; + dev->type = XBOX_INPUT_DEVICE::DEVICE_INVALID; + dev->port = std::to_string(PORT_INVALID); + dev->bPendingRemoval = false; + dev->bSignaled = false; + + switch (dev->type) + { + case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE: + case XBOX_INPUT_DEVICE::MS_CONTROLLER_S: + dev->info.ctrl.common.bAutoPollDefault = false; + dev->info.ctrl.common.ucType = 0; + dev->info.ctrl.common.ucSubType = 0; + dev->info.ctrl.common.ucInputStateSize = 0; + dev->info.ctrl.common.ucFeedbackSize = 0; + dev->info.ctrl.common.dwPacketNumber = 0; + dev->info.ctrl.slots[SLOT_TOP] = dev->info.ctrl.slots[SLOT_BOTTOM] = nullptr; + std::memset(&dev->info.sbc.in_buffer, 0, sizeof(XpadInput)); + break; + + case XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER: + dev->info.sbc.common.bAutoPollDefault = false; + dev->info.sbc.common.ucType = 0; + dev->info.sbc.common.ucSubType = 0; + dev->info.sbc.common.ucInputStateSize = 0; + dev->info.sbc.common.ucFeedbackSize = 0; + dev->info.sbc.common.dwPacketNumber = 0; + std::memset(&dev->info.sbc.in_buffer, 0, sizeof(SBCInput)); + break; + + case XBOX_INPUT_DEVICE::ARCADE_STICK: + dev->info.arcade.common.bAutoPollDefault = false; + dev->info.arcade.common.ucType = 0; + dev->info.arcade.common.ucSubType = 0; + dev->info.arcade.common.ucInputStateSize = 0; + dev->info.arcade.common.ucFeedbackSize = 0; + dev->info.arcade.common.dwPacketNumber = 0; + std::memset(&dev->info.sbc.in_buffer, 0, sizeof(XpadInput)); + break; + + case XBOX_INPUT_DEVICE::MEMORY_UNIT: { + assert(dev->upstream != nullptr && (dev->upstream->type == XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE || + dev->upstream->type == XBOX_INPUT_DEVICE::MS_CONTROLLER_S)); + int port1, slot; + PortStr2Int(port, &port1, &slot); + assert(slot != PORT_INVALID); + dev->upstream->info.ctrl.slots[slot] = nullptr; + } + break; + + default: + assert(0); + } + + UpdateXppState(dev, type, port); + dev->upstream = nullptr; g_bIsDevicesEmulating = false; } @@ -280,8 +330,143 @@ void SetupXboxDeviceTypes() return; } - EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_GAMEPAD Found at 0x%08X", (uintptr_t)g_DeviceType_Gamepad); + EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_GAMEPAD found at 0x%08X", (uintptr_t)g_DeviceType_Gamepad); } + + // The MU device type is not present in the xpp table, because that only holds the types that XInputOpen supports, and MUs are not + // one of those. Insted, this type is referenced by MU_Init, the main MU initialization function, so we can derive it from that. + // Unfortunately, the offset of the MU type varies slightly between xdk revisions, so we cannot just read from a fixed offset. What's + // constant is that the type is always hardcoded in a push instruction immediately followed by a call, and there are no other push - call + // instructions between the start of MU_Init and the offset of interest. + + if (uint8_t *start = reinterpret_cast(g_SymbolAddresses["MU_Init"])) { + _CodeInfo ci; + ci.code = start; + ci.codeLen = 100; + ci.codeOffset = 0; + ci.dt = Decode32Bits; + ci.features = DF_NONE; + std::array<_DInst, 50> info; + unsigned i; + + distorm_decompose(&ci, info.data(), 50, &i); + + i = 0; + const auto &it = std::find_if(info.begin(), info.end(), [&info, &i](_DInst &op) { + if (!(op.opcode == I_PUSH)) { + ++i; + return false; + } + + if (((i + 1) <= 49) && (info[i + 1].opcode == I_CALL)) { + return true; + } + + ++i; + return false; + }); + + if (it == info.end()) { + EmuLog(LOG_LEVEL::WARNING, "XDEVICE_TYPE_MEMORY_UNIT was not found inside MU_Init"); + } + else { + g_DeviceType_MU = reinterpret_cast(*reinterpret_cast((it->addr + start + 1))); + EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT found at 0x%08X", reinterpret_cast(g_DeviceType_MU)); + } + } + else { + EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT was not found because MU_Init could not be found"); + } +} + +void UpdateXppState(DeviceState *dev, XBOX_INPUT_DEVICE type, std::string_view port) +{ + xbox::PXPP_DEVICE_TYPE xpp; + switch (type) + { + case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE: + case XBOX_INPUT_DEVICE::MS_CONTROLLER_S: + case XBOX_INPUT_DEVICE::ARCADE_STICK: + xpp = g_DeviceType_Gamepad; + break; + + case XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER: + xpp = g_DeviceType_SBC; + break; + + case XBOX_INPUT_DEVICE::MEMORY_UNIT: + xpp = g_DeviceType_MU; + break; + + default: + xpp = nullptr; + } + + assert(xpp != nullptr); + + int port1, slot; + PortStr2Int(port, &port1, &slot); + xbox::ulong_xt port_mask = 1 << port1; + + // Guard against the unfortunate case where XGetDevices or XGetDeviceChanges have already checked for g_bIsDevicesInitializing + // and g_bIsDevicesEmulating and a thread switch happens to this function + while (g_bXppGuard) {} + + if (xpp == g_DeviceType_MU) { + if ((dev->upstream->type != XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE) || + (dev->upstream->type != XBOX_INPUT_DEVICE::MS_CONTROLLER_S) || + dev->upstream->bPendingRemoval) { + xpp->CurrentConnected &= ~port_mask; + xpp->CurrentConnected &= ~(port_mask << 16); + } + else { + for (unsigned i = 0, j = 0; i < XBOX_CTRL_NUM_SLOTS; ++i, j += 16) { + if (xpp == dev->type && !dev->bPendingRemoval) { + xpp->CurrentConnected |= (port_mask << j); + } + else { + xpp->CurrentConnected &= ~(port_mask << j); + } + } + } + } + else { + if (xpp == dev->type && !dev->bPendingRemoval) { + xpp->CurrentConnected |= port_mask; + } + else { + xpp->CurrentConnected &= ~port_mask; + } + } + + xpp->ChangeConnected = xpp->PreviousConnected ^ xpp->CurrentConnected; +} + +template +xbox::dword_xt CxbxImpl_XInputHandler(xbox::HANDLE hDevice, xbox::PXINPUT_STATE pState) +{ + xbox::dword_xt status = ERROR_DEVICE_NOT_CONNECTED; + PXBOX_DEV_CONNECTIVITY Device = (PXBOX_DEV_CONNECTIVITY)hDevice; + int Port = Device->XboxPort; + + if ((g_XboxDevices[Port].hXboxDevice == hDevice) && !g_XboxDevices[Port].bPendingRemoval) { + if (g_XboxDevices[Port].bAutoPoll != IsXInputPoll) { + g_XboxDevices[Port].bIoInProgress = true; + if (g_InputDeviceManager.UpdateXboxPortInput(Port, g_XboxDevices[Port].InState, DIRECTION_IN, to_underlying(g_XboxDevices[Port].XboxType))) { + g_XboxDevices[Port].XboxDeviceInfo.dwPacketNumber++; + } + g_XboxDevices[Port].bIoInProgress = false; + } + + if constexpr (!IsXInputPoll) { + std::memcpy((void *)&pState->Gamepad, g_XboxDevices[Port].InState, g_XboxDevices[Port].XboxDeviceInfo.ucInputStateSize); + pState->dwPacketNumber = g_XboxDevices[Port].XboxDeviceInfo.dwPacketNumber; + } + + status = ERROR_SUCCESS; + } + + return status; } // ****************************************************************** @@ -312,63 +497,6 @@ xbox::void_xt WINAPI xbox::EMUPATCH(XInitDevices) }).detach(); } -// This is called to emulate async for both XGetDevices and XGetDeviceChanges -void UpdateConnectedDeviceState(xbox::PXPP_DEVICE_TYPE DeviceType) { - - // Do not process the queries until initialize delay and device emulating are complete. - if (g_bIsDevicesInitializing || g_bIsDevicesEmulating){ - return; - } - - int Port, PortMask; - for (Port = PORT_1, PortMask = 1; Port <= PORT_4; Port++, PortMask <<= 1) { - if (DeviceType == g_XboxControllerHostBridge[Port].XboxType && !g_XboxControllerHostBridge[Port].bPendingRemoval) { - DeviceType->CurrentConnected |= PortMask; - } - else { - DeviceType->CurrentConnected &= ~PortMask; - } - - if (static_cast(g_XboxControllerHostBridge[Port].bPendingRemoval) & - ~(static_cast(g_XboxControllerHostBridge[Port].bSignaled))) { - g_XboxControllerHostBridge[Port].bSignaled = true; - SDL_Event DeviceRemoveEvent; - SDL_memset(&DeviceRemoveEvent, 0, sizeof(SDL_Event)); - DeviceRemoveEvent.type = Sdl::DeviceRemoveAck_t; - DeviceRemoveEvent.user.data1 = new int(Port); - SDL_PushEvent(&DeviceRemoveEvent); - } - } - DeviceType->ChangeConnected = DeviceType->PreviousConnected ^ DeviceType->CurrentConnected; -} - -template -xbox::dword_xt CxbxImpl_XInputHandler(xbox::HANDLE hDevice, xbox::PXINPUT_STATE pState) -{ - xbox::dword_xt status = ERROR_DEVICE_NOT_CONNECTED; - PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; - int Port = Device->XboxPort; - - if ((g_XboxControllerHostBridge[Port].hXboxDevice == hDevice) && !g_XboxControllerHostBridge[Port].bPendingRemoval) { - if (g_XboxControllerHostBridge[Port].bAutoPoll != IsXInputPoll) { - g_XboxControllerHostBridge[Port].bIoInProgress = true; - if (g_InputDeviceManager.UpdateXboxPortInput(Port, g_XboxControllerHostBridge[Port].InState, DIRECTION_IN, to_underlying(g_XboxControllerHostBridge[Port].XboxType))) { - g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber++; - } - g_XboxControllerHostBridge[Port].bIoInProgress = false; - } - - if constexpr (!IsXInputPoll) { - std::memcpy((void *)&pState->Gamepad, g_XboxControllerHostBridge[Port].InState, g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize); - pState->dwPacketNumber = g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber; - } - - status = ERROR_SUCCESS; - } - - return status; -} - // ****************************************************************** // * patch: XGetDevices // * Note: This could be unpatched however, @@ -383,18 +511,38 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDevices) { LOG_FUNC_ONE_ARG(DeviceType); - UpdateConnectedDeviceState(DeviceType); + g_bXppGuard = true; + static dword_xt last_connected = 0; + if (g_bIsDevicesInitializing || g_bIsDevicesEmulating) { + g_bXppGuard = false; + RETURN(last_connected); + } + + for (unsigned i = 0; i < 12; ++i) { + if (static_cast(g_devs[i].bPendingRemoval) & + ~(static_cast(g_devs[i].bSignaled))) { + g_devs[i].bSignaled = true; + SDL_Event DeviceRemoveEvent; + SDL_memset(&DeviceRemoveEvent, 0, sizeof(SDL_Event)); + DeviceRemoveEvent.type = Sdl::DeviceRemoveAck_t; + DeviceRemoveEvent.user.data1 = new char[g_devs[i].port.size()]; + std::strcpy(static_cast(DeviceRemoveEvent.user.data1), g_devs[i].port.c_str()); + SDL_PushEvent(&DeviceRemoveEvent); + } + } UCHAR oldIrql = xbox::KeRaiseIrqlToDpcLevel(); - dword_xt ret = DeviceType->CurrentConnected; + last_connected = DeviceType->CurrentConnected; DeviceType->ChangeConnected = 0; DeviceType->PreviousConnected = DeviceType->CurrentConnected; xbox::KfLowerIrql(oldIrql); - RETURN(ret); + g_bXppGuard = false; + + RETURN(last_connected); } // ****************************************************************** @@ -417,11 +565,29 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(XGetDeviceChanges) LOG_FUNC_ARG(pdwRemovals) LOG_FUNC_END; - UpdateConnectedDeviceState(DeviceType); + g_bXppGuard = true; + if (g_bIsDevicesInitializing || g_bIsDevicesEmulating) { + *pdwInsertions = 0; + *pdwRemovals = 0; + g_bXppGuard = false; + RETURN(FALSE); + } + + for (unsigned i = 0; i < 12; ++i) { + if (static_cast(g_devs[i].bPendingRemoval) & + ~(static_cast(g_devs[i].bSignaled))) { + g_devs[i].bSignaled = true; + SDL_Event DeviceRemoveEvent; + SDL_memset(&DeviceRemoveEvent, 0, sizeof(SDL_Event)); + DeviceRemoveEvent.type = Sdl::DeviceRemoveAck_t; + DeviceRemoveEvent.user.data1 = new char[g_devs[i].port.size()]; + std::strcpy(static_cast(DeviceRemoveEvent.user.data1), g_devs[i].port.c_str()); + SDL_PushEvent(&DeviceRemoveEvent); + } + } BOOL ret = FALSE; - if(!DeviceType->ChangeConnected) { *pdwInsertions = 0; @@ -448,6 +614,7 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(XGetDeviceChanges) xbox::KfLowerIrql(oldIrql); } + g_bXppGuard = false; RETURN(ret); } @@ -470,12 +637,17 @@ xbox::HANDLE WINAPI xbox::EMUPATCH(XInputOpen) LOG_FUNC_ARG(pPollingParameters) LOG_FUNC_END; + if (DeviceType == g_DeviceType_MU) { + // MUs cannot be opened with XInputOpen + RETURN(NULL); + } + if (dwPort >= PORT_1 && dwPort <= PORT_4) { - if (DeviceType == g_XboxControllerHostBridge[dwPort].XboxType) { - g_XboxControllerHostBridge[dwPort].bAutoPoll = pPollingParameters != xbox::zeroptr ? - pPollingParameters->fAutoPoll : g_XboxControllerHostBridge[dwPort].bAutoPollDefault; - g_XboxControllerHostBridge[dwPort].hXboxDevice = &g_XboxControllerHostBridge[dwPort]; - RETURN(g_XboxControllerHostBridge[dwPort].hXboxDevice); + if (DeviceType == g_XboxDevices[dwPort].XboxType) { + g_XboxDevices[dwPort].bAutoPoll = pPollingParameters != xbox::zeroptr ? + pPollingParameters->fAutoPoll : g_XboxDevices[dwPort].bAutoPollDefault; + g_XboxDevices[dwPort].hXboxDevice = &g_XboxDevices[dwPort]; + RETURN(g_XboxDevices[dwPort].hXboxDevice); } } @@ -492,9 +664,9 @@ xbox::void_xt WINAPI xbox::EMUPATCH(XInputClose) { LOG_FUNC_ONE_ARG(hDevice); - PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; + PXBOX_DEV_CONNECTIVITY Device = (PXBOX_DEV_CONNECTIVITY)hDevice; int Port = Device->XboxPort; - if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice) { + if (g_XboxDevices[Port].hXboxDevice == hDevice) { Device->hXboxDevice = NULL; } } @@ -530,12 +702,12 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XInputGetCapabilities) LOG_FUNC_END; dword_xt ret = ERROR_DEVICE_NOT_CONNECTED; - PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; + PXBOX_DEV_CONNECTIVITY Device = (PXBOX_DEV_CONNECTIVITY)hDevice; int Port = Device->XboxPort; - if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice && !g_XboxControllerHostBridge[Port].bPendingRemoval) { - pCapabilities->SubType = g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType; + if (g_XboxDevices[Port].hXboxDevice == hDevice && !g_XboxDevices[Port].bPendingRemoval) { + pCapabilities->SubType = g_XboxDevices[Port].XboxDeviceInfo.ucSubType; UCHAR* pCap = (UCHAR*)(&pCapabilities->In); - memset(pCap, 0xFF, g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize); + memset(pCap, 0xFF, g_XboxDevices[Port].XboxDeviceInfo.ucInputStateSize + g_XboxDevices[Port].XboxDeviceInfo.ucFeedbackSize); ret = ERROR_SUCCESS; } @@ -579,11 +751,11 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XInputSetState) LOG_FUNC_ARG(pFeedback) LOG_FUNC_END; - PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; + PXBOX_DEV_CONNECTIVITY Device = (PXBOX_DEV_CONNECTIVITY)hDevice; int Port = Device->XboxPort; - if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice && !g_XboxControllerHostBridge[Port].bPendingRemoval) { + if (g_XboxDevices[Port].hXboxDevice == hDevice && !g_XboxDevices[Port].bPendingRemoval) { pFeedback->Header.dwStatus = ERROR_IO_PENDING; - g_InputDeviceManager.UpdateXboxPortInput(Port, (void*)&pFeedback->Rumble, DIRECTION_OUT, to_underlying(g_XboxControllerHostBridge[Port].XboxType)); + g_InputDeviceManager.UpdateXboxPortInput(Port, (void*)&pFeedback->Rumble, DIRECTION_OUT, to_underlying(g_XboxDevices[Port].XboxType)); pFeedback->Header.dwStatus = ERROR_SUCCESS; if (pFeedback->Header.hEvent != NULL && ObReferenceObjectByHandle(pFeedback->Header.hEvent, &xbox::ExEventObjectType, (PVOID*)&pFeedback->Header.IoCompletedEvent) == ERROR_SUCCESS) { diff --git a/src/gui/DlgInputConfig.cpp b/src/gui/DlgInputConfig.cpp index 744e809ad..03d567291 100644 --- a/src/gui/DlgInputConfig.cpp +++ b/src/gui/DlgInputConfig.cpp @@ -79,12 +79,8 @@ void SyncInputSettings(int port_num, int dev_type, bool is_opt) g_EmuShared->SetInputGeneralSettings(&g_Settings->m_input_general); port_num = PORT_INVALID; } -#if 0 // lle usb - ipc_send_kernel_update(IPC_UPDATE_KERNEL::CONFIG_INPUT_SYNC, PORT_DEC(Gui2XboxPortArray[port_num]), - reinterpret_cast(g_ChildWnd)); -#else + ipc_send_kernel_update(IPC_UPDATE_KERNEL::CONFIG_INPUT_SYNC, port_num, reinterpret_cast(g_ChildWnd)); -#endif } } @@ -165,6 +161,13 @@ INT_PTR CALLBACK DlgInputConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPAR HWND hHandle = GetDlgItem(hWndDlg, IDC_DEVICE_PORT1 + port); int DeviceType = SendMessage(hHandle, CB_GETITEMDATA, SendMessage(hHandle, CB_GETCURSEL, 0, 0), 0); g_Settings->m_input_port[port].Type = DeviceType; + if (DeviceType != to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE) || + DeviceType != to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S)) { + // Forcefully set the child devices to none. This will happen if the user sets MUs in the controller dialog but + // then they set the parent device to a device that cannot support them in the input dialog + g_Settings->m_input_port[port].TopSlotType = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID); + g_Settings->m_input_port[port].BottomSlotType = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID); + } SyncInputSettings(port, DeviceType, false); } } diff --git a/src/gui/controllers/DlgDukeControllerConfig.cpp b/src/gui/controllers/DlgDukeControllerConfig.cpp index 09576cca4..ccbfe3ed5 100644 --- a/src/gui/controllers/DlgDukeControllerConfig.cpp +++ b/src/gui/controllers/DlgDukeControllerConfig.cpp @@ -81,7 +81,7 @@ void DukeInputWindow::Initialize(HWND hwnd, int port_num, int dev_type) } SendMessage(m_hwnd_window, WM_SETTEXT, 0, - reinterpret_cast((title + std::to_string(PORT_INC(m_port_num))).c_str())); + reinterpret_cast((title + PortUserFormat(std::to_string(m_port_num))).c_str())); // Set the maximum profile name lenght the user can enter in the profile combobox SendMessage(m_hwnd_profile_list, CB_LIMITTEXT, 49, 0); diff --git a/src/gui/controllers/DlgSBControllerConfig.cpp b/src/gui/controllers/DlgSBControllerConfig.cpp index 37db7bd8c..4627c879e 100644 --- a/src/gui/controllers/DlgSBControllerConfig.cpp +++ b/src/gui/controllers/DlgSBControllerConfig.cpp @@ -54,7 +54,7 @@ void SbcInputWindow::Initialize(HWND hwnd, int port_num, int dev_type) // Set window title std::string title("Steel Battalion Controller at port "); SendMessage(m_hwnd_window, WM_SETTEXT, 0, - reinterpret_cast((title + std::to_string(PORT_INC(m_port_num))).c_str())); + reinterpret_cast((title + PortUserFormat(std::to_string(m_port_num))).c_str())); // Set the maximum profile name lenght the user can enter in the profile combobox SendMessage(m_hwnd_profile_list, CB_LIMITTEXT, 49, 0);