diff --git a/Source/Core/Core/WiiUtils.cpp b/Source/Core/Core/WiiUtils.cpp index f721107d76..4235c9ba96 100644 --- a/Source/Core/Core/WiiUtils.cpp +++ b/Source/Core/Core/WiiUtils.cpp @@ -686,16 +686,16 @@ UpdateResult DoDiscUpdate(UpdateCallback update_callback, const std::string& ima return result; } -bool CheckNAND(IOS::HLE::Kernel& ios) +NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios) { - bool bad = false; + NANDCheckResult result; const auto es = ios.GetES(); // Check for NANDs that were used with old Dolphin versions. if (File::Exists(Common::RootUserPath(Common::FROM_CONFIGURED_ROOT) + "/sys/replace")) { ERROR_LOG(CORE, "CheckNAND: NAND was used with old versions, so it is likely to be damaged"); - bad = true; + result.bad = true; } for (const u64 title_id : es->GetInstalledTitles()) @@ -704,12 +704,12 @@ bool CheckNAND(IOS::HLE::Kernel& ios) if (!File::IsDirectory(Common::GetTitleContentPath(title_id, Common::FROM_CONFIGURED_ROOT))) { ERROR_LOG(CORE, "CheckNAND: Missing content directory for title %016" PRIx64, title_id); - bad = true; + result.bad = true; } if (!File::IsDirectory(Common::GetTitleDataPath(title_id, Common::FROM_CONFIGURED_ROOT))) { ERROR_LOG(CORE, "CheckNAND: Missing data directory for title %016" PRIx64, title_id); - bad = true; + result.bad = true; } // Check for incomplete title installs (missing ticket, TMD or contents). @@ -717,13 +717,26 @@ bool CheckNAND(IOS::HLE::Kernel& ios) if (!IOS::ES::IsDiscTitle(title_id) && !ticket.IsValid()) { ERROR_LOG(CORE, "CheckNAND: Missing ticket for title %016" PRIx64, title_id); - bad = true; + result.titles_to_remove.insert(title_id); + result.bad = true; } + const std::string content_dir = + Common::GetTitleContentPath(title_id, Common::FROM_CONFIGURED_ROOT); + const auto tmd = es->FindInstalledTMD(title_id); if (!tmd.IsValid()) { - WARN_LOG(CORE, "CheckNAND: Missing TMD for title %016" PRIx64, title_id); + if (File::ScanDirectoryTree(content_dir, false).children.empty()) + { + WARN_LOG(CORE, "CheckNAND: Missing TMD for title %016" PRIx64, title_id); + } + else + { + ERROR_LOG(CORE, "CheckNAND: Missing TMD for title %016" PRIx64, title_id); + result.titles_to_remove.insert(title_id); + result.bad = true; + } // Further checks require the TMD to be valid. continue; } @@ -736,11 +749,12 @@ bool CheckNAND(IOS::HLE::Kernel& ios) (tmd.GetTitleFlags() & IOS::ES::TitleFlags::TITLE_TYPE_WFS_MAYBE) == 0) { ERROR_LOG(CORE, "CheckNAND: Missing contents for title %016" PRIx64, title_id); - bad = true; + result.titles_to_remove.insert(title_id); + result.bad = true; } } - return !bad; + return result; } bool RepairNAND(IOS::HLE::Kernel& ios) @@ -759,15 +773,18 @@ bool RepairNAND(IOS::HLE::Kernel& ios) File::CreateDir(content_dir); File::CreateDir(data_dir); - // If there's nothing in the content/data directories and no ticket, + // If there's nothing in the content directory and no ticket, // this title shouldn't exist at all on the NAND. - if (File::ScanDirectoryTree(content_dir, false).children.empty() && - File::ScanDirectoryTree(data_dir, false).children.empty() && - !DiscIO::FindSignedTicket(title_id).IsValid()) + // WARNING: This will delete associated save data! + const auto content_files = File::ScanDirectoryTree(content_dir, false).children; + const bool has_no_tmd_but_contents = + !es->FindInstalledTMD(title_id).IsValid() && !content_files.empty(); + if (has_no_tmd_but_contents || !DiscIO::FindSignedTicket(title_id).IsValid()) { - es->DeleteTitle(title_id); + const std::string title_dir = Common::GetTitlePath(title_id, Common::FROM_CONFIGURED_ROOT); + File::DeleteDirRecursively(title_dir); } } - return CheckNAND(ios); + return !CheckNAND(ios).bad; } } diff --git a/Source/Core/Core/WiiUtils.h b/Source/Core/Core/WiiUtils.h index 2320b1f154..0047e2c63d 100644 --- a/Source/Core/Core/WiiUtils.h +++ b/Source/Core/Core/WiiUtils.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "Common/CommonTypes.h" @@ -58,6 +59,11 @@ UpdateResult DoOnlineUpdate(UpdateCallback update_callback, const std::string& r UpdateResult DoDiscUpdate(UpdateCallback update_callback, const std::string& image_path); // Check the emulated NAND for common issues. -bool CheckNAND(IOS::HLE::Kernel& ios); +struct NANDCheckResult +{ + bool bad = false; + std::unordered_set titles_to_remove; +}; +NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios); bool RepairNAND(IOS::HLE::Kernel& ios); } diff --git a/Source/Core/DolphinQt2/MenuBar.cpp b/Source/Core/DolphinQt2/MenuBar.cpp index b7b7410e6f..401f7f4d7e 100644 --- a/Source/Core/DolphinQt2/MenuBar.cpp +++ b/Source/Core/DolphinQt2/MenuBar.cpp @@ -22,6 +22,7 @@ #include "Core/IOS/IOS.h" #include "Core/Movie.h" #include "Core/State.h" +#include "Core/TitleDatabase.h" #include "Core/WiiUtils.h" #include "DiscIO/NANDImporter.h" #include "DolphinQt2/AboutDialog.h" @@ -537,21 +538,37 @@ void MenuBar::ExportWiiSaves() void MenuBar::CheckNAND() { IOS::HLE::Kernel ios; - if (WiiUtils::CheckNAND(ios)) + WiiUtils::NANDCheckResult result = WiiUtils::CheckNAND(ios); + if (!result.bad) { QMessageBox::information(this, tr("NAND Check"), tr("No issues have been detected.")); return; } - if (QMessageBox::question( - this, tr("NAND Check"), - tr("The emulated NAND is damaged. System titles such as the Wii Menu and " - "the Wii Shop Channel may not work correctly.\n\n" - "Do you want to try to repair the NAND?")) != QMessageBox::Yes) + QString message = tr("The emulated NAND is damaged. System titles such as the Wii Menu and " + "the Wii Shop Channel may not work correctly.\n\n" + "Do you want to try to repair the NAND?"); + if (!result.titles_to_remove.empty()) { - return; + message += tr("\n\nWARNING: Fixing this NAND requires the deletion of titles that have " + "incomplete data on the NAND, including all associated save data. " + "By continuing, the following title(s) will be removed:\n\n"); + Core::TitleDatabase title_db; + for (const u64 title_id : result.titles_to_remove) + { + const std::string name = title_db.GetTitleName(title_id); + message += !name.empty() ? + QStringLiteral("%1 (%2)") + .arg(QString::fromStdString(name)) + .arg(title_id, 16, 16, QLatin1Char('0')) : + QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char('0')); + message += QStringLiteral("\n"); + } } + if (QMessageBox::question(this, tr("NAND Check"), message) != QMessageBox::Yes) + return; + if (WiiUtils::RepairNAND(ios)) { QMessageBox::information(this, tr("NAND Check"), tr("The NAND has been repaired.")); diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index cda03e9c6d..592f338cd2 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -1312,20 +1312,34 @@ void CFrame::OnImportBootMiiBackup(wxCommandEvent& WXUNUSED(event)) void CFrame::OnCheckNAND(wxCommandEvent&) { IOS::HLE::Kernel ios; - if (WiiUtils::CheckNAND(ios)) + WiiUtils::NANDCheckResult result = WiiUtils::CheckNAND(ios); + if (!result.bad) { wxMessageBox(_("No issues have been detected."), _("NAND Check"), wxOK | wxICON_INFORMATION); return; } - if (wxMessageBox("The emulated NAND is damaged. System titles such as the Wii Menu and " - "the Wii Shop Channel may not work correctly.\n\n" - "Do you want to try to repair the NAND?", - _("NAND Check"), wxYES_NO) != wxYES) + wxString message = _("The emulated NAND is damaged. System titles such as the Wii Menu and " + "the Wii Shop Channel may not work correctly.\n\n" + "Do you want to try to repair the NAND?"); + if (!result.titles_to_remove.empty()) { - return; + message += _("\n\nWARNING: Fixing this NAND requires the deletion of titles that have " + "incomplete data on the NAND, including all associated save data. " + "By continuing, the following title(s) will be removed:\n\n"); + Core::TitleDatabase title_db; + for (const u64 title_id : result.titles_to_remove) + { + const std::string name = title_db.GetTitleName(title_id); + message += !name.empty() ? StringFromFormat("%s (%016" PRIx64 ")", name.c_str(), title_id) : + StringFromFormat("%016" PRIx64, title_id); + message += "\n"; + } } + if (wxMessageBox(message, _("NAND Check"), wxYES_NO) != wxYES) + return; + if (WiiUtils::RepairNAND(ios)) { wxMessageBox(_("The NAND has been repaired."), _("NAND Check"), wxOK | wxICON_INFORMATION);