Deficuet.APFS 0.0.1-alpha.4

This is a prerelease version of Deficuet.APFS.
dotnet add package Deficuet.APFS --version 0.0.1-alpha.4
                    
NuGet\Install-Package Deficuet.APFS -Version 0.0.1-alpha.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Deficuet.APFS" Version="0.0.1-alpha.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Deficuet.APFS" Version="0.0.1-alpha.4" />
                    
Directory.Packages.props
<PackageReference Include="Deficuet.APFS" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Deficuet.APFS --version 0.0.1-alpha.4
                    
#r "nuget: Deficuet.APFS, 0.0.1-alpha.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Deficuet.APFS@0.0.1-alpha.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Deficuet.APFS&version=0.0.1-alpha.4&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Deficuet.APFS&version=0.0.1-alpha.4&prerelease
                    
Install as a Cake Tool

APFS — AzurPromilia Package File System

A read-only virtual file system for game asset packages, built on ROVFS (and therefore WinFsp). APFS is the data manager implementation: it maps a JSON-describable asset tree onto two flat, case-insensitive dictionaries and serves reads from segmented package files.

Concepts

Type Role
SegmentedFileEntry One virtual file: PackageName, ResSubPath, PathType, plus Offset/Size of the segment inside its backing package file.
DirectoryData Population-phase (intermediate) directory node holding child files and directories in case-insensitive dictionaries.
DirectoryView Static (final) directory: natural-sort-ordered EntryInfo<string>[] plus a case-insensitive name→index map for O(1) marker lookup; Win32 wildcard matching is built in.
AzurPromiliaFileDataManager The ROVFS data manager: path resolution, directory contexts, backing file handles, and FileInfo assembly.

Population workflow

The manager follows a populate first, mount once, never modify lifecycle. Entry and directory objects are created outside this project; after population they are handed to the manager, and the tree can be serialized to / deserialized from JSON (also outside this project) for persistence.

  1. Create a root DirectoryData instance acting as the volume root.

  2. For every file call root.AddFile(path, entry). path is the file's full path relative to the node, /-separated, and never starts with a separator; missing intermediate directories are created implicitly (case-insensitively merged). The per-directory key inserts a _{PackResName}-{offset}-{size} suffix between the file name and its extension, so identically named segments from different packages stay unique:

    root.AddFile("characters/hero/hero.bin", new SegmentedFileEntry
    {
        PackageName = "pkg001",
        ResSubPath = "characters/hero/hero.bin",
        PackResName = "pkg001",
        PathType = PathType.Static,
        Offset = 0,
        Size = 1048576,
    });
    // dictionary key: "hero_pkg001-0-1048576.bin"
    
  3. Call manager.AddFileTree(root). This traverses the tree breadth-first, finalizes every node into a DirectoryView, assigns a unique IndexNumber to every file and directory (counter starts at 0, also persisted with the intermediate tree), accumulates TotalSize, and stores every node under its full path. Full paths are rooted at \ and use \ separators — this is exactly the form WinFsp passes to the file system, regardless of whether the volume is mounted on a drive letter or into an existing empty folder (the mount point prefix never reaches user mode).

  4. Mount. AddFileTree may be called exactly once; the internal maps must not be modified afterwards.

All lookups (TryResolveFile, TryResolveDirectory, TryCheckNodeExists) use StringComparer.OrdinalIgnoreCase dictionaries, matching the case-insensitive volume flag applied by ROVFS.

Backing files

PathType Physical location
Static {RootDirectoryPath}\StreamingAssets\.res\{PackageName}\{ResSubPath}
Cache {RootDirectoryPath}\.res\{PackageName}\BundleFiles\{ResSubPath}

RootDirectoryPath must not end with a separator. Handles are opened read-only with FILE_FLAG_OVERLAPPED, so multiple segments of the same package can be read concurrently through independent handles. A missing or unreadable backing file is logged and mapped to a Win32-derived NT status.

Mounting example

var manager = new AzurPromiliaFileDataManager(rootDirectoryPath, mountPoint)
{
    GlobalTimestamp = volumeCreationFileTime,
};
manager.PackageTimestampMap["pkg001"] = packageFileTime;
manager.AddFileTree(root); // populated tree from the steps above

var fileSystem = new ReadOnlyFileSystem<
    SegmentedFileEntry, DirectoryView, SafeFileHandle, string, string>(manager);
var host = new FileSystemHost(fileSystem);
host.Mount("X:"); // or host.Mount(@"C:\Some\Empty\Folder");

Dependencies

  • Deficuet.ROVFS 0.0.1-alpha.14
  • NaturalSort.Extension 4.4.1
  • Newtonsoft.Json 13.0.4 (used by the external serialization code, not by this library)
  • System.Text.Encoding.CodePages 8.0.0
  • WinFsp 2.1 must be installed on the machine.

Both libraries target netstandard2.0; Deficuet.ROVFS transitively brings in System.Memory, which Unity consumers must also ship.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.1-alpha.4 67 9/13/2026
0.0.1-alpha.3 69 9/13/2026
0.0.1-alpha.2 73 9/8/2026
0.0.1-alpha.1 71 9/8/2026