diff --git a/CMakeLists.txt b/CMakeLists.txt index 9748b8f8..ac6cdfb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION endif() if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE RelWithDebInfo) + set(CMAKE_BUILD_TYPE Release) endif() project(Alber) diff --git a/include/cheats.hpp b/include/cheats.hpp index 2be25827..ca8abe1d 100644 --- a/include/cheats.hpp +++ b/include/cheats.hpp @@ -9,6 +9,8 @@ // Forward-declare this since it's just passed and we don't want to include memory.hpp and increase compile time class Memory; +constexpr u32 badCheatHandle = 0xFFFFFFFF; + class Cheats { public: enum class CheatType { @@ -24,6 +26,7 @@ class Cheats { Cheats(Memory& mem, HIDService& hid); u32 addCheat(const Cheat& cheat); + u32 addCheat(const u8* data, size_t size); void removeCheat(u32 id); void enableCheat(u32 id); void disableCheat(u32 id); diff --git a/include/panda_qt/cheats_window.hpp b/include/panda_qt/cheats_window.hpp index 2160a1f6..c82b2bd8 100644 --- a/include/panda_qt/cheats_window.hpp +++ b/include/panda_qt/cheats_window.hpp @@ -1,26 +1,26 @@ #pragma once -#include -#include #include #include +#include +#include + #include "emulator.hpp" class QListWidget; -class CheatsWindow final : public QWidget -{ - Q_OBJECT +class CheatsWindow final : public QWidget { + Q_OBJECT -public: - CheatsWindow(Emulator* emu, const std::filesystem::path& path, QWidget* parent = nullptr); - ~CheatsWindow() = default; + public: + CheatsWindow(Emulator* emu, const std::filesystem::path& path, QWidget* parent = nullptr); + ~CheatsWindow() = default; -private: - void addEntry(); - void removeClicked(); + private: + void addEntry(); + void removeClicked(); - QListWidget* cheatList; - std::filesystem::path cheatPath; - Emulator* emu; + QListWidget* cheatList; + std::filesystem::path cheatPath; + Emulator* emu; }; diff --git a/include/panda_qt/main_window.hpp b/include/panda_qt/main_window.hpp index 39a8b35f..d5eccc93 100644 --- a/include/panda_qt/main_window.hpp +++ b/include/panda_qt/main_window.hpp @@ -17,12 +17,19 @@ #include "panda_qt/text_editor.hpp" #include "services/hid.hpp" +struct CheatMessage +{ + u32 handle; + std::vector cheat; + std::function callback; +}; + class MainWindow : public QMainWindow { Q_OBJECT private: // Types of messages we might send from the GUI thread to the emulator thread - enum class MessageType { LoadROM, Reset, Pause, Resume, TogglePause, DumpRomFS, PressKey, ReleaseKey, LoadLuaScript }; + enum class MessageType { LoadROM, Reset, Pause, Resume, TogglePause, DumpRomFS, PressKey, ReleaseKey, LoadLuaScript, EditCheat }; // Tagged union representing our message queue messages struct EmulatorMessage { @@ -40,6 +47,10 @@ class MainWindow : public QMainWindow { struct { std::string* str; } string; + + struct { + CheatMessage* c; + } cheat; }; }; @@ -59,8 +70,6 @@ class MainWindow : public QMainWindow { TextEditorWindow* luaEditor; QMenuBar* menuBar = nullptr; - QAction* cheatsEditorAction = nullptr; - void swapEmuBuffer(); void emuThreadMainLoop(); void selectLuaFile(); @@ -83,4 +92,5 @@ class MainWindow : public QMainWindow { void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; void loadLuaScript(const std::string& code); + void editCheat(u32 handle, const std::vector& cheat, const std::function& callback); }; \ No newline at end of file diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index 83e7cdc4..7b8b71c2 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -1,4 +1,5 @@ #include "cheats.hpp" +#include "swap.hpp" Cheats::Cheats(Memory& mem, HIDService& hid) : ar(mem, hid) { reset(); } @@ -23,6 +24,27 @@ u32 Cheats::addCheat(const Cheat& cheat) { return cheats.size() - 1; } +u32 Cheats::addCheat(const u8* data, size_t size) { + if ((size % 8) != 0) { + return badCheatHandle; + } + + Cheats::Cheat cheat; + cheat.enabled = true; + cheat.type = Cheats::CheatType::ActionReplay; + + for (size_t i = 0; i < size; i += 8) { + auto read32 = [](const u8* ptr) { return (u32(ptr[3]) << 24) | (u32(ptr[2]) << 16) | (u32(ptr[1]) << 8) | u32(ptr[0]); }; + + // Data is passed to us in big endian so we bswap + u32 firstWord = Common::swap32(read32(data + i)); + u32 secondWord = Common::swap32(read32(data + i + 4)); + cheat.instructions.insert(cheat.instructions.end(), {firstWord, secondWord}); + } + + return addCheat(cheat); +} + void Cheats::removeCheat(u32 id) { if (id >= cheats.size()) { return; diff --git a/src/panda_qt/cheats_window.cpp b/src/panda_qt/cheats_window.cpp index 909ba0cb..c6628125 100644 --- a/src/panda_qt/cheats_window.cpp +++ b/src/panda_qt/cheats_window.cpp @@ -1,313 +1,248 @@ #include "panda_qt/cheats_window.hpp" -#include -#include -#include -#include -#include + #include #include -#include -#include #include +#include +#include +#include +#include +#include +#include + #include "cheats.hpp" #include "emulator.hpp" +#include "panda_qt/main_window.hpp" -using CheatHandle = u32; +MainWindow* mainWindow = nullptr; -CheatHandle BAD_CHEAT = 0xFFFFFFFF; - -struct CheatMetadata -{ - CheatHandle handle = BAD_CHEAT; - std::string name = "New cheat"; - std::string code; - bool enabled = true; +struct CheatMetadata { + u32 handle = badCheatHandle; + std::string name = "New cheat"; + std::string code; + bool enabled = true; }; -u32 addCheat(Emulator* emu, u8* data, size_t size) -{ - if ((size % 8) != 0) { - return BAD_CHEAT; +class CheatEntryWidget : public QWidget { + public: + CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent); + + void Update() { + name->setText(metadata.name.c_str()); + enabled->setChecked(metadata.enabled); + update(); } - Cheats::Cheat cheat; - cheat.enabled = true; - cheat.type = Cheats::CheatType::ActionReplay; - - for (size_t i = 0; i < size; i += 8) { - auto read32 = [](const u8* ptr) { return (u32(ptr[3]) << 24) | (u32(ptr[2]) << 16) | (u32(ptr[1]) << 8) | u32(ptr[0]); }; - - // Data is passed to us in big endian so we bswap - u32 firstWord = Common::swap32(read32(data + i)); - u32 secondWord = Common::swap32(read32(data + i + 4)); - cheat.instructions.insert(cheat.instructions.end(), {firstWord, secondWord}); + void Remove() { + emu->getCheats().removeCheat(metadata.handle); + cheatList->takeItem(cheatList->row(listItem)); + deleteLater(); } - return emu->getCheats().addCheat(cheat); -} + const CheatMetadata& GetMetadata() { return metadata; } -class CheatEntryWidget : public QWidget -{ -public: - CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent); + void SetMetadata(const CheatMetadata& metadata) { this->metadata = metadata; } - void Update() - { - name->setText(metadata.name.c_str()); - enabled->setChecked(metadata.enabled); - update(); - } + private: + void checkboxChanged(int state); + void editClicked(); - void Remove() - { - emu->getCheats().removeCheat(metadata.handle); - cheatList->takeItem(cheatList->row(listItem)); - deleteLater(); - } - - const CheatMetadata& GetMetadata() - { - return metadata; - } - - void SetMetadata(const CheatMetadata& metadata) - { - this->metadata = metadata; - } - -private: - void checkboxChanged(int state); - void editClicked(); - - Emulator* emu; - CheatMetadata metadata; - u32 handle; - QLabel* name; - QCheckBox* enabled; - QListWidget* cheatList; - QListWidgetItem* listItem; + Emulator* emu; + CheatMetadata metadata; + u32 handle; + QLabel* name; + QCheckBox* enabled; + QListWidget* cheatList; + QListWidgetItem* listItem; }; -class CheatEditDialog : public QDialog -{ -public: - CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry); +class CheatEditDialog : public QDialog { + public: + CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry); - void accepted(); - void rejected(); -private: - Emulator* emu; - CheatEntryWidget& cheatEntry; - QTextEdit* codeEdit; - QLineEdit* nameEdit; + void accepted(); + void rejected(); + + private: + Emulator* emu; + CheatEntryWidget& cheatEntry; + QTextEdit* codeEdit; + QLineEdit* nameEdit; }; CheatEntryWidget::CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent) - : QWidget(), emu(emu), metadata(metadata), cheatList(parent) -{ - QHBoxLayout* layout = new QHBoxLayout; + : QWidget(), emu(emu), metadata(metadata), cheatList(parent) { + QHBoxLayout* layout = new QHBoxLayout; - enabled = new QCheckBox; - enabled->setChecked(metadata.enabled); + enabled = new QCheckBox; + enabled->setChecked(metadata.enabled); - name = new QLabel(metadata.name.c_str()); - QPushButton* buttonEdit = new QPushButton(tr("Edit")); + name = new QLabel(metadata.name.c_str()); + QPushButton* buttonEdit = new QPushButton(tr("Edit")); - connect(enabled, &QCheckBox::stateChanged, this, &CheatEntryWidget::checkboxChanged); - connect(buttonEdit, &QPushButton::clicked, this, &CheatEntryWidget::editClicked); + connect(enabled, &QCheckBox::stateChanged, this, &CheatEntryWidget::checkboxChanged); + connect(buttonEdit, &QPushButton::clicked, this, &CheatEntryWidget::editClicked); - layout->addWidget(enabled); - layout->addWidget(name); - layout->addWidget(buttonEdit); - setLayout(layout); + layout->addWidget(enabled); + layout->addWidget(name); + layout->addWidget(buttonEdit); + setLayout(layout); - listItem = new QListWidgetItem; - listItem->setSizeHint(sizeHint()); - parent->addItem(listItem); - parent->setItemWidget(listItem, this); + listItem = new QListWidgetItem; + listItem->setSizeHint(sizeHint()); + parent->addItem(listItem); + parent->setItemWidget(listItem, this); } -void CheatEntryWidget::checkboxChanged(int state) -{ - bool enabled = state == Qt::Checked; - if (metadata.handle == BAD_CHEAT) - { - printf("Cheat handle is bad, this shouldn't happen\n"); - return; - } +void CheatEntryWidget::checkboxChanged(int state) { + bool enabled = state == Qt::Checked; + if (metadata.handle == badCheatHandle) { + printf("Cheat handle is bad, this shouldn't happen\n"); + return; + } - if (enabled) - { - emu->getCheats().enableCheat(metadata.handle); - metadata.enabled = true; - } - else - { - emu->getCheats().disableCheat(metadata.handle); - metadata.enabled = false; - } + if (enabled) { + emu->getCheats().enableCheat(metadata.handle); + metadata.enabled = true; + } else { + emu->getCheats().disableCheat(metadata.handle); + metadata.enabled = false; + } } -void CheatEntryWidget::editClicked() -{ - CheatEditDialog* dialog = new CheatEditDialog(emu, *this); - dialog->show(); +void CheatEntryWidget::editClicked() { + CheatEditDialog* dialog = new CheatEditDialog(emu, *this); + dialog->show(); } -CheatEditDialog::CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry) : QDialog(), emu(emu), cheatEntry(cheatEntry) -{ - setAttribute(Qt::WA_DeleteOnClose); - setModal(true); +CheatEditDialog::CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry) : QDialog(), emu(emu), cheatEntry(cheatEntry) { + setAttribute(Qt::WA_DeleteOnClose); + setModal(true); - QVBoxLayout* layout = new QVBoxLayout; - const CheatMetadata& metadata = cheatEntry.GetMetadata(); - codeEdit = new QTextEdit; - nameEdit = new QLineEdit; - nameEdit->setText(metadata.name.c_str()); - nameEdit->setPlaceholderText(tr("Cheat name")); - layout->addWidget(nameEdit); + QVBoxLayout* layout = new QVBoxLayout; + const CheatMetadata& metadata = cheatEntry.GetMetadata(); + codeEdit = new QTextEdit; + nameEdit = new QLineEdit; + nameEdit->setText(metadata.name.c_str()); + nameEdit->setPlaceholderText(tr("Cheat name")); + layout->addWidget(nameEdit); - QFont font; - font.setFamily("Courier"); - font.setFixedPitch(true); - font.setPointSize(10); - codeEdit->setFont(font); + QFont font; + font.setFamily("Courier"); + font.setFixedPitch(true); + font.setPointSize(10); + codeEdit->setFont(font); - if (metadata.code.size() != 0) - { - // Nicely format it like so: - // 01234567 89ABCDEF - // 01234567 89ABCDEF - std::string formattedCode; - for (size_t i = 0; i < metadata.code.size(); i += 2) - { - if (i != 0) { - if (i % 8 == 0 && i % 16 != 0) - { - formattedCode += " "; - } - else if (i % 16 == 0) - { - formattedCode += "\n"; - } - } + if (metadata.code.size() != 0) { + // Nicely format it like so: + // 01234567 89ABCDEF + // 01234567 89ABCDEF + std::string formattedCode; + for (size_t i = 0; i < metadata.code.size(); i += 2) { + if (i != 0) { + if (i % 8 == 0 && i % 16 != 0) { + formattedCode += " "; + } else if (i % 16 == 0) { + formattedCode += "\n"; + } + } - formattedCode += metadata.code[i]; - formattedCode += metadata.code[i + 1]; - } - codeEdit->setText(formattedCode.c_str()); - } + formattedCode += metadata.code[i]; + formattedCode += metadata.code[i + 1]; + } + codeEdit->setText(formattedCode.c_str()); + } - layout->addWidget(codeEdit); - setLayout(layout); + layout->addWidget(codeEdit); + setLayout(layout); - auto buttons = QDialogButtonBox::Ok | QDialogButtonBox::Cancel; - QDialogButtonBox* button_box = new QDialogButtonBox(buttons); - layout->addWidget(button_box); + auto buttons = QDialogButtonBox::Ok | QDialogButtonBox::Cancel; + QDialogButtonBox* button_box = new QDialogButtonBox(buttons); + layout->addWidget(button_box); - connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); - connect(this, &QDialog::rejected, this, &CheatEditDialog::rejected); - connect(this, &QDialog::accepted, this, &CheatEditDialog::accepted); + connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(this, &QDialog::rejected, this, &CheatEditDialog::rejected); + connect(this, &QDialog::accepted, this, &CheatEditDialog::accepted); } -void CheatEditDialog::accepted() -{ - QString code = codeEdit->toPlainText(); - code.replace(QRegularExpression("[^0-9a-fA-F]"), ""); +void CheatEditDialog::accepted() { + QString code = codeEdit->toPlainText(); + code.replace(QRegularExpression("[^0-9a-fA-F]"), ""); - CheatMetadata metadata = cheatEntry.GetMetadata(); - bool isEditing = metadata.handle != BAD_CHEAT; - metadata.name = nameEdit->text().toStdString(); - metadata.code = code.toStdString(); + CheatMetadata metadata = cheatEntry.GetMetadata(); + metadata.name = nameEdit->text().toStdString(); + metadata.code = code.toStdString(); - std::vector bytes; - for (size_t i = 0; i < metadata.code.size(); i += 2) - { - std::string hex = metadata.code.substr(i, 2); - bytes.push_back((uint8_t)std::stoul(hex, nullptr, 16)); - } + std::vector bytes; + for (size_t i = 0; i < metadata.code.size(); i += 2) { + std::string hex = metadata.code.substr(i, 2); + bytes.push_back((uint8_t)std::stoul(hex, nullptr, 16)); + } - if (isEditing) - { - emu->getCheats().removeCheat(metadata.handle); - u32 handle = addCheat(emu, bytes.data(), bytes.size()); - metadata.handle = handle; - cheatEntry.SetMetadata(metadata); - } - else - { - if (metadata.name.empty()) - { - metadata.name = tr("Cheat code").toStdString(); - } - u32 handle = addCheat(emu, bytes.data(), bytes.size()); - metadata.handle = handle; - cheatEntry.SetMetadata(metadata); - } - - cheatEntry.Update(); + mainWindow->editCheat(cheatEntry.GetMetadata().handle, bytes, [this](u32 handle) { + CheatMetadata metadata = cheatEntry.GetMetadata(); + metadata.handle = handle; + cheatEntry.SetMetadata(metadata); + cheatEntry.Update(); + }); } -void CheatEditDialog::rejected() -{ - bool isEditing = cheatEntry.GetMetadata().handle != BAD_CHEAT; +void CheatEditDialog::rejected() { + bool isEditing = cheatEntry.GetMetadata().handle != badCheatHandle; - if (!isEditing) - { - // Was adding a cheat but pressed cancel - cheatEntry.Remove(); - } + if (!isEditing) { + // Was adding a cheat but pressed cancel + cheatEntry.Remove(); + } } CheatsWindow::CheatsWindow(Emulator* emu, const std::filesystem::path& cheatPath, QWidget* parent) -: QWidget(parent, Qt::Window), emu(emu), cheatPath(cheatPath) -{ - QVBoxLayout* layout = new QVBoxLayout; - layout->setContentsMargins(6, 6, 6, 6); - setLayout(layout); + : QWidget(parent, Qt::Window), emu(emu), cheatPath(cheatPath) { + mainWindow = static_cast(parent); - cheatList = new QListWidget; - layout->addWidget(cheatList); + QVBoxLayout* layout = new QVBoxLayout; + layout->setContentsMargins(6, 6, 6, 6); + setLayout(layout); - QWidget* buttonBox = new QWidget; - QHBoxLayout* buttonLayout = new QHBoxLayout; + cheatList = new QListWidget; + layout->addWidget(cheatList); - QPushButton* buttonAdd = new QPushButton(tr("Add")); - QPushButton* buttonRemove = new QPushButton(tr("Remove")); + QWidget* buttonBox = new QWidget; + QHBoxLayout* buttonLayout = new QHBoxLayout; - connect(buttonAdd, &QPushButton::clicked, this, &CheatsWindow::addEntry); - connect(buttonRemove, &QPushButton::clicked, this, &CheatsWindow::removeClicked); + QPushButton* buttonAdd = new QPushButton(tr("Add")); + QPushButton* buttonRemove = new QPushButton(tr("Remove")); - buttonLayout->addWidget(buttonAdd); - buttonLayout->addWidget(buttonRemove); - buttonBox->setLayout(buttonLayout); + connect(buttonAdd, &QPushButton::clicked, this, &CheatsWindow::addEntry); + connect(buttonRemove, &QPushButton::clicked, this, &CheatsWindow::removeClicked); - layout->addWidget(buttonBox); + buttonLayout->addWidget(buttonAdd); + buttonLayout->addWidget(buttonRemove); + buttonBox->setLayout(buttonLayout); - // TODO: load cheats from saved cheats per game - // for (const CheatMetadata& metadata : getSavedCheats()) - // { - // new CheatEntryWidget(emu, metadata, cheatList); - // } + layout->addWidget(buttonBox); + + // TODO: load cheats from saved cheats per game + // for (const CheatMetadata& metadata : getSavedCheats()) + // { + // new CheatEntryWidget(emu, metadata, cheatList); + // } } -void CheatsWindow::addEntry() -{ - // CheatEntryWidget is added to the list when it's created - CheatEntryWidget* entry = new CheatEntryWidget(emu, {BAD_CHEAT, "New cheat", "", true}, cheatList); - CheatEditDialog* dialog = new CheatEditDialog(emu, *entry); - dialog->show(); +void CheatsWindow::addEntry() { + // CheatEntryWidget is added to the list when it's created + CheatEntryWidget* entry = new CheatEntryWidget(emu, {badCheatHandle, "New cheat", "", true}, cheatList); + CheatEditDialog* dialog = new CheatEditDialog(emu, *entry); + dialog->show(); } -void CheatsWindow::removeClicked() -{ - QListWidgetItem* item = cheatList->currentItem(); - if (item == nullptr) - { - return; - } +void CheatsWindow::removeClicked() { + QListWidgetItem* item = cheatList->currentItem(); + if (item == nullptr) { + return; + } - CheatEntryWidget* entry = static_cast(cheatList->itemWidget(item)); - entry->Remove(); + CheatEntryWidget* entry = static_cast(cheatList->itemWidget(item)); + entry->Remove(); } diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index be5e4fd6..d1f86173 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -6,6 +6,8 @@ #include #include +#include "cheats.hpp" + MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), screen(this) { setWindowTitle("Alber"); // Enable drop events for loading ROMs @@ -48,8 +50,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) auto dumpRomFSAction = toolsMenu->addAction(tr("Dump RomFS")); auto luaEditorAction = toolsMenu->addAction(tr("Open Lua Editor")); - cheatsEditorAction = toolsMenu->addAction(tr("Open Cheats Editor")); - cheatsEditorAction->setEnabled(false); + auto cheatsEditorAction = toolsMenu->addAction(tr("Open Cheats Editor")); connect(dumpRomFSAction, &QAction::triggered, this, &MainWindow::dumpRomFS); connect(luaEditorAction, &QAction::triggered, this, &MainWindow::openLuaEditor); connect(cheatsEditorAction, &QAction::triggered, this, &MainWindow::openCheatsEditor); @@ -63,7 +64,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) // Set up misc objects aboutWindow = new AboutWindow(nullptr); configWindow = new ConfigWindow(this); - cheatsEditor = new CheatsWindow(emu, {}); + cheatsEditor = new CheatsWindow(emu, {}, this); luaEditor = new TextEditorWindow(this, "script.lua", ""); auto args = QCoreApplication::arguments(); @@ -245,7 +246,6 @@ void MainWindow::dispatchMessage(const EmulatorMessage& message) { switch (message.type) { case MessageType::LoadROM: emu->loadROM(*message.path.p); - cheatsEditorAction->setEnabled(true); // Clean up the allocated path delete message.path.p; break; @@ -255,6 +255,21 @@ void MainWindow::dispatchMessage(const EmulatorMessage& message) { delete message.string.str; break; + case MessageType::EditCheat: { + u32 handle = message.cheat.c->handle; + const std::vector& cheat = message.cheat.c->cheat; + const std::function& callback = message.cheat.c->callback; + bool isEditing = handle != badCheatHandle; + if (isEditing) { + emu->getCheats().removeCheat(handle); + u32 handle = emu->getCheats().addCheat(cheat.data(), cheat.size()); + } else { + u32 handle = emu->getCheats().addCheat(cheat.data(), cheat.size()); + callback(handle); + } + delete message.cheat.c; + } break; + case MessageType::Pause: emu->pause(); break; case MessageType::Resume: emu->resume(); break; case MessageType::TogglePause: emu->togglePause(); break; @@ -326,4 +341,16 @@ void MainWindow::loadLuaScript(const std::string& code) { // Make a copy of the code on the heap to send via the message queue message.string.str = new std::string(code); sendMessage(message); +} + +void MainWindow::editCheat(u32 handle, const std::vector& cheat, const std::function& callback) { + EmulatorMessage message{.type = MessageType::EditCheat}; + + CheatMessage* c = new CheatMessage(); + c->handle = handle; + c->cheat = cheat; + c->callback = callback; + + message.cheat.c = c; + sendMessage(message); } \ No newline at end of file