aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorswurl <swurl@swurl.xyz>2025-05-10 15:40:15 +0000
committercrueter <crueter@noreply.localhost>2025-05-10 15:40:15 +0000
commit28d2b0638022531f315afb11e2ede8da9ca595be (patch)
treef2d7d65b619c4aae2de7470fb223f3aea96487c5 /src
parent09c72e9f98354e89ea8b49a882db8dae47dc3149 (diff)
Options for Data Migration (#95)
Copy, move, or link Co-authored-by: KeatonTheBot <onikeaton@gmail.com> Signed-off-by: swurl <swurl@swurl.xyz> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/95 Co-authored-by: swurl <swurl@swurl.xyz> Co-committed-by: swurl <swurl@swurl.xyz>
Diffstat (limited to 'src')
-rw-r--r--src/yuzu/CMakeLists.txt1
-rw-r--r--src/yuzu/migration_dialog.cpp63
-rw-r--r--src/yuzu/migration_dialog.h30
-rw-r--r--src/yuzu/user_data_migration.cpp221
-rw-r--r--src/yuzu/user_data_migration.h9
5 files changed, 268 insertions, 56 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index ef2dd57044..15b898279b 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -237,6 +237,7 @@ add_executable(yuzu
yuzu.qrc
yuzu.rc
+ migration_dialog.h migration_dialog.cpp
)
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
diff --git a/src/yuzu/migration_dialog.cpp b/src/yuzu/migration_dialog.cpp
new file mode 100644
index 0000000000..d4a6e9cd3c
--- /dev/null
+++ b/src/yuzu/migration_dialog.cpp
@@ -0,0 +1,63 @@
+#include "migration_dialog.h"
+
+#include <QApplication>
+#include <QCheckBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QStyle>
+
+MigrationDialog::MigrationDialog(
+ QWidget *parent)
+ : QDialog(parent)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ m_text = new QLabel(this);
+ m_boxes = new QVBoxLayout;
+ m_buttons = new QHBoxLayout;
+
+ layout->addWidget(m_text, 1);
+ layout->addLayout(m_boxes, 1);
+ layout->addLayout(m_buttons, 1);
+}
+
+MigrationDialog::~MigrationDialog() {
+ m_boxes->deleteLater();
+ m_buttons->deleteLater();
+}
+
+void MigrationDialog::setText(
+ const QString &text)
+{
+ m_text->setText(text);
+}
+
+void MigrationDialog::addBox(
+ QWidget *box)
+{
+ m_boxes->addWidget(box);
+
+}
+
+QAbstractButton *MigrationDialog::addButton(
+ const QString &text, const bool reject)
+{
+ QAbstractButton *button = new QPushButton(this);
+ button->setText(text);
+ m_buttons->addWidget(button, 1);
+
+ connect(button, &QAbstractButton::clicked, this, [this, button, reject]() {
+ if (reject) {
+ this->reject();
+ } else {
+ m_clickedButton = button;
+ this->accept();
+ }
+ });
+ return button;
+}
+
+QAbstractButton *MigrationDialog::clickedButton() const
+{
+ return m_clickedButton;
+}
diff --git a/src/yuzu/migration_dialog.h b/src/yuzu/migration_dialog.h
new file mode 100644
index 0000000000..7123fd6b87
--- /dev/null
+++ b/src/yuzu/migration_dialog.h
@@ -0,0 +1,30 @@
+#ifndef MIGRATION_DIALOG_H
+#define MIGRATION_DIALOG_H
+
+#include <QMessageBox>
+#include <QWidget>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+
+class MigrationDialog : public QDialog {
+ Q_OBJECT
+public:
+ MigrationDialog(QWidget *parent = nullptr);
+
+ virtual ~MigrationDialog();
+
+ void setText(const QString &text);
+ void addBox(QWidget *box);
+ QAbstractButton *addButton(const QString &text, const bool reject = false);
+
+ QAbstractButton *clickedButton() const;
+
+private:
+ QLabel *m_text;
+ QVBoxLayout *m_boxes;
+ QHBoxLayout *m_buttons;
+
+ QAbstractButton *m_clickedButton;
+};
+
+#endif // MIGRATION_DIALOG_H
diff --git a/src/yuzu/user_data_migration.cpp b/src/yuzu/user_data_migration.cpp
index d890550222..1946254fb2 100644
--- a/src/yuzu/user_data_migration.cpp
+++ b/src/yuzu/user_data_migration.cpp
@@ -5,20 +5,25 @@
// SPDX-FileCopyrightText: Copyright 2025 eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+#include "user_data_migration.h"
#include <QMessageBox>
#include <QPushButton>
#include <QString>
#include <QTranslator>
#include "common/fs/path_util.h"
-#include "user_data_migration.h"
+#include "migration_dialog.h"
// Needs to be included at the end due to https://bugreports.qt.io/browse/QTBUG-73263
+#include <QButtonGroup>
#include <QCheckBox>
+#include <QRadioButton>
#include <filesystem>
namespace fs = std::filesystem;
-UserDataMigrator::UserDataMigrator(QMainWindow* main_window) {
+UserDataMigrator::UserDataMigrator(
+ QMainWindow *main_window)
+{
// NOTE: Logging is not initialized yet, do not produce logs here.
// Check migration if config directory does not exist
@@ -29,25 +34,58 @@ UserDataMigrator::UserDataMigrator(QMainWindow* main_window) {
}
}
-void UserDataMigrator::ShowMigrationPrompt(QMainWindow* main_window) {
+void UserDataMigrator::ShowMigrationPrompt(
+ QMainWindow *main_window)
+{
namespace fs = std::filesystem;
- const QString migration_prompt_message =
- main_window->tr("Would you like to migrate your data for use in Eden?\n"
- "Select the corresponding button to migrate data from that emulator.\n"
- "(This may take a while; The old data will not be deleted)\n\n"
- "Clearing shader cache is recommended for all users.\nDo not uncheck unless you know what you're doing.");
+ const QString migration_prompt_message = main_window->tr(
+ "Would you like to migrate your data for use in Eden?\n"
+ "Select the corresponding button to migrate data from that emulator.\n"
+ "This may take a while.");
bool any_found = false;
- QMessageBox migration_prompt;
+ MigrationDialog migration_prompt;
migration_prompt.setWindowTitle(main_window->tr("Migration"));
- migration_prompt.setIcon(QMessageBox::Information);
QCheckBox *clear_shaders = new QCheckBox(&migration_prompt);
clear_shaders->setText(main_window->tr("Clear Shader Cache"));
+ clear_shaders->setToolTip(main_window->tr(
+ "Clearing shader cache is recommended for all users.\nDo not uncheck unless you know what "
+ "you're doing."));
clear_shaders->setChecked(true);
- migration_prompt.setCheckBox(clear_shaders);
+
+ QRadioButton *keep_old = new QRadioButton(&migration_prompt);
+ keep_old->setText(main_window->tr("Keep Old Data"));
+ keep_old->setToolTip(
+ main_window->tr("Keeps the old data directory. This is recommended if you aren't\n"
+ "space-constrained and want to keep separate data for the old emulator."));
+ keep_old->setChecked(true);
+
+ QRadioButton *clear_old = new QRadioButton(&migration_prompt);
+ clear_old->setText(main_window->tr("Clear Old Data"));
+ clear_old->setToolTip(main_window->tr("Deletes the old data directory.\nThis is recommended on "
+ "devices with space constraints."));
+ clear_old->setChecked(false);
+
+ QRadioButton *link = new QRadioButton(&migration_prompt);
+ link->setText(main_window->tr("Link Old Directory"));
+ link->setToolTip(
+ main_window->tr("Creates a filesystem link between the old directory and Eden directory.\n"
+ "This is recommended if you want to share data between emulators.."));
+ link->setChecked(false);
+
+ // Link and Clear Old are mutually exclusive
+ QButtonGroup *group = new QButtonGroup(&migration_prompt);
+ group->addButton(keep_old);
+ group->addButton(clear_old);
+ group->addButton(link);
+
+ migration_prompt.addBox(clear_shaders);
+ migration_prompt.addBox(keep_old);
+ migration_prompt.addBox(clear_old);
+ migration_prompt.addBox(link);
// Reflection would make this code 10x better
// but for now... MACRO MADNESS!!!!
@@ -55,10 +93,13 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow* main_window) {
QMap<QString, LegacyEmu> legacyMap;
QMap<QString, QAbstractButton *> buttonMap;
-#define EMU_MAP(name) const bool name##_found = fs::is_directory(Common::FS::GetLegacyPath(Common::FS::LegacyPath::name##Dir)); \
- legacyMap[main_window->tr(#name)] = LegacyEmu::name; \
- found[main_window->tr(#name)] = name##_found; \
- if (name##_found) any_found = true;
+#define EMU_MAP(name) \
+ const bool name##_found = fs::is_directory( \
+ Common::FS::GetLegacyPath(Common::FS::LegacyPath::name##Dir)); \
+ legacyMap[main_window->tr(#name)] = LegacyEmu::name; \
+ found[main_window->tr(#name)] = name##_found; \
+ if (name##_found) \
+ any_found = true;
EMU_MAP(Citron)
EMU_MAP(Sudachi)
@@ -68,30 +109,44 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow* main_window) {
#undef EMU_MAP
if (any_found) {
- QString promptText = main_window->tr("Eden has detected user data for the following emulators:");
+ QString promptText = main_window->tr(
+ "Eden has detected user data for the following emulators:");
QMapIterator iter(found);
while (iter.hasNext()) {
iter.next();
- if (!iter.value()) continue;
+ if (!iter.value())
+ continue;
- buttonMap[iter.key()] = migration_prompt.addButton(iter.key(), QMessageBox::YesRole);
+ buttonMap[iter.key()] = migration_prompt.addButton(iter.key());
promptText.append(main_window->tr("\n- %1").arg(iter.key()));
}
promptText.append(main_window->tr("\n\n"));
migration_prompt.setText(promptText + migration_prompt_message);
- migration_prompt.addButton(QMessageBox::No);
+ migration_prompt.addButton(main_window->tr("No"), true);
migration_prompt.exec();
+ MigrationStrategy strategy;
+ if (link->isChecked()) {
+ strategy = MigrationStrategy::Link;
+ } else if (clear_old->isChecked()) {
+ strategy = MigrationStrategy::Move;
+ } else {
+ strategy = MigrationStrategy::Copy;
+ }
+
QMapIterator buttonIter(buttonMap);
while (buttonIter.hasNext()) {
buttonIter.next();
if (buttonIter.value() == migration_prompt.clickedButton()) {
- MigrateUserData(main_window, legacyMap[iter.key()], clear_shaders->isChecked());
+ MigrateUserData(main_window,
+ legacyMap[buttonIter.key()],
+ clear_shaders->isChecked(),
+ strategy);
return;
}
}
@@ -104,31 +159,43 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow* main_window) {
return;
}
-void UserDataMigrator::ShowMigrationCancelledMessage(QMainWindow* main_window) {
- QMessageBox::information(
- main_window, main_window->tr("Migration"),
- main_window
- ->tr("You can manually re-trigger this prompt by deleting the "
- "new config directory:\n"
- "%1")
- .arg(QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::ConfigDir))),
- QMessageBox::Ok);
+void UserDataMigrator::ShowMigrationCancelledMessage(
+ QMainWindow *main_window)
+{
+ QMessageBox::information(main_window,
+ main_window->tr("Migration"),
+ main_window
+ ->tr("You can manually re-trigger this prompt by deleting the "
+ "new config directory:\n"
+ "%1")
+ .arg(QString::fromStdString(Common::FS::GetEdenPathString(
+ Common::FS::EdenPath::ConfigDir))),
+ QMessageBox::Ok);
}
-void UserDataMigrator::MigrateUserData(QMainWindow* main_window,
- const LegacyEmu selected_legacy_emu, const bool clear_shader_cache) {
+void UserDataMigrator::MigrateUserData(
+ QMainWindow *main_window,
+ const LegacyEmu selected_legacy_emu,
+ const bool clear_shader_cache,
+ const MigrationStrategy strategy)
+{
namespace fs = std::filesystem;
const auto copy_options = fs::copy_options::update_existing | fs::copy_options::recursive;
+ QString success_text = main_window->tr("Data was migrated successfully.");
+
std::string legacy_user_dir;
std::string legacy_config_dir;
std::string legacy_cache_dir;
-#define LEGACY_EMU(emu) case LegacyEmu::emu: \
- legacy_user_dir = Common::FS::GetLegacyPath(Common::FS::LegacyPath::emu##Dir).string(); \
- legacy_config_dir = Common::FS::GetLegacyPath(Common::FS::LegacyPath::emu##ConfigDir).string(); \
- legacy_cache_dir = Common::FS::GetLegacyPath(Common::FS::LegacyPath::emu##CacheDir).string(); \
- break;
+#define LEGACY_EMU(emu) \
+case LegacyEmu::emu: \
+ legacy_user_dir = Common::FS::GetLegacyPath(Common::FS::LegacyPath::emu##Dir).string(); \
+ legacy_config_dir = Common::FS::GetLegacyPath(Common::FS::LegacyPath::emu##ConfigDir) \
+ .string(); \
+ legacy_cache_dir = Common::FS::GetLegacyPath(Common::FS::LegacyPath::emu##CacheDir) \
+ .string(); \
+ break;
switch (selected_legacy_emu) {
LEGACY_EMU(Citron)
@@ -139,30 +206,74 @@ void UserDataMigrator::MigrateUserData(QMainWindow* main_window,
#undef LEGACY_EMU
- fs::copy(legacy_user_dir, Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir), copy_options);
+ fs::path eden_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir);
+ fs::path config_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir);
+ fs::path cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir);
+ fs::path shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
- if (fs::is_directory(legacy_config_dir)) {
- fs::copy(legacy_config_dir, Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir),
- copy_options);
- }
- if (fs::is_directory(legacy_cache_dir)) {
- fs::copy(legacy_cache_dir, Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir),
- copy_options);
+ fs::remove_all(eden_dir);
+
+ switch (strategy) {
+ case MigrationStrategy::Link:
+ // Create symlinks/directory junctions if requested
+ fs::create_directory_symlink(legacy_user_dir, eden_dir);
+
+// Windows doesn't need any more links, because cache and config
+// are already children of the root directory
+#ifndef WIN32
+ if (fs::is_directory(legacy_config_dir)) {
+ fs::create_directory_symlink(legacy_config_dir, config_dir);
+ }
+
+ if (fs::is_directory(legacy_cache_dir)) {
+ fs::create_directory_symlink(legacy_cache_dir, cache_dir);
+ }
+#endif
+ break;
+ case MigrationStrategy::Move:
+ // Rename directories if deletion is requested (achieves the same result)
+ fs::rename(legacy_user_dir, eden_dir);
+
+// Windows doesn't need any more renames, because cache and config
+// are already children of the root directory
+#ifndef WIN32
+ if (fs::is_directory(legacy_config_dir)) {
+ fs::rename(legacy_config_dir, config_dir);
+ }
+
+ if (fs::is_directory(legacy_cache_dir)) {
+ fs::rename(legacy_cache_dir, cache_dir);
+ }
+#endif
+ break;
+ case MigrationStrategy::Copy:
+ default:
+ // Default behavior: copy
+ fs::copy(legacy_user_dir, eden_dir, copy_options);
+
+ if (fs::is_directory(legacy_config_dir)) {
+ fs::copy(legacy_config_dir, config_dir, copy_options);
+ }
+
+ if (fs::is_directory(legacy_cache_dir)) {
+ fs::copy(legacy_cache_dir, cache_dir, copy_options);
+ }
+
+ success_text.append(
+ main_window->tr("\n\nIf you wish to clean up the files which were left in the old "
+ "data location, you can do so by deleting the following directory:\n"
+ "%1").arg(QString::fromStdString(legacy_user_dir)));
+ break;
}
// Delete and re-create shader dir
if (clear_shader_cache) {
- fs::remove_all(Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir));
- fs::create_directory(Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir));
+ fs::remove_all(shader_dir);
+ fs::create_directory(shader_dir);
}
- QMessageBox::information(
- main_window, main_window->tr("Migration"),
- main_window
- ->tr("Data was migrated successfully.\n\n"
- "If you wish to clean up the files which were left in the old "
- "data location, you can do so by deleting the following directory:\n"
- "%1")
- .arg(QString::fromStdString(legacy_user_dir)),
- QMessageBox::Ok);
+ QMessageBox::information(main_window,
+ main_window->tr("Migration"),
+ success_text,
+ QMessageBox::Ok);
}
diff --git a/src/yuzu/user_data_migration.h b/src/yuzu/user_data_migration.h
index 098f19280b..3c4f745b87 100644
--- a/src/yuzu/user_data_migration.h
+++ b/src/yuzu/user_data_migration.h
@@ -21,7 +21,14 @@ private:
Suyu,
};
+ enum class MigrationStrategy {
+ Copy,
+ Move,
+ Link,
+ };
+
void ShowMigrationPrompt(QMainWindow* main_window);
void ShowMigrationCancelledMessage(QMainWindow* main_window);
- void MigrateUserData(QMainWindow* main_window, const LegacyEmu selected_legacy_emu, const bool clear_shader_cache);
+ void MigrateUserData(QMainWindow* main_window, const LegacyEmu selected_legacy_emu,
+ const bool clear_shader_cache, const MigrationStrategy strategy);
};