nsyshid: Add support for emulated Dimensions Toypad (#1371)

This commit is contained in:
Joshua de Reeper 2024-11-11 07:58:01 +00:00 committed by GitHub
parent 2e829479d9
commit ca2e0a7c31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 1690 additions and 52 deletions

View File

@ -465,6 +465,8 @@ add_library(CemuCafe
OS/libs/nsyshid/BackendLibusb.h
OS/libs/nsyshid/BackendWindowsHID.cpp
OS/libs/nsyshid/BackendWindowsHID.h
OS/libs/nsyshid/Dimensions.cpp
OS/libs/nsyshid/Dimensions.h
OS/libs/nsyshid/Infinity.cpp
OS/libs/nsyshid/Infinity.h
OS/libs/nsyshid/Skylander.cpp

View File

@ -1,4 +1,6 @@
#include "BackendEmulated.h"
#include "Dimensions.h"
#include "Infinity.h"
#include "Skylander.h"
#include "config/CemuConfig.h"
@ -33,5 +35,12 @@ namespace nsyshid::backend::emulated
auto device = std::make_shared<InfinityBaseDevice>();
AttachDevice(device);
}
if (GetConfig().emulated_usb_devices.emulate_dimensions_toypad && !FindDeviceById(0x0E6F, 0x0241))
{
cemuLog_logDebug(LogType::Force, "Attaching Emulated Toypad");
// Add Dimensions Toypad
auto device = std::make_shared<DimensionsToypadDevice>();
AttachDevice(device);
}
}
} // namespace nsyshid::backend::emulated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,108 @@
#include <mutex>
#include "nsyshid.h"
#include "Backend.h"
#include "Common/FileStream.h"
namespace nsyshid
{
class DimensionsToypadDevice final : public Device
{
public:
DimensionsToypadDevice();
~DimensionsToypadDevice() = default;
bool Open() override;
void Close() override;
bool IsOpened() override;
ReadResult Read(ReadMessage* message) override;
WriteResult Write(WriteMessage* message) override;
bool GetDescriptor(uint8 descType,
uint8 descIndex,
uint8 lang,
uint8* output,
uint32 outputMaxLength) override;
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
bool SetReport(ReportMessage* message) override;
private:
bool m_IsOpened;
};
class DimensionsUSB
{
public:
struct DimensionsMini final
{
std::unique_ptr<FileStream> dimFile;
std::array<uint8, 0x2D * 0x04> data{};
uint8 index = 255;
uint8 pad = 255;
uint32 id = 0;
void Save();
};
void SendCommand(std::span<const uint8, 32> buf);
std::array<uint8, 32> GetStatus();
void GenerateRandomNumber(std::span<const uint8, 8> buf, uint8 sequence,
std::array<uint8, 32>& replyBuf);
void InitializeRNG(uint32 seed);
void GetChallengeResponse(std::span<const uint8, 8> buf, uint8 sequence,
std::array<uint8, 32>& replyBuf);
void QueryBlock(uint8 index, uint8 page, std::array<uint8, 32>& replyBuf,
uint8 sequence);
void WriteBlock(uint8 index, uint8 page, std::span<const uint8, 4> toWriteBuf, std::array<uint8, 32>& replyBuf,
uint8 sequence);
void GetModel(std::span<const uint8, 8> buf, uint8 sequence,
std::array<uint8, 32>& replyBuf);
bool RemoveFigure(uint8 pad, uint8 index, bool fullRemove);
bool TempRemove(uint8 index);
bool CancelRemove(uint8 index);
uint32 LoadFigure(const std::array<uint8, 0x2D * 0x04>& buf, std::unique_ptr<FileStream> file, uint8 pad, uint8 index);
bool CreateFigure(fs::path pathName, uint32 id);
bool MoveFigure(uint8 pad, uint8 index, uint8 oldPad, uint8 oldIndex);
static std::map<const uint32, const char*> GetListMinifigs();
static std::map<const uint32, const char*> GetListTokens();
std::string FindFigure(uint32 figNum);
protected:
std::mutex m_dimensionsMutex;
std::array<DimensionsMini, 7> m_figures{};
private:
void RandomUID(std::array<uint8, 0x2D * 0x04>& uidBuffer);
uint8 GenerateChecksum(const std::array<uint8, 32>& data,
int numOfBytes) const;
std::array<uint8, 8> Decrypt(std::span<const uint8, 8> buf, std::optional<std::array<uint8, 16>> key);
std::array<uint8, 8> Encrypt(std::span<const uint8, 8> buf, std::optional<std::array<uint8, 16>> key);
std::array<uint8, 16> GenerateFigureKey(const std::array<uint8, 0x2D * 0x04>& uid);
std::array<uint8, 4> PWDGenerate(const std::array<uint8, 0x2D * 0x04>& uid);
std::array<uint8, 4> DimensionsRandomize(const std::vector<uint8> key, uint8 count);
uint32 GetFigureId(const std::array<uint8, 0x2D * 0x04>& buf);
uint32 Scramble(const std::array<uint8, 7>& uid, uint8 count);
uint32 GetNext();
DimensionsMini& GetFigureByIndex(uint8 index);
uint32 m_randomA;
uint32 m_randomB;
uint32 m_randomC;
uint32 m_randomD;
bool m_isAwake = false;
std::queue<std::array<uint8, 32>> m_figureAddedRemovedResponses;
std::queue<std::array<uint8, 32>> m_queries;
};
extern DimensionsUSB g_dimensionstoypad;
} // namespace nsyshid

View File

@ -346,6 +346,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
auto usbdevices = parser.get("EmulatedUsbDevices");
emulated_usb_devices.emulate_skylander_portal = usbdevices.get("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal);
emulated_usb_devices.emulate_infinity_base = usbdevices.get("EmulateInfinityBase", emulated_usb_devices.emulate_infinity_base);
emulated_usb_devices.emulate_dimensions_toypad = usbdevices.get("EmulateDimensionsToypad", emulated_usb_devices.emulate_dimensions_toypad);
}
void CemuConfig::Save(XMLConfigParser& parser)
@ -545,6 +546,7 @@ void CemuConfig::Save(XMLConfigParser& parser)
auto usbdevices = config.set("EmulatedUsbDevices");
usbdevices.set("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal.GetValue());
usbdevices.set("EmulateInfinityBase", emulated_usb_devices.emulate_infinity_base.GetValue());
usbdevices.set("EmulateDimensionsToypad", emulated_usb_devices.emulate_dimensions_toypad.GetValue());
}
GameEntry* CemuConfig::GetGameEntryByTitleId(uint64 titleId)

View File

@ -521,6 +521,7 @@ struct CemuConfig
{
ConfigValue<bool> emulate_skylander_portal{false};
ConfigValue<bool> emulate_infinity_base{false};
ConfigValue<bool> emulate_dimensions_toypad{false};
}emulated_usb_devices{};
private:

View File

