diff --git a/src/duckstation-qt/generalsettingswidget.cpp b/src/duckstation-qt/generalsettingswidget.cpp index efe38d487..d6c0b53e6 100644 --- a/src/duckstation-qt/generalsettingswidget.cpp +++ b/src/duckstation-qt/generalsettingswidget.cpp @@ -25,6 +25,8 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW "LoadDevicesFromSaveStates", false); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.applyGameSettings, "Main", "ApplyGameSettings", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.autoLoadCheats, "Main", "AutoLoadCheats", + false); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.showOSDMessages, "Display", "ShowOSDMessages", true); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.showFPS, "Display", "ShowFPS", false); @@ -104,7 +106,7 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW "to use XInput over SDL2 for compatibility.")); // Since this one is compile-time selected, we don't put it in the .ui file. - int current_col = 1; + int current_col = 0; int current_row = m_ui.formLayout_4->rowCount() - current_col; #ifdef WITH_DISCORD_PRESENCE { diff --git a/src/duckstation-qt/generalsettingswidget.ui b/src/duckstation-qt/generalsettingswidget.ui index 9d3b09b68..6c10f087a 100644 --- a/src/duckstation-qt/generalsettingswidget.ui +++ b/src/duckstation-qt/generalsettingswidget.ui @@ -81,6 +81,13 @@ + + + + Automatically Load Cheats + + + diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 756951b0f..ebd7c3a1a 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -306,6 +306,12 @@ void MainWindow::onChangeDiscFromPlaylistMenuAboutToHide() m_ui.menuChangeDiscFromPlaylist->clear(); } +void MainWindow::onCheatsMenuAboutToShow() +{ + m_ui.menuCheats->clear(); + m_host_interface->populateCheatsMenu(m_ui.menuCheats); +} + void MainWindow::onRemoveDiscActionTriggered() { m_host_interface->changeDisc(QString()); @@ -545,9 +551,11 @@ void MainWindow::updateEmulationActions(bool starting, bool running) m_ui.actionReset->setDisabled(starting || !running); m_ui.actionPause->setDisabled(starting || !running); m_ui.actionChangeDisc->setDisabled(starting || !running); + m_ui.actionCheats->setDisabled(starting || !running); m_ui.actionScreenshot->setDisabled(starting || !running); m_ui.actionViewSystemDisplay->setEnabled(starting || running); m_ui.menuChangeDisc->setDisabled(starting || !running); + m_ui.menuCheats->setDisabled(starting || !running); m_ui.actionSaveState->setDisabled(starting || !running); m_ui.menuSaveState->setDisabled(starting || !running); @@ -622,6 +630,8 @@ void MainWindow::connectSignals() &MainWindow::onChangeDiscFromPlaylistMenuAboutToShow); connect(m_ui.menuChangeDiscFromPlaylist, &QMenu::aboutToHide, this, &MainWindow::onChangeDiscFromPlaylistMenuAboutToHide); + connect(m_ui.menuCheats, &QMenu::aboutToShow, this, &MainWindow::onCheatsMenuAboutToShow); + connect(m_ui.actionCheats, &QAction::triggered, [this] { m_ui.menuCheats->exec(QCursor::pos()); }); connect(m_ui.actionRemoveDisc, &QAction::triggered, this, &MainWindow::onRemoveDiscActionTriggered); connect(m_ui.actionAddGameDirectory, &QAction::triggered, [this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); }); diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index e53053866..3fdfcc7a4 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -61,6 +61,7 @@ private Q_SLOTS: void onChangeDiscFromGameListActionTriggered(); void onChangeDiscFromPlaylistMenuAboutToShow(); void onChangeDiscFromPlaylistMenuAboutToHide(); + void onCheatsMenuAboutToShow(); void onRemoveDiscActionTriggered(); void onViewToolbarActionToggled(bool checked); void onViewStatusBarActionToggled(bool checked); diff --git a/src/duckstation-qt/mainwindow.ui b/src/duckstation-qt/mainwindow.ui index 14e80ffee..5e47ee565 100644 --- a/src/duckstation-qt/mainwindow.ui +++ b/src/duckstation-qt/mainwindow.ui @@ -55,6 +55,15 @@ + + + Cheats + + + + :/icons/actions-tools-wizard.png:/icons/actions-tools-wizard.png + + Load State @@ -82,6 +91,8 @@ + + @@ -203,6 +214,7 @@ + @@ -385,6 +397,15 @@ Change Disc... + + + + :/icons/actions-tools-wizard-32.png:/icons/actions-tools-wizard-32.png + + + Cheats... + + diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index ddacf57e3..d2a15eb18 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -5,6 +5,7 @@ #include "common/file_system.h" #include "common/log.h" #include "common/string_util.h" +#include "core/cheats.h" #include "core/controller.h" #include "core/gpu.h" #include "core/system.h" @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -952,6 +954,87 @@ void QtHostInterface::populatePlaylistEntryMenu(QMenu* menu) } } +void QtHostInterface::populateCheatsMenu(QMenu* menu) +{ + Assert(!isOnWorkerThread()); + if (!System::IsValid()) + return; + + const bool has_cheat_list = System::HasCheatList(); + + QAction* action = menu->addAction(tr("&Load Cheats...")); + connect(action, &QAction::triggered, [this]() { + QString filename = QFileDialog::getOpenFileName(m_main_window, tr("Select Cheat File"), QString(), + tr("PCSXR/Libretro Cheat Files (*.cht);;All Files (*.*)")); + if (!filename.isEmpty()) + loadCheatList(filename); + }); + + action = menu->addAction(tr("&Save Cheats...")); + action->setEnabled(has_cheat_list); + connect(action, &QAction::triggered, [this]() { + QString filename = QFileDialog::getSaveFileName(m_main_window, tr("Select Cheat File"), QString(), + tr("PCSXR/Libretro Cheat Files (*.cht);;All Files (*.*)")); + if (!filename.isEmpty()) + SaveCheatList(filename.toUtf8().constData()); + }); + + QMenu* enabled_menu = menu->addMenu(tr("&Enabled Cheats")); + enabled_menu->setEnabled(has_cheat_list); + QMenu* apply_menu = menu->addMenu(tr("&Apply Cheats")); + apply_menu->setEnabled(has_cheat_list); + if (has_cheat_list) + { + CheatList* cl = System::GetCheatList(); + for (u32 i = 0; i < cl->GetCodeCount(); i++) + { + CheatCode& cc = cl->GetCode(i); + QString desc(QString::fromStdString(cc.description)); + action = enabled_menu->addAction(desc); + action->setCheckable(true); + action->setChecked(cc.enabled); + connect(action, &QAction::toggled, [this, i](bool enabled) { setCheatEnabled(i, enabled); }); + + action = apply_menu->addAction(desc); + connect(action, &QAction::triggered, [this, i]() { applyCheat(i); }); + } + } +} + +void QtHostInterface::loadCheatList(const QString& filename) +{ + if (!isOnWorkerThread()) + { + QMetaObject::invokeMethod(this, "loadCheatList", Qt::QueuedConnection, Q_ARG(const QString&, filename)); + return; + } + + LoadCheatList(filename.toUtf8().constData()); +} + +void QtHostInterface::setCheatEnabled(quint32 index, bool enabled) +{ + if (!isOnWorkerThread()) + { + QMetaObject::invokeMethod(this, "setCheatEnabled", Qt::QueuedConnection, Q_ARG(quint32, index), + Q_ARG(bool, enabled)); + return; + } + + SetCheatCodeState(index, enabled, g_settings.auto_load_cheats); +} + +void QtHostInterface::applyCheat(u32 index) +{ + if (!isOnWorkerThread()) + { + QMetaObject::invokeMethod(this, "applyCheat", Qt::QueuedConnection, Q_ARG(quint32, index)); + return; + } + + ApplyCheatCode(index); +} + void QtHostInterface::loadState(const QString& filename) { if (!isOnWorkerThread()) diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h index 663bb26eb..0d5c48721 100644 --- a/src/duckstation-qt/qthostinterface.h +++ b/src/duckstation-qt/qthostinterface.h @@ -90,6 +90,9 @@ public: /// Fills menu with the current playlist entries. The disc index is marked as checked. void populatePlaylistEntryMenu(QMenu* menu); + /// Fills menu with the current cheat options. + void populateCheatsMenu(QMenu* menu); + ALWAYS_INLINE QString getSavePathForInputProfile(const QString& name) const { return QString::fromStdString(GetSavePathForInputProfile(name.toUtf8().constData())); @@ -158,6 +161,9 @@ public Q_SLOTS: void saveScreenshot(); void redrawDisplayWindow(); void toggleFullscreen(); + void loadCheatList(const QString& filename); + void setCheatEnabled(quint32 index, bool enabled); + void applyCheat(quint32 index); private Q_SLOTS: void doStopThread(); diff --git a/src/duckstation-qt/resources/icons/actions-tools-wizard-32.png b/src/duckstation-qt/resources/icons/actions-tools-wizard-32.png new file mode 100644 index 000000000..34e4a79ba Binary files /dev/null and b/src/duckstation-qt/resources/icons/actions-tools-wizard-32.png differ diff --git a/src/duckstation-qt/resources/icons/actions-tools-wizard-32@2x.png b/src/duckstation-qt/resources/icons/actions-tools-wizard-32@2x.png new file mode 100644 index 000000000..9fe9a333d Binary files /dev/null and b/src/duckstation-qt/resources/icons/actions-tools-wizard-32@2x.png differ diff --git a/src/duckstation-qt/resources/icons/actions-tools-wizard.png b/src/duckstation-qt/resources/icons/actions-tools-wizard.png new file mode 100644 index 000000000..64bc04c00 Binary files /dev/null and b/src/duckstation-qt/resources/icons/actions-tools-wizard.png differ diff --git a/src/duckstation-qt/resources/icons/actions-tools-wizard@2x.png b/src/duckstation-qt/resources/icons/actions-tools-wizard@2x.png new file mode 100644 index 000000000..34e4a79ba Binary files /dev/null and b/src/duckstation-qt/resources/icons/actions-tools-wizard@2x.png differ diff --git a/src/duckstation-qt/resources/resources.qrc b/src/duckstation-qt/resources/resources.qrc index ba533b5cd..c1e775c78 100644 --- a/src/duckstation-qt/resources/resources.qrc +++ b/src/duckstation-qt/resources/resources.qrc @@ -19,6 +19,10 @@ icons/star-4.png icons/star-5.png icons/address-book-new-22.png + icons/actions-tools-wizard.png + icons/actions-tools-wizard@2x.png + icons/actions-tools-wizard-32.png + icons/actions-tools-wizard-32@2x.png icons/applications-internet.png icons/system-search.png icons/list-add.png