diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f4146bb..b2b2ceaa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -552,6 +552,8 @@ qt_add_resources(RESOURCE_FILES src/shadps4.qrc) set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/about_dialog.h src/qt_gui/about_dialog.ui + src/qt_gui/cheats_patches.cpp + src/qt_gui/cheats_patches.h src/qt_gui/main_window_ui.h src/qt_gui/main_window.cpp src/qt_gui/main_window.h diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp new file mode 100644 index 00000000..5677570c --- /dev/null +++ b/src/qt_gui/cheats_patches.cpp @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cheats_patches.h" +#include "common/path_util.h" + +CheatsPatches::CheatsPatches(const QString& gameName, const QString& gameSerial, + const QString& gameVersion, const QString& gameSize, + const QPixmap& gameImage, QWidget* parent) + : QWidget(parent), m_gameName(gameName), m_gameSerial(gameSerial), m_gameVersion(gameVersion), + m_gameSize(gameSize), m_gameImage(gameImage) { + setupUI(); + resize(800, 400); +} + +CheatsPatches::~CheatsPatches() {} + +void CheatsPatches::setupUI() { + const auto& CHEATS_DIR = Common::FS::GetUserPath(Common::FS::PathType::CheatsDir); + QString CHEATS_DIR_QString = QString::fromStdString(CHEATS_DIR.string()); + QString NameCheatJson = m_gameSerial + "_" + m_gameVersion + ".json"; + m_cheatFilePath = CHEATS_DIR_QString + "/" + NameCheatJson; + + QHBoxLayout* mainLayout = new QHBoxLayout(this); + + // Create the game info group box + QGroupBox* gameInfoGroupBox = new QGroupBox("Game Information"); + QVBoxLayout* gameInfoLayout = new QVBoxLayout(gameInfoGroupBox); + gameInfoLayout->setAlignment(Qt::AlignTop); + + QLabel* gameImageLabel = new QLabel(); + if (!m_gameImage.isNull()) { + gameImageLabel->setPixmap(m_gameImage.scaled(250, 250, Qt::KeepAspectRatio)); + } else { + gameImageLabel->setText("No Image Available"); + } + gameImageLabel->setAlignment(Qt::AlignCenter); + gameInfoLayout->addWidget(gameImageLabel, 0, Qt::AlignCenter); + + QLabel* gameNameLabel = new QLabel(m_gameName); + gameNameLabel->setAlignment(Qt::AlignLeft); + gameNameLabel->setWordWrap(true); + gameInfoLayout->addWidget(gameNameLabel); + + QLabel* gameSerialLabel = new QLabel("Serial: " + m_gameSerial); + gameSerialLabel->setAlignment(Qt::AlignLeft); + gameInfoLayout->addWidget(gameSerialLabel); + + QLabel* gameVersionLabel = new QLabel("Version: " + m_gameVersion); + gameVersionLabel->setAlignment(Qt::AlignLeft); + gameInfoLayout->addWidget(gameVersionLabel); + + QLabel* gameSizeLabel = new QLabel("Size: " + m_gameSize); + gameSizeLabel->setAlignment(Qt::AlignLeft); + gameInfoLayout->addWidget(gameSizeLabel); + + // Create the tab widget + QTabWidget* tabWidget = new QTabWidget(); + QWidget* cheatsTab = new QWidget(); + QWidget* patchesTab = new QWidget(); + + // Layouts for the tabs + QVBoxLayout* cheatsLayout = new QVBoxLayout(); + QVBoxLayout* patchesLayout = new QVBoxLayout(); + + // Setup the cheats tab + QGroupBox* cheatsGroupBox = new QGroupBox("Cheats"); + rightLayout = new QVBoxLayout(cheatsGroupBox); + checkBoxStyle = "QCheckBox { font-size: 19px; }"; + buttonStyle = "QPushButton { font-size: 19px; }"; + rightLayout->setAlignment(Qt::AlignTop); + + loadCheats(m_cheatFilePath); + cheatsGroupBox->setLayout(rightLayout); + QScrollArea* scrollArea = new QScrollArea(); + scrollArea->setWidgetResizable(true); + scrollArea->setWidget(cheatsGroupBox); + cheatsLayout->addWidget(scrollArea); + + // Add a check for updates button + QHBoxLayout* buttonLayout = new QHBoxLayout(); + QPushButton* checkUpdateButton = new QPushButton("Download Cheats"); + connect(checkUpdateButton, &QPushButton::clicked, [=]() { + if (QFile::exists(m_cheatFilePath)) { + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this, "File Exists", + "File already exists. Do you want to replace it?", + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::No) { + return; + } + } + + const QString url = + "https://raw.githubusercontent.com/GoldHEN/GoldHEN_Cheat_Repository/main/json/" + + m_gameSerial + "_" + m_gameVersion + ".json"; + + QNetworkAccessManager* manager = new QNetworkAccessManager(this); + QNetworkRequest request(url); + QNetworkReply* reply = manager->get(request); + + connect(reply, &QNetworkReply::finished, [=]() { + if (reply->error() == QNetworkReply::NoError) { + QByteArray jsonData = reply->readAll(); + QFile cheatFile(m_cheatFilePath); + if (cheatFile.open(QIODevice::WriteOnly)) { + cheatFile.write(jsonData); + cheatFile.close(); + loadCheats(m_cheatFilePath); + } + } else { + QMessageBox::warning(this, "Cheats/Patches not found", + "No Cheats/Patches found for this game in this version."); + } + reply->deleteLater(); + }); + }); + buttonLayout->addWidget(checkUpdateButton); + cheatsLayout->addLayout(buttonLayout); + cheatsTab->setLayout(cheatsLayout); + patchesTab->setLayout(patchesLayout); + tabWidget->addTab(cheatsTab, "Cheats"); + tabWidget->addTab(patchesTab, "Patches"); + mainLayout->addWidget(gameInfoGroupBox, 1); + mainLayout->addWidget(tabWidget, 3); + setLayout(mainLayout); +} + +void CheatsPatches::loadCheats(const QString& filePath) { + QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) { + QByteArray jsonData = file.readAll(); + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); + QJsonObject jsonObject = jsonDoc.object(); + QJsonArray modsArray = jsonObject["mods"].toArray(); + addMods(modsArray); + } +} + +void CheatsPatches::addMods(const QJsonArray& modsArray) { + QLayoutItem* item; + while ((item = rightLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; + } + m_cheats.clear(); + + for (const QJsonValue& modValue : modsArray) { + QJsonObject modObject = modValue.toObject(); + QString modName = modObject["name"].toString(); + QString modType = modObject["type"].toString(); + + Cheat cheat; + cheat.name = modName; + cheat.type = modType; + + QJsonArray memoryArray = modObject["memory"].toArray(); + for (const QJsonValue& memoryValue : memoryArray) { + QJsonObject memoryObject = memoryValue.toObject(); + MemoryMod memoryMod; + memoryMod.offset = memoryObject["offset"].toString(); + memoryMod.on = memoryObject["on"].toString(); + memoryMod.off = memoryObject["off"].toString(); + cheat.memoryMods.append(memoryMod); + } + + m_cheats[modName] = cheat; + + if (modType == "checkbox") { + QCheckBox* cheatCheckBox = new QCheckBox(modName); + cheatCheckBox->setStyleSheet(checkBoxStyle); + rightLayout->addWidget(cheatCheckBox); + connect(cheatCheckBox, &QCheckBox::toggled, + [=](bool checked) { applyCheat(modName, checked); }); + } else if (modType == "button") { + QPushButton* cheatButton = new QPushButton(modName); + cheatButton->setStyleSheet(buttonStyle); + rightLayout->addWidget(cheatButton); + connect(cheatButton, &QPushButton::clicked, [=]() { applyCheat(modName, true); }); + } + } + + QLabel* creditsLabel = new QLabel(); + QString creditsText = "Author: "; + + QFile file(m_cheatFilePath); + if (file.open(QIODevice::ReadOnly)) { + QByteArray jsonData = file.readAll(); + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); + QJsonObject jsonObject = jsonDoc.object(); + QJsonArray creditsArray = jsonObject["credits"].toArray(); + for (const QJsonValue& creditValue : creditsArray) { + creditsText += creditValue.toString() + ", "; + } + if (creditsText.endsWith(", ")) { + creditsText.chop(2); + } + } + creditsLabel->setText(creditsText); + creditsLabel->setAlignment(Qt::AlignLeft); + rightLayout->addWidget(creditsLabel); +} + +void CheatsPatches::applyCheat(const QString& modName, bool enabled) { + if (!m_cheats.contains(modName)) + return; + + Cheat cheat = m_cheats[modName]; + + for (const MemoryMod& memoryMod : cheat.memoryMods) { + QString value = enabled ? memoryMod.on : memoryMod.off; + + std::string modNameStr = modName.toStdString(); + std::string offsetStr = memoryMod.offset.toStdString(); + std::string valueStr = value.toStdString(); + + LOG_INFO(Loader, "Cheat applied:{}, Offset:{}, Value:{}", modNameStr, offsetStr, valueStr); + + // Implement + // Send a request to modify the process memory. + } +} diff --git a/src/qt_gui/cheats_patches.h b/src/qt_gui/cheats_patches.h new file mode 100644 index 00000000..329b9aec --- /dev/null +++ b/src/qt_gui/cheats_patches.h @@ -0,0 +1,56 @@ +#ifndef CHEATS_PATCHES_H +#define CHEATS_PATCHES_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class CheatsPatches : public QWidget { + Q_OBJECT + +public: + CheatsPatches(const QString& gameName, const QString& gameSerial, const QString& gameVersion, + const QString& gameSize, const QPixmap& gameImage, QWidget* parent = nullptr); + ~CheatsPatches(); + +private: + void setupUI(); + void loadCheats(const QString& filePath); + void addMods(const QJsonArray& modsArray); + void applyCheat(const QString& modName, bool enabled); + + struct MemoryMod { + QString offset; + QString on; + QString off; + }; + + struct Cheat { + QString name; + QString type; + QVector memoryMods; + }; + + QString m_gameName; + QString m_gameSerial; + QString m_gameVersion; + QString m_gameSize; + QPixmap m_gameImage; + QString m_cheatFilePath; + + QVBoxLayout* rightLayout; + QString checkBoxStyle; + QString buttonStyle; + + QMap m_cheats; +}; + +#endif // CHEATS_PATCHES_H diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 35acb1d3..f217b1c7 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -3,27 +3,22 @@ #pragma once -#include #include #include #include #include -#include #include #include #include #include #include -#include -#include #include #include #include #include #include -#include -#include +#include "cheats_patches.h" #include "game_info.h" #include "trophy_viewer.h" @@ -139,257 +134,16 @@ public: } if (selected == &openCheats) { - const auto& CHEATS_DIR = Common::FS::GetUserPath(Common::FS::PathType::CheatsDir); - QString CHEATS_DIR_QString = QString::fromStdString(CHEATS_DIR.string()); - const QString NameCheatJson = QString::fromStdString(m_games[itemID].serial) + "_" + - QString::fromStdString(m_games[itemID].version) + ".json"; - const QString cheatFilePath = CHEATS_DIR_QString + "/" + NameCheatJson; - - QWidget* cheatWidget = new QWidget(); - cheatWidget->setAttribute(Qt::WA_DeleteOnClose); - cheatWidget->setWindowTitle("Cheats/Patches"); - cheatWidget->resize(700, 300); - - QVBoxLayout* mainLayout = new QVBoxLayout(cheatWidget); - QHBoxLayout* horizontalLayout = new QHBoxLayout(); - - // GroupBox for game information (Left side) - QGroupBox* gameInfoGroupBox = new QGroupBox(); - QVBoxLayout* leftLayout = new QVBoxLayout(gameInfoGroupBox); - leftLayout->setAlignment(Qt::AlignTop); - - // Game image - QLabel* gameImage = new QLabel(); - QPixmap pixmap(QString::fromStdString(m_games[itemID].icon_path)); - gameImage->setPixmap(pixmap.scaled(250, 250, Qt::KeepAspectRatio)); - gameImage->setAlignment(Qt::AlignCenter); - leftLayout->addWidget(gameImage, 0, Qt::AlignCenter); - - // Game name - QLabel* gameName = new QLabel(QString::fromStdString(m_games[itemID].name)); - gameName->setAlignment(Qt::AlignLeft); - gameName->setWordWrap(true); - leftLayout->addWidget(gameName); - - // Game serial - QLabel* gameSerial = - new QLabel("Serial: " + QString::fromStdString(m_games[itemID].serial)); - gameSerial->setAlignment(Qt::AlignLeft); - leftLayout->addWidget(gameSerial); - - // Game version - QLabel* gameVersion = - new QLabel("Version: " + QString::fromStdString(m_games[itemID].version)); - gameVersion->setAlignment(Qt::AlignLeft); - leftLayout->addWidget(gameVersion); - - // Game size - QLabel* gameSize = new QLabel("Size: " + QString::fromStdString(m_games[itemID].size)); - gameSize->setAlignment(Qt::AlignLeft); - leftLayout->addWidget(gameSize); - - // Check for credits and add QLabel if exists - QFile file(cheatFilePath); - if (file.open(QIODevice::ReadOnly)) { - QByteArray jsonData = file.readAll(); - QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); - QJsonObject jsonObject = jsonDoc.object(); - - if (jsonObject.contains("credits")) { - QJsonArray creditsArray = jsonObject["credits"].toArray(); - if (!creditsArray.isEmpty()) { - QStringList creditsList; - for (const QJsonValue& value : creditsArray) { - creditsList.append(value.toString()); - } - QString credits = creditsList.join(", "); - QLabel* creditsLabel = new QLabel("Author: " + credits); - creditsLabel->setAlignment(Qt::AlignLeft); - leftLayout->addWidget(creditsLabel); - } - } - } - - gameInfoGroupBox->setLayout(leftLayout); - horizontalLayout->addWidget(gameInfoGroupBox); - QScrollArea* scrollArea = new QScrollArea(); - scrollArea->setWidgetResizable(true); - - // Right - QGroupBox* cheatsGroupBox = new QGroupBox(); - QVBoxLayout* rightLayout = new QVBoxLayout(cheatsGroupBox); - QString checkBoxStyle = "QCheckBox { font-size: 19px; }"; - QString buttonStyle = "QPushButton { font-size: 19px; }"; - rightLayout->setAlignment(Qt::AlignTop); - - // Function to add checkboxes and buttons to the layout - auto addMods = [=](const QJsonArray& modsArray) { - // Limpar widgets existentes - QLayoutItem* item; - while ((item = rightLayout->takeAt(0)) != nullptr) { - delete item->widget(); - delete item; - } - - for (const QJsonValue& modValue : modsArray) { - QJsonObject modObject = modValue.toObject(); - QString modName = modObject["name"].toString(); - QString modType = modObject["type"].toString(); - - if (modType == "checkbox") { - bool isEnabled = modObject.contains("is_enabled") - ? modObject["is_enabled"].toBool() - : false; - QCheckBox* cheatCheckBox = new QCheckBox(modName); - cheatCheckBox->setStyleSheet(checkBoxStyle); - cheatCheckBox->setChecked(isEnabled); - rightLayout->addWidget(cheatCheckBox); - - // Connect the toggled(bool) signal to handle state change - connect(cheatCheckBox, &QCheckBox::toggled, [=](bool checked) { - if (checked) { - // Implement action when checkbox is checked - } else { - // Implement action when checkbox is unchecked - } - }); - } else if (modType == "button") { - QPushButton* cheatButton = new QPushButton(modName); - cheatButton->setStyleSheet(buttonStyle); - connect(cheatButton, &QPushButton::clicked, [=]() { - // Implementar a ação do botão !!! - }); - rightLayout->addWidget(cheatButton); - } - } - }; - - QNetworkAccessManager* manager = new QNetworkAccessManager(cheatWidget); - - auto loadCheats = [=](const QString& filePath) { - QFile file(filePath); - if (file.open(QIODevice::ReadOnly)) { - QByteArray jsonData = file.readAll(); - QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); - QJsonObject jsonObject = jsonDoc.object(); - QJsonArray modsArray = jsonObject["mods"].toArray(); - addMods(modsArray); - } - }; - - loadCheats(cheatFilePath); - cheatsGroupBox->setLayout(rightLayout); - scrollArea->setWidget(cheatsGroupBox); - horizontalLayout->addWidget(scrollArea); - mainLayout->addLayout(horizontalLayout); - - QHBoxLayout* buttonLayout = new QHBoxLayout(); - QPushButton* checkUpdateButton = new QPushButton("Check Update"); - QPushButton* cancelButton = new QPushButton("Cancel"); - QPushButton* saveButton = new QPushButton("Save"); - - connect(checkUpdateButton, &QPushButton::clicked, [=]() { - if (QFile::exists(cheatFilePath)) { - QMessageBox::StandardButton reply; - reply = QMessageBox::question(cheatWidget, "File Exists", - "File already exists. Do you want to replace it?", - QMessageBox::Yes | QMessageBox::No); - if (reply == QMessageBox::No) { - return; - } - } - - // Cheats repository URL, replace with a synced fork of the repository - const QString url = "https://raw.githubusercontent.com/GoldHEN/" - "GoldHEN_Cheat_Repository/main/json/" + - NameCheatJson; - - QNetworkRequest request(url); - QNetworkReply* reply = manager->get(request); - - connect(reply, &QNetworkReply::finished, [=]() { - if (reply->error() == QNetworkReply::NoError) { - QByteArray jsonData = reply->readAll(); - - // Save the JSON file in the cheats folder - QFile cheatFile(cheatFilePath); - if (cheatFile.open(QIODevice::WriteOnly)) { - cheatFile.write(jsonData); - cheatFile.close(); - } - - // Reload and add new widgets - loadCheats(cheatFilePath); - } else { - QMessageBox::warning( - cheatWidget, "Cheats/Patches not found", - "No Cheats/Patches found for this game in this version."); - } - - reply->deleteLater(); - }); - }); - - connect(cancelButton, &QPushButton::clicked, [=]() { cheatWidget->close(); }); - - connect(saveButton, &QPushButton::clicked, [=]() { - QJsonDocument jsonDoc; - QFile file(cheatFilePath); - if (file.open(QIODevice::ReadOnly)) { - jsonDoc = QJsonDocument::fromJson(file.readAll()); - file.close(); - } - - QJsonObject json = jsonDoc.object(); - QJsonArray modsArray = json["mods"].toArray(); - - QMap modMap; - for (int i = 0; i < rightLayout->count(); ++i) { - QWidget* widget = rightLayout->itemAt(i)->widget(); - if (QCheckBox* checkBox = qobject_cast(widget)) { - modMap[checkBox->text()] = checkBox->isChecked(); - } - // Buttons don't need state saving, so we ignore them. - } - - for (auto it = modMap.begin(); it != modMap.end(); ++it) { - bool found = false; - for (int i = 0; i < modsArray.size(); ++i) { - QJsonObject mod = modsArray[i].toObject(); - if (mod["name"].toString() == it.key()) { - mod["is_enabled"] = it.value(); - modsArray[i] = mod; - found = true; - break; - } - } - if (!found) { - QJsonObject newMod; - newMod["name"] = it.key(); - newMod["is_enabled"] = it.value(); - modsArray.append(newMod); - } - } - - json["mods"] = modsArray; - jsonDoc.setObject(json); - - if (file.open(QIODevice::WriteOnly)) { - file.write(jsonDoc.toJson()); - file.close(); - QMessageBox::information(cheatWidget, "Save", "Settings saved."); - cheatWidget->close(); - } else { - QMessageBox::warning(cheatWidget, "Error", "Could not open file for writing."); - } - }); - - buttonLayout->addWidget(checkUpdateButton); - buttonLayout->addWidget(cancelButton); - buttonLayout->addWidget(saveButton); - mainLayout->addLayout(buttonLayout); - cheatWidget->setLayout(mainLayout); - cheatWidget->show(); + QString gameName = QString::fromStdString(m_games[itemID].name); + QString gameSerial = QString::fromStdString(m_games[itemID].serial); + QString gameVersion = QString::fromStdString(m_games[itemID].version); + QString gameSize = QString::fromStdString(m_games[itemID].size); + QPixmap gameImage(QString::fromStdString(m_games[itemID].icon_path)); + CheatsPatches* cheatsPatches = + new CheatsPatches(gameName, gameSerial, gameVersion, gameSize, gameImage); + cheatsPatches->show(); + connect(widget->parent(), &QWidget::destroyed, cheatsPatches, + [widget, cheatsPatches]() { cheatsPatches->deleteLater(); }); } if (selected == &openTrophyViewer) {