Skip to content

Latest commit

 

History

History
264 lines (204 loc) · 9.52 KB

File metadata and controls

264 lines (204 loc) · 9.52 KB

09 — Leftovers

The archaeology. What shipped on a retail disc that was not meant to be read.

1. A 68000 disassembler, in the retail game

cdi_app contains a symbol called line_disassemble at 0x2fb8, and the next symbol is at 0x3d22. 3,434 bytes of disassembler, in a 23,236-byte game module — 14.8 % of the executable.

It is not a stub. The full 68000 mnemonic table is in the binary:

ADD AND BCD CHG CHK CLR CMP DIV EOR EXG EXT ILLEGAL JMP JSR LEA LINK
MOVE MUL NEG NEGX NOP NOT OR PEA RESET RTE RTR RTS STOP SET SUB SWAP
TAS TRAP TRAPV TST UNLK    CCR SR USP

and so is the condition-code table, packed two characters to a slot in the order the 68000 encodes them:

T F HILSCCCSNEEQVCVSPLMIGELTGTLE
 T  F  HI LS CC CS NE EQ VC VS PL MI GE LT GT LE

Around it is a complete exception handler:

init_catcher   set_catcher   call_catcher   catch_display   catch_dump_regs
make_error     signal_catch  critical       critical_no_sys
critical_intro critical_game_int
AllRegs (74 bytes)   currentpc   in_debugger   except_catch

with the exception names it prints:

Bus   Address   Illegal   Divide by zero   CHK   TrapV
Privilage violation   Trace   Line A   Line F   Format Error
Uninitialized Vector Interrupt   Spurious Interrupt   Unknown %04x

Privilage. Two misspellings on one disc, in two different files, by two different people — the credit roll says Interacive and the debugger says Privilage.

AllRegs is 74 bytes: sixteen 32-bit registers is 64, plus PC (4), SR (2) and USP (4) is 74 exactly. The handler dumps every register and disassembles the instruction at the fault address.

2. And a switch on the retail player to turn it on

/nvr/CATCHER_ON
"not "
"catcher %sinstalled\r"
"0.52"
"Exception Catcher %s  "

/nvr is the CD-i player's non-volatile RAM, the same place the save file lives. The game checks for a file called CATCHER_ON there, and if it finds one it installs the debugger and prints catcher installed; if it does not, it prints catcher not installed.

So the crash handler is not dead code that the linker failed to strip. It is a deliberately retained development tool with a runtime switch, and the switch is on the player rather than on the disc — which means it survives pressing and can, in principle, be turned on by anyone who can write to their player's NVRAM. The catcher even has its own version number, 0.52.

cdi_loader and cdi_invaders carry a second such switch, /nvr/DISPLAY_ON. What it does is not readable from the strings.

3. Twenty names in the default high-score table

The save file /nvr/Apprentice holds a high-score table, and its factory contents are compiled into cdi_app as the initial value of nvr_buffer at file offset 0x5690:

u16 u16   00 01 00 01
then 20 x
    char[2]  rank, "01".."20"
    u32      score, big-endian
    char[6]  name

244 bytes, ending exactly where the string music.rtf begins, which is what closes the reading.

python tools/cdinvr.py
 01   200,000  | LUKE |      11   100,000  |DENNIS|
 02   190,000  | TIM  |      12    90,000  | OLU  |
 03   180,000  | ARJEN|      13    80,000  |SIBBY |
 04   170,000  |STEFAN|      14    70,000  |DANION|
 05   160,000  | LUC  |      15    60,000  | SAM  |
 06   150,000  |JOOST |      16    50,000  | MARC |
 07   140,000  |JORIS |      17    40,000  | DAVE |
 08   130,000  |JOOP  |      18    30,000  |ELLIS |
 09   120,000  | JSB  |      19    20,000  | RIK  |
 10   110,000  | ROB  |      20    10,000  | ERIC |

Scores descend by exactly 10,000 a place, so they are placeholders. The names are not.

Cross-reference them against the rest of the disc and the top five light up:

python tools/cdinvr.py cross
    LUKE  bibliographic, volume:data_preparer
     TIM  bibliographic, volume:data_preparer
   ARJEN  volume:data_preparer
  STEFAN  -- nowhere else on the disc
     LUC  volume:data_preparer
   JOOST  bibliographic

The volume descriptor's data-preparer field reads Tim, Luke, Luc and Arjen. Four names, and four of the top five scores. JOOST at sixth is Joost Egmond, the composer, who is in the credits. STEFAN at fourth is in neither.

The credit roll names three people. The high-score table names twenty, in what is plausibly seniority order, and fifteen of them appear nowhere else on the disc at all. It is the closest thing to a staff list The Vision Factory left on this pressing, and it is in the one place a player would see it — the first screen after the attract mode.

