Cracking a Hostile Executable
Conan the Cimmerian’s Amiga executables resisted the usual approach this
project uses on every other game here — disassemble the static code,
annotate it, trace data structures from labelled call sites. That approach
doesn’t work on Conan, because there’s almost no static code to disassemble.
This page tells that story; the byte-level detail behind every claim below
lives in docs/conan/plan.md and docs/conan/amiga/engine.md, which this
page summarizes rather than repeats.
There are only 632 bytes of code
Section titled “There are only 632 bytes of code”Both Game (the main game) and Conan (the splash-screen loader) are Amiga
HUNK executables with the same shape: two tiny CODE hunks totalling
632 bytes, one large BSS reserve, and one large DATA hunk
(89,588 bytes in Game, 37,984 in Conan) that’s compressed on disk. The
632 bytes of code are a self-decompressing bootstrap: they unpack the
DATA hunk into the BSS reserve at runtime, and the real game logic lives
in whatever comes out of that decompression — not anywhere directly visible
in the file.
This is why Game.cnf/Conan.cnf’s zero-length CODE ranges beyond the
first aren’t misconfigurations to fix — there just isn’t more code on disk to
widen the range into. The productive move was analysing the decompressed
data directly, which meant first working out what compressed it.
The decompressor turned out to be a custom, backwards-reading LZ77
Section titled “The decompressor turned out to be a custom, backwards-reading LZ77”The DATA hunk isn’t PackBits (the compressor every other resource in this
game’s .res/.L32 files uses) — an earlier pass assumed it was and got a
plausible-looking 148,002-byte blob that was actually wrong data end to end,
corrected in docs/conan/plan.md’s “Palette Investigation Summary”. The real
algorithm is a custom engine embedded in hunk 4, and rather than hand-port
it (the usual failure mode for a hostile decompressor — see this project’s
own reverse-engineering playbook on emulating a game’s own routine instead of
re-deriving it by hand), tools/conan/decompress-data-hunk/ runs the game’s
own 496-byte decompression routine under a musashi 68k emulator core
and dumps the output buffer directly. Verified by legible recovered game
text in the output (arbitrary English prose surviving is strong evidence —
a wrong plane order, wrong stride, or wrong decompressor reliably produces
garbage, not readable sentences), not just a byte-count match.
The palette hunt (now resolved, after two dead ends)
Section titled “The palette hunt (now resolved, after two dead ends)”With correctly decompressed data in hand, the palette search still took two more wrong turns before it landed:
- Wrong byte width. Standard 16-bit-per-entry (Amiga OCS 12-bit,
0RGB) and 32-bit RRGGBB00 (the Spirit/Vengeance/WIME convention) palette
scans both came back empty against
Gamehunk1 and gave only sliding-window artefacts against hunk2 — matches that looked like a 32-entry table but were really one region getting hit repeatedly at every possible byte offset. - The real shape: a 4-bit-nibble
{R, G, B, pad=0}record — each channel a raw 0–15 value, never left-shifted into a byte. Once that shape was tried,Gamehunk2 offset0xe8c(originally misread as0xe88, one record early — see the engine doc’s correction) turned out to hold four 32-entry palettes: a base scene palette, a darkened night variant, a warm variant, and the base with three slots forced to white.
The DOS port’s own executables (CONAN.EXE, START.EXE) needed a separate
trace, because they never touch VGA hardware directly — an early “no
0x3C8/0x3C9 port writes anywhere” scan was a true negative about a
false premise: all palette I/O goes through a swappable driver module
(MCGA.DVR for VGA/MCGA, EGA.DVR for EGA/Tandy) named in GAME.CFG, not
inlined in the game executable. Tracing each driver’s own jump table found
the real palette tables at CONAN.EXE+0x12163 (loader screens) and
START.EXE+0x2b8f7 (in-game scene palettes) — nibble triples again, same
shape as the Amiga side, just packed 3 bytes instead of 4.
Cross-platform check: decoding the Amiga’s 4-byte records and the DOS
port’s 3-byte records completely independently and comparing gives 0 of 576
mismatched nibble components for the loader palettes, and 381 of 384 for the
in-game scene palettes (the one genuine difference is an authoring change
in the night variant, not a decode error). See
docs/conan/amiga/engine.md § “Per-screen palettes” and § “In-game scene
palettes” for the full derivation, including how each palette block’s exact
boundaries were pinned from both sides rather than inferred from where a
byte scan happened to start.
The DOS scene format came from a cracked copy
Section titled “The DOS scene format came from a cracked copy”SCENES.RES’s SCEN format (236 scene/room object-placement lists, 6,542
object entries) is fully traced — but not from either Amiga executable, which
generally have no scene-drawing code to find given how little static code
either one contains. It came from the DOS port’s START.EXE, and getting
there took an unexpected detour: this copy of START.EXE had been written
off as “LZEXE-compressed but rejected by the standard unpacker.”
The rejection turned out to be a tampered MZ header, not an unsupported
compression variant — RUNME.COM in the same DOS release embeds the strings
CONAN.EXE and RAZOR.COM, i.e. this copy had been crumbled through a
contemporary cracking group’s loader, which rewrote the executable’s entry
point (e_ip/e_cs) to point at its own unpacking stub instead of the
original LZEXE stub. Patching those two fields back to the real LZEXE entry
point (found by searching for the LZEXE stub’s own byte signature elsewhere
in the file) let the stock unlzexe unpack it cleanly. From there, tracing
_LoadScene gave a byte-exact 4-byte-header + packed-bitfield object format
— see docs/conan/amiga/engine.md § “SCEN Format” for the full trace,
including two earlier failed decode attempts (bit-width brute force against
a wrong header-size assumption) that are kept on record with why each one
structurally couldn’t have worked.
.L32 sub-frame splitting: a hardcoded animation table
Section titled “.L32 sub-frame splitting: a hardcoded animation table”One more case worth telling: HORSE.L32 contains images that are really
horizontal filmstrips — several riders side-by-side in one bitmap — with no
FRML resource anywhere describing how to split them (see the
Image Gallery for the rendered filmstrips
themselves). The split lives in CONAN.EXE as a hardcoded 68-step lookup
table: six parallel 70-byte arrays giving, per animation step, which of the
11 loaded resources to draw from, a source rectangle within it, and where to
place it on screen (including a vertical-anchor adjustment that keeps a
horse’s feet on one ground line as its sprite grows from a tiny distant
silhouette to a near-full-screen close-up). Cross-checked two independent
ways — a pixel-domain re-derivation from the background-column structure of
the images themselves, and the exact frame counts the two originally
puzzling renders show — both matching the table exactly. See
docs/conan/amiga/engine.md § “.L32 sub-frame splitting” for the byte
offsets and the full verification.
What’s still open
Section titled “What’s still open”The animation-frame segmentation described on the
Animation Gallery page is the one area still
honestly marked as a hypothesis rather than confirmed: 23 of 101 FRML
resources have a code-confirmed classification (scene decoration), but the
other 78 — mostly combat sprites — rely on a render-derived geometric guess
rather than a located code table, because no such table was found in either
executable after four different search approaches. See
docs/conan/TODO.md, item conan-frml-combat-animation-ranges, for the
current status.