mirror of
https://github.com/cemu-project/Cemu
synced 2024-07-03 08:18:39 +00:00
Compare commits
3 Commits
d8122036a1
...
f0b7f587d2
Author | SHA1 | Date | |
---|---|---|---|
|
f0b7f587d2 | ||
|
aefbb918be | ||
|
93b58ae6f7 |
|
@ -457,10 +457,14 @@ add_library(CemuCafe
|
||||||
OS/libs/nsyshid/AttachDefaultBackends.cpp
|
OS/libs/nsyshid/AttachDefaultBackends.cpp
|
||||||
OS/libs/nsyshid/Whitelist.cpp
|
OS/libs/nsyshid/Whitelist.cpp
|
||||||
OS/libs/nsyshid/Whitelist.h
|
OS/libs/nsyshid/Whitelist.h
|
||||||
|
OS/libs/nsyshid/BackendEmulated.cpp
|
||||||
|
OS/libs/nsyshid/BackendEmulated.h
|
||||||
OS/libs/nsyshid/BackendLibusb.cpp
|
OS/libs/nsyshid/BackendLibusb.cpp
|
||||||
OS/libs/nsyshid/BackendLibusb.h
|
OS/libs/nsyshid/BackendLibusb.h
|
||||||
OS/libs/nsyshid/BackendWindowsHID.cpp
|
OS/libs/nsyshid/BackendWindowsHID.cpp
|
||||||
OS/libs/nsyshid/BackendWindowsHID.h
|
OS/libs/nsyshid/BackendWindowsHID.h
|
||||||
|
OS/libs/nsyshid/Skylander.cpp
|
||||||
|
OS/libs/nsyshid/Skylander.h
|
||||||
OS/libs/nsyskbd/nsyskbd.cpp
|
OS/libs/nsyskbd/nsyskbd.cpp
|
||||||
OS/libs/nsyskbd/nsyskbd.h
|
OS/libs/nsyskbd/nsyskbd.h
|
||||||
OS/libs/nsysnet/nsysnet.cpp
|
OS/libs/nsysnet/nsysnet.cpp
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "nsyshid.h"
|
#include "nsyshid.h"
|
||||||
#include "Backend.h"
|
#include "Backend.h"
|
||||||
|
#include "BackendEmulated.h"
|
||||||
|
|
||||||
#if NSYSHID_ENABLE_BACKEND_LIBUSB
|
#if NSYSHID_ENABLE_BACKEND_LIBUSB
|
||||||
|
|
||||||
|
@ -37,5 +38,13 @@ namespace nsyshid::backend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // NSYSHID_ENABLE_BACKEND_WINDOWS_HID
|
#endif // NSYSHID_ENABLE_BACKEND_WINDOWS_HID
|
||||||
|
// add emulated backend
|
||||||
|
{
|
||||||
|
auto backendEmulated = std::make_shared<backend::emulated::BackendEmulated>();
|
||||||
|
if (backendEmulated->IsInitialisedOk())
|
||||||
|
{
|
||||||
|
AttachBackend(backendEmulated);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // namespace nsyshid::backend
|
} // namespace nsyshid::backend
|
||||||
|
|
|
@ -23,6 +23,55 @@ namespace nsyshid
|
||||||
/* +0x12 */ uint16be maxPacketSizeTX;
|
/* +0x12 */ uint16be maxPacketSizeTX;
|
||||||
} HID_t;
|
} HID_t;
|
||||||
|
|
||||||
|
struct TransferCommand
|
||||||
|
{
|
||||||
|
uint8* data;
|
||||||
|
sint32 length;
|
||||||
|
|
||||||
|
TransferCommand(uint8* data, sint32 length)
|
||||||
|
: data(data), length(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual ~TransferCommand() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReadMessage final : TransferCommand
|
||||||
|
{
|
||||||
|
sint32 bytesRead;
|
||||||
|
|
||||||
|
ReadMessage(uint8* data, sint32 length, sint32 bytesRead)
|
||||||
|
: bytesRead(bytesRead), TransferCommand(data, length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
using TransferCommand::TransferCommand;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WriteMessage final : TransferCommand
|
||||||
|
{
|
||||||
|
sint32 bytesWritten;
|
||||||
|
|
||||||
|
WriteMessage(uint8* data, sint32 length, sint32 bytesWritten)
|
||||||
|
: bytesWritten(bytesWritten), TransferCommand(data, length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
using TransferCommand::TransferCommand;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReportMessage final : TransferCommand
|
||||||
|
{
|
||||||
|
uint8* reportData;
|
||||||
|
sint32 length;
|
||||||
|
uint8* originalData;
|
||||||
|
sint32 originalLength;
|
||||||
|
|
||||||
|
ReportMessage(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength)
|
||||||
|
: reportData(reportData), length(length), originalData(originalData),
|
||||||
|
originalLength(originalLength), TransferCommand(reportData, length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
using TransferCommand::TransferCommand;
|
||||||
|
};
|
||||||
|
|
||||||
static_assert(offsetof(HID_t, vendorId) == 0x8, "");
|
static_assert(offsetof(HID_t, vendorId) == 0x8, "");
|
||||||
static_assert(offsetof(HID_t, productId) == 0xA, "");
|
static_assert(offsetof(HID_t, productId) == 0xA, "");
|
||||||
static_assert(offsetof(HID_t, ifIndex) == 0xC, "");
|
static_assert(offsetof(HID_t, ifIndex) == 0xC, "");
|
||||||
|
@ -69,7 +118,7 @@ namespace nsyshid
|
||||||
ErrorTimeout,
|
ErrorTimeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ReadResult Read(uint8* data, sint32 length, sint32& bytesRead) = 0;
|
virtual ReadResult Read(ReadMessage* message) = 0;
|
||||||
|
|
||||||
enum class WriteResult
|
enum class WriteResult
|
||||||
{
|
{
|
||||||
|
@ -78,7 +127,7 @@ namespace nsyshid
|
||||||
ErrorTimeout,
|
ErrorTimeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual WriteResult Write(uint8* data, sint32 length, sint32& bytesWritten) = 0;
|
virtual WriteResult Write(WriteMessage* message) = 0;
|
||||||
|
|
||||||
virtual bool GetDescriptor(uint8 descType,
|
virtual bool GetDescriptor(uint8 descType,
|
||||||
uint8 descIndex,
|
uint8 descIndex,
|
||||||
|
@ -86,9 +135,9 @@ namespace nsyshid
|
||||||
uint8* output,
|
uint8* output,
|
||||||
uint32 outputMaxLength) = 0;
|
uint32 outputMaxLength) = 0;
|
||||||
|
|
||||||
virtual bool SetProtocol(uint32 ifIndef, uint32 protocol) = 0;
|
virtual bool SetProtocol(uint8 ifIndef, uint8 protocol) = 0;
|
||||||
|
|
||||||
virtual bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) = 0;
|
virtual bool SetReport(ReportMessage* message) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Backend {
|
class Backend {
|
||||||
|
@ -121,6 +170,8 @@ namespace nsyshid
|
||||||
|
|
||||||
std::shared_ptr<Device> FindDevice(std::function<bool(const std::shared_ptr<Device>&)> isWantedDevice);
|
std::shared_ptr<Device> FindDevice(std::function<bool(const std::shared_ptr<Device>&)> isWantedDevice);
|
||||||
|
|
||||||
|
bool FindDeviceById(uint16 vendorId, uint16 productId);
|
||||||
|
|
||||||
bool IsDeviceWhitelisted(uint16 vendorId, uint16 productId);
|
bool IsDeviceWhitelisted(uint16 vendorId, uint16 productId);
|
||||||
|
|
||||||
// called from OnAttach() - attach devices that your backend can see here
|
// called from OnAttach() - attach devices that your backend can see here
|
||||||
|
|
29
src/Cafe/OS/libs/nsyshid/BackendEmulated.cpp
Normal file
29
src/Cafe/OS/libs/nsyshid/BackendEmulated.cpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#include "BackendEmulated.h"
|
||||||
|
#include "Skylander.h"
|
||||||
|
#include "config/CemuConfig.h"
|
||||||
|
|
||||||
|
namespace nsyshid::backend::emulated
|
||||||
|
{
|
||||||
|
BackendEmulated::BackendEmulated()
|
||||||
|
{
|
||||||
|
cemuLog_logDebug(LogType::Force, "nsyshid::BackendEmulated: emulated backend initialised");
|
||||||
|
}
|
||||||
|
|
||||||
|
BackendEmulated::~BackendEmulated() = default;
|
||||||
|
|
||||||
|
bool BackendEmulated::IsInitialisedOk()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackendEmulated::AttachVisibleDevices()
|
||||||
|
{
|
||||||
|
if (GetConfig().emulated_usb_devices.emulate_skylander_portal && !FindDeviceById(0x1430, 0x0150))
|
||||||
|
{
|
||||||
|
cemuLog_logDebug(LogType::Force, "Attaching Emulated Portal");
|
||||||
|
// Add Skylander Portal
|
||||||
|
auto device = std::make_shared<SkylanderPortalDevice>();
|
||||||
|
AttachDevice(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace nsyshid::backend::emulated
|
16
src/Cafe/OS/libs/nsyshid/BackendEmulated.h
Normal file
16
src/Cafe/OS/libs/nsyshid/BackendEmulated.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include "nsyshid.h"
|
||||||
|
#include "Backend.h"
|
||||||
|
|
||||||
|
namespace nsyshid::backend::emulated
|
||||||
|
{
|
||||||
|
class BackendEmulated : public nsyshid::Backend {
|
||||||
|
public:
|
||||||
|
BackendEmulated();
|
||||||
|
~BackendEmulated();
|
||||||
|
|
||||||
|
bool IsInitialisedOk() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void AttachVisibleDevices() override;
|
||||||
|
};
|
||||||
|
} // namespace nsyshid::backend::emulated
|
|
@ -230,6 +230,17 @@ namespace nsyshid::backend::libusb
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<int, ConfigDescriptor> MakeConfigDescriptor(libusb_device* device, uint8 config_num)
|
||||||
|
{
|
||||||
|
libusb_config_descriptor* descriptor = nullptr;
|
||||||
|
const int ret = libusb_get_config_descriptor(device, config_num, &descriptor);
|
||||||
|
if (ret == LIBUSB_SUCCESS)
|
||||||
|
return {ret, ConfigDescriptor{descriptor, libusb_free_config_descriptor}};
|
||||||
|
|
||||||
|
return {ret, ConfigDescriptor{nullptr, [](auto) {
|
||||||
|
}}};
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Device> BackendLibusb::CheckAndCreateDevice(libusb_device* dev)
|
std::shared_ptr<Device> BackendLibusb::CheckAndCreateDevice(libusb_device* dev)
|
||||||
{
|
{
|
||||||
struct libusb_device_descriptor desc;
|
struct libusb_device_descriptor desc;
|
||||||
|
@ -241,6 +252,17 @@ namespace nsyshid::backend::libusb
|
||||||
ret);
|
ret);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
std::vector<ConfigDescriptor> config_descriptors{};
|
||||||
|
for (uint8 i = 0; i < desc.bNumConfigurations; ++i)
|
||||||
|
{
|
||||||
|
auto [ret, config_descriptor] = MakeConfigDescriptor(dev, i);
|
||||||
|
if (ret != LIBUSB_SUCCESS || !config_descriptor)
|
||||||
|
{
|
||||||
|
cemuLog_log(LogType::Force, "Failed to make config descriptor {} for {:04x}:{:04x}: {}",
|
||||||
|
i, desc.idVendor, desc.idProduct, libusb_error_name(ret));
|
||||||
|
}
|
||||||
|
config_descriptors.emplace_back(std::move(config_descriptor));
|
||||||
|
}
|
||||||
if (desc.idVendor == 0x0e6f && desc.idProduct == 0x0241)
|
if (desc.idVendor == 0x0e6f && desc.idProduct == 0x0241)
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_logDebug(LogType::Force,
|
||||||
|
@ -253,7 +275,8 @@ namespace nsyshid::backend::libusb
|
||||||
2,
|
2,
|
||||||
0,
|
0,
|
||||||
libusb_get_bus_number(dev),
|
libusb_get_bus_number(dev),
|
||||||
libusb_get_device_address(dev));
|
libusb_get_device_address(dev),
|
||||||
|
std::move(config_descriptors));
|
||||||
// figure out device endpoints
|
// figure out device endpoints
|
||||||
if (!FindDefaultDeviceEndpoints(dev,
|
if (!FindDefaultDeviceEndpoints(dev,
|
||||||
device->m_libusbHasEndpointIn,
|
device->m_libusbHasEndpointIn,
|
||||||
|
@ -335,7 +358,8 @@ namespace nsyshid::backend::libusb
|
||||||
uint8 interfaceSubClass,
|
uint8 interfaceSubClass,
|
||||||
uint8 protocol,
|
uint8 protocol,
|
||||||
uint8 libusbBusNumber,
|
uint8 libusbBusNumber,
|
||||||
uint8 libusbDeviceAddress)
|
uint8 libusbDeviceAddress,
|
||||||
|
std::vector<ConfigDescriptor> configs)
|
||||||
: Device(vendorId,
|
: Device(vendorId,
|
||||||
productId,
|
productId,
|
||||||
interfaceIndex,
|
interfaceIndex,
|
||||||
|
@ -351,6 +375,7 @@ namespace nsyshid::backend::libusb
|
||||||
m_libusbHasEndpointOut(false),
|
m_libusbHasEndpointOut(false),
|
||||||
m_libusbEndpointOut(0)
|
m_libusbEndpointOut(0)
|
||||||
{
|
{
|
||||||
|
m_config_descriptors = std::move(configs);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceLibusb::~DeviceLibusb()
|
DeviceLibusb::~DeviceLibusb()
|
||||||
|
@ -418,20 +443,8 @@ namespace nsyshid::backend::libusb
|
||||||
}
|
}
|
||||||
this->m_handleInUseCounter = 0;
|
this->m_handleInUseCounter = 0;
|
||||||
}
|
}
|
||||||
if (libusb_kernel_driver_active(this->m_libusbHandle, 0) == 1)
|
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): kernel driver active");
|
int ret = ClaimAllInterfaces(0);
|
||||||
if (libusb_detach_kernel_driver(this->m_libusbHandle, 0) == 0)
|
|
||||||
{
|
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): kernel driver detached");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): failed to detach kernel driver");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
int ret = libusb_claim_interface(this->m_libusbHandle, 0);
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): cannot claim interface");
|
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): cannot claim interface");
|
||||||
|
@ -471,7 +484,7 @@ namespace nsyshid::backend::libusb
|
||||||
return m_libusbHandle != nullptr && m_handleInUseCounter >= 0;
|
return m_libusbHandle != nullptr && m_handleInUseCounter >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Device::ReadResult DeviceLibusb::Read(uint8* data, sint32 length, sint32& bytesRead)
|
Device::ReadResult DeviceLibusb::Read(ReadMessage* message)
|
||||||
{
|
{
|
||||||
auto handleLock = AquireHandleLock();
|
auto handleLock = AquireHandleLock();
|
||||||
if (!handleLock->IsValid())
|
if (!handleLock->IsValid())
|
||||||
|
@ -488,8 +501,8 @@ namespace nsyshid::backend::libusb
|
||||||
{
|
{
|
||||||
ret = libusb_bulk_transfer(handleLock->GetHandle(),
|
ret = libusb_bulk_transfer(handleLock->GetHandle(),
|
||||||
this->m_libusbEndpointIn,
|
this->m_libusbEndpointIn,
|
||||||
data,
|
message->data,
|
||||||
length,
|
message->length,
|
||||||
&actualLength,
|
&actualLength,
|
||||||
timeout);
|
timeout);
|
||||||
}
|
}
|
||||||
|
@ -500,8 +513,8 @@ namespace nsyshid::backend::libusb
|
||||||
// success
|
// success
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::read(): read {} of {} bytes",
|
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::read(): read {} of {} bytes",
|
||||||
actualLength,
|
actualLength,
|
||||||
length);
|
message->length);
|
||||||
bytesRead = actualLength;
|
message->bytesRead = actualLength;
|
||||||
return ReadResult::Success;
|
return ReadResult::Success;
|
||||||
}
|
}
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_logDebug(LogType::Force,
|
||||||
|
@ -510,7 +523,7 @@ namespace nsyshid::backend::libusb
|
||||||
return ReadResult::Error;
|
return ReadResult::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
Device::WriteResult DeviceLibusb::Write(uint8* data, sint32 length, sint32& bytesWritten)
|
Device::WriteResult DeviceLibusb::Write(WriteMessage* message)
|
||||||
{
|
{
|
||||||
auto handleLock = AquireHandleLock();
|
auto handleLock = AquireHandleLock();
|
||||||
if (!handleLock->IsValid())
|
if (!handleLock->IsValid())
|
||||||
|
@ -520,23 +533,23 @@ namespace nsyshid::backend::libusb
|
||||||
return WriteResult::Error;
|
return WriteResult::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesWritten = 0;
|
message->bytesWritten = 0;
|
||||||
int actualLength = 0;
|
int actualLength = 0;
|
||||||
int ret = libusb_bulk_transfer(handleLock->GetHandle(),
|
int ret = libusb_bulk_transfer(handleLock->GetHandle(),
|
||||||
this->m_libusbEndpointOut,
|
this->m_libusbEndpointOut,
|
||||||
data,
|
message->data,
|
||||||
length,
|
message->length,
|
||||||
&actualLength,
|
&actualLength,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
// success
|
// success
|
||||||
bytesWritten = actualLength;
|
message->bytesWritten = actualLength;
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_logDebug(LogType::Force,
|
||||||
"nsyshid::DeviceLibusb::write(): wrote {} of {} bytes",
|
"nsyshid::DeviceLibusb::write(): wrote {} of {} bytes",
|
||||||
bytesWritten,
|
message->bytesWritten,
|
||||||
length);
|
message->length);
|
||||||
return WriteResult::Success;
|
return WriteResult::Success;
|
||||||
}
|
}
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_logDebug(LogType::Force,
|
||||||
|
@ -685,7 +698,65 @@ namespace nsyshid::backend::libusb
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeviceLibusb::SetProtocol(uint32 ifIndex, uint32 protocol)
|
template<typename Configs, typename Function>
|
||||||
|
static int DoForEachInterface(const Configs& configs, uint8 config_num, Function action)
|
||||||
|
{
|
||||||
|
int ret = LIBUSB_ERROR_NOT_FOUND;
|
||||||
|
if (configs.size() <= config_num || !configs[config_num])
|
||||||
|
return ret;
|
||||||
|
for (uint8 i = 0; i < configs[config_num]->bNumInterfaces; ++i)
|
||||||
|
{
|
||||||
|
ret = action(i);
|
||||||
|
if (ret < LIBUSB_SUCCESS)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DeviceLibusb::ClaimAllInterfaces(uint8 config_num)
|
||||||
|
{
|
||||||
|
const int ret = DoForEachInterface(m_config_descriptors, config_num, [this](uint8 i) {
|
||||||
|
if (libusb_kernel_driver_active(this->m_libusbHandle, i))
|
||||||
|
{
|
||||||
|
const int ret2 = libusb_detach_kernel_driver(this->m_libusbHandle, i);
|
||||||
|
if (ret2 < LIBUSB_SUCCESS && ret2 != LIBUSB_ERROR_NOT_FOUND &&
|
||||||
|
ret2 != LIBUSB_ERROR_NOT_SUPPORTED)
|
||||||
|
{
|
||||||
|
cemuLog_log(LogType::Force, "Failed to detach kernel driver {}", libusb_error_name(ret2));
|
||||||
|
return ret2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return libusb_claim_interface(this->m_libusbHandle, i);
|
||||||
|
});
|
||||||
|
if (ret < LIBUSB_SUCCESS)
|
||||||
|
{
|
||||||
|
cemuLog_log(LogType::Force, "Failed to release all interfaces for config {}", config_num);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DeviceLibusb::ReleaseAllInterfaces(uint8 config_num)
|
||||||
|
{
|
||||||
|
const int ret = DoForEachInterface(m_config_descriptors, config_num, [this](uint8 i) {
|
||||||
|
return libusb_release_interface(AquireHandleLock()->GetHandle(), i);
|
||||||
|
});
|
||||||
|
if (ret < LIBUSB_SUCCESS && ret != LIBUSB_ERROR_NO_DEVICE && ret != LIBUSB_ERROR_NOT_FOUND)
|
||||||
|
{
|
||||||
|
cemuLog_log(LogType::Force, "Failed to release all interfaces for config {}", config_num);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DeviceLibusb::ReleaseAllInterfacesForCurrentConfig()
|
||||||
|
{
|
||||||
|
int config_num;
|
||||||
|
const int get_config_ret = libusb_get_configuration(AquireHandleLock()->GetHandle(), &config_num);
|
||||||
|
if (get_config_ret < LIBUSB_SUCCESS)
|
||||||
|
return get_config_ret;
|
||||||
|
return ReleaseAllInterfaces(config_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceLibusb::SetProtocol(uint8 ifIndex, uint8 protocol)
|
||||||
{
|
{
|
||||||
auto handleLock = AquireHandleLock();
|
auto handleLock = AquireHandleLock();
|
||||||
if (!handleLock->IsValid())
|
if (!handleLock->IsValid())
|
||||||
|
@ -693,28 +764,21 @@ namespace nsyshid::backend::libusb
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetProtocol(): device is not opened");
|
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetProtocol(): device is not opened");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (m_interfaceIndex != ifIndex)
|
||||||
|
m_interfaceIndex = ifIndex;
|
||||||
|
|
||||||
// ToDo: implement this
|
ReleaseAllInterfacesForCurrentConfig();
|
||||||
#if 0
|
int ret = libusb_set_configuration(AquireHandleLock()->GetHandle(), protocol);
|
||||||
// is this correct? Discarding "ifIndex" seems like a bad idea
|
if (ret == LIBUSB_SUCCESS)
|
||||||
int ret = libusb_set_configuration(handleLock->getHandle(), protocol);
|
ret = ClaimAllInterfaces(protocol);
|
||||||
if (ret == 0) {
|
|
||||||
cemuLog_logDebug(LogType::Force,
|
if (ret == LIBUSB_SUCCESS)
|
||||||
"nsyshid::DeviceLibusb::setProtocol(): success");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
cemuLog_logDebug(LogType::Force,
|
|
||||||
"nsyshid::DeviceLibusb::setProtocol(): failed with error code: {}",
|
|
||||||
ret);
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// pretend that everything is fine
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeviceLibusb::SetReport(uint8* reportData, sint32 length, uint8* originalData,
|
bool DeviceLibusb::SetReport(ReportMessage* message)
|
||||||
sint32 originalLength)
|
|
||||||
{
|
{
|
||||||
auto handleLock = AquireHandleLock();
|
auto handleLock = AquireHandleLock();
|
||||||
if (!handleLock->IsValid())
|
if (!handleLock->IsValid())
|
||||||
|
@ -723,20 +787,20 @@ namespace nsyshid::backend::libusb
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToDo: implement this
|
int ret = libusb_control_transfer(handleLock->GetHandle(),
|
||||||
#if 0
|
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
|
||||||
// not sure if libusb_control_transfer() is the right candidate for this
|
LIBUSB_REQUEST_SET_CONFIGURATION,
|
||||||
int ret = libusb_control_transfer(handleLock->getHandle(),
|
512,
|
||||||
bmRequestType,
|
0,
|
||||||
bRequest,
|
message->originalData,
|
||||||
wValue,
|
message->originalLength,
|
||||||
wIndex,
|
0);
|
||||||
reportData,
|
|
||||||
length,
|
|
||||||
timeout);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// pretend that everything is fine
|
if (ret != message->originalLength)
|
||||||
|
{
|
||||||
|
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetReport(): Control Transfer Failed: {}", libusb_error_name(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "nsyshid.h"
|
#include "nsyshid.h"
|
||||||
|
|
||||||
#if NSYSHID_ENABLE_BACKEND_LIBUSB
|
#if 1
|
||||||
|
|
||||||
#include <libusb-1.0/libusb.h>
|
#include <libusb-1.0/libusb.h>
|
||||||
#include "Backend.h"
|
#include "Backend.h"
|
||||||
|
@ -44,6 +44,11 @@ namespace nsyshid::backend::libusb
|
||||||
bool& endpointOutFound, uint8& endpointOut, uint16& endpointOutMaxPacketSize);
|
bool& endpointOutFound, uint8& endpointOut, uint16& endpointOutMaxPacketSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using UniquePtr = std::unique_ptr<T, void (*)(T*)>;
|
||||||
|
|
||||||
|
using ConfigDescriptor = UniquePtr<libusb_config_descriptor>;
|
||||||
|
|
||||||
class DeviceLibusb : public nsyshid::Device {
|
class DeviceLibusb : public nsyshid::Device {
|
||||||
public:
|
public:
|
||||||
DeviceLibusb(libusb_context* ctx,
|
DeviceLibusb(libusb_context* ctx,
|
||||||
|
@ -53,7 +58,8 @@ namespace nsyshid::backend::libusb
|
||||||
uint8 interfaceSubClass,
|
uint8 interfaceSubClass,
|
||||||
uint8 protocol,
|
uint8 protocol,
|
||||||
uint8 libusbBusNumber,
|
uint8 libusbBusNumber,
|
||||||
uint8 libusbDeviceAddress);
|
uint8 libusbDeviceAddress,
|
||||||
|
std::vector<ConfigDescriptor> configs);
|
||||||
|
|
||||||
~DeviceLibusb() override;
|
~DeviceLibusb() override;
|
||||||
|
|
||||||
|
@ -63,9 +69,9 @@ namespace nsyshid::backend::libusb
|
||||||
|
|
||||||
bool IsOpened() override;
|
bool IsOpened() override;
|
||||||
|
|
||||||
ReadResult Read(uint8* data, sint32 length, sint32& bytesRead) override;
|
ReadResult Read(ReadMessage* message) override;
|
||||||
|
|
||||||
WriteResult Write(uint8* data, sint32 length, sint32& bytesWritten) override;
|
WriteResult Write(WriteMessage* message) override;
|
||||||
|
|
||||||
bool GetDescriptor(uint8 descType,
|
bool GetDescriptor(uint8 descType,
|
||||||
uint8 descIndex,
|
uint8 descIndex,
|
||||||
|
@ -73,9 +79,13 @@ namespace nsyshid::backend::libusb
|
||||||
uint8* output,
|
uint8* output,
|
||||||
uint32 outputMaxLength) override;
|
uint32 outputMaxLength) override;
|
||||||
|
|
||||||
bool SetProtocol(uint32 ifIndex, uint32 protocol) override;
|
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
||||||
|
|
||||||
bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) override;
|
int ClaimAllInterfaces(uint8 config_num);
|
||||||
|
int ReleaseAllInterfaces(uint8 config_num);
|
||||||
|
int ReleaseAllInterfacesForCurrentConfig();
|
||||||
|
|
||||||
|
bool SetReport(ReportMessage* message) override;
|
||||||
|
|
||||||
uint8 m_libusbBusNumber;
|
uint8 m_libusbBusNumber;
|
||||||
uint8 m_libusbDeviceAddress;
|
uint8 m_libusbDeviceAddress;
|
||||||
|
@ -92,6 +102,7 @@ namespace nsyshid::backend::libusb
|
||||||
std::atomic<sint32> m_handleInUseCounter;
|
std::atomic<sint32> m_handleInUseCounter;
|
||||||
std::condition_variable m_handleInUseCounterDecremented;
|
std::condition_variable m_handleInUseCounterDecremented;
|
||||||
libusb_device_handle* m_libusbHandle;
|
libusb_device_handle* m_libusbHandle;
|
||||||
|
std::vector<ConfigDescriptor> m_config_descriptors;
|
||||||
|
|
||||||
class HandleLock {
|
class HandleLock {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -196,20 +196,20 @@ namespace nsyshid::backend::windows
|
||||||
return m_hFile != INVALID_HANDLE_VALUE;
|
return m_hFile != INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Device::ReadResult DeviceWindowsHID::Read(uint8* data, sint32 length, sint32& bytesRead)
|
Device::ReadResult DeviceWindowsHID::Read(ReadMessage* message)
|
||||||
{
|
{
|
||||||
bytesRead = 0;
|
message->bytesRead = 0;
|
||||||
DWORD bt;
|
DWORD bt;
|
||||||
OVERLAPPED ovlp = {0};
|
OVERLAPPED ovlp = {0};
|
||||||
ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
|
|
||||||
uint8* tempBuffer = (uint8*)malloc(length + 1);
|
uint8* tempBuffer = (uint8*)malloc(message->length + 1);
|
||||||
sint32 transferLength = 0; // minus report byte
|
sint32 transferLength = 0; // minus report byte
|
||||||
|
|
||||||
_debugPrintHex("HID_READ_BEFORE", data, length);
|
_debugPrintHex("HID_READ_BEFORE", message->data, message->length);
|
||||||
|
|
||||||
cemuLog_logDebug(LogType::Force, "HidRead Begin (Length 0x{:08x})", length);
|
cemuLog_logDebug(LogType::Force, "HidRead Begin (Length 0x{:08x})", message->length);
|
||||||
BOOL readResult = ReadFile(this->m_hFile, tempBuffer, length + 1, &bt, &ovlp);
|
BOOL readResult = ReadFile(this->m_hFile, tempBuffer, message->length + 1, &bt, &ovlp);
|
||||||
if (readResult != FALSE)
|
if (readResult != FALSE)
|
||||||
{
|
{
|
||||||
// sometimes we get the result immediately
|
// sometimes we get the result immediately
|
||||||
|
@ -247,7 +247,7 @@ namespace nsyshid::backend::windows
|
||||||
ReadResult result = ReadResult::Success;
|
ReadResult result = ReadResult::Success;
|
||||||
if (bt != 0)
|
if (bt != 0)
|
||||||
{
|
{
|
||||||
memcpy(data, tempBuffer + 1, transferLength);
|
memcpy(message->data, tempBuffer + 1, transferLength);
|
||||||
sint32 hidReadLength = transferLength;
|
sint32 hidReadLength = transferLength;
|
||||||
|
|
||||||
char debugOutput[1024] = {0};
|
char debugOutput[1024] = {0};
|
||||||
|
@ -257,7 +257,7 @@ namespace nsyshid::backend::windows
|
||||||
}
|
}
|
||||||
cemuLog_logDebug(LogType::Force, "HIDRead data: {}", debugOutput);
|
cemuLog_logDebug(LogType::Force, "HIDRead data: {}", debugOutput);
|
||||||
|
|
||||||
bytesRead = transferLength;
|
message->bytesRead = transferLength;
|
||||||
result = ReadResult::Success;
|
result = ReadResult::Success;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -270,19 +270,19 @@ namespace nsyshid::backend::windows
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Device::WriteResult DeviceWindowsHID::Write(uint8* data, sint32 length, sint32& bytesWritten)
|
Device::WriteResult DeviceWindowsHID::Write(WriteMessage* message)
|
||||||
{
|
{
|
||||||
bytesWritten = 0;
|
message->bytesWritten = 0;
|
||||||
DWORD bt;
|
DWORD bt;
|
||||||
OVERLAPPED ovlp = {0};
|
OVERLAPPED ovlp = {0};
|
||||||
ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
|
|
||||||
uint8* tempBuffer = (uint8*)malloc(length + 1);
|
uint8* tempBuffer = (uint8*)malloc(message->length + 1);
|
||||||
memcpy(tempBuffer + 1, data, length);
|
memcpy(tempBuffer + 1, message->data, message->length);
|
||||||
tempBuffer[0] = 0; // report byte?
|
tempBuffer[0] = 0; // report byte?
|
||||||
|
|
||||||
cemuLog_logDebug(LogType::Force, "HidWrite Begin (Length 0x{:08x})", length);
|
cemuLog_logDebug(LogType::Force, "HidWrite Begin (Length 0x{:08x})", message->length);
|
||||||
BOOL writeResult = WriteFile(this->m_hFile, tempBuffer, length + 1, &bt, &ovlp);
|
BOOL writeResult = WriteFile(this->m_hFile, tempBuffer, message->length + 1, &bt, &ovlp);
|
||||||
if (writeResult != FALSE)
|
if (writeResult != FALSE)
|
||||||
{
|
{
|
||||||
// sometimes we get the result immediately
|
// sometimes we get the result immediately
|
||||||
|
@ -314,7 +314,7 @@ namespace nsyshid::backend::windows
|
||||||
|
|
||||||
if (bt != 0)
|
if (bt != 0)
|
||||||
{
|
{
|
||||||
bytesWritten = length;
|
message->bytesWritten = message->length;
|
||||||
return WriteResult::Success;
|
return WriteResult::Success;
|
||||||
}
|
}
|
||||||
return WriteResult::Error;
|
return WriteResult::Error;
|
||||||
|
@ -400,19 +400,19 @@ namespace nsyshid::backend::windows
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeviceWindowsHID::SetProtocol(uint32 ifIndef, uint32 protocol)
|
bool DeviceWindowsHID::SetProtocol(uint8 ifIndex, uint8 protocol)
|
||||||
{
|
{
|
||||||
// ToDo: implement this
|
// ToDo: implement this
|
||||||
// pretend that everything is fine
|
// pretend that everything is fine
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeviceWindowsHID::SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength)
|
bool DeviceWindowsHID::SetReport(ReportMessage* message)
|
||||||
{
|
{
|
||||||
sint32 retryCount = 0;
|
sint32 retryCount = 0;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
BOOL r = HidD_SetOutputReport(this->m_hFile, reportData, length);
|
BOOL r = HidD_SetOutputReport(this->m_hFile, message->reportData, message->length);
|
||||||
if (r != FALSE)
|
if (r != FALSE)
|
||||||
break;
|
break;
|
||||||
Sleep(20); // retry
|
Sleep(20); // retry
|
||||||
|
|
|
@ -41,15 +41,15 @@ namespace nsyshid::backend::windows
|
||||||
|
|
||||||
bool IsOpened() override;
|
bool IsOpened() override;
|
||||||
|
|
||||||
ReadResult Read(uint8* data, sint32 length, sint32& bytesRead) override;
|
ReadResult Read(ReadMessage* message) override;
|
||||||
|
|
||||||
WriteResult Write(uint8* data, sint32 length, sint32& bytesWritten) override;
|
WriteResult Write(WriteMessage* message) override;
|
||||||
|
|
||||||
bool GetDescriptor(uint8 descType, uint8 descIndex, uint8 lang, uint8* output, uint32 outputMaxLength) override;
|
bool GetDescriptor(uint8 descType, uint8 descIndex, uint8 lang, uint8* output, uint32 outputMaxLength) override;
|
||||||
|
|
||||||
bool SetProtocol(uint32 ifIndef, uint32 protocol) override;
|
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
||||||
|
|
||||||
bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) override;
|
bool SetReport(ReportMessage* message) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wchar_t* m_devicePath;
|
wchar_t* m_devicePath;
|
||||||
|
|
1002
src/Cafe/OS/libs/nsyshid/Skylander.cpp
Normal file
1002
src/Cafe/OS/libs/nsyshid/Skylander.cpp
Normal file
File diff suppressed because it is too large
Load Diff
105
src/Cafe/OS/libs/nsyshid/Skylander.h
Normal file
105
src/Cafe/OS/libs/nsyshid/Skylander.h
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "nsyshid.h"
|
||||||
|
#include "Backend.h"
|
||||||
|
|
||||||
|
#include "Common/FileStream.h"
|
||||||
|
|
||||||
|
namespace nsyshid
|
||||||
|
{
|
||||||
|
class SkylanderPortalDevice final : public Device {
|
||||||
|
public:
|
||||||
|
SkylanderPortalDevice();
|
||||||
|
~SkylanderPortalDevice() = default;
|
||||||
|
|
||||||
|
bool Open() override;
|
||||||
|
|
||||||
|
void Close() override;
|
||||||
|
|
||||||
|
bool IsOpened() override;
|
||||||
|
|
||||||
|
ReadResult Read(ReadMessage* message) override;
|
||||||
|
|
||||||
|
WriteResult Write(WriteMessage* message) override;
|
||||||
|
|
||||||
|
bool GetDescriptor(uint8 descType,
|
||||||
|
uint8 descIndex,
|
||||||
|
uint8 lang,
|
||||||
|
uint8* output,
|
||||||
|
uint32 outputMaxLength) override;
|
||||||
|
|
||||||
|
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
||||||
|
|
||||||
|
bool SetReport(ReportMessage* message) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_IsOpened;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr uint16 BLOCK_COUNT = 0x40;
|
||||||
|
constexpr uint16 BLOCK_SIZE = 0x10;
|
||||||
|
constexpr uint16 FIGURE_SIZE = BLOCK_COUNT * BLOCK_SIZE;
|
||||||
|
constexpr uint8 MAX_SKYLANDERS = 16;
|
||||||
|
|
||||||
|
class SkylanderUSB {
|
||||||
|
public:
|
||||||
|
struct Skylander final
|
||||||
|
{
|
||||||
|
std::unique_ptr<FileStream> skyFile;
|
||||||
|
uint8 status = 0;
|
||||||
|
std::queue<uint8> queuedStatus;
|
||||||
|
std::array<uint8, BLOCK_COUNT * BLOCK_SIZE> data{};
|
||||||
|
uint32 lastId = 0;
|
||||||
|
void Save();
|
||||||
|
|
||||||
|
enum : uint8
|
||||||
|
{
|
||||||
|
REMOVED = 0,
|
||||||
|
READY = 1,
|
||||||
|
REMOVING = 2,
|
||||||
|
ADDED = 3
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SkylanderLEDColor final
|
||||||
|
{
|
||||||
|
uint8 red = 0;
|
||||||
|
uint8 green = 0;
|
||||||
|
uint8 blue = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ControlTransfer(uint8* buf, sint32 originalLength);
|
||||||
|
|
||||||
|
void Activate();
|
||||||
|
void Deactivate();
|
||||||
|
void SetLeds(uint8 side, uint8 r, uint8 g, uint8 b);
|
||||||
|
|
||||||
|
std::array<uint8, 64> GetStatus();
|
||||||
|
void QueryBlock(uint8 skyNum, uint8 block, uint8* replyBuf);
|
||||||
|
void WriteBlock(uint8 skyNum, uint8 block, const uint8* toWriteBuf,
|
||||||
|
uint8* replyBuf);
|
||||||
|
|
||||||
|
uint8 LoadSkylander(uint8* buf, std::unique_ptr<FileStream> file);
|
||||||
|
bool RemoveSkylander(uint8 skyNum);
|
||||||
|
bool CreateSkylander(fs::path pathName, uint16 skyId, uint16 skyVar);
|
||||||
|
uint16 SkylanderCRC16(uint16 initValue, const uint8* buffer, uint32 size);
|
||||||
|
static std::map<const std::pair<const uint16, const uint16>, const char*> GetListSkylanders();
|
||||||
|
std::string FindSkylander(uint16 skyId, uint16 skyVar);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::mutex m_skyMutex;
|
||||||
|
std::mutex m_queryMutex;
|
||||||
|
std::array<Skylander, MAX_SKYLANDERS> m_skylanders;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::queue<std::array<uint8, 64>> m_queries;
|
||||||
|
bool m_activated = true;
|
||||||
|
uint8 m_interruptCounter = 0;
|
||||||
|
SkylanderLEDColor m_colorRight = {};
|
||||||
|
SkylanderLEDColor m_colorLeft = {};
|
||||||
|
SkylanderLEDColor m_colorTrap = {};
|
||||||
|
};
|
||||||
|
extern SkylanderUSB g_skyportal;
|
||||||
|
} // namespace nsyshid
|
|
@ -256,6 +256,19 @@ namespace nsyshid
|
||||||
device->m_productId);
|
device->m_productId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FindDeviceById(uint16 vendorId, uint16 productId)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(hidMutex);
|
||||||
|
for (const auto& device : deviceList)
|
||||||
|
{
|
||||||
|
if (device->m_vendorId == vendorId && device->m_productId == productId)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void export_HIDAddClient(PPCInterpreter_t* hCPU)
|
void export_HIDAddClient(PPCInterpreter_t* hCPU)
|
||||||
{
|
{
|
||||||
ppcDefineParamTypePtr(hidClient, HIDClient_t, 0);
|
ppcDefineParamTypePtr(hidClient, HIDClient_t, 0);
|
||||||
|
@ -366,8 +379,8 @@ namespace nsyshid
|
||||||
void export_HIDSetProtocol(PPCInterpreter_t* hCPU)
|
void export_HIDSetProtocol(PPCInterpreter_t* hCPU)
|
||||||
{
|
{
|
||||||
ppcDefineParamU32(hidHandle, 0); // r3
|
ppcDefineParamU32(hidHandle, 0); // r3
|
||||||
ppcDefineParamU32(ifIndex, 1); // r4
|
ppcDefineParamU8(ifIndex, 1); // r4
|
||||||
ppcDefineParamU32(protocol, 2); // r5
|
ppcDefineParamU8(protocol, 2); // r5
|
||||||
ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6
|
ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6
|
||||||
ppcDefineParamMPTR(callbackParamMPTR, 4); // r7
|
ppcDefineParamMPTR(callbackParamMPTR, 4); // r7
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetProtocol(...)");
|
cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetProtocol(...)");
|
||||||
|
@ -406,7 +419,8 @@ namespace nsyshid
|
||||||
sint32 originalLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR)
|
sint32 originalLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR)
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "_hidSetReportAsync begin");
|
cemuLog_logDebug(LogType::Force, "_hidSetReportAsync begin");
|
||||||
if (device->SetReport(reportData, length, originalData, originalLength))
|
ReportMessage message(reportData, length, originalData, originalLength);
|
||||||
|
if (device->SetReport(&message))
|
||||||
{
|
{
|
||||||
DoHIDTransferCallback(callbackFuncMPTR,
|
DoHIDTransferCallback(callbackFuncMPTR,
|
||||||
callbackParamMPTR,
|
callbackParamMPTR,
|
||||||
|
@ -433,7 +447,8 @@ namespace nsyshid
|
||||||
{
|
{
|
||||||
_debugPrintHex("_hidSetReportSync Begin", reportData, length);
|
_debugPrintHex("_hidSetReportSync Begin", reportData, length);
|
||||||
sint32 returnCode = 0;
|
sint32 returnCode = 0;
|
||||||
if (device->SetReport(reportData, length, originalData, originalLength))
|
ReportMessage message(reportData, length, originalData, originalLength);
|
||||||
|
if (device->SetReport(&message))
|
||||||
{
|
{
|
||||||
returnCode = originalLength;
|
returnCode = originalLength;
|
||||||
}
|
}
|
||||||
|
@ -511,17 +526,16 @@ namespace nsyshid
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memset(data, 0, maxLength);
|
memset(data, 0, maxLength);
|
||||||
|
ReadMessage message(data, maxLength, 0);
|
||||||
sint32 bytesRead = 0;
|
Device::ReadResult readResult = device->Read(&message);
|
||||||
Device::ReadResult readResult = device->Read(data, maxLength, bytesRead);
|
|
||||||
switch (readResult)
|
switch (readResult)
|
||||||
{
|
{
|
||||||
case Device::ReadResult::Success:
|
case Device::ReadResult::Success:
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): read {} of {} bytes",
|
cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): read {} of {} bytes",
|
||||||
bytesRead,
|
message.bytesRead,
|
||||||
maxLength);
|
maxLength);
|
||||||
return bytesRead;
|
return message.bytesRead;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Device::ReadResult::Error:
|
case Device::ReadResult::Error:
|
||||||
|
@ -609,15 +623,15 @@ namespace nsyshid
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): cannot write to a non-opened device");
|
cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): cannot write to a non-opened device");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
sint32 bytesWritten = 0;
|
WriteMessage message(data, maxLength, 0);
|
||||||
Device::WriteResult writeResult = device->Write(data, maxLength, bytesWritten);
|
Device::WriteResult writeResult = device->Write(&message);
|
||||||
switch (writeResult)
|
switch (writeResult)
|
||||||
{
|
{
|
||||||
case Device::WriteResult::Success:
|
case Device::WriteResult::Success:
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): wrote {} of {} bytes", bytesWritten,
|
cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): wrote {} of {} bytes", message.bytesWritten,
|
||||||
maxLength);
|
maxLength);
|
||||||
return bytesWritten;
|
return message.bytesWritten;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Device::WriteResult::Error:
|
case Device::WriteResult::Error:
|
||||||
|
@ -758,6 +772,11 @@ namespace nsyshid
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Backend::FindDeviceById(uint16 vendorId, uint16 productId)
|
||||||
|
{
|
||||||
|
return nsyshid::FindDeviceById(vendorId, productId);
|
||||||
|
}
|
||||||
|
|
||||||
bool Backend::IsDeviceWhitelisted(uint16 vendorId, uint16 productId)
|
bool Backend::IsDeviceWhitelisted(uint16 vendorId, uint16 productId)
|
||||||
{
|
{
|
||||||
return Whitelist::GetInstance().IsDeviceWhitelisted(vendorId, productId);
|
return Whitelist::GetInstance().IsDeviceWhitelisted(vendorId, productId);
|
||||||
|
|
|
@ -358,6 +358,10 @@ void CemuConfig::Load(XMLConfigParser& parser)
|
||||||
auto dsuc = input.get("DSUC");
|
auto dsuc = input.get("DSUC");
|
||||||
dsu_client.host = dsuc.get_attribute("host", dsu_client.host);
|
dsu_client.host = dsuc.get_attribute("host", dsu_client.host);
|
||||||
dsu_client.port = dsuc.get_attribute("port", dsu_client.port);
|
dsu_client.port = dsuc.get_attribute("port", dsu_client.port);
|
||||||
|
|
||||||
|
// emulatedusbdevices
|
||||||
|
auto usbdevices = parser.get("EmulatedUsbDevices");
|
||||||
|
emulated_usb_devices.emulate_skylander_portal = usbdevices.get("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CemuConfig::Save(XMLConfigParser& parser)
|
void CemuConfig::Save(XMLConfigParser& parser)
|
||||||
|
@ -551,6 +555,10 @@ void CemuConfig::Save(XMLConfigParser& parser)
|
||||||
auto dsuc = input.set("DSUC");
|
auto dsuc = input.set("DSUC");
|
||||||
dsuc.set_attribute("host", dsu_client.host);
|
dsuc.set_attribute("host", dsu_client.host);
|
||||||
dsuc.set_attribute("port", dsu_client.port);
|
dsuc.set_attribute("port", dsu_client.port);
|
||||||
|
|
||||||
|
// emulated usb devices
|
||||||
|
auto usbdevices = config.set("EmulatedUsbDevices");
|
||||||
|
usbdevices.set("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
GameEntry* CemuConfig::GetGameEntryByTitleId(uint64 titleId)
|
GameEntry* CemuConfig::GetGameEntryByTitleId(uint64 titleId)
|
||||||
|
|
|
@ -514,6 +514,12 @@ struct CemuConfig
|
||||||
|
|
||||||
NetworkService GetAccountNetworkService(uint32 persistentId);
|
NetworkService GetAccountNetworkService(uint32 persistentId);
|
||||||
void SetAccountSelectedService(uint32 persistentId, NetworkService serviceIndex);
|
void SetAccountSelectedService(uint32 persistentId, NetworkService serviceIndex);
|
||||||
|
|
||||||
|
// emulated usb devices
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
ConfigValue<bool> emulate_skylander_portal{false};
|
||||||
|
}emulated_usb_devices{};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GameEntry* GetGameEntryByTitleId(uint64 titleId);
|
GameEntry* GetGameEntryByTitleId(uint64 titleId);
|
||||||
|
|
|
@ -101,6 +101,8 @@ add_library(CemuGui
|
||||||
PairingDialog.h
|
PairingDialog.h
|
||||||
TitleManager.cpp
|
TitleManager.cpp
|
||||||
TitleManager.h
|
TitleManager.h
|
||||||
|
EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp
|
||||||
|
EmulatedUSBDevices/EmulatedUSBDeviceFrame.h
|
||||||
windows/PPCThreadsViewer
|
windows/PPCThreadsViewer
|
||||||
windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp
|
windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp
|
||||||
windows/PPCThreadsViewer/DebugPPCThreadsWindow.h
|
windows/PPCThreadsViewer/DebugPPCThreadsWindow.h
|
||||||
|
|
304
src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp
Normal file
304
src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
#include "gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "config/CemuConfig.h"
|
||||||
|
#include "gui/helpers/wxHelpers.h"
|
||||||
|
#include "gui/wxHelper.h"
|
||||||
|
#include "util/helpers/helpers.h"
|
||||||
|
|
||||||
|
#include "Cafe/OS/libs/nsyshid/nsyshid.h"
|
||||||
|
|
||||||
|
#include "Common/FileStream.h"
|
||||||
|
|
||||||
|
#include <wx/arrstr.h>
|
||||||
|
#include <wx/button.h>
|
||||||
|
#include <wx/checkbox.h>
|
||||||
|
#include <wx/combobox.h>
|
||||||
|
#include <wx/filedlg.h>
|
||||||
|
#include <wx/msgdlg.h>
|
||||||
|
#include <wx/notebook.h>
|
||||||
|
#include <wx/panel.h>
|
||||||
|
#include <wx/sizer.h>
|
||||||
|
#include <wx/statbox.h>
|
||||||
|
#include <wx/stattext.h>
|
||||||
|
#include <wx/stream.h>
|
||||||
|
#include <wx/textctrl.h>
|
||||||
|
#include <wx/textentry.h>
|
||||||
|
#include <wx/valnum.h>
|
||||||
|
#include <wx/wfstream.h>
|
||||||
|
|
||||||
|
#include "resource/embedded/resources.h"
|
||||||
|
#include "EmulatedUSBDeviceFrame.h"
|
||||||
|
|
||||||
|
EmulatedUSBDeviceFrame::EmulatedUSBDeviceFrame(wxWindow* parent)
|
||||||
|
: wxFrame(parent, wxID_ANY, _("Emulated USB Devices"), wxDefaultPosition,
|
||||||
|
wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL)
|
||||||
|
{
|
||||||
|
SetIcon(wxICON(X_BOX));
|
||||||
|
|
||||||
|
auto& config = GetConfig();
|
||||||
|
|
||||||
|
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
auto* notebook = new wxNotebook(this, wxID_ANY);
|
||||||
|
|
||||||
|
notebook->AddPage(AddSkylanderPage(notebook), _("Skylanders Portal"));
|
||||||
|
|
||||||
|
sizer->Add(notebook, 1, wxEXPAND | wxALL, 2);
|
||||||
|
|
||||||
|
SetSizerAndFit(sizer);
|
||||||
|
Layout();
|
||||||
|
Centre(wxBOTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmulatedUSBDeviceFrame::~EmulatedUSBDeviceFrame() {}
|
||||||
|
|
||||||
|
wxPanel* EmulatedUSBDeviceFrame::AddSkylanderPage(wxNotebook* notebook)
|
||||||
|
{
|
||||||
|
auto* panel = new wxPanel(notebook);
|
||||||
|
auto* panelSizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
auto* box = new wxStaticBox(panel, wxID_ANY, _("Skylanders Manager"));
|
||||||
|
auto* boxSizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
||||||
|
|
||||||
|
auto* row = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
m_emulatePortal =
|
||||||
|
new wxCheckBox(box, wxID_ANY, _("Emulate Skylander Portal"));
|
||||||
|
m_emulatePortal->SetValue(
|
||||||
|
GetConfig().emulated_usb_devices.emulate_skylander_portal);
|
||||||
|
m_emulatePortal->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) {
|
||||||
|
GetConfig().emulated_usb_devices.emulate_skylander_portal =
|
||||||
|
m_emulatePortal->IsChecked();
|
||||||
|
g_config.Save();
|
||||||
|
});
|
||||||
|
row->Add(m_emulatePortal, 1, wxEXPAND | wxALL, 2);
|
||||||
|
boxSizer->Add(row, 1, wxEXPAND | wxALL, 2);
|
||||||
|
for (int i = 0; i < nsyshid::MAX_SKYLANDERS; i++)
|
||||||
|
{
|
||||||
|
boxSizer->Add(AddSkylanderRow(i, box), 1, wxEXPAND | wxALL, 2);
|
||||||
|
}
|
||||||
|
panelSizer->Add(boxSizer, 1, wxEXPAND | wxALL, 2);
|
||||||
|
panel->SetSizerAndFit(panelSizer);
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxBoxSizer* EmulatedUSBDeviceFrame::AddSkylanderRow(uint8 row_number,
|
||||||
|
wxStaticBox* box)
|
||||||
|
{
|
||||||
|
auto* row = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
row->Add(new wxStaticText(box, wxID_ANY,
|
||||||
|
fmt::format("{} {}", _("Skylander").ToStdString(),
|
||||||
|
(row_number + 1))),
|
||||||
|
1, wxEXPAND | wxALL, 2);
|
||||||
|
m_skylanderSlots[row_number] =
|
||||||
|
new wxTextCtrl(box, wxID_ANY, _("None"), wxDefaultPosition, wxDefaultSize,
|
||||||
|
wxTE_READONLY);
|
||||||
|
m_skylanderSlots[row_number]->SetMinSize(wxSize(150, -1));
|
||||||
|
m_skylanderSlots[row_number]->Disable();
|
||||||
|
row->Add(m_skylanderSlots[row_number], 1, wxEXPAND | wxALL, 2);
|
||||||
|
auto* loadButton = new wxButton(box, wxID_ANY, _("Load"));
|
||||||
|
loadButton->Bind(wxEVT_BUTTON, [row_number, this](wxCommandEvent&) {
|
||||||
|
LoadSkylander(row_number);
|
||||||
|
});
|
||||||
|
auto* createButton = new wxButton(box, wxID_ANY, _("Create"));
|
||||||
|
createButton->Bind(wxEVT_BUTTON, [row_number, this](wxCommandEvent&) {
|
||||||
|
CreateSkylander(row_number);
|
||||||
|
});
|
||||||
|
auto* clearButton = new wxButton(box, wxID_ANY, _("Clear"));
|
||||||
|
clearButton->Bind(wxEVT_BUTTON, [row_number, this](wxCommandEvent&) {
|
||||||
|
ClearSkylander(row_number);
|
||||||
|
});
|
||||||
|
row->Add(loadButton, 1, wxEXPAND | wxALL, 2);
|
||||||
|
row->Add(createButton, 1, wxEXPAND | wxALL, 2);
|
||||||
|
row->Add(clearButton, 1, wxEXPAND | wxALL, 2);
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulatedUSBDeviceFrame::LoadSkylander(uint8 slot)
|
||||||
|
{
|
||||||
|
wxFileDialog openFileDialog(this, _("Open Skylander dump"), "", "",
|
||||||
|
"Skylander files (*.sky;*.bin;*.dump;*.dmp)|*.sky;*.bin;*.dump;*.dmp",
|
||||||
|
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
|
if (openFileDialog.ShowModal() != wxID_OK || openFileDialog.GetPath().empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
LoadSkylanderPath(slot, openFileDialog.GetPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulatedUSBDeviceFrame::LoadSkylanderPath(uint8 slot, wxString path)
|
||||||
|
{
|
||||||
|
std::unique_ptr<FileStream> skyFile(FileStream::openFile2(_utf8ToPath(path.utf8_string()), true));
|
||||||
|
if (!skyFile)
|
||||||
|
{
|
||||||
|
wxMessageDialog open_error(this, "Error Opening File: " + path.c_str());
|
||||||
|
open_error.ShowModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint8, 0x40 * 0x10> fileData;
|
||||||
|
if (skyFile->readData(fileData.data(), fileData.size()) != fileData.size())
|
||||||
|
{
|
||||||
|
wxMessageDialog open_error(this, "Failed to read file! File was too small");
|
||||||
|
open_error.ShowModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ClearSkylander(slot);
|
||||||
|
|
||||||
|
uint16 skyId = uint16(fileData[0x11]) << 8 | uint16(fileData[0x10]);
|
||||||
|
uint16 skyVar = uint16(fileData[0x1D]) << 8 | uint16(fileData[0x1C]);
|
||||||
|
|
||||||
|
uint8 portalSlot = nsyshid::g_skyportal.LoadSkylander(fileData.data(),
|
||||||
|
std::move(skyFile));
|
||||||
|
m_skySlots[slot] = std::tuple(portalSlot, skyId, skyVar);
|
||||||
|
UpdateSkylanderEdits();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulatedUSBDeviceFrame::CreateSkylander(uint8 slot)
|
||||||
|
{
|
||||||
|
CreateSkylanderDialog create_dlg(this, slot);
|
||||||
|
create_dlg.ShowModal();
|
||||||
|
if (create_dlg.GetReturnCode() == 1)
|
||||||
|
{
|
||||||
|
LoadSkylanderPath(slot, create_dlg.GetFilePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulatedUSBDeviceFrame::ClearSkylander(uint8 slot)
|
||||||
|
{
|
||||||
|
if (auto slotInfos = m_skySlots[slot])
|
||||||
|
{
|
||||||
|
auto [curSlot, id, var] = slotInfos.value();
|
||||||
|
nsyshid::g_skyportal.RemoveSkylander(curSlot);
|
||||||
|
m_skySlots[slot] = {};
|
||||||
|
UpdateSkylanderEdits();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateSkylanderDialog::CreateSkylanderDialog(wxWindow* parent, uint8 slot)
|
||||||
|
: wxDialog(parent, wxID_ANY, _("Skylander Figure Creator"), wxDefaultPosition, wxSize(500, 150))
|
||||||
|
{
|
||||||
|
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
auto* comboRow = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
auto* comboBox = new wxComboBox(this, wxID_ANY);
|
||||||
|
comboBox->Append("---Select---", reinterpret_cast<void*>(0xFFFFFFFF));
|
||||||
|
wxArrayString filterlist;
|
||||||
|
for (const auto& it : nsyshid::g_skyportal.GetListSkylanders())
|
||||||
|
{
|
||||||
|
const uint32 variant = uint32(uint32(it.first.first) << 16) | uint32(it.first.second);
|
||||||
|
comboBox->Append(it.second, reinterpret_cast<void*>(variant));
|
||||||
|
filterlist.Add(it.second);
|
||||||
|
}
|
||||||
|
comboBox->SetSelection(0);
|
||||||
|
bool enabled = comboBox->AutoComplete(filterlist);
|
||||||
|
comboRow->Add(comboBox, 1, wxEXPAND | wxALL, 2);
|
||||||
|
|
||||||
|
auto* idVarRow = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
wxIntegerValidator<uint32> validator;
|
||||||
|
|
||||||
|
auto* labelId = new wxStaticText(this, wxID_ANY, "ID:");
|
||||||
|
auto* labelVar = new wxStaticText(this, wxID_ANY, "Variant:");
|
||||||
|
auto* editId = new wxTextCtrl(this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0, validator);
|
||||||
|
auto* editVar = new wxTextCtrl(this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0, validator);
|
||||||
|
|
||||||
|
idVarRow->Add(labelId, 1, wxALL, 5);
|
||||||
|
idVarRow->Add(editId, 1, wxALL, 5);
|
||||||
|
idVarRow->Add(labelVar, 1, wxALL, 5);
|
||||||
|
idVarRow->Add(editVar, 1, wxALL, 5);
|
||||||
|
|
||||||
|
auto* buttonRow = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
auto* createButton = new wxButton(this, wxID_ANY, _("Create"));
|
||||||
|
createButton->Bind(wxEVT_BUTTON, [editId, editVar, this](wxCommandEvent&) {
|
||||||
|
long longSkyId;
|
||||||
|
if (!editId->GetValue().ToLong(&longSkyId) || longSkyId > 0xFFFF)
|
||||||
|
{
|
||||||
|
wxMessageDialog id_error(this, "Error Converting ID!", "ID Entered is Invalid");
|
||||||
|
id_error.ShowModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long longSkyVar;
|
||||||
|
if (!editVar->GetValue().ToLong(&longSkyVar) || longSkyVar > 0xFFFF)
|
||||||
|
{
|
||||||
|
wxMessageDialog id_error(this, "Error Converting Variant!", "Variant Entered is Invalid");
|
||||||
|
id_error.ShowModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint16 skyId = longSkyId & 0xFFFF;
|
||||||
|
uint16 skyVar = longSkyVar & 0xFFFF;
|
||||||
|
wxString predefName = nsyshid::g_skyportal.FindSkylander(skyId, skyVar) + ".sky";
|
||||||
|
wxFileDialog
|
||||||
|
saveFileDialog(this, _("Create Skylander file"), "", predefName,
|
||||||
|
"SKY files (*.sky)|*.sky", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
|
|
||||||
|
if (saveFileDialog.ShowModal() == wxID_CANCEL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_filePath = saveFileDialog.GetPath();
|
||||||
|
|
||||||
|
if(!nsyshid::g_skyportal.CreateSkylander(_utf8ToPath(m_filePath.utf8_string()), skyId, skyVar))
|
||||||
|
{
|
||||||
|
wxMessageDialog errorMessage(this, "Failed to create file");
|
||||||
|
errorMessage.ShowModal();
|
||||||
|
this->EndModal(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->EndModal(1);
|
||||||
|
});
|
||||||
|
auto* cancelButton = new wxButton(this, wxID_ANY, _("Cancel"));
|
||||||
|
cancelButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||||
|
this->EndModal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
comboBox->Bind(wxEVT_COMBOBOX, [comboBox, editId, editVar, this](wxCommandEvent&) {
|
||||||
|
const uint64 sky_info = reinterpret_cast<uint64>(comboBox->GetClientData(comboBox->GetSelection()));
|
||||||
|
if (sky_info != 0xFFFFFFFF)
|
||||||
|
{
|
||||||
|
const uint16 skyId = sky_info >> 16;
|
||||||
|
const uint16 skyVar = sky_info & 0xFFFF;
|
||||||
|
|
||||||
|
editId->SetValue(wxString::Format(wxT("%i"), skyId));
|
||||||
|
editVar->SetValue(wxString::Format(wxT("%i"), skyVar));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
buttonRow->Add(createButton, 1, wxALL, 5);
|
||||||
|
buttonRow->Add(cancelButton, 1, wxALL, 5);
|
||||||
|
|
||||||
|
sizer->Add(comboRow, 1, wxEXPAND | wxALL, 2);
|
||||||
|
sizer->Add(idVarRow, 1, wxEXPAND | wxALL, 2);
|
||||||
|
sizer->Add(buttonRow, 1, wxEXPAND | wxALL, 2);
|
||||||
|
|
||||||
|
this->SetSizer(sizer);
|
||||||
|
this->Centre(wxBOTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString CreateSkylanderDialog::GetFilePath() const
|
||||||
|
{
|
||||||
|
return m_filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulatedUSBDeviceFrame::UpdateSkylanderEdits()
|
||||||
|
{
|
||||||
|
for (auto i = 0; i < nsyshid::MAX_SKYLANDERS; i++)
|
||||||
|
{
|
||||||
|
std::string displayString;
|
||||||
|
if (auto sd = m_skySlots[i])
|
||||||
|
{
|
||||||
|
auto [portalSlot, skyId, skyVar] = sd.value();
|
||||||
|
displayString = nsyshid::g_skyportal.FindSkylander(skyId, skyVar);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
displayString = "None";
|
||||||
|
}
|
||||||
|
|
||||||
|
m_skylanderSlots[i]->ChangeValue(displayString);
|
||||||
|
}
|
||||||
|
}
|
44
src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h
Normal file
44
src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <wx/dialog.h>
|
||||||
|
#include <wx/frame.h>
|
||||||
|
|
||||||
|
#include "Cafe/OS/libs/nsyshid/Skylander.h"
|
||||||
|
|
||||||
|
class wxBoxSizer;
|
||||||
|
class wxCheckBox;
|
||||||
|
class wxFlexGridSizer;
|
||||||
|
class wxNotebook;
|
||||||
|
class wxPanel;
|
||||||
|
class wxStaticBox;
|
||||||
|
class wxString;
|
||||||
|
class wxTextCtrl;
|
||||||
|
|
||||||
|
class EmulatedUSBDeviceFrame : public wxFrame {
|
||||||
|
public:
|
||||||
|
EmulatedUSBDeviceFrame(wxWindow* parent);
|
||||||
|
~EmulatedUSBDeviceFrame();
|
||||||
|
|
||||||
|
private:
|
||||||
|
wxCheckBox* m_emulatePortal;
|
||||||
|
std::array<wxTextCtrl*, nsyshid::MAX_SKYLANDERS> m_skylanderSlots;
|
||||||
|
std::array<std::optional<std::tuple<uint8, uint16, uint16>>, nsyshid::MAX_SKYLANDERS> m_skySlots;
|
||||||
|
|
||||||
|
wxPanel* AddSkylanderPage(wxNotebook* notebook);
|
||||||
|
wxBoxSizer* AddSkylanderRow(uint8 row_number, wxStaticBox* box);
|
||||||
|
void LoadSkylander(uint8 slot);
|
||||||
|
void LoadSkylanderPath(uint8 slot, wxString path);
|
||||||
|
void CreateSkylander(uint8 slot);
|
||||||
|
void ClearSkylander(uint8 slot);
|
||||||
|
void UpdateSkylanderEdits();
|
||||||
|
};
|
||||||
|
class CreateSkylanderDialog : public wxDialog {
|
||||||
|
public:
|
||||||
|
explicit CreateSkylanderDialog(wxWindow* parent, uint8 slot);
|
||||||
|
wxString GetFilePath() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
wxString m_filePath;
|
||||||
|
};
|
|
@ -30,6 +30,7 @@
|
||||||
#include "Cafe/Filesystem/FST/FST.h"
|
#include "Cafe/Filesystem/FST/FST.h"
|
||||||
|
|
||||||
#include "gui/TitleManager.h"
|
#include "gui/TitleManager.h"
|
||||||
|
#include "gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h"
|
||||||
|
|
||||||
#include "Cafe/CafeSystem.h"
|
#include "Cafe/CafeSystem.h"
|
||||||
|
|
||||||
|
@ -110,6 +111,7 @@ enum
|
||||||
MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER = 20600,
|
MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER = 20600,
|
||||||
MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER,
|
MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER,
|
||||||
MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER,
|
MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER,
|
||||||
|
MAINFRAME_MENU_ID_TOOLS_EMULATED_USB_DEVICES,
|
||||||
// cpu
|
// cpu
|
||||||
// cpu->timer speed
|
// cpu->timer speed
|
||||||
MAINFRAME_MENU_ID_TIMER_SPEED_1X = 20700,
|
MAINFRAME_MENU_ID_TIMER_SPEED_1X = 20700,
|
||||||
|
@ -188,6 +190,7 @@ EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_INPUT, MainWindow::OnOptionsInput)
|
||||||
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, MainWindow::OnToolsInput)
|
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, MainWindow::OnToolsInput)
|
||||||
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, MainWindow::OnToolsInput)
|
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, MainWindow::OnToolsInput)
|
||||||
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER, MainWindow::OnToolsInput)
|
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER, MainWindow::OnToolsInput)
|
||||||
|
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_EMULATED_USB_DEVICES, MainWindow::OnToolsInput)
|
||||||
// cpu menu
|
// cpu menu
|
||||||
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_8X, MainWindow::OnDebugSetting)
|
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_8X, MainWindow::OnDebugSetting)
|
||||||
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_4X, MainWindow::OnDebugSetting)
|
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_4X, MainWindow::OnDebugSetting)
|
||||||
|
@ -1515,6 +1518,29 @@ void MainWindow::OnToolsInput(wxCommandEvent& event)
|
||||||
});
|
});
|
||||||
m_title_manager->Show();
|
m_title_manager->Show();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAINFRAME_MENU_ID_TOOLS_EMULATED_USB_DEVICES:
|
||||||
|
{
|
||||||
|
if (m_usb_devices)
|
||||||
|
{
|
||||||
|
m_usb_devices->Show(true);
|
||||||
|
m_usb_devices->Raise();
|
||||||
|
m_usb_devices->SetFocus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_usb_devices = new EmulatedUSBDeviceFrame(this);
|
||||||
|
m_usb_devices->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event)
|
||||||
|
{
|
||||||
|
if (event.CanVeto()) {
|
||||||
|
m_usb_devices->Show(false);
|
||||||
|
event.Veto();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_usb_devices->Show(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2166,6 +2192,7 @@ void MainWindow::RecreateMenu()
|
||||||
m_memorySearcherMenuItem->Enable(false);
|
m_memorySearcherMenuItem->Enable(false);
|
||||||
toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, _("&Title Manager"));
|
toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, _("&Title Manager"));
|
||||||
toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER, _("&Download Manager"));
|
toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER, _("&Download Manager"));
|
||||||
|
toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_EMULATED_USB_DEVICES, _("&Emulated USB Devices"));
|
||||||
|
|
||||||
m_menuBar->Append(toolsMenu, _("&Tools"));
|
m_menuBar->Append(toolsMenu, _("&Tools"));
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ struct GameEntry;
|
||||||
class DiscordPresence;
|
class DiscordPresence;
|
||||||
class TitleManager;
|
class TitleManager;
|
||||||
class GraphicPacksWindow2;
|
class GraphicPacksWindow2;
|
||||||
|
class EmulatedUSBDeviceFrame;
|
||||||
class wxLaunchGameEvent;
|
class wxLaunchGameEvent;
|
||||||
|
|
||||||
wxDECLARE_EVENT(wxEVT_LAUNCH_GAME, wxLaunchGameEvent);
|
wxDECLARE_EVENT(wxEVT_LAUNCH_GAME, wxLaunchGameEvent);
|
||||||
|
@ -164,6 +165,7 @@ private:
|
||||||
|
|
||||||
MemorySearcherTool* m_toolWindow = nullptr;
|
MemorySearcherTool* m_toolWindow = nullptr;
|
||||||
TitleManager* m_title_manager = nullptr;
|
TitleManager* m_title_manager = nullptr;
|
||||||
|
EmulatedUSBDeviceFrame* m_usb_devices = nullptr;
|
||||||
PadViewFrame* m_padView = nullptr;
|
PadViewFrame* m_padView = nullptr;
|
||||||
GraphicPacksWindow2* m_graphic_pack_window = nullptr;
|
GraphicPacksWindow2* m_graphic_pack_window = nullptr;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user