From c4ca8d2367e3283e2adcc37833376d68b2ece6fc Mon Sep 17 00:00:00 2001 From: crueter Date: Wed, 11 Jun 2025 22:57:13 +0000 Subject: Properly migrate internal NAND, Load, etc. directories (#167) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/167 Co-authored-by: crueter Co-committed-by: crueter --- src/yuzu/user_data_migration.cpp | 245 +++++++++++++++++++-------------------- 1 file changed, 120 insertions(+), 125 deletions(-) (limited to 'src/yuzu/user_data_migration.cpp') diff --git a/src/yuzu/user_data_migration.cpp b/src/yuzu/user_data_migration.cpp index 3851e183b2..7424fc2c44 100644 --- a/src/yuzu/user_data_migration.cpp +++ b/src/yuzu/user_data_migration.cpp @@ -40,150 +40,144 @@ 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."); + // define strings here for easy access + static constexpr const char *prompt_prefix_text + = "Eden has detected user data for the following emulators:"; - bool any_found = false; + static constexpr const char *migration_prompt_message + = "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."; + + static constexpr const char *clear_shader_tooltip + = "Clearing shader cache is recommended for all " + "users.\nDo not uncheck unless you know what " + "you're doing."; + + static constexpr const char *keep_old_data_tooltip + = "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."; + + static constexpr const char *clear_old_data_tooltip + = "Deletes the old data directory.\nThis is recommended on " + "devices with space constraints."; + + static constexpr const char *link_old_dir_tooltip + = "Creates a filesystem link between the old directory and Eden directory.\n" + "This is recommended if you want to share data between emulators."; + + // actual migration code MigrationDialog migration_prompt; - migration_prompt.setWindowTitle(main_window->tr("Migration")); - - 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); - - 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 + migration_prompt.setWindowTitle(QObject::tr("Migration")); + + // 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!!!! - QMap found; - QMap legacyMap; - QMap 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)] = MigrationWorker::LegacyEmu::name; \ - found[main_window->tr(#name)] = name##_found; \ - if (name##_found) \ - any_found = true; - - EMU_MAP(Citron) - EMU_MAP(Sudachi) - EMU_MAP(Yuzu) - EMU_MAP(Suyu) - -#undef EMU_MAP - - if (any_found) { - 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; - - QAbstractButton *button = migration_prompt.addButton(iter.key()); - - buttonMap[iter.key()] = button; - 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(main_window->tr("No"), true); - - migration_prompt.exec(); - - MigrationWorker::MigrationStrategy strategy; - if (link->isChecked()) { - strategy = MigrationWorker::MigrationStrategy::Link; - } else if (clear_old->isChecked()) { - strategy = MigrationWorker::MigrationStrategy::Move; - } else { - strategy = MigrationWorker::MigrationStrategy::Copy; - } - - QMapIterator buttonIter(buttonMap); - - while (buttonIter.hasNext()) { - buttonIter.next(); - if (buttonIter.value() == migration_prompt.clickedButton()) { - MigrateUserData(main_window, - legacyMap[buttonIter.key()], - clear_shaders->isChecked(), - strategy); - return; - } - } - - // If we're here, the user chose not to migrate - ShowMigrationCancelledMessage(main_window); - } - else // no other data was found + // MACRO MADNESS + +#define BUTTON(clazz, name, text, tooltip, checkState) \ + clazz *name = new clazz(&migration_prompt); \ + name->setText(QObject::tr(text)); \ + name->setToolTip(QObject::tr(tooltip)); \ + name->setChecked(checkState); \ + migration_prompt.addBox(name); + + BUTTON(QCheckBox, clear_shaders, "Clear Shader Cache", clear_shader_tooltip, true) + +#define RADIO(name, text, tooltip, checkState) \ + BUTTON(QRadioButton, name, text, tooltip, checkState) \ + group->addButton(name); + + RADIO(keep_old, "Keep Old Data", keep_old_data_tooltip, true) + RADIO(clear_old, "Clear Old Data", clear_old_data_tooltip, false) + RADIO(link_old, "Link Old Directory", link_old_dir_tooltip, false) + +#undef RADIO +#undef BUTTON + + std::vector found{}; + + for (const Emulator &emu : legacy_emus) + if (fs::is_directory(emu.get_user_dir())) + found.emplace_back(emu); + + if (found.empty()) { return; + } + + // makes my life easier + qRegisterMetaType(); + + QString prompt_text = QObject::tr(prompt_prefix_text); + + // natural language processing is a nightmare + for (const Emulator &emu : found) { + prompt_text.append(QStringLiteral("\n- %1").arg(QObject::tr(emu.name))); + + QAbstractButton *button = migration_prompt.addButton(QObject::tr(emu.name)); + + // This is cursed, but it's actually the most efficient way by a mile + button->setProperty("emulator", QVariant::fromValue(emu)); + } + + prompt_text.append(QObject::tr("\n\n")); + prompt_text.append(QObject::tr(migration_prompt_message)); + + migration_prompt.setText(prompt_text); + migration_prompt.addButton(QObject::tr("No"), true); + + migration_prompt.exec(); + + QAbstractButton *button = migration_prompt.clickedButton(); + + if (button->text() == QObject::tr("No")) { + return ShowMigrationCancelledMessage(main_window); + } + + MigrationWorker::MigrationStrategy strategy; + switch (group->checkedId()) { + default: + [[fallthrough]]; + case 0: + strategy = MigrationWorker::MigrationStrategy::Copy; + break; + case 1: + strategy = MigrationWorker::MigrationStrategy::Move; + break; + case 2: + strategy = MigrationWorker::MigrationStrategy::Link; + break; + } + + MigrateUserData(main_window, + button->property("emulator").value(), + clear_shaders->isChecked(), + strategy); } 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") + QObject::tr("Migration"), + QObject::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 MigrationWorker::LegacyEmu selected_legacy_emu, + const Emulator selected_legacy_emu, const bool clear_shader_cache, const MigrationWorker::MigrationStrategy strategy) { - // Create a dialog to let the user know it's migrating, some users noted confusion. + selected_emu = selected_legacy_emu; + + // Create a dialog to let the user know it's migrating QProgressDialog *progress = new QProgressDialog(main_window); - progress->setWindowTitle(main_window->tr("Migrating")); - progress->setLabelText(main_window->tr("Migrating, this may take a while...")); + progress->setWindowTitle(QObject::tr("Migrating")); + progress->setLabelText(QObject::tr("Migrating, this may take a while...")); progress->setRange(0, 0); progress->setCancelButton(nullptr); progress->setWindowModality(Qt::WindowModality::ApplicationModal); @@ -194,13 +188,14 @@ void UserDataMigrator::MigrateUserData(QMainWindow *main_window, thread->connect(thread, &QThread::started, worker, &MigrationWorker::process); - thread->connect(worker, &MigrationWorker::finished, progress, [=](const QString &success_text) { + thread->connect(worker, &MigrationWorker::finished, progress, [=, this](const QString &success_text, const std::string &path) { progress->close(); QMessageBox::information(main_window, - main_window->tr("Migration"), + QObject::tr("Migration"), success_text, QMessageBox::Ok); + migrated = true; thread->quit(); }); -- cgit v1.2.3