Jelajahi Sumber

game_list: Register content with ContentProvider

Zach Hilman 7 tahun lalu
induk
melakukan
41d2565f29

+ 3 - 2
src/core/hle/service/am/applets/web_browser.cpp

@@ -86,7 +86,7 @@ static FileSys::VirtualFile GetManualRomFS() {
     if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success)
         return out;
 
-    const auto& installed{FileSystem::GetUnionContents()};
+    const auto& installed{Core::System::GetInstance().GetContentProvider()};
     const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(),
                                         FileSys::ContentRecordType::Manual);
 
@@ -154,7 +154,8 @@ void WebBrowser::Execute() {
 
     auto& frontend{Core::System::GetInstance().GetWebBrowser()};
 
-    frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
+    frontend.OpenPage(
+        filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
 }
 
 void WebBrowser::UnpackRomFS() {

+ 6 - 3
src/yuzu/game_list.cpp

@@ -18,6 +18,7 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "core/file_sys/patch_manager.h"
+#include "core/file_sys/registered_cache.h"
 #include "yuzu/compatibility_list.h"
 #include "yuzu/game_list.h"
 #include "yuzu/game_list_p.h"
@@ -193,8 +194,9 @@ void GameList::onFilterCloseClicked() {
     main_window->filterBarSetChecked(false);
 }
 
-GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
-    : QWidget{parent}, vfs(std::move(vfs)) {
+GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvider* provider,
+                   GMainWindow* parent)
+    : QWidget{parent}, vfs(std::move(vfs)), provider(provider) {
     watcher = new QFileSystemWatcher(this);
     connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
 
@@ -428,7 +430,8 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
 
     emit ShouldCancelWorker();
 
-    GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan, compatibility_list);
+    GameListWorker* worker =
+        new GameListWorker(vfs, provider, dir_path, deep_scan, compatibility_list);
 
     connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
     connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating,

+ 5 - 2
src/yuzu/game_list.h

@@ -26,8 +26,9 @@ class GameListSearchField;
 class GMainWindow;
 
 namespace FileSys {
+class ManualContentProvider;
 class VfsFilesystem;
-}
+} // namespace FileSys
 
 enum class GameListOpenTarget {
     SaveData,
@@ -47,7 +48,8 @@ public:
         COLUMN_COUNT, // Number of columns
     };
 
-    explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, GMainWindow* parent = nullptr);
+    explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs,
+                      FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr);
     ~GameList() override;
 
     void clearFilter();
@@ -85,6 +87,7 @@ private:
     void RefreshGameDirectory();
 
     std::shared_ptr<FileSys::VfsFilesystem> vfs;
+    FileSys::ManualContentProvider* provider;
     GameListSearchField* search_field;
     GMainWindow* main_window = nullptr;
     QVBoxLayout* layout = nullptr;

+ 57 - 68
src/yuzu/game_list_worker.cpp

@@ -12,12 +12,15 @@
 
 #include "common/common_paths.h"
 #include "common/file_util.h"
+#include "core/core.h"
+#include "core/file_sys/card_image.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/file_sys/submission_package.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/loader/loader.h"
 #include "yuzu/compatibility_list.h"
@@ -119,20 +122,25 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
 }
 } // 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),
+GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs,
+                               FileSys::ManualContentProvider* provider, QString dir_path,
+                               bool deep_scan, const CompatibilityList& compatibility_list)
+    : vfs(std::move(vfs)), provider(provider), 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);
+void GameListWorker::AddTitlesToGameList() {
+    const auto& cache = dynamic_cast<FileSys::ContentProviderUnion&>(
+        Core::System::GetInstance().GetContentProvider());
+    const auto installed_games = cache.ListEntriesFilterOrigin(
+        std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
 
-    for (const auto& game : installed_games) {
-        const auto file = cache.GetEntryUnparsed(game);
+    for (const auto& [slot, game] : installed_games) {
+        if (slot == FileSys::ContentProviderUnionSlot::FrontendManual)
+            continue;
+
+        const auto file = cache.GetEntryUnparsed(game.title_id, game.type);
         std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
         if (!loader)
             continue;
@@ -150,45 +158,13 @@ void GameListWorker::AddInstalledTitlesToGameList() {
         emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id,
                                           compatibility_list, patch));
     }
-
-    const auto control_data = cache.ListEntriesFilter(FileSys::TitleType::Application,
-                                                      FileSys::ContentRecordType::Control);
-
-    for (const auto& entry : control_data) {
-        auto nca = cache.GetEntry(entry);
-        if (nca != nullptr) {
-            nca_control_map.insert_or_assign(entry.title_id, std::move(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 {
-        if (stop_processing) {
-            // Breaks the callback loop
-            return false;
-        }
-
-        const std::string physical_name = directory + DIR_SEP + virtual_name;
-        const QFileInfo file_info(QString::fromStdString(physical_name));
-        if (!file_info.isDir() && file_info.suffix() == QStringLiteral("nca")) {
-            auto nca =
-                std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
-            if (nca->GetType() == FileSys::NCAContentType::Control) {
-                const u64 title_id = nca->GetTitleId();
-                nca_control_map.insert_or_assign(title_id, std::move(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 {
+void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path,
+                                    unsigned int recursion) {
+    const auto callback = [this, target, recursion](u64* num_entries_out,
+                                                    const std::string& directory,
+                                                    const std::string& virtual_name) -> bool {
         if (stop_processing) {
             // Breaks the callback loop.
             return false;
@@ -198,7 +174,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
         const bool is_dir = FileUtil::IsDirectory(physical_name);
         if (!is_dir &&
             (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
-            auto loader = Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));
+            const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read);
+            auto loader = Loader::GetLoader(file);
             if (!loader) {
                 return true;
             }
@@ -209,31 +186,42 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
                 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);
+            if (target == ScanTarget::FillManualContentProvider) {
+                if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
+                    provider->AddEntry(FileSys::TitleType::Application,
+                                       FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
+                                       program_id, file);
+                } else if (res2 == Loader::ResultStatus::Success &&
+                           (file_type == Loader::FileType::XCI ||
+                            file_type == Loader::FileType::NSP)) {
+                    const auto nsp = file_type == Loader::FileType::NSP
+                                         ? std::make_shared<FileSys::NSP>(file)
+                                         : FileSys::XCI{file}.GetSecurePartitionNSP();
+                    for (const auto& title : nsp->GetNCAs()) {
+                        for (const auto& entry : title.second) {
+                            provider->AddEntry(entry.first.first, entry.first.second, title.first,
+                                               entry.second->GetBaseFile());
+                        }
+                    }
+                }
+            } else {
+                std::vector<u8> icon;
+                const auto res1 = loader->ReadIcon(icon);
 
-            const FileSys::PatchManager patch{program_id};
+                std::string name = " ";
+                const auto res3 = loader->ReadTitle(name);
 
-            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);
-                }
-            }
+                const FileSys::PatchManager patch{program_id};
 
-            emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id,
-                                              compatibility_list, patch));
+                emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id,
+                                                  compatibility_list, patch));
+            }
         } else if (is_dir && recursion > 0) {
             watch_list.append(QString::fromStdString(physical_name));
-            AddFstEntriesToGameList(physical_name, recursion - 1);
+            ScanFileSystem(target, physical_name, recursion - 1);
         }
 
         return true;
@@ -245,10 +233,11 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
 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();
+    provider->ClearAllEntries();
+    ScanFileSystem(ScanTarget::FillManualContentProvider, dir_path.toStdString(),
+                   deep_scan ? 256 : 0);
+    AddTitlesToGameList();
+    ScanFileSystem(ScanTarget::PopulateGameList, dir_path.toStdString(), deep_scan ? 256 : 0);
     emit Finished(watch_list);
 }
 

+ 11 - 5
src/yuzu/game_list_worker.h

@@ -33,7 +33,8 @@ class GameListWorker : public QObject, public QRunnable {
     Q_OBJECT
 
 public:
-    GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan,
+    GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs,
+                   FileSys::ManualContentProvider* provider, QString dir_path, bool deep_scan,
                    const CompatibilityList& compatibility_list);
     ~GameListWorker() override;
 
@@ -58,12 +59,17 @@ signals:
     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);
+    void AddTitlesToGameList();
+
+    enum class ScanTarget {
+        FillManualContentProvider,
+        PopulateGameList,
+    };
+
+    void ScanFileSystem(ScanTarget target, const std::string& dir_path, unsigned int recursion = 0);
 
     std::shared_ptr<FileSys::VfsFilesystem> vfs;
-    std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map;
+    FileSys::ManualContentProvider* provider;
     QStringList watch_list;
     QString dir_path;
     bool deep_scan;

+ 14 - 9
src/yuzu/main.cpp

@@ -168,7 +168,8 @@ static void InitializeLogging() {
 
 GMainWindow::GMainWindow()
     : config(new Config()), emu_thread(nullptr),
-      vfs(std::make_shared<FileSys::RealVfsFilesystem>()) {
+      vfs(std::make_shared<FileSys::RealVfsFilesystem>()),
+      provider(std::make_unique<FileSys::ManualContentProvider>()) {
     InitializeLogging();
 
     debug_context = Tegra::DebugContext::Construct();
@@ -200,11 +201,15 @@ GMainWindow::GMainWindow()
                        .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));
     show();
 
+    Core::System::GetInstance().SetContentProvider(
+        std::make_unique<FileSys::ContentProviderUnion>());
+    Core::System::GetInstance().RegisterContentProvider(
+        FileSys::ContentProviderUnionSlot::FrontendManual, provider.get());
+    Service::FileSystem::CreateFactories(*vfs);
+
     // Gen keys if necessary
     OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
 
-    // Necessary to load titles from nand in gamelist.
-    Service::FileSystem::CreateFactories(*vfs);
     game_list->LoadCompatibilityList();
     game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
 
@@ -416,7 +421,7 @@ void GMainWindow::InitializeWidgets() {
     render_window = new GRenderWindow(this, emu_thread.get());
     render_window->hide();
 
-    game_list = new GameList(vfs, this);
+    game_list = new GameList(vfs, provider.get(), this);
     ui.horizontalLayout->addWidget(game_list);
 
     loading_screen = new LoadingScreen(this);
@@ -1141,7 +1146,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
         return;
     }
 
-    const auto installed = Service::FileSystem::GetUnionContents();
+    const auto& installed = Core::System::GetInstance().GetContentProvider();
     const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id);
 
     if (!romfs_title_id) {
@@ -1886,14 +1891,14 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
     }
 }
 
