havoc-remaster/docs/FORMATS.md
pyr0ball 198977ed2b feat: crack GRAFIX0N.DAT texture format + extractor
WORLDS/GRAFIX0N.DAT is a big-endian (Mac-origin) tileset: an 8-byte header
(file size at 0x08, entry count at 0x0c), 16-byte entry records (BE offset +
id), and per-graphic blocks with a BE width/height/frames/id header followed by
w*h*frames 8-bit palette indices. Confirmed against the in-game loader at
0x444db0 (16-byte record stride, BE->LE dword swap).

GRAFIX00.DAT decodes to 29 graphics: explosion (id 901, 8 frames), smoke
(id 911, 8 frames), and 27 single-frame 64x64 structure/wall textures
(ids 8001-8028). tools/grafix_extract.py renders them to PNG with any
768-byte RGB palette (the XPAL palettes from INTRFACE.FF work).

- tools/grafix_extract.py: parser + PNG renderer
- docs/FORMATS.md: GRAFIX0N.DAT spec
- README: status update

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TN4Ytn3gdWRonNmHpisWQv
2026-07-03 09:36:41 -07:00

7.9 KiB
Raw Permalink Blame History

Havoc file formats (reverse-engineered)

All offsets/sizes verified against the retail data set (Reality Bytes, Inc., 1995).

Binaries

File Type Notes
HAVOC.EXE PE32 i386, GUI Imports DDRAW, DSOUND, WINMM, WSOCK32, GDI32, USER32, KERNEL32. No Direct3D — 3D is a software rasterizer drawing into a DirectDraw surface. Contains a CD-presence check ("...Extra Player CD...").
LAUNCHER.EXE PE32 i386, GUI Front-end menu (see havoc_9 screenshot).
SETUP.EXE PE32 i386, GUI Config/setup.
DIRECTX/ DX5-era Ships 16-bit DSETUP16.DLL/DDRAW16.DLL — the 16-bit installer path is what breaks on 64-bit Windows, not the game itself.

.FF — "FlashFile" archive implemented in tools/ff_extract.py

Used by MUSIC.FF, SOUND.FF, OBJECTS.FF, INTRFACE.FF. Built by the bundled DOS tool FFCREATE.EXE (found inside MUSIC.FF/SOUND.FF).

uint32  file_count                      (little-endian)
repeat file_count:
    uint32  absolute_offset             (little-endian, from start of .FF)
    char[]  name                        (NUL-terminated, variable length)
<concatenated payloads>                 entry size = next_offset - this_offset

Notes:

  • The table may contain a trailing empty-name entry — skip it.
  • Entry payloads are raw and self-describing (e.g. RIFF/WAVE, MZ, bitmaps).

Archive contents

Archive Count Entry kinds
MUSIC.FF 116 WAVE<id>.DAT PCM segments, PLST<id>.DAT playlists, FFCREATE.EXE
SOUND.FF 65 WAVE<id>.DAT sound effects, FFCREATE.EXE
OBJECTS.FF 526 ASND<id>.DAT (enemy/pickup models + object sounds — format TBD)
INTRFACE.FF 547 BitB<id>.dat headers, BitR<id>.dat rasters, XPAL<id>.dat palettes, STR#<id>.dat string tables, RTbl<id>.dat remap tables, xpXl<id>.dat palette variants

Music: WAVE segments + PLST playlists

  • WAVE<id>.DAT — standard RIFF/WAVE, PCM mono, 22050 Hz, 8-bit.
  • PLST<id>.DAT — a song = an ordered sequence of WAVE segments. Payload is big-endian (note: the .FF table around it is little-endian — mixed by design):
uint16  segment_count        (big-endian; how many WAVE segments belong to this group)
uint16  step_count           (big-endian)
uint16[step_count]  sequence  (big-endian; 1-based segment index)

Segment resolution: a playlist named PLST<id> has base id = int(id, 16); step value N maps to wave id base + N - 1 (e.g. PLST07D1 step 2 → WAVE07D2). Concatenating the referenced segments' PCM reproduces the song exactly (verified by ear).

INTRFACE.FF bitmap system implemented in tools/ff_extract.py

All entries share a 4-digit hex ID that links related assets (e.g. 797C = main menu): BitB797C.dat (dimensions) + BitR797C.dat (pixels) + xpal797c.dat (palette) + STR#797C.dat (strings).

BitB<id>.dat — bitmap header (4 bytes)

uint16  width   (big-endian)
uint16  height  (big-endian)

BitR<id>.dat — raw bitmap raster

Raw 8-bit palette-indexed pixels, row-major, width * height bytes total. No header.

XPAL<id>.dat / xpal<id>.dat — palette (768 bytes)

256 RGB triplets, 8-bit per channel (NOT 6-bit VGA). One palette per screen/sprite group.

