Reverse-engineering notes on Liberation: Captive II (Byte Engineers / Mindscape, 1994) for the Amiga CD32 — a first-person polygon RPG whose 3D engine ships as three separate shared libraries, whose city is built at run time by three separate programs, and whose 169 MB data track is 91 % digitised speech.
This repository documents the Mindscape release: one MODE1/2048 track of
82,502 sectors and ten Red Book audio tracks, 187 files, one codec, and the
largest data track on any CD32 disc in this series by a factor of 1.4.
Documentation only. No game assets, no extracted art, no audio, no
executable code is committed here. The tools in tools/ reproduce
every figure, table and image in these pages from your own legally obtained
copy.
| Doc | Contents |
|---|---|
| 00-overview.md | The release, the two version numbers, and the nine days in which the game changed its name |
| 01-disc-image.md | The cue sheet, ten audio tracks, the sector map, and 71 % of a CD |
| 02-trademark-block.md | The .TM block, and the fifth disc to carry the same 2,048 bytes |
| 03-filesystem.md | ISO 9660 field by field, and an executable that disproves its own datestamp |
| 04-boot-chain.md | A boot script that mounts a reset-surviving RAM disk, and two programs for one library |
| 05-compression.md | The census, and an RNC\2 container that is not RNC ProPack |
| 06-executables.md | Nine libraries, 181 calls, a backup-copy fallback, and Akiko measured to zero |
| 07-3d-engine.md | Three Wyvern libraries, a 512-entry sine table, a perspective divide that retries on overflow |
| 08-graphics.md | ImgA, AmSp, CHAR, and 10,792 pre-rendered wall sprites |
| 09-text-system.md | The text bytecode, the generators, and a note to translators that shipped |
| 10-audio.md | 34 minutes of Red Book, 195 minutes of speech, and 63 lines nobody recorded |
| 11-leftovers.md | The archaeology |
| 12-open-questions.md | Thirteen things unresolved, with the measurement beside each |
| notes/file-inventory.md | All 197 directory records with LBA, size, timestamp, magic and SHA-1 |
| notes/speech-index.md | Every .LOG table, every rate, and the 63 unrecorded line numbers |
| notes/sprite-banks.md | Every AmSp bank, every ImgA container, every .x3g object |
| notes/text-records.md | Record statistics for the six compiled text files, and the speech cross-check |
| notes/string-inventory.md | Every legible string in every executable, by file and offset |
| cd32-platformnotes-doc | Platform checklist — what to look for on any CD32 or CDTV disc. Shared by every Amiga CD pipeline; this repo does not keep a copy |
The two version numbers in the dump's filename are two people. The first
string in the game executable is
$VER: Liberation : Ratt V2.02 : Wyvern V2.00c :, followed by
(c) 1993 ( The Byte Engineers. The rights of Antony Crowther and Ross Goodley to be identified as the authors of this program are hereby asserted. : Friday 08-Apr-94 09:35:08 ). Ratt is Crowther's handle; Wyvern signs all three
of the 3D libraries in libs/ (MATH 1.2, TRIDEE 1.3, VECTOR 1.5, all
(C) Wyvern 1992). Two authors, two version numbers, and a third set on the
engine.
The executable disproves its own datestamp. Twelve of the 197 directory
records read 1992-02-05, two years before the master — the platform notes'
classic "check before you call this a dead clock". Here it can be checked
without leaving the disc: /captiveII is one of the twelve, and the binary
itself says it was linked Friday 08-Apr-94 09:35:08. Same 245,628 bytes, no
earlier release to inherit from. It is a wrong clock, on the machine that
assembled the last twelve files, and the file's own version string is what
proves it.
Akiko is not used, and this was the best candidate left. Zero
lea $B80000,aN, zero movea.l #$B80000,aN, zero references to the C2P port
at $B80038, zero $C0DE0000, across every code and data hunk on the disc.
The seven byte-pattern hits are all identifiable coincidences. The reasons are
in the same binary: nonvolatile.library and lowlevel.library — the only
two CD32-specific modules — are both opened behind one runtime flag at
$3299(a5), and neither open is fatal if it fails; the root holds a Workbench
icon; and four floppy volume names of the A1200 release are compiled in. The
same executable had to run on an A1200.
The 3D engine is a blitter program, and its surfaces are pre-rendered.
vector.library's 47 absolute custom-chip references are 45 Blitter
registers and nothing else — no bitplane pointer, no colour register, no
copper. tridee.library touches no hardware at all. And the 71 WallNN.VGM
files, 11.9 MB, are not textures: each is four AmSp banks of 42, 45, 24 and
41 sprites — 10,792 pre-computed perspective projections of wall faces,
every one four planes plus a one-bit mask, blitted by view. There is no
texture mapper, no span filler and no chunky buffer, which is the other reason
Akiko had nothing to do.
A 512-entry sine table and a divide that retries on overflow.
math.library's table is the last 1,024 bytes of the file, amplitude 16,384,
one turn, cosine taken as sine + 128 entries; 498 of the 512 entries are
exactly round(16384·sin(2πn/512)) and 14 are one bit out. The perspective
divide catches divs.w's overflow flag and halves the numerator's scale and
retries the same divide, giving up a bit of precision rather than clipping —
four instructions where a near-plane clip would have been.
The game runs three other programs to write itself. /CityGen 1.12,
/PlotGen and /BuildingGen 1.3 are separate AmigaDOS executables that
captiveII launches by name to lay out the city, lay out a building, and
generate the plot, the news reports, the police dossier and every conversation
in the game. BuildingGen makes no library calls at all in 23 KB of code.
PlotGen opens each of its three inputs twice — CaptiveII:PGE.TXT and then
CaptiveII_Disk3:PGE.TXT, the third floppy of the A1200 release, fallback and
all.
The disc ships a manual for its own scripting language, addressed to
translators. PGE.txt opens with 384 words explaining the text-generation
controls one at a time — ^On[a|b|c] for a random alternative, ^Nx-y for a
random number, ^P for a personal name, ^J for a profession — signed
Ross Goodley and ending "if you cannot make anything of it you phone me
direct on (0742) …". 0742 was Sheffield until PhONEday retired it in April
1995. No translation shipped, but three things on the disc were built for one:
that note, twenty-three accented Latin capitals in both fonts that nothing
prints, and generator vocabulary in German, French and romanised Japanese
(Beobachter, Fernsehnachrichten, actualites, cha no mise,
Terebi no Shimbun).
Both authors are in the random-surname table. crowther and goodley
sit in the list of names the game gives to procedurally generated citizens,
between cherryh, gaiman, gibson, leary and crowley on one side and
cleese, chapman, palin and gilliam on the other. The first-name list
has Tone in it.
Three hours and fifteen minutes of speech, recorded twice, with 63 gaps.
1,248 lines in two voices — Voice 1 mostly 7–12 kHz, Voice 2 mostly
15–16 kHz, different performances of different lengths, with the rate chosen
per clip (fifteen distinct rates across shop1/Voice1) — stored as bare runs of IFF
8SVX with a .LOG of file offsets beside them. An entry of 0xFFFFFFFF
means the line was never recorded, and there are 63 of them, 58 in Voice 1
and 5 in Voice 2. The dialogue bytecode's 0xB1 speech markers index the same
table, and the counts agree exactly: 566 distinct markers in char1.txt
against 566 lines in Char1/Voice1.LOG.
A boot script that mounts a reset-surviving RAM disk. Twenty-two non-blank
lines, and the middle of it mounts ramdrive.device as RRD: from a Devs/Mountlist
on the disc, makes it bootable with a one-line startup sequence (rrd:WARNING),
copies c/WARNING onto it, and relabels it Ram_Reset. A recoverable RAM
disk outranks the CD at boot, so a reset boots the warning instead of the
game. No other disc in this series does anything like it.
The loader carries second copies of its own executables. c/Liberation
names CaptiveII:CaptiveII and CaptiveII:BackUp/CaptiveII in consecutive
strings, and captiveII names :CityGen :PlotGen :BuildingGen followed by
:backup/CityGen :backup/PlotGen :backup/BuildingGen. All four backup copies
are byte-identical, and the timestamps show each was made four to six seconds
after its original was built. 292 KB of insurance against a 1994
double-speed drive missing a sector.
And a demo build's refusal, still in the retail binary. At hunk 0x546A:
[Sorry, this is only a 1 disk demo.] — bracketed, so it is a dialogue record
in the text system, not a printf.
Every number above comes out of tools/ run against the eleven
files of a standard rip. Python 3, pillow for the renderers, capstone for
the disassembly helpers.
cd tools
python pvd.py "../<Track 01>.iso" # volume descriptors
python isofs.py "../<Track 01>.iso" # the directory
python timestamps.py "../<Track 01>.iso" # the build log
python sectormap.py "../<Track 01>.iso" # what no file claims
python tmblock.py "../<Track 01>.iso" # the trademark block
python tracks.py .. # the audio tracks
python extract.py "../<Track 01>.iso" ../_work/iso
python census.py ../_work/iso # before any magic scan
python scan.py --magic ../_work/iso/captiveII
python scan.py --akiko ../_work/iso/captiveII
python scan.py --regs ../_work/iso/libs/vector.library
python hunk.py ../_work/iso/captiveII
python libcalls.py ../_work/iso/captiveII dos=0x150 exec=0x154 \
intuition=0x158 graphics=0x15C vector=0x160 \
tridee=0x164 nonvolatile=0x168 lowlevel=0x3350 math=0x34A0
python libinfo.py ../_work/iso/libs/math.library --sine 0x9E4
python img.py ../_work/iso/backpack.img
python amsp.py ../_work/iso/Wall01.VGM
python fnt.py ../_work/iso/0Liberation.FNT 0 40
python x3g.py ../_work/iso/0CityVectors.x3g
python sty.py ../_work/iso/intro.sty
python speech.py ../_work/iso/Char1 ../_work/iso/shop1
python samples.py ../_work/iso/Samples.BIN
python text.py ../_work/iso/Char1/char1.txt
python orphans.py ../_work/iso --named
python orphans.py ../_work/iso --unnamed
tools/rnc2.py is the one tool here that does not work: it is a faithful
transcription of the disc's own decruncher that desynchronises after two
tokens, and 12-open-questions.md says exactly
where and what was ruled out.
| cd32-platformnotes-doc | the shared checklist |
| cd32-dragonstone-doc | Dragonstone, Core Design, 1995 |
| cd32-marvinsmarvellousadventure-doc | Marvin's Marvellous Adventure, 21st Century, 1995 |
| cd32-prey-doc | Prey: An Alien Encounter, Almathera, 1993 — and its 1992 CDTV master |
| cd32-thesperislegacy-doc | The Speris Legacy, Team 17, 1996 |
| cd32-legends-doc | Legends, Krisalis / Guildhall, 1996 |