diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b5db06ae9d..3dbe2e3243 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#include #include #include #include @@ -36,6 +35,7 @@ #include "configuration/configure_per_game.h" #include "configuration/configure_tas.h" #include "core/file_sys/romfs_factory.h" +#include "core/core_timing.h" #include "core/file_sys/vfs/vfs.h" #include "core/file_sys/vfs/vfs_real.h" #include "core/frontend/applets/cabinet.h" @@ -121,7 +121,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #endif #include "common/settings.h" #include "core/core.h" -#include "core/core_timing.h" #include "core/crypto/key_manager.h" #include "core/file_sys/card_image.h" #include "core/file_sys/common_funcs.h" @@ -149,7 +148,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "video_core/shader_notify.h" #include "yuzu/about_dialog.h" #include "yuzu/bootmanager.h" -#include "yuzu/compatdb.h" #include "yuzu/compatibility_list.h" #include "yuzu/configuration/configure_dialog.h" #include "yuzu/configuration/configure_input_per_game.h" @@ -319,6 +317,22 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) Common::FS::CreateEdenPaths(); this->config = std::make_unique(); + if (user_data_migrator.migrated) { + // Sort-of hack whereby we only move the old dir if it's a subfolder of the user dir + #define MIGRATE_DIR(type) std::string type##path = Common::FS::GetEdenPathString(Common::FS::EdenPath::type##Dir); \ + if (type##path.starts_with(user_data_migrator.selected_emu.get_user_dir())) { \ + boost::replace_all(type##path, user_data_migrator.selected_emu.lower_name(), "eden"); \ + Common::FS::SetEdenPath(Common::FS::EdenPath::type##Dir, type##path); \ + } + + MIGRATE_DIR(NAND) + MIGRATE_DIR(SDMC) + MIGRATE_DIR(Dump) + MIGRATE_DIR(Load) + + #undef MIGRATE_DIR + } + #ifdef __unix__ SetupSigInterrupts(); SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); @@ -506,13 +520,14 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) SetupPrepareForSleep(); + // Some moron added a race condition to the status bar + // so now we have to make this completely unnecessary call + // to prevent the UI from blowing up. + UpdateUITheme(); + QStringList args = QApplication::arguments(); if (args.size() < 2) { - // Some moron added a race condition to the status bar - // so now we have to make this completely unnecessary call - // to prevent the UI from blowing up. - UpdateUITheme(); return; } diff --git a/src/yuzu/migration_dialog.cpp b/src/yuzu/migration_dialog.cpp index d4a6e9cd3c..9cc40841e8 100644 --- a/src/yuzu/migration_dialog.cpp +++ b/src/yuzu/migration_dialog.cpp @@ -47,10 +47,11 @@ QAbstractButton *MigrationDialog::addButton( m_buttons->addWidget(button, 1); connect(button, &QAbstractButton::clicked, this, [this, button, reject]() { + m_clickedButton = button; + if (reject) { this->reject(); } else { - m_clickedButton = button; this->accept(); } }); diff --git a/src/yuzu/migration_worker.cpp b/src/yuzu/migration_worker.cpp index 2bb6eee944..bd96fec5de 100644 --- a/src/yuzu/migration_worker.cpp +++ b/src/yuzu/migration_worker.cpp @@ -1,10 +1,13 @@ #include "migration_worker.h" +#include +#include +#include #include #include "common/fs/path_util.h" -MigrationWorker::MigrationWorker(const LegacyEmu selected_legacy_emu_, +MigrationWorker::MigrationWorker(const Emulator selected_legacy_emu_, const bool clear_shader_cache_, const MigrationStrategy strategy_) : QObject() @@ -18,31 +21,13 @@ void MigrationWorker::process() namespace fs = std::filesystem; const auto copy_options = fs::copy_options::update_existing | fs::copy_options::recursive; - std::string legacy_user_dir; - std::string legacy_config_dir; - std::string legacy_cache_dir; + std::string legacy_user_dir = selected_legacy_emu.get_user_dir(); + std::string legacy_config_dir = selected_legacy_emu.get_config_dir(); + std::string legacy_cache_dir = selected_legacy_emu.get_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; - - switch (selected_legacy_emu) { - LEGACY_EMU(Citron) - LEGACY_EMU(Sudachi) - LEGACY_EMU(Yuzu) - LEGACY_EMU(Suyu) - } - -#undef LEGACY_EMU - - fs::path eden_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir); + 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 cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir); fs::path shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); try { @@ -62,7 +47,7 @@ void MigrationWorker::process() emit error(tr("Linking the old directory failed. You may need to re-run with " "administrative privileges on Windows.\nOS gave error: %1") .arg(tr(e.what()))); - return; + std::exit(-1); } // Windows doesn't need any more links, because cache and config @@ -119,5 +104,5 @@ void MigrationWorker::process() fs::create_directory(shader_dir); } - emit finished(success_text); + emit finished(success_text, legacy_user_dir); } diff --git a/src/yuzu/migration_worker.h b/src/yuzu/migration_worker.h index 49bb695b32..205e552a94 100644 --- a/src/yuzu/migration_worker.h +++ b/src/yuzu/migration_worker.h @@ -2,25 +2,58 @@ #define MIGRATION_WORKER_H #include +#include "common/fs/path_util.h" + +using namespace Common::FS; + +typedef struct Emulator { + const char *name; + + LegacyPath e_user_dir; + LegacyPath e_config_dir; + LegacyPath e_cache_dir; + + const std::string get_user_dir() const { + return Common::FS::GetLegacyPath(e_user_dir).string(); + } + + const std::string get_config_dir() const { + return Common::FS::GetLegacyPath(e_config_dir).string(); + } + + const std::string get_cache_dir() const { + return Common::FS::GetLegacyPath(e_cache_dir).string(); + } + + const std::string lower_name() const { + std::string lower_name{name}; + std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), + [](unsigned char c){ return std::tolower(c); }); + + return lower_name; + } +} Emulator; + +#define EMU(name) Emulator{#name, name##Dir, name##ConfigDir, name##CacheDir} +static constexpr std::array legacy_emus = { + EMU(Citron), + EMU(Sudachi), + EMU(Suyu), + EMU(Yuzu), +}; +#undef EMU class MigrationWorker : public QObject { Q_OBJECT public: - enum class LegacyEmu { - Citron, - Sudachi, - Yuzu, - Suyu, - }; - enum class MigrationStrategy { Copy, Move, Link, }; - MigrationWorker(const LegacyEmu selected_legacy_emu, + MigrationWorker(const Emulator selected_legacy_emu, const bool clear_shader_cache, const MigrationStrategy strategy); @@ -28,11 +61,11 @@ public slots: void process(); signals: - void finished(const QString &success_text); + void finished(const QString &success_text, const std::string &user_dir); void error(const QString &error_message); private: - LegacyEmu selected_legacy_emu; + Emulator selected_legacy_emu; bool clear_shader_cache; MigrationStrategy strategy; QString success_text = tr("Data was migrated successfully."); 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")); + migration_prompt.setWindowTitle(QObject::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 + // 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); + // MACRO MADNESS - // Reflection would make this code 10x better - // but for now... MACRO MADNESS!!!! - QMap found; - QMap legacyMap; - QMap buttonMap; +#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); -#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; + BUTTON(QCheckBox, clear_shaders, "Clear Shader Cache", clear_shader_tooltip, true) - EMU_MAP(Citron) - EMU_MAP(Sudachi) - EMU_MAP(Yuzu) - EMU_MAP(Suyu) +#define RADIO(name, text, tooltip, checkState) \ + BUTTON(QRadioButton, name, text, tooltip, checkState) \ + group->addButton(name); -#undef EMU_MAP + 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) - if (any_found) { - QString promptText = main_window->tr( - "Eden has detected user data for the following emulators:"); - QMapIterator iter(found); +#undef RADIO +#undef BUTTON - while (iter.hasNext()) { - iter.next(); - if (!iter.value()) - continue; + std::vector found{}; - QAbstractButton *button = migration_prompt.addButton(iter.key()); + for (const Emulator &emu : legacy_emus) + if (fs::is_directory(emu.get_user_dir())) + found.emplace_back(emu); - 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); + if (found.empty()) { + return; } - else // no other data was found - 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(); }); diff --git a/src/yuzu/user_data_migration.h b/src/yuzu/user_data_migration.h index 3482b6c398..3684d14916 100644 --- a/src/yuzu/user_data_migration.h +++ b/src/yuzu/user_data_migration.h @@ -14,11 +14,14 @@ class UserDataMigrator { public: UserDataMigrator(QMainWindow* main_window); + bool migrated{false}; + Emulator selected_emu; + private: void ShowMigrationPrompt(QMainWindow* main_window); void ShowMigrationCancelledMessage(QMainWindow* main_window); void MigrateUserData(QMainWindow* main_window, - const MigrationWorker::LegacyEmu selected_legacy_emu, + const Emulator selected_legacy_emu, const bool clear_shader_cache, const MigrationWorker::MigrationStrategy strategy); };