From eda4b95cfc92d4e0851bab989bfa384af5d870b8 Mon Sep 17 00:00:00 2001 From: Maufeat Date: Sat, 10 Jan 2026 22:52:48 +0100 Subject: [PATCH] test changes and stability --- src/core/file_sys/external_content_index.cpp | 22 +++-- src/core/file_sys/patch_manager.cpp | 85 ++++++++++++-------- 2 files changed, 61 insertions(+), 46 deletions(-) diff --git a/src/core/file_sys/external_content_index.cpp b/src/core/file_sys/external_content_index.cpp index eff069c5c1..ea9628757c 100644 --- a/src/core/file_sys/external_content_index.cpp +++ b/src/core/file_sys/external_content_index.cpp @@ -158,12 +158,10 @@ void ExternalContentIndexer::ParseContainerNSP(VirtualFile file, bool is_update) continue; const auto& cnmt = *cnmt_opt; - const auto base_id = BaseTitleId(title_id); - if (is_update && cnmt.GetType() == TitleType::Update) { ParsedUpdate candidate{}; // Register updates under their Update TID so PatchManager can find/apply them - candidate.title_id = FileSys::GetUpdateTitleID(base_id); + candidate.title_id = cnmt.GetTitleID(); candidate.version = cnmt.GetTitleVersion(); for (const auto& rec : cnmt.GetContentRecords()) { const auto it = nca_map.find({cnmt.GetType(), rec.type}); @@ -171,7 +169,7 @@ void ExternalContentIndexer::ParseContainerNSP(VirtualFile file, bool is_update) candidate.ncas[rec.type] = it->second->GetBaseFile(); } } - auto& vec = m_updates_by_title[base_id]; + auto& vec = m_updates_by_title[candidate.title_id]; vec.emplace_back(std::move(candidate)); } else if (cnmt.GetType() == TitleType::AOC) { const auto dlc_title_id = cnmt.GetTitleID(); @@ -201,12 +199,10 @@ void ExternalContentIndexer::ParseLooseCnmtNca(VirtualFile meta_nca_file, const return; const auto& cnmt = *cnmt_opt; - const auto base_id = BaseTitleId(cnmt.GetTitleID()); - if (is_update && cnmt.GetType() == TitleType::Update) { ParsedUpdate candidate{}; // Register updates under their Update TID so PatchManager can find/apply them - candidate.title_id = FileSys::GetUpdateTitleID(base_id); + candidate.title_id = cnmt.GetTitleID(); candidate.version = cnmt.GetTitleVersion(); for (const auto& rec : cnmt.GetContentRecords()) { @@ -218,7 +214,7 @@ void ExternalContentIndexer::ParseLooseCnmtNca(VirtualFile meta_nca_file, const } } - auto& vec = m_updates_by_title[base_id]; + auto& vec = m_updates_by_title[candidate.title_id]; vec.emplace_back(std::move(candidate)); } else if (cnmt.GetType() == TitleType::AOC) { const auto dlc_title_id = cnmt.GetTitleID(); @@ -259,11 +255,11 @@ bool ExternalContentIndexer::IsMeta(const NCA& nca) { } void ExternalContentIndexer::Commit() { - // Updates: register all discovered versions per base title under unique variant TIDs, + // Updates: register all discovered versions per update title under unique variant TIDs, // and additionally register the highest version under the canonical update TID for default // usage. size_t update_variants_count = 0; - for (auto& [base_title, vec] : m_updates_by_title) { + for (auto& [update_tid, vec] : m_updates_by_title) { if (vec.empty()) continue; // sort ascending by version, dedupe identical versions (for NAND overlap, for example) @@ -281,14 +277,14 @@ void ExternalContentIndexer::Commit() { for (const auto& [rtype, file] : latest.ncas) { if (!file) continue; - const auto canonical_tid = FileSys::GetUpdateTitleID(base_title); + const auto canonical_tid = update_tid; m_provider.AddEntry(TitleType::Update, rtype, canonical_tid, file); } - // variants under update_tid + i (i starts at1 to avoid colliding with canonical) + // variants under update_tid | (i << 48) for (size_t i = 0; i < vec.size(); ++i) { const auto& upd = vec[i]; - const u64 variant_tid = FileSys::GetUpdateTitleID(base_title) + static_cast(i + 1); + const u64 variant_tid = update_tid | (static_cast(i + 1) << 48); for (const auto& [rtype, file] : upd.ncas) { if (!file) continue; diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 42e098c738..994c4f3fcc 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -150,7 +150,7 @@ std::vector EnumerateUpdateVariants(const ContentProvider& provider, u64 ba std::vector tids; const auto entries = provider.ListEntriesFilter(TitleType::Update, type); for (const auto& e : entries) { - if (GetBaseTitleID(e.title_id) == base_title_id) { + if ((e.title_id & 0xFF00FFFFFFFFFFFFULL) == GetUpdateTitleID(base_title_id)) { tids.push_back(e.title_id); } } @@ -245,7 +245,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { // Game Updates std::optional selected_update_tid; - if (!update_disabled) { + if (!update_disabled && (title_id & 0x00FF000000000800ULL) == 0) { selected_update_tid = ChooseUpdateVariant(content_provider, title_id, ContentRecordType::Program, fs_controller); if (!selected_update_tid.has_value()) { @@ -557,54 +557,73 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs auto romfs = base_romfs; // Game Updates - std::optional selected_update_tid = ChooseUpdateVariant(content_provider, title_id, type, fs_controller); - if (!selected_update_tid.has_value()) { - selected_update_tid = GetUpdateTitleID(title_id); + std::optional selected_update_tid; + const bool is_update = (title_id & 0x00FF000000000800ULL) != 0; + if (!is_update) { + selected_update_tid = ChooseUpdateVariant(content_provider, title_id, type, fs_controller); + if (!selected_update_tid.has_value()) { + selected_update_tid = GetUpdateTitleID(title_id); + } } - const auto update_raw = content_provider.GetEntryRaw(*selected_update_tid, type); + + const auto update_raw = selected_update_tid.has_value() + ? content_provider.GetEntryRaw(*selected_update_tid, type) + : nullptr; const auto& disabled = Settings::values.disabled_addons[title_id]; const auto update_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); - if (!update_disabled && update_raw != nullptr && base_nca != nullptr) { - const auto new_nca = std::make_shared(update_raw, base_nca); - if (new_nca->GetStatus() == Loader::ResultStatus::Success && - new_nca->GetRomFS() != nullptr) { - const auto version = content_provider.GetEntryVersion(*selected_update_tid).value_or(0); - LOG_DEBUG(Loader, " RomFS: Update ({}) applied successfully", FormatTitleVersion(version)); - romfs = new_nca->GetRomFS(); + bool applied_update = false; + + if (!update_disabled && base_nca != nullptr && selected_update_tid.has_value()) { + if (update_raw != nullptr) { + const auto new_nca = std::make_shared(update_raw, base_nca); + if (new_nca->GetStatus() == Loader::ResultStatus::Success && + new_nca->GetRomFS() != nullptr) { + const auto version = + content_provider.GetEntryVersion(*selected_update_tid).value_or(0); + LOG_DEBUG(Loader, " RomFS: Update ({}) applied successfully", + FormatTitleVersion(version)); + romfs = new_nca->GetRomFS(); + applied_update = true; } - } else if (!update_disabled && base_nca != nullptr) { - ContentRecordType alt_type = type; - - if (type == ContentRecordType::Program) { - alt_type = ContentRecordType::Data; - } else if (type == ContentRecordType::Data) { - alt_type = ContentRecordType::Program; } - if (alt_type != type) { - const auto alt_update_raw = - content_provider.GetEntryRaw(*selected_update_tid, alt_type); - if (alt_update_raw != nullptr) { - const auto new_nca = std::make_shared(alt_update_raw, base_nca); - if (new_nca->GetStatus() == Loader::ResultStatus::Success && - new_nca->GetRomFS() != nullptr) { - LOG_DEBUG(Loader, " RomFS: Update (fallback {}) applied successfully", - alt_type == ContentRecordType::Data ? "DATA" : "PROGRAM"); - romfs = new_nca->GetRomFS(); + if (!applied_update) { + ContentRecordType alt_type = type; + if (type == ContentRecordType::Program) { + alt_type = ContentRecordType::Data; + } else if (type == ContentRecordType::Data) { + alt_type = ContentRecordType::Program; + } + + if (alt_type != type) { + const auto alt_update_raw = + content_provider.GetEntryRaw(*selected_update_tid, alt_type); + if (alt_update_raw != nullptr) { + const auto new_nca = std::make_shared(alt_update_raw, base_nca); + if (new_nca->GetStatus() == Loader::ResultStatus::Success && + new_nca->GetRomFS() != nullptr) { + LOG_DEBUG(Loader, " RomFS: Update (fallback {}) applied successfully", + alt_type == ContentRecordType::Data ? "DATA" : "PROGRAM"); + romfs = new_nca->GetRomFS(); + applied_update = true; } else { LOG_WARNING(Loader, " RomFS: Update (fallback) NCA is not valid"); } + } } } - } else if (!update_disabled && packed_update_raw != nullptr && base_nca != nullptr) { + } + + if (!applied_update && !update_disabled && packed_update_raw != nullptr && base_nca != nullptr) { const auto new_nca = std::make_shared(packed_update_raw, base_nca); if (new_nca->GetStatus() == Loader::ResultStatus::Success && new_nca->GetRomFS() != nullptr) { LOG_INFO(Loader, " RomFS: Update (PACKED) applied successfully"); romfs = new_nca->GetRomFS(); + applied_update = true; } } @@ -617,7 +636,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs } std::vector PatchManager::GetPatches(VirtualFile update_raw) const { - if (title_id == 0) { + if (title_id == 0 || (title_id & 0x00FF000000000800ULL) != 0) { return {}; } @@ -788,7 +807,7 @@ std::vector PatchManager::GetPatches(VirtualFile update_raw) const { dlc_match.reserve(dlc_entries.size()); std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), [this](const ContentProviderEntry& entry) { - return GetBaseTitleID(entry.title_id) == title_id && + return GetBaseTitleID(entry.title_id) == GetBaseTitleID(title_id) && content_provider.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; });