diff --git a/Sledge.Formats.Packages/FileSystem/PackageFileResolver.cs b/Sledge.Formats.Packages/FileSystem/PackageFileResolver.cs new file mode 100644 index 0000000..d78a9da --- /dev/null +++ b/Sledge.Formats.Packages/FileSystem/PackageFileResolver.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Sledge.Formats.FileSystem; + +namespace Sledge.Formats.Packages.FileSystem +{ + /// + /// A file resolver for a package. + /// + public class PackageFileResolver : IFileResolver, IDisposable + { + private readonly bool _leaveOpen; + private readonly IPackage _package; + + /// + /// Create an instance wrapping an instance. + /// + /// The instance + /// False to dispose the package when this instance is disposed, true to leave it undisposed + public PackageFileResolver(IPackage package, bool leaveOpen = false) + { + _package = package; + _leaveOpen = leaveOpen; + } + + private string NormalisePath(string path) + { + return path.TrimStart('/'); + } + + public bool FolderExists(string path) + { + path = NormalisePath(path) + "/"; + if (path == "/") return true; + return _package.Entries.Any(x => x.Path.StartsWith(path)); + } + + public bool FileExists(string path) + { + path = NormalisePath(path); + return _package.Entries.Any(x => x.Path == path); + } + + public long FileSize(string path) + { + path = NormalisePath(path); + var entry = _package.Entries.FirstOrDefault(x => x.Path == path) ?? throw new FileNotFoundException(); + return entry.Size; + } + + public Stream OpenFile(string path) + { + path = NormalisePath(path); + var entry = _package.Entries.FirstOrDefault(x => x.Path == path) ?? throw new FileNotFoundException(); + return _package.Open(entry); + } + + public IEnumerable GetFiles(string path) + { + if (!FolderExists(path)) throw new DirectoryNotFoundException(); + path = NormalisePath(path) + "/"; + if (path == "/") path = ""; + + return _package.Entries.Where(x => x.Path != path && x.Path.StartsWith(path)) + .Select(x => x.Path.Substring(path.Length)) + .Where(x => !x.Contains('/')) + .Select(x => path + x); + } + + public IEnumerable GetFolders(string path) + { + if (!FolderExists(path)) throw new DirectoryNotFoundException(); + path = NormalisePath(path) + "/"; + if (path == "/") path = ""; + + return _package.Entries.Where(x => x.Path != path && x.Path.StartsWith(path)) + .Select(x => x.Path.Substring(path.Length)) + .Where(x => x.Contains('/')) + .Select(x => path + x.Split('/').First()) + .Distinct(); + } + + public void Dispose() + { + if (_leaveOpen) return; + _package?.Dispose(); + } + } +} diff --git a/Sledge.Formats.Packages/Sledge.Formats.Packages.csproj b/Sledge.Formats.Packages/Sledge.Formats.Packages.csproj index d96c814..c9ed772 100644 --- a/Sledge.Formats.Packages/Sledge.Formats.Packages.csproj +++ b/Sledge.Formats.Packages/Sledge.Formats.Packages.csproj @@ -11,8 +11,8 @@ sledge-logo.png https://github.com/LogicAndTrick/sledge-formats Git - Add standard Stream constructor to PakPackage and VpkPackage - 1.0.2 + Implement PackageFileResolver as an IFileResolver for VPK/PAK packages + 1.1.0 diff --git a/Sledge.Formats.Tests/FileSystem/TestAllFileResolvers.cs b/Sledge.Formats.Tests/FileSystem/TestAllFileResolvers.cs index bc58863..60f1eb8 100644 --- a/Sledge.Formats.Tests/FileSystem/TestAllFileResolvers.cs +++ b/Sledge.Formats.Tests/FileSystem/TestAllFileResolvers.cs @@ -6,6 +6,8 @@ using System.Text; using Sledge.Formats.FileSystem; using System.IO; +using Sledge.Formats.Packages; +using Sledge.Formats.Packages.FileSystem; namespace Sledge.Formats.Tests.FileSystem; @@ -48,7 +50,8 @@ public static void Initialize(TestContext testContext) { "Disk", CreateDiskFileResolver() }, { "ZipArchive", CreateZipArchiveResolver() }, { "VirtualSubdirectory", CreateVirtualSubdirectoryFileResolver() }, - { "Composite", CreateCompositeFileResolver() } + { "Composite", CreateCompositeFileResolver() }, + { "Package", CreatePackageResolver() } }; } @@ -121,6 +124,22 @@ private static IFileResolver CreateZipArchiveResolver() return resolver; } + private static IFileResolver CreatePackageResolver() + { + // since this library can't create pak files (yet???), I made this in PakScape manually + const string pakFile = "UEFDS5UAAABAAgAAZm9sZGVyMy9mMy50eHRmb2xkZXIyL2RhdGEyLmxvZ2ZvbGRlcjIvZGF0YTEubG9nZm9sZGVyL2RhdGEyLmxvZ2ZvbGRlci9kYXRhMS5sb2d0ZXN0Mi50eHR0ZXN0MS50eHRmb2xkZXI1L2ZvbGRlcjUuMS9hYWEudHh0Zm9sZGVyNC9mNC50eHRmb2xkZXIzL2YzLnR4dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAOAAAAZm9sZGVyMi9kYXRhMi5sb2cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAEQAAAGZvbGRlcjIvZGF0YTEubG9nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKwAAABEAAABmb2xkZXIvZGF0YTIubG9nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAQAAAAZm9sZGVyL2RhdGExLmxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMAAAAEAAAAHRlc3QyLnR4dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXAAAAAkAAAB0ZXN0MS50eHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGUAAAAJAAAAZm9sZGVyNS9mb2xkZXI1LjEvYWFhLnR4dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABuAAAAGQAAAGZvbGRlcjQvZjQudHh0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhwAAAA4AAAA="; + var ms = new MemoryStream(Convert.FromBase64String(pakFile)); + var package = new PakPackage(ms); + var resolver = new PackageFileResolver(package, true); + CleanupActions.Add(() => + { + resolver.Dispose(); + package.Dispose(); + ms.Dispose(); + }); + return resolver; + } + private static IFileResolver CreateCompositeFileResolver() { var ms = new MemoryStream(); @@ -183,6 +202,7 @@ void AddDisk(string filepath) [DataRow("ZipArchive")] [DataRow("VirtualSubdirectory")] [DataRow("Composite")] + [DataRow("Package")] public void TestRootDirectory(string implementationName) { // test that "" and "/" both take us to the root folder @@ -200,6 +220,7 @@ public void TestRootDirectory(string implementationName) [DataRow("ZipArchive")] [DataRow("VirtualSubdirectory")] [DataRow("Composite")] + [DataRow("Package")] public void TestFolderExists(string implementationName) { var resolver = _resolvers[implementationName]; @@ -222,6 +243,7 @@ public void TestFolderExists(string implementationName) [DataRow("ZipArchive")] [DataRow("VirtualSubdirectory")] [DataRow("Composite")] + [DataRow("Package")] public void TestFileExists(string implementationName) { var resolver = _resolvers[implementationName]; @@ -244,6 +266,7 @@ public void TestFileExists(string implementationName) [DataRow("ZipArchive")] [DataRow("VirtualSubdirectory")] [DataRow("Composite")] + [DataRow("Package")] public void TestOpenFile(string implementationName) { var resolver = _resolvers[implementationName]; @@ -260,6 +283,7 @@ public void TestOpenFile(string implementationName) [DataRow("ZipArchive")] [DataRow("VirtualSubdirectory")] [DataRow("Composite")] + [DataRow("Package")] public void TestFileSize(string implementationName) { var resolver = _resolvers[implementationName]; @@ -274,6 +298,7 @@ public void TestFileSize(string implementationName) [DataRow("ZipArchive")] [DataRow("VirtualSubdirectory")] [DataRow("Composite")] + [DataRow("Package")] public void TestFileNotFound(string implementationName) { var resolver = _resolvers[implementationName]; @@ -300,6 +325,7 @@ public void TestFileNotFound(string implementationName) [DataRow("ZipArchive")] [DataRow("VirtualSubdirectory")] [DataRow("Composite")] + [DataRow("Package")] public void TestGetFiles(string implementationName) { var resolver = _resolvers[implementationName]; @@ -316,6 +342,7 @@ public void TestGetFiles(string implementationName) [DataRow("ZipArchive")] [DataRow("VirtualSubdirectory")] [DataRow("Composite")] + [DataRow("Package")] public void TestGetFolders(string implementationName) { var resolver = _resolvers[implementationName]; diff --git a/Sledge.Formats.Tests/Sledge.Formats.Tests.csproj b/Sledge.Formats.Tests/Sledge.Formats.Tests.csproj index 6264a47..6686dc0 100644 --- a/Sledge.Formats.Tests/Sledge.Formats.Tests.csproj +++ b/Sledge.Formats.Tests/Sledge.Formats.Tests.csproj @@ -15,6 +15,7 @@ + diff --git a/Sledge.Formats/FileSystem/ZipArchiveResolver.cs b/Sledge.Formats/FileSystem/ZipArchiveResolver.cs index 05283bf..00c1851 100644 --- a/Sledge.Formats/FileSystem/ZipArchiveResolver.cs +++ b/Sledge.Formats/FileSystem/ZipArchiveResolver.cs @@ -25,7 +25,7 @@ public ZipArchiveResolver(string filePath) } /// - /// Create an instance for a . + /// Create an instance for a . /// /// The ZipArchive instance /// False to dispose the archive when this instance is disposed, true to leave it undisposed