havoc-remaster/docs/FORMATS.md
pyr0ball acd4057e72 feat: crack INTRFACE.FF bitmap format + render all UI assets
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>
2026-06-29 00:25:08 -07:00

5.7 KiB
Raw 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.

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