zpack bundles a directory tree into a single .zpak archive.
It preserves relative paths, streams file contents instead of buffering them,
and guarantees a byte-for-byte round trip.
zpack is both a CLI and a Zig library, with no dependencies beyond the standard
library.
Documentation • Quickstart • Install • CLI • Library • Format
zpack pack assets/ game.zpak # bundle a directory into an archive
zpack list game.zpak # show contents without extracting
zpack verify game.zpak # rehash every entry, no extraction
zpack unpack game.zpak extracted/ # extract an archive back to disk
zpack ids game.zpak > assets.zig # generate compile-checked asset handles
zpack manifest game.zpak # dump the index as ZONA full round trip:
$ zpack pack assets/ game.zpak
Packed 56 files (77.2 MiB -> 71.7 MiB, 7.1% smaller)
$ zpack verify game.zpak
Verified 56 entries
$ zpack unpack game.zpak extracted/
Extracted 56 files
$ diff -r assets/ extracted/ # no output: identicalEntries are sorted by path before writing, so packing the same tree twice produces the same bytes.
Files matching patterns in <input-dir>/.zpackignore are left out of the
archive - see the pattern reference.
Grab a binary for your platform from releases, or build from source:
zig build -Doptimize=ReleaseFastThe executable lands in zig-out/bin/zpack. zpack targets Zig 0.16.0 and
uses the new std.Io interfaces, so it will not build on 0.15 or earlier.
Full instructions, including package managers and PATH setup, are in
the install guide.
main.zig contains only argument handling. Packing and reading live in the
zpack module, so a game can depend on it without invoking the CLI.
zig fetch --save git+https://github.com/masonschafercodes/zpack#v0.0.2const zpack = @import("zpack");
var archive = try zpack.Archive.open(gpa, io, .cwd(), "game.zpak");
defer archive.deinit();
// Metadata lookup, no I/O. `entry.size` says how much room a read needs.
const entry = archive.find("textures/player.png") orelse return error.MissingAsset;
// Read into memory you already own. Nothing is allocated, and the bytes are
// checked against the stored hash.
var scratch: [32 * 1024]u8 = undefined;
const png = try archive.read(entry, &scratch);Four runnable examples live in examples/, each a single file that
builds its own archive first:
zig build example-01 # Pack a directory and read a file back
zig build example-02 # Load assets without allocating per asset
zig build example-03 # Look assets up by compile-time handle
zig build example-04 # Stream an entry instead of buffering itThe full API reference is in the docs.
Everything lives at the documentation site:
| Section | Covers |
|---|---|
| Start here | What zpack is, install, quickstart, concepts |
| CLI | Every command, .zpackignore, exit codes |
| Guides | Asset handles, build.zig integration, manifests, streaming, reproducibility |
| Library | pack, Archive, EntryReader, format, Ignore, errors, memory model |
| Format | The v1 byte layout, validation rules, security model, compatibility |
zig build test # every test, plus builds every example
zig fmt build.zig src tests examplesSee contributing.