/// <summary> /// Restores a backup on the game /// </summary> /// <param name="backupInformation">The backup information</param> /// <returns>True if the backup was successful</returns> public async Task <bool> RestoreAsync(GameBackups_BackupInfo backupInformation) { using (await AsyncLock.LockAsync()) { Logger.Info("A backup restore has been requested for {0}", backupInformation.GameDisplayName); try { // Get the backup directory GameBackups_ExistingBackup?existingBackup = backupInformation.GetPrimaryBackup; // Make sure a backup exists if (existingBackup == null) { Logger.Info("Restore failed - the input location could not be found"); await Message.DisplayMessageAsync(String.Format(Resources.Restore_MissingBackup, backupInformation.GameDisplayName), Resources.Restore_FailedHeader, MessageType.Error); return(false); } if (backupInformation.RestoreDirectories is null) { throw new InvalidOperationException("A restore can only be performed on an info object with valid directories"); } // Make sure we have write access to the restore destinations if (backupInformation.RestoreDirectories.Any(x => !File.CheckDirectoryWriteAccess(x.SearchPattern.DirPath))) { Logger.Info("Restore failed - one or more restore destinations lack write access"); // Request to restart as admin await App.RequestRestartAsAdminAsync(); return(false); } using (var tempDir = new TempDirectory(true)) { using var archiveTempDir = new TempDirectory(true); bool hasCreatedTempBackup = false; try { // If the backup is an archive, extract it to temp if (existingBackup.IsCompressed) { using var file = System.IO.File.OpenRead(existingBackup.Path); using var zip = new ZipArchive(file, ZipArchiveMode.Read); zip.ExtractToDirectory(archiveTempDir.TempPath); } // Move existing files to temp in case the restore fails foreach (BackupSearchPattern item in backupInformation.RestoreDirectories) { // Make sure the directory exists if (!item.SearchPattern.DirPath.DirectoryExists) { continue; } // Get the destination directory var destDir = tempDir.TempPath + item.ID; // Check if the entire directory should be moved if (item.SearchPattern.IsEntireDir()) { // Move the directory File.MoveDirectory(item.SearchPattern.DirPath, destDir, true, true); } else { File.MoveFiles(item.SearchPattern, destDir, true); } } hasCreatedTempBackup = true; // Restore each backup directory foreach (BackupSearchPattern item in backupInformation.RestoreDirectories) { // Get the combined directory path var dirPath = (existingBackup.IsCompressed ? archiveTempDir.TempPath : existingBackup.Path) + item.ID; // Restore the backup if (dirPath.DirectoryExists) { File.CopyDirectory(dirPath, item.SearchPattern.DirPath, false, true); } } } catch { // Delete restored files if restore began if (hasCreatedTempBackup) { foreach (BackupSearchPattern item in backupInformation.RestoreDirectories) { // Make sure the directory exists if (!item.SearchPattern.DirPath.DirectoryExists) { continue; } // Check if the entire directory should be deleted if (item.SearchPattern.IsEntireDir()) { // Delete the directory File.DeleteDirectory(item.SearchPattern.DirPath); } else { // Delete each file foreach (FileSystemPath file in item.SearchPattern.GetFiles()) { // Delete the file File.DeleteFile(file); } } } } // Restore temp backup foreach (BackupSearchPattern item in backupInformation.RestoreDirectories) { // Get the combined directory path var dirPath = tempDir.TempPath + item.ID; // Make sure there is a directory to restore if (!dirPath.DirectoryExists) { continue; } // Restore File.MoveDirectory(dirPath, item.SearchPattern.DirPath, false, false); } Logger.Info("Restore failed - clean up succeeded"); throw; } } Logger.Info("Restore complete"); return(true); } catch (Exception ex) { Logger.Error(ex, "Restoring game"); await Message.DisplayExceptionMessageAsync(ex, String.Format(Resources.Restore_Failed, backupInformation.GameDisplayName), Resources.Restore_FailedHeader); return(false); } } }
/// <summary> /// Performs a backup on the game /// </summary> /// <param name="backupInformation">The backup information</param> /// <returns>True if the backup was successful</returns> public async Task <bool> BackupAsync(GameBackups_BackupInfo backupInformation) { using (await AsyncLock.LockAsync()) { Logger.Info("A backup has been requested for {0}", backupInformation.GameDisplayName); try { // Make sure we have write access to the backup location if (!File.CheckDirectoryWriteAccess(BackupDirectory)) { Logger.Info("Backup failed - backup location lacks write access"); // Request to restart as admin await App.RequestRestartAsAdminAsync(); return(false); } if (backupInformation.BackupDirectories is null) { throw new InvalidOperationException("A backup can only be performed on an info object with valid directories"); } // Make sure all the directories to back up exist if (!backupInformation.BackupDirectories.Select(x => x.SearchPattern.DirPath).DirectoriesExist()) { Logger.Info("Backup failed - the input directories could not be found"); await Message.DisplayMessageAsync(String.Format(Resources.Backup_MissingDirectoriesError, backupInformation.GameDisplayName), Resources.Backup_FailedHeader, MessageType.Error); return(false); } // Check if the backup should be compressed bool compress = Data.Backup_CompressBackups; Logger.Debug(compress ? $"The backup will be compressed" : $"The backup will not be compressed"); // Perform the backup and keep track if it succeeded bool success = await(compress ? PerformCompressedBackupAsync(backupInformation.BackupDirectories, backupInformation.CompressedBackupLocation, backupInformation.GameDisplayName) : PerformBackupAsync(backupInformation.BackupDirectories, backupInformation.BackupLocation, backupInformation.GameDisplayName)); if (!success) { return(false); } FileSystemPath newBackup = compress ? backupInformation.CompressedBackupLocation : backupInformation.BackupLocation; // Remove old backups for the game RemoveOldBackups(newBackup, backupInformation.ExistingBackups !); return(true); } catch (Exception ex) { // Handle error Logger.Error(ex, "Backing up game"); // Display message to user await Message.DisplayExceptionMessageAsync(ex, String.Format(Resources.Backup_Failed, backupInformation.GameDisplayName), Resources.Backup_FailedHeader); // Return that backup did not succeed return(false); } } }