From f06aef4f8382d258e16bac2ea8a19aa48dda72a0 Mon Sep 17 00:00:00 2001 From: Tillmann Karras Date: Fri, 3 Apr 2026 23:50:59 +0100 Subject: Improve NAND import progress dialog Now with cancel button and an actual progress bar. For simplicity, we do two passes on the progress bar, one for loading the NAND into memory and one for extracting it. The user directory is likely on an SSD, making the extraction pass invisibly fast. --- Source/Core/DiscIO/NANDImporter.cpp | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'Source/Core/DiscIO/NANDImporter.cpp') diff --git a/Source/Core/DiscIO/NANDImporter.cpp b/Source/Core/DiscIO/NANDImporter.cpp index 54122d0626..f4c94bbb2c 100644 --- a/Source/Core/DiscIO/NANDImporter.cpp +++ b/Source/Core/DiscIO/NANDImporter.cpp @@ -23,8 +23,7 @@ NANDImporter::NANDImporter() : m_nand_root(File::GetUserPath(D_WIIROOT_IDX)) } NANDImporter::~NANDImporter() = default; -void NANDImporter::ImportNANDBin(const std::string& path_to_bin, - std::function update_callback, +void NANDImporter::ImportNANDBin(const std::string& path_to_bin, UpdateCallback update_callback, const std::function& get_otp_dump_path) { m_update_callback = std::move(update_callback); @@ -35,8 +34,13 @@ void NANDImporter::ImportNANDBin(const std::string& path_to_bin, return; ExportKeys(); - ProcessEntry(0, ""); + + if (!ExtractFiles()) + return; + ExtractCertificates(); + + std::ignore = m_update_callback(Step::Extracting, m_progress_cur, m_progress_max); } bool NANDImporter::ReadNANDBin(const std::string& path_to_bin, @@ -60,10 +64,8 @@ bool NANDImporter::ReadNANDBin(const std::string& path_to_bin, for (size_t i = 0; i < NAND_TOTAL_BLOCKS; i++) { - // Instead of updating on every cycle, we only update every 1000 cycles for a balance between - // not updating fast enough vs updating too fast - if (i % 1000 == 0) - m_update_callback(); + if (m_update_callback(Step::Loading, int(i), int(NAND_TOTAL_BLOCKS))) + return false; file.ReadBytes(&m_nand[i * NAND_BLOCK_SIZE], NAND_BLOCK_SIZE); @@ -121,6 +123,14 @@ bool NANDImporter::FindSuperblock() return true; } +bool NANDImporter::ExtractFiles() +{ + constexpr u16 CLUSTER_CHAIN_END = 0xFFFB; + m_progress_cur = 0; + m_progress_max = std::ranges::count(m_superblock->fat, CLUSTER_CHAIN_END); + return ProcessEntry(0, ""); +} + std::string NANDImporter::GetPath(const NANDFSTEntry& entry, const std::string& parent_path) { std::string name(entry.name, strnlen(entry.name, sizeof(NANDFSTEntry::name))); @@ -131,25 +141,26 @@ std::string NANDImporter::GetPath(const NANDFSTEntry& entry, const std::string& return parent_path + '/' + name; } -void NANDImporter::ProcessEntry(u16 entry_number, const std::string& parent_path) +bool NANDImporter::ProcessEntry(u16 entry_number, const std::string& parent_path) { while (entry_number != 0xffff) { if (entry_number >= m_superblock->fst.size()) { ERROR_LOG_FMT(DISCIO, "FST entry number {} out of range", entry_number); - return; + return false; } const NANDFSTEntry entry = m_superblock->fst[entry_number]; const std::string path = GetPath(entry, parent_path); INFO_LOG_FMT(DISCIO, "Entry: {} Path: {}", entry, path); - m_update_callback(); Type type = static_cast(entry.mode & 3); if (type == Type::File) { + if (m_update_callback(Step::Extracting, m_progress_cur++, m_progress_max)) + return false; std::vector data = GetEntryData(entry); File::IOFile file(m_nand_root + path, "wb"); file.WriteBytes(data.data(), data.size()); @@ -157,7 +168,8 @@ void NANDImporter::ProcessEntry(u16 entry_number, const std::string& parent_path else if (type == Type::Directory) { File::CreateDir(m_nand_root + path); - ProcessEntry(entry.sub, path); + if (!ProcessEntry(entry.sub, path)) + return false; } else { @@ -166,6 +178,7 @@ void NANDImporter::ProcessEntry(u16 entry_number, const std::string& parent_path entry_number = entry.sib; } + return true; } std::vector NANDImporter::GetEntryData(const NANDFSTEntry& entry) const -- cgit v1.2.3