From aefbb918beb8718af8f190a73018ff63bf801d95 Mon Sep 17 00:00:00 2001 From: Joshua de Reeper Date: Fri, 28 Jun 2024 14:44:49 +0100 Subject: [PATCH] nsyshid: Skylander emulation fixes and code cleanup (#1244) --- src/Cafe/OS/libs/nsyshid/Skylander.cpp | 79 +++++++++++++++++-- src/Cafe/OS/libs/nsyshid/Skylander.h | 17 ++-- .../EmulatedUSBDeviceFrame.cpp | 78 ++++-------------- .../EmulatedUSBDeviceFrame.h | 6 +- 4 files changed, 101 insertions(+), 79 deletions(-) diff --git a/src/Cafe/OS/libs/nsyshid/Skylander.cpp b/src/Cafe/OS/libs/nsyshid/Skylander.cpp index 3123d14d..241e1969 100644 --- a/src/Cafe/OS/libs/nsyshid/Skylander.cpp +++ b/src/Cafe/OS/libs/nsyshid/Skylander.cpp @@ -1,5 +1,7 @@ #include "Skylander.h" +#include + #include "nsyshid.h" #include "Backend.h" @@ -9,8 +11,8 @@ namespace nsyshid { SkylanderUSB g_skyportal; - const std::map, const std::string> - listSkylanders = { + const std::map, const char*> + s_listSkylanders = { {{0, 0x0000}, "Whirlwind"}, {{0, 0x1801}, "Series 2 Whirlwind"}, {{0, 0x1C02}, "Polar Whirlwind"}, @@ -845,6 +847,49 @@ namespace nsyshid return false; } + bool SkylanderUSB::CreateSkylander(fs::path pathName, uint16 skyId, uint16 skyVar) + { + FileStream* skyFile(FileStream::createFile2(pathName)); + if (!skyFile) + { + return false; + } + + std::array data{}; + + uint32 first_block = 0x690F0F0F; + uint32 other_blocks = 0x69080F7F; + memcpy(&data[0x36], &first_block, sizeof(first_block)); + for (size_t index = 1; index < 0x10; index++) + { + memcpy(&data[(index * 0x40) + 0x36], &other_blocks, sizeof(other_blocks)); + } + std::random_device rd; + std::mt19937 mt(rd()); + std::uniform_int_distribution dist(0, 255); + data[0] = dist(mt); + data[1] = dist(mt); + data[2] = dist(mt); + data[3] = dist(mt); + data[4] = data[0] ^ data[1] ^ data[2] ^ data[3]; + data[5] = 0x81; + data[6] = 0x01; + data[7] = 0x0F; + + memcpy(&data[0x10], &skyId, sizeof(skyId)); + memcpy(&data[0x1C], &skyVar, sizeof(skyVar)); + + uint16 crc = nsyshid::g_skyportal.SkylanderCRC16(0xFFFF, data.data(), 0x1E); + + memcpy(&data[0x1E], &crc, sizeof(crc)); + + skyFile->writeData(data.data(), data.size()); + + delete skyFile; + + return true; + } + void SkylanderUSB::QueryBlock(uint8 skyNum, uint8 block, uint8* replyBuf) { std::lock_guard lock(m_skyMutex); @@ -865,7 +910,7 @@ namespace nsyshid } void SkylanderUSB::WriteBlock(uint8 skyNum, uint8 block, - const uint8* toWriteBuf, uint8* replyBuf) + const uint8* toWriteBuf, uint8* replyBuf) { std::lock_guard lock(m_skyMutex); @@ -919,21 +964,39 @@ namespace nsyshid status |= s.status; } interruptResponse = {0x53, 0x00, 0x00, 0x00, 0x00, m_interruptCounter++, - 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}; + 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(&interruptResponse[1], &status, sizeof(status)); } return interruptResponse; } + std::string SkylanderUSB::FindSkylander(uint16 skyId, uint16 skyVar) + { + for (const auto& it : GetListSkylanders()) + { + if(it.first.first == skyId && it.first.second == skyVar) + { + return it.second; + } + } + return fmt::format("Unknown ({} {})", skyId, skyVar); + } + + std::map, const char*> SkylanderUSB::GetListSkylanders() + { + return s_listSkylanders; + } + void SkylanderUSB::Skylander::Save() { if (!skyFile) return; + skyFile->SetPosition(0); skyFile->writeData(data.data(), data.size()); } } // namespace nsyshid \ No newline at end of file diff --git a/src/Cafe/OS/libs/nsyshid/Skylander.h b/src/Cafe/OS/libs/nsyshid/Skylander.h index dfa370dc..a1ca7f8f 100644 --- a/src/Cafe/OS/libs/nsyshid/Skylander.h +++ b/src/Cafe/OS/libs/nsyshid/Skylander.h @@ -1,3 +1,5 @@ +#pragma once + #include #include "nsyshid.h" @@ -36,7 +38,10 @@ namespace nsyshid bool m_IsOpened; }; - extern const std::map, const std::string> listSkylanders; + constexpr uint16 BLOCK_COUNT = 0x40; + constexpr uint16 BLOCK_SIZE = 0x10; + constexpr uint16 FIGURE_SIZE = BLOCK_COUNT * BLOCK_SIZE; + constexpr uint8 MAX_SKYLANDERS = 16; class SkylanderUSB { public: @@ -45,7 +50,7 @@ namespace nsyshid std::unique_ptr skyFile; uint8 status = 0; std::queue queuedStatus; - std::array data{}; + std::array data{}; uint32 lastId = 0; void Save(); @@ -74,16 +79,19 @@ namespace nsyshid std::array GetStatus(); void QueryBlock(uint8 skyNum, uint8 block, uint8* replyBuf); void WriteBlock(uint8 skyNum, uint8 block, const uint8* toWriteBuf, - uint8* replyBuf); + uint8* replyBuf); uint8 LoadSkylander(uint8* buf, std::unique_ptr file); bool RemoveSkylander(uint8 skyNum); + bool CreateSkylander(fs::path pathName, uint16 skyId, uint16 skyVar); uint16 SkylanderCRC16(uint16 initValue, const uint8* buffer, uint32 size); + static std::map, const char*> GetListSkylanders(); + std::string FindSkylander(uint16 skyId, uint16 skyVar); protected: std::mutex m_skyMutex; std::mutex m_queryMutex; - std::array m_skylanders; + std::array m_skylanders; private: std::queue> m_queries; @@ -92,7 +100,6 @@ namespace nsyshid SkylanderLEDColor m_colorRight = {}; SkylanderLEDColor m_colorLeft = {}; SkylanderLEDColor m_colorTrap = {}; - }; extern SkylanderUSB g_skyportal; } // namespace nsyshid \ No newline at end of file diff --git a/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp b/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp index 58c1823c..f43c3690 100644 --- a/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp +++ b/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp @@ -1,7 +1,6 @@ #include "gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h" #include -#include #include "config/CemuConfig.h" #include "gui/helpers/wxHelpers.h" @@ -9,7 +8,6 @@ #include "util/helpers/helpers.h" #include "Cafe/OS/libs/nsyshid/nsyshid.h" -#include "Cafe/OS/libs/nsyshid/Skylander.h" #include "Common/FileStream.h" @@ -75,7 +73,7 @@ wxPanel* EmulatedUSBDeviceFrame::AddSkylanderPage(wxNotebook* notebook) }); row->Add(m_emulatePortal, 1, wxEXPAND | wxALL, 2); boxSizer->Add(row, 1, wxEXPAND | wxALL, 2); - for (int i = 0; i < 16; i++) + for (int i = 0; i < nsyshid::MAX_SKYLANDERS; i++) { boxSizer->Add(AddSkylanderRow(i, box), 1, wxEXPAND | wxALL, 2); } @@ -153,7 +151,7 @@ void EmulatedUSBDeviceFrame::LoadSkylanderPath(uint8 slot, wxString path) uint16 skyVar = uint16(fileData[0x1D]) << 8 | uint16(fileData[0x1C]); uint8 portalSlot = nsyshid::g_skyportal.LoadSkylander(fileData.data(), - std::move(skyFile)); + std::move(skyFile)); m_skySlots[slot] = std::tuple(portalSlot, skyId, skyVar); UpdateSkylanderEdits(); } @@ -189,11 +187,11 @@ CreateSkylanderDialog::CreateSkylanderDialog(wxWindow* parent, uint8 slot) auto* comboBox = new wxComboBox(this, wxID_ANY); comboBox->Append("---Select---", reinterpret_cast(0xFFFFFFFF)); wxArrayString filterlist; - for (auto it = nsyshid::listSkylanders.begin(); it != nsyshid::listSkylanders.end(); it++) + for (const auto& it : nsyshid::g_skyportal.GetListSkylanders()) { - const uint32 variant = uint32(uint32(it->first.first) << 16) | uint32(it->first.second); - comboBox->Append(it->second, reinterpret_cast(variant)); - filterlist.Add(it->second); + const uint32 variant = uint32(uint32(it.first.first) << 16) | uint32(it.first.second); + comboBox->Append(it.second, reinterpret_cast(variant)); + filterlist.Add(it.second); } comboBox->SetSelection(0); bool enabled = comboBox->AutoComplete(filterlist); @@ -233,16 +231,7 @@ CreateSkylanderDialog::CreateSkylanderDialog(wxWindow* parent, uint8 slot) } uint16 skyId = longSkyId & 0xFFFF; uint16 skyVar = longSkyVar & 0xFFFF; - const auto foundSky = nsyshid::listSkylanders.find(std::make_pair(skyId, skyVar)); - wxString predefName; - if (foundSky != nsyshid::listSkylanders.end()) - { - predefName = foundSky->second + ".sky"; - } - else - { - predefName = wxString::Format(_("Unknown(%i %i).sky"), skyId, skyVar); - } + wxString predefName = nsyshid::g_skyportal.FindSkylander(skyId, skyVar) + ".sky"; wxFileDialog saveFileDialog(this, _("Create Skylander file"), "", predefName, "SKY files (*.sky)|*.sky", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); @@ -251,46 +240,15 @@ CreateSkylanderDialog::CreateSkylanderDialog(wxWindow* parent, uint8 slot) return; m_filePath = saveFileDialog.GetPath(); - - wxFileOutputStream output_stream(saveFileDialog.GetPath()); - if (!output_stream.IsOk()) + + if(!nsyshid::g_skyportal.CreateSkylander(_utf8ToPath(m_filePath.utf8_string()), skyId, skyVar)) { - wxMessageDialog saveError(this, "Error Creating Skylander File"); + wxMessageDialog errorMessage(this, "Failed to create file"); + errorMessage.ShowModal(); + this->EndModal(0); return; } - std::array data{}; - - uint32 first_block = 0x690F0F0F; - uint32 other_blocks = 0x69080F7F; - memcpy(&data[0x36], &first_block, sizeof(first_block)); - for (size_t index = 1; index < 0x10; index++) - { - memcpy(&data[(index * 0x40) + 0x36], &other_blocks, sizeof(other_blocks)); - } - std::random_device rd; - std::mt19937 mt(rd()); - std::uniform_int_distribution dist(0, 255); - data[0] = dist(mt); - data[1] = dist(mt); - data[2] = dist(mt); - data[3] = dist(mt); - data[4] = data[0] ^ data[1] ^ data[2] ^ data[3]; - data[5] = 0x81; - data[6] = 0x01; - data[7] = 0x0F; - - memcpy(&data[0x10], &skyId, sizeof(skyId)); - memcpy(&data[0x1C], &skyVar, sizeof(skyVar)); - - uint16 crc = nsyshid::g_skyportal.SkylanderCRC16(0xFFFF, data.data(), 0x1E); - - memcpy(&data[0x1E], &crc, sizeof(crc)); - - output_stream.SeekO(0); - output_stream.WriteAll(data.data(), data.size()); - output_stream.Close(); - this->EndModal(1); }); auto* cancelButton = new wxButton(this, wxID_ANY, _("Cancel")); @@ -328,21 +286,13 @@ wxString CreateSkylanderDialog::GetFilePath() const void EmulatedUSBDeviceFrame::UpdateSkylanderEdits() { - for (auto i = 0; i < 16; i++) + for (auto i = 0; i < nsyshid::MAX_SKYLANDERS; i++) { std::string displayString; if (auto sd = m_skySlots[i]) { auto [portalSlot, skyId, skyVar] = sd.value(); - auto foundSky = nsyshid::listSkylanders.find(std::make_pair(skyId, skyVar)); - if (foundSky != nsyshid::listSkylanders.end()) - { - displayString = foundSky->second; - } - else - { - displayString = fmt::format("Unknown (Id:{} Var:{})", skyId, skyVar); - } + displayString = nsyshid::g_skyportal.FindSkylander(skyId, skyVar); } else { diff --git a/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h b/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h index 6acb7da8..8988cb8a 100644 --- a/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h +++ b/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h @@ -5,6 +5,8 @@ #include #include +#include "Cafe/OS/libs/nsyshid/Skylander.h" + class wxBoxSizer; class wxCheckBox; class wxFlexGridSizer; @@ -21,8 +23,8 @@ class EmulatedUSBDeviceFrame : public wxFrame { private: wxCheckBox* m_emulatePortal; - std::array m_skylanderSlots; - std::array>, 16> m_skySlots; + std::array m_skylanderSlots; + std::array>, nsyshid::MAX_SKYLANDERS> m_skySlots; wxPanel* AddSkylanderPage(wxNotebook* notebook); wxBoxSizer* AddSkylanderRow(uint8 row_number, wxStaticBox* box);