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).
This commit is contained in:
Léo Lam
2017-10-03 16:45:59 +02:00
parent 2974c56e50
commit 239167245d
8 changed files with 164 additions and 2 deletions

View File

@ -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);
}
}

View File

@ -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);
}