Format Structure
PAQ files consist of a 4-byte magic header followed by a sequence of entries:Binary Layout
Field Details
Magic Header
Value:"paq\0" (ASCII βpβ, βaβ, βqβ, NUL terminator)Hex:
70 61 71 00
Used to identify PAQ files and distinguish from other formats.
Entry Name
Type: C-string (NUL-terminated)Encoding: UTF-8
Content: Relative file path Examples:
- Original files use Windows-style backslashes (
\) - Paths are relative (no leading slash or drive letter)
- Decoders should normalize separators and reject
..traversal
Payload Size
Type:uint32_t (4 bytes)Endianness: Little-endian
Range: 0 to 4GB Specifies the exact byte length of the following payload.
Payload
Type: Raw bytesLength: Specified by size field The complete file contents, stored as-is with no compression or encryption.
Example Entry
Hex dump of a single entry:- Magic:
paq\0β - Name:
"game\ui\panel.png"(17 bytes including NUL) - Size:
0x0000042a= 1066 bytes - Payload: 1066 bytes of PNG image data
- Next entry starts at offset
0x01b + 1066 = 0x441
Python Decoder
The reference decoder is insrc/grim/paq.py:
Usage
Extract all entries:CLI Tool
The rewrite includes an extraction CLI:.jazfiles β.png(decoded with alpha)- Path separators normalized to
/ - Directory structure preserved
Known PAQ Files
crimson.paq
crimson.paq
Size: ~15 MB
Contents: Core game assets
Contents: Core game assets
game/- Textures and spritesgame/projs.png- Projectile atlasgame/bodies*.jaz- Creature spritesgame/blood*.jaz- Blood effectsgame/particles.png- Particle atlas
ui.paq
ui.paq
Size: ~2 MB
Contents: UI elements
Contents: UI elements
ui/panel*.png- Menu panelsui/buttons/*.png- Button graphicsui/icons/*.png- Weapon/perk icons
sounds.paq
sounds.paq
Size: ~8 MB
Contents: Sound effects (WAV)
Contents: Sound effects (WAV)
sounds/shoot_*.wav- Weapon soundssounds/hit_*.wav- Impact soundssounds/creature_*.wav- Enemy sounds
music.paq
music.paq
Size: ~30 MB
Contents: Music tracks (OGG Vorbis)
Contents: Music tracks (OGG Vorbis)
music/menu.ogg- Menu thememusic/gameplay_*.ogg- Combat tracks
Format Properties
Pros
- Simple to parse (no compression, no index)
- Sequential access is fast
- Easy to append new entries
- No external dependencies
Cons
- No compression (large files)
- No random access (must scan from start)
- No checksums or integrity verification
- No metadata (timestamps, permissions)
Security Considerations
Path Traversal
Risk: Malicious entry names like..\..\..\windows\system32\evil.dll could write outside the extraction directory.
Mitigation:
Size Bombs
Risk: Entry with size field0xffffffff (4GB) could exhaust memory.
Mitigation:
Comparison with Other Formats
| Feature | PAQ | ZIP | TAR |
|---|---|---|---|
| Compression | No | Yes | No (external) |
| Random access | No | Yes | No |
| Central directory | No | Yes | No |
| Metadata | No | Yes | Yes |
| Streaming | Yes | Partial | Yes |
| Complexity | Very low | High | Medium |
Related Formats
JAZ Textures
Compressed texture format inside PAQ archives
Config Files
Binary configuration blobs
Save Files
Obfuscated save game format
Implementation Notes
- The decoder uses
constructfor declarative binary parsing GreedyRangereads entries until EOF (no explicit count field)- Entry names are validated to prevent directory traversal
- The extractor automatically converts JAZ to PNG
References
- Source:
src/grim/paq.py - Tests:
tests/test_paq.py - CLI:
src/crimson/cli.py(extractcommand)