Skip to content

Commit aefbb91

Browse files
authored
nsyshid: Skylander emulation fixes and code cleanup (#1244)
1 parent 93b58ae commit aefbb91

File tree

4 files changed

+101
-79
lines changed

4 files changed

+101
-79
lines changed

src/Cafe/OS/libs/nsyshid/Skylander.cpp

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "Skylander.h"
22

3+
#include <random>
4+
35
#include "nsyshid.h"
46
#include "Backend.h"
57

@@ -9,8 +11,8 @@ namespace nsyshid
911
{
1012
SkylanderUSB g_skyportal;
1113

12-
const std::map<const std::pair<const uint16, const uint16>, const std::string>
13-
listSkylanders = {
14+
const std::map<const std::pair<const uint16, const uint16>, const char*>
15+
s_listSkylanders = {
1416
{{0, 0x0000}, "Whirlwind"},
1517
{{0, 0x1801}, "Series 2 Whirlwind"},
1618
{{0, 0x1C02}, "Polar Whirlwind"},
@@ -845,6 +847,49 @@ namespace nsyshid
845847
return false;
846848
}
847849

850+
bool SkylanderUSB::CreateSkylander(fs::path pathName, uint16 skyId, uint16 skyVar)
851+
{
852+
FileStream* skyFile(FileStream::createFile2(pathName));
853+
if (!skyFile)
854+
{
855+
return false;
856+
}
857+
858+
std::array<uint8, BLOCK_COUNT * BLOCK_SIZE> data{};
859+
860+
uint32 first_block = 0x690F0F0F;
861+
uint32 other_blocks = 0x69080F7F;
862+
memcpy(&data[0x36], &first_block, sizeof(first_block));
863+
for (size_t index = 1; index < 0x10; index++)
864+
{
865+
memcpy(&data[(index * 0x40) + 0x36], &other_blocks, sizeof(other_blocks));
866+
}
867+
std::random_device rd;
868+
std::mt19937 mt(rd());
869+
std::uniform_int_distribution<int> dist(0, 255);
870+
data[0] = dist(mt);
871+
data[1] = dist(mt);
872+
data[2] = dist(mt);
873+
data[3] = dist(mt);
874+
data[4] = data[0] ^ data[1] ^ data[2] ^ data[3];
875+
data[5] = 0x81;
876+
data[6] = 0x01;
877+
data[7] = 0x0F;
878+
879+
memcpy(&data[0x10], &skyId, sizeof(skyId));
880+
memcpy(&data[0x1C], &skyVar, sizeof(skyVar));
881+
882+
uint16 crc = nsyshid::g_skyportal.SkylanderCRC16(0xFFFF, data.data(), 0x1E);
883+
884+
memcpy(&data[0x1E], &crc, sizeof(crc));
885+
886+
skyFile->writeData(data.data(), data.size());
887+
888+
delete skyFile;
889+
890+
return true;
891+
}
892+
848893
void SkylanderUSB::QueryBlock(uint8 skyNum, uint8 block, uint8* replyBuf)
849894
{
850895
std::lock_guard lock(m_skyMutex);
@@ -865,7 +910,7 @@ namespace nsyshid
865910
}
866911

867912
void SkylanderUSB::WriteBlock(uint8 skyNum, uint8 block,
868-
const uint8* toWriteBuf, uint8* replyBuf)
913+
const uint8* toWriteBuf, uint8* replyBuf)
869914
{
870915
std::lock_guard lock(m_skyMutex);
871916

@@ -919,21 +964,39 @@ namespace nsyshid
919964
status |= s.status;
920965
}
921966
interruptResponse = {0x53, 0x00, 0x00, 0x00, 0x00, m_interruptCounter++,
922-
active, 0x00, 0x00, 0x00, 0x00, 0x00,
923-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
924-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
925-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
926-
0x00, 0x00};
967+
active, 0x00, 0x00, 0x00, 0x00, 0x00,
968+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
969+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
970+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
971+
0x00, 0x00};
927972
memcpy(&interruptResponse[1], &status, sizeof(status));
928973
}
929974
return interruptResponse;
930975
}
931976

