- 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>
3.3 KiB
3.3 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 cockpit/HUD/menu bitmaps — format TBD |
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).
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 |