Skip to content

Commit

Permalink
Implement PackageFileResolver as an IFileResolver for VPK/PAK packages
Browse files Browse the repository at this point in the history
Resolves #33
  • Loading branch information
LogicAndTrick committed Oct 19, 2024
1 parent 32ad1f9 commit 0f71864
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 4 deletions.
91 changes: 91 additions & 0 deletions Sledge.Formats.Packages/FileSystem/PackageFileResolver.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// A file resolver for a package.
/// </summary>
public class PackageFileResolver : IFileResolver, IDisposable
{
private readonly bool _leaveOpen;
private readonly IPackage _package;

/// <summary>
/// Create an instance wrapping an <see cref="IPackage"/> instance.
/// </summary>
/// <param name="package">The <see cref="IPackage"/> instance</param>
/// <param name="leaveOpen">False to dispose the package when this instance is disposed, true to leave it undisposed</param>
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<string> 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<string> 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();
}
}
}
4 changes: 2 additions & 2 deletions Sledge.Formats.Packages/Sledge.Formats.Packages.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<PackageIcon>sledge-logo.png</PackageIcon>
<RepositoryUrl>https://github.com/LogicAndTrick/sledge-formats</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>Add standard Stream constructor to PakPackage and VpkPackage</PackageReleaseNotes>
<Version>1.0.2</Version>
<PackageReleaseNotes>Implement PackageFileResolver as an IFileResolver for VPK/PAK packages</PackageReleaseNotes>
<Version>1.1.0</Version>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
Expand Down
29 changes: 28 additions & 1 deletion Sledge.Formats.Tests/FileSystem/TestAllFileResolvers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -48,7 +50,8 @@ public static void Initialize(TestContext testContext)
{ "Disk", CreateDiskFileResolver() },
{ "ZipArchive", CreateZipArchiveResolver() },
{ "VirtualSubdirectory", CreateVirtualSubdirectoryFileResolver() },
{ "Composite", CreateCompositeFileResolver() }
{ "Composite", CreateCompositeFileResolver() },
{ "Package", CreatePackageResolver() }
};
}

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand All @@ -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];
Expand All @@ -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];
Expand All @@ -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];
Expand All @@ -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];
Expand All @@ -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];
Expand All @@ -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];
Expand All @@ -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];
Expand Down
1 change: 1 addition & 0 deletions Sledge.Formats.Tests/Sledge.Formats.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Sledge.Formats.Packages\Sledge.Formats.Packages.csproj" />
<ProjectReference Include="..\Sledge.Formats\Sledge.Formats.csproj" />
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion Sledge.Formats/FileSystem/ZipArchiveResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public ZipArchiveResolver(string filePath)
}

/// <summary>
/// Create an instance for a <see cref="ZipArchiveResolver"/>.
/// Create an instance for a <see cref="ZipArchive"/>.
/// </summary>
/// <param name="zip">The ZipArchive instance</param>
/// <param name="leaveOpen">False to dispose the archive when this instance is disposed, true to leave it undisposed</param>
Expand Down

0 comments on commit 0f71864

Please sign in to comment.