diff --git a/Source/Core/DiscIO/NANDImporter.cpp b/Source/Core/DiscIO/NANDImporter.cpp index 5a9983ce03..94a15a84af 100644 --- a/Source/Core/DiscIO/NANDImporter.cpp +++ b/Source/Core/DiscIO/NANDImporter.cpp @@ -27,11 +27,12 @@ NANDImporter::NANDImporter() = default; NANDImporter::~NANDImporter() = default; void NANDImporter::ImportNANDBin(const std::string& path_to_bin, - std::function update_callback) + std::function update_callback, + std::function get_otp_dump_path) { m_update_callback = std::move(update_callback); - if (!ReadNANDBin(path_to_bin)) + if (!ReadNANDBin(path_to_bin, get_otp_dump_path)) return; const std::string nand_root = File::GetUserPath(D_WIIROOT_IDX); @@ -45,20 +46,20 @@ void NANDImporter::ImportNANDBin(const std::string& path_to_bin, ExtractCertificates(nand_root); } -bool NANDImporter::ReadNANDBin(const std::string& path_to_bin) +bool NANDImporter::ReadNANDBin(const std::string& path_to_bin, + std::function get_otp_dump_path) { constexpr size_t NAND_TOTAL_BLOCKS = 0x40000; constexpr size_t NAND_BLOCK_SIZE = 0x800; constexpr size_t NAND_ECC_BLOCK_SIZE = 0x40; constexpr size_t NAND_BIN_SIZE = - (NAND_BLOCK_SIZE + NAND_ECC_BLOCK_SIZE) * NAND_TOTAL_BLOCKS + NAND_KEYS_SIZE; // 0x21000400 + (NAND_BLOCK_SIZE + NAND_ECC_BLOCK_SIZE) * NAND_TOTAL_BLOCKS; // 0x21000000 File::IOFile file(path_to_bin, "rb"); - if (file.GetSize() != NAND_BIN_SIZE) + const u64 image_size = file.GetSize(); + if (image_size != NAND_BIN_SIZE + NAND_KEYS_SIZE && image_size != NAND_BIN_SIZE) { - PanicAlertT("This file does not look like a BootMii NAND backup. (0x%" PRIx64 - " does not equal 0x%zx)", - file.GetSize(), NAND_BIN_SIZE); + PanicAlertT("This file does not look like a BootMii NAND backup."); return false; } @@ -76,8 +77,20 @@ bool NANDImporter::ReadNANDBin(const std::string& path_to_bin) } m_nand_keys.resize(NAND_KEYS_SIZE); - file.ReadBytes(m_nand_keys.data(), NAND_KEYS_SIZE); - return true; + + // Read the OTP/SEEPROM dump. + // If it is not included in the NAND image, get a path to the dump and read key data from it. + if (image_size == NAND_BIN_SIZE) + { + const std::string otp_dump_path = get_otp_dump_path(); + if (otp_dump_path.empty()) + return false; + File::IOFile keys_file{otp_dump_path, "rb"}; + return keys_file.ReadBytes(m_nand_keys.data(), NAND_KEYS_SIZE); + } + + // Otherwise, just read the key data from the NAND image. + return file.ReadBytes(m_nand_keys.data(), NAND_KEYS_SIZE); } void NANDImporter::FindSuperblock() diff --git a/Source/Core/DiscIO/NANDImporter.h b/Source/Core/DiscIO/NANDImporter.h index 09ed7034cb..a6b55854b8 100644 --- a/Source/Core/DiscIO/NANDImporter.h +++ b/Source/Core/DiscIO/NANDImporter.h @@ -18,7 +18,11 @@ public: NANDImporter(); ~NANDImporter(); - void ImportNANDBin(const std::string& path_to_bin, std::function update_callback); + // 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 update_callback, + std::function get_otp_dump_path); bool ExtractCertificates(const std::string& nand_root); private: @@ -38,7 +42,7 @@ private: }; #pragma pack(pop) - bool ReadNANDBin(const std::string& path_to_bin); + bool ReadNANDBin(const std::string& path_to_bin, std::function get_otp_dump_path); void FindSuperblock(); std::string GetPath(const NANDFSTEntry& entry, const std::string& parent_path); std::string FormatDebugString(const NANDFSTEntry& entry); diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp index 304327a4b7..325a096600 100644 --- a/Source/Core/DolphinQt2/MainWindow.cpp +++ b/Source/Core/DolphinQt2/MainWindow.cpp @@ -51,6 +51,7 @@ #include "DolphinQt2/NetPlay/NetPlayDialog.h" #include "DolphinQt2/NetPlay/NetPlaySetupDialog.h" #include "DolphinQt2/QtUtils/QueueOnObject.h" +#include "DolphinQt2/QtUtils/RunOnObject.h" #include "DolphinQt2/QtUtils/WindowActivationEventFilter.h" #include "DolphinQt2/Resources.h" #include "DolphinQt2/Settings.h" @@ -879,13 +880,24 @@ void MainWindow::OnImportNANDBackup() auto beginning = QDateTime::currentDateTime().toMSecsSinceEpoch(); auto result = std::async(std::launch::async, [&] { - DiscIO::NANDImporter().ImportNANDBin(file.toStdString(), [&dialog, beginning] { - QueueOnObject(dialog, [&dialog, beginning] { - dialog->setLabelText( - tr("Importing NAND backup\n Time elapsed: %1s") - .arg((QDateTime::currentDateTime().toMSecsSinceEpoch() - beginning) / 1000)); - }); - }); + DiscIO::NANDImporter().ImportNANDBin( + file.toStdString(), + [&dialog, beginning] { + QueueOnObject(dialog, [&dialog, beginning] { + dialog->setLabelText( + tr("Importing NAND backup\n Time elapsed: %1s") + .arg((QDateTime::currentDateTime().toMSecsSinceEpoch() - beginning) / 1000)); + }); + }, + [this] { + return RunOnObject(this, [this] { + return QFileDialog::getOpenFileName(this, tr("Select the OTP/SEEPROM dump"), + QDir::currentPath(), + tr("BootMii OTP/SEEPROM dump (*.bin);;" + "All Files (*)")) + .toStdString(); + }); + }); QueueOnObject(dialog, &QProgressDialog::close); }); diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index 05b819a99b..752448ea95 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -1301,7 +1301,14 @@ void CFrame::OnImportBootMiiBackup(wxCommandEvent& WXUNUSED(event)) wxProgressDialog dialog(_("Importing NAND backup"), _("Working..."), 100, this, wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_SMOOTH); - DiscIO::NANDImporter().ImportNANDBin(file_name, [&dialog] { dialog.Pulse(); }); + DiscIO::NANDImporter().ImportNANDBin( + file_name, [&dialog] { dialog.Pulse(); }, + [this] { + return WxStrToStr(wxFileSelector( + _("Select the OTP/SEEPROM dump"), wxEmptyString, wxEmptyString, wxEmptyString, + _("BootMii OTP/SEEPROM dump (*.bin)") + "|*.bin|" + wxGetTranslation(wxALL_FILES), + wxFD_OPEN | wxFD_PREVIEW | wxFD_FILE_MUST_EXIST, this)); + }); wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM}); }