mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-02-15 05:43:36 +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
|
||||
{
|
||||
Title = string.Format(LocaleManager.Instance["SaveManagerHeading"], userProfile.Name),
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
PrimaryButtonText = "Backup Savedata",
|
||||
PrimaryButtonCommand = new BackupSavedataCommand(_owner),
|
||||
SecondaryButtonText = "Restore Savedata",
|
||||
SecondaryButtonCommand = new RestoreSavedataCommand(_owner),
|
||||
CloseButtonText = LocaleManager.Instance["UserProfilesClose"],
|
||||
Content = manager,
|
||||
Padding = new Thickness(0)
|
||||
|
|
Loading…
Reference in a new issue