User Data Migration from Citron, Sudachi, and Yuzu (#91)

Includes citron, sudachi, yuzu

currently broken, because the eden dir is always made early?

Signed-off-by: swurl <swurl@swurl.xyz>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/91
Co-authored-by: swurl <swurl@swurl.xyz>
Co-committed-by: swurl <swurl@swurl.xyz>
This commit is contained in:
swurl 2025-05-08 22:16:07 +00:00 committed by crueter
parent d659d6f5b4
commit f786802b9b
62 changed files with 703 additions and 384 deletions

View file

@ -232,6 +232,9 @@ add_executable(yuzu
vk_device_info.h
compatdb.cpp
compatdb.h
user_data_migration.cpp
user_data_migration.h
yuzu.qrc
yuzu.rc
)

View file

@ -30,7 +30,7 @@ QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
QString GetImagePath(Common::UUID uuid) {
const auto path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) /
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
return QString::fromStdString(Common::FS::PathToUTF8String(path));
}

View file

@ -57,7 +57,7 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system,
default_profile{QWebEngineProfile::defaultProfile()}, global_settings{
default_profile->settings()} {
default_profile->setPersistentStoragePath(QString::fromStdString(Common::FS::PathToUTF8String(
Common::FS::GetYuzuPath(Common::FS::YuzuPath::YuzuDir) / "qtwebengine")));
Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir) / "qtwebengine")));
QWebEngineScript gamepad;
QWebEngineScript window_nx;
@ -336,7 +336,7 @@ void QtNXWebEngineView::LoadExtractedFonts() {
QWebEngineScript load_nx_font;
auto fonts_dir_str = Common::FS::PathToUTF8String(
Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "fonts/");
Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "fonts/");
std::replace(fonts_dir_str.begin(), fonts_dir_str.end(), '\\', '/');

View file

@ -60,7 +60,7 @@ static void PruneDumpDirectory(const std::filesystem::path& dump_path) {
void InstallCrashHandler() {
// Write crash dumps to profile directory.
const auto dump_path = GetYuzuPath(Common::FS::YuzuPath::CrashDumpsDir);
const auto dump_path = GetEdenPath(Common::FS::EdenPath::CrashDumpsDir);
PruneDumpDirectory(dump_path);
#if defined(_WIN32)

View file

@ -21,7 +21,7 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
connect(ui->open_log_button, &QPushButton::clicked, []() {
const auto path =
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LogDir));
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LogDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
});

View file

