diff options
| author | 2018-08-30 21:34:43 -0400 | |
|---|---|---|
| committer | 2018-08-30 21:34:43 -0400 | |
| commit | 26aaa86ece9cf96b477e0755f5ee7ebe471ef85a (patch) | |
| tree | 792b04795f475dbb86b6d678533f4703e09a60d4 /src | |
| parent | gl_shader_decompiler: Implement POPC (#1203) (diff) | |
| parent | Show game compatibility within yuzu (diff) | |
| download | yuzu-26aaa86ece9cf96b477e0755f5ee7ebe471ef85a.tar.gz yuzu-26aaa86ece9cf96b477e0755f5ee7ebe471ef85a.tar.xz yuzu-26aaa86ece9cf96b477e0755f5ee7ebe471ef85a.zip | |
Merge pull request #1195 from FearlessTobi/port-gamelist-compat
yuzu: Show game compatibility in the game list (PR ported from Citra)
Diffstat (limited to 'src')
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/yuzu/game_list.cpp | 65 | ||||
| -rw-r--r-- | src/yuzu/game_list.h | 6 | ||||
| -rw-r--r-- | src/yuzu/game_list_p.h | 62 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 19 | ||||
| -rw-r--r-- | src/yuzu/main.h | 3 | ||||
| -rw-r--r-- | src/yuzu/util/util.cpp | 11 | ||||
| -rw-r--r-- | src/yuzu/util/util.h | 7 |
8 files changed, 174 insertions, 3 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 46ed232d8..ea9ea69e4 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -70,6 +70,9 @@ set(UIS | |||
| 70 | main.ui | 70 | main.ui |
| 71 | ) | 71 | ) |
| 72 | 72 | ||
| 73 | file(GLOB COMPAT_LIST | ||
| 74 | ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc | ||
| 75 | ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) | ||
| 73 | file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*) | 76 | file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*) |
| 74 | file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) | 77 | file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) |
| 75 | 78 | ||
| @@ -77,6 +80,7 @@ qt5_wrap_ui(UI_HDRS ${UIS}) | |||
| 77 | 80 | ||
| 78 | target_sources(yuzu | 81 | target_sources(yuzu |
| 79 | PRIVATE | 82 | PRIVATE |
| 83 | ${COMPAT_LIST} | ||
| 80 | ${ICONS} | 84 | ${ICONS} |
| 81 | ${THEMES} | 85 | ${THEMES} |
| 82 | ${UI_HDRS} | 86 | ${UI_HDRS} |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 867a3c6f1..27525938a 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -7,10 +7,14 @@ | |||
| 7 | #include <QDir> | 7 | #include <QDir> |
| 8 | #include <QFileInfo> | 8 | #include <QFileInfo> |
| 9 | #include <QHeaderView> | 9 | #include <QHeaderView> |
| 10 | #include <QJsonArray> | ||
| 11 | #include <QJsonDocument> | ||
| 12 | #include <QJsonObject> | ||
| 10 | #include <QKeyEvent> | 13 | #include <QKeyEvent> |
| 11 | #include <QMenu> | 14 | #include <QMenu> |
| 12 | #include <QThreadPool> | 15 | #include <QThreadPool> |
| 13 | #include <boost/container/flat_map.hpp> | 16 | #include <boost/container/flat_map.hpp> |
| 17 | #include <fmt/format.h> | ||
| 14 | #include "common/common_paths.h" | 18 | #include "common/common_paths.h" |
| 15 | #include "common/logging/log.h" | 19 | #include "common/logging/log.h" |
| 16 | #include "common/string_util.h" | 20 | #include "common/string_util.h" |
| @@ -224,6 +228,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) | |||
| 224 | 228 | ||
| 225 | item_model->insertColumns(0, COLUMN_COUNT); | 229 | item_model->insertColumns(0, COLUMN_COUNT); |
| 226 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); | 230 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); |
| 231 | item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); | ||
| 227 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); | 232 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); |
| 228 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); | 233 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); |
| 229 | 234 | ||
| @@ -325,12 +330,62 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { | |||
| 325 | 330 | ||
| 326 | QMenu context_menu; | 331 | QMenu context_menu; |
| 327 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); | 332 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); |
| 333 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); | ||
| 334 | |||
| 328 | open_save_location->setEnabled(program_id != 0); | 335 | open_save_location->setEnabled(program_id != 0); |
| 336 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | ||
| 337 | navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); | ||
| 338 | |||
| 329 | connect(open_save_location, &QAction::triggered, | 339 | connect(open_save_location, &QAction::triggered, |
| 330 | [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); | 340 | [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); |
| 341 | connect(navigate_to_gamedb_entry, &QAction::triggered, | ||
| 342 | [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); | ||
| 343 | |||
| 331 | context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); | 344 | context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); |
| 332 | } | 345 | } |
| 333 | 346 | ||
| 347 | void GameList::LoadCompatibilityList() { | ||
| 348 | QFile compat_list{":compatibility_list/compatibility_list.json"}; | ||
| 349 | |||
| 350 | if (!compat_list.open(QFile::ReadOnly | QFile::Text)) { | ||
| 351 | LOG_ERROR(Frontend, "Unable to open game compatibility list"); | ||
| 352 | return; | ||
| 353 | } | ||
| 354 | |||
| 355 | if (compat_list.size() == 0) { | ||
| 356 | LOG_WARNING(Frontend, "Game compatibility list is empty"); | ||
| 357 | return; | ||
| 358 | } | ||
| 359 | |||
| 360 | const QByteArray content = compat_list.readAll(); | ||
| 361 | if (content.isEmpty()) { | ||
| 362 | LOG_ERROR(Frontend, "Unable to completely read game compatibility list"); | ||
| 363 | return; | ||
| 364 | } | ||
| 365 | |||
| 366 | const QString string_content = content; | ||
| 367 | QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8()); | ||
| 368 | QJsonArray arr = json.array(); | ||
| 369 | |||
| 370 | for (const QJsonValue& value : arr) { | ||
| 371 | QJsonObject game = value.toObject(); | ||
| 372 | |||
| 373 | if (game.contains("compatibility") && game["compatibility"].isDouble()) { | ||
| 374 | int compatibility = game["compatibility"].toInt(); | ||
| 375 | QString directory = game["directory"].toString(); | ||
| 376 | QJsonArray ids = game["releases"].toArray(); | ||
| 377 | |||
| 378 | for (const QJsonValue& value : ids) { | ||
| 379 | QJsonObject object = value.toObject(); | ||
| 380 | QString id = object["id"].toString(); | ||
| 381 | compatibility_list.emplace( | ||
| 382 | id.toUpper().toStdString(), | ||
| 383 | std::make_pair(QString::number(compatibility), directory)); | ||
| 384 | } | ||
| 385 | } | ||
| 386 | } | ||
| 387 | } | ||
| 388 | |||
| 334 | void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { | 389 | void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { |
| 335 | if (!FileUtil::Exists(dir_path.toStdString()) || | 390 | if (!FileUtil::Exists(dir_path.toStdString()) || |
| 336 | !FileUtil::IsDirectory(dir_path.toStdString())) { | 391 | !FileUtil::IsDirectory(dir_path.toStdString())) { |
| @@ -345,7 +400,7 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { | |||
| 345 | 400 | ||
| 346 | emit ShouldCancelWorker(); | 401 | emit ShouldCancelWorker(); |
| 347 | 402 | ||
| 348 | GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan); | 403 | GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan, compatibility_list); |
| 349 | 404 | ||
| 350 | connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); | 405 | connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); |
| 351 | connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, | 406 | connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, |
| @@ -523,11 +578,19 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||
| 523 | } | 578 | } |
| 524 | } | 579 | } |
| 525 | 580 | ||
| 581 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | ||
| 582 | |||
| 583 | // The game list uses this as compatibility number for untested games | ||
| 584 | QString compatibility("99"); | ||
| 585 | if (it != compatibility_list.end()) | ||
| 586 | compatibility = it->second.first; | ||
| 587 | |||
| 526 | emit EntryReady({ | 588 | emit EntryReady({ |
| 527 | new GameListItemPath( | 589 | new GameListItemPath( |
| 528 | FormatGameName(physical_name), icon, QString::fromStdString(name), | 590 | FormatGameName(physical_name), icon, QString::fromStdString(name), |
| 529 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), | 591 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), |
| 530 | program_id), | 592 | program_id), |
| 593 | new GameListItemCompat(compatibility), | ||
| 531 | new GameListItem( | 594 | new GameListItem( |
| 532 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), | 595 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), |
| 533 | new GameListItemSize(FileUtil::GetSize(physical_name)), | 596 | new GameListItemSize(FileUtil::GetSize(physical_name)), |
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 20252e778..c01351dc9 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -29,6 +29,7 @@ class GameList : public QWidget { | |||
| 29 | public: | 29 | public: |
| 30 | enum { | 30 | enum { |
| 31 | COLUMN_NAME, | 31 | COLUMN_NAME, |
| 32 | COLUMN_COMPATIBILITY, | ||
| 32 | COLUMN_FILE_TYPE, | 33 | COLUMN_FILE_TYPE, |
| 33 | COLUMN_SIZE, | 34 | COLUMN_SIZE, |
| 34 | COLUMN_COUNT, // Number of columns | 35 | COLUMN_COUNT, // Number of columns |
| @@ -68,6 +69,7 @@ public: | |||
| 68 | void setFilterFocus(); | 69 | void setFilterFocus(); |
| 69 | void setFilterVisible(bool visibility); | 70 | void setFilterVisible(bool visibility); |
| 70 | 71 | ||
| 72 | void LoadCompatibilityList(); | ||
| 71 | void PopulateAsync(const QString& dir_path, bool deep_scan); | 73 | void PopulateAsync(const QString& dir_path, bool deep_scan); |
| 72 | 74 | ||
| 73 | void SaveInterfaceLayout(); | 75 | void SaveInterfaceLayout(); |
| @@ -79,6 +81,9 @@ signals: | |||
| 79 | void GameChosen(QString game_path); | 81 | void GameChosen(QString game_path); |
| 80 | void ShouldCancelWorker(); | 82 | void ShouldCancelWorker(); |
| 81 | void OpenFolderRequested(u64 program_id, GameListOpenTarget target); | 83 | void OpenFolderRequested(u64 program_id, GameListOpenTarget target); |
| 84 | void NavigateToGamedbEntryRequested( | ||
| 85 | u64 program_id, | ||
| 86 | std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list); | ||
| 82 | 87 | ||
| 83 | private slots: | 88 | private slots: |
| 84 | void onTextChanged(const QString& newText); | 89 | void onTextChanged(const QString& newText); |
| @@ -100,6 +105,7 @@ private: | |||
| 100 | QStandardItemModel* item_model = nullptr; | 105 | QStandardItemModel* item_model = nullptr; |
| 101 | GameListWorker* current_worker = nullptr; | 106 | GameListWorker* current_worker = nullptr; |
| 102 | QFileSystemWatcher* watcher = nullptr; | 107 | QFileSystemWatcher* watcher = nullptr; |
| 108 | std::unordered_map<std::string, std::pair<QString, QString>> compatibility_list; | ||
| 103 | }; | 109 | }; |
| 104 | 110 | ||
| 105 | Q_DECLARE_METATYPE(GameListOpenTarget); | 111 | Q_DECLARE_METATYPE(GameListOpenTarget); |
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 1d6c85400..b9676d069 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h | |||
| @@ -8,11 +8,15 @@ | |||
| 8 | #include <atomic> | 8 | #include <atomic> |
| 9 | #include <map> | 9 | #include <map> |
| 10 | #include <memory> | 10 | #include <memory> |
| 11 | #include <unordered_map> | ||
| 11 | #include <utility> | 12 | #include <utility> |
| 13 | #include <QCoreApplication> | ||
| 12 | #include <QImage> | 14 | #include <QImage> |
| 15 | #include <QObject> | ||
| 13 | #include <QRunnable> | 16 | #include <QRunnable> |
| 14 | #include <QStandardItem> | 17 | #include <QStandardItem> |
| 15 | #include <QString> | 18 | #include <QString> |
| 19 | #include "common/logging/log.h" | ||
| 16 | #include "common/string_util.h" | 20 | #include "common/string_util.h" |
| 17 | #include "core/file_sys/content_archive.h" | 21 | #include "core/file_sys/content_archive.h" |
| 18 | #include "ui_settings.h" | 22 | #include "ui_settings.h" |
| @@ -29,6 +33,17 @@ static QPixmap GetDefaultIcon(u32 size) { | |||
| 29 | return icon; | 33 | return icon; |
| 30 | } | 34 | } |
| 31 | 35 | ||
| 36 | static auto FindMatchingCompatibilityEntry( | ||
| 37 | const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list, | ||
| 38 | u64 program_id) { | ||
| 39 | return std::find_if( | ||
| 40 | compatibility_list.begin(), compatibility_list.end(), | ||
| 41 | [program_id](const std::pair<std::string, std::pair<QString, QString>>& element) { | ||
| 42 | std::string pid = fmt::format("{:016X}", program_id); | ||
| 43 | return element.first == pid; | ||
| 44 | }); | ||
| 45 | } | ||
| 46 | |||
| 32 | class GameListItem : public QStandardItem { | 47 | class GameListItem : public QStandardItem { |
| 33 | 48 | ||
| 34 | public: | 49 | public: |
| @@ -96,6 +111,45 @@ public: | |||
| 96 | } | 111 | } |
| 97 | }; | 112 | }; |
| 98 | 113 | ||
| 114 | class GameListItemCompat : public GameListItem { | ||
| 115 | Q_DECLARE_TR_FUNCTIONS(GameListItemCompat) | ||
| 116 | public: | ||
| 117 | static const int CompatNumberRole = Qt::UserRole + 1; | ||
| 118 | GameListItemCompat() = default; | ||
| 119 | explicit GameListItemCompat(const QString& compatiblity) { | ||
| 120 | struct CompatStatus { | ||
| 121 | QString color; | ||
| 122 | const char* text; | ||
| 123 | const char* tooltip; | ||
| 124 | }; | ||
| 125 | // clang-format off | ||
| 126 | static const std::map<QString, CompatStatus> status_data = { | ||
| 127 | {"0", {"#5c93ed", QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}}, | ||
| 128 | {"1", {"#47d35c", QT_TR_NOOP("Great"), QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}}, | ||
| 129 | {"2", {"#94b242", QT_TR_NOOP("Okay"), QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}}, | ||
| 130 | {"3", {"#f2d624", QT_TR_NOOP("Bad"), QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}}, | ||
| 131 | {"4", {"#FF0000", QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}}, | ||
| 132 | {"5", {"#828282", QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}}, | ||
| 133 | {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; | ||
| 134 | // clang-format on | ||
| 135 | |||
| 136 | auto iterator = status_data.find(compatiblity); | ||
| 137 | if (iterator == status_data.end()) { | ||
| 138 | LOG_WARNING(Frontend, "Invalid compatibility number {}", compatiblity.toStdString()); | ||
| 139 | return; | ||
| 140 | } | ||
| 141 | CompatStatus status = iterator->second; | ||
| 142 | setData(compatiblity, CompatNumberRole); | ||
| 143 | setText(QObject::tr(status.text)); | ||
| 144 | setToolTip(QObject::tr(status.tooltip)); | ||
| 145 | setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); | ||
| 146 | } | ||
| 147 | |||
| 148 | bool operator<(const QStandardItem& other) const override { | ||
| 149 | return data(CompatNumberRole) < other.data(CompatNumberRole); | ||
| 150 | } | ||
| 151 | }; | ||
| 152 | |||
| 99 | /** | 153 | /** |
| 100 | * A specialization of GameListItem for size values. | 154 | * A specialization of GameListItem for size values. |
| 101 | * This class ensures that for every numerical size value it holds (in bytes), a correct | 155 | * This class ensures that for every numerical size value it holds (in bytes), a correct |
| @@ -141,8 +195,11 @@ class GameListWorker : public QObject, public QRunnable { | |||
| 141 | Q_OBJECT | 195 | Q_OBJECT |
| 142 | 196 | ||
| 143 | public: | 197 | public: |
| 144 | GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan) | 198 | GameListWorker( |
| 145 | : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan) {} | 199 | FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan, |
| 200 | const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) | ||
| 201 | : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan), | ||
| 202 | compatibility_list(compatibility_list) {} | ||
| 146 | 203 | ||
| 147 | public slots: | 204 | public slots: |
| 148 | /// Starts the processing of directory tree information. | 205 | /// Starts the processing of directory tree information. |
| @@ -170,6 +227,7 @@ private: | |||
| 170 | QStringList watch_list; | 227 | QStringList watch_list; |
| 171 | QString dir_path; | 228 | QString dir_path; |
| 172 | bool deep_scan; | 229 | bool deep_scan; |
| 230 | const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list; | ||
| 173 | std::atomic_bool stop_processing; | 231 | std::atomic_bool stop_processing; |
| 174 | 232 | ||
| 175 | void AddInstalledTitlesToGameList(std::shared_ptr<FileSys::RegisteredCache> cache); | 233 | void AddInstalledTitlesToGameList(std::shared_ptr<FileSys::RegisteredCache> cache); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ffa9f72aa..1501aedc4 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include <QMessageBox> | 16 | #include <QMessageBox> |
| 17 | #include <QtGui> | 17 | #include <QtGui> |
| 18 | #include <QtWidgets> | 18 | #include <QtWidgets> |
| 19 | #include <fmt/format.h> | ||
| 19 | #include "common/common_paths.h" | 20 | #include "common/common_paths.h" |
| 20 | #include "common/logging/backend.h" | 21 | #include "common/logging/backend.h" |
| 21 | #include "common/logging/filter.h" | 22 | #include "common/logging/filter.h" |
| @@ -35,6 +36,7 @@ | |||
| 35 | #include "core/gdbstub/gdbstub.h" | 36 | #include "core/gdbstub/gdbstub.h" |
| 36 | #include "core/loader/loader.h" | 37 | #include "core/loader/loader.h" |
| 37 | #include "core/settings.h" | 38 | #include "core/settings.h" |
| 39 | #include "game_list_p.h" | ||
| 38 | #include "video_core/debug_utils/debug_utils.h" | 40 | #include "video_core/debug_utils/debug_utils.h" |
| 39 | #include "yuzu/about_dialog.h" | 41 | #include "yuzu/about_dialog.h" |
| 40 | #include "yuzu/bootmanager.h" | 42 | #include "yuzu/bootmanager.h" |
| @@ -134,6 +136,7 @@ GMainWindow::GMainWindow() | |||
| 134 | 136 | ||
| 135 | // Necessary to load titles from nand in gamelist. | 137 | // Necessary to load titles from nand in gamelist. |
| 136 | Service::FileSystem::CreateFactories(vfs); | 138 | Service::FileSystem::CreateFactories(vfs); |
| 139 | game_list->LoadCompatibilityList(); | ||
| 137 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 140 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); |
| 138 | 141 | ||
| 139 | // Show one-time "callout" messages to the user | 142 | // Show one-time "callout" messages to the user |
| @@ -349,6 +352,8 @@ void GMainWindow::RestoreUIState() { | |||
| 349 | void GMainWindow::ConnectWidgetEvents() { | 352 | void GMainWindow::ConnectWidgetEvents() { |
| 350 | connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); | 353 | connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); |
| 351 | connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); | 354 | connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); |
| 355 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, | ||
| 356 | &GMainWindow::OnGameListNavigateToGamedbEntry); | ||
| 352 | 357 | ||
| 353 | connect(this, &GMainWindow::EmulationStarting, render_window, | 358 | connect(this, &GMainWindow::EmulationStarting, render_window, |
| 354 | &GRenderWindow::OnEmulationStarting); | 359 | &GRenderWindow::OnEmulationStarting); |
| @@ -678,6 +683,20 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 678 | QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); | 683 | QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); |
| 679 | } | 684 | } |
| 680 | 685 | ||
| 686 | void GMainWindow::OnGameListNavigateToGamedbEntry( | ||
| 687 | u64 program_id, | ||
| 688 | std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) { | ||
| 689 | |||
| 690 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | ||
| 691 | |||
| 692 | QString directory; | ||
| 693 | |||
| 694 | if (it != compatibility_list.end()) | ||
| 695 | directory = it->second.second; | ||
| 696 | |||
| 697 | QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory)); | ||
| 698 | } | ||
| 699 | |||
| 681 | void GMainWindow::OnMenuLoadFile() { | 700 | void GMainWindow::OnMenuLoadFile() { |
| 682 | QString extensions; | 701 | QString extensions; |
| 683 | for (const auto& piece : game_list->supported_file_extensions) | 702 | for (const auto& piece : game_list->supported_file_extensions) |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index d1d34552b..fd2436f4d 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -124,6 +124,9 @@ private slots: | |||
| 124 | /// Called whenever a user selects a game in the game list widget. | 124 | /// Called whenever a user selects a game in the game list widget. |
| 125 | void OnGameListLoadFile(QString game_path); | 125 | void OnGameListLoadFile(QString game_path); |
| 126 | void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); | 126 | void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); |
| 127 | void OnGameListNavigateToGamedbEntry( | ||
| 128 | u64 program_id, | ||
| 129 | std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list); | ||
| 127 | void OnMenuLoadFile(); | 130 | void OnMenuLoadFile(); |
| 128 | void OnMenuLoadFolder(); | 131 | void OnMenuLoadFolder(); |
| 129 | void OnMenuInstallToNAND(); | 132 | void OnMenuInstallToNAND(); |
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index 91d3f7def..e99042a23 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <cmath> | 6 | #include <cmath> |
| 7 | #include <QPainter> | ||
| 7 | #include "yuzu/util/util.h" | 8 | #include "yuzu/util/util.h" |
| 8 | 9 | ||
| 9 | QFont GetMonospaceFont() { | 10 | QFont GetMonospaceFont() { |
| @@ -24,3 +25,13 @@ QString ReadableByteSize(qulonglong size) { | |||
| 24 | .arg(size / std::pow(1024, digit_groups), 0, 'f', 1) | 25 | .arg(size / std::pow(1024, digit_groups), 0, 'f', 1) |
| 25 | .arg(units[digit_groups]); | 26 | .arg(units[digit_groups]); |
| 26 | } | 27 | } |
| 28 | |||
| 29 | QPixmap CreateCirclePixmapFromColor(const QColor& color) { | ||
| 30 | QPixmap circle_pixmap(16, 16); | ||
| 31 | circle_pixmap.fill(Qt::transparent); | ||
| 32 | QPainter painter(&circle_pixmap); | ||
| 33 | painter.setPen(color); | ||
| 34 | painter.setBrush(color); | ||
| 35 | painter.drawEllipse(0, 0, 15, 15); | ||
| 36 | return circle_pixmap; | ||
| 37 | } | ||
diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h index ab443ef9b..e6790f260 100644 --- a/src/yuzu/util/util.h +++ b/src/yuzu/util/util.h | |||
| @@ -12,3 +12,10 @@ QFont GetMonospaceFont(); | |||
| 12 | 12 | ||
| 13 | /// Convert a size in bytes into a readable format (KiB, MiB, etc.) | 13 | /// Convert a size in bytes into a readable format (KiB, MiB, etc.) |
| 14 | QString ReadableByteSize(qulonglong size); | 14 | QString ReadableByteSize(qulonglong size); |
| 15 | |||
| 16 | /** | ||
| 17 | * Creates a circle pixmap from a specified color | ||
| 18 | * @param color The color the pixmap shall have | ||
| 19 | * @return QPixmap circle pixmap | ||
| 20 | */ | ||
| 21 | QPixmap CreateCirclePixmapFromColor(const QColor& color); | ||