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

135 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 |