Merge pull request #3730 from FearlessTobi/game-compat-fixes

citra_qt: Refactor game list compatibility system
This commit is contained in:
James Rowe 2018-05-11 11:59:53 -06:00 committed by GitHub
commit 80bfd87270
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 19 deletions

View File

@ -48,7 +48,7 @@ configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qr
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
message(STATUS "Downloading compatibility list for citra...") message(STATUS "Downloading compatibility list for citra...")
file(DOWNLOAD file(DOWNLOAD
https://api.citra-emu.org/gamedb/titleid/ https://api.citra-emu.org/gamedb/
"${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS) "${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
endif() endif()

View File

@ -8,6 +8,7 @@
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QHeaderView> #include <QHeaderView>
#include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QKeyEvent> #include <QKeyEvent>
@ -325,12 +326,20 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
QAction* open_application_location = context_menu.addAction(tr("Open Application Location")); QAction* open_application_location = context_menu.addAction(tr("Open Application Location"));
QAction* open_update_location = context_menu.addAction(tr("Open Update Data Location")); QAction* open_update_location = context_menu.addAction(tr("Open Update Data Location"));
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
open_save_location->setEnabled(program_id != 0); open_save_location->setEnabled(program_id != 0);
open_application_location->setVisible(FileUtil::Exists( open_application_location->setVisible(FileUtil::Exists(
Service::AM::GetTitleContentPath(Service::FS::MediaType::SDMC, program_id))); Service::AM::GetTitleContentPath(Service::FS::MediaType::SDMC, program_id)));
open_update_location->setEnabled(0x00040000'00000000 <= program_id && open_update_location->setEnabled(0x00040000'00000000 <= program_id &&
program_id <= 0x00040000'FFFFFFFF); program_id <= 0x00040000'FFFFFFFF);
auto it = std::find_if(
compatibility_list.begin(), compatibility_list.end(),
[program_id](const std::pair<std::string, std::pair<QString, QString>>& element) {
std::string pid = Common::StringFromFormat("%016" PRIX64, program_id);
return element.first == pid;
});
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end());
connect(open_save_location, &QAction::triggered, connect(open_save_location, &QAction::triggered,
[&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SAVE_DATA); }); [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SAVE_DATA); });
@ -338,6 +347,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
[&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::APPLICATION); }); [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::APPLICATION); });
connect(open_update_location, &QAction::triggered, connect(open_update_location, &QAction::triggered,
[&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::UPDATE_DATA); }); [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::UPDATE_DATA); });
connect(navigate_to_gamedb_entry, &QAction::triggered,
[&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); });
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
} }
@ -363,14 +374,23 @@ void GameList::LoadCompatibilityList() {
const QString string_content = content; const QString string_content = content;
QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8()); QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8());
QJsonObject list = json.object(); QJsonArray arr = json.array();
QStringList game_ids = list.keys();
for (QString id : game_ids) {
QJsonObject game = list[id].toObject();
if (game.contains("compatibility") && game["compatibility"].isString()) { for (const QJsonValue& value : arr) {
QString compatibility = game["compatibility"].toString(); QJsonObject game = value.toObject();
compatibility_list.insert(std::make_pair(id.toUpper().toStdString(), compatibility));
if (game.contains("compatibility") && game["compatibility"].isDouble()) {
int compatibility = game["compatibility"].toInt();
QString directory = game["directory"].toString();
QJsonArray ids = game["releases"].toArray();
for (const QJsonValue& value : ids) {
QJsonObject object = value.toObject();
QString id = object["id"].toString();
compatibility_list.insert(
std::make_pair(id.toUpper().toStdString(),
std::make_pair(QString::number(compatibility), directory)));
}
} }
} }
} }
@ -478,17 +498,17 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
return update_smdh; return update_smdh;
}(); }();
auto it = std::find_if(compatibility_list.begin(), compatibility_list.end(), auto it = std::find_if(
[program_id](const std::pair<std::string, QString>& element) { compatibility_list.begin(), compatibility_list.end(),
std::string pid = [program_id](const std::pair<std::string, std::pair<QString, QString>>& element) {
Common::StringFromFormat("%016" PRIX64, program_id); std::string pid = Common::StringFromFormat("%016" PRIX64, program_id);
return element.first == pid; return element.first == pid;
}); });
// The game list uses this as compatibility number for untested games // The game list uses this as compatibility number for untested games
QString compatibility("99"); QString compatibility("99");
if (it != compatibility_list.end()) if (it != compatibility_list.end())
compatibility = it->second; compatibility = it->second.first;
emit EntryReady({ emit EntryReady({
new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id), new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id),

View File

@ -85,6 +85,9 @@ signals:
void GameChosen(QString game_path); void GameChosen(QString game_path);
void ShouldCancelWorker(); void ShouldCancelWorker();
void OpenFolderRequested(u64 program_id, GameListOpenTarget target); void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
void NavigateToGamedbEntryRequested(
u64 program_id,
std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
private slots: private slots:
void onTextChanged(const QString& newText); void onTextChanged(const QString& newText);
@ -106,7 +109,7 @@ private:
QStandardItemModel* item_model = nullptr; QStandardItemModel* item_model = nullptr;
GameListWorker* current_worker = nullptr; GameListWorker* current_worker = nullptr;
QFileSystemWatcher* watcher = nullptr; QFileSystemWatcher* watcher = nullptr;
std::unordered_map<std::string, QString> compatibility_list; std::unordered_map<std::string, std::pair<QString, QString>> compatibility_list;
}; };
Q_DECLARE_METATYPE(GameListOpenTarget); Q_DECLARE_METATYPE(GameListOpenTarget);

View File

@ -260,8 +260,9 @@ class GameListWorker : public QObject, public QRunnable {
Q_OBJECT Q_OBJECT
public: public:
GameListWorker(QString dir_path, bool deep_scan, GameListWorker(
const std::unordered_map<std::string, QString>& compatibility_list) QString dir_path, bool deep_scan,
const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list)
: QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan), : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan),
compatibility_list(compatibility_list) {} compatibility_list(compatibility_list) {}
@ -289,7 +290,7 @@ private:
QStringList watch_list; QStringList watch_list;
QString dir_path; QString dir_path;
bool deep_scan; bool deep_scan;
const std::unordered_map<std::string, QString>& compatibility_list; const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list;
std::atomic_bool stop_processing; std::atomic_bool stop_processing;
void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);

