diff --git a/src/citra_qt/cheats.cpp b/src/citra_qt/cheats.cpp index 720609205..fc6f102a2 100644 --- a/src/citra_qt/cheats.cpp +++ b/src/citra_qt/cheats.cpp @@ -3,10 +3,12 @@ // Refer to the license.txt file included. #include +#include #include #include "citra_qt/cheats.h" #include "core/cheats/cheat_base.h" #include "core/cheats/cheats.h" +#include "core/cheats/gateway_cheat.h" #include "core/core.h" #include "core/hle/kernel/process.h" #include "ui_cheats.h" @@ -21,14 +23,23 @@ CheatDialog::CheatDialog(QWidget* parent) ui->tableCheats->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed); ui->tableCheats->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); ui->tableCheats->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed); - ui->textDetails->setEnabled(false); + ui->lineName->setEnabled(false); + ui->textCode->setEnabled(false); ui->textNotes->setEnabled(false); const auto game_id = fmt::format( "{:016X}", Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id); ui->labelTitle->setText(tr("Title ID: %1").arg(QString::fromStdString(game_id))); connect(ui->buttonClose, &QPushButton::released, this, &CheatDialog::OnCancel); + connect(ui->buttonAddCheat, &QPushButton::released, this, &CheatDialog::OnAddCheat); connect(ui->tableCheats, &QTableWidget::cellClicked, this, &CheatDialog::OnRowSelected); + connect(ui->lineName, &QLineEdit::textEdited, this, &CheatDialog::OnTextEdited); + connect(ui->textNotes, &QPlainTextEdit::textChanged, this, &CheatDialog::OnTextEdited); + connect(ui->textCode, &QPlainTextEdit::textChanged, this, &CheatDialog::OnTextEdited); + + connect(ui->buttonSave, &QPushButton::released, + [this] { SaveCheat(ui->tableCheats->currentRow()); }); + connect(ui->buttonDelete, &QPushButton::released, this, &CheatDialog::OnDeleteCheat); LoadCheats(); } @@ -36,7 +47,7 @@ CheatDialog::CheatDialog(QWidget* parent) CheatDialog::~CheatDialog() = default; void CheatDialog::LoadCheats() { - const auto& cheats = Core::System::GetInstance().CheatEngine().GetCheats(); + cheats = Core::System::GetInstance().CheatEngine().GetCheats(); ui->tableCheats->setRowCount(cheats.size()); @@ -56,20 +67,184 @@ void CheatDialog::LoadCheats() { } } +bool CheatDialog::CheckSaveCheat() { + auto answer = QMessageBox::warning( + this, tr("Cheats"), tr("Would you like to save the current cheat?"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Cancel); + + if (answer == QMessageBox::Yes) { + return SaveCheat(last_row); + } else { + return answer != QMessageBox::Cancel; + } +} + +bool CheatDialog::SaveCheat(int row) { + if (ui->lineName->text().isEmpty()) { + QMessageBox::critical(this, tr("Save Cheat"), tr("Please enter a cheat name.")); + return false; + } + if (ui->textCode->toPlainText().isEmpty()) { + QMessageBox::critical(this, tr("Save Cheat"), tr("Please enter the cheat code.")); + return false; + } + + // Check if the cheat lines are valid + auto code_lines = ui->textCode->toPlainText().split("\n", QString::SkipEmptyParts); + for (int i = 0; i < code_lines.size(); ++i) { + Cheats::GatewayCheat::CheatLine cheat_line(code_lines[i].toStdString()); + if (cheat_line.valid) + continue; + + auto answer = QMessageBox::warning( + this, tr("Save Cheat"), + tr("Cheat code line %1 is not valid.\nWould you like to ignore the error and continue?") + .arg(i + 1), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (answer == QMessageBox::No) + return false; + } + + auto cheat = std::make_shared(ui->lineName->text().toStdString(), + ui->textCode->toPlainText().toStdString(), + ui->textNotes->toPlainText().toStdString()); + + if (newly_created) { + Core::System::GetInstance().CheatEngine().AddCheat(cheat); + newly_created = false; + } else { + Core::System::GetInstance().CheatEngine().UpdateCheat(row, cheat); + } + Core::System::GetInstance().CheatEngine().SaveCheatFile(); + + int previous_row = ui->tableCheats->currentRow(); + int previous_col = ui->tableCheats->currentColumn(); + LoadCheats(); + ui->tableCheats->setCurrentCell(previous_row, previous_col); + + edited = false; + ui->buttonSave->setEnabled(false); + ui->buttonAddCheat->setEnabled(true); + return true; +} + +void CheatDialog::closeEvent(QCloseEvent* event) { + if (edited && !CheckSaveCheat()) { + event->ignore(); + return; + } + event->accept(); +} + void CheatDialog::OnCancel() { close(); } void CheatDialog::OnRowSelected(int row, int column) { - ui->textDetails->setEnabled(true); + if (row == last_row) { + return; + } + if (edited && !CheckSaveCheat()) { + ui->tableCheats->setCurrentCell(last_row, last_col); + return; + } + if (row < cheats.size()) { + if (newly_created) { + // Remove the newly created dummy item + newly_created = false; + ui->tableCheats->setRowCount(ui->tableCheats->rowCount() - 1); + } + + const auto& current_cheat = cheats[row]; + ui->lineName->setText(QString::fromStdString(current_cheat->GetName())); + ui->textNotes->setPlainText(QString::fromStdString(current_cheat->GetComments())); + ui->textCode->setPlainText(QString::fromStdString(current_cheat->GetCode())); + } + + edited = false; + ui->buttonSave->setEnabled(false); + ui->buttonDelete->setEnabled(true); + ui->buttonAddCheat->setEnabled(true); + ui->lineName->setEnabled(true); + ui->textCode->setEnabled(true); ui->textNotes->setEnabled(true); - const auto& current_cheat = Core::System::GetInstance().CheatEngine().GetCheats()[row]; - ui->textNotes->setPlainText(QString::fromStdString(current_cheat->GetComments())); - ui->textDetails->setPlainText(QString::fromStdString(current_cheat->ToString())); + + last_row = row; + last_col = column; } void CheatDialog::OnCheckChanged(int state) { const QCheckBox* checkbox = qobject_cast(sender()); int row = static_cast(checkbox->property("row").toInt()); - Core::System::GetInstance().CheatEngine().GetCheats()[row]->SetEnabled(state); + cheats[row]->SetEnabled(state); + Core::System::GetInstance().CheatEngine().SaveCheatFile(); +} + +void CheatDialog::OnTextEdited() { + edited = true; + ui->buttonSave->setEnabled(true); +} + +void CheatDialog::OnDeleteCheat() { + if (newly_created) { + newly_created = false; + } else { + Core::System::GetInstance().CheatEngine().RemoveCheat(ui->tableCheats->currentRow()); + Core::System::GetInstance().CheatEngine().SaveCheatFile(); + } + + LoadCheats(); + if (cheats.empty()) { + ui->lineName->setText(""); + ui->textCode->setPlainText(""); + ui->textNotes->setPlainText(""); + ui->lineName->setEnabled(false); + ui->textCode->setEnabled(false); + ui->textNotes->setEnabled(false); + ui->buttonDelete->setEnabled(false); + last_row = last_col = -1; + } else { + if (last_row >= ui->tableCheats->rowCount()) { + last_row = ui->tableCheats->rowCount() - 1; + } + ui->tableCheats->setCurrentCell(last_row, last_col); + + const auto& current_cheat = cheats[last_row]; + ui->lineName->setText(QString::fromStdString(current_cheat->GetName())); + ui->textNotes->setPlainText(QString::fromStdString(current_cheat->GetComments())); + ui->textCode->setPlainText(QString::fromStdString(current_cheat->GetCode())); + } + + edited = false; + ui->buttonSave->setEnabled(false); + ui->buttonAddCheat->setEnabled(true); +} + +void CheatDialog::OnAddCheat() { + if (edited && !CheckSaveCheat()) { + return; + } + + int row = ui->tableCheats->rowCount(); + ui->tableCheats->setRowCount(row + 1); + ui->tableCheats->setCurrentCell(row, 1); + + // create a dummy item + ui->tableCheats->setItem(row, 1, new QTableWidgetItem(tr("[new cheat]"))); + ui->tableCheats->setItem(row, 2, new QTableWidgetItem("")); + ui->lineName->setText(""); + ui->lineName->setPlaceholderText(tr("[new cheat]")); + ui->textCode->setPlainText(""); + ui->textNotes->setPlainText(""); + ui->lineName->setEnabled(true); + ui->textCode->setEnabled(true); + ui->textNotes->setEnabled(true); + ui->buttonSave->setEnabled(true); + ui->buttonDelete->setEnabled(true); + ui->buttonAddCheat->setEnabled(false); + + edited = false; + newly_created = true; + last_row = row; + last_col = 1; } diff --git a/src/citra_qt/cheats.h b/src/citra_qt/cheats.h index d532175ab..89c08b1c5 100644 --- a/src/citra_qt/cheats.h +++ b/src/citra_qt/cheats.h @@ -7,6 +7,10 @@ #include #include +namespace Cheats { +class CheatBase; +} + namespace Ui { class CheatDialog; } // namespace Ui @@ -19,12 +23,38 @@ public: ~CheatDialog(); private: - std::unique_ptr ui; - + /** + * Loads the cheats from the CheatEngine, and populates the table. + */ void LoadCheats(); + /** + * Pops up a message box asking if the user wants to save the current cheat. + * If the user selected Yes, attempts to save the current cheat. + * @return true if the user selected No, or if the cheat was saved successfully + * false if the user selected Cancel, or if the user selected Yes but saving failed + */ + bool CheckSaveCheat(); + + /** + * Saves the current cheat as the row-th cheat in the cheat list. + * @return true if the cheat is saved successfully, false otherwise + */ + bool SaveCheat(int row); + + void closeEvent(QCloseEvent* event) override; + private slots: void OnCancel(); void OnRowSelected(int row, int column); void OnCheckChanged(int state); + void OnTextEdited(); + void OnDeleteCheat(); + void OnAddCheat(); + +private: + std::unique_ptr ui; + std::vector> cheats; + bool edited = false, newly_created = false; + int last_row = -1, last_col = -1; }; diff --git a/src/citra_qt/cheats.ui b/src/citra_qt/cheats.ui index 08c0da19f..b6bb35b9e 100644 --- a/src/citra_qt/cheats.ui +++ b/src/citra_qt/cheats.ui @@ -22,182 +22,192 @@ Cheats - - - - 10 - 10 - 300 - 31 - - - - - 10 - - - - Title ID: - - - - - - 10 - 570 - 841 - 41 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Close - - - - - - - - - 10 - 80 - 551 - 471 - - - - - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::SelectRows - - - false - - - 3 - - - true - - - false - - - - + + + + + + + + 10 + - - - Name + Title ID: - - + + + + + + Qt::Horizontal + + + + + - Type + Add Cheat - - - - - - - - - 10 - 60 - 121 - 16 - - - - Available Cheats: - - - - - - 580 - 440 - 271 - 111 - - - - - - - true - - - - - - - - - 580 - 420 - 111 - 16 - - - - Notes: - - - - - - 580 - 80 - 271 - 311 - - - - - - - true - - - - - - - - - 580 - 60 - 55 - 16 - - - - Code: - - + + + + + + + + + + + + + 10 + 60 + 121 + 16 + + + + Available Cheats: + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SelectRows + + + QAbstractItemView::SingleSelection + + + false + + + 3 + + + true + + + false + + + + + + + + + Name + + + + + Type + + + + + + + + + + + + + + Qt::Horizontal + + + + + + + Save + + + false + + + + + + + Delete + + + false + + + + + + + + + + + + + Name: + + + + + + + + + + + + Notes: + + + + + + + + + + Code: + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + + + + Close + + + + + +