ZArchiveSharp

API Reference

Complete API documentation for ZArchiveSharp. All types are in the ZArchiveSharp namespace unless otherwise noted.

Namespaces

Namespace Contents
ZArchiveSharp Archive reader/writer, format structures, tool
ZArchiveSharp.Zstd zstd encoder/decoder, compression options
ZArchiveSharp.Seekable Seekable zstd format (Foot + Head)
ZArchiveSharp.Pipeline Pipeline engine, batch operations, progress

ZArchiveWriter

Writes .zar archive files. Faithful port of zarchivewriter.cpp.

Constructors

// Stream-based (recommended)
public ZArchiveWriter(Stream output, IZarBlockCompressor? compressor = null)

// Callback-based (for advanced scenarios)
public ZArchiveWriter(
    Action<int> newOutputFile,
    Action<byte[], int, int> writeOutputData,
    IZarBlockCompressor? compressor = null)

Methods

StartNewFile

public void StartNewFile(string path)

Begins writing a new file entry. Path is relative to the archive root, using / or \ as separators.

Parameters:

Exceptions:

AppendData

public void AppendData(ReadOnlySpan<byte> data)
public void AppendData(byte[] data, int offset, int count)
public void AppendData(Stream input)

Appends data to the current file. Data is buffered and compressed in 64 KiB blocks. The Stream overload pumps the stream to end (64 KiB takes), so entry streams need no manual buffering; all three forms produce identical bytes.

Parameters:

Exceptions:

Finalize

public void Finalize()

Writes the archive footer and SHA-256 integrity hash. Must be called after all files are written.

Exceptions:

Dispose

public void Dispose()

Releases resources. Automatically calls Finalize() if not already done.

Example

using var output = File.Create("archive.zar");
using var writer = new ZArchiveWriter(output);

writer.StartNewFile("readme.txt");
writer.AppendData("Hello, World!"u8);

writer.StartNewFile("data/binary.dat");
writer.AppendData(binaryData);

writer.Finalize();

ZArchiveReader

Reads .zar archive files. Faithful port of zarchivereader.h.

Static Methods

TryOpen

public static ZArchiveReader? TryOpen(string path)
public static ZArchiveReader? TryOpen(Stream stream, bool leaveOpen = false)

Opens an archive. Returns null on invalid archives (never throws).

Parameters:

Returns: Reader instance, or null if invalid

Properties

Property Type Description
InvalidNode uint Constant 0xFFFFFFFF for path-not-found
Dictionary ZstdDictionary? Dictionary for dictionary-packed archives (null = plain; inert for plain blocks; dictionary blocks read without it fail, never mis-decode)

Methods

FileExists

public bool FileExists(string path)

Checks if a file exists at the given path.

DirectoryExists

public bool DirectoryExists(string path)

Checks if a directory exists at the given path.

ReadFile

public byte[] ReadFile(string path)

Reads and decompresses an entire file.

Parameters:

Returns: File contents

Exceptions:

ReadFileRange

public byte[] ReadFileRange(string path, long offset, long length)

Reads a range of bytes from a file.

Parameters:

Returns: Requested byte range

ReadDirectory

public IReadOnlyList<ZArchiveReader.DirEntry> ReadDirectory(string path)

Lists directory contents.

Parameters:

Returns: List of directory entries

GetName

public string GetName(uint nameIndex)

Retrieves a name by its index in the name table.

DirEntry Structure

public readonly struct DirEntry
{
    public string Name { get; }      // Entry name
    public bool IsFile { get; }      // True for files
    public bool IsDirectory { get; } // True for directories
    public ulong Size { get; }       // File size (0 for directories)
}

Thread Safety

The reader is thread-safe for concurrent reads (single lock, like the C++ mutex).

Example

using var reader = ZArchiveReader.TryOpen("archive.zar");
if (reader == null)
{
    Console.WriteLine("Invalid archive");
    return;
}

// List root directory
foreach (var entry in reader.ReadDirectory("/"))
{
    Console.WriteLine($"{entry.Name}: {(entry.IsFile ? $"{entry.Size} bytes" : "DIR")}");
}

// Read a file
byte[] data = reader.ReadFile("readme.txt");

ZArchiveTool

High-level pack/extract operations. Port of main.cpp CLI behavior.

Static Methods

Pack