(What is measured: the twenty names, their order, and which of them occur in other fields. What is inferred: that the order means anything. Someone had to type twenty names into a table and the obvious orders are seniority, desk position or whoever was in the room.)

4. The seventh level that is five empty maps

The last game assets written before the volume was closed, at 14:56:32 and 14:56:33 on 1994-10-19, are map7_1.dat through map7_5.dat.

Every other map file on the disc is 116,736 or 118,784 bytes and is a three-chunk container: layout, tile sheet, palette. These five are 16,384 bytes each, with no container header at all — a bare u16 array, the same period-6 layout as a real map's chunk 0, with:

  • every attribute word zero, where a real map has values in 0x400x62
  • 1,453 to 1,640 non-zero bytes out of 16,384, so 82 % empty
  • five different files, so they are not copies of one stub

And there is no level7.dat, no intro7.dat and no l7_em.dat. Levels one to six each have an intro, a level file, three maps and an enemy file. Level seven has five maps and nothing else.

cdi_app names them: the string map7_%d.dat is in the binary, immediately after cr_gfx.dat and immediately before levelb.dat. So the game loads them. Five rather than three also breaks the pattern — every other level has exactly three maps.

What they are is not determinable from the disc. A seventh level cut down to its maps, an endgame sequence that reuses the map loader for something that is not a level, or a test fixture that was still being edited an hour before mastering are all consistent with the bytes. It is the single most suggestive thing on the volume and it does not resolve.

5. Developer diagnostics that shipped

The block loader prints:

start_blockload(): Warning: aborting current blockload
loading block rtf from path %d
loading block rtf %s
blockload: null a_list%s; skipping
start_blockload(): srqcmem(): %R
start_blockload(): cd_play(): %R
blockload finished
blockload aborted
blockload: load error (%R)
blockload: null blocknext, signal ignored

%R is Microware's error-code format. function(): call(): %R is the shape of a message written for a terminal, on a console that has none.

cdi_invaders goes further and prints its own configuration:

cdi_config at %x
cdi_config module has bad magic
compat=%d startline=%d pal_flag=%d hz=%d (%dHz display)
chdir %s: %R
chxdir %s: %R
dc_crlct A   dc_crlct B   dc_crfct A   dc_crfct B
dc_crlct A2  dc_crlct B2  dc_exec

The dc_* set are MCD212 display-control register names — line control table and frame control table, per plane — printed by name.

6. A loader for a disc layout this disc does not have

cdi_loader contains:

APPL00/CMDS/cdi_appl00

There is no APPL00 directory on this volume; all four applications are in one /CMDS/. The loader writes decimal digits into that template, so it supports up to a hundred applications, each in its own APPLnn directory, and cdi_start and cdi_invaders carry the matching chdir / chxdir / %s/CMDS strings.

This is the studio's general-purpose CD-i loader, carried onto a disc that needed one directory and four slots. It is the kind of leftover that tells you about the tooling rather than about the title.

7. A studio leader still at version 0.55

cdi_factory, the module that plays The Vision Factory's own logo, contains:

DThe Vision Factory Leader v0.55

Shipped at 0.55, alongside an exception catcher at 0.52. Neither reached 1.0.

8. The publisher's own bumper has the wrong file-number byte

/CMDS/philips.rtf is 625 sectors of Form 2 real-time data — RL7 video, DYUV, Level B stereo audio, three channels — and its directory entry has file number 0. Its sibling factory.rtf, which is the same kind of file, has 1.

Every other real-time file on the disc is marked correctly. The one that was supplied by the publisher rather than built by the studio is not.

9. What is not here

Worth recording, because the absence is informative:

  • No all-zero files, on a disc using 19.9 % of its capacity. Room does not produce dead files.

  • No abandoned localisation. Everything on the disc is English, and there is only one text file per role.

  • No unreferenced assets, and the check closes. Twenty filename templates are embedded across the six modules:

    con_gfx.dat  cr_gfx.dat   demo%d.dat   go_gfx.dat   hi_gfx.dat
    int_gfx.dat  intro%d.dat  invaders.dat l%d_em.dat   level%d.dat
    levelb.dat   map%d_%d.dat map7_%d.dat  tit_gfx.dat  treas1.dat
    endmus.rtf   factory.rtf  music.rtf    othermus.rtf philips.rtf
    

    Expand them and they match all 56 .dat and .rtf files on the volume, with none left over in either direction. Not one asset on this disc is unreachable, and not one name in the binary points at a file that is not there.

  • No personal data. Twenty first names and one address-free credit roll.

  • No SCCS keywords. Merlin's three text files all open with @(#); none of these three do.

  • No MPEG, no Digital Video cartridge requirement.