using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using Robust.Shared.ContentPack; using Robust.Shared.Utility; namespace Robust.Client.Replays.Loading; /// /// Simple interface that the replay system loads files from. /// [NotContentImplementable] public interface IReplayFileReader : IDisposable { /// /// Check whether a file exists in the replay data. /// /// The path to check. Doesn't need to be rooted. /// True if the file exists. bool Exists(ResPath path); /// /// Open a file in the replay data. /// /// The path to the file. Doesn't need to be rooted. /// A stream containing the file contents. /// Thrown if the file does not exist. Stream Open(ResPath path); /// /// Returns all files in the replay data. /// /// /// File paths are rooted. /// IEnumerable AllFiles { get; } } /// /// Replay file reader that loads files from the VFS (). /// public sealed class ReplayFileReaderResources : IReplayFileReader { private readonly IResourceManager _resourceManager; private readonly ResPath _prefix; /// The resource manager. /// The directory in the VFS that contains the replay files. Must be rooted. public ReplayFileReaderResources(IResourceManager resourceManager, ResPath prefix) { _resourceManager = resourceManager; _prefix = prefix; } public bool Exists(ResPath path) { return _resourceManager.ContentFileExists(GetPath(path)); } public Stream Open(ResPath path) { return _resourceManager.ContentFileRead(GetPath(path)); } public IEnumerable AllFiles { get { foreach (var path in _resourceManager.ContentFindRelativeFiles(_prefix)) { yield return path.ToRelativePath(); } } } private ResPath GetPath(ResPath path) => _prefix / path.ToRelativePath(); public void Dispose() { // Don't need to do anything. } } /// /// Replay file reader that loads files from a zip file. /// /// /// The zip archive is disposed when this instance is disposed. /// public sealed class ReplayFileReaderZip : IReplayFileReader { private readonly ZipArchive _archive; private readonly ResPath _prefix; /// The archive to read files from. /// The directory in the zip that contains the replay files. Must NOT be rooted. public ReplayFileReaderZip(ZipArchive archive, ResPath prefix) { _archive = archive; _prefix = prefix; } public bool Exists(ResPath path) { return GetEntry(path) != null; } public Stream Open(ResPath path) { var entry = GetEntry(path); if (entry == null) throw new FileNotFoundException(); return entry.Open(); } public IEnumerable AllFiles { get { foreach (var entry in _archive.Entries) { // Ignore directories. if (entry.FullName.EndsWith("/")) continue; var entryPath = new ResPath(entry.FullName); if (entryPath.TryRelativeTo(_prefix, out var path)) yield return path.Value.ToRootedPath(); } } } private ZipArchiveEntry? GetEntry(ResPath path) => _archive.GetEntry((_prefix / path.ToRelativePath()).ToString()); public void Dispose() { _archive.Dispose(); } }