mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-15 05:47:56 -07:00
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:
parent
2974c56e50
commit
239167245d
@ -685,4 +685,89 @@ UpdateResult DoDiscUpdate(UpdateCallback update_callback, const std::string& ima
|
|||||||
DiscIO::NANDContentManager::Access().ClearCache();
|
DiscIO::NANDContentManager::Access().ClearCache();
|
||||||
return result;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,14 @@
|
|||||||
|
|
||||||
// Small utility functions for common Wii related tasks.
|
// Small utility functions for common Wii related tasks.
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
class Kernel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace WiiUtils
|
namespace WiiUtils
|
||||||
{
|
{
|
||||||
bool InstallWAD(const std::string& wad_path);
|
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.
|
// Perform a disc update with behaviour similar to the System Menu.
|
||||||
UpdateResult DoDiscUpdate(UpdateCallback update_callback, const std::string& image_path);
|
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);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "Core/IOS/IOS.h"
|
#include "Core/IOS/IOS.h"
|
||||||
#include "Core/Movie.h"
|
#include "Core/Movie.h"
|
||||||
#include "Core/State.h"
|
#include "Core/State.h"
|
||||||
|
#include "Core/WiiUtils.h"
|
||||||
#include "DiscIO/NANDImporter.h"
|
#include "DiscIO/NANDImporter.h"
|
||||||
#include "DolphinQt2/AboutDialog.h"
|
#include "DolphinQt2/AboutDialog.h"
|
||||||
#include "DolphinQt2/GameList/GameFile.h"
|
#include "DolphinQt2/GameList/GameFile.h"
|
||||||
@ -114,7 +115,7 @@ void MenuBar::AddToolsMenu()
|
|||||||
AddAction(tools_menu, QStringLiteral(""), this, [this] { emit BootWiiSystemMenu(); });
|
AddAction(tools_menu, QStringLiteral(""), this, [this] { emit BootWiiSystemMenu(); });
|
||||||
m_import_backup = AddAction(gc_ipl, tr("Import BootMii NAND Backup..."), this,
|
m_import_backup = AddAction(gc_ipl, tr("Import BootMii NAND Backup..."), this,
|
||||||
[this] { emit ImportNANDBackup(); });
|
[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,
|
m_extract_certificates = AddAction(tools_menu, tr("Extract Certificates from NAND"), this,
|
||||||
&MenuBar::NANDExtractCertificates);
|
&MenuBar::NANDExtractCertificates);
|
||||||
|
|
||||||
@ -473,6 +474,7 @@ void MenuBar::UpdateToolsMenu(bool emulation_started)
|
|||||||
m_pal_ipl->setEnabled(!emulation_started &&
|
m_pal_ipl->setEnabled(!emulation_started &&
|
||||||
File::Exists(SConfig::GetInstance().GetBootROMPath(EUR_DIR)));
|
File::Exists(SConfig::GetInstance().GetBootROMPath(EUR_DIR)));
|
||||||
m_import_backup->setEnabled(!emulation_started);
|
m_import_backup->setEnabled(!emulation_started);
|
||||||
|
m_check_nand->setEnabled(!emulation_started);
|
||||||
|
|
||||||
if (!emulation_started)
|
if (!emulation_started)
|
||||||
{
|
{
|
||||||
@ -532,6 +534,35 @@ void MenuBar::ExportWiiSaves()
|
|||||||
CWiiSaveCrypted::ExportAllSaves();
|
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()
|
void MenuBar::NANDExtractCertificates()
|
||||||
{
|
{
|
||||||
if (DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX)))
|
if (DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX)))
|
||||||
|
@ -113,6 +113,7 @@ private:
|
|||||||
void InstallWAD();
|
void InstallWAD();
|
||||||
void ImportWiiSave();
|
void ImportWiiSave();
|
||||||
void ExportWiiSaves();
|
void ExportWiiSaves();
|
||||||
|
void CheckNAND();
|
||||||
void NANDExtractCertificates();
|
void NANDExtractCertificates();
|
||||||
|
|
||||||
void OnSelectionChanged(QSharedPointer<GameFile> game_file);
|
void OnSelectionChanged(QSharedPointer<GameFile> game_file);
|
||||||
@ -131,6 +132,7 @@ private:
|
|||||||
QAction* m_ntscu_ipl;
|
QAction* m_ntscu_ipl;
|
||||||
QAction* m_pal_ipl;
|
QAction* m_pal_ipl;
|
||||||
QAction* m_import_backup;
|
QAction* m_import_backup;
|
||||||
|
QAction* m_check_nand;
|
||||||
QAction* m_extract_certificates;
|
QAction* m_extract_certificates;
|
||||||
|
|
||||||
// Emulation
|
// Emulation
|
||||||
|
@ -348,6 +348,7 @@ private:
|
|||||||
void OnInstallWAD(wxCommandEvent& event);
|
void OnInstallWAD(wxCommandEvent& event);
|
||||||
void OnUninstallWAD(wxCommandEvent& event);
|
void OnUninstallWAD(wxCommandEvent& event);
|
||||||
void OnImportBootMiiBackup(wxCommandEvent& event);
|
void OnImportBootMiiBackup(wxCommandEvent& event);
|
||||||
|
void OnCheckNAND(wxCommandEvent& event);
|
||||||
void OnExtractCertificates(wxCommandEvent& event);
|
void OnExtractCertificates(wxCommandEvent& event);
|
||||||
void OnPerformOnlineWiiUpdate(wxCommandEvent& event);
|
void OnPerformOnlineWiiUpdate(wxCommandEvent& event);
|
||||||
void OnPerformDiscWiiUpdate(wxCommandEvent& event);
|
void OnPerformDiscWiiUpdate(wxCommandEvent& event);
|
||||||
|
@ -185,6 +185,7 @@ void CFrame::BindMenuBarEvents()
|
|||||||
Bind(wxEVT_MENU, &CFrame::OnInstallWAD, this, IDM_MENU_INSTALL_WAD);
|
Bind(wxEVT_MENU, &CFrame::OnInstallWAD, this, IDM_MENU_INSTALL_WAD);
|
||||||
Bind(wxEVT_MENU, &CFrame::OnLoadWiiMenu, this, IDM_LOAD_WII_MENU);
|
Bind(wxEVT_MENU, &CFrame::OnLoadWiiMenu, this, IDM_LOAD_WII_MENU);
|
||||||
Bind(wxEVT_MENU, &CFrame::OnImportBootMiiBackup, this, IDM_IMPORT_NAND);
|
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);
|
Bind(wxEVT_MENU, &CFrame::OnExtractCertificates, this, IDM_EXTRACT_CERTIFICATES);
|
||||||
for (const int idm : {IDM_PERFORM_ONLINE_UPDATE_CURRENT, IDM_PERFORM_ONLINE_UPDATE_EUR,
|
for (const int idm : {IDM_PERFORM_ONLINE_UPDATE_CURRENT, IDM_PERFORM_ONLINE_UPDATE_EUR,
|
||||||
IDM_PERFORM_ONLINE_UPDATE_JPN, IDM_PERFORM_ONLINE_UPDATE_KOR,
|
IDM_PERFORM_ONLINE_UPDATE_JPN, IDM_PERFORM_ONLINE_UPDATE_KOR,
|
||||||
@ -1308,6 +1309,34 @@ void CFrame::OnImportBootMiiBackup(wxCommandEvent& WXUNUSED(event))
|
|||||||
UpdateLoadWiiMenuItem();
|
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))
|
void CFrame::OnExtractCertificates(wxCommandEvent& WXUNUSED(event))
|
||||||
{
|
{
|
||||||
DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX));
|
DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX));
|
||||||
|
@ -105,6 +105,7 @@ enum
|
|||||||
IDM_LIST_INSTALL_WAD,
|
IDM_LIST_INSTALL_WAD,
|
||||||
IDM_LIST_UNINSTALL_WAD,
|
IDM_LIST_UNINSTALL_WAD,
|
||||||
IDM_IMPORT_NAND,
|
IDM_IMPORT_NAND,
|
||||||
|
IDM_CHECK_NAND,
|
||||||
IDM_EXTRACT_CERTIFICATES,
|
IDM_EXTRACT_CERTIFICATES,
|
||||||
IDM_PERFORM_ONLINE_UPDATE_CURRENT,
|
IDM_PERFORM_ONLINE_UPDATE_CURRENT,
|
||||||
IDM_PERFORM_ONLINE_UPDATE_EUR,
|
IDM_PERFORM_ONLINE_UPDATE_EUR,
|
||||||
|
@ -235,6 +235,7 @@ wxMenu* MainMenuBar::CreateToolsMenu() const
|
|||||||
tools_menu->Append(IDM_MENU_INSTALL_WAD, _("Install WAD..."));
|
tools_menu->Append(IDM_MENU_INSTALL_WAD, _("Install WAD..."));
|
||||||
tools_menu->Append(IDM_LOAD_WII_MENU, dummy_string);
|
tools_menu->Append(IDM_LOAD_WII_MENU, dummy_string);
|
||||||
tools_menu->Append(IDM_IMPORT_NAND, _("Import BootMii NAND Backup..."));
|
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"));
|
tools_menu->Append(IDM_EXTRACT_CERTIFICATES, _("Extract Certificates from NAND"));
|
||||||
auto* const online_update_menu = new wxMenu;
|
auto* const online_update_menu = new wxMenu;
|
||||||
online_update_menu->Append(IDM_PERFORM_ONLINE_UPDATE_CURRENT, _("Current Region"));
|
online_update_menu->Append(IDM_PERFORM_ONLINE_UPDATE_CURRENT, _("Current Region"));
|
||||||
@ -582,7 +583,7 @@ void MainMenuBar::RefreshWiiToolsLabels() const
|
|||||||
// inconsistent data.
|
// inconsistent data.
|
||||||
const bool enable_wii_tools = !Core::IsRunning() || !SConfig::GetInstance().bWii;
|
const bool enable_wii_tools = !Core::IsRunning() || !SConfig::GetInstance().bWii;
|
||||||
for (const int index :
|
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_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_EUR, IDM_PERFORM_ONLINE_UPDATE_JPN, IDM_PERFORM_ONLINE_UPDATE_KOR,
|
||||||
IDM_PERFORM_ONLINE_UPDATE_USA})
|
IDM_PERFORM_ONLINE_UPDATE_USA})
|
||||||
|
Loading…
Reference in New Issue
Block a user