-
Notifications
You must be signed in to change notification settings - Fork 0
Captive Disassembly
Documentation baseline: v1.1.126.
Complete reverse-engineered data from CAPPO.EXE v1.06 (Oct 7 1992), PC conversion by TAG, original by RATT.
- Unpacked binary SHA-256:
fa7d5ca76d26f614476ed41f27cf737084942e9216b20b4605734df9ede9aee4 - Compressed size: 73,379 bytes (LZEXE v0.91)
- Unpacked size: 144,556 bytes
- Entry point:
CS:IP = 0000:0000, Stack:SS:SP = 230B:0200 - Executables:
CAPPO.EXE(main),GM.EXE(sound),INTRO.EXE(intro),FILEPLAY.EXE(utility)
Three distinct pseudo-random number generators recovered from the binary.
uint16_t prng_main(uint16_t state) {
state = state * 0x5E5 + 0x29;
state = ror16(state, 3);
state ^= 0x0800;
return state;
}Same LCG constants but different rotation and no XOR:
uint16_t prng_combat(uint16_t state) {
state = state * 0x5E5 + 0x29;
state = ror16(state, 2);
return state;
}uint16_t prng_mapgen(uint16_t state) {
state = state * 0x5E5 + 0x29;
state = ror16(state, 4);
state ^= 0x0800;
return state;
}All three share the LCG state * 0x5E5 + 0x29 but differ in rotation count and post-XOR.
25 creature types with HP, category, and speed data.
4 bytes per type: min_hp, max_hp.
| Type | HP Min | HP Max | Category | Speed |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 1 | 10 | 150 | 1 | 2 |
| 2 | 20 | 200 | 1 | 3 |
| 3 | 30 | 300 | 2 | 2 |
| 4 | 50 | 400 | 2 | 3 |
| 5 | 80 | 600 | 3 | 2 |
| 6 | 100 | 800 | 3 | 3 |
| 7 | 150 | 1000 | 4 | 2 |
| 8 | 200 | 1200 | 4 | 3 |
| 9 | 300 | 1500 | 4 | 4 |
| 10 | 400 | 2000 | 5 | 2 |
| 11 | 500 | 2500 | 5 | 3 |
| 12 | 600 | 3000 | 5 | 4 |
| 13 | 800 | 4000 | 5 | 3 |
| 14 | 1000 | 5000 | 6 | 2 |
| 15 | 1500 | 6000 | 6 | 3 |
| 16 | 2000 | 7000 | 6 | 4 |
| 17 | 3000 | 8000 | 6 | 3 |
| 18 | 5000 | 10000 | 7 | 2 |
| 19 | 7000 | 12000 | 7 | 3 |
| 20 | 10000 | 15000 | 7 | 4 |
| 21 | 19000 | 22000 | 7 | 3 |
| 22 | 12000 | 18000 | 7 | 4 |
| 23 | 15000 | 20000 | 7 | 5 |
| 24 | 20000 | 25000 | 7 | 5 |
Category table at DS:0x9A42 (file 0x18232), 1 byte per type.
Speed table at DS:0xA1A4 (file 0x18994), 1 byte per type.
base = min_hp + ((max_hp - min_hp) * difficulty) / 8;
hp = ((base * 2 * creature_modifier) >> 8) + 6;2 bytes per entry: graphic_id, frame_index. Maps the 25 creature types to sprite sets in ALIEN1-ALIEN6.PL5.
base_damage = weapon_lo_byte * weapon_hi_byte
damage = base_damage << min(shift_count, 3) // max x8
if (damage > 0xFFFD) damage = 0xFFFD // sign overflow cap
base = min(20, combat_val / divisor + 1)
Type 0x60: damage = base * 4
Type 0x61: damage = base * 8
Category acts as a scaling tier.
reduction = body_part_hp / 32
body_part_damage = incoming_damage / 4
| Constant | Value | Source |
|---|---|---|
| Energy per attack | 3 | combat.c:238 |
| XP per kill | hp_max / 10 |
combat.c:263 |
| Gold per kill | hp_max / 5 + 1 |
combat.c:265 |
| Item drop chance | 1 in 3 | combat.c:267 |
| Level-up HP bonus | +10 | combat.c:276 |
| Level-up energy bonus | +5 | combat.c:278 |
| Skill | Base XP | Max Level |
|---|---|---|
| ROBOTICS | 10 | 0x42 (66) |
| BRAWLING | 8 | 0x18 (24) |
| SWORDS | 30 | 0x18 |
| HANDGUNS | 72 | 0x18 |
| RIFLES | 216 | 0x18 |
| AUTOMATICS | 648 | 0x18 |
| LASERS | 1,944 | 0x18 |
| CANNONS | 3,888 | 0x18 |
| SPAYGUNS | 7,776 | 0x18 |
| EXPERIENCE | 7,960 | 0x18 |
Geometric progression: each weapon tier is roughly 3x the previous.
ROBOTICS: threshold += threshold >> 3 + 8 (per level)
Other skills: threshold += threshold >> 4 + round (per level)
dx = xp_value + 3 + difficulty
result = skill_level * dx * 12 // via (4+8) shift
Display level = xp >> 10. XP maximum: 0xF8FFFFFF. Overflow-safe addition from 0x87EC.
46+ items with type codes:
| Type Code | Category | Examples |
|---|---|---|
0x27 / 0x65
|
Body part | HEAD, CHEST, ARM, LEG, FOOT, HAND |
0x08 |
Consumable | BATTERY |
0x21 |
Equipment | OPTIC, CAMERA, BATTLE-GLOVE, WAR-BLADE |
0x30 |
Ranged weapon | PISTOL, COLT, MAGNUM, RIFLE, SHOTGUN, HAND-LASER |
0x60 |
Explosive | EXPLOSIVES |
0x10 |
Ammunition | CARTRIDGES, A51 MISSILES, SHELLS, LASER PACK |
0x20 |
Chip/data | DROID CHIP, CLIPBOARD |
0x00 |
Special | DEV-SCAPE, MINE, DIE, BALL, MAP |
-
Melee damage: 18 entries at file
0x1A006, 2 bytes each (lo,hi; damage = lo × hi) - Ranged damage: 20 entries, 4 bytes each (base, modifier, flag, 0)
-
Upgrade tier prices: 23 entries at file
0x1A220
23 variants with type code and base price. Melee tiers 1-5 (indices 1-5), ranged tiers 1-14 (indices 6-19), plus named variants A12, L22, X42.
Uses ROL16 operations on game_level_hi/lo + base_val + shop_tier + difficulty. Early game (level ≤ 5): fixed price 0x80.
Grade and tier gating based on difficulty (capped at 100) and shop material level.
HEAD, CHEST, ARM, LEG, FOOT, HAND, GOLD, BATTERY, EXPLOSIVES,
DEV-SCAPE, OPTIC, CAMERA, KNUCLE-DUSTER, BATTLE-GLOVE, WAR-BLADE,
LIGHT-BLADE, FIRE-AXE, PISTOL, COLT, MAGNUM, RIFLE, SHOTGUN,
HUNTER, UZIE, RAPEDO, BOOSTER, HAND-LASER, LYTE-ZAPPER, ION-PULSE,
MONO-CANNON, A51-LAUNCHER, TWIN-CANNON, AIROSOLL, ACID-DISPERSER,
FLAME-THROWER, A12-, L22-, X42-2-, CARTRIDGES, A51 MISSILES,
SHELLS, LASER PACK, SONIC PACK, POISON, ACID, FLAMBOS,
PLANET PROBE, CLIPBOARD, MINE, DIE, BALL, SUPER BALL, MAP,
DROID CHIP, MESSAGE FROM RATT, SHIT
Category-to-creature-type mapping table with difficulty offsets and modifiers (16 entries each).
| Pattern | Type Range | Behavior |
|---|---|---|
| Single | < 0x0A | One creature |
| Single + flag | 0x0A-0x0B | One creature with subcell flag 0x20 |
| Paired | 0x0C | Two creatures |
| Triple | 0x0D-0x0E | Three creatures |
| Flanking trio | 0x0F | Opposite + perpendicular directions |
| Paired + flag | 0x15 | Two creatures with flag |
Subcell position: two 16-entry tables for placement within cells based on facing direction.
- Grid: 64 wide × 32 tall
- Up to 15 levels per base
- Mission seed:
((mission - 1) * 11) + base_id
- Grid: 10 wide × 56 tall, 5 bytes per cell
- Four CA types selected by
(seed + level_num) & 3:
| Type | Name |
|---|---|
| 0 | MAZE |
| 1 | ROOMS |
| 2 | OPEN |
| 3 | MIXED |
Wall connectivity using bits 0x101 / 0x808, producing 0x10 flag. Wall thickness codes: 0x10, 0x18, 0x80, 0xC0.
- 4×4 grid of 16×8 sections in a flattened 64×32 allocation
- Section unlocking is progressive per seed (hard-coded masks for seeds 0-4)
- Floor 0 root at column 30 for seed 0
- Carving: random walk with up to 12,289 moves, 720 cell cap
- Room injection: 1-in-31 chance during carving
- Door placement: max 8 per base, guaranteed minimum 1
count = (PRNG & 7) + 1
position = PRNG & 0x7F // capped at 0x6F
width = (PRNG & 7) + 1
di = 0x44E0 → row 55, column 32 (55*320 + 32)
bp = 0x70 → 112 lines
cx = 0x48 → 72 words = 144 bytes/line
dx = 0xB0 → stride skip (320 - 144)
| Constant | Value |
|---|---|
| Original resolution | 320×200 (DOS/ST), 320×256 (Amiga) |
| Viewport width | 144 pixels |
| Viewport height | 112 pixels |
| Viewport X offset | 32 |
| Viewport Y offset | 55 |
Not a full 5×5 grid. The viewport samples 19 cells in a trapezoidal frustum:
Range 4: 5 cells (-2, -1, 0, +1, +2)
Range 3: 5 cells (-2, -1, 0, +1, +2)
Range 2: 3 cells (-1, 0, +1)
Range 1: 3 cells (-1, 0, +1)
Range 0: 3 cells (-1, 0, +1)
Canonical cell order:
{4,-2}, {4,-1}, {4,0}, {4,1}, {4,2},
{3,-2}, {3,-1}, {3,0}, {3,1}, {3,2},
{2,-1}, {2,0}, {2,1},
{1,-1}, {1,0}, {1,1},
{0,-1}, {0,0}, {0,1}
7 rule groups with cascading logic. Walls at specific cells occlude other cells:
| Wall at cell | Hides cells |
|---|---|
| 9 | 4, 5, 10 |
| 5 | 0, 1, 6 |
| 6 | 1, 2 |
| 7 | 2, 3 |
| 8 | 3, 4, 9 |
(Additional rules in the 7-group cascade documented in captive_view_window.c:32-87.)
The renderer copies pre-projected panels via masked blits into a 144×112 view buffer. No texture mapping or runtime scaling — all perspective is pre-baked into the panel graphics.
8-byte runtime descriptor at segment:0x00C0 + (graphic_id * 8). Source bank via relocated word table at code-segment:0x423C.
Fields: source_offset, destination_offset, width_bytes, height, flags, source_bank, source_segment.
| File | Purpose |
|---|---|
NEW_ADL.BIN |
AdLib driver |
NEW_ROL.BIN |
Roland driver |
NEW_BEP.BIN |
PC beeper driver |
CAP_A.BIN |
AdLib instrument patches |
CAP_R.BIN |
Roland instrument patches |
_SBNEW.DAT |
Sound Blaster driver |
SB15.CTV |
SB 1.5 Creative Voice |
SB20.CTV |
SB 2.0 Creative Voice |
SBPRO.CTV |
SB Pro Creative Voice |
26 patches extracted from CAP_A.BIN, 16 bytes each:
Byte layout per patch:
[0-1] AM/VIB/EG/KSR/MULT (operator 1, 2)
[2-3] KSL/TL (operator 1, 2)
[4-5] AR/DR (operator 1, 2)
[6-7] SL/RR (operator 1, 2)
[8-9] Waveform (operator 1, 2)
[10] FB/CON
[11] Volume mask
- 49 SFX entries as bytecode programs for OPL2 register writes
- Total bytecode: 2,132 bytes
- All sound is FM synthesis — no PCM samples
- OPL2 frequency table: 128 entries
4 simultaneous voices. Per-voice state:
- Data pointer and position
- Delay counter
- Channel and note offset
- Loop table
- Subroutine return address
- Per-voice PRNG state
14 categories with up to 11 variations each (numbered 0-9, A, B):
| Category | Context |
|---|---|
| BATT | Battle |
| COMPROOM | Computer room |
| ESCAPED | Mission success |
| FCBASE | Fast combat |
| FINAL2 | Final mission / endgame |
| GENBASE | General exploration |
| HOLOMAP | Holamap navigation |
| LONGNT | Long night / exploration |
| MAIN2 | Main title screen |
| RUNNING | Chase sequence |
| SHOPKEEP | Shop interaction |
| TRAPPED | Game over |
| VCBASE | Varied combat |
| W | World / ambient |
File format: SOUND\{CATEGORY}{NUMBER}.MID
| File | Content | Dimensions |
|---|---|---|
GAMESCRN.PL5 |
Main game screen / HUD | 320×200 |
ROOFS.PL5 |
Ceiling/floor textures | — |
WALLA-WALLE.PL5 |
Wall texture sets A-E | — |
DOORS1.PL5 |
Door textures | — |
DOORS2.PL5 |
Alternative door textures | — |
SHOP1.PL5 |
Shop background | — |
SHOP2.PL5 |
Alternative shop background | — |
ICONS.PL5 |
UI icons (inventory, HUD) | — |
ALIEN1-ALIEN6.PL5 |
Creature sprites (6 sets) | — |
OBJECTS.PL5 |
Item/object sprites | 16×16 frames, 20×12 grid |
ANIM1-ANIM3.PL5 |
Animation frames | — |
KEYBOARD.PL5 |
On-screen keyboard | — |
- Resolution: 320×200 pixels
- Palette: 32 colors
- Raw file size: 40,000 bytes
- No compression header
16 intro/cutscene animations: TEST0.ANM through TEST15.ANM.
48 syllables in 4-character slots. Names are 2-3 syllables concatenated:
VI RUP YUL SCO PHY RAT QUE CHA
SY POC E EX DE LAP EL MID
SO SI LE NE SIC THA ENE INS
OO ES GIN CEP LTE PE DER S
DON S ING ST Y ED BERY SY
LUME TON FAR HAM KAL APE BEE INK
Pattern: "STATION " + generated_suffix
Consonants: Z
Vowels 1: Q U P
Vowels 2: T R S L M
Vowels 3: B
Suffix 1: G R L
Suffix 2: S
- Version string:
HOLAMAP SYSTEM V5 - Coordinate format:
CO-OD 150N-150W - Location types:
PLANET,MOON,SPACE-STATION - Surface types:
LAND-MISS,LAND-LEV0,LAND
| Grade | Name |
|---|---|
| 0 | SHIT |
| 1-3 | (unnamed) |
| 4 | HUMAN |
| 5 | TINDRON |
| 6 | COPPATOR |
| 7 | BRONZITE |
| 8 | IRONIDE |
| 9 | CROMIZE |
ROBOTICS, BRAWLING, SWORDS, HANDGUNS, RIFLES, AUTOMATICS, LASERS, CANNONS, SPAYGUNS, EXPERIENCE
AG-SCAN, ROOT-FINDER, MAPPER, RADAR, MAGNA-SCAN, BODY-SCAN, VISION-CORRECTOR, VISOR, ANTI-GRAV, SHIELD, FIRE-SHIELD, GREASER
HEAD, CHEST, ARM, LEG, FOOT, HAND
| Field | Value |
|---|---|
| HP | 100 |
| Energy | 100 |
| Starting XP | 1024 |
| Weapon damage |
0x050A (lo×hi encoding) |
| Body part HP | 255 (all 6 parts) |
| Starting gold | 100 |
"DESTROY THE GENERATORS."
"TOP-LEFT, BOTTOM-LEFT, TOP-RIGHT, BOTTOM-RIGHT."
"PUT PLANET-PROBE IN HOLAMAP."
"BASE HAS BEEN DESTROYED!"
"TRILL HAS BEEN RESCUED!"
"DROID'S HAVE FAILED!"
"TRILL HAS BEEN LEFT TO DIE."
"DROID'S ARE TO BE WASTED"
"TRILL HAS BE KILLED!"
"DROIDS BRUTALLY OUTWITTED"
"SWAN NOT YET IN ORBIT"
"LANDER NOT IN DOCK"
"LANDING SUCCESSFUL"
"DOCKING COMPLETE"
"LANDER LAUNCHED"
"FLIGHT PATH SET"
"ARRIVED AT DESTINATION"
"PROBE REVEALS A BASE"
"PROBE LAUNCHED"
"WELCOME STRANGER TO MY"
"'HUMBLE SHOP"
"HOW MAY I BE OF ASSISTANCE"
"CALL AGAIN LATER"
"THIS WILL COST YOU"
"FOR NEXT LEVEL IN"
"YOU DO NOT HAVE ENOUGH!"
"ACCEPT"
"I DON'T STOCK THIS OBJECT"
"MODEL OF"
"PLEASE REMOVE"
"GRADE OF THE"
"REPAIR"
Save filename: CAPTIVE1.SAV
"RESTORE SAVED GAME"
"RE-LOAD"
"FILE EXISTS"
"SHALL I REPLACE FILE?"
"LOADING SAVED GAME"
"FILE ERROR" / "FILE NOT FOUND"
"LOADING" / "SAVING" / "FORMATTING"
"LOADING RESTRAINT"
Q W E R T Y U I O P
A S D F G H J K L
Z X C V B N M = . /
- Mode 13h framebuffer at physical
0xA0000 - Resolution: 320×200
- Size: 64,000 bytes
- Memory dump size:
0x100000
| Field | Value |
|---|---|
| Compression | LZEXE v0.91 |
| Compressed size | 73,379 bytes |
| Unpacked size | 144,556 bytes |
| Code+data | 143,532 bytes |
| DOS API calls | 28 (file I/O, memory management) |
| Entry point | CS:IP = 0000:0000 |
| Stack | SS:SP = 230B:0200 |