Qt: Give better error messages when Wii save importing fails.

This commit is contained in:
Admiral H. Curtiss 2020-12-31 23:26:16 +01:00
parent d9c686db30
commit 2932b5f8cd
4 changed files with 63 additions and 31 deletions

View File

@ -477,28 +477,28 @@ StoragePointer MakeDataBinStorage(IOS::HLE::IOSC* iosc, const std::string& path,
return StoragePointer{new DataBinStorage{iosc, path, mode}}; return StoragePointer{new DataBinStorage{iosc, path, mode}};
} }
bool Copy(Storage* source, Storage* dest) CopyResult Copy(Storage* source, Storage* dest)
{ {
// first make sure we can read all the data from the source // first make sure we can read all the data from the source
const auto header = source->ReadHeader(); const auto header = source->ReadHeader();
if (!header) if (!header)
{ {
ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to read header"); ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to read header");
return false; return CopyResult::CorruptedSource;
} }
const auto bk_header = source->ReadBkHeader(); const auto bk_header = source->ReadBkHeader();
if (!bk_header) if (!bk_header)
{ {
ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to read bk header"); ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to read bk header");
return false; return CopyResult::CorruptedSource;
} }
const auto files = source->ReadFiles(); const auto files = source->ReadFiles();
if (!files) if (!files)
{ {
ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to read files"); ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to read files");
return false; return CopyResult::CorruptedSource;
} }
// once we have confirmed we can read the source, erase corresponding save in the destination // once we have confirmed we can read the source, erase corresponding save in the destination
@ -507,7 +507,7 @@ bool Copy(Storage* source, Storage* dest)
if (!dest->EraseSave()) if (!dest->EraseSave())
{ {
ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to erase existing save"); ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to erase existing save");
return false; return CopyResult::Error;
} }
} }
@ -515,25 +515,25 @@ bool Copy(Storage* source, Storage* dest)
if (!dest->WriteHeader(*header)) if (!dest->WriteHeader(*header))
{ {
ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to write header"); ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to write header");
return false; return CopyResult::Error;
} }
if (!dest->WriteBkHeader(*bk_header)) if (!dest->WriteBkHeader(*bk_header))
{ {
ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to write bk header"); ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to write bk header");
return false; return CopyResult::Error;
} }
if (!dest->WriteFiles(*files)) if (!dest->WriteFiles(*files))
{ {
ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to write files"); ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to write files");
return false; return CopyResult::Error;
} }
return true; return CopyResult::Success;
} }
bool Import(const std::string& data_bin_path, std::function<bool()> can_overwrite) CopyResult Import(const std::string& data_bin_path, std::function<bool()> can_overwrite)
{ {
IOS::HLE::Kernel ios; IOS::HLE::Kernel ios;
const auto data_bin = MakeDataBinStorage(&ios.GetIOSC(), data_bin_path, "rb"); const auto data_bin = MakeDataBinStorage(&ios.GetIOSC(), data_bin_path, "rb");
@ -541,23 +541,23 @@ bool Import(const std::string& data_bin_path, std::function<bool()> can_overwrit
if (!header) if (!header)
{ {
ERROR_LOG_FMT(CORE, "WiiSave::Import: Failed to read header"); ERROR_LOG_FMT(CORE, "WiiSave::Import: Failed to read header");
return false; return CopyResult::CorruptedSource;
} }
if (!WiiUtils::EnsureTMDIsImported(*ios.GetFS(), *ios.GetES(), header->tid)) if (!WiiUtils::EnsureTMDIsImported(*ios.GetFS(), *ios.GetES(), header->tid))
{ {
ERROR_LOG_FMT(CORE, "WiiSave::Import: Failed to find or import TMD for title {:16x}", ERROR_LOG_FMT(CORE, "WiiSave::Import: Failed to find or import TMD for title {:16x}",
header->tid); header->tid);
return false; return CopyResult::TitleMissing;
} }
const auto nand = MakeNandStorage(ios.GetFS().get(), header->tid); const auto nand = MakeNandStorage(ios.GetFS().get(), header->tid);
if (nand->SaveExists() && !can_overwrite()) if (nand->SaveExists() && !can_overwrite())
return false; return CopyResult::Cancelled;
return Copy(data_bin.get(), nand.get()); return Copy(data_bin.get(), nand.get());
} }
static bool Export(u64 tid, std::string_view export_path, IOS::HLE::Kernel* ios) static CopyResult Export(u64 tid, std::string_view export_path, IOS::HLE::Kernel* ios)
{ {
const std::string path = fmt::format("{}/private/wii/title/{}{}{}{}/data.bin", export_path, const std::string path = fmt::format("{}/private/wii/title/{}{}{}{}/data.bin", export_path,
static_cast<char>(tid >> 24), static_cast<char>(tid >> 16), static_cast<char>(tid >> 24), static_cast<char>(tid >> 16),
@ -566,7 +566,7 @@ static bool Export(u64 tid, std::string_view export_path, IOS::HLE::Kernel* ios)
MakeDataBinStorage(&ios->GetIOSC(), path, "w+b").get()); MakeDataBinStorage(&ios->GetIOSC(), path, "w+b").get());
} }
bool Export(u64 tid, std::string_view export_path) CopyResult Export(u64 tid, std::string_view export_path)
{ {
IOS::HLE::Kernel ios; IOS::HLE::Kernel ios;
return Export(tid, export_path, &ios); return Export(tid, export_path, &ios);
@ -578,7 +578,7 @@ size_t ExportAll(std::string_view export_path)
size_t exported_save_count = 0; size_t exported_save_count = 0;
for (const u64 title : ios.GetES()->GetInstalledTitles()) for (const u64 title : ios.GetES()->GetInstalledTitles())
{ {
if (Export(title, export_path, &ios)) if (Export(title, export_path, &ios) == CopyResult::Success)
++exported_save_count; ++exported_save_count;
} }
return exported_save_count; return exported_save_count;

View File

@ -32,12 +32,21 @@ using StoragePointer = std::unique_ptr<Storage, StorageDeleter>;
StoragePointer MakeNandStorage(IOS::HLE::FS::FileSystem* fs, u64 tid); StoragePointer MakeNandStorage(IOS::HLE::FS::FileSystem* fs, u64 tid);
StoragePointer MakeDataBinStorage(IOS::HLE::IOSC* iosc, const std::string& path, const char* mode); StoragePointer MakeDataBinStorage(IOS::HLE::IOSC* iosc, const std::string& path, const char* mode);
bool Copy(Storage* source, Storage* destination); enum class CopyResult
{
Success,
Error,
Cancelled,
CorruptedSource,
TitleMissing,
};
CopyResult Copy(Storage* source, Storage* destination);
/// Import a save into the NAND from a .bin file. /// Import a save into the NAND from a .bin file.
bool Import(const std::string& data_bin_path, std::function<bool()> can_overwrite); CopyResult Import(const std::string& data_bin_path, std::function<bool()> can_overwrite);
/// Export a save to a .bin file. /// Export a save to a .bin file.
bool Export(u64 tid, std::string_view export_path); CopyResult Export(u64 tid, std::string_view export_path);
/// Export all saves that are in the NAND. Returns the number of exported saves. /// Export all saves that are in the NAND. Returns the number of exported saves.
size_t ExportAll(std::string_view export_path); size_t ExportAll(std::string_view export_path);
} // namespace WiiSave } // namespace WiiSave

View File

@ -452,8 +452,11 @@ void GameList::ExportWiiSave()
QList<std::string> failed; QList<std::string> failed;
for (const auto& game : GetSelectedGames()) for (const auto& game : GetSelectedGames())
{ {
if (!WiiSave::Export(game->GetTitleID(), export_dir.toStdString())) if (WiiSave::Export(game->GetTitleID(), export_dir.toStdString()) !=
WiiSave::CopyResult::Success)
{
failed.push_back(game->GetName(UICommon::GameFile::Variant::LongAndPossiblyCustom)); failed.push_back(game->GetName(UICommon::GameFile::Variant::LongAndPossiblyCustom));
}
} }
if (!failed.isEmpty()) if (!failed.isEmpty())

View File

@ -1066,19 +1066,39 @@ void MenuBar::ImportWiiSave()
if (file.isEmpty()) if (file.isEmpty())
return; return;
bool cancelled = false;
auto can_overwrite = [&] { auto can_overwrite = [&] {
bool yes = ModalMessageBox::question( return ModalMessageBox::question(
this, tr("Save Import"), this, tr("Save Import"),
tr("Save data for this title already exists in the NAND. Consider backing up " tr("Save data for this title already exists in the NAND. Consider backing up "
"the current data before overwriting.\nOverwrite now?")) == QMessageBox::Yes; "the current data before overwriting.\nOverwrite now?")) == QMessageBox::Yes;
cancelled = !yes;
return yes;
}; };
if (WiiSave::Import(file.toStdString(), can_overwrite))
ModalMessageBox::information(this, tr("Save Import"), tr("Successfully imported save files.")); const auto result = WiiSave::Import(file.toStdString(), can_overwrite);
else if (!cancelled) switch (result)
ModalMessageBox::critical(this, tr("Save Import"), tr("Failed to import save files.")); {
case WiiSave::CopyResult::Success:
ModalMessageBox::information(this, tr("Save Import"), tr("Successfully imported save file."));
break;
case WiiSave::CopyResult::CorruptedSource:
ModalMessageBox::critical(this, tr("Save Import"),
tr("Failed to import save file. The given file appears to be "
"corrupted or is not a valid Wii save."));
break;
case WiiSave::CopyResult::TitleMissing:
ModalMessageBox::critical(
this, tr("Save Import"),
tr("Failed to import save file. Please launch the game once, then try again."));
break;
case WiiSave::CopyResult::Cancelled:
break;
default:
ModalMessageBox::critical(
this, tr("Save Import"),
tr("Failed to import save file. Your NAND may be corrupt, or something is preventing "
"access to files within it. Try repairing your NAND (Tools -> Manage NAND -> Check "
"NAND...), then import the save again."));
break;
}
} }
void MenuBar::ExportWiiSaves() void MenuBar::ExportWiiSaves()