summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2018-08-30 21:34:43 -0400
committerGravatar GitHub2018-08-30 21:34:43 -0400
commit26aaa86ece9cf96b477e0755f5ee7ebe471ef85a (patch)
tree792b04795f475dbb86b6d678533f4703e09a60d4 /src
parentgl_shader_decompiler: Implement POPC (#1203) (diff)
parentShow game compatibility within yuzu (diff)
downloadyuzu-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.txt4
-rw-r--r--src/yuzu/game_list.cpp65
-rw-r--r--src/yuzu/game_list.h6
-rw-r--r--src/yuzu/game_list_p.h62
-rw-r--r--src/yuzu/main.cpp19
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu/util/util.cpp11
-rw-r--r--src/yuzu/util/util.h7
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
73file(GLOB COMPAT_LIST
74 ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
75 ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
73file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*) 76file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*)
74file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) 77file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*)
75 78
@@ -77,6 +80,7 @@ qt5_wrap_ui(UI_HDRS ${UIS})
77 80
78target_sources(yuzu 81target_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
347void 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
334void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { 389void 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 {
29public: 29public:
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
83private slots: 88private 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
105Q_DECLARE_METATYPE(GameListOpenTarget); 111Q_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
36static 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
32class GameListItem : public QStandardItem { 47class GameListItem : public QStandardItem {
33 48
34public: 49public:
@@ -96,6 +111,45 @@ public:
96 } 111 }
97}; 112};
98 113
114class GameListItemCompat : public GameListItem {
115 Q_DECLARE_TR_FUNCTIONS(GameListItemCompat)
116public:
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
143public: 197public:
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
147public slots: 204public 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() {
349void GMainWindow::ConnectWidgetEvents() { 352void 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
686void 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
681void GMainWindow::OnMenuLoadFile() { 700void 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
9QFont GetMonospaceFont() { 10QFont 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
29QPixmap 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.)
14QString ReadableByteSize(qulonglong size); 14QString 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 */
21QPixmap CreateCirclePixmapFromColor(const QColor& color);