Files
eden/src/yuzu/user_data_migration.cpp
T
crueter a53823646c [qt] fix translations (#2773)
Linguist strongly dislikes lookup tables of this sort due to the fact
that it looks for tr(), qsTr(), etc. when determining what strings need
translations. However, it does provide QT_TR_NOOP which marks the
string for translation *without* running the translation, which is designed
to allow for static or constexpr lookup tables. So let's use that.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2773
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2025-10-18 22:19:06 +02:00

204 lines
7.2 KiB
C++

// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "user_data_migration.h"
#include <QMessageBox>
#include <QPushButton>
#include <QString>
#include <QTranslator>
#include "common/fs/path_util.h"
#include "qt_common/qt_string_lookup.h"
#include "yuzu/migration_dialog.h"
// Needs to be included at the end due to https://bugreports.qt.io/browse/QTBUG-73263
#include <QButtonGroup>
#include <QCheckBox>
#include <QGuiApplication>
#include <QProgressDialog>
#include <QRadioButton>
#include <QThread>
#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;
// define strings here for easy access
QString prompt_prefix_text = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationPromptPrefix);
QString migration_prompt_message = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationPrompt);
QString clear_shader_tooltip = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationTooltipClearShader);
QString keep_old_data_tooltip = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationTooltipKeepOld);
QString clear_old_data_tooltip = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationTooltipClearOld);
QString link_old_dir_tooltip = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationTooltipLinkOld);
// actual migration code
MigrationDialog migration_prompt;
migration_prompt.setWindowTitle(QObject::tr("Migration"));
// mutually exclusive
QButtonGroup *group = new QButtonGroup(&migration_prompt);
// MACRO MADNESS
#define BUTTON(clazz, name, text, tooltip, checkState) \
clazz *name = new clazz(&migration_prompt); \
name->setText(text); \
name->setToolTip(tooltip); \
name->setChecked(checkState); \
migration_prompt.addBox(name);
BUTTON(QCheckBox, clear_shaders, QObject::tr("Clear Shader Cache"), clear_shader_tooltip, true)
u32 id = 0;
#define RADIO(name, text, tooltip, checkState) \
BUTTON(QRadioButton, name, text, tooltip, checkState) \
group->addButton(name, ++id);
RADIO(keep_old, QObject::tr("Keep Old Data"), keep_old_data_tooltip, true)
RADIO(clear_old, QObject::tr("Clear Old Data"), clear_old_data_tooltip, false)
RADIO(link_old, QObject::tr("Link Old Directory"), link_old_dir_tooltip, false)
#undef RADIO
#undef BUTTON
std::vector<Emulator> 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<Emulator>();
QString prompt_text = prompt_prefix_text;
// natural language processing is a nightmare
for (const Emulator &emu : found) {
prompt_text = prompt_text % QStringLiteral("\n ") % emu.name();
QAbstractButton *button = migration_prompt.addButton(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 = prompt_text % QStringLiteral("\n\n") % 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 1:
strategy = MigrationWorker::MigrationStrategy::Copy;
break;
case 2:
strategy = MigrationWorker::MigrationStrategy::Move;
break;
case 3:
strategy = MigrationWorker::MigrationStrategy::Link;
break;
}
MigrateUserData(main_window,
button->property("emulator").value<Emulator>(),
clear_shaders->isChecked(),
strategy);
}
void UserDataMigrator::ShowMigrationCancelledMessage(QMainWindow *main_window)
{
QMessageBox::information(main_window,
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 Emulator selected_legacy_emu,
const bool clear_shader_cache,
const MigrationWorker::MigrationStrategy strategy)
{
selected_emu = selected_legacy_emu;
// Create a dialog to let the user know it's migrating
QProgressDialog *progress = new QProgressDialog(main_window);
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);
QThread *thread = new QThread(main_window);
MigrationWorker *worker = new MigrationWorker(selected_legacy_emu, clear_shader_cache, strategy);
worker->moveToThread(thread);
thread->connect(thread, &QThread::started, worker, &MigrationWorker::process);
thread->connect(worker,
&MigrationWorker::finished,
progress,
[=, this](const QString &success_text, const std::string &path) {
progress->close();
QMessageBox::information(main_window,
QObject::tr("Migration"),
success_text,
QMessageBox::Ok);
migrated = true;
thread->quit();
});
thread->connect(worker, &MigrationWorker::finished, worker, &QObject::deleteLater);
thread->connect(thread, &QThread::finished, thread, &QObject::deleteLater);
thread->start();
progress->exec();
}