977+
std::string SkylanderUSB::FindSkylander(uint16 skyId, uint16 skyVar)
978+
{
979+
for (const auto& it : GetListSkylanders())
980+
{
981+
if(it.first.first == skyId && it.first.second == skyVar)
982+
{
983+
return it.second;
984+
}
985+
}
986+
return fmt::format("Unknown ({} {})", skyId, skyVar);
987+
}
988+
989+
std::map<const std::pair<const uint16, const uint16>, const char*> SkylanderUSB::GetListSkylanders()
990+
{
991+
return s_listSkylanders;
992+
}
993+
932994
void SkylanderUSB::Skylander::Save()
933995
{
934996
if (!skyFile)
935997
return;
936998

999+
skyFile->SetPosition(0);
9371000
skyFile->writeData(data.data(), data.size());
9381001
}
9391002
} // namespace nsyshid

src/Cafe/OS/libs/nsyshid/Skylander.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#pragma once
2+
13
#include <mutex>
24

35
#include "nsyshid.h"
@@ -36,7 +38,10 @@ namespace nsyshid
3638
bool m_IsOpened;
3739
};
3840

39-
extern const std::map<const std::pair<const uint16, const uint16>, const std::string> listSkylanders;
41+
constexpr uint16 BLOCK_COUNT = 0x40;
42+
constexpr uint16 BLOCK_SIZE = 0x10;
43+
constexpr uint16 FIGURE_SIZE = BLOCK_COUNT * BLOCK_SIZE;
44+
constexpr uint8 MAX_SKYLANDERS = 16;
4045

4146
class SkylanderUSB {
4247
public:
@@ -45,7 +50,7 @@ namespace nsyshid
4550
std::unique_ptr<FileStream> skyFile;
4651
uint8 status = 0;
4752
std::queue<uint8> queuedStatus;
48-
std::array<uint8, 0x40 * 0x10> data{};
53+
std::array<uint8, BLOCK_COUNT * BLOCK_SIZE> data{};
4954
uint32 lastId = 0;
5055
void Save();
5156

@@ -74,16 +79,19 @@ namespace nsyshid
7479
std::array<uint8, 64> GetStatus();
7580
void QueryBlock(uint8 skyNum, uint8 block, uint8* replyBuf);
7681
void WriteBlock(uint8 skyNum, uint8 block, const uint8* toWriteBuf,
77-
uint8* replyBuf);
82+
uint8* replyBuf);
7883

7984
uint8 LoadSkylander(uint8* buf, std::unique_ptr<FileStream> file);
8085
bool RemoveSkylander(uint8 skyNum);
86+
bool CreateSkylander(fs::path pathName, uint16 skyId, uint16 skyVar);
8187
uint16 SkylanderCRC16(uint16 initValue, const uint8* buffer, uint32 size);
88+
static std::map<const std::pair<const uint16, const uint16>, const char*> GetListSkylanders();
89+
std::string FindSkylander(uint16 skyId, uint16 skyVar);
8290

8391
protected:
8492
std::mutex m_skyMutex;
8593
std::mutex m_queryMutex;
86-
std::array<Skylander, 16> m_skylanders;
94+
std::array<Skylander, MAX_SKYLANDERS> m_skylanders;
8795

8896
private:
8997
std::queue<std::array<uint8, 64>> m_queries;
@@ -92,7 +100,6 @@ namespace nsyshid
92100
SkylanderLEDColor m_colorRight = {};
93101
SkylanderLEDColor m_colorLeft = {};
94102
SkylanderLEDColor m_colorTrap = {};
95-
96103
};
97104
extern SkylanderUSB g_skyportal;
98105
} // namespace nsyshid

src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp

Lines changed: 14 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
#include "gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h"
22

33
#include <algorithm>
4-
#include <random>
54

65
#include "config/CemuConfig.h"
76
#include "gui/helpers/wxHelpers.h"
87
#include "gui/wxHelper.h"
98
#include "util/helpers/helpers.h"
109

