diff --git a/src/qt_gui/compatibility_info.cpp b/src/qt_gui/compatibility_info.cpp index aecac60c..69fb3e37 100644 --- a/src/qt_gui/compatibility_info.cpp +++ b/src/qt_gui/compatibility_info.cpp @@ -18,9 +18,10 @@ CompatibilityInfoClass::CompatibilityInfoClass() }; CompatibilityInfoClass::~CompatibilityInfoClass() = default; -void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) { - if (LoadCompatibilityFile()) - return; +void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent, bool forced) { + if (!forced) + if (LoadCompatibilityFile()) + return; QNetworkReply* reply = FetchPage(1); if (!WaitForReply(reply)) @@ -45,7 +46,8 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) { QMessageBox::critical(parent, tr("Error"), tr("Unable to update compatibility data! Try again later.")); // Try loading compatibility_file.json again - LoadCompatibilityFile(); + if (!forced) + LoadCompatibilityFile(); return; } diff --git a/src/qt_gui/compatibility_info.h b/src/qt_gui/compatibility_info.h index dcbaef84..0c47c27f 100644 --- a/src/qt_gui/compatibility_info.h +++ b/src/qt_gui/compatibility_info.h @@ -84,7 +84,7 @@ public: CompatibilityInfoClass(); ~CompatibilityInfoClass(); - void UpdateCompatibilityDatabase(QWidget* parent = nullptr); + void UpdateCompatibilityDatabase(QWidget* parent = nullptr, bool forced = false); bool LoadCompatibilityFile(); CompatibilityEntry GetCompatibilityInfo(const std::string& serial); void ExtractCompatibilityInfo(QByteArray response); diff --git a/src/qt_gui/game_grid_frame.cpp b/src/qt_gui/game_grid_frame.cpp index b932e46c..2ebb09e5 100644 --- a/src/qt_gui/game_grid_frame.cpp +++ b/src/qt_gui/game_grid_frame.cpp @@ -3,9 +3,12 @@ #include "common/path_util.h" #include "game_grid_frame.h" +#include "qt_gui/compatibility_info.h" -GameGridFrame::GameGridFrame(std::shared_ptr game_info_get, QWidget* parent) - : QTableWidget(parent), m_game_info(game_info_get) { +GameGridFrame::GameGridFrame(std::shared_ptr game_info_get, + std::shared_ptr compat_info_get, + QWidget* parent) + : QTableWidget(parent), m_game_info(game_info_get), m_compat_info(compat_info_get) { icon_size = Config::getIconSizeGrid(); windowWidth = parent->width(); this->setShowGrid(false); @@ -29,7 +32,7 @@ GameGridFrame::GameGridFrame(std::shared_ptr game_info_get, QWidg connect(this->horizontalScrollBar(), &QScrollBar::valueChanged, this, &GameGridFrame::RefreshGridBackgroundImage); connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) { - m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, this, false); + m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info, this, false); }); } diff --git a/src/qt_gui/game_grid_frame.h b/src/qt_gui/game_grid_frame.h index c0976768..4825d6da 100644 --- a/src/qt_gui/game_grid_frame.h +++ b/src/qt_gui/game_grid_frame.h @@ -10,6 +10,7 @@ #include "game_info.h" #include "game_list_utils.h" #include "gui_context_menus.h" +#include "qt_gui/compatibility_info.h" class GameGridFrame : public QTableWidget { Q_OBJECT @@ -29,11 +30,14 @@ private: GameListUtils m_game_list_utils; GuiContextMenus m_gui_context_menus; std::shared_ptr m_game_info; + std::shared_ptr m_compat_info; std::shared_ptr> m_games_shared; bool validCellSelected = false; public: - explicit GameGridFrame(std::shared_ptr game_info_get, QWidget* parent = nullptr); + explicit GameGridFrame(std::shared_ptr game_info_get, + std::shared_ptr compat_info_get, + QWidget* parent = nullptr); void PopulateGameGrid(QVector m_games, bool fromSearch); bool IsValidCellSelected(); diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index 53159d8e..bba3c289 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "common/config.h" #include "common/logging/log.h" #include "common/path_util.h" #include "common/string_util.h" @@ -72,7 +73,7 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, }); connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) { - m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, this, true); + m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info, this, true); }); connect(this, &QTableWidget::cellClicked, this, [=, this](int row, int column) { @@ -80,11 +81,6 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QDesktopServices::openUrl(QUrl(m_game_info->m_games[row].compatibility.url)); } }); - - // Do not show status column if it is not enabled - if (!Config::getCompatibilityEnabled()) { - this->setColumnHidden(2, true); - } } void GameListFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow, @@ -108,6 +104,8 @@ void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) { } void GameListFrame::PopulateGameList() { + // Do not show status column if it is not enabled + this->setColumnHidden(2, !Config::getCompatibilityEnabled()); this->setRowCount(m_game_info->m_games.size()); ResizeIcons(icon_size); @@ -241,7 +239,7 @@ void GameListFrame::SetCompatibilityItem(int row, int column, CompatibilityEntry break; case CompatibilityStatus::Nothing: color = QStringLiteral("#212121"); - status_explanation = tr("Games does not initialize properly / crashes the emulator"); + status_explanation = tr("Game does not initialize properly / crashes the emulator"); break; case CompatibilityStatus::Boots: color = QStringLiteral("#828282"); diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 3cc12c11..d26b860c 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -11,6 +11,9 @@ #include #include "cheats_patches.h" +#include "common/config.h" +#include "common/version.h" +#include "compatibility_info.h" #include "game_info.h" #include "trophy_viewer.h" @@ -27,8 +30,9 @@ class GuiContextMenus : public QObject { Q_OBJECT public: - void RequestGameMenu(const QPoint& pos, QVector m_games, QTableWidget* widget, - bool isList) { + void RequestGameMenu(const QPoint& pos, QVector m_games, + std::shared_ptr m_compat_info, + QTableWidget* widget, bool isList) { QPoint global_pos = widget->viewport()->mapToGlobal(pos); int itemID = 0; if (isList) { @@ -91,6 +95,21 @@ public: menu.addMenu(deleteMenu); + // Compatibility submenu. + QMenu* compatibilityMenu = new QMenu(tr("Compatibility..."), widget); + QAction* updateCompatibility = new QAction(tr("Update database"), widget); + QAction* viewCompatibilityReport = new QAction(tr("View report"), widget); + QAction* submitCompatibilityReport = new QAction(tr("Submit a report"), widget); + + compatibilityMenu->addAction(updateCompatibility); + compatibilityMenu->addAction(viewCompatibilityReport); + compatibilityMenu->addAction(submitCompatibilityReport); + + menu.addMenu(compatibilityMenu); + + compatibilityMenu->setEnabled(Config::getCompatibilityEnabled()); + viewCompatibilityReport->setEnabled(!m_games[itemID].compatibility.url.isEmpty()); + // Show menu. auto selected = menu.exec(global_pos); if (!selected) { @@ -360,6 +379,31 @@ public: } } } + + if (selected == updateCompatibility) { + m_compat_info->UpdateCompatibilityDatabase(widget, true); + } + + if (selected == viewCompatibilityReport) { + if (!m_games[itemID].compatibility.url.isEmpty()) + QDesktopServices::openUrl(QUrl(m_games[itemID].compatibility.url)); + } + + if (selected == submitCompatibilityReport) { + QUrl url = QUrl("https://github.com/shadps4-emu/shadps4-game-compatibility/issues/new"); + QUrlQuery query; + query.addQueryItem("template", QString("game_compatibility.yml")); + query.addQueryItem( + "title", QString("%1 - %2").arg(QString::fromStdString(m_games[itemID].serial), + QString::fromStdString(m_games[itemID].name))); + query.addQueryItem("game-name", QString::fromStdString(m_games[itemID].name)); + query.addQueryItem("game-code", QString::fromStdString(m_games[itemID].serial)); + query.addQueryItem("game-version", QString::fromStdString(m_games[itemID].version)); + query.addQueryItem("emulator-version", QString(Common::VERSION)); + url.setQuery(query); + + QDesktopServices::openUrl(url); + } } int GetRowIndex(QTreeWidget* treeWidget, QTreeWidgetItem* item) { diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index adb42fc2..4a9b4d94 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -140,7 +140,7 @@ void MainWindow::CreateDockWindows() { m_dock_widget.reset(new QDockWidget(tr("Game List"), this)); m_game_list_frame.reset(new GameListFrame(m_game_info, m_compat_info, this)); m_game_list_frame->setObjectName("gamelist"); - m_game_grid_frame.reset(new GameGridFrame(m_game_info, this)); + m_game_grid_frame.reset(new GameGridFrame(m_game_info, m_compat_info, this)); m_game_grid_frame->setObjectName("gamegridlist"); m_elf_viewer.reset(new ElfViewer(this)); m_elf_viewer->setObjectName("elflist"); @@ -253,20 +253,26 @@ void MainWindow::CreateConnects() { &MainWindow::StartGame); connect(ui->configureAct, &QAction::triggered, this, [this]() { - auto settingsDialog = new SettingsDialog(m_physical_devices, this); + auto settingsDialog = new SettingsDialog(m_physical_devices, m_compat_info, this); connect(settingsDialog, &SettingsDialog::LanguageChanged, this, &MainWindow::OnLanguageChanged); + connect(settingsDialog, &SettingsDialog::CompatibilityChanged, this, + &MainWindow::RefreshGameTable); + settingsDialog->exec(); }); connect(ui->settingsButton, &QPushButton::clicked, this, [this]() { - auto settingsDialog = new SettingsDialog(m_physical_devices, this); + auto settingsDialog = new SettingsDialog(m_physical_devices, m_compat_info, this); connect(settingsDialog, &SettingsDialog::LanguageChanged, this, &MainWindow::OnLanguageChanged); + connect(settingsDialog, &SettingsDialog::CompatibilityChanged, this, + &MainWindow::RefreshGameTable); + settingsDialog->exec(); }); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 97c891e4..f9095d8a 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -6,6 +6,8 @@ #include #include +#include "common/config.h" +#include "qt_gui/compatibility_info.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" #endif @@ -54,7 +56,9 @@ QStringList languageNames = {"Arabic", const QVector languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9, 15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 30, 28}; -SettingsDialog::SettingsDialog(std::span physical_devices, QWidget* parent) +SettingsDialog::SettingsDialog(std::span physical_devices, + std::shared_ptr m_compat_info, + QWidget* parent) : QDialog(parent), ui(new Ui::SettingsDialog) { ui->setupUi(this); ui->tabWidgetSettings->setUsesScrollButtons(false); @@ -140,6 +144,16 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->updaterGroupBox->setVisible(false); ui->GUIgroupBox->setMaximumSize(265, 16777215); #endif + connect(ui->updateCompatibilityButton, &QPushButton::clicked, this, + [this, parent, m_compat_info]() { + m_compat_info->UpdateCompatibilityDatabase(this, true); + emit CompatibilityChanged(); + }); + + connect(ui->enableCompatibilityCheckBox, &QCheckBox::stateChanged, this, [this](int state) { + Config::setCompatibilityEnabled(state); + emit CompatibilityChanged(); + }); } // Input TAB @@ -195,6 +209,9 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge #endif ui->GUIgroupBox->installEventFilter(this); ui->disableTrophycheckBox->installEventFilter(this); + ui->enableCompatibilityCheckBox->installEventFilter(this); + ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this); + ui->updateCompatibilityButton->installEventFilter(this); // Input ui->hideCursorGroupBox->installEventFilter(this); @@ -285,6 +302,10 @@ void SettingsDialog::LoadValuesFromConfig() { ui->vkSyncValidationCheckBox->setChecked( toml::find_or(data, "Vulkan", "validation_sync", false)); ui->rdocCheckBox->setChecked(toml::find_or(data, "Vulkan", "rdocEnable", false)); + ui->enableCompatibilityCheckBox->setChecked( + toml::find_or(data, "General", "compatibilityEnabled", false)); + ui->checkCompatibilityOnStartupCheckBox->setChecked( + toml::find_or(data, "General", "checkCompatibilityOnStartup", false)); #ifdef ENABLE_UPDATER ui->updateCheckBox->setChecked(toml::find_or(data, "General", "autoUpdate", false)); @@ -402,6 +423,12 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("GUIgroupBox"); } else if (elementName == "disableTrophycheckBox") { text = tr("disableTrophycheckBox"); + } else if (elementName == "enableCompatibilityCheckBox") { + text = tr("enableCompatibilityCheckBox"); + } else if (elementName == "checkCompatibilityOnStartupCheckBox") { + text = tr("checkCompatibilityOnStartupCheckBox"); + } else if (elementName == "updateCompatibilityButton") { + text = tr("updateCompatibilityButton"); } // Input @@ -515,6 +542,8 @@ void SettingsDialog::UpdateSettings() { Config::setRdocEnabled(ui->rdocCheckBox->isChecked()); Config::setAutoUpdate(ui->updateCheckBox->isChecked()); Config::setUpdateChannel(ui->updateComboBox->currentText().toStdString()); + Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked()); + Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked()); #ifdef ENABLE_DISCORD_RPC auto* rpc = Common::Singleton::Instance(); diff --git a/src/qt_gui/settings_dialog.h b/src/qt_gui/settings_dialog.h index 987b35d4..892e6767 100644 --- a/src/qt_gui/settings_dialog.h +++ b/src/qt_gui/settings_dialog.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -10,6 +11,7 @@ #include "common/config.h" #include "common/path_util.h" +#include "qt_gui/compatibility_info.h" namespace Ui { class SettingsDialog; @@ -18,7 +20,9 @@ class SettingsDialog; class SettingsDialog : public QDialog { Q_OBJECT public: - explicit SettingsDialog(std::span physical_devices, QWidget* parent = nullptr); + explicit SettingsDialog(std::span physical_devices, + std::shared_ptr m_compat_info, + QWidget* parent = nullptr); ~SettingsDialog(); bool eventFilter(QObject* obj, QEvent* event) override; @@ -28,6 +32,7 @@ public: signals: void LanguageChanged(const std::string& locale); + void CompatibilityChanged(); private: void LoadValuesFromConfig(); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index faa0bf84..af80743d 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -306,7 +306,7 @@ - 275 + 265 0 @@ -574,25 +574,88 @@ - - - - - Qt::Orientation::Horizontal + + + + + + 0 + 0 + - + - 40 - 20 + 0 + 0 - - - - - + + Game Compatibility + + + + 1 + + + 11 + + + + + Display Compatibility Data + + + + + + + Update Compatibility Database On Startup + + + + + + + 1 + + + 0 + + + + + + 0 + 0 + + + + + 197 + 28 + + + + + 16777215 + 16777215 + + + + Update Compatibility Database + + + + + + + + + + + diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index ddaa4fe0..9e648720 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -677,6 +677,21 @@ Play title music Play title music + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1221,6 +1236,21 @@ backButtonBehaviorGroupBox Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1334,6 +1364,11 @@ Serial Serial + + + Compatibility + Compatibility + Region @@ -1369,6 +1404,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate