havoc-remaster/docs/FORMATS.md
pyr0ball 2287f3be12 Initial commit: Havoc preservation toolkit + format docs
- ff_extract.py: .FF (FlashFile) archive extractor with music song
  assembly (WAVE+PLST), ogg/mp3 conversion, and FL Studio pack export
  (samples + per-song MIDI + mapping).
- docs/FORMATS.md: reverse-engineered file formats (.FF, WAVE/PLST music,
  world data + binary notes).
- Original game data is gitignored (copyright Reality Bytes, 1995).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 23:33:30 -07:00

73 lines
3.3 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` cockpit/HUD/menu bitmaps — format TBD |
## 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).
## 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 |