Critical runtime patch: the game engine always forces palette index 2 to (0, 0, 0) black at runtime. The stored XPAL files may contain any value there (often #e78484 salmon from the development environment). Any renderer must override index 2 to black after loading. Index 0 and 1 are the transparency/key color (#ff00ff magenta in most palettes).

Discovery method: comparing XPAL7A12 (correct black background) vs XPAL7A1C (wrong salmon background) revealed only 3 differing entries out of 256 -- indices 0, 1, and 2.

STR#<id>.dat — string table

Length-prefixed strings for the screen's UI labels. First byte = string length, then UTF-8 text. Example: STR#797C contains all main menu button labels and the copyright notice.

Two special-purpose string tables live in INTRFACE.FF at fixed IDs:

ID Content
7D02 Pickup banner text -- all item and weapon pickup notification strings
7D03 HUD active weapon name display strings

See docs/WEAPONS.md for the complete weapon list derived from these tables.

Identified screens (640x480 full-screen bitmaps)

ID Content (from STR# strings)
797C Main menu
79E0 Network options
79AE Load/save game
7A44 Settings menu
7A4E Sound/music settings
7A58 Graphics settings
7A62 Keyboard settings
7A6C Joystick settings
7ADA Info/help
7A12 Network client/server status
79F4 Network client options
79FE Network server options

Identified sprites

Size Count Content
640x480 32 Full-screen menu/UI backgrounds
512x236, 512x230, 512x218 3 Cockpit windshield frames (color variants)
428x196 12 Vehicle selection screens with 3D-rendered thumbnails
104x432 3 Cockpit side panel strips
420x46, 228x46 29 Button/UI strip elements
48x48 57 Vehicle HUD icons (monochrome green, multiple vehicles/angles)
18x18 13 Small UI icons

World data (WORLDS/) — TODO (3D terrain)

Per-level sets <NN> = 01..06. Six worlds confirmed from EXE string table + player memory:

File prefix World name Biome
LAND01xx Badlands Desert
LAND02xx Fallout Ice/Snow
LAND03xx Tyrak Alien
LAND04xx Wasteland Desert (harder)
LAND05xx Malterra Ice (harder)
LAND06xx Overlord Alien (harder)

Level suffix codes within each world: 01=Level 1, 02=Level 2, 03=Level 3, 04=Boss, 0B=Bonus. Network variants use 0N, 1N, 2N prefixes for the three biomes at Easy/Medium/Hard.

Music track ID is stored at offset 18 as big-endian uint16 in each LAND*.DAT file.

Pattern Guess Status
MAP<lvl>.MAP tile/heightmap grid, 1 byte per cell layout confirmed (byte grid); dims TBD
LAND<lvl>.DAT terrain geometry/tile defs TBD
GRAFIX0N.DAT 64x64 texture tilesets cracked, see below (tools/grafix_extract.py)
STUF<lvl>.DAT entity placement (contains Item records) TBD

GRAFIX0N.DAT — texture tileset implemented in tools/grafix_extract.py

Big-endian (Mac-origin data, byte-swapped by the in-game loader at VA 0x444db0). GRAFIX00.DAT = 29 graphics: an explosion sprite (id 901, 8 frames), a smoke sprite (id 911, 8 frames), and 27 single-frame 64x64 structure/wall textures (ids 8001-8028: metal panels, hazard stripes, warning bars, arrows, circuit patterns).

Header:
  0x00  8 bytes  reserved (0)
  0x08  u32 BE   total file size
  0x0c  u16 BE   entry count N
  0x20  N*16     entry records
Entry record (16 bytes):
  0x00  u32 BE   absolute file offset of the graphic block
  0x04  u16 BE   id
  0x06  10 bytes reserved
Graphic block (at record offset):
  0x00  u16 BE   width           (64)
  0x02  u16 BE   height          (64)
  0x04  u16 BE   frame count
  0x06  u16 BE   id
  0x08  8 bytes  reserved
  0x10  w*h*frames bytes         8-bit palette indices

Pixels are 8-bit indices into a 256-colour palette (768-byte RGB, e.g. the XPAL palettes in INTRFACE.FF). Index 1 (magenta ff 00 ff) is the transparency key. The exact world palette per tileset is still TBD; UI XPAL0000 renders them plausibly.

python tools/grafix_extract.py WORLDS/GRAFIX00.DAT -p tools/out_intrface/raw/XPAL0000.DAT

Other top-level data — TODO

File Notes
BIGFILE.DAT (64 MB) starts Copyright 1995, Reality Bytes, Inc. then embedded RIFF data — bulk audio/stream pool
STRDATA.DAT length-prefixed UI string table (Out of memory., Not enough disk space., …)
HAVOC.ICO 32×32 4-bit icon