public override async Task ExtractSave(IIndelibleDirectory outputDirectory) { await foreach (var _ in outputDirectory.CopyFromDirectory(this.ContentDirectory, true)) { } ; }
public async override Task <ISaveGame> CreateSave(IIndelibleDirectory saveContents) { var oldLatestFile = this.ProfileRoot.OpenFile("latest"); // todo decide on some way to organize these saves. var newGuid = Guid.NewGuid(); var saveName = $"{DateTimeOffset.UtcNow.ToString(DateFormat)}-{newGuid}"; var saveDirectory = this.ProfileRoot.OpenDirectory(saveName); var contentDirectory = saveDirectory.OpenDirectory("content"); // Copy it twice, once for backup, once for head. await foreach (var _ in contentDirectory.CopyFromDirectory(saveContents, true)) { } ; if (oldLatestFile.Created) { var oldLatestName = oldLatestFile.ReadAllText(); var oldLatest = this.ProfileRoot.OpenDirectory(oldLatestName); // Delete the old head oldLatest.Delete(); } this.ProfileRoot.OpenFile("latest").WriteAllText(saveName, Encoding.UTF8); return(this.GetSave(saveDirectory)); }
public SaveProfile(Guid profileGuid, string saveType, string profileName, IIndelibleDirectory profileRoot) { this.Guid = profileGuid; this.ProfileName = profileName; this.ProfileRoot = profileRoot; this.SaveType = saveType; }
public CopyingSaveGame(DateTimeOffset createdTime, Guid saveGuid, string saveType, IIndelibleDirectory contentDirectory) : base(createdTime, saveGuid, saveType) { this.ContentDirectory = contentDirectory; }
private async Task CopyRecursiveNonDiffs(IIndelibleDirectory outputDirectory) { var baseDir = this.DiffDir.OpenDirectory("copy"); await foreach (var _ in outputDirectory.CopyFromDirectory(baseDir, true)) { } ; }
private async Task CreateBaseSave(IIndelibleDirectory saveContents) { var contentDirectory = this.ProfileRoot.OpenDirectory("base/content"); await foreach (var _ in contentDirectory.CopyFromDirectory(saveContents)) { } ; }
public ReplaceStrategySaveProfile(Guid profileGuid, string saveType, string profileName, IIndelibleDirectory profileRoot) : base(profileGuid, saveType, profileName, profileRoot) { var saveManifest = this.ProfileRoot.OpenFile("profile"); if (!saveManifest.Created) { saveManifest.WriteAllText($"{profileName}{Environment.NewLine}{nameof(SaveManagementStrategy.Replace)}"); } }
public async IAsyncEnumerable <IFileRecord> GetFileRecordsAsync(IIndelibleDirectory directoryRoot) { await using var context = new DatabaseContext(this.Options.Options); var files = directoryRoot.EnumerateFilesRecursive().ToList(); var records = context.FileRecords .Include(r => r.Metadata) .Where(r => files.Select(f => f.FileGuid).Contains(r.RecordID)); foreach (var r in records) { yield return(new FileRecord(files.First(f => f.FileGuid == r.RecordID), r.MimeType, r.Metadata.AsMetadataCollection(r.RecordID))); } }
public async override Task <ISaveGame> CreateSave(IIndelibleDirectory saveContents) { // todo decide on some way to organize these saves. var newGuid = Guid.NewGuid(); var saveName = $"{DateTimeOffset.UtcNow.ToString(DateFormat)}-{newGuid}"; var saveDirectory = this.ProfileRoot.OpenDirectory(saveName); var contentDirectory = saveDirectory.OpenDirectory("content"); await foreach (var _ in contentDirectory.CopyFromDirectory(saveContents, true)) { } ; this.ProfileRoot.OpenFile("latest").WriteAllText(saveName, Encoding.UTF8); return(this.GetSave(saveDirectory) !); }
/// <summary> /// Copies all the files and directories from the source directory recursively into this directory. /// </summary> /// <param name="this">The destination directory.</param> /// <param name="source">The source directory.</param> /// <param name="overwrite">Whether or not to overwrite existing files.</param> /// <returns>An enumerable of copied files.</returns> public static async IAsyncEnumerable <IFile> CopyFromDirectory(this IIndelibleDirectory @this, IIndelibleDirectory source, bool overwrite) { if (@this == source) { yield break; } // Do the parent directory foreach (var f in source.EnumerateFiles()) { yield return(await @this.CopyFromAsync(f, overwrite)); } var queuedDirs = source.EnumerateDirectories() .Select(d => (@this, d)).ToList(); // BFS over all the children. Queue <(IIndelibleDirectory, IDirectory)> dirsToProcess = new Queue <(IIndelibleDirectory, IDirectory)>(queuedDirs); while (dirsToProcess.Count > 0) { var(parent, src) = dirsToProcess.Dequeue(); var dst = parent.OpenDirectory(src.Name); foreach (var f in src.EnumerateFiles()) { yield return(await dst.CopyFromAsync(f, overwrite)); } var children = src.EnumerateDirectories() .Select(d => (dst, d)).ToList(); foreach (var childDirectory in children) { dirsToProcess.Enqueue(childDirectory); } } }
public async override Task <ISaveGame> CreateSave(IIndelibleDirectory saveContents) { if (!this.ProfileRoot.ContainsDirectory("base")) { await this.CreateBaseSave(saveContents); } using var rollingHash = new RollingHash(32); // setup var newGuid = Guid.NewGuid(); var saveName = $"{DateTimeOffset.UtcNow.ToString(DateFormat)}-{newGuid}"; var saveDirectory = this.ProfileRoot.OpenDirectory(saveName); var contentDirectory = saveDirectory.OpenDirectory("content"); // diff is for anything that exists in the base directory var diffDir = contentDirectory.OpenDirectory("diff"); // copy is for anything that does not and can not be diffed. var copyDir = contentDirectory.OpenDirectory("copy"); // Traverse base directory in tandem with saveContents var baseDir = this.ProfileRoot.OpenDirectory("base/content").AsReadOnly(); foreach (var f in saveContents.EnumerateFiles()) { if (!baseDir.ContainsFile(f.Name)) { await copyDir.CopyFromAsync(f); continue; } using var targetStream = f.OpenReadStream(); using var baseStream = baseDir.OpenFile(f.Name).OpenReadStream(); using var outStream = diffDir.OpenFile(f.Name).OpenStream(); using var decoder = new VcEncoder(baseStream, targetStream, outStream, rollingHash: rollingHash, blockSize: 32); VCDiffResult result = await decoder.EncodeAsync(); if (result != VCDiffResult.SUCCESS) { throw new IOException($"Failed to encode delta for {f.Name}"); } } foreach (var d in saveContents.EnumerateDirectories().Where(d => !baseDir.ContainsDirectory(d.Name))) { // Copy all directories not in the base. await foreach (var _ in copyDir.OpenDirectory(d.Name).CopyFromDirectory(d)) { } ; } var queuedDirs = (from targetDir in saveContents.EnumerateDirectories() where baseDir.ContainsDirectory(targetDir.Name) select(diffDir, baseDir.OpenDirectory(targetDir.Name), targetDir)).ToList(); Queue <(IDirectory parentDir, IReadOnlyDirectory baseDir, IDirectory targetDir)> dirsToProcess = new Queue <(IDirectory, IReadOnlyDirectory, IDirectory)>(queuedDirs); while (dirsToProcess.Count > 0) { var(parent, src, diff) = dirsToProcess.Dequeue(); var dst = parent.OpenDirectory(src.Name); foreach (var f in src.EnumerateFiles()) { if (!diff.ContainsFile(f.Name)) { continue; } using var baseStream = f.OpenReadStream(); using var targetStream = diff.OpenFile(f.Name).OpenReadStream(); using var outStream = dst.OpenFile(f.Name).OpenStream(); using var decoder = new VcEncoder(baseStream, targetStream, outStream, rollingHash: rollingHash, blockSize: 32); VCDiffResult result = await decoder.EncodeAsync(); if (result != VCDiffResult.SUCCESS) { throw new IOException($"Failed to decode delta for {f.Name}"); } } var children = from targetDir in diff.EnumerateDirectories() where src.ContainsDirectory(targetDir.Name) select(dst, src.OpenDirectory(targetDir.Name), targetDir); foreach (var childDirectory in children) { dirsToProcess.Enqueue(childDirectory); } } this.ProfileRoot.OpenFile("latest").WriteAllText(saveName, Encoding.UTF8); return(this.GetSave(saveDirectory) !); }
public override async Task ExtractSave(IIndelibleDirectory outputDirectory) { await CopyRecursiveDiffs(outputDirectory); await CopyRecursiveNonDiffs(outputDirectory); }
private ISaveGame?GetSave(IIndelibleDirectory internalSaveDir) { string name = internalSaveDir.Name; if (!DateTimeOffset.TryParseExact(name[0..DateFormat.Length], DateFormat, CultureInfo.InvariantCulture,
public override Task <ISaveGame> CreateSave(IIndelibleDirectory saveContents) { return(Task.FromResult <ISaveGame>(new EmptySaveGame(DateTimeOffset.UtcNow, Guid.NewGuid(), this.SaveType))); }
public DiffingSaveGame(DateTimeOffset createdTime, Guid saveGuid, string saveType, IIndelibleDirectory baseDir, IIndelibleDirectory diffDir) : base(createdTime, saveGuid, saveType) { this.BaseDir = baseDir; this.DiffDir = diffDir; }
private async Task CopyRecursiveDiffs(IIndelibleDirectory outputDirectory) { var diffDir = this.DiffDir.OpenDirectory("diff"); // Do the parent directory foreach (var f in this.BaseDir.EnumerateFiles()) { if (!diffDir.ContainsFile(f.Name)) { continue; } using var baseStream = f.OpenReadStream(); using var diffStream = diffDir.OpenFile(f.Name).OpenReadStream(); using var outStream = outputDirectory.OpenFile(f.Name).OpenStream(); using var decoder = new VcDecoder(baseStream, diffStream, outStream); (VCDiffResult result, var _) = await decoder.DecodeAsync(); if (result != VCDiffResult.SUCCESS) { throw new IOException($"Failed to decode delta for {f.Name}"); } } var queuedDirs = (from baseDir in this.BaseDir.EnumerateDirectories() where diffDir.ContainsDirectory(baseDir.Name) select(outputDirectory, baseDir, diffDir.OpenDirectory(baseDir.Name))).ToList(); // BFS over all the children. Queue <(IIndelibleDirectory parentDir, IDirectory baseDir, IDirectory diffDir)> dirsToProcess = new Queue <(IIndelibleDirectory, IDirectory, IDirectory)>(queuedDirs); while (dirsToProcess.Count > 0) { var(parent, src, diff) = dirsToProcess.Dequeue(); var dst = parent.OpenDirectory(src.Name); foreach (var f in src.EnumerateFiles()) { if (!diff.ContainsFile(f.Name)) { continue; } using var baseStream = f.OpenReadStream(); using var diffStream = diff.OpenFile(f.Name).OpenReadStream(); using var outStream = dst.OpenFile(f.Name).OpenStream(); using var decoder = new VcDecoder(baseStream, diffStream, outStream); (VCDiffResult result, var _) = await decoder.DecodeAsync(); if (result != VCDiffResult.SUCCESS) { throw new IOException($"Failed to decode delta for {f.Name}"); } } var children = from baseDir in src.EnumerateDirectories() where diff.ContainsDirectory(baseDir.Name) select(dst, baseDir, diff.OpenDirectory(baseDir.Name)); foreach (var childDirectory in children) { dirsToProcess.Enqueue(childDirectory); } } }
/// <summary> /// Describe a file copy with the given source file and the target directory. /// The resultant file will have the same file name as the source. /// </summary> /// <param name="source">The source file.</param> /// <param name="destinationDirectory">The target directory.</param> public CopyFileTask(TaskResult <FileInfo> source, IIndelibleDirectory destinationDirectory) { this.Source = source; this.Destination = destinationDirectory; }
public abstract Task ExtractSave(IIndelibleDirectory outputDirectory);
/// <summary> /// Copies all the files and directories from the source directory recursively into this directory. /// </summary> /// <param name="this">The destination directory.</param> /// <param name="source">The source directory.</param> /// <param name="overwrite"></param> /// <returns>An enumerable of copied files.</returns> public static IAsyncEnumerable <IFile> CopyFromDirectory(this IIndelibleDirectory @this, IIndelibleDirectory source) => CopyFromDirectory(@this, source, false);
public override Task ExtractSave(IIndelibleDirectory outputDirectory) { return(Task.CompletedTask); }
public abstract Task <ISaveGame> CreateSave(IIndelibleDirectory saveContents);
private ISaveGame GetSave(IIndelibleDirectory internalSaveDir) { string name = internalSaveDir.Name; var date = DateTimeOffset.ParseExact(name[0..DateFormat.Length], DateFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
public GameSaveManager(IIndelibleDirectory saveRoot) { this.SaveRoot = saveRoot; }