View File

@ -7,6 +7,7 @@
#include <thread> #include <thread>
#include <glad/glad.h> #include <glad/glad.h>
#define QT_NO_OPENGL #define QT_NO_OPENGL
#include <cinttypes>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QFileDialog> #include <QFileDialog>
#include <QFutureWatcher> #include <QFutureWatcher>
@ -399,6 +400,8 @@ void GMainWindow::RestoreUIState() {
void GMainWindow::ConnectWidgetEvents() { void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile);
connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
&GMainWindow::OnGameListNavigateToGamedbEntry);
connect(this, &GMainWindow::EmulationStarting, render_window, connect(this, &GMainWindow::EmulationStarting, render_window,
&GRenderWindow::OnEmulationStarting); &GRenderWindow::OnEmulationStarting);
@ -806,6 +809,25 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
} }
void GMainWindow::OnGameListNavigateToGamedbEntry(
u64 program_id,
std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) {
auto it = std::find_if(
compatibility_list.begin(), compatibility_list.end(),
[program_id](const std::pair<std::string, std::pair<QString, QString>>& element) {
std::string pid = Common::StringFromFormat("%016" PRIX64, program_id);
return element.first == pid;
});
QString directory = "";
if (it != compatibility_list.end())
directory = it->second.second;
QDesktopServices::openUrl(QUrl("https://citra-emu.org/game/" + directory));
}
void GMainWindow::OnMenuLoadFile() { void GMainWindow::OnMenuLoadFile() {
QString extensions; QString extensions;
for (const auto& piece : game_list->supported_file_extensions) for (const auto& piece : game_list->supported_file_extensions)

View File

@ -145,6 +145,9 @@ private slots:
/// Called whenever a user selects a game in the game list widget. /// Called whenever a user selects a game in the game list widget.
void OnGameListLoadFile(QString game_path); void OnGameListLoadFile(QString game_path);
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
void OnGameListNavigateToGamedbEntry(
u64 program_id,
std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
void OnMenuLoadFile(); void OnMenuLoadFile();
void OnMenuInstallCIA(); void OnMenuInstallCIA();
void OnUpdateProgress(size_t written, size_t total); void OnUpdateProgress(size_t written, size_t total);