[cheats] add runtime cheats toggles with master code support (#4334)

add per-cheat runtime toggles (thread-safe).
add master code handling (master code cannot be disabled, as noted in Atmosphere's implementation).
add configurable global / per-game initial states for cheat entries.
remove fixed opcode limits and do some fixes to text parsing / file loading.
do not run debug log while cheat logging is not enabled, to reduce performance impacts.
update the `Using Cheats` document to reflect the changes.

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4334
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: John <john@eden-emu.dev>
This commit is contained in:
Soar Qin 2026-10-30 03:47:38 +01:00 • committed by crueter
parent b4d5760014
commit 69665063a9
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
24 changed files with 365 additions and 55 deletions

View file

@ -91,6 +91,14 @@
<addaction name="action_Configure"/>
<addaction name="action_Configure_Current_Game"/>
</widget>
<widget class="QMenu" name="menu_Cheats">
<property name="visible">
<bool>false</bool>
</property>
<property name="title">
<string>&amp;Cheats</string>
</property>
</widget>
<widget class="QMenu" name="menu_View">
<property name="title">
<string>&amp;View</string>
@ -241,6 +249,7 @@
</widget>
<addaction name="menu_File"/>
<addaction name="menu_Emulation"/>
<addaction name="menu_Cheats"/>
<addaction name="menu_View"/>
<addaction name="menu_Tools"/>
<addaction name="menu_Multiplayer"/>

View file

@ -78,10 +78,12 @@
#include <QGuiApplication>
#include <QInputDialog>
#include <QMimeData>
#include <QMouseEvent>
#include <QPalette>
#include <QProgressDialog>
#include <QScreen>
#include <QShortcut>
#include <QSignalBlocker>
#include <QStatusBar>
#include <QtConcurrentRun>
@ -134,6 +136,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#endif
// Core //
#include "core/core.h"
#include "core/frontend/applets/general.h"
#include "core/frontend/applets/mii_edit.h"
#include "core/frontend/applets/software_keyboard.h"
@ -147,6 +150,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/savedata_factory.h"
#include "core/memory/dmnt_cheat_types.h"
#include "core/tools/renderdoc.h"
@ -1434,6 +1438,9 @@ void MainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
}
void MainWindow::ConnectWidgetEvents() {
ui->menu_Cheats->installEventFilter(this);
ui->menu_Cheats->menuAction()->setVisible(false);
connect(game_list, &GameList::BootGame, this, &MainWindow::BootGameFromList);
connect(game_list, &GameList::GameChosen, this, &MainWindow::OnGameListLoadFile);
connect(game_list, &GameList::OpenDirectory, this, &MainWindow::OnGameListOpenDirectory);
@ -1468,6 +1475,8 @@ void MainWindow::ConnectWidgetEvents() {
// Software Keyboard Applet
connect(this, &MainWindow::EmulationStarting, this, &MainWindow::SoftwareKeyboardExit);
connect(this, &MainWindow::EmulationStopping, this, &MainWindow::SoftwareKeyboardExit);
connect(this, &MainWindow::EmulationStarting, this, &MainWindow::RefreshRuntimeCheats);
connect(this, &MainWindow::EmulationStopping, this, &MainWindow::ClearRuntimeCheats);
connect(&status_bar_update_timer, &QTimer::timeout, this, &MainWindow::UpdateStatusBar);
@ -1475,6 +1484,68 @@ void MainWindow::ConnectWidgetEvents() {
&MultiplayerState::UpdateThemedIcons);
}
void MainWindow::RefreshRuntimeCheats() {
ClearRuntimeCheats();
const auto cheats = QtCommon::system->GetRuntimeCheats();
auto* disable_all = ui->menu_Cheats->addAction(tr("Disable All Cheats"));
connect(disable_all, &QAction::triggered, this, [this] {
for (auto* action : runtime_cheat_actions) {
if (action->isEnabled()) {
action->setChecked(false);
}
}
});
auto* enable_all = ui->menu_Cheats->addAction(tr("Enable All Cheats"));
connect(enable_all, &QAction::triggered, this, [this] {
for (auto* action : runtime_cheat_actions) {
if (action->isEnabled()) {
action->setChecked(true);
}
}
});
ui->menu_Cheats->addSeparator();
runtime_cheat_actions.reserve(cheats.size());
for (const auto& cheat : cheats) {
const QString name = cheat.name.empty() ? tr("Unnamed Cheat")
: QString::fromUtf8(cheat.name);
auto* action = ui->menu_Cheats->addAction(name);
action->setCheckable(true);
action->setChecked(cheat.enabled || cheat.is_master);
action->setEnabled(!cheat.is_master);
connect(action, &QAction::toggled, this, [action, id = cheat.id](bool enabled) {
if (!QtCommon::system->SetCheatEnabled(id, enabled)) {
const QSignalBlocker blocker{action};
action->setChecked(!enabled);
}
});
runtime_cheat_actions.push_back(action);
}
ui->menu_Cheats->menuAction()->setVisible(!runtime_cheat_actions.empty());
}
void MainWindow::ClearRuntimeCheats() {
ui->menu_Cheats->clear();
runtime_cheat_actions.clear();
ui->menu_Cheats->menuAction()->setVisible(false);
}
bool MainWindow::eventFilter(QObject* watched, QEvent* event) {
if (watched == ui->menu_Cheats && event->type() == QEvent::MouseButtonRelease) {
const auto* mouse_event = static_cast<QMouseEvent*>(event);
auto* action = ui->menu_Cheats->actionAt(mouse_event->pos());
if (mouse_event->button() == Qt::LeftButton && action && action->isEnabled() &&
action->isCheckable()) {
action->trigger();
return true;
}
}
return QMainWindow::eventFilter(watched, event);
}
void MainWindow::ConnectMenuEvents() {
const auto connect_menu = [&]<typename Fn>(QAction* action, const Fn& event_fn) {
connect(action, &QAction::triggered, this, event_fn);

View file

@ -176,6 +176,9 @@ public:
std::filesystem::path GetShortcutPath(QtCommon::Game::ShortcutTarget target);
protected:
bool eventFilter(QObject* watched, QEvent* event) override;
signals:
/**
@ -264,6 +267,8 @@ private:
void ConnectWidgetEvents();
void ConnectMenuEvents();
void UpdateMenuState();
void RefreshRuntimeCheats();
void ClearRuntimeCheats();
void SetupPrepareForSleep();
@ -552,6 +557,7 @@ private:
QString startup_icon_theme;
QActionGroup* game_size_actions;
std::vector<QAction*> runtime_cheat_actions;
// Debugger panes
ControllerDialog* controller_dialog = nullptr;