2020-01-05 06:49:44 -05:00
|
|
|
|
using Gtk;
|
|
|
|
|
using LibHac;
|
2020-08-30 12:51:53 -04:00
|
|
|
|
using Ryujinx.Common.Configuration;
|
2020-01-21 17:23:11 -05:00
|
|
|
|
using Ryujinx.HLE.FileSystem;
|
2020-01-05 06:49:44 -05:00
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
|
|
|
|
namespace Ryujinx.Ui
|
|
|
|
|
{
|
|
|
|
|
internal class Migration
|
|
|
|
|
{
|
2020-01-21 17:23:11 -05:00
|
|
|
|
private VirtualFileSystem _virtualFileSystem;
|
2020-01-05 06:49:44 -05:00
|
|
|
|
|
2020-01-21 17:23:11 -05:00
|
|
|
|
public Migration(VirtualFileSystem virtualFileSystem)
|
2020-01-05 06:49:44 -05:00
|
|
|
|
{
|
2020-01-21 17:23:11 -05:00
|
|
|
|
_virtualFileSystem = virtualFileSystem;
|
2020-01-05 06:49:44 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-21 17:23:11 -05:00
|
|
|
|
public static bool DoMigrationForStartup(MainWindow parentWindow, VirtualFileSystem virtualFileSystem)
|
2020-01-05 06:49:44 -05:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2020-01-21 17:23:11 -05:00
|
|
|
|
Migration migration = new Migration(virtualFileSystem);
|
2020-01-05 06:49:44 -05:00
|
|
|
|
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()
|
|
|
|
|
{
|
2020-01-21 17:23:11 -05:00
|
|
|
|
// Make sure FsClient is initialized
|
|
|
|
|
_virtualFileSystem.Reload();
|
|
|
|
|
|
2020-01-05 06:49:44 -05:00
|
|
|
|
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);
|
|
|
|
|
|
2020-01-21 17:23:11 -05:00
|
|
|
|
SaveImporter importer = new SaveImporter(oldSaveDir, _virtualFileSystem.FsClient);
|
2020-01-05 06:49:44 -05:00
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-05 10:39:35 -05:00
|
|
|
|
public static bool IsMigrationNeeded()
|
2020-01-05 06:49:44 -05:00
|
|
|
|
{
|
2020-08-30 12:51:53 -04:00
|
|
|
|
if (AppDataManager.IsCustomBasePath) return false;
|
|
|
|
|
|
2020-01-05 06:49:44 -05:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|