diff options
Diffstat (limited to 'src/yuzu')
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/yuzu/game_list.cpp | 221 | ||||
| -rw-r--r-- | src/yuzu/game_list_p.h | 76 | ||||
| -rw-r--r-- | src/yuzu/game_list_worker.cpp | 239 | ||||
| -rw-r--r-- | src/yuzu/game_list_worker.h | 72 | 
5 files changed, 324 insertions, 286 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index ea9ea69e4..a2b6e984e 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -43,6 +43,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/game_list.cpp b/src/yuzu/game_list.cpp index a3b841684..86532e4a9 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -18,17 +18,10 @@  #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/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 +429,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 +436,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_p.h b/src/yuzu/game_list_p.h index a70a151c5..2720bf143 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: @@ -197,49 +177,13 @@ public:      }  }; -/** - * 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); -}; +inline 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; +        }); +} diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp new file mode 100644 index 000000000..9f26935d6 --- /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/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 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_worker.h b/src/yuzu/game_list_worker.h new file mode 100644 index 000000000..42c93fc31 --- /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" + +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 std::unordered_map<std::string, std::pair<QString, QString>>& 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 std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list; +    std::atomic_bool stop_processing; +};  | 
