Skip to content

Commit

Permalink
RMG: implement netplay cheats support (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rosalie241 committed Jan 28, 2025
1 parent 7b4629b commit 6b1ccd2
Show file tree
Hide file tree
Showing 10 changed files with 378 additions and 34 deletions.
1 change: 1 addition & 0 deletions Source/RMG/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ set(RMG_SOURCES
UserInterface/Widget/KeybindButton.cpp
UserInterface/Dialog/SettingsDialog.cpp
UserInterface/Dialog/SettingsDialog.ui
UserInterface/Dialog/Cheats/CheatsCommon.cpp
UserInterface/Dialog/Cheats/CheatsDialog.cpp
UserInterface/Dialog/Cheats/CheatsDialog.ui
UserInterface/Dialog/Cheats/AddCheatDialog.cpp
Expand Down
201 changes: 201 additions & 0 deletions Source/RMG/UserInterface/Dialog/Cheats/CheatsCommon.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "CheatsCommon.hpp"

#include <QJsonObject>

#include <RMG-Core/Core.hpp>

using namespace CheatsCommon;

//
// Local Functions
//

static int find_json_cheat(const CoreCheat& cheat, const QJsonArray& json)
{
QJsonObject cheatObject;
QString targetCheatName = QString::fromStdString(cheat.Name);

for (qsizetype i = 0; i < json.size(); i++)
{
cheatObject = json[i].toObject();

if (cheatObject["name"].toString() == targetCheatName)
{
return (int)i;
}
}

return -1;
}

//
// Exported Function
//

bool CheatsCommon::IsCheatEnabled(bool netplay, const QJsonArray& json, const CoreCheat& cheat)
{
if (netplay)
{
int index = find_json_cheat(cheat, json);
return index != -1 && json[index].toObject().contains("codes");
}
else
{
return CoreIsCheatEnabled(cheat);
}
}

bool CheatsCommon::EnableCheat(bool netplay, QJsonArray& json, const CoreCheat& cheat, bool enabled)
{
if (netplay)
{
QJsonObject cheatObject;
QJsonArray codesArray;
QString targetCheatName = QString::fromStdString(cheat.Name);

if (enabled)
{
for (const auto& cheatCode : cheat.CheatCodes)
{
QJsonObject codeObject;
codeObject["address"] = (qint64)cheatCode.Address;
codeObject["value"] = (qint64)cheatCode.Value;
codeObject["use_option"] = cheatCode.UseOptions;
codeObject["option_index"] = cheatCode.OptionIndex;
codeObject["option_size"] = cheatCode.OptionSize;
codesArray.push_back(codeObject);
}

cheatObject["name"] = targetCheatName;
cheatObject["codes"] = codesArray;

int index = find_json_cheat(cheat, json);
if (index != -1)
{
// copy over the option object
cheatObject["option"] = json[index].toObject().value("option").toObject();
json[index] = cheatObject;
}
else
{
json.push_back(cheatObject);
}
}
else
{
int index = find_json_cheat(cheat, json);
if (index != -1)
{
cheatObject = json[index].toObject();

// if cheat object has an option key
// we have to keep that key because
// else the UI will behave inconsistent
// with the non-netplay cheats dialog
if (cheatObject.contains("option"))
{
cheatObject.remove("codes");
json[index] = cheatObject;
}
else
{
json.removeAt(index);
}
}
}

return true;
}
else
{
return CoreEnableCheat(cheat, enabled);
}
}

bool CheatsCommon::HasCheatOptionSet(bool netplay, const QJsonArray& json, const CoreCheat& cheat)
{
if (netplay)
{
int index = find_json_cheat(cheat, json);
return index != -1 && json[index].toObject().contains("option");
}
else
{
return CoreHasCheatOptionSet(cheat);
}
}

bool CheatsCommon::GetCheatOption(bool netplay, const QJsonArray& json, const CoreCheat& cheat, CoreCheatOption& option)
{
if (netplay)
{
QJsonObject cheatObject;
QJsonObject optionObject;

int index = find_json_cheat(cheat, json);
if (index == -1)
{
return false;
}

cheatObject = json[index].toObject();
optionObject = cheatObject["option"].toObject();
option.Name = optionObject["name"].toString().toStdString();
option.Size = optionObject["size"].toInt();
option.Value = optionObject["value"].toInteger();
return true;
}
else
{
return CoreGetCheatOption(cheat, option);
}
}
bool CheatsCommon::SetCheatOption(bool netplay, QJsonArray& json, const CoreCheat& cheat, CoreCheatOption& option)
{
if (netplay)
{
QJsonObject cheatObject;
QJsonObject optionObject;
QString targetCheatName = QString::fromStdString(cheat.Name);

int index = find_json_cheat(cheat, json);
if (index != -1)
{
cheatObject = json[index].toObject();
optionObject["name"] = QString::fromStdString(option.Name);
optionObject["size"] = option.Size;
optionObject["value"] = (qint64)option.Value;

cheatObject["option"] = optionObject;

json[index] = cheatObject;
}
else
{ // create dummy object when option has been set
// without the user having the cheat enabled
optionObject["name"] = QString::fromStdString(option.Name);
optionObject["size"] = option.Size;
optionObject["value"] = (qint64)option.Value;

cheatObject["name"] = targetCheatName;
cheatObject["option"] = optionObject;

json.append(cheatObject);
}

return true;
}
else
{
return CoreGetCheatOption(cheat, option);
}
}
37 changes: 37 additions & 0 deletions Source/RMG/UserInterface/Dialog/Cheats/CheatsCommon.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CHEATSCOMMONH_HPP
#define CHEATSCOMMONH_HPP

#include <QJsonArray>
#include <QComboBox>
#include <QString>

#include <RMG-Core/Core.hpp>

namespace CheatsCommon
{
// Checks whether cheat is enabled
bool IsCheatEnabled(bool netplay, const QJsonArray& json, const CoreCheat& cheat);

// Enables Cheat
bool EnableCheat(bool netplay, QJsonArray& json, const CoreCheat& cheat, bool enabled);

// Returns whether cheat has option set
bool HasCheatOptionSet(bool netplay, const QJsonArray& json, const CoreCheat& cheat);

// Retrieves cheat option
bool GetCheatOption(bool netplay, const QJsonArray& json, const CoreCheat& cheat, CoreCheatOption& option);

// Sets cheat option
bool SetCheatOption(bool netplay, QJsonArray& json, const CoreCheat& cheat, CoreCheatOption& option);
}

#endif // CHEATSCOMMONH_HPP
35 changes: 27 additions & 8 deletions Source/RMG/UserInterface/Dialog/Cheats/CheatsDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@

#include "AddCheatDialog.hpp"
#include "ChooseCheatOptionDialog.hpp"

#include "Utilities/QtMessageBox.hpp"
#include "CheatsCommon.hpp"

#include <QJsonObject>
#include <QFileInfo>

#include <RMG-Core/Core.hpp>
Expand All @@ -23,10 +24,13 @@ Q_DECLARE_METATYPE(CoreCheat);
using namespace UserInterface::Dialog;
using namespace Utilities;

CheatsDialog::CheatsDialog(QWidget *parent) : QDialog(parent)
CheatsDialog::CheatsDialog(QWidget *parent, bool netplay, QJsonArray cheatsJson) : QDialog(parent)
{
qRegisterMetaType<CoreCheat>();

this->netplay = netplay;
this->cheatsJson = cheatsJson;

this->setupUi(this);
this->loadCheats();
}
Expand All @@ -40,6 +44,11 @@ bool CheatsDialog::HasFailed(void)
return this->failedToParseCheats;
}

QJsonArray CheatsDialog::GetJson(void)
{
return this->cheatsJson;
}

void CheatsDialog::loadCheats(void)
{
std::vector<CoreCheat> cheats;
Expand All @@ -59,7 +68,7 @@ void CheatsDialog::loadCheats(void)
QString name = QString::fromStdString(cheat.Name);
QString section;
QStringList sections = name.split("\\");
bool enabled = CoreIsCheatEnabled(cheat);
bool enabled = CheatsCommon::IsCheatEnabled(this->netplay, this->cheatsJson, cheat);

for (int i = 0; i < sections.size(); i++)
{
Expand Down Expand Up @@ -173,7 +182,8 @@ QString CheatsDialog::getTreeWidgetItemTextFromCheat(CoreCheat cheat)
if (cheat.HasOptions)
{
CoreCheatOption cheatOption;
if (!CoreHasCheatOptionSet(cheat) || !CoreGetCheatOption(cheat, cheatOption))
if (!CheatsCommon::HasCheatOptionSet(this->netplay, this->cheatsJson, cheat) ||
!CheatsCommon::GetCheatOption(this->netplay, this->cheatsJson, cheat, cheatOption))
{
text = cheatName + " (=> ???? - Not Set)";
}
Expand Down Expand Up @@ -203,22 +213,22 @@ void CheatsDialog::on_cheatsTreeWidget_itemChanged(QTreeWidgetItem *item, int co
CoreCheat cheat = item->data(column, Qt::UserRole).value<CoreCheat>();
bool enabled = (item->checkState(column) == Qt::CheckState::Checked);

if (enabled && cheat.HasOptions && !CoreHasCheatOptionSet(cheat))
if (enabled && cheat.HasOptions && !CheatsCommon::HasCheatOptionSet(this->netplay, this->cheatsJson, cheat))
{
// ask user to select an option
this->on_cheatsTreeWidget_itemDoubleClicked(item, column);

// make sure the user selected an option,
// if they did then enable the cheat as requested,
// if not, reset the checkbox
if (!CoreHasCheatOptionSet(cheat))
if (!CheatsCommon::HasCheatOptionSet(this->netplay, this->cheatsJson, cheat))
{
item->setCheckState(0, Qt::CheckState::Unchecked);
return;
}
}

CoreEnableCheat(cheat, enabled);
CheatsCommon::EnableCheat(this->netplay, this->cheatsJson, cheat, enabled);
}

void CheatsDialog::on_cheatsTreeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
Expand Down Expand Up @@ -251,9 +261,12 @@ void CheatsDialog::on_cheatsTreeWidget_itemDoubleClicked(QTreeWidgetItem *item,
return;
}

Dialog::ChooseCheatOptionDialog dialog(cheat, this);
Dialog::ChooseCheatOptionDialog dialog(cheat, this->netplay, this->cheatsJson, this);
dialog.exec();

// update json
this->cheatsJson = dialog.GetJson();

// re-load cheat options
item->setText(column, this->getTreeWidgetItemTextFromCheat(cheat));
}
Expand Down Expand Up @@ -312,6 +325,12 @@ void CheatsDialog::on_removeCheatButton_clicked(void)

void CheatsDialog::accept(void)
{
if (this->netplay)
{
QDialog::accept();
return;
}

CoreSettingsSave();

if (!CoreApplyCheats())
Expand Down
9 changes: 7 additions & 2 deletions Source/RMG/UserInterface/Dialog/Cheats/CheatsDialog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
#ifndef CHEATSDIALOG_HPP
#define CHEATSDIALOG_HPP

#include <QWidget>
#include <QJsonArray>
#include <QDialog>
#include <QWidget>

#include <RMG-Core/Core.hpp>

Expand All @@ -26,14 +27,18 @@ class CheatsDialog : public QDialog, private Ui::CheatsDialog
Q_OBJECT

public:
CheatsDialog(QWidget *parent);
CheatsDialog(QWidget *parent, bool netplay = false, QJsonArray cheatsJson = {});
~CheatsDialog(void);

bool HasFailed(void);
QJsonArray GetJson(void);


private:
bool needCloseRom = false;
bool failedToParseCheats = false;
bool netplay = false;
QJsonArray cheatsJson;

void loadCheats(void);

Expand Down
Loading

0 comments on commit 6b1ccd2

Please sign in to comment.