mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Use Core timing instead of thread
Skylander code tidy ups Convert c array to std::array and fix comments Formatting fixes/review changes Variable comment Migrate portal to System Impl and code tidy ups Use struct Restore review changes Minor fix to schedule transfer method Change descriptors to hex and fix comments
This commit is contained in:
@@ -399,8 +399,6 @@ add_library(core
|
||||
IOS/USB/Bluetooth/WiimoteHIDAttr.h
|
||||
IOS/USB/Common.cpp
|
||||
IOS/USB/Common.h
|
||||
IOS/USB/EmulatedUSBDevice.cpp
|
||||
IOS/USB/EmulatedUSBDevice.h
|
||||
IOS/USB/Emulated/Skylander.cpp
|
||||
IOS/USB/Emulated/Skylander.h
|
||||
IOS/USB/Host.cpp
|
||||
|
||||
@@ -556,11 +556,6 @@ void SetUSBDeviceWhitelist(const std::set<std::pair<u16, u16>>& devices)
|
||||
const Info<bool> MAIN_EMULATE_SKYLANDER_PORTAL{
|
||||
{System::Main, "EmulatedUSBDevices", "EmulateSkylanderPortal"}, false};
|
||||
|
||||
bool EmulateSkylanderPortal()
|
||||
{
|
||||
return Config::Get(Config::MAIN_EMULATE_SKYLANDER_PORTAL);
|
||||
}
|
||||
|
||||
// The reason we need this function is because some memory card code
|
||||
// expects to get a non-NTSC-K region even if we're emulating an NTSC-K Wii.
|
||||
DiscIO::Region ToGameCubeRegion(DiscIO::Region region)
|
||||
|
||||
@@ -345,7 +345,6 @@ void SetUSBDeviceWhitelist(const std::set<std::pair<u16, u16>>& devices);
|
||||
// Main.EmulatedUSBDevices
|
||||
|
||||
extern const Info<bool> MAIN_EMULATE_SKYLANDER_PORTAL;
|
||||
bool EmulateSkylanderPortal();
|
||||
|
||||
// GameCube path utility functions
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ bool IsSettingSaveable(const Config::Location& config_location)
|
||||
for (const std::string_view section :
|
||||
{"NetPlay", "General", "GBA", "Display", "Network", "Analytics", "AndroidOverlayButtons",
|
||||
"DSP", "GameList", "FifoPlayer", "AutoUpdate", "Movie", "Input", "Debug",
|
||||
"BluetoothPassthrough", "USBPassthrough", "Interface"})
|
||||
"BluetoothPassthrough", "USBPassthrough", "Interface", "EmulatedUSBDevices"})
|
||||
{
|
||||
if (config_location.section == section)
|
||||
return true;
|
||||
|
||||
@@ -38,6 +38,13 @@ void TransferCommand::OnTransferComplete(s32 return_value) const
|
||||
m_ios.EnqueueIPCReply(ios_request, return_value, 0, CoreTiming::FromThread::NON_CPU);
|
||||
}
|
||||
|
||||
void TransferCommand::ScheduleTransferCompletion(s32 return_value, u32 expected_time_us) const
|
||||
{
|
||||
auto ticks = SystemTimers::GetTicksPerSecond();
|
||||
s64 cycles_in_future = static_cast<s64>((static_cast<u64>(ticks) * expected_time_us) / 1'000'000);
|
||||
m_ios.EnqueueIPCReply(ios_request, return_value, cycles_in_future, CoreTiming::FromThread::ANY);
|
||||
}
|
||||
|
||||
void IsoMessage::SetPacketReturnValue(const size_t packet_num, const u16 return_value) const
|
||||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
|
||||
@@ -109,13 +109,10 @@ struct TransferCommand
|
||||
// Called after a transfer has completed to reply to the IPC request.
|
||||
// This can be overridden for additional processing before replying.
|
||||
virtual void OnTransferComplete(s32 return_value) const;
|
||||
void ScheduleTransferCompletion(s32 return_value, u32 expected_time_us) const;
|
||||
std::unique_ptr<u8[]> MakeBuffer(size_t size) const;
|
||||
void FillBuffer(const u8* src, size_t size) const;
|
||||
|
||||
// Fake Transfers
|
||||
u64 expected_time;
|
||||
u32 expected_count;
|
||||
|
||||
private:
|
||||
Kernel& m_ios;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,9 @@
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Core/IOS/USB/EmulatedUSBDevice.h"
|
||||
#include "Core/IOS/USB/Common.h"
|
||||
|
||||
// The maximum possible characters the portal can handle.
|
||||
// The status array is 32 bits and every character takes 2 bits.
|
||||
@@ -18,7 +19,7 @@ constexpr u8 MAX_SKYLANDERS = 16;
|
||||
|
||||
namespace IOS::HLE::USB
|
||||
{
|
||||
class SkylanderUSB final : public EmulatedUSBDevice
|
||||
class SkylanderUSB final : public Device
|
||||
{
|
||||
public:
|
||||
SkylanderUSB(Kernel& ios, const std::string& device_name);
|
||||
@@ -37,20 +38,20 @@ public:
|
||||
int SubmitTransfer(std::unique_ptr<BulkMessage> message) override;
|
||||
int SubmitTransfer(std::unique_ptr<IntrMessage> message) override;
|
||||
int SubmitTransfer(std::unique_ptr<IsoMessage> message) override;
|
||||
|
||||
protected:
|
||||
std::queue<std::array<u8, 64>> q_queries;
|
||||
void ScheduleTransfer(std::unique_ptr<TransferCommand> command, const std::array<u8, 64>& data,
|
||||
s32 expected_count, u64 expected_time_us);
|
||||
|
||||
private:
|
||||
Kernel& m_ios;
|
||||
u16 m_vid = 0;
|
||||
u16 m_pid = 0;
|
||||
u8 m_active_interface = 0;
|
||||
bool m_device_attached = false;
|
||||
DeviceDescriptor deviceDesc;
|
||||
std::vector<ConfigDescriptor> configDesc;
|
||||
std::vector<InterfaceDescriptor> interfaceDesc;
|
||||
std::vector<EndpointDescriptor> endpointDesc;
|
||||
bool m_has_initialised = false;
|
||||
DeviceDescriptor m_device_descriptor;
|
||||
std::vector<ConfigDescriptor> m_config_descriptor;
|
||||
std::vector<InterfaceDescriptor> m_interface_descriptor;
|
||||
std::vector<EndpointDescriptor> m_endpoint_descriptor;
|
||||
std::queue<std::array<u8, 64>> m_queries;
|
||||
};
|
||||
|
||||
struct Skylander final
|
||||
@@ -60,12 +61,22 @@ struct Skylander final
|
||||
std::queue<u8> queued_status;
|
||||
std::array<u8, 0x40 * 0x10> data{};
|
||||
u32 last_id = 0;
|
||||
void save();
|
||||
void Save();
|
||||
|
||||
enum : u8
|
||||
{
|
||||
REMOVED = 0,
|
||||
READY = 1,
|
||||
REMOVING = 2,
|
||||
ADDED = 3
|
||||
};
|
||||
};
|
||||
|
||||
struct LedColor final
|
||||
struct SkylanderLEDColor final
|
||||
{
|
||||
u8 r = 0, g = 0, b = 0;
|
||||
u8 red = 0;
|
||||
u8 green = 0;
|
||||
u8 blue = 0;
|
||||
};
|
||||
|
||||
class SkylanderPortal final
|
||||
@@ -81,22 +92,21 @@ public:
|
||||
void QueryBlock(u8 sky_num, u8 block, u8* reply_buf);
|
||||
void WriteBlock(u8 sky_num, u8 block, const u8* to_write_buf, u8* reply_buf);
|
||||
|
||||
bool CreateSkylander(const std::string& file_path, u16 sky_id, u16 sky_var);
|
||||
bool RemoveSkylander(u8 sky_num);
|
||||
u8 LoadSkylander(u8* buf, File::IOFile in_file);
|
||||
|
||||
protected:
|
||||
std::mutex sky_mutex;
|
||||
|
||||
bool activated = true;
|
||||
bool status_updated = false;
|
||||
u8 interrupt_counter = 0;
|
||||
LedColor color_right = {};
|
||||
LedColor color_left = {};
|
||||
LedColor color_trap = {};
|
||||
bool m_activated = true;
|
||||
bool m_status_updated = false;
|
||||
u8 m_interrupt_counter = 0;
|
||||
SkylanderLEDColor m_color_right = {};
|
||||
SkylanderLEDColor m_color_left = {};
|
||||
SkylanderLEDColor m_color_trap = {};
|
||||
|
||||
Skylander skylanders[MAX_SKYLANDERS];
|
||||
std::array<Skylander, MAX_SKYLANDERS> skylanders;
|
||||
};
|
||||
|
||||
extern SkylanderPortal g_skyportal;
|
||||
|
||||
} // namespace IOS::HLE::USB
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#include "Core/IOS/USB/EmulatedUSBDevice.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/Timer.h"
|
||||
#include "Core/Core.h"
|
||||
|
||||
namespace IOS::HLE::USB
|
||||
{
|
||||
EmulatedUSBDevice::EmulatedUSBDevice(Kernel& ios, const std::string& device_name) : m_ios(ios)
|
||||
{
|
||||
}
|
||||
|
||||
EmulatedUSBDevice::~EmulatedUSBDevice()
|
||||
{
|
||||
}
|
||||
|
||||
EmulatedUSBDevice::FakeTransferThread::~FakeTransferThread()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void EmulatedUSBDevice::FakeTransferThread::Start()
|
||||
{
|
||||
if (Core::WantsDeterminism())
|
||||
return;
|
||||
|
||||
if (m_thread_running.TestAndSet())
|
||||
{
|
||||
m_thread = std::thread([this] {
|
||||
Common::SetCurrentThreadName("Fake Transfer Thread");
|
||||
while (m_thread_running.IsSet())
|
||||
{
|
||||
if (!m_transfers.empty())
|
||||
{
|
||||
std::lock_guard lk{m_transfers_mutex};
|
||||
u64 timestamp = Common::Timer::NowUs();
|
||||
for (auto iterator = m_transfers.begin(); iterator != m_transfers.end();)
|
||||
{
|
||||
auto* command = iterator->second.get();
|
||||
if (command->expected_time > timestamp)
|
||||
{
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
command->FillBuffer(iterator->first.data(), command->expected_count);
|
||||
command->OnTransferComplete(command->expected_count);
|
||||
iterator = m_transfers.erase(iterator);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedUSBDevice::FakeTransferThread::Stop()
|
||||
{
|
||||
if (m_thread_running.TestAndClear())
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
void EmulatedUSBDevice::FakeTransferThread::AddTransfer(std::unique_ptr<TransferCommand> command,
|
||||
std::array<u8, 64> data)
|
||||
{
|
||||
std::lock_guard lk{m_transfers_mutex};
|
||||
m_transfers.emplace(data, std::move(command));
|
||||
}
|
||||
|
||||
void EmulatedUSBDevice::FakeTransferThread::ClearTransfers()
|
||||
{
|
||||
std::lock_guard lk{m_transfers_mutex};
|
||||
m_transfers.clear();
|
||||
}
|
||||
|
||||
bool EmulatedUSBDevice::FakeTransferThread::GetTransfers()
|
||||
{
|
||||
std::lock_guard lk{m_transfers_mutex};
|
||||
return m_transfers.empty();
|
||||
}
|
||||
|
||||
} // namespace IOS::HLE::USB
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/IOS/USB/Common.h"
|
||||
|
||||
namespace IOS::HLE::USB
|
||||
{
|
||||
class EmulatedUSBDevice : public Device
|
||||
{
|
||||
public:
|
||||
EmulatedUSBDevice(Kernel& ios, const std::string& device_name);
|
||||
virtual ~EmulatedUSBDevice();
|
||||
|
||||
protected:
|
||||
class FakeTransferThread final
|
||||
{
|
||||
public:
|
||||
explicit FakeTransferThread(EmulatedUSBDevice* device) : m_device(device) {}
|
||||
~FakeTransferThread();
|
||||
void Start();
|
||||
void Stop();
|
||||
void AddTransfer(std::unique_ptr<TransferCommand> command, std::array<u8, 64> data);
|
||||
void ClearTransfers();
|
||||
bool GetTransfers();
|
||||
|
||||
private:
|
||||
EmulatedUSBDevice* m_device = nullptr;
|
||||
Common::Flag m_thread_running;
|
||||
std::thread m_thread;
|
||||
Common::Flag m_is_initialized;
|
||||
std::map<std::array<u8, 64>, std::unique_ptr<TransferCommand>> m_transfers;
|
||||
std::mutex m_transfers_mutex;
|
||||
};
|
||||
FakeTransferThread m_transfer_thread{this};
|
||||
FakeTransferThread& GetTransferThread() { return m_transfer_thread; }
|
||||
|
||||
private:
|
||||
Kernel& m_ios;
|
||||
};
|
||||
} // namespace IOS::HLE::USB
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "Core/IOS/USB/Common.h"
|
||||
#include "Core/IOS/USB/Emulated/Skylander.h"
|
||||
#include "Core/IOS/USB/LibusbDevice.h"
|
||||
#include "Core/NetPlayProto.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
namespace IOS::HLE
|
||||
{
|
||||
@@ -35,7 +37,7 @@ USBHost::~USBHost() = default;
|
||||
|
||||
std::optional<IPCReply> USBHost::Open(const OpenRequest& request)
|
||||
{
|
||||
if (!m_has_initialised && !Core::WantsDeterminism())
|
||||
if (!m_has_initialised)
|
||||
{
|
||||
GetScanThread().Start();
|
||||
// Force a device scan to complete, because some games (including Your Shape) only care
|
||||
@@ -97,12 +99,15 @@ bool USBHost::ShouldAddDevice(const USB::Device& device) const
|
||||
return true;
|
||||
}
|
||||
|
||||
void USBHost::Update()
|
||||
{
|
||||
if (Core::WantsDeterminism())
|
||||
UpdateDevices();
|
||||
}
|
||||
|
||||
// This is called from the scan thread. Returns false if we failed to update the device list.
|
||||
bool USBHost::UpdateDevices(const bool always_add_hooks)
|
||||
{
|
||||
if (Core::WantsDeterminism())
|
||||
return true;
|
||||
|
||||
DeviceChangeHooks hooks;
|
||||
std::set<u64> plugged_devices;
|
||||
// If we failed to get a new, up-to-date list of devices, we cannot detect device removals.
|
||||
@@ -116,43 +121,35 @@ bool USBHost::UpdateDevices(const bool always_add_hooks)
|
||||
bool USBHost::AddNewDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks,
|
||||
const bool always_add_hooks)
|
||||
{
|
||||
if (Config::EmulateSkylanderPortal())
|
||||
{
|
||||
auto skylanderportal = std::make_unique<USB::SkylanderUSB>(m_ios, "Skylander Portal");
|
||||
if (!ShouldAddDevice(*skylanderportal))
|
||||
return true;
|
||||
const u64 skyid = skylanderportal->GetId();
|
||||
new_devices.insert(skyid);
|
||||
if (AddDevice(std::move(skylanderportal)))
|
||||
{
|
||||
hooks.emplace(GetDeviceById(skyid), ChangeEvent::Inserted);
|
||||
}
|
||||
}
|
||||
AddEmulatedDevices(new_devices, hooks, always_add_hooks);
|
||||
#ifdef __LIBUSB__
|
||||
auto whitelist = Config::GetUSBDeviceWhitelist();
|
||||
if (whitelist.empty())
|
||||
return true;
|
||||
|
||||
if (m_context.IsValid())
|
||||
if (!Core::WantsDeterminism())
|
||||
{
|
||||
const int ret = m_context.GetDeviceList([&](libusb_device* device) {
|
||||
libusb_device_descriptor descriptor;
|
||||
libusb_get_device_descriptor(device, &descriptor);
|
||||
if (whitelist.count({descriptor.idVendor, descriptor.idProduct}) == 0)
|
||||
return true;
|
||||
|
||||
auto usb_device = std::make_unique<USB::LibusbDevice>(m_ios, device, descriptor);
|
||||
if (!ShouldAddDevice(*usb_device))
|
||||
return true;
|
||||
|
||||
const u64 id = usb_device->GetId();
|
||||
new_devices.insert(id);
|
||||
if (AddDevice(std::move(usb_device)) || always_add_hooks)
|
||||
hooks.emplace(GetDeviceById(id), ChangeEvent::Inserted);
|
||||
auto whitelist = Config::GetUSBDeviceWhitelist();
|
||||
if (whitelist.empty())
|
||||
return true;
|
||||
});
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
WARN_LOG_FMT(IOS_USB, "GetDeviceList failed: {}", LibusbUtils::ErrorWrap(ret));
|
||||
|
||||
if (m_context.IsValid())
|
||||
{
|
||||
const int ret = m_context.GetDeviceList([&](libusb_device* device) {
|
||||
libusb_device_descriptor descriptor;
|
||||
libusb_get_device_descriptor(device, &descriptor);
|
||||
if (whitelist.count({descriptor.idVendor, descriptor.idProduct}) == 0)
|
||||
return true;
|
||||
|
||||
auto usb_device = std::make_unique<USB::LibusbDevice>(m_ios, device, descriptor);
|
||||
if (!ShouldAddDevice(*usb_device))
|
||||
return true;
|
||||
|
||||
const u64 id = usb_device->GetId();
|
||||
new_devices.insert(id);
|
||||
if (AddDevice(std::move(usb_device)) || always_add_hooks)
|
||||
hooks.emplace(GetDeviceById(id), ChangeEvent::Inserted);
|
||||
return true;
|
||||
});
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
WARN_LOG_FMT(IOS_USB, "GetDeviceList failed: {}", LibusbUtils::ErrorWrap(ret));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
@@ -188,6 +185,24 @@ void USBHost::DispatchHooks(const DeviceChangeHooks& hooks)
|
||||
OnDeviceChangeEnd();
|
||||
}
|
||||
|
||||
void USBHost::AddEmulatedDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks,
|
||||
bool always_add_hooks)
|
||||
{
|
||||
if (Config::Get(Config::MAIN_EMULATE_SKYLANDER_PORTAL) && !NetPlay::IsNetPlayRunning())
|
||||
{
|
||||
auto skylanderportal = std::make_unique<USB::SkylanderUSB>(m_ios, "Skylander Portal");
|
||||
if (ShouldAddDevice(*skylanderportal))
|
||||
{
|
||||
const u64 skyid = skylanderportal->GetId();
|
||||
new_devices.insert(skyid);
|
||||
if (AddDevice(std::move(skylanderportal)) || always_add_hooks)
|
||||
{
|
||||
hooks.emplace(GetDeviceById(skyid), ChangeEvent::Inserted);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
USBHost::ScanThread::~ScanThread()
|
||||
{
|
||||
Stop();
|
||||
@@ -195,14 +210,19 @@ USBHost::ScanThread::~ScanThread()
|
||||
|
||||
void USBHost::ScanThread::WaitForFirstScan()
|
||||
{
|
||||
m_first_scan_complete_event.Wait();
|
||||
if (m_thread_running.IsSet())
|
||||
{
|
||||
m_first_scan_complete_event.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void USBHost::ScanThread::Start()
|
||||
{
|
||||
if (Core::WantsDeterminism())
|
||||
{
|
||||
m_host->UpdateDevices();
|
||||
return;
|
||||
|
||||
}
|
||||
if (m_thread_running.TestAndSet())
|
||||
{
|
||||
m_thread = std::thread([this] {
|
||||
|
||||
@@ -76,10 +76,13 @@ protected:
|
||||
|
||||
private:
|
||||
bool AddDevice(std::unique_ptr<USB::Device> device);
|
||||
void Update() override;
|
||||
bool UpdateDevices(bool always_add_hooks = false);
|
||||
bool AddNewDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks, bool always_add_hooks);
|
||||
void DetectRemovedDevices(const std::set<u64>& plugged_devices, DeviceChangeHooks& hooks);
|
||||
void DispatchHooks(const DeviceChangeHooks& hooks);
|
||||
void AddEmulatedDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks,
|
||||
bool always_add_hooks);
|
||||
|
||||
bool m_has_initialised = false;
|
||||
LibusbUtils::Context m_context;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Core/HW/SI/SI.h"
|
||||
#include "Core/HW/Sram.h"
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include "IOS/USB/Emulated/Skylander.h"
|
||||
#include "VideoCommon/CommandProcessor.h"
|
||||
#include "VideoCommon/Fifo.h"
|
||||
#include "VideoCommon/GeometryShaderManager.h"
|
||||
@@ -47,6 +48,7 @@ struct System::Impl
|
||||
Fifo::FifoManager m_fifo;
|
||||
GeometryShaderManager m_geometry_shader_manager;
|
||||
GPFifo::GPFifoManager m_gp_fifo;
|
||||
IOS::HLE::USB::SkylanderPortal m_skylander_portal;
|
||||
Memory::MemoryManager m_memory;
|
||||
MemoryInterface::MemoryInterfaceState m_memory_interface_state;
|
||||
PixelEngine::PixelEngineManager m_pixel_engine;
|
||||
@@ -151,6 +153,11 @@ GPFifo::GPFifoManager& System::GetGPFifo() const
|
||||
return m_impl->m_gp_fifo;
|
||||
}
|
||||
|
||||
IOS::HLE::USB::SkylanderPortal& System::GetSkylanderPortal() const
|
||||
{
|
||||
return m_impl->m_skylander_portal;
|
||||
}
|
||||
|
||||
Memory::MemoryManager& System::GetMemory() const
|
||||
{
|
||||
return m_impl->m_memory;
|
||||
|
||||
@@ -47,6 +47,10 @@ namespace GPFifo
|
||||
{
|
||||
class GPFifoManager;
|
||||
}
|
||||
namespace IOS::HLE::USB
|
||||
{
|
||||
class SkylanderPortal;
|
||||
};
|
||||
namespace Memory
|
||||
{
|
||||
class MemoryManager;
|
||||
@@ -116,6 +120,7 @@ public:
|
||||
Fifo::FifoManager& GetFifo() const;
|
||||
GeometryShaderManager& GetGeometryShaderManager() const;
|
||||
GPFifo::GPFifoManager& GetGPFifo() const;
|
||||
IOS::HLE::USB::SkylanderPortal& GetSkylanderPortal() const;
|
||||
Memory::MemoryManager& GetMemory() const;
|
||||
MemoryInterface::MemoryInterfaceState& GetMemoryInterfaceState() const;
|
||||
PixelEngine::PixelEngineManager& GetPixelEngine() const;
|
||||
|
||||
@@ -373,7 +373,6 @@
|
||||
<ClInclude Include="Core\IOS\USB\Bluetooth\WiimoteDevice.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Bluetooth\WiimoteHIDAttr.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Common.h" />
|
||||
<ClInclude Include="Core\IOS\USB\EmulatedUSBDevice.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Emulated\Skylander.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Host.h" />
|
||||
<ClInclude Include="Core\IOS\USB\LibusbDevice.h" />
|
||||
@@ -987,7 +986,6 @@
|
||||
<ClCompile Include="Core\IOS\USB\Bluetooth\WiimoteDevice.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Bluetooth\WiimoteHIDAttr.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Common.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\EmulatedUSBDevice.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Emulated\Skylander.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Host.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\LibusbDevice.cpp" />
|
||||
|
||||
@@ -221,7 +221,7 @@ void MenuBar::AddToolsMenu()
|
||||
|
||||
tools_menu->addAction(tr("FIFO Player"), this, &MenuBar::ShowFIFOPlayer);
|
||||
|
||||
tools_menu->addAction(tr("&Skylanders Portal"), this, [this] { emit ShowSkylanderPortal(); });
|
||||
tools_menu->addAction(tr("&Skylanders Portal"), this, &MenuBar::ShowSkylanderPortal);
|
||||
|
||||
tools_menu->addSeparator();
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// DolphinQt code copied and modified for Dolphin from the RPCS3 Qt utility for Creating, Loading
|
||||
// and Clearing skylanders
|
||||
|
||||
@@ -6,12 +9,14 @@
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QCompleter>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QEvent>
|
||||
#include <QFileDialog>
|
||||
#include <QGroupBox>
|
||||
#include <QKeyEvent>
|
||||
#include <QKeySequence>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
@@ -22,15 +27,16 @@
|
||||
#include "Common/IOFile.h"
|
||||
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "DolphinQt/QtUtils/DolphinFileDialog.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
SkylanderPortalWindow* SkylanderPortalWindow::inst = nullptr;
|
||||
std::optional<std::tuple<u8, u16, u16>> SkylanderPortalWindow::sky_slots[MAX_SKYLANDERS];
|
||||
QString last_skylander_path;
|
||||
// Qt is not guaranteed to keep track of file paths using native file pickers, so we use this
|
||||
// static variable to ensure we open at the most recent Skylander file location
|
||||
static QString s_last_skylander_path;
|
||||
|
||||
const std::map<const std::pair<const u16, const u16>, const std::string> list_skylanders = {
|
||||
const std::map<const std::pair<const u16, const u16>, const char*> list_skylanders = {
|
||||
{{0, 0x0000}, "Whirlwind"},
|
||||
{{0, 0x1801}, "Series 2 Whirlwind"},
|
||||
{{0, 0x1C02}, "Polar Whirlwind"},
|
||||
@@ -513,49 +519,10 @@ const std::map<const std::pair<const u16, const u16>, const std::string> list_sk
|
||||
{{3503, 0x0000}, "Kaos Trophy"},
|
||||
};
|
||||
|
||||
u16 skylander_crc16(u16 init_value, const u8* buffer, u32 size)
|
||||
{
|
||||
const unsigned short CRC_CCITT_TABLE[256] = {
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A,
|
||||
0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294,
|
||||
0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462,
|
||||
0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509,
|
||||
0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695,
|
||||
0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5,
|
||||
0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948,
|
||||
0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
|
||||
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
|
||||
0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B,
|
||||
0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F,
|
||||
0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB,
|
||||
0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046,
|
||||
0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290,
|
||||
0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E,
|
||||
0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691,
|
||||
0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
|
||||
0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D,
|
||||
0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16,
|
||||
0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8,
|
||||
0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E,
|
||||
0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93,
|
||||
0x3EB2, 0x0ED1, 0x1EF0};
|
||||
|
||||
u16 crc = init_value;
|
||||
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
const u16 tmp = (crc >> 8) ^ buffer[i];
|
||||
crc = (crc << 8) ^ CRC_CCITT_TABLE[tmp];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
SkylanderPortalWindow::SkylanderPortalWindow(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
setWindowTitle(tr("Skylanders Manager"));
|
||||
setObjectName(QString::fromStdString("skylanders_manager"));
|
||||
setObjectName(tr("skylanders_manager"));
|
||||
setMinimumSize(QSize(700, 200));
|
||||
|
||||
CreateMainWindow();
|
||||
@@ -568,33 +535,32 @@ SkylanderPortalWindow::SkylanderPortalWindow(QWidget* parent) : QWidget(parent)
|
||||
OnEmulationStateChanged(Core::GetState());
|
||||
};
|
||||
|
||||
SkylanderPortalWindow::~SkylanderPortalWindow()
|
||||
{
|
||||
}
|
||||
SkylanderPortalWindow::~SkylanderPortalWindow() = default;
|
||||
|
||||
void SkylanderPortalWindow::CreateMainWindow()
|
||||
{
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout();
|
||||
auto* main_layout = new QVBoxLayout();
|
||||
|
||||
QGroupBox* checkbox_group = new QGroupBox();
|
||||
QHBoxLayout* checkboxLayout = new QHBoxLayout();
|
||||
checkboxLayout->setAlignment(Qt::AlignHCenter);
|
||||
checkbox = new QCheckBox(QString::fromStdString("Emulate Skylander Portal"), this);
|
||||
connect(checkbox, &QCheckBox::toggled, [=](bool checked) { EmulatePortal(checked); });
|
||||
checkboxLayout->addWidget(checkbox);
|
||||
checkbox_group->setLayout(checkboxLayout);
|
||||
mainLayout->addWidget(checkbox_group);
|
||||
auto* checkbox_group = new QGroupBox();
|
||||
auto* checkbox_layout = new QHBoxLayout();
|
||||
checkbox_layout->setAlignment(Qt::AlignHCenter);
|
||||
m_checkbox = new QCheckBox(tr("Emulate Skylander Portal"), this);
|
||||
m_checkbox->setChecked(Config::Get(Config::MAIN_EMULATE_SKYLANDER_PORTAL));
|
||||
connect(m_checkbox, &QCheckBox::toggled, [=](bool checked) { EmulatePortal(checked); });
|
||||
checkbox_layout->addWidget(m_checkbox);
|
||||
checkbox_group->setLayout(checkbox_layout);
|
||||
main_layout->addWidget(checkbox_group);
|
||||
|
||||
auto add_line = [](QVBoxLayout* vbox) {
|
||||
QFrame* line = new QFrame();
|
||||
auto* line = new QFrame();
|
||||
line->setFrameShape(QFrame::HLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
vbox->addWidget(line);
|
||||
};
|
||||
|
||||
group_skylanders = new QGroupBox(tr("Active Portal Skylanders:"));
|
||||
QVBoxLayout* vbox_group = new QVBoxLayout();
|
||||
QScrollArea* scroll_area = new QScrollArea();
|
||||
m_group_skylanders = new QGroupBox(tr("Active Portal Skylanders:"));
|
||||
auto* vbox_group = new QVBoxLayout();
|
||||
auto* scroll_area = new QScrollArea();
|
||||
|
||||
for (auto i = 0; i < MAX_SKYLANDERS; i++)
|
||||
{
|
||||
@@ -603,21 +569,21 @@ void SkylanderPortalWindow::CreateMainWindow()
|
||||
add_line(vbox_group);
|
||||
}
|
||||
|
||||
QHBoxLayout* hbox_skylander = new QHBoxLayout();
|
||||
QLabel* label_skyname = new QLabel(QString(tr("Skylander %1")).arg(i + 1));
|
||||
edit_skylanders[i] = new QLineEdit();
|
||||
edit_skylanders[i]->setEnabled(false);
|
||||
auto* hbox_skylander = new QHBoxLayout();
|
||||
auto* label_skyname = new QLabel(QString(tr("Skylander %1")).arg(i + 1));
|
||||
m_edit_skylanders[i] = new QLineEdit();
|
||||
m_edit_skylanders[i]->setEnabled(false);
|
||||
|
||||
QPushButton* clear_btn = new QPushButton(tr("Clear"));
|
||||
QPushButton* create_btn = new QPushButton(tr("Create"));
|
||||
QPushButton* load_btn = new QPushButton(tr("Load"));
|
||||
auto* clear_btn = new QPushButton(tr("Clear"));
|
||||
auto* create_btn = new QPushButton(tr("Create"));
|
||||
auto* load_btn = new QPushButton(tr("Load"));
|
||||
|
||||
connect(clear_btn, &QAbstractButton::clicked, this, [this, i]() { ClearSkylander(i); });
|
||||
connect(create_btn, &QAbstractButton::clicked, this, [this, i]() { CreateSkylander(i); });
|
||||
connect(load_btn, &QAbstractButton::clicked, this, [this, i]() { LoadSkylander(i); });
|
||||
|
||||
hbox_skylander->addWidget(label_skyname);
|
||||
hbox_skylander->addWidget(edit_skylanders[i]);
|
||||
hbox_skylander->addWidget(m_edit_skylanders[i]);
|
||||
hbox_skylander->addWidget(clear_btn);
|
||||
hbox_skylander->addWidget(create_btn);
|
||||
hbox_skylander->addWidget(load_btn);
|
||||
@@ -625,12 +591,12 @@ void SkylanderPortalWindow::CreateMainWindow()
|
||||
vbox_group->addLayout(hbox_skylander);
|
||||
}
|
||||
|
||||
group_skylanders->setLayout(vbox_group);
|
||||
scroll_area->setWidget(group_skylanders);
|
||||
m_group_skylanders->setLayout(vbox_group);
|
||||
scroll_area->setWidget(m_group_skylanders);
|
||||
scroll_area->setWidgetResizable(true);
|
||||
group_skylanders->setVisible(false);
|
||||
mainLayout->addWidget(scroll_area);
|
||||
setLayout(mainLayout);
|
||||
m_group_skylanders->setVisible(Config::Get(Config::MAIN_EMULATE_SKYLANDER_PORTAL));
|
||||
main_layout->addWidget(scroll_area);
|
||||
setLayout(main_layout);
|
||||
|
||||
UpdateEdits();
|
||||
}
|
||||
@@ -639,29 +605,29 @@ void SkylanderPortalWindow::OnEmulationStateChanged(Core::State state)
|
||||
{
|
||||
const bool running = state != Core::State::Uninitialized;
|
||||
|
||||
checkbox->setEnabled(!running);
|
||||
m_checkbox->setEnabled(!running);
|
||||
}
|
||||
|
||||
CreateSkylanderDialog::CreateSkylanderDialog(QWidget* parent) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Skylander Creator"));
|
||||
setObjectName(QString::fromStdString("skylanders_creator"));
|
||||
setObjectName(tr("skylanders_creator"));
|
||||
setMinimumSize(QSize(500, 150));
|
||||
QVBoxLayout* layout = new QVBoxLayout;
|
||||
auto* layout = new QVBoxLayout;
|
||||
|
||||
QComboBox* combo_skylist = new QComboBox();
|
||||
auto* combo_skylist = new QComboBox();
|
||||
QStringList filterlist;
|
||||
for (const auto& entry : list_skylanders)
|
||||
{
|
||||
const uint qvar = (entry.first.first << 16) | entry.first.second;
|
||||
combo_skylist->addItem(QString::fromStdString(entry.second), QVariant(qvar));
|
||||
filterlist << QString::fromStdString(entry.second.c_str());
|
||||
combo_skylist->addItem(tr(entry.second), QVariant(qvar));
|
||||
filterlist << tr(entry.second);
|
||||
}
|
||||
combo_skylist->addItem(tr("--Unknown--"), QVariant(0xFFFFFFFF));
|
||||
combo_skylist->setEditable(true);
|
||||
combo_skylist->setInsertPolicy(QComboBox::NoInsert);
|
||||
|
||||
QCompleter* co_compl = new QCompleter(filterlist, this);
|
||||
auto* co_compl = new QCompleter(filterlist, this);
|
||||
co_compl->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
co_compl->setCompletionMode(QCompleter::PopupCompletion);
|
||||
co_compl->setFilterMode(Qt::MatchContains);
|
||||
@@ -669,18 +635,17 @@ CreateSkylanderDialog::CreateSkylanderDialog(QWidget* parent) : QDialog(parent)
|
||||
|
||||
layout->addWidget(combo_skylist);
|
||||
|
||||
QFrame* line = new QFrame();
|
||||
auto* line = new QFrame();
|
||||
line->setFrameShape(QFrame::HLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
layout->addWidget(line);
|
||||
|
||||
QHBoxLayout* hbox_idvar = new QHBoxLayout();
|
||||
QLabel* label_id = new QLabel(tr("ID:"));
|
||||
QLabel* label_var = new QLabel(tr("Variant:"));
|
||||
QLineEdit* edit_id = new QLineEdit(QString::fromStdString("0"));
|
||||
QLineEdit* edit_var = new QLineEdit(QString::fromStdString("0"));
|
||||
QRegularExpressionValidator* rxv =
|
||||
new QRegularExpressionValidator(QRegularExpression(QString::fromStdString("\\d*")), this);
|
||||
auto* hbox_idvar = new QHBoxLayout();
|
||||
auto* label_id = new QLabel(tr("ID:"));
|
||||
auto* label_var = new QLabel(tr("Variant:"));
|
||||
auto* edit_id = new QLineEdit(tr("0"));
|
||||
auto* edit_var = new QLineEdit(tr("0"));
|
||||
auto* rxv = new QRegularExpressionValidator(QRegularExpression(tr("\\d*")), this);
|
||||
edit_id->setValidator(rxv);
|
||||
edit_var->setValidator(rxv);
|
||||
hbox_idvar->addWidget(label_id);
|
||||
@@ -689,13 +654,9 @@ CreateSkylanderDialog::CreateSkylanderDialog(QWidget* parent) : QDialog(parent)
|
||||
hbox_idvar->addWidget(edit_var);
|
||||
layout->addLayout(hbox_idvar);
|
||||
|
||||
QHBoxLayout* hbox_buttons = new QHBoxLayout();
|
||||
QPushButton* btn_create = new QPushButton(tr("Create"), this);
|
||||
QPushButton* btn_cancel = new QPushButton(tr("Cancel"), this);
|
||||
hbox_buttons->addStretch();
|
||||
hbox_buttons->addWidget(btn_create);
|
||||
hbox_buttons->addWidget(btn_cancel);
|
||||
layout->addLayout(hbox_buttons);
|
||||
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
buttons->button(QDialogButtonBox::Ok)->setText(tr("Create"));
|
||||
layout->addWidget(buttons);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
@@ -711,7 +672,7 @@ CreateSkylanderDialog::CreateSkylanderDialog(QWidget* parent) : QDialog(parent)
|
||||
}
|
||||
});
|
||||
|
||||
connect(btn_create, &QAbstractButton::clicked, this, [=, this]() {
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, [=, this]() {
|
||||
bool ok_id = false, ok_var = false;
|
||||
const u16 sky_id = edit_id->text().toUShort(&ok_id);
|
||||
if (!ok_id)
|
||||
@@ -728,61 +689,40 @@ CreateSkylanderDialog::CreateSkylanderDialog(QWidget* parent) : QDialog(parent)
|
||||
return;
|
||||
}
|
||||
|
||||
QString predef_name = last_skylander_path;
|
||||
QString predef_name = s_last_skylander_path;
|
||||
const auto found_sky = list_skylanders.find(std::make_pair(sky_id, sky_var));
|
||||
if (found_sky != list_skylanders.end())
|
||||
{
|
||||
predef_name += QString::fromStdString(found_sky->second + ".sky");
|
||||
std::string name = std::string(found_sky->second) + ".sky";
|
||||
predef_name += tr(name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
QString str = QString::fromStdString("Unknown(%1 %2).sky");
|
||||
QString str = tr("Unknown(%1 %2).sky");
|
||||
predef_name += str.arg(sky_id, sky_var);
|
||||
}
|
||||
|
||||
file_path = QFileDialog::getSaveFileName(this, tr("Create Skylander File"), predef_name,
|
||||
tr("Skylander Object (*.sky);;"));
|
||||
if (file_path.isEmpty())
|
||||
m_file_path = QFileDialog::getSaveFileName(this, tr("Create Skylander File"), predef_name,
|
||||
tr("Skylander Object (*.sky);;"));
|
||||
if (m_file_path.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
File::IOFile sky_file(file_path.toStdString(), "w+b");
|
||||
if (!sky_file)
|
||||
auto& system = Core::System::GetInstance();
|
||||
|
||||
if (!system.GetSkylanderPortal().CreateSkylander(m_file_path.toStdString(), sky_id, sky_var))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed to create skylander file!"),
|
||||
tr("Failed to create skylander file:\n%1").arg(file_path),
|
||||
tr("Failed to create skylander file:\n%1").arg(m_file_path),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<u8, 0x40 * 0x10> buf{};
|
||||
const auto file_data = buf.data();
|
||||
// Set the block permissions
|
||||
u32 first_block = 0x690F0F0F;
|
||||
u32 other_blocks = 0x69080F7F;
|
||||
memcpy(&file_data[0x36], &first_block, sizeof(first_block));
|
||||
for (u32 index = 1; index < 0x10; index++)
|
||||
{
|
||||
memcpy(&file_data[(index * 0x40) + 0x36], &other_blocks, sizeof(other_blocks));
|
||||
}
|
||||
// Set the skylander info
|
||||
u16 sky_info = (sky_id | sky_var) + 1;
|
||||
memcpy(&file_data[0], &sky_info, sizeof(sky_info));
|
||||
memcpy(&file_data[0x10], &sky_id, sizeof(sky_id));
|
||||
memcpy(&file_data[0x1C], &sky_var, sizeof(sky_var));
|
||||
// Set checksum
|
||||
u16 checksum = skylander_crc16(0xFFFF, file_data, 0x1E);
|
||||
memcpy(&file_data[0x1E], &checksum, sizeof(checksum));
|
||||
|
||||
sky_file.WriteBytes(buf.data(), buf.size());
|
||||
sky_file.Close();
|
||||
|
||||
last_skylander_path = QFileInfo(file_path).absolutePath() + QString::fromStdString("/");
|
||||
s_last_skylander_path = QFileInfo(m_file_path).absolutePath() + tr("/");
|
||||
accept();
|
||||
});
|
||||
|
||||
connect(btn_cancel, &QAbstractButton::clicked, this, &QDialog::reject);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
connect(co_compl, QOverload<const QString&>::of(&QCompleter::activated),
|
||||
[=](const QString& text) {
|
||||
@@ -793,13 +733,13 @@ CreateSkylanderDialog::CreateSkylanderDialog(QWidget* parent) : QDialog(parent)
|
||||
|
||||
QString CreateSkylanderDialog::GetFilePath() const
|
||||
{
|
||||
return file_path;
|
||||
return m_file_path;
|
||||
}
|
||||
|
||||
void SkylanderPortalWindow::EmulatePortal(bool emulate)
|
||||
{
|
||||
Config::SetBaseOrCurrent(Config::MAIN_EMULATE_SKYLANDER_PORTAL, emulate);
|
||||
group_skylanders->setVisible(emulate);
|
||||
m_group_skylanders->setVisible(emulate);
|
||||
}
|
||||
|
||||
void SkylanderPortalWindow::CreateSkylander(u8 slot)
|
||||
@@ -814,12 +754,12 @@ void SkylanderPortalWindow::CreateSkylander(u8 slot)
|
||||
void SkylanderPortalWindow::LoadSkylander(u8 slot)
|
||||
{
|
||||
const QString file_path = DolphinFileDialog::getOpenFileName(
|
||||
this, tr("Select Skylander File"), last_skylander_path, tr("Skylander (*.sky);;"));
|
||||
this, tr("Select Skylander File"), s_last_skylander_path, tr("Skylander (*.sky);;"));
|
||||
if (file_path.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
last_skylander_path = QFileInfo(file_path).absolutePath() + QString::fromStdString("/");
|
||||
s_last_skylander_path = QFileInfo(file_path).absolutePath() + tr("/");
|
||||
|
||||
LoadSkylanderPath(slot, file_path);
|
||||
}
|
||||
@@ -859,19 +799,26 @@ void SkylanderPortalWindow::LoadSkylanderPath(u8 slot, const QString& path)
|
||||
DEBUG_LOG_FMT(IOS_USB, "Sky Var: {}, 0x1D: {} 0x1C: {}", sky_var, file_data[0x1D],
|
||||
file_data[0x1C]);
|
||||
|
||||
u8 portal_slot = IOS::HLE::USB::g_skyportal.LoadSkylander(file_data.data(), std::move(sky_file));
|
||||
sky_slots[slot] = std::tuple(portal_slot, sky_id, sky_var);
|
||||
auto& system = Core::System::GetInstance();
|
||||
|
||||
u8 portal_slot = system.GetSkylanderPortal().LoadSkylander(file_data.data(), std::move(sky_file));
|
||||
if (portal_slot == 0xFF)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed to load the skylander file!"),
|
||||
tr("Failed to load the skylander file(%1)!\n").arg(path), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
m_sky_slots[slot] = {portal_slot, sky_id, sky_var};
|
||||
UpdateEdits();
|
||||
}
|
||||
|
||||
void SkylanderPortalWindow::ClearSkylander(u8 slot)
|
||||
{
|
||||
if (auto slot_infos = sky_slots[slot])
|
||||
auto& system = Core::System::GetInstance();
|
||||
if (auto slot_infos = m_sky_slots[slot])
|
||||
{
|
||||
auto [cur_slot, id, var] = slot_infos.value();
|
||||
IOS::HLE::USB::g_skyportal.RemoveSkylander(cur_slot);
|
||||
sky_slots[slot] = {};
|
||||
system.GetSkylanderPortal().RemoveSkylander(slot_infos->portal_slot);
|
||||
m_sky_slots[slot].reset();
|
||||
UpdateEdits();
|
||||
}
|
||||
}
|
||||
@@ -881,26 +828,24 @@ void SkylanderPortalWindow::UpdateEdits()
|
||||
for (auto i = 0; i < MAX_SKYLANDERS; i++)
|
||||
{
|
||||
QString display_string;
|
||||
if (auto sd = sky_slots[i])
|
||||
if (auto sd = m_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));
|
||||
auto found_sky = list_skylanders.find(std::make_pair(sd->sky_id, sd->sky_var));
|
||||
if (found_sky != list_skylanders.end())
|
||||
{
|
||||
display_string = QString::fromStdString(found_sky->second);
|
||||
display_string = tr(found_sky->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
display_string =
|
||||
QString(QString::fromStdString("Unknown (Id:%1 Var:%2)")).arg(sky_id).arg(sky_var);
|
||||
display_string = QString(tr("Unknown (Id:%1 Var:%2)")).arg(sd->sky_id).arg(sd->sky_var);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
display_string = QString::fromStdString("None");
|
||||
display_string = tr("None");
|
||||
}
|
||||
|
||||
edit_skylanders[i]->setText(display_string);
|
||||
m_edit_skylanders[i]->setText(display_string);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,39 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QGroupBox>
|
||||
#include <QLineEdit>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
#include "Core/Core.h"
|
||||
#include "Core/IOS/USB/Emulated/Skylander.h"
|
||||
|
||||
class QDialogButtonBox;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class QSpinBox;
|
||||
class QTabWidget;
|
||||
class QCheckBox;
|
||||
class QGroupBox;
|
||||
class QLineEdit;
|
||||
|
||||
struct Skylander
|
||||
{
|
||||
u8 portal_slot;
|
||||
u16 sky_id;
|
||||
u16 sky_var;
|
||||
};
|
||||
|
||||
class SkylanderPortalWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SkylanderPortalWindow(QWidget* parent = nullptr);
|
||||
~SkylanderPortalWindow();
|
||||
~SkylanderPortalWindow() override;
|
||||
|
||||
protected:
|
||||
QLineEdit* edit_skylanders[MAX_SKYLANDERS]{};
|
||||
static std::optional<std::tuple<u8, u16, u16>> sky_slots[MAX_SKYLANDERS];
|
||||
std::array<QLineEdit*, MAX_SKYLANDERS> m_edit_skylanders;
|
||||
std::array<std::optional<Skylander>, MAX_SKYLANDERS> m_sky_slots;
|
||||
|
||||
private:
|
||||
void CreateMainWindow();
|
||||
@@ -38,13 +45,10 @@ private:
|
||||
void LoadSkylanderPath(u8 slot, const QString& path);
|
||||
void UpdateEdits();
|
||||
void closeEvent(QCloseEvent* bar) override;
|
||||
|
||||
static SkylanderPortalWindow* inst;
|
||||
|
||||
QCheckBox* checkbox;
|
||||
QGroupBox* group_skylanders;
|
||||
|
||||
bool eventFilter(QObject* object, QEvent* event) final override;
|
||||
|
||||
QCheckBox* m_checkbox;
|
||||
QGroupBox* m_group_skylanders;
|
||||
};
|
||||
|
||||
class CreateSkylanderDialog : public QDialog
|
||||
@@ -56,5 +60,5 @@ public:
|
||||
QString GetFilePath() const;
|
||||
|
||||
protected:
|
||||
QString file_path;
|
||||
QString m_file_path;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user