mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 14:45:41 +12:00
Use message queues
This commit is contained in:
parent
63f54478f0
commit
a473a34794
7 changed files with 266 additions and 269 deletions
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -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
|
// Make a copy of the code on the heap to send via the message queue
|
||||||
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);
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue