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:
parent
263200f982
commit
a82fdea1ac
|
@ -547,6 +547,57 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex
|
||||||
return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
|
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,
|
InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
|
||||||
const VfsCopyFunction& copy) {
|
const VfsCopyFunction& copy) {
|
||||||
const auto ncas = nsp.GetNCAsCollapsed();
|
const auto ncas = nsp.GetNCAsCollapsed();
|
||||||
|
@ -560,31 +611,44 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
|
||||||
return InstallResult::ErrorMetaFailed;
|
return InstallResult::ErrorMetaFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install Metadata File
|
|
||||||
const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32);
|
const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32);
|
||||||
const auto meta_id = Common::HexStringToArray<16>(meta_id_raw);
|
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 section0 = (*meta_iter)->GetSubdirectories()[0];
|
||||||
const auto cnmt_file = section0->GetFiles()[0];
|
const auto cnmt_file = section0->GetFiles()[0];
|
||||||
const CNMT cnmt(cnmt_file);
|
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()) {
|
for (const auto& record : cnmt.GetContentRecords()) {
|
||||||
// Ignore DeltaFragments, they are not useful to us
|
// Ignore DeltaFragments, they are not useful to us
|
||||||
if (record.type == ContentRecordType::DeltaFragment)
|
if (record.type == ContentRecordType::DeltaFragment) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
|
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
|
||||||
if (nca == nullptr)
|
if (nca == nullptr) {
|
||||||
return InstallResult::ErrorCopyFailed;
|
return InstallResult::ErrorCopyFailed;
|
||||||
|
}
|
||||||
const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
|
const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
|
||||||
if (res2 != InstallResult::Success)
|
if (res2 != InstallResult::Success) {
|
||||||
return res2;
|
return res2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Refresh();
|
Refresh();
|
||||||
|
if (result) {
|
||||||
|
return InstallResult::ErrorAlreadyExists;
|
||||||
|
}
|
||||||
return InstallResult::Success;
|
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);
|
mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
|
||||||
memcpy(&c_rec.nca_id, &c_rec.hash, 16);
|
memcpy(&c_rec.nca_id, &c_rec.hash, 16);
|
||||||
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
|
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
|
||||||
if (!RawInstallYuzuMeta(new_cnmt))
|
if (!RawInstallYuzuMeta(new_cnmt)) {
|
||||||
return InstallResult::ErrorMetaFailed;
|
return InstallResult::ErrorMetaFailed;
|
||||||
|
}
|
||||||
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
|
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);
|
auto out = dir->CreateFileRelative(path);
|
||||||
if (out == nullptr)
|
if (out == nullptr) {
|
||||||
return InstallResult::ErrorCopyFailed;
|
return InstallResult::ErrorCopyFailed;
|
||||||
|
}
|
||||||
return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success
|
return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success
|
||||||
: InstallResult::ErrorCopyFailed;
|
: InstallResult::ErrorCopyFailed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile
|
||||||
|
|
||||||
enum class InstallResult {
|
enum class InstallResult {
|
||||||
Success,
|
Success,
|
||||||
|
OverwriteExisting,
|
||||||
ErrorAlreadyExists,
|
ErrorAlreadyExists,
|
||||||
ErrorCopyFailed,
|
ErrorCopyFailed,
|
||||||
ErrorMetaFailed,
|
ErrorMetaFailed,
|
||||||
|
@ -132,9 +133,9 @@ public:
|
||||||
// Parsing function defines the conversion from raw file to NCA. If there are other steps
|
// 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
|
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
|
||||||
// parsing function.
|
// parsing function.
|
||||||
explicit RegisteredCache(VirtualDir dir,
|
explicit RegisteredCache(
|
||||||
ContentProviderParsingFunction parsing_function =
|
VirtualDir dir, ContentProviderParsingFunction parsing_function =
|
||||||
[](const VirtualFile& file, const NcaID& id) { return file; });
|
[](const VirtualFile& file, const NcaID& id) { return file; });
|
||||||
~RegisteredCache() override;
|
~RegisteredCache() override;
|
||||||
|
|
||||||
void Refresh() override;
|
void Refresh() override;
|
||||||
|
@ -154,6 +155,9 @@ public:
|
||||||
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||||
std::optional<u64> title_id = {}) const override;
|
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
|
// 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.
|
// there is a meta NCA and all of them are accessible.
|
||||||
InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
|
InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
|
||||||
|
|
Reference in New Issue