From 788eca55de407e661e37eb2efb40918d1d6aed30 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 30 Nov 2019 15:24:24 -0600 Subject: [PATCH] Rename RyuFs directory to Ryujinx --- Ryujinx.HLE/FileSystem/VirtualFileSystem.cs | 2 +- Ryujinx/Ui/MainWindow.cs | 21 +- Ryujinx/Ui/Migration.cs | 300 +++++--------------- Ryujinx/Ui/SaveImporter.cs | 218 ++++++++++++++ 4 files changed, 305 insertions(+), 236 deletions(-) create mode 100644 Ryujinx/Ui/SaveImporter.cs diff --git a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index 4ac7410dca..257a55a22f 100644 --- a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.FileSystem { public class VirtualFileSystem : IDisposable { - public const string BasePath = "RyuFs"; + public const string BasePath = "Ryujinx"; public const string NandPath = "bis"; public const string SdCardPath = "sdcard"; public const string SystemPath = "system"; diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 19997d2fa4..2588d00117 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -7,7 +7,6 @@ using Ryujinx.Graphics.Gal.OpenGL; using Ryujinx.Graphics.Gal; using Ryujinx.HLE.FileSystem; using Ryujinx.Profiler; -using Ryujinx.Ui; using System; using System.Diagnostics; using System.IO; @@ -88,16 +87,26 @@ namespace Ryujinx.Ui ApplicationLibrary.ApplicationAdded += Application_Added; + bool continueWithStartup = Migration.PromptIfMigrationNeededForStartup(this, out bool migrationNeeded); + if (!continueWithStartup) + { + End(); + } + _renderer = new OglRenderer(); _audioOut = InitializeAudioEngine(); _device = new HLE.Switch(_renderer, _audioOut); - bool continueAfterMigration = Migration.TryMigrateForStartup(this, _device); - if (!continueAfterMigration) + if (migrationNeeded) { - End(); + bool migrationSuccessful = Migration.DoMigrationForStartup(this, _device); + + if (!migrationSuccessful) + { + End(); + } } _treeView = _gameTable; @@ -449,8 +458,8 @@ namespace Ryujinx.Ui } Profile.FinishProfiling(); - _device.Dispose(); - _audioOut.Dispose(); + _device?.Dispose(); + _audioOut?.Dispose(); DiscordClient?.Dispose(); Logger.Shutdown(); Environment.Exit(0); diff --git a/Ryujinx/Ui/Migration.cs b/Ryujinx/Ui/Migration.cs index b775feb09d..9c7e2ffc33 100644 --- a/Ryujinx/Ui/Migration.cs +++ b/Ryujinx/Ui/Migration.cs @@ -1,18 +1,9 @@ using Gtk; using LibHac; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Shim; -using LibHac.FsSystem; -using LibHac.FsSystem.Save; -using LibHac.Ncm; -using Ryujinx.HLE.Utilities; using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using Switch = Ryujinx.HLE.Switch; @@ -27,15 +18,18 @@ namespace Ryujinx.Ui Device = device; } - public static bool TryMigrateForStartup(Window parentWindow, Switch device) + public static bool PromptIfMigrationNeededForStartup(Window parentWindow, out bool isMigrationNeeded) { const int responseYes = -8; - if (!IsMigrationNeeded(device.FileSystem.GetBasePath())) + if (!IsMigrationNeeded()) { + isMigrationNeeded = false; return true; } + isMigrationNeeded = true; + int dialogResponse; using (MessageDialog dialog = new MessageDialog(parentWindow, DialogFlags.Modal, MessageType.Question, @@ -51,11 +45,11 @@ namespace Ryujinx.Ui dialogResponse = dialog.Run(); } - if (dialogResponse != responseYes) - { - return false; - } + return dialogResponse == responseYes; + } + public static bool DoMigrationForStartup(Window parentWindow, Switch device) + { try { Migration migration = new Migration(device); @@ -83,259 +77,107 @@ namespace Ryujinx.Ui // Returns the number of saves migrated public int Migrate() { - string basePath = Device.FileSystem.GetBasePath(); - string backupPath = Path.Combine(basePath, "Migration backup (Can delete if successful)"); - string backupUserSavePath = Path.Combine(backupPath, "nand/user/save"); + string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - if (!IsMigrationNeeded(basePath)) - return 0; + string oldBasePath = Path.Combine(appDataPath, "RyuFs"); + string newBasePath = Path.Combine(appDataPath, "Ryujinx"); - BackupSaves(basePath, backupPath); + string oldSaveDir = Path.Combine(oldBasePath, "nand/user/save"); - MigrateDirectories(basePath); + CopyRyuFs(oldBasePath, newBasePath); - return MigrateSaves(Device.System.FsClient, backupUserSavePath); + var importer = new SaveImporter(oldSaveDir, Device.System.FsClient); + return importer.Import(); } - private static bool IsMigrationNeeded(string basePath) + private static void CopyRyuFs(string oldPath, string newPath) { - bool missingNewDirs = !Directory.Exists(Path.Combine(basePath, "bis")) && - !Directory.Exists(Path.Combine(basePath, "sdcard")); + Directory.CreateDirectory(newPath); - bool hasOldDirs = Directory.Exists(Path.Combine(basePath, "nand")) || - Directory.Exists(Path.Combine(basePath, "sdmc")); + CopyExcept(oldPath, newPath, "nand", "sdmc"); - return missingNewDirs && hasOldDirs; + 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 MigrateDirectories(string basePath) + private static void CopyExcept(string srcPath, string dstPath, params string[] exclude) { - RenameDirectory(Path.Combine(basePath, "nand"), Path.Combine(basePath, "bis")); - RenameDirectory(Path.Combine(basePath, "sdmc"), Path.Combine(basePath, "sdcard")); - } + exclude = exclude.Select(x => x.ToLowerInvariant()).ToArray(); - private static bool RenameDirectory(string oldDir, string newDir) - { - if (Directory.Exists(newDir)) - return false; + DirectoryInfo srcDir = new DirectoryInfo(srcPath); - if (!Directory.Exists(oldDir)) + if (!srcDir.Exists) { - Directory.CreateDirectory(newDir); - - return true; + return; } - Directory.CreateDirectory(Path.GetDirectoryName(newDir)); - Directory.Move(oldDir, newDir); + Directory.CreateDirectory(dstPath); - return true; - } - - private static void BackupSaves(string basePath, string backupPath) - { - Directory.CreateDirectory(backupPath); - - string userSaveDir = Path.Combine(basePath, "nand/user/save"); - string backupUserSaveDir = Path.Combine(backupPath, "nand/user/save"); - - if (Directory.Exists(userSaveDir)) + foreach (DirectoryInfo subDir in srcDir.EnumerateDirectories()) { - RenameDirectory(userSaveDir, backupUserSaveDir); + if (exclude.Contains(subDir.Name.ToLowerInvariant())) + { + continue; + } + + CopyDirectory(subDir.FullName, Path.Combine(dstPath, subDir.Name)); } - string systemSaveDir = Path.Combine(basePath, "nand/system/save"); - string backupSystemSaveDir = Path.Combine(backupPath, "nand/system/save"); - - if (Directory.Exists(systemSaveDir)) + foreach (FileInfo file in srcDir.EnumerateFiles()) { - RenameDirectory(systemSaveDir, backupSystemSaveDir); + file.CopyTo(Path.Combine(dstPath, file.Name)); } } - // Returns the number of saves migrated - private static int MigrateSaves(FileSystemClient fsClient, string rootSaveDir) + private static void CopyDirectory(string srcPath, string dstPath) { - if (!Directory.Exists(rootSaveDir)) + Directory.CreateDirectory(dstPath); + + DirectoryInfo srcDir = new DirectoryInfo(srcPath); + + if (!srcDir.Exists) { - return 0; + return; } - SaveFinder finder = new SaveFinder(); - finder.FindSaves(rootSaveDir); + Directory.CreateDirectory(dstPath); - foreach (SaveToMigrate save in finder.Saves) + foreach (DirectoryInfo subDir in srcDir.EnumerateDirectories()) { - Result migrateResult = MigrateSave(fsClient, save); - - if (migrateResult.IsFailure()) - { - throw new HorizonResultException(migrateResult, $"Error migrating save {save.Path}"); - } + CopyDirectory(subDir.FullName, Path.Combine(dstPath, subDir.Name)); } - return finder.Saves.Count; - } - - private static Result MigrateSave(FileSystemClient fs, SaveToMigrate save) - { - SaveDataAttribute key = save.Attribute; - - Result result = fs.CreateSaveData(key.TitleId, key.UserId, key.TitleId, 0, 0, 0); - if (result.IsFailure()) return result; - - bool isOldMounted = false; - bool isNewMounted = false; - - try + foreach (FileInfo file in srcDir.EnumerateFiles()) { - result = fs.Register("OldSave".ToU8Span(), new LocalFileSystem(save.Path)); - if (result.IsFailure()) return result; - - isOldMounted = true; - - result = fs.MountSaveData("NewSave".ToU8Span(), key.TitleId, key.UserId); - if (result.IsFailure()) return result; - - isNewMounted = true; - - result = fs.CopyDirectory("OldSave:/", "NewSave:/"); - if (result.IsFailure()) return result; - - result = fs.Commit("NewSave"); - } - finally - { - if (isOldMounted) - { - fs.Unmount("OldSave"); - } - - if (isNewMounted) - { - fs.Unmount("NewSave"); - } - } - - return result; - } - - private class SaveFinder - { - public List Saves { get; } = new List(); - - public void FindSaves(string rootPath) - { - foreach (string subDir in Directory.EnumerateDirectories(rootPath)) - { - if (TryGetUInt64(subDir, out ulong saveDataId)) - { - SearchSaveId(subDir, saveDataId); - } - } - } - - private void SearchSaveId(string path, ulong saveDataId) - { - foreach (string subDir in Directory.EnumerateDirectories(path)) - { - if (TryGetUserId(subDir, out UserId userId)) - { - SearchUser(subDir, saveDataId, userId); - } - } - } - - private void SearchUser(string path, ulong saveDataId, UserId userId) - { - foreach (string subDir in Directory.EnumerateDirectories(path)) - { - if (TryGetUInt64(subDir, out ulong titleId) && TryGetDataPath(subDir, out string dataPath)) - { - SaveDataAttribute attribute = new SaveDataAttribute - { - Type = SaveDataType.SaveData, - UserId = userId, - TitleId = new TitleId(titleId) - }; - - SaveToMigrate save = new SaveToMigrate(dataPath, attribute); - - Saves.Add(save); - } - } - } - - private static bool TryGetDataPath(string path, out string dataPath) - { - string committedPath = Path.Combine(path, "0"); - string workingPath = Path.Combine(path, "1"); - - if (Directory.Exists(committedPath) && Directory.EnumerateFileSystemEntries(committedPath).Any()) - { - dataPath = committedPath; - return true; - } - - if (Directory.Exists(workingPath) && Directory.EnumerateFileSystemEntries(workingPath).Any()) - { - dataPath = workingPath; - return true; - } - - dataPath = default; - return false; - } - - private static bool TryGetUInt64(string path, out ulong converted) - { - string name = Path.GetFileName(path); - - if (name.Length == 16) - { - try - { - converted = Convert.ToUInt64(name, 16); - return true; - } - catch { } - } - - converted = default; - return false; - } - - private static bool TryGetUserId(string path, out UserId userId) - { - string name = Path.GetFileName(path); - - if (name.Length == 32) - { - try - { - UInt128 id = new UInt128(name); - - userId = Unsafe.As(ref id); - return true; - } - catch { } - } - - userId = default; - return false; + file.CopyTo(Path.Combine(dstPath, file.Name)); } } - private class SaveToMigrate + private static bool IsMigrationNeeded() { - public string Path { get; } - public SaveDataAttribute Attribute { get; } + string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - public SaveToMigrate(string path, SaveDataAttribute attribute) - { - Path = path; - Attribute = attribute; - } + string oldBasePath = Path.Combine(appDataPath, "RyuFs"); + string newBasePath = Path.Combine(appDataPath, "Ryujinx"); + + return Directory.Exists(oldBasePath) && !Directory.Exists(newBasePath); } } } diff --git a/Ryujinx/Ui/SaveImporter.cs b/Ryujinx/Ui/SaveImporter.cs new file mode 100644 index 0000000000..b0a5f64336 --- /dev/null +++ b/Ryujinx/Ui/SaveImporter.cs @@ -0,0 +1,218 @@ +using LibHac; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Shim; +using LibHac.FsSystem; +using LibHac.FsSystem.Save; +using LibHac.Ncm; +using Ryujinx.HLE.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Ui +{ + internal class SaveImporter + { + private FileSystemClient FsClient { get; } + private string ImportPath { get; } + + public SaveImporter(string importPath, FileSystemClient destFsClient) + { + ImportPath = importPath; + FsClient = destFsClient; + } + + // Returns the number of saves imported + public int Import() + { + return ImportSaves(FsClient, ImportPath); + } + + private static int ImportSaves(FileSystemClient fsClient, string rootSaveDir) + { + if (!Directory.Exists(rootSaveDir)) + { + return 0; + } + + SaveFinder finder = new SaveFinder(); + finder.FindSaves(rootSaveDir); + + foreach (SaveToImport save in finder.Saves) + { + Result importResult = ImportSave(fsClient, save); + + if (importResult.IsFailure()) + { + throw new HorizonResultException(importResult, $"Error importing save {save.Path}"); + } + } + + return finder.Saves.Count; + } + + private static Result ImportSave(FileSystemClient fs, SaveToImport save) + { + SaveDataAttribute key = save.Attribute; + + Result result = fs.CreateSaveData(key.TitleId, key.UserId, key.TitleId, 0, 0, 0); + if (result.IsFailure()) return result; + + bool isOldMounted = false; + bool isNewMounted = false; + + try + { + result = fs.Register("OldSave".ToU8Span(), new LocalFileSystem(save.Path)); + if (result.IsFailure()) return result; + + isOldMounted = true; + + result = fs.MountSaveData("NewSave".ToU8Span(), key.TitleId, key.UserId); + if (result.IsFailure()) return result; + + isNewMounted = true; + + result = fs.CopyDirectory("OldSave:/", "NewSave:/"); + if (result.IsFailure()) return result; + + result = fs.Commit("NewSave"); + } + finally + { + if (isOldMounted) + { + fs.Unmount("OldSave"); + } + + if (isNewMounted) + { + fs.Unmount("NewSave"); + } + } + + return result; + } + + private class SaveFinder + { + public List Saves { get; } = new List(); + + public void FindSaves(string rootPath) + { + foreach (string subDir in Directory.EnumerateDirectories(rootPath)) + { + if (TryGetUInt64(subDir, out ulong saveDataId)) + { + SearchSaveId(subDir, saveDataId); + } + } + } + + private void SearchSaveId(string path, ulong saveDataId) + { + foreach (string subDir in Directory.EnumerateDirectories(path)) + { + if (TryGetUserId(subDir, out UserId userId)) + { + SearchUser(subDir, saveDataId, userId); + } + } + } + + private void SearchUser(string path, ulong saveDataId, UserId userId) + { + foreach (string subDir in Directory.EnumerateDirectories(path)) + { + if (TryGetUInt64(subDir, out ulong titleId) && TryGetDataPath(subDir, out string dataPath)) + { + SaveDataAttribute attribute = new SaveDataAttribute + { + Type = SaveDataType.SaveData, + UserId = userId, + TitleId = new TitleId(titleId) + }; + + SaveToImport save = new SaveToImport(dataPath, attribute); + + Saves.Add(save); + } + } + } + + private static bool TryGetDataPath(string path, out string dataPath) + { + string committedPath = Path.Combine(path, "0"); + string workingPath = Path.Combine(path, "1"); + + if (Directory.Exists(committedPath) && Directory.EnumerateFileSystemEntries(committedPath).Any()) + { + dataPath = committedPath; + return true; + } + + if (Directory.Exists(workingPath) && Directory.EnumerateFileSystemEntries(workingPath).Any()) + { + dataPath = workingPath; + return true; + } + + dataPath = default; + return false; + } + + private static bool TryGetUInt64(string path, out ulong converted) + { + string name = Path.GetFileName(path); + + if (name.Length == 16) + { + try + { + converted = Convert.ToUInt64(name, 16); + return true; + } + catch { } + } + + converted = default; + return false; + } + + private static bool TryGetUserId(string path, out UserId userId) + { + string name = Path.GetFileName(path); + + if (name.Length == 32) + { + try + { + UInt128 id = new UInt128(name); + + userId = Unsafe.As(ref id); + return true; + } + catch { } + } + + userId = default; + return false; + } + } + + private class SaveToImport + { + public string Path { get; } + public SaveDataAttribute Attribute { get; } + + public SaveToImport(string path, SaveDataAttribute attribute) + { + Path = path; + Attribute = attribute; + } + } + } +}