Implement emulator data migration functionality + prompt

The migration prompt appears when Lime3DS is started while the new user data directory doesn't exist and the old directory *does* exist
This commit is contained in:
OpenSauce04 2024-07-17 20:15:03 +01:00 committed by OpenSauce
parent d2a5260d62
commit 7731956906
5 changed files with 80 additions and 4 deletions

View file

@ -19,17 +19,22 @@
#else
#ifdef _WIN32
#define EMU_DATA_DIR "Lime3DS"
#define LEGACY_EMU_DATA_DIR "Citra"
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#if TARGET_OS_IPHONE // TODO: Kill iOS
#define APPLE_EMU_DATA_DIR "Documents" DIR_SEP "Lime3DS"
#define LEGACY_APPLE_EMU_DATA_DIR "Documents" DIR_SEP "Citra"
#else
#define APPLE_EMU_DATA_DIR "Library" DIR_SEP "Application Support" DIR_SEP "Lime3DS"
#define LEGACY_APPLE_EMU_DATA_DIR "Library" DIR_SEP "Application Support" DIR_SEP "Citra"
#endif
// For compatibility with XDG paths.
#define EMU_DATA_DIR "lime3ds-emu"
#define LEGACY_EMU_DATA_DIR "citra-emu"
#else
#define EMU_DATA_DIR "lime3ds-emu"
#define LEGACY_EMU_DATA_DIR "citra-emu"
#endif
#endif

View file

