예제 #1
0
        /// <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) !);
        }