From 3e60c31d26f1f68525c7784ad9aaa3ecccce47dc Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Tue, 22 Jun 2021 00:38:59 +0200 Subject: [PATCH] Updated kernel file nt functions to support MUs + fixed bugs in xapi functions and input gui related to MUs --- import/XbSymbolDatabase | 2 +- src/common/input/InputDevice.h | 19 +- src/common/input/InputManager.cpp | 48 ++-- src/common/input/InputManager.h | 4 +- src/common/input/InputWindow.cpp | 12 +- src/common/input/InputWindow.h | 7 +- src/common/input/SdlJoystick.cpp | 15 +- src/common/input/SdlJoystick.h | 6 - src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 2 +- src/core/hle/D3D8/Direct3D9/Direct3D9.h | 3 +- src/core/hle/Patches.cpp | 4 +- src/core/hle/XAPI/Xapi.cpp | 239 +++++++++++------- src/core/hle/XAPI/Xapi.h | 12 + src/core/kernel/common/types.h | 1 + src/core/kernel/exports/EmuKrnlNt.cpp | 166 ++++++++---- src/core/kernel/init/CxbxKrnl.cpp | 29 ++- src/core/kernel/support/EmuFile.cpp | 127 +++++++++- src/core/kernel/support/EmuFile.h | 61 ++++- .../controllers/DlgDukeControllerConfig.cpp | 26 +- 19 files changed, 551 insertions(+), 232 deletions(-) diff --git a/import/XbSymbolDatabase b/import/XbSymbolDatabase index 9a411fea5..8b000c0ca 160000 --- a/import/XbSymbolDatabase +++ b/import/XbSymbolDatabase @@ -1 +1 @@ -Subproject commit 9a411fea58d97f4abe49e08893c57d6d9ff7f666 +Subproject commit 8b000c0ca7f20d88dddd95e80ad257ba2a0cffaa diff --git a/src/common/input/InputDevice.h b/src/common/input/InputDevice.h index 1438d61e2..a56ab1f9b 100644 --- a/src/common/input/InputDevice.h +++ b/src/common/input/InputDevice.h @@ -144,18 +144,23 @@ 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; @@ -163,8 +168,8 @@ private: std::vector m_Outputs; // xbox port(s) this device is attached to std::vector m_XboxPort; - // button bindings to the xbox device buttons - std::map m_Bindings; + // 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 0c3346190..6bde93123 100644 --- a/src/common/input/InputManager.cpp +++ b/src/common/input/InputManager.cpp @@ -129,7 +129,7 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd) 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 + (XBOX_CTRL_NUM_SLOTS * i) + slot], &g_devs[CTRL_OFFSET + i], type, port + "." + std::to_string(slot)); } } } @@ -225,7 +225,7 @@ void InputDeviceManager::RemoveDevice(std::function Ca void InputDeviceManager::UpdateDevices(std::string_view port, bool ack) { DeviceState *dev, *upstream; - int port1, type, slot; + int port1, slot, type; PortStr2Int(port, &port1, &slot); dev = &g_devs[port1]; @@ -234,16 +234,27 @@ void InputDeviceManager::UpdateDevices(std::string_view port, bool ack) g_EmuShared->GetInputDevTypeSettings(&type, port1); } else { // Port references a device attached to a slot port - assert(dev->type == XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE || - dev->type == XBOX_INPUT_DEVICE::MS_CONTROLLER_S); upstream = dev; dev = dev->slots[slot]; g_EmuShared->GetInputSlotTypeSettings(&type, port1, slot); } - // connect slot + // updating a slot if (dev == nullptr) { - ConnectDevice(&g_devs[MU_OFFSET + port1 + slot], upstream, type, port); + // connect slot + if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) && + g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port1) + slot].type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { + ConnectDevice(&g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port1) + slot], upstream, type, port); + } + // disconnect slot + else if (type == to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) && + g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port1) + slot].type != XBOX_INPUT_DEVICE::DEVICE_INVALID) { + DisconnectDevice(&g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port1) + slot], port, ack); + } + // update bindings slot + else { + // MUs don't have any host devices attached, so this is a nop for now + } } // connect else if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) && @@ -253,8 +264,8 @@ void InputDeviceManager::UpdateDevices(std::string_view port, bool ack) // disconnect else if (type == to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) && dev->type != XBOX_INPUT_DEVICE::DEVICE_INVALID) { - // We don't need to check of we need to destroy child devices because the UpdateInputEvent_t message always - // calls us on the entire slot connectivity if the device has slots available + // 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 @@ -309,7 +320,7 @@ void InputDeviceManager::DisconnectDevice(DeviceState *dev, std::string_view por void InputDeviceManager::BindHostDevice(int type, std::string_view port) { if (type == to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT)) { - // MUs don't have any host device bound, so we just return + // MUs don't have any host devices bound, so we just return return; } @@ -322,6 +333,8 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port) auto dev = FindDevice(std::string(dev_name)); if (dev != nullptr) { + std::string port1(port); + dev->ClearBindings(port1); std::vector controls = dev->GetIoControls(); for (int index = 0; index < dev_num_buttons[type]; index++) { std::string dev_button(dev_control_names[index]); @@ -331,7 +344,7 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port) } return false; }); - dev->SetBindings(index, (it != controls.end()) ? *it : nullptr); + dev->SetBindings(index, (it != controls.end()) ? *it : nullptr, port1); } dev->SetPort(port, true); } @@ -348,18 +361,19 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi // 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 : m_Devices) { - if (dev->GetPort(std::to_string(port))) { + std::string port1 = std::to_string(port); + if (dev->GetPort(port1)) { 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, buffer, direction); + has_changed = UpdateInputXpad(dev, buffer, direction, port1); m_Mtx.unlock(); return has_changed; case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): - has_changed = UpdateInputSBC(dev, buffer, direction, port); + has_changed = UpdateInputSBC(dev, buffer, direction, port, port1); m_Mtx.unlock(); return has_changed; @@ -383,9 +397,9 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi 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 &Port1) { - std::map bindings = Device->GetBindings(); + std::map bindings = Device->GetBindings(Port1); assert(bindings.size() == static_cast(dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE)])); if (Direction == DIRECTION_IN) { @@ -447,9 +461,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, const std::string &Port1) { - std::map bindings = Device->GetBindings(); + std::map bindings = Device->GetBindings(Port1); assert(bindings.size() == static_cast(dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER)])); // NOTE: the output state is not supported diff --git a/src/common/input/InputManager.h b/src/common/input/InputManager.h index c1acc856d..56605a806 100644 --- a/src/common/input/InputManager.h +++ b/src/common/input/InputManager.h @@ -165,9 +165,9 @@ 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 &Port1); // 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, const std::string &Port1); // bind a host device to an emulated device void BindHostDevice(int type, std::string_view port); // connect a device to the emulated machine diff --git a/src/common/input/InputWindow.cpp b/src/common/input/InputWindow.cpp index 05b0bad3f..12d617037 100644 --- a/src/common/input/InputWindow.cpp +++ b/src/common/input/InputWindow.cpp @@ -43,7 +43,7 @@ InputWindow::~InputWindow() m_DeviceConfig = nullptr; } -int InputWindow::IsProfileSaved() +bool InputWindow::IsProfileSaved() { if (m_bHasChanges) { PopupReturn ret = PopupQuestion(m_hwnd_window, "Current configuration is not saved. Save before closing?"); @@ -53,24 +53,24 @@ int InputWindow::IsProfileSaved() char name[50]; SendMessage(m_hwnd_profile_list, WM_GETTEXT, sizeof(name), reinterpret_cast(name)); if (SaveProfile(std::string(name))) { - return EXIT_SAVE; + return true; } - return EXIT_ABORT; + return false; } case PopupReturn::No: { - return EXIT_IGNORE; + return true; } case PopupReturn::Cancel: default: { - return EXIT_ABORT; + return false; } } } - return EXIT_IGNORE; + return true; } void InputWindow::UpdateDeviceList() diff --git a/src/common/input/InputWindow.h b/src/common/input/InputWindow.h index cbad576d4..b0fcbdbff 100644 --- a/src/common/input/InputWindow.h +++ b/src/common/input/InputWindow.h @@ -42,10 +42,6 @@ #define BUTTON_SWAP 9 #define SLOTS_CHANGED 10 -#define EXIT_ABORT 0 -#define EXIT_SAVE 1 -#define EXIT_IGNORE 2 - #define XINPUT_DEFAULT 0 #define DINPUT_DEFAULT 1 @@ -65,7 +61,7 @@ public: virtual void ClearBindings() = 0; virtual void UpdateProfile(const std::string& name, int command); void UpdateCurrentDevice(); - virtual int IsProfileSaved(); + bool IsProfileSaved(); void SwapMoCursorAxis(Button *button); @@ -111,7 +107,6 @@ public: void BindDefault(); void ClearBindings() override; void UpdateProfile(const std::string &name, int command) override; - int IsProfileSaved() override; void SaveSlotConfig(); diff --git a/src/common/input/SdlJoystick.cpp b/src/common/input/SdlJoystick.cpp index bb4a0bf9d..e571ee3a7 100644 --- a/src/common/input/SdlJoystick.cpp +++ b/src/common/input/SdlJoystick.cpp @@ -163,20 +163,19 @@ namespace Sdl std::string port = std::to_string(*static_cast(Event.user.data1)); int port1, slot; PortStr2Int(port, &port1, &slot); - if (g_devs[port1].type == XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE || g_devs[port1].type == XBOX_INPUT_DEVICE::MS_CONTROLLER_S) { - // Force an update of the entire slot connectivity of this port - g_InputDeviceManager.UpdateDevices(port + ".0", false); - g_InputDeviceManager.UpdateDevices(port + ".1", false); - } + g_InputDeviceManager.UpdateDevices(port, false); + // 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(std::string(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; } } diff --git a/src/common/input/SdlJoystick.h b/src/common/input/SdlJoystick.h index 05055f7a9..b6d18008e 100644 --- a/src/common/input/SdlJoystick.h +++ b/src/common/input/SdlJoystick.h @@ -43,12 +43,6 @@ namespace Sdl } SDL_INIT_STATUS; - struct UPDATE_INPUT_DATA { - int Port; - int Slot; - bool Opt; - }; - extern uint32_t ExitEvent_t; extern uint32_t PopulateEvent_t; extern uint32_t UpdateInputEvent_t; diff --git a/src/core/hle/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..9d5a68fe5 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.h +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.h @@ -37,7 +37,8 @@ #define DIRECTDRAW_VERSION 0x0700 #include -extern void LookupTrampolines(); +extern void LookupTrampolinesD3D(); +extern void LookupTrampolinesXAPI(); // initialize render window extern void CxbxInitWindow(bool bFullInit); diff --git a/src/core/hle/Patches.cpp b/src/core/hle/Patches.cpp index 9e113068e..ad76ad3d4 100644 --- a/src/core/hle/Patches.cpp +++ b/src/core/hle/Patches.cpp @@ -365,6 +365,7 @@ 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), }; @@ -445,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 a335e80ef..7949b86b1 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -43,8 +43,6 @@ #include "Windef.h" #include #include "core\hle\XAPI\Xapi.h" -#include "distorm.h" -#include "mnemonics.h" #include @@ -63,9 +61,26 @@ std::atomic g_bXppGuard = false; // 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_Mounted_MUs = 0; +xbox::char_xt g_AltLett_MU = 0; xbox::ulong_xt *g_XapiMountedMUs = &g_Mounted_MUs; -std::mutex g_MuLock; +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) { @@ -168,8 +183,6 @@ void UpdateXppState(DeviceState *dev, XBOX_INPUT_DEVICE type, std::string_view p while (g_bXppGuard) {} if (xpp == g_DeviceType_MU) { - 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; @@ -193,7 +206,7 @@ void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type, if (g_bIsChihiro) { // Don't emulate XID devices during Chihiro Emulation g_bIsDevicesEmulating = false; - return ret; + return; } // Set up common device state int port1, slot; @@ -280,7 +293,7 @@ void DestructHleInputDevice(DeviceState *dev) dev->bSignaled = false; dev->slots[SLOT_TOP] = dev->slots[SLOT_BOTTOM] = nullptr; - switch (dev->type) + switch (type) { case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE: case XBOX_INPUT_DEVICE::MS_CONTROLLER_S: @@ -314,8 +327,7 @@ void DestructHleInputDevice(DeviceState *dev) break; case XBOX_INPUT_DEVICE::MEMORY_UNIT: { - assert(dev->upstream != nullptr && (dev->upstream->type == XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE || - dev->upstream->type == XBOX_INPUT_DEVICE::MS_CONTROLLER_S)); + assert(dev->upstream != nullptr); int port1, slot; PortStr2Int(port, &port1, &slot); assert(slot != PORT_INVALID); @@ -403,77 +415,23 @@ void SetupXboxDeviceTypes() EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_GAMEPAD found at 0x%08X", (uintptr_t)g_DeviceType_Gamepad); } - // The MU device type is not present in the xpp table, because that only holds the types that XInputOpen supports, and MUs are not - // one of those. Insted, this type is referenced by MU_Init, the main MU initialization function, so we can derive it from that. - // Unfortunately, the offset of the MU type varies slightly between xdk revisions, so we cannot just read from a fixed offset. What's - // constant is that the type is always hardcoded in a push instruction immediately followed by a call, and there are no other push - call - // instructions between the start of MU_Init and the offset of interest. - - if (uint8_t *start = reinterpret_cast(g_SymbolAddresses["MU_Init"])) { - _CodeInfo ci; - ci.code = start; - ci.codeLen = 100; - ci.codeOffset = 0; - ci.dt = Decode32Bits; - ci.features = DF_NONE; - std::array<_DInst, 50> info; - unsigned i; - - distorm_decompose(&ci, info.data(), 50, &i); - - i = 0; - const auto &it = std::find_if(info.begin(), info.end(), [&info, &i](_DInst &op) { - if (!(op.opcode == I_PUSH)) { - ++i; - return false; - } - - if (((i + 1) <= 49) && (info[i + 1].opcode == I_CALL)) { - return true; - } - - ++i; - return false; - }); - - if (it == info.end()) { - EmuLog(LOG_LEVEL::WARNING, "XDEVICE_TYPE_MEMORY_UNIT was not found inside MU_Init"); - } - else { - g_DeviceType_MU = reinterpret_cast(*reinterpret_cast((it->addr + start + 1))); - EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT found at 0x%08X", reinterpret_cast(g_DeviceType_MU)); - } + 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 because MU_Init could not be found"); + EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT was not found by XbSymbolDatabase"); } - // 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"); - } + 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::WARNING, "XapiMountedMUs was not found because this xbe does not have a library version address"); + EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs was not found by XbSymbolDatabase"); } } @@ -545,10 +503,10 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDevices) LOG_FUNC_ONE_ARG(DeviceType); g_bXppGuard = true; - static dword_xt last_connected = 0; + if (g_bIsDevicesInitializing || g_bIsDevicesEmulating) { g_bXppGuard = false; - RETURN(last_connected); + RETURN(DeviceType->CurrentConnected); } for (unsigned i = 0; i < 12; ++i) { @@ -558,15 +516,14 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDevices) SDL_Event DeviceRemoveEvent; SDL_memset(&DeviceRemoveEvent, 0, sizeof(SDL_Event)); DeviceRemoveEvent.type = Sdl::DeviceRemoveAck_t; - DeviceRemoveEvent.user.data1 = new char[g_devs[i].port.size()]; - std::strcpy(static_cast(DeviceRemoveEvent.user.data1), g_devs[i].port.c_str()); + DeviceRemoveEvent.user.data1 = new std::string(g_devs[i].port); SDL_PushEvent(&DeviceRemoveEvent); } } UCHAR oldIrql = xbox::KeRaiseIrqlToDpcLevel(); - last_connected = DeviceType->CurrentConnected; + dword_xt ret = DeviceType->CurrentConnected; DeviceType->ChangeConnected = 0; DeviceType->PreviousConnected = DeviceType->CurrentConnected; @@ -575,7 +532,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDevices) g_bXppGuard = false; - RETURN(last_connected); + RETURN(ret); } // ****************************************************************** @@ -613,8 +570,7 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(XGetDeviceChanges) SDL_Event DeviceRemoveEvent; SDL_memset(&DeviceRemoveEvent, 0, sizeof(SDL_Event)); DeviceRemoveEvent.type = Sdl::DeviceRemoveAck_t; - DeviceRemoveEvent.user.data1 = new char[g_devs[i].port.size()]; - std::strcpy(static_cast(DeviceRemoveEvent.user.data1), g_devs[i].port.c_str()); + DeviceRemoveEvent.user.data1 = new std::string(g_devs[i].port); SDL_PushEvent(&DeviceRemoveEvent); } } @@ -1384,6 +1340,22 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XMountMUA) 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; @@ -1465,8 +1437,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XMountMURootA) if (pchDrive != zeroptr) { *pchDrive = 0; } - RtlNtStatusToDosError(status); - RETURN(status); + RETURN(RtlNtStatusToDosError(status)); } MuSetMounted(lett); @@ -1498,21 +1469,115 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XUnmountMU) 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)) { - RtlNtStatusToDosError(status); - RETURN(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 3902fd71e..b70125f64 100644 --- a/src/core/hle/XAPI/Xapi.h +++ b/src/core/hle/XAPI/Xapi.h @@ -693,6 +693,18 @@ xbox::dword_xt WINAPI EMUPATCH(XUnmountMU) 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 633b826a1..7f672a7f1 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,55 @@ 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: { + 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,16 +1563,15 @@ 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; - // 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 + // 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) { XboxPartitionTable partitionTable = CxbxGetPartitionTable(); - int partitionNumber = CxbxGetPartitionNumberFromPath(path); + int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); FATX_SUPERBLOCK superBlock = CxbxGetFatXSuperBlock(partitionNumber); XboxSizeInfo->BytesPerSector = 512; @@ -1525,24 +1588,31 @@ XBSYSAPI EXPORTNUM(218) xbox::ntstatus_xt NTAPI xbox::NtQueryVolumeInformationFi XboxSizeInfo->TotalAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; XboxSizeInfo->AvailableAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; - RETURN(xbox::status_success); + status = status_success; } - - pos = path.rfind(L"\\EmuMu"); - if (pos != std::string::npos) { - // We are accessing a MU + 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 - RETURN(xbox::status_success); + 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; } - EmuLog(LOG_LEVEL::WARNING, "%s: Unrecongnized handle 0x%X with class FileFsSizeInformation", __func__, FileHandle); - - RETURN(xbox::status_invalid_handle); + 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 99dfb5dcf..edd786d6a 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -1509,15 +1509,21 @@ __declspec(noreturn) void CxbxKrnlInit CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition7, CxbxBasePath + "Partition7"); CxbxRegisterDeviceHostPath(DevicePrefix + "\\Chihiro", CxbxBasePath + "Chihiro"); - // 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 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."); @@ -1942,6 +1948,11 @@ void CxbxKrnlShutDown(bool is_reboot) // Shutdown the memory manager g_VMManager.Shutdown(); + if (g_io_mu_metadata) { + delete g_io_mu_metadata; + g_io_mu_metadata = nullptr; + } + // Shutdown the render manager if (g_renderbase != nullptr) { g_renderbase->Shutdown(); diff --git a/src/core/kernel/support/EmuFile.cpp b/src/core/kernel/support/EmuFile.cpp index 846b6c651..c96f2dae1 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,102 @@ 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; + } + + EmuNtSymbolicLinkObject *ret = FindNtSymbolicLinkObjectByDevice(DeviceCdrom0); + if (ret != nullptr && path.rfind(ret->wHostSymbolicLinkPath) != std::string::npos) { + 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 +193,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); } @@ -701,7 +796,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; @@ -709,11 +804,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; @@ -833,6 +929,10 @@ NTSTATUS EmuNtSymbolicLinkObject::Init(std::string aSymbolicLinkName, std::strin } else { + std::mbstate_t ps = std::mbstate_t(); + const char *src = HostSymbolicLinkPath.c_str(); + wHostSymbolicLinkPath.resize(HostSymbolicLinkPath.size()); + std::mbsrtowcs(wHostSymbolicLinkPath.data(), &src, wHostSymbolicLinkPath.size(), &ps); NtSymbolicLinkObjects[DriveLetter - 'A'] = this; EmuLog(LOG_LEVEL::DEBUG, "Linked \"%s\" to \"%s\" (residing at \"%s\")", aSymbolicLinkName.c_str(), aFullPath.c_str(), HostSymbolicLinkPath.c_str()); } @@ -918,6 +1018,19 @@ EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(const HANDLE Handl } +EmuNtSymbolicLinkObject *FindNtSymbolicLinkObjectByDevice(const std::string_view Device) +{ + for (char DriveLetter = 'A'; DriveLetter <= 'Z'; DriveLetter++) + { + EmuNtSymbolicLinkObject *result = NtSymbolicLinkObjects[DriveLetter - 'A']; + if ((result != nullptr) && (result->XboxSymbolicLinkPath == Device)) + return result; + } + + return nullptr; +} + + void _CxbxPVOIDDeleter(PVOID *ptr) { if (*ptr) { diff --git a/src/core/kernel/support/EmuFile.h b/src/core/kernel/support/EmuFile.h index 487d33949..fe0dabc52 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 @@ -106,6 +107,18 @@ 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; @@ -218,6 +231,7 @@ public: bool IsHostBasedPath; std::string XboxSymbolicLinkPath; std::string HostSymbolicLinkPath; + std::wstring wHostSymbolicLinkPath; HANDLE RootDirectoryHandle; NTSTATUS Init(std::string aSymbolicLinkName, std::string aFullPath); ~EmuNtSymbolicLinkObject(); @@ -235,9 +249,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); @@ -248,6 +284,8 @@ EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDriveLetter(const char DriveL EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByName(std::string SymbolicLinkName); void FindEmuDirPathByDevice(std::string DeviceName, EmuDirPath& hybrid_path); EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(HANDLE Handle); +EmuNtSymbolicLinkObject *FindNtSymbolicLinkObjectByDevice(const std::string_view Device); +DeviceType CxbxrGetDeviceTypeFromHandle(HANDLE hFile); void CleanupSymbolicLinks(); HANDLE CxbxGetDeviceNativeRootHandle(std::string XboxFullPath); @@ -314,16 +352,25 @@ 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); diff --git a/src/gui/controllers/DlgDukeControllerConfig.cpp b/src/gui/controllers/DlgDukeControllerConfig.cpp index ccbfe3ed5..fefaba7a6 100644 --- a/src/gui/controllers/DlgDukeControllerConfig.cpp +++ b/src/gui/controllers/DlgDukeControllerConfig.cpp @@ -88,6 +88,14 @@ void DukeInputWindow::Initialize(HWND hwnd, int port_num, int dev_type) 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 + LRESULT index_top = SendMessage(m_hwnd_slot_list[SLOT_TOP], CB_ADDSTRING, 0, + reinterpret_cast(GetInputDeviceName(to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)).c_str())); + LRESULT index_bottom = SendMessage(m_hwnd_slot_list[SLOT_BOTTOM], CB_ADDSTRING, 0, + reinterpret_cast(GetInputDeviceName(to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)).c_str())); + SendMessage(m_hwnd_slot_list[SLOT_TOP], CB_SETITEMDATA, index_top, to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)); + SendMessage(m_hwnd_slot_list[SLOT_BOTTOM], CB_SETITEMDATA, index_bottom, to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)); + SendMessage(m_hwnd_slot_list[SLOT_TOP], CB_SETCURSEL, index_top, 0); + SendMessage(m_hwnd_slot_list[SLOT_BOTTOM], CB_SETCURSEL, index_bottom, 0); EnableWindow(m_hwnd_slot_list[SLOT_TOP], FALSE); EnableWindow(m_hwnd_slot_list[SLOT_BOTTOM], FALSE); } @@ -248,24 +256,6 @@ void DukeInputWindow::DetectOutput(int ms) } } -int DukeInputWindow::IsProfileSaved() -{ - if (int ret = InputWindow::IsProfileSaved()) { - if (ret == EXIT_IGNORE) { - return EXIT_IGNORE; - } - else { - if (m_dev_type != to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK)) { - SaveSlotConfig(); - } - - return EXIT_SAVE; - } - } - - return EXIT_ABORT; -} - void DukeInputWindow::SaveSlotConfig() { int DeviceType = SendMessage(m_hwnd_slot_list[SLOT_TOP], CB_GETITEMDATA, SendMessage(m_hwnd_slot_list[SLOT_TOP], CB_GETCURSEL, 0, 0), 0);