registered_cache: Remove previous update/dlc if it exists on install
- This checks for and removes old updates or dlc based on title id. If a content meta nca exists within the registered cache, it will attempt to remove all the ncas associated with the content meta before installing a new update/dlc
This commit is contained in:
		| @@ -547,6 +547,57 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex | ||||
|     return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); | ||||
| } | ||||
|  | ||||
| bool RegisteredCache::RemoveExistingEntry(const u64 title_id) { | ||||
|     const auto delete_nca = [this](const NcaID& id) { | ||||
|         const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||||
|  | ||||
|         if (dir->GetFileRelative(path) == nullptr) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         Core::Crypto::SHA256Hash hash{}; | ||||
|         mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0); | ||||
|         const auto dirname = fmt::format("000000{:02X}", hash[0]); | ||||
|  | ||||
|         const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); | ||||
|  | ||||
|         const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false))); | ||||
|  | ||||
|         return res; | ||||
|     }; | ||||
|  | ||||
|     // Get the Content Provider | ||||
|     const auto& installed = Core::System::GetInstance().GetContentProvider(); | ||||
|     // If an update exists, remove | ||||
|     if (installed.HasEntry(title_id, ContentRecordType::Meta)) { | ||||
|         LOG_INFO(Loader, | ||||
|                  "Previous Update (v{}) for title_id={:016X} detected! Attempting to remove...", | ||||
|                  installed.GetEntryVersion(title_id).value_or(0), title_id); | ||||
|         // Get all the ncas associated with the current update CNMT and delete them | ||||
|         const auto& meta_old_id = | ||||
|             GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{}); | ||||
|         const auto& program_id = | ||||
|             GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{}); | ||||
|         const auto& data_id = | ||||
|             GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{}); | ||||
|         const auto& control_id = | ||||
|             GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{}); | ||||
|         const auto& html_id = | ||||
|             GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{}); | ||||
|         const auto& legal_id = | ||||
|             GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{}); | ||||
|  | ||||
|         delete_nca(meta_old_id); | ||||
|         delete_nca(program_id); | ||||
|         delete_nca(data_id); | ||||
|         delete_nca(control_id); | ||||
|         delete_nca(html_id); | ||||
|         delete_nca(legal_id); | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, | ||||
|                                             const VfsCopyFunction& copy) { | ||||
|     const auto ncas = nsp.GetNCAsCollapsed(); | ||||
| @@ -560,31 +611,44 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex | ||||
|         return InstallResult::ErrorMetaFailed; | ||||
|     } | ||||
|  | ||||
|     // Install Metadata File | ||||
|     const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32); | ||||
|     const auto meta_id = Common::HexStringToArray<16>(meta_id_raw); | ||||
|  | ||||
|     const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id); | ||||
|     if (res != InstallResult::Success) | ||||
|         return res; | ||||
|  | ||||
|     // Install all the other NCAs | ||||
|     const auto section0 = (*meta_iter)->GetSubdirectories()[0]; | ||||
|     const auto cnmt_file = section0->GetFiles()[0]; | ||||
|     const CNMT cnmt(cnmt_file); | ||||
|  | ||||
|     // Get the title id stored within the CNMT | ||||
|     const auto title_id = cnmt.GetTitleID(); | ||||
|     // Removes an entry if it exists | ||||
|     const auto result = RemoveExistingEntry(title_id); | ||||
|  | ||||
|     // Install Metadata File | ||||
|     const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id); | ||||
|     if (res != InstallResult::Success) { | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     // Install all the other NCAs | ||||
|     for (const auto& record : cnmt.GetContentRecords()) { | ||||
|         // Ignore DeltaFragments, they are not useful to us | ||||
|         if (record.type == ContentRecordType::DeltaFragment) | ||||
|         if (record.type == ContentRecordType::DeltaFragment) { | ||||
|             continue; | ||||
|         } | ||||
|         const auto nca = GetNCAFromNSPForID(nsp, record.nca_id); | ||||
|         if (nca == nullptr) | ||||
|         if (nca == nullptr) { | ||||
|             return InstallResult::ErrorCopyFailed; | ||||
|         } | ||||
|         const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id); | ||||
|         if (res2 != InstallResult::Success) | ||||
|         if (res2 != InstallResult::Success) { | ||||
|             return res2; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Refresh(); | ||||
|     if (result) { | ||||
|         return InstallResult::ErrorAlreadyExists; | ||||
|     } | ||||
|     return InstallResult::Success; | ||||
| } | ||||
|  | ||||
| @@ -610,8 +674,9 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, | ||||
|     mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0); | ||||
|     memcpy(&c_rec.nca_id, &c_rec.hash, 16); | ||||
|     const CNMT new_cnmt(header, opt_header, {c_rec}, {}); | ||||
|     if (!RawInstallYuzuMeta(new_cnmt)) | ||||
|     if (!RawInstallYuzuMeta(new_cnmt)) { | ||||
|         return InstallResult::ErrorMetaFailed; | ||||
|     } | ||||
|     return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); | ||||
| } | ||||
|  | ||||
| @@ -649,8 +714,9 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti | ||||
|     } | ||||
|  | ||||
|     auto out = dir->CreateFileRelative(path); | ||||
|     if (out == nullptr) | ||||
|     if (out == nullptr) { | ||||
|         return InstallResult::ErrorCopyFailed; | ||||
|     } | ||||
|     return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success | ||||
|                                                   : InstallResult::ErrorCopyFailed; | ||||
| } | ||||
|   | ||||
| @@ -34,6 +34,7 @@ using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile | ||||
|  | ||||
| enum class InstallResult { | ||||
|     Success, | ||||
|     OverwriteExisting, | ||||
|     ErrorAlreadyExists, | ||||
|     ErrorCopyFailed, | ||||
|     ErrorMetaFailed, | ||||
| @@ -132,9 +133,9 @@ public: | ||||
|     // Parsing function defines the conversion from raw file to NCA. If there are other steps | ||||
|     // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom | ||||
|     // parsing function. | ||||
|     explicit RegisteredCache(VirtualDir dir, | ||||
|                              ContentProviderParsingFunction parsing_function = | ||||
|                                  [](const VirtualFile& file, const NcaID& id) { return file; }); | ||||
|     explicit RegisteredCache( | ||||
|         VirtualDir dir, ContentProviderParsingFunction parsing_function = | ||||
|                             [](const VirtualFile& file, const NcaID& id) { return file; }); | ||||
|     ~RegisteredCache() override; | ||||
|  | ||||
|     void Refresh() override; | ||||
| @@ -154,6 +155,9 @@ public: | ||||
|         std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, | ||||
|         std::optional<u64> title_id = {}) const override; | ||||
|  | ||||
|     // Removes an existing entry based on title id | ||||
|     bool RemoveExistingEntry(const u64 title_id); | ||||
|  | ||||
|     // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure | ||||
|     // there is a meta NCA and all of them are accessible. | ||||
|     InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user