BitB/BitR/XPAL/STR# system fully reverse-engineered: - BitB<id>.dat: big-endian uint16 width/height header - BitR<id>.dat: raw 8-bit palette-indexed pixels (no header) - XPAL<id>.dat: 256 RGB triplets, 8-bit per channel - STR#<id>.dat: length-prefixed UI string tables - Runtime patch: index 2 must be forced to black (0,0,0) -- the game engine overwrites this at runtime but stored values vary Discovery: XPAL7A12 (correct render) vs XPAL7A1C (salmon background) differ in only 3 palette entries (0,1,2); index 2 is the canvas black. All 32 full-screen menu backgrounds, 57 vehicle HUD icons, cockpit windshield frames, vehicle selection screens, and button strips now render correctly. Documented in docs/FORMATS.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.7 KiB
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— standardRIFF/WAVE, PCM mono, 22050 Hz, 8-bit.PLST<id>.DAT— a song = an ordered sequence of WAVE segments. Payload is big-endian (note: the.FFtable 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.
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 plus N/N2 variants (biomes: desert, snow/ice, lava, ocean).
| 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 |
terrain/texture tilesets | TBD |
STUF<lvl>.DAT |
entity placement (contains Item records) |
TBD |
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 |