Use message queues

This commit is contained in:
offtkp 2024-01-27 18:20:42 +02:00
parent 63f54478f0
commit a473a34794
7 changed files with 266 additions and 269 deletions

View file

@ -14,7 +14,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION
endif() endif()
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo) set(CMAKE_BUILD_TYPE Release)
endif() endif()
project(Alber) project(Alber)

View file

@ -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 // Forward-declare this since it's just passed and we don't want to include memory.hpp and increase compile time
class Memory; class Memory;
constexpr u32 badCheatHandle = 0xFFFFFFFF;
class Cheats { class Cheats {
public: public:
enum class CheatType { enum class CheatType {
@ -24,6 +26,7 @@ class Cheats {
Cheats(Memory& mem, HIDService& hid); Cheats(Memory& mem, HIDService& hid);
u32 addCheat(const Cheat& cheat); u32 addCheat(const Cheat& cheat);
u32 addCheat(const u8* data, size_t size);
void removeCheat(u32 id); void removeCheat(u32 id);
void enableCheat(u32 id); void enableCheat(u32 id);
void disableCheat(u32 id); void disableCheat(u32 id);

View file

@ -1,26 +1,26 @@
#pragma once #pragma once
#include <filesystem>
#include <memory>
#include <QAction> #include <QAction>
#include <QWidget> #include <QWidget>
#include <filesystem>
#include <memory>
#include "emulator.hpp" #include "emulator.hpp"
class QListWidget; class QListWidget;
class CheatsWindow final : public QWidget class CheatsWindow final : public QWidget {
{ Q_OBJECT
Q_OBJECT
public: public:
CheatsWindow(Emulator* emu, const std::filesystem::path& path, QWidget* parent = nullptr); CheatsWindow(Emulator* emu, const std::filesystem::path& path, QWidget* parent = nullptr);
~CheatsWindow() = default; ~CheatsWindow() = default;
private: private:
void addEntry(); void addEntry();
void removeClicked(); void removeClicked();
QListWidget* cheatList; QListWidget* cheatList;
std::filesystem::path cheatPath; std::filesystem::path cheatPath;
Emulator* emu; Emulator* emu;
}; };

View file

@ -17,12 +17,19 @@
#include "panda_qt/text_editor.hpp" #include "panda_qt/text_editor.hpp"
#include "services/hid.hpp" #include "services/hid.hpp"
struct CheatMessage
{
u32 handle;
std::vector<uint8_t> cheat;
std::function<void(u32)> callback;
};
class MainWindow : public QMainWindow { class MainWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
private: private:
// Types of messages we might send from the GUI thread to the emulator thread // 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 // Tagged union representing our message queue messages
struct EmulatorMessage { struct EmulatorMessage {
@ -40,6 +47,10 @@ class MainWindow : public QMainWindow {
struct { struct {
std::string* str; std::string* str;
} string; } string;
struct {
CheatMessage* c;
} cheat;
}; };
}; };
@ -59,8 +70,6 @@ class MainWindow : public QMainWindow {
TextEditorWindow* luaEditor; TextEditorWindow* luaEditor;
QMenuBar* menuBar = nullptr; QMenuBar* menuBar = nullptr;
QAction* cheatsEditorAction = nullptr;
void swapEmuBuffer(); void swapEmuBuffer();
void emuThreadMainLoop(); void emuThreadMainLoop();
void selectLuaFile(); void selectLuaFile();
@ -83,4 +92,5 @@ class MainWindow : public QMainWindow {
void keyPressEvent(QKeyEvent* event) override; void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override;
void loadLuaScript(const std::string& code); void loadLuaScript(const std::string& code);
void editCheat(u32 handle, const std::vector<uint8_t>& cheat, const std::function<void(u32)>& callback);
}; };

View file

@ -1,4 +1,5 @@
#include "cheats.hpp" #include "cheats.hpp"
#include "swap.hpp"
Cheats::Cheats(Memory& mem, HIDService& hid) : ar(mem, hid) { reset(); } Cheats::Cheats(Memory& mem, HIDService& hid) : ar(mem, hid) { reset(); }
@ -23,6 +24,27 @@ u32 Cheats::addCheat(const Cheat& cheat) {
return cheats.size() - 1; 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) { void Cheats::removeCheat(u32 id) {
if (id >= cheats.size()) { if (id >= cheats.size()) {
return; return;

View file

@ -1,313 +1,248 @@
#include "panda_qt/cheats_window.hpp" #include "panda_qt/cheats_window.hpp"
#include <qlistwidget.h>
#include <QVBoxLayout>
#include <QListWidget>
#include <QPushButton>
#include <QLabel>
#include <QCheckBox> #include <QCheckBox>
#include <QDialog> #include <QDialog>
#include <QLineEdit>
#include <QTextEdit>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
#include "cheats.hpp" #include "cheats.hpp"
#include "emulator.hpp" #include "emulator.hpp"
#include "panda_qt/main_window.hpp"
using CheatHandle = u32; MainWindow* mainWindow = nullptr;
CheatHandle BAD_CHEAT = 0xFFFFFFFF; struct CheatMetadata {
u32 handle = badCheatHandle;
struct CheatMetadata std::string name = "New cheat";
{ std::string code;
CheatHandle handle = BAD_CHEAT; bool enabled = true;
std::string name = "New cheat";
std::string code;
bool enabled = true;
}; };
u32 addCheat(Emulator* emu, u8* data, size_t size) class CheatEntryWidget : public QWidget {
{ public:
if ((size % 8) != 0) { CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent);
return BAD_CHEAT;
void Update() {
name->setText(metadata.name.c_str());
enabled->setChecked(metadata.enabled);
update();
} }
Cheats::Cheat cheat; void Remove() {
cheat.enabled = true; emu->getCheats().removeCheat(metadata.handle);
cheat.type = Cheats::CheatType::ActionReplay; cheatList->takeItem(cheatList->row(listItem));
deleteLater();
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 emu->getCheats().addCheat(cheat); const CheatMetadata& GetMetadata() { return metadata; }
}
class CheatEntryWidget : public QWidget void SetMetadata(const CheatMetadata& metadata) { this->metadata = metadata; }
{
public:
CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent);
void Update() private:
{ void checkboxChanged(int state);
name->setText(metadata.name.c_str()); void editClicked();
enabled->setChecked(metadata.enabled);
update();
}
void Remove() Emulator* emu;
{ CheatMetadata metadata;
emu->getCheats().removeCheat(metadata.handle); u32 handle;
cheatList->takeItem(cheatList->row(listItem)); QLabel* name;
deleteLater(); QCheckBox* enabled;
} QListWidget* cheatList;
QListWidgetItem* listItem;
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;
}; };
class CheatEditDialog : public QDialog class CheatEditDialog : public QDialog {
{ public:
public: CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry);
CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry);
void accepted(); void accepted();
void rejected(); void rejected();
private:
Emulator* emu; private:
CheatEntryWidget& cheatEntry; Emulator* emu;
QTextEdit* codeEdit; CheatEntryWidget& cheatEntry;
QLineEdit* nameEdit; QTextEdit* codeEdit;
QLineEdit* nameEdit;
}; };
CheatEntryWidget::CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent) CheatEntryWidget::CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent)
: QWidget(), emu(emu), metadata(metadata), cheatList(parent) : QWidget(), emu(emu), metadata(metadata), cheatList(parent) {
{ QHBoxLayout* layout = new QHBoxLayout;
QHBoxLayout* layout = new QHBoxLayout;
enabled = new QCheckBox; enabled = new QCheckBox;
enabled->setChecked(metadata.enabled); enabled->setChecked(metadata.enabled);
name = new QLabel(metadata.name.c_str()); name = new QLabel(metadata.name.c_str());
QPushButton* buttonEdit = new QPushButton(tr("Edit")); QPushButton* buttonEdit = new QPushButton(tr("Edit"));
connect(enabled, &QCheckBox::stateChanged, this, &CheatEntryWidget::checkboxChanged); connect(enabled, &QCheckBox::stateChanged, this, &CheatEntryWidget::checkboxChanged);
connect(buttonEdit, &QPushButton::clicked, this, &CheatEntryWidget::editClicked); connect(buttonEdit, &QPushButton::clicked, this, &CheatEntryWidget::editClicked);
layout->addWidget(enabled); layout->addWidget(enabled);
layout->addWidget(name); layout->addWidget(name);
layout->addWidget(buttonEdit); layout->addWidget(buttonEdit);
setLayout(layout); setLayout(layout);
listItem = new QListWidgetItem; listItem = new QListWidgetItem;
listItem->setSizeHint(sizeHint()); listItem->setSizeHint(sizeHint());
parent->addItem(listItem); parent->addItem(listItem);
parent->setItemWidget(listItem, this); parent->setItemWidget(listItem, this);
} }
void CheatEntryWidget::checkboxChanged(int state) void CheatEntryWidget::checkboxChanged(int state) {
{ bool enabled = state == Qt::Checked;
bool enabled = state == Qt::Checked; if (metadata.handle == badCheatHandle) {
if (metadata.handle == BAD_CHEAT) printf("Cheat handle is bad, this shouldn't happen\n");
{ return;
printf("Cheat handle is bad, this shouldn't happen\n"); }
return;
}
if (enabled) if (enabled) {
{ emu->getCheats().enableCheat(metadata.handle);
emu->getCheats().enableCheat(metadata.handle); metadata.enabled = true;
metadata.enabled = true; } else {
} emu->getCheats().disableCheat(metadata.handle);
else metadata.enabled = false;
{ }
emu->getCheats().disableCheat(metadata.handle);
metadata.enabled = false;
}
} }
void CheatEntryWidget::editClicked() void CheatEntryWidget::editClicked() {
{ CheatEditDialog* dialog = new CheatEditDialog(emu, *this);
CheatEditDialog* dialog = new CheatEditDialog(emu, *this); dialog->show();
dialog->show();
} }
CheatEditDialog::CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry) : QDialog(), emu(emu), cheatEntry(cheatEntry) CheatEditDialog::CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry) : QDialog(), emu(emu), cheatEntry(cheatEntry) {
{ setAttribute(Qt::WA_DeleteOnClose);
setAttribute(Qt::WA_DeleteOnClose); setModal(true);
setModal(true);
QVBoxLayout* layout = new QVBoxLayout; QVBoxLayout* layout = new QVBoxLayout;
const CheatMetadata& metadata = cheatEntry.GetMetadata(); const CheatMetadata& metadata = cheatEntry.GetMetadata();
codeEdit = new QTextEdit; codeEdit = new QTextEdit;
nameEdit = new QLineEdit; nameEdit = new QLineEdit;
nameEdit->setText(metadata.name.c_str()); nameEdit->setText(metadata.name.c_str());
nameEdit->setPlaceholderText(tr("Cheat name")); nameEdit->setPlaceholderText(tr("Cheat name"));
layout->addWidget(nameEdit); layout->addWidget(nameEdit);
QFont font; QFont font;
font.setFamily("Courier"); font.setFamily("Courier");
font.setFixedPitch(true); font.setFixedPitch(true);
font.setPointSize(10); font.setPointSize(10);
codeEdit->setFont(font); codeEdit->setFont(font);
if (metadata.code.size() != 0) if (metadata.code.size() != 0) {
{ // Nicely format it like so:
// Nicely format it like so: // 01234567 89ABCDEF
// 01234567 89ABCDEF // 01234567 89ABCDEF
// 01234567 89ABCDEF std::string formattedCode;
std::string formattedCode; for (size_t i = 0; i < metadata.code.size(); i += 2) {
for (size_t i = 0; i < metadata.code.size(); i += 2) if (i != 0) {
{ if (i % 8 == 0 && i % 16 != 0) {
if (i != 0) { formattedCode += " ";
if (i % 8 == 0 && i % 16 != 0) } else if (i % 16 == 0) {
{ formattedCode += "\n";
formattedCode += " "; }
} }
else if (i % 16 == 0)
{
formattedCode += "\n";
}
}
formattedCode += metadata.code[i]; formattedCode += metadata.code[i];
formattedCode += metadata.code[i + 1]; formattedCode += metadata.code[i + 1];
} }
codeEdit->setText(formattedCode.c_str()); codeEdit->setText(formattedCode.c_str());
} }
layout->addWidget(codeEdit); layout->addWidget(codeEdit);
setLayout(layout); setLayout(layout);
auto buttons = QDialogButtonBox::Ok | QDialogButtonBox::Cancel; auto buttons = QDialogButtonBox::Ok | QDialogButtonBox::Cancel;
QDialogButtonBox* button_box = new QDialogButtonBox(buttons); QDialogButtonBox* button_box = new QDialogButtonBox(buttons);
layout->addWidget(button_box); layout->addWidget(button_box);
connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(this, &QDialog::rejected, this, &CheatEditDialog::rejected); connect(this, &QDialog::rejected, this, &CheatEditDialog::rejected);
connect(this, &QDialog::accepted, this, &CheatEditDialog::accepted); connect(this, &QDialog::accepted, this, &CheatEditDialog::accepted);
} }
void CheatEditDialog::accepted() void CheatEditDialog::accepted() {
{ QString code = codeEdit->toPlainText();
QString code = codeEdit->toPlainText(); code.replace(QRegularExpression("[^0-9a-fA-F]"), "");
code.replace(QRegularExpression("[^0-9a-fA-F]"), "");
CheatMetadata metadata = cheatEntry.GetMetadata(); CheatMetadata metadata = cheatEntry.GetMetadata();
bool isEditing = metadata.handle != BAD_CHEAT; metadata.name = nameEdit->text().toStdString();
metadata.name = nameEdit->text().toStdString(); metadata.code = code.toStdString();
metadata.code = code.toStdString();
std::vector<uint8_t> bytes; std::vector<uint8_t> bytes;
for (size_t i = 0; i < metadata.code.size(); i += 2) for (size_t i = 0; i < metadata.code.size(); i += 2) {
{ std::string hex = metadata.code.substr(i, 2);
std::string hex = metadata.code.substr(i, 2); bytes.push_back((uint8_t)std::stoul(hex, nullptr, 16));
bytes.push_back((uint8_t)std::stoul(hex, nullptr, 16)); }
}
if (isEditing) mainWindow->editCheat(cheatEntry.GetMetadata().handle, bytes, [this](u32 handle) {
{ CheatMetadata metadata = cheatEntry.GetMetadata();
emu->getCheats().removeCheat(metadata.handle); metadata.handle = handle;
u32 handle = addCheat(emu, bytes.data(), bytes.size()); cheatEntry.SetMetadata(metadata);
metadata.handle = handle; cheatEntry.Update();
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();
} }
void CheatEditDialog::rejected() void CheatEditDialog::rejected() {
{ bool isEditing = cheatEntry.GetMetadata().handle != badCheatHandle;
bool isEditing = cheatEntry.GetMetadata().handle != BAD_CHEAT;
if (!isEditing) if (!isEditing) {
{ // Was adding a cheat but pressed cancel
// Was adding a cheat but pressed cancel cheatEntry.Remove();
cheatEntry.Remove(); }
}
} }
CheatsWindow::CheatsWindow(Emulator* emu, const std::filesystem::path& cheatPath, QWidget* parent) CheatsWindow::CheatsWindow(Emulator* emu, const std::filesystem::path& cheatPath, QWidget* parent)
: QWidget(parent, Qt::Window), emu(emu), cheatPath(cheatPath) : QWidget(parent, Qt::Window), emu(emu), cheatPath(cheatPath) {
{ mainWindow = static_cast<MainWindow*>(parent);
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(6, 6, 6, 6);
setLayout(layout);
cheatList = new QListWidget; QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(cheatList); layout->setContentsMargins(6, 6, 6, 6);
setLayout(layout);
QWidget* buttonBox = new QWidget; cheatList = new QListWidget;
QHBoxLayout* buttonLayout = new QHBoxLayout; layout->addWidget(cheatList);
QPushButton* buttonAdd = new QPushButton(tr("Add")); QWidget* buttonBox = new QWidget;
QPushButton* buttonRemove = new QPushButton(tr("Remove")); QHBoxLayout* buttonLayout = new QHBoxLayout;
connect(buttonAdd, &QPushButton::clicked, this, &CheatsWindow::addEntry); QPushButton* buttonAdd = new QPushButton(tr("Add"));
connect(buttonRemove, &QPushButton::clicked, this, &CheatsWindow::removeClicked); QPushButton* buttonRemove = new QPushButton(tr("Remove"));
buttonLayout->addWidget(buttonAdd); connect(buttonAdd, &QPushButton::clicked, this, &CheatsWindow::addEntry);
buttonLayout->addWidget(buttonRemove); connect(buttonRemove, &QPushButton::clicked, this, &CheatsWindow::removeClicked);
buttonBox->setLayout(buttonLayout);
layout->addWidget(buttonBox); buttonLayout->addWidget(buttonAdd);
buttonLayout->addWidget(buttonRemove);
buttonBox->setLayout(buttonLayout);
// TODO: load cheats from saved cheats per game layout->addWidget(buttonBox);
// for (const CheatMetadata& metadata : getSavedCheats())
// { // TODO: load cheats from saved cheats per game
// new CheatEntryWidget(emu, metadata, cheatList); // for (const CheatMetadata& metadata : getSavedCheats())
// } // {
// new CheatEntryWidget(emu, metadata, cheatList);
// }
} }
void CheatsWindow::addEntry() void CheatsWindow::addEntry() {
{ // CheatEntryWidget is added to the list when it's created
// CheatEntryWidget is added to the list when it's created CheatEntryWidget* entry = new CheatEntryWidget(emu, {badCheatHandle, "New cheat", "", true}, cheatList);
CheatEntryWidget* entry = new CheatEntryWidget(emu, {BAD_CHEAT, "New cheat", "", true}, cheatList); CheatEditDialog* dialog = new CheatEditDialog(emu, *entry);
CheatEditDialog* dialog = new CheatEditDialog(emu, *entry); dialog->show();
dialog->show();
} }
void CheatsWindow::removeClicked() void CheatsWindow::removeClicked() {
{ QListWidgetItem* item = cheatList->currentItem();
QListWidgetItem* item = cheatList->currentItem(); if (item == nullptr) {
if (item == nullptr) return;
{ }
return;
}
CheatEntryWidget* entry = static_cast<CheatEntryWidget*>(cheatList->itemWidget(item)); CheatEntryWidget* entry = static_cast<CheatEntryWidget*>(cheatList->itemWidget(item));
entry->Remove(); entry->Remove();
} }

View file

@ -6,6 +6,8 @@
#include <cstdio> #include <cstdio>
#include <fstream> #include <fstream>
#include "cheats.hpp"
MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), screen(this) { MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), screen(this) {
setWindowTitle("Alber"); setWindowTitle("Alber");
// Enable drop events for loading ROMs // 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 dumpRomFSAction = toolsMenu->addAction(tr("Dump RomFS"));
auto luaEditorAction = toolsMenu->addAction(tr("Open Lua Editor")); auto luaEditorAction = toolsMenu->addAction(tr("Open Lua Editor"));
cheatsEditorAction = toolsMenu->addAction(tr("Open Cheats Editor")); auto cheatsEditorAction = toolsMenu->addAction(tr("Open Cheats Editor"));
cheatsEditorAction->setEnabled(false);
connect(dumpRomFSAction, &QAction::triggered, this, &MainWindow::dumpRomFS); connect(dumpRomFSAction, &QAction::triggered, this, &MainWindow::dumpRomFS);
connect(luaEditorAction, &QAction::triggered, this, &MainWindow::openLuaEditor); connect(luaEditorAction, &QAction::triggered, this, &MainWindow::openLuaEditor);
connect(cheatsEditorAction, &QAction::triggered, this, &MainWindow::openCheatsEditor); connect(cheatsEditorAction, &QAction::triggered, this, &MainWindow::openCheatsEditor);
@ -63,7 +64,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
// Set up misc objects // Set up misc objects
aboutWindow = new AboutWindow(nullptr); aboutWindow = new AboutWindow(nullptr);
configWindow = new ConfigWindow(this); configWindow = new ConfigWindow(this);
cheatsEditor = new CheatsWindow(emu, {}); cheatsEditor = new CheatsWindow(emu, {}, this);
luaEditor = new TextEditorWindow(this, "script.lua", ""); luaEditor = new TextEditorWindow(this, "script.lua", "");
auto args = QCoreApplication::arguments(); auto args = QCoreApplication::arguments();
@ -245,7 +246,6 @@ void MainWindow::dispatchMessage(const EmulatorMessage& message) {
switch (message.type) { switch (message.type) {
case MessageType::LoadROM: case MessageType::LoadROM:
emu->loadROM(*message.path.p); emu->loadROM(*message.path.p);
cheatsEditorAction->setEnabled(true);
// Clean up the allocated path // Clean up the allocated path
delete message.path.p; delete message.path.p;
break; break;
@ -255,6 +255,21 @@ void MainWindow::dispatchMessage(const EmulatorMessage& message) {
delete message.string.str; delete message.string.str;
break; break;
case MessageType::EditCheat: {
u32 handle = message.cheat.c->handle;
const std::vector<uint8_t>& cheat = message.cheat.c->cheat;
const std::function<void(u32)>& 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::Pause: emu->pause(); break;
case MessageType::Resume: emu->resume(); break; case MessageType::Resume: emu->resume(); break;
case MessageType::TogglePause: emu->togglePause(); break; case MessageType::TogglePause: emu->togglePause(); break;
@ -327,3 +342,15 @@ void MainWindow::loadLuaScript(const std::string& code) {
message.string.str = new std::string(code); message.string.str = new std::string(code);
sendMessage(message); sendMessage(message);
} }
void MainWindow::editCheat(u32 handle, const std::vector<uint8_t>& cheat, const std::function<void(u32)>& 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);
}