diff --git a/src/common/input/InputManager.cpp b/src/common/input/InputManager.cpp index 5dd8321c0..0c3346190 100644 --- a/src/common/input/InputManager.cpp +++ b/src/common/input/InputManager.cpp @@ -117,18 +117,19 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd) for (unsigned i = 0; i < 4; ++i) { int type; g_EmuShared->GetInputDevTypeSettings(&type, i); - std::string port = std::to_string(i); if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) { + 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 + slot], &g_devs[CTRL_OFFSET + i], type, port + std::to_string(slot)); + ConstructHleInputDevice(&g_devs[MU_OFFSET + slot], &g_devs[CTRL_OFFSET + i], type, port + "." + std::to_string(slot)); } } } @@ -137,6 +138,7 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd) 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: @@ -224,8 +226,8 @@ void InputDeviceManager::UpdateDevices(std::string_view port, bool ack) { DeviceState *dev, *upstream; int port1, type, slot; - dev = &g_devs[port1]; PortStr2Int(port, &port1, &slot); + dev = &g_devs[port1]; if (slot == PORT_INVALID) { // Port references a device attached to an xbox port upstream = nullptr; diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 8878746da..a335e80ef 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -59,10 +59,38 @@ std::atomic g_bIsDevicesEmulating = false; // Protects access to xpp types std::atomic g_bXppGuard = false; -// allocate enough memory for the max number of devices we can support simultaneously +// 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[4 + 8]; +xbox::ulong_xt g_Mounted_MUs = 0; // fallback if XapiMountedMUs is not found +xbox::ulong_xt *g_XapiMountedMUs = &g_Mounted_MUs; +std::mutex g_MuLock; + +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) { @@ -103,7 +131,7 @@ bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) void UpdateXppState(DeviceState *dev, XBOX_INPUT_DEVICE type, std::string_view port) { - xbox::PXPP_DEVICE_TYPE xpp; + xbox::PXPP_DEVICE_TYPE xpp = nullptr; switch (type) { case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE: @@ -121,44 +149,38 @@ void UpdateXppState(DeviceState *dev, XBOX_INPUT_DEVICE type, std::string_view p break; default: - xpp = nullptr; + assert(0); } - assert(xpp != nullptr); + 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 port1, slot; PortStr2Int(port, &port1, &slot); xbox::ulong_xt port_mask = 1 << port1; + 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) { - if ((dev->upstream->type != XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE) || - (dev->upstream->type != XBOX_INPUT_DEVICE::MS_CONTROLLER_S) || - dev->upstream->bPendingRemoval) { - xpp->CurrentConnected &= ~port_mask; - xpp->CurrentConnected &= ~(port_mask << 16); - } - else { - for (unsigned i = 0, j = 0; i < XBOX_CTRL_NUM_SLOTS; ++i, j += 16) { - if (xpp == dev->type && !dev->bPendingRemoval) { - xpp->CurrentConnected |= (port_mask << j); - } - else { - xpp->CurrentConnected &= ~(port_mask << j); - } - } + assert((dev->upstream->type == XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE) || + (dev->upstream->type == XBOX_INPUT_DEVICE::MS_CONTROLLER_S)); + assert(slot != PORT_INVALID); + if (slot == 1) { + slot_mask = 16; } } + + if (xpp == dev->type && !dev->bPendingRemoval) { + xpp->CurrentConnected |= (port_mask << slot_mask); + } else { - if (xpp == dev->type && !dev->bPendingRemoval) { - xpp->CurrentConnected |= port_mask; - } - else { - xpp->CurrentConnected &= ~port_mask; - } + xpp->CurrentConnected &= ~(port_mask << slot_mask); } xpp->ChangeConnected = xpp->PreviousConnected ^ xpp->CurrentConnected; @@ -425,6 +447,34 @@ void SetupXboxDeviceTypes() else { EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT was not found because MU_Init could not be found"); } + + // Temporary code until XapiMountedMUs is derived by XbSymbolDatabase + Xbe::LibraryVersion *pLibraryVersion = reinterpret_cast(CxbxKrnl_Xbe->m_Header.dwLibraryVersionsAddr); + if (pLibraryVersion != nullptr) { + if (uint8_t *start = reinterpret_cast(g_SymbolAddresses["XUnmountMU"])) { + uint32_t offset = 0; + for (unsigned v = 0; v < CxbxKrnl_Xbe->m_Header.dwLibraryVersions; ++v) { + if (std::strcmp(pLibraryVersion[v].szName, "XAPILIB") == 0) { + if (pLibraryVersion[v].wBuildVersion < 4242) { + offset = 0x1D; + } + else { + offset = 0x2A; + } + break; + } + } + // skip 2 because the address is hard-coded inside a test instruction + g_XapiMountedMUs = reinterpret_cast(*reinterpret_cast((g_SymbolAddresses["XUnmountMU"] + offset + 2))); + EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs found at 0x%08X", reinterpret_cast(g_XapiMountedMUs)); + } + else { + EmuLog(LOG_LEVEL::WARNING, "XapiMountedMUs was not found because XUnmountMU could not be found"); + } + } + else { + EmuLog(LOG_LEVEL::WARNING, "XapiMountedMUs was not found because this xbe does not have a library version address"); + } } template @@ -1318,18 +1368,28 @@ 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); + } + + MuSetMounted(lett); + if (pchDrive != zeroptr) { + *pchDrive = lett; + } + + RETURN(ERROR_SUCCESS); } // ****************************************************************** @@ -1378,16 +1438,41 @@ 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; + } + RtlNtStatusToDosError(status); + RETURN(status); + } + + MuSetMounted(lett); + if (pchDrive != zeroptr) { + *pchDrive = lett; + } RETURN(ERROR_SUCCESS); } @@ -1401,14 +1486,29 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XUnmountMU) dword_xt dwSlot ) { - - LOG_FUNC_BEGIN LOG_FUNC_ARG(dwPort) LOG_FUNC_ARG(dwSlot) LOG_FUNC_END; - LOG_UNIMPLEMENTED(); + std::lock_guard lock(g_MuLock); + + char_xt lett = MuPort2Lett(dwPort, dwSlot); + if (!MuIsMounted(lett)) { + RETURN(ERROR_INVALID_DRIVE); + } + + 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)) { + RtlNtStatusToDosError(status); + RETURN(status); + } + + MuClearMounted(lett); RETURN(ERROR_SUCCESS); } diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index a8c7d9bfe..633b826a1 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -1501,25 +1501,48 @@ XBSYSAPI EXPORTNUM(218) xbox::ntstatus_xt NTAPI xbox::NtQueryVolumeInformationFi if ((DWORD)FileInformationClass == FileFsSizeInformation) { 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 or a MU, so we need to figure out the correct one first + const std::wstring path = CxbxGetFinalPathNameByHandle(FileHandle); + size_t pos = path.rfind(L"\\EmuDisk\\Partition"); + if (pos != std::string::npos) { + // We are accessing a disk partition - XboxSizeInfo->BytesPerSector = 512; + XboxPartitionTable partitionTable = CxbxGetPartitionTable(); + int partitionNumber = CxbxGetPartitionNumberFromPath(path); + 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; + + RETURN(xbox::status_success); } - XboxSizeInfo->TotalAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; - XboxSizeInfo->AvailableAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; + pos = path.rfind(L"\\EmuMu"); + if (pos != std::string::npos) { + // We are accessing a MU - RETURN(xbox::status_success); + 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 + + RETURN(xbox::status_success); + } + + EmuLog(LOG_LEVEL::WARNING, "%s: Unrecongnized handle 0x%X with class FileFsSizeInformation", __func__, FileHandle); + + RETURN(xbox::status_invalid_handle); } // 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 00111d56a..99dfb5dcf 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -1509,16 +1509,15 @@ __declspec(noreturn) void CxbxKrnlInit CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition7, CxbxBasePath + "Partition7"); CxbxRegisterDeviceHostPath(DevicePrefix + "\\Chihiro", CxbxBasePath + "Chihiro"); - // Create MU directories - for (unsigned i = 0; i < 8; ++i) { - std::error_code error; - static char mu_letter = 'F'; - std::string mu_path = MuBasePath + mu_letter; - if (!(std::filesystem::exists(mu_path) || std::filesystem::create_directory(mu_path, error))) { - CxbxKrnlCleanup("Failed to create memory unit directories"); - } - ++mu_letter; - } + // Create the MU directories + CxbxRegisterDeviceHostPath(DeviceMU0, MuBasePath + "F"); + CxbxRegisterDeviceHostPath(DeviceMU1, MuBasePath + "G"); + CxbxRegisterDeviceHostPath(DeviceMU2, MuBasePath + "H"); + CxbxRegisterDeviceHostPath(DeviceMU3, MuBasePath + "I"); + CxbxRegisterDeviceHostPath(DeviceMU4, MuBasePath + "J"); + CxbxRegisterDeviceHostPath(DeviceMU5, MuBasePath + "K"); + CxbxRegisterDeviceHostPath(DeviceMU6, MuBasePath + "L"); + CxbxRegisterDeviceHostPath(DeviceMU7, MuBasePath + "M"); // Create default symbolic links : EmuLogInit(LOG_LEVEL::DEBUG, "Creating default symbolic links."); diff --git a/src/core/kernel/support/EmuFile.cpp b/src/core/kernel/support/EmuFile.cpp index c10e0cbb3..846b6c651 100644 --- a/src/core/kernel/support/EmuFile.cpp +++ b/src/core/kernel/support/EmuFile.cpp @@ -139,7 +139,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 +173,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) @@ -267,6 +277,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. @@ -289,6 +300,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; diff --git a/src/core/kernel/support/EmuFile.h b/src/core/kernel/support/EmuFile.h index 33c11bf3e..487d33949 100644 --- a/src/core/kernel/support/EmuFile.h +++ b/src/core/kernel/support/EmuFile.h @@ -73,6 +73,7 @@ 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,6 +96,14 @@ 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'; extern std::string CxbxBasePath; @@ -318,6 +327,8 @@ typedef struct _FATX_SUPERBLOCK 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 03d567291..3530a9c96 100644 --- a/src/gui/DlgInputConfig.cpp +++ b/src/gui/DlgInputConfig.cpp @@ -161,7 +161,7 @@ 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) || + 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