diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c78b70aac..67205126af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) message(STATUS "Downloading compatibility list for citra...") 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) endif() diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index 3043db2c1f..aa08bcf597 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -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_application_location = context_menu.addAction(tr("Open Application 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_application_location->setVisible(FileUtil::Exists( Service::AM::GetTitleContentPath(Service::FS::MediaType::SDMC, program_id))); open_update_location->setEnabled(0x00040000'00000000 <= program_id && program_id <= 0x00040000'FFFFFFFF); + auto it = std::find_if( + compatibility_list.begin(), compatibility_list.end(), + [program_id](const std::pair>& 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, [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SAVE_DATA); }); @@ -338,6 +347,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::APPLICATION); }); connect(open_update_location, &QAction::triggered, [&]() { 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)); } @@ -363,14 +374,23 @@ void GameList::LoadCompatibilityList() { const QString string_content = content; QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8()); - QJsonObject list = json.object(); - QStringList game_ids = list.keys(); - for (QString id : game_ids) { - QJsonObject game = list[id].toObject(); + QJsonArray arr = json.array(); - if (game.contains("compatibility") && game["compatibility"].isString()) { - QString compatibility = game["compatibility"].toString(); - compatibility_list.insert(std::make_pair(id.toUpper().toStdString(), compatibility)); + for (const QJsonValue& value : arr) { + QJsonObject game = value.toObject(); + + 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; }(); - auto it = std::find_if(compatibility_list.begin(), compatibility_list.end(), - [program_id](const std::pair& element) { - std::string pid = - Common::StringFromFormat("%016" PRIX64, program_id); - return element.first == pid; - }); + auto it = std::find_if( + compatibility_list.begin(), compatibility_list.end(), + [program_id](const std::pair>& element) { + std::string pid = Common::StringFromFormat("%016" PRIX64, program_id); + return element.first == pid; + }); // The game list uses this as compatibility number for untested games QString compatibility("99"); if (it != compatibility_list.end()) - compatibility = it->second; + compatibility = it->second.first; emit EntryReady({ new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id), diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h index 3fabd50883..91a0997dcf 100644 --- a/src/citra_qt/game_list.h +++ b/src/citra_qt/game_list.h @@ -85,6 +85,9 @@ signals: void GameChosen(QString game_path); void ShouldCancelWorker(); void OpenFolderRequested(u64 program_id, GameListOpenTarget target); + void NavigateToGamedbEntryRequested( + u64 program_id, + std::unordered_map>& compatibility_list); private slots: void onTextChanged(const QString& newText); @@ -106,7 +109,7 @@ private: QStandardItemModel* item_model = nullptr; GameListWorker* current_worker = nullptr; QFileSystemWatcher* watcher = nullptr; - std::unordered_map compatibility_list; + std::unordered_map> compatibility_list; }; Q_DECLARE_METATYPE(GameListOpenTarget); diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h index 5e2134ae5b..d39281f596 100644 --- a/src/citra_qt/game_list_p.h +++ b/src/citra_qt/game_list_p.h @@ -260,8 +260,9 @@ class GameListWorker : public QObject, public QRunnable { Q_OBJECT public: - GameListWorker(QString dir_path, bool deep_scan, - const std::unordered_map& compatibility_list) + GameListWorker( + QString dir_path, bool deep_scan, + const std::unordered_map>& compatibility_list) : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan), compatibility_list(compatibility_list) {} @@ -289,7 +290,7 @@ private: QStringList watch_list; QString dir_path; bool deep_scan; - const std::unordered_map& compatibility_list; + const std::unordered_map>& compatibility_list; std::atomic_bool stop_processing; void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index be70467677..029c6459c1 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -7,6 +7,7 @@ #include #include #define QT_NO_OPENGL +#include #include #include #include @@ -397,6 +398,8 @@ void GMainWindow::RestoreUIState() { void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); + connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, + &GMainWindow::OnGameListNavigateToGamedbEntry); connect(this, &GMainWindow::EmulationStarting, render_window, &GRenderWindow::OnEmulationStarting); @@ -802,6 +805,25 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); } +void GMainWindow::OnGameListNavigateToGamedbEntry( + u64 program_id, + std::unordered_map>& compatibility_list) { + + auto it = std::find_if( + compatibility_list.begin(), compatibility_list.end(), + [program_id](const std::pair>& 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() { QString extensions; for (const auto& piece : game_list->supported_file_extensions) diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 686624603f..4ec33cc0dc 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -145,6 +145,9 @@ private slots: /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); + void OnGameListNavigateToGamedbEntry( + u64 program_id, + std::unordered_map>& compatibility_list); void OnMenuLoadFile(); void OnMenuInstallCIA(); void OnUpdateProgress(size_t written, size_t total);