diff --git a/Source/Core/Core/HW/WiiSave.cpp b/Source/Core/Core/HW/WiiSave.cpp index 98498a29cb..2bc87b8dad 100644 --- a/Source/Core/Core/HW/WiiSave.cpp +++ b/Source/Core/Core/HW/WiiSave.cpp @@ -477,28 +477,28 @@ StoragePointer MakeDataBinStorage(IOS::HLE::IOSC* iosc, const std::string& path, 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 const auto header = source->ReadHeader(); if (!header) { ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to read header"); - return false; + return CopyResult::CorruptedSource; } const auto bk_header = source->ReadBkHeader(); if (!bk_header) { ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to read bk header"); - return false; + return CopyResult::CorruptedSource; } const auto files = source->ReadFiles(); if (!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 @@ -507,7 +507,7 @@ bool Copy(Storage* source, Storage* dest) if (!dest->EraseSave()) { 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)) { ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to write header"); - return false; + return CopyResult::Error; } if (!dest->WriteBkHeader(*bk_header)) { ERROR_LOG_FMT(CORE, "WiiSave::Copy: Failed to write bk header"); - return false; + return CopyResult::Error; } if (!dest->WriteFiles(*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 can_overwrite) +CopyResult Import(const std::string& data_bin_path, std::function can_overwrite) { IOS::HLE::Kernel ios; 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 can_overwrit if (!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)) { ERROR_LOG_FMT(CORE, "WiiSave::Import: Failed to find or import TMD for title {:16x}", header->tid); - return false; + return CopyResult::TitleMissing; } const auto nand = MakeNandStorage(ios.GetFS().get(), header->tid); if (nand->SaveExists() && !can_overwrite()) - return false; + return CopyResult::Cancelled; 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, static_cast(tid >> 24), static_cast(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()); } -bool Export(u64 tid, std::string_view export_path) +CopyResult Export(u64 tid, std::string_view export_path) { IOS::HLE::Kernel 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; for (const u64 title : ios.GetES()->GetInstalledTitles()) { - if (Export(title, export_path, &ios)) + if (Export(title, export_path, &ios) == CopyResult::Success) ++exported_save_count; } return exported_save_count; diff --git a/Source/Core/Core/HW/WiiSave.h b/Source/Core/Core/HW/WiiSave.h index d989498a94..f7d414586a 100644 --- a/Source/Core/Core/HW/WiiSave.h +++ b/Source/Core/Core/HW/WiiSave.h @@ -32,12 +32,21 @@ using StoragePointer = std::unique_ptr; StoragePointer MakeNandStorage(IOS::HLE::FS::FileSystem* fs, u64 tid); 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. -bool Import(const std::string& data_bin_path, std::function can_overwrite); +CopyResult Import(const std::string& data_bin_path, std::function can_overwrite); /// 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. size_t ExportAll(std::string_view export_path); } // namespace WiiSave diff --git a/Source/Core/DolphinQt/GameList/GameList.cpp b/Source/Core/DolphinQt/GameList/GameList.cpp index e3056b4ef4..1b458bdb35 100644 --- a/Source/Core/DolphinQt/GameList/GameList.cpp +++ b/Source/Core/DolphinQt/GameList/GameList.cpp @@ -452,8 +452,11 @@ void GameList::ExportWiiSave() QList failed; 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)); + } } if (!failed.isEmpty()) diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index 3869fcf8e5..b1f6d40169 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -1066,19 +1066,39 @@ void MenuBar::ImportWiiSave() if (file.isEmpty()) return; - bool cancelled = false; auto can_overwrite = [&] { - bool yes = ModalMessageBox::question( - this, tr("Save Import"), - tr("Save data for this title already exists in the NAND. Consider backing up " - "the current data before overwriting.\nOverwrite now?")) == QMessageBox::Yes; - cancelled = !yes; - return yes; + return ModalMessageBox::question( + this, tr("Save Import"), + tr("Save data for this title already exists in the NAND. Consider backing up " + "the current data before overwriting.\nOverwrite now?")) == QMessageBox::Yes; }; - if (WiiSave::Import(file.toStdString(), can_overwrite)) - ModalMessageBox::information(this, tr("Save Import"), tr("Successfully imported save files.")); - else if (!cancelled) - ModalMessageBox::critical(this, tr("Save Import"), tr("Failed to import save files.")); + + const auto result = WiiSave::Import(file.toStdString(), can_overwrite); + switch (result) + { + 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()