Merge pull request #1195 from FearlessTobi/port-gamelist-compat
yuzu: Show game compatibility in the game list (PR ported from Citra)
This commit is contained in:
commit
26aaa86ece
13 changed files with 196 additions and 7 deletions
|
@ -10,7 +10,7 @@ ln -sf /usr/bin/ccache /usr/lib/ccache/cc
|
||||||
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
|
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
ccache --show-stats > ccache_before
|
ccache --show-stats > ccache_before
|
||||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
|
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
|
||||||
ninja
|
ninja
|
||||||
ccache --show-stats > ccache_after
|
ccache --show-stats > ccache_after
|
||||||
diff -U100 ccache_before ccache_after || true
|
diff -U100 ccache_before ccache_after || true
|
||||||
|
|
|
@ -10,7 +10,7 @@ mkdir build && cd build
|
||||||
export PATH=/usr/local/opt/ccache/libexec:$PATH
|
export PATH=/usr/local/opt/ccache/libexec:$PATH
|
||||||
ccache --show-stats > ccache_before
|
ccache --show-stats > ccache_before
|
||||||
cmake --version
|
cmake --version
|
||||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
|
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
|
||||||
make -j4
|
make -j4
|
||||||
ccache --show-stats > ccache_after
|
ccache --show-stats > ccache_after
|
||||||
diff -U100 ccache_before ccache_after || true
|
diff -U100 ccache_before ccache_after || true
|
||||||
|
|
|
@ -41,6 +41,19 @@ function(check_submodules_present)
|
||||||
endfunction()
|
endfunction()
|
||||||
check_submodules_present()
|
check_submodules_present()
|
||||||
|
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||||
|
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||||
|
COPYONLY)
|
||||||
|
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||||
|
message(STATUS "Downloading compatibility list for yuzu...")
|
||||||
|
file(DOWNLOAD
|
||||||
|
https://api.yuzu-emu.org/gamedb/
|
||||||
|
"${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
|
||||||
|
endif()
|
||||||
|
if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||||
|
file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Detect current compilation architecture and create standard definitions
|
# Detect current compilation architecture and create standard definitions
|
||||||
# =======================================================================
|
# =======================================================================
|
||||||
|
|
||||||
|
|
|
@ -41,9 +41,9 @@ before_build:
|
||||||
- ps: |
|
- ps: |
|
||||||
if ($env:BUILD_TYPE -eq 'msvc') {
|
if ($env:BUILD_TYPE -eq 'msvc') {
|
||||||
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
|
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
|
||||||
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 .. 2>&1 && exit 0'
|
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1 && exit 0'
|
||||||
} else {
|
} else {
|
||||||
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release .. 2>&1"
|
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1"
|
||||||
}
|
}
|
||||||
- cd ..
|
- cd ..
|
||||||
|
|
||||||
|
|
5
dist/compatibility_list/compatibility_list.qrc
vendored
Normal file
5
dist/compatibility_list/compatibility_list.qrc
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="compatibility_list">
|
||||||
|
<file>compatibility_list.json</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
|
@ -70,6 +70,9 @@ set(UIS
|
||||||
main.ui
|
main.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
|
file(GLOB COMPAT_LIST
|
||||||
|
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||||
|
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||||
file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*)
|
file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*)
|
||||||
file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*)
|
file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*)
|
||||||
|
|
||||||
|
@ -77,6 +80,7 @@ qt5_wrap_ui(UI_HDRS ${UIS})
|
||||||
|
|
||||||
target_sources(yuzu
|
target_sources(yuzu
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
${COMPAT_LIST}
|
||||||
${ICONS}
|
${ICONS}
|
||||||
${THEMES}
|
${THEMES}
|
||||||
${UI_HDRS}
|
${UI_HDRS}
|
||||||
|
|
|
@ -7,10 +7,14 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
#include <boost/container/flat_map.hpp>
|
#include <boost/container/flat_map.hpp>
|
||||||
|
#include <fmt/format.h>
|
||||||
#include "common/common_paths.h"
|
#include "common/common_paths.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
@ -224,6 +228,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
|
||||||
|
|
||||||
item_model->insertColumns(0, COLUMN_COUNT);
|
item_model->insertColumns(0, COLUMN_COUNT);
|
||||||
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name");
|
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name");
|
||||||
|
item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility");
|
||||||
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
|
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
|
||||||
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
|
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
|
||||||
|
|
||||||
|
@ -325,12 +330,62 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
||||||
|
|
||||||
QMenu context_menu;
|
QMenu context_menu;
|
||||||
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* 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);
|
||||||
|
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||||
|
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
|
||||||
|
|
||||||
connect(open_save_location, &QAction::triggered,
|
connect(open_save_location, &QAction::triggered,
|
||||||
[&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); });
|
[&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); });
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameList::LoadCompatibilityList() {
|
||||||
|
QFile compat_list{":compatibility_list/compatibility_list.json"};
|
||||||
|
|
||||||
|
if (!compat_list.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
|
LOG_ERROR(Frontend, "Unable to open game compatibility list");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compat_list.size() == 0) {
|
||||||
|
LOG_WARNING(Frontend, "Game compatibility list is empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray content = compat_list.readAll();
|
||||||
|
if (content.isEmpty()) {
|
||||||
|
LOG_ERROR(Frontend, "Unable to completely read game compatibility list");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString string_content = content;
|
||||||
|
QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8());
|
||||||
|
QJsonArray arr = json.array();
|
||||||
|
|
||||||
|
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.emplace(
|
||||||
|
id.toUpper().toStdString(),
|
||||||
|
std::make_pair(QString::number(compatibility), directory));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
|
void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
|
||||||
if (!FileUtil::Exists(dir_path.toStdString()) ||
|
if (!FileUtil::Exists(dir_path.toStdString()) ||
|
||||||
!FileUtil::IsDirectory(dir_path.toStdString())) {
|
!FileUtil::IsDirectory(dir_path.toStdString())) {
|
||||||
|
@ -345,7 +400,7 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
|
||||||
|
|
||||||
emit ShouldCancelWorker();
|
emit ShouldCancelWorker();
|
||||||
|
|
||||||
GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan);
|
GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan, compatibility_list);
|
||||||
|
|
||||||
connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
|
connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
|
||||||
connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating,
|
connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating,
|
||||||
|
@ -523,11 +578,19 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||||
|
|
||||||
|
// The game list uses this as compatibility number for untested games
|
||||||
|
QString compatibility("99");
|
||||||
|
if (it != compatibility_list.end())
|
||||||
|
compatibility = it->second.first;
|
||||||
|
|
||||||
emit EntryReady({
|
emit EntryReady({
|
||||||
new GameListItemPath(
|
new GameListItemPath(
|
||||||
FormatGameName(physical_name), icon, QString::fromStdString(name),
|
FormatGameName(physical_name), icon, QString::fromStdString(name),
|
||||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
|
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
|
||||||
program_id),
|
program_id),
|
||||||
|
new GameListItemCompat(compatibility),
|
||||||
new GameListItem(
|
new GameListItem(
|
||||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||||
new GameListItemSize(FileUtil::GetSize(physical_name)),
|
new GameListItemSize(FileUtil::GetSize(physical_name)),
|
||||||
|
|
|
@ -29,6 +29,7 @@ class GameList : public QWidget {
|
||||||
public:
|
public:
|
||||||
enum {
|
enum {
|
||||||
COLUMN_NAME,
|
COLUMN_NAME,
|
||||||
|
COLUMN_COMPATIBILITY,
|
||||||
COLUMN_FILE_TYPE,
|
COLUMN_FILE_TYPE,
|
||||||
COLUMN_SIZE,
|
COLUMN_SIZE,
|
||||||
COLUMN_COUNT, // Number of columns
|
COLUMN_COUNT, // Number of columns
|
||||||
|
@ -68,6 +69,7 @@ public:
|
||||||
void setFilterFocus();
|
void setFilterFocus();
|
||||||
void setFilterVisible(bool visibility);
|
void setFilterVisible(bool visibility);
|
||||||
|
|
||||||
|
void LoadCompatibilityList();
|
||||||
void PopulateAsync(const QString& dir_path, bool deep_scan);
|
void PopulateAsync(const QString& dir_path, bool deep_scan);
|
||||||
|
|
||||||
void SaveInterfaceLayout();
|
void SaveInterfaceLayout();
|
||||||
|
@ -79,6 +81,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);
|
||||||
|
@ -100,6 +105,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, std::pair<QString, QString>> compatibility_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(GameListOpenTarget);
|
Q_DECLARE_METATYPE(GameListOpenTarget);
|
||||||
|
|
|
@ -8,11 +8,15 @@
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
#include <QObject>
|
||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
#include <QStandardItem>
|
#include <QStandardItem>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include "common/logging/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "ui_settings.h"
|
#include "ui_settings.h"
|
||||||
|
@ -29,6 +33,17 @@ static QPixmap GetDefaultIcon(u32 size) {
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto FindMatchingCompatibilityEntry(
|
||||||
|
const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list,
|
||||||
|
u64 program_id) {
|
||||||
|
return std::find_if(
|
||||||
|
compatibility_list.begin(), compatibility_list.end(),
|
||||||
|
[program_id](const std::pair<std::string, std::pair<QString, QString>>& element) {
|
||||||
|
std::string pid = fmt::format("{:016X}", program_id);
|
||||||
|
return element.first == pid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class GameListItem : public QStandardItem {
|
class GameListItem : public QStandardItem {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -96,6 +111,45 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GameListItemCompat : public GameListItem {
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(GameListItemCompat)
|
||||||
|
public:
|
||||||
|
static const int CompatNumberRole = Qt::UserRole + 1;
|
||||||
|
GameListItemCompat() = default;
|
||||||
|
explicit GameListItemCompat(const QString& compatiblity) {
|
||||||
|
struct CompatStatus {
|
||||||
|
QString color;
|
||||||
|
const char* text;
|
||||||
|
const char* tooltip;
|
||||||
|
};
|
||||||
|
// clang-format off
|
||||||
|
static const std::map<QString, CompatStatus> status_data = {
|
||||||
|
{"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.")}},
|
||||||
|
{"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.")}},
|
||||||
|
{"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.")}},
|
||||||
|
{"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.")}},
|
||||||
|
{"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.")}},
|
||||||
|
{"5", {"#828282", QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}},
|
||||||
|
{"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
auto iterator = status_data.find(compatiblity);
|
||||||
|
if (iterator == status_data.end()) {
|
||||||
|
LOG_WARNING(Frontend, "Invalid compatibility number {}", compatiblity.toStdString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CompatStatus status = iterator->second;
|
||||||
|
setData(compatiblity, CompatNumberRole);
|
||||||
|
setText(QObject::tr(status.text));
|
||||||
|
setToolTip(QObject::tr(status.tooltip));
|
||||||
|
setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const QStandardItem& other) const override {
|
||||||
|
return data(CompatNumberRole) < other.data(CompatNumberRole);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A specialization of GameListItem for size values.
|
* A specialization of GameListItem for size values.
|
||||||
* This class ensures that for every numerical size value it holds (in bytes), a correct
|
* 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 {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan)
|
GameListWorker(
|
||||||
: vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
|
FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan,
|
||||||
|
const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list)
|
||||||
|
: vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan),
|
||||||
|
compatibility_list(compatibility_list) {}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/// Starts the processing of directory tree information.
|
/// Starts the processing of directory tree information.
|
||||||
|
@ -170,6 +227,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, std::pair<QString, QString>>& compatibility_list;
|
||||||
std::atomic_bool stop_processing;
|
std::atomic_bool stop_processing;
|
||||||
|
|
||||||
void AddInstalledTitlesToGameList(std::shared_ptr<FileSys::RegisteredCache> cache);
|
void AddInstalledTitlesToGameList(std::shared_ptr<FileSys::RegisteredCache> cache);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
#include <QtWidgets>
|
#include <QtWidgets>
|
||||||
|
#include <fmt/format.h>
|
||||||
#include "common/common_paths.h"
|
#include "common/common_paths.h"
|
||||||
#include "common/logging/backend.h"
|
#include "common/logging/backend.h"
|
||||||
#include "common/logging/filter.h"
|
#include "common/logging/filter.h"
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
#include "core/gdbstub/gdbstub.h"
|
#include "core/gdbstub/gdbstub.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
#include "game_list_p.h"
|
||||||
#include "video_core/debug_utils/debug_utils.h"
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
#include "yuzu/about_dialog.h"
|
#include "yuzu/about_dialog.h"
|
||||||
#include "yuzu/bootmanager.h"
|
#include "yuzu/bootmanager.h"
|
||||||
|
@ -134,6 +136,7 @@ GMainWindow::GMainWindow()
|
||||||
|
|
||||||
// Necessary to load titles from nand in gamelist.
|
// Necessary to load titles from nand in gamelist.
|
||||||
Service::FileSystem::CreateFactories(vfs);
|
Service::FileSystem::CreateFactories(vfs);
|
||||||
|
game_list->LoadCompatibilityList();
|
||||||
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||||
|
|
||||||
// Show one-time "callout" messages to the user
|
// Show one-time "callout" messages to the user
|
||||||
|
@ -349,6 +352,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);
|
||||||
|
@ -678,6 +683,20 @@ 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 = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||||
|
|
||||||
|
QString directory;
|
||||||
|
|
||||||
|
if (it != compatibility_list.end())
|
||||||
|
directory = it->second.second;
|
||||||
|
|
||||||
|
QDesktopServices::openUrl(QUrl("https://yuzu-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)
|
||||||
|
|
|
@ -124,6 +124,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 OnMenuLoadFolder();
|
void OnMenuLoadFolder();
|
||||||
void OnMenuInstallToNAND();
|
void OnMenuInstallToNAND();
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <QPainter>
|
||||||
#include "yuzu/util/util.h"
|
#include "yuzu/util/util.h"
|
||||||
|
|
||||||
QFont GetMonospaceFont() {
|
QFont GetMonospaceFont() {
|
||||||
|
@ -24,3 +25,13 @@ QString ReadableByteSize(qulonglong size) {
|
||||||
.arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
|
.arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
|
||||||
.arg(units[digit_groups]);
|
.arg(units[digit_groups]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPixmap CreateCirclePixmapFromColor(const QColor& color) {
|
||||||
|
QPixmap circle_pixmap(16, 16);
|
||||||
|
circle_pixmap.fill(Qt::transparent);
|
||||||
|
QPainter painter(&circle_pixmap);
|
||||||
|
painter.setPen(color);
|
||||||
|
painter.setBrush(color);
|
||||||
|
painter.drawEllipse(0, 0, 15, 15);
|
||||||
|
return circle_pixmap;
|
||||||
|
}
|
||||||
|
|
|
@ -12,3 +12,10 @@ QFont GetMonospaceFont();
|
||||||
|
|
||||||
/// Convert a size in bytes into a readable format (KiB, MiB, etc.)
|
/// Convert a size in bytes into a readable format (KiB, MiB, etc.)
|
||||||
QString ReadableByteSize(qulonglong size);
|
QString ReadableByteSize(qulonglong size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a circle pixmap from a specified color
|
||||||
|
* @param color The color the pixmap shall have
|
||||||
|
* @return QPixmap circle pixmap
|
||||||
|
*/
|
||||||
|
QPixmap CreateCirclePixmapFromColor(const QColor& color);
|
||||||
|
|
Loading…
Reference in a new issue