1110
#include "Cafe/OS/libs/nsyshid/nsyshid.h"
12-
#include "Cafe/OS/libs/nsyshid/Skylander.h"
1311

1412
#include "Common/FileStream.h"
1513

@@ -75,7 +73,7 @@ wxPanel* EmulatedUSBDeviceFrame::AddSkylanderPage(wxNotebook* notebook)
7573
});
7674
row->Add(m_emulatePortal, 1, wxEXPAND | wxALL, 2);
7775
boxSizer->Add(row, 1, wxEXPAND | wxALL, 2);
78-
for (int i = 0; i < 16; i++)
76+
for (int i = 0; i < nsyshid::MAX_SKYLANDERS; i++)
7977
{
8078
boxSizer->Add(AddSkylanderRow(i, box), 1, wxEXPAND | wxALL, 2);
8179
}
@@ -153,7 +151,7 @@ void EmulatedUSBDeviceFrame::LoadSkylanderPath(uint8 slot, wxString path)
153151
uint16 skyVar = uint16(fileData[0x1D]) << 8 | uint16(fileData[0x1C]);
154152

155153
uint8 portalSlot = nsyshid::g_skyportal.LoadSkylander(fileData.data(),
156-
std::move(skyFile));
154+
std::move(skyFile));
157155
m_skySlots[slot] = std::tuple(portalSlot, skyId, skyVar);
158156
UpdateSkylanderEdits();
159157
}
@@ -189,11 +187,11 @@ CreateSkylanderDialog::CreateSkylanderDialog(wxWindow* parent, uint8 slot)
189187
auto* comboBox = new wxComboBox(this, wxID_ANY);
190188
comboBox->Append("---Select---", reinterpret_cast<void*>(0xFFFFFFFF));
191189
wxArrayString filterlist;
192-
for (auto it = nsyshid::listSkylanders.begin(); it != nsyshid::listSkylanders.end(); it++)
190+
for (const auto& it : nsyshid::g_skyportal.GetListSkylanders())
193191
{
194-
const uint32 variant = uint32(uint32(it->first.first) << 16) | uint32(it->first.second);
195-
comboBox->Append(it->second, reinterpret_cast<void*>(variant));
196-
filterlist.Add(it->second);
192+
const uint32 variant = uint32(uint32(it.first.first) << 16) | uint32(it.first.second);
193+
comboBox->Append(it.second, reinterpret_cast<void*>(variant));
194+
filterlist.Add(it.second);
197195
}
198196
comboBox->SetSelection(0);
199197
bool enabled = comboBox->AutoComplete(filterlist);
@@ -233,16 +231,7 @@ CreateSkylanderDialog::CreateSkylanderDialog(wxWindow* parent, uint8 slot)
233231
}
234232
uint16 skyId = longSkyId & 0xFFFF;
235233
uint16 skyVar = longSkyVar & 0xFFFF;
236-
const auto foundSky = nsyshid::listSkylanders.find(std::make_pair(skyId, skyVar));
237-
wxString predefName;
238-
if (foundSky != nsyshid::listSkylanders.end())
239-
{
240-
predefName = foundSky->second + ".sky";
241-
}
242-
else
243-
{
244-
predefName = wxString::Format(_("Unknown(%i %i).sky"), skyId, skyVar);
245-
}
234+
wxString predefName = nsyshid::g_skyportal.FindSkylander(skyId, skyVar) + ".sky";
246235
wxFileDialog
247236
saveFileDialog(this, _("Create Skylander file"), "", predefName,
248237
"SKY files (*.sky)|*.sky", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
@@ -251,46 +240,15 @@ CreateSkylanderDialog::CreateSkylanderDialog(wxWindow* parent, uint8 slot)
251240
return;
252241

253242
m_filePath = saveFileDialog.GetPath();
254-
255-
wxFileOutputStream output_stream(saveFileDialog.GetPath());
256-
if (!output_stream.IsOk())
243+
244+
if(!nsyshid::g_skyportal.CreateSkylander(_utf8ToPath(m_filePath.utf8_string()), skyId, skyVar))
257245
{
258-
wxMessageDialog saveError(this, "Error Creating Skylander File");
246+
wxMessageDialog errorMessage(this, "Failed to create file");
247+
errorMessage.ShowModal();
248+
this->EndModal(0);
259249
return;
260250
}
261251

262-
std::array<uint8, 0x40 * 0x10> data{};
263-
264-
uint32 first_block = 0x690F0F0F;
265-
uint32 other_blocks = 0x69080F7F;
266-
memcpy(&data[0x36], &first_block, sizeof(first_block));
267-
for (size_t index = 1; index < 0x10; index++)
268-
{
269-
memcpy(&data[(index * 0x40) + 0x36], &other_blocks, sizeof(other_blocks));
270-
}
271-
std::random_device rd;
272-
std::mt19937 mt(rd());
273-
std::uniform_int_distribution<int> dist(0, 255);
274-
data[0] = dist(mt);
275-
data[1] = dist(mt);
276-
data[2] = dist(mt);
277-
data[3] = dist(mt);
278-
data[4] = data[0] ^ data[1] ^ data[2] ^ data[3];
279-
data[5] = 0x81;
280-
data[6] = 0x01;
281-
data[7] = 0x0F;
282-
283-
memcpy(&data[0x10], &skyId, sizeof(skyId));
284-
memcpy(&data[0x1C], &skyVar, sizeof(skyVar));
285-
286-
uint16 crc = nsyshid::g_skyportal.SkylanderCRC16(0xFFFF, data.data(), 0x1E);
287-
288-
memcpy(&data[0x1E], &crc, sizeof(crc));
289-
290-
output_stream.SeekO(0);
291-
output_stream.WriteAll(data.data(), data.size());
292-
output_stream.Close();
293-
294252
this->EndModal(1);
295253
});
296254
auto* cancelButton = new wxButton(this, wxID_ANY, _("Cancel"));
@@ -328,21 +286,13 @@ wxString CreateSkylanderDialog::GetFilePath() const
328286

