From c725f3c86cba71f513c9af352f65c4dffb5f187a Mon Sep 17 00:00:00 2001 From: t895 Date: Sun, 21 Jan 2024 16:36:37 -0500 Subject: frontend_common: Move integrity verification to content_manager --- src/frontend_common/content_manager.h | 137 +++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 2 deletions(-) (limited to 'src/frontend_common') diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h index 23f2979db..92276700b 100644 --- a/src/frontend_common/content_manager.h +++ b/src/frontend_common/content_manager.h @@ -11,10 +11,12 @@ #include "core/file_sys/content_archive.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 "core/loader/nca.h" namespace ContentManager { @@ -25,6 +27,12 @@ enum class InstallResult { BaseInstallAttempted, }; +enum class GameVerificationResult { + Success, + Failed, + NotImplemented, +}; + /** * \brief Removes a single installed DLC * \param fs_controller [FileSystemController] reference from the Core::System instance @@ -121,7 +129,7 @@ inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_contro * \param filename Path to the NSP file * \param callback Optional callback to report the progress of the installation. The first size_t * parameter is the total size of the virtual file and the second is the current progress. If you - * return false to the callback, it will cancel the installation as soon as possible. + * return true to the callback, it will cancel the installation as soon as possible. * \return [InstallResult] representing how the installation finished */ inline InstallResult InstallNSP( @@ -186,7 +194,7 @@ inline InstallResult InstallNSP( * \param title_type Type of NCA package to install * \param callback Optional callback to report the progress of the installation. The first size_t * parameter is the total size of the virtual file and the second is the current progress. If you - * return false to the callback, it will cancel the installation as soon as possible. + * return true to the callback, it will cancel the installation as soon as possible. * \return [InstallResult] representing how the installation finished */ inline InstallResult InstallNCA( @@ -235,4 +243,129 @@ inline InstallResult InstallNCA( } } +/** + * \brief Verifies the installed contents for a given ManualContentProvider + * \param system Raw pointer to the system instance + * \param provider Raw pointer to the content provider that's tracking indexed games + * \param callback Optional callback to report the progress of the installation. The first size_t + * parameter is the total size of the installed contents and the second is the current progress. If + * you return true to the callback, it will cancel the installation as soon as possible. + * \return A list of entries that failed to install. Returns an empty vector if successful. + */ +inline std::vector VerifyInstalledContents( + Core::System* system, FileSys::ManualContentProvider* provider, + const std::function& callback = std::function()) { + // Get content registries. + auto bis_contents = system->GetFileSystemController().GetSystemNANDContents(); + auto user_contents = system->GetFileSystemController().GetUserNANDContents(); + + std::vector content_providers; + if (bis_contents) { + content_providers.push_back(bis_contents); + } + if (user_contents) { + content_providers.push_back(user_contents); + } + + // Get associated NCA files. + std::vector nca_files; + + // Get all installed IDs. + size_t total_size = 0; + for (auto nca_provider : content_providers) { + const auto entries = nca_provider->ListEntriesFilter(); + + for (const auto& entry : entries) { + auto nca_file = nca_provider->GetEntryRaw(entry.title_id, entry.type); + if (!nca_file) { + continue; + } + + total_size += nca_file->GetSize(); + nca_files.push_back(std::move(nca_file)); + } + } + + // Declare a list of file names which failed to verify. + std::vector failed; + + size_t processed_size = 0; + bool cancelled = false; + auto nca_callback = [&](size_t nca_processed, size_t nca_total) { + cancelled = callback(total_size, processed_size + nca_processed); + return !cancelled; + }; + + // Using the NCA loader, determine if all NCAs are valid. + for (auto& nca_file : nca_files) { + Loader::AppLoader_NCA nca_loader(nca_file); + + auto status = nca_loader.VerifyIntegrity(nca_callback); + if (cancelled) { + break; + } + if (status != Loader::ResultStatus::Success) { + FileSys::NCA nca(nca_file); + const auto title_id = nca.GetTitleId(); + std::string title_name = "unknown"; + + const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id), + FileSys::ContentRecordType::Control); + if (control && control->GetStatus() == Loader::ResultStatus::Success) { + const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), + *provider}; + const auto [nacp, logo] = pm.ParseControlNCA(*control); + if (nacp) { + title_name = nacp->GetApplicationName(); + } + } + + if (title_id > 0) { + failed.push_back( + fmt::format("{} ({:016X}) ({})", nca_file->GetName(), title_id, title_name)); + } else { + failed.push_back(fmt::format("{} (unknown)", nca_file->GetName())); + } + } + + processed_size += nca_file->GetSize(); + } + return failed; +} + +/** + * \brief Verifies the contents of a given game + * \param system Raw pointer to the system instance + * \param game_path Patch to the game file + * \param callback Optional callback to report the progress of the installation. The first size_t + * parameter is the total size of the installed contents and the second is the current progress. If + * you return true to the callback, it will cancel the installation as soon as possible. + * \return GameVerificationResult representing how the verification process finished + */ +inline GameVerificationResult VerifyGameContents( + Core::System* system, const std::string& game_path, + const std::function& callback = std::function()) { + const auto loader = Loader::GetLoader( + *system, system->GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read)); + if (loader == nullptr) { + return GameVerificationResult::NotImplemented; + } + + bool cancelled = false; + auto loader_callback = [&](size_t processed, size_t total) { + cancelled = callback(total, processed); + return !cancelled; + }; + + const auto status = loader->VerifyIntegrity(loader_callback); + if (cancelled || status == Loader::ResultStatus::ErrorIntegrityVerificationNotImplemented) { + return GameVerificationResult::NotImplemented; + } + + if (status == Loader::ResultStatus::ErrorIntegrityVerificationFailed) { + return GameVerificationResult::Failed; + } + return GameVerificationResult::Success; +} + } // namespace ContentManager -- cgit v1.2.3 From 961b5586a5a48a90191eacb0b767e03a7f65abbb Mon Sep 17 00:00:00 2001 From: t895 Date: Sun, 21 Jan 2024 19:31:26 -0500 Subject: frontend_common: Remove default value for installer callbacks We never used these without callbacks and these will break without them in their current state. I could write the default value to return false always but that's unnecessary for now. --- src/frontend_common/content_manager.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'src/frontend_common') diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h index 92276700b..0b0fee73e 100644 --- a/src/frontend_common/content_manager.h +++ b/src/frontend_common/content_manager.h @@ -127,14 +127,14 @@ inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_contro * \param system Raw pointer to the system instance * \param vfs Raw pointer to the VfsFilesystem instance in Core::System * \param filename Path to the NSP file - * \param callback Optional callback to report the progress of the installation. The first size_t + * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the virtual file and the second is the current progress. If you * return true to the callback, it will cancel the installation as soon as possible. * \return [InstallResult] representing how the installation finished */ -inline InstallResult InstallNSP( - Core::System* system, FileSys::VfsFilesystem* vfs, const std::string& filename, - const std::function& callback = std::function()) { +inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vfs, + const std::string& filename, + const std::function& callback) { const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, std::size_t block_size) { if (src == nullptr || dest == nullptr) { @@ -192,15 +192,15 @@ inline InstallResult InstallNSP( * \param filename Path to the NCA file * \param registered_cache Raw pointer to the registered cache that the NCA will be installed to * \param title_type Type of NCA package to install - * \param callback Optional callback to report the progress of the installation. The first size_t + * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the virtual file and the second is the current progress. If you * return true to the callback, it will cancel the installation as soon as possible. * \return [InstallResult] representing how the installation finished */ -inline InstallResult InstallNCA( - FileSys::VfsFilesystem* vfs, const std::string& filename, - FileSys::RegisteredCache* registered_cache, const FileSys::TitleType title_type, - const std::function& callback = std::function()) { +inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& filename, + FileSys::RegisteredCache* registered_cache, + const FileSys::TitleType title_type, + const std::function& callback) { const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, std::size_t block_size) { if (src == nullptr || dest == nullptr) { @@ -247,14 +247,14 @@ inline InstallResult InstallNCA( * \brief Verifies the installed contents for a given ManualContentProvider * \param system Raw pointer to the system instance * \param provider Raw pointer to the content provider that's tracking indexed games - * \param callback Optional callback to report the progress of the installation. The first size_t + * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the installed contents and the second is the current progress. If * you return true to the callback, it will cancel the installation as soon as possible. * \return A list of entries that failed to install. Returns an empty vector if successful. */ inline std::vector VerifyInstalledContents( Core::System* system, FileSys::ManualContentProvider* provider, - const std::function& callback = std::function()) { + const std::function& callback) { // Get content registries. auto bis_contents = system->GetFileSystemController().GetSystemNANDContents(); auto user_contents = system->GetFileSystemController().GetUserNANDContents(); @@ -337,14 +337,14 @@ inline std::vector VerifyInstalledContents( * \brief Verifies the contents of a given game * \param system Raw pointer to the system instance * \param game_path Patch to the game file - * \param callback Optional callback to report the progress of the installation. The first size_t + * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the installed contents and the second is the current progress. If * you return true to the callback, it will cancel the installation as soon as possible. * \return GameVerificationResult representing how the verification process finished */ inline GameVerificationResult VerifyGameContents( Core::System* system, const std::string& game_path, - const std::function& callback = std::function()) { + const std::function& callback) { const auto loader = Loader::GetLoader( *system, system->GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read)); if (loader == nullptr) { -- cgit v1.2.3