Compare commits
9 Commits
4d54d680be
...
2784129ada
Author | SHA1 | Date |
---|---|---|
Joshua de Reeper | 2784129ada | |
Joshua de Reeper | cf225c0d48 | |
splatoon1enjoyer | 13b90874f9 | |
Exzap | cf41c3b136 | |
Xphalnos | 97d8cf4ba3 | |
GaryOderNichts | b2a6cccc89 | |
GaryOderNichts | 10d553e1c9 | |
Exzap | 3f8722f0a6 | |
Exzap | 065fb7eb58 |
|
@ -28,7 +28,6 @@ jobs:
|
|||
run: |
|
||||
cd dependencies/vcpkg
|
||||
git fetch --unshallow
|
||||
git checkout 431eb6bda0950874c8d4ed929cc66e15d8aae46f
|
||||
|
||||
- name: Setup release mode parameters (for deploy)
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
|
@ -138,7 +137,6 @@ jobs:
|
|||
run: |
|
||||
cd dependencies/vcpkg
|
||||
git fetch --unshallow
|
||||
git checkout 431eb6bda0950874c8d4ed929cc66e15d8aae46f
|
||||
|
||||
- name: Setup release mode parameters (for deploy)
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
|
@ -218,7 +216,6 @@ jobs:
|
|||
run: |
|
||||
cd dependencies/vcpkg
|
||||
git fetch --unshallow
|
||||
git pull --all
|
||||
|
||||
- name: Setup release mode parameters (for deploy)
|
||||
if: ${{ inputs.deploymode == 'release' }}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 53bef8994c541b6561884a8395ea35715ece75db
|
||||
Subproject commit cbf4a6641528cee6f172328984576f51698de726
|
|
@ -445,10 +445,14 @@ add_library(CemuCafe
|
|||
OS/libs/nsyshid/AttachDefaultBackends.cpp
|
||||
OS/libs/nsyshid/Whitelist.cpp
|
||||
OS/libs/nsyshid/Whitelist.h
|
||||
OS/libs/nsyshid/BackendEmulated.cpp
|
||||
OS/libs/nsyshid/BackendEmulated.h
|
||||
OS/libs/nsyshid/BackendLibusb.cpp
|
||||
OS/libs/nsyshid/BackendLibusb.h
|
||||
OS/libs/nsyshid/BackendWindowsHID.cpp
|
||||
OS/libs/nsyshid/BackendWindowsHID.h
|
||||
OS/libs/nsyshid/Skylander.cpp
|
||||
OS/libs/nsyshid/Skylander.h
|
||||
OS/libs/nsyskbd/nsyskbd.cpp
|
||||
OS/libs/nsyskbd/nsyskbd.h
|
||||
OS/libs/nsysnet/nsysnet.cpp
|
||||
|
|
|
@ -763,6 +763,11 @@ namespace coreinit
|
|||
uint32 coreIndex = OSGetCoreId();
|
||||
if (!newThread->context.hasCoreAffinitySet(coreIndex))
|
||||
return false;
|
||||
// special case: if current and new thread are running only on the same core then reschedule even if priority is equal
|
||||
// this resolves a deadlock in Just Dance 2019 where one thread would always reacquire the same mutex within it's timeslice, blocking another thread on the same core from acquiring it
|
||||
if ((1<<coreIndex) == newThread->context.affinity && currentThread->context.affinity == newThread->context.affinity && currentThread->effectivePriority == newThread->effectivePriority)
|
||||
return true;
|
||||
// otherwise reschedule if new thread has higher priority
|
||||
return newThread->effectivePriority < currentThread->effectivePriority;
|
||||
}
|
||||
|
||||
|
|
|
@ -308,6 +308,22 @@ void nnActExport_GetPrincipalIdEx(PPCInterpreter_t* hCPU)
|
|||
osLib_returnFromFunction(hCPU, 0); // ResultSuccess
|
||||
}
|
||||
|
||||
void nnActExport_GetTransferableId(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamU32(unique, 0);
|
||||
|
||||
cemuLog_logDebug(LogType::Force, "nn_act.GetTransferableId(0x{:08x})", hCPU->gpr[3]);
|
||||
|
||||
uint64 transferableId;
|
||||
uint32 r = nn::act::GetTransferableIdEx(&transferableId, unique, iosu::act::ACT_SLOT_CURRENT);
|
||||
if (NN_RESULT_IS_FAILURE(r))
|
||||
{
|
||||
transferableId = 0;
|
||||
}
|
||||
|
||||
osLib_returnFromFunction64(hCPU, _swapEndianU64(transferableId));
|
||||
}
|
||||
|
||||
void nnActExport_GetTransferableIdEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamStructPtr(transferableId, uint64, 0);
|
||||
|
@ -691,6 +707,7 @@ void nnAct_load()
|
|||
osLib_addFunction("nn_act", "GetPrincipalId__Q2_2nn3actFv", nnActExport_GetPrincipalId);
|
||||
osLib_addFunction("nn_act", "GetPrincipalIdEx__Q2_2nn3actFPUiUc", nnActExport_GetPrincipalIdEx);
|
||||
// transferable id
|
||||
osLib_addFunction("nn_act", "GetTransferableId__Q2_2nn3actFUi", nnActExport_GetTransferableId);
|
||||
osLib_addFunction("nn_act", "GetTransferableIdEx__Q2_2nn3actFPULUiUc", nnActExport_GetTransferableIdEx);
|
||||
// persistent id
|
||||
osLib_addFunction("nn_act", "GetPersistentId__Q2_2nn3actFv", nnActExport_GetPersistentId);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "nsyshid.h"
|
||||
#include "Backend.h"
|
||||
#include "BackendEmulated.h"
|
||||
|
||||
#if NSYSHID_ENABLE_BACKEND_LIBUSB
|
||||
|
||||
|
@ -37,5 +38,13 @@ namespace nsyshid::backend
|
|||
}
|
||||
}
|
||||
#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
|
||||
|
|
|
@ -23,6 +23,55 @@ namespace nsyshid
|
|||
/* +0x12 */ uint16be maxPacketSizeTX;
|
||||
} 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, productId) == 0xA, "");
|
||||
static_assert(offsetof(HID_t, ifIndex) == 0xC, "");
|
||||
|
@ -69,7 +118,7 @@ namespace nsyshid
|
|||
ErrorTimeout,
|
||||
};
|
||||
|
||||
virtual ReadResult Read(uint8* data, sint32 length, sint32& bytesRead) = 0;
|
||||
virtual ReadResult Read(ReadMessage* message) = 0;
|
||||
|
||||
enum class WriteResult
|
||||
{
|
||||
|
@ -78,7 +127,7 @@ namespace nsyshid
|
|||
ErrorTimeout,
|
||||
};
|
||||
|
||||
virtual WriteResult Write(uint8* data, sint32 length, sint32& bytesWritten) = 0;
|
||||
virtual WriteResult Write(WriteMessage* message) = 0;
|
||||
|
||||
virtual bool GetDescriptor(uint8 descType,
|
||||
uint8 descIndex,
|
||||
|
@ -88,7 +137,7 @@ namespace nsyshid
|
|||
|
||||
virtual bool SetProtocol(uint32 ifIndef, uint32 protocol) = 0;
|
||||
|
||||
virtual bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) = 0;
|
||||
virtual bool SetReport(ReportMessage* message) = 0;
|
||||
};
|
||||
|
||||
class Backend {
|
||||
|
@ -121,6 +170,8 @@ namespace nsyshid
|
|||
|
||||
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);
|
||||
|
||||
// called from OnAttach() - attach devices that your backend can see here
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -241,11 +241,6 @@ namespace nsyshid::backend::libusb
|
|||
ret);
|
||||
return nullptr;
|
||||
}
|
||||
if (desc.idVendor == 0x0e6f && desc.idProduct == 0x0241)
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force,
|
||||
"nsyshid::BackendLibusb::CheckAndCreateDevice(): lego dimensions portal detected");
|
||||
}
|
||||
auto device = std::make_shared<DeviceLibusb>(m_ctx,
|
||||
desc.idVendor,
|
||||
desc.idProduct,
|
||||
|
@ -471,7 +466,7 @@ namespace nsyshid::backend::libusb
|
|||
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();
|
||||
if (!handleLock->IsValid())
|
||||
|
@ -488,8 +483,8 @@ namespace nsyshid::backend::libusb
|
|||
{
|
||||
ret = libusb_bulk_transfer(handleLock->GetHandle(),
|
||||
this->m_libusbEndpointIn,
|
||||
data,
|
||||
length,
|
||||
message->data,
|
||||
message->length,
|
||||
&actualLength,
|
||||
timeout);
|
||||
}
|
||||
|
@ -500,8 +495,8 @@ namespace nsyshid::backend::libusb
|
|||
// success
|
||||
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::read(): read {} of {} bytes",
|
||||
actualLength,
|
||||
length);
|
||||
bytesRead = actualLength;
|
||||
message->length);
|
||||
message->bytesRead = actualLength;
|
||||
return ReadResult::Success;
|
||||
}
|
||||
cemuLog_logDebug(LogType::Force,
|
||||
|
@ -510,7 +505,7 @@ namespace nsyshid::backend::libusb
|
|||
return ReadResult::Error;
|
||||
}
|
||||
|
||||
Device::WriteResult DeviceLibusb::Write(uint8* data, sint32 length, sint32& bytesWritten)
|
||||
Device::WriteResult DeviceLibusb::Write(WriteMessage* message)
|
||||
{
|
||||
auto handleLock = AquireHandleLock();
|
||||
if (!handleLock->IsValid())
|
||||
|
@ -520,23 +515,23 @@ namespace nsyshid::backend::libusb
|
|||
return WriteResult::Error;
|
||||
}
|
||||
|
||||
bytesWritten = 0;
|
||||
message->bytesWritten = 0;
|
||||
int actualLength = 0;
|
||||
int ret = libusb_bulk_transfer(handleLock->GetHandle(),
|
||||
this->m_libusbEndpointOut,
|
||||
data,
|
||||
length,
|
||||
message->data,
|
||||
message->length,
|
||||
&actualLength,
|
||||
0);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
// success
|
||||
bytesWritten = actualLength;
|
||||
message->bytesWritten = actualLength;
|
||||
cemuLog_logDebug(LogType::Force,
|
||||
"nsyshid::DeviceLibusb::write(): wrote {} of {} bytes",
|
||||
bytesWritten,
|
||||
length);
|
||||
message->bytesWritten,
|
||||
message->length);
|
||||
return WriteResult::Success;
|
||||
}
|
||||
cemuLog_logDebug(LogType::Force,
|
||||
|
@ -713,8 +708,7 @@ namespace nsyshid::backend::libusb
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DeviceLibusb::SetReport(uint8* reportData, sint32 length, uint8* originalData,
|
||||
sint32 originalLength)
|
||||
bool DeviceLibusb::SetReport(ReportMessage* message)
|
||||
{
|
||||
auto handleLock = AquireHandleLock();
|
||||
if (!handleLock->IsValid())
|
||||
|
@ -731,8 +725,8 @@ namespace nsyshid::backend::libusb
|
|||
bRequest,
|
||||
wValue,
|
||||
wIndex,
|
||||
reportData,
|
||||
length,
|
||||
message->reportData,
|
||||
message->length,
|
||||
timeout);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -63,9 +63,9 @@ namespace nsyshid::backend::libusb
|
|||
|
||||
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,
|
||||
|
@ -75,7 +75,7 @@ namespace nsyshid::backend::libusb
|
|||
|
||||
bool SetProtocol(uint32 ifIndex, uint32 protocol) override;
|
||||
|
||||
bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) override;
|
||||
bool SetReport(ReportMessage* message) override;
|
||||
|
||||
uint8 m_libusbBusNumber;
|
||||
uint8 m_libusbDeviceAddress;
|
||||
|
|
|
@ -196,20 +196,20 @@ namespace nsyshid::backend::windows
|
|||
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;
|
||||
OVERLAPPED ovlp = {0};
|
||||
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
|
||||
|
||||
_debugPrintHex("HID_READ_BEFORE", data, length);
|
||||
_debugPrintHex("HID_READ_BEFORE", message->data, message->length);
|
||||
|
||||
cemuLog_logDebug(LogType::Force, "HidRead Begin (Length 0x{:08x})", length);
|
||||
BOOL readResult = ReadFile(this->m_hFile, tempBuffer, length + 1, &bt, &ovlp);
|
||||
cemuLog_logDebug(LogType::Force, "HidRead Begin (Length 0x{:08x})", message->length);
|
||||
BOOL readResult = ReadFile(this->m_hFile, tempBuffer, message->length + 1, &bt, &ovlp);
|
||||
if (readResult != FALSE)
|
||||
{
|
||||
// sometimes we get the result immediately
|
||||
|
@ -247,7 +247,7 @@ namespace nsyshid::backend::windows
|
|||
ReadResult result = ReadResult::Success;
|
||||
if (bt != 0)
|
||||
{
|
||||
memcpy(data, tempBuffer + 1, transferLength);
|
||||
memcpy(message->data, tempBuffer + 1, transferLength);
|
||||
sint32 hidReadLength = transferLength;
|
||||
|
||||
char debugOutput[1024] = {0};
|
||||
|
@ -257,7 +257,7 @@ namespace nsyshid::backend::windows
|
|||
}
|
||||
cemuLog_logDebug(LogType::Force, "HIDRead data: {}", debugOutput);
|
||||
|
||||
bytesRead = transferLength;
|
||||
message->bytesRead = transferLength;
|
||||
result = ReadResult::Success;
|
||||
}
|
||||
else
|
||||
|
@ -270,19 +270,19 @@ namespace nsyshid::backend::windows
|
|||
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;
|
||||
OVERLAPPED ovlp = {0};
|
||||
ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
uint8* tempBuffer = (uint8*)malloc(length + 1);
|
||||
memcpy(tempBuffer + 1, data, length);
|
||||
uint8* tempBuffer = (uint8*)malloc(message->length + 1);
|
||||
memcpy(tempBuffer + 1, message->data, message->length);
|
||||
tempBuffer[0] = 0; // report byte?
|
||||
|
||||
cemuLog_logDebug(LogType::Force, "HidWrite Begin (Length 0x{:08x})", length);
|
||||
BOOL writeResult = WriteFile(this->m_hFile, tempBuffer, length + 1, &bt, &ovlp);
|
||||
cemuLog_logDebug(LogType::Force, "HidWrite Begin (Length 0x{:08x})", message->length);
|
||||
BOOL writeResult = WriteFile(this->m_hFile, tempBuffer, message->length + 1, &bt, &ovlp);
|
||||
if (writeResult != FALSE)
|
||||
{
|
||||
// sometimes we get the result immediately
|
||||
|
@ -314,7 +314,7 @@ namespace nsyshid::backend::windows
|
|||
|
||||
if (bt != 0)
|
||||
{
|
||||
bytesWritten = length;
|
||||
message->bytesWritten = message->length;
|
||||
return WriteResult::Success;
|
||||
}
|
||||
return WriteResult::Error;
|
||||
|
@ -407,12 +407,12 @@ namespace nsyshid::backend::windows
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DeviceWindowsHID::SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength)
|
||||
bool DeviceWindowsHID::SetReport(ReportMessage* message)
|
||||
{
|
||||
sint32 retryCount = 0;
|
||||
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)
|
||||
break;
|
||||
Sleep(20); // retry
|
||||
|
|
|
@ -41,15 +41,15 @@ namespace nsyshid::backend::windows
|
|||
|
||||
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 SetProtocol(uint32 ifIndef, uint32 protocol) override;
|
||||
|
||||
bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) override;
|
||||
bool SetReport(ReportMessage* message) override;
|
||||
|
||||
private:
|
||||
wchar_t* m_devicePath;
|
||||
|
|
|
@ -0,0 +1,401 @@
|
|||
#include "Skylander.h"
|
||||
|
||||
#include "nsyshid.h"
|
||||
#include "Backend.h"
|
||||
|
||||
namespace nsyshid
|
||||
{
|
||||
SkylanderUSB g_skyportal;
|
||||
|
||||
SkylanderPortalDevice::SkylanderPortalDevice()
|
||||
: Device(0x1430, 0x0150, 1, 2, 0)
|
||||
{
|
||||
m_IsOpened = false;
|
||||
}
|
||||
|
||||
bool SkylanderPortalDevice::Open()
|
||||
{
|
||||
if (!IsOpened())
|
||||
{
|
||||
m_IsOpened = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkylanderPortalDevice::Close()
|
||||
{
|
||||
if (IsOpened())
|
||||
{
|
||||
m_IsOpened = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SkylanderPortalDevice::IsOpened()
|
||||
{
|
||||
return m_IsOpened;
|
||||
}
|
||||
|
||||
Device::ReadResult SkylanderPortalDevice::Read(ReadMessage* message)
|
||||
{
|
||||
memcpy(message->data, g_skyportal.get_status().data(), message->length);
|
||||
message->bytesRead = message->length;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
return Device::ReadResult::Success;
|
||||
}
|
||||
|
||||
Device::WriteResult SkylanderPortalDevice::Write(WriteMessage* message)
|
||||
{
|
||||
message->bytesWritten = message->length;
|
||||
return Device::WriteResult::Success;
|
||||
}
|
||||
|
||||
bool SkylanderPortalDevice::GetDescriptor(uint8 descType,
|
||||
uint8 descIndex,
|
||||
uint8 lang,
|
||||
uint8* output,
|
||||
uint32 outputMaxLength)
|
||||
{
|
||||
uint8 configurationDescriptor[0x29];
|
||||
|
||||
uint8* currentWritePtr;
|
||||
|
||||
// configuration descriptor
|
||||
currentWritePtr = configurationDescriptor + 0;
|
||||
*(uint8*)(currentWritePtr + 0) = 9; // bLength
|
||||
*(uint8*)(currentWritePtr + 1) = 2; // bDescriptorType
|
||||
*(uint16be*)(currentWritePtr + 2) = 0x0029; // wTotalLength
|
||||
*(uint8*)(currentWritePtr + 4) = 1; // bNumInterfaces
|
||||
*(uint8*)(currentWritePtr + 5) = 1; // bConfigurationValue
|
||||
*(uint8*)(currentWritePtr + 6) = 0; // iConfiguration
|
||||
*(uint8*)(currentWritePtr + 7) = 0x80; // bmAttributes
|
||||
*(uint8*)(currentWritePtr + 8) = 0xFA; // MaxPower
|
||||
currentWritePtr = currentWritePtr + 9;
|
||||
// configuration descriptor
|
||||
*(uint8*)(currentWritePtr + 0) = 9; // bLength
|
||||
*(uint8*)(currentWritePtr + 1) = 0x04; // bDescriptorType
|
||||
*(uint8*)(currentWritePtr + 2) = 0; // bInterfaceNumber
|
||||
*(uint8*)(currentWritePtr + 3) = 0; // bAlternateSetting
|
||||
*(uint8*)(currentWritePtr + 4) = 2; // bNumEndpoints
|
||||
*(uint8*)(currentWritePtr + 5) = 3; // bInterfaceClass
|
||||
*(uint8*)(currentWritePtr + 6) = 0; // bInterfaceSubClass
|
||||
*(uint8*)(currentWritePtr + 7) = 0; // bInterfaceProtocol
|
||||
*(uint8*)(currentWritePtr + 8) = 0; // iInterface
|
||||
currentWritePtr = currentWritePtr + 9;
|
||||
// configuration descriptor
|
||||
*(uint8*)(currentWritePtr + 0) = 9; // bLength
|
||||
*(uint8*)(currentWritePtr + 1) = 0x21; // bDescriptorType
|
||||
*(uint16be*)(currentWritePtr + 2) = 0x0111; // bcdHID
|
||||
*(uint8*)(currentWritePtr + 4) = 0x00; // bCountryCode
|
||||
*(uint8*)(currentWritePtr + 5) = 0x01; // bNumDescriptors
|
||||
*(uint8*)(currentWritePtr + 6) = 0x22; // bDescriptorType
|
||||
*(uint16be*)(currentWritePtr + 7) = 0x001D; // wDescriptorLength
|
||||
currentWritePtr = currentWritePtr + 9;
|
||||
// endpoint descriptor 1
|
||||
*(uint8*)(currentWritePtr + 0) = 7; // bLength
|
||||
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
|
||||
*(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress
|
||||
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
|
||||
*(uint16be*)(currentWritePtr + 4) = 0x40; // wMaxPacketSize
|
||||
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
|
||||
currentWritePtr = currentWritePtr + 7;
|
||||
// endpoint descriptor 2
|
||||
*(uint8*)(currentWritePtr + 0) = 7; // bLength
|
||||
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
|
||||
*(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress
|
||||
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
|
||||
*(uint16be*)(currentWritePtr + 4) = 0x40; // wMaxPacketSize
|
||||
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
|
||||
currentWritePtr = currentWritePtr + 7;
|
||||
|
||||
cemu_assert_debug((currentWritePtr - configurationDescriptor) == 0x29);
|
||||
|
||||
memcpy(output, configurationDescriptor,
|
||||
std::min<uint32>(outputMaxLength, sizeof(configurationDescriptor)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkylanderPortalDevice::SetProtocol(uint32 ifIndex, uint32 protocol)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkylanderPortalDevice::SetReport(ReportMessage* message)
|
||||
{
|
||||
g_skyportal.control_transfer(message->originalData, message->originalLength);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkylanderUSB::control_transfer(uint8* buf, sint32 originalLength)
|
||||
{
|
||||
std::array<uint8, 64> interrupt_response = {};
|
||||
switch (buf[0])
|
||||
{
|
||||
case 'A':
|
||||
{
|
||||
interrupt_response = {buf[0], buf[1], 0xFF, 0x77};
|
||||
g_skyportal.activate();
|
||||
break;
|
||||
}
|
||||
case 'C':
|
||||
{
|
||||
// Colours
|
||||
break;
|
||||
}
|
||||
case 'J':
|
||||
{
|
||||
interrupt_response = {buf[0]};
|
||||
break;
|
||||
}
|
||||
case 'L':
|
||||
{
|
||||
// Trap Team Portal side lights
|
||||
break;
|
||||
}
|
||||
case 'M':
|
||||
{
|
||||
interrupt_response = {buf[0], buf[1], 0x00, 0x19};
|
||||
break;
|
||||
}
|
||||
case 'Q':
|
||||
{
|
||||
const uint8 sky_num = buf[1] & 0xF;
|
||||
const uint8 block = buf[2];
|
||||
g_skyportal.query_block(sky_num, block, interrupt_response.data());
|
||||
break;
|
||||
}
|
||||
case 'R':
|
||||
{
|
||||
interrupt_response = {buf[0], 0x02, 0x1b};
|
||||
// g_skyportal.deactivate();
|
||||
break;
|
||||
}
|
||||
case 'S':
|
||||
{
|
||||
// Status
|
||||
break;
|
||||
}
|
||||
case 'V':
|
||||
{
|
||||
// Unsure
|
||||
break;
|
||||
}
|
||||
case 'W':
|
||||
{
|
||||
const uint8 sky_num = buf[1] & 0xF;
|
||||
const uint8 block = buf[2];
|
||||
g_skyportal.write_block(sky_num, block, &buf[3], interrupt_response.data());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
cemu_assert_error();
|
||||
break;
|
||||
}
|
||||
if (interrupt_response[0] != 0)
|
||||
{
|
||||
std::lock_guard lock(m_queryMutex);
|
||||
m_queries.push(interrupt_response);
|
||||
}
|
||||
}
|
||||
|
||||
void SkylanderUSB::activate()
|
||||
{
|
||||
std::lock_guard lock(sky_mutex);
|
||||
if (m_activated)
|
||||
{
|
||||
// If the portal was already active no change is needed
|
||||
return;
|
||||
}
|
||||
|
||||
// If not we need to advertise change to all the figures present on the portal
|
||||
for (auto& s : skylanders)
|
||||
{
|
||||
if (s.status & 1)
|
||||
{
|
||||
s.queued_status.push(3);
|
||||
s.queued_status.push(1);
|
||||
}
|
||||
}
|
||||
|
||||
m_activated = true;
|
||||
}
|
||||
|
||||
void SkylanderUSB::deactivate()
|
||||
{
|
||||
std::lock_guard lock(sky_mutex);
|
||||
|
||||
for (auto& s : skylanders)
|
||||
{
|
||||
// check if at the end of the updates there would be a figure on the portal
|
||||
if (!s.queued_status.empty())
|
||||
{
|
||||
s.status = s.queued_status.back();
|
||||
s.queued_status = std::queue<uint8>();
|
||||
}
|
||||
|
||||
s.status &= 1;
|
||||
}
|
||||
|
||||
m_activated = false;
|
||||
}
|
||||
|
||||
uint8 SkylanderUSB::load_skylander(uint8* buf, std::FILE* file)
|
||||
{
|
||||
std::lock_guard lock(sky_mutex);
|
||||
|
||||
uint32 sky_serial = 0;
|
||||
for (int i = 3; i > -1; i--)
|
||||
{
|
||||
sky_serial <<= 8;
|
||||
sky_serial |= buf[i];
|
||||
}
|
||||
uint8 found_slot = 0xFF;
|
||||
|
||||
// mimics spot retaining on the portal
|
||||
for (auto i = 0; i < 16; i++)
|
||||
{
|
||||
if ((skylanders[i].status & 1) == 0)
|
||||
{
|
||||
if (skylanders[i].last_id == sky_serial)
|
||||
{
|
||||
found_slot = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < found_slot)
|
||||
{
|
||||
found_slot = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_slot != 0xFF)
|
||||
{
|
||||
auto& skylander = skylanders[found_slot];
|
||||
memcpy(skylander.data.data(), buf, skylander.data.size());
|
||||
skylander.sky_file = std::move(file);
|
||||
skylander.status = Skylander::ADDED;
|
||||
skylander.queued_status.push(Skylander::ADDED);
|
||||
skylander.queued_status.push(Skylander::READY);
|
||||
skylander.last_id = sky_serial;
|
||||
}
|
||||
return found_slot;
|
||||
}
|
||||
|
||||
bool SkylanderUSB::remove_skylander(uint8 sky_num)
|
||||
{
|
||||
std::lock_guard lock(sky_mutex);
|
||||
auto& thesky = skylanders[sky_num];
|
||||
|
||||
if (thesky.status & 1)
|
||||
{
|
||||
thesky.status = 2;
|
||||
thesky.queued_status.push(2);
|
||||
thesky.queued_status.push(0);
|
||||
thesky.Save();
|
||||
std::fclose(thesky.sky_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SkylanderUSB::query_block(uint8 sky_num, uint8 block, uint8* reply_buf)
|
||||
{
|
||||
std::lock_guard lock(sky_mutex);
|
||||
|
||||
const auto& skylander = skylanders[sky_num];
|
||||
|
||||
reply_buf[0] = 'Q';
|
||||
reply_buf[2] = block;
|
||||
if (skylander.status & 1)
|
||||
{
|
||||
reply_buf[1] = (0x10 | sky_num);
|
||||
memcpy(reply_buf + 3, skylander.data.data() + (16 * block), 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
reply_buf[1] = sky_num;
|
||||
}
|
||||
}
|
||||
|
||||
void SkylanderUSB::write_block(uint8 sky_num, uint8 block,
|
||||
const uint8* to_write_buf, uint8* reply_buf)
|
||||
{
|
||||
std::lock_guard lock(sky_mutex);
|
||||
|
||||
auto& skylander = skylanders[sky_num];
|
||||
|
||||
reply_buf[0] = 'W';
|
||||
reply_buf[2] = block;
|
||||
|
||||
if (skylander.status & 1)
|
||||
{
|
||||
reply_buf[1] = (0x10 | sky_num);
|
||||
memcpy(skylander.data.data() + (block * 16), to_write_buf, 16);
|
||||
skylander.Save();
|
||||
}
|
||||
else
|
||||
{
|
||||
reply_buf[1] = sky_num;
|
||||
}
|
||||
}
|
||||
|
||||
std::array<uint8, 64> SkylanderUSB::get_status()
|
||||
{
|
||||
std::array<uint8, 64> interrupt_response = {};
|
||||
|
||||
if (!m_queries.empty())
|
||||
{
|
||||
std::lock_guard lock(m_queryMutex);
|
||||
interrupt_response = m_queries.front();
|
||||
m_queries.pop();
|
||||
// This needs to happen after ~22 milliseconds
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard lock(sky_mutex);
|
||||
|
||||
uint32 status = 0;
|
||||
uint8 active = 0x00;
|
||||
if (m_activated)
|
||||
{
|
||||
active = 0x01;
|
||||
}
|
||||
|
||||
for (int i = 16 - 1; i >= 0; i--)
|
||||
{
|
||||
auto& s = skylanders[i];
|
||||
|
||||
if (!s.queued_status.empty())
|
||||
{
|
||||
s.status = s.queued_status.front();
|
||||
s.queued_status.pop();
|
||||
}
|
||||
status <<= 2;
|
||||
status |= s.status;
|
||||
}
|
||||
interrupt_response = {0x53, 0x00, 0x00, 0x00, 0x00, m_interrupt_counter++,
|
||||
active, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00};
|
||||
memcpy(&interrupt_response[1], &status, sizeof(status));
|
||||
}
|
||||
return interrupt_response;
|
||||
}
|
||||
|
||||
void SkylanderUSB::Skylander::Save()
|
||||
{
|
||||
if (!sky_file)
|
||||
return;
|
||||
|
||||
#if BOOST_OS_WINDOWS
|
||||
_fseeki64(sky_file, 0, 0);
|
||||
#else
|
||||
fseeko(sky_file, 0, 0);
|
||||
#endif
|
||||
std::fwrite(&data[0], sizeof(data[0]), data.size(), sky_file);
|
||||
}
|
||||
} // namespace nsyshid
|
|
@ -0,0 +1,81 @@
|
|||
#include <mutex>
|
||||
|
||||
#include "nsyshid.h"
|
||||
#include "Backend.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(uint32 ifIndex, uint32 protocol) override;
|
||||
|
||||
bool SetReport(ReportMessage* message) override;
|
||||
|
||||
private:
|
||||
bool m_IsOpened;
|
||||
};
|
||||
|
||||
class SkylanderUSB {
|
||||
public:
|
||||
struct Skylander final
|
||||
{
|
||||
std::FILE* sky_file;
|
||||
uint8 status = 0;
|
||||
std::queue<uint8> queued_status;
|
||||
std::array<uint8, 0x40 * 0x10> data{};
|
||||
uint32 last_id = 0;
|
||||
void Save();
|
||||
|
||||
enum : uint8
|
||||
{
|
||||
REMOVED = 0,
|
||||
READY = 1,
|
||||
REMOVING = 2,
|
||||
ADDED = 3
|
||||
};
|
||||
};
|
||||
void control_transfer(uint8* buf, sint32 originalLength);
|
||||
|
||||
void activate();
|
||||
void deactivate();
|
||||
void set_leds(uint8 r, uint8 g, uint8 b);
|
||||
|
||||
std::array<uint8, 64> get_status();
|
||||
void query_block(uint8 sky_num, uint8 block, uint8* reply_buf);
|
||||
void write_block(uint8 sky_num, uint8 block, const uint8* to_write_buf,
|
||||
uint8* reply_buf);
|
||||
|
||||
uint8 load_skylander(uint8* buf, std::FILE* file);
|
||||
bool remove_skylander(uint8 sky_num);
|
||||
|
||||
protected:
|
||||
std::mutex sky_mutex;
|
||||
std::array<Skylander, 16> skylanders;
|
||||
|
||||
private:
|
||||
std::queue<std::array<uint8, 64>> m_queries;
|
||||
bool m_activated = true;
|
||||
uint8 m_interrupt_counter = 0;
|
||||
std::mutex m_queryMutex;
|
||||
};
|
||||
extern SkylanderUSB g_skyportal;
|
||||
} // namespace nsyshid
|
|
@ -256,6 +256,19 @@ namespace nsyshid
|
|||
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)
|
||||
{
|
||||
ppcDefineParamTypePtr(hidClient, HIDClient_t, 0);
|
||||
|
@ -406,7 +419,8 @@ namespace nsyshid
|
|||
sint32 originalLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR)
|
||||
{
|
||||
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,
|
||||
callbackParamMPTR,
|
||||
|
@ -434,7 +448,8 @@ namespace nsyshid
|
|||
{
|
||||
_debugPrintHex("_hidSetReportSync Begin", reportData, length);
|
||||
sint32 returnCode = 0;
|
||||
if (device->SetReport(reportData, length, originalData, originalLength))
|
||||
ReportMessage message(reportData, length, originalData, originalLength);
|
||||
if (device->SetReport(&message))
|
||||
{
|
||||
returnCode = originalLength;
|
||||
}
|
||||
|
@ -511,17 +526,16 @@ namespace nsyshid
|
|||
return -1;
|
||||
}
|
||||
memset(data, 0, maxLength);
|
||||
|
||||
sint32 bytesRead = 0;
|
||||
Device::ReadResult readResult = device->Read(data, maxLength, bytesRead);
|
||||
ReadMessage message(data, maxLength, 0);
|
||||
Device::ReadResult readResult = device->Read(&message);
|
||||
switch (readResult)
|
||||
{
|
||||
case Device::ReadResult::Success:
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): read {} of {} bytes",
|
||||
bytesRead,
|
||||
message.bytesRead,
|
||||
maxLength);
|
||||
return bytesRead;
|
||||
return message.bytesRead;
|
||||
}
|
||||
break;
|
||||
case Device::ReadResult::Error:
|
||||
|
@ -609,15 +623,15 @@ namespace nsyshid
|
|||
cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): cannot write to a non-opened device");
|
||||
return -1;
|
||||
}
|
||||
sint32 bytesWritten = 0;
|
||||
Device::WriteResult writeResult = device->Write(data, maxLength, bytesWritten);
|
||||
WriteMessage message(data, maxLength, 0);
|
||||
Device::WriteResult writeResult = device->Write(&message);
|
||||
switch (writeResult)
|
||||
{
|
||||
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);
|
||||
return bytesWritten;
|
||||
return message.bytesWritten;
|
||||
}
|
||||
break;
|
||||
case Device::WriteResult::Error:
|
||||
|
@ -758,6 +772,11 @@ namespace nsyshid
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool Backend::FindDeviceById(uint16 vendorId, uint16 productId)
|
||||
{
|
||||
return nsyshid::FindDeviceById(vendorId, productId);
|
||||
}
|
||||
|
||||
bool Backend::IsDeviceWhitelisted(uint16 vendorId, uint16 productId)
|
||||
{
|
||||
return Whitelist::GetInstance().IsDeviceWhitelisted(vendorId, productId);
|
||||
|
|
|
@ -213,6 +213,32 @@ void zlib125Export_inflateReset2(PPCInterpreter_t* hCPU)
|
|||
osLib_returnFromFunction(hCPU, r);
|
||||
}
|
||||
|
||||
void zlib125Export_deflateInit_(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamStructPtr(zstream, z_stream_ppc2, 0);
|
||||
ppcDefineParamS32(level, 1);
|
||||
ppcDefineParamStr(version, 2);
|
||||
ppcDefineParamS32(streamsize, 3);
|
||||
|
||||
z_stream hzs;
|
||||
zlib125_setupHostZStream(zstream, &hzs, false);
|
||||
|
||||
// setup internal memory allocator if requested
|
||||
if (zstream->zalloc == nullptr)
|
||||
zstream->zalloc = PPCInterpreter_makeCallableExportDepr(zlib125_zcalloc);
|
||||
if (zstream->zfree == nullptr)
|
||||
zstream->zfree = PPCInterpreter_makeCallableExportDepr(zlib125_zcfree);
|
||||
|
||||
if (streamsize != sizeof(z_stream_ppc2))
|
||||
assert_dbg();
|
||||
|
||||
sint32 r = deflateInit_(&hzs, level, version, sizeof(z_stream));
|
||||
|
||||
zlib125_setupUpdateZStream(&hzs, zstream);
|
||||
|
||||
osLib_returnFromFunction(hCPU, r);
|
||||
}
|
||||
|
||||
void zlib125Export_deflateInit2_(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
ppcDefineParamStructPtr(zstream, z_stream_ppc2, 0);
|
||||
|
@ -345,6 +371,7 @@ namespace zlib
|
|||
osLib_addFunction("zlib125", "inflateReset", zlib125Export_inflateReset);
|
||||
osLib_addFunction("zlib125", "inflateReset2", zlib125Export_inflateReset2);
|
||||
|
||||
osLib_addFunction("zlib125", "deflateInit_", zlib125Export_deflateInit_);
|
||||
osLib_addFunction("zlib125", "deflateInit2_", zlib125Export_deflateInit2_);
|
||||
osLib_addFunction("zlib125", "deflateBound", zlib125Export_deflateBound);
|
||||
osLib_addFunction("zlib125", "deflate", zlib125Export_deflate);
|
||||
|
|
|
@ -2418,6 +2418,9 @@ bool ppcAssembler_assembleSingleInstruction(char const* text, PPCAssemblerInOut*
|
|||
_ppcAssembler_translateAlias(instructionName);
|
||||
// parse operands
|
||||
internalInfo.listOperandStr.clear();
|
||||
|
||||
bool isInString = false;
|
||||
|
||||
while (currentPtr < endPtr)
|
||||
{
|
||||
currentPtr++;
|
||||
|
@ -2425,7 +2428,10 @@ bool ppcAssembler_assembleSingleInstruction(char const* text, PPCAssemblerInOut*
|
|||
// find end of operand
|
||||
while (currentPtr < endPtr)
|
||||
{
|
||||
if (*currentPtr == ',')
|
||||
if (*currentPtr == '"')
|
||||
isInString=!isInString;
|
||||
|
||||
if (*currentPtr == ',' && !isInString)
|
||||
break;
|
||||
currentPtr++;
|
||||
}
|
||||
|
|
|
@ -131,7 +131,12 @@ uint32 ActiveSettings::GetPersistentId()
|
|||
|
||||
bool ActiveSettings::IsOnlineEnabled()
|
||||
{
|
||||
return GetConfig().account.online_enabled && Account::GetAccount(GetPersistentId()).IsValidOnlineAccount() && HasRequiredOnlineFiles();
|
||||
if(!Account::GetAccount(GetPersistentId()).IsValidOnlineAccount())
|
||||
return false;
|
||||
if(!HasRequiredOnlineFiles())
|
||||
return false;
|
||||
NetworkService networkService = static_cast<NetworkService>(GetConfig().GetAccountNetworkService(GetPersistentId()));
|
||||
return networkService == NetworkService::Nintendo || networkService == NetworkService::Pretendo || networkService == NetworkService::Custom;
|
||||
}
|
||||
|
||||
bool ActiveSettings::HasRequiredOnlineFiles()
|
||||
|
@ -139,8 +144,9 @@ bool ActiveSettings::HasRequiredOnlineFiles()
|
|||
return s_has_required_online_files;
|
||||
}
|
||||
|
||||
NetworkService ActiveSettings::GetNetworkService() {
|
||||
return static_cast<NetworkService>(GetConfig().account.active_service.GetValue());
|
||||
NetworkService ActiveSettings::GetNetworkService()
|
||||
{
|
||||
return GetConfig().GetAccountNetworkService(GetPersistentId());
|
||||
}
|
||||
|
||||
bool ActiveSettings::DumpShadersEnabled()
|
||||
|
|
|
@ -328,8 +328,22 @@ void CemuConfig::Load(XMLConfigParser& parser)
|
|||
// account
|
||||
auto acc = parser.get("Account");
|
||||
account.m_persistent_id = acc.get("PersistentId", account.m_persistent_id);
|
||||
account.online_enabled = acc.get("OnlineEnabled", account.online_enabled);
|
||||
account.active_service = acc.get("ActiveService",account.active_service);
|
||||
// legacy online settings, we only parse these for upgrading purposes
|
||||
account.legacy_online_enabled = acc.get("OnlineEnabled", account.legacy_online_enabled);
|
||||
account.legacy_active_service = acc.get("ActiveService",account.legacy_active_service);
|
||||
// per-account online setting
|
||||
auto accService = parser.get("AccountService");
|
||||
account.service_select.clear();
|
||||
for (auto element = accService.get("SelectedService"); element.valid(); element = accService.get("SelectedService", element))
|
||||
{
|
||||
uint32 persistentId = element.get_attribute<uint32>("PersistentId", 0);
|
||||
sint32 serviceIndex = element.get_attribute<sint32>("Service", 0);
|
||||
NetworkService networkService = static_cast<NetworkService>(serviceIndex);
|
||||
if (persistentId < Account::kMinPersistendId)
|
||||
continue;
|
||||
if(networkService == NetworkService::Offline || networkService == NetworkService::Nintendo || networkService == NetworkService::Pretendo || networkService == NetworkService::Custom)
|
||||
account.service_select.emplace(persistentId, networkService);
|
||||
}
|
||||
// debug
|
||||
auto debug = parser.get("Debug");
|
||||
#if BOOST_OS_WINDOWS
|
||||
|
@ -344,6 +358,10 @@ void CemuConfig::Load(XMLConfigParser& parser)
|
|||
auto dsuc = input.get("DSUC");
|
||||
dsu_client.host = dsuc.get_attribute("host", dsu_client.host);
|
||||
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)
|
||||
|
@ -512,8 +530,17 @@ void CemuConfig::Save(XMLConfigParser& parser)
|
|||
// account
|
||||
auto acc = config.set("Account");
|
||||
acc.set("PersistentId", account.m_persistent_id.GetValue());
|
||||
acc.set("OnlineEnabled", account.online_enabled.GetValue());
|
||||
acc.set("ActiveService",account.active_service.GetValue());
|
||||
// legacy online mode setting
|
||||
acc.set("OnlineEnabled", account.legacy_online_enabled.GetValue());
|
||||
acc.set("ActiveService",account.legacy_active_service.GetValue());
|
||||
// per-account online setting
|
||||
auto accService = config.set("AccountService");
|
||||
for(auto& it : account.service_select)
|
||||
{
|
||||
auto entry = accService.set("SelectedService");
|
||||
entry.set_attribute("PersistentId", it.first);
|
||||
entry.set_attribute("Service", static_cast<sint32>(it.second));
|
||||
}
|
||||
// debug
|
||||
auto debug = config.set("Debug");
|
||||
#if BOOST_OS_WINDOWS
|
||||
|
@ -528,6 +555,10 @@ void CemuConfig::Save(XMLConfigParser& parser)
|
|||
auto dsuc = input.set("DSUC");
|
||||
dsuc.set_attribute("host", dsu_client.host);
|
||||
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)
|
||||
|
@ -609,3 +640,30 @@ void CemuConfig::AddRecentNfcFile(std::string_view file)
|
|||
while (recent_nfc_files.size() > kMaxRecentEntries)
|
||||
recent_nfc_files.pop_back();
|
||||
}
|
||||
|
||||
NetworkService CemuConfig::GetAccountNetworkService(uint32 persistentId)
|
||||
{
|
||||
auto it = account.service_select.find(persistentId);
|
||||
if (it != account.service_select.end())
|
||||
{
|
||||
NetworkService serviceIndex = it->second;
|
||||
// make sure the returned service is valid
|
||||
if (serviceIndex != NetworkService::Offline &&
|
||||
serviceIndex != NetworkService::Nintendo &&
|
||||
serviceIndex != NetworkService::Pretendo &&
|
||||
serviceIndex != NetworkService::Custom)
|
||||
return NetworkService::Offline;
|
||||
if( static_cast<NetworkService>(serviceIndex) == NetworkService::Custom && !NetworkConfig::XMLExists() )
|
||||
return NetworkService::Offline; // custom is selected but no custom config exists
|
||||
return serviceIndex;
|
||||
}
|
||||
// if not found, return the legacy value
|
||||
if(!account.legacy_online_enabled)
|
||||
return NetworkService::Offline;
|
||||
return static_cast<NetworkService>(account.legacy_active_service.GetValue() + 1); // +1 because "Offline" now takes index 0
|
||||
}
|
||||
|
||||
void CemuConfig::SetAccountSelectedService(uint32 persistentId, NetworkService serviceIndex)
|
||||
{
|
||||
account.service_select[persistentId] = serviceIndex;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include <wx/language.h>
|
||||
#include <wx/intl.h>
|
||||
|
||||
enum class NetworkService;
|
||||
|
||||
struct GameEntry
|
||||
{
|
||||
GameEntry() = default;
|
||||
|
@ -483,8 +485,9 @@ struct CemuConfig
|
|||
struct
|
||||
{
|
||||
ConfigValueBounds<uint32> m_persistent_id{ Account::kMinPersistendId, Account::kMinPersistendId, 0xFFFFFFFF };
|
||||
ConfigValue<bool> online_enabled{false};
|
||||
ConfigValue<int> active_service{0};
|
||||
ConfigValue<bool> legacy_online_enabled{false};
|
||||
ConfigValue<int> legacy_active_service{0};
|
||||
std::unordered_map<uint32, NetworkService> service_select; // per-account service index. Key is persistentId
|
||||
}account{};
|
||||
|
||||
// input
|
||||
|
@ -509,6 +512,16 @@ struct CemuConfig
|
|||
bool GetGameListCustomName(uint64 titleId, std::string& customName);
|
||||
void SetGameListCustomName(uint64 titleId, std::string customName);
|
||||
|
||||
NetworkService GetAccountNetworkService(uint32 persistentId);
|
||||
void SetAccountSelectedService(uint32 persistentId, NetworkService serviceIndex);
|
||||
|
||||
// emulated usb devices
|
||||
struct
|
||||
{
|
||||
ConfigValue<bool> emulate_skylander_portal{false};
|
||||
ConfigValue<bool> emulate_infinity_base{true};
|
||||
}emulated_usb_devices{};
|
||||
|
||||
private:
|
||||
GameEntry* GetGameEntryByTitleId(uint64 titleId);
|
||||
GameEntry* CreateGameEntry(uint64 titleId);
|
||||
|
|
|
@ -34,14 +34,15 @@ void NetworkConfig::Load(XMLConfigParser& parser)
|
|||
|
||||
bool NetworkConfig::XMLExists()
|
||||
{
|
||||
static std::optional<bool> s_exists; // caches result of fs::exists
|
||||
if(s_exists.has_value())
|
||||
return *s_exists;
|
||||
std::error_code ec;
|
||||
if (!fs::exists(ActiveSettings::GetConfigPath("network_services.xml"), ec))
|
||||
{
|
||||
if (static_cast<NetworkService>(GetConfig().account.active_service.GetValue()) == NetworkService::Custom)
|
||||
{
|
||||
GetConfig().account.active_service = 0;
|
||||
}
|
||||
s_exists = false;
|
||||
return false;
|
||||
}
|
||||
s_exists = true;
|
||||
return true;
|
||||
}
|
|
@ -5,9 +5,11 @@
|
|||
|
||||
enum class NetworkService
|
||||
{
|
||||
Offline,
|
||||
Nintendo,
|
||||
Pretendo,
|
||||
Custom
|
||||
Custom,
|
||||
COUNT = Custom
|
||||
};
|
||||
|
||||
struct NetworkConfig
|
||||
|
|
|
@ -101,6 +101,8 @@ add_library(CemuGui
|
|||
PairingDialog.h
|
||||
TitleManager.cpp
|
||||
TitleManager.h
|
||||
EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp
|
||||
EmulatedUSBDevices/EmulatedUSBDeviceFrame.h
|
||||
windows/PPCThreadsViewer
|
||||
windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp
|
||||
windows/PPCThreadsViewer/DebugPPCThreadsWindow.h
|
||||
|
|
|
@ -0,0 +1,659 @@
|
|||
#include "gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h"
|
||||
|
||||
#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 "Cafe/OS/libs/nsyshid/Skylander.h"
|
||||
|
||||
#include <wx/button.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/filedlg.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/textctrl.h>
|
||||
|
||||
#include "resource/embedded/resources.h"
|
||||
|
||||
const std::map<const std::pair<const uint16, const uint16>, const std::string>
|
||||
list_skylanders = {
|
||||
{{0, 0x0000}, "Whirlwind"},
|
||||
{{0, 0x1801}, "Series 2 Whirlwind"},
|
||||
{{0, 0x1C02}, "Polar Whirlwind"},
|
||||
{{0, 0x2805}, "Horn Blast Whirlwind"},
|
||||
{{0, 0x3810}, "Eon's Elite Whirlwind"},
|
||||
{{1, 0x0000}, "Sonic Boom"},
|
||||
{{1, 0x1801}, "Series 2 Sonic Boom"},
|
||||
{{2, 0x0000}, "Warnado"},
|
||||
{{2, 0x2206}, "LightCore Warnado"},
|
||||
{{3, 0x0000}, "Lightning Rod"},
|
||||
{{3, 0x1801}, "Series 2 Lightning Rod"},
|
||||
{{4, 0x0000}, "Bash"},
|
||||
{{4, 0x1801}, "Series 2 Bash"},
|
||||
{{5, 0x0000}, "Terrafin"},
|
||||
{{5, 0x1801}, "Series 2 Terrafin"},
|
||||
{{5, 0x2805}, "Knockout Terrafin"},
|
||||
{{5, 0x3810}, "Eon's Elite Terrafin"},
|
||||
{{6, 0x0000}, "Dino Rang"},
|
||||
{{6, 0x4810}, "Eon's Elite Dino Rang"},
|
||||
{{7, 0x0000}, "Prism Break"},
|
||||
{{7, 0x1801}, "Series 2 Prism Break"},
|
||||
{{7, 0x2805}, "Hyper Beam Prism Break"},
|
||||
{{7, 0x1206}, "LightCore Prism Break"},
|
||||
{{8, 0x0000}, "Sunburn"},
|
||||
{{9, 0x0000}, "Eruptor"},
|
||||
{{9, 0x1801}, "Series 2 Eruptor"},
|
||||
{{9, 0x2C02}, "Volcanic Eruptor"},
|
||||
{{9, 0x2805}, "Lava Barf Eruptor"},
|
||||
{{9, 0x1206}, "LightCore Eruptor"},
|
||||
{{9, 0x3810}, "Eon's Elite Eruptor"},
|
||||
{{10, 0x0000}, "Ignitor"},
|
||||
{{10, 0x1801}, "Series 2 Ignitor"},
|
||||
{{10, 0x1C03}, "Legendary Ignitor"},
|
||||
{{11, 0x0000}, "Flameslinger"},
|
||||
{{11, 0x1801}, "Series 2 Flameslinger"},
|
||||
{{12, 0x0000}, "Zap"},
|
||||
{{12, 0x1801}, "Series 2 Zap"},
|
||||
{{13, 0x0000}, "Wham Shell"},
|
||||
{{13, 0x2206}, "LightCore Wham Shell"},
|
||||
{{14, 0x0000}, "Gill Grunt"},
|
||||
{{14, 0x1801}, "Series 2 Gill Grunt"},
|
||||
{{14, 0x2805}, "Anchors Away Gill Grunt"},
|
||||
{{14, 0x3805}, "Tidal Wave Gill Grunt"},
|
||||
{{14, 0x3810}, "Eon's Elite Gill Grunt"},
|
||||
{{15, 0x0000}, "Slam Bam"},
|
||||
{{15, 0x1801}, "Series 2 Slam Bam"},
|
||||
{{15, 0x1C03}, "Legendary Slam Bam"},
|
||||
{{15, 0x4810}, "Eon's Elite Slam Bam"},
|
||||
{{16, 0x0000}, "Spyro"},
|
||||
{{16, 0x1801}, "Series 2 Spyro"},
|
||||
{{16, 0x2C02}, "Dark Mega Ram Spyro"},
|
||||
{{16, 0x2805}, "Mega Ram Spyro"},
|
||||
{{16, 0x3810}, "Eon's Elite Spyro"},
|
||||
{{17, 0x0000}, "Voodood"},
|
||||
{{17, 0x4810}, "Eon's Elite Voodood"},
|
||||
{{18, 0x0000}, "Double Trouble"},
|
||||
{{18, 0x1801}, "Series 2 Double Trouble"},
|
||||
{{18, 0x1C02}, "Royal Double Trouble"},
|
||||
{{19, 0x0000}, "Trigger Happy"},
|
||||
{{19, 0x1801}, "Series 2 Trigger Happy"},
|
||||
{{19, 0x2C02}, "Springtime Trigger Happy"},
|
||||
{{19, 0x2805}, "Big Bang Trigger Happy"},
|
||||
{{19, 0x3810}, "Eon's Elite Trigger Happy"},
|
||||
{{20, 0x0000}, "Drobot"},
|
||||
{{20, 0x1801}, "Series 2 Drobot"},
|
||||
{{20, 0x1206}, "LightCore Drobot"},
|
||||
{{21, 0x0000}, "Drill Seargeant"},
|
||||
{{21, 0x1801}, "Series 2 Drill Seargeant"},
|
||||
{{22, 0x0000}, "Boomer"},
|
||||
{{22, 0x4810}, "Eon's Elite Boomer"},
|
||||
{{23, 0x0000}, "Wrecking Ball"},
|
||||
{{23, 0x1801}, "Series 2 Wrecking Ball"},
|
||||
{{24, 0x0000}, "Camo"},
|
||||
{{24, 0x2805}, "Thorn Horn Camo"},
|
||||
{{25, 0x0000}, "Zook"},
|
||||
{{25, 0x1801}, "Series 2 Zook"},
|
||||
{{25, 0x4810}, "Eon's Elite Zook"},
|
||||
{{26, 0x0000}, "Stealth Elf"},
|
||||
{{26, 0x1801}, "Series 2 Stealth Elf"},
|
||||
{{26, 0x2C02}, "Dark Stealth Elf"},
|
||||
{{26, 0x1C03}, "Legendary Stealth Elf"},
|
||||
{{26, 0x2805}, "Ninja Stealth Elf"},
|
||||
{{26, 0x3810}, "Eon's Elite Stealth Elf"},
|
||||
{{27, 0x0000}, "Stump Smash"},
|
||||
{{27, 0x1801}, "Series 2 Stump Smash"},
|
||||
{{28, 0x0000}, "Dark Spyro"},
|
||||
{{29, 0x0000}, "Hex"},
|
||||
{{29, 0x1801}, "Series 2 Hex"},
|
||||
{{29, 0x1206}, "LightCore Hex"},
|
||||
{{30, 0x0000}, "Chop Chop"},
|
||||
{{30, 0x1801}, "Series 2 Chop Chop"},
|
||||
{{30, 0x2805}, "Twin Blade Chop Chop"},
|
||||
{{30, 0x3810}, "Eon's Elite Chop Chop"},
|
||||
{{31, 0x0000}, "Ghost Roaster"},
|
||||
{{31, 0x4810}, "Eon's Elite Ghost Roaster"},
|
||||
{{32, 0x0000}, "Cynder"},
|
||||
{{32, 0x1801}, "Series 2 Cynder"},
|
||||
{{32, 0x2805}, "Phantom Cynder"},
|
||||
{{100, 0x0000}, "Jet Vac"},
|
||||
{{100, 0x1403}, "Legendary Jet Vac"},
|
||||
{{100, 0x2805}, "Turbo Jet Vac"},
|
||||
{{100, 0x3805}, "Full Blast Jet Vac"},
|
||||
{{100, 0x1206}, "LightCore Jet Vac"},
|
||||
{{101, 0x0000}, "Swarm"},
|
||||
{{102, 0x0000}, "Crusher"},
|
||||
{{102, 0x1602}, "Granite Crusher"},
|
||||
{{103, 0x0000}, "Flashwing"},
|
||||
{{103, 0x1402}, "Jade Flash Wing"},
|
||||
{{103, 0x2206}, "LightCore Flashwing"},
|
||||
{{104, 0x0000}, "Hot Head"},
|
||||
{{105, 0x0000}, "Hot Dog"},
|
||||
{{105, 0x1402}, "Molten Hot Dog"},
|
||||
{{105, 0x2805}, "Fire Bone Hot Dog"},
|
||||
{{106, 0x0000}, "Chill"},
|
||||
{{106, 0x1603}, "Legendary Chill"},
|
||||
{{106, 0x2805}, "Blizzard Chill"},
|
||||
{{106, 0x1206}, "LightCore Chill"},
|
||||
{{107, 0x0000}, "Thumpback"},
|
||||
{{108, 0x0000}, "Pop Fizz"},
|
||||
{{108, 0x1402}, "Punch Pop Fizz"},
|
||||
{{108, 0x3C02}, "Love Potion Pop Fizz"},
|
||||
{{108, 0x2805}, "Super Gulp Pop Fizz"},
|
||||
{{108, 0x3805}, "Fizzy Frenzy Pop Fizz"},
|
||||
{{108, 0x1206}, "LightCore Pop Fizz"},
|
||||
{{109, 0x0000}, "Ninjini"},
|
||||
{{109, 0x1602}, "Scarlet Ninjini"},
|
||||
{{110, 0x0000}, "Bouncer"},
|
||||
{{110, 0x1603}, "Legendary Bouncer"},
|
||||
{{111, 0x0000}, "Sprocket"},
|
||||
{{111, 0x2805}, "Heavy Duty Sprocket"},
|
||||
{{112, 0x0000}, "Tree Rex"},
|
||||
{{112, 0x1602}, "Gnarly Tree Rex"},
|
||||
{{113, 0x0000}, "Shroomboom"},
|
||||
{{113, 0x3805}, "Sure Shot Shroomboom"},
|
||||
{{113, 0x1206}, "LightCore Shroomboom"},
|
||||
{{114, 0x0000}, "Eye Brawl"},
|
||||
{{115, 0x0000}, "Fright Rider"},
|
||||
{{200, 0x0000}, "Anvil Rain"},
|
||||
{{201, 0x0000}, "Hidden Treasure"},
|
||||
{{201, 0x2000}, "Platinum Hidden Treasure"},
|
||||
{{202, 0x0000}, "Healing Elixir"},
|
||||
{{203, 0x0000}, "Ghost Pirate Swords"},
|
||||
{{204, 0x0000}, "Time Twist Hourglass"},
|
||||
{{205, 0x0000}, "Sky Iron Shield"},
|
||||
{{206, 0x0000}, "Winged Boots"},
|
||||
{{207, 0x0000}, "Sparx the Dragonfly"},
|
||||
{{208, 0x0000}, "Dragonfire Cannon"},
|
||||
{{208, 0x1602}, "Golden Dragonfire Cannon"},
|
||||
{{209, 0x0000}, "Scorpion Striker"},
|
||||
{{210, 0x3002}, "Biter's Bane"},
|
||||
{{210, 0x3008}, "Sorcerous Skull"},
|
||||
{{210, 0x300B}, "Axe of Illusion"},
|
||||
{{210, 0x300E}, "Arcane Hourglass"},
|
||||
{{210, 0x3012}, "Spell Slapper"},
|
||||
{{210, 0x3014}, "Rune Rocket"},
|
||||
{{211, 0x3001}, "Tidal Tiki"},
|
||||
{{211, 0x3002}, "Wet Walter"},
|
||||
{{211, 0x3006}, "Flood Flask"},
|
||||
{{211, 0x3406}, "Legendary Flood Flask"},
|
||||
{{211, 0x3007}, "Soaking Staff"},
|
||||
{{211, 0x300B}, "Aqua Axe"},
|
||||
{{211, 0x3016}, "Frost Helm"},
|
||||
{{212, 0x3003}, "Breezy Bird"},
|
||||
{{212, 0x3006}, "Drafty Decanter"},
|
||||
{{212, 0x300D}, "Tempest Timer"},
|
||||
{{212, 0x3010}, "Cloudy Cobra"},
|
||||
{{212, 0x3011}, "Storm Warning"},
|
||||
{{212, 0x3018}, "Cyclone Saber"},
|
||||
{{213, 0x3004}, "Spirit Sphere"},
|
||||
{{213, 0x3404}, "Legendary Spirit Sphere"},
|
||||
{{213, 0x3008}, "Spectral Skull"},
|
||||
{{213, 0x3408}, "Legendary Spectral Skull"},
|
||||
{{213, 0x300B}, "Haunted Hatchet"},
|
||||
{{213, 0x300C}, "Grim Gripper"},
|
||||
{{213, 0x3010}, "Spooky Snake"},
|
||||
{{213, 0x3017}, "Dream Piercer"},
|
||||
{{214, 0x3000}, "Tech Totem"},
|
||||
{{214, 0x3007}, "Automatic Angel"},
|
||||
{{214, 0x3009}, "Factory Flower"},
|
||||
{{214, 0x300C}, "Grabbing Gadget"},
|
||||
{{214, 0x3016}, "Makers Mana"},
|
||||
{{214, 0x301A}, "Topsy Techy"},
|
||||
{{215, 0x3005}, "Eternal Flame"},
|
||||
{{215, 0x3009}, "Fire Flower"},
|
||||
{{215, 0x3011}, "Scorching Stopper"},
|
||||
{{215, 0x3012}, "Searing Spinner"},
|
||||
{{215, 0x3017}, "Spark Spear"},
|
||||
{{215, 0x301B}, "Blazing Belch"},
|
||||
{{216, 0x3000}, "Banded Boulder"},
|
||||
{{216, 0x3003}, "Rock Hawk"},
|
||||
{{216, 0x300A}, "Slag Hammer"},
|
||||
{{216, 0x300E}, "Dust Of Time"},
|
||||
{{216, 0x3013}, "Spinning Sandstorm"},
|
||||
{{216, 0x301A}, "Rubble Trouble"},
|
||||
{{217, 0x3003}, "Oak Eagle"},
|
||||
{{217, 0x3005}, "Emerald Energy"},
|
||||
{{217, 0x300A}, "Weed Whacker"},
|
||||
{{217, 0x3010}, "Seed Serpent"},
|
||||
{{217, 0x3018}, "Jade Blade"},
|
||||
{{217, 0x301B}, "Shrub Shrieker"},
|
||||
{{218, 0x3000}, "Dark Dagger"},
|
||||
{{218, 0x3014}, "Shadow Spider"},
|
||||
{{218, 0x301A}, "Ghastly Grimace"},
|
||||
{{219, 0x3000}, "Shining Ship"},
|
||||
{{219, 0x300F}, "Heavenly Hawk"},
|
||||
{{219, 0x301B}, "Beam Scream"},
|
||||
{{220, 0x301E}, "Kaos Trap"},
|
||||
{{220, 0x351F}, "Ultimate Kaos Trap"},
|
||||
{{230, 0x0000}, "Hand of Fate"},
|
||||
{{230, 0x3403}, "Legendary Hand of Fate"},
|
||||
{{231, 0x0000}, "Piggy Bank"},
|
||||
{{232, 0x0000}, "Rocket Ram"},
|
||||
{{233, 0x0000}, "Tiki Speaky"},
|
||||
{{300, 0x0000}, "Dragon’s Peak"},
|
||||
{{301, 0x0000}, "Empire of Ice"},
|
||||
{{302, 0x0000}, "Pirate Seas"},
|
||||
{{303, 0x0000}, "Darklight Crypt"},
|
||||
{{304, 0x0000}, "Volcanic Vault"},
|
||||
{{305, 0x0000}, "Mirror of Mystery"},
|
||||
{{306, 0x0000}, "Nightmare Express"},
|
||||
{{307, 0x0000}, "Sunscraper Spire"},
|
||||
{{308, 0x0000}, "Midnight Museum"},
|
||||
{{404, 0x0000}, "Legendary Bash"},
|
||||
{{416, 0x0000}, "Legendary Spyro"},
|
||||
{{419, 0x0000}, "Legendary Trigger Happy"},
|
||||
{{430, 0x0000}, "Legendary Chop Chop"},
|
||||
{{450, 0x0000}, "Gusto"},
|
||||
{{451, 0x0000}, "Thunderbolt"},
|
||||
{{452, 0x0000}, "Fling Kong"},
|
||||
{{453, 0x0000}, "Blades"},
|
||||
{{453, 0x3403}, "Legendary Blades"},
|
||||
{{454, 0x0000}, "Wallop"},
|
||||
{{455, 0x0000}, "Head Rush"},
|
||||
{{455, 0x3402}, "Nitro Head Rush"},
|
||||
{{456, 0x0000}, "Fist Bump"},
|
||||
{{457, 0x0000}, "Rocky Roll"},
|
||||
{{458, 0x0000}, "Wildfire"},
|
||||
{{458, 0x3402}, "Dark Wildfire"},
|
||||
{{459, 0x0000}, "Ka Boom"},
|
||||
{{460, 0x0000}, "Trail Blazer"},
|
||||
{{461, 0x0000}, "Torch"},
|
||||
{{462, 0x3000}, "Snap Shot"},
|
||||
{{462, 0x3402}, "Dark Snap Shot"},
|
||||
{{463, 0x0000}, "Lob Star"},
|
||||
{{463, 0x3402}, "Winterfest Lob-Star"},
|
||||
{{464, 0x0000}, "Flip Wreck"},
|
||||
{{465, 0x0000}, "Echo"},
|
||||
{{466, 0x0000}, "Blastermind"},
|
||||
{{467, 0x0000}, "Enigma"},
|
||||
{{468, 0x0000}, "Deja Vu"},
|
||||
{{468, 0x3403}, "Legendary Deja Vu"},
|
||||
{{469, 0x0000}, "Cobra Candabra"},
|
||||
{{469, 0x3402}, "King Cobra Cadabra"},
|
||||
{{470, 0x0000}, "Jawbreaker"},
|
||||
{{470, 0x3403}, "Legendary Jawbreaker"},
|
||||
{{471, 0x0000}, "Gearshift"},
|
||||
{{472, 0x0000}, "Chopper"},
|
||||
{{473, 0x0000}, "Tread Head"},
|
||||
{{474, 0x0000}, "Bushwack"},
|
||||
{{474, 0x3403}, "Legendary Bushwack"},
|
||||
{{475, 0x0000}, "Tuff Luck"},
|
||||
{{476, 0x0000}, "Food Fight"},
|
||||
{{476, 0x3402}, "Dark Food Fight"},
|
||||
{{477, 0x0000}, "High Five"},
|
||||
{{478, 0x0000}, "Krypt King"},
|
||||
{{478, 0x3402}, "Nitro Krypt King"},
|
||||
{{479, 0x0000}, "Short Cut"},
|
||||
{{480, 0x0000}, "Bat Spin"},
|
||||
{{481, 0x0000}, "Funny Bone"},
|
||||
{{482, 0x0000}, "Knight Light"},
|
||||
{{483, 0x0000}, "Spotlight"},
|
||||
{{484, 0x0000}, "Knight Mare"},
|
||||
{{485, 0x0000}, "Blackout"},
|
||||
{{502, 0x0000}, "Bop"},
|
||||
{{505, 0x0000}, "Terrabite"},
|
||||
{{506, 0x0000}, "Breeze"},
|
||||
{{508, 0x0000}, "Pet Vac"},
|
||||
{{508, 0x3402}, "Power Punch Pet Vac"},
|
||||
{{507, 0x0000}, "Weeruptor"},
|
||||
{{507, 0x3402}, "Eggcellent Weeruptor"},
|
||||
{{509, 0x0000}, "Small Fry"},
|
||||
{{510, 0x0000}, "Drobit"},
|
||||
{{519, 0x0000}, "Trigger Snappy"},
|
||||
{{526, 0x0000}, "Whisper Elf"},
|
||||
{{540, 0x0000}, "Barkley"},
|
||||
{{540, 0x3402}, "Gnarly Barkley"},
|
||||
{{541, 0x0000}, "Thumpling"},
|
||||
{{514, 0x0000}, "Gill Runt"},
|
||||
{{542, 0x0000}, "Mini-Jini"},
|
||||
{{503, 0x0000}, "Spry"},
|
||||
{{504, 0x0000}, "Hijinx"},
|
||||
{{543, 0x0000}, "Eye Small"},
|
||||
{{601, 0x0000}, "King Pen"},
|
||||
{{602, 0x0000}, "Tri-Tip"},
|
||||
{{603, 0x0000}, "Chopscotch"},
|
||||
{{604, 0x0000}, "Boom Bloom"},
|
||||
{{605, 0x0000}, "Pit Boss"},
|
||||
{{606, 0x0000}, "Barbella"},
|
||||
{{607, 0x0000}, "Air Strike"},
|
||||
{{608, 0x0000}, "Ember"},
|
||||
{{609, 0x0000}, "Ambush"},
|
||||
{{610, 0x0000}, "Dr. Krankcase"},
|
||||
{{611, 0x0000}, "Hood Sickle"},
|
||||
{{612, 0x0000}, "Tae Kwon Crow"},
|
||||
{{613, 0x0000}, "Golden Queen"},
|
||||
{{614, 0x0000}, "Wolfgang"},
|
||||
{{615, 0x0000}, "Pain-Yatta"},
|
||||
{{616, 0x0000}, "Mysticat"},
|
||||
{{617, 0x0000}, "Starcast"},
|
||||
{{618, 0x0000}, "Buckshot"},
|
||||
{{619, 0x0000}, "Aurora"},
|
||||
{{620, 0x0000}, "Flare Wolf"},
|
||||
{{621, 0x0000}, "Chompy Mage"},
|
||||
{{622, 0x0000}, "Bad Juju"},
|
||||
{{623, 0x0000}, "Grave Clobber"},
|
||||
{{624, 0x0000}, "Blaster-Tron"},
|
||||
{{625, 0x0000}, "Ro-Bow"},
|
||||
{{626, 0x0000}, "Chain Reaction"},
|
||||
{{627, 0x0000}, "Kaos"},
|
||||
{{628, 0x0000}, "Wild Storm"},
|
||||
{{629, 0x0000}, "Tidepool"},
|
||||
{{630, 0x0000}, "Crash Bandicoot"},
|
||||
{{631, 0x0000}, "Dr. Neo Cortex"},
|
||||
{{1000, 0x0000}, "Boom Jet (Bottom)"},
|
||||
{{1001, 0x0000}, "Free Ranger (Bottom)"},
|
||||
{{1001, 0x2403}, "Legendary Free Ranger (Bottom)"},
|
||||
{{1002, 0x0000}, "Rubble Rouser (Bottom)"},
|
||||
{{1003, 0x0000}, "Doom Stone (Bottom)"},
|
||||
{{1004, 0x0000}, "Blast Zone (Bottom)"},
|
||||
{{1004, 0x2402}, "Dark Blast Zone (Bottom)"},
|
||||
{{1005, 0x0000}, "Fire Kraken (Bottom)"},
|
||||
{{1005, 0x2402}, "Jade Fire Kraken (Bottom)"},
|
||||
{{1006, 0x0000}, "Stink Bomb (Bottom)"},
|
||||
{{1007, 0x0000}, "Grilla Drilla (Bottom)"},
|
||||
{{1008, 0x0000}, "Hoot Loop (Bottom)"},
|
||||
{{1008, 0x2402}, "Enchanted Hoot Loop (Bottom)"},
|
||||
{{1009, 0x0000}, "Trap Shadow (Bottom)"},
|
||||
{{1010, 0x0000}, "Magna Charge (Bottom)"},
|
||||
{{1010, 0x2402}, "Nitro Magna Charge (Bottom)"},
|
||||
{{1011, 0x0000}, "Spy Rise (Bottom)"},
|
||||
{{1012, 0x0000}, "Night Shift (Bottom)"},
|
||||
{{1012, 0x2403}, "Legendary Night Shift (Bottom)"},
|
||||
{{1013, 0x0000}, "Rattle Shake (Bottom)"},
|
||||
{{1013, 0x2402}, "Quick Draw Rattle Shake (Bottom)"},
|
||||
{{1014, 0x0000}, "Freeze Blade (Bottom)"},
|
||||
{{1014, 0x2402}, "Nitro Freeze Blade (Bottom)"},
|
||||
{{1015, 0x0000}, "Wash Buckler (Bottom)"},
|
||||
{{1015, 0x2402}, "Dark Wash Buckler (Bottom)"},
|
||||
{{2000, 0x0000}, "Boom Jet (Top)"},
|
||||
{{2001, 0x0000}, "Free Ranger (Top)"},
|
||||
{{2001, 0x2403}, "Legendary Free Ranger (Top)"},
|
||||
{{2002, 0x0000}, "Rubble Rouser (Top)"},
|
||||
{{2003, 0x0000}, "Doom Stone (Top)"},
|
||||
{{2004, 0x0000}, "Blast Zone (Top)"},
|
||||
{{2004, 0x2402}, "Dark Blast Zone (Top)"},
|
||||
{{2005, 0x0000}, "Fire Kraken (Top)"},
|
||||
{{2005, 0x2402}, "Jade Fire Kraken (Top)"},
|
||||
{{2006, 0x0000}, "Stink Bomb (Top)"},
|
||||
{{2007, 0x0000}, "Grilla Drilla (Top)"},
|
||||
{{2008, 0x0000}, "Hoot Loop (Top)"},
|
||||
{{2008, 0x2402}, "Enchanted Hoot Loop (Top)"},
|
||||
{{2009, 0x0000}, "Trap Shadow (Top)"},
|
||||
{{2010, 0x0000}, "Magna Charge (Top)"},
|
||||
{{2010, 0x2402}, "Nitro Magna Charge (Top)"},
|
||||
{{2011, 0x0000}, "Spy Rise (Top)"},
|
||||
{{2012, 0x0000}, "Night Shift (Top)"},
|
||||
{{2012, 0x2403}, "Legendary Night Shift (Top)"},
|
||||
{{2013, 0x0000}, "Rattle Shake (Top)"},
|
||||
{{2013, 0x2402}, "Quick Draw Rattle Shake (Top)"},
|
||||
{{2014, 0x0000}, "Freeze Blade (Top)"},
|
||||
{{2014, 0x2402}, "Nitro Freeze Blade (Top)"},
|
||||
{{2015, 0x0000}, "Wash Buckler (Top)"},
|
||||
{{2015, 0x2402}, "Dark Wash Buckler (Top)"},
|
||||
{{3000, 0x0000}, "Scratch"},
|
||||
{{3001, 0x0000}, "Pop Thorn"},
|
||||
{{3002, 0x0000}, "Slobber Tooth"},
|
||||
{{3002, 0x2402}, "Dark Slobber Tooth"},
|
||||
{{3003, 0x0000}, "Scorp"},
|
||||
{{3004, 0x0000}, "Fryno"},
|
||||
{{3004, 0x3805}, "Hog Wild Fryno"},
|
||||
{{3005, 0x0000}, "Smolderdash"},
|
||||
{{3005, 0x2206}, "LightCore Smolderdash"},
|
||||
{{3006, 0x0000}, "Bumble Blast"},
|
||||
{{3006, 0x2402}, "Jolly Bumble Blast"},
|
||||
{{3006, 0x2206}, "LightCore Bumble Blast"},
|
||||
{{3007, 0x0000}, "Zoo Lou"},
|
||||
{{3007, 0x2403}, "Legendary Zoo Lou"},
|
||||
{{3008, 0x0000}, "Dune Bug"},
|
||||
{{3009, 0x0000}, "Star Strike"},
|
||||
{{3009, 0x2602}, "Enchanted Star Strike"},
|
||||
{{3009, 0x2206}, "LightCore Star Strike"},
|
||||
{{3010, 0x0000}, "Countdown"},
|
||||
{{3010, 0x2402}, "Kickoff Countdown"},
|
||||
{{3010, 0x2206}, "LightCore Countdown"},
|
||||
{{3011, 0x0000}, "Wind Up"},
|
||||
{{3011, 0x2404}, "Gear Head VVind Up"},
|
||||
{{3012, 0x0000}, "Roller Brawl"},
|
||||
{{3013, 0x0000}, "Grim Creeper"},
|
||||
{{3013, 0x2603}, "Legendary Grim Creeper"},
|
||||
{{3013, 0x2206}, "LightCore Grim Creeper"},
|
||||
{{3014, 0x0000}, "Rip Tide"},
|
||||
{{3015, 0x0000}, "Punk Shock"},
|
||||
{{3200, 0x0000}, "Battle Hammer"},
|
||||
{{3201, 0x0000}, "Sky Diamond"},
|
||||
{{3202, 0x0000}, "Platinum Sheep"},
|
||||
{{3203, 0x0000}, "Groove Machine"},
|
||||
{{3204, 0x0000}, "UFO Hat"},
|
||||
{{3300, 0x0000}, "Sheep Wreck Island"},
|
||||
{{3301, 0x0000}, "Tower of Time"},
|
||||
{{3302, 0x0000}, "Fiery Forge"},
|
||||
{{3303, 0x0000}, "Arkeyan Crossbow"},
|
||||
{{3220, 0x0000}, "Jet Stream"},
|
||||
{{3221, 0x0000}, "Tomb Buggy"},
|
||||
{{3222, 0x0000}, "Reef Ripper"},
|
||||
{{3223, 0x0000}, "Burn Cycle"},
|
||||
{{3224, 0x0000}, "Hot Streak"},
|
||||
{{3224, 0x4402}, "Dark Hot Streak"},
|
||||
{{3224, 0x4004}, "E3 Hot Streak"},
|
||||
{{3224, 0x441E}, "Golden Hot Streak"},
|
||||
{{3225, 0x0000}, "Shark Tank"},
|
||||
{{3226, 0x0000}, "Thump Truck"},
|
||||
{{3227, 0x0000}, "Crypt Crusher"},
|
||||
{{3228, 0x0000}, "Stealth Stinger"},
|
||||
{{3228, 0x4402}, "Nitro Stealth Stinger"},
|
||||
{{3231, 0x0000}, "Dive Bomber"},
|
||||
{{3231, 0x4402}, "Spring Ahead Dive Bomber"},
|
||||
{{3232, 0x0000}, "Sky Slicer"},
|
||||
{{3233, 0x0000}, "Clown Cruiser (Nintendo Only)"},
|
||||
{{3233, 0x4402}, "Dark Clown Cruiser (Nintendo Only)"},
|
||||
{{3234, 0x0000}, "Gold Rusher"},
|
||||
{{3234, 0x4402}, "Power Blue Gold Rusher"},
|
||||
{{3235, 0x0000}, "Shield Striker"},
|
||||
{{3236, 0x0000}, "Sun Runner"},
|
||||
{{3236, 0x4403}, "Legendary Sun Runner"},
|
||||
{{3237, 0x0000}, "Sea Shadow"},
|
||||
{{3237, 0x4402}, "Dark Sea Shadow"},
|
||||
{{3238, 0x0000}, "Splatter Splasher"},
|
||||
{{3238, 0x4402}, "Power Blue Splatter Splasher"},
|
||||
{{3239, 0x0000}, "Soda Skimmer"},
|
||||
{{3239, 0x4402}, "Nitro Soda Skimmer"},
|
||||
{{3240, 0x0000}, "Barrel Blaster (Nintendo Only)"},
|
||||
{{3240, 0x4402}, "Dark Barrel Blaster (Nintendo Only)"},
|
||||
{{3241, 0x0000}, "Buzz Wing"},
|
||||
{{3400, 0x0000}, "Fiesta"},
|
||||
{{3400, 0x4515}, "Frightful Fiesta"},
|
||||
{{3401, 0x0000}, "High Volt"},
|
||||
{{3402, 0x0000}, "Splat"},
|
||||
{{3402, 0x4502}, "Power Blue Splat"},
|
||||
{{3406, 0x0000}, "Stormblade"},
|
||||
{{3411, 0x0000}, "Smash Hit"},
|
||||
{{3411, 0x4502}, "Steel Plated Smash Hit"},
|
||||
{{3412, 0x0000}, "Spitfire"},
|
||||
{{3412, 0x4502}, "Dark Spitfire"},
|
||||
{{3413, 0x0000}, "Hurricane Jet Vac"},
|
||||
{{3413, 0x4503}, "Legendary Hurricane Jet Vac"},
|
||||
{{3414, 0x0000}, "Double Dare Trigger Happy"},
|
||||
{{3414, 0x4502}, "Power Blue Double Dare Trigger Happy"},
|
||||
{{3415, 0x0000}, "Super Shot Stealth Elf"},
|
||||
{{3415, 0x4502}, "Dark Super Shot Stealth Elf"},
|
||||
{{3416, 0x0000}, "Shark Shooter Terrafin"},
|
||||
{{3417, 0x0000}, "Bone Bash Roller Brawl"},
|
||||
{{3417, 0x4503}, "Legendary Bone Bash Roller Brawl"},
|
||||
{{3420, 0x0000}, "Big Bubble Pop Fizz"},
|
||||
{{3420, 0x450E}, "Birthday Bash Big Bubble Pop Fizz"},
|
||||
{{3421, 0x0000}, "Lava Lance Eruptor"},
|
||||
{{3422, 0x0000}, "Deep Dive Gill Grunt"},
|
||||
{{3423, 0x0000}, "Turbo Charge Donkey Kong (Nintendo Only)"},
|
||||
{{3423, 0x4502}, "Dark Turbo Charge Donkey Kong (Nintendo Only)"},
|
||||
{{3424, 0x0000}, "Hammer Slam Bowser (Nintendo Only)"},
|
||||
{{3424, 0x4502}, "Dark Hammer Slam Bowser (Nintendo Only)"},
|
||||
{{3425, 0x0000}, "Dive-Clops"},
|
||||
{{3425, 0x450E}, "Missile-Tow Dive-Clops"},
|
||||
{{3426, 0x0000}, "Astroblast"},
|
||||
{{3426, 0x4503}, "Legendary Astroblast"},
|
||||
{{3427, 0x0000}, "Nightfall"},
|
||||
{{3428, 0x0000}, "Thrillipede"},
|
||||
{{3428, 0x450D}, "Eggcited Thrillipede"},
|
||||
{{3500, 0x0000}, "Sky Trophy"},
|
||||
{{3501, 0x0000}, "Land Trophy"},
|
||||
{{3502, 0x0000}, "Sea Trophy"},
|
||||
{{3503, 0x0000}, "Kaos Trophy"},
|
||||
};
|
||||
|
||||
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* panel_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto* box = new wxStaticBox(panel, wxID_ANY, _("Skylanders Manager"));
|
||||
auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
||||
|
||||
auto* row = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_emulate_portal =
|
||||
new wxCheckBox(box, wxID_ANY, _("Emulate Skylander Portal"));
|
||||
m_emulate_portal->SetValue(
|
||||
GetConfig().emulated_usb_devices.emulate_skylander_portal);
|
||||
m_emulate_portal->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) {
|
||||
GetConfig().emulated_usb_devices.emulate_skylander_portal =
|
||||
m_emulate_portal->IsChecked();
|
||||
g_config.Save();
|
||||
});
|
||||
row->Add(m_emulate_portal, 1, wxEXPAND | wxALL, 2);
|
||||
box_sizer->Add(row, 1, wxEXPAND | wxALL, 2);
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
box_sizer->Add(AddSkylanderRow(i, box), 1, wxEXPAND | wxALL, 2);
|
||||
}
|
||||
panel_sizer->Add(box_sizer, 1, wxEXPAND | wxALL, 2);
|
||||
panel->SetSizerAndFit(panel_sizer);
|
||||
|
||||
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_skylander_slots[row_number] =
|
||||
new wxTextCtrl(box, wxID_ANY, _("None"), wxDefaultPosition, wxDefaultSize,
|
||||
wxTE_READONLY);
|
||||
m_skylander_slots[row_number]->SetMinSize(wxSize(150, -1));
|
||||
m_skylander_slots[row_number]->Disable();
|
||||
row->Add(m_skylander_slots[row_number], 1, wxEXPAND | wxALL, 2);
|
||||
auto* load_button = new wxButton(box, wxID_ANY, _("Load"));
|
||||
load_button->Bind(wxEVT_BUTTON, [row_number, this](wxCommandEvent&) {
|
||||
LoadSkylander(row_number);
|
||||
});
|
||||
auto* clear_button = new wxButton(box, wxID_ANY, _("Clear"));
|
||||
clear_button->Bind(wxEVT_BUTTON, [row_number, this](wxCommandEvent&) {
|
||||
ClearSkylander(row_number);
|
||||
});
|
||||
row->Add(load_button, 1, wxEXPAND | wxALL, 2);
|
||||
row->Add(clear_button, 1, wxEXPAND | wxALL, 2);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::LoadSkylander(uint8 slot)
|
||||
{
|
||||
wxFileDialog openFileDialog(this, _("Open Skylander dump"), "", "",
|
||||
"SKY files (*.sky)|*.sky",
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (openFileDialog.ShowModal() != wxID_OK || openFileDialog.GetPath().empty())
|
||||
return;
|
||||
|
||||
FILE* sky_file = std::fopen(openFileDialog.GetPath().c_str(), "r+b");
|
||||
if (!sky_file)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<uint8, 0x40 * 0x10> file_data;
|
||||
size_t read_count = std::fread(&file_data[0], sizeof(file_data[0]),
|
||||
file_data.size(), sky_file);
|
||||
if (read_count != file_data.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
ClearSkylander(slot);
|
||||
|
||||
uint16 sky_id = uint16(file_data[0x11]) << 8 | uint16(file_data[0x10]);
|
||||
uint16 sky_var = uint16(file_data[0x1D]) << 8 | uint16(file_data[0x1C]);
|
||||
|
||||
uint8 portal_slot = nsyshid::g_skyportal.load_skylander(file_data.data(),
|
||||
std::move(sky_file));
|
||||
sky_slots[slot] = std::tuple(portal_slot, sky_id, sky_var);
|
||||
UpdateSkylanderEdits();
|
||||
}
|
||||
void EmulatedUSBDeviceFrame::CreateSkylander(uint8 slot)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Create Skylander: {}", slot);
|
||||
}
|
||||
void EmulatedUSBDeviceFrame::ClearSkylander(uint8 slot)
|
||||
{
|
||||
if (auto slot_infos = sky_slots[slot])
|
||||
{
|
||||
auto [cur_slot, id, var] = slot_infos.value();
|
||||
nsyshid::g_skyportal.remove_skylander(cur_slot);
|
||||
sky_slots[slot] = {};
|
||||
UpdateSkylanderEdits();
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedUSBDeviceFrame::UpdateSkylanderEdits()
|
||||
{
|
||||
for (auto i = 0; i < 16; i++)
|
||||
{
|
||||
std::string display_string;
|
||||
if (auto sd = sky_slots[i])
|
||||
{
|
||||
auto [portal_slot, sky_id, sky_var] = sd.value();
|
||||
auto found_sky = list_skylanders.find(std::make_pair(sky_id, sky_var));
|
||||
if (found_sky != list_skylanders.end())
|
||||
{
|
||||
display_string = found_sky->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
display_string = fmt::format("Unknown (Id:{} Var:{})", sky_id, sky_var);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
display_string = "None";
|
||||
}
|
||||
|
||||
m_skylander_slots[i]->ChangeValue(display_string);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <wx/frame.h>
|
||||
|
||||
class wxBoxSizer;
|
||||
class wxCheckBox;
|
||||
class wxFlexGridSizer;
|
||||
class wxNotebook;
|
||||
class wxPanel;
|
||||
class wxStaticBox;
|
||||
class wxTextCtrl;
|
||||
|
||||
class EmulatedUSBDeviceFrame : public wxFrame {
|
||||
public:
|
||||
EmulatedUSBDeviceFrame(wxWindow* parent);
|
||||
~EmulatedUSBDeviceFrame();
|
||||
|
||||
private:
|
||||
wxCheckBox* m_emulate_portal;
|
||||
std::array<wxTextCtrl*, 16> m_skylander_slots;
|
||||
std::array<std::optional<std::tuple<uint8, uint16, uint16>>, 16> sky_slots;
|
||||
|
||||
wxPanel* AddSkylanderPage(wxNotebook* notebook);
|
||||
wxBoxSizer* AddSkylanderRow(uint8 row_number, wxStaticBox* box);
|
||||
void LoadSkylander(uint8 slot);
|
||||
void CreateSkylander(uint8 slot);
|
||||
void ClearSkylander(uint8 slot);
|
||||
void UpdateSkylanderEdits();
|
||||
};
|
|
@ -683,18 +683,6 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
|
|||
content->Add(m_delete_account, 0, wxEXPAND | wxALL | wxALIGN_RIGHT, 5);
|
||||
m_delete_account->Bind(wxEVT_BUTTON, &GeneralSettings2::OnAccountDelete, this);
|
||||
|
||||
wxString choices[] = { _("Nintendo"), _("Pretendo"), _("Custom") };
|
||||
m_active_service = new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 3, wxRA_SPECIFY_COLS);
|
||||
if (!NetworkConfig::XMLExists())
|
||||
m_active_service->Enable(2, false);
|
||||
|
||||
m_active_service->SetItemToolTip(0, _("Connect to the official Nintendo Network Service"));
|
||||
m_active_service->SetItemToolTip(1, _("Connect to the Pretendo Network Service"));
|
||||
m_active_service->SetItemToolTip(2, _("Connect to a custom Network Service (configured via network_services.xml)"));
|
||||
|
||||
m_active_service->Bind(wxEVT_RADIOBOX, &GeneralSettings2::OnAccountServiceChanged,this);
|
||||
content->Add(m_active_service, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
box_sizer->Add(content, 1, wxEXPAND, 5);
|
||||
|
||||
online_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
@ -704,17 +692,33 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
|
|||
m_active_account->Enable(false);
|
||||
m_create_account->Enable(false);
|
||||
m_delete_account->Enable(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
wxString choices[] = { _("Offline"), _("Nintendo"), _("Pretendo"), _("Custom") };
|
||||
m_active_service = new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 4, wxRA_SPECIFY_COLS);
|
||||
if (!NetworkConfig::XMLExists())
|
||||
m_active_service->Enable(3, false);
|
||||
|
||||
m_active_service->SetItemToolTip(0, _("Online functionality disabled for this account"));
|
||||
m_active_service->SetItemToolTip(1, _("Connect to the official Nintendo Network Service"));
|
||||
m_active_service->SetItemToolTip(2, _("Connect to the Pretendo Network Service"));
|
||||
m_active_service->SetItemToolTip(3, _("Connect to a custom Network Service (configured via network_services.xml)"));
|
||||
|
||||
m_active_service->Bind(wxEVT_RADIOBOX, &GeneralSettings2::OnAccountServiceChanged,this);
|
||||
online_panel_sizer->Add(m_active_service, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
if (CafeSystem::IsTitleRunning())
|
||||
{
|
||||
m_active_service->Enable(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
auto* box = new wxStaticBox(online_panel, wxID_ANY, _("Online settings"));
|
||||
auto* box = new wxStaticBox(online_panel, wxID_ANY, _("Online play requirements"));
|
||||
auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
||||
|
||||
m_online_enabled = new wxCheckBox(box, wxID_ANY, _("Enable online mode"));
|
||||
m_online_enabled->Bind(wxEVT_CHECKBOX, &GeneralSettings2::OnOnlineEnable, this);
|
||||
box_sizer->Add(m_online_enabled, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
auto* row = new wxFlexGridSizer(0, 2, 0, 0);
|
||||
row->SetFlexibleDirection(wxBOTH);
|
||||
|
@ -873,6 +877,14 @@ GeneralSettings2::GeneralSettings2(wxWindow* parent, bool game_launched)
|
|||
DisableSettings(game_launched);
|
||||
}
|
||||
|
||||
uint32 GeneralSettings2::GetSelectedAccountPersistentId()
|
||||
{
|
||||
const auto active_account = m_active_account->GetSelection();
|
||||
if (active_account == wxNOT_FOUND)
|
||||
return GetConfig().account.m_persistent_id.GetInitValue();
|
||||
return dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(active_account))->GetAccount().GetPersistentId();
|
||||
}
|
||||
|
||||
void GeneralSettings2::StoreConfig()
|
||||
{
|
||||
auto* app = (CemuApp*)wxTheApp;
|
||||
|
@ -1038,14 +1050,7 @@ void GeneralSettings2::StoreConfig()
|
|||
config.notification.friends = m_friends_data->GetValue();
|
||||
|
||||
// account
|
||||
const auto active_account = m_active_account->GetSelection();
|
||||
if (active_account == wxNOT_FOUND)
|
||||
config.account.m_persistent_id = config.account.m_persistent_id.GetInitValue();
|
||||
else
|
||||
config.account.m_persistent_id = dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(active_account))->GetAccount().GetPersistentId();
|
||||
|
||||
config.account.online_enabled = m_online_enabled->GetValue();
|
||||
config.account.active_service = m_active_service->GetSelection();
|
||||
config.account.m_persistent_id = GetSelectedAccountPersistentId();
|
||||
|
||||
// debug
|
||||
config.crash_dump = (CrashDump)m_crash_dump->GetSelection();
|
||||
|
@ -1371,14 +1376,13 @@ void GeneralSettings2::UpdateAccountInformation()
|
|||
{
|
||||
m_account_grid->SetSplitterPosition(100);
|
||||
|
||||
m_online_status->SetLabel(_("At least one issue has been found"));
|
||||
|
||||
const auto selection = m_active_account->GetSelection();
|
||||
if(selection == wxNOT_FOUND)
|
||||
{
|
||||
m_validate_online->SetBitmap(wxBITMAP_PNG_FROM_DATA(PNG_ERROR).ConvertToImage().Scale(16, 16));
|
||||
m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() & ~wxBORDER_NONE);
|
||||
ResetAccountInformation();
|
||||
m_online_status->SetLabel(_("No account selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1404,11 +1408,26 @@ void GeneralSettings2::UpdateAccountInformation()
|
|||
index = 0;
|
||||
country_property->SetChoiceSelection(index);
|
||||
|
||||
const bool online_valid = account.IsValidOnlineAccount() && ActiveSettings::HasRequiredOnlineFiles();
|
||||
if (online_valid)
|
||||
const bool online_fully_valid = account.IsValidOnlineAccount() && ActiveSettings::HasRequiredOnlineFiles();
|
||||
if (ActiveSettings::HasRequiredOnlineFiles())
|
||||
{
|
||||
if(account.IsValidOnlineAccount())
|
||||
m_online_status->SetLabel(_("Selected account is a valid online account"));
|
||||
else
|
||||
m_online_status->SetLabel(_("Selected account is not linked to a NNID or PNID"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(NCrypto::OTP_IsPresent() != NCrypto::SEEPROM_IsPresent())
|
||||
m_online_status->SetLabel(_("OTP.bin or SEEPROM.bin is missing"));
|
||||
else if(NCrypto::OTP_IsPresent() && NCrypto::SEEPROM_IsPresent())
|
||||
m_online_status->SetLabel(_("OTP and SEEPROM present but no certificate files were found"));
|
||||
else
|
||||
m_online_status->SetLabel(_("Online play is not set up. Follow the guide below to get started"));
|
||||
}
|
||||
|
||||
if(online_fully_valid)
|
||||
{
|
||||
|
||||
m_online_status->SetLabel(_("Your account is a valid online account"));
|
||||
m_validate_online->SetBitmap(wxBITMAP_PNG_FROM_DATA(PNG_CHECK_YES).ConvertToImage().Scale(16, 16));
|
||||
m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() | wxBORDER_NONE);
|
||||
}
|
||||
|
@ -1417,7 +1436,28 @@ void GeneralSettings2::UpdateAccountInformation()
|
|||
m_validate_online->SetBitmap(wxBITMAP_PNG_FROM_DATA(PNG_ERROR).ConvertToImage().Scale(16, 16));
|
||||
m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() & ~wxBORDER_NONE);
|
||||
}
|
||||
|
||||
|
||||
// enable/disable network service field depending on online requirements
|
||||
m_active_service->Enable(online_fully_valid && !CafeSystem::IsTitleRunning());
|
||||
if(online_fully_valid)
|
||||
{
|
||||
NetworkService service = GetConfig().GetAccountNetworkService(account.GetPersistentId());
|
||||
m_active_service->SetSelection(static_cast<int>(service));
|
||||
// set the config option here for the selected service
|
||||
// this will guarantee that it's actually written to settings.xml
|
||||
// allowing us to eventually get rid of the legacy option in the (far) future
|
||||
GetConfig().SetAccountSelectedService(account.GetPersistentId(), service);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_active_service->SetSelection(0); // force offline
|
||||
}
|
||||
wxString tmp = _("Network service");
|
||||
tmp.append(" (");
|
||||
tmp.append(wxString::FromUTF8(boost::nowide::narrow(account.GetMiiName())));
|
||||
tmp.append(")");
|
||||
m_active_service->SetLabel(tmp);
|
||||
|
||||
// refresh pane size
|
||||
m_account_grid->InvalidateBestSize();
|
||||
//m_account_grid->GetParent()->FitInside();
|
||||
|
@ -1663,9 +1703,8 @@ void GeneralSettings2::ApplyConfig()
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_online_enabled->SetValue(config.account.online_enabled);
|
||||
m_active_service->SetSelection(config.account.active_service);
|
||||
m_active_service->SetSelection((int)config.GetAccountNetworkService(ActiveSettings::GetPersistentId()));
|
||||
|
||||
UpdateAccountInformation();
|
||||
|
||||
// debug
|
||||
|
@ -1673,20 +1712,6 @@ void GeneralSettings2::ApplyConfig()
|
|||
m_gdb_port->SetValue(config.gdb_port.GetValue());
|
||||
}
|
||||
|
||||
void GeneralSettings2::OnOnlineEnable(wxCommandEvent& event)
|
||||
{
|
||||
event.Skip();
|
||||
if (!m_online_enabled->GetValue())
|
||||
return;
|
||||
|
||||
// show warning if player enables online mode
|
||||
const auto result = wxMessageBox(_("Please be aware that online mode lets you connect to OFFICIAL servers and therefore there is a risk of getting banned.\nOnly proceed if you are willing to risk losing online access with your Wii U and/or NNID."),
|
||||
_("Warning"), wxYES_NO | wxCENTRE | wxICON_EXCLAMATION, this);
|
||||
if (result == wxNO)
|
||||
m_online_enabled->SetValue(false);
|
||||
}
|
||||
|
||||
|
||||
void GeneralSettings2::OnAudioAPISelected(wxCommandEvent& event)
|
||||
{
|
||||
IAudioAPI::AudioAPI api;
|
||||
|
@ -1952,6 +1977,9 @@ void GeneralSettings2::OnActiveAccountChanged(wxCommandEvent& event)
|
|||
|
||||
void GeneralSettings2::OnAccountServiceChanged(wxCommandEvent& event)
|
||||
{
|
||||
auto& config = GetConfig();
|
||||
uint32 peristentId = GetSelectedAccountPersistentId();
|
||||
config.SetAccountSelectedService(peristentId, static_cast<NetworkService>(m_active_service->GetSelection()));
|
||||
UpdateAccountInformation();
|
||||
}
|
||||
|
||||
|
@ -2005,12 +2033,12 @@ void GeneralSettings2::OnShowOnlineValidator(wxCommandEvent& event)
|
|||
err << _("The following error(s) have been found:") << '\n';
|
||||
|
||||
if (validator.otp == OnlineValidator::FileState::Missing)
|
||||
err << _("otp.bin missing in Cemu root directory") << '\n';
|
||||
err << _("otp.bin missing in Cemu directory") << '\n';
|
||||
else if(validator.otp == OnlineValidator::FileState::Corrupted)
|
||||
err << _("otp.bin is invalid") << '\n';
|
||||
|
||||
if (validator.seeprom == OnlineValidator::FileState::Missing)
|
||||
err << _("seeprom.bin missing in Cemu root directory") << '\n';
|
||||
err << _("seeprom.bin missing in Cemu directory") << '\n';
|
||||
else if(validator.seeprom == OnlineValidator::FileState::Corrupted)
|
||||
err << _("seeprom.bin is invalid") << '\n';
|
||||
|
||||
|
@ -2045,9 +2073,10 @@ void GeneralSettings2::OnShowOnlineValidator(wxCommandEvent& event)
|
|||
|
||||
wxString GeneralSettings2::GetOnlineAccountErrorMessage(OnlineAccountError error)
|
||||
{
|
||||
switch (error) {
|
||||
switch (error)
|
||||
{
|
||||
case OnlineAccountError::kNoAccountId:
|
||||
return _("AccountId missing (The account is not connected to a NNID)");
|
||||
return _("AccountId missing (The account is not connected to a NNID/PNID)");
|
||||
case OnlineAccountError::kNoPasswordCached:
|
||||
return _("IsPasswordCacheEnabled is set to false (The remember password option on your Wii U must be enabled for this account before dumping it)");
|
||||
case OnlineAccountError::kPasswordCacheEmpty:
|
||||
|
|
|
@ -71,7 +71,6 @@ private:
|
|||
wxButton* m_create_account, * m_delete_account;
|
||||
wxChoice* m_active_account;
|
||||
wxRadioBox* m_active_service;
|
||||
wxCheckBox* m_online_enabled;
|
||||
wxCollapsiblePane* m_account_information;
|
||||
wxPropertyGrid* m_account_grid;
|
||||
wxBitmapButton* m_validate_online;
|
||||
|
@ -99,10 +98,11 @@ private:
|
|||
void OnMLCPathSelect(wxCommandEvent& event);
|
||||
void OnMLCPathChar(wxKeyEvent& event);
|
||||
void OnShowOnlineValidator(wxCommandEvent& event);
|
||||
void OnOnlineEnable(wxCommandEvent& event);
|
||||
void OnAccountServiceChanged(wxCommandEvent& event);
|
||||
static wxString GetOnlineAccountErrorMessage(OnlineAccountError error);
|
||||
|
||||
uint32 GetSelectedAccountPersistentId();
|
||||
|
||||
// updates cemu audio devices
|
||||
void UpdateAudioDevice();
|
||||
// refreshes audio device list for dropdown
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "Cafe/Filesystem/FST/FST.h"
|
||||
|
||||
#include "gui/TitleManager.h"
|
||||
#include "gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h"
|
||||
|
||||
#include "Cafe/CafeSystem.h"
|
||||
|
||||
|
@ -110,6 +111,7 @@ enum
|
|||
MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER = 20600,
|
||||
MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER,
|
||||
MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER,
|
||||
MAINFRAME_MENU_ID_TOOLS_EMULATED_USB_DEVICES,
|
||||
// cpu
|
||||
// cpu->timer speed
|
||||
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_TITLE_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
|
||||
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_8X, MainWindow::OnDebugSetting)
|
||||
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_4X, MainWindow::OnDebugSetting)
|
||||
|
@ -948,38 +951,6 @@ void MainWindow::OnAccountSelect(wxCommandEvent& event)
|
|||
g_config.Save();
|
||||
}
|
||||
|
||||
//void MainWindow::OnConsoleRegion(wxCommandEvent& event)
|
||||
//{
|
||||
// switch (event.GetId())
|
||||
// {
|
||||
// case MAINFRAME_MENU_ID_OPTIONS_REGION_AUTO:
|
||||
// GetConfig().console_region = ConsoleRegion::Auto;
|
||||
// break;
|
||||
// case MAINFRAME_MENU_ID_OPTIONS_REGION_JPN:
|
||||
// GetConfig().console_region = ConsoleRegion::JPN;
|
||||
// break;
|
||||
// case MAINFRAME_MENU_ID_OPTIONS_REGION_USA:
|
||||
// GetConfig().console_region = ConsoleRegion::USA;
|
||||
// break;
|
||||
// case MAINFRAME_MENU_ID_OPTIONS_REGION_EUR:
|
||||
// GetConfig().console_region = ConsoleRegion::EUR;
|
||||
// break;
|
||||
// case MAINFRAME_MENU_ID_OPTIONS_REGION_CHN:
|
||||
// GetConfig().console_region = ConsoleRegion::CHN;
|
||||
// break;
|
||||
// case MAINFRAME_MENU_ID_OPTIONS_REGION_KOR:
|
||||
// GetConfig().console_region = ConsoleRegion::KOR;
|
||||
// break;
|
||||
// case MAINFRAME_MENU_ID_OPTIONS_REGION_TWN:
|
||||
// GetConfig().console_region = ConsoleRegion::TWN;
|
||||
// break;
|
||||
// default:
|
||||
// cemu_assert_debug(false);
|
||||
// }
|
||||
//
|
||||
// g_config.Save();
|
||||
//}
|
||||
|
||||
void MainWindow::OnConsoleLanguage(wxCommandEvent& event)
|
||||
{
|
||||
switch (event.GetId())
|
||||
|
@ -1547,6 +1518,29 @@ void MainWindow::OnToolsInput(wxCommandEvent& event)
|
|||
});
|
||||
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;
|
||||
}
|
||||
|
@ -2198,6 +2192,7 @@ void MainWindow::RecreateMenu()
|
|||
m_memorySearcherMenuItem->Enable(false);
|
||||
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_EMULATED_USB_DEVICES, _("&Emulated USB Devices"));
|
||||
|
||||
m_menuBar->Append(toolsMenu, _("&Tools"));
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ struct GameEntry;
|
|||
class DiscordPresence;
|
||||
class TitleManager;
|
||||
class GraphicPacksWindow2;
|
||||
class EmulatedUSBDeviceFrame;
|
||||
class wxLaunchGameEvent;
|
||||
|
||||
wxDECLARE_EVENT(wxEVT_LAUNCH_GAME, wxLaunchGameEvent);
|
||||
|
@ -164,6 +165,7 @@ private:
|
|||
|
||||
MemorySearcherTool* m_toolWindow = nullptr;
|
||||
TitleManager* m_title_manager = nullptr;
|
||||
EmulatedUSBDeviceFrame* m_usb_devices = nullptr;
|
||||
PadViewFrame* m_padView = nullptr;
|
||||
GraphicPacksWindow2* m_graphic_pack_window = nullptr;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "cemu",
|
||||
"version-string": "1.0",
|
||||
"builtin-baseline": "53bef8994c541b6561884a8395ea35715ece75db",
|
||||
"builtin-baseline": "cbf4a6641528cee6f172328984576f51698de726",
|
||||
"dependencies": [
|
||||
"pugixml",
|
||||
"zlib",
|
||||
|
|
Loading…
Reference in New Issue