@ -47,15 +47,15 @@ void ConfigureFilesystem::changeEvent(QEvent* event) {
void ConfigureFilesystem::SetConfiguration() {
ui->nand_directory_edit->setText(
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir)));
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir)));
ui->sdmc_directory_edit->setText(
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::SDMCDir)));
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir)));
ui->gamecard_path_edit->setText(
QString::fromStdString(Settings::values.gamecard_path.GetValue()));
ui->dump_path_edit->setText(
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::DumpDir)));
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::DumpDir)));
ui->load_path_edit->setText(
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LoadDir)));
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LoadDir)));
ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted.GetValue());
ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game.GetValue());
@ -68,13 +68,13 @@ void ConfigureFilesystem::SetConfiguration() {
}
void ConfigureFilesystem::ApplyConfiguration() {
Common::FS::SetYuzuPath(Common::FS::YuzuPath::NANDDir,
Common::FS::SetEdenPath(Common::FS::EdenPath::NANDDir,
ui->nand_directory_edit->text().toStdString());
Common::FS::SetYuzuPath(Common::FS::YuzuPath::SDMCDir,
Common::FS::SetEdenPath(Common::FS::EdenPath::SDMCDir,
ui->sdmc_directory_edit->text().toStdString());
Common::FS::SetYuzuPath(Common::FS::YuzuPath::DumpDir,
Common::FS::SetEdenPath(Common::FS::EdenPath::DumpDir,
ui->dump_path_edit->text().toStdString());
Common::FS::SetYuzuPath(Common::FS::YuzuPath::LoadDir,
Common::FS::SetEdenPath(Common::FS::EdenPath::LoadDir,
ui->load_path_edit->text().toStdString());
Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked();
@ -126,12 +126,12 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
}
void ConfigureFilesystem::ResetMetadata() {
if (!Common::FS::Exists(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
if (!Common::FS::Exists(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) /
"game_list/")) {
QMessageBox::information(this, tr("Reset Metadata Cache"),
tr("The metadata cache is already empty."));
} else if (Common::FS::RemoveDirRecursively(
Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "game_list")) {
Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list")) {
QMessageBox::information(this, tr("Reset Metadata Cache"),
tr("The operation completed successfully."));
UISettings::values.is_game_list_reload_pending.exchange(true);

View file

@ -80,7 +80,7 @@ void ConfigurePerGameAddons::ApplyConfiguration() {
std::sort(disabled_addons.begin(), disabled_addons.end());
std::sort(current.begin(), current.end());
if (disabled_addons != current) {
Common::FS::RemoveFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
Common::FS::RemoveFile(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) /
"game_list" / fmt::format("{:016X}.pv.txt", title_id));
}

View file

@ -35,7 +35,7 @@ constexpr std::array<u8, 107> backup_jpeg{
QString GetImagePath(const Common::UUID& uuid) {
const auto path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) /
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
return QString::fromStdString(Common::FS::PathToUTF8String(path));
}
@ -288,7 +288,7 @@ void ConfigureProfileManager::SetUserImage() {
}
const auto raw_path = QString::fromStdString(Common::FS::PathToUTF8String(
Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000010"));
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000010"));
const QFileInfo raw_info{raw_path};
if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) {
QMessageBox::warning(this, tr("Error deleting file"),

View file

@ -28,14 +28,14 @@ ConfigureTasDialog::~ConfigureTasDialog() = default;
void ConfigureTasDialog::LoadConfiguration() {
ui->tas_path_edit->setText(
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir)));
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::TASDir)));
ui->tas_enable->setChecked(Settings::values.tas_enable.GetValue());
ui->tas_loop_script->setChecked(Settings::values.tas_loop.GetValue());
ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load.GetValue());
}
void ConfigureTasDialog::ApplyConfiguration() {
Common::FS::SetYuzuPath(Common::FS::YuzuPath::TASDir, ui->tas_path_edit->text().toStdString());
Common::FS::SetEdenPath(Common::FS::EdenPath::TASDir, ui->tas_path_edit->text().toStdString());
Settings::values.tas_enable.SetValue(ui->tas_enable->isChecked());
Settings::values.tas_loop.SetValue(ui->tas_loop_script->isChecked());
Settings::values.pause_tas_on_load.SetValue(ui->tas_pause_on_load->isChecked());

View file

@ -144,8 +144,8 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] {
auto dir =
QFileDialog::getExistingDirectory(this, tr("Select Screenshots Path..."),
QString::fromStdString(Common::FS::GetYuzuPathString(
Common::FS::YuzuPath::ScreenshotsDir)));
QString::fromStdString(Common::FS::GetEdenPathString(
Common::FS::EdenPath::ScreenshotsDir)));
if (!dir.isEmpty()) {
if (dir.back() != QChar::fromLatin1('/')) {
dir.append(QChar::fromLatin1('/'));
@ -176,7 +176,7 @@ void ConfigureUi::ApplyConfiguration() {
UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked();
Common::FS::SetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir,
Common::FS::SetEdenPath(Common::FS::EdenPath::ScreenshotsDir,
ui->screenshot_path_edit->text().toStdString());
const u32 height = ScreenshotDimensionToInt(ui->screenshot_height->currentText());
@ -208,7 +208,7 @@ void ConfigureUi::SetConfiguration() {
ui->enable_screenshot_save_as->setChecked(
UISettings::values.enable_screenshot_save_as.GetValue());
ui->screenshot_path_edit->setText(QString::fromStdString(
Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)));
Common::FS::GetEdenPathString(Common::FS::EdenPath::ScreenshotsDir)));
const auto height = UISettings::values.screenshot_height.GetValue();
if (height == 0) {

View file

@ -14,7 +14,7 @@ namespace FS = Common::FS;
namespace {
bool ProfileExistsInFilesystem(std::string_view profile_name) {
return FS::Exists(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input" /
return FS::Exists(FS::GetEdenPath(FS::EdenPath::ConfigDir) / "input" /
fmt::format("{}.ini", profile_name));
}
@ -29,7 +29,7 @@ std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) {
} // namespace
InputProfiles::InputProfiles() {
const auto input_profile_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input";
const auto input_profile_loc = FS::GetEdenPath(FS::EdenPath::ConfigDir) / "input";
if (!FS::IsDir(input_profile_loc)) {
return;

View file

@ -46,6 +46,7 @@ const std::array<int, 2> QtConfig::default_ringcon_analogs{{
QtConfig::QtConfig(const std::string& config_name, const ConfigType config_type)
: Config(config_type) {
Initialize(config_name);
if (config_type != ConfigType::InputProfile) {
ReadQtValues();

View file

@ -38,7 +38,7 @@ QString GetGameListCachedObject(const std::string& filename, const std::string&
}
const auto path =
Common::FS::PathToUTF8String(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
Common::FS::PathToUTF8String(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) /
"game_list" / fmt::format("{}.{}", filename, ext));
void(Common::FS::CreateParentDirs(path));
@ -70,7 +70,7 @@ std::pair<std::vector<u8>, std::string> GetGameListCachedObject(
}
const auto game_list_dir =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "game_list";
Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list";
const auto jpeg_name = fmt::format("{}.jpeg", filename);
const auto app_name = fmt::format("{}.appname.txt", filename);

View file

@ -211,7 +211,7 @@ static const QList<u64> bad_update_games{
const int GMainWindow::max_recent_files_item;
static void RemoveCachedContents() {
const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir);
const auto cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir);
const auto offline_fonts = cache_dir / "fonts";
const auto offline_manual = cache_dir / "offline_web_applet_manual";
const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information";
@ -301,15 +301,20 @@ bool GMainWindow::CheckDarkMode() {
#endif // __unix__
}
GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan)
GMainWindow::GMainWindow(bool has_broken_vulkan)
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
user_data_migrator{this},
vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
provider{std::make_unique<FileSys::ManualContentProvider>()} {
Common::FS::CreateEdenPaths();
this->config = std::make_unique<QtConfig>();
#ifdef __unix__
SetupSigInterrupts();
SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
#endif
UISettings::RestoreWindowState(config);
system->Initialize();
Common::Log::Initialize();
@ -2322,7 +2327,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
switch (target) {
case GameListOpenTarget::SaveData: {
open_target = tr("Save Data");
const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
const auto nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir);
auto vfs_nand_dir =
vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
@ -2378,7 +2383,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
}
case GameListOpenTarget::ModData: {
open_target = tr("Mod Data");
path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir) /
path = Common::FS::GetEdenPath(Common::FS::EdenPath::LoadDir) /
fmt::format("{:016X}", program_id);
break;
}
@ -2400,7 +2405,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
}
void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir);
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)};
if (!Common::FS::CreateDirs(shader_cache_folder_path)) {
QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"),
@ -2519,7 +2524,7 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
RemoveAddOnContent(program_id, type);
break;
}
Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
Common::FS::RemoveDirRecursively(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) /
"game_list");
game_list->PopulateAsync(UISettings::values.game_dirs);
}
@ -2625,7 +2630,7 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id, GameListRemoveTa
return "";
}
}();
const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir);
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id);
const auto target_file = shader_cache_folder_path / target_file_name;
@ -2646,7 +2651,7 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id, GameListRemoveTa
void GMainWindow::RemoveVulkanDriverPipelineCache(u64 program_id) {
static constexpr std::string_view target_file_name = "vulkan_pipelines.bin";
const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir);
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id);
const auto target_file = shader_cache_folder_path / target_file_name;
@ -2660,7 +2665,7 @@ void GMainWindow::RemoveVulkanDriverPipelineCache(u64 program_id) {
}
void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) {
const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir);
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id);
if (!Common::FS::Exists(program_shader_cache_dir)) {
@ -2683,7 +2688,7 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id, const std::string& g
program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini")
: fmt::format("{:016X}.ini", program_id);
const auto custom_config_file_path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "custom" / config_file_name;
Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir) / "custom" / config_file_name;
if (!Common::FS::Exists(custom_config_file_path)) {
QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
@ -2701,7 +2706,7 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id, const std::string& g
}
void GMainWindow::RemoveCacheStorage(u64 program_id) {
const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
const auto nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir);
auto vfs_nand_dir =
vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
@ -2759,8 +2764,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
const auto base_romfs = base_nca->GetRomFS();
const auto dump_dir =
target == DumpRomFSTarget::Normal
? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)
: Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents";
? Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)
: Common::FS::GetEdenPath(Common::FS::EdenPath::SDMCDir) / "atmosphere" / "contents";
const auto romfs_dir = fmt::format("{:016X}/romfs", title_id);
const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir);
@ -3018,7 +3023,7 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi
// Get path to Yuzu icons directory & icon extension
std::string ico_extension = "png";
#if defined(_WIN32)
out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::IconsDir);
out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir);
ico_extension = "ico";
#elif defined(__linux__) || defined(__FreeBSD__)
out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
@ -3141,13 +3146,13 @@ void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
std::filesystem::path fs_path;
if (directory == QStringLiteral("SDMC")) {
fs_path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "Nintendo/Contents/registered";
Common::FS::GetEdenPath(Common::FS::EdenPath::SDMCDir) / "Nintendo/Contents/registered";
} else if (directory == QStringLiteral("UserNAND")) {
fs_path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "user/Contents/registered";
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "user/Contents/registered";
} else if (directory == QStringLiteral("SysNAND")) {
fs_path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/Contents/registered";
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/Contents/registered";
} else {
fs_path = directory.toStdString();
}
@ -3373,7 +3378,7 @@ void GMainWindow::OnMenuInstallToNAND() {
: tr("%n file(s) failed to install\n", "", failed_files.size()));
QMessageBox::information(this, tr("Install Results"), install_results);
Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
Common::FS::RemoveDirRecursively(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) /
"game_list");
game_list->PopulateAsync(UISettings::values.game_dirs);
ui->action_Install_File_NAND->setEnabled(true);
@ -3791,11 +3796,11 @@ void GMainWindow::OnConfigure() {
LOG_WARNING(Frontend, "Failed to remove configuration file");
}
if (!Common::FS::RemoveDirContentsRecursively(
Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "custom")) {
Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir) / "custom")) {
LOG_WARNING(Frontend, "Failed to remove custom configuration files");
}
if (!Common::FS::RemoveDirRecursively(
Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "game_list")) {
Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list")) {
LOG_WARNING(Frontend, "Failed to remove game metadata cache files");
}
@ -4151,7 +4156,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
void GMainWindow::OnOpenYuzuFolder() {
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::YuzuDir))));
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::EdenDir))));
}
void GMainWindow::OnVerifyInstalledContents() {
@ -4366,7 +4371,7 @@ void GMainWindow::OnInstallDecryptionKeys() {
return;
}
const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
const auto yuzu_keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
for (auto key_file : source_key_files) {
std::filesystem::path destination_key_file = yuzu_keys_dir / key_file.filename();
if (!std::filesystem::copy_file(key_file, destination_key_file,
@ -4542,7 +4547,7 @@ void GMainWindow::OnCaptureScreenshot() {
const u64 title_id = system->GetApplicationProcessProgramID();
const auto screenshot_path =
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir));
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::ScreenshotsDir));
const auto date =
QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz"));
QString filename = QStringLiteral("%1/%2_%3.png")
@ -4570,7 +4575,7 @@ void GMainWindow::OnCaptureScreenshot() {
// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant
void GMainWindow::MigrateConfigFiles() {
const auto config_dir_fs_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir);
const auto config_dir_fs_path = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir);
const QDir config_dir =
QDir(QString::fromStdString(Common::FS::PathToUTF8String(config_dir_fs_path)));
const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini")));
@ -5289,8 +5294,6 @@ static void SetHighDPIAttributes() {
}
int main(int argc, char* argv[]) {
std::unique_ptr<QtConfig> config = std::make_unique<QtConfig>();
UISettings::RestoreWindowState(config);
bool has_broken_vulkan = false;
bool is_child = false;
if (CheckEnvVars(&is_child)) {
@ -5315,7 +5318,7 @@ int main(int argc, char* argv[]) {
Common::ConfigureNvidiaEnvironmentFlags();
// Init settings params
QCoreApplication::setOrganizationName(QStringLiteral("yuzu"));
QCoreApplication::setOrganizationName(QStringLiteral("eden"));
QCoreApplication::setApplicationName(QStringLiteral("eden"));
#ifdef _WIN32
@ -5340,7 +5343,7 @@ int main(int argc, char* argv[]) {
// Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop
// suffix.
QGuiApplication::setDesktopFileName(QStringLiteral("org.yuzu_emu.yuzu"));
QGuiApplication::setDesktopFileName(QStringLiteral("org.eden_emu.eden"));
#endif
SetHighDPIAttributes();
@ -5374,7 +5377,7 @@ int main(int argc, char* argv[]) {
// generating shaders
setlocale(LC_ALL, "C");
GMainWindow main_window{std::move(config), has_broken_vulkan};
GMainWindow main_window{has_broken_vulkan};
// After settings have been loaded by GMainWindow, apply the filter
main_window.show();

View file

@ -18,6 +18,7 @@
#include "configuration/qt_config.h"
#include "frontend_common/content_manager.h"
#include "input_common/drivers/tas_input.h"
#include "user_data_migration.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/hotkeys.h"
#include "yuzu/util/controller_navigation.h"
@ -167,7 +168,7 @@ class GMainWindow : public QMainWindow {
public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan);
explicit GMainWindow(bool has_broken_vulkan);
~GMainWindow() override;
bool DropAction(QDropEvent* event);
@ -508,6 +509,7 @@ private:
QSlider* volume_slider = nullptr;
QTimer status_bar_update_timer;
UserDataMigrator user_data_migrator;
std::unique_ptr<QtConfig> config;
// Whether emulation is currently running in yuzu.

View file

@ -26,7 +26,7 @@ std::optional<std::filesystem::path> GetCurrentUserPlayTimePath(
if (!uuid.has_value()) {
return std::nullopt;
}
return Common::FS::GetYuzuPath(Common::FS::YuzuPath::PlayTimeDir) /
return Common::FS::GetEdenPath(Common::FS::EdenPath::PlayTimeDir) /
uuid->RawString().append(".bin");
}

View file

@ -57,7 +57,7 @@ u32 CalculateWidth(u32 height, Settings::AspectRatio ratio) {
void SaveWindowState() {
const auto window_state_config_loc =
FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini");
FS::PathToUTF8String(FS::GetEdenPath(FS::EdenPath::ConfigDir) / "window_state.ini");
void(FS::CreateParentDir(window_state_config_loc));
QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat);
@ -73,12 +73,12 @@ void SaveWindowState() {
void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig) {
const auto window_state_config_loc =
FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini");
FS::PathToUTF8String(FS::GetEdenPath(FS::EdenPath::ConfigDir) / "window_state.ini");
// Migrate window state from old location
if (!FS::Exists(window_state_config_loc) && qtConfig->Exists("UI", "UILayout\\geometry")) {
const auto config_loc =
FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "qt-config.ini");
FS::PathToUTF8String(FS::GetEdenPath(FS::EdenPath::ConfigDir) / "qt-config.ini");
QSettings config(QString::fromStdString(config_loc), QSettings::IniFormat);
config.beginGroup(QStringLiteral("UI"));

View file

@ -0,0 +1,168 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// SPDX-FileCopyrightText: Copyright 2025 eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QMessageBox>
#include <QPushButton>
#include <QString>
#include <QTranslator>
#include "common/fs/path_util.h"
#include "user_data_migration.h"
// Needs to be included at the end due to https://bugreports.qt.io/browse/QTBUG-73263
#include <QCheckBox>
#include <filesystem>
namespace fs = std::filesystem;
UserDataMigrator::UserDataMigrator(QMainWindow* main_window) {
// NOTE: Logging is not initialized yet, do not produce logs here.
// Check migration if config directory does not exist
// TODO: ProfileManager messes with us a bit here, and force-creates the /nand/system/save/8000000000000010/su/avators/profiles.dat
// file. Find a way to reorder operations and have it create after this guy runs.
if (!fs::is_directory(Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir))) {
ShowMigrationPrompt(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.");
bool any_found = false;
QMessageBox 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->setChecked(true);
migration_prompt.setCheckBox(clear_shaders);
// Reflection would make this code 10x better
// but for now... MACRO MADNESS!!!!
QMap<QString, bool> found;
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;
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;
buttonMap[iter.key()] = migration_prompt.addButton(iter.key(), QMessageBox::YesRole);
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.exec();
QMapIterator buttonIter(buttonMap);
while (buttonIter.hasNext()) {
buttonIter.next();
if (buttonIter.value() == migration_prompt.clickedButton()) {
MigrateUserData(main_window, legacyMap[iter.key()], clear_shaders->isChecked());
return;
}
}
// If we're here, the user chose not to migrate
ShowMigrationCancelledMessage(main_window);
}
else // no other data was found
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::MigrateUserData(QMainWindow* main_window,
const LegacyEmu selected_legacy_emu, const bool clear_shader_cache) {
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;
#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::copy(legacy_user_dir, Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir), copy_options);
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);
}
// 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));
}
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);
}

View file

@ -0,0 +1,27 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// SPDX-FileCopyrightText: Copyright 2025 eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QMainWindow>
class UserDataMigrator {
public:
UserDataMigrator(QMainWindow* main_window);
private:
enum class LegacyEmu {
Citron,
Sudachi,
Yuzu,
Suyu,
};
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);
};