Skip to content

# Quake PAK was implemented as SEA ARC - #199

Open
Hawkynt wants to merge 13 commits into
mainfrom
fix/quake-pak-real-wire-format
Open

# Quake PAK was implemented as SEA ARC#199
Hawkynt wants to merge 13 commits into
mainfrom
fix/quake-pak-real-wire-format

Conversation

@Hawkynt

@Hawkynt Hawkynt commented Sep 6, 2026

Copy link
Copy Markdown
Owner

The format was not Quake PAK

FileFormat.Pak identified itself as id Software's Quake PACK archive, but its reader, writer and modifier all delegated to SEA ARC. The tests agreed with the implementation rather than the format: they explicitly asserted ARC's 0x1A 0x00 end marker. As a result, files produced by this descriptor were not Quake PAK files and real PACK archives could not be read by it.

The released Quake source defines the format directly:

  • 12-byte header: "PACK", little-endian directory offset, little-endian directory length
  • each directory record is 64 bytes: char name[56], int filepos, int filelen
  • the original engine accepts at most 2048 records

What changed

  • replace the ARC-backed reader with a real PACK header/directory reader
  • replace the ARC-backed writer with canonical payloads + trailing directory + patched 12-byte header
  • remove the FileFormat.Arc project dependency entirely
  • add the missing PACK magic to the descriptor
  • correct listing, extraction, bounded entry opening and the layout map to use Quake offsets/sizes
  • pin the original engine's 2048-entry limit in reader, writer and modifier

Changed-byte editing

Canonical Quake PAK is also a much better random-access format than the old implementation implied. Its directory is already the trailer:

  • add writes new payload bytes where the old directory began, then appends a regenerated directory
  • same-name replace does the same and wipes the old payload when no surviving alias overlaps it
  • remove rewrites only the directory and wipes only unreferenced removed payload ranges
  • untouched payloads never move
  • non-trailing/non-canonical layouts reject before the first write and retain the verified rebuild fallback

So for canonical archives the cost is O(directory metadata + changed payload bytes), not O(total archive bytes).

Tests

  • an independently hand-built minimal PACK vector pins the real 12-byte header and 64-byte directory record; it does not use our writer
  • the writer is checked byte-for-byte for header, payload and directory geometry
  • 4 MiB untouched-member I/O-budget tests require add/replace/remove to stay below 128 KiB of archive reads/writes while preserving that member's physical offset and bytes
  • replacement/removal verify stale payload wiping
  • an aliased-payload test proves removing one directory entry does not wipe bytes still referenced by another
  • missing-name removal performs zero writes

Reference: id Software's released Quake WinQuake/common.c / QW/client/common.c definitions of dpackheader_t, dpackfile_t, and MAX_FILES_IN_PACK. No implementation code was copied; the repository implementation is written from the published structure definitions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant