aboutsummaryrefslogtreecommitdiff
path: root/src/yuzu
diff options
context:
space:
mode:
authorkleidis <kleidis1@protonmail.com>2025-12-31 21:20:30 +0100
committercrueter <crueter@eden-emu.dev>2025-12-31 21:20:30 +0100
commitb0cd47c005d80d07f6924fd514dd582afa27b25d (patch)
treedb5b27ac9ed4880a272bdf21b45017872b5af7be /src/yuzu
parent18af560a43ba0d2862ce8781fe1566766bc0dd0a (diff)
[qt, android] Implement custom save path setting and migration + Implement custom path settings for Android (#3154)
Needs careful review and especially testing Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3154 Reviewed-by: DraVee <dravee@eden-emu.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev> Co-authored-by: kleidis <kleidis1@protonmail.com> Co-committed-by: kleidis <kleidis1@protonmail.com>
Diffstat (limited to 'src/yuzu')
-rw-r--r--src/yuzu/configuration/configure_filesystem.cpp136
-rw-r--r--src/yuzu/configuration/configure_filesystem.h6
-rw-r--r--src/yuzu/configuration/configure_filesystem.ui17
-rw-r--r--src/yuzu/main_window.cpp14
4 files changed, 166 insertions, 7 deletions
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp
index aae954d21e..545032eee3 100644
--- a/src/yuzu/configuration/configure_filesystem.cpp
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -5,8 +5,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "yuzu/configuration/configure_filesystem.h"
+#include <filesystem>
#include <QFileDialog>
#include <QMessageBox>
+#include <QProgressDialog>
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/settings.h"
@@ -24,6 +26,8 @@ ConfigureFilesystem::ConfigureFilesystem(QWidget* parent)
[this] { SetDirectory(DirectoryTarget::NAND, ui->nand_directory_edit); });
connect(ui->sdmc_directory_button, &QToolButton::pressed, this,
[this] { SetDirectory(DirectoryTarget::SD, ui->sdmc_directory_edit); });
+ connect(ui->save_directory_button, &QToolButton::pressed, this,
+ [this] { SetSaveDirectory(); });
connect(ui->gamecard_path_button, &QToolButton::pressed, this,
[this] { SetDirectory(DirectoryTarget::Gamecard, ui->gamecard_path_edit); });
connect(ui->dump_path_button, &QToolButton::pressed, this,
@@ -55,6 +59,8 @@ void ConfigureFilesystem::SetConfiguration() {
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir)));
ui->sdmc_directory_edit->setText(
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir)));
+ ui->save_directory_edit->setText(
+ QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::SaveDir)));
ui->gamecard_path_edit->setText(
QString::fromStdString(Settings::values.gamecard_path.GetValue()));
ui->dump_path_edit->setText(
@@ -77,6 +83,8 @@ void ConfigureFilesystem::ApplyConfiguration() {
ui->nand_directory_edit->text().toStdString());
Common::FS::SetEdenPath(Common::FS::EdenPath::SDMCDir,
ui->sdmc_directory_edit->text().toStdString());
+ Common::FS::SetEdenPath(Common::FS::EdenPath::SaveDir,
+ ui->save_directory_edit->text().toStdString());
Common::FS::SetEdenPath(Common::FS::EdenPath::DumpDir,
ui->dump_path_edit->text().toStdString());
Common::FS::SetEdenPath(Common::FS::EdenPath::LoadDir,
@@ -100,6 +108,9 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
case DirectoryTarget::SD:
caption = tr("Select Emulated SD Directory...");
break;
+ case DirectoryTarget::Save:
+ caption = tr("Select Save Data Directory...");
+ break;
case DirectoryTarget::Gamecard:
caption = tr("Select Gamecard Path...");
break;
@@ -130,6 +141,131 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
edit->setText(str);
}
+void ConfigureFilesystem::SetSaveDirectory() {
+ const QString current_path = ui->save_directory_edit->text();
+ const QString nand_path = ui->nand_directory_edit->text();
+
+ QMessageBox msgBox(this);
+ msgBox.setWindowTitle(tr("Save Data Directory"));
+ msgBox.setText(tr("Choose an action for the save data directory:"));
+
+ QPushButton* customButton = msgBox.addButton(tr("Set Custom Path"), QMessageBox::ActionRole);
+ QPushButton* resetButton = msgBox.addButton(tr("Reset to NAND"), QMessageBox::ActionRole);
+ msgBox.addButton(QMessageBox::Cancel);
+
+ msgBox.exec();
+
+ if (msgBox.clickedButton() == customButton) {
+ QString new_path = QFileDialog::getExistingDirectory(
+ this, tr("Select Save Data Directory..."), current_path);
+
+ if (new_path.isNull() || new_path.isEmpty()) {
+ return;
+ }
+
+ if (new_path.back() != QChar::fromLatin1('/')) {
+ new_path.append(QChar::fromLatin1('/'));
+ }
+
+ if (new_path != current_path) {
+ PromptSaveMigration(current_path, new_path);
+ ui->save_directory_edit->setText(new_path);
+ }
+ } else if (msgBox.clickedButton() == resetButton) {
+ if (current_path != nand_path) {
+ PromptSaveMigration(current_path, nand_path);
+ ui->save_directory_edit->setText(nand_path);
+ }
+ }
+}
+
+void ConfigureFilesystem::PromptSaveMigration(const QString& from_path, const QString& to_path) {
+ namespace fs = std::filesystem;
+
+ const fs::path source_save_dir = fs::path(from_path.toStdString()) / "user" / "save";
+ const fs::path dest_save_dir = fs::path(to_path.toStdString()) / "user" / "save";
+
+ std::error_code ec;
+
+ bool source_has_saves = false;
+ if (Common::FS::Exists(source_save_dir)) {
+ bool source_empty = fs::is_empty(source_save_dir, ec);
+ source_has_saves = !ec && !source_empty;
+ }
+
+ // Check if destination already has saves
+ bool dest_has_saves = false;
+ if (Common::FS::Exists(dest_save_dir)) {
+ bool dest_empty = fs::is_empty(dest_save_dir, ec);
+ dest_has_saves = !ec && !dest_empty;
+ }
+
+ if (!source_has_saves) {
+ return;
+ }
+
+ QString message;
+ if (dest_has_saves) {
+ message = tr("Save data exists in both the old and new locations.\n\n"
+ "Old: %1\n"
+ "New: %2\n\n"
+ "Would you like to migrate saves from the old location?\n"
+ "WARNING: This will overwrite any conflicting saves in the new location!")
+ .arg(QString::fromStdString(source_save_dir.string()))
+ .arg(QString::fromStdString(dest_save_dir.string()));
+ } else {
+ message = tr("Would you like to migrate your save data to the new location?\n\n"
+ "From: %1\n"
+ "To: %2")
+ .arg(QString::fromStdString(source_save_dir.string()))
+ .arg(QString::fromStdString(dest_save_dir.string()));
+ }
+
+ QMessageBox::StandardButton reply = QMessageBox::question(
+ this, 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 (!Common::FS::Exists(dest_save_dir)) {
+ if (!Common::FS::CreateDirs(dest_save_dir)) {
+ progress.close();
+ QMessageBox::warning(this, tr("Migration Failed"),
+ tr("Failed to create destination directory."));
+ return;
+ }
+ }
+
+ fs::copy(source_save_dir, dest_save_dir,
+ fs::copy_options::recursive | fs::copy_options::overwrite_existing, ec);
+
+ progress.close();
+
+ if (ec) {
+ QMessageBox::warning(this, tr("Migration Failed"),
+ tr("Failed to migrate save data:\n%1")
+ .arg(QString::fromStdString(ec.message())));
+ return;
+ }
+
+ QMessageBox::StandardButton deleteReply = QMessageBox::question(
+ this, tr("Migration Complete"),
+ tr("Save data has been migrated successfully.\n\n"
+ "Would you like to delete the old save data?"),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+
+ if (deleteReply == QMessageBox::Yes) {
+ Common::FS::RemoveDirRecursively(source_save_dir);
+ }
+}
+
void ConfigureFilesystem::ResetMetadata() {
QtCommon::Game::ResetMetadata();
}
diff --git a/src/yuzu/configuration/configure_filesystem.h b/src/yuzu/configuration/configure_filesystem.h
index 31d2f1d56d..d8c26a783a 100644
--- a/src/yuzu/configuration/configure_filesystem.h
+++ b/src/yuzu/configuration/configure_filesystem.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -30,12 +33,15 @@ private:
enum class DirectoryTarget {
NAND,
SD,
+ Save,
Gamecard,
Dump,
Load,
};
void SetDirectory(DirectoryTarget target, QLineEdit* edit);
+ void SetSaveDirectory();
+ void PromptSaveMigration(const QString& from_path, const QString& to_path);
void ResetMetadata();
void UpdateEnabledControls();
diff --git a/src/yuzu/configuration/configure_filesystem.ui b/src/yuzu/configuration/configure_filesystem.ui
index 2f6030b5c4..75c61c74a6 100644
--- a/src/yuzu/configuration/configure_filesystem.ui
+++ b/src/yuzu/configuration/configure_filesystem.ui
@@ -59,6 +59,23 @@
</property>
</widget>
</item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_save">
+ <property name="text">
+ <string>Save Data</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLineEdit" name="save_directory_edit"/>
+ </item>
+ <item row="2" column="3">
+ <widget class="QToolButton" name="save_directory_button">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp
index ed3d0f8466..6d5d6fc03e 100644
--- a/src/yuzu/main_window.cpp
+++ b/src/yuzu/main_window.cpp
@@ -2331,9 +2331,9 @@ void MainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
switch (target) {
case GameListOpenTarget::SaveData: {
open_target = tr("Save Data");
- const auto nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir);
- auto vfs_nand_dir =
- QtCommon::vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
+ const auto save_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::SaveDir);
+ auto vfs_save_dir =
+ QtCommon::vfs->OpenDirectory(Common::FS::PathToUTF8String(save_dir), FileSys::OpenMode::Read);
if (has_user_save) {
// User save data
@@ -2341,17 +2341,17 @@ void MainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
assert(user_id);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
- {}, vfs_nand_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account,
+ {}, vfs_save_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account,
program_id, user_id->AsU128(), 0);
- path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
+ path = Common::FS::ConcatPathSafe(save_dir, user_save_data_path);
} else {
// Device save data
const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath(
- {}, vfs_nand_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account,
+ {}, vfs_save_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account,
program_id, {}, 0);
- path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path);
+ path = Common::FS::ConcatPathSafe(save_dir, device_save_data_path);
}
if (!Common::FS::CreateDirs(path)) {