diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt
index c149850ecd..fb522da699 100644
--- a/rpcs3/CMakeLists.txt
+++ b/rpcs3/CMakeLists.txt
@@ -6,17 +6,17 @@ set(CMAKE_CXX_STANDARD 14)
include(CheckCXXCompilerFlag)
# Qt section
-find_package(Qt5 5.7 COMPONENTS Widgets)
+find_package(Qt5 5.7 COMPONENTS Widgets Network)
if(WIN32)
find_package(Qt5 5.7 COMPONENTS WinExtras REQUIRED)
- set(RPCS3_QT_LIBS Qt5::Widgets Qt5::WinExtras)
+ set(RPCS3_QT_LIBS Qt5::Widgets Qt5::WinExtras Qt5::Network)
else()
find_package(Qt5 5.7 COMPONENTS DBus)
if(Qt5DBus_FOUND)
- set(RPCS3_QT_LIBS Qt5::Widgets Qt5::DBus)
+ set(RPCS3_QT_LIBS Qt5::Widgets Qt5::DBus Qt5::Network)
add_definitions(-DHAVE_QTDBUS)
else()
- set(RPCS3_QT_LIBS Qt5::Widgets)
+ set(RPCS3_QT_LIBS Qt5::Widgets Qt5::Network)
endif()
endif()
diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj
index 60c435f72c..d6e572dec9 100644
--- a/rpcs3/rpcs3.vcxproj
+++ b/rpcs3/rpcs3.vcxproj
@@ -376,6 +376,11 @@
true
true
+
+ true
+ true
+ true
+
true
true
@@ -511,6 +516,11 @@
true
true
+
+ true
+ true
+ true
+
true
true
@@ -656,6 +666,11 @@
true
true
+
+ true
+ true
+ true
+
true
true
@@ -791,6 +806,11 @@
true
true
+
+ true
+ true
+ true
+
true
true
@@ -893,6 +913,7 @@
+
@@ -1266,6 +1287,24 @@
"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"
+
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing game_compatibility.h...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing game_compatibility.h...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing game_compatibility.h...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing game_compatibility.h...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"
+
diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters
index f630997167..ab4e7c5f49 100644
--- a/rpcs3/rpcs3.vcxproj.filters
+++ b/rpcs3/rpcs3.vcxproj.filters
@@ -569,6 +569,21 @@
Gui\misc dialogs
+
+ Generated Files\Release - LLVM
+
+
+ Generated Files\Debug
+
+
+ Generated Files\Release
+
+
+ Generated Files\Debug - LLVM
+
+
+ Gui\game list
+
@@ -762,6 +777,9 @@
Gui\misc dialogs
+
+ Gui\game list
+
diff --git a/rpcs3/rpcs3qt/game_compatibility.cpp b/rpcs3/rpcs3qt/game_compatibility.cpp
new file mode 100644
index 0000000000..30725d9f71
--- /dev/null
+++ b/rpcs3/rpcs3qt/game_compatibility.cpp
@@ -0,0 +1,240 @@
+#include "game_compatibility.h"
+
+#include
+
+constexpr auto qstr = QString::fromStdString;
+inline std::string sstr(const QString& _in) { return _in.toStdString(); }
+
+game_compatibility::game_compatibility(std::shared_ptr settings) : m_xgui_settings(settings)
+{
+ m_filepath = m_xgui_settings->GetSettingsDir() + "/compat_database.dat";
+ m_url = "https://rpcs3.net/compatibility?api=v1&export";
+ m_network_request = QNetworkRequest(QUrl(m_url));
+
+ RequestCompatibility();
+}
+
+void game_compatibility::RequestCompatibility(bool online)
+{
+ // Creates new map from database
+ auto ReadJSON = [=](const QJsonObject& json_data, bool after_download)
+ {
+ int return_code = json_data["return_code"].toInt();
+
+ if (return_code < 0)
+ {
+ if (after_download)
+ {
+ std::string error_message;
+ switch (return_code)
+ {
+ case -1:
+ error_message = "Server Error - Internal Error";
+ break;
+ case -2:
+ error_message = "Server Error - Maintenance Mode";
+ break;
+ default:
+ error_message = "Server Error - Unknown Error";
+ break;
+ }
+ LOG_ERROR(GENERAL, "Compatibility error: { %s: return code %d }", error_message, return_code);
+ Q_EMIT DownloadError(qstr(error_message) + " " + QString::number(return_code));
+ }
+ else
+ {
+ LOG_ERROR(GENERAL, "Compatibility error: { Database Error - Invalid: return code %d }", return_code);
+ }
+ return false;
+ }
+
+ if (!json_data["results"].isObject())
+ {
+ LOG_ERROR(GENERAL, "Compatibility error: { Database Error - No Results found }");
+ return false;
+ }
+
+ m_compat_database.clear();
+
+ QJsonObject json_results = json_data["results"].toObject();
+
+ // Retrieve status data for every valid entry
+ for (const auto& key : json_results.keys())
+ {
+ if (!json_results[key].isObject())
+ {
+ LOG_ERROR(GENERAL, "Compatibility error: { Database Error - Unusable object %s }", sstr(key));
+ continue;
+ }
+
+ QJsonObject json_result = json_results[key].toObject();
+
+ // Retrieve compatibility information from json
+ Compat_Status compat_status = Status_Data.at(json_result.value("status").toString("NoResult"));
+
+ // Add date if possible
+ compat_status.date = json_result.value("date").toString();
+
+ // Add status to map
+ m_compat_database.emplace(std::pair(sstr(key), compat_status));
+ }
+
+ return true;
+ };
+
+ if (!online)
+ {
+ // Retrieve database from file
+ QFile file(m_filepath);
+
+ if (!file.exists())
+ {
+ LOG_NOTICE(GENERAL, "Compatibility notice: { Database file not found: %s }", sstr(m_filepath));
+ return;
+ }
+
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ LOG_ERROR(GENERAL, "Compatibility error: { Database Error - Could not read database from file: %s }", sstr(m_filepath));
+ return;
+ }
+
+ QByteArray data = file.readAll();
+ file.close();
+
+ LOG_NOTICE(GENERAL, "Compatibility notice: { Finished reading database from file: %s }", sstr(m_filepath));
+
+ // Create new map from database
+ ReadJSON(QJsonDocument::fromJson(data).object(), online);
+
+ return;
+ }
+
+ if (QSslSocket::supportsSsl() == false)
+ {
+ LOG_ERROR(GENERAL, "Can not retrieve the online database! Please make sure your system supports SSL.");
+ QMessageBox::warning(nullptr, tr("Warning!"), tr("Can not retrieve the online database! Please make sure your system supports SSL."));
+ return;
+ }
+
+ LOG_NOTICE(GENERAL, "SSL supported! Beginning compatibility database download from: %s", sstr(m_url));
+
+ // Send request and wait for response
+ m_network_access_manager.reset(new QNetworkAccessManager());
+ QNetworkReply* network_reply = m_network_access_manager->get(m_network_request);
+
+ // Show Progress
+ m_progress_dialog.reset(new QProgressDialog(tr(".Please wait."), tr("Abort"), 0, 100));
+ m_progress_dialog->setWindowTitle(tr("Downloading Database"));
+ m_progress_dialog->setFixedWidth(QLabel("This is the very length of the progressbar due to hidpi reasons.").sizeHint().width());
+ m_progress_dialog->setValue(0);
+ m_progress_dialog->show();
+
+ // Animate progress dialog a bit more
+ m_progress_timer.reset(new QTimer(this));
+ connect(m_progress_timer.get(), &QTimer::timeout, [&]()
+ {
+ switch (++m_timer_count % 3)
+ {
+ case 0:
+ m_timer_count = 0;
+ m_progress_dialog->setLabelText(tr(".Please wait."));
+ break;
+ case 1:
+ m_progress_dialog->setLabelText(tr("..Please wait.."));
+ break;
+ default:
+ m_progress_dialog->setLabelText(tr("...Please wait..."));
+ break;
+ }
+ });
+ m_progress_timer->start(500);
+
+ // Handle abort
+ connect(m_progress_dialog.get(), &QProgressDialog::rejected, network_reply, &QNetworkReply::abort);
+
+ // Handle progress
+ connect(network_reply, &QNetworkReply::downloadProgress, [&](qint64 bytesReceived, qint64 bytesTotal)
+ {
+ m_progress_dialog->setMaximum(bytesTotal);
+ m_progress_dialog->setValue(bytesReceived);
+ });
+
+ // Handle response according to its contents
+ connect(network_reply, &QNetworkReply::finished, [=]()
+ {
+ // Clean up Progress Dialog
+ if (m_progress_dialog)
+ {
+ m_progress_dialog->close();
+ }
+ if (m_progress_timer)
+ {
+ m_progress_timer->stop();
+ }
+
+ // Handle Errors
+ if (network_reply->error() != QNetworkReply::NoError)
+ {
+ // We failed to retrieve a new database, therefore refresh gamelist to old state
+ QString error = network_reply->errorString();
+ Q_EMIT DownloadError(error);
+ LOG_ERROR(GENERAL, "Compatibility error: { Network Error - %s }", sstr(error));
+ return;
+ }
+
+ LOG_NOTICE(GENERAL, "Compatibility notice: { Database download finished }");
+
+ // Read data from network reply
+ QByteArray data = network_reply->readAll();
+ network_reply->deleteLater();
+
+ // Create new map from database and write database to file if database was valid
+ if (ReadJSON(QJsonDocument::fromJson(data).object(), online))
+ {
+ // We have a new database in map, therefore refresh gamelist to new state
+ Q_EMIT DownloadFinished();
+
+ // Write database to file
+ QFile file(m_filepath);
+
+ if (file.exists())
+ {
+ LOG_NOTICE(GENERAL, "Compatibility notice: { Database file found: %s }", sstr(m_filepath));
+ }
+
+ if (!file.open(QIODevice::WriteOnly))
+ {
+ LOG_ERROR(GENERAL, "Compatibility error: { Database Error - Could not write database to file: %s }", sstr(m_filepath));
+ return;
+ }
+
+ file.write(data);
+ file.close();
+
+ LOG_SUCCESS(GENERAL, "Compatibility success: { Write database to file: %s }", sstr(m_filepath));
+ }
+ });
+
+ // We want to retrieve a new database, therefore refresh gamelist and indicate that
+ Q_EMIT DownloadStarted();
+}
+
+Compat_Status game_compatibility::GetCompatibility(const std::string& title_id)
+{
+ if (m_compat_database.empty())
+ {
+ return Status_Data.at("NoData");
+ }
+ else if (m_compat_database.count(title_id) > 0)
+ {
+ return m_compat_database[title_id];
+ }
+
+ return Status_Data.at("NoResult");
+}
+
+Compat_Status game_compatibility::GetStatusData(const QString& status)
+{
+ return Status_Data.at(status);
+}
diff --git a/rpcs3/rpcs3qt/game_compatibility.h b/rpcs3/rpcs3qt/game_compatibility.h
new file mode 100644
index 0000000000..12f9dea357
--- /dev/null
+++ b/rpcs3/rpcs3qt/game_compatibility.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "gui_settings.h"
+
+class game_compatibility : public QObject
+{
+ Q_OBJECT
+
+ const std::map Status_Data =
+ {
+ { "Playable", { "", "#2ecc71", QObject::tr("Playable"), QObject::tr("Games that can be properly played from start to finish") } },
+ { "Ingame", { "", "#f1c40f", QObject::tr("Ingame"), QObject::tr("Games that go somewhere but not far enough to be considered playable") } },
+ { "Intro", { "", "#f39c12", QObject::tr("Intro"), QObject::tr("Games that only display some screens") } },
+ { "Loadable", { "", "#e74c3c", QObject::tr("Loadable"), QObject::tr("Games that display a black screen with an active framerate") } },
+ { "Nothing", { "", "#2c3e50", QObject::tr("Nothing"), QObject::tr("Games that show nothing") } },
+ { "NoResult", { "", "", QObject::tr("No results found"), QObject::tr("There is no entry for this game or application in the compatibility database yet.") } },
+ { "NoData", { "", "", QObject::tr("Database missing"), QObject::tr("Right click here and download the current database.\nMake sure you are connected to the internet.") } },
+ { "Download", { "", "", QObject::tr("Retrieving..."), QObject::tr("Downloading the compatibility database. Please wait...") } }
+ };
+ int m_timer_count = 0;
+ QString m_filepath;
+ QString m_url;
+ QNetworkRequest m_network_request;
+ std::shared_ptr m_xgui_settings;
+ std::unique_ptr m_progress_timer;
+ std::unique_ptr m_progress_dialog;
+ std::unique_ptr m_network_access_manager;
+ std::map m_compat_database;
+
+public:
+ /** Handles reads, writes and downloads for the compatibility database */
+ game_compatibility(std::shared_ptr settings);
+
+ /** Reads database. If online set to true: Downloads and writes the database to file */
+ void RequestCompatibility(bool online = false);
+
+ /** Returns the compatibility status for the requested title */
+ Compat_Status GetCompatibility(const std::string& title_id);
+
+ /** Returns the data for the requested status */
+ Compat_Status GetStatusData(const QString& status);
+
+Q_SIGNALS:
+ void DownloadStarted();
+ void DownloadFinished();
+ void DownloadError(const QString& error);
+};
+
+class compat_pixmap : public QPixmap
+{
+public:
+ compat_pixmap(const QColor& color) : QPixmap(16, 16)
+ {
+ fill(Qt::transparent);
+
+ QPainter painter(this);
+ painter.setPen(color);
+ painter.setBrush(color);
+ painter.drawEllipse(0, 0, 15, 15);
+ }
+};
diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp
index f2f80ad43a..a8b440ecd5 100644
--- a/rpcs3/rpcs3qt/game_list_frame.cpp
+++ b/rpcs3/rpcs3qt/game_list_frame.cpp
@@ -172,6 +172,7 @@ game_list_frame::game_list_frame(std::shared_ptr guiSettings, std:
m_gameList->setHorizontalHeaderItem(gui::column_resolution, new QTableWidgetItem(tr("Supported Resolutions")));
m_gameList->setHorizontalHeaderItem(gui::column_sound, new QTableWidgetItem(tr("Sound Formats")));
m_gameList->setHorizontalHeaderItem(gui::column_parental, new QTableWidgetItem(tr("Parental Level")));
+ m_gameList->setHorizontalHeaderItem(gui::column_compat, new QTableWidgetItem(tr("Compatibility")));
// since this won't work somehow: gameList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
for (int i = 0; i < m_gameList->horizontalHeader()->count(); i++)
@@ -179,6 +180,8 @@ game_list_frame::game_list_frame(std::shared_ptr guiSettings, std:
m_gameList->horizontalHeaderItem(i)->setTextAlignment(Qt::AlignLeft);
}
+ m_game_compat = std::make_unique(xgui_settings);
+
m_Central_Widget = new QStackedWidget(this);
m_Central_Widget->addWidget(m_gameList);
m_Central_Widget->addWidget(m_xgrid);
@@ -197,9 +200,10 @@ game_list_frame::game_list_frame(std::shared_ptr guiSettings, std:
QAction* showResolutionColAct = new QAction(tr("Show Supported Resolutions"), this);
QAction* showSoundFormatColAct = new QAction(tr("Show Sound Formats"), this);
QAction* showParentalLevelColAct = new QAction(tr("Show Parental Levels"), this);
+ QAction* showCompatibilityAct = new QAction(tr("Show Compatibilities"), this);
m_columnActs = { showIconColAct, showNameColAct, showSerialColAct, showFWColAct, showAppVersionColAct, showCategoryColAct, showPathColAct,
- showResolutionColAct, showSoundFormatColAct, showParentalLevelColAct };
+ showResolutionColAct, showSoundFormatColAct, showParentalLevelColAct, showCompatibilityAct };
// Events
connect(m_gameList, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu);
@@ -216,10 +220,39 @@ game_list_frame::game_list_frame(std::shared_ptr guiSettings, std:
connect(m_xgrid, &QTableWidget::doubleClicked, this, &game_list_frame::doubleClickedSlot);
connect(m_xgrid, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu);
+ connect(m_game_compat.get(), &game_compatibility::DownloadStarted, [=]()
+ {
+ for (auto& game : m_game_data)
+ {
+ game.compat = m_game_compat->GetStatusData("Download");
+ }
+ Refresh();
+ });
+ connect(m_game_compat.get(), &game_compatibility::DownloadFinished, [=]()
+ {
+ for (auto& game : m_game_data)
+ {
+ game.compat = m_game_compat->GetCompatibility(game.info.serial);
+ }
+ Refresh();
+ });
+ connect(m_game_compat.get(), &game_compatibility::DownloadError, [=](const QString& error)
+ {
+ for (auto& game : m_game_data)
+ {
+ game.compat = m_game_compat->GetCompatibility(game.info.serial);
+ }
+ Refresh();
+ QMessageBox::warning(this, tr("Warning!"), tr("Failed to retrieve the online compatibility database!\nFalling back to local database.\n\n") + tr(qPrintable(error)));
+ });
+
connect(m_Search_Bar, &QLineEdit::textChanged, this, &game_list_frame::SetSearchText);
connect(m_Slider_Size, &QSlider::valueChanged, this, &game_list_frame::RequestIconSizeActSet);
- connect(m_Slider_Size, &QSlider::sliderReleased, this, [&]{ xgui_settings->SetValue(gui::gl_iconSize, m_Slider_Size->value()); });
+ connect(m_Slider_Size, &QSlider::sliderReleased, this, [&]
+ {
+ xgui_settings->SetValue(gui::gl_iconSize, m_Slider_Size->value());
+ });
connect(m_Slider_Size, &QSlider::actionTriggered, [&](int action)
{
if (action != QAbstractSlider::SliderNoAction && action != QAbstractSlider::SliderMove)
@@ -457,7 +490,7 @@ void game_list_frame::Refresh(const bool fromDrive, const bool scrollAfter)
QPixmap pxmap = PaintedPixmap(img, hasCustomConfig);
- m_game_data.push_back({ game, img, pxmap, true, bootable, hasCustomConfig });
+ m_game_data.push_back({ game, m_game_compat->GetCompatibility(game.serial), img, pxmap, true, bootable, hasCustomConfig });
}
auto op = [](const GUI_GameInfo& game1, const GUI_GameInfo& game2)
@@ -638,6 +671,7 @@ void game_list_frame::ShowSpecifiedContextMenu(const QPoint &pos, int row)
QAction* openConfig = myMenu.addAction(tr("&Open Config Folder"));
myMenu.addSeparator();
QAction* checkCompat = myMenu.addAction(tr("&Check Game Compatibility"));
+ QAction* downloadCompat = myMenu.addAction(tr("&Download Compatibility Database"));
connect(boot, &QAction::triggered, [=]
{
@@ -711,6 +745,10 @@ void game_list_frame::ShowSpecifiedContextMenu(const QPoint &pos, int row)
QString link = "https://rpcs3.net/compatibility?g=" + qstr(currGame.serial);
QDesktopServices::openUrl(QUrl(link));
});
+ connect(downloadCompat, &QAction::triggered, [=]
+ {
+ m_game_compat->RequestCompatibility(true);
+ });
//Disable options depending on software category
QString category = qstr(currGame.category);
@@ -1054,6 +1092,15 @@ int game_list_frame::PopulateGameList()
title_item->setIcon(QIcon(":/Icons/cog_black.png"));
}
+ // Compatibility
+ QTableWidgetItem* compat_item = new QTableWidgetItem;
+ compat_item->setFlags(compat_item->flags() & ~Qt::ItemIsEditable);
+ compat_item->setText(game.compat.text + (game.compat.date.isEmpty() ? "" : " (" + game.compat.date + ")"));
+ compat_item->setToolTip(game.compat.tooltip);
+ if (!game.compat.color.isEmpty())
+ {
+ compat_item->setData(Qt::DecorationRole, compat_pixmap(game.compat.color));
+ }
m_gameList->setItem(row, gui::column_icon, icon_item);
m_gameList->setItem(row, gui::column_name, title_item);
m_gameList->setItem(row, gui::column_serial, l_GetItem(game.info.serial));
@@ -1064,8 +1111,12 @@ int game_list_frame::PopulateGameList()
m_gameList->setItem(row, gui::column_resolution, l_GetItem(GetStringFromU32(game.info.resolution, resolution::mode, true)));
m_gameList->setItem(row, gui::column_sound, l_GetItem(GetStringFromU32(game.info.sound_format, sound::format, true)));
m_gameList->setItem(row, gui::column_parental, l_GetItem(GetStringFromU32(game.info.parental_lvl, parental::level)));
+ m_gameList->setItem(row, gui::column_compat, compat_item);
- if (selected_item == game.info.icon_path) result = row;
+ if (selected_item == game.info.icon_path)
+ {
+ result = row;
+ }
row++;
}
diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h
index bb46d737f8..97c9fb278e 100644
--- a/rpcs3/rpcs3qt/game_list_frame.h
+++ b/rpcs3/rpcs3qt/game_list_frame.h
@@ -5,8 +5,8 @@
#include "game_list.h"
#include "game_list_grid.h"
-#include "gui_settings.h"
#include "emu_settings.h"
+#include "game_compatibility.h"
#include
#include
@@ -163,6 +163,7 @@ namespace sound
struct GUI_GameInfo
{
GameInfo info;
+ Compat_Status compat;
QImage icon;
QPixmap pxmap;
bool isVisible;
@@ -259,6 +260,7 @@ private:
// Game List
game_list* m_gameList;
+ std::unique_ptr m_game_compat;
QList m_columnActs;
Qt::SortOrder m_colSortOrder;
int m_sortColumn;
diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h
index 2fd81dc837..364a3b0e6b 100644
--- a/rpcs3/rpcs3qt/gui_settings.h
+++ b/rpcs3/rpcs3qt/gui_settings.h
@@ -10,6 +10,14 @@
#include
#include
+struct Compat_Status
+{
+ QString date;
+ QString color;
+ QString text;
+ QString tooltip;
+};
+
struct gui_save
{
QString key;
@@ -53,6 +61,7 @@ namespace gui
column_resolution,
column_sound,
column_parental,
+ column_compat,
column_count
};