@ -1,4 +1,4 @@
#include "gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h"
#include "EmulatedUSBDeviceFrame.h"
#include <algorithm>
@ -8,14 +8,17 @@
#include "util/helpers/helpers.h"
#include "Cafe/OS/libs/nsyshid/nsyshid.h"
#include "Cafe/OS/libs/nsyshid/Dimensions.h"
#include "Common/FileStream.h"
#include <wx/arrstr.h>
#include <wx/button.h>
#include <wx/combobox.h>
#include <wx/checkbox.h>
#include <wx/combobox.h>
#include <wx/filedlg.h>
#include <wx/log.h>
#include <wx/msgdlg.h>
#include <wx/notebook.h>
#include <wx/panel.h>
@ -29,7 +32,6 @@
#include <wx/wfstream.h>
#include "resource/embedded/resources.h"
#include "EmulatedUSBDeviceFrame.h"
EmulatedUSBDeviceFrame::EmulatedUSBDeviceFrame(wxWindow* parent)
: wxFrame(parent, wxID_ANY, _("Emulated USB Devices"), wxDefaultPosition,
@ -44,6 +46,7 @@ EmulatedUSBDeviceFrame::EmulatedUSBDeviceFrame(wxWindow* parent)
notebook->AddPage(AddSkylanderPage(notebook), _("Skylanders Portal"));
notebook->AddPage(AddInfinityPage(notebook), _("Infinity Base"));
notebook->AddPage(AddDimensionsPage(notebook), _("Dimensions Toypad"));
sizer->Add(notebook, 1, wxEXPAND | wxALL, 2);
@ -120,8 +123,52 @@ wxPanel* EmulatedUSBDeviceFrame::AddInfinityPage(wxNotebook* notebook)
return panel;
}
wxBoxSizer* EmulatedUSBDeviceFrame::AddSkylanderRow(uint8 rowNumber,
wxStaticBox* box)
wxPanel* EmulatedUSBDeviceFrame::AddDimensionsPage(wxNotebook* notebook)
{
auto* panel = new wxPanel(notebook);
auto* panel_sizer = new wxBoxSizer(wxVERTICAL);
auto* box = new wxStaticBox(panel, wxID_ANY, _("Dimensions Manager"));
auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
auto* row = new wxBoxSizer(wxHORIZONTAL);
m_emulateToypad =
new wxCheckBox(box, wxID_ANY, _("Emulate Dimensions Toypad"));
m_emulateToypad->SetValue(
GetConfig().emulated_usb_devices.emulate_dimensions_toypad);
m_emulateToypad->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) {
GetConfig().emulated_usb_devices.emulate_dimensions_toypad =
m_emulateToypad->IsChecked();
g_config.Save();
});
row->Add(m_emulateToypad, 1, wxEXPAND | wxALL, 2);
box_sizer->Add(row, 1, wxEXPAND | wxALL, 2);
auto* top_row = new wxBoxSizer(wxHORIZONTAL);
auto* bottom_row = new wxBoxSizer(wxHORIZONTAL);
auto* dummy = new wxStaticText(box, wxID_ANY, "");
top_row->Add(AddDimensionPanel(2, 0, box), 1, wxEXPAND | wxALL, 2);
top_row->Add(dummy, 1, wxEXPAND | wxLEFT | wxRIGHT, 2);
top_row->Add(AddDimensionPanel(1, 1, box), 1, wxEXPAND | wxALL, 2);
top_row->Add(dummy, 1, wxEXPAND | wxLEFT | wxRIGHT, 2);
top_row->Add(AddDimensionPanel(3, 2, box), 1, wxEXPAND | wxALL, 2);
bottom_row->Add(AddDimensionPanel(2, 3, box), 1, wxEXPAND | wxALL, 2);
bottom_row->Add(AddDimensionPanel(2, 4, box), 1, wxEXPAND | wxALL, 2);
bottom_row->Add(dummy, 1, wxEXPAND | wxLEFT | wxRIGHT, 0);
bottom_row->Add(AddDimensionPanel(3, 5, box), 1, wxEXPAND | wxALL, 2);
bottom_row->Add(AddDimensionPanel(3, 6, box), 1, wxEXPAND | wxALL, 2);
box_sizer->Add(top_row, 1, wxEXPAND | wxALL, 2);
box_sizer->Add(bottom_row, 1, wxEXPAND | wxALL, 2);
panel_sizer->Add(box_sizer, 1, wxEXPAND | wxALL, 2);
panel->SetSizerAndFit(panel_sizer);
return panel;
}
wxBoxSizer* EmulatedUSBDeviceFrame::AddSkylanderRow(uint8 rowNumber, wxStaticBox* box)
{
auto* row = new wxBoxSizer(wxHORIZONTAL);
@ -184,6 +231,44 @@ wxBoxSizer* EmulatedUSBDeviceFrame::AddInfinityRow(wxString name, uint8 rowNumbe
return row;
}
wxBoxSizer* EmulatedUSBDeviceFrame::AddDimensionPanel(uint8 pad, uint8 index, wxStaticBox* box)
{
auto* panel = new wxBoxSizer(wxVERTICAL);
auto* combo_row = new wxBoxSizer(wxHORIZONTAL);
m_dimensionSlots[index] = new wxTextCtrl(box, wxID_ANY, _("None"), wxDefaultPosition, wxDefaultSize,
wxTE_READONLY);
combo_row->Add(m_dimensionSlots[index], 1, wxEXPAND | wxALL, 2);
auto* move_button = new wxButton(box, wxID_ANY, _("Move"));
move_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) {
MoveMinifig(pad, index);
});
combo_row->Add(move_button, 1, wxEXPAND | wxALL, 2);
auto* button_row = new wxBoxSizer(wxHORIZONTAL);
auto* load_button = new wxButton(box, wxID_ANY, _("Load"));
load_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) {
LoadMinifig(pad, index);
});
auto* clear_button = new wxButton(box, wxID_ANY, _("Clear"));
clear_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) {
ClearMinifig(pad, index);
});
auto* create_button = new wxButton(box, wxID_ANY, _("Create"));
create_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) {
CreateMinifig(pad, index);
});
button_row->Add(clear_button, 1, wxEXPAND | wxALL, 2);
button_row->Add(create_button, 1, wxEXPAND | wxALL, 2);
button_row->Add(load_button, 1, wxEXPAND | wxALL, 2);
panel->Add(combo_row, 1, wxEXPAND | wxALL, 2);
panel->Add(button_row, 1, wxEXPAND | wxALL, 2);
return panel;
}
void EmulatedUSBDeviceFrame::LoadSkylander(uint8 slot)
{
wxFileDialog openFileDialog(this, _("Open Skylander dump"), "", "",
@ -308,7 +393,7 @@ CreateSkylanderDialog::CreateSkylanderDialog(wxWindow* parent, uint8 slot)
m_filePath = saveFileDialog.GetPath();
if(!nsyshid::g_skyportal.CreateSkylander(_utf8ToPath(m_filePath.utf8_string()), skyId, skyVar))
if (!nsyshid::g_skyportal.CreateSkylander(_utf8ToPath(m_filePath.utf8_string()), skyId, skyVar))
{
wxMessageDialog errorMessage(this, "Failed to create file");
errorMessage.ShowModal();
@ -351,6 +436,80 @@ wxString CreateSkylanderDialog::GetFilePath() const
return m_filePath;
}
void EmulatedUSBDeviceFrame::UpdateSkylanderEdits()
{
for (auto i = 0; i < nsyshid::MAX_SKYLANDERS; i++)
{
std::string displayString;
if (auto sd = m_skySlots[i])
{
auto [portalSlot, skyId, skyVar] = sd.value();
displayString = nsyshid::g_skyportal.FindSkylander(skyId, skyVar);
}
else
{
displayString = "None";
}
m_skylanderSlots[i]->ChangeValue(displayString);
}
}
void EmulatedUSBDeviceFrame::LoadFigure(uint8 slot)
{
wxFileDialog openFileDialog(this, _("Open Infinity Figure dump"), "", "",
"BIN files (*.bin)|*.bin",
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() != wxID_OK || openFileDialog.GetPath().empty())
{
wxMessageDialog errorMessage(this, "File Okay Error");
errorMessage.ShowModal();
return;
}
LoadFigurePath(slot, openFileDialog.GetPath());
}
void EmulatedUSBDeviceFrame::LoadFigurePath(uint8 slot, wxString path)
{
std::unique_ptr<FileStream> infFile(FileStream::openFile2(_utf8ToPath(path.utf8_string()), true));
if (!infFile)
{
wxMessageDialog errorMessage(this, "File Open Error");
errorMessage.ShowModal();
return;
}
std::array<uint8, nsyshid::INF_FIGURE_SIZE> fileData;
if (infFile->readData(fileData.data(), fileData.size()) != fileData.size())
{
wxMessageDialog open_error(this, "Failed to read file! File was too small");
open_error.ShowModal();
return;
}
ClearFigure(slot);
uint32 number = nsyshid::g_infinitybase.LoadFigure(fileData, std::move(infFile), slot);
m_infinitySlots[slot]->ChangeValue(nsyshid::g_infinitybase.FindFigure(number).second);
}
void EmulatedUSBDeviceFrame::CreateFigure(uint8 slot)
{
cemuLog_log(LogType::Force, "Create Figure: {}", slot);
CreateInfinityFigureDialog create_dlg(this, slot);
create_dlg.ShowModal();
if (create_dlg.GetReturnCode() == 1)
{
LoadFigurePath(slot, create_dlg.GetFilePath());
}
}
void EmulatedUSBDeviceFrame::ClearFigure(uint8 slot)
{
m_infinitySlots[slot]->ChangeValue("None");
nsyshid::g_infinitybase.RemoveFigure(slot);
}
CreateInfinityFigureDialog::CreateInfinityFigureDialog(wxWindow* parent, uint8 slot)
: wxDialog(parent, wxID_ANY, _("Infinity Figure Creator"), wxDefaultPosition, wxSize(500, 150))
{
@ -447,76 +606,231 @@ wxString CreateInfinityFigureDialog::GetFilePath() const
return m_filePath;
}
void EmulatedUSBDeviceFrame::LoadFigure(uint8 slot)
void EmulatedUSBDeviceFrame::LoadMinifig(uint8 pad, uint8 index)
{
wxFileDialog openFileDialog(this, _("Open Infinity Figure dump"), "", "",
"BIN files (*.bin)|*.bin",
wxFileDialog openFileDialog(this, _("Load Dimensions Figure"), "", "",
"Dimensions files (*.bin)|*.bin",
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() != wxID_OK || openFileDialog.GetPath().empty())
return;
LoadMinifigPath(openFileDialog.GetPath(), pad, index);
}
void EmulatedUSBDeviceFrame::LoadMinifigPath(wxString path_name, uint8 pad, uint8 index)
{
std::unique_ptr<FileStream> dim_file(FileStream::openFile2(_utf8ToPath(path_name.utf8_string()), true));
if (!dim_file)
{
wxMessageDialog errorMessage(this, "File Okay Error");
wxMessageDialog errorMessage(this, "Failed to open minifig file");
errorMessage.ShowModal();
return;
}
LoadFigurePath(slot, openFileDialog.GetPath());
}
std::array<uint8, 0x2D * 0x04> file_data;
void EmulatedUSBDeviceFrame::LoadFigurePath(uint8 slot, wxString path)
{
std::unique_ptr<FileStream> infFile(FileStream::openFile2(_utf8ToPath(path.utf8_string()), true));
if (!infFile)
if (dim_file->readData(file_data.data(), file_data.size()) != file_data.size())
{
wxMessageDialog errorMessage(this, "File Open Error");
wxMessageDialog errorMessage(this, "Failed to read minifig file data");
errorMessage.ShowModal();
return;
}
std::array<uint8, nsyshid::INF_FIGURE_SIZE> fileData;
if (infFile->readData(fileData.data(), fileData.size()) != fileData.size())
{
wxMessageDialog open_error(this, "Failed to read file! File was too small");
open_error.ShowModal();
return;
}
ClearFigure(slot);
ClearMinifig(pad, index);
uint32 number = nsyshid::g_infinitybase.LoadFigure(fileData, std::move(infFile), slot);
m_infinitySlots[slot]->ChangeValue(nsyshid::g_infinitybase.FindFigure(number).second);
uint32 id = nsyshid::g_dimensionstoypad.LoadFigure(file_data, std::move(dim_file), pad, index);
m_dimensionSlots[index]->ChangeValue(nsyshid::g_dimensionstoypad.FindFigure(id));
m_dimSlots[index] = id;
}
void EmulatedUSBDeviceFrame::CreateFigure(uint8 slot)
void EmulatedUSBDeviceFrame::ClearMinifig(uint8 pad, uint8 index)
{
cemuLog_log(LogType::Force, "Create Figure: {}", slot);
CreateInfinityFigureDialog create_dlg(this, slot);
nsyshid::g_dimensionstoypad.RemoveFigure(pad, index, true);
m_dimensionSlots[index]->ChangeValue("None");
m_dimSlots[index] = std::nullopt;
}
void EmulatedUSBDeviceFrame::CreateMinifig(uint8 pad, uint8 index)
{
CreateDimensionFigureDialog create_dlg(this);
create_dlg.ShowModal();
if (create_dlg.GetReturnCode() == 1)
{
LoadFigurePath(slot, create_dlg.GetFilePath());
LoadMinifigPath(create_dlg.GetFilePath(), pad, index);
}
}
void EmulatedUSBDeviceFrame::ClearFigure(uint8 slot)
void EmulatedUSBDeviceFrame::MoveMinifig(uint8 pad, uint8 index)
{
m_infinitySlots[slot]->ChangeValue("None");
nsyshid::g_infinitybase.RemoveFigure(slot);
}
if (!m_dimSlots[index])
return;
void EmulatedUSBDeviceFrame::UpdateSkylanderEdits()
{
for (auto i = 0; i < nsyshid::MAX_SKYLANDERS; i++)
MoveDimensionFigureDialog move_dlg(this, index);
nsyshid::g_dimensionstoypad.TempRemove(index);
move_dlg.ShowModal();
if (move_dlg.GetReturnCode() == 1)
{
std::string displayString;
if (auto sd = m_skySlots[i])
nsyshid::g_dimensionstoypad.MoveFigure(move_dlg.GetNewPad(), move_dlg.GetNewIndex(), pad, index);
if (index != move_dlg.GetNewIndex())
{
auto [portalSlot, skyId, skyVar] = sd.value();
displayString = nsyshid::g_skyportal.FindSkylander(skyId, skyVar);
m_dimSlots[move_dlg.GetNewIndex()] = m_dimSlots[index];
m_dimensionSlots[move_dlg.GetNewIndex()]->ChangeValue(m_dimensionSlots[index]->GetValue());
m_dimSlots[index] = std::nullopt;
m_dimensionSlots[index]->ChangeValue("None");
}
}
else
{
displayString = "None";
}
m_skylanderSlots[i]->ChangeValue(displayString);
nsyshid::g_dimensionstoypad.CancelRemove(index);
}
}
CreateDimensionFigureDialog::CreateDimensionFigureDialog(wxWindow* parent)
: wxDialog(parent, wxID_ANY, _("Dimensions Figure Creator"), wxDefaultPosition, wxSize(500, 200))
{
auto* sizer = new wxBoxSizer(wxVERTICAL);
auto* comboRow = new wxBoxSizer(wxHORIZONTAL);
auto* comboBox = new wxComboBox(this, wxID_ANY);
comboBox->Append("---Select---", reinterpret_cast<void*>(0xFFFFFFFF));
wxArrayString filterlist;
for (const auto& it : nsyshid::g_dimensionstoypad.GetListMinifigs())
{
const uint32 figure = it.first;
comboBox->Append(it.second, reinterpret_cast<void*>(figure));
filterlist.Add(it.second);
}
comboBox->SetSelection(0);
bool enabled = comboBox->AutoComplete(filterlist);
comboRow->Add(comboBox, 1, wxEXPAND | wxALL, 2);
auto* figNumRow = new wxBoxSizer(wxHORIZONTAL);
wxIntegerValidator<uint32> validator;
auto* labelFigNum = new wxStaticText(this, wxID_ANY, "Figure Number:");
auto* editFigNum = new wxTextCtrl(this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0, validator);
figNumRow->Add(labelFigNum, 1, wxALL, 5);
figNumRow->Add(editFigNum, 1, wxALL, 5);
auto* buttonRow = new wxBoxSizer(wxHORIZONTAL);
auto* createButton = new wxButton(this, wxID_ANY, _("Create"));
createButton->Bind(wxEVT_BUTTON, [editFigNum, this](wxCommandEvent&) {
long longFigNum;
if (!editFigNum->GetValue().ToLong(&longFigNum) || longFigNum > 0xFFFF)
{
wxMessageDialog idError(this, "Error Converting Figure Number!", "Number Entered is Invalid");
idError.ShowModal();
this->EndModal(0);
}
uint16 figNum = longFigNum & 0xFFFF;
auto figure = nsyshid::g_dimensionstoypad.FindFigure(figNum);
wxString predefName = figure + ".bin";
wxFileDialog
saveFileDialog(this, _("Create Dimensions Figure file"), "", predefName,
"BIN files (*.bin)|*.bin", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (saveFileDialog.ShowModal() == wxID_CANCEL)
this->EndModal(0);
m_filePath = saveFileDialog.GetPath();
nsyshid::g_dimensionstoypad.CreateFigure(_utf8ToPath(m_filePath.utf8_string()), figNum);
this->EndModal(1);
});
auto* cancelButton = new wxButton(this, wxID_ANY, _("Cancel"));
cancelButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
this->EndModal(0);
});
comboBox->Bind(wxEVT_COMBOBOX, [comboBox, editFigNum, this](wxCommandEvent&) {
const uint64 fig_info = reinterpret_cast<uint64>(comboBox->GetClientData(comboBox->GetSelection()));
if (fig_info != 0xFFFF)
{
const uint16 figNum = fig_info & 0xFFFF;
editFigNum->SetValue(wxString::Format(wxT("%i"), figNum));
}
});
buttonRow->Add(createButton, 1, wxALL, 5);
buttonRow->Add(cancelButton, 1, wxALL, 5);
sizer->Add(comboRow, 1, wxEXPAND | wxALL, 2);
sizer->Add(figNumRow, 1, wxEXPAND | wxALL, 2);
sizer->Add(buttonRow, 1, wxEXPAND | wxALL, 2);
this->SetSizer(sizer);
this->Centre(wxBOTH);
}
wxString CreateDimensionFigureDialog::GetFilePath() const
{
return m_filePath;
}
MoveDimensionFigureDialog::MoveDimensionFigureDialog(EmulatedUSBDeviceFrame* parent, uint8 currentIndex)
: wxDialog(parent, wxID_ANY, _("Dimensions Figure Mover"), wxDefaultPosition, wxSize(700, 300))
{
auto* sizer = new wxGridSizer(2, 5, 10, 10);
std::array<std::optional<uint32>, 7> ids = parent->GetCurrentMinifigs();
sizer->Add(AddMinifigSlot(2, 0, currentIndex, ids[0]), 1, wxALL, 5);
sizer->Add(new wxStaticText(this, wxID_ANY, ""), 1, wxALL, 5);
sizer->Add(AddMinifigSlot(1, 1, currentIndex, ids[1]), 1, wxALL, 5);
sizer->Add(new wxStaticText(this, wxID_ANY, ""), 1, wxALL, 5);
sizer->Add(AddMinifigSlot(3, 2, currentIndex, ids[2]), 1, wxALL, 5);
sizer->Add(AddMinifigSlot(2, 3, currentIndex, ids[3]), 1, wxALL, 5);
sizer->Add(AddMinifigSlot(2, 4, currentIndex, ids[4]), 1, wxALL, 5);
sizer->Add(new wxStaticText(this, wxID_ANY, ""), 1, wxALL, 5);
sizer->Add(AddMinifigSlot(3, 5, currentIndex, ids[5]), 1, wxALL, 5);
sizer->Add(AddMinifigSlot(3, 6, currentIndex, ids[6]), 1, wxALL, 5);
this->SetSizer(sizer);
this->Centre(wxBOTH);
}
wxBoxSizer* MoveDimensionFigureDialog::AddMinifigSlot(uint8 pad, uint8 index, uint8 currentIndex, std::optional<uint32> currentId)
{
auto* panel = new wxBoxSizer(wxVERTICAL);
auto* label = new wxStaticText(this, wxID_ANY, "None");
if (currentId)
label->SetLabel(nsyshid::g_dimensionstoypad.FindFigure(currentId.value()));
auto* moveButton = new wxButton(this, wxID_ANY, _("Move Here"));
if (index == currentIndex)
moveButton->SetLabelText("Pick up and Place");
moveButton->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) {
m_newPad = pad;
m_newIndex = index;
this->EndModal(1);
});
panel->Add(label, 1, wxALL, 5);
panel->Add(moveButton, 1, wxALL, 5);
return panel;
}
uint8 MoveDimensionFigureDialog::GetNewPad() const
{
return m_newPad;
}
uint8 MoveDimensionFigureDialog::GetNewIndex() const
{
return m_newIndex;
}
std::array<std::optional<uint32>, 7> EmulatedUSBDeviceFrame::GetCurrentMinifigs()
{
return m_dimSlots;
}

View File

@ -17,33 +17,47 @@ class wxStaticBox;
class wxString;
class wxTextCtrl;
class EmulatedUSBDeviceFrame : public wxFrame {
class EmulatedUSBDeviceFrame : public wxFrame
{
public:
EmulatedUSBDeviceFrame(wxWindow* parent);
~EmulatedUSBDeviceFrame();
std::array<std::optional<uint32>, 7> GetCurrentMinifigs();
private:
wxCheckBox* m_emulatePortal;
wxCheckBox* m_emulateBase;
wxCheckBox* m_emulateToypad;
std::array<wxTextCtrl*, nsyshid::MAX_SKYLANDERS> m_skylanderSlots;
std::array<wxTextCtrl*, nsyshid::MAX_FIGURES> m_infinitySlots;
std::array<wxTextCtrl*, 7> m_dimensionSlots;
std::array<std::optional<std::tuple<uint8, uint16, uint16>>, nsyshid::MAX_SKYLANDERS> m_skySlots;
std::array<std::optional<uint32>, 7> m_dimSlots;
wxPanel* AddSkylanderPage(wxNotebook* notebook);
wxPanel* AddInfinityPage(wxNotebook* notebook);
wxPanel* AddDimensionsPage(wxNotebook* notebook);
wxBoxSizer* AddSkylanderRow(uint8 row_number, wxStaticBox* box);
wxBoxSizer* AddInfinityRow(wxString name, uint8 row_number, wxStaticBox* box);
wxBoxSizer* AddDimensionPanel(uint8 pad, uint8 index, wxStaticBox* box);
void LoadSkylander(uint8 slot);
void LoadSkylanderPath(uint8 slot, wxString path);
void CreateSkylander(uint8 slot);
void ClearSkylander(uint8 slot);
void UpdateSkylanderEdits();
void LoadFigure(uint8 slot);
void LoadFigurePath(uint8 slot, wxString path);
void CreateFigure(uint8 slot);
void ClearFigure(uint8 slot);
void UpdateSkylanderEdits();
void LoadMinifig(uint8 pad, uint8 index);
void LoadMinifigPath(wxString path_name, uint8 pad, uint8 index);
void CreateMinifig(uint8 pad, uint8 index);
void ClearMinifig(uint8 pad, uint8 index);
void MoveMinifig(uint8 pad, uint8 index);
};
class CreateSkylanderDialog : public wxDialog {
class CreateSkylanderDialog : public wxDialog
{
public:
explicit CreateSkylanderDialog(wxWindow* parent, uint8 slot);
wxString GetFilePath() const;
@ -52,7 +66,8 @@ class CreateSkylanderDialog : public wxDialog {
wxString m_filePath;
};
class CreateInfinityFigureDialog : public wxDialog {
class CreateInfinityFigureDialog : public wxDialog
{
public:
explicit CreateInfinityFigureDialog(wxWindow* parent, uint8 slot);
wxString GetFilePath() const;
@ -60,3 +75,28 @@ class CreateInfinityFigureDialog : public wxDialog {
protected:
wxString m_filePath;
};
class CreateDimensionFigureDialog : public wxDialog
{
public:
explicit CreateDimensionFigureDialog(wxWindow* parent);
wxString GetFilePath() const;
protected:
wxString m_filePath;
};
class MoveDimensionFigureDialog : public wxDialog
{
public:
explicit MoveDimensionFigureDialog(EmulatedUSBDeviceFrame* parent, uint8 currentIndex);
uint8 GetNewPad() const;
uint8 GetNewIndex() const;
protected:
uint8 m_newIndex = 0;
uint8 m_newPad = 0;
private:
wxBoxSizer* AddMinifigSlot(uint8 pad, uint8 index, uint8 oldIndex, std::optional<uint32> currentId);
};