diff --git a/import/XbSymbolDatabase b/import/XbSymbolDatabase index c2fe7365a..8b000c0ca 160000 --- a/import/XbSymbolDatabase +++ b/import/XbSymbolDatabase @@ -1 +1 @@ -Subproject commit c2fe7365adae51ffabd9fe318ecdfaeac2cbe097 +Subproject commit 8b000c0ca7f20d88dddd95e80ad257ba2a0cffaa diff --git a/src/common/Settings.cpp b/src/common/Settings.cpp index 0a36f8662..6b082b6d6 100644 --- a/src/common/Settings.cpp +++ b/src/common/Settings.cpp @@ -33,7 +33,6 @@ #include "core\kernel\support\Emu.h" #include "EmuShared.h" #include -#include "common\input\InputManager.h" #include "common\input\layout_xbox_device.h" #include #include "common/util/cliConfig.hpp" @@ -148,6 +147,8 @@ static struct { const char* type = "Type"; const char* device = "DeviceName"; const char* config = "ProfileName"; + const char *top_slot = "TopSlot"; + const char *bottom_slot = "BottomSlot"; } sect_input_port; static const char* section_input_profiles = "input-profile-"; @@ -465,15 +466,24 @@ bool Settings::LoadConfig() // ==== Input Port Begin ==== for (int port_num = 0; port_num < 4; port_num++) { - std::string current_section = std::string(section_input_port) + std::to_string(port_num); - int ret = m_si.GetLongValue(current_section.c_str(), sect_input_port.type, -2); - if (ret == -2) { + for (int slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) { m_input_port[port_num].Type = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID); - continue; + m_input_port[port_num].SlotType[slot] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID); + + std::string current_section = std::string(section_input_port) + std::to_string(port_num); + int ret = m_si.GetLongValue(current_section.c_str(), sect_input_port.type, -2); + if (ret == -2) { + continue; + } + m_input_port[port_num].Type = ret; + m_input_port[port_num].DeviceName = m_si.GetValue(current_section.c_str(), sect_input_port.device); + m_input_port[port_num].ProfileName = TrimQuoteFromString(m_si.GetValue(current_section.c_str(), sect_input_port.config)); + ret = m_si.GetLongValue(current_section.c_str(), slot == 0 ? sect_input_port.top_slot : sect_input_port.bottom_slot, -2); + if (ret == -2) { + continue; + } + m_input_port[port_num].SlotType[slot] = ret; } - m_input_port[port_num].Type = ret; - m_input_port[port_num].DeviceName = m_si.GetValue(current_section.c_str(), sect_input_port.device); - m_input_port[port_num].ProfileName = TrimQuoteFromString(m_si.GetValue(current_section.c_str(), sect_input_port.config)); } // ==== Input Port End ============== @@ -644,6 +654,8 @@ bool Settings::Save(std::string file_path) m_si.SetLongValue(current_section.c_str(), sect_input_port.type, m_input_port[port_num].Type, nullptr, false, true); m_si.SetValue(current_section.c_str(), sect_input_port.device, m_input_port[port_num].DeviceName.c_str(), nullptr, true); m_si.SetValue(current_section.c_str(), sect_input_port.config, quoted_prf_str.c_str(), nullptr, true); + m_si.SetLongValue(current_section.c_str(), sect_input_port.top_slot, m_input_port[port_num].SlotType[SLOT_TOP], nullptr, false, true); + m_si.SetLongValue(current_section.c_str(), sect_input_port.bottom_slot, m_input_port[port_num].SlotType[SLOT_BOTTOM], nullptr, false, true); } // ==== Input Port End ============== @@ -769,6 +781,8 @@ void Settings::SyncToEmulator() // register xbox device input settings for (int i = 0; i < 4; i++) { g_EmuShared->SetInputDevTypeSettings(&m_input_port[i].Type, i); + g_EmuShared->SetInputSlotTypeSettings(&m_input_port[i].SlotType[SLOT_TOP], i, SLOT_TOP); + g_EmuShared->SetInputSlotTypeSettings(&m_input_port[i].SlotType[SLOT_BOTTOM], i, SLOT_BOTTOM); if (m_input_port[i].Type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) { g_EmuShared->SetInputDevNameSettings(m_input_port[i].DeviceName.c_str(), i); auto it = std::find_if(m_input_profiles[m_input_port[i].Type].begin(), diff --git a/src/common/Settings.hpp b/src/common/Settings.hpp index 000ff615d..d24a6f755 100644 --- a/src/common/Settings.hpp +++ b/src/common/Settings.hpp @@ -29,7 +29,7 @@ #include "Cxbx.h" #include "SimpleIni.h" -#include "input\InputDevice.h" +#include "common\input\InputManager.h" #include "common\util\CxbxUtil.h" #include #include @@ -147,6 +147,7 @@ public: struct s_input_port { int Type; + int SlotType[XBOX_CTRL_NUM_SLOTS]; std::string DeviceName; std::string ProfileName; }; diff --git a/src/common/input/InputDevice.cpp b/src/common/input/InputDevice.cpp index f00ec285d..aa63b6dc7 100644 --- a/src/common/input/InputDevice.cpp +++ b/src/common/input/InputDevice.cpp @@ -33,8 +33,10 @@ // https://github.com/dolphin-emu/dolphin #include "InputDevice.h" +#include "InputManager.h" #include "common\util\CxbxUtil.h" #include +#include std::string GetInputDeviceName(int dev_type) @@ -90,6 +92,33 @@ std::string GetInputDeviceName(int dev_type) return str; } +std::string PortUserFormat(std::string_view port) +{ + int port_num, slot; + PortStr2Int(port, &port_num, &slot); + ++port_num; + if (slot != PORT_INVALID) { + ++slot; + return std::to_string(port_num) + "." + std::to_string(slot); + } + else { + return std::to_string(port_num); + } +} + +void PortStr2Int(std::string_view port, int *port_num, int *slot) +{ + *slot = PORT_INVALID; + auto &ret = std::from_chars(port.data(), port.data() + port.size(), *port_num); + 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 +159,31 @@ const std::vector InputDevice::GetIoControls() }); return vec; } + +const auto InputDevice::FindPort(std::string_view Port) const +{ + return std::find_if(m_XboxPort.begin(), m_XboxPort.end(), [Port](std::string_view CurrPort) { + if (CurrPort == Port) { + return true; + } + return false; + }); +} + +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; +} diff --git a/src/common/input/InputDevice.h b/src/common/input/InputDevice.h index c0c8cbcef..18323d8b8 100644 --- a/src/common/input/InputDevice.h +++ b/src/common/input/InputDevice.h @@ -36,15 +36,6 @@ #include #include "SDL.h" -#define PORT_INVALID -1 -#define PORT_1 0 -#define PORT_2 1 -#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 @@ -72,11 +63,13 @@ XBOX_INPUT_DEVICE; inline bool g_bIsTrackingMoLeave = false; 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 *port_num, int *slot); /* Abstract class which represents a host device usable for input/output */ class InputDevice @@ -124,9 +117,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 +127,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 @@ -141,27 +136,32 @@ protected: public: // retrieves the map of input bindings - const std::map GetBindings() const { + const std::map GetBindings(const std::string &Port) const { std::lock_guard lck(m_BindingsMtx); - return m_Bindings; + return m_Bindings.find(Port)->second; } // sets a pair in the map of the input bindings - void SetBindings(int XButton, IoControl* Control) { + void SetBindings(int XButton, IoControl* Control, const std::string &Port) { std::lock_guard lck(m_BindingsMtx); - m_Bindings[XButton] = Control; + m_Bindings[Port][XButton] = Control; + } + // clears all input bindings for the specified xbox port + void ClearBindings(const std::string &Port) { + std::lock_guard lck(m_BindingsMtx); + m_Bindings[Port].clear(); } private: - // arbitrary ID assigned by to the device + // arbitrary ID assigned to the device int m_ID; // all the input controls detected and usable on this device std::vector m_Inputs; // 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 }; - // button bindings to the xbox device buttons - std::map m_Bindings; + std::vector m_XboxPort; + // per xbox port button bindings to the xbox device buttons + std::unordered_map> m_Bindings; }; #endif diff --git a/src/common/input/InputManager.cpp b/src/common/input/InputManager.cpp index 7bab6f1b5..894d32356 100644 --- a/src/common/input/InputManager.cpp +++ b/src/common/input/InputManager.cpp @@ -47,16 +47,11 @@ #include "EmuShared.h" #include "devices\usb\OHCI.h" #include "core/common/video/RenderBase.hpp" +#include // hle input specific #include "core\hle\XAPI\Xapi.h" -int Gui2XboxPortArray[4] = { - 3, - 4, - 1, - 2 -}; int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)] = { XBOX_CTRL_NUM_BUTTONS, // MS_CONTROLLER_DUKE @@ -69,7 +64,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; @@ -94,13 +89,13 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd) }); m_Cv.wait(lck, []() { - return (Sdl::SdlInitStatus != Sdl::SDL_NOT_INIT) && - (XInput::XInputInitStatus != XInput::XINPUT_NOT_INIT) && - (RawInput::RawInputInitStatus != RawInput::RAWINPUT_NOT_INIT); + return (Sdl::InitStatus != Sdl::NOT_INIT) && + (XInput::InitStatus != XInput::NOT_INIT) && + (RawInput::InitStatus != RawInput::NOT_INIT); }); lck.unlock(); - if (Sdl::SdlInitStatus < 0 || XInput::XInputInitStatus < 0 || RawInput::RawInputInitStatus < 0) { + if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0) { CxbxKrnlCleanup("Failed to initialize input subsystem! Consult debug log for more information"); } @@ -108,10 +103,43 @@ 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 < MAX_DEVS; ++i) { + g_devs[i].type = XBOX_INPUT_DEVICE::DEVICE_INVALID; + g_devs[i].port = std::to_string(PORT_INVALID); + } + + for (unsigned i = 0; i < XBOX_NUM_PORTS; ++i) { + int type; + g_EmuShared->GetInputDevTypeSettings(&type, i); + if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) { + std::string port = std::to_string(i); + 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); + BindHostDevice(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 + (XBOX_CTRL_NUM_SLOTS * i) + 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); + BindHostDevice(type, port); + break; + + default: + assert(0); + } + } + } } RawInput::IgnoreHotplug = false; @@ -188,164 +216,116 @@ 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 port_num, slot, type; + PortStr2Int(port, &port_num, &slot); + dev = &g_devs[port_num]; + if (slot == PORT_INVALID) { // Port references a device attached to an xbox port + upstream = nullptr; + g_EmuShared->GetInputDevTypeSettings(&type, port_num); + } + else { // Port references a device attached to a slot port + upstream = dev; + dev = dev->slots[slot]; + g_EmuShared->GetInputSlotTypeSettings(&type, port_num, slot); + } + + // updating a slot + if (dev == nullptr) { + // connect slot + if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) && + g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port_num) + slot].type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { + ConnectDevice(&g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port_num) + slot], upstream, type, port); + } + // disconnect slot + else if (type == to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) && + g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port_num) + slot].type != XBOX_INPUT_DEVICE::DEVICE_INVALID) { + DisconnectDevice(&g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port_num) + slot], port, ack); + } + // update bindings slot + else { + // MUs don't have any host devices attached, so this is a nop for now + } + } // 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 if we need to destroy child devices because the UpdateInputEvent_t message always + // calls us on the entire slot connectivity of a port + DisconnectDevice(dev, port, ack); } // update bindings else { - auto dev = g_InputDeviceManager.FindDevice(usb_port, 0); - if (dev != nullptr) { - dev->SetPort(usb_port, false); + auto host_dev = g_InputDeviceManager.FindDevice(port); + if (host_dev != nullptr) { + host_dev->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); } -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) { + DestructHleInputDevice(dev); } else { - EmuLog(LOG_LEVEL::WARNING, "Attempted to detach a device not attached to the emulated machine"); + dev->bPendingRemoval = true; + } + auto host_dev = g_InputDeviceManager.FindDevice(port); + if (host_dev != nullptr) { + host_dev->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 devices 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 port_num, slot; + PortStr2Int(port, &port_num, &slot); + g_EmuShared->GetInputDevNameSettings(dev_name, port_num); + g_EmuShared->GetInputBindingsSettings(dev_control_names, dev_num_buttons[type], port_num); auto dev = FindDevice(std::string(dev_name)); if (dev != nullptr) { - std::vector controls = dev->GetIoControls(); + std::string port_str(port); + dev->ClearBindings(port_str); + 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) { @@ -354,62 +334,62 @@ void InputDeviceManager::BindHostDevice(int port, int usb_port, int type) } return false; }); - dev->SetBindings(index, (it != controls.end()) ? *it : nullptr); + dev->SetBindings(index, (it != controls.end()) ? *it : nullptr, port_str); } - 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) { + std::string port_str = std::to_string(port); + if (dev->GetPort(port_str)) { + 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, port_str); + 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, port_str); + 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", + PortUserFormat(port_str).c_str(), 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; } -bool InputDeviceManager::UpdateInputXpad(std::shared_ptr& Device, void* Buffer, int Direction) +bool InputDeviceManager::UpdateInputXpad(std::shared_ptr& Device, void* Buffer, int Direction, const std::string &Port) { - std::map bindings = Device->GetBindings(); + std::map bindings = Device->GetBindings(Port); assert(bindings.size() == static_cast(dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE)])); if (Direction == DIRECTION_IN) { @@ -471,9 +451,9 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr& Device, v return true; } -bool InputDeviceManager::UpdateInputSBC(std::shared_ptr& Device, void* Buffer, int Direction, int Port) +bool InputDeviceManager::UpdateInputSBC(std::shared_ptr& Device, void* Buffer, int Direction, int Port_num, const std::string &Port) { - std::map bindings = Device->GetBindings(); + std::map bindings = Device->GetBindings(Port); assert(bindings.size() == static_cast(dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER)])); // NOTE: the output state is not supported @@ -494,7 +474,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr& Device, vo // 8 -> TunerDial Down // 9 -> GearLever Up // 10 -> GearLever Down - static uint16_t last_in_state[4] = { 0, 0, 0, 0 }; + static uint16_t last_in_state[XBOX_NUM_PORTS] = { 0, 0, 0, 0 }; SBCInput *in_buf = static_cast(Buffer); for (int i = 0; i < 4; i++) { ControlState state = (bindings[i] != nullptr) ? dynamic_cast(bindings[i])->GetState() : 0.0; @@ -510,10 +490,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] >> j) & 1)) { + if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) { in_buf->wButtons[0] ^= (1 << i); } - (last_in_state[Port] &= ~(1 << j)) |= (curr_in_state << j); + (last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j); } for (int i = 6; i < 34; i++) { @@ -530,10 +510,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] >> j) & 1)) { + if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) { in_buf->wButtons[2] ^= (1 << (i % 16)); } - (last_in_state[Port] &= ~(1 << j)) |= (curr_in_state << j); + (last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j); } for (int i = 39; i < 49; i += 2) { @@ -592,7 +572,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] >> j) & 1)) { + if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) { switch (i) { case 52: @@ -620,7 +600,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr& Device, vo break; } } - (last_in_state[Port] &= ~(1 << j)) |= (curr_in_state << j); + (last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j); } } @@ -630,7 +610,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr& Device, vo void InputDeviceManager::RefreshDevices() { std::unique_lock lck(m_Mtx); - Sdl::SdlPopulateOK = false; + Sdl::PopulateOK = false; m_Devices.clear(); lck.unlock(); XInput::PopulateDevices(); @@ -638,7 +618,7 @@ void InputDeviceManager::RefreshDevices() Sdl::PopulateDevices(); lck.lock(); m_Cv.wait(lck, []() { - return Sdl::SdlPopulateOK; + return Sdl::PopulateOK; }); for (auto &dev : m_Devices) { if (StrStartsWith(dev->GetDeviceName(), "KeyboardMouse")) { @@ -690,18 +670,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 +736,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 8edf43dd1..e47f2cfdf 100644 --- a/src/common/input/InputManager.h +++ b/src/common/input/InputManager.h @@ -37,6 +37,21 @@ #undef SetPort #endif +#define PORT_INVALID -1 +#define PORT_1 0 +#define PORT_2 1 +#define PORT_3 2 +#define PORT_4 3 +#define XBOX_NUM_PORTS 4 + +#define SLOT_TOP 0 +#define SLOT_BOTTOM 1 +#define XBOX_CTRL_NUM_SLOTS 2 + +#define CTRL_OFFSET 0 +#define MU_OFFSET 4 +#define MAX_DEVS (XBOX_NUM_PORTS + XBOX_CTRL_NUM_SLOTS * XBOX_NUM_PORTS) + extern int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)]; inline XBOX_INPUT_DEVICE input_support_list[] = { @@ -47,6 +62,13 @@ inline XBOX_INPUT_DEVICE input_support_list[] = { XBOX_INPUT_DEVICE::ARCADE_STICK, }; +inline XBOX_INPUT_DEVICE slot_support_list[] = { + XBOX_INPUT_DEVICE::DEVICE_INVALID, + XBOX_INPUT_DEVICE::MEMORY_UNIT, + // Microphone + // Phantasy star online keyboard +}; + #pragma pack(1) // xpad in/out buffers stripped of the first two bytes @@ -92,36 +114,36 @@ 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 - uint32_t dwPacketNumber; -} -CXBX_XINPUT_DEVICE_INFO, *PCXBX_XINPUT_DEVICE_INFO; - -union CXBX_XINPUT_IN_STATE { - XpadInput Gamepad; - SBCInput SBC; +union InputBuff { + XpadInput ctrl; + 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 DeviceInfo { + 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; + InputBuff buff; +}; + +struct DeviceState { + std::string port; + int port_idx; + XBOX_INPUT_DEVICE type; + bool bPendingRemoval; + bool bSignaled; + DeviceInfo info; + DeviceState *slots[XBOX_CTRL_NUM_SLOTS]; + DeviceState *upstream; +}; + +extern DeviceState g_devs[MAX_DEVS]; + class InputDeviceManager { @@ -129,7 +151,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 @@ -143,9 +165,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 @@ -154,15 +176,15 @@ public: private: // update input for an xbox controller - bool UpdateInputXpad(std::shared_ptr& Device, void* Buffer, int Direction); + bool UpdateInputXpad(std::shared_ptr& Device, void* Buffer, int Direction, const std::string &Port); // update input for a Steel Battalion controller - bool UpdateInputSBC(std::shared_ptr& Device, void* Buffer, int Direction, int Port); + bool UpdateInputSBC(std::shared_ptr& Device, void* Buffer, int Direction, int Port_num, const std::string &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; @@ -181,7 +203,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/InputWindow.cpp b/src/common/input/InputWindow.cpp index 69fcef6ed..2caec9292 100644 --- a/src/common/input/InputWindow.cpp +++ b/src/common/input/InputWindow.cpp @@ -69,6 +69,7 @@ bool InputWindow::IsProfileSaved() } } + return true; } @@ -177,6 +178,29 @@ void InputWindow::BindButton(int ControlID) } } +void InputWindow::UpdateProfile(const std::string &name, int command) +{ + switch (command) + { + case PROFILE_LOAD: + LoadProfile(name); + break; + + case PROFILE_SAVE: + SaveProfile(name); + break; + + case PROFILE_DELETE: + DeleteProfile(name); + break; + + case BUTTON_CLEAR: + case BUTTON_SWAP: + m_bHasChanges = true; + break; + } +} + InputWindow::ProfileIt InputWindow::FindProfile(const std::string& name) { auto it = std::find_if(g_Settings->m_input_profiles[m_dev_type].begin(), @@ -236,6 +260,7 @@ bool InputWindow::SaveProfile(const std::string& name) g_Settings->m_input_port[m_port_num].DeviceName = profile.DeviceName; g_Settings->m_input_port[m_port_num].ProfileName = profile.ProfileName; g_Settings->m_input_profiles[m_dev_type].push_back(std::move(profile)); + m_bHasChanges = false; return true; } diff --git a/src/common/input/InputWindow.h b/src/common/input/InputWindow.h index e860bb52b..4df21cae4 100644 --- a/src/common/input/InputWindow.h +++ b/src/common/input/InputWindow.h @@ -58,7 +58,7 @@ public: void UpdateDeviceList(); void BindButton(int ControlID); virtual void ClearBindings() = 0; - virtual void UpdateProfile(const std::string& name, int command) = 0; + virtual void UpdateProfile(const std::string& name, int command); void UpdateCurrentDevice(); bool IsProfileSaved(); void SwapMoCursorAxis(Button *button); @@ -74,6 +74,7 @@ protected: void OverwriteProfile(const std::string& name); void LoadDefaultProfile(); virtual int EnableDefaultButton() = 0; + virtual void SaveSlotConfig() = 0; // xbox device under configuration EmuDevice* m_DeviceConfig; @@ -106,6 +107,7 @@ public: void BindDefault(); void ClearBindings() override; void UpdateProfile(const std::string &name, int command) override; + void SaveSlotConfig() override; private: @@ -118,6 +120,8 @@ private: HWND m_hwnd_rumble; // handle of the rumble combobox HWND m_hwnd_rumble_list; + // handles of the slot combobox + HWND m_hwnd_slot_list[XBOX_CTRL_NUM_SLOTS]; // currently selected rumble control std::string m_rumble; }; @@ -127,7 +131,7 @@ class SbcInputWindow : public InputWindow public: void Initialize(HWND hwnd, int port_num, int dev_type) override; void ClearBindings() override; - void UpdateProfile(const std::string &name, int command) override; + void SaveSlotConfig() override; private: diff --git a/src/common/input/RawDevice.cpp b/src/common/input/RawDevice.cpp index a4c6c4343..fdceecc7b 100644 --- a/src/common/input/RawDevice.cpp +++ b/src/common/input/RawDevice.cpp @@ -35,7 +35,7 @@ namespace RawInput { - int RawInputInitStatus = RAWINPUT_NOT_INIT; + int InitStatus = NOT_INIT; bool IgnoreHotplug = true; void Init(std::mutex &Mtx, bool is_gui, HWND hwnd) @@ -44,7 +44,7 @@ namespace RawInput if (is_gui) { // We don't need to monitor xinput device changes from the gui, because there we have the refresh button to detect changes - RawInputInitStatus = RAWINPUT_INIT_SUCCESS; + InitStatus = INIT_SUCCESS; return; } @@ -69,16 +69,16 @@ namespace RawInput static_cast(sizeof(decltype(devices)::value_type)))) { EmuLog(LOG_LEVEL::ERROR2, "RegisterRawInputDevices failed: %i", GetLastError()); - RawInputInitStatus = RAWINPUT_INIT_ERROR; + InitStatus = INIT_ERROR; return; } - RawInputInitStatus = RAWINPUT_INIT_SUCCESS; + InitStatus = INIT_SUCCESS; } void DeInit() { - RawInputInitStatus = RAWINPUT_NOT_INIT; + InitStatus = NOT_INIT; IgnoreHotplug = true; } } diff --git a/src/common/input/RawDevice.h b/src/common/input/RawDevice.h index 7be2bc3f5..6d81a44a9 100644 --- a/src/common/input/RawDevice.h +++ b/src/common/input/RawDevice.h @@ -33,15 +33,15 @@ namespace RawInput { - typedef enum _RAWINPUT_INIT_STATUS : int + typedef enum _INIT_STATUS : int { - RAWINPUT_NOT_INIT = -2, - RAWINPUT_INIT_ERROR, - RAWINPUT_INIT_SUCCESS, + NOT_INIT = -2, + INIT_ERROR, + INIT_SUCCESS, } - RAWINPUT_INIT_STATUS; + INIT_STATUS; - extern int RawInputInitStatus; + extern int InitStatus; extern bool IgnoreHotplug; // initialize RawInput diff --git a/src/common/input/SdlJoystick.cpp b/src/common/input/SdlJoystick.cpp index 530686f5f..6a50d662b 100644 --- a/src/common/input/SdlJoystick.cpp +++ b/src/common/input/SdlJoystick.cpp @@ -54,8 +54,8 @@ namespace Sdl uint32_t PopulateEvent_t; uint32_t UpdateInputEvent_t; uint32_t DeviceRemoveAck_t; - int SdlInitStatus = SDL_NOT_INIT; - bool SdlPopulateOK = false; + int InitStatus = NOT_INIT; + bool PopulateOK = false; void Init(std::mutex& Mtx, std::condition_variable& Cv, bool is_gui) { @@ -65,7 +65,7 @@ namespace Sdl if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) < 0) { EmuLog(LOG_LEVEL::ERROR2, "Failed to initialize SDL subsystem! The error was: %s", SDL_GetError()); - SdlInitStatus = SDL_INIT_ERROR; + InitStatus = INIT_ERROR; lck.unlock(); Cv.notify_one(); return; @@ -74,7 +74,7 @@ namespace Sdl if (CustomEvent_t == (uint32_t)-1) { SDL_Quit(); EmuLog(LOG_LEVEL::ERROR2, "Failed to create SDL custom events!"); - SdlInitStatus = SDL_INIT_ERROR; + InitStatus = INIT_ERROR; lck.unlock(); Cv.notify_one(); return; @@ -96,7 +96,7 @@ namespace Sdl break; } } - SdlInitStatus = SDL_INIT_SUCCESS; + InitStatus = INIT_SUCCESS; lck.unlock(); Cv.notify_one(); @@ -133,10 +133,6 @@ namespace Sdl case SDL_JOYBUTTONUP: id = Event.jbutton.which; break; - - default: { - // unreachable - } } auto dev = g_InputDeviceManager.FindDevice(id); if (dev != nullptr) { @@ -147,7 +143,7 @@ namespace Sdl for (int i = 0; i < SDL_NumJoysticks(); i++) { OpenSdlDevice(i); } - SdlPopulateOK = true; + PopulateOK = true; Cv.notify_one(); } else if (Event.type == ExitEvent_t) { @@ -160,15 +156,22 @@ 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 port_num, slot; + PortStr2Int(port, &port_num, &slot); + + g_InputDeviceManager.UpdateDevices(port, false); + // Force an update of the entire slot connectivity of this port + g_InputDeviceManager.UpdateDevices(port + ".0", false); + g_InputDeviceManager.UpdateDevices(port + ".1", false); } - delete Event.user.data1; + delete static_cast(Event.user.data1); Event.user.data1 = nullptr; } else if (Event.type == DeviceRemoveAck_t) { - g_InputDeviceManager.UpdateDevices(*static_cast(Event.user.data1), true); - delete Event.user.data1; + g_InputDeviceManager.UpdateDevices(*static_cast(Event.user.data1), true); + delete static_cast(Event.user.data1); Event.user.data1 = nullptr; } } @@ -178,7 +181,7 @@ namespace Sdl void DeInit(std::thread& Thr) { - SdlInitStatus = SDL_NOT_INIT; + InitStatus = NOT_INIT; if (!Thr.joinable()) { return; } diff --git a/src/common/input/SdlJoystick.h b/src/common/input/SdlJoystick.h index b6d18008e..688d84a07 100644 --- a/src/common/input/SdlJoystick.h +++ b/src/common/input/SdlJoystick.h @@ -35,20 +35,20 @@ namespace Sdl { - typedef enum _SDL_INIT_STATUS : int + typedef enum _INIT_STATUS : int { - SDL_NOT_INIT = -2, - SDL_INIT_ERROR, - SDL_INIT_SUCCESS, + NOT_INIT = -2, + INIT_ERROR, + INIT_SUCCESS, } - SDL_INIT_STATUS; + INIT_STATUS; extern uint32_t ExitEvent_t; extern uint32_t PopulateEvent_t; extern uint32_t UpdateInputEvent_t; extern uint32_t DeviceRemoveAck_t; - extern int SdlInitStatus; - extern bool SdlPopulateOK; + extern int InitStatus; + extern bool PopulateOK; // initialize SDL void Init(std::mutex& Mtx, std::condition_variable& Cv, bool is_gui); diff --git a/src/common/input/XInputPad.cpp b/src/common/input/XInputPad.cpp index 1ac1a9597..53016d672 100644 --- a/src/common/input/XInputPad.cpp +++ b/src/common/input/XInputPad.cpp @@ -70,7 +70,7 @@ namespace XInput static XInputGetState_t PXInputGetState = nullptr; static bool haveGuideButton = false; - int XInputInitStatus = XINPUT_NOT_INIT; + int InitStatus = NOT_INIT; uint8_t DevicesConnected = 0; static const struct @@ -118,7 +118,7 @@ namespace XInput hXInput = ::LoadLibrary(TEXT(xinput_dll_name.c_str())); if (!hXInput) { EmuLog(LOG_LEVEL::ERROR2, "Failed to initialize XInput subsystem!"); - XInputInitStatus = XINPUT_INIT_ERROR; + InitStatus = INIT_ERROR; return; } } @@ -147,15 +147,15 @@ namespace XInput ::FreeLibrary(hXInput); hXInput = nullptr; EmuLog(LOG_LEVEL::ERROR2, "Failed to find XInput functions!"); - XInputInitStatus = XINPUT_INIT_ERROR; + InitStatus = INIT_ERROR; return; } - XInputInitStatus = XINPUT_INIT_SUCCESS; + InitStatus = INIT_SUCCESS; } void DeInit() { - XInputInitStatus = XINPUT_NOT_INIT; + InitStatus = NOT_INIT; if (hXInput) { ::FreeLibrary(hXInput); diff --git a/src/common/input/XInputPad.h b/src/common/input/XInputPad.h index f6faae243..c64822805 100644 --- a/src/common/input/XInputPad.h +++ b/src/common/input/XInputPad.h @@ -34,15 +34,15 @@ namespace XInput { - typedef enum _XINPUT_INIT_STATUS : int + typedef enum _INIT_STATUS : int { - XINPUT_NOT_INIT = -2, - XINPUT_INIT_ERROR, - XINPUT_INIT_SUCCESS, + NOT_INIT = -2, + INIT_ERROR, + INIT_SUCCESS, } - XINPUT_INIT_STATUS; + INIT_STATUS; - extern int XInputInitStatus; + extern int InitStatus; extern uint8_t DevicesConnected; // initialize XInput diff --git a/src/common/win32/EmuShared.h b/src/common/win32/EmuShared.h index 923d5f9c7..1f685437c 100644 --- a/src/common/win32/EmuShared.h +++ b/src/common/win32/EmuShared.h @@ -135,6 +135,8 @@ class EmuShared : public Mutex // ****************************************************************** void GetInputDevTypeSettings(int* type, int port) { Lock(); *type = m_DeviceType[port]; Unlock(); } void SetInputDevTypeSettings(const int* type, int port) { Lock(); m_DeviceType[port] = *type; Unlock(); } + void GetInputSlotTypeSettings(int *type, int port, int slot) { Lock(); *type = m_SlotDeviceType[port][slot]; Unlock(); } + void SetInputSlotTypeSettings(const int *type, int port, int slot) { Lock(); m_SlotDeviceType[port][slot] = *type; Unlock(); } void GetInputDevNameSettings(char* name, int port) { Lock(); strncpy(name, m_DeviceName[port], 50); Unlock(); } void SetInputDevNameSettings(const char* name, int port) { Lock(); strncpy(m_DeviceName[port], name, 50); Unlock(); } void GetInputBindingsSettings(char button_str[][HOST_BUTTON_NAME_LENGTH], int max_num_buttons, int port) @@ -365,9 +367,10 @@ class EmuShared : public Mutex bool m_bFirstLaunch; bool m_bClipCursor; unsigned int m_dwKrnlProcID; // Only used for kernel mode level. - int m_DeviceType[4]; - char m_DeviceControlNames[4][HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH]; - char m_DeviceName[4][50]; + int m_DeviceType[XBOX_NUM_PORTS]; + int m_SlotDeviceType[XBOX_NUM_PORTS][XBOX_CTRL_NUM_SLOTS]; + char m_DeviceControlNames[XBOX_NUM_PORTS][HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH]; + char m_DeviceName[XBOX_NUM_PORTS][50]; char m_TitleMountPath[xbox::max_path]; // Settings class in memory should not be tampered by third-party. diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index a4c759001..8a8daba8a 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -352,7 +352,7 @@ g_EmuCDPD; XB_TRAMPOLINES(XB_trampoline_declare); -void LookupTrampolines() +void LookupTrampolinesD3D() { XB_TRAMPOLINES(XB_trampoline_lookup); } diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.h b/src/core/hle/D3D8/Direct3D9/Direct3D9.h index eb402c85f..f3ae9c596 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.h +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.h @@ -37,7 +37,7 @@ #define DIRECTDRAW_VERSION 0x0700 #include -extern void LookupTrampolines(); +void LookupTrampolinesD3D(); // initialize render window extern void CxbxInitWindow(bool bFullInit); diff --git a/src/core/hle/Patches.cpp b/src/core/hle/Patches.cpp index 7fa2bb3f3..ad76ad3d4 100644 --- a/src/core/hle/Patches.cpp +++ b/src/core/hle/Patches.cpp @@ -365,6 +365,8 @@ std::map g_PatchTable = { PATCH_ENTRY("XSetProcessQuantumLength", xbox::EMUPATCH(XSetProcessQuantumLength), PATCH_ALWAYS), PATCH_ENTRY("timeKillEvent", xbox::EMUPATCH(timeKillEvent), PATCH_ALWAYS), PATCH_ENTRY("timeSetEvent", xbox::EMUPATCH(timeSetEvent), PATCH_ALWAYS), + PATCH_ENTRY("XReadMUMetaData", xbox::EMUPATCH(XReadMUMetaData), PATCH_ALWAYS), + PATCH_ENTRY("XUnmountMU", xbox::EMUPATCH(XUnmountMU), PATCH_ALWAYS), }; std::unordered_map g_FunctionHooks; @@ -444,7 +446,8 @@ void EmuInstallPatches() EmuInstallPatch(it.first, it.second); } - LookupTrampolines(); + LookupTrampolinesD3D(); + LookupTrampolinesXAPI(); } void* GetPatchedFunctionTrampoline(const std::string functionName) diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 6498d6e51..0cdef1d9a 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -43,24 +43,69 @@ #include "Windef.h" #include #include "core\hle\XAPI\Xapi.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 joystick (mutually exclusive) + 8 memory units +DeviceState g_devs[MAX_DEVS]; + +xbox::ulong_xt g_Mounted_MUs = 0; +xbox::char_xt g_AltLett_MU = 0; +xbox::ulong_xt *g_XapiMountedMUs = &g_Mounted_MUs; +xbox::char_xt *g_XapiAltLett_MU = &g_AltLett_MU; +std::recursive_mutex g_MuLock; + +// Declare trampolines +#define XB_TRAMPOLINES(XB_MACRO) \ + XB_MACRO(xbox::dword_xt, WINAPI, XUnmountAlternateTitleA, (xbox::char_xt) ); \ + XB_MACRO(xbox::ntstatus_xt, WINAPI, XapiMapLetterToDirectory, (xbox::PSTRING, xbox::PSTRING, const xbox::PCHAR, xbox::bool_xt, xbox::PCWSTR, xbox::bool_xt) ); \ + +XB_TRAMPOLINES(XB_trampoline_declare); + +void LookupTrampolinesXAPI() +{ + XB_TRAMPOLINES(XB_trampoline_lookup); +} + +#undef XB_TRAMPOLINES + + +static inline xbox::char_xt MuPort2Lett(xbox::dword_xt port, xbox::dword_xt slot) +{ + return 'F' + (XBOX_CTRL_NUM_SLOTS * port) + slot; +} + +static inline int MuPort2Idx(xbox::dword_xt port, xbox::dword_xt slot) +{ + return (port << 1) + slot; +} + +static inline bool MuIsMounted(xbox::char_xt lett) +{ + return *g_XapiMountedMUs & (1 << (lett - 'F')); +} + +static inline void MuSetMounted(xbox::char_xt lett) +{ + *g_XapiMountedMUs |= (1 << (lett - 'F')); +} + +static inline void MuClearMounted(xbox::char_xt lett) +{ + *g_XapiMountedMUs &= ~(1 << (lett - 'F')); +} bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) { @@ -82,9 +127,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,124 +144,209 @@ bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) return false; } -bool operator!=(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) +void UpdateXppState(DeviceState *dev, XBOX_INPUT_DEVICE type, std::string_view port) { - return !(XppType == XidType); + xbox::PXPP_DEVICE_TYPE xpp = nullptr; + 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: + assert(0); + } + + if (xpp == nullptr) { + // This will happen with xbes that act like launchers, and don't link against the xinput libraries, which results in all the global + // xpp types being nullptr. Test case: Innocent Tears + return; + } + + int port_num, slot; + PortStr2Int(port, &port_num, &slot); + xbox::ulong_xt port_mask = 1 << port_num; + xbox::ulong_xt slot_mask = 0; + + // 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) { + assert(slot != PORT_INVALID); + if (slot == 1) { + slot_mask = 16; + } + } + + if (xpp == dev->type && !dev->bPendingRemoval) { + xpp->CurrentConnected |= (port_mask << slot_mask); + } + else { + xpp->CurrentConnected &= ~(port_mask << slot_mask); + } + + xpp->ChangeConnected = xpp->PreviousConnected ^ xpp->CurrentConnected; } -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 g_bIsDevicesEmulating = false; - return ret; + return; } + // Set up common device state + int port_num, slot; + PortStr2Int(port, &port_num, &slot); + dev->upstream = nullptr; + dev->port = port; + dev->port_idx = port_num; + dev->bPendingRemoval = false; + dev->bSignaled = false; + dev->info.dwPacketNumber = 0; + dev->slots[SLOT_TOP] = dev->slots[SLOT_BOTTOM] = nullptr; // 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_DUKE): + dev->type = XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE; + dev->info.bAutoPollDefault = true; + dev->info.ucType = XINPUT_DEVTYPE_GAMEPAD; + dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD; + dev->info.ucInputStateSize = sizeof(XpadInput); + dev->info.ucFeedbackSize = sizeof(XpadOutput); + 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::MS_CONTROLLER_S): + dev->type = XBOX_INPUT_DEVICE::MS_CONTROLLER_S; + dev->info.bAutoPollDefault = true; + dev->info.ucType = XINPUT_DEVTYPE_GAMEPAD; + dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT; + dev->info.ucInputStateSize = sizeof(XpadInput); + dev->info.ucFeedbackSize = sizeof(XpadOutput); + 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::STEEL_BATTALION_CONTROLLER): + dev->type = XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER; + dev->info.buff.sbc.ucGearLever = 8; + dev->info.buff.sbc.sAimingX = static_cast(0x7F); + dev->info.buff.sbc.sAimingY = static_cast(0x7F); + dev->info.bAutoPollDefault = true; + dev->info.ucType = XINPUT_DEVTYPE_STEELBATTALION; + dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT; + dev->info.ucInputStateSize = sizeof(SBCInput); + dev->info.ucFeedbackSize = sizeof(SBCOutput); + 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::ARCADE_STICK): + dev->type = XBOX_INPUT_DEVICE::ARCADE_STICK; + dev->info.bAutoPollDefault = true; + dev->info.ucType = XINPUT_DEVTYPE_GAMEPAD; + dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_ARCADE_STICK; + dev->info.ucInputStateSize = sizeof(XpadInput); + dev->info.ucFeedbackSize = sizeof(XpadOutput); + 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; + assert(upstream != nullptr); + dev->upstream = upstream; + dev->type = XBOX_INPUT_DEVICE::MEMORY_UNIT; + dev->port_idx = PORT_INVALID; + assert(slot != PORT_INVALID); + dev->upstream->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; + + EmuLogEx(CXBXR_MODULE::INPSYS, LOG_LEVEL::INFO, "Attached device %s to port %s", GetInputDeviceName(type).c_str(), PortUserFormat(port).c_str()); } -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)); + // Clear common device state + XBOX_INPUT_DEVICE type = dev->type; + std::string port = dev->port; + dev->port_idx = PORT_INVALID; + dev->type = XBOX_INPUT_DEVICE::DEVICE_INVALID; + dev->port = std::to_string(PORT_INVALID); + dev->bPendingRemoval = false; + dev->bSignaled = false; + dev->slots[SLOT_TOP] = dev->slots[SLOT_BOTTOM] = nullptr; + + switch (type) + { + case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE: + case XBOX_INPUT_DEVICE::MS_CONTROLLER_S: + dev->info.bAutoPollDefault = false; + dev->info.ucType = 0; + dev->info.ucSubType = 0; + dev->info.ucInputStateSize = 0; + dev->info.ucFeedbackSize = 0; + dev->info.dwPacketNumber = 0; + std::memset(&dev->info.buff.ctrl, 0, sizeof(XpadInput)); + break; + + case XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER: + dev->info.bAutoPollDefault = false; + dev->info.ucType = 0; + dev->info.ucSubType = 0; + dev->info.ucInputStateSize = 0; + dev->info.ucFeedbackSize = 0; + dev->info.dwPacketNumber = 0; + std::memset(&dev->info.buff.sbc, 0, sizeof(SBCInput)); + break; + + case XBOX_INPUT_DEVICE::ARCADE_STICK: + dev->info.bAutoPollDefault = false; + dev->info.ucType = 0; + dev->info.ucSubType = 0; + dev->info.ucInputStateSize = 0; + dev->info.ucFeedbackSize = 0; + dev->info.dwPacketNumber = 0; + std::memset(&dev->info.buff.ctrl, 0, sizeof(XpadInput)); + break; + + case XBOX_INPUT_DEVICE::MEMORY_UNIT: { + assert(dev->upstream != nullptr); + int port_num, slot; + PortStr2Int(port, &port_num, &slot); + assert(slot != PORT_INVALID); + dev->upstream->slots[slot] = nullptr; + } + break; + + default: + assert(0); + } + + UpdateXppState(dev, type, port); + dev->upstream = nullptr; g_bIsDevicesEmulating = false; + + EmuLogEx(CXBXR_MODULE::INPSYS, LOG_LEVEL::INFO, "Detached device %s from port %s", GetInputDeviceName(to_underlying(type)).c_str(), PortUserFormat(port).c_str()); } void SetupXboxDeviceTypes() @@ -280,8 +416,52 @@ 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); } + + if (xbox::addr_xt mu_xpp_type = g_SymbolAddresses["g_DeviceType_MU"]) { + g_DeviceType_MU = reinterpret_cast(mu_xpp_type); + 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 by XbSymbolDatabase"); + } + + if (xbox::addr_xt xapi_mounted_mu = g_SymbolAddresses["g_XapiMountedMUs"]) { + g_XapiMountedMUs = reinterpret_cast(xapi_mounted_mu); + EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs found at 0x%08X", reinterpret_cast(g_XapiMountedMUs)); + + g_XapiAltLett_MU = reinterpret_cast(g_XapiMountedMUs - 1); + EmuLog(LOG_LEVEL::INFO, "XapiAltLett_MU found at 0x%08X", reinterpret_cast(g_XapiAltLett_MU)); + } + else { + EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs was not found by XbSymbolDatabase"); + } +} + +template +xbox::dword_xt CxbxImpl_XInputHandler(xbox::HANDLE hDevice, xbox::PXINPUT_STATE pState) +{ + xbox::dword_xt status = ERROR_DEVICE_NOT_CONNECTED; + DeviceState *dev = static_cast(hDevice); + int port = dev->port_idx; + + if ((g_devs[port].info.hHandle == hDevice) && !g_devs[port].bPendingRemoval) { + if (g_devs[port].info.bAutoPoll != IsXInputPoll) { + if (g_InputDeviceManager.UpdateXboxPortInput(port, &g_devs[port].info.buff, DIRECTION_IN, to_underlying(g_devs[port].type))) { + g_devs[port].info.dwPacketNumber++; + } + } + + if constexpr (!IsXInputPoll) { + std::memcpy((void *)&pState->Gamepad, &g_devs[port].info.buff, g_devs[port].info.ucInputStateSize); + pState->dwPacketNumber = g_devs[port].info.dwPacketNumber; + } + + status = ERROR_SUCCESS; + } + + return status; } // ****************************************************************** @@ -312,63 +492,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,7 +506,24 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDevices) { LOG_FUNC_ONE_ARG(DeviceType); - UpdateConnectedDeviceState(DeviceType); + g_bXppGuard = true; + + if (g_bIsDevicesInitializing || g_bIsDevicesEmulating) { + g_bXppGuard = false; + RETURN(DeviceType->CurrentConnected); + } + + 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 std::string(g_devs[i].port); + SDL_PushEvent(&DeviceRemoveEvent); + } + } UCHAR oldIrql = xbox::KeRaiseIrqlToDpcLevel(); @@ -394,6 +534,8 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDevices) xbox::KfLowerIrql(oldIrql); + g_bXppGuard = false; + RETURN(ret); } @@ -417,11 +559,28 @@ 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 std::string(g_devs[i].port); + SDL_PushEvent(&DeviceRemoveEvent); + } + } BOOL ret = FALSE; - if(!DeviceType->ChangeConnected) { *pdwInsertions = 0; @@ -448,6 +607,7 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(XGetDeviceChanges) xbox::KfLowerIrql(oldIrql); } + g_bXppGuard = false; RETURN(ret); } @@ -470,12 +630,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_devs[dwPort].type) { + g_devs[dwPort].info.bAutoPoll = pPollingParameters != xbox::zeroptr ? + pPollingParameters->fAutoPoll : g_devs[dwPort].info.bAutoPollDefault; + g_devs[dwPort].info.hHandle = &g_devs[dwPort]; + RETURN(g_devs[dwPort].info.hHandle); } } @@ -492,10 +657,9 @@ xbox::void_xt WINAPI xbox::EMUPATCH(XInputClose) { LOG_FUNC_ONE_ARG(hDevice); - PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; - int Port = Device->XboxPort; - if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice) { - Device->hXboxDevice = NULL; + DeviceState *dev = static_cast(hDevice); + if (g_devs[dev->port_idx].info.hHandle == hDevice) { + dev->info.hHandle = NULL; } } @@ -530,12 +694,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; - int Port = Device->XboxPort; - if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice && !g_XboxControllerHostBridge[Port].bPendingRemoval) { - pCapabilities->SubType = g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType; + DeviceState *dev = static_cast(hDevice); + int port = dev->port_idx; + if (g_devs[port].info.hHandle == hDevice && !g_devs[port].bPendingRemoval) { + pCapabilities->SubType = g_devs[port].info.ucSubType; UCHAR* pCap = (UCHAR*)(&pCapabilities->In); - memset(pCap, 0xFF, g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize); + std::memset(pCap, 0xFF, g_devs[port].info.ucInputStateSize + g_devs[port].info.ucFeedbackSize); ret = ERROR_SUCCESS; } @@ -579,11 +743,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; - int Port = Device->XboxPort; - if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice && !g_XboxControllerHostBridge[Port].bPendingRemoval) { + DeviceState *dev = static_cast(hDevice); + int port = dev->port_idx; + if (g_devs[port].info.hHandle == hDevice && !g_devs[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_devs[port].type)); pFeedback->Header.dwStatus = ERROR_SUCCESS; if (pFeedback->Header.hEvent != NULL && ObReferenceObjectByHandle(pFeedback->Header.hEvent, &xbox::ExEventObjectType, (PVOID*)&pFeedback->Header.IoCompletedEvent) == ERROR_SUCCESS) { @@ -1164,18 +1328,44 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XMountMUA) PCHAR pchDrive ) { - - LOG_FUNC_BEGIN LOG_FUNC_ARG(dwPort) LOG_FUNC_ARG(dwSlot) LOG_FUNC_ARG(pchDrive) LOG_FUNC_END; - // TODO: Actually allow memory card emulation? This might make transferring - // game saves a bit easier if the memory card directory was configurable. =] + std::lock_guard lock(g_MuLock); - RETURN(E_FAIL); + char lett = MuPort2Lett(dwPort, dwSlot); + if (MuIsMounted(lett)) { + if (pchDrive != zeroptr) { + *pchDrive = lett; + } + RETURN(ERROR_ALREADY_ASSIGNED); + } + + char title_id_buff[9]; + std::sprintf(title_id_buff, "%08lx", CxbxKrnl_Xbe->m_Certificate.dwTitleId); + std::string mu_path_str(DrivePrefix + lett + ":"); + std::string mu_dev_str(DeviceMU + std::to_string(MuPort2Idx(dwPort, dwSlot))); + ANSI_STRING mu_dev, mu_path; + RtlInitAnsiString(&mu_path, mu_path_str.data()); + RtlInitAnsiString(&mu_dev, mu_dev_str.data()); + mu_dev_str += '\\'; + + ntstatus_xt status = XB_TRMP(XapiMapLetterToDirectory)(&mu_path, &mu_dev, title_id_buff, 1, + reinterpret_cast(CxbxKrnl_Xbe->m_Certificate.wsTitleName), 0); + + if (!nt_success(status)) { + RETURN(RtlNtStatusToDosError(status)); + } + + MuSetMounted(lett); + if (pchDrive != zeroptr) { + *pchDrive = lett; + } + + RETURN(ERROR_SUCCESS); } // ****************************************************************** @@ -1224,20 +1414,174 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XMountMURootA) PCHAR pchDrive ) { - - LOG_FUNC_BEGIN LOG_FUNC_ARG(dwPort) LOG_FUNC_ARG(dwSlot) LOG_FUNC_ARG(pchDrive) LOG_FUNC_END; - // TODO: The params are probably wrong... - LOG_UNIMPLEMENTED(); + std::lock_guard lock(g_MuLock); + + char_xt lett = MuPort2Lett(dwPort, dwSlot); + if (MuIsMounted(lett)) { + if (pchDrive != zeroptr) { + *pchDrive = lett; + } + RETURN(ERROR_ALREADY_ASSIGNED); + } + + std::string mu_path_str(DrivePrefix + lett + ":"); + std::string mu_dev_str(DeviceMU + std::to_string(MuPort2Idx(dwPort, dwSlot))); + ANSI_STRING mu_dev, mu_path; + RtlInitAnsiString(&mu_path, mu_path_str.data()); + RtlInitAnsiString(&mu_dev, mu_dev_str.data()); + ntstatus_xt status = IoCreateSymbolicLink(&mu_path, &mu_dev); + + if (!nt_success(status)) { + if (pchDrive != zeroptr) { + *pchDrive = 0; + } + RETURN(RtlNtStatusToDosError(status)); + } + + MuSetMounted(lett); + if (pchDrive != zeroptr) { + *pchDrive = lett; + } RETURN(ERROR_SUCCESS); } +// ****************************************************************** +// * patch: XUnmountMU +// ****************************************************************** +xbox::dword_xt WINAPI xbox::EMUPATCH(XUnmountMU) +( + dword_xt dwPort, + dword_xt dwSlot +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(dwPort) + LOG_FUNC_ARG(dwSlot) + LOG_FUNC_END; + + std::lock_guard lock(g_MuLock); + + char_xt lett = MuPort2Lett(dwPort, dwSlot); + if (!MuIsMounted(lett)) { + RETURN(ERROR_INVALID_DRIVE); + } + + if (*g_XapiAltLett_MU == lett) { + XB_TRMP(XUnmountAlternateTitleA)('X'); + } + + std::string mu_path_str(DrivePrefix + lett + ":"); + ANSI_STRING mu_path; + RtlInitAnsiString(&mu_path, mu_path_str.data()); + ntstatus_xt status = IoDeleteSymbolicLink(&mu_path); + + if (!nt_success(status)) { + RETURN(RtlNtStatusToDosError(status)); + } + + MuClearMounted(lett); + g_io_mu_metadata->flush(static_cast(lett)); + + RETURN(ERROR_SUCCESS); +} + +// ****************************************************************** +// * patch: XReadMUMetaData +// ****************************************************************** +xbox::dword_xt WINAPI xbox::EMUPATCH(XReadMUMetaData) +( + IN dword_xt dwPort, + IN dword_xt dwSlot, + IN LPVOID lpBuffer, + IN dword_xt dwByteOffset, + IN dword_xt dwNumberOfBytesToRead +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(dwPort) + LOG_FUNC_ARG(dwSlot) + LOG_FUNC_ARG(lpBuffer) + LOG_FUNC_ARG(dwByteOffset) + LOG_FUNC_ARG(dwNumberOfBytesToRead) + LOG_FUNC_END; + + // NOTE: in reality, this function should actually use IoSynchronousFsdRequest to read the metadata of the MU. Unfortunately, + // that requires kernel support for device objects, which when this was implemented, was non-existent. So, we instead cheat + // and use NtFsControlFile to perform the same action. + + std::lock_guard lock(g_MuLock); + + bool unmount = false; + char_xt lett = MuPort2Lett(dwPort, dwSlot); + if (!MuIsMounted(lett)) { + unmount = true; + dword_xt ret = EMUPATCH(XMountMURootA(dwPort, dwSlot, &lett)); + if (ret != ERROR_SUCCESS) { + RETURN(ret); + } + } + + OBJECT_ATTRIBUTES obj; + std::string mu_path_str(DrivePrefix + lett + ":"); + ANSI_STRING mu_path; + RtlInitAnsiString(&mu_path, mu_path_str.data()); + XB_InitializeObjectAttributes(&obj, &mu_path, obj_case_insensitive, ObDosDevicesDirectory()); + + HANDLE handle; + IO_STATUS_BLOCK io_status_block; + ntstatus_xt status = NtOpenFile(&handle, + SYNCHRONIZE | GENERIC_READ, + &obj, + &io_status_block, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_SYNCHRONOUS_IO_ALERT); + + if (nt_success(status)) { + fatx_volume_metadata volume; + volume.offset = dwByteOffset; + volume.length = dwNumberOfBytesToRead; + volume.buffer = new char[dwNumberOfBytesToRead]; + + status = NtFsControlFile(handle, + zeroptr, + zeroptr, + zeroptr, + &io_status_block, + fsctl_read_fatx_metadata, + &volume, + sizeof(volume), + zeroptr, + 0); + + if (nt_success(status)) { + std::memcpy(lpBuffer, volume.buffer, dwNumberOfBytesToRead); + status = status_success; + } + else { + status = status_unrecognized_volume; + } + + delete[] volume.buffer; + NtClose(handle); + } + else { + status = status_unrecognized_volume; + } + + if (unmount) { + EMUPATCH(XUnmountMU(dwPort, dwSlot)); + } + + RETURN(RtlNtStatusToDosError(status)); +} + // ****************************************************************** // * patch: OutputDebugStringA // ****************************************************************** diff --git a/src/core/hle/XAPI/Xapi.h b/src/core/hle/XAPI/Xapi.h index 6beb91c8a..1b5d2949f 100644 --- a/src/core/hle/XAPI/Xapi.h +++ b/src/core/hle/XAPI/Xapi.h @@ -25,6 +25,8 @@ #ifndef XAPI_H #define XAPI_H +void LookupTrampolinesXAPI(); + #include "xbox_types.h" namespace xbox { @@ -684,6 +686,27 @@ xbox::dword_xt WINAPI EMUPATCH(XMountMURootA) PCHAR pchDrive ); +// ****************************************************************** +// * patch: XUnmountMU +// ****************************************************************** +xbox::dword_xt WINAPI EMUPATCH(XUnmountMU) +( + dword_xt dwPort, + dword_xt dwSlot +); + +// ****************************************************************** +// * patch: XReadMUMetaData +// ****************************************************************** +xbox::dword_xt WINAPI EMUPATCH(XReadMUMetaData) +( + IN dword_xt dwPort, + IN dword_xt dwSlot, + IN LPVOID lpBuffer, + IN dword_xt dwByteOffset, + IN dword_xt dwNumberOfBytesToRead +); + // ****************************************************************** // * patch: XMountAlternateTitleA // ****************************************************************** diff --git a/src/core/kernel/common/types.h b/src/core/kernel/common/types.h index d579da3a5..b7afa1bbd 100644 --- a/src/core/kernel/common/types.h +++ b/src/core/kernel/common/types.h @@ -100,6 +100,7 @@ inline constexpr dword_xt status_unable_to_free_vm = 0xC000001AL; inline constexpr dword_xt status_free_vm_not_at_base = 0xC000009FL; inline constexpr dword_xt status_memory_not_allocated = 0xC00000A0L; inline constexpr dword_xt status_not_committed = 0xC000002DL; +inline constexpr dword_xt status_unrecognized_volume = 0xC000014FL; // ****************************************************************** // * Registry value types diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index a8c7d9bfe..a5c1a2db9 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -619,8 +619,7 @@ XBSYSAPI EXPORTNUM(196) xbox::ntstatus_xt NTAPI xbox::NtDeviceIoControlFile switch (IoControlCode) { - case 0x4D014: // IOCTL_SCSI_PASS_THROUGH_DIRECT - { + case 0x4D014: { // IOCTL_SCSI_PASS_THROUGH_DIRECT PSCSI_PASS_THROUGH_DIRECT PassThrough = (PSCSI_PASS_THROUGH_DIRECT)InputBuffer; PDVDX2_AUTHENTICATION Authentication = (PDVDX2_AUTHENTICATION)PassThrough->DataBuffer; @@ -628,34 +627,63 @@ XBSYSAPI EXPORTNUM(196) xbox::ntstatus_xt NTAPI xbox::NtDeviceIoControlFile Authentication->AuthenticationPage.CDFValid = 1; Authentication->AuthenticationPage.PartitionArea = 1; Authentication->AuthenticationPage.Authentication = 1; - break; } - case 0x70000: // IOCTL_DISK_GET_DRIVE_GEOMETRY - { + break; + + case 0x70000: { // IOCTL_DISK_GET_DRIVE_GEOMETRY PDISK_GEOMETRY DiskGeometry = (PDISK_GEOMETRY)OutputBuffer; - DiskGeometry->MediaType = FixedMedia; - DiskGeometry->TracksPerCylinder = 1; - DiskGeometry->SectorsPerTrack = 1; - DiskGeometry->BytesPerSector = 512; - DiskGeometry->Cylinders.QuadPart = 0x1400000; // Around 10GB, size of stock xbox HDD - break; + DeviceType type = CxbxrGetDeviceTypeFromHandle(FileHandle); + if (type == DeviceType::Harddisk0) { + DiskGeometry->MediaType = FixedMedia; + DiskGeometry->TracksPerCylinder = 1; + DiskGeometry->SectorsPerTrack = 1; + DiskGeometry->BytesPerSector = 512; + DiskGeometry->Cylinders.QuadPart = 0x1400000; // 10GB, size of stock xbox HDD + } + else if (type == DeviceType::MU) { + DiskGeometry->MediaType = FixedMedia; + DiskGeometry->TracksPerCylinder = 1; + DiskGeometry->SectorsPerTrack = 1; + DiskGeometry->BytesPerSector = 512; + DiskGeometry->Cylinders.QuadPart = 0x4000; // 8MB, Microsoft original MUs + } + else { + EmuLog(LOG_LEVEL::WARNING, "%s: Unrecongnized handle 0x%X with IoControlCode IOCTL_DISK_GET_DRIVE_GEOMETRY.", __func__, FileHandle); + ret = status_invalid_handle; + } } - case 0x74004: // IOCTL_DISK_GET_PARTITION_INFO - { + break; + + case 0x74004: { // IOCTL_DISK_GET_PARTITION_INFO PPARTITION_INFORMATION partitioninfo = (PPARTITION_INFORMATION)OutputBuffer; - XboxPartitionTable partitionTable = CxbxGetPartitionTable(); - int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); - - // Now we read from the partition table, to fill in the partitionInfo struct - partitioninfo->PartitionNumber = partitionNumber; - partitioninfo->StartingOffset.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBAStart * 512; - partitioninfo->PartitionLength.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize * 512; - partitioninfo->HiddenSectors = partitionTable.TableEntries[partitionNumber - 1].Reserved; - partitioninfo->RecognizedPartition = true; - break; + DeviceType type = CxbxrGetDeviceTypeFromHandle(FileHandle); + if (type == DeviceType::Harddisk0) { + XboxPartitionTable partitionTable = CxbxGetPartitionTable(); + int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); + + // Now we read from the partition table, to fill in the partitionInfo struct + partitioninfo->PartitionNumber = partitionNumber; + partitioninfo->StartingOffset.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBAStart * 512; + partitioninfo->PartitionLength.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize * 512; + partitioninfo->HiddenSectors = partitionTable.TableEntries[partitionNumber - 1].Reserved; + partitioninfo->RecognizedPartition = true; + } + else if (type == DeviceType::MU) { + partitioninfo->PartitionNumber = 0; + partitioninfo->StartingOffset.QuadPart = 0; // FIXME: where does the MU partition start? + partitioninfo->PartitionLength.QuadPart = 16384; // 8MB + partitioninfo->HiddenSectors = 0; + partitioninfo->RecognizedPartition = true; + } + else { + EmuLog(LOG_LEVEL::WARNING, "%s: Unrecongnized handle 0x%X with IoControlCode IOCTL_DISK_GET_PARTITION_INFO.", __func__, FileHandle); + ret = status_invalid_handle; + } } + break; + default: LOG_UNIMPLEMENTED(); } @@ -802,19 +830,56 @@ XBSYSAPI EXPORTNUM(200) xbox::ntstatus_xt NTAPI xbox::NtFsControlFile LOG_FUNC_ARG(OutputBufferLength) LOG_FUNC_END; - NTSTATUS ret = STATUS_INVALID_PARAMETER; + ntstatus_xt ret = STATUS_INVALID_PARAMETER; + + switch (FsControlCode) + { + case fsctl_dismount_volume: { + // HACK: this should just free the resources assocoated with the volume, it should not reformat it + int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); + if (partitionNumber > 0) { + CxbxFormatPartitionByHandle(FileHandle); + ret = xbox::status_success; + } + } + break; + + case fsctl_read_fatx_metadata: { + const std::wstring path = CxbxGetFinalPathNameByHandle(FileHandle); + size_t pos = path.rfind(L"\\EmuMu"); + if (pos != std::string::npos && path[pos + 6] == '\\') { + // Ensure that InputBuffer is indeed what we think it is + pfatx_volume_metadata volume = static_cast(InputBuffer); + assert(InputBufferLength == sizeof(fatx_volume_metadata)); + g_io_mu_metadata->read(path[pos + 7], volume->offset, static_cast(volume->buffer), volume->length); + ret = xbox::status_success; + } + else { + ret = status_invalid_handle; + } + } + break; + + case fsctl_write_fatx_metadata: { + const std::wstring path = CxbxGetFinalPathNameByHandle(FileHandle); + size_t pos = path.rfind(L"\\EmuMu"); + if (pos != std::string::npos && path[pos + 6] == '\\') { + // Ensure that InputBuffer is indeed what we think it is + pfatx_volume_metadata volume = static_cast(InputBuffer); + assert(InputBufferLength == sizeof(fatx_volume_metadata)); + g_io_mu_metadata->write(path[pos + 7], volume->offset, static_cast(volume->buffer), volume->length); + ret = xbox::status_success; + } + else { + ret = status_invalid_handle; + } + } + break; - switch (FsControlCode) { - case 0x00090020: // FSCTL_DISMOUNT_VOLUME - int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); - if (partitionNumber > 0) { - CxbxFormatPartitionByHandle(FileHandle); - ret = xbox::status_success; - } - break; } - LOG_UNIMPLEMENTED(); + LOG_INCOMPLETE(); + RETURN(ret); } @@ -1499,27 +1564,56 @@ XBSYSAPI EXPORTNUM(218) xbox::ntstatus_xt NTAPI xbox::NtQueryVolumeInformationFi // FileFsSizeInformation is a special case that should read from our emulated partition table if ((DWORD)FileInformationClass == FileFsSizeInformation) { + ntstatus_xt status; PFILE_FS_SIZE_INFORMATION XboxSizeInfo = (PFILE_FS_SIZE_INFORMATION)FileInformation; - XboxPartitionTable partitionTable = CxbxGetPartitionTable(); - int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); - FATX_SUPERBLOCK superBlock = CxbxGetFatXSuperBlock(partitionNumber); + // This might access the HDD, a MU or the DVD drive, so we need to figure out the correct one first + DeviceType type = CxbxrGetDeviceTypeFromHandle(FileHandle); + if (type == DeviceType::Harddisk0) { - XboxSizeInfo->BytesPerSector = 512; + XboxPartitionTable partitionTable = CxbxGetPartitionTable(); + int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); + FATX_SUPERBLOCK superBlock = CxbxGetFatXSuperBlock(partitionNumber); - // In some cases, the emulated partition hasn't been formatted yet, as these are forwarded to a real folder, this doesn't actually matter. - // We just pretend they are valid by defaulting the SectorsPerAllocationUnit value to the most common for system partitions - XboxSizeInfo->SectorsPerAllocationUnit = 32; + XboxSizeInfo->BytesPerSector = 512; - // If there is a valid cluster size, we calculate SectorsPerAllocationUnit from that instead - if (superBlock.ClusterSize > 0) { - XboxSizeInfo->SectorsPerAllocationUnit = superBlock.ClusterSize; + // In some cases, the emulated partition hasn't been formatted yet, as these are forwarded to a real folder, this doesn't actually matter. + // We just pretend they are valid by defaulting the SectorsPerAllocationUnit value to the most common for system partitions + XboxSizeInfo->SectorsPerAllocationUnit = 32; + + // If there is a valid cluster size, we calculate SectorsPerAllocationUnit from that instead + if (superBlock.ClusterSize > 0) { + XboxSizeInfo->SectorsPerAllocationUnit = superBlock.ClusterSize; + } + + XboxSizeInfo->TotalAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; + XboxSizeInfo->AvailableAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; + + status = status_success; + } + else if (type == DeviceType::MU) { + + XboxSizeInfo->BytesPerSector = 512; + XboxSizeInfo->SectorsPerAllocationUnit = 32; + XboxSizeInfo->TotalAllocationUnits.QuadPart = 512; // 8MB -> ((1024)^2 * 8) / (BytesPerSector * SectorsPerAllocationUnit) + XboxSizeInfo->AvailableAllocationUnits.QuadPart = 512; // constant, so there's always free space available to write stuff + + status = status_success; + } + else if (type == DeviceType::Cdrom0) { + + XboxSizeInfo->BytesPerSector = 2048; + XboxSizeInfo->SectorsPerAllocationUnit = 1; + XboxSizeInfo->TotalAllocationUnits.QuadPart = 3820880; // assuming DVD-9 (dual layer), redump reports a total size in bytes of 7825162240 + + status = status_success; + } + else { + EmuLog(LOG_LEVEL::WARNING, "%s: Unrecongnized handle 0x%X with class FileFsSizeInformation.", __func__, FileHandle); + status = status_invalid_handle; } - XboxSizeInfo->TotalAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; - XboxSizeInfo->AvailableAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; - - RETURN(xbox::status_success); + RETURN(status); } // Get the required size for the host buffer diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index 6babf23c0..ef960cf52 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -96,6 +96,7 @@ char szFilePath_EEPROM_bin[MAX_PATH] = { 0 }; char szFilePath_Xbe[xbox::max_path*2] = { 0 }; // NOTE: LAUNCH_DATA_HEADER's szLaunchPath is xbox::max_path*2 = 520 std::string CxbxBasePath; +std::string MuBasePath; HANDLE CxbxBasePathHandle; Xbe* CxbxKrnl_Xbe = NULL; bool g_bIsChihiro = false; @@ -1433,11 +1434,14 @@ __declspec(noreturn) void CxbxKrnlInit char szBuffer[sizeof(szFilePath_Xbe)]; g_EmuShared->GetStorageLocation(szBuffer); + MuBasePath = std::string(szBuffer) + "\\EmuMu"; CxbxBasePath = std::string(szBuffer) + "\\EmuDisk"; CxbxResolveHostToFullPath(CxbxBasePath, "Cxbx-Reloaded's EmuDisk directory"); + CxbxResolveHostToFullPath(MuBasePath, "Cxbx-Reloaded's EmuMu directory"); // Since canonical always remove the extra slash, we need to manually add it back. // TODO: Once CxbxBasePath is filesystem::path, replace CxbxBasePath's + operators to / for include path separator internally. CxbxBasePath = std::filesystem::path(CxbxBasePath).append("").string(); + MuBasePath = std::filesystem::path(MuBasePath).append("").string(); } // Determine xbe path @@ -1505,6 +1509,22 @@ __declspec(noreturn) void CxbxKrnlInit CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition7, CxbxBasePath + "Partition7"); CxbxRegisterDeviceHostPath(DevicePrefix + "\\Chihiro", CxbxBasePath + "Chihiro"); + // Create the MU directories and the bin files + CxbxRegisterDeviceHostPath(DeviceMU0, MuBasePath + "F", false, sizeof(FATX_SUPERBLOCK)); + CxbxRegisterDeviceHostPath(DeviceMU1, MuBasePath + "G", false, sizeof(FATX_SUPERBLOCK)); + CxbxRegisterDeviceHostPath(DeviceMU2, MuBasePath + "H", false, sizeof(FATX_SUPERBLOCK)); + CxbxRegisterDeviceHostPath(DeviceMU3, MuBasePath + "I", false, sizeof(FATX_SUPERBLOCK)); + CxbxRegisterDeviceHostPath(DeviceMU4, MuBasePath + "J", false, sizeof(FATX_SUPERBLOCK)); + CxbxRegisterDeviceHostPath(DeviceMU5, MuBasePath + "K", false, sizeof(FATX_SUPERBLOCK)); + CxbxRegisterDeviceHostPath(DeviceMU6, MuBasePath + "L", false, sizeof(FATX_SUPERBLOCK)); + CxbxRegisterDeviceHostPath(DeviceMU7, MuBasePath + "M", false, sizeof(FATX_SUPERBLOCK)); + + std::mbstate_t ps = std::mbstate_t(); + const char *src = MuBasePath.c_str(); + std::wstring wMuBasePath(MuBasePath.size(), L'0'); + std::mbsrtowcs(wMuBasePath.data(), &src, wMuBasePath.size(), &ps); + g_io_mu_metadata = new io_mu_metadata(wMuBasePath); + // Create default symbolic links : EmuLogInit(LOG_LEVEL::DEBUG, "Creating default symbolic links."); { @@ -1747,6 +1767,13 @@ void CxbxInitFilePaths() CxbxKrnlCleanup("%s : Couldn't create Cxbx-Reloaded EmuDisk folder!", __func__); } + // Make sure the EmuDMu folder exists + std::string emuMu = std::string(szFolder_CxbxReloadedData) + std::string("\\EmuMu"); + result = std::filesystem::exists(emuMu); + if (!result && !std::filesystem::create_directory(emuMu)) { + CxbxKrnlCleanup("%s : Couldn't create Cxbx-Reloaded EmuMu folder!", __func__); + } + snprintf(szFilePath_EEPROM_bin, MAX_PATH, "%s\\EEPROM.bin", szFolder_CxbxReloadedData); GetModuleFileName(GetModuleHandle(nullptr), szFilePath_CxbxReloaded_Exe, MAX_PATH); @@ -1918,6 +1945,11 @@ void CxbxKrnlShutDown(bool is_reboot) // Shutdown the input device manager g_InputDeviceManager.Shutdown(); + if (g_io_mu_metadata) { + delete g_io_mu_metadata; + g_io_mu_metadata = nullptr; + } + // Shutdown the memory manager g_VMManager.Shutdown(); diff --git a/src/core/kernel/support/EmuFile.cpp b/src/core/kernel/support/EmuFile.cpp index 682a0fd4f..b7afe8429 100644 --- a/src/core/kernel/support/EmuFile.cpp +++ b/src/core/kernel/support/EmuFile.cpp @@ -31,13 +31,13 @@ #include #include #include +#include #include #include #include #pragma warning(disable:4005) // Ignore redefined status values #include #pragma warning(default:4005) -#include "core\kernel\init\CxbxKrnl.h" #include "Logging.h" #include "common/util/strConverter.hpp" // utf16_to_ascii #include "common/util/cliConfig.hpp" @@ -84,7 +84,103 @@ XboxPartitionTable BackupPartTbl = } }; -void CxbxCreatePartitionHeaderFile(std::string filename, bool partition0 = false) +io_mu_metadata *g_io_mu_metadata = nullptr; + +io_mu_metadata::io_mu_metadata(const std::wstring_view root_path) : m_root_path(root_path) +{ + for (unsigned i = 0; i < 8; ++i) { + m_buff[i] = new char[sizeof(FATX_SUPERBLOCK)]; + assert(m_buff[i] != nullptr); + std::wstring path = m_root_path + static_cast(L'F' + i) + L".bin"; + std::fstream fs(path, std::ios_base::in | std::ios_base::out | std::ios_base::binary); + if (!fs.is_open()) { + CxbxKrnlCleanup("%s: could not open MU bin file at \"%ls\"!", __func__, path.c_str()); + } + fs.seekg(0); + fs.read(m_buff[i], sizeof(FATX_SUPERBLOCK)); + // if the signature is not "fatx" or we read less bytes than expected, then we assume the bin file is either corrupted or + // unformatted, so we reformat it now + FATX_SUPERBLOCK *volume = reinterpret_cast(m_buff[i]); + if ((fs.gcount() != sizeof(FATX_SUPERBLOCK)) || (volume->Signature != fatx_signature)) { + volume->Signature = fatx_signature; + volume->VolumeID = 0x11223344 + i; + volume->ClusterSize = 32; + volume->FatCopies = 1; + std::memset(volume->Name, 0, mu_max_name_lenght); + std::memset(volume->OnlineData, 0, fatx_online_data_length); + std::memset(volume->Unused, 0xFF, fatx_reserved_length); + fs.write(m_buff[i], sizeof(FATX_SUPERBLOCK)); + } + } +} + +io_mu_metadata::~io_mu_metadata() +{ + std::unique_lock lck(m_rw_lock); + for (unsigned i = 0; i < 8; ++i) { + std::wstring path = m_root_path + static_cast(L'F' + i) + L".bin"; + std::ofstream ofs(path, std::ios_base::out | std::ios_base::binary); + if (!ofs.is_open()) { + EmuLog(LOG_LEVEL::ERROR2, "%s: could not open MU bin file at \"%ls\"!", __func__, path.c_str()); + delete[] m_buff[i]; + continue; + } + ofs.seekp(0); + ofs.write(m_buff[i], sizeof(FATX_SUPERBLOCK)); + delete[] m_buff[i]; + } +} + +void io_mu_metadata::read(const wchar_t lett, std::size_t offset, char *buff, std::size_t size) +{ + std::shared_lock lck(m_rw_lock); // allows for concurrent reads + std::memcpy(buff, m_buff[lett - L'F'] + offset, size); +} + +void io_mu_metadata::write(const wchar_t lett, std::size_t offset, const char *buff, std::size_t size) +{ + std::unique_lock lck(m_rw_lock); // blocks when there is rw in progress + std::memcpy(m_buff[lett - L'F'] + offset, buff, size); +} + +void io_mu_metadata::flush(const wchar_t lett) +{ + std::unique_lock lck(m_rw_lock); + std::wstring path = m_root_path + lett + L".bin"; + std::ofstream ofs(path, std::ios_base::out | std::ios_base::binary); + if (!ofs.is_open()) { + EmuLog(LOG_LEVEL::ERROR2, "%s: could not open MU bin file at \"%ls\"!", __func__, path.c_str()); + return; + } + ofs.seekp(0); + ofs.write(m_buff[lett - L'F'], sizeof(FATX_SUPERBLOCK)); + ofs.flush(); +} + +DeviceType CxbxrGetDeviceTypeFromHandle(HANDLE hFile) +{ + const std::wstring path = CxbxGetFinalPathNameByHandle(hFile); + + size_t pos = path.rfind(L"\\EmuDisk\\Partition"); + if (pos != std::string::npos) { + return DeviceType::Harddisk0; + } + + pos = path.rfind(L"\\EmuMu"); + if (pos != std::string::npos) { + return DeviceType::MU; + } + + EmuDirPath hybrid_path; + FindEmuDirPathByDevice(DeviceCdrom0, hybrid_path); + if (hybrid_path.HostDirPath != "") { + return DeviceType::Cdrom0; + } + + return DeviceType::Invalid; +} + +void CxbxCreatePartitionHeaderFile(std::string filename, bool partition0 = false, std::size_t size = 512 * ONE_KB) { HANDLE hf = CreateFile(filename.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); if (!hf) { @@ -98,7 +194,7 @@ void CxbxCreatePartitionHeaderFile(std::string filename, bool partition0 = false WriteFile(hf, &BackupPartTbl, sizeof(XboxPartitionTable), &NumberOfBytesWritten, 0); } - SetFilePointer(hf, 512 * ONE_KB, 0, FILE_BEGIN); + SetFilePointer(hf, size, 0, FILE_BEGIN); SetEndOfFile(hf); CloseHandle(hf); } @@ -139,7 +235,7 @@ FATX_SUPERBLOCK CxbxGetFatXSuperBlock(int partitionNumber) return superblock; } -static std::wstring CxbxGetFinalPathNameByHandle(HANDLE hFile) +std::wstring CxbxGetFinalPathNameByHandle(HANDLE hFile) { constexpr size_t INITIAL_BUF_SIZE = MAX_PATH; std::wstring path(INITIAL_BUF_SIZE, '\0'); @@ -173,20 +269,30 @@ static bool CxbxIsPathInsideEmuDisk(const std::filesystem::path& path) return match.first == rootPath.end(); } -int CxbxGetPartitionNumberFromHandle(HANDLE hFile) +static int CxbxGetPartitionNumber(const std::wstring_view path) { - // Get which partition number is being accessed, by parsing the filename and extracting the last portion - const std::wstring path = CxbxGetFinalPathNameByHandle(hFile); - const std::wstring_view partitionString = L"\\EmuDisk\\Partition"; const size_t pos = path.rfind(partitionString); if (pos == std::string::npos) { return 0; } - const std::wstring partitionNumberString = path.substr(pos + partitionString.length(), 1); + const std::wstring_view partitionNumberString = path.substr(pos + partitionString.length(), 1); // wcstol returns 0 on non-numeric characters, so we don't need to error check here - return wcstol(partitionNumberString.c_str(), nullptr, 0); + return wcstol(partitionNumberString.data(), nullptr, 0); +} + +int CxbxGetPartitionNumberFromPath(const std::wstring_view path) +{ + return CxbxGetPartitionNumber(path); +} + +int CxbxGetPartitionNumberFromHandle(HANDLE hFile) +{ + // Get which partition number is being accessed, by parsing the filename and extracting the last portion + const std::wstring path = CxbxGetFinalPathNameByHandle(hFile); + + return CxbxGetPartitionNumber(path); } std::filesystem::path CxbxGetPartitionDataPathFromHandle(HANDLE hFile) @@ -248,7 +354,14 @@ const std::string DriveA = DrivePrefix + "A:"; // A: could be CDROM const std::string DriveC = DrivePrefix + "C:"; // C: is HDD0 const std::string DriveD = DrivePrefix + "D:"; // D: is DVD Player const std::string DriveE = DrivePrefix + "E:"; -const std::string DriveF = DrivePrefix + "F:"; +const std::string DriveF = DrivePrefix + "F:"; // MU port 0, slot top +const std::string DriveG = DrivePrefix + "G:"; // MU port 0, slot bottom +const std::string DriveH = DrivePrefix + "H:"; // MU port 1, slot top +const std::string DriveI = DrivePrefix + "I:"; // MU port 1, slot bottom +const std::string DriveJ = DrivePrefix + "J:"; // MU port 2, slot top +const std::string DriveK = DrivePrefix + "K:"; // MU port 2, slot bottom +const std::string DriveL = DrivePrefix + "L:"; // MU port 3, slot top +const std::string DriveM = DrivePrefix + "M:"; // MU port 3, slot bottom const std::string DriveS = DrivePrefix + "S:"; const std::string DriveT = DrivePrefix + "T:"; // T: is Title persistent data region const std::string DriveU = DrivePrefix + "U:"; // U: is User persistent data region @@ -260,6 +373,7 @@ const std::string DriveZ = DrivePrefix + "Z:"; // Z: is Title utility data regio const std::string DevicePrefix = "\\Device"; const std::string DeviceCdrom0 = DevicePrefix + "\\CdRom0"; const std::string DeviceHarddisk0 = DevicePrefix + "\\Harddisk0"; +const std::string DeviceMU = DevicePrefix + "\\MU_"; const std::string DeviceHarddisk0PartitionPrefix = DevicePrefix + "\\Harddisk0\\partition"; const std::string DeviceHarddisk0Partition0 = DeviceHarddisk0PartitionPrefix + "0"; // Contains raw config sectors (like XBOX_REFURB_INFO) + entire hard disk const std::string DeviceHarddisk0Partition1 = DeviceHarddisk0PartitionPrefix + "1"; // Data partition. Contains TDATA and UDATA folders. @@ -282,6 +396,14 @@ const std::string DeviceHarddisk0Partition17 = DeviceHarddisk0PartitionPrefix + const std::string DeviceHarddisk0Partition18 = DeviceHarddisk0PartitionPrefix + "18"; const std::string DeviceHarddisk0Partition19 = DeviceHarddisk0PartitionPrefix + "19"; const std::string DeviceHarddisk0Partition20 = DeviceHarddisk0PartitionPrefix + "20"; // 20 = Largest possible partition number +const std::string DeviceMU0 = DeviceMU + "0"; +const std::string DeviceMU1 = DeviceMU + "1"; +const std::string DeviceMU2 = DeviceMU + "2"; +const std::string DeviceMU3 = DeviceMU + "3"; +const std::string DeviceMU4 = DeviceMU + "4"; +const std::string DeviceMU5 = DeviceMU + "5"; +const std::string DeviceMU6 = DeviceMU + "6"; +const std::string DeviceMU7 = DeviceMU + "7"; // 7 = Largest possible mu number EmuNtSymbolicLinkObject* NtSymbolicLinkObjects['Z' - 'A' + 1]; std::vector Devices; @@ -675,7 +797,7 @@ std::string CxbxConvertXboxToHostPath(const std::string_view XboxDevicePath) return XbePath; } -int CxbxRegisterDeviceHostPath(const std::string_view XboxDevicePath, std::string HostDevicePath, bool IsFile) +int CxbxRegisterDeviceHostPath(const std::string_view XboxDevicePath, std::string HostDevicePath, bool IsFile, std::size_t size) { XboxDevice newDevice; newDevice.XboxDevicePath = XboxDevicePath; @@ -683,11 +805,12 @@ int CxbxRegisterDeviceHostPath(const std::string_view XboxDevicePath, std::strin bool succeeded{ false }; - // All HDD partitions have a .bin file to allow direct file io on the partition info - if (_strnicmp(XboxDevicePath.data(), DeviceHarddisk0PartitionPrefix.c_str(), DeviceHarddisk0PartitionPrefix.length()) == 0) { + // All HDD and MU partitions have a .bin file to allow direct file io on the partition info + if (_strnicmp(XboxDevicePath.data(), DeviceHarddisk0PartitionPrefix.c_str(), DeviceHarddisk0PartitionPrefix.length()) == 0 || + _strnicmp(XboxDevicePath.data(), DeviceMU.c_str(), DeviceMU.length()) == 0) { std::string partitionHeaderPath = HostDevicePath + ".bin"; if (!std::filesystem::exists(partitionHeaderPath)) { - CxbxCreatePartitionHeaderFile(partitionHeaderPath, XboxDevicePath == DeviceHarddisk0Partition0); + CxbxCreatePartitionHeaderFile(partitionHeaderPath, XboxDevicePath == DeviceHarddisk0Partition0, size); } succeeded = true; diff --git a/src/core/kernel/support/EmuFile.h b/src/core/kernel/support/EmuFile.h index 81d86d6dd..eedc2e2b2 100644 --- a/src/core/kernel/support/EmuFile.h +++ b/src/core/kernel/support/EmuFile.h @@ -26,6 +26,7 @@ #include +#include "core\kernel\init\CxbxKrnl.h" #include #include #include @@ -62,17 +63,18 @@ extern const std::string DriveC; extern const std::string DriveD; extern const std::string DriveE; extern const std::string DriveF; -extern const std::string DriveS; -extern const std::string DriveT; -extern const std::string DriveU; -extern const std::string DriveV; -extern const std::string DriveW; -extern const std::string DriveX; -extern const std::string DriveY; +extern const std::string DriveG; +extern const std::string DriveH; +extern const std::string DriveI; +extern const std::string DriveJ; +extern const std::string DriveK; +extern const std::string DriveL; +extern const std::string DriveM; extern const std::string DriveZ; extern const std::string DevicePrefix; extern const std::string DeviceCdrom0; extern const std::string DeviceHarddisk0; +extern const std::string DeviceMU; extern const std::string DeviceHarddisk0PartitionPrefix; extern const std::string DeviceHarddisk0Partition0; extern const std::string DeviceHarddisk0Partition1; @@ -95,8 +97,28 @@ extern const std::string DeviceHarddisk0Partition17; extern const std::string DeviceHarddisk0Partition18; extern const std::string DeviceHarddisk0Partition19; extern const std::string DeviceHarddisk0Partition20; +extern const std::string DeviceMU0; +extern const std::string DeviceMU1; +extern const std::string DeviceMU2; +extern const std::string DeviceMU3; +extern const std::string DeviceMU4; +extern const std::string DeviceMU5; +extern const std::string DeviceMU6; +extern const std::string DeviceMU7; constexpr char CxbxAutoMountDriveLetter = 'D'; +enum class DeviceType : int { + Invalid = -1, + Cdrom0, + Harddisk0, + MU, +}; + +inline constexpr xbox::ulong_xt fsctl_dismount_volume = 0x00090020; +inline constexpr xbox::ulong_xt fsctl_read_fatx_metadata = 0x0009411C; +inline constexpr xbox::ulong_xt fsctl_write_fatx_metadata = 0x00098120; + +inline constexpr std::size_t mu_max_name_lenght = 32 * sizeof(xbox::wchar_xt); // MU names are in wide chars extern std::string CxbxBasePath; extern HANDLE CxbxBasePathHandle; @@ -226,9 +248,31 @@ struct EmuDirPath { HANDLE HostDirHandle; }; +typedef struct _fatx_volume_metadata { + xbox::dword_xt offset; + xbox::dword_xt length; + xbox::PVOID buffer; +} fatx_volume_metadata, *pfatx_volume_metadata; + CHAR* NtStatusToString(IN NTSTATUS Status); -int CxbxRegisterDeviceHostPath(std::string_view XboxFullPath, std::string HostDevicePath, bool IsFile = false); +class io_mu_metadata +{ +public: + io_mu_metadata(const std::wstring_view root_path); + ~io_mu_metadata(); + void read(const wchar_t lett, std::size_t offset, char *buff, std::size_t size); + void write(const wchar_t lett, std::size_t offset, const char *buff, std::size_t size); + void flush(const wchar_t lett); + +private: + char *m_buff[8]; + const std::wstring m_root_path; + std::shared_mutex m_rw_lock; +}; +extern io_mu_metadata *g_io_mu_metadata; + +int CxbxRegisterDeviceHostPath(std::string_view XboxFullPath, std::string HostDevicePath, bool IsFile = false, std::size_t size = 512 * ONE_KB); int CxbxDeviceIndexByDevicePath(const char *XboxDevicePath); XboxDevice* CxbxDeviceByDevicePath(const std::string_view XboxDevicePath); XboxDevice* CxbxDeviceByHostPath(const std::string_view HostPath); @@ -239,6 +283,7 @@ EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDriveLetter(const char DriveL EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByName(std::string SymbolicLinkName); void FindEmuDirPathByDevice(std::string DeviceName, EmuDirPath& hybrid_path); EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(HANDLE Handle); +DeviceType CxbxrGetDeviceTypeFromHandle(HANDLE hFile); void CleanupSymbolicLinks(); HANDLE CxbxGetDeviceNativeRootHandle(std::string XboxFullPath); @@ -305,19 +350,30 @@ typedef struct XboxPartitionTableEntry TableEntries[14]; } XboxPartitionTable; +inline constexpr xbox::ulong_xt fatx_name_length = 32; +inline constexpr xbox::ulong_xt fatx_online_data_length = 2048; +inline constexpr xbox::ulong_xt fatx_reserved_length = 1968; + +inline constexpr xbox::ulong_xt fatx_signature = 'XTAF'; + typedef struct _FATX_SUPERBLOCK { - char Tag[4]; - unsigned int VolumeID; - unsigned int ClusterSize; - USHORT FatCopies; - int Resvd; - char Unused[4078]; + xbox::ulong_xt Signature; + xbox::ulong_xt VolumeID; + xbox::ulong_xt ClusterSize; + xbox::ulong_xt FatCopies; + xbox::wchar_xt Name[fatx_name_length]; + xbox::uchar_xt OnlineData[fatx_online_data_length]; + xbox::uchar_xt Unused[fatx_reserved_length]; } FATX_SUPERBLOCK; +static_assert(sizeof(FATX_SUPERBLOCK) == PAGE_SIZE); + XboxPartitionTable CxbxGetPartitionTable(); FATX_SUPERBLOCK CxbxGetFatXSuperBlock(int partitionNumber); int CxbxGetPartitionNumberFromHandle(HANDLE hFile); +int CxbxGetPartitionNumberFromPath(const std::wstring_view path); +std::wstring CxbxGetFinalPathNameByHandle(HANDLE hFile); std::filesystem::path CxbxGetPartitionDataPathFromHandle(HANDLE hFile); void CxbxFormatPartitionByHandle(HANDLE hFile); diff --git a/src/gui/DlgInputConfig.cpp b/src/gui/DlgInputConfig.cpp index b614aa38e..69b0fa319 100644 --- a/src/gui/DlgInputConfig.cpp +++ b/src/gui/DlgInputConfig.cpp @@ -51,6 +51,8 @@ void SyncInputSettings(int port_num, int dev_type, bool is_opt) if (!is_opt) { // Sync updated input to kernel process to use run-time settings. g_EmuShared->SetInputDevTypeSettings(&g_Settings->m_input_port[port_num].Type, port_num); + g_EmuShared->SetInputSlotTypeSettings(&g_Settings->m_input_port[port_num].SlotType[SLOT_TOP], port_num, SLOT_TOP); + g_EmuShared->SetInputSlotTypeSettings(&g_Settings->m_input_port[port_num].SlotType[SLOT_BOTTOM], port_num, SLOT_BOTTOM); if (dev_type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) { std::string dev_name = g_Settings->m_input_port[port_num].DeviceName; @@ -77,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 } } @@ -163,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].SlotType[SLOT_TOP] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID); + g_Settings->m_input_port[port].SlotType[SLOT_BOTTOM] = 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 0eca3788e..5f66839e2 100644 --- a/src/gui/controllers/DlgDukeControllerConfig.cpp +++ b/src/gui/controllers/DlgDukeControllerConfig.cpp @@ -49,6 +49,8 @@ void DukeInputWindow::Initialize(HWND hwnd, int port_num, int dev_type) m_hwnd_device_list = GetDlgItem(m_hwnd_window, IDC_DEVICE_LIST); m_hwnd_profile_list = GetDlgItem(m_hwnd_window, IDC_PROFILE_NAME); m_hwnd_default = GetDlgItem(m_hwnd_window, IDC_DEFAULT); + m_hwnd_slot_list[SLOT_TOP] = GetDlgItem(hwnd, IDC_DEVICE_LIST_TOP_SLOT); + m_hwnd_slot_list[SLOT_BOTTOM] = GetDlgItem(hwnd, IDC_DEVICE_LIST_BOTTOM_SLOT); m_dev_type = dev_type; m_max_num_buttons = dev_num_buttons[dev_type]; m_port_num = port_num; @@ -79,11 +81,35 @@ 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); + if (m_dev_type == to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK)) { + // The arcade joystick does not have slot ports so we always disable the corresponding options + for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) { + LRESULT index = SendMessage(m_hwnd_slot_list[slot], CB_ADDSTRING, 0, + reinterpret_cast(GetInputDeviceName(to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)).c_str())); + SendMessage(m_hwnd_slot_list[slot], CB_SETITEMDATA, index, to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)); + SendMessage(m_hwnd_slot_list[slot], CB_SETCURSEL, index, 0); + EnableWindow(m_hwnd_slot_list[slot], FALSE); + } + } + else { + // Set up the device types we support in the slot ports + for (auto slot_type : slot_support_list) { + for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) { + LRESULT index = SendMessage(m_hwnd_slot_list[slot], CB_ADDSTRING, 0, + reinterpret_cast(GetInputDeviceName(to_underlying(slot_type)).c_str())); + SendMessage(m_hwnd_slot_list[slot], CB_SETITEMDATA, index, to_underlying(slot_type)); + if (g_Settings->m_input_port[m_port_num].SlotType[slot] == to_underlying(slot_type)) { + SendMessage(m_hwnd_slot_list[slot], CB_SETCURSEL, index, 0); + } + } + } + } + // construct emu device m_DeviceConfig = new EmuDevice(m_dev_type, m_hwnd_window, this); @@ -181,32 +207,12 @@ void DukeInputWindow::UpdateProfile(const std::string &name, int command) { switch (command) { - case PROFILE_LOAD: { - LoadProfile(name); - } - break; - - case PROFILE_SAVE: { - SaveProfile(name); - } - break; - - case PROFILE_DELETE: { - DeleteProfile(name); - } - break; - - case RUMBLE_CLEAR: { + case RUMBLE_CLEAR: m_rumble = std::string(); - } - break; - - case BUTTON_CLEAR: - case BUTTON_SWAP: { - m_bHasChanges = true; - } - break; + break; + default: + InputWindow::UpdateProfile(name, command); } } @@ -239,6 +245,14 @@ void DukeInputWindow::DetectOutput(int ms) } } +void DukeInputWindow::SaveSlotConfig() +{ + for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) { + g_Settings->m_input_port[m_port_num].SlotType[slot] = SendMessage(m_hwnd_slot_list[slot], CB_GETITEMDATA, + SendMessage(m_hwnd_slot_list[slot], CB_GETCURSEL, 0, 0), 0); + } +} + static INT_PTR CALLBACK DlgRumbleConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); INT_PTR CALLBACK DlgXidControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) @@ -266,6 +280,7 @@ INT_PTR CALLBACK DlgXidControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wPar case WM_CLOSE: { if (g_InputWindow->IsProfileSaved()) { + g_InputWindow->SaveSlotConfig(); delete g_InputWindow; g_InputWindow = nullptr; EndDialog(hWndDlg, wParam); diff --git a/src/gui/controllers/DlgSBControllerConfig.cpp b/src/gui/controllers/DlgSBControllerConfig.cpp index e7a640c6c..7a83dea9a 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); @@ -81,40 +81,19 @@ void SbcInputWindow::ClearBindings() m_bHasChanges = true; } -void SbcInputWindow::UpdateProfile(const std::string &name, int command) -{ - switch (command) - { - case PROFILE_LOAD: { - LoadProfile(name); - } - break; - - case PROFILE_SAVE: { - SaveProfile(name); - } - break; - - case PROFILE_DELETE: { - DeleteProfile(name); - } - break; - - case BUTTON_CLEAR: - case BUTTON_SWAP: { - m_bHasChanges = true; - } - break; - - } -} - int SbcInputWindow::EnableDefaultButton() { // The SBC window does not have a default button, so we return a dummy value here return -1; } +void SbcInputWindow::SaveSlotConfig() +{ + for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) { + g_Settings->m_input_port[m_port_num].SlotType[slot] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID); + } +} + INT_PTR CALLBACK DlgSBControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) @@ -138,6 +117,7 @@ INT_PTR CALLBACK DlgSBControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wPara case WM_CLOSE: { if (g_InputWindow->IsProfileSaved()) { + g_InputWindow->SaveSlotConfig(); delete g_InputWindow; g_InputWindow = nullptr; EndDialog(hWndDlg, wParam); diff --git a/src/gui/resource/Cxbx.rc b/src/gui/resource/Cxbx.rc index 75bb11375..e54afd15b 100644 --- a/src/gui/resource/Cxbx.rc +++ b/src/gui/resource/Cxbx.rc @@ -31,6 +31,10 @@ BEGIN BEGIN END + IDD_XID_DUKE_CFG, DIALOG + BEGIN + END + IDD_SBC_CFG, DIALOG BEGIN BOTTOMMARGIN, 269 @@ -113,7 +117,7 @@ BEGIN "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,23,166,139,10 END -IDD_XID_DUKE_CFG DIALOGEX 0, 0, 528, 280 +IDD_XID_DUKE_CFG DIALOGEX 0, 0, 528, 300 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU FONT 8, "Verdana", 0, 0, 0x1 BEGIN @@ -180,8 +184,12 @@ BEGIN GROUPBOX "Rumble",IDC_RUMBLE,396,212,121,34,WS_GROUP PUSHBUTTON "",IDC_SET_MOTOR,443,224,57,14,BS_FLAT LTEXT "Motor",IDC_STATIC,412,224,26,14,SS_CENTERIMAGE - PUSHBUTTON "Default Bindings",IDC_DEFAULT,362,256,69,14,BS_FLAT - PUSHBUTTON "Clear",IDC_CLEAR,443,256,50,14,BS_FLAT + PUSHBUTTON "Default Bindings",IDC_DEFAULT,362,265,69,14,BS_FLAT + PUSHBUTTON "Clear",IDC_CLEAR,443,265,50,14,BS_FLAT + GROUPBOX "Top Slot",IDC_DEVICE_TOP_SLOT,12,254,97,35,WS_GROUP + COMBOBOX IDC_DEVICE_LIST_TOP_SLOT,21,267,79,15,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Bottom Slot",IDC_DEVICE_BOTTOM_SLOT,117,254,97,35,WS_GROUP + COMBOBOX IDC_DEVICE_LIST_BOTTOM_SLOT,126,267,79,15,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP END IDD_RUMBLE_CFG DIALOGEX 0, 0, 155, 35 @@ -566,6 +574,11 @@ BEGIN 0 END +IDD_XID_DUKE_CFG AFX_DIALOG_LAYOUT +BEGIN + 0 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -687,7 +700,7 @@ BEGIN MENUITEM "&Clear entire Symbol Cache", ID_CACHE_CLEARHLECACHE_ALL,MFT_STRING,MFS_ENABLED MENUITEM "&Rescan title Symbol Cache", ID_CACHE_CLEARHLECACHE_CURRENT,MFT_STRING,MFS_ENABLED END - MENUITEM "Clear Cache Partitions", ID_SETTINGS_CLEAR_PARTITIONS,MFT_STRING,MFS_ENABLED + MENUITEM "Clear Cache Partitions", ID_SETTINGS_CLEAR_PARTITIONS,MFT_STRING,MFS_ENABLED MENUITEM "", -1, MFT_SEPARATOR POPUP "Experimental", 65535,MFT_STRING,MFS_ENABLED BEGIN diff --git a/src/gui/resource/ResCxbx.h b/src/gui/resource/ResCxbx.h index 727aeb028..7d121167e 100644 --- a/src/gui/resource/ResCxbx.h +++ b/src/gui/resource/ResCxbx.h @@ -1,4 +1,4 @@ -//{{NO_DEPENDENCIES}} +//{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by Cxbx.rc // @@ -94,6 +94,10 @@ #define IDC_LOG_VSHCACHE 962 #define IDC_LOG_RINP 963 #define IDC_LOG_JVS 964 +#define IDC_DEVICE_LIST_TOP_SLOT 995 +#define IDC_DEVICE_LIST_BOTTOM_SLOT 996 +#define IDC_DEVICE_TOP_SLOT 997 +#define IDC_DEVICE_BOTTOM_SLOT 998 #define IDC_SET_MOTOR 999 #define IDC_SET_X 1000 #define IDC_SET_Y 1001