public static void Pack(
    string inputDirectory,
    string? outputFile = null,
    Action<string>? progress = null,
    IZarBlockCompressor? compressor = null,
    bool deterministicOrder = true)

Packs a directory into a .zar file.

Parameters:

Exceptions:

Extract

public static void Extract(string inputFile, string outputDirectory)

Extracts an archive to a directory.

Parameters:

Exceptions:


ZarPipelineOptions (ZArchiveSharp.Pipeline)

Options for ZarPipeline pack/extract work (also honored by the zar CLI flags --level, --check, --dict).

Property Type Default Description
Level int 6 zstd level 1–22 for packing
Checksum bool false Write per-block content checksums
Dictionary ZstdDictionary? null Pack dictionary frames / extract them (never stored in the archive; inert for plain frames; ignored when Compressor is set)
CollisionPolicy ZarCollisionPolicy Fail What to do when the output path already exists
MaxDegreeOfParallelism int 4 Batch parallelism
DeterministicOrder bool true Sort entries ordinally before packing
NameOrder IReadOnlyList<string>? null Pre-seeded name-table order; null = pack order (first appearance). Set to a source-walk (discovery) order for byte-parity with packers that write names in discovery order

IZarBlockCompressor

Interface for custom block compressors.

public interface IZarBlockCompressor
{
    int Compress(ReadOnlySpan<byte> source, Span<byte> destination);
}

Returns: Compressed size, or -1 to store the block raw (uncompressed).

ZarRawCompressor

Built-in compressor that stores every block raw (no compression).

public sealed class ZarRawCompressor : IZarBlockCompressor

ZstdCompressionOptions (ZArchiveSharp.Zstd)

Options for the zstd compressor.

Properties

Property Type Default Description
Level int 6 Compression level (1–22)
ChecksumFlag bool false Write 4-byte XXH64 content checksum
Dictionary ZstdDictionary? null Dictionary history (null = plain frames)

Static Methods

public static ZstdCompressionOptions FromLevel(int level)

Creates options for the specified level (1–22).


ZstdCompressor (ZArchiveSharp.Zstd)

Pure-C# zstd encoder. Implements IZarBlockCompressor.

Constructor

public ZstdCompressor(ZstdCompressionOptions? options = null)

Properties

Property Type Description
Options ZstdCompressionOptions Active options

Methods

Compress

public int Compress(ReadOnlySpan<byte> source, Span<byte> destination)

Compresses source as a single-shot frame. Returns frame size, or -1 when the frame would not fit or would not be smaller.

CompressBlock

public byte[] CompressBlock(ReadOnlySpan<byte> source)

Compresses and returns the frame as a new byte array.

GetCompressBound

public static int GetCompressBound(int sourceSize)

Returns the maximum possible compressed size for a given input size.

DecompressFrame

public static byte[] DecompressFrame(ReadOnlySpan<byte> src, int maxSize)

Decompresses a zstd frame.

Parameters:

Returns: Decompressed data


ZstdCompressionStream (ZArchiveSharp.Zstd)

Write-only zstd compression stream. Buffers everything written and emits one logical frame with an unknown-size header on Dispose() — byte-identical to encoding the concatenated input in one shot. Flush() emits the 6-byte frame header once payload exists. Not seekable; async is thin-over-sync.

public ZstdCompressionStream(Stream destination, int level = 6, bool checksum = false, bool leaveOpen = false)
public ZstdCompressionStream(Stream destination, ZstdCompressionOptions options, bool leaveOpen = false)

Exceptions:

Example

using var dest = File.Create("data.zst");
using (var enc = new ZstdCompressionStream(dest, level: 6, checksum: true))
{
    await source.CopyToAsync(enc);
} // frame finalized here

---

## ZstdDecompressionStream (ZArchiveSharp.Zstd)

Read-only zstd decompression stream over concatenated frames (skippable frames
skipped). Decodes incrementally through the shared block path with
`ZstdDecoderOptions` caps enforced per frame. Not seekable; async is
thin-over-sync.

