JinxRyu/Ryujinx/Ui/Migration.cs
mageven b9398f1f3a
Allow launching with custom data directories (#1505)
* Allow launching with custom data directories

Don't load alternate keys when using custom directory

* Address gdkchan's comments

* Misc fixes to log levels

Added more enabled log levels by default

Moved successful config updation to Notice as
1. It's not a warning
2. Warnings could've been disabled by the config load and hence message
   would be lost
2020-08-30 18:51:53 +02:00

189 lines
6.3 KiB
C#

using Gtk;
using LibHac;
using Ryujinx.Common.Configuration;
using Ryujinx.HLE.FileSystem;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
namespace Ryujinx.Ui
{
internal class Migration
{
private VirtualFileSystem _virtualFileSystem;
public Migration(VirtualFileSystem virtualFileSystem)
{
_virtualFileSystem = virtualFileSystem;
}
public static bool PromptIfMigrationNeededForStartup(Window parentWindow, out bool isMigrationNeeded)
{
if (!IsMigrationNeeded())
{
isMigrationNeeded = false;
return true;
}
isMigrationNeeded = true;
int dialogResponse;
using (MessageDialog dialog = new MessageDialog(parentWindow, DialogFlags.Modal, MessageType.Question,
ButtonsType.YesNo, "What's this?"))
{
dialog.Title = "Data Migration Needed";
dialog.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
dialog.Text =
"The folder structure of Ryujinx's RyuFs folder has been updated and renamed to \"Ryujinx\". " +
"Your RyuFs folder must be copied and migrated to the new \"Ryujinx\" structure. Would you like to do the migration now?\n\n" +
"Select \"Yes\" to automatically perform the migration. Your old RyuFs folder will remain as it is.\n\n" +
"Selecting \"No\" will exit Ryujinx without changing anything.";
dialogResponse = dialog.Run();
}
return dialogResponse == (int)ResponseType.Yes;
}
public static bool DoMigrationForStartup(MainWindow parentWindow, VirtualFileSystem virtualFileSystem)
{
try
{
Migration migration = new Migration(virtualFileSystem);
int saveCount = migration.Migrate();
using MessageDialog dialogSuccess = new MessageDialog(parentWindow, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, null)
{
Title = "Migration Success",
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
Text = $"Data migration was successful. {saveCount} saves were migrated.",
};
dialogSuccess.Run();
return true;
}
catch (HorizonResultException ex)
{
GtkDialog.CreateErrorDialog(ex.Message);
return false;
}
}
// Returns the number of saves migrated
public int Migrate()
{
// Make sure FsClient is initialized
_virtualFileSystem.Reload();
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string oldBasePath = Path.Combine(appDataPath, "RyuFs");
string newBasePath = Path.Combine(appDataPath, "Ryujinx");
string oldSaveDir = Path.Combine(oldBasePath, "nand/user/save");
CopyRyuFs(oldBasePath, newBasePath);
SaveImporter importer = new SaveImporter(oldSaveDir, _virtualFileSystem.FsClient);
return importer.Import();
}
private static void CopyRyuFs(string oldPath, string newPath)
{
Directory.CreateDirectory(newPath);
CopyExcept(oldPath, newPath, "nand", "bis", "sdmc", "sdcard");
string oldNandPath = Path.Combine(oldPath, "nand");
string newNandPath = Path.Combine(newPath, "bis");
CopyExcept(oldNandPath, newNandPath, "system", "user");
string oldSdPath = Path.Combine(oldPath, "sdmc");
string newSdPath = Path.Combine(newPath, "sdcard");
CopyDirectory(oldSdPath, newSdPath);
string oldSystemPath = Path.Combine(oldNandPath, "system");
string newSystemPath = Path.Combine(newNandPath, "system");
CopyExcept(oldSystemPath, newSystemPath, "save");
string oldUserPath = Path.Combine(oldNandPath, "user");
string newUserPath = Path.Combine(newNandPath, "user");
CopyExcept(oldUserPath, newUserPath, "save");
}
private static void CopyExcept(string srcPath, string dstPath, params string[] exclude)
{
exclude = exclude.Select(x => x.ToLowerInvariant()).ToArray();
DirectoryInfo srcDir = new DirectoryInfo(srcPath);
if (!srcDir.Exists)
{
return;
}
Directory.CreateDirectory(dstPath);
foreach (DirectoryInfo subDir in srcDir.EnumerateDirectories())
{
if (exclude.Contains(subDir.Name.ToLowerInvariant()))
{
continue;
}
CopyDirectory(subDir.FullName, Path.Combine(dstPath, subDir.Name));
}
foreach (FileInfo file in srcDir.EnumerateFiles())
{
file.CopyTo(Path.Combine(dstPath, file.Name));
}
}
private static void CopyDirectory(string srcPath, string dstPath)
{
Directory.CreateDirectory(dstPath);
DirectoryInfo srcDir = new DirectoryInfo(srcPath);
if (!srcDir.Exists)
{
return;
}
Directory.CreateDirectory(dstPath);
foreach (DirectoryInfo subDir in srcDir.EnumerateDirectories())
{
CopyDirectory(subDir.FullName, Path.Combine(dstPath, subDir.Name));
}
foreach (FileInfo file in srcDir.EnumerateFiles())
{
file.CopyTo(Path.Combine(dstPath, file.Name));
}
}
public static bool IsMigrationNeeded()
{
if (AppDataManager.IsCustomBasePath) return false;
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string oldBasePath = Path.Combine(appDataPath, "RyuFs");
string newBasePath = Path.Combine(appDataPath, "Ryujinx");
return Directory.Exists(oldBasePath) && !Directory.Exists(newBasePath);
}
}
}