SCUMM - The Infernal Machine Researched by Lloyd Rosen At first glance, the old SCUMM LFL files look very disorganized and messy. This is probably due to the workings of the LucasArts Entertainment Company (hereafter referred to as LEC) SCUMM. As you probably know, SCUMM is an acronym for "Script Creating Utility for Maniac Mansion". The originator - Ron Gilbert - pioneered and set standards for graphical Adventures with Maniac Mansion. Maniac Mansion (and also the succeeding games) was made using SCUMM. However, SCUMM is not a package of individual sets of tools like script compilers, graphics editors and like. It was entirely point'n'click driven, and everything was done inside SCUMM itself. This explains the 'messiness' of LFL files - the authors added objects, sounds and scripts as they went along, working on the individual scenes. SCUMM enabled the different "blocks" (SCUMM term for scripts, sounds, objects etc.) to be reused in other rooms even though they were stored in different locations. SCUMM was originally done to fit into the small memory of the C64, and the interpreter and LFL's feature many clever ideas to shrink the file sizes. NOTE: All this information, structures and stuff applies to the Amiga version of SCUMM (i.e. Zak McKracken which I used for the research), and any differences between platform versions must be added by the respective researchers. THE INDEX FILE 00.LFL is the index file which is loaded and processed by the interpreter. This info applies for Amiga Zak/Maniac Mansion. Loom does not decode correctly yet. Offset Size Sym Description -------------------------------------------------------------------------------------- 0 - 1 word null two first bytes are skipped. 2 - 3 word OFLEN length of Object Flags block 4 OFLEN number of single byte flags for objects. ... byte NROOM number of rooms for this game byte RDISK NROOM bytes, which tells which room is on which disk (for disk based games), in ASCII. NROOM words, unknown offsets (most are zero). byte NCOST number of costumes used in this game bytes COSTREF NCOST bytes which tells which Room the costume is stored in. NCOST woeds of the costume offsets in the rooms. byte NMSCRIPT number of main-scripts in this game bytes MSCRIPTREF NMSCRIPT bytes which tells which Room the script is stored in. NMSCRIPT words of script offsets in the rooms. byte NSOUND number of sounds in this game. bytes SOUNDREF NSOUND bytes which tells which Room the sound is stored in. NSOUND words of sound offsets in the rooms. That's all. Text in the scripts is easily decoded. Normal ASCII text, with bit 7 set if a space follow the char. SCUMM has 256 variables, integer sized. OPCODES ACTOR_MODIFY(Actor, Value, Attribute, Args) ------------------------------------------------------------------------------ Attributes: 1 Change walking sound. 2 Change colour (Args = Colournum) 3 Change name (Args = null terminated string) 4 Change costume 5 Change dialogue colour (?) 13 (byte, byte, byte, bytes...) 53 (byte, var, byte, bytes...) 93 (var, byte, byte, bytes...) D3 (var, var, byte, bytes...) ACTOR_SAY(Actor, String) * Terminated by a zero ------------------------------------------------------------------------------ 14 (byte, bytes...) 94 (var, bytes...) SOUND_PLAY(Sound) ------------------------------------------------------------------------------ 02 (byte) 82 (var) 1C (byte) 9C (var) ACTOR_GETROOM(Var, Actor) Sets variable to which Room actor's currently in. ------------------------------------------------------------------------------ 03 (byte, byte) 83 (byte, var) JMP_VARGT(Var, Value) ------------------------------------------------------------------------------ 04 (byte, word) 84 (byte, var) JMP_VAREQ(Var, Value) ------------------------------------------------------------------------------ 08 (byte, word) 88 (byte, var) SETVAR_IND(VarInd, Value) * Note SCUMM has 256 variables ------------------------------------------------------------------------------ 0A (byte, word) 8A (byte, var) OBJ_JMPBIT6(Object) ------------------------------------------------------------------------------ 6F (word) EF (var) OBJ_JMPBIT7(Object) ------------------------------------------------------------------------------ 0F (word) 8F (var) OBJ_COPYFLAG(Var, Object) ------------------------------------------------------------------------------ 10 (byte, word) 90 (byte, var) OBJ_CLRBIT7 (Object) ------------------------------------------------------------------------------ 17 (word) 97 (var) JMP (Rel) ------------------------------------------------------------------------------ 18 (word) VAR_LOADWORD (Var, Value) ------------------------------------------------------------------------------ 1A (byte, word) 9A (byte, var) JMP_CLR5(Object, Rel) ------------------------------------------------------------------------------ 1F (word, word) 9F (var, word) VAR_LOADMULTIPLE(StartVar, Count) ------------------------------------------------------------------------------ 26 (byte, byte, bytes...) A6 (byte, byte, varinds...) OBJ_CLR4(Object) ------------------------------------------------------------------------------ 77 (word) F7 (var) OBJ_SET4(Object) ------------------------------------------------------------------------------ 37 (word) B7 (var) OBJ_SET5(Object) ------------------------------------------------------------------------------ 57 (word) D7 (var) OBJ_CLR6(Object) ------------------------------------------------------------------------------ 67 (word) E7 (var) OBJ_SET6(Object) ------------------------------------------------------------------------------ 27 (word) A7 (var) VAR_ADDIND (VarInd, Value) ------------------------------------------------------------------------------ 2A (byte, word) AA (byte, var) VAR_SUBIND (VarInd, Value) ------------------------------------------------------------------------------ 6A (byte, word) EA (byte, var) VAR_LOADBYTE (Var, Value) ------------------------------------------------------------------------------ 2C (byte, byte) AC (byte, var) JMP_CLR4 (Object, Rel) ------------------------------------------------------------------------------ 7F (word) FF (var) JMP_SET4 (Object, Rel) ------------------------------------------------------------------------------ 3F (word) BF (var) JMP_SET5 (Object, Rel) ------------------------------------------------------------------------------ 5F (word) DF (var) JMP_SET6 (Object, Rel) ------------------------------------------------------------------------------ 2F (word) AF (var) JMP_CLR7 (Object, Rel) ------------------------------------------------------------------------------ 4F (word) CF (var) JMP_CLR7 (Object, Rel) ------------------------------------------------------------------------------ 4F (word) CF (var) SET_WALKBOX8 (Box, Value) ------------------------------------------------------------------------------ 30 (byte, byte) B0 (var, byte) ACT_WALKTOOBJ (Actor, Object) ------------------------------------------------------------------------------ 36 (byte, word) 76 (byte, var) B6 (var, word) F6 (var, var) ACT_WALKTO (Actor, X, Y) ------------------------------------------------------------------------------ 01 (byte, byte, byte) 21 (byte, byte, var) 41 (byte, var, byte) 61 (byte, var, var) 81 (var, byte, byte) A1 (var, byte, var) C1 (var, var, byte) E1 (var, var, var) OP_05 (Object, X, Y) ------------------------------------------------------------------------------ 05 (word, byte, byte) 25 (word, byte, var) 45 (word, var, byte) 65 (word, var, var) 85 (var, byte, byte) A5 (var, byte, var) C5 (var, var, byte) E5 (var, var, var) OP_06 (Var, GlbActor) ------------------------------------------------------------------------------ 06 (byte, byte) 86 (byte, var) OP_09 (GlbActor, Value) ------------------------------------------------------------------------------ 09 (byte, word) 49 (byte, var) 89 (var, word) C9 (var, var) OP_0B (Object, Value) ------------------------------------------------------------------------------ 4B (word, byte) <- scumm bug :) 0B (word, byte) 8B (var, var) OP_0C (op1, op2) ??checkitout ------------------------------------------------------------------------------ 0C (byte, byte) 8C (var, byte) OP_0D (GlbActor1, GlbActor2, GlbActor3) ------------------------------------------------------------------------------ 0D (byte, byte, byte) 4D (byte, var, byte) 8D (var, byte, byte) CD (var, var, byte) OP_0E (GlbActor, Object) ------------------------------------------------------------------------------ 0E (byte, word) 4E (byte, var) 8E (var, word) CE (var, var) OP_11 (GlbActor, Op) ------------------------------------------------------------------------------ 11 (byte, byte) 51 (byte, var) 91 (var, byte) D1 (var, var) OP_12 (Value) ------------------------------------------------------------------------------ 12 (byte) 92 (var) OP_15 (Var, X, Y) ------------------------------------------------------------------------------ 15 (byte, byte, byte) 55 (byte, byte, var) 95 (byte, var, byte) D5 (byte, var, var) OP_16 (Var, Op) ------------------------------------------------------------------------------ 16 (byte, byte) 96 (byte, var) OP_19 (Op1, [ Op2, Op3 ]) ------------------------------------------------------------------------------ (if Op1 == 0xFC return) (if Op1 == 0xFB return ELSE grab Op2 and Op3) 19 (byte, (word, word) ) 39 (byte, (word, var) ) 59 (byte, (var, word) ) 79 (byte, (var, var) ) 99 (var, (word, word) ) B9 (var, (word, var) ) D9 (var, (var, word) ) F9 (var, (var, var) ) VAR_BITOP (Var, Bit, Op) ------------------------------------------------------------------------------ 1B (byte, byte, byte) 5B (byte, byte, var) 9B (byte, var, byte) DB (byte, var, var) If Op == 0 then bit clr else bit set OP_1D (Object, Op, Rel) ------------------------------------------------------------------------------ 1D (word, byte, word) 5D (word, var, word) 9D (var, byte, word) DD (var, var, word) OP_1E (GlbActor, X, Y) ------------------------------------------------------------------------------ 1E (byte, byte, byte) 3E (byte, byte, var) 5E (byte, var, byte) 7E (byte, var, var) 9E (var, byte, byte) BE (var, byte, var) DE (var, var, byte) FE (var, var, var) OP_20 (void) ------------------------------------------------------------------------------ 20 OP_FILEOP (Var, Op) ------------------------------------------------------------------------------ 22 (byte, byte) A2 (byte, var) ACT_GETYPOS (Var, GlbActor) ------------------------------------------------------------------------------ 23 (byte, byte) A3 (byte, var) OP_24 (Object, Op1, Op2, Op3) ------------------------------------------------------------------------------ 24 (word, byte, byte, byte) 64 (word, var, byte, byte) A4 (var, byte, byte, byte) E4 (var, var, byte, byte) OP_29 (Object, Op) ------------------------------------------------------------------------------ 29 (word, byte) 69 (word, var) A9 (var, byte) E9 (var, var) OP_2B (Script) ------------------------------------------------------------------------------ 2B (byte) ACTOR_MOVETOROOM (GlbActor, Room) ------------------------------------------------------------------------------ 2D (byte, byte) 6D (byte, var) AD (var, byte) ED (var, var) OP_2E (Op1, Op2, Op3) ------------------------------------------------------------------------------ 2E (byte, byte, byte) BIT_TEST (Var1, Var2, Bit) ------------------------------------------------------------------------------ 31 (byte, byte, byte) OP_32 (op) ------------------------------------------------------------------------------ 32 (byte) B2 (var) ROOM_COLOR (Value, Color, Op) ------------------------------------------------------------------------------ 33 (byte, byte, byte) 73 (byte, var, byte) B3 (var, byte, byte) F3 (var, var, byte) JMP_VARNZ (Var, Rel) ------------------------------------------------------------------------------ 28 (byte, word) A8 (var, word) JMP_VARNE (Var, Rel) ------------------------------------------------------------------------------ 48 (byte, word, word) C8 (byte, var, word) JMP_VARLT (Var, Value, Rel) ------------------------------------------------------------------------------ 38 (byte, word, word) B8 (byte, var, word) JMP_VARLE (Var, Value, Rel) ------------------------------------------------------------------------------ 44 (byte, word, word) C4 (byte, var, word) JMP_VARGE (Var, Value, Rel) ------------------------------------------------------------------------------ 78 (byte, word, word) F8 (byte, var, word) VAR_SUB (Var, Value) ------------------------------------------------------------------------------ 3A (byte, word) BA (byte, var) VAR_ADD (Var, Value) ------------------------------------------------------------------------------ 5A (byte, word) DA (byte, var) SCRIPT_ADD (Script) ------------------------------------------------------------------------------ 42 (byte) C2 (var) SCRIPT_ADD2 (?? chain/atonce ??) (Script) ------------------------------------------------------------------------------ 4A (byte) CA (var) VAR_INC (Var) ------------------------------------------------------------------------------ 46 (byte) OBJ_PICKUP (Object) ------------------------------------------------------------------------------ 50 (word) D0 (var) SCRIPT_REMEMBER () (remembers current positions and skips past the next 3 bytes) ------------------------------------------------------------------------------ 58 NOP ------------------------------------------------------------------------------ 5C, 6E, DC NEW_ROOM (Room) ------------------------------------------------------------------------------ 72 (byte) F2 (var) DEBUG_FAIL ------------------------------------------------------------------------------ 6B (stops interpreter and print script information) VAR_COPYSNDFLAG (Var, Sound) ------------------------------------------------------------------------------ 7C (byte, byte) FC (byte, var) SCRIPT_STOP ------------------------------------------------------------------------------ 00 A0 OBJ_SETOPEN (Object) ------------------------------------------------------------------------------ 07 (word) 87 (var) OBJ_SETCLOSE (Object) ------------------------------------------------------------------------------ 47 (word) C7 (var) OP_34 (GlbActor1, GlbActor2) ------------------------------------------------------------------------------ 34 (word, word) 74 (word, var) B4 (var, word) F4 (var, var) OP_35 (Var, X, Y) ------------------------------------------------------------------------------ 35 (byte, byte, byte) 75 (byte, byte, var) B5 (byte, var, byte) F5 (byte, var, var) OP_3B (GlbActor) ------------------------------------------------------------------------------ 3B (byte) BB (var) OP_3C (Op) (SOUND) ------------------------------------------------------------------------------ 3C (byte) BC (var) OP_3D (GlbActor, Op) ------------------------------------------------------------------------------ 3D (byte, byte) 7D (byte, var) BD (var, byte) FD (var, var) ACTOR_GETYPOS (Var, GlbActor) ------------------------------------------------------------------------------ 43 (byte, byte) C3 (byte, var) OP_4C (void) ------------------------------------------------------------------------------ 4C OP_52 (GlbActor) ------------------------------------------------------------------------------ 52 (byte) D2 (var) OBJ_NAME (Object, string...) ------------------------------------------------------------------------------ 54 (byte, bytes...) (null terminated) D4 (var, bytes...) (null terminated) OP_56 (Var, byte) ------------------------------------------------------------------------------ 56 (byte, byte) D6 (byte, var) OP_60 (op) ------------------------------------------------------------------------------ 60 (word) E0 (var) OP_62 (Script) ------------------------------------------------------------------------------ 62 (byte) E2 (var) ACTOR_GETFACE (Var, Actor) ------------------------------------------------------------------------------ 63 (byte, byte) E3 (byte, var) OP_66 (Var, GlbActor) ------------------------------------------------------------------------------ 66 (byte, byte) E6 (byte, var) OP_6C (Var, Object) ------------------------------------------------------------------------------ 6C (byte, byte) EC (byte, var OP_70 (Op1+8, Op2, Op2) ------------------------------------------------------------------------------ 70 (byte, byte, byte) F0 (var, byte, byte) ACTOR_GETCOSTUME (Var, GlbActor) ------------------------------------------------------------------------------ 71 (byte, byte) F1 (byte, var) VERB_MODIFY (Cmd, Verb, (...)) ------------------------------------------------------------------------------ If (Cmd == 0) { 7A (byte, byte) FA (byte, var) } else if (Cmd == 0xff) { 7A (byte, byte) } else { 7A (byte, byte, byte, byte, string...) FA (byte, byte, var, byte, string...) } ACTOR_GETBOX (Var, GlbActor) ------------------------------------------------------------------------------ 7B (byte, byte) FB (byte, var) RESTART (?) (void) ------------------------------------------------------------------------------ 98 OP_C0 (void) ------------------------------------------------------------------------------ C0 SCRIPT.ASK (Var, Script) ------------------------------------------------------------------------------ 68 (byte, byte) E8 (byte, var) $D2 (SAY) format $D2 (text),02,00 02 if more text follows $D2 (text),00 is spoken right away Here follows some code from the mailbox on the street outside Zak's flat: D8 497427F30200 8F 09 0C00 D8 "it's" ++ JMP_FB4 flag jump_offset (signed) say 6C6F636B65642E00 "locked." 18 1000 JMP_ADDR offset 0010 000C "already opened.",00 object door code say("it's"++) ifeq(bit4) say("locked.") else say("already opened.") endif 00 = END_EXEC D8 = say 8F = jump if flag bit 4 set ROOM STRUCTURE Offset Size Sym Description -------------------------------------------------------------------------------------- 0 - 1 word RM_SCRIPT Offset to Room's main script (init ?) 2 byte ? 3 byte ? 4 - 5 word RM_WIDTH Width of Room in pixels 6 - 7 word RM_HEIGHT Height of Room in pixels 8 - 9 word ? 10 - 11 word RM_BGIMG Offset to background image data 12 - 13 word ? 14 - 15 word ? 16 - 17 word ? 18 - 19 word ? 20 byte RM_NUMOBJS Number of objects in this Room 21 byte RM_WALKBOXOFF Offset to Walking Boxes in Room 22 byte RM_NUMSND Number of sounds needed for this Room 23 byte RM_NUMSCR Number of mainscripts needed for this Room 24 - 25 word ? 26 - 27 word ? 28 RM_IMAGES RM_NUMOBJS worth of offsets to imagery of the objects. -- RM_OBJECTS RM_NUMOBJECTS worth of offsets to object structures/code. -- RM_SOUNDS Table of RM_NUMSND bytes describing which sounds needed (i.e. loaded at startup) RM_SCRIPTS Table of RM_NUMSCR bytes describing which mainscripts needed OBJECT STRUCTURES Offset Size Sym Description -------------------------------------------------------------------------------------- 0 - 1 OBJTOP (word) OLEN Length of Object block 2 - 3 ? (null) 4 - 5 (word) ID Object ID (0-780) 6 (byte) ? 7 (byte) XPOS Horizontal position of image (blocks) 8 (byte) YPOS Vertical position of image (blocks) 9 (byte) WIDTH Width of image (blocks) 10 (byte) ? (Owner?) 11 (byte) WLKX WalkTo Horizontal Position (blocks) 12 (byte) WLKY WalkTo Vertical Position (blocks) 13 (byte) IMGHF Image Height and WalkTo Facing Bits 0-1 defines which way the actor will face when reached the object: %00 = Left %01 = Right %10 = Down (front towards user) %11 = Up (back towards user) Bits 2-7 defines the height of the image in rows. The interpreter divides by 8 to get the height in blocks. 14 (byte) NAMOFF Offset from 0 to object name. 15 (....) VATBL here follows byte pairs to define verb-actions: 1st byte is verb number (as set by opcode 0x7A) 2nd byte is offset from 0 to verb-action script. .... Table is null-terminated. NAMOFF Null-terminated string for object name. .... Verb-Action scripts follow here until OLEN or NULL reached. COSTUME STRUCTURES Offset Size Sym Description -------------------------------------------------------------------------------------- 0 - 1 COST_TOP word COST_LEN Length of Costume block 2 3 4 5 byte $58 (88) If not equal, SCUMM fails. 6 byte ? (something to do with actor colour) 7 - 8 word COST_78 (offset from COST_TOP) 9 - 10 word COST_IMG offset to image data Offset Size Sym Description -------------------------------------------------------------------------------------- COST_78 word Offset Size Sym Description -------------------------------------------------------------------------------------- 0 byte COSTP_WIDTH Width of feet image (pixels) 2 byte COSTP_HEIGHT Height of feet image (pixels) 4 byte COSTP_XOFF X Offset of feet 6 byte COSTP_YOFF Y Offset of feet 7 8 9 10 11 12