GCMemcardManager: Rewrite file importing logic to provide a better user experience.

This commit is contained in:
Admiral H. Curtiss 2020-07-31 20:56:52 +02:00
parent 3286d2df3d
commit 08dccb8727
4 changed files with 134 additions and 13 deletions

View file

@ -335,4 +335,12 @@ std::vector<Savefile> GetSavefiles(const GCMemcard& card, const std::vector<u8>&
}
return files;
}
size_t GetBlockCount(const std::vector<Savefile>& savefiles)
{
size_t block_count = 0;
for (const Savefile& savefile : savefiles)
block_count += savefile.blocks.size();
return block_count;
}
} // namespace Memcard

View file

@ -42,4 +42,7 @@ std::string GetDefaultExtension(SavefileFormat format);
// Reads multiple savefiles from a card. Returns empty vector if even a single file can't be read.
std::vector<Savefile> GetSavefiles(const GCMemcard& card, const std::vector<u8>& file_indices);
// Gets the total amount of blocks the given saves use.
size_t GetBlockCount(const std::vector<Savefile>& savefiles);
} // namespace Memcard

View file

@ -34,6 +34,7 @@
#include "Common/FileUtil.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Common/VariantUtil.h"
#include "Core/Config/MainSettings.h"
#include "Core/HW/GCMemcard/GCMemcard.h"
@ -457,28 +458,117 @@ void GCMemcardManager::ExportFiles(Memcard::SavefileFormat format)
}
}
void GCMemcardManager::ImportFile()
void GCMemcardManager::ImportFiles(int slot, const std::vector<Memcard::Savefile>& savefiles)
{
QString path = QFileDialog::getOpenFileName(
this, tr("Import Save File"), QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)),
tr("Native GCI File (*.gci)") + QStringLiteral(";;") + tr("MadCatz Gameshark files(*.gcs)") +
QStringLiteral(";;") + tr("Datel MaxDrive/Pro files(*.sav)"));
if (path.isEmpty())
auto& card = m_slot_memcard[slot];
if (!card)
return;
const auto result = m_slot_memcard[m_active_slot]->ImportGci(path.toStdString());
const size_t number_of_files = savefiles.size();
const size_t number_of_blocks = Memcard::GetBlockCount(savefiles);
const size_t free_files = Memcard::DIRLEN - card->GetNumFiles();
const size_t free_blocks = card->GetFreeBlocks();
if (result != Memcard::GCMemcardImportFileRetVal::SUCCESS)
QStringList error_messages;
if (number_of_files > free_files)
{
ModalMessageBox::critical(this, tr("Import failed"), tr("Failed to import \"%1\".").arg(path));
error_messages.push_back(
tr("Not enough free files on the target memory card. At least %n free file(s) required.",
"", static_cast<int>(number_of_files)));
}
if (number_of_blocks > free_blocks)
{
error_messages.push_back(
tr("Not enough free blocks on the target memory card. At least %n free block(s) required.",
"", static_cast<int>(number_of_blocks)));
}
for (const Memcard::Savefile& savefile : savefiles)
{
if (card->TitlePresent(savefile.dir_entry))
{
const std::string filename = Memcard::GenerateFilename(savefile.dir_entry);
error_messages.push_back(tr("The target memory card already contains a file \"%1\".")
.arg(QString::fromStdString(filename)));
}
}
if (!error_messages.empty())
{
ModalMessageBox::warning(this, tr("Import Failed"), error_messages.join(QLatin1Char('\n')));
return;
}
if (!m_slot_memcard[m_active_slot]->Save())
PanicAlertFmtT("File write failed");
for (const Memcard::Savefile& savefile : savefiles)
{
std::vector<Memcard::GCMBlock> blocks(savefile.blocks);
const auto result = card->ImportFile(savefile.dir_entry, blocks);
UpdateSlotTable(m_active_slot);
// we've already checked everything that could realistically fail here, so this should only
// happen if the memory card data is corrupted in some way
if (result != Memcard::GCMemcardImportFileRetVal::SUCCESS)
{
const std::string filename = Memcard::GenerateFilename(savefile.dir_entry);
ModalMessageBox::warning(
this, tr("Import Failed"),
tr("Failed to import \"%1\".").arg(QString::fromStdString(filename)));
break;
}
}
if (!card->Save())
{
ModalMessageBox::warning(this, tr("Import Failed"),
tr("Failed to write modified memory card to disk."));
}
UpdateSlotTable(slot);
}
void GCMemcardManager::ImportFile()
{
auto& card = m_slot_memcard[m_active_slot];
if (!card)
return;
const QStringList paths = QFileDialog::getOpenFileNames(
this, tr("Import Save File(s)"), QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)),
QStringLiteral("%1 (*.gci *.gcs *.sav);;%2 (*.gci);;%3 (*.gcs);;%4 (*.sav);;%5 (*)")
.arg(tr("Supported file formats"), GetFormatDescription(Memcard::SavefileFormat::GCI),
GetFormatDescription(Memcard::SavefileFormat::GCS),
GetFormatDescription(Memcard::SavefileFormat::SAV), tr("All Files")));
if (paths.isEmpty())
return;
std::vector<Memcard::Savefile> savefiles;
savefiles.reserve(paths.size());
QStringList errors;
for (const QString& path : paths)
{
auto read_result = Memcard::ReadSavefile(path.toStdString());
std::visit(overloaded{
[&](Memcard::Savefile savefile) { savefiles.emplace_back(std::move(savefile)); },
[&](Memcard::ReadSavefileErrorCode error_code) {
errors.push_back(
tr("%1: %2").arg(path, GetErrorMessageForErrorCode(error_code)));
},
},
std::move(read_result));
}
if (!errors.empty())
{
ModalMessageBox::warning(
this, tr("Import Failed"),
tr("Encountered the following errors while opening save files:\n%1\n\nAborting import.")
.arg(errors.join(QStringLiteral("\n"))));
return;
}
ImportFiles(m_active_slot, savefiles);
}
void GCMemcardManager::CopyFiles()
@ -713,3 +803,18 @@ QString GCMemcardManager::GetErrorMessagesForErrorCode(const Memcard::GCMemcardE
return sl.join(QLatin1Char{'\n'});
}
QString GCMemcardManager::GetErrorMessageForErrorCode(Memcard::ReadSavefileErrorCode code)
{
switch (code)
{
case Memcard::ReadSavefileErrorCode::OpenFileFail:
return tr("Failed to open file.");
case Memcard::ReadSavefileErrorCode::IOError:
return tr("Failed to read from file.");
case Memcard::ReadSavefileErrorCode::DataCorrupted:
return tr("Data in unrecognized format or corrupted.");
default:
return tr("Unknown error.");
}
}

View file

@ -17,6 +17,8 @@ namespace Memcard
{
class GCMemcard;
class GCMemcardErrorCode;
struct Savefile;
enum class ReadSavefileErrorCode;
enum class SavefileFormat;
} // namespace Memcard
@ -41,6 +43,7 @@ public:
~GCMemcardManager();
static QString GetErrorMessagesForErrorCode(const Memcard::GCMemcardErrorCode& code);
static QString GetErrorMessageForErrorCode(Memcard::ReadSavefileErrorCode code);
private:
struct IconAnimationData;
@ -57,6 +60,8 @@ private:
std::vector<u8> GetSelectedFileIndices();
void ImportFiles(int slot, const std::vector<Memcard::Savefile>& savefiles);
void CopyFiles();
void ImportFile();
void DeleteFiles();