GCMemcardManager: Rewrite file importing logic to provide a better user experience.
This commit is contained in:
parent
3286d2df3d
commit
08dccb8727
|
@ -335,4 +335,12 @@ std::vector<Savefile> GetSavefiles(const GCMemcard& card, const std::vector<u8>&
|
||||||
}
|
}
|
||||||
return files;
|
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
|
} // namespace Memcard
|
||||||
|
|
|
@ -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.
|
// 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);
|
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
|
} // namespace Memcard
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
|
#include "Common/VariantUtil.h"
|
||||||
|
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
#include "Core/HW/GCMemcard/GCMemcard.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(
|
auto& card = m_slot_memcard[slot];
|
||||||
this, tr("Import Save File"), QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)),
|
if (!card)
|
||||||
tr("Native GCI File (*.gci)") + QStringLiteral(";;") + tr("MadCatz Gameshark files(*.gcs)") +
|
|
||||||
QStringLiteral(";;") + tr("Datel MaxDrive/Pro files(*.sav)"));
|
|
||||||
|
|
||||||
if (path.isEmpty())
|
|
||||||
return;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_slot_memcard[m_active_slot]->Save())
|
for (const Memcard::Savefile& savefile : savefiles)
|
||||||
PanicAlertFmtT("File write failed");
|
{
|
||||||
|
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()
|
void GCMemcardManager::CopyFiles()
|
||||||
|
@ -713,3 +803,18 @@ QString GCMemcardManager::GetErrorMessagesForErrorCode(const Memcard::GCMemcardE
|
||||||
|
|
||||||
return sl.join(QLatin1Char{'\n'});
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ namespace Memcard
|
||||||
{
|
{
|
||||||
class GCMemcard;
|
class GCMemcard;
|
||||||
class GCMemcardErrorCode;
|
class GCMemcardErrorCode;
|
||||||
|
struct Savefile;
|
||||||
|
enum class ReadSavefileErrorCode;
|
||||||
enum class SavefileFormat;
|
enum class SavefileFormat;
|
||||||
} // namespace Memcard
|
} // namespace Memcard
|
||||||
|
|
||||||
|
@ -41,6 +43,7 @@ public:
|
||||||
~GCMemcardManager();
|
~GCMemcardManager();
|
||||||
|
|
||||||
static QString GetErrorMessagesForErrorCode(const Memcard::GCMemcardErrorCode& code);
|
static QString GetErrorMessagesForErrorCode(const Memcard::GCMemcardErrorCode& code);
|
||||||
|
static QString GetErrorMessageForErrorCode(Memcard::ReadSavefileErrorCode code);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct IconAnimationData;
|
struct IconAnimationData;
|
||||||
|
@ -57,6 +60,8 @@ private:
|
||||||
|
|
||||||
std::vector<u8> GetSelectedFileIndices();
|
std::vector<u8> GetSelectedFileIndices();
|
||||||
|
|
||||||
|
void ImportFiles(int slot, const std::vector<Memcard::Savefile>& savefiles);
|
||||||
|
|
||||||
void CopyFiles();
|
void CopyFiles();
|
||||||
void ImportFile();
|
void ImportFile();
|
||||||
void DeleteFiles();
|
void DeleteFiles();
|
||||||
|
|
Loading…
Reference in New Issue