mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 06:05:40 +12:00
Merge branch 'master' into imgooi-lua
This commit is contained in:
commit
bede1b49f0
10 changed files with 474 additions and 27 deletions
|
@ -198,10 +198,10 @@ set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp)
|
|||
if(NOT ANDROID)
|
||||
if(ENABLE_QT_GUI)
|
||||
set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp
|
||||
src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp
|
||||
src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp src/panda_qt/cheats_window.cpp
|
||||
)
|
||||
set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp
|
||||
include/panda_qt/config_window.hpp include/panda_qt/text_editor.hpp
|
||||
include/panda_qt/config_window.hpp include/panda_qt/text_editor.hpp include/panda_qt/cheats_window.hpp
|
||||
)
|
||||
|
||||
source_group("Source Files\\Qt" FILES ${FRONTEND_SOURCE_FILES})
|
||||
|
|
|
@ -24,6 +24,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);
|
||||
|
@ -32,6 +33,7 @@ class Cheats {
|
|||
|
||||
void clear();
|
||||
bool haveCheats() const { return cheatsLoaded; }
|
||||
static constexpr u32 badCheatHandle = 0xFFFFFFFF;
|
||||
|
||||
private:
|
||||
ActionReplay ar; // An ActionReplay cheat machine for executing CTRPF codes
|
||||
|
|
26
include/panda_qt/cheats_window.hpp
Normal file
26
include/panda_qt/cheats_window.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <QAction>
|
||||
#include <QWidget>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
#include "emulator.hpp"
|
||||
|
||||
class QListWidget;
|
||||
|
||||
class CheatsWindow final : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CheatsWindow(Emulator* emu, const std::filesystem::path& path, QWidget* parent = nullptr);
|
||||
~CheatsWindow() = default;
|
||||
|
||||
private:
|
||||
void addEntry();
|
||||
void removeClicked();
|
||||
|
||||
QListWidget* cheatList;
|
||||
std::filesystem::path cheatPath;
|
||||
Emulator* emu;
|
||||
};
|
|
@ -5,6 +5,7 @@
|
|||
#include <QtWidgets>
|
||||
#include <atomic>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
@ -12,16 +13,36 @@
|
|||
#include "emulator.hpp"
|
||||
#include "panda_qt/about_window.hpp"
|
||||
#include "panda_qt/config_window.hpp"
|
||||
#include "panda_qt/cheats_window.hpp"
|
||||
#include "panda_qt/screen.hpp"
|
||||
#include "panda_qt/text_editor.hpp"
|
||||
#include "services/hid.hpp"
|
||||
|
||||
struct CheatMessage {
|
||||
u32 handle;
|
||||
std::vector<uint8_t> cheat;
|
||||
std::function<void(u32)> 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,
|
||||
SetCirclePadX,
|
||||
SetCirclePadY,
|
||||
LoadLuaScript,
|
||||
EditCheat,
|
||||
};
|
||||
|
||||
// Tagged union representing our message queue messages
|
||||
struct EmulatorMessage {
|
||||
|
@ -36,9 +57,17 @@ class MainWindow : public QMainWindow {
|
|||
u32 key;
|
||||
} key;
|
||||
|
||||
struct {
|
||||
s16 value;
|
||||
} circlepad;
|
||||
|
||||
struct {
|
||||
std::string* str;
|
||||
} string;
|
||||
|
||||
struct {
|
||||
CheatMessage* c;
|
||||
} cheat;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -54,6 +83,7 @@ class MainWindow : public QMainWindow {
|
|||
ScreenWidget screen;
|
||||
AboutWindow* aboutWindow;
|
||||
ConfigWindow* configWindow;
|
||||
CheatsWindow* cheatsEditor;
|
||||
TextEditorWindow* luaEditor;
|
||||
QMenuBar* menuBar = nullptr;
|
||||
|
||||
|
@ -63,6 +93,7 @@ class MainWindow : public QMainWindow {
|
|||
void selectROM();
|
||||
void dumpRomFS();
|
||||
void openLuaEditor();
|
||||
void openCheatsEditor();
|
||||
void showAboutMenu();
|
||||
void sendMessage(const EmulatorMessage& message);
|
||||
void dispatchMessage(const EmulatorMessage& message);
|
||||
|
@ -78,4 +109,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<uint8_t>& cheat, const std::function<void(u32)>& callback);
|
||||
};
|
|
@ -139,6 +139,65 @@ void ActionReplay::executeDType(const Cheat& cheat, u32 instruction) {
|
|||
switch (instruction) {
|
||||
case 0xD3000000: offset1 = cheat[pc++]; break;
|
||||
case 0xD3000001: offset2 = cheat[pc++]; break;
|
||||
|
||||
case 0xD6000000:
|
||||
write32(*activeOffset + cheat[pc++], u32(*activeData));
|
||||
*activeOffset += 4;
|
||||
break;
|
||||
|
||||
case 0xD6000001:
|
||||
write32(*activeOffset + cheat[pc++], u32(data1));
|
||||
*activeOffset += 4;
|
||||
break;
|
||||
|
||||
case 0xD6000002:
|
||||
write32(*activeOffset + cheat[pc++], u32(data2));
|
||||
*activeOffset += 4;
|
||||
break;
|
||||
|
||||
case 0xD7000000:
|
||||
write16(*activeOffset + cheat[pc++], u16(*activeData));
|
||||
*activeOffset += 2;
|
||||
break;
|
||||
|
||||
case 0xD7000001:
|
||||
write16(*activeOffset + cheat[pc++], u16(data1));
|
||||
*activeOffset += 2;
|
||||
break;
|
||||
|
||||
case 0xD7000002:
|
||||
write16(*activeOffset + cheat[pc++], u16(data2));
|
||||
*activeOffset += 2;
|
||||
break;
|
||||
|
||||
case 0xD8000000:
|
||||
write8(*activeOffset + cheat[pc++], u8(*activeData));
|
||||
*activeOffset += 1;
|
||||
break;
|
||||
|
||||
case 0xD8000001:
|
||||
write8(*activeOffset + cheat[pc++], u8(data1));
|
||||
*activeOffset += 1;
|
||||
break;
|
||||
|
||||
case 0xD8000002:
|
||||
write8(*activeOffset + cheat[pc++], u8(data2));
|
||||
*activeOffset += 1;
|
||||
break;
|
||||
|
||||
|
||||
case 0xD9000000: *activeData = read32(cheat[pc++] + *activeOffset); break;
|
||||
case 0xD9000001: data1 = read32(cheat[pc++] + *activeOffset); break;
|
||||
case 0xD9000002: data2 = read32(cheat[pc++] + *activeOffset); break;
|
||||
|
||||
case 0xDA000000: *activeData = read16(cheat[pc++] + *activeOffset); break;
|
||||
case 0xDA000001: data1 = read16(cheat[pc++] + *activeOffset); break;
|
||||
case 0xDA000002: data2 = read16(cheat[pc++] + *activeOffset); break;
|
||||
|
||||
case 0xDB000000: *activeData = read8(cheat[pc++] + *activeOffset); break;
|
||||
case 0xDB000001: data1 = read8(cheat[pc++] + *activeOffset); break;
|
||||
case 0xDB000002: data2 = read8(cheat[pc++] + *activeOffset); break;
|
||||
|
||||
case 0xDC000000: *activeOffset += cheat[pc++]; break;
|
||||
|
||||
// DD000000 XXXXXXXX - if KEYPAD has value XXXXXXXX execute next block
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -134,25 +134,7 @@ void HydraCore::setPollInputCallback(void (*callback)()) { pollInputCallback = c
|
|||
void HydraCore::setCheckButtonCallback(s32 (*callback)(u32 player, hydra::ButtonType button)) { checkButtonCallback = callback; }
|
||||
|
||||
u32 HydraCore::addCheat(const u8* data, u32 size) {
|
||||
// Every 3DS cheat is a multiple of 64 bits == 8 bytes
|
||||
if ((size % 8) != 0) {
|
||||
return hydra::BAD_CHEAT;
|
||||
}
|
||||
|
||||
Cheats::Cheat cheat;
|
||||
cheat.enabled = true;
|
||||
cheat.type = Cheats::CheatType::ActionReplay;
|
||||
|
||||
for (u32 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 emulator->getCheats().addCheat(cheat);
|
||||
return emulator->getCheats().addCheat(data, size);
|
||||
};
|
||||
|
||||
void HydraCore::removeCheat(u32 id) { emulator->getCheats().removeCheat(id); }
|
||||
|
|
268
src/panda_qt/cheats_window.cpp
Normal file
268
src/panda_qt/cheats_window.cpp
Normal file
|
@ -0,0 +1,268 @@
|
|||
#include "panda_qt/cheats_window.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include <QTimer>
|
||||
#include <functional>
|
||||
|
||||
#include "cheats.hpp"
|
||||
#include "emulator.hpp"
|
||||
#include "panda_qt/main_window.hpp"
|
||||
|
||||
MainWindow* mainWindow = nullptr;
|
||||
|
||||
struct CheatMetadata {
|
||||
u32 handle = Cheats::badCheatHandle;
|
||||
std::string name = "New cheat";
|
||||
std::string code;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
void dispatchToMainThread(std::function<void()> callback) {
|
||||
QTimer* timer = new QTimer();
|
||||
timer->moveToThread(qApp->thread());
|
||||
timer->setSingleShot(true);
|
||||
QObject::connect(timer, &QTimer::timeout, [=]()
|
||||
{
|
||||
callback();
|
||||
timer->deleteLater();
|
||||
});
|
||||
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class CheatEditDialog : public QDialog {
|
||||
public:
|
||||
CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry);
|
||||
|
||||
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;
|
||||
|
||||
enabled = new QCheckBox;
|
||||
enabled->setChecked(metadata.enabled);
|
||||
|
||||
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);
|
||||
|
||||
layout->addWidget(enabled);
|
||||
layout->addWidget(name);
|
||||
layout->addWidget(buttonEdit);
|
||||
setLayout(layout);
|
||||
|
||||
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 == Cheats::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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
layout->addWidget(codeEdit);
|
||||
setLayout(layout);
|
||||
|
||||
auto buttons = QDialogButtonBox::Ok | QDialogButtonBox::Cancel;
|
||||
QDialogButtonBox* buttonBox = new QDialogButtonBox(buttons);
|
||||
layout->addWidget(buttonBox);
|
||||
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttonBox, &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]"), "");
|
||||
|
||||
CheatMetadata metadata = cheatEntry.getMetadata();
|
||||
metadata.name = nameEdit->text().toStdString();
|
||||
metadata.code = code.toStdString();
|
||||
cheatEntry.setMetadata(metadata);
|
||||
|
||||
std::vector<u8> bytes;
|
||||
for (size_t i = 0; i < metadata.code.size(); i += 2) {
|
||||
std::string hex = metadata.code.substr(i, 2);
|
||||
bytes.push_back((u8)std::stoul(hex, nullptr, 16));
|
||||
}
|
||||
|
||||
mainWindow->editCheat(cheatEntry.getMetadata().handle, bytes, [this](u32 handle) {
|
||||
dispatchToMainThread([this, handle]() {
|
||||
if (handle == Cheats::badCheatHandle) {
|
||||
cheatEntry.Remove();
|
||||
return;
|
||||
} else {
|
||||
CheatMetadata metadata = cheatEntry.getMetadata();
|
||||
metadata.handle = handle;
|
||||
cheatEntry.setMetadata(metadata);
|
||||
cheatEntry.Update();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void CheatEditDialog::rejected() {
|
||||
bool isEditing = cheatEntry.getMetadata().handle != Cheats::badCheatHandle;
|
||||
if (!isEditing) {
|
||||
// Was adding a cheat but user pressed cancel
|
||||
cheatEntry.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
CheatsWindow::CheatsWindow(Emulator* emu, const std::filesystem::path& cheatPath, QWidget* parent)
|
||||
: 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;
|
||||
layout->addWidget(cheatList);
|
||||
|
||||
QWidget* buttonBox = new QWidget;
|
||||
QHBoxLayout* buttonLayout = new QHBoxLayout;
|
||||
|
||||
QPushButton* buttonAdd = new QPushButton(tr("Add"));
|
||||
QPushButton* buttonRemove = new QPushButton(tr("Remove"));
|
||||
|
||||
connect(buttonAdd, &QPushButton::clicked, this, &CheatsWindow::addEntry);
|
||||
connect(buttonRemove, &QPushButton::clicked, this, &CheatsWindow::removeClicked);
|
||||
|
||||
buttonLayout->addWidget(buttonAdd);
|
||||
buttonLayout->addWidget(buttonRemove);
|
||||
buttonBox->setLayout(buttonLayout);
|
||||
|
||||
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, {Cheats::badCheatHandle, "New cheat", "", true}, cheatList);
|
||||
CheatEditDialog* dialog = new CheatEditDialog(emu, *entry);
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
void CheatsWindow::removeClicked() {
|
||||
QListWidgetItem* item = cheatList->currentItem();
|
||||
if (item == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
CheatEntryWidget* entry = static_cast<CheatEntryWidget*>(cheatList->itemWidget(item));
|
||||
entry->Remove();
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
#include "cheats.hpp"
|
||||
#include "imgui.h"
|
||||
#include "backends/imgui_impl_sdl2.h"
|
||||
#include "backends/imgui_impl_opengl3.h"
|
||||
|
@ -56,20 +57,23 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
|||
|
||||
auto dumpRomFSAction = toolsMenu->addAction(tr("Dump RomFS"));
|
||||
auto luaEditorAction = toolsMenu->addAction(tr("Open Lua Editor"));
|
||||
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);
|
||||
|
||||
auto aboutAction = aboutMenu->addAction(tr("About Panda3DS"));
|
||||
connect(aboutAction, &QAction::triggered, this, &MainWindow::showAboutMenu);
|
||||
|
||||
emu = new Emulator();
|
||||
emu->setOutputSize(screen.surfaceWidth, screen.surfaceHeight);
|
||||
|
||||
// Set up misc objects
|
||||
aboutWindow = new AboutWindow(nullptr);
|
||||
configWindow = new ConfigWindow(this);
|
||||
cheatsEditor = new CheatsWindow(emu, {}, this);
|
||||
luaEditor = new TextEditorWindow(this, "script.lua", "");
|
||||
|
||||
emu = new Emulator();
|
||||
emu->setOutputSize(screen.surfaceWidth, screen.surfaceHeight);
|
||||
|
||||
auto args = QCoreApplication::arguments();
|
||||
if (args.size() > 1) {
|
||||
auto romPath = std::filesystem::current_path() / args.at(1).toStdU16String();
|
||||
|
@ -293,6 +297,7 @@ MainWindow::~MainWindow() {
|
|||
delete menuBar;
|
||||
delete aboutWindow;
|
||||
delete configWindow;
|
||||
delete cheatsEditor;
|
||||
delete luaEditor;
|
||||
}
|
||||
|
||||
|
@ -342,6 +347,7 @@ void MainWindow::showAboutMenu() {
|
|||
}
|
||||
|
||||
void MainWindow::openLuaEditor() { luaEditor->show(); }
|
||||
void MainWindow::openCheatsEditor() { cheatsEditor->show(); }
|
||||
|
||||
void MainWindow::dispatchMessage(const EmulatorMessage& message) {
|
||||
switch (message.type) {
|
||||
|
@ -356,12 +362,29 @@ void MainWindow::dispatchMessage(const EmulatorMessage& message) {
|
|||
delete message.string.str;
|
||||
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 != Cheats::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;
|
||||
case MessageType::Reset: emu->reset(Emulator::ReloadOption::Reload); break;
|
||||
case MessageType::PressKey: emu->getServiceManager().getHID().pressKey(message.key.key); break;
|
||||
case MessageType::ReleaseKey: emu->getServiceManager().getHID().releaseKey(message.key.key); break;
|
||||
case MessageType::SetCirclePadX: emu->getServiceManager().getHID().setCirclepadX(message.circlepad.value); break;
|
||||
case MessageType::SetCirclePadY: emu->getServiceManager().getHID().setCirclepadY(message.circlepad.value); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,7 +392,12 @@ void MainWindow::keyPressEvent(QKeyEvent* event) {
|
|||
auto pressKey = [this](u32 key) {
|
||||
EmulatorMessage message{.type = MessageType::PressKey};
|
||||
message.key.key = key;
|
||||
sendMessage(message);
|
||||
};
|
||||
|
||||
auto setCirclePad = [this](MessageType type, s16 value) {
|
||||
EmulatorMessage message{.type = type};
|
||||
message.circlepad.value = value;
|
||||
sendMessage(message);
|
||||
};
|
||||
|
||||
|
@ -382,6 +410,11 @@ void MainWindow::keyPressEvent(QKeyEvent* event) {
|
|||
case Qt::Key_Q: pressKey(HID::Keys::L); break;
|
||||
case Qt::Key_P: pressKey(HID::Keys::R); break;
|
||||
|
||||
case Qt::Key_W: setCirclePad(MessageType::SetCirclePadY, 0x9C); break;
|
||||
case Qt::Key_A: setCirclePad(MessageType::SetCirclePadX, -0x9C); break;
|
||||
case Qt::Key_S: setCirclePad(MessageType::SetCirclePadY, -0x9C); break;
|
||||
case Qt::Key_D: setCirclePad(MessageType::SetCirclePadX, 0x9C); break;
|
||||
|
||||
case Qt::Key_Right: pressKey(HID::Keys::Right); break;
|
||||
case Qt::Key_Left: pressKey(HID::Keys::Left); break;
|
||||
case Qt::Key_Up: pressKey(HID::Keys::Up); break;
|
||||
|
@ -398,7 +431,12 @@ void MainWindow::keyReleaseEvent(QKeyEvent* event) {
|
|||
auto releaseKey = [this](u32 key) {
|
||||
EmulatorMessage message{.type = MessageType::ReleaseKey};
|
||||
message.key.key = key;
|
||||
sendMessage(message);
|
||||
};
|
||||
|
||||
auto releaseCirclePad = [this](MessageType type) {
|
||||
EmulatorMessage message{.type = type};
|
||||
message.circlepad.value = 0;
|
||||
sendMessage(message);
|
||||
};
|
||||
|
||||
|
@ -411,6 +449,12 @@ void MainWindow::keyReleaseEvent(QKeyEvent* event) {
|
|||
case Qt::Key_Q: releaseKey(HID::Keys::L); break;
|
||||
case Qt::Key_P: releaseKey(HID::Keys::R); break;
|
||||
|
||||
case Qt::Key_W:
|
||||
case Qt::Key_S: releaseCirclePad(MessageType::SetCirclePadY); break;
|
||||
|
||||
case Qt::Key_A:
|
||||
case Qt::Key_D: releaseCirclePad(MessageType::SetCirclePadX); break;
|
||||
|
||||
case Qt::Key_Right: releaseKey(HID::Keys::Right); break;
|
||||
case Qt::Key_Left: releaseKey(HID::Keys::Left); break;
|
||||
case Qt::Key_Up: releaseKey(HID::Keys::Up); break;
|
||||
|
@ -427,4 +471,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<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);
|
||||
}
|
|
@ -16,8 +16,8 @@ TextEditorWindow::TextEditorWindow(QWidget* parent, const std::string& filename,
|
|||
ZepReplExCommand::Register(zepWidget.GetEditor(), &replProvider);
|
||||
|
||||
// Default to standard mode instead of vim mode, initialize text box
|
||||
zepWidget.GetEditor().SetGlobalMode(Zep::ZepMode_Standard::StaticName());
|
||||
zepWidget.GetEditor().InitWithText(filename, initialText);
|
||||
zepWidget.GetEditor().SetGlobalMode(Zep::ZepMode_Standard::StaticName());
|
||||
|
||||
// Layout for widgets
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout();
|
||||
|
@ -41,4 +41,4 @@ TextEditorWindow::TextEditorWindow(QWidget* parent, const std::string& filename,
|
|||
|
||||
mainLayout->addWidget(button);
|
||||
mainLayout->addWidget(&zepWidget);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue