Files

5.3 KiB

Read-only native PAK inventory

tools/pak_inventory.py validates the observed Enfusion PAK container structure, bounded decompression and SHA-256 of every indexed file without extracting payloads. It does not verify that an entry is a valid prefab, compiled script, resource identity, platform binary or loadable scenario. A successful report always keeps platform_or_resource_semantics_verified and release_qualified false.

python3 tools/pak_inventory.py /tmp/PRIVATE-SESSION/packages/core/data.pak \
  --output /tmp/NEW-pak-inventory.json
python3 tests/test_pak_inventory.py

The optional JSON report must not exist. Without it, JSON goes to stdout. Input is opened read-only, without following the final symlink; nonregular inputs such as FIFOs are refused without blocking. No extracted file or directory is created. Report hashes identify observed content, not publisher authenticity or authorship.

Observed format supported

The format was inspected from installed stable Tools build 24870687, and the successor's first actual private Core packing candidate. This is a strict observed-format implementation, not a claim of an official universal PAK specification.

  • FORM, a big-endian 32-bit length equal to the complete file size minus eight, then PAC1.
  • Exactly HEAD, DATA, FILE chunks, in that order. Each has a four-byte tag and big-endian 32-bit payload length; no guessed alignment padding or additional chunks.
  • A 28-byte HEAD with prefix 03 00 01 00 and four trailing zero bytes. The intervening twenty bytes are opaque and preserved as hexadecimal, not interpreted as identities, checksums or platform claims.
  • FILE begins with two zero bytes followed by a little-endian root child count. Tree nodes have an unsigned byte kind (0 directory, 1 file), unsigned byte UTF-8 component length and that name. A directory then has a little-endian 32-bit child count. A file then has a 24-byte descriptor: little-endian offset/stored size/expanded size, six observed zero bytes, compression flag/level, and four opaque trailing bytes. Those trailing bytes are not assumed to be CRCs.
  • Observed compression tuples (0,0) and (1,6) mean raw and zlib respectively. Other flags, levels, reserved fields or versions fail as unsupported. The parser requires exact decompressed size, native zlib checksum/end-of-stream and absence of trailing or concatenated compressed streams.
  • Every DATA byte must belong to exactly one indexed, contiguous file extent. Overlap, unindexed gaps, unindexed tails and deduplicated/sparse variants are refused rather than guessed.

Paths reject traversal, separators inside components, drive syntax, NUL/control bytes, empty/dot names, trailing dot/space, Windows reserved device names, noncanonical Unicode normalization and case-insensitive collisions. Files and directories share the same collision namespace.

Bounds

Default limits are 4 GiB archive, 32 MiB FILE metadata, 250,000 total tree nodes, depth 64, 1 GiB expanded data per file, 8 GiB aggregate expanded data and a 1024:1 expansion ratio. Metadata is validated before payload decompression. Expanded bytes are streamed in chunks of at most 1 MiB and never written to disk; malicious streams exceeding a smaller declared size are detected after at most that declared size plus one byte. The Python API exposes explicit positive Limits for callers needing smaller budgets. Directory depth cannot be raised above 128. The CLI deliberately uses the reviewed defaults.

The file's size and nanosecond modification/change times are checked before and after inspection. This detects ordinary concurrent modification, not an adversarial modification/reversion with forged timestamps. Unknown format variants, malformed metadata, checksum errors, size overflow/bounds violations and excessive expansion fail closed.

Actual read-only observations

The installed Workbench addons/core/data.pak was read and hashed: 1,064 files, 1,192 total tree nodes, 25,423,457 expanded bytes; archive SHA-256 a2f2aee2b1043ebee24995581fc53fb5f3713195fda432e6c2b4c5acdcc64ea4. Its payload was not extracted or copied into this repository. Installed game addons/data/data.pak and Workbench shaders-dx12.pak were inspected for structural metadata only; those observations also used HEAD 03000100 and the same descriptor flag pairs.

The first successor Core candidate at /tmp/erri-native-package-core-20260919-1046/packages/core/data.pak passes this structural/content-hash inventory: 107 files, 122 total tree nodes, 605,769 expanded bytes; archive SHA-256 a071b5a7298543734bb72c72bb87c0dbfc447b3a176870299abd0a3def4fe623. Inventory report: /tmp/erri-core-first-pak-inventory.json. That build emitted 24 native UI resource-leak errors and its original runner remained waiting on the display wrapper. This archive inventory does not clear those errors or qualify that packaging run. Native packed-only loading is separate.

Eleven focused tests use clearly synthetic archives: raw/compressed nested content, truncation at every byte, header/chunk/version errors, unsafe/colliding paths, descriptor flags/bounds/overlaps/gaps, zlib checksum/truncation/trailing streams, large streamed output, metadata and expansion limits, underspecified decompression bombs, FIFO/symlink refusal, no-overwrite JSON behavior and concurrent input mutation. They invoke no engine and fabricate no real build measurements.