mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-02-15 13:53:37 +00:00
added button to backup savedata
added button to restore backup
This commit is contained in:
parent
7004821a9e
commit
fb3c282544
3 changed files with 197 additions and 3 deletions
73
Ryujinx.Ava/Ui/Controls/BackupSavedataCommand.cs
Normal file
73
Ryujinx.Ava/Ui/Controls/BackupSavedataCommand.cs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Logging;
|
||||||
|
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 OpenFolderDialog());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> OpenFolderDialog()
|
||||||
|
{
|
||||||
|
OpenFolderDialog dialog = new()
|
||||||
|
{
|
||||||
|
Title = LocaleManager.Instance["OpenFolderDialogTitle"]
|
||||||
|
};
|
||||||
|
|
||||||
|
return await dialog.ShowAsync(parentControl.VisualRoot as MainWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateBackupZip(string directoryPath)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(directoryPath) && Directory.Exists(directoryPath))
|
||||||
|
{
|
||||||
|
string saveDir = Path.Combine(AppDataManager.BaseDirPath, AppDataManager.DefaultNandDir, "user", "save");
|
||||||
|
|
||||||
|
string zipFolderPath = Path.Combine(directoryPath, "Ryujinx_backup.zip");
|
||||||
|
|
||||||
|
Logger.Info.Value.Print(LogClass.Application, $"Start creating backup...", nameof(BackupSavedataCommand));
|
||||||
|
|
||||||
|
if (File.Exists(zipFolderPath))
|
||||||
|
{
|
||||||
|
File.Delete(zipFolderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipFile.CreateFromDirectory(saveDir, zipFolderPath);
|
||||||
|
|
||||||
|
Logger.Info.Value.Print(LogClass.Application, $"Backup done. Zip is locate under {directoryPath}", nameof(BackupSavedataCommand));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
119
Ryujinx.Ava/Ui/Controls/RestoreSavedataCommand.cs
Normal file
119
Ryujinx.Ava/Ui/Controls/RestoreSavedataCommand.cs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
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.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Ui.Controls
|
||||||
|
{
|
||||||
|
internal class RestoreSavedataCommand : ICommand
|
||||||
|
{
|
||||||
|
public event EventHandler CanExecuteChanged;
|
||||||
|
|
||||||
|
private readonly IControl parentControl;
|
||||||
|
|
||||||
|
public RestoreSavedataCommand(IControl parentControl)
|
||||||
|
{
|
||||||
|
this.parentControl = parentControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> ShowConditionMessage()
|
||||||
|
{
|
||||||
|
return await ContentDialogHelper.CreateChoiceDialog("Restore Backup",
|
||||||
|
"You have to start every game at least once to create a save directory for the game before you can Restore the backup save data!",
|
||||||
|
"Do you want to continue?");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanExecute(object parameter)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(object parameter)
|
||||||
|
{
|
||||||
|
RestoreSavedataBackup();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void RestoreSavedataBackup()
|
||||||
|
{
|
||||||
|
if (!(await ShowConditionMessage())) return;
|
||||||
|
|
||||||
|
string[] backupZipFiles = await ShowFolderDialog();
|
||||||
|
|
||||||
|
ExtractBackupToSaveDirectory(backupZipFiles);
|
||||||
|
|
||||||
|
Logger.Info.Value.Print(LogClass.Application, $"Done extracting savedata backup!", nameof(RestoreSavedataCommand));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string[]> ShowFolderDialog()
|
||||||
|
{
|
||||||
|
OpenFileDialog dialog = new()
|
||||||
|
{
|
||||||
|
Title = LocaleManager.Instance["OpenFileDialogTitle"],
|
||||||
|
AllowMultiple = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
return await dialog.ShowAsync(parentControl.VisualRoot as MainWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExtractBackupToSaveDirectory(string[] backupZipFiles)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(backupZipFiles.First()) && File.Exists(backupZipFiles.First()))
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
|
||||||
|
string saveDir = Path.Combine(AppDataManager.BaseDirPath, AppDataManager.DefaultNandDir, "user", "save");
|
||||||
|
ReplaceSavedataFilesWithBackupSaveFiles(Directory.GetDirectories(tempZipExtractionPath), saveDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReplaceSavedataFilesWithBackupSaveFiles(string[] backupSavedataPath, string saveDirectory)
|
||||||
|
{
|
||||||
|
//All current save files for later replacement
|
||||||
|
string[] userSaveFiles = GetSaveFilesInAllSubDirectories(saveDirectory);
|
||||||
|
|
||||||
|
//Loops through every Title save folder and replaces all user save data files with the savedata files inside the extracted backup folder
|
||||||
|
//Logic to decide wich file is replaces is based on der filename and parent directory
|
||||||
|
foreach (string[] backupSaveFiles in backupSavedataPath.Select(GetSaveFilesInAllSubDirectories))
|
||||||
|
{
|
||||||
|
foreach (string backupSaveFile in backupSaveFiles)
|
||||||
|
{
|
||||||
|
foreach (string userSaveFile in GetSaveFilesWithSameNameAndParentDir(userSaveFiles, backupSaveFile))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Copy(backupSaveFile, userSaveFile, true);
|
||||||
|
Logger.Info.Value.Print(LogClass.Application, $"Copied Savedata {backupSaveFile} to {userSaveFile}", nameof(RestoreSavedataCommand));
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
Logger.Error.Value.Print(LogClass.Application, $"Could not copy Savedata {backupSaveFile} to {userSaveFile}", nameof(RestoreSavedataCommand));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] GetSaveFilesWithSameNameAndParentDir(string[] userSaveFiles, string backupSaveFile)
|
||||||
|
{
|
||||||
|
return userSaveFiles.Where(sf => Path.GetFileName(sf) == Path.GetFileName(backupSaveFile) && Directory.GetParent(sf).Name == Directory.GetParent(backupSaveFile).Name).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] GetSaveFilesInAllSubDirectories(string rootDirectory)
|
||||||
|
{
|
||||||
|
string[] unnecessarySaveFiles = { ".lock", "ExtraData0", "ExtraData1" };
|
||||||
|
|
||||||
|
return Directory.GetFiles(rootDirectory, "*", SearchOption.AllDirectories).Where(file => !unnecessarySaveFiles.Any(usf => file.EndsWith(usf))).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -148,8 +148,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
||||||
ContentDialog contentDialog = new ContentDialog
|
ContentDialog contentDialog = new ContentDialog
|
||||||
{
|
{
|
||||||
Title = string.Format(LocaleManager.Instance["SaveManagerHeading"], userProfile.Name),
|
Title = string.Format(LocaleManager.Instance["SaveManagerHeading"], userProfile.Name),
|
||||||
PrimaryButtonText = "",
|
PrimaryButtonText = "Backup Savedata",
|
||||||
SecondaryButtonText = "",
|
PrimaryButtonCommand = new BackupSavedataCommand(_owner),
|
||||||
|
SecondaryButtonText = "Restore Savedata",
|
||||||
|
SecondaryButtonCommand = new RestoreSavedataCommand(_owner),
|
||||||
CloseButtonText = LocaleManager.Instance["UserProfilesClose"],
|
CloseButtonText = LocaleManager.Instance["UserProfilesClose"],
|
||||||
Content = manager,
|
Content = manager,
|
||||||
Padding = new Thickness(0)
|
Padding = new Thickness(0)
|
||||||
|
|
Loading…
Reference in a new issue