From 94a2c9de75920b0f0faf317afb74d2804ee960f7 Mon Sep 17 00:00:00 2001 From: Akisuke Date: Sun, 18 Dec 2022 02:28:41 +0100 Subject: [PATCH] renamed commands to exporter and importer, changed button location to saveManager and reimplemented exporter using libhac instead of c# file api --- Ryujinx.Ava/Common/ApplicationHelper.cs | 5 + .../Ui/Controls/BackupSavedataCommand.cs | 83 ------ .../Ui/Controls/NavigationDialogHost.axaml.cs | 13 +- Ryujinx.Ava/Ui/Controls/SaveDataExporter.cs | 242 ++++++++++++++++++ ...SavedataCommand.cs => SaveDataImporter.cs} | 44 ++-- Ryujinx.Ava/Ui/Controls/SaveManager.axaml | 7 +- Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs | 24 +- .../Ui/ViewModels/MainWindowViewModel.cs | 2 +- .../Ui/ViewModels/UserProfileViewModel.cs | 8 +- 9 files changed, 305 insertions(+), 123 deletions(-) delete mode 100644 Ryujinx.Ava/Ui/Controls/BackupSavedataCommand.cs create mode 100644 Ryujinx.Ava/Ui/Controls/SaveDataExporter.cs rename Ryujinx.Ava/Ui/Controls/{RestoreSavedataCommand.cs => SaveDataImporter.cs} (81%) diff --git a/Ryujinx.Ava/Common/ApplicationHelper.cs b/Ryujinx.Ava/Common/ApplicationHelper.cs index 7f7666142..6f9228377 100644 --- a/Ryujinx.Ava/Common/ApplicationHelper.cs +++ b/Ryujinx.Ava/Common/ApplicationHelper.cs @@ -421,5 +421,10 @@ namespace Ryujinx.Ava.Common return Result.Success; } + + internal static (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string subSrcPath, string subDstPath) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/BackupSavedataCommand.cs b/Ryujinx.Ava/Ui/Controls/BackupSavedataCommand.cs deleted file mode 100644 index 2975f2c46..000000000 --- a/Ryujinx.Ava/Ui/Controls/BackupSavedataCommand.cs +++ /dev/null @@ -1,83 +0,0 @@ - -using Avalonia.Controls; -using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Ui.Windows; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using System; -using System.IO; -using System.IO.Compression; -using System.Threading.Tasks; -using System.Windows.Input; -using Logger = Ryujinx.Common.Logging.Logger; -using Path = System.IO.Path; - -namespace Ryujinx.Ava.Ui.Controls -{ - internal class BackupSavedataCommand : ICommand - { - public event EventHandler CanExecuteChanged; - - private readonly IControl parentControl; - - public BackupSavedataCommand(IControl parentControl) - { - this.parentControl = parentControl; - } - - public bool CanExecute(object parameter) - { - return true; - } - - public void Execute(object parameter) - { - SaveUserSaveDirectoryAsZip(); - } - - public async void SaveUserSaveDirectoryAsZip() - { - CreateBackupZip(await GetAndPrepareBackupPath()); - } - - private async Task GetAndPrepareBackupPath() - { - SaveFileDialog saveFileDialog = new SaveFileDialog() - { - Title = LocaleManager.Instance["CreateZipFileDialogTitle"], - InitialFileName = "Ryujinx_backup.zip", - Filters = new System.Collections.Generic.List(new[] { new FileDialogFilter() { Extensions = new System.Collections.Generic.List() { "zip" } } }) - }; - - string zipPath = await saveFileDialog.ShowAsync(parentControl.VisualRoot as MainWindow); - - if (File.Exists(zipPath)) - { - File.Delete(zipPath); - } - - return zipPath; - } - - private void CreateBackupZip(string userBackupPath) - { - if (!string.IsNullOrWhiteSpace(userBackupPath) && Directory.Exists(Directory.GetParent(userBackupPath).FullName)) - { - string saveDir = Path.Combine(AppDataManager.BaseDirPath, AppDataManager.DefaultNandDir, "user", "save"); - - try - { - Logger.Info.Value.Print(LogClass.Application, $"Start creating backup...", nameof(BackupSavedataCommand)); - - ZipFile.CreateFromDirectory(saveDir, userBackupPath); - - Logger.Info.Value.Print(LogClass.Application, $"Backup done. Zip is locate under {userBackupPath}", nameof(BackupSavedataCommand)); - } - catch (Exception) - { - Logger.Error.Value.Print(LogClass.Application, $"Could not create backup zip file.", nameof(BackupSavedataCommand)); - } - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/NavigationDialogHost.axaml.cs b/Ryujinx.Ava/Ui/Controls/NavigationDialogHost.axaml.cs index ced883286..bfa7c69c4 100644 --- a/Ryujinx.Ava/Ui/Controls/NavigationDialogHost.axaml.cs +++ b/Ryujinx.Ava/Ui/Controls/NavigationDialogHost.axaml.cs @@ -2,11 +2,14 @@ using Avalonia; using Avalonia.Controls; using FluentAvalonia.UI.Controls; using LibHac; +using LibHac.FsSystem; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Ui.ViewModels; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.Ui.App.Common; using System; +using System.Collections.Generic; using System.Threading.Tasks; namespace Ryujinx.Ava.Ui.Controls @@ -17,20 +20,24 @@ namespace Ryujinx.Ava.Ui.Controls public ContentManager ContentManager { get; } public VirtualFileSystem VirtualFileSystem { get; } public HorizonClient HorizonClient { get; } + public List Applications { get; } public UserProfileViewModel ViewModel { get; set; } public NavigationDialogHost() { InitializeComponent(); + + } public NavigationDialogHost(AccountManager accountManager, ContentManager contentManager, - VirtualFileSystem virtualFileSystem, HorizonClient horizonClient) + VirtualFileSystem virtualFileSystem, HorizonClient horizonClient, List applications) { AccountManager = accountManager; ContentManager = contentManager; VirtualFileSystem = virtualFileSystem; HorizonClient = horizonClient; + Applications = applications; ViewModel = new UserProfileViewModel(this); @@ -60,9 +67,9 @@ namespace Ryujinx.Ava.Ui.Controls } public static async Task Show(AccountManager ownerAccountManager, ContentManager ownerContentManager, - VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient) + VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient, List applications) { - var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient); + var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient, applications); ContentDialog contentDialog = new ContentDialog { Title = LocaleManager.Instance["UserProfileWindowTitle"], diff --git a/Ryujinx.Ava/Ui/Controls/SaveDataExporter.cs b/Ryujinx.Ava/Ui/Controls/SaveDataExporter.cs new file mode 100644 index 000000000..148a3d52c --- /dev/null +++ b/Ryujinx.Ava/Ui/Controls/SaveDataExporter.cs @@ -0,0 +1,242 @@ +using Avalonia.Controls; +using LibHac; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.Fs.Shim; +using LibHac.Tools.Fs; +using LibHac.Tools.FsSystem; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.Ui.Models; +using Ryujinx.Ava.Ui.Windows; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.FileSystem; +using Ryujinx.Ui.App.Common; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.IO.Compression; +using System.Threading.Tasks; +using Logger = Ryujinx.Common.Logging.Logger; +using Path = System.IO.Path; + +namespace Ryujinx.Ava.Ui.Controls +{ + internal class SaveDataExporter + { + private readonly UserProfile _userProfile; + private readonly HorizonClient _horizonClient; + private readonly VirtualFileSystem _virtualFileSystem; + private readonly UserId _userId; + + public SaveDataExporter(UserProfile userProfile, HorizonClient horizonClient, VirtualFileSystem virtualFileSystem) + { + _userProfile = userProfile; + _horizonClient = horizonClient; + _virtualFileSystem = virtualFileSystem; + _userId = new UserId( + ulong.Parse(_userProfile.UserId.High.ToString(), System.Globalization.NumberStyles.HexNumber), + ulong.Parse(_userProfile.UserId.Low.ToString(), System.Globalization.NumberStyles.HexNumber) + ); + } + + public async void SaveUserSaveDirectoryAsZip(MainWindow mainWindow, List saves, List applications) + { + string backupFolder = await GetAndPrepareBackupPath(mainWindow); + CreateBackup(backupFolder, saves, applications); + + ZipFile.CreateFromDirectory(backupFolder, backupFolder + ".zip"); + Directory.Delete(backupFolder, true); + } + + private void CreateBackup(string backupPath, List saves, List applications) + { + string mountName = "save"; + string outputMountName = "output"; + + foreach (ApplicationData application in applications) + { + try + { + //Register destination folder as output and mount output + Result registerOutpDirResult = RegisterOutputDirectory(Path.Combine(backupPath, application.TitleId), outputMountName); + if (registerOutpDirResult.IsFailure()) + { + Logger.Error.Value.Print(LogClass.Application, $"Could not register and mount output directory."); + + } + + //Mount SaveData as save, opens the saveDataIterators and starts reading saveDataInfo + Result openAndReadSaveDataResult = OpenSaveDataIteratorAndReadSaveData(mountName, application, out SaveDataInfo saveDataInfo); + + if(openAndReadSaveDataResult.IsFailure()) + { + Logger.Error.Value.Print(LogClass.Application, $"Could not open save Iterator and start reading for application: {application.TitleName}"); + } + else + { + //Copies the whole directory from save mount to output mount + Result copyDirResult = CopySaveDataDirectory(mountName, outputMountName); + } + + + //Unmount save and output + UnmountDirectory(saveDataInfo.ProgramId.Value, mountName); + UnmountDirectory(saveDataInfo.ProgramId.Value, outputMountName); + } + catch (Exception) + { + continue; + } + } + } + + private Result RegisterOutputDirectory(string backupPath, string mountName) + { + using UniqueRef outputFileSystem = new UniqueRef(new LibHac.FsSystem.LocalFileSystem(backupPath)); + + Result registerResult = _horizonClient.Fs.Register(mountName.ToU8Span(), ref outputFileSystem.Ref()); + if (registerResult.IsFailure()) return registerResult.Miss(); + + return registerResult; + } + + private Result GetSaveDataIterator(out SaveDataInfo saveDataInfo, ApplicationData application) + { + return _horizonClient.Fs.FindSaveDataWithFilter(out saveDataInfo, + SaveDataSpaceId.User, + SaveDataFilter.Make(ulong.Parse(application.TitleId, NumberStyles.HexNumber), + saveType: default, + _userId, + saveDataId: default, + index: default)); + } + + private Result MountSaveDataDirectory(ulong programId, string mountName) + { + U8Span mountNameu8 = mountName.ToU8Span(); + + if (!_horizonClient.Fs.IsMounted(mountNameu8)) + { + return _horizonClient.Fs.MountSaveData(mountNameu8, ConvertProgramIdToApplicationId(programId), _userId); + } + + return Result.Success; + } + + private Result UnmountDirectory(ulong programId, string mountName) + { + U8Span mountNameu8 = mountName.ToU8Span(); + + if (_horizonClient.Fs.IsMounted(mountNameu8)) + { + _horizonClient.Fs.Unmount(mountNameu8); + } + + return Result.Success; + } + + private Result OpenSaveDataIteratorAndReadSaveData(string mountName, ApplicationData application, out SaveDataInfo saveDataInfo) + { + Result getSvDataIteratorResult = GetSaveDataIterator(out saveDataInfo, application); + if (getSvDataIteratorResult.IsFailure()) return getSvDataIteratorResult; + + Result mountSvDataResult = MountSaveDataDirectory(saveDataInfo.ProgramId.Value, mountName); + if (mountSvDataResult.IsFailure()) return mountSvDataResult; + + UniqueRef saveDataIterator = new UniqueRef(); + Result openSvDataIteratorResult = _horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator, SaveDataSpaceId.User); + if (openSvDataIteratorResult.IsFailure()) return openSvDataIteratorResult; + + Result readSvDataInfoResult = saveDataIterator.Get.ReadSaveDataInfo(out long readCount, new Span(ref saveDataInfo)); + if (readSvDataInfoResult.IsFailure()) return readSvDataInfoResult; + + return Result.Success; + } + + private Result CopySaveDataDirectory(string sourcePath, string destPath) + { + Result openDir = _horizonClient.Fs.OpenDirectory(out DirectoryHandle dirHandle, $"{sourcePath}:/".ToU8Span(), OpenDirectoryMode.All); + if (openDir.IsFailure()) return openDir; + + using (dirHandle) + { + sourcePath = $"{sourcePath}:/"; + destPath = $"{destPath}:/"; + foreach (DirectoryEntryEx entry in _horizonClient.Fs.EnumerateEntries(sourcePath, "*", SearchOptions.Default)) + { + string subSrcPath = PathTools.Normalize(PathTools.Combine(sourcePath, entry.Name)); + string subDstPath = PathTools.Normalize(PathTools.Combine(destPath, entry.Name)); + + if (entry.Type == DirectoryEntryType.Directory) + { + Result copyDirResult = CopyDirectory(subSrcPath, subDstPath); + } + if (entry.Type == DirectoryEntryType.File) + { + Result copyFinalDir = CopyFile(subSrcPath, subDstPath, entry.Size); + } + } + + _horizonClient.Fs.CloseDirectory(dirHandle); + + return Result.Success; + } + } + + private Result CopyDirectory(string subSrcPath, string subDstPath) + { + _horizonClient.Fs.EnsureDirectoryExists(subDstPath); + + Result copyDirResult = _horizonClient.Fs.CopyDirectory(subSrcPath, subDstPath); + + if (copyDirResult.IsFailure()) + { + Logger.Error.Value.Print(LogClass.Application, $"Could not copy directory: \n{subSrcPath} to destination {subDstPath}"); + } + + Logger.Info.Value.Print(LogClass.Application, $"Successfully copied directory: \n{subSrcPath} to destination {subDstPath}"); + + return copyDirResult; + } + + private Result CopyFile(string subSrcPath, string subDstPath, long entrySize) + { + _horizonClient.Fs.CreateOrOverwriteFile(subDstPath, entrySize); + + Result copyFileResult = _horizonClient.Fs.CopyFile(subSrcPath, subDstPath); + if (copyFileResult.IsFailure()) + { + Logger.Error.Value.Print(LogClass.Application, $"Could not copy file: \n{subSrcPath} to destination '{subDstPath}"); + } + + Logger.Info.Value.Print(LogClass.Application, $"Successfully copied file: \n{subSrcPath} to destination {subDstPath}"); + + return copyFileResult; + } + + private LibHac.Ncm.ApplicationId ConvertProgramIdToApplicationId(ulong programId) + { + return new LibHac.Ncm.ApplicationId(programId); + } + + private async Task GetAndPrepareBackupPath(MainWindow mainWindow) + { + SaveFileDialog saveFileDialog = new SaveFileDialog() + { + Title = LocaleManager.Instance["CreateZipFileDialogTitle"], + InitialFileName = "ryujinx_savedata_backup" + }; + + string zipPath = await saveFileDialog.ShowAsync(mainWindow); + + if (File.Exists(zipPath)) + { + File.Delete(zipPath); + } + + return zipPath; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/RestoreSavedataCommand.cs b/Ryujinx.Ava/Ui/Controls/SaveDataImporter.cs similarity index 81% rename from Ryujinx.Ava/Ui/Controls/RestoreSavedataCommand.cs rename to Ryujinx.Ava/Ui/Controls/SaveDataImporter.cs index f8575f5c4..e1fb30bfd 100644 --- a/Ryujinx.Ava/Ui/Controls/RestoreSavedataCommand.cs +++ b/Ryujinx.Ava/Ui/Controls/SaveDataImporter.cs @@ -1,8 +1,11 @@ using Avalonia.Controls; +using LibHac; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.Ui.Models; using Ryujinx.Ava.Ui.Windows; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.HLE.FileSystem; using System; using System.Collections.Generic; using System.IO; @@ -10,21 +13,16 @@ using System.IO.Compression; using System.Linq; using System.Text; using System.Threading.Tasks; -using System.Windows.Input; using Path = System.IO.Path; namespace Ryujinx.Ava.Ui.Controls { - internal class RestoreSavedataCommand : ICommand + internal class SaveDataImporter { - public event EventHandler CanExecuteChanged; - private readonly IControl parentControl; - - public RestoreSavedataCommand(IControl parentControl) - { - this.parentControl = parentControl; - } + private readonly UserProfile _userProfile; + private readonly HorizonClient _horizonClient; + private readonly VirtualFileSystem _virtualFileSystem; private async Task ShowConditionMessage() { @@ -33,28 +31,18 @@ namespace Ryujinx.Ava.Ui.Controls "Do you want to continue?"); } - public bool CanExecute(object parameter) - { - return true; - } - - public void Execute(object parameter) - { - RestoreSavedataBackup(); - } - - public async void RestoreSavedataBackup() + public async void RestoreSavedataBackup(MainWindow mainWindow) { if (!(await ShowConditionMessage())) return; - string[] backupZipFiles = await ShowFolderDialog(); + string[] backupZipFiles = await ShowFolderDialog(mainWindow); ExtractBackupToSaveDirectory(backupZipFiles); - Logger.Info.Value.Print(LogClass.Application, $"Done extracting savedata backup!", nameof(RestoreSavedataCommand)); + Logger.Info.Value.Print(LogClass.Application, $"Done extracting savedata backup!", nameof(SaveDataImporter)); } - private async Task ShowFolderDialog() + private async Task ShowFolderDialog(MainWindow mainWindow) { OpenFileDialog dialog = new() { @@ -63,7 +51,7 @@ namespace Ryujinx.Ava.Ui.Controls Filters = new List(new[] { new FileDialogFilter() { Extensions = new List() { "zip" } } }) }; - return await dialog.ShowAsync(parentControl.VisualRoot as MainWindow); + return await dialog.ShowAsync(mainWindow); } private Dictionary GetTitleIdWithSavedataPath(string saveDirectoryPath) @@ -84,7 +72,7 @@ namespace Ryujinx.Ava.Ui.Controls } catch (Exception) { - Logger.Error.Value.Print(LogClass.Application, $"Could not extract hex from savedata file: {saveDataExtra0file}", nameof(RestoreSavedataCommand)); + Logger.Error.Value.Print(LogClass.Application, $"Could not extract hex from savedata file: {saveDataExtra0file}", nameof(SaveDataImporter)); } } @@ -110,7 +98,7 @@ namespace Ryujinx.Ava.Ui.Controls string tempZipExtractionPath = Path.GetTempPath(); ZipFile.ExtractToDirectory(backupZipFiles.First(), tempZipExtractionPath, true); - Logger.Info.Value.Print(LogClass.Application, $"Extracted Backup zip to temp path: {tempZipExtractionPath}", nameof(RestoreSavedataCommand)); + Logger.Info.Value.Print(LogClass.Application, $"Extracted Backup zip to temp path: {tempZipExtractionPath}", nameof(SaveDataImporter)); string saveDir = Path.Combine(AppDataManager.BaseDirPath, AppDataManager.DefaultNandDir, "user", "save"); @@ -130,11 +118,11 @@ namespace Ryujinx.Ava.Ui.Controls try { Directory.Move(Directory.GetParent(titleIdAndBackupPath.Value).FullName, Directory.GetParent(titleIdsWithSavePaths[titleIdAndBackupPath.Key]).FullName); - Logger.Info.Value.Print(LogClass.Application, $"Copied Savedata {titleIdAndBackupPath.Value} to {titleIdsWithSavePaths[titleIdAndBackupPath.Key]}", nameof(RestoreSavedataCommand)); + Logger.Info.Value.Print(LogClass.Application, $"Copied Savedata {titleIdAndBackupPath.Value} to {titleIdsWithSavePaths[titleIdAndBackupPath.Key]}", nameof(SaveDataImporter)); } catch (Exception) { - Logger.Error.Value.Print(LogClass.Application, $"Could not copy Savedata {titleIdAndBackupPath.Value} to {titleIdsWithSavePaths[titleIdAndBackupPath.Key]}", nameof(RestoreSavedataCommand)); + Logger.Error.Value.Print(LogClass.Application, $"Could not copy Savedata {titleIdAndBackupPath.Value} to {titleIdsWithSavePaths[titleIdAndBackupPath.Key]}", nameof(SaveDataImporter)); } } } diff --git a/Ryujinx.Ava/Ui/Controls/SaveManager.axaml b/Ryujinx.Ava/Ui/Controls/SaveManager.axaml index 8721d2a7b..cf58b4a0e 100644 --- a/Ryujinx.Ava/Ui/Controls/SaveManager.axaml +++ b/Ryujinx.Ava/Ui/Controls/SaveManager.axaml @@ -99,5 +99,10 @@ - + + + + + + \ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs b/Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs index 499cd918e..de5084c1f 100644 --- a/Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs +++ b/Ryujinx.Ava/Ui/Controls/SaveManager.axaml.cs @@ -4,15 +4,19 @@ using DynamicData.Binding; using LibHac; using LibHac.Common; using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.Fs.Impl; using LibHac.Fs.Shim; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Ui.Models; +using Ryujinx.Ava.Ui.Windows; using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.App.Common; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Threading.Tasks; using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile; @@ -26,6 +30,7 @@ namespace Ryujinx.Ava.Ui.Controls private int _sortIndex; private int _orderIndex; private ObservableCollection _view = new ObservableCollection(); + private readonly List _applications; private string _search; public ObservableCollection Saves { get; set; } = new ObservableCollection(); @@ -71,11 +76,12 @@ namespace Ryujinx.Ava.Ui.Controls InitializeComponent(); } - public SaveManager(UserProfile userProfile, HorizonClient horizonClient, VirtualFileSystem virtualFileSystem) + public SaveManager(UserProfile userProfile, HorizonClient horizonClient, VirtualFileSystem virtualFileSystem, List applications) { _userProfile = userProfile; _horizonClient = horizonClient; _virtualFileSystem = virtualFileSystem; + _applications = applications; InitializeComponent(); DataContext = this; @@ -83,8 +89,22 @@ namespace Ryujinx.Ava.Ui.Controls Task.Run(LoadSaves); } + public void ImportBackup() + { + SaveDataImporter restoreSavedataCommand = new SaveDataImporter(); + restoreSavedataCommand.RestoreSavedataBackup(Parent.VisualRoot as MainWindow); + } + + public void ExportBackup() + { + SaveDataExporter backupSavedataCommand = new SaveDataExporter(_userProfile, _horizonClient, _virtualFileSystem); + backupSavedataCommand.SaveUserSaveDirectoryAsZip(Parent.VisualRoot as MainWindow, Saves.ToList(), _applications); + + } + public void LoadSaves() { + Saves.Clear(); var saveDataFilter = SaveDataFilter.Make(programId: default, saveType: SaveDataType.Account, new UserId((ulong)_userProfile.UserId.High, (ulong)_userProfile.UserId.Low), saveDataId: default, index: default); @@ -92,7 +112,7 @@ namespace Ryujinx.Ava.Ui.Controls using var saveDataIterator = new UniqueRef(); _horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); - + Span saveDataInfo = stackalloc SaveDataInfo[10]; while (true) diff --git a/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs index e90301784..f9bf68792 100644 --- a/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs @@ -1062,7 +1062,7 @@ namespace Ryujinx.Ava.Ui.ViewModels public async void ManageProfiles() { - await NavigationDialogHost.Show(_owner.AccountManager, _owner.ContentManager, _owner.VirtualFileSystem, _owner.LibHacHorizonManager.RyujinxClient); + await NavigationDialogHost.Show(_owner.AccountManager, _owner.ContentManager, _owner.VirtualFileSystem, _owner.LibHacHorizonManager.RyujinxClient, Applications.ToList()); } public async void OpenAboutWindow() diff --git a/Ryujinx.Ava/Ui/ViewModels/UserProfileViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/UserProfileViewModel.cs index 378419bf3..fbc25714f 100644 --- a/Ryujinx.Ava/Ui/ViewModels/UserProfileViewModel.cs +++ b/Ryujinx.Ava/Ui/ViewModels/UserProfileViewModel.cs @@ -143,15 +143,13 @@ namespace Ryujinx.Ava.Ui.ViewModels { UserProfile userProfile = _highlightedProfile ?? SelectedProfile; - SaveManager manager = new SaveManager(userProfile, _owner.HorizonClient, _owner.VirtualFileSystem); + SaveManager manager = new SaveManager(userProfile, _owner.HorizonClient, _owner.VirtualFileSystem, _owner.Applications); ContentDialog contentDialog = new ContentDialog { Title = string.Format(LocaleManager.Instance["SaveManagerHeading"], userProfile.Name), - PrimaryButtonText = "Backup Savedata", - PrimaryButtonCommand = new BackupSavedataCommand(_owner), - SecondaryButtonText = "Restore Savedata", - SecondaryButtonCommand = new RestoreSavedataCommand(_owner), + PrimaryButtonText = "", + SecondaryButtonText = "", CloseButtonText = LocaleManager.Instance["UserProfilesClose"], Content = manager, Padding = new Thickness(0)