329287
void EmulatedUSBDeviceFrame::UpdateSkylanderEdits()
330288
{
331-
for (auto i = 0; i < 16; i++)
289+
for (auto i = 0; i < nsyshid::MAX_SKYLANDERS; i++)
332290
{
333291
std::string displayString;
334292
if (auto sd = m_skySlots[i])
335293
{
336294
auto [portalSlot, skyId, skyVar] = sd.value();
337-
auto foundSky = nsyshid::listSkylanders.find(std::make_pair(skyId, skyVar));
338-
if (foundSky != nsyshid::listSkylanders.end())
339-
{
340-
displayString = foundSky->second;
341-
}
342-
else
343-
{
344-
displayString = fmt::format("Unknown (Id:{} Var:{})", skyId, skyVar);
345-
}
295+
displayString = nsyshid::g_skyportal.FindSkylander(skyId, skyVar);
346296
}
347297
else
348298
{

src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <wx/dialog.h>
66
#include <wx/frame.h>
77

8+
#include "Cafe/OS/libs/nsyshid/Skylander.h"
9+
810
class wxBoxSizer;
911
class wxCheckBox;
1012
class wxFlexGridSizer;
@@ -21,8 +23,8 @@ class EmulatedUSBDeviceFrame : public wxFrame {
2123

2224
private:
2325
wxCheckBox* m_emulatePortal;
24-
std::array<wxTextCtrl*, 16> m_skylanderSlots;
25-
std::array<std::optional<std::tuple<uint8, uint16, uint16>>, 16> m_skySlots;
26+
std::array<wxTextCtrl*, nsyshid::MAX_SKYLANDERS> m_skylanderSlots;
27+
std::array<std::optional<std::tuple<uint8, uint16, uint16>>, nsyshid::MAX_SKYLANDERS> m_skySlots;
2628

2729
wxPanel* AddSkylanderPage(wxNotebook* notebook);
2830
wxBoxSizer* AddSkylanderRow(uint8 row_number, wxStaticBox* box);

0 commit comments

Comments
 (0)