diff options
Diffstat (limited to 'src/yuzu')
-rw-r--r-- | src/yuzu/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/yuzu/compatibility_list.cpp | 18 | ||||
-rw-r--r-- | src/yuzu/compatibility_list.h | 17 | ||||
-rw-r--r-- | src/yuzu/game_list.cpp | 222 | ||||
-rw-r--r-- | src/yuzu/game_list.h | 10 | ||||
-rw-r--r-- | src/yuzu/game_list_p.h | 67 | ||||
-rw-r--r-- | src/yuzu/game_list_worker.cpp | 239 | ||||
-rw-r--r-- | src/yuzu/game_list_worker.h | 72 | ||||
-rw-r--r-- | src/yuzu/main.cpp | 16 | ||||
-rw-r--r-- | src/yuzu/main.h | 6 |
10 files changed, 367 insertions, 304 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index ea9ea69e4..f48b69809 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -9,6 +9,8 @@ add_executable(yuzu about_dialog.h bootmanager.cpp bootmanager.h + compatibility_list.cpp + compatibility_list.h configuration/config.cpp configuration/config.h configuration/configure_audio.cpp @@ -43,6 +45,8 @@ add_executable(yuzu game_list.cpp game_list.h game_list_p.h + game_list_worker.cpp + game_list_worker.h hotkeys.cpp hotkeys.h main.cpp diff --git a/src/yuzu/compatibility_list.cpp b/src/yuzu/compatibility_list.cpp new file mode 100644 index 000000000..2d2cfd03c --- /dev/null +++ b/src/yuzu/compatibility_list.cpp @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> + +#include <fmt/format.h> + +#include "yuzu/compatibility_list.h" + +CompatibilityList::const_iterator FindMatchingCompatibilityEntry( + const CompatibilityList& compatibility_list, u64 program_id) { + return std::find_if(compatibility_list.begin(), compatibility_list.end(), + [program_id](const auto& element) { + std::string pid = fmt::format("{:016X}", program_id); + return element.first == pid; + }); +} diff --git a/src/yuzu/compatibility_list.h b/src/yuzu/compatibility_list.h new file mode 100644 index 000000000..bc0175bd3 --- /dev/null +++ b/src/yuzu/compatibility_list.h @@ -0,0 +1,17 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <unordered_map> + +#include <QString> + +#include "common/common_types.h" + +using CompatibilityList = std::unordered_map<std::string, std::pair<QString, QString>>; + +CompatibilityList::const_iterator FindMatchingCompatibilityEntry( + const CompatibilityList& compatibility_list, u64 program_id); diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index a3b841684..8c6e16d47 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -18,17 +18,11 @@ #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "core/file_sys/content_archive.h" -#include "core/file_sys/control_metadata.h" -#include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" -#include "core/file_sys/registered_cache.h" -#include "core/file_sys/romfs.h" -#include "core/file_sys/vfs_real.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/loader/loader.h" +#include "yuzu/compatibility_list.h" #include "yuzu/game_list.h" #include "yuzu/game_list_p.h" +#include "yuzu/game_list_worker.h" #include "yuzu/main.h" #include "yuzu/ui_settings.h" @@ -436,45 +430,6 @@ void GameList::LoadInterfaceLayout() { const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; -static bool HasSupportedFileExtension(const std::string& file_name) { - const QFileInfo file = QFileInfo(QString::fromStdString(file_name)); - return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); -} - -static bool IsExtractedNCAMain(const std::string& file_name) { - return QFileInfo(QString::fromStdString(file_name)).fileName() == "main"; -} - -static QString FormatGameName(const std::string& physical_name) { - const QString physical_name_as_qstring = QString::fromStdString(physical_name); - const QFileInfo file_info(physical_name_as_qstring); - - if (IsExtractedNCAMain(physical_name)) { - return file_info.dir().path(); - } - - return physical_name_as_qstring; -} - -static QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, - bool updatable = true) { - QString out; - for (const auto& kv : patch_manager.GetPatchVersionNames()) { - if (!updatable && kv.first == FileSys::PatchType::Update) - continue; - - if (kv.second.empty()) { - out.append(fmt::format("{}\n", FileSys::FormatPatchTypeName(kv.first)).c_str()); - } else { - out.append(fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), kv.second) - .c_str()); - } - } - - out.chop(1); - return out; -} - void GameList::RefreshGameDirectory() { if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); @@ -482,176 +437,3 @@ void GameList::RefreshGameDirectory() { PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); } } - -static void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, - const std::shared_ptr<FileSys::NCA>& nca, - std::vector<u8>& icon, std::string& name) { - auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); - if (icon_file != nullptr) - icon = icon_file->ReadAllBytes(); - if (nacp != nullptr) - name = nacp->GetApplicationName(); -} - -GameListWorker::GameListWorker( - 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) {} - -GameListWorker::~GameListWorker() = default; - -void GameListWorker::AddInstalledTitlesToGameList() { - const auto cache = Service::FileSystem::GetUnionContents(); - const auto installed_games = cache->ListEntriesFilter(FileSys::TitleType::Application, - FileSys::ContentRecordType::Program); - - for (const auto& game : installed_games) { - const auto& file = cache->GetEntryUnparsed(game); - std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); - if (!loader) - continue; - - std::vector<u8> icon; - std::string name; - u64 program_id = 0; - loader->ReadProgramId(program_id); - - const FileSys::PatchManager patch{program_id}; - const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); - if (control != nullptr) - GetMetadataFromControlNCA(patch, control, icon, name); - - 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({ - new GameListItemPath( - FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name), - QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), - program_id), - new GameListItemCompat(compatibility), - new GameListItem(FormatPatchNameVersions(patch)), - new GameListItem( - QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), - new GameListItemSize(file->GetSize()), - }); - } - - const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application, - FileSys::ContentRecordType::Control); - - for (const auto& entry : control_data) { - const auto nca = cache->GetEntry(entry); - if (nca != nullptr) - nca_control_map.insert_or_assign(entry.title_id, nca); - } -} - -void GameListWorker::FillControlMap(const std::string& dir_path) { - const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory, - const std::string& virtual_name) -> bool { - std::string physical_name = directory + DIR_SEP + virtual_name; - - if (stop_processing) - return false; // Breaks the callback loop. - - bool is_dir = FileUtil::IsDirectory(physical_name); - QFileInfo file_info(physical_name.c_str()); - if (!is_dir && file_info.suffix().toStdString() == "nca") { - auto nca = - std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); - if (nca->GetType() == FileSys::NCAContentType::Control) - nca_control_map.insert_or_assign(nca->GetTitleId(), nca); - } - return true; - }; - - FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback); -} - -void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { - const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory, - const std::string& virtual_name) -> bool { - std::string physical_name = directory + DIR_SEP + virtual_name; - - if (stop_processing) - return false; // Breaks the callback loop. - - bool is_dir = FileUtil::IsDirectory(physical_name); - if (!is_dir && - (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { - std::unique_ptr<Loader::AppLoader> loader = - Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); - if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown || - loader->GetFileType() == Loader::FileType::Error) && - !UISettings::values.show_unknown)) - return true; - - std::vector<u8> icon; - const auto res1 = loader->ReadIcon(icon); - - u64 program_id = 0; - const auto res2 = loader->ReadProgramId(program_id); - - std::string name = " "; - const auto res3 = loader->ReadTitle(name); - - const FileSys::PatchManager patch{program_id}; - - if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success && - res2 == Loader::ResultStatus::Success) { - // Use from metadata pool. - if (nca_control_map.find(program_id) != nca_control_map.end()) { - const auto nca = nca_control_map[program_id]; - GetMetadataFromControlNCA(patch, nca, icon, name); - } - } - - 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({ - new GameListItemPath( - FormatGameName(physical_name), icon, QString::fromStdString(name), - QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), - program_id), - new GameListItemCompat(compatibility), - new GameListItem(FormatPatchNameVersions(patch, loader->IsRomFSUpdatable())), - new GameListItem( - QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), - new GameListItemSize(FileUtil::GetSize(physical_name)), - }); - } else if (is_dir && recursion > 0) { - watch_list.append(QString::fromStdString(physical_name)); - AddFstEntriesToGameList(physical_name, recursion - 1); - } - - return true; - }; - - FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); -} - -void GameListWorker::run() { - stop_processing = false; - watch_list.append(dir_path); - FillControlMap(dir_path.toStdString()); - AddInstalledTitlesToGameList(); - AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); - nca_control_map.clear(); - emit Finished(watch_list); -} - -void GameListWorker::Cancel() { - this->disconnect(); - stop_processing = true; -} diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 3fcb298ed..2713e7b54 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -4,8 +4,6 @@ #pragma once -#include <unordered_map> - #include <QFileSystemWatcher> #include <QHBoxLayout> #include <QLabel> @@ -21,6 +19,7 @@ #include <QWidget> #include "common/common_types.h" +#include "yuzu/compatibility_list.h" class GameListWorker; class GMainWindow; @@ -90,9 +89,8 @@ signals: void GameChosen(QString game_path); void ShouldCancelWorker(); void OpenFolderRequested(u64 program_id, GameListOpenTarget target); - void NavigateToGamedbEntryRequested( - u64 program_id, - std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list); + void NavigateToGamedbEntryRequested(u64 program_id, + const CompatibilityList& compatibility_list); private slots: void onTextChanged(const QString& newText); @@ -114,7 +112,7 @@ private: QStandardItemModel* item_model = nullptr; GameListWorker* current_worker = nullptr; QFileSystemWatcher* watcher = nullptr; - std::unordered_map<std::string, std::pair<QString, QString>> compatibility_list; + CompatibilityList compatibility_list; }; Q_DECLARE_METATYPE(GameListOpenTarget); diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index a70a151c5..f22e422e5 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -6,9 +6,7 @@ #include <algorithm> #include <array> -#include <atomic> #include <map> -#include <memory> #include <string> #include <unordered_map> #include <utility> @@ -16,7 +14,6 @@ #include <QCoreApplication> #include <QImage> #include <QObject> -#include <QRunnable> #include <QStandardItem> #include <QString> @@ -26,12 +23,6 @@ #include "yuzu/ui_settings.h" #include "yuzu/util/util.h" -namespace FileSys { -class NCA; -class RegisteredCache; -class VfsFilesystem; -} // namespace FileSys - /** * Gets the default icon (for games without valid SMDH) * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) @@ -43,17 +34,6 @@ static QPixmap GetDefaultIcon(u32 size) { 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 { public: @@ -196,50 +176,3 @@ public: return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); } }; - -/** - * Asynchronous worker object for populating the game list. - * Communicates with other threads through Qt's signal/slot system. - */ -class GameListWorker : public QObject, public QRunnable { - Q_OBJECT - -public: - GameListWorker( - std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan, - const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list); - ~GameListWorker() override; - -public slots: - /// Starts the processing of directory tree information. - void run() override; - /// Tells the worker that it should no longer continue processing. Thread-safe. - void Cancel(); - -signals: - /** - * The `EntryReady` signal is emitted once an entry has been prepared and is ready - * to be added to the game list. - * @param entry_items a list with `QStandardItem`s that make up the columns of the new entry. - */ - void EntryReady(QList<QStandardItem*> entry_items); - - /** - * After the worker has traversed the game directory looking for entries, this signal is emmited - * with a list of folders that should be watched for changes as well. - */ - void Finished(QStringList watch_list); - -private: - std::shared_ptr<FileSys::VfsFilesystem> vfs; - std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map; - QStringList watch_list; - QString dir_path; - bool deep_scan; - const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list; - std::atomic_bool stop_processing; - - void AddInstalledTitlesToGameList(); - void FillControlMap(const std::string& dir_path); - void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); -}; diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp new file mode 100644 index 000000000..e228d61bd --- /dev/null +++ b/src/yuzu/game_list_worker.cpp @@ -0,0 +1,239 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include <QDir> +#include <QFileInfo> + +#include "common/common_paths.h" +#include "common/file_util.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/mode.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/loader/loader.h" +#include "yuzu/compatibility_list.h" +#include "yuzu/game_list.h" +#include "yuzu/game_list_p.h" +#include "yuzu/game_list_worker.h" +#include "yuzu/ui_settings.h" + +namespace { +void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, + const std::shared_ptr<FileSys::NCA>& nca, std::vector<u8>& icon, + std::string& name) { + auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); + if (icon_file != nullptr) + icon = icon_file->ReadAllBytes(); + if (nacp != nullptr) + name = nacp->GetApplicationName(); +} + +bool HasSupportedFileExtension(const std::string& file_name) { + const QFileInfo file = QFileInfo(QString::fromStdString(file_name)); + return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); +} + +bool IsExtractedNCAMain(const std::string& file_name) { + return QFileInfo(QString::fromStdString(file_name)).fileName() == "main"; +} + +QString FormatGameName(const std::string& physical_name) { + const QString physical_name_as_qstring = QString::fromStdString(physical_name); + const QFileInfo file_info(physical_name_as_qstring); + + if (IsExtractedNCAMain(physical_name)) { + return file_info.dir().path(); + } + + return physical_name_as_qstring; +} + +QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, bool updatable = true) { + QString out; + for (const auto& kv : patch_manager.GetPatchVersionNames()) { + if (!updatable && kv.first == FileSys::PatchType::Update) + continue; + + if (kv.second.empty()) { + out.append(fmt::format("{}\n", FileSys::FormatPatchTypeName(kv.first)).c_str()); + } else { + out.append(fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), kv.second) + .c_str()); + } + } + + out.chop(1); + return out; +} +} // Anonymous namespace + +GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan, + const CompatibilityList& compatibility_list) + : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan), + compatibility_list(compatibility_list) {} + +GameListWorker::~GameListWorker() = default; + +void GameListWorker::AddInstalledTitlesToGameList() { + const auto cache = Service::FileSystem::GetUnionContents(); + const auto installed_games = cache->ListEntriesFilter(FileSys::TitleType::Application, + FileSys::ContentRecordType::Program); + + for (const auto& game : installed_games) { + const auto& file = cache->GetEntryUnparsed(game); + std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); + if (!loader) + continue; + + std::vector<u8> icon; + std::string name; + u64 program_id = 0; + loader->ReadProgramId(program_id); + + const FileSys::PatchManager patch{program_id}; + const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); + if (control != nullptr) + GetMetadataFromControlNCA(patch, control, icon, name); + + 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({ + new GameListItemPath( + FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name), + QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), + program_id), + new GameListItemCompat(compatibility), + new GameListItem(FormatPatchNameVersions(patch)), + new GameListItem( + QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), + new GameListItemSize(file->GetSize()), + }); + } + + const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application, + FileSys::ContentRecordType::Control); + + for (const auto& entry : control_data) { + const auto nca = cache->GetEntry(entry); + if (nca != nullptr) + nca_control_map.insert_or_assign(entry.title_id, nca); + } +} + +void GameListWorker::FillControlMap(const std::string& dir_path) { + const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory, + const std::string& virtual_name) -> bool { + std::string physical_name = directory + DIR_SEP + virtual_name; + + if (stop_processing) + return false; // Breaks the callback loop. + + bool is_dir = FileUtil::IsDirectory(physical_name); + QFileInfo file_info(physical_name.c_str()); + if (!is_dir && file_info.suffix().toStdString() == "nca") { + auto nca = + std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); + if (nca->GetType() == FileSys::NCAContentType::Control) + nca_control_map.insert_or_assign(nca->GetTitleId(), nca); + } + return true; + }; + + FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback); +} + +void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { + const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory, + const std::string& virtual_name) -> bool { + std::string physical_name = directory + DIR_SEP + virtual_name; + + if (stop_processing) + return false; // Breaks the callback loop. + + bool is_dir = FileUtil::IsDirectory(physical_name); + if (!is_dir && + (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { + std::unique_ptr<Loader::AppLoader> loader = + Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); + if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown || + loader->GetFileType() == Loader::FileType::Error) && + !UISettings::values.show_unknown)) + return true; + + std::vector<u8> icon; + const auto res1 = loader->ReadIcon(icon); + + u64 program_id = 0; + const auto res2 = loader->ReadProgramId(program_id); + + std::string name = " "; + const auto res3 = loader->ReadTitle(name); + + const FileSys::PatchManager patch{program_id}; + + if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success && + res2 == Loader::ResultStatus::Success) { + // Use from metadata pool. + if (nca_control_map.find(program_id) != nca_control_map.end()) { + const auto nca = nca_control_map[program_id]; + GetMetadataFromControlNCA(patch, nca, icon, name); + } + } + + 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({ + new GameListItemPath( + FormatGameName(physical_name), icon, QString::fromStdString(name), + QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), + program_id), + new GameListItemCompat(compatibility), + new GameListItem(FormatPatchNameVersions(patch, loader->IsRomFSUpdatable())), + new GameListItem( + QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), + new GameListItemSize(FileUtil::GetSize(physical_name)), + }); + } else if (is_dir && recursion > 0) { + watch_list.append(QString::fromStdString(physical_name)); + AddFstEntriesToGameList(physical_name, recursion - 1); + } + + return true; + }; + + FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); +} + +void GameListWorker::run() { + stop_processing = false; + watch_list.append(dir_path); + FillControlMap(dir_path.toStdString()); + AddInstalledTitlesToGameList(); + AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); + nca_control_map.clear(); + emit Finished(watch_list); +} + +void GameListWorker::Cancel() { + this->disconnect(); + stop_processing = true; +} diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h new file mode 100644 index 000000000..09d20c42f --- /dev/null +++ b/src/yuzu/game_list_worker.h @@ -0,0 +1,72 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <atomic> +#include <map> +#include <memory> +#include <string> +#include <unordered_map> + +#include <QList> +#include <QObject> +#include <QRunnable> +#include <QString> + +#include "common/common_types.h" +#include "yuzu/compatibility_list.h" + +class QStandardItem; + +namespace FileSys { +class NCA; +class VfsFilesystem; +} // namespace FileSys + +/** + * Asynchronous worker object for populating the game list. + * Communicates with other threads through Qt's signal/slot system. + */ +class GameListWorker : public QObject, public QRunnable { + Q_OBJECT + +public: + GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan, + const CompatibilityList& compatibility_list); + ~GameListWorker() override; + + /// Starts the processing of directory tree information. + void run() override; + + /// Tells the worker that it should no longer continue processing. Thread-safe. + void Cancel(); + +signals: + /** + * The `EntryReady` signal is emitted once an entry has been prepared and is ready + * to be added to the game list. + * @param entry_items a list with `QStandardItem`s that make up the columns of the new entry. + */ + void EntryReady(QList<QStandardItem*> entry_items); + + /** + * After the worker has traversed the game directory looking for entries, this signal is emitted + * with a list of folders that should be watched for changes as well. + */ + void Finished(QStringList watch_list); + +private: + void AddInstalledTitlesToGameList(); + void FillControlMap(const std::string& dir_path); + void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); + + std::shared_ptr<FileSys::VfsFilesystem> vfs; + std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map; + QStringList watch_list; + QString dir_path; + bool deep_scan; + const CompatibilityList& compatibility_list; + std::atomic_bool stop_processing; +}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index cb8135c42..e36914f14 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -47,6 +47,7 @@ #include "video_core/debug_utils/debug_utils.h" #include "yuzu/about_dialog.h" #include "yuzu/bootmanager.h" +#include "yuzu/compatibility_list.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_dialog.h" #include "yuzu/debugger/console.h" @@ -444,6 +445,8 @@ QStringList GMainWindow::GetUnsupportedGLExtensions() { unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev"); if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) unsupported_ext.append("ARB_texture_mirror_clamp_to_edge"); + if (!GLAD_GL_ARB_base_instance) + unsupported_ext.append("ARB_base_instance"); // Extensions required to support some texture formats. if (!GLAD_GL_EXT_texture_compression_s3tc) @@ -606,7 +609,7 @@ void GMainWindow::BootGame(const QString& filename) { } setWindowTitle(QString("yuzu %1| %4 | %2-%3") - .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc, + .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc, QString::fromStdString(title_name))); render_window->show(); @@ -641,7 +644,7 @@ void GMainWindow::ShutdownGame() { game_list->show(); game_list->setFilterFocus(); setWindowTitle(QString("yuzu %1| %2-%3") - .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); + .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); // Disable status bar updates status_bar_update_timer.stop(); @@ -723,14 +726,11 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target 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); +void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, + const CompatibilityList& compatibility_list) { + const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); QString directory; - if (it != compatibility_list.end()) directory = it->second.second; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 089ea2445..552e3e61c 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -13,6 +13,7 @@ #include "common/common_types.h" #include "core/core.h" #include "ui_main.h" +#include "yuzu/compatibility_list.h" #include "yuzu/hotkeys.h" class Config; @@ -137,9 +138,8 @@ 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<std::string, std::pair<QString, QString>>& compatibility_list); + void OnGameListNavigateToGamedbEntry(u64 program_id, + const CompatibilityList& compatibility_list); void OnMenuLoadFile(); void OnMenuLoadFolder(); void OnMenuInstallToNAND(); |