Merge pull request #2246 from ergo720/mem_units
Added support for memory unit devices
This commit is contained in:
commit
cd768c6800
|
@ -1 +1 @@
|
|||
Subproject commit c2fe7365adae51ffabd9fe318ecdfaeac2cbe097
|
||||
Subproject commit 8b000c0ca7f20d88dddd95e80ad257ba2a0cffaa
|
|
@ -33,7 +33,6 @@
|
|||
#include "core\kernel\support\Emu.h"
|
||||
#include "EmuShared.h"
|
||||
#include <filesystem>
|
||||
#include "common\input\InputManager.h"
|
||||
#include "common\input\layout_xbox_device.h"
|
||||
#include <fstream>
|
||||
#include "common/util/cliConfig.hpp"
|
||||
|
@ -148,6 +147,8 @@ static struct {
|
|||
const char* type = "Type";
|
||||
const char* device = "DeviceName";
|
||||
const char* config = "ProfileName";
|
||||
const char *top_slot = "TopSlot";
|
||||
const char *bottom_slot = "BottomSlot";
|
||||
} sect_input_port;
|
||||
|
||||
static const char* section_input_profiles = "input-profile-";
|
||||
|
@ -465,15 +466,24 @@ bool Settings::LoadConfig()
|
|||
// ==== Input Port Begin ====
|
||||
|
||||
for (int port_num = 0; port_num < 4; port_num++) {
|
||||
std::string current_section = std::string(section_input_port) + std::to_string(port_num);
|
||||
int ret = m_si.GetLongValue(current_section.c_str(), sect_input_port.type, -2);
|
||||
if (ret == -2) {
|
||||
for (int slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) {
|
||||
m_input_port[port_num].Type = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
||||
continue;
|
||||
m_input_port[port_num].SlotType[slot] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
||||
|
||||
std::string current_section = std::string(section_input_port) + std::to_string(port_num);
|
||||
int ret = m_si.GetLongValue(current_section.c_str(), sect_input_port.type, -2);
|
||||
if (ret == -2) {
|
||||
continue;
|
||||
}
|
||||
m_input_port[port_num].Type = ret;
|
||||
m_input_port[port_num].DeviceName = m_si.GetValue(current_section.c_str(), sect_input_port.device);
|
||||
m_input_port[port_num].ProfileName = TrimQuoteFromString(m_si.GetValue(current_section.c_str(), sect_input_port.config));
|
||||
ret = m_si.GetLongValue(current_section.c_str(), slot == 0 ? sect_input_port.top_slot : sect_input_port.bottom_slot, -2);
|
||||
if (ret == -2) {
|
||||
continue;
|
||||
}
|
||||
m_input_port[port_num].SlotType[slot] = ret;
|
||||
}
|
||||
m_input_port[port_num].Type = ret;
|
||||
m_input_port[port_num].DeviceName = m_si.GetValue(current_section.c_str(), sect_input_port.device);
|
||||
m_input_port[port_num].ProfileName = TrimQuoteFromString(m_si.GetValue(current_section.c_str(), sect_input_port.config));
|
||||
}
|
||||
|
||||
// ==== Input Port End ==============
|
||||
|
@ -644,6 +654,8 @@ bool Settings::Save(std::string file_path)
|
|||
m_si.SetLongValue(current_section.c_str(), sect_input_port.type, m_input_port[port_num].Type, nullptr, false, true);
|
||||
m_si.SetValue(current_section.c_str(), sect_input_port.device, m_input_port[port_num].DeviceName.c_str(), nullptr, true);
|
||||
m_si.SetValue(current_section.c_str(), sect_input_port.config, quoted_prf_str.c_str(), nullptr, true);
|
||||
m_si.SetLongValue(current_section.c_str(), sect_input_port.top_slot, m_input_port[port_num].SlotType[SLOT_TOP], nullptr, false, true);
|
||||
m_si.SetLongValue(current_section.c_str(), sect_input_port.bottom_slot, m_input_port[port_num].SlotType[SLOT_BOTTOM], nullptr, false, true);
|
||||
}
|
||||
|
||||
// ==== Input Port End ==============
|
||||
|
@ -769,6 +781,8 @@ void Settings::SyncToEmulator()
|
|||
// register xbox device input settings
|
||||
for (int i = 0; i < 4; i++) {
|
||||
g_EmuShared->SetInputDevTypeSettings(&m_input_port[i].Type, i);
|
||||
g_EmuShared->SetInputSlotTypeSettings(&m_input_port[i].SlotType[SLOT_TOP], i, SLOT_TOP);
|
||||
g_EmuShared->SetInputSlotTypeSettings(&m_input_port[i].SlotType[SLOT_BOTTOM], i, SLOT_BOTTOM);
|
||||
if (m_input_port[i].Type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
|
||||
g_EmuShared->SetInputDevNameSettings(m_input_port[i].DeviceName.c_str(), i);
|
||||
auto it = std::find_if(m_input_profiles[m_input_port[i].Type].begin(),
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#include "Cxbx.h"
|
||||
|
||||
#include "SimpleIni.h"
|
||||
#include "input\InputDevice.h"
|
||||
#include "common\input\InputManager.h"
|
||||
#include "common\util\CxbxUtil.h"
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
@ -147,6 +147,7 @@ public:
|
|||
|
||||
struct s_input_port {
|
||||
int Type;
|
||||
int SlotType[XBOX_CTRL_NUM_SLOTS];
|
||||
std::string DeviceName;
|
||||
std::string ProfileName;
|
||||
};
|
||||
|
|
|
@ -33,8 +33,10 @@
|
|||
// https://github.com/dolphin-emu/dolphin
|
||||
|
||||
#include "InputDevice.h"
|
||||
#include "InputManager.h"
|
||||
#include "common\util\CxbxUtil.h"
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
|
||||
|
||||
std::string GetInputDeviceName(int dev_type)
|
||||
|
@ -90,6 +92,33 @@ std::string GetInputDeviceName(int dev_type)
|
|||
return str;
|
||||
}
|
||||
|
||||
std::string PortUserFormat(std::string_view port)
|
||||
{
|
||||
int port_num, slot;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
++port_num;
|
||||
if (slot != PORT_INVALID) {
|
||||
++slot;
|
||||
return std::to_string(port_num) + "." + std::to_string(slot);
|
||||
}
|
||||
else {
|
||||
return std::to_string(port_num);
|
||||
}
|
||||
}
|
||||
|
||||
void PortStr2Int(std::string_view port, int *port_num, int *slot)
|
||||
{
|
||||
*slot = PORT_INVALID;
|
||||
auto &ret = std::from_chars(port.data(), port.data() + port.size(), *port_num);
|
||||
assert(ret.ec != std::errc::invalid_argument);
|
||||
if (ret.ptr != port.data() + port.size()) {
|
||||
++ret.ptr;
|
||||
ret = std::from_chars(ret.ptr, port.data() + port.size(), *slot);
|
||||
assert(ret.ec != std::errc::invalid_argument);
|
||||
assert(ret.ptr == port.data() + port.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor, delete all inputs/outputs on device destruction
|
||||
InputDevice::~InputDevice()
|
||||
{
|
||||
|
@ -130,3 +159,31 @@ const std::vector<InputDevice::IoControl*> InputDevice::GetIoControls()
|
|||
});
|
||||
return vec;
|
||||
}
|
||||
|
||||
const auto InputDevice::FindPort(std::string_view Port) const
|
||||
{
|
||||
return std::find_if(m_XboxPort.begin(), m_XboxPort.end(), [Port](std::string_view CurrPort) {
|
||||
if (CurrPort == Port) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void InputDevice::SetPort(std::string_view Port, bool Connect)
|
||||
{
|
||||
if (Connect) {
|
||||
m_XboxPort.emplace_back(Port);
|
||||
}
|
||||
else {
|
||||
const auto &it = FindPort(Port);
|
||||
if (it != m_XboxPort.end()) {
|
||||
m_XboxPort.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InputDevice::GetPort(std::string_view Port) const
|
||||
{
|
||||
return FindPort(Port) != m_XboxPort.end() ? true : false;
|
||||
}
|
||||
|
|
|
@ -36,15 +36,6 @@
|
|||
#include <condition_variable>
|
||||
#include "SDL.h"
|
||||
|
||||
#define PORT_INVALID -1
|
||||
#define PORT_1 0
|
||||
#define PORT_2 1
|
||||
#define PORT_3 2
|
||||
#define PORT_4 3
|
||||
|
||||
#define PORT_INC(port) ((port) + 1)
|
||||
#define PORT_DEC(port) ((port) - 1)
|
||||
|
||||
#define DIRECTION_IN 0
|
||||
#define DIRECTION_OUT 1
|
||||
|
||||
|
@ -72,11 +63,13 @@ XBOX_INPUT_DEVICE;
|
|||
inline bool g_bIsTrackingMoLeave = false;
|
||||
inline bool g_bIsTrackingMoMove = false;
|
||||
|
||||
// Lookup array used to translate a gui port to an xbox usb port and vice versa
|
||||
extern int Gui2XboxPortArray[4];
|
||||
|
||||
// Global function used to retrieve the printable name of a xid type
|
||||
// Retrieves the printable name of a xid type
|
||||
std::string GetInputDeviceName(int dev_type);
|
||||
// Converts the port number in the user format
|
||||
std::string PortUserFormat(std::string_view);
|
||||
// Extracts port and slot number from a port formatted as a string
|
||||
void PortStr2Int(std::string_view port, int *port_num, int *slot);
|
||||
|
||||
/* Abstract class which represents a host device usable for input/output */
|
||||
class InputDevice
|
||||
|
@ -124,9 +117,9 @@ public:
|
|||
// sets the ID of this device
|
||||
void SetId(int ID) { m_ID = ID; }
|
||||
// retrieves the port this device is attached to
|
||||
bool GetPort(int Port) const { return m_XboxPort[Port]; }
|
||||
bool GetPort(std::string_view Port) const;
|
||||
// sets the port this device is attached to
|
||||
void SetPort(int Port, bool Connect) { m_XboxPort[Port] = Connect; }
|
||||
void SetPort(std::string_view Port, bool Connect);
|
||||
|
||||
|
||||
protected:
|
||||
|
@ -134,6 +127,8 @@ protected:
|
|||
void AddInput(Input* const In);
|
||||
// adds an output control to the device
|
||||
void AddOutput(Output* const Out);
|
||||
// searches for a port
|
||||
const auto FindPort(std::string_view Port) const;
|
||||
// indicates that the device has new input data available
|
||||
bool m_bDirty;
|
||||
// lock for the bindings map
|
||||
|
@ -141,27 +136,32 @@ protected:
|
|||
|
||||
public:
|
||||
// retrieves the map of input bindings
|
||||
const std::map<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;
|
||||
// all the output controls detected and usable on this device
|
||||
std::vector<Output*> m_Outputs;
|
||||
// xbox port(s) this device is attached to
|
||||
bool m_XboxPort[4] = { false, false, false, false };
|
||||
// button bindings to the xbox device buttons
|
||||
std::map<int, IoControl*> m_Bindings;
|
||||
std::vector<std::string> m_XboxPort;
|
||||
// per xbox port button bindings to the xbox device buttons
|
||||
std::unordered_map<std::string, std::map<int, IoControl*>> m_Bindings;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -47,16 +47,11 @@
|
|||
#include "EmuShared.h"
|
||||
#include "devices\usb\OHCI.h"
|
||||
#include "core/common/video/RenderBase.hpp"
|
||||
#include <charconv>
|
||||
|
||||
// hle input specific
|
||||
#include "core\hle\XAPI\Xapi.h"
|
||||
|
||||
int Gui2XboxPortArray[4] = {
|
||||
3,
|
||||
4,
|
||||
1,
|
||||
2
|
||||
};
|
||||
|
||||
int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)] = {
|
||||
XBOX_CTRL_NUM_BUTTONS, // MS_CONTROLLER_DUKE
|
||||
|
@ -69,7 +64,7 @@ int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)] = {
|
|||
XBOX_CTRL_NUM_BUTTONS, // ARCADE_STICK
|
||||
};
|
||||
|
||||
extern CXBX_CONTROLLER_HOST_BRIDGE g_XboxControllerHostBridge[4]; // hle xinput
|
||||
void UpdateXppState(DeviceState *dev, XBOX_INPUT_DEVICE type, std::string_view port);
|
||||
|
||||
|
||||
InputDeviceManager g_InputDeviceManager;
|
||||
|
@ -94,13 +89,13 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
|
|||
});
|
||||
|
||||
m_Cv.wait(lck, []() {
|
||||
return (Sdl::SdlInitStatus != Sdl::SDL_NOT_INIT) &&
|
||||
(XInput::XInputInitStatus != XInput::XINPUT_NOT_INIT) &&
|
||||
(RawInput::RawInputInitStatus != RawInput::RAWINPUT_NOT_INIT);
|
||||
return (Sdl::InitStatus != Sdl::NOT_INIT) &&
|
||||
(XInput::InitStatus != XInput::NOT_INIT) &&
|
||||
(RawInput::InitStatus != RawInput::NOT_INIT);
|
||||
});
|
||||
lck.unlock();
|
||||
|
||||
if (Sdl::SdlInitStatus < 0 || XInput::XInputInitStatus < 0 || RawInput::RawInputInitStatus < 0) {
|
||||
if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0) {
|
||||
CxbxKrnlCleanup("Failed to initialize input subsystem! Consult debug log for more information");
|
||||
}
|
||||
|
||||
|
@ -108,10 +103,43 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
|
|||
RefreshDevices();
|
||||
|
||||
if (!is_gui) {
|
||||
UpdateDevices(PORT_1, false);
|
||||
UpdateDevices(PORT_2, false);
|
||||
UpdateDevices(PORT_3, false);
|
||||
UpdateDevices(PORT_4, false);
|
||||
for (unsigned i = 0; i < MAX_DEVS; ++i) {
|
||||
g_devs[i].type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
g_devs[i].port = std::to_string(PORT_INVALID);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < XBOX_NUM_PORTS; ++i) {
|
||||
int type;
|
||||
g_EmuShared->GetInputDevTypeSettings(&type, i);
|
||||
if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
|
||||
std::string port = std::to_string(i);
|
||||
switch (type)
|
||||
{
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): {
|
||||
ConstructHleInputDevice(&g_devs[CTRL_OFFSET + i], nullptr, type, port);
|
||||
BindHostDevice(type, port);
|
||||
for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) {
|
||||
g_EmuShared->GetInputSlotTypeSettings(&type, i, slot);
|
||||
if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
|
||||
assert(type == to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT));
|
||||
ConstructHleInputDevice(&g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * i) + slot], &g_devs[CTRL_OFFSET + i], type, port + "." + std::to_string(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
|
||||
ConstructHleInputDevice(&g_devs[CTRL_OFFSET + i], nullptr, type, port);
|
||||
BindHostDevice(type, port);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RawInput::IgnoreHotplug = false;
|
||||
|
@ -188,164 +216,116 @@ void InputDeviceManager::RemoveDevice(std::function<bool(const InputDevice*)> Ca
|
|||
}
|
||||
}
|
||||
|
||||
void InputDeviceManager::UpdateDevices(int port, bool ack)
|
||||
void InputDeviceManager::UpdateDevices(std::string_view port, bool ack)
|
||||
{
|
||||
if (port > PORT_4 || port < PORT_1) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "Invalid port number. The port was %d", PORT_INC(port));
|
||||
return;
|
||||
}
|
||||
int type;
|
||||
#if 0 // lle usb
|
||||
int usb_port = PORT_DEC(Gui2XboxPortArray[port]);
|
||||
#else
|
||||
int usb_port = port;
|
||||
#endif
|
||||
g_EmuShared->GetInputDevTypeSettings(&type, port);
|
||||
DeviceState *dev, *upstream;
|
||||
int port_num, slot, type;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
dev = &g_devs[port_num];
|
||||
|
||||
if (slot == PORT_INVALID) { // Port references a device attached to an xbox port
|
||||
upstream = nullptr;
|
||||
g_EmuShared->GetInputDevTypeSettings(&type, port_num);
|
||||
}
|
||||
else { // Port references a device attached to a slot port
|
||||
upstream = dev;
|
||||
dev = dev->slots[slot];
|
||||
g_EmuShared->GetInputSlotTypeSettings(&type, port_num, slot);
|
||||
}
|
||||
|
||||
// updating a slot
|
||||
if (dev == nullptr) {
|
||||
// connect slot
|
||||
if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) &&
|
||||
g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port_num) + slot].type == XBOX_INPUT_DEVICE::DEVICE_INVALID) {
|
||||
ConnectDevice(&g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port_num) + slot], upstream, type, port);
|
||||
}
|
||||
// disconnect slot
|
||||
else if (type == to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) &&
|
||||
g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port_num) + slot].type != XBOX_INPUT_DEVICE::DEVICE_INVALID) {
|
||||
DisconnectDevice(&g_devs[MU_OFFSET + (XBOX_CTRL_NUM_SLOTS * port_num) + slot], port, ack);
|
||||
}
|
||||
// update bindings slot
|
||||
else {
|
||||
// MUs don't have any host devices attached, so this is a nop for now
|
||||
}
|
||||
}
|
||||
// connect
|
||||
if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) &&
|
||||
//g_XidDeviceObjArray[usb_port].xid_dev == nullptr) { lle usb
|
||||
g_XboxControllerHostBridge[usb_port].XboxType == XBOX_INPUT_DEVICE::DEVICE_INVALID) {
|
||||
ConnectDevice(port, usb_port, type);
|
||||
else if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) &&
|
||||
dev->type == XBOX_INPUT_DEVICE::DEVICE_INVALID) {
|
||||
ConnectDevice(dev, upstream, type, port);
|
||||
}
|
||||
// disconnect
|
||||
else if (type == to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) &&
|
||||
//g_XidDeviceObjArray[usb_port].xid_dev != nullptr) { lle usb
|
||||
g_XboxControllerHostBridge[usb_port].XboxType != XBOX_INPUT_DEVICE::DEVICE_INVALID) {
|
||||
DisconnectDevice(port, usb_port, ack);
|
||||
dev->type != XBOX_INPUT_DEVICE::DEVICE_INVALID) {
|
||||
// We don't need to check if we need to destroy child devices because the UpdateInputEvent_t message always
|
||||
// calls us on the entire slot connectivity of a port
|
||||
DisconnectDevice(dev, port, ack);
|
||||
}
|
||||
// update bindings
|
||||
else {
|
||||
auto dev = g_InputDeviceManager.FindDevice(usb_port, 0);
|
||||
if (dev != nullptr) {
|
||||
dev->SetPort(usb_port, false);
|
||||
auto host_dev = g_InputDeviceManager.FindDevice(port);
|
||||
if (host_dev != nullptr) {
|
||||
host_dev->SetPort(port, false);
|
||||
}
|
||||
if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
|
||||
if (type != to_underlying(g_XboxControllerHostBridge[port].XboxType)) {
|
||||
// this will happen when the user changes the type of an existing xbox device type connected to a port
|
||||
if (g_XboxControllerHostBridge[port].bPendingRemoval == false) {
|
||||
g_XboxControllerHostBridge[port].bPendingRemoval = true;
|
||||
if (type != to_underlying(dev->type)) {
|
||||
// This will happen when the user changes the type of an existing xbox device type connected to a port.
|
||||
// We don't need to check of we need to destroy child devices because the UpdateInputEvent_t message always
|
||||
// calls us on the entire slot connectivity if the device has slots available
|
||||
if (dev->bPendingRemoval == false) {
|
||||
dev->bPendingRemoval = true;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
DestructHleInputDevice(port);
|
||||
if (!ConstructHleInputDevice(type, port)) {
|
||||
return;
|
||||
}
|
||||
DestructHleInputDevice(dev);
|
||||
ConstructHleInputDevice(dev, upstream, type, port);
|
||||
}
|
||||
}
|
||||
BindHostDevice(port, usb_port, type);
|
||||
BindHostDevice(type, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputDeviceManager::ConnectDevice(int port, int usb_port, int type)
|
||||
void InputDeviceManager::ConnectDevice(DeviceState *dev, DeviceState *upstream, int type, std::string_view port)
|
||||
{
|
||||
#if 0 // lle usb
|
||||
switch (type)
|
||||
{
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): {
|
||||
if (ConstructHub(usb_port)) {
|
||||
if (!ConstructXpadDuke(usb_port)) {
|
||||
DestructHub(usb_port);
|
||||
return;
|
||||
}
|
||||
g_XidDeviceObjArray[usb_port].xid_type = type;
|
||||
EmuLog(LOG_LEVEL::INFO, "Attached device %s to port %d", GetInputDeviceName(type).c_str(), PORT_INC(port));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): {
|
||||
EmuLog(LOG_LEVEL::INFO, "%s: device %s is not yet supported", __func__, GetInputDeviceName(type).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
EmuLog(LOG_LEVEL::WARNING, "Attempted to attach an unknown device type (type was %d)", type);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (!ConstructHleInputDevice(type, port)) {
|
||||
return;
|
||||
}
|
||||
EmuLog(LOG_LEVEL::INFO, "Attached device %s to port %d", GetInputDeviceName(type).c_str(), PORT_INC(port));
|
||||
#endif
|
||||
BindHostDevice(port, usb_port, type);
|
||||
ConstructHleInputDevice(dev, upstream, type, port);
|
||||
BindHostDevice(type, port);
|
||||
}
|
||||
|
||||
void InputDeviceManager::DisconnectDevice(int port, int usb_port, bool ack)
|
||||
void InputDeviceManager::DisconnectDevice(DeviceState *dev, std::string_view port, bool ack)
|
||||
{
|
||||
#if 0 // lle usb
|
||||
if (g_XidDeviceObjArray[usb_port].xid_dev != nullptr) {
|
||||
int type = g_XidDeviceObjArray[usb_port].xid_type;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): {
|
||||
if (ack) {
|
||||
assert(g_HubObjArray[usb_port] != nullptr);
|
||||
DestructHub(usb_port);
|
||||
DestructXpadDuke(usb_port);
|
||||
g_HostController->OHCI_SetRemovalFlag(usb_port, false);
|
||||
EmuLog(LOG_LEVEL::INFO, "Detached device %s from port %d", GetInputDeviceName(type).c_str(), PORT_INC(port));
|
||||
}
|
||||
else {
|
||||
g_HostController->OHCI_SetRemovalFlag(usb_port, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): {
|
||||
EmuLog(LOG_LEVEL::INFO, "%s: device %s is not yet supported", __func__, GetInputDeviceName(type).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
EmuLog(LOG_LEVEL::WARNING, "Attempted to detach an unknown device type (type was %d)", type);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (g_XboxControllerHostBridge[port].XboxType != XBOX_INPUT_DEVICE::DEVICE_INVALID) {
|
||||
if (ack) {
|
||||
int type = to_underlying(g_XboxControllerHostBridge[port].XboxType);
|
||||
DestructHleInputDevice(port);
|
||||
EmuLog(LOG_LEVEL::INFO, "Detached device %s from port %d", GetInputDeviceName(type).c_str(), PORT_INC(port));
|
||||
}
|
||||
else {
|
||||
g_XboxControllerHostBridge[port].bPendingRemoval = true;
|
||||
}
|
||||
auto dev = g_InputDeviceManager.FindDevice(usb_port, 0);
|
||||
if (dev != nullptr) {
|
||||
dev->SetPort(usb_port, false);
|
||||
}
|
||||
if (ack) {
|
||||
DestructHleInputDevice(dev);
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::WARNING, "Attempted to detach a device not attached to the emulated machine");
|
||||
dev->bPendingRemoval = true;
|
||||
}
|
||||
auto host_dev = g_InputDeviceManager.FindDevice(port);
|
||||
if (host_dev != nullptr) {
|
||||
host_dev->SetPort(port, false);
|
||||
}
|
||||
}
|
||||
|
||||
void InputDeviceManager::BindHostDevice(int port, int usb_port, int type)
|
||||
void InputDeviceManager::BindHostDevice(int type, std::string_view port)
|
||||
{
|
||||
if (type == to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT)) {
|
||||
// MUs don't have any host devices bound, so we just return
|
||||
return;
|
||||
}
|
||||
|
||||
char dev_name[50];
|
||||
char dev_control_names[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
||||
|
||||
g_EmuShared->GetInputDevNameSettings(dev_name, port);
|
||||
g_EmuShared->GetInputBindingsSettings(dev_control_names, dev_num_buttons[type], port);
|
||||
int port_num, slot;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
g_EmuShared->GetInputDevNameSettings(dev_name, port_num);
|
||||
g_EmuShared->GetInputBindingsSettings(dev_control_names, dev_num_buttons[type], port_num);
|
||||
|
||||
auto dev = FindDevice(std::string(dev_name));
|
||||
if (dev != nullptr) {
|
||||
std::vector<InputDevice::IoControl*> controls = dev->GetIoControls();
|
||||
std::string port_str(port);
|
||||
dev->ClearBindings(port_str);
|
||||
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]);
|
||||
auto it = std::find_if(controls.begin(), controls.end(), [&dev_button](const auto control) {
|
||||
|
@ -354,62 +334,62 @@ void InputDeviceManager::BindHostDevice(int port, int usb_port, int type)
|
|||
}
|
||||
return false;
|
||||
});
|
||||
dev->SetBindings(index, (it != controls.end()) ? *it : nullptr);
|
||||
dev->SetBindings(index, (it != controls.end()) ? *it : nullptr, port_str);
|
||||
}
|
||||
dev->SetPort(usb_port, true);
|
||||
dev->SetPort(port, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool InputDeviceManager::UpdateXboxPortInput(int usb_port, void* Buffer, int Direction, int xid_type)
|
||||
bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int direction, int type)
|
||||
{
|
||||
assert(Direction == DIRECTION_IN || Direction == DIRECTION_OUT);
|
||||
assert(xid_type > to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) &&
|
||||
xid_type < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX));
|
||||
assert(direction == DIRECTION_IN || direction == DIRECTION_OUT);
|
||||
assert(type > to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) &&
|
||||
type < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX));
|
||||
bool has_changed = false;
|
||||
|
||||
// First check if ImGui is focus, then ignore any input update occur.
|
||||
// If somebody else is currently holding the lock, we won't wait and instead report no input changes
|
||||
if (!g_renderbase->IsImGuiFocus() && m_Mtx.try_lock()) {
|
||||
for (auto &dev_ptr : m_Devices) {
|
||||
if (dev_ptr->GetPort(usb_port)) {
|
||||
switch (xid_type)
|
||||
for (auto &dev : m_Devices) {
|
||||
std::string port_str = std::to_string(port);
|
||||
if (dev->GetPort(port_str)) {
|
||||
switch (type)
|
||||
{
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK): {
|
||||
has_changed = UpdateInputXpad(dev_ptr, Buffer, Direction);
|
||||
}
|
||||
break;
|
||||
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK):
|
||||
has_changed = UpdateInputXpad(dev, buffer, direction, port_str);
|
||||
m_Mtx.unlock();
|
||||
return has_changed;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): {
|
||||
has_changed = UpdateInputSBC(dev_ptr, Buffer, Direction, usb_port);
|
||||
}
|
||||
break;
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
|
||||
has_changed = UpdateInputSBC(dev, buffer, direction, port, port_str);
|
||||
m_Mtx.unlock();
|
||||
return has_changed;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT):
|
||||
assert(0);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE): {
|
||||
EmuLog(LOG_LEVEL::WARNING, "An unsupported device is attached at port %d! The device was %s",
|
||||
Gui2XboxPortArray[usb_port], GetInputDeviceName(xid_type).c_str());
|
||||
}
|
||||
break;
|
||||
case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE):
|
||||
EmuLog(LOG_LEVEL::ERROR2, "An unsupported device is attached at port %d! The device was %s",
|
||||
PortUserFormat(port_str).c_str(), GetInputDeviceName(type).c_str());
|
||||
break;
|
||||
|
||||
default: {
|
||||
EmuLog(LOG_LEVEL::WARNING, "An unknown device attached at port %d! The type was %s",
|
||||
Gui2XboxPortArray[usb_port], GetInputDeviceName(xid_type).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Mtx.unlock();
|
||||
}
|
||||
|
||||
return has_changed;
|
||||
}
|
||||
|
||||
bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction)
|
||||
bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, const std::string &Port)
|
||||
{
|
||||
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings();
|
||||
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings(Port);
|
||||
assert(bindings.size() == static_cast<size_t>(dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE)]));
|
||||
|
||||
if (Direction == DIRECTION_IN) {
|
||||
|
@ -471,9 +451,9 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<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_num, const std::string &Port)
|
||||
{
|
||||
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings();
|
||||
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings(Port);
|
||||
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
|
||||
|
@ -494,7 +474,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
// 8 -> TunerDial Down
|
||||
// 9 -> GearLever Up
|
||||
// 10 -> GearLever Down
|
||||
static uint16_t last_in_state[4] = { 0, 0, 0, 0 };
|
||||
static uint16_t last_in_state[XBOX_NUM_PORTS] = { 0, 0, 0, 0 };
|
||||
SBCInput *in_buf = static_cast<SBCInput *>(Buffer);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
|
@ -510,10 +490,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
for (int i = 4, j = 0; i < 6; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
|
||||
if ((~curr_in_state) & ((last_in_state[Port] >> j) & 1)) {
|
||||
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
|
||||
in_buf->wButtons[0] ^= (1 << i);
|
||||
}
|
||||
(last_in_state[Port] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
}
|
||||
|
||||
for (int i = 6; i < 34; i++) {
|
||||
|
@ -530,10 +510,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
for (int i = 34, j = 2; i < 39; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
|
||||
if ((~curr_in_state) & ((last_in_state[Port] >> j) & 1)) {
|
||||
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
|
||||
in_buf->wButtons[2] ^= (1 << (i % 16));
|
||||
}
|
||||
(last_in_state[Port] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
}
|
||||
|
||||
for (int i = 39; i < 49; i += 2) {
|
||||
|
@ -592,7 +572,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
for (int i = 52, j = 7; i < 56; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
|
||||
if ((~curr_in_state) & ((last_in_state[Port] >> j) & 1)) {
|
||||
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
|
||||
switch (i)
|
||||
{
|
||||
case 52:
|
||||
|
@ -620,7 +600,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
break;
|
||||
}
|
||||
}
|
||||
(last_in_state[Port] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -630,7 +610,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
void InputDeviceManager::RefreshDevices()
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(m_Mtx);
|
||||
Sdl::SdlPopulateOK = false;
|
||||
Sdl::PopulateOK = false;
|
||||
m_Devices.clear();
|
||||
lck.unlock();
|
||||
XInput::PopulateDevices();
|
||||
|
@ -638,7 +618,7 @@ void InputDeviceManager::RefreshDevices()
|
|||
Sdl::PopulateDevices();
|
||||
lck.lock();
|
||||
m_Cv.wait(lck, []() {
|
||||
return Sdl::SdlPopulateOK;
|
||||
return Sdl::PopulateOK;
|
||||
});
|
||||
for (auto &dev : m_Devices) {
|
||||
if (StrStartsWith(dev->GetDeviceName(), "KeyboardMouse")) {
|
||||
|
@ -690,18 +670,12 @@ std::shared_ptr<InputDevice> InputDeviceManager::FindDevice(SDL_JoystickID id) c
|
|||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<InputDevice> InputDeviceManager::FindDevice(int usb_port, int dummy) const
|
||||
std::shared_ptr<InputDevice> InputDeviceManager::FindDevice(std::string_view port) const
|
||||
{
|
||||
// Ignore dummy, it's just used to overload the function
|
||||
|
||||
if (usb_port == PORT_INVALID) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lck(m_Mtx);
|
||||
|
||||
auto it = std::find_if(m_Devices.begin(), m_Devices.end(), [usb_port](const auto& Device) {
|
||||
return Device->GetPort(usb_port);
|
||||
auto it = std::find_if(m_Devices.begin(), m_Devices.end(), [port](const auto& Device) {
|
||||
return Device->GetPort(port);
|
||||
});
|
||||
if (it != m_Devices.end()) {
|
||||
return *it;
|
||||
|
@ -762,7 +736,7 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
|
|||
int type;
|
||||
g_EmuShared->GetInputDevTypeSettings(&type, port);
|
||||
if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
|
||||
BindHostDevice(port, port, type);
|
||||
BindHostDevice(type, std::to_string(port));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,21 @@
|
|||
#undef SetPort
|
||||
#endif
|
||||
|
||||
#define PORT_INVALID -1
|
||||
#define PORT_1 0
|
||||
#define PORT_2 1
|
||||
#define PORT_3 2
|
||||
#define PORT_4 3
|
||||
#define XBOX_NUM_PORTS 4
|
||||
|
||||
#define SLOT_TOP 0
|
||||
#define SLOT_BOTTOM 1
|
||||
#define XBOX_CTRL_NUM_SLOTS 2
|
||||
|
||||
#define CTRL_OFFSET 0
|
||||
#define MU_OFFSET 4
|
||||
#define MAX_DEVS (XBOX_NUM_PORTS + XBOX_CTRL_NUM_SLOTS * XBOX_NUM_PORTS)
|
||||
|
||||
extern int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)];
|
||||
|
||||
inline XBOX_INPUT_DEVICE input_support_list[] = {
|
||||
|
@ -47,6 +62,13 @@ inline XBOX_INPUT_DEVICE input_support_list[] = {
|
|||
XBOX_INPUT_DEVICE::ARCADE_STICK,
|
||||
};
|
||||
|
||||
inline XBOX_INPUT_DEVICE slot_support_list[] = {
|
||||
XBOX_INPUT_DEVICE::DEVICE_INVALID,
|
||||
XBOX_INPUT_DEVICE::MEMORY_UNIT,
|
||||
// Microphone
|
||||
// Phantasy star online keyboard
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
// xpad in/out buffers stripped of the first two bytes
|
||||
|
@ -92,36 +114,36 @@ struct SBCOutput {
|
|||
|
||||
#pragma pack()
|
||||
|
||||
|
||||
// hle specific input types
|
||||
typedef struct _CXBX_XINPUT_DEVICE_INFO {
|
||||
uint8_t ucType; // xbox controller type
|
||||
uint8_t ucSubType; // xbox controller subtype
|
||||
uint8_t ucInputStateSize; // xbox controller input state size in bytes, not include dwPacketNumber
|
||||
uint8_t ucFeedbackSize; // xbox controller feedback size in bytes, not include FeedbackHeader
|
||||
uint32_t dwPacketNumber;
|
||||
}
|
||||
CXBX_XINPUT_DEVICE_INFO, *PCXBX_XINPUT_DEVICE_INFO;
|
||||
|
||||
union CXBX_XINPUT_IN_STATE {
|
||||
XpadInput Gamepad;
|
||||
SBCInput SBC;
|
||||
union InputBuff {
|
||||
XpadInput ctrl;
|
||||
SBCInput sbc;
|
||||
};
|
||||
|
||||
// this structure is for use of tracking the xbox controllers assigned to 4 ports.
|
||||
typedef struct _CXBX_CONTROLLER_HOST_BRIDGE {
|
||||
HANDLE hXboxDevice; // xbox device handle to this device, we use the address of this bridge as the handle, only set after opened. cleared after closed.
|
||||
int XboxPort; // xbox port# for this xbox controller
|
||||
XBOX_INPUT_DEVICE XboxType; // xbox device type
|
||||
CXBX_XINPUT_IN_STATE *InState;
|
||||
bool bPendingRemoval;
|
||||
bool bSignaled;
|
||||
bool bIoInProgress;
|
||||
bool bAutoPoll; // autopoll on/off, as instructed by the title in XInputOpen
|
||||
bool bAutoPollDefault; // default autopoll value, depending on device type
|
||||
CXBX_XINPUT_DEVICE_INFO XboxDeviceInfo;
|
||||
}
|
||||
CXBX_CONTROLLER_HOST_BRIDGE, *PCXBX_CONTROLLER_HOST_BRIDGE;
|
||||
struct DeviceInfo {
|
||||
xbox::HANDLE hHandle; // device handle returned by xapi
|
||||
bool bAutoPoll; // autopoll on/off, as instructed by the title in XInputOpen
|
||||
bool bAutoPollDefault; // default autopoll value, depending on device type
|
||||
uint8_t ucType; // xapi type
|
||||
uint8_t ucSubType; // xapi subtype
|
||||
uint8_t ucInputStateSize; // input state size in bytes, does not include dwPacketNumber
|
||||
uint8_t ucFeedbackSize; // feedback size in bytes, does not include FeedbackHeader
|
||||
uint32_t dwPacketNumber;
|
||||
InputBuff buff;
|
||||
};
|
||||
|
||||
struct DeviceState {
|
||||
std::string port;
|
||||
int port_idx;
|
||||
XBOX_INPUT_DEVICE type;
|
||||
bool bPendingRemoval;
|
||||
bool bSignaled;
|
||||
DeviceInfo info;
|
||||
DeviceState *slots[XBOX_CTRL_NUM_SLOTS];
|
||||
DeviceState *upstream;
|
||||
};
|
||||
|
||||
extern DeviceState g_devs[MAX_DEVS];
|
||||
|
||||
|
||||
class InputDeviceManager
|
||||
{
|
||||
|
@ -129,7 +151,7 @@ public:
|
|||
void Initialize(bool is_gui, HWND hwnd);
|
||||
void Shutdown();
|
||||
// read/write the input/output from/to the device attached to the supplied xbox port
|
||||
bool UpdateXboxPortInput(int usb_port, void* Buffer, int Direction, int xid_type);
|
||||
bool UpdateXboxPortInput(int port, void *buffer, int direction, int type);
|
||||
// add the device to the list of availble devices
|
||||
void AddDevice(std::shared_ptr<InputDevice> Device);
|
||||
// remove the device from the list of availble devices
|
||||
|
@ -143,9 +165,9 @@ public:
|
|||
// find device from its sdl id
|
||||
std::shared_ptr<InputDevice> FindDevice(SDL_JoystickID id) const;
|
||||
// find device from its xbox port
|
||||
std::shared_ptr<InputDevice> FindDevice(int usb_port, int dummy) const;
|
||||
std::shared_ptr<InputDevice> FindDevice(std::string_view port) const;
|
||||
// attach/detach guest devices to the emulated machine
|
||||
void UpdateDevices(int port, bool ack);
|
||||
void UpdateDevices(std::string_view port, bool ack);
|
||||
// update input options
|
||||
void UpdateOpt(bool is_gui);
|
||||
// device hotplug event handler
|
||||
|
@ -154,15 +176,15 @@ public:
|
|||
|
||||
private:
|
||||
// update input for an xbox controller
|
||||
bool UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction);
|
||||
bool UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, const std::string &Port);
|
||||
// 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_num, const std::string &Port);
|
||||
// bind a host device to an emulated device
|
||||
void BindHostDevice(int port, int usb_port, int type);
|
||||
void BindHostDevice(int type, std::string_view port);
|
||||
// connect a device to the emulated machine
|
||||
void ConnectDevice(int port, int usb_port, int type);
|
||||
void ConnectDevice(DeviceState *dev, DeviceState *upstream, int type, std::string_view port);
|
||||
// disconnect a device from the emulated machine
|
||||
void DisconnectDevice(int port, int usb_port, bool ack);
|
||||
void DisconnectDevice(DeviceState *dev, std::string_view port, bool ack);
|
||||
|
||||
// all enumerated devices currently detected and supported
|
||||
std::vector<std::shared_ptr<InputDevice>> m_Devices;
|
||||
|
@ -181,7 +203,7 @@ private:
|
|||
extern InputDeviceManager g_InputDeviceManager;
|
||||
|
||||
// hle input functions
|
||||
bool ConstructHleInputDevice(int Type, int Port);
|
||||
void DestructHleInputDevice(int Port);
|
||||
void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type, std::string_view port);
|
||||
void DestructHleInputDevice(DeviceState *dev);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -69,6 +69,7 @@ bool InputWindow::IsProfileSaved()
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -177,6 +178,29 @@ void InputWindow::BindButton(int ControlID)
|
|||
}
|
||||
}
|
||||
|
||||
void InputWindow::UpdateProfile(const std::string &name, int command)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case PROFILE_LOAD:
|
||||
LoadProfile(name);
|
||||
break;
|
||||
|
||||
case PROFILE_SAVE:
|
||||
SaveProfile(name);
|
||||
break;
|
||||
|
||||
case PROFILE_DELETE:
|
||||
DeleteProfile(name);
|
||||
break;
|
||||
|
||||
case BUTTON_CLEAR:
|
||||
case BUTTON_SWAP:
|
||||
m_bHasChanges = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
InputWindow::ProfileIt InputWindow::FindProfile(const std::string& name)
|
||||
{
|
||||
auto it = std::find_if(g_Settings->m_input_profiles[m_dev_type].begin(),
|
||||
|
@ -236,6 +260,7 @@ bool InputWindow::SaveProfile(const std::string& name)
|
|||
g_Settings->m_input_port[m_port_num].DeviceName = profile.DeviceName;
|
||||
g_Settings->m_input_port[m_port_num].ProfileName = profile.ProfileName;
|
||||
g_Settings->m_input_profiles[m_dev_type].push_back(std::move(profile));
|
||||
|
||||
m_bHasChanges = false;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ public:
|
|||
void UpdateDeviceList();
|
||||
void BindButton(int ControlID);
|
||||
virtual void ClearBindings() = 0;
|
||||
virtual void UpdateProfile(const std::string& name, int command) = 0;
|
||||
virtual void UpdateProfile(const std::string& name, int command);
|
||||
void UpdateCurrentDevice();
|
||||
bool IsProfileSaved();
|
||||
void SwapMoCursorAxis(Button *button);
|
||||
|
@ -74,6 +74,7 @@ protected:
|
|||
void OverwriteProfile(const std::string& name);
|
||||
void LoadDefaultProfile();
|
||||
virtual int EnableDefaultButton() = 0;
|
||||
virtual void SaveSlotConfig() = 0;
|
||||
|
||||
// xbox device under configuration
|
||||
EmuDevice* m_DeviceConfig;
|
||||
|
@ -106,6 +107,7 @@ public:
|
|||
void BindDefault();
|
||||
void ClearBindings() override;
|
||||
void UpdateProfile(const std::string &name, int command) override;
|
||||
void SaveSlotConfig() override;
|
||||
|
||||
|
||||
private:
|
||||
|
@ -118,6 +120,8 @@ private:
|
|||
HWND m_hwnd_rumble;
|
||||
// handle of the rumble combobox
|
||||
HWND m_hwnd_rumble_list;
|
||||
// handles of the slot combobox
|
||||
HWND m_hwnd_slot_list[XBOX_CTRL_NUM_SLOTS];
|
||||
// currently selected rumble control
|
||||
std::string m_rumble;
|
||||
};
|
||||
|
@ -127,7 +131,7 @@ class SbcInputWindow : public InputWindow
|
|||
public:
|
||||
void Initialize(HWND hwnd, int port_num, int dev_type) override;
|
||||
void ClearBindings() override;
|
||||
void UpdateProfile(const std::string &name, int command) override;
|
||||
void SaveSlotConfig() override;
|
||||
|
||||
|
||||
private:
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
namespace RawInput
|
||||
{
|
||||
int RawInputInitStatus = RAWINPUT_NOT_INIT;
|
||||
int InitStatus = NOT_INIT;
|
||||
bool IgnoreHotplug = true;
|
||||
|
||||
void Init(std::mutex &Mtx, bool is_gui, HWND hwnd)
|
||||
|
@ -44,7 +44,7 @@ namespace RawInput
|
|||
|
||||
if (is_gui) {
|
||||
// We don't need to monitor xinput device changes from the gui, because there we have the refresh button to detect changes
|
||||
RawInputInitStatus = RAWINPUT_INIT_SUCCESS;
|
||||
InitStatus = INIT_SUCCESS;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -69,16 +69,16 @@ namespace RawInput
|
|||
static_cast<UINT>(sizeof(decltype(devices)::value_type))))
|
||||
{
|
||||
EmuLog(LOG_LEVEL::ERROR2, "RegisterRawInputDevices failed: %i", GetLastError());
|
||||
RawInputInitStatus = RAWINPUT_INIT_ERROR;
|
||||
InitStatus = INIT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
RawInputInitStatus = RAWINPUT_INIT_SUCCESS;
|
||||
InitStatus = INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
RawInputInitStatus = RAWINPUT_NOT_INIT;
|
||||
InitStatus = NOT_INIT;
|
||||
IgnoreHotplug = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,15 +33,15 @@
|
|||
|
||||
namespace RawInput
|
||||
{
|
||||
typedef enum _RAWINPUT_INIT_STATUS : int
|
||||
typedef enum _INIT_STATUS : int
|
||||
{
|
||||
RAWINPUT_NOT_INIT = -2,
|
||||
RAWINPUT_INIT_ERROR,
|
||||
RAWINPUT_INIT_SUCCESS,
|
||||
NOT_INIT = -2,
|
||||
INIT_ERROR,
|
||||
INIT_SUCCESS,
|
||||
}
|
||||
RAWINPUT_INIT_STATUS;
|
||||
INIT_STATUS;
|
||||
|
||||
extern int RawInputInitStatus;
|
||||
extern int InitStatus;
|
||||
extern bool IgnoreHotplug;
|
||||
|
||||
// initialize RawInput
|
||||
|
|
|
@ -54,8 +54,8 @@ namespace Sdl
|
|||
uint32_t PopulateEvent_t;
|
||||
uint32_t UpdateInputEvent_t;
|
||||
uint32_t DeviceRemoveAck_t;
|
||||
int SdlInitStatus = SDL_NOT_INIT;
|
||||
bool SdlPopulateOK = false;
|
||||
int InitStatus = NOT_INIT;
|
||||
bool PopulateOK = false;
|
||||
|
||||
void Init(std::mutex& Mtx, std::condition_variable& Cv, bool is_gui)
|
||||
{
|
||||
|
@ -65,7 +65,7 @@ namespace Sdl
|
|||
|
||||
if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) < 0) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Failed to initialize SDL subsystem! The error was: %s", SDL_GetError());
|
||||
SdlInitStatus = SDL_INIT_ERROR;
|
||||
InitStatus = INIT_ERROR;
|
||||
lck.unlock();
|
||||
Cv.notify_one();
|
||||
return;
|
||||
|
@ -74,7 +74,7 @@ namespace Sdl
|
|||
if (CustomEvent_t == (uint32_t)-1) {
|
||||
SDL_Quit();
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Failed to create SDL custom events!");
|
||||
SdlInitStatus = SDL_INIT_ERROR;
|
||||
InitStatus = INIT_ERROR;
|
||||
lck.unlock();
|
||||
Cv.notify_one();
|
||||
return;
|
||||
|
@ -96,7 +96,7 @@ namespace Sdl
|
|||
break;
|
||||
}
|
||||
}
|
||||
SdlInitStatus = SDL_INIT_SUCCESS;
|
||||
InitStatus = INIT_SUCCESS;
|
||||
lck.unlock();
|
||||
Cv.notify_one();
|
||||
|
||||
|
@ -133,10 +133,6 @@ namespace Sdl
|
|||
case SDL_JOYBUTTONUP:
|
||||
id = Event.jbutton.which;
|
||||
break;
|
||||
|
||||
default: {
|
||||
// unreachable
|
||||
}
|
||||
}
|
||||
auto dev = g_InputDeviceManager.FindDevice(id);
|
||||
if (dev != nullptr) {
|
||||
|
@ -147,7 +143,7 @@ namespace Sdl
|
|||
for (int i = 0; i < SDL_NumJoysticks(); i++) {
|
||||
OpenSdlDevice(i);
|
||||
}
|
||||
SdlPopulateOK = true;
|
||||
PopulateOK = true;
|
||||
Cv.notify_one();
|
||||
}
|
||||
else if (Event.type == ExitEvent_t) {
|
||||
|
@ -160,15 +156,22 @@ namespace Sdl
|
|||
else {
|
||||
XInput::GetDeviceChanges();
|
||||
DInput::GetDeviceChanges();
|
||||
g_InputDeviceManager.UpdateDevices(*static_cast<int *>(Event.user.data1), false);
|
||||
std::string port = std::to_string(*static_cast<int *>(Event.user.data1));
|
||||
int port_num, slot;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
|
||||
g_InputDeviceManager.UpdateDevices(port, false);
|
||||
// Force an update of the entire slot connectivity of this port
|
||||
g_InputDeviceManager.UpdateDevices(port + ".0", false);
|
||||
g_InputDeviceManager.UpdateDevices(port + ".1", false);
|
||||
}
|
||||
|
||||
delete Event.user.data1;
|
||||
delete static_cast<int *>(Event.user.data1);
|
||||
Event.user.data1 = nullptr;
|
||||
}
|
||||
else if (Event.type == DeviceRemoveAck_t) {
|
||||
g_InputDeviceManager.UpdateDevices(*static_cast<int*>(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;
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +181,7 @@ namespace Sdl
|
|||
|
||||
void DeInit(std::thread& Thr)
|
||||
{
|
||||
SdlInitStatus = SDL_NOT_INIT;
|
||||
InitStatus = NOT_INIT;
|
||||
if (!Thr.joinable()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -35,20 +35,20 @@
|
|||
|
||||
namespace Sdl
|
||||
{
|
||||
typedef enum _SDL_INIT_STATUS : int
|
||||
typedef enum _INIT_STATUS : int
|
||||
{
|
||||
SDL_NOT_INIT = -2,
|
||||
SDL_INIT_ERROR,
|
||||
SDL_INIT_SUCCESS,
|
||||
NOT_INIT = -2,
|
||||
INIT_ERROR,
|
||||
INIT_SUCCESS,
|
||||
}
|
||||
SDL_INIT_STATUS;
|
||||
INIT_STATUS;
|
||||
|
||||
extern uint32_t ExitEvent_t;
|
||||
extern uint32_t PopulateEvent_t;
|
||||
extern uint32_t UpdateInputEvent_t;
|
||||
extern uint32_t DeviceRemoveAck_t;
|
||||
extern int SdlInitStatus;
|
||||
extern bool SdlPopulateOK;
|
||||
extern int InitStatus;
|
||||
extern bool PopulateOK;
|
||||
|
||||
// initialize SDL
|
||||
void Init(std::mutex& Mtx, std::condition_variable& Cv, bool is_gui);
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace XInput
|
|||
static XInputGetState_t PXInputGetState = nullptr;
|
||||
|
||||
static bool haveGuideButton = false;
|
||||
int XInputInitStatus = XINPUT_NOT_INIT;
|
||||
int InitStatus = NOT_INIT;
|
||||
uint8_t DevicesConnected = 0;
|
||||
|
||||
static const struct
|
||||
|
@ -118,7 +118,7 @@ namespace XInput
|
|||
hXInput = ::LoadLibrary(TEXT(xinput_dll_name.c_str()));
|
||||
if (!hXInput) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Failed to initialize XInput subsystem!");
|
||||
XInputInitStatus = XINPUT_INIT_ERROR;
|
||||
InitStatus = INIT_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -147,15 +147,15 @@ namespace XInput
|
|||
::FreeLibrary(hXInput);
|
||||
hXInput = nullptr;
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Failed to find XInput functions!");
|
||||
XInputInitStatus = XINPUT_INIT_ERROR;
|
||||
InitStatus = INIT_ERROR;
|
||||
return;
|
||||
}
|
||||
XInputInitStatus = XINPUT_INIT_SUCCESS;
|
||||
InitStatus = INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
XInputInitStatus = XINPUT_NOT_INIT;
|
||||
InitStatus = NOT_INIT;
|
||||
if (hXInput)
|
||||
{
|
||||
::FreeLibrary(hXInput);
|
||||
|
|
|
@ -34,15 +34,15 @@
|
|||
|
||||
namespace XInput
|
||||
{
|
||||
typedef enum _XINPUT_INIT_STATUS : int
|
||||
typedef enum _INIT_STATUS : int
|
||||
{
|
||||
XINPUT_NOT_INIT = -2,
|
||||
XINPUT_INIT_ERROR,
|
||||
XINPUT_INIT_SUCCESS,
|
||||
NOT_INIT = -2,
|
||||
INIT_ERROR,
|
||||
INIT_SUCCESS,
|
||||
}
|
||||
XINPUT_INIT_STATUS;
|
||||
INIT_STATUS;
|
||||
|
||||
extern int XInputInitStatus;
|
||||
extern int InitStatus;
|
||||
extern uint8_t DevicesConnected;
|
||||
|
||||
// initialize XInput
|
||||
|
|
|
@ -135,6 +135,8 @@ class EmuShared : public Mutex
|
|||
// ******************************************************************
|
||||
void GetInputDevTypeSettings(int* type, int port) { Lock(); *type = m_DeviceType[port]; Unlock(); }
|
||||
void SetInputDevTypeSettings(const int* type, int port) { Lock(); m_DeviceType[port] = *type; Unlock(); }
|
||||
void GetInputSlotTypeSettings(int *type, int port, int slot) { Lock(); *type = m_SlotDeviceType[port][slot]; Unlock(); }
|
||||
void SetInputSlotTypeSettings(const int *type, int port, int slot) { Lock(); m_SlotDeviceType[port][slot] = *type; Unlock(); }
|
||||
void GetInputDevNameSettings(char* name, int port) { Lock(); strncpy(name, m_DeviceName[port], 50); Unlock(); }
|
||||
void SetInputDevNameSettings(const char* name, int port) { Lock(); strncpy(m_DeviceName[port], name, 50); Unlock(); }
|
||||
void GetInputBindingsSettings(char button_str[][HOST_BUTTON_NAME_LENGTH], int max_num_buttons, int port)
|
||||
|
@ -365,9 +367,10 @@ class EmuShared : public Mutex
|
|||
bool m_bFirstLaunch;
|
||||
bool m_bClipCursor;
|
||||
unsigned int m_dwKrnlProcID; // Only used for kernel mode level.
|
||||
int m_DeviceType[4];
|
||||
char m_DeviceControlNames[4][HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
||||
char m_DeviceName[4][50];
|
||||
int m_DeviceType[XBOX_NUM_PORTS];
|
||||
int m_SlotDeviceType[XBOX_NUM_PORTS][XBOX_CTRL_NUM_SLOTS];
|
||||
char m_DeviceControlNames[XBOX_NUM_PORTS][HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
||||
char m_DeviceName[XBOX_NUM_PORTS][50];
|
||||
char m_TitleMountPath[xbox::max_path];
|
||||
|
||||
// Settings class in memory should not be tampered by third-party.
|
||||
|
|
|
@ -352,7 +352,7 @@ g_EmuCDPD;
|
|||
|
||||
XB_TRAMPOLINES(XB_trampoline_declare);
|
||||
|
||||
void LookupTrampolines()
|
||||
void LookupTrampolinesD3D()
|
||||
{
|
||||
XB_TRAMPOLINES(XB_trampoline_lookup);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#define DIRECTDRAW_VERSION 0x0700
|
||||
#include <ddraw.h>
|
||||
|
||||
extern void LookupTrampolines();
|
||||
void LookupTrampolinesD3D();
|
||||
|
||||
// initialize render window
|
||||
extern void CxbxInitWindow(bool bFullInit);
|
||||
|
|
|
@ -365,6 +365,8 @@ 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),
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, subhook::Hook> g_FunctionHooks;
|
||||
|
@ -444,7 +446,8 @@ void EmuInstallPatches()
|
|||
EmuInstallPatch(it.first, it.second);
|
||||
}
|
||||
|
||||
LookupTrampolines();
|
||||
LookupTrampolinesD3D();
|
||||
LookupTrampolinesXAPI();
|
||||
}
|
||||
|
||||
void* GetPatchedFunctionTrampoline(const std::string functionName)
|
||||
|
|
|
@ -43,24 +43,69 @@
|
|||
#include "Windef.h"
|
||||
#include <vector>
|
||||
#include "core\hle\XAPI\Xapi.h"
|
||||
#include <charconv>
|
||||
|
||||
|
||||
xbox::PXPP_DEVICE_TYPE g_DeviceType_Gamepad = nullptr;
|
||||
xbox::PXPP_DEVICE_TYPE g_DeviceType_SBC = nullptr;
|
||||
xbox::PXPP_DEVICE_TYPE g_DeviceType_MU = nullptr;
|
||||
|
||||
// Flag is unset after initialize devices is done by simulate LLE USB thread.
|
||||
std::atomic<bool> g_bIsDevicesInitializing = true;
|
||||
std::atomic<bool> g_bIsDevicesEmulating = false;
|
||||
static CXBX_XINPUT_IN_STATE g_InState[4];
|
||||
|
||||
// Global bridge for xbox controller to host, 4 elements for 4 ports.
|
||||
CXBX_CONTROLLER_HOST_BRIDGE g_XboxControllerHostBridge[4] = {
|
||||
{ NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, &g_InState[0], false, false, false, false, false, { 0, 0, 0, 0, 0 } },
|
||||
{ NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, &g_InState[1], false, false, false, false, false, { 0, 0, 0, 0, 0 } },
|
||||
{ NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, &g_InState[2], false, false, false, false, false, { 0, 0, 0, 0, 0 } },
|
||||
{ NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, &g_InState[3], false, false, false, false, false, { 0, 0, 0, 0, 0 } },
|
||||
};
|
||||
// Protects access to xpp types
|
||||
std::atomic<bool> g_bXppGuard = false;
|
||||
|
||||
// Allocate enough memory for the max number of devices we can support simultaneously
|
||||
// 4 duke / S / sbc / arcade joystick (mutually exclusive) + 8 memory units
|
||||
DeviceState g_devs[MAX_DEVS];
|
||||
|
||||
xbox::ulong_xt g_Mounted_MUs = 0;
|
||||
xbox::char_xt g_AltLett_MU = 0;
|
||||
xbox::ulong_xt *g_XapiMountedMUs = &g_Mounted_MUs;
|
||||
xbox::char_xt *g_XapiAltLett_MU = &g_AltLett_MU;
|
||||
std::recursive_mutex g_MuLock;
|
||||
|
||||
// Declare trampolines
|
||||
#define XB_TRAMPOLINES(XB_MACRO) \
|
||||
XB_MACRO(xbox::dword_xt, WINAPI, XUnmountAlternateTitleA, (xbox::char_xt) ); \
|
||||
XB_MACRO(xbox::ntstatus_xt, WINAPI, XapiMapLetterToDirectory, (xbox::PSTRING, xbox::PSTRING, const xbox::PCHAR, xbox::bool_xt, xbox::PCWSTR, xbox::bool_xt) ); \
|
||||
|
||||
XB_TRAMPOLINES(XB_trampoline_declare);
|
||||
|
||||
void LookupTrampolinesXAPI()
|
||||
{
|
||||
XB_TRAMPOLINES(XB_trampoline_lookup);
|
||||
}
|
||||
|
||||
#undef XB_TRAMPOLINES
|
||||
|
||||
|
||||
static inline xbox::char_xt MuPort2Lett(xbox::dword_xt port, xbox::dword_xt slot)
|
||||
{
|
||||
return 'F' + (XBOX_CTRL_NUM_SLOTS * port) + slot;
|
||||
}
|
||||
|
||||
static inline int MuPort2Idx(xbox::dword_xt port, xbox::dword_xt slot)
|
||||
{
|
||||
return (port << 1) + slot;
|
||||
}
|
||||
|
||||
static inline bool MuIsMounted(xbox::char_xt lett)
|
||||
{
|
||||
return *g_XapiMountedMUs & (1 << (lett - 'F'));
|
||||
}
|
||||
|
||||
static inline void MuSetMounted(xbox::char_xt lett)
|
||||
{
|
||||
*g_XapiMountedMUs |= (1 << (lett - 'F'));
|
||||
}
|
||||
|
||||
static inline void MuClearMounted(xbox::char_xt lett)
|
||||
{
|
||||
*g_XapiMountedMUs &= ~(1 << (lett - 'F'));
|
||||
}
|
||||
|
||||
bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType)
|
||||
{
|
||||
|
@ -82,9 +127,15 @@ bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType)
|
|||
}
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::MEMORY_UNIT: {
|
||||
if (XppType == g_DeviceType_MU) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::LIGHT_GUN:
|
||||
case XBOX_INPUT_DEVICE::STEERING_WHEEL:
|
||||
case XBOX_INPUT_DEVICE::MEMORY_UNIT:
|
||||
case XBOX_INPUT_DEVICE::IR_DONGLE:
|
||||
default:
|
||||
break;
|
||||
|
@ -93,124 +144,209 @@ bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool operator!=(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType)
|
||||
void UpdateXppState(DeviceState *dev, XBOX_INPUT_DEVICE type, std::string_view port)
|
||||
{
|
||||
return !(XppType == XidType);
|
||||
xbox::PXPP_DEVICE_TYPE xpp = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE:
|
||||
case XBOX_INPUT_DEVICE::MS_CONTROLLER_S:
|
||||
case XBOX_INPUT_DEVICE::ARCADE_STICK:
|
||||
xpp = g_DeviceType_Gamepad;
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER:
|
||||
xpp = g_DeviceType_SBC;
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::MEMORY_UNIT:
|
||||
xpp = g_DeviceType_MU;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (xpp == nullptr) {
|
||||
// This will happen with xbes that act like launchers, and don't link against the xinput libraries, which results in all the global
|
||||
// xpp types being nullptr. Test case: Innocent Tears
|
||||
return;
|
||||
}
|
||||
|
||||
int port_num, slot;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
xbox::ulong_xt port_mask = 1 << port_num;
|
||||
xbox::ulong_xt slot_mask = 0;
|
||||
|
||||
// Guard against the unfortunate case where XGetDevices or XGetDeviceChanges have already checked for g_bIsDevicesInitializing
|
||||
// and g_bIsDevicesEmulating and a thread switch happens to this function
|
||||
while (g_bXppGuard) {}
|
||||
|
||||
if (xpp == g_DeviceType_MU) {
|
||||
assert(slot != PORT_INVALID);
|
||||
if (slot == 1) {
|
||||
slot_mask = 16;
|
||||
}
|
||||
}
|
||||
|
||||
if (xpp == dev->type && !dev->bPendingRemoval) {
|
||||
xpp->CurrentConnected |= (port_mask << slot_mask);
|
||||
}
|
||||
else {
|
||||
xpp->CurrentConnected &= ~(port_mask << slot_mask);
|
||||
}
|
||||
|
||||
xpp->ChangeConnected = xpp->PreviousConnected ^ xpp->CurrentConnected;
|
||||
}
|
||||
|
||||
bool ConstructHleInputDevice(int Type, int Port)
|
||||
void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type, std::string_view port)
|
||||
{
|
||||
g_bIsDevicesEmulating = true;
|
||||
bool ret = true;
|
||||
|
||||
if (g_bIsChihiro) {
|
||||
// Don't emulate XID devices during Chihiro Emulation
|
||||
g_bIsDevicesEmulating = false;
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
// Set up common device state
|
||||
int port_num, slot;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
dev->upstream = nullptr;
|
||||
dev->port = port;
|
||||
dev->port_idx = port_num;
|
||||
dev->bPendingRemoval = false;
|
||||
dev->bSignaled = false;
|
||||
dev->info.dwPacketNumber = 0;
|
||||
dev->slots[SLOT_TOP] = dev->slots[SLOT_BOTTOM] = nullptr;
|
||||
|
||||
// NOTE: initialize bAutoPollDefault to its default state, which varies depending on the device type
|
||||
switch (Type)
|
||||
switch (type)
|
||||
{
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): {
|
||||
g_XboxControllerHostBridge[Port].XboxPort = Port;
|
||||
g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE;
|
||||
g_XboxControllerHostBridge[Port].bPendingRemoval = false;
|
||||
g_XboxControllerHostBridge[Port].bSignaled = false;
|
||||
g_XboxControllerHostBridge[Port].bIoInProgress = false;
|
||||
g_XboxControllerHostBridge[Port].bAutoPollDefault = true;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(XpadInput);
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(XpadOutput);
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0;
|
||||
}
|
||||
break;
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE):
|
||||
dev->type = XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE;
|
||||
dev->info.bAutoPollDefault = true;
|
||||
dev->info.ucType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
|
||||
dev->info.ucInputStateSize = sizeof(XpadInput);
|
||||
dev->info.ucFeedbackSize = sizeof(XpadOutput);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): {
|
||||
g_XboxControllerHostBridge[Port].XboxPort = Port;
|
||||
g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::MS_CONTROLLER_S;
|
||||
g_XboxControllerHostBridge[Port].bPendingRemoval = false;
|
||||
g_XboxControllerHostBridge[Port].bSignaled = false;
|
||||
g_XboxControllerHostBridge[Port].bIoInProgress = false;
|
||||
g_XboxControllerHostBridge[Port].bAutoPollDefault = true;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(XpadInput);
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(XpadOutput);
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0;
|
||||
}
|
||||
break;
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S):
|
||||
dev->type = XBOX_INPUT_DEVICE::MS_CONTROLLER_S;
|
||||
dev->info.bAutoPollDefault = true;
|
||||
dev->info.ucType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT;
|
||||
dev->info.ucInputStateSize = sizeof(XpadInput);
|
||||
dev->info.ucFeedbackSize = sizeof(XpadOutput);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): {
|
||||
g_XboxControllerHostBridge[Port].XboxPort = Port;
|
||||
g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER;
|
||||
g_XboxControllerHostBridge[Port].InState->SBC.ucGearLever = 8;
|
||||
g_XboxControllerHostBridge[Port].InState->SBC.sAimingX = static_cast<uint8_t>(0x7F);
|
||||
g_XboxControllerHostBridge[Port].InState->SBC.sAimingY = static_cast<uint8_t>(0x7F);
|
||||
g_XboxControllerHostBridge[Port].bPendingRemoval = false;
|
||||
g_XboxControllerHostBridge[Port].bSignaled = false;
|
||||
g_XboxControllerHostBridge[Port].bIoInProgress = false;
|
||||
g_XboxControllerHostBridge[Port].bAutoPollDefault = true;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_STEELBATTALION;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(SBCInput);
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(SBCOutput);
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0;
|
||||
}
|
||||
break;
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
|
||||
dev->type = XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER;
|
||||
dev->info.buff.sbc.ucGearLever = 8;
|
||||
dev->info.buff.sbc.sAimingX = static_cast<uint8_t>(0x7F);
|
||||
dev->info.buff.sbc.sAimingY = static_cast<uint8_t>(0x7F);
|
||||
dev->info.bAutoPollDefault = true;
|
||||
dev->info.ucType = XINPUT_DEVTYPE_STEELBATTALION;
|
||||
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT;
|
||||
dev->info.ucInputStateSize = sizeof(SBCInput);
|
||||
dev->info.ucFeedbackSize = sizeof(SBCOutput);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK): {
|
||||
g_XboxControllerHostBridge[Port].XboxPort = Port;
|
||||
g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::ARCADE_STICK;
|
||||
g_XboxControllerHostBridge[Port].bPendingRemoval = false;
|
||||
g_XboxControllerHostBridge[Port].bSignaled = false;
|
||||
g_XboxControllerHostBridge[Port].bIoInProgress = false;
|
||||
g_XboxControllerHostBridge[Port].bAutoPollDefault = true;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_ARCADE_STICK;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(XpadInput);
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(XpadOutput);
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0;
|
||||
}
|
||||
break;
|
||||
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK):
|
||||
dev->type = XBOX_INPUT_DEVICE::ARCADE_STICK;
|
||||
dev->info.bAutoPollDefault = true;
|
||||
dev->info.ucType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_ARCADE_STICK;
|
||||
dev->info.ucInputStateSize = sizeof(XpadInput);
|
||||
dev->info.ucFeedbackSize = sizeof(XpadOutput);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE):
|
||||
EmuLog(LOG_LEVEL::INFO, "%s: device %s is not yet supported", __func__, GetInputDeviceName(Type).c_str());
|
||||
ret = false;
|
||||
assert(upstream != nullptr);
|
||||
dev->upstream = upstream;
|
||||
dev->type = XBOX_INPUT_DEVICE::MEMORY_UNIT;
|
||||
dev->port_idx = PORT_INVALID;
|
||||
assert(slot != PORT_INVALID);
|
||||
dev->upstream->slots[slot] = dev;
|
||||
break;
|
||||
|
||||
default:
|
||||
EmuLog(LOG_LEVEL::WARNING, "Attempted to attach an unknown device type (type was %d)", Type);
|
||||
ret = false;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
UpdateXppState(dev, static_cast<XBOX_INPUT_DEVICE>(type), port);
|
||||
|
||||
g_bIsDevicesEmulating = false;
|
||||
return ret;
|
||||
|
||||
EmuLogEx(CXBXR_MODULE::INPSYS, LOG_LEVEL::INFO, "Attached device %s to port %s", GetInputDeviceName(type).c_str(), PortUserFormat(port).c_str());
|
||||
}
|
||||
|
||||
void DestructHleInputDevice(int Port)
|
||||
void DestructHleInputDevice(DeviceState *dev)
|
||||
{
|
||||
g_bIsDevicesEmulating = true;
|
||||
|
||||
g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
g_XboxControllerHostBridge[Port].XboxPort = PORT_INVALID;
|
||||
while (g_XboxControllerHostBridge[Port].bIoInProgress) {}
|
||||
g_XboxControllerHostBridge[Port].bPendingRemoval = false;
|
||||
g_XboxControllerHostBridge[Port].bSignaled = false;
|
||||
g_XboxControllerHostBridge[Port].bIoInProgress = false;
|
||||
g_XboxControllerHostBridge[Port].bAutoPollDefault = false;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = 0;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = 0;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = 0;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = 0;
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0;
|
||||
std::memset(&g_InState[Port], 0, sizeof(CXBX_XINPUT_IN_STATE));
|
||||
// Clear common device state
|
||||
XBOX_INPUT_DEVICE type = dev->type;
|
||||
std::string port = dev->port;
|
||||
dev->port_idx = PORT_INVALID;
|
||||
dev->type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
dev->port = std::to_string(PORT_INVALID);
|
||||
dev->bPendingRemoval = false;
|
||||
dev->bSignaled = false;
|
||||
dev->slots[SLOT_TOP] = dev->slots[SLOT_BOTTOM] = nullptr;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE:
|
||||
case XBOX_INPUT_DEVICE::MS_CONTROLLER_S:
|
||||
dev->info.bAutoPollDefault = false;
|
||||
dev->info.ucType = 0;
|
||||
dev->info.ucSubType = 0;
|
||||
dev->info.ucInputStateSize = 0;
|
||||
dev->info.ucFeedbackSize = 0;
|
||||
dev->info.dwPacketNumber = 0;
|
||||
std::memset(&dev->info.buff.ctrl, 0, sizeof(XpadInput));
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER:
|
||||
dev->info.bAutoPollDefault = false;
|
||||
dev->info.ucType = 0;
|
||||
dev->info.ucSubType = 0;
|
||||
dev->info.ucInputStateSize = 0;
|
||||
dev->info.ucFeedbackSize = 0;
|
||||
dev->info.dwPacketNumber = 0;
|
||||
std::memset(&dev->info.buff.sbc, 0, sizeof(SBCInput));
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::ARCADE_STICK:
|
||||
dev->info.bAutoPollDefault = false;
|
||||
dev->info.ucType = 0;
|
||||
dev->info.ucSubType = 0;
|
||||
dev->info.ucInputStateSize = 0;
|
||||
dev->info.ucFeedbackSize = 0;
|
||||
dev->info.dwPacketNumber = 0;
|
||||
std::memset(&dev->info.buff.ctrl, 0, sizeof(XpadInput));
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::MEMORY_UNIT: {
|
||||
assert(dev->upstream != nullptr);
|
||||
int port_num, slot;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
assert(slot != PORT_INVALID);
|
||||
dev->upstream->slots[slot] = nullptr;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
UpdateXppState(dev, type, port);
|
||||
dev->upstream = nullptr;
|
||||
|
||||
g_bIsDevicesEmulating = false;
|
||||
|
||||
EmuLogEx(CXBXR_MODULE::INPSYS, LOG_LEVEL::INFO, "Detached device %s from port %s", GetInputDeviceName(to_underlying(type)).c_str(), PortUserFormat(port).c_str());
|
||||
}
|
||||
|
||||
void SetupXboxDeviceTypes()
|
||||
|
@ -280,8 +416,52 @@ void SetupXboxDeviceTypes()
|
|||
return;
|
||||
}
|
||||
|
||||
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_GAMEPAD Found at 0x%08X", (uintptr_t)g_DeviceType_Gamepad);
|
||||
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_GAMEPAD found at 0x%08X", (uintptr_t)g_DeviceType_Gamepad);
|
||||
}
|
||||
|
||||
if (xbox::addr_xt mu_xpp_type = g_SymbolAddresses["g_DeviceType_MU"]) {
|
||||
g_DeviceType_MU = reinterpret_cast<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 by XbSymbolDatabase");
|
||||
}
|
||||
|
||||
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::INFO, "XapiMountedMUs was not found by XbSymbolDatabase");
|
||||
}
|
||||
}
|
||||
|
||||
template<bool IsXInputPoll>
|
||||
xbox::dword_xt CxbxImpl_XInputHandler(xbox::HANDLE hDevice, xbox::PXINPUT_STATE pState)
|
||||
{
|
||||
xbox::dword_xt status = ERROR_DEVICE_NOT_CONNECTED;
|
||||
DeviceState *dev = static_cast<DeviceState *>(hDevice);
|
||||
int port = dev->port_idx;
|
||||
|
||||
if ((g_devs[port].info.hHandle == hDevice) && !g_devs[port].bPendingRemoval) {
|
||||
if (g_devs[port].info.bAutoPoll != IsXInputPoll) {
|
||||
if (g_InputDeviceManager.UpdateXboxPortInput(port, &g_devs[port].info.buff, DIRECTION_IN, to_underlying(g_devs[port].type))) {
|
||||
g_devs[port].info.dwPacketNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (!IsXInputPoll) {
|
||||
std::memcpy((void *)&pState->Gamepad, &g_devs[port].info.buff, g_devs[port].info.ucInputStateSize);
|
||||
pState->dwPacketNumber = g_devs[port].info.dwPacketNumber;
|
||||
}
|
||||
|
||||
status = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -312,63 +492,6 @@ xbox::void_xt WINAPI xbox::EMUPATCH(XInitDevices)
|
|||
}).detach();
|
||||
}
|
||||
|
||||
// This is called to emulate async for both XGetDevices and XGetDeviceChanges
|
||||
void UpdateConnectedDeviceState(xbox::PXPP_DEVICE_TYPE DeviceType) {
|
||||
|
||||
// Do not process the queries until initialize delay and device emulating are complete.
|
||||
if (g_bIsDevicesInitializing || g_bIsDevicesEmulating){
|
||||
return;
|
||||
}
|
||||
|
||||
int Port, PortMask;
|
||||
for (Port = PORT_1, PortMask = 1; Port <= PORT_4; Port++, PortMask <<= 1) {
|
||||
if (DeviceType == g_XboxControllerHostBridge[Port].XboxType && !g_XboxControllerHostBridge[Port].bPendingRemoval) {
|
||||
DeviceType->CurrentConnected |= PortMask;
|
||||
}
|
||||
else {
|
||||
DeviceType->CurrentConnected &= ~PortMask;
|
||||
}
|
||||
|
||||
if (static_cast<uint8_t>(g_XboxControllerHostBridge[Port].bPendingRemoval) &
|
||||
~(static_cast<uint8_t>(g_XboxControllerHostBridge[Port].bSignaled))) {
|
||||
g_XboxControllerHostBridge[Port].bSignaled = true;
|
||||
SDL_Event DeviceRemoveEvent;
|
||||
SDL_memset(&DeviceRemoveEvent, 0, sizeof(SDL_Event));
|
||||
DeviceRemoveEvent.type = Sdl::DeviceRemoveAck_t;
|
||||
DeviceRemoveEvent.user.data1 = new int(Port);
|
||||
SDL_PushEvent(&DeviceRemoveEvent);
|
||||
}
|
||||
}
|
||||
DeviceType->ChangeConnected = DeviceType->PreviousConnected ^ DeviceType->CurrentConnected;
|
||||
}
|
||||
|
||||
template<bool IsXInputPoll>
|
||||
xbox::dword_xt CxbxImpl_XInputHandler(xbox::HANDLE hDevice, xbox::PXINPUT_STATE pState)
|
||||
{
|
||||
xbox::dword_xt status = ERROR_DEVICE_NOT_CONNECTED;
|
||||
PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice;
|
||||
int Port = Device->XboxPort;
|
||||
|
||||
if ((g_XboxControllerHostBridge[Port].hXboxDevice == hDevice) && !g_XboxControllerHostBridge[Port].bPendingRemoval) {
|
||||
if (g_XboxControllerHostBridge[Port].bAutoPoll != IsXInputPoll) {
|
||||
g_XboxControllerHostBridge[Port].bIoInProgress = true;
|
||||
if (g_InputDeviceManager.UpdateXboxPortInput(Port, g_XboxControllerHostBridge[Port].InState, DIRECTION_IN, to_underlying(g_XboxControllerHostBridge[Port].XboxType))) {
|
||||
g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber++;
|
||||
}
|
||||
g_XboxControllerHostBridge[Port].bIoInProgress = false;
|
||||
}
|
||||
|
||||
if constexpr (!IsXInputPoll) {
|
||||
std::memcpy((void *)&pState->Gamepad, g_XboxControllerHostBridge[Port].InState, g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize);
|
||||
pState->dwPacketNumber = g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber;
|
||||
}
|
||||
|
||||
status = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XGetDevices
|
||||
// * Note: This could be unpatched however,
|
||||
|
@ -383,7 +506,24 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDevices)
|
|||
{
|
||||
LOG_FUNC_ONE_ARG(DeviceType);
|
||||
|
||||
UpdateConnectedDeviceState(DeviceType);
|
||||
g_bXppGuard = true;
|
||||
|
||||
if (g_bIsDevicesInitializing || g_bIsDevicesEmulating) {
|
||||
g_bXppGuard = false;
|
||||
RETURN(DeviceType->CurrentConnected);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 12; ++i) {
|
||||
if (static_cast<uint8_t>(g_devs[i].bPendingRemoval) &
|
||||
~(static_cast<uint8_t>(g_devs[i].bSignaled))) {
|
||||
g_devs[i].bSignaled = true;
|
||||
SDL_Event DeviceRemoveEvent;
|
||||
SDL_memset(&DeviceRemoveEvent, 0, sizeof(SDL_Event));
|
||||
DeviceRemoveEvent.type = Sdl::DeviceRemoveAck_t;
|
||||
DeviceRemoveEvent.user.data1 = new std::string(g_devs[i].port);
|
||||
SDL_PushEvent(&DeviceRemoveEvent);
|
||||
}
|
||||
}
|
||||
|
||||
UCHAR oldIrql = xbox::KeRaiseIrqlToDpcLevel();
|
||||
|
||||
|
@ -394,6 +534,8 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDevices)
|
|||
|
||||
xbox::KfLowerIrql(oldIrql);
|
||||
|
||||
g_bXppGuard = false;
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
|
@ -417,11 +559,28 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(XGetDeviceChanges)
|
|||
LOG_FUNC_ARG(pdwRemovals)
|
||||
LOG_FUNC_END;
|
||||
|
||||
UpdateConnectedDeviceState(DeviceType);
|
||||
g_bXppGuard = true;
|
||||
if (g_bIsDevicesInitializing || g_bIsDevicesEmulating) {
|
||||
*pdwInsertions = 0;
|
||||
*pdwRemovals = 0;
|
||||
g_bXppGuard = false;
|
||||
RETURN(FALSE);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 12; ++i) {
|
||||
if (static_cast<uint8_t>(g_devs[i].bPendingRemoval) &
|
||||
~(static_cast<uint8_t>(g_devs[i].bSignaled))) {
|
||||
g_devs[i].bSignaled = true;
|
||||
SDL_Event DeviceRemoveEvent;
|
||||
SDL_memset(&DeviceRemoveEvent, 0, sizeof(SDL_Event));
|
||||
DeviceRemoveEvent.type = Sdl::DeviceRemoveAck_t;
|
||||
DeviceRemoveEvent.user.data1 = new std::string(g_devs[i].port);
|
||||
SDL_PushEvent(&DeviceRemoveEvent);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL ret = FALSE;
|
||||
|
||||
|
||||
if(!DeviceType->ChangeConnected)
|
||||
{
|
||||
*pdwInsertions = 0;
|
||||
|
@ -448,6 +607,7 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(XGetDeviceChanges)
|
|||
|
||||
xbox::KfLowerIrql(oldIrql);
|
||||
}
|
||||
g_bXppGuard = false;
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
@ -470,12 +630,17 @@ xbox::HANDLE WINAPI xbox::EMUPATCH(XInputOpen)
|
|||
LOG_FUNC_ARG(pPollingParameters)
|
||||
LOG_FUNC_END;
|
||||
|
||||
if (DeviceType == g_DeviceType_MU) {
|
||||
// MUs cannot be opened with XInputOpen
|
||||
RETURN(NULL);
|
||||
}
|
||||
|
||||
if (dwPort >= PORT_1 && dwPort <= PORT_4) {
|
||||
if (DeviceType == g_XboxControllerHostBridge[dwPort].XboxType) {
|
||||
g_XboxControllerHostBridge[dwPort].bAutoPoll = pPollingParameters != xbox::zeroptr ?
|
||||
pPollingParameters->fAutoPoll : g_XboxControllerHostBridge[dwPort].bAutoPollDefault;
|
||||
g_XboxControllerHostBridge[dwPort].hXboxDevice = &g_XboxControllerHostBridge[dwPort];
|
||||
RETURN(g_XboxControllerHostBridge[dwPort].hXboxDevice);
|
||||
if (DeviceType == g_devs[dwPort].type) {
|
||||
g_devs[dwPort].info.bAutoPoll = pPollingParameters != xbox::zeroptr ?
|
||||
pPollingParameters->fAutoPoll : g_devs[dwPort].info.bAutoPollDefault;
|
||||
g_devs[dwPort].info.hHandle = &g_devs[dwPort];
|
||||
RETURN(g_devs[dwPort].info.hHandle);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -492,10 +657,9 @@ xbox::void_xt WINAPI xbox::EMUPATCH(XInputClose)
|
|||
{
|
||||
LOG_FUNC_ONE_ARG(hDevice);
|
||||
|
||||
PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice;
|
||||
int Port = Device->XboxPort;
|
||||
if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice) {
|
||||
Device->hXboxDevice = NULL;
|
||||
DeviceState *dev = static_cast<DeviceState *>(hDevice);
|
||||
if (g_devs[dev->port_idx].info.hHandle == hDevice) {
|
||||
dev->info.hHandle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -530,12 +694,12 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XInputGetCapabilities)
|
|||
LOG_FUNC_END;
|
||||
|
||||
dword_xt ret = ERROR_DEVICE_NOT_CONNECTED;
|
||||
PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice;
|
||||
int Port = Device->XboxPort;
|
||||
if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice && !g_XboxControllerHostBridge[Port].bPendingRemoval) {
|
||||
pCapabilities->SubType = g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType;
|
||||
DeviceState *dev = static_cast<DeviceState *>(hDevice);
|
||||
int port = dev->port_idx;
|
||||
if (g_devs[port].info.hHandle == hDevice && !g_devs[port].bPendingRemoval) {
|
||||
pCapabilities->SubType = g_devs[port].info.ucSubType;
|
||||
UCHAR* pCap = (UCHAR*)(&pCapabilities->In);
|
||||
memset(pCap, 0xFF, g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize);
|
||||
std::memset(pCap, 0xFF, g_devs[port].info.ucInputStateSize + g_devs[port].info.ucFeedbackSize);
|
||||
ret = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -579,11 +743,11 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XInputSetState)
|
|||
LOG_FUNC_ARG(pFeedback)
|
||||
LOG_FUNC_END;
|
||||
|
||||
PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice;
|
||||
int Port = Device->XboxPort;
|
||||
if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice && !g_XboxControllerHostBridge[Port].bPendingRemoval) {
|
||||
DeviceState *dev = static_cast<DeviceState *>(hDevice);
|
||||
int port = dev->port_idx;
|
||||
if (g_devs[port].info.hHandle == hDevice && !g_devs[port].bPendingRemoval) {
|
||||
pFeedback->Header.dwStatus = ERROR_IO_PENDING;
|
||||
g_InputDeviceManager.UpdateXboxPortInput(Port, (void*)&pFeedback->Rumble, DIRECTION_OUT, to_underlying(g_XboxControllerHostBridge[Port].XboxType));
|
||||
g_InputDeviceManager.UpdateXboxPortInput(port, (void*)&pFeedback->Rumble, DIRECTION_OUT, to_underlying(g_devs[port].type));
|
||||
pFeedback->Header.dwStatus = ERROR_SUCCESS;
|
||||
if (pFeedback->Header.hEvent != NULL &&
|
||||
ObReferenceObjectByHandle(pFeedback->Header.hEvent, &xbox::ExEventObjectType, (PVOID*)&pFeedback->Header.IoCompletedEvent) == ERROR_SUCCESS) {
|
||||
|
@ -1164,18 +1328,44 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XMountMUA)
|
|||
PCHAR pchDrive
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(dwPort)
|
||||
LOG_FUNC_ARG(dwSlot)
|
||||
LOG_FUNC_ARG(pchDrive)
|
||||
LOG_FUNC_END;
|
||||
|
||||
// TODO: Actually allow memory card emulation? This might make transferring
|
||||
// game saves a bit easier if the memory card directory was configurable. =]
|
||||
std::lock_guard lock(g_MuLock);
|
||||
|
||||
RETURN(E_FAIL);
|
||||
char lett = MuPort2Lett(dwPort, dwSlot);
|
||||
if (MuIsMounted(lett)) {
|
||||
if (pchDrive != zeroptr) {
|
||||
*pchDrive = lett;
|
||||
}
|
||||
RETURN(ERROR_ALREADY_ASSIGNED);
|
||||
}
|
||||
|
||||
char title_id_buff[9];
|
||||
std::sprintf(title_id_buff, "%08lx", CxbxKrnl_Xbe->m_Certificate.dwTitleId);
|
||||
std::string mu_path_str(DrivePrefix + lett + ":");
|
||||
std::string mu_dev_str(DeviceMU + std::to_string(MuPort2Idx(dwPort, dwSlot)));
|
||||
ANSI_STRING mu_dev, mu_path;
|
||||
RtlInitAnsiString(&mu_path, mu_path_str.data());
|
||||
RtlInitAnsiString(&mu_dev, mu_dev_str.data());
|
||||
mu_dev_str += '\\';
|
||||
|
||||
ntstatus_xt status = XB_TRMP(XapiMapLetterToDirectory)(&mu_path, &mu_dev, title_id_buff, 1,
|
||||
reinterpret_cast<const char16_t *>(CxbxKrnl_Xbe->m_Certificate.wsTitleName), 0);
|
||||
|
||||
if (!nt_success(status)) {
|
||||
RETURN(RtlNtStatusToDosError(status));
|
||||
}
|
||||
|
||||
MuSetMounted(lett);
|
||||
if (pchDrive != zeroptr) {
|
||||
*pchDrive = lett;
|
||||
}
|
||||
|
||||
RETURN(ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -1224,20 +1414,174 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XMountMURootA)
|
|||
PCHAR pchDrive
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(dwPort)
|
||||
LOG_FUNC_ARG(dwSlot)
|
||||
LOG_FUNC_ARG(pchDrive)
|
||||
LOG_FUNC_END;
|
||||
|
||||
// TODO: The params are probably wrong...
|
||||
LOG_UNIMPLEMENTED();
|
||||
std::lock_guard lock(g_MuLock);
|
||||
|
||||
char_xt lett = MuPort2Lett(dwPort, dwSlot);
|
||||
if (MuIsMounted(lett)) {
|
||||
if (pchDrive != zeroptr) {
|
||||
*pchDrive = lett;
|
||||
}
|
||||
RETURN(ERROR_ALREADY_ASSIGNED);
|
||||
}
|
||||
|
||||
std::string mu_path_str(DrivePrefix + lett + ":");
|
||||
std::string mu_dev_str(DeviceMU + std::to_string(MuPort2Idx(dwPort, dwSlot)));
|
||||
ANSI_STRING mu_dev, mu_path;
|
||||
RtlInitAnsiString(&mu_path, mu_path_str.data());
|
||||
RtlInitAnsiString(&mu_dev, mu_dev_str.data());
|
||||
ntstatus_xt status = IoCreateSymbolicLink(&mu_path, &mu_dev);
|
||||
|
||||
if (!nt_success(status)) {
|
||||
if (pchDrive != zeroptr) {
|
||||
*pchDrive = 0;
|
||||
}
|
||||
RETURN(RtlNtStatusToDosError(status));
|
||||
}
|
||||
|
||||
MuSetMounted(lett);
|
||||
if (pchDrive != zeroptr) {
|
||||
*pchDrive = lett;
|
||||
}
|
||||
|
||||
RETURN(ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XUnmountMU
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(XUnmountMU)
|
||||
(
|
||||
dword_xt dwPort,
|
||||
dword_xt dwSlot
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(dwPort)
|
||||
LOG_FUNC_ARG(dwSlot)
|
||||
LOG_FUNC_END;
|
||||
|
||||
std::lock_guard lock(g_MuLock);
|
||||
|
||||
char_xt lett = MuPort2Lett(dwPort, dwSlot);
|
||||
if (!MuIsMounted(lett)) {
|
||||
RETURN(ERROR_INVALID_DRIVE);
|
||||
}
|
||||
|
||||
if (*g_XapiAltLett_MU == lett) {
|
||||
XB_TRMP(XUnmountAlternateTitleA)('X');
|
||||
}
|
||||
|
||||
std::string mu_path_str(DrivePrefix + lett + ":");
|
||||
ANSI_STRING mu_path;
|
||||
RtlInitAnsiString(&mu_path, mu_path_str.data());
|
||||
ntstatus_xt status = IoDeleteSymbolicLink(&mu_path);
|
||||
|
||||
if (!nt_success(status)) {
|
||||
RETURN(RtlNtStatusToDosError(status));
|
||||
}
|
||||
|
||||
MuClearMounted(lett);
|
||||
g_io_mu_metadata->flush(static_cast<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
|
||||
// ******************************************************************
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#ifndef XAPI_H
|
||||
#define XAPI_H
|
||||
|
||||
void LookupTrampolinesXAPI();
|
||||
|
||||
#include "xbox_types.h"
|
||||
|
||||
namespace xbox {
|
||||
|
@ -684,6 +686,27 @@ xbox::dword_xt WINAPI EMUPATCH(XMountMURootA)
|
|||
PCHAR pchDrive
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XUnmountMU
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI EMUPATCH(XUnmountMU)
|
||||
(
|
||||
dword_xt dwPort,
|
||||
dword_xt dwSlot
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XReadMUMetaData
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI EMUPATCH(XReadMUMetaData)
|
||||
(
|
||||
IN dword_xt dwPort,
|
||||
IN dword_xt dwSlot,
|
||||
IN LPVOID lpBuffer,
|
||||
IN dword_xt dwByteOffset,
|
||||
IN dword_xt dwNumberOfBytesToRead
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XMountAlternateTitleA
|
||||
// ******************************************************************
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -619,8 +619,7 @@ XBSYSAPI EXPORTNUM(196) xbox::ntstatus_xt NTAPI xbox::NtDeviceIoControlFile
|
|||
|
||||
switch (IoControlCode)
|
||||
{
|
||||
case 0x4D014: // IOCTL_SCSI_PASS_THROUGH_DIRECT
|
||||
{
|
||||
case 0x4D014: { // IOCTL_SCSI_PASS_THROUGH_DIRECT
|
||||
PSCSI_PASS_THROUGH_DIRECT PassThrough = (PSCSI_PASS_THROUGH_DIRECT)InputBuffer;
|
||||
PDVDX2_AUTHENTICATION Authentication = (PDVDX2_AUTHENTICATION)PassThrough->DataBuffer;
|
||||
|
||||
|
@ -628,34 +627,63 @@ XBSYSAPI EXPORTNUM(196) xbox::ntstatus_xt NTAPI xbox::NtDeviceIoControlFile
|
|||
Authentication->AuthenticationPage.CDFValid = 1;
|
||||
Authentication->AuthenticationPage.PartitionArea = 1;
|
||||
Authentication->AuthenticationPage.Authentication = 1;
|
||||
break;
|
||||
}
|
||||
case 0x70000: // IOCTL_DISK_GET_DRIVE_GEOMETRY
|
||||
{
|
||||
break;
|
||||
|
||||
case 0x70000: { // IOCTL_DISK_GET_DRIVE_GEOMETRY
|
||||
PDISK_GEOMETRY DiskGeometry = (PDISK_GEOMETRY)OutputBuffer;
|
||||
|
||||
DiskGeometry->MediaType = FixedMedia;
|
||||
DiskGeometry->TracksPerCylinder = 1;
|
||||
DiskGeometry->SectorsPerTrack = 1;
|
||||
DiskGeometry->BytesPerSector = 512;
|
||||
DiskGeometry->Cylinders.QuadPart = 0x1400000; // Around 10GB, size of stock xbox HDD
|
||||
break;
|
||||
DeviceType type = CxbxrGetDeviceTypeFromHandle(FileHandle);
|
||||
if (type == DeviceType::Harddisk0) {
|
||||
DiskGeometry->MediaType = FixedMedia;
|
||||
DiskGeometry->TracksPerCylinder = 1;
|
||||
DiskGeometry->SectorsPerTrack = 1;
|
||||
DiskGeometry->BytesPerSector = 512;
|
||||
DiskGeometry->Cylinders.QuadPart = 0x1400000; // 10GB, size of stock xbox HDD
|
||||
}
|
||||
else if (type == DeviceType::MU) {
|
||||
DiskGeometry->MediaType = FixedMedia;
|
||||
DiskGeometry->TracksPerCylinder = 1;
|
||||
DiskGeometry->SectorsPerTrack = 1;
|
||||
DiskGeometry->BytesPerSector = 512;
|
||||
DiskGeometry->Cylinders.QuadPart = 0x4000; // 8MB, Microsoft original MUs
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::WARNING, "%s: Unrecongnized handle 0x%X with IoControlCode IOCTL_DISK_GET_DRIVE_GEOMETRY.", __func__, FileHandle);
|
||||
ret = status_invalid_handle;
|
||||
}
|
||||
}
|
||||
case 0x74004: // IOCTL_DISK_GET_PARTITION_INFO
|
||||
{
|
||||
break;
|
||||
|
||||
case 0x74004: { // IOCTL_DISK_GET_PARTITION_INFO
|
||||
PPARTITION_INFORMATION partitioninfo = (PPARTITION_INFORMATION)OutputBuffer;
|
||||
|
||||
XboxPartitionTable partitionTable = CxbxGetPartitionTable();
|
||||
int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle);
|
||||
|
||||
// Now we read from the partition table, to fill in the partitionInfo struct
|
||||
partitioninfo->PartitionNumber = partitionNumber;
|
||||
partitioninfo->StartingOffset.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBAStart * 512;
|
||||
partitioninfo->PartitionLength.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize * 512;
|
||||
partitioninfo->HiddenSectors = partitionTable.TableEntries[partitionNumber - 1].Reserved;
|
||||
partitioninfo->RecognizedPartition = true;
|
||||
break;
|
||||
DeviceType type = CxbxrGetDeviceTypeFromHandle(FileHandle);
|
||||
if (type == DeviceType::Harddisk0) {
|
||||
XboxPartitionTable partitionTable = CxbxGetPartitionTable();
|
||||
int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle);
|
||||
|
||||
// Now we read from the partition table, to fill in the partitionInfo struct
|
||||
partitioninfo->PartitionNumber = partitionNumber;
|
||||
partitioninfo->StartingOffset.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBAStart * 512;
|
||||
partitioninfo->PartitionLength.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize * 512;
|
||||
partitioninfo->HiddenSectors = partitionTable.TableEntries[partitionNumber - 1].Reserved;
|
||||
partitioninfo->RecognizedPartition = true;
|
||||
}
|
||||
else if (type == DeviceType::MU) {
|
||||
partitioninfo->PartitionNumber = 0;
|
||||
partitioninfo->StartingOffset.QuadPart = 0; // FIXME: where does the MU partition start?
|
||||
partitioninfo->PartitionLength.QuadPart = 16384; // 8MB
|
||||
partitioninfo->HiddenSectors = 0;
|
||||
partitioninfo->RecognizedPartition = true;
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::WARNING, "%s: Unrecongnized handle 0x%X with IoControlCode IOCTL_DISK_GET_PARTITION_INFO.", __func__, FileHandle);
|
||||
ret = status_invalid_handle;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_UNIMPLEMENTED();
|
||||
}
|
||||
|
@ -802,19 +830,56 @@ XBSYSAPI EXPORTNUM(200) xbox::ntstatus_xt NTAPI xbox::NtFsControlFile
|
|||
LOG_FUNC_ARG(OutputBufferLength)
|
||||
LOG_FUNC_END;
|
||||
|
||||
NTSTATUS ret = STATUS_INVALID_PARAMETER;
|
||||
ntstatus_xt ret = STATUS_INVALID_PARAMETER;
|
||||
|
||||
switch (FsControlCode)
|
||||
{
|
||||
case fsctl_dismount_volume: {
|
||||
// HACK: this should just free the resources assocoated with the volume, it should not reformat it
|
||||
int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle);
|
||||
if (partitionNumber > 0) {
|
||||
CxbxFormatPartitionByHandle(FileHandle);
|
||||
ret = xbox::status_success;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case fsctl_read_fatx_metadata: {
|
||||
const std::wstring path = CxbxGetFinalPathNameByHandle(FileHandle);
|
||||
size_t pos = path.rfind(L"\\EmuMu");
|
||||
if (pos != std::string::npos && path[pos + 6] == '\\') {
|
||||
// Ensure that InputBuffer is indeed what we think it is
|
||||
pfatx_volume_metadata volume = static_cast<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,27 +1564,56 @@ XBSYSAPI EXPORTNUM(218) xbox::ntstatus_xt NTAPI xbox::NtQueryVolumeInformationFi
|
|||
|
||||
// FileFsSizeInformation is a special case that should read from our emulated partition table
|
||||
if ((DWORD)FileInformationClass == FileFsSizeInformation) {
|
||||
ntstatus_xt status;
|
||||
PFILE_FS_SIZE_INFORMATION XboxSizeInfo = (PFILE_FS_SIZE_INFORMATION)FileInformation;
|
||||
|
||||
XboxPartitionTable partitionTable = CxbxGetPartitionTable();
|
||||
int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle);
|
||||
FATX_SUPERBLOCK superBlock = CxbxGetFatXSuperBlock(partitionNumber);
|
||||
// This might access the HDD, a MU or the DVD drive, so we need to figure out the correct one first
|
||||
DeviceType type = CxbxrGetDeviceTypeFromHandle(FileHandle);
|
||||
if (type == DeviceType::Harddisk0) {
|
||||
|
||||
XboxSizeInfo->BytesPerSector = 512;
|
||||
XboxPartitionTable partitionTable = CxbxGetPartitionTable();
|
||||
int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle);
|
||||
FATX_SUPERBLOCK superBlock = CxbxGetFatXSuperBlock(partitionNumber);
|
||||
|
||||
// In some cases, the emulated partition hasn't been formatted yet, as these are forwarded to a real folder, this doesn't actually matter.
|
||||
// We just pretend they are valid by defaulting the SectorsPerAllocationUnit value to the most common for system partitions
|
||||
XboxSizeInfo->SectorsPerAllocationUnit = 32;
|
||||
XboxSizeInfo->BytesPerSector = 512;
|
||||
|
||||
// If there is a valid cluster size, we calculate SectorsPerAllocationUnit from that instead
|
||||
if (superBlock.ClusterSize > 0) {
|
||||
XboxSizeInfo->SectorsPerAllocationUnit = superBlock.ClusterSize;
|
||||
// In some cases, the emulated partition hasn't been formatted yet, as these are forwarded to a real folder, this doesn't actually matter.
|
||||
// We just pretend they are valid by defaulting the SectorsPerAllocationUnit value to the most common for system partitions
|
||||
XboxSizeInfo->SectorsPerAllocationUnit = 32;
|
||||
|
||||
// If there is a valid cluster size, we calculate SectorsPerAllocationUnit from that instead
|
||||
if (superBlock.ClusterSize > 0) {
|
||||
XboxSizeInfo->SectorsPerAllocationUnit = superBlock.ClusterSize;
|
||||
}
|
||||
|
||||
XboxSizeInfo->TotalAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit;
|
||||
XboxSizeInfo->AvailableAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit;
|
||||
|
||||
status = status_success;
|
||||
}
|
||||
else if (type == DeviceType::MU) {
|
||||
|
||||
XboxSizeInfo->BytesPerSector = 512;
|
||||
XboxSizeInfo->SectorsPerAllocationUnit = 32;
|
||||
XboxSizeInfo->TotalAllocationUnits.QuadPart = 512; // 8MB -> ((1024)^2 * 8) / (BytesPerSector * SectorsPerAllocationUnit)
|
||||
XboxSizeInfo->AvailableAllocationUnits.QuadPart = 512; // constant, so there's always free space available to write stuff
|
||||
|
||||
status = status_success;
|
||||
}
|
||||
else if (type == DeviceType::Cdrom0) {
|
||||
|
||||
XboxSizeInfo->BytesPerSector = 2048;
|
||||
XboxSizeInfo->SectorsPerAllocationUnit = 1;
|
||||
XboxSizeInfo->TotalAllocationUnits.QuadPart = 3820880; // assuming DVD-9 (dual layer), redump reports a total size in bytes of 7825162240
|
||||
|
||||
status = status_success;
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::WARNING, "%s: Unrecongnized handle 0x%X with class FileFsSizeInformation.", __func__, FileHandle);
|
||||
status = status_invalid_handle;
|
||||
}
|
||||
|
||||
XboxSizeInfo->TotalAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit;
|
||||
XboxSizeInfo->AvailableAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit;
|
||||
|
||||
RETURN(xbox::status_success);
|
||||
RETURN(status);
|
||||
}
|
||||
|
||||
// Get the required size for the host buffer
|
||||
|
|
|
@ -96,6 +96,7 @@ char szFilePath_EEPROM_bin[MAX_PATH] = { 0 };
|
|||
char szFilePath_Xbe[xbox::max_path*2] = { 0 }; // NOTE: LAUNCH_DATA_HEADER's szLaunchPath is xbox::max_path*2 = 520
|
||||
|
||||
std::string CxbxBasePath;
|
||||
std::string MuBasePath;
|
||||
HANDLE CxbxBasePathHandle;
|
||||
Xbe* CxbxKrnl_Xbe = NULL;
|
||||
bool g_bIsChihiro = false;
|
||||
|
@ -1433,11 +1434,14 @@ __declspec(noreturn) void CxbxKrnlInit
|
|||
char szBuffer[sizeof(szFilePath_Xbe)];
|
||||
g_EmuShared->GetStorageLocation(szBuffer);
|
||||
|
||||
MuBasePath = std::string(szBuffer) + "\\EmuMu";
|
||||
CxbxBasePath = std::string(szBuffer) + "\\EmuDisk";
|
||||
CxbxResolveHostToFullPath(CxbxBasePath, "Cxbx-Reloaded's EmuDisk directory");
|
||||
CxbxResolveHostToFullPath(MuBasePath, "Cxbx-Reloaded's EmuMu directory");
|
||||
// Since canonical always remove the extra slash, we need to manually add it back.
|
||||
// TODO: Once CxbxBasePath is filesystem::path, replace CxbxBasePath's + operators to / for include path separator internally.
|
||||
CxbxBasePath = std::filesystem::path(CxbxBasePath).append("").string();
|
||||
MuBasePath = std::filesystem::path(MuBasePath).append("").string();
|
||||
}
|
||||
|
||||
// Determine xbe path
|
||||
|
@ -1505,6 +1509,22 @@ __declspec(noreturn) void CxbxKrnlInit
|
|||
CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition7, CxbxBasePath + "Partition7");
|
||||
CxbxRegisterDeviceHostPath(DevicePrefix + "\\Chihiro", CxbxBasePath + "Chihiro");
|
||||
|
||||
// Create the MU directories and the bin files
|
||||
CxbxRegisterDeviceHostPath(DeviceMU0, MuBasePath + "F", false, sizeof(FATX_SUPERBLOCK));
|
||||
CxbxRegisterDeviceHostPath(DeviceMU1, MuBasePath + "G", false, sizeof(FATX_SUPERBLOCK));
|
||||
CxbxRegisterDeviceHostPath(DeviceMU2, MuBasePath + "H", false, sizeof(FATX_SUPERBLOCK));
|
||||
CxbxRegisterDeviceHostPath(DeviceMU3, MuBasePath + "I", false, sizeof(FATX_SUPERBLOCK));
|
||||
CxbxRegisterDeviceHostPath(DeviceMU4, MuBasePath + "J", false, sizeof(FATX_SUPERBLOCK));
|
||||
CxbxRegisterDeviceHostPath(DeviceMU5, MuBasePath + "K", false, sizeof(FATX_SUPERBLOCK));
|
||||
CxbxRegisterDeviceHostPath(DeviceMU6, MuBasePath + "L", false, sizeof(FATX_SUPERBLOCK));
|
||||
CxbxRegisterDeviceHostPath(DeviceMU7, MuBasePath + "M", false, sizeof(FATX_SUPERBLOCK));
|
||||
|
||||
std::mbstate_t ps = std::mbstate_t();
|
||||
const char *src = MuBasePath.c_str();
|
||||
std::wstring wMuBasePath(MuBasePath.size(), L'0');
|
||||
std::mbsrtowcs(wMuBasePath.data(), &src, wMuBasePath.size(), &ps);
|
||||
g_io_mu_metadata = new io_mu_metadata(wMuBasePath);
|
||||
|
||||
// Create default symbolic links :
|
||||
EmuLogInit(LOG_LEVEL::DEBUG, "Creating default symbolic links.");
|
||||
{
|
||||
|
@ -1747,6 +1767,13 @@ void CxbxInitFilePaths()
|
|||
CxbxKrnlCleanup("%s : Couldn't create Cxbx-Reloaded EmuDisk folder!", __func__);
|
||||
}
|
||||
|
||||
// Make sure the EmuDMu folder exists
|
||||
std::string emuMu = std::string(szFolder_CxbxReloadedData) + std::string("\\EmuMu");
|
||||
result = std::filesystem::exists(emuMu);
|
||||
if (!result && !std::filesystem::create_directory(emuMu)) {
|
||||
CxbxKrnlCleanup("%s : Couldn't create Cxbx-Reloaded EmuMu folder!", __func__);
|
||||
}
|
||||
|
||||
snprintf(szFilePath_EEPROM_bin, MAX_PATH, "%s\\EEPROM.bin", szFolder_CxbxReloadedData);
|
||||
|
||||
GetModuleFileName(GetModuleHandle(nullptr), szFilePath_CxbxReloaded_Exe, MAX_PATH);
|
||||
|
@ -1918,6 +1945,11 @@ void CxbxKrnlShutDown(bool is_reboot)
|
|||
// Shutdown the input device manager
|
||||
g_InputDeviceManager.Shutdown();
|
||||
|
||||
if (g_io_mu_metadata) {
|
||||
delete g_io_mu_metadata;
|
||||
g_io_mu_metadata = nullptr;
|
||||
}
|
||||
|
||||
// Shutdown the memory manager
|
||||
g_VMManager.Shutdown();
|
||||
|
||||
|
|
|
@ -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,103 @@ XboxPartitionTable BackupPartTbl =
|
|||
}
|
||||
};
|
||||
|
||||
void CxbxCreatePartitionHeaderFile(std::string filename, bool partition0 = false)
|
||||
io_mu_metadata *g_io_mu_metadata = nullptr;
|
||||
|
||||
io_mu_metadata::io_mu_metadata(const std::wstring_view root_path) : m_root_path(root_path)
|
||||
{
|
||||
for (unsigned i = 0; i < 8; ++i) {
|
||||
m_buff[i] = new char[sizeof(FATX_SUPERBLOCK)];
|
||||
assert(m_buff[i] != nullptr);
|
||||
std::wstring path = m_root_path + static_cast<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;
|
||||
}
|
||||
|
||||
EmuDirPath hybrid_path;
|
||||
FindEmuDirPathByDevice(DeviceCdrom0, hybrid_path);
|
||||
if (hybrid_path.HostDirPath != "") {
|
||||
return DeviceType::Cdrom0;
|
||||
}
|
||||
|
||||
return DeviceType::Invalid;
|
||||
}
|
||||
|
||||
void CxbxCreatePartitionHeaderFile(std::string filename, bool partition0 = false, std::size_t size = 512 * ONE_KB)
|
||||
{
|
||||
HANDLE hf = CreateFile(filename.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
|
||||
if (!hf) {
|
||||
|
@ -98,7 +194,7 @@ void CxbxCreatePartitionHeaderFile(std::string filename, bool partition0 = false
|
|||
WriteFile(hf, &BackupPartTbl, sizeof(XboxPartitionTable), &NumberOfBytesWritten, 0);
|
||||
}
|
||||
|
||||
SetFilePointer(hf, 512 * ONE_KB, 0, FILE_BEGIN);
|
||||
SetFilePointer(hf, size, 0, FILE_BEGIN);
|
||||
SetEndOfFile(hf);
|
||||
CloseHandle(hf);
|
||||
}
|
||||
|
@ -139,7 +235,7 @@ FATX_SUPERBLOCK CxbxGetFatXSuperBlock(int partitionNumber)
|
|||
return superblock;
|
||||
}
|
||||
|
||||
static std::wstring CxbxGetFinalPathNameByHandle(HANDLE hFile)
|
||||
std::wstring CxbxGetFinalPathNameByHandle(HANDLE hFile)
|
||||
{
|
||||
constexpr size_t INITIAL_BUF_SIZE = MAX_PATH;
|
||||
std::wstring path(INITIAL_BUF_SIZE, '\0');
|
||||
|
@ -173,20 +269,30 @@ static bool CxbxIsPathInsideEmuDisk(const std::filesystem::path& path)
|
|||
return match.first == rootPath.end();
|
||||
}
|
||||
|
||||
int CxbxGetPartitionNumberFromHandle(HANDLE hFile)
|
||||
static int CxbxGetPartitionNumber(const std::wstring_view path)
|
||||
{
|
||||
// Get which partition number is being accessed, by parsing the filename and extracting the last portion
|
||||
const std::wstring path = CxbxGetFinalPathNameByHandle(hFile);
|
||||
|
||||
const std::wstring_view partitionString = L"\\EmuDisk\\Partition";
|
||||
const size_t pos = path.rfind(partitionString);
|
||||
if (pos == std::string::npos) {
|
||||
return 0;
|
||||
}
|
||||
const std::wstring partitionNumberString = path.substr(pos + partitionString.length(), 1);
|
||||
const std::wstring_view partitionNumberString = path.substr(pos + partitionString.length(), 1);
|
||||
|
||||
// wcstol returns 0 on non-numeric characters, so we don't need to error check here
|
||||
return wcstol(partitionNumberString.c_str(), nullptr, 0);
|
||||
return wcstol(partitionNumberString.data(), nullptr, 0);
|
||||
}
|
||||
|
||||
int CxbxGetPartitionNumberFromPath(const std::wstring_view path)
|
||||
{
|
||||
return CxbxGetPartitionNumber(path);
|
||||
}
|
||||
|
||||
int CxbxGetPartitionNumberFromHandle(HANDLE hFile)
|
||||
{
|
||||
// Get which partition number is being accessed, by parsing the filename and extracting the last portion
|
||||
const std::wstring path = CxbxGetFinalPathNameByHandle(hFile);
|
||||
|
||||
return CxbxGetPartitionNumber(path);
|
||||
}
|
||||
|
||||
std::filesystem::path CxbxGetPartitionDataPathFromHandle(HANDLE hFile)
|
||||
|
@ -248,7 +354,14 @@ const std::string DriveA = DrivePrefix + "A:"; // A: could be CDROM
|
|||
const std::string DriveC = DrivePrefix + "C:"; // C: is HDD0
|
||||
const std::string DriveD = DrivePrefix + "D:"; // D: is DVD Player
|
||||
const std::string DriveE = DrivePrefix + "E:";
|
||||
const std::string DriveF = DrivePrefix + "F:";
|
||||
const std::string DriveF = DrivePrefix + "F:"; // MU port 0, slot top
|
||||
const std::string DriveG = DrivePrefix + "G:"; // MU port 0, slot bottom
|
||||
const std::string DriveH = DrivePrefix + "H:"; // MU port 1, slot top
|
||||
const std::string DriveI = DrivePrefix + "I:"; // MU port 1, slot bottom
|
||||
const std::string DriveJ = DrivePrefix + "J:"; // MU port 2, slot top
|
||||
const std::string DriveK = DrivePrefix + "K:"; // MU port 2, slot bottom
|
||||
const std::string DriveL = DrivePrefix + "L:"; // MU port 3, slot top
|
||||
const std::string DriveM = DrivePrefix + "M:"; // MU port 3, slot bottom
|
||||
const std::string DriveS = DrivePrefix + "S:";
|
||||
const std::string DriveT = DrivePrefix + "T:"; // T: is Title persistent data region
|
||||
const std::string DriveU = DrivePrefix + "U:"; // U: is User persistent data region
|
||||
|
@ -260,6 +373,7 @@ const std::string DriveZ = DrivePrefix + "Z:"; // Z: is Title utility data regio
|
|||
const std::string DevicePrefix = "\\Device";
|
||||
const std::string DeviceCdrom0 = DevicePrefix + "\\CdRom0";
|
||||
const std::string DeviceHarddisk0 = DevicePrefix + "\\Harddisk0";
|
||||
const std::string DeviceMU = DevicePrefix + "\\MU_";
|
||||
const std::string DeviceHarddisk0PartitionPrefix = DevicePrefix + "\\Harddisk0\\partition";
|
||||
const std::string DeviceHarddisk0Partition0 = DeviceHarddisk0PartitionPrefix + "0"; // Contains raw config sectors (like XBOX_REFURB_INFO) + entire hard disk
|
||||
const std::string DeviceHarddisk0Partition1 = DeviceHarddisk0PartitionPrefix + "1"; // Data partition. Contains TDATA and UDATA folders.
|
||||
|
@ -282,6 +396,14 @@ const std::string DeviceHarddisk0Partition17 = DeviceHarddisk0PartitionPrefix +
|
|||
const std::string DeviceHarddisk0Partition18 = DeviceHarddisk0PartitionPrefix + "18";
|
||||
const std::string DeviceHarddisk0Partition19 = DeviceHarddisk0PartitionPrefix + "19";
|
||||
const std::string DeviceHarddisk0Partition20 = DeviceHarddisk0PartitionPrefix + "20"; // 20 = Largest possible partition number
|
||||
const std::string DeviceMU0 = DeviceMU + "0";
|
||||
const std::string DeviceMU1 = DeviceMU + "1";
|
||||
const std::string DeviceMU2 = DeviceMU + "2";
|
||||
const std::string DeviceMU3 = DeviceMU + "3";
|
||||
const std::string DeviceMU4 = DeviceMU + "4";
|
||||
const std::string DeviceMU5 = DeviceMU + "5";
|
||||
const std::string DeviceMU6 = DeviceMU + "6";
|
||||
const std::string DeviceMU7 = DeviceMU + "7"; // 7 = Largest possible mu number
|
||||
|
||||
EmuNtSymbolicLinkObject* NtSymbolicLinkObjects['Z' - 'A' + 1];
|
||||
std::vector<XboxDevice> Devices;
|
||||
|
@ -675,7 +797,7 @@ std::string CxbxConvertXboxToHostPath(const std::string_view XboxDevicePath)
|
|||
return XbePath;
|
||||
}
|
||||
|
||||
int CxbxRegisterDeviceHostPath(const std::string_view XboxDevicePath, std::string HostDevicePath, bool IsFile)
|
||||
int CxbxRegisterDeviceHostPath(const std::string_view XboxDevicePath, std::string HostDevicePath, bool IsFile, std::size_t size)
|
||||
{
|
||||
XboxDevice newDevice;
|
||||
newDevice.XboxDevicePath = XboxDevicePath;
|
||||
|
@ -683,11 +805,12 @@ int CxbxRegisterDeviceHostPath(const std::string_view XboxDevicePath, std::strin
|
|||
|
||||
bool succeeded{ false };
|
||||
|
||||
// All HDD partitions have a .bin file to allow direct file io on the partition info
|
||||
if (_strnicmp(XboxDevicePath.data(), DeviceHarddisk0PartitionPrefix.c_str(), DeviceHarddisk0PartitionPrefix.length()) == 0) {
|
||||
// All HDD and MU partitions have a .bin file to allow direct file io on the partition info
|
||||
if (_strnicmp(XboxDevicePath.data(), DeviceHarddisk0PartitionPrefix.c_str(), DeviceHarddisk0PartitionPrefix.length()) == 0 ||
|
||||
_strnicmp(XboxDevicePath.data(), DeviceMU.c_str(), DeviceMU.length()) == 0) {
|
||||
std::string partitionHeaderPath = HostDevicePath + ".bin";
|
||||
if (!std::filesystem::exists(partitionHeaderPath)) {
|
||||
CxbxCreatePartitionHeaderFile(partitionHeaderPath, XboxDevicePath == DeviceHarddisk0Partition0);
|
||||
CxbxCreatePartitionHeaderFile(partitionHeaderPath, XboxDevicePath == DeviceHarddisk0Partition0, size);
|
||||
}
|
||||
|
||||
succeeded = true;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
|
||||
#include <core\kernel\exports\xboxkrnl.h>
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
@ -62,17 +63,18 @@ extern const std::string DriveC;
|
|||
extern const std::string DriveD;
|
||||
extern const std::string DriveE;
|
||||
extern const std::string DriveF;
|
||||
extern const std::string DriveS;
|
||||
extern const std::string DriveT;
|
||||
extern const std::string DriveU;
|
||||
extern const std::string DriveV;
|
||||
extern const std::string DriveW;
|
||||
extern const std::string DriveX;
|
||||
extern const std::string DriveY;
|
||||
extern const std::string DriveG;
|
||||
extern const std::string DriveH;
|
||||
extern const std::string DriveI;
|
||||
extern const std::string DriveJ;
|
||||
extern const std::string DriveK;
|
||||
extern const std::string DriveL;
|
||||
extern const std::string DriveM;
|
||||
extern const std::string DriveZ;
|
||||
extern const std::string DevicePrefix;
|
||||
extern const std::string DeviceCdrom0;
|
||||
extern const std::string DeviceHarddisk0;
|
||||
extern const std::string DeviceMU;
|
||||
extern const std::string DeviceHarddisk0PartitionPrefix;
|
||||
extern const std::string DeviceHarddisk0Partition0;
|
||||
extern const std::string DeviceHarddisk0Partition1;
|
||||
|
@ -95,8 +97,28 @@ extern const std::string DeviceHarddisk0Partition17;
|
|||
extern const std::string DeviceHarddisk0Partition18;
|
||||
extern const std::string DeviceHarddisk0Partition19;
|
||||
extern const std::string DeviceHarddisk0Partition20;
|
||||
extern const std::string DeviceMU0;
|
||||
extern const std::string DeviceMU1;
|
||||
extern const std::string DeviceMU2;
|
||||
extern const std::string DeviceMU3;
|
||||
extern const std::string DeviceMU4;
|
||||
extern const std::string DeviceMU5;
|
||||
extern const std::string DeviceMU6;
|
||||
extern const std::string DeviceMU7;
|
||||
constexpr char CxbxAutoMountDriveLetter = 'D';
|
||||
|
||||
enum class DeviceType : int {
|
||||
Invalid = -1,
|
||||
Cdrom0,
|
||||
Harddisk0,
|
||||
MU,
|
||||
};
|
||||
|
||||
inline constexpr xbox::ulong_xt fsctl_dismount_volume = 0x00090020;
|
||||
inline constexpr xbox::ulong_xt fsctl_read_fatx_metadata = 0x0009411C;
|
||||
inline constexpr xbox::ulong_xt fsctl_write_fatx_metadata = 0x00098120;
|
||||
|
||||
inline constexpr std::size_t mu_max_name_lenght = 32 * sizeof(xbox::wchar_xt); // MU names are in wide chars
|
||||
extern std::string CxbxBasePath;
|
||||
extern HANDLE CxbxBasePathHandle;
|
||||
|
||||
|
@ -226,9 +248,31 @@ struct EmuDirPath {
|
|||
HANDLE HostDirHandle;
|
||||
};
|
||||
|
||||
typedef struct _fatx_volume_metadata {
|
||||
xbox::dword_xt offset;
|
||||
xbox::dword_xt length;
|
||||
xbox::PVOID buffer;
|
||||
} fatx_volume_metadata, *pfatx_volume_metadata;
|
||||
|
||||
CHAR* NtStatusToString(IN NTSTATUS Status);
|
||||
|
||||
int CxbxRegisterDeviceHostPath(std::string_view XboxFullPath, std::string HostDevicePath, bool IsFile = false);
|
||||
class io_mu_metadata
|
||||
{
|
||||
public:
|
||||
io_mu_metadata(const std::wstring_view root_path);
|
||||
~io_mu_metadata();
|
||||
void read(const wchar_t lett, std::size_t offset, char *buff, std::size_t size);
|
||||
void write(const wchar_t lett, std::size_t offset, const char *buff, std::size_t size);
|
||||
void flush(const wchar_t lett);
|
||||
|
||||
private:
|
||||
char *m_buff[8];
|
||||
const std::wstring m_root_path;
|
||||
std::shared_mutex m_rw_lock;
|
||||
};
|
||||
extern io_mu_metadata *g_io_mu_metadata;
|
||||
|
||||
int CxbxRegisterDeviceHostPath(std::string_view XboxFullPath, std::string HostDevicePath, bool IsFile = false, std::size_t size = 512 * ONE_KB);
|
||||
int CxbxDeviceIndexByDevicePath(const char *XboxDevicePath);
|
||||
XboxDevice* CxbxDeviceByDevicePath(const std::string_view XboxDevicePath);
|
||||
XboxDevice* CxbxDeviceByHostPath(const std::string_view HostPath);
|
||||
|
@ -239,6 +283,7 @@ EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDriveLetter(const char DriveL
|
|||
EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByName(std::string SymbolicLinkName);
|
||||
void FindEmuDirPathByDevice(std::string DeviceName, EmuDirPath& hybrid_path);
|
||||
EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(HANDLE Handle);
|
||||
DeviceType CxbxrGetDeviceTypeFromHandle(HANDLE hFile);
|
||||
void CleanupSymbolicLinks();
|
||||
|
||||
HANDLE CxbxGetDeviceNativeRootHandle(std::string XboxFullPath);
|
||||
|
@ -305,19 +350,30 @@ typedef struct
|
|||
XboxPartitionTableEntry TableEntries[14];
|
||||
} XboxPartitionTable;
|
||||
|
||||
inline constexpr xbox::ulong_xt fatx_name_length = 32;
|
||||
inline constexpr xbox::ulong_xt fatx_online_data_length = 2048;
|
||||
inline constexpr xbox::ulong_xt fatx_reserved_length = 1968;
|
||||
|
||||
inline constexpr xbox::ulong_xt fatx_signature = 'XTAF';
|
||||
|
||||
typedef struct _FATX_SUPERBLOCK
|
||||
{
|
||||
char Tag[4];
|
||||
unsigned int VolumeID;
|
||||
unsigned int ClusterSize;
|
||||
USHORT FatCopies;
|
||||
int Resvd;
|
||||
char Unused[4078];
|
||||
xbox::ulong_xt Signature;
|
||||
xbox::ulong_xt VolumeID;
|
||||
xbox::ulong_xt ClusterSize;
|
||||
xbox::ulong_xt FatCopies;
|
||||
xbox::wchar_xt Name[fatx_name_length];
|
||||
xbox::uchar_xt OnlineData[fatx_online_data_length];
|
||||
xbox::uchar_xt Unused[fatx_reserved_length];
|
||||
} FATX_SUPERBLOCK;
|
||||
|
||||
static_assert(sizeof(FATX_SUPERBLOCK) == PAGE_SIZE);
|
||||
|
||||
XboxPartitionTable CxbxGetPartitionTable();
|
||||
FATX_SUPERBLOCK CxbxGetFatXSuperBlock(int partitionNumber);
|
||||
int CxbxGetPartitionNumberFromHandle(HANDLE hFile);
|
||||
int CxbxGetPartitionNumberFromPath(const std::wstring_view path);
|
||||
std::wstring CxbxGetFinalPathNameByHandle(HANDLE hFile);
|
||||
std::filesystem::path CxbxGetPartitionDataPathFromHandle(HANDLE hFile);
|
||||
void CxbxFormatPartitionByHandle(HANDLE hFile);
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@ void SyncInputSettings(int port_num, int dev_type, bool is_opt)
|
|||
if (!is_opt) {
|
||||
// Sync updated input to kernel process to use run-time settings.
|
||||
g_EmuShared->SetInputDevTypeSettings(&g_Settings->m_input_port[port_num].Type, port_num);
|
||||
g_EmuShared->SetInputSlotTypeSettings(&g_Settings->m_input_port[port_num].SlotType[SLOT_TOP], port_num, SLOT_TOP);
|
||||
g_EmuShared->SetInputSlotTypeSettings(&g_Settings->m_input_port[port_num].SlotType[SLOT_BOTTOM], port_num, SLOT_BOTTOM);
|
||||
|
||||
if (dev_type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
|
||||
std::string dev_name = g_Settings->m_input_port[port_num].DeviceName;
|
||||
|
@ -77,12 +79,8 @@ void SyncInputSettings(int port_num, int dev_type, bool is_opt)
|
|||
g_EmuShared->SetInputGeneralSettings(&g_Settings->m_input_general);
|
||||
port_num = PORT_INVALID;
|
||||
}
|
||||
#if 0 // lle usb
|
||||
ipc_send_kernel_update(IPC_UPDATE_KERNEL::CONFIG_INPUT_SYNC, PORT_DEC(Gui2XboxPortArray[port_num]),
|
||||
reinterpret_cast<std::uintptr_t>(g_ChildWnd));
|
||||
#else
|
||||
|
||||
ipc_send_kernel_update(IPC_UPDATE_KERNEL::CONFIG_INPUT_SYNC, port_num, reinterpret_cast<std::uintptr_t>(g_ChildWnd));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,6 +161,13 @@ INT_PTR CALLBACK DlgInputConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPAR
|
|||
HWND hHandle = GetDlgItem(hWndDlg, IDC_DEVICE_PORT1 + port);
|
||||
int DeviceType = SendMessage(hHandle, CB_GETITEMDATA, SendMessage(hHandle, CB_GETCURSEL, 0, 0), 0);
|
||||
g_Settings->m_input_port[port].Type = DeviceType;
|
||||
if (DeviceType != to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE) &&
|
||||
DeviceType != to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S)) {
|
||||
// Forcefully set the child devices to none. This will happen if the user sets MUs in the controller dialog but
|
||||
// then they set the parent device to a device that cannot support them in the input dialog
|
||||
g_Settings->m_input_port[port].SlotType[SLOT_TOP] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
||||
g_Settings->m_input_port[port].SlotType[SLOT_BOTTOM] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
||||
}
|
||||
SyncInputSettings(port, DeviceType, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ void DukeInputWindow::Initialize(HWND hwnd, int port_num, int dev_type)
|
|||
m_hwnd_device_list = GetDlgItem(m_hwnd_window, IDC_DEVICE_LIST);
|
||||
m_hwnd_profile_list = GetDlgItem(m_hwnd_window, IDC_PROFILE_NAME);
|
||||
m_hwnd_default = GetDlgItem(m_hwnd_window, IDC_DEFAULT);
|
||||
m_hwnd_slot_list[SLOT_TOP] = GetDlgItem(hwnd, IDC_DEVICE_LIST_TOP_SLOT);
|
||||
m_hwnd_slot_list[SLOT_BOTTOM] = GetDlgItem(hwnd, IDC_DEVICE_LIST_BOTTOM_SLOT);
|
||||
m_dev_type = dev_type;
|
||||
m_max_num_buttons = dev_num_buttons[dev_type];
|
||||
m_port_num = port_num;
|
||||
|
@ -79,11 +81,35 @@ void DukeInputWindow::Initialize(HWND hwnd, int port_num, int dev_type)
|
|||
|
||||
}
|
||||
SendMessage(m_hwnd_window, WM_SETTEXT, 0,
|
||||
reinterpret_cast<LPARAM>((title + std::to_string(PORT_INC(m_port_num))).c_str()));
|
||||
reinterpret_cast<LPARAM>((title + PortUserFormat(std::to_string(m_port_num))).c_str()));
|
||||
|
||||
// Set the maximum profile name lenght the user can enter in the profile combobox
|
||||
SendMessage(m_hwnd_profile_list, CB_LIMITTEXT, 49, 0);
|
||||
|
||||
if (m_dev_type == to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK)) {
|
||||
// The arcade joystick does not have slot ports so we always disable the corresponding options
|
||||
for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) {
|
||||
LRESULT index = SendMessage(m_hwnd_slot_list[slot], CB_ADDSTRING, 0,
|
||||
reinterpret_cast<LPARAM>(GetInputDeviceName(to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)).c_str()));
|
||||
SendMessage(m_hwnd_slot_list[slot], CB_SETITEMDATA, index, to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID));
|
||||
SendMessage(m_hwnd_slot_list[slot], CB_SETCURSEL, index, 0);
|
||||
EnableWindow(m_hwnd_slot_list[slot], FALSE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Set up the device types we support in the slot ports
|
||||
for (auto slot_type : slot_support_list) {
|
||||
for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) {
|
||||
LRESULT index = SendMessage(m_hwnd_slot_list[slot], CB_ADDSTRING, 0,
|
||||
reinterpret_cast<LPARAM>(GetInputDeviceName(to_underlying(slot_type)).c_str()));
|
||||
SendMessage(m_hwnd_slot_list[slot], CB_SETITEMDATA, index, to_underlying(slot_type));
|
||||
if (g_Settings->m_input_port[m_port_num].SlotType[slot] == to_underlying(slot_type)) {
|
||||
SendMessage(m_hwnd_slot_list[slot], CB_SETCURSEL, index, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// construct emu device
|
||||
m_DeviceConfig = new EmuDevice(m_dev_type, m_hwnd_window, this);
|
||||
|
||||
|
@ -181,32 +207,12 @@ void DukeInputWindow::UpdateProfile(const std::string &name, int command)
|
|||
{
|
||||
switch (command)
|
||||
{
|
||||
case PROFILE_LOAD: {
|
||||
LoadProfile(name);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROFILE_SAVE: {
|
||||
SaveProfile(name);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROFILE_DELETE: {
|
||||
DeleteProfile(name);
|
||||
}
|
||||
break;
|
||||
|
||||
case RUMBLE_CLEAR: {
|
||||
case RUMBLE_CLEAR:
|
||||
m_rumble = std::string();
|
||||
}
|
||||
break;
|
||||
|
||||
case BUTTON_CLEAR:
|
||||
case BUTTON_SWAP: {
|
||||
m_bHasChanges = true;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
default:
|
||||
InputWindow::UpdateProfile(name, command);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,6 +245,14 @@ void DukeInputWindow::DetectOutput(int ms)
|
|||
}
|
||||
}
|
||||
|
||||
void DukeInputWindow::SaveSlotConfig()
|
||||
{
|
||||
for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) {
|
||||
g_Settings->m_input_port[m_port_num].SlotType[slot] = SendMessage(m_hwnd_slot_list[slot], CB_GETITEMDATA,
|
||||
SendMessage(m_hwnd_slot_list[slot], CB_GETCURSEL, 0, 0), 0);
|
||||
}
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK DlgRumbleConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
INT_PTR CALLBACK DlgXidControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
|
@ -266,6 +280,7 @@ INT_PTR CALLBACK DlgXidControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wPar
|
|||
case WM_CLOSE:
|
||||
{
|
||||
if (g_InputWindow->IsProfileSaved()) {
|
||||
g_InputWindow->SaveSlotConfig();
|
||||
delete g_InputWindow;
|
||||
g_InputWindow = nullptr;
|
||||
EndDialog(hWndDlg, wParam);
|
||||
|
|
|
@ -54,7 +54,7 @@ void SbcInputWindow::Initialize(HWND hwnd, int port_num, int dev_type)
|
|||
// Set window title
|
||||
std::string title("Steel Battalion Controller at port ");
|
||||
SendMessage(m_hwnd_window, WM_SETTEXT, 0,
|
||||
reinterpret_cast<LPARAM>((title + std::to_string(PORT_INC(m_port_num))).c_str()));
|
||||
reinterpret_cast<LPARAM>((title + PortUserFormat(std::to_string(m_port_num))).c_str()));
|
||||
|
||||
// Set the maximum profile name lenght the user can enter in the profile combobox
|
||||
SendMessage(m_hwnd_profile_list, CB_LIMITTEXT, 49, 0);
|
||||
|
@ -81,40 +81,19 @@ void SbcInputWindow::ClearBindings()
|
|||
m_bHasChanges = true;
|
||||
}
|
||||
|
||||
void SbcInputWindow::UpdateProfile(const std::string &name, int command)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case PROFILE_LOAD: {
|
||||
LoadProfile(name);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROFILE_SAVE: {
|
||||
SaveProfile(name);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROFILE_DELETE: {
|
||||
DeleteProfile(name);
|
||||
}
|
||||
break;
|
||||
|
||||
case BUTTON_CLEAR:
|
||||
case BUTTON_SWAP: {
|
||||
m_bHasChanges = true;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int SbcInputWindow::EnableDefaultButton()
|
||||
{
|
||||
// The SBC window does not have a default button, so we return a dummy value here
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SbcInputWindow::SaveSlotConfig()
|
||||
{
|
||||
for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) {
|
||||
g_Settings->m_input_port[m_port_num].SlotType[slot] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK DlgSBControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
|
@ -138,6 +117,7 @@ INT_PTR CALLBACK DlgSBControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wPara
|
|||
case WM_CLOSE:
|
||||
{
|
||||
if (g_InputWindow->IsProfileSaved()) {
|
||||
g_InputWindow->SaveSlotConfig();
|
||||
delete g_InputWindow;
|
||||
g_InputWindow = nullptr;
|
||||
EndDialog(hWndDlg, wParam);
|
||||
|
|
|
@ -31,6 +31,10 @@ BEGIN
|
|||
BEGIN
|
||||
END
|
||||
|
||||
IDD_XID_DUKE_CFG, DIALOG
|
||||
BEGIN
|
||||
END
|
||||
|
||||
IDD_SBC_CFG, DIALOG
|
||||
BEGIN
|
||||
BOTTOMMARGIN, 269
|
||||
|
@ -113,7 +117,7 @@ BEGIN
|
|||
"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,23,166,139,10
|
||||
END
|
||||
|
||||
IDD_XID_DUKE_CFG DIALOGEX 0, 0, 528, 280
|
||||
IDD_XID_DUKE_CFG DIALOGEX 0, 0, 528, 300
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
FONT 8, "Verdana", 0, 0, 0x1
|
||||
BEGIN
|
||||
|
@ -180,8 +184,12 @@ BEGIN
|
|||
GROUPBOX "Rumble",IDC_RUMBLE,396,212,121,34,WS_GROUP
|
||||
PUSHBUTTON "",IDC_SET_MOTOR,443,224,57,14,BS_FLAT
|
||||
LTEXT "Motor",IDC_STATIC,412,224,26,14,SS_CENTERIMAGE
|
||||
PUSHBUTTON "Default Bindings",IDC_DEFAULT,362,256,69,14,BS_FLAT
|
||||
PUSHBUTTON "Clear",IDC_CLEAR,443,256,50,14,BS_FLAT
|
||||
PUSHBUTTON "Default Bindings",IDC_DEFAULT,362,265,69,14,BS_FLAT
|
||||
PUSHBUTTON "Clear",IDC_CLEAR,443,265,50,14,BS_FLAT
|
||||
GROUPBOX "Top Slot",IDC_DEVICE_TOP_SLOT,12,254,97,35,WS_GROUP
|
||||
COMBOBOX IDC_DEVICE_LIST_TOP_SLOT,21,267,79,15,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
GROUPBOX "Bottom Slot",IDC_DEVICE_BOTTOM_SLOT,117,254,97,35,WS_GROUP
|
||||
COMBOBOX IDC_DEVICE_LIST_BOTTOM_SLOT,126,267,79,15,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
END
|
||||
|
||||
IDD_RUMBLE_CFG DIALOGEX 0, 0, 155, 35
|
||||
|
@ -566,6 +574,11 @@ BEGIN
|
|||
0
|
||||
END
|
||||
|
||||
IDD_XID_DUKE_CFG AFX_DIALOG_LAYOUT
|
||||
BEGIN
|
||||
0
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -687,7 +700,7 @@ BEGIN
|
|||
MENUITEM "&Clear entire Symbol Cache", ID_CACHE_CLEARHLECACHE_ALL,MFT_STRING,MFS_ENABLED
|
||||
MENUITEM "&Rescan title Symbol Cache", ID_CACHE_CLEARHLECACHE_CURRENT,MFT_STRING,MFS_ENABLED
|
||||
END
|
||||
MENUITEM "Clear Cache Partitions", ID_SETTINGS_CLEAR_PARTITIONS,MFT_STRING,MFS_ENABLED
|
||||
MENUITEM "Clear Cache Partitions", ID_SETTINGS_CLEAR_PARTITIONS,MFT_STRING,MFS_ENABLED
|
||||
MENUITEM "", -1, MFT_SEPARATOR
|
||||
POPUP "Experimental", 65535,MFT_STRING,MFS_ENABLED
|
||||
BEGIN
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//{{NO_DEPENDENCIES}}
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Cxbx.rc
|
||||
//
|
||||
|
@ -94,6 +94,10 @@
|
|||
#define IDC_LOG_VSHCACHE 962
|
||||
#define IDC_LOG_RINP 963
|
||||
#define IDC_LOG_JVS 964
|
||||
#define IDC_DEVICE_LIST_TOP_SLOT 995
|
||||
#define IDC_DEVICE_LIST_BOTTOM_SLOT 996
|
||||
#define IDC_DEVICE_TOP_SLOT 997
|
||||
#define IDC_DEVICE_BOTTOM_SLOT 998
|
||||
#define IDC_SET_MOTOR 999
|
||||
#define IDC_SET_X 1000
|
||||
#define IDC_SET_Y 1001
|
||||
|
|
Loading…
Reference in New Issue