-std::optional<u64> GMainWindow::SelectRomFSDumpTarget(
-    const FileSys::RegisteredCacheUnion& installed, u64 program_id) {
+std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed,
+                                                      u64 program_id) {
     const auto dlc_entries =
         installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
-    std::vector<FileSys::RegisteredCacheEntry> dlc_match;
+    std::vector<FileSys::ContentProviderEntry> dlc_match;
     dlc_match.reserve(dlc_entries.size());
     std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
-                 [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) {
+                 [&program_id, &installed](const FileSys::ContentProviderEntry& entry) {
                      return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id &&
                             installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
                  });

+ 4 - 2
src/yuzu/main.h

@@ -37,7 +37,8 @@ struct SoftwareKeyboardParameters;
 } // namespace Core::Frontend
 
 namespace FileSys {
-class RegisteredCacheUnion;
+class ContentProvider;
+class ManualContentProvider;
 class VfsFilesystem;
 } // namespace FileSys
 
@@ -204,7 +205,7 @@ private slots:
     void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
 
 private:
-    std::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&, u64 program_id);
+    std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
     void UpdateStatusBar();
 
     Ui::MainWindow ui;
@@ -232,6 +233,7 @@ private:
 
     // FS
     std::shared_ptr<FileSys::VfsFilesystem> vfs;
+    std::unique_ptr<FileSys::ManualContentProvider> provider;
 
     // Debugger panes
     ProfilerWidget* profilerWidget;

+ 2 - 0
src/yuzu_cmd/yuzu.cpp

@@ -33,6 +33,7 @@
 #include "yuzu_cmd/emu_window/emu_window_sdl2.h"
 
 #include <getopt.h>
+#include "core/file_sys/registered_cache.h"
 #ifndef _MSC_VER
 #include <unistd.h>
 #endif
@@ -178,6 +179,7 @@ int main(int argc, char** argv) {
     }
 
     Core::System& system{Core::System::GetInstance()};
+    system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
     system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
     Service::FileSystem::CreateFactories(*system.GetFilesystem());