Updated kernel file nt functions to support MUs + fixed bugs in xapi functions and input gui related to MUs

This commit is contained in:
ergo720 2021-06-22 00:38:59 +02:00
parent 88a786b700
commit 3e60c31d26
19 changed files with 551 additions and 232 deletions

@ -1 +1 @@
Subproject commit 9a411fea58d97f4abe49e08893c57d6d9ff7f666
Subproject commit 8b000c0ca7f20d88dddd95e80ad257ba2a0cffaa

View File

@ -144,18 +144,23 @@ protected:
public:
// retrieves the map of input bindings
const std::map<int, IoControl*> GetBindings() const {
const std::map<int, IoControl*> GetBindings(const std::string &Port) const {
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<Input*> m_Inputs;
@ -163,8 +168,8 @@ private:
std::vector<Output*> m_Outputs;
// xbox port(s) this device is attached to
std::vector<std::string> m_XboxPort;
// button bindings to the xbox device buttons
std::map<int, IoControl*> m_Bindings;
// per xbox port button bindings to the xbox device buttons
std::unordered_map<std::string, std::map<int, IoControl*>> m_Bindings;
};
#endif

View File

@ -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<bool(const InputDevice*)> 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<InputDevice::IoControl *> 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<InputDevice>& Device, void* Buffer, int Direction)
bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, const std::string &Port1)
{
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings();
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings(Port1);
assert(bindings.size() == static_cast<size_t>(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<InputDevice>& Device, v
return true;
}
bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port)
bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port, const std::string &Port1)
{
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings();
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings(Port1);
assert(bindings.size() == static_cast<size_t>(dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER)]));
// NOTE: the output state is not supported

View File

@ -165,9 +165,9 @@ public:
private:
// update input for an xbox controller
bool UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction);
bool UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, const std::string &Port1);
// update input for a Steel Battalion controller
bool UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port);
bool UpdateInputSBC(std::shared_ptr<InputDevice>& 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

View File

@ -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<LPARAM>(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()

View File

@ -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();

View File

@ -163,20 +163,19 @@ namespace Sdl
std::string port = std::to_string(*static_cast<int *>(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<int *>(Event.user.data1);
Event.user.data1 = nullptr;
}
else if (Event.type == DeviceRemoveAck_t) {
g_InputDeviceManager.UpdateDevices(std::string(static_cast<char *>(Event.user.data1)), true);
delete Event.user.data1;
g_InputDeviceManager.UpdateDevices(*static_cast<std::string *>(Event.user.data1), true);
delete static_cast<std::string *>(Event.user.data1);
Event.user.data1 = nullptr;
}
}

View File

@ -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;

View File

@ -352,7 +352,7 @@ g_EmuCDPD;
XB_TRAMPOLINES(XB_trampoline_declare);
void LookupTrampolines()
void LookupTrampolinesD3D()
{
XB_TRAMPOLINES(XB_trampoline_lookup);
}

View File

@ -37,7 +37,8 @@
#define DIRECTDRAW_VERSION 0x0700
#include <ddraw.h>
extern void LookupTrampolines();
extern void LookupTrampolinesD3D();
extern void LookupTrampolinesXAPI();
// initialize render window
extern void CxbxInitWindow(bool bFullInit);

View File

@ -365,6 +365,7 @@ std::map<const std::string, const xbox_patch_t> 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)

View File

@ -43,8 +43,6 @@
#include "Windef.h"
#include <vector>
#include "core\hle\XAPI\Xapi.h"
#include "distorm.h"
#include "mnemonics.h"
#include <charconv>
@ -63,9 +61,26 @@ std::atomic<bool> 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<uint8_t *>(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<xbox::PXPP_DEVICE_TYPE>(*reinterpret_cast<uint32_t *>((it->addr + start + 1)));
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT found at 0x%08X", reinterpret_cast<uintptr_t>(g_DeviceType_MU));
}
if (xbox::addr_xt mu_xpp_type = g_SymbolAddresses["g_DeviceType_MU"]) {
g_DeviceType_MU = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(mu_xpp_type);
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT found at 0x%08X", reinterpret_cast<uintptr_t>(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<Xbe::LibraryVersion *>(CxbxKrnl_Xbe->m_Header.dwLibraryVersionsAddr);
if (pLibraryVersion != nullptr) {
if (uint8_t *start = reinterpret_cast<uint8_t *>(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<xbox::ulong_xt *>(*reinterpret_cast<uint32_t *>((g_SymbolAddresses["XUnmountMU"] + offset + 2)));
EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs found at 0x%08X", reinterpret_cast<uintptr_t>(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<xbox::ulong_xt *>(xapi_mounted_mu);
EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs found at 0x%08X", reinterpret_cast<uintptr_t>(g_XapiMountedMUs));
g_XapiAltLett_MU = reinterpret_cast<xbox::char_xt *>(g_XapiMountedMUs - 1);
EmuLog(LOG_LEVEL::INFO, "XapiAltLett_MU found at 0x%08X", reinterpret_cast<uintptr_t>(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<char *>(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<char *>(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<const char16_t *>(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<wchar_t>(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
// ******************************************************************

View File

@ -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
// ******************************************************************

View File

@ -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

View File

@ -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<pfatx_volume_metadata>(InputBuffer);
assert(InputBufferLength == sizeof(fatx_volume_metadata));
g_io_mu_metadata->read(path[pos + 7], volume->offset, static_cast<char *>(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<pfatx_volume_metadata>(InputBuffer);
assert(InputBufferLength == sizeof(fatx_volume_metadata));
g_io_mu_metadata->write(path[pos + 7], volume->offset, static_cast<const char *>(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

View File

@ -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();

View File

@ -31,13 +31,13 @@
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <cassert>
#include <Shlobj.h>
#include <Shlwapi.h>
#pragma warning(disable:4005) // Ignore redefined status values
#include <ntstatus.h>
#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<wchar_t>(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<FATX_SUPERBLOCK *>(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<std::shared_mutex> lck(m_rw_lock);
for (unsigned i = 0; i < 8; ++i) {
std::wstring path = m_root_path + static_cast<wchar_t>(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<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> 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) {

View File

@ -26,6 +26,7 @@
#include <core\kernel\exports\xboxkrnl.h>
#include "core\kernel\init\CxbxKrnl.h"
#include <vector>
#include <cstdio>
#include <string>
@ -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);

View File

@ -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<LPARAM>(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<LPARAM>(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);