From 2974c56e500428ef6a769d3eb7f0f17ac74b84de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 1 Oct 2017 12:37:17 +0200 Subject: [PATCH 1/4] ESFormats: Make Content easier to compare --- Source/Core/Core/IOS/ES/Formats.cpp | 11 +++++++++++ Source/Core/Core/IOS/ES/Formats.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index aa71ad32ee..0f5af9833c 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -67,6 +67,17 @@ bool Content::IsOptional() const return (type & 0x4000) != 0; } +bool operator==(const Content& lhs, const Content& rhs) +{ + auto fields = [](const Content& c) { return std::tie(c.id, c.index, c.type, c.size, c.sha1); }; + return fields(lhs) == fields(rhs); +} + +bool operator!=(const Content& lhs, const Content& rhs) +{ + return !operator==(lhs, rhs); +} + SignedBlobReader::SignedBlobReader(const std::vector& bytes) : m_bytes(bytes) { } diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index cbacb6cfcb..51d67f91d3 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -93,6 +93,8 @@ struct Content std::array sha1; }; static_assert(sizeof(Content) == 36, "Content has the wrong size"); +bool operator==(const Content&, const Content&); +bool operator!=(const Content&, const Content&); struct TimeLimit { From 239167245dfea23f563429c30d07b3752dd83cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Tue, 3 Oct 2017 16:45:59 +0200 Subject: [PATCH 2/4] Add a way to check the NAND for issues and fix them Old versions of Dolphin are so broken regarding NAND handling that we need this to repair common issues and avoid issues with titles like the System Menu or the Wii Shop. This isn't an exhaustive check, but this will catch most issues and offer to fix them automatically (if possible). --- Source/Core/Core/WiiUtils.cpp | 85 +++++++++++++++++++++++++++ Source/Core/Core/WiiUtils.h | 12 ++++ Source/Core/DolphinQt2/MenuBar.cpp | 33 ++++++++++- Source/Core/DolphinQt2/MenuBar.h | 2 + Source/Core/DolphinWX/Frame.h | 1 + Source/Core/DolphinWX/FrameTools.cpp | 29 +++++++++ Source/Core/DolphinWX/Globals.h | 1 + Source/Core/DolphinWX/MainMenuBar.cpp | 3 +- 8 files changed, 164 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/WiiUtils.cpp b/Source/Core/Core/WiiUtils.cpp index 4ce3f03103..f721107d76 100644 --- a/Source/Core/Core/WiiUtils.cpp +++ b/Source/Core/Core/WiiUtils.cpp @@ -685,4 +685,89 @@ UpdateResult DoDiscUpdate(UpdateCallback update_callback, const std::string& ima DiscIO::NANDContentManager::Access().ClearCache(); return result; } + +bool CheckNAND(IOS::HLE::Kernel& ios) +{ + bool bad = false; + 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; + } + + for (const u64 title_id : es->GetInstalledTitles()) + { + // Check for missing title sub directories. + 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; + } + 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; + } + + // Check for incomplete title installs (missing ticket, TMD or contents). + const auto ticket = DiscIO::FindSignedTicket(title_id); + if (!IOS::ES::IsDiscTitle(title_id) && !ticket.IsValid()) + { + ERROR_LOG(CORE, "CheckNAND: Missing ticket for title %016" PRIx64, title_id); + bad = true; + } + + const auto tmd = es->FindInstalledTMD(title_id); + if (!tmd.IsValid()) + { + WARN_LOG(CORE, "CheckNAND: Missing TMD for title %016" PRIx64, title_id); + // Further checks require the TMD to be valid. + continue; + } + + const auto installed_contents = es->GetStoredContentsFromTMD(tmd); + const bool is_installed = std::any_of(installed_contents.begin(), installed_contents.end(), + [](const auto& content) { return !content.IsShared(); }); + + if (is_installed && installed_contents != tmd.GetContents() && + (tmd.GetTitleFlags() & IOS::ES::TitleFlags::TITLE_TYPE_WFS_MAYBE) == 0) + { + ERROR_LOG(CORE, "CheckNAND: Missing contents for title %016" PRIx64, title_id); + bad = true; + } + } + + return !bad; +} + +bool RepairNAND(IOS::HLE::Kernel& ios) +{ + const auto es = ios.GetES(); + + // Delete an old, unneeded file + File::Delete(Common::RootUserPath(Common::FROM_CONFIGURED_ROOT) + "/sys/replace"); + + for (const u64 title_id : es->GetInstalledTitles()) + { + // Create missing title sub directories. + const std::string content_dir = + Common::GetTitleContentPath(title_id, Common::FROM_CONFIGURED_ROOT); + const std::string data_dir = Common::GetTitleDataPath(title_id, Common::FROM_CONFIGURED_ROOT); + File::CreateDir(content_dir); + File::CreateDir(data_dir); + + // If there's nothing in the content/data directories 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()) + { + es->DeleteTitle(title_id); + } + } + return CheckNAND(ios); +} } diff --git a/Source/Core/Core/WiiUtils.h b/Source/Core/Core/WiiUtils.h index 49e8175bc2..2320b1f154 100644 --- a/Source/Core/Core/WiiUtils.h +++ b/Source/Core/Core/WiiUtils.h @@ -12,6 +12,14 @@ // Small utility functions for common Wii related tasks. +namespace IOS +{ +namespace HLE +{ +class Kernel; +} +} + namespace WiiUtils { bool InstallWAD(const std::string& wad_path); @@ -48,4 +56,8 @@ UpdateResult DoOnlineUpdate(UpdateCallback update_callback, const std::string& r // Perform a disc update with behaviour similar to the System Menu. UpdateResult DoDiscUpdate(UpdateCallback update_callback, const std::string& image_path); + +// Check the emulated NAND for common issues. +bool 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 6fb6cc79a5..b7b7410e6f 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/WiiUtils.h" #include "DiscIO/NANDImporter.h" #include "DolphinQt2/AboutDialog.h" #include "DolphinQt2/GameList/GameFile.h" @@ -114,7 +115,7 @@ void MenuBar::AddToolsMenu() AddAction(tools_menu, QStringLiteral(""), this, [this] { emit BootWiiSystemMenu(); }); m_import_backup = AddAction(gc_ipl, tr("Import BootMii NAND Backup..."), this, [this] { emit ImportNANDBackup(); }); - + m_check_nand = AddAction(tools_menu, tr("Check NAND..."), this, &MenuBar::CheckNAND); m_extract_certificates = AddAction(tools_menu, tr("Extract Certificates from NAND"), this, &MenuBar::NANDExtractCertificates); @@ -473,6 +474,7 @@ void MenuBar::UpdateToolsMenu(bool emulation_started) m_pal_ipl->setEnabled(!emulation_started && File::Exists(SConfig::GetInstance().GetBootROMPath(EUR_DIR))); m_import_backup->setEnabled(!emulation_started); + m_check_nand->setEnabled(!emulation_started); if (!emulation_started) { @@ -532,6 +534,35 @@ void MenuBar::ExportWiiSaves() CWiiSaveCrypted::ExportAllSaves(); } +void MenuBar::CheckNAND() +{ + IOS::HLE::Kernel ios; + if (WiiUtils::CheckNAND(ios)) + { + 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) + { + return; + } + + if (WiiUtils::RepairNAND(ios)) + { + QMessageBox::information(this, tr("NAND Check"), tr("The NAND has been repaired.")); + return; + } + + QMessageBox::critical(this, tr("NAND Check"), + tr("The NAND could not be repaired. It is recommended to back up " + "your current data and start over with a fresh NAND.")); +} + void MenuBar::NANDExtractCertificates() { if (DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX))) diff --git a/Source/Core/DolphinQt2/MenuBar.h b/Source/Core/DolphinQt2/MenuBar.h index c4a8c6a41c..d9745e7e67 100644 --- a/Source/Core/DolphinQt2/MenuBar.h +++ b/Source/Core/DolphinQt2/MenuBar.h @@ -113,6 +113,7 @@ private: void InstallWAD(); void ImportWiiSave(); void ExportWiiSaves(); + void CheckNAND(); void NANDExtractCertificates(); void OnSelectionChanged(QSharedPointer game_file); @@ -131,6 +132,7 @@ private: QAction* m_ntscu_ipl; QAction* m_pal_ipl; QAction* m_import_backup; + QAction* m_check_nand; QAction* m_extract_certificates; // Emulation diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h index a7005841ef..24cb56a94b 100644 --- a/Source/Core/DolphinWX/Frame.h +++ b/Source/Core/DolphinWX/Frame.h @@ -348,6 +348,7 @@ private: void OnInstallWAD(wxCommandEvent& event); void OnUninstallWAD(wxCommandEvent& event); void OnImportBootMiiBackup(wxCommandEvent& event); + void OnCheckNAND(wxCommandEvent& event); void OnExtractCertificates(wxCommandEvent& event); void OnPerformOnlineWiiUpdate(wxCommandEvent& event); void OnPerformDiscWiiUpdate(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index e8c7b4f487..cda03e9c6d 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -185,6 +185,7 @@ void CFrame::BindMenuBarEvents() Bind(wxEVT_MENU, &CFrame::OnInstallWAD, this, IDM_MENU_INSTALL_WAD); Bind(wxEVT_MENU, &CFrame::OnLoadWiiMenu, this, IDM_LOAD_WII_MENU); Bind(wxEVT_MENU, &CFrame::OnImportBootMiiBackup, this, IDM_IMPORT_NAND); + Bind(wxEVT_MENU, &CFrame::OnCheckNAND, this, IDM_CHECK_NAND); Bind(wxEVT_MENU, &CFrame::OnExtractCertificates, this, IDM_EXTRACT_CERTIFICATES); for (const int idm : {IDM_PERFORM_ONLINE_UPDATE_CURRENT, IDM_PERFORM_ONLINE_UPDATE_EUR, IDM_PERFORM_ONLINE_UPDATE_JPN, IDM_PERFORM_ONLINE_UPDATE_KOR, @@ -1308,6 +1309,34 @@ void CFrame::OnImportBootMiiBackup(wxCommandEvent& WXUNUSED(event)) UpdateLoadWiiMenuItem(); } +void CFrame::OnCheckNAND(wxCommandEvent&) +{ + IOS::HLE::Kernel ios; + if (WiiUtils::CheckNAND(ios)) + { + 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) + { + return; + } + + if (WiiUtils::RepairNAND(ios)) + { + wxMessageBox(_("The NAND has been repaired."), _("NAND Check"), wxOK | wxICON_INFORMATION); + return; + } + + wxMessageBox(_("The NAND could not be repaired. It is recommended to back up " + "your current data and start over with a fresh NAND."), + _("NAND Check"), wxOK | wxICON_ERROR); +} + void CFrame::OnExtractCertificates(wxCommandEvent& WXUNUSED(event)) { DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX)); diff --git a/Source/Core/DolphinWX/Globals.h b/Source/Core/DolphinWX/Globals.h index dad9550cd7..1975716bce 100644 --- a/Source/Core/DolphinWX/Globals.h +++ b/Source/Core/DolphinWX/Globals.h @@ -105,6 +105,7 @@ enum IDM_LIST_INSTALL_WAD, IDM_LIST_UNINSTALL_WAD, IDM_IMPORT_NAND, + IDM_CHECK_NAND, IDM_EXTRACT_CERTIFICATES, IDM_PERFORM_ONLINE_UPDATE_CURRENT, IDM_PERFORM_ONLINE_UPDATE_EUR, diff --git a/Source/Core/DolphinWX/MainMenuBar.cpp b/Source/Core/DolphinWX/MainMenuBar.cpp index c9b505e891..fdb6b633c6 100644 --- a/Source/Core/DolphinWX/MainMenuBar.cpp +++ b/Source/Core/DolphinWX/MainMenuBar.cpp @@ -235,6 +235,7 @@ wxMenu* MainMenuBar::CreateToolsMenu() const tools_menu->Append(IDM_MENU_INSTALL_WAD, _("Install WAD...")); tools_menu->Append(IDM_LOAD_WII_MENU, dummy_string); tools_menu->Append(IDM_IMPORT_NAND, _("Import BootMii NAND Backup...")); + tools_menu->Append(IDM_CHECK_NAND, _("Check NAND...")); tools_menu->Append(IDM_EXTRACT_CERTIFICATES, _("Extract Certificates from NAND")); auto* const online_update_menu = new wxMenu; online_update_menu->Append(IDM_PERFORM_ONLINE_UPDATE_CURRENT, _("Current Region")); @@ -582,7 +583,7 @@ void MainMenuBar::RefreshWiiToolsLabels() const // inconsistent data. const bool enable_wii_tools = !Core::IsRunning() || !SConfig::GetInstance().bWii; for (const int index : - {IDM_MENU_INSTALL_WAD, IDM_EXPORT_ALL_SAVE, IDM_IMPORT_SAVE, IDM_IMPORT_NAND, + {IDM_MENU_INSTALL_WAD, IDM_EXPORT_ALL_SAVE, IDM_IMPORT_SAVE, IDM_IMPORT_NAND, IDM_CHECK_NAND, IDM_EXTRACT_CERTIFICATES, IDM_LOAD_WII_MENU, IDM_PERFORM_ONLINE_UPDATE_CURRENT, IDM_PERFORM_ONLINE_UPDATE_EUR, IDM_PERFORM_ONLINE_UPDATE_JPN, IDM_PERFORM_ONLINE_UPDATE_KOR, IDM_PERFORM_ONLINE_UPDATE_USA}) From e1c0b8d01124f13d07d0b7bd0e271e88b1178d0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 8 Oct 2017 18:23:11 +0200 Subject: [PATCH 3/4] TitleDatabase: Add GetTitleName for title IDs --- Source/Core/Core/TitleDatabase.cpp | 9 +++++++++ Source/Core/Core/TitleDatabase.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/Source/Core/Core/TitleDatabase.cpp b/Source/Core/Core/TitleDatabase.cpp index ea8b2916f1..29fe07e57b 100644 --- a/Source/Core/Core/TitleDatabase.cpp +++ b/Source/Core/Core/TitleDatabase.cpp @@ -14,6 +14,7 @@ #include "Common/MsgHandler.h" #include "Common/StringUtil.h" #include "Core/ConfigManager.h" +#include "Core/IOS/ES/Formats.h" #include "DiscIO/Enums.h" namespace Core @@ -165,6 +166,14 @@ std::string TitleDatabase::GetTitleName(const std::string& game_id, TitleType ty return iterator != map.end() ? iterator->second : ""; } +std::string TitleDatabase::GetTitleName(u64 title_id) const +{ + const std::string id{ + {static_cast((title_id >> 24) & 0xff), static_cast((title_id >> 16) & 0xff), + static_cast((title_id >> 8) & 0xff), static_cast(title_id & 0xff)}}; + return GetTitleName(id, IOS::ES::IsChannel(title_id) ? TitleType::Channel : TitleType::Other); +} + std::string TitleDatabase::Describe(const std::string& game_id, TitleType type) const { const std::string title_name = GetTitleName(game_id, type); diff --git a/Source/Core/Core/TitleDatabase.h b/Source/Core/Core/TitleDatabase.h index 6b6c53fd9d..27793407a3 100644 --- a/Source/Core/Core/TitleDatabase.h +++ b/Source/Core/Core/TitleDatabase.h @@ -7,6 +7,8 @@ #include #include +#include "Common/CommonTypes.h" + namespace Core { // Reader for title database files. @@ -25,6 +27,7 @@ public: // Get a user friendly title name for a game ID. // This falls back to returning an empty string if none could be found. std::string GetTitleName(const std::string& game_id, TitleType = TitleType::Other) const; + std::string GetTitleName(u64 title_id) const; // Get a description for a game ID (title name if available + game ID). std::string Describe(const std::string& game_id, TitleType = TitleType::Other) const; From 02e17594b06151e652a1b51ce1018ce4747feecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Fri, 6 Oct 2017 21:45:28 +0200 Subject: [PATCH 4/4] WiiUtils: Attempt to fix the NAND more aggressively Change the repair logic to fix issues more aggressively by deleting bad titles. This is necessary because of a bug in Dolphin's WAD boot code. The UI code was updated to inform the user about titles that will be deleted if they continue a repair, before deleting anything. --- Source/Core/Core/WiiUtils.cpp | 47 +++++++++++++++++++--------- Source/Core/Core/WiiUtils.h | 8 ++++- Source/Core/DolphinQt2/MenuBar.cpp | 31 +++++++++++++----- Source/Core/DolphinWX/FrameTools.cpp | 26 +++++++++++---- 4 files changed, 83 insertions(+), 29 deletions(-) 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);