mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-08-21 18:05:20 -05:00
Merge pull request #14550 from Tilka/import_nand
Improve NAND import progress dialog
This commit is contained in:
@@ -102,10 +102,10 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_importNANDB
|
||||
|
||||
return DiscIO::NANDImporter().ImportNANDBin(
|
||||
path,
|
||||
[] {
|
||||
// This callback gets called every now and then in case we want to update the GUI. However,
|
||||
// we have no way of knowing what the current progress is, so we can't do anything
|
||||
// especially useful. DolphinQt chooses to show the elapsed time, for reference.
|
||||
[](DiscIO::NANDImporter::Step, int, int) {
|
||||
// This callback gets called every now and then in case we want to update the GUI.
|
||||
// TODO
|
||||
return false;
|
||||
},
|
||||
[] {
|
||||
// This callback gets called if the NAND file does not have decryption keys appended to it.
|
||||
|
||||
@@ -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<void()> update_callback,
|
||||
void NANDImporter::ImportNANDBin(const std::string& path_to_bin, UpdateCallback update_callback,
|
||||
const std::function<std::string()>& 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<Type>(entry.mode & 3);
|
||||
if (type == Type::File)
|
||||
{
|
||||
if (m_update_callback(Step::Extracting, m_progress_cur++, m_progress_max))
|
||||
return false;
|
||||
std::vector<u8> 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<u8> NANDImporter::GetEntryData(const NANDFSTEntry& entry) const
|
||||
|
||||
@@ -23,10 +23,18 @@ public:
|
||||
NANDImporter();
|
||||
~NANDImporter();
|
||||
|
||||
enum class Step
|
||||
{
|
||||
Loading,
|
||||
Extracting,
|
||||
};
|
||||
// Return true to cancel.
|
||||
using UpdateCallback = std::function<bool(Step step, int cur, int max)>;
|
||||
|
||||
// Extract a NAND image to the configured NAND root.
|
||||
// If the associated OTP/SEEPROM dump (keys.bin) is not included in the image,
|
||||
// get_otp_dump_path will be called to get a path to it.
|
||||
void ImportNANDBin(const std::string& path_to_bin, std::function<void()> update_callback,
|
||||
void ImportNANDBin(const std::string& path_to_bin, UpdateCallback update_callback,
|
||||
const std::function<std::string()>& get_otp_dump_path);
|
||||
bool ExtractCertificates();
|
||||
|
||||
@@ -67,9 +75,10 @@ private:
|
||||
bool ReadNANDBin(const std::string& path_to_bin,
|
||||
const std::function<std::string()>& get_otp_dump_path);
|
||||
bool FindSuperblock();
|
||||
bool ExtractFiles();
|
||||
std::string GetPath(const NANDFSTEntry& entry, const std::string& parent_path);
|
||||
std::string FormatDebugString(const NANDFSTEntry& entry);
|
||||
void ProcessEntry(u16 entry_number, const std::string& parent_path);
|
||||
bool ProcessEntry(u16 entry_number, const std::string& parent_path);
|
||||
std::vector<u8> GetEntryData(const NANDFSTEntry& entry) const;
|
||||
void ExportKeys();
|
||||
|
||||
@@ -78,7 +87,9 @@ private:
|
||||
std::vector<u8> m_nand_keys;
|
||||
std::unique_ptr<Common::AES::Context> m_aes_ctx;
|
||||
std::unique_ptr<NANDSuperblock> m_superblock;
|
||||
std::function<void()> m_update_callback;
|
||||
UpdateCallback m_update_callback;
|
||||
u16 m_progress_cur = 0;
|
||||
u16 m_progress_max = 0;
|
||||
};
|
||||
} // namespace DiscIO
|
||||
|
||||
|
||||
@@ -1881,20 +1881,24 @@ void MainWindow::OnImportNANDBackup()
|
||||
return;
|
||||
|
||||
ParallelProgressDialog dialog(this);
|
||||
dialog.GetRaw()->setMinimum(0);
|
||||
dialog.GetRaw()->setMaximum(0);
|
||||
dialog.GetRaw()->setLabelText(tr("Importing NAND backup"));
|
||||
dialog.GetRaw()->setCancelButton(nullptr);
|
||||
|
||||
auto beginning = QDateTime::currentDateTime().toMSecsSinceEpoch();
|
||||
dialog.GetRaw()->setWindowTitle(tr("Importing NAND backup"));
|
||||
|
||||
std::future<void> result = std::async(std::launch::async, [&] {
|
||||
DiscIO::NANDImporter().ImportNANDBin(
|
||||
file.toStdString(),
|
||||
[&dialog, beginning] {
|
||||
dialog.SetLabelText(
|
||||
tr("Importing NAND backup\n Time elapsed: %1s")
|
||||
.arg((QDateTime::currentDateTime().toMSecsSinceEpoch() - beginning) / 1000));
|
||||
[&dialog](DiscIO::NANDImporter::Step step, u32 cur, u32 max) {
|
||||
switch (step)
|
||||
{
|
||||
case DiscIO::NANDImporter::Step::Loading:
|
||||
dialog.SetLabelText(tr("Loading NAND..."));
|
||||
break;
|
||||
case DiscIO::NANDImporter::Step::Extracting:
|
||||
dialog.SetLabelText(tr("Extracting NAND..."));
|
||||
break;
|
||||
}
|
||||
dialog.SetValue(cur);
|
||||
dialog.SetMaximum(max);
|
||||
return dialog.WasCanceled();
|
||||
},
|
||||
[this] {
|
||||
std::optional<std::string> keys_file = RunOnObject(this, [this] {
|
||||
|
||||
Reference in New Issue
Block a user