Qt: Give better error messages when Wii save importing fails.
This commit is contained in:
parent
d9c686db30
commit
2932b5f8cd
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -452,9 +452,12 @@ 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())
|
||||||
{
|
{
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue