Cleanup + Fix warnings

This commit is contained in:
Isaac Marovitz 2023-10-04 10:03:57 -04:00
parent 79f94020df
commit c8b39e0c98
No known key found for this signature in database
GPG key ID: 97250B2B09A132E1
6 changed files with 89 additions and 281 deletions

View file

@ -1,10 +0,0 @@
using System;
namespace Ryujinx.Ava.Common.SaveManager
{
public readonly record struct BackupRequestOutcome
{
public bool DidFail { get; init; }
public string Message { get; init; }
}
}

View file

@ -1,36 +0,0 @@
using LibHac.Fs;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.Common.Helper;
using Ryujinx.Ui.Common.SaveManager;
using System;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Common.SaveManager
{
public interface ISaveManager
{
public event EventHandler<LoadingBarEventArgs> BackupProgressUpdated;
public event EventHandler<ImportSaveEventArgs> BackupImportSave;
#region Backup
public Task<BackupRequestOutcome> BackupUserSaveDataToZip(UserId userId,
string location,
SaveOptions saveOptions = SaveOptions.Default);
public Task<BackupRequestOutcome> BackupUserTitleSaveDataToZip(UserId userId,
ulong titleId,
string location,
SaveOptions saveOptions = SaveOptions.Default);
#endregion
#region Restore
public Task<BackupRequestOutcome> RestoreUserSaveDataFromZip(UserId userId,
string sourceDataPath);
public Task<BackupRequestOutcome> RestoreUserTitleSaveFromZip(UserId userId,
ulong titleId,
string sourceDataPath);
#endregion
}
}

View file

@ -5,11 +5,9 @@ using LibHac.Fs;
using LibHac.Fs.Shim; using LibHac.Fs.Shim;
using LibHac.Ns; using LibHac.Ns;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using Ryujinx.Ui.Common.SaveManager; using Ryujinx.Ui.Common.SaveManager;
using System; using System;
@ -23,52 +21,32 @@ using Path = System.IO.Path;
namespace Ryujinx.Ava.Common.SaveManager namespace Ryujinx.Ava.Common.SaveManager
{ {
public class SaveManager : ISaveManager public class SaveManager
{ {
// UI Metadata // UI Metadata
public event EventHandler<LoadingBarEventArgs> BackupProgressUpdated; public event EventHandler<LoadingBarEventArgs> BackupProgressUpdated;
public event EventHandler<ImportSaveEventArgs> BackupImportSave; public event EventHandler<ImportSaveEventArgs> BackupImportSave;
private readonly LoadingBarEventArgs _loadingEventArgs; private readonly LoadingBarEventArgs _loadingEventArgs = new();
private readonly HorizonClient _horizonClient; private readonly HorizonClient _horizonClient;
private readonly AccountManager _accountManager;
public SaveManager(HorizonClient hzClient, AccountManager acctManager) public SaveManager(HorizonClient hzClient)
{ {
_loadingEventArgs = new();
_horizonClient = hzClient; _horizonClient = hzClient;
_accountManager = acctManager;
} }
#region Backup #region Backup
public Task<BackupRequestOutcome> BackupUserTitleSaveDataToZip(LibHacUserId userId, public async Task<bool> BackupUserSaveDataToZip(LibHacUserId userId, Uri savePath)
ulong titleId,
string location,
SaveOptions saveOptions = SaveOptions.Default)
{ {
throw new NotImplementedException(); var userSaves = GetUserSaveData(userId).ToArray();
} if (userSaves.Length == 0)
public async Task<BackupRequestOutcome> BackupUserSaveDataToZip(LibHacUserId userId,
string location,
SaveOptions saveOptions = SaveOptions.Default)
{
// TODO: Eventually add cancellation source
var userSaves = GetUserSaveData(userId, saveOptions);
if (userSaves.IsNullOrEmpty())
{ {
Logger.Warning?.Print(LogClass.Application, "No save data found"); Logger.Warning?.Print(LogClass.Application, "No save data found");
return new BackupRequestOutcome return false;
{
DidFail = false,
Message = "No save data found"
};
} }
_loadingEventArgs.Curr = 0; _loadingEventArgs.Curr = 0;
_loadingEventArgs.Max = userSaves.Count() + 1; // add one for metadata file _loadingEventArgs.Max = userSaves.Length + 1; // Add one for metadata file
BackupProgressUpdated?.Invoke(this, _loadingEventArgs); BackupProgressUpdated?.Invoke(this, _loadingEventArgs);
// Create the top level temp dir for the intermediate copies - ensure it's empty // Create the top level temp dir for the intermediate copies - ensure it's empty
@ -80,25 +58,13 @@ namespace Ryujinx.Ava.Common.SaveManager
// Delete temp for good measure? // Delete temp for good measure?
_ = Directory.CreateDirectory(backupTempDir); _ = Directory.CreateDirectory(backupTempDir);
var outcome = await BatchCopySavesToTempDir(userId, userSaves, backupTempDir) return await BatchCopySavesToTempDir(userSaves, backupTempDir)
&& CompleteBackup(location, userId, backupTempDir); && CreateOrReplaceZipFile(backupTempDir, savePath.LocalPath);
return new BackupRequestOutcome
{
DidFail = !outcome,
Message = outcome
? string.Empty
: "Failed to backup user saves"
};
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error?.Print(LogClass.Application, $"Failed to backup user data - {ex.Message}"); Logger.Error?.Print(LogClass.Application, $"Failed to backup user data - {ex.Message}");
return new BackupRequestOutcome return false;
{
DidFail = true,
Message = $"Failed to backup user data - {ex.Message}"
};
} }
finally finally
{ {
@ -107,36 +73,20 @@ namespace Ryujinx.Ava.Common.SaveManager
Directory.Delete(backupTempDir, true); Directory.Delete(backupTempDir, true);
} }
} }
// Produce the actual zip
bool CompleteBackup(string location, LibHacUserId userId, string backupTempDir)
{
var currDate = DateTime.UtcNow.ToString("yyyy-MM-dd");
var profileName = _accountManager
.GetAllUsers()
.FirstOrDefault(u => u.UserId.ToLibHacUserId() == userId)?.Name;
var backupFile = Path.Combine(location, $"{profileName}_{currDate}_saves.zip");
return CreateOrReplaceZipFile(backupTempDir, backupFile);
}
} }
private IEnumerable<BackupSaveMeta> GetUserSaveData(LibHacUserId userId, SaveOptions saveOptions) private IEnumerable<BackupSaveMeta> GetUserSaveData(LibHacUserId userId)
{ {
try try
{ {
// Almost all games have user savess // Almost all games have user saves
var userSaves = GetSaveData(userId, SaveDataType.Account) var userSaves = GetSaveData(userId, SaveDataType.Account)
.ToList(); .ToList();
var deviceSaves = saveOptions.HasFlag(SaveOptions.SaveTypeDevice) var deviceSaves = GetSaveData(default, SaveDataType.Device);
? GetSaveData(default, SaveDataType.Device)
: Enumerable.Empty<BackupSaveMeta>();
userSaves.AddRange(deviceSaves); userSaves.AddRange(deviceSaves);
var bcatSaves = saveOptions.HasFlag(SaveOptions.SaveTypeDevice) var bcatSaves = GetSaveData(default, SaveDataType.Bcat);
? GetSaveData(default, SaveDataType.Bcat)
: Enumerable.Empty<BackupSaveMeta>();
userSaves.AddRange(bcatSaves); userSaves.AddRange(bcatSaves);
return userSaves; return userSaves;
@ -195,14 +145,11 @@ namespace Ryujinx.Ava.Common.SaveManager
return saves; return saves;
} }
private async Task<bool> BatchCopySavesToTempDir(LibHacUserId userId, IEnumerable<BackupSaveMeta> userSaves, string backupTempDir) private async Task<bool> BatchCopySavesToTempDir(IEnumerable<BackupSaveMeta> userSaves, string backupTempDir)
{ {
// Generate a metadata item so users know what titleIds ares in case they're moving around, jksv, sanity, etc
Dictionary<ulong, UserFriendlyAppData> userFriendlyMetadataMap = new();
try try
{ {
// batch intermediate copies so we don't overwhelm systems // Batch intermediate copies so we don't overwhelm systems
const int BATCH_SIZE = 5; const int BATCH_SIZE = 5;
List<Task<bool>> tempCopyTasks = new(BATCH_SIZE); List<Task<bool>> tempCopyTasks = new(BATCH_SIZE);
@ -219,29 +166,11 @@ namespace Ryujinx.Ava.Common.SaveManager
// Add backup task // Add backup task
tempCopyTasks.Add(CopySaveDataToIntermediateDirectory(meta, backupTempDir)); tempCopyTasks.Add(CopySaveDataToIntermediateDirectory(meta, backupTempDir));
// Add title metadata entry - might be dupe from bcat/device
if (!userFriendlyMetadataMap.ContainsKey(meta.TitleId.Value))
{
var titleIdHex = meta.TitleId.Value.ToString("x16");
var appData = MainWindow.MainWindowViewModel.Applications
.FirstOrDefault(x => x.TitleId.Equals(titleIdHex, StringComparison.OrdinalIgnoreCase));
userFriendlyMetadataMap.Add(meta.TitleId.Value, new UserFriendlyAppData
{
Title = appData?.TitleName,
TitleId = meta.TitleId.Value,
TitleIdHex = titleIdHex
});
}
} }
// wait for any outstanding temp copies to complete // wait for any outstanding temp copies to complete
_ = await Task.WhenAll(tempCopyTasks); _ = await Task.WhenAll(tempCopyTasks);
// finally, move the metadata tag file into the backup dir and track progress
await WriteMetadataFile(backupTempDir, userId, userFriendlyMetadataMap);
_loadingEventArgs.Curr++; _loadingEventArgs.Curr++;
BackupProgressUpdated?.Invoke(this, _loadingEventArgs); BackupProgressUpdated?.Invoke(this, _loadingEventArgs);
@ -253,34 +182,6 @@ namespace Ryujinx.Ava.Common.SaveManager
} }
return false; return false;
#region LocalMethods
async Task WriteMetadataFile(string backupTempDir,
LibHacUserId userId,
Dictionary<ulong, UserFriendlyAppData> userFriendlyMetadataMap)
{
try
{
var userProfile = _accountManager.GetAllUsers()
.FirstOrDefault(u => u.UserId.ToLibHacUserId() == userId);
var tagFile = Path.Combine(backupTempDir, "tag.json");
var completeMeta = System.Text.Json.JsonSerializer.Serialize(new UserFriendlySaveMetadata
{
UserId = userId.ToString(),
ProfileName = userProfile.Name,
CreationTimeUtc = DateTime.UtcNow,
ApplicationMap = userFriendlyMetadataMap.Values
});
await File.WriteAllTextAsync(tagFile, completeMeta);
}
catch (Exception ex)
{
Logger.Error?.Print(LogClass.Application, $"Failed to write user friendly save metadata file - {ex.Message}");
}
}
#endregion
} }
private async Task<bool> CopySaveDataToIntermediateDirectory(BackupSaveMeta saveMeta, string destinationDir) private async Task<bool> CopySaveDataToIntermediateDirectory(BackupSaveMeta saveMeta, string destinationDir)
@ -322,14 +223,7 @@ namespace Ryujinx.Ava.Common.SaveManager
#endregion #endregion
#region Restore #region Restore
public Task<BackupRequestOutcome> RestoreUserTitleSaveFromZip(LibHacUserId userId, public async Task<bool> RestoreUserSaveDataFromZip(LibHacUserId userId,
ulong titleId,
string sourceDataPath)
{
throw new NotImplementedException();
}
public async Task<BackupRequestOutcome> RestoreUserSaveDataFromZip(LibHacUserId userId,
string sourceDataPath) string sourceDataPath)
{ {
var sourceInfo = new FileInfo(sourceDataPath); var sourceInfo = new FileInfo(sourceDataPath);
@ -352,11 +246,7 @@ namespace Ryujinx.Ava.Common.SaveManager
var error = $"Failed to extract save backup zip {ex.Message}"; var error = $"Failed to extract save backup zip {ex.Message}";
Logger.Error?.Print(LogClass.Application, error); Logger.Error?.Print(LogClass.Application, error);
return new() return false;
{
DidFail = true,
Message = error
};
} }
} }
@ -370,10 +260,7 @@ namespace Ryujinx.Ava.Common.SaveManager
var identifiedTitleDirectories = GetTitleDirectories(sourceInfo); var identifiedTitleDirectories = GetTitleDirectories(sourceInfo);
if (identifiedTitleDirectories.IsNullOrEmpty()) if (identifiedTitleDirectories.IsNullOrEmpty())
{ {
return new() return false;
{
DidFail = true,
};
} }
// Start import // Start import
@ -403,18 +290,12 @@ namespace Ryujinx.Ava.Common.SaveManager
// let the import complete // let the import complete
_ = await Task.WhenAll(importBuffer); _ = await Task.WhenAll(importBuffer);
return new BackupRequestOutcome return true;
{
DidFail = false
};
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error?.Print(LogClass.Application, $"Failed to import save data - {ex.Message}"); Logger.Error?.Print(LogClass.Application, $"Failed to import save data - {ex.Message}");
return new BackupRequestOutcome return false;
{
DidFail = false
};
} }
finally finally
{ {

View file

@ -1,23 +0,0 @@
using System;
namespace Ryujinx.Ava.Common.SaveManager
{
[Flags]
public enum SaveOptions
{
// Save Data Types
SaveTypeAccount,
SaveTypeBcat,
SaveTypeDevice,
SaveTypeAll = SaveTypeAccount | SaveTypeBcat | SaveTypeDevice,
// Request Semantics -- Not Implemented
SkipEmptyDirectories,
FlattenSaveStructure,
StopOnFirstFailure,
UseDateInName,
ObfuscateZipExtension,
Default = SaveTypeAll
}
}

View file

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.Ava.Common.SaveManager
{
internal readonly record struct UserFriendlyAppData
{
public ulong TitleId { get; init; }
public string Title { get; init; }
public string TitleIdHex { get; init; }
}
internal readonly record struct UserFriendlySaveMetadata
{
public string UserId { get; init; }
public string ProfileName { get; init; }
public DateTime CreationTimeUtc { get; init; }
public IEnumerable<UserFriendlyAppData> ApplicationMap { get; init; }
}
}

View file

@ -1,6 +1,7 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Notifications; using Avalonia.Controls.Notifications;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using DynamicData; using DynamicData;
@ -22,6 +23,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using Ryujinx.Ui.Common.SaveManager; using Ryujinx.Ui.Common.SaveManager;
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -36,9 +38,8 @@ namespace Ryujinx.Ava.UI.Views.User
private AccountManager _accountManager; private AccountManager _accountManager;
private HorizonClient _horizonClient; private HorizonClient _horizonClient;
private VirtualFileSystem _virtualFileSystem;
private NavigationDialogHost _parent; private NavigationDialogHost _parent;
private ISaveManager _saveManager; private SaveManager _saveManager;
public UserSaveManagerView() public UserSaveManagerView()
{ {
@ -59,10 +60,10 @@ namespace Ryujinx.Ava.UI.Views.User
switch (arg.NavigationMode) switch (arg.NavigationMode)
{ {
case NavigationMode.New: case NavigationMode.New:
(_parent, _accountManager, _horizonClient, _virtualFileSystem) = (_parent, _accountManager, _horizonClient, _) =
((NavigationDialogHost parent, AccountManager accountManager, HorizonClient client, VirtualFileSystem virtualFileSystem))arg.Parameter; ((NavigationDialogHost parent, AccountManager accountManager, HorizonClient client, VirtualFileSystem virtualFileSystem))arg.Parameter;
_saveManager = new SaveManager(_horizonClient, _accountManager); _saveManager = new SaveManager(_horizonClient);
_saveManager.BackupProgressUpdated += BackupManager_ProgressUpdate; _saveManager.BackupProgressUpdated += BackupManager_ProgressUpdate;
_saveManager.BackupImportSave += BackupManager_ImportSave; _saveManager.BackupImportSave += BackupManager_ImportSave;
@ -79,11 +80,11 @@ namespace Ryujinx.Ava.UI.Views.User
ViewModel.Saves.Clear(); ViewModel.Saves.Clear();
var saves = new ObservableCollection<SaveModel>(); var saves = new ObservableCollection<SaveModel>();
var saveDataFilter = SaveDataFilter.Make( var saveDataFilter = SaveDataFilter.Make(
programId: default, default,
saveType: SaveDataType.Account, SaveDataType.Account,
new UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low), new UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low),
saveDataId: default, default,
index: default); default);
using var saveDataIterator = new UniqueRef<SaveDataIterator>(); using var saveDataIterator = new UniqueRef<SaveDataIterator>();
@ -143,7 +144,8 @@ namespace Ryujinx.Ava.UI.Views.User
{ {
if (button.DataContext is SaveModel saveModel) if (button.DataContext is SaveModel saveModel)
{ {
var result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DeleteUserSave], var result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance[LocaleKeys.DeleteUserSave],
LocaleManager.Instance[LocaleKeys.IrreversibleActionNote], LocaleManager.Instance[LocaleKeys.IrreversibleActionNote],
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo], ""); LocaleManager.Instance[LocaleKeys.InputDialogNo], "");
@ -160,16 +162,26 @@ namespace Ryujinx.Ava.UI.Views.User
private async void GenerateProfileSaveBackup(object sender, RoutedEventArgs e) private async void GenerateProfileSaveBackup(object sender, RoutedEventArgs e)
{ {
OpenFolderDialog dialog = new() var window = ((TopLevel)_parent.GetVisualRoot()) as Window;
{ var currDate = DateTime.UtcNow.ToString("yyyy-MM-dd");
Title = LocaleManager.Instance[LocaleKeys.SaveManagerChooseBackupFolderTitle] var profileName = _accountManager.LastOpenedUser.Name;
}; var fileName = $"{profileName}_{currDate}_saves";
var backupDir = await dialog.ShowAsync(((TopLevel)_parent.GetVisualRoot()) as Window); var file = await window.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
if (string.IsNullOrWhiteSpace(backupDir))
{ {
return; Title = LocaleManager.Instance[LocaleKeys.SaveManagerChooseBackupFolderTitle],
} DefaultExtension = "zip",
SuggestedFileName = fileName,
FileTypeChoices = new List<FilePickerFileType>
{
new("zip")
{
Patterns = new[] { "*.zip" },
AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
MimeTypes = new[] { "application/zip" }
}
}
});
// Disable the user from doing anything until we complete // Disable the user from doing anything until we complete
ViewModel.IsGoBackEnabled = false; ViewModel.IsGoBackEnabled = false;
@ -178,23 +190,21 @@ namespace Ryujinx.Ava.UI.Views.User
{ {
// Could potentially seed with existing saves already enumerated but we still need bcat and device data // Could potentially seed with existing saves already enumerated but we still need bcat and device data
var result = await _saveManager.BackupUserSaveDataToZip( var result = await _saveManager.BackupUserSaveDataToZip(
userId: _accountManager.LastOpenedUser.UserId.ToLibHacUserId(), _accountManager.LastOpenedUser.UserId.ToLibHacUserId(),
location: backupDir, file.Path);
saveOptions: SaveOptions.Default);
var notificationType = result.DidFail var notificationType = result
? NotificationType.Error ? NotificationType.Success
: NotificationType.Success; : NotificationType.Error;
var message = result.DidFail var message = result
? LocaleManager.Instance[LocaleKeys.SaveManagerBackupFailed] ? LocaleManager.Instance[LocaleKeys.SaveManagerBackupComplete]
: LocaleManager.Instance[LocaleKeys.SaveManagerBackupComplete]; : LocaleManager.Instance[LocaleKeys.SaveManagerBackupFailed];
NotificationHelper.Show(LocaleManager.Instance[LocaleKeys.NotificationBackupTitle], NotificationHelper.Show(
LocaleManager.Instance[LocaleKeys.NotificationBackupTitle],
message, message,
notificationType); notificationType);
return;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -209,37 +219,42 @@ namespace Ryujinx.Ava.UI.Views.User
private async void ImportSaveBackup(object sender, RoutedEventArgs e) private async void ImportSaveBackup(object sender, RoutedEventArgs e)
{ {
bool userConfirmation = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.SaveManagerConfirmRestoreTitle], bool userConfirmation = await ContentDialogHelper.CreateChoiceDialog(
LocaleManager.Instance[LocaleKeys.SaveManagerConfirmRestoreTitle],
LocaleManager.Instance[LocaleKeys.SaveManagerChooseRestoreZipPrimaryMessage], LocaleManager.Instance[LocaleKeys.SaveManagerChooseRestoreZipPrimaryMessage],
LocaleManager.Instance[LocaleKeys.SaveManagerChooseRestoreZipSecondaryMessage], LocaleManager.Instance[LocaleKeys.SaveManagerChooseRestoreZipSecondaryMessage],
primaryButtonKey: LocaleKeys.SaveMangerRestoreUserConfirm, LocaleKeys.SaveMangerRestoreUserConfirm,
closeButtonKey: LocaleKeys.SaveMangerRestoreUserCancel); LocaleKeys.SaveMangerRestoreUserCancel);
if (!userConfirmation) if (!userConfirmation)
{ {
return; return;
} }
OpenFileDialog dialog = new() var window = ((TopLevel)_parent.GetVisualRoot()) as Window;
var fileResult = await window.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.SaveManagerChooseRestoreZipTitle], Title = LocaleManager.Instance[LocaleKeys.SaveManagerChooseRestoreZipTitle],
AllowMultiple = false, AllowMultiple = false,
Filters = { FileTypeFilter = new List<FilePickerFileType>
new FileDialogFilter() { {
Name = "Zip files", new("zip")
Extensions = { "zip" }, {
Patterns = new[] { "*.zip" },
AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
MimeTypes = new[] { "application/zip" }
} }
} }
}; });
var saveBackupZip = await dialog.ShowAsync(((TopLevel)_parent.GetVisualRoot()) as Window); if (fileResult.Count <= 0)
if (saveBackupZip is null
|| saveBackupZip.Length == 0
|| string.IsNullOrWhiteSpace(saveBackupZip[0]))
{ {
return; return;
} }
var saveBackupZip = fileResult[0].Path.LocalPath;
// Disable the user from doing anything until we complete // Disable the user from doing anything until we complete
ViewModel.IsGoBackEnabled = false; ViewModel.IsGoBackEnabled = false;
@ -247,16 +262,16 @@ namespace Ryujinx.Ava.UI.Views.User
{ {
// Could potentially seed with existing saves already enumerated but we still need bcat and device data // Could potentially seed with existing saves already enumerated but we still need bcat and device data
var result = await _saveManager.RestoreUserSaveDataFromZip( var result = await _saveManager.RestoreUserSaveDataFromZip(
userId: _accountManager.LastOpenedUser.UserId.ToLibHacUserId(), _accountManager.LastOpenedUser.UserId.ToLibHacUserId(),
sourceDataPath: saveBackupZip[0]); saveBackupZip);
var notificationType = result.DidFail var notificationType = result
? NotificationType.Error ? NotificationType.Success
: NotificationType.Success; : NotificationType.Error;
var message = result.DidFail var message = result
? LocaleManager.Instance[LocaleKeys.SaveManagerRestoreFailed] ? LocaleManager.Instance[LocaleKeys.SaveManagerRestoreComplete]
: LocaleManager.Instance[LocaleKeys.SaveManagerRestoreComplete]; : LocaleManager.Instance[LocaleKeys.SaveManagerRestoreFailed];
if (!string.IsNullOrWhiteSpace(ViewModel.Search)) if (!string.IsNullOrWhiteSpace(ViewModel.Search))
{ {
@ -266,7 +281,8 @@ namespace Ryujinx.Ava.UI.Views.User
}); });
} }
NotificationHelper.Show(LocaleManager.Instance[LocaleKeys.NotificationBackupTitle], NotificationHelper.Show(
LocaleManager.Instance[LocaleKeys.NotificationBackupTitle],
message, message,
notificationType); notificationType);
} }