diff --git a/src/citra_qt/configuration/configure_storage.cpp b/src/citra_qt/configuration/configure_storage.cpp index a8cabd987..2bf1c85ca 100644 --- a/src/citra_qt/configuration/configure_storage.cpp +++ b/src/citra_qt/configuration/configure_storage.cpp @@ -6,12 +6,17 @@ #include #include #include "citra_qt/configuration/configure_storage.h" + +#include +#include + #include "common/file_util.h" #include "common/settings.h" #include "ui_configure_storage.h" ConfigureStorage::ConfigureStorage(bool is_powered_on_, QWidget* parent) - : QWidget(parent), ui(std::make_unique()), is_powered_on{is_powered_on_} { + : QWidget(parent), ui(std::make_unique()), is_powered_on{is_powered_on_}, + parent{parent} { ui->setupUi(this); SetConfiguration(); @@ -22,12 +27,15 @@ ConfigureStorage::ConfigureStorage(bool is_powered_on_, QWidget* parent) connect(ui->change_nand_dir, &QPushButton::clicked, this, [this]() { ui->change_nand_dir->setEnabled(false); + const QString og_path = + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); const QString dir_path = QFileDialog::getExistingDirectory( this, tr("Select NAND Directory"), QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)), QFileDialog::ShowDirsOnly); if (!dir_path.isEmpty()) { FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir, dir_path.toStdString()); + MigrateFolder(og_path, dir_path); SetConfiguration(); } ui->change_nand_dir->setEnabled(true); @@ -40,12 +48,15 @@ ConfigureStorage::ConfigureStorage(bool is_powered_on_, QWidget* parent) connect(ui->change_sdmc_dir, &QPushButton::clicked, this, [this]() { ui->change_sdmc_dir->setEnabled(false); + const QString og_path = + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)); const QString dir_path = QFileDialog::getExistingDirectory( this, tr("Select SDMC Directory"), QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), QFileDialog::ShowDirsOnly); if (!dir_path.isEmpty()) { FileUtil::UpdateUserPath(FileUtil::UserPath::SDMCDir, dir_path.toStdString()); + MigrateFolder(og_path, dir_path); SetConfiguration(); } ui->change_sdmc_dir->setEnabled(true); @@ -95,6 +106,81 @@ void ConfigureStorage::ApplyConfiguration() { } } +void ConfigureStorage::MigrateFolder(const QString& from, const QString& dest) { + bool source_exists = FileUtil::Exists(from.toStdString()); + bool dest_exists = FileUtil::Exists(dest.toStdString()); + + bool dest_has_content = false; + bool source_has_content = false; + + if (source_exists) { + source_has_content = !FileUtil::IsEmptyDir(from.toStdString()); + } + + if (dest_exists) { + dest_has_content = !FileUtil::IsEmptyDir(dest.toStdString()); + } + + if (!source_has_content) { + return; + } + + QString message; + if (dest_has_content) { + message = tr("Data exists in both the old and new locations.\n\n" + "Old: %1\n" + "New: %2\n\n" + "Would you like to migrate the data from the old location?\n" + "WARNING: This will overwrite any data in the new location!") + .arg(from) + .arg(dest); + } else { + message = tr("Would you like to migrate your data to the new location?\n\n" + "From: %1\n" + "To: %2") + .arg(from) + .arg(dest); + } + + QMessageBox::StandardButton reply = + QMessageBox::question(parent, tr("Migrate Save Data"), message, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (reply != QMessageBox::Yes) { + return; + } + + QProgressDialog progress(tr("Migrating save data..."), tr("Cancel"), 0, 0, this); + progress.setWindowModality(Qt::WindowModal); + progress.setMinimumDuration(0); + progress.show(); + + if (!dest_exists) { + if (!FileUtil::CreateFullPath(dest.toStdString())) { + progress.close(); + QMessageBox::warning(this, tr("Migration Failed"), + tr("Failed to create destination directory.")); + return; + } + } + + FileUtil::CopyDir(from.toStdString() + "/", dest.toStdString() + "/"); + + progress.close(); + + QMessageBox::StandardButton deleteReply = + QMessageBox::question(this, tr("Migration Complete"), + tr("Data has been migrated successfully.\n\n" + "Would you like to delete the old data?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + + if (deleteReply == QMessageBox::Yes) { + if (!FileUtil::DeleteDirRecursively(from.toStdString())) { + QMessageBox::warning(this, tr("Deletion Failed"), tr("Failed to delete old data.")); + } + } +} + void ConfigureStorage::RetranslateUI() { ui->retranslateUi(this); } diff --git a/src/citra_qt/configuration/configure_storage.h b/src/citra_qt/configuration/configure_storage.h index 48850fbc2..b0eee97ca 100644 --- a/src/citra_qt/configuration/configure_storage.h +++ b/src/citra_qt/configuration/configure_storage.h @@ -1,10 +1,11 @@ -// Copyright 2021 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include +#include #include namespace Ui { @@ -19,9 +20,13 @@ public: ~ConfigureStorage() override; void ApplyConfiguration(); + + void MigrateFolder(const QString& from, const QString& dest); + void RetranslateUI(); void SetConfiguration(); std::unique_ptr ui; bool is_powered_on; + QWidget* parent; }; diff --git a/src/citra_qt/configuration/configure_storage.ui b/src/citra_qt/configuration/configure_storage.ui index 5cfd0c449..aec77cd56 100644 --- a/src/citra_qt/configuration/configure_storage.ui +++ b/src/citra_qt/configuration/configure_storage.ui @@ -79,13 +79,6 @@ - - - - NOTE: This does not move the contents of the previous directory to the new one. - - - @@ -143,14 +136,7 @@ - - - - - NOTE: This does not move the contents of the previous directory to the new one. - - - + > diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 52f406226..26bc50c2d 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -155,6 +155,24 @@ static void StripTailDirSlashes(std::string& fname) { fname.resize(i); } +bool IsEmptyDir(const std::string& folder_path) { + if (!IsDirectory(folder_path)) { + return false; + } + + bool has_entries = false; + + ForeachDirectoryEntry(nullptr, folder_path, + [&has_entries]([[maybe_unused]] u64* num_entries_out, + [[maybe_unused]] const std::string& directory, + [[maybe_unused]] const std::string& virtual_name) -> bool { + has_entries = true; + return false; + }); + + return !has_entries; +} + bool Exists(const std::string& filename) { std::string copy(filename); StripTailDirSlashes(copy); diff --git a/src/common/file_util.h b/src/common/file_util.h index 56dbeea93..2fd81265e 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -121,6 +121,9 @@ private: friend class boost::serialization::access; }; +// Check if a folder is empty +[[nodiscard]] bool IsEmptyDir(const std::string& folder_path); + // Returns true if file filename exists [[nodiscard]] bool Exists(const std::string& filename);