From 695ae6a71f6680f04215e739a3b8c188b556b7df Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 28 Oct 2025 21:02:10 +0100 Subject: [PATCH] fix version display + loading --- src/core/file_sys/patch_manager.cpp | 189 ++++++++++++++++++---------- src/yuzu/game_list_worker.cpp | 4 + 2 files changed, 130 insertions(+), 63 deletions(-) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index c1a0f23131..3b3194cb0d 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "common/hex_util.h" #include "common/logging/log.h" @@ -65,6 +67,30 @@ std::string FormatTitleVersion(u32 version, return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); } +static std::array ParseVersionComponents(std::string_view label) { + std::array out{0, 0, 0, 0}; + if (!label.empty() && (label.front() == 'v' || label.front() == 'V')) { + label.remove_prefix(1); + } + size_t part = 0; + size_t start = 0; + std::string s(label); + while (part < out.size() && start < s.size()) { + size_t dot = s.find('.', start); + auto token = s.substr(start, dot == std::string::npos ? std::string::npos : dot - start); + try { + out[part] = std::stoi(token); + } catch (...) { + out[part] = 0; + } + ++part; + if (dot == std::string::npos) + break; + start = dot + 1; + } + return out; +} + // Returns a directory with name matching name case-insensitive. Returns nullptr if directory // doesn't have a directory with name. VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) { @@ -563,6 +589,30 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs } else { LOG_WARNING(Loader, " RomFS: Update NCA is not valid"); } + } 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_INFO(Loader, " RomFS: Update (fallback {}) applied successfully", + alt_type == ContentRecordType::Data ? "DATA" : "PROGRAM"); + romfs = new_nca->GetRomFS(); + } else { + LOG_WARNING(Loader, " RomFS: Update (fallback) NCA is not valid"); + } + } + } } else if (!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 && @@ -590,74 +640,87 @@ std::vector PatchManager::GetPatches(VirtualFile update_raw) const { std::vector out; const auto& disabled = Settings::values.disabled_addons[title_id]; - // Game Updates (default latest) - const auto update_tid = GetUpdateTitleID(title_id); - PatchManager update{update_tid, fs_controller, content_provider}; - const auto metadata = update.GetControlMetadata(); - - const auto update_disabled = - std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); - Patch update_patch = {.enabled = !update_disabled, - .name = "Update", - .version = "", - .type = PatchType::Update, - .program_id = title_id, - .title_id = title_id}; + auto variant_tids = + EnumerateUpdateVariants(content_provider, title_id, ContentRecordType::Program); + { + auto data_tids = + EnumerateUpdateVariants(content_provider, title_id, ContentRecordType::Data); + variant_tids.insert(variant_tids.end(), data_tids.begin(), data_tids.end()); + auto control_tids = + EnumerateUpdateVariants(content_provider, title_id, ContentRecordType::Control); + variant_tids.insert(variant_tids.end(), control_tids.begin(), control_tids.end()); + std::sort(variant_tids.begin(), variant_tids.end()); + variant_tids.erase(std::unique(variant_tids.begin(), variant_tids.end()), + variant_tids.end()); + } + + if (!variant_tids.empty()) { + const auto update_disabled = + std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); + + out.push_back({.enabled = !update_disabled, + .name = "Update", + .version = "", + .type = PatchType::Update, + .program_id = title_id, + .title_id = title_id}); + + std::optional selected_variant_tid; + if (!update_disabled) { + const bool has_pref = HasVariantPreference(Settings::values.disabled_addons[title_id]); + if (has_pref) { + selected_variant_tid = ChooseUpdateVariant( + content_provider, title_id, ContentRecordType::Program, fs_controller); + } else { + selected_variant_tid = GetUpdateTitleID(title_id); + } + } - out.push_back(update_patch); + std::vector> variant_labels; + variant_labels.reserve(variant_tids.size()); - std::optional selected_variant_tid; - if (!update_disabled) { - const bool has_pref = HasVariantPreference(Settings::values.disabled_addons[title_id]); - if (has_pref) { - selected_variant_tid = ChooseUpdateVariant(content_provider, title_id, - ContentRecordType::Program, fs_controller); - } else { - selected_variant_tid = GetUpdateTitleID(title_id); + for (const auto tid : variant_tids) { + variant_labels.emplace_back(GetUpdateVersionLabel(tid, fs_controller, content_provider), + tid); } - } - const auto variant_tids = - EnumerateUpdateVariants(content_provider, title_id, ContentRecordType::Program); - std::vector> variant_labels; - variant_labels.reserve(variant_tids.size()); - for (const auto tid : variant_tids) { - variant_labels.emplace_back(GetUpdateVersionLabel(tid, fs_controller, content_provider), - tid); - } - std::sort(variant_labels.begin(), variant_labels.end(), [this](auto const& a, auto const& b) { - const auto va = content_provider.GetEntryVersion(a.second).value_or(0); - const auto vb = content_provider.GetEntryVersion(b.second).value_or(0); - if (va != vb) - return va > vb; - return a.first < b.first; - }); - std::set seen_versions; - if (!out.empty() && !out.back().version.empty()) { - auto ver = out.back().version; - if (!ver.empty() && (ver.front() == 'v' || ver.front() == 'V')) { - ver.erase(ver.begin()); - } - seen_versions.insert(std::move(ver)); - } - for (const auto& [label, tid] : variant_labels) { - std::string version = label; - if (!version.empty() && (version.front() == 'v' || version.front() == 'V')) { - version.erase(version.begin()); - } - if (seen_versions.find(version) != seen_versions.end()) { - continue; + std::sort(variant_labels.begin(), variant_labels.end(), + [this](auto const& a, auto const& b) { + const auto va = content_provider.GetEntryVersion(a.second).value_or(0); + const auto vb = content_provider.GetEntryVersion(b.second).value_or(0); + + if (va != vb) + return va > vb; + + const auto ca = ParseVersionComponents(a.first); + const auto cb = ParseVersionComponents(b.first); + + if (ca != cb) + return ca > cb; + + return a.first > b.first; + }); + + std::set seen_versions; + for (const auto& [label, tid] : variant_labels) { + std::string version = label; + if (!version.empty() && (version.front() == 'v' || version.front() == 'V')) { + version.erase(version.begin()); + } + if (seen_versions.find(version) != seen_versions.end()) { + continue; + } + const bool is_selected = + selected_variant_tid.has_value() && tid == *selected_variant_tid; + const bool variant_disabled = update_disabled || !is_selected; + out.push_back({.enabled = !variant_disabled, + .name = "Update", + .version = version, + .type = PatchType::Update, + .program_id = title_id, + .title_id = tid}); + seen_versions.insert(version); } - const auto toggle_name = fmt::format("Update {}", label); - const bool is_selected = selected_variant_tid.has_value() && tid == *selected_variant_tid; - const bool variant_disabled = update_disabled || !is_selected; - out.push_back({.enabled = !variant_disabled, - .name = "Update", - .version = version, - .type = PatchType::Update, - .program_id = title_id, - .title_id = tid}); - seen_versions.insert(version); } // General Mods (LayeredFS and IPS) diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 4542b63100..f4c7877062 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -173,6 +173,10 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, continue; } + if (is_update && patch.version.empty()) { + continue; + } + const QString type = QString::fromStdString(patch.enabled ? patch.name : "[D] " + patch.name);