diff options
author | t895 <clombardo169@gmail.com> | 2024-01-19 00:56:43 -0500 |
---|---|---|
committer | t895 <clombardo169@gmail.com> | 2024-01-19 17:09:35 -0500 |
commit | ccd3dd842f2bf7cf16c7b93e3b83a2afc8af4a69 (patch) | |
tree | 552347f0f71c9341a139f929907fef9243c6e337 /src/frontend_common | |
parent | b4a8e1ef8a749c591839e5d3b6deb18607504300 (diff) |
frontend_common: Add content manager utility functions
Creates utility functions to remove/install DLC, updates, and base game content
Diffstat (limited to 'src/frontend_common')
-rw-r--r-- | src/frontend_common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/frontend_common/content_manager.h | 168 |
2 files changed, 169 insertions, 0 deletions
diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt index 22e9337c4..94d8cc4c3 100644 --- a/src/frontend_common/CMakeLists.txt +++ b/src/frontend_common/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(frontend_common STATIC config.cpp config.h + content_manager.h ) create_target_directory_groups(frontend_common) diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h new file mode 100644 index 000000000..8e55f4ca0 --- /dev/null +++ b/src/frontend_common/content_manager.h @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <boost/algorithm/string.hpp> +#include "common/common_types.h" +#include "common/literals.h" +#include "core/core.h" +#include "core/file_sys/common_funcs.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/mode.h" +#include "core/file_sys/nca_metadata.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" + +namespace ContentManager { + +enum class InstallResult { + Success, + Overwrite, + Failure, + BaseInstallAttempted, +}; + +inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_controller, + const u64 title_id) { + return fs_controller.GetUserNANDContents()->RemoveExistingEntry(title_id) || + fs_controller.GetSDMCContents()->RemoveExistingEntry(title_id); +} + +inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) { + size_t count{}; + const auto& fs_controller = system->GetFileSystemController(); + const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( + FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); + std::vector<u64> program_dlc_entries; + + for (const auto& entry : dlc_entries) { + if (FileSys::GetBaseTitleID(entry.title_id) == program_id) { + program_dlc_entries.push_back(entry.title_id); + } + } + + for (const auto& entry : program_dlc_entries) { + if (RemoveDLC(fs_controller, entry)) { + ++count; + } + } + return count; +} + +inline bool RemoveUpdate(const Service::FileSystem::FileSystemController& fs_controller, + const u64 program_id) { + const auto update_id = program_id | 0x800; + return fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || + fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id); +} + +inline bool RemoveBaseContent(const Service::FileSystem::FileSystemController& fs_controller, + const u64 program_id) { + return fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || + fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); +} + +inline InstallResult InstallNSP( + Core::System* system, FileSys::VfsFilesystem* vfs, const std::string& filename, + const std::function<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) { + const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, + std::size_t block_size) { + if (src == nullptr || dest == nullptr) { + return false; + } + if (!dest->Resize(src->GetSize())) { + return false; + } + + using namespace Common::Literals; + std::vector<u8> buffer(1_MiB); + + for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { + if (callback(src->GetSize(), i)) { + dest->Resize(0); + return false; + } + const auto read = src->Read(buffer.data(), buffer.size(), i); + dest->Write(buffer.data(), read, i); + } + return true; + }; + + std::shared_ptr<FileSys::NSP> nsp; + FileSys::VirtualFile file = vfs->OpenFile(filename, FileSys::Mode::Read); + if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) { + nsp = std::make_shared<FileSys::NSP>(file); + if (nsp->IsExtractedType()) { + return InstallResult::Failure; + } + } else { + return InstallResult::Failure; + } + + if (nsp->GetStatus() != Loader::ResultStatus::Success) { + return InstallResult::Failure; + } + const auto res = + system->GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy); + switch (res) { + case FileSys::InstallResult::Success: + return InstallResult::Success; + case FileSys::InstallResult::OverwriteExisting: + return InstallResult::Overwrite; + case FileSys::InstallResult::ErrorBaseInstall: + return InstallResult::BaseInstallAttempted; + default: + return InstallResult::Failure; + } +} + +inline InstallResult InstallNCA( + FileSys::VfsFilesystem* vfs, const std::string& filename, + FileSys::RegisteredCache* registered_cache, const FileSys::TitleType title_type, + const std::function<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) { + const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, + std::size_t block_size) { + if (src == nullptr || dest == nullptr) { + return false; + } + if (!dest->Resize(src->GetSize())) { + return false; + } + + using namespace Common::Literals; + std::vector<u8> buffer(1_MiB); + + for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { + if (callback(src->GetSize(), i)) { + dest->Resize(0); + return false; + } + const auto read = src->Read(buffer.data(), buffer.size(), i); + dest->Write(buffer.data(), read, i); + } + return true; + }; + + const auto nca = std::make_shared<FileSys::NCA>(vfs->OpenFile(filename, FileSys::Mode::Read)); + const auto id = nca->GetStatus(); + + // Game updates necessary are missing base RomFS + if (id != Loader::ResultStatus::Success && + id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { + return InstallResult::Failure; + } + + const auto res = registered_cache->InstallEntry(*nca, title_type, true, copy); + if (res == FileSys::InstallResult::Success) { + return InstallResult::Success; + } else if (res == FileSys::InstallResult::OverwriteExisting) { + return InstallResult::Overwrite; + } else { + return InstallResult::Failure; + } +} + +} // namespace ContentManager |