summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Lam <leolino.lam@gmail.com>2017-10-11 13:01:39 +0200
committerGitHub <noreply@github.com>2017-10-11 13:01:39 +0200
commitd26eda3023a0d896f1992d9deffbbcc47195c2a3 (patch)
treeea0d41d797135166fe9a358d8e7d63be8497d2a0
parentceed4d6f930f06aec6b68b353d99a19f46a0e26f (diff)
parent02e17594b06151e652a1b51ce1018ce4747feecd (diff)
Merge pull request #6096 from leoetlino/wii-fsck
Add a way to check the NAND for issues and fix them
-rw-r--r--Source/Core/Core/IOS/ES/Formats.cpp11
-rw-r--r--Source/Core/Core/IOS/ES/Formats.h2
-rw-r--r--Source/Core/Core/TitleDatabase.cpp9
-rw-r--r--Source/Core/Core/TitleDatabase.h3
-rw-r--r--Source/Core/Core/WiiUtils.cpp102
-rw-r--r--Source/Core/Core/WiiUtils.h18
-rw-r--r--Source/Core/DolphinQt2/MenuBar.cpp50
-rw-r--r--Source/Core/DolphinQt2/MenuBar.h2
-rw-r--r--Source/Core/DolphinWX/Frame.h1
-rw-r--r--Source/Core/DolphinWX/FrameTools.cpp43
-rw-r--r--Source/Core/DolphinWX/Globals.h1
-rw-r--r--Source/Core/DolphinWX/MainMenuBar.cpp3
12 files changed, 243 insertions, 2 deletions
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<u8>& 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<u8, 20> 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
{
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<char>((title_id >> 24) & 0xff), static_cast<char>((title_id >> 16) & 0xff),
+ static_cast<char>((title_id >> 8) & 0xff), static_cast<char>(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 <string>
#include <unordered_map>
+#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;
diff --git a/Source/Core/Core/WiiUtils.cpp b/Source/Core/Core/WiiUtils.cpp
index 4ce3f03103..4235c9ba96 100644
--- a/Source/Core/Core/WiiUtils.cpp
+++ b/Source/Core/Core/WiiUtils.cpp
@@ -685,4 +685,106 @@ UpdateResult DoDiscUpdate(UpdateCallback update_callback, const std::string& ima
DiscIO::NANDContentManager::Access().ClearCache();
return result;
}
+
+NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios)
+{
+ 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");
+ result.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);
+ 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);
+ result.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);
+ 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())
+ {
+ 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;
+ }
+
+ 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);
+ result.titles_to_remove.insert(title_id);
+ result.bad = true;
+ }
+ }
+
+ return result;
+}
+
+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 directory and no ticket,
+ // this title shouldn't exist at all on the NAND.
+ // 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())
+ {
+ const std::string title_dir = Common::GetTitlePath(title_id, Common::FROM_CONFIGURED_ROOT);
+ File::DeleteDirRecursively(title_dir);
+ }
+ }
+ return !CheckNAND(ios).bad;
+}
}
diff --git a/Source/Core/Core/WiiUtils.h b/Source/Core/Core/WiiUtils.h
index 49e8175bc2..0047e2c63d 100644
--- a/Source/Core/Core/WiiUtils.h
+++ b/Source/Core/Core/WiiUtils.h
@@ -7,11 +7,20 @@
#include <cstddef>
#include <functional>
#include <string>
+#include <unordered_set>
#include "Common/CommonTypes.h"
// 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 +57,13 @@ 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.
+struct NANDCheckResult
+{
+ bool bad = false;
+ std::unordered_set<u64> 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 6fb6cc79a5..401f7f4d7e 100644
--- a/Source/Core/DolphinQt2/MenuBar.cpp
+++ b/Source/Core/DolphinQt2/MenuBar.cpp
@@ -22,6 +22,8 @@
#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"
#include "DolphinQt2/GameList/GameFile.h"
@@ -114,7 +116,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 +475,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 +535,51 @@ void MenuBar::ExportWiiSaves()
CWiiSaveCrypted::ExportAllSaves();
}
+void MenuBar::CheckNAND()
+{
+ IOS::HLE::Kernel ios;
+ WiiUtils::NANDCheckResult result = WiiUtils::CheckNAND(ios);
+ if (!result.bad)
+ {
+ QMessageBox::information(this, tr("NAND Check"), tr("No issues have been detected."));
+ return;
+ }
+
+ 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())
+ {
+ 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."));
+ 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<GameFile> 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..592f338cd2 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,48 @@ void CFrame::OnImportBootMiiBackup(wxCommandEvent& WXUNUSED(event))
UpdateLoadWiiMenuItem();
}
+void CFrame::OnCheckNAND(wxCommandEvent&)
+{
+ IOS::HLE::Kernel ios;
+ WiiUtils::NANDCheckResult result = WiiUtils::CheckNAND(ios);
+ if (!result.bad)
+ {
+ wxMessageBox(_("No issues have been detected."), _("NAND Check"), wxOK | wxICON_INFORMATION);
+ return;
+ }
+
+ 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())
+ {
+ 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);
+ 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})