```csharp
public ZstdDecompressionStream(Stream compressed, bool leaveOpen = false)
public ZstdDecompressionStream(Stream compressed, ZstdDecoderOptions options, bool leaveOpen = false)

Exceptions:

Example

using var src = File.OpenRead("data.zst");
using var dec = new ZstdDecompressionStream(src);
using var outMs = new MemoryStream();
await dec.CopyToAsync(outMs);

ZstdDictionary (ZArchiveSharp.Zstd)

Immutable, thread-safe reusable zstd dictionary (use only; training is out of scope). A supplied dictionary is always active per frame — history plus, for formatted dictionaries, initial tables; frames carrying a dictionary ID require it to match DictId.

public static ZstdDictionary FromBytes(byte[] dict) // auto-detect formatted vs raw
public static ZstdDictionary FromRawPrefix(ReadOnlySpan<byte> prefix, uint dictId = 0)
Property Type Description
DictId int Dictionary ID (0 = no ID field; compared as a 32-bit pattern)
IsFormatted bool True when loaded from magic-headed bytes
ContentSize int History content size in bytes

Dictionary-aware entry points (all additive; null = today’s behavior):

// ZstdCompressionOptions
public ZstdDictionary? Dictionary { get; init; }

// ZstdCompressor
public byte[] CompressBlock(ReadOnlySpan<byte> source, ZstdDictionary? dict);

// ZstdDecompressor
public static byte[] Decompress(byte[] src, ZstdDictionary? dict);
public static byte[] Decompress(byte[] src, int offset, int length, ZstdDictionary? dict);
public static byte[] Decompress(byte[] src, int offset, int length, ZstdDictionary? dict, ZstdDecoderOptions options);

// ZstdCompressionStream: options may carry Dictionary (header deferred to Dispose)
// ZstdDecompressionStream
public ZstdDecompressionStream(Stream compressed, ZstdDecoderOptions options, ZstdDictionary? dict, bool leaveOpen = false);

Example

var dict = ZstdDictionary.FromBytes(File.ReadAllBytes("words.dict"));
var options = new ZstdCompressionOptions { Level = 6, Dictionary = dict };
byte[] frame = new ZstdCompressor(options).CompressBlock(data);
byte[] back = ZstdDecompressor.Decompress(frame, dict);

ZstdCli (ZArchiveSharp.Pipeline)

Callable form of the zar zstd contract (single zstd streams, not archives). Failures map onto the ZarchiveCli exit-code table (no new codes); cancellation propagates OperationCanceledException.

public static bool TryParse(string[] args, out ZstdJob? job, out string? error,
    int defaultLevel = 6, string? defaultDictPath = null,
    bool defaultChecksum = false, bool defaultQuiet = false, bool defaultStdout = false);
public static Task<int> RunAsync(ZstdJob job, Stream stdin, Stream stdout,
    Action<string>? log, Action<string> error, CancellationToken ct = default);

TryParse takes the tokens after zstd (-c/--compress, -d/--decompress with exactly one required, -l/--level, --dict, --stdout, --check / --no-check, -q/--quiet, -h/--help) and never throws. RunAsync opens file paths (null = the given stdin/stdout streams, flushed but never closed), deletes a created file output when the run fails, and sends failures to error.


SeekableCli (ZArchiveSharp.Pipeline)

Callable form of the zar seekable contract (seekable zstd files: compress, decompress with byte/frame slicing, and seek-table listing). Same conventions as ZstdCli (exit-code reuse, cancellation, partial-output cleanup).

public static bool TryParse(string[] args, out SeekableJob? job, out string? error,
    int defaultLevel = 3, bool? defaultChecksum = null,
    bool defaultQuiet = false, bool defaultStdout = false);
public static Task<int> RunAsync(SeekableJob job, Stream stdin, Stream stdout,
    Action<string>? log, Action<string> error, CancellationToken ct = default);
public static bool TryParseByteSize(string? value, out ulong size, out string? error);

TryParse takes the tokens after seekable, verb first (compress|c, decompress|d, list|l): compress takes -l/--level (1–22, default 3), -s/--frame-size (TryParseByteSize syntax, default 2M, capped at 1G), --frame-size-policy, --checksum/--no-checksum (default on), --seek-table-file; decompress takes --from/--to (end), --from-frame/--to-frame (last), --seek-table-file; list takes --from-frame/--to-frame/--num-frames, -d/--detail, --seek-table-format foot|head. Compress/decompress share -f/--force, -c/--stdout, -q/--quiet (ignored by list). The verb validates its own options, so a misplaced flag errors instead of being ignored. Compress with a file input and no output path derives <input>.zst; decompress defaults to stdout.


SevenZip (ZArchiveSharp.Pipeline)

Archive-container stage: finds an external 7z binary and extracts .zip/.rar/.7z/.tar/.gz through ProcessRunner (ZarManager stage-1 port; 7z stays external by design).

public static string? FindTool(string? preferredPath = null,
    IEnumerable<string>? searchDirectories = null, bool probeWellKnownLocations = true);
public static void Extract(string archivePath, string destDir, string? toolPath = null,
    IProgress<double>? progress = null, PauseToken pause = default,
    CancellationToken cancellationToken = default);
public static string? PickIsoCandidate(IEnumerable<string> extractedFiles);

FindTool checks the explicit path, then the standard Windows install location, then searchDirectories (default: PATH) for 7z/7zz. Extract runs x "archive" -o"dest" -y -bsp1 with full paths preserved (the oracle’s flat e would mangle directory trees). PickIsoCandidate returns the first .iso in ordinal order (extension case-insensitive) or null — when several ISOs are present the rest are ignored, like the oracle.


ZstdStrategy (ZArchiveSharp.Zstd)

Compression strategy selector. Maps to libzstd’s ZSTD_strategy.

Value Name Typical Levels
1 Fast 1
2 DoubleFast 2–3
3 Greedy 4–5
4 Lazy 6–7
5 Lazy2 8–9
6 BtLazy2 10–12
7 BtOpt 13–15
8 BtUltra 16–18
9 BtUltra2 19–22

SeekableWriter (ZArchiveSharp.Seekable)

Writes seekable zstd files with Foot/Head seek tables.

Constructor

public SeekableWriter(SeekableOptions? options = null)

Properties

Property Type Description
SeekTable SeekTable Current seek table (frames logged so far)

Methods

Write

public void Write(ReadOnlySpan<byte> data)
public void Write(Stream input)

Appends data, emitting full frames as needed. The Stream overload pumps in 128 KiB takes, so regular files frame exactly like one span write and like the oracle CLI; short-read streams can shift Compressed-policy boundaries (like odd oracle reads would) while Uncompressed boundaries never move — every framing decodes identically.

Finish

public byte[] Finish()

Finalizes and returns the complete seekable file bytes.

FinishHead

public byte[] FinishHead()

Returns just the seek table as a standalone Head frame.


SeekableReader (ZArchiveSharp.Seekable)

Reads seekable zstd files.

Constructors

// Parse embedded Foot table
public SeekableReader(byte[] data)

// Use external seek table (e.g., standalone Head)
public SeekableReader(byte[] data, SeekTable table)

// Stream-backed: parses the Foot from the tail, reads frames on demand
// (multi-GB files never sit fully in memory; stream not owned)
public SeekableReader(Stream stream)
public SeekableReader(Stream stream, SeekTable table)

The stream must be readable and seekable and stay open for the reader’s lifetime; decode results are identical to the byte-array constructors.

Properties

Property Type Description
Table SeekTable Parsed seek table
DecompressedLength long Total decompressed size
FrameCount int Number of frames

Methods

DecompressAll

public byte[] DecompressAll()

Decompresses the entire payload.

DecompressRange

public byte[] DecompressRange(long offset, long length)

Decompresses a byte range, decoding only the frames the range touches.


Error Handling

Exception Types

Exception Namespace When Thrown
ZarArchiveOpenException ZArchiveSharp.Pipeline Archive fails to open
ZarInputOpenException ZArchiveSharp.Pipeline Input file cannot be opened
ZarEntryCreateException ZArchiveSharp.Pipeline Archive entry creation fails
ZstdException ZArchiveSharp.Zstd zstd decompression error
IOException System I/O errors
InvalidOperationException System Invalid state (corrupt archive, etc.)

Handling Corrupt Archives

try
{
    ZArchiveTool.Extract("corrupt.zar", "output");
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"Corrupt archive: {ex.Message}");
}
catch (IOException ex)
{
    Console.WriteLine($"I/O error: {ex.Message}");
}

Null-Safe Reader Pattern

// TryOpen never throws — returns null on invalid archives
using var reader = ZArchiveReader.TryOpen("maybe-valid.zar");
if (reader == null)
{
    Console.WriteLine("Invalid or corrupt archive");
    return;
}