@ -771,8 +771,11 @@ void SetUserPath(const std::string& path) {
} else {
#ifdef _WIN32
user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
std::string& legacy_user_path = g_paths[UserPath::LegacyUserDir];
if (!FileUtil::IsDirectory(user_path)) {
user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
legacy_user_path = AppDataRoamingDirectory() + DIR_SEP LEGACY_EMU_DATA_DIR DIR_SEP;
} else {
LOG_INFO(Common_Filesystem, "Using the local user directory");
}
@ -784,6 +787,7 @@ void SetUserPath(const std::string& path) {
g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
#else
std::string& legacy_user_path = g_paths[UserPath::LegacyUserDir];
auto current_dir = FileUtil::GetCurrentDir();
if (current_dir.has_value() &&
FileUtil::Exists(current_dir.value() + USERDATA_DIR DIR_SEP)) {
@ -792,10 +796,16 @@ void SetUserPath(const std::string& path) {
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
} else {
std::string data_dir = GetUserDirectory("XDG_DATA_HOME") + DIR_SEP EMU_DATA_DIR DIR_SEP;
std::string legacy_data_dir =
GetUserDirectory("XDG_DATA_HOME") + DIR_SEP LEGACY_EMU_DATA_DIR DIR_SEP;
std::string config_dir =
GetUserDirectory("XDG_CONFIG_HOME") + DIR_SEP EMU_DATA_DIR DIR_SEP;
std::string cache_dir =
GetUserDirectory("XDG_CACHE_HOME") + DIR_SEP EMU_DATA_DIR DIR_SEP;
g_paths.emplace(UserPath::LegacyConfigDir, GetUserDirectory("XDG_CONFIG_HOME") +
DIR_SEP LEGACY_EMU_DATA_DIR DIR_SEP);
g_paths.emplace(UserPath::LegacyCacheDir, GetUserDirectory("XDG_CACHE_HOME") +
DIR_SEP LEGACY_EMU_DATA_DIR DIR_SEP);
#if defined(__APPLE__)
// If XDG directories don't already exist from a previous setup, use standard macOS
@ -803,12 +813,14 @@ void SetUserPath(const std::string& path) {
if (!FileUtil::Exists(data_dir) && !FileUtil::Exists(config_dir) &&
!FileUtil::Exists(cache_dir)) {
data_dir = GetHomeDirectory() + DIR_SEP APPLE_EMU_DATA_DIR DIR_SEP;
legacy_data_dir = GetHomeDirectory() + DIR_SEP LEGACY_APPLE_EMU_DATA_DIR DIR_SEP;
config_dir = data_dir + CONFIG_DIR DIR_SEP;
cache_dir = data_dir + CACHE_DIR DIR_SEP;
}
#endif
user_path = data_dir;
legacy_user_path = legacy_data_dir;
g_paths.emplace(UserPath::ConfigDir, config_dir);
g_paths.emplace(UserPath::CacheDir, cache_dir);
}

View file

@ -32,17 +32,20 @@ enum class UserPath {
ConfigDir,
DLLDir,
DumpDir,
IconsDir,
LegacyCacheDir, // LegacyCacheDir and LegacyConfigDir are only defined if migrating these
LegacyConfigDir, // directories is necessary (aka not a child of LegacyUserDir)
LegacyUserDir,
LoadDir,
LogDir,
NANDDir,
PlayTimeDir,
RootDir,
SDMCDir,
ShaderDir,
StatesDir,
SysDataDir,
UserDir,
IconsDir,
PlayTimeDir,
};
// Replaces install-specific paths with standard placeholders, and back again

View file

@ -159,12 +159,16 @@ static QString PrettyProductName() {
GMainWindow::GMainWindow(Core::System& system_)
: ui{std::make_unique<Ui::MainWindow>()}, system{system_}, movie{system.Movie()},
config{std::make_unique<Config>()}, emu_thread{nullptr} {
emu_thread{nullptr} {
Common::Log::Initialize();
Common::Log::Start();
Debugger::ToggleConsole();
CheckForMigration();
this->config = std::make_unique<Config>();
#ifdef __unix__
SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
#endif
@ -1099,6 +1103,55 @@ void GMainWindow::ShowUpdaterWidgets() {
}
#endif
void GMainWindow::CheckForMigration() {
namespace fs = std::filesystem;
if (fs::is_directory(FileUtil::GetUserPath(FileUtil::UserPath::LegacyUserDir)) &&
!fs::is_directory(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))) {
if (QMessageBox::information(
this, tr("Migration"),
tr("Lime3DS has moved to a new data directory.\n\n"
"Would you like to migrate your Citra data to this new "
"location?\n"
"(This may take a while; The old data will not be deleted)"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) {
MigrateUserData();
} else {
QMessageBox::information(this, tr("Migration"),
tr("You can manually re-trigger this prompt by deleting the "
"new user data directory:\n"
"%1")
.arg(QString::fromStdString(
FileUtil::GetUserPath(FileUtil::UserPath::UserDir))),
QMessageBox::Ok);
}
}
}
void GMainWindow::MigrateUserData() {
namespace fs = std::filesystem;
const auto copyOptions = fs::copy_options::update_existing | fs::copy_options::recursive;
fs::copy(FileUtil::GetUserPath(FileUtil::UserPath::LegacyUserDir),
FileUtil::GetUserPath(FileUtil::UserPath::UserDir), copyOptions);
if (fs::is_directory(FileUtil::GetUserPath(FileUtil::UserPath::LegacyConfigDir))) {
fs::copy(FileUtil::GetUserPath(FileUtil::UserPath::LegacyConfigDir),
FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir), copyOptions);
}
if (fs::is_directory(FileUtil::GetUserPath(FileUtil::UserPath::LegacyCacheDir))) {
fs::copy(FileUtil::GetUserPath(FileUtil::UserPath::LegacyCacheDir),
FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), copyOptions);
}
QMessageBox::information(
this, tr("Migration"),
tr("Data was migrated successfully. Lime3DS will now start.\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(FileUtil::GetUserPath(FileUtil::UserPath::LegacyUserDir))),
QMessageBox::Ok);
}
#if defined(HAVE_SDL2) && defined(__unix__) && !defined(__APPLE__)
static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
if (!QDBusConnection::sessionBus().isConnected()) {

View file

@ -171,6 +171,9 @@ private:
void CheckForUpdates();
#endif
void CheckForMigration();
void MigrateUserData();
/**
* Stores the filename in the recently loaded files list.
* The new filename is stored at the beginning of the recently loaded files list.