Open main menu

SCUMM/Technical Reference/SCUMM 6 resource files

< SCUMM‎ | Technical Reference
Revision as of 11:42, 11 April 2011 by Furrykef (talk | contribs) (clarifications regarding sizes and offsets)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Scumm 6 data format

1. Introduction

The first SCUMM version was written for Maniac Mansion. At that time, floppy disk was the most common medium for games, so naturally the file formats reflect this. Basically each floppy contained one or more rooms with all the data needed to play in those rooms. Later games came on CD and all data was grouped into 2 files. The main data file is merely a big bunch of room files put together. The second file is an index to make random access easy.

1.1 Other resources

Other people are working on similar stuff. Some information came from these:

1.2 Basics

First of all, the data can be "encrypted". To decrypt just XOR each byte with a key. According to the ScummVM code, there are 3 keys in use: 0x00, 0xFF and 0x69. DOTT uses 0x69.

All SCUMM files are made of blocks like this:

  type: 32
  size: 32be
  data: ...

Note that the size includes this header! For example, if 'data' contains 32 bytes, then size will be 40, because the block's header is 8 bytes long.

2. Index file

RNAM : room name list

  vlc (end code 0x00, so room no starts at 1)
    room no : 8
    name    : 8*9 (binary inverted)

List the name of the rooms, note that you must apply a binary not on the names (name[i] = ~name[i]). The list must be terminated with a zero byte.

MAXS : maximal address for ...

  max var        : 16le
  unk            : 16le (perhaps actors as it's 16 in dott, but that
                         doesn't really fit with later versions)
  max bit var    : 16le
  max local objs : 16le
  max arrays     : 16le
  unk2           : 16le (really dunno as it's 0 in dott)
  max verb       : 16le
  max fl objs    : 16le
  max inv        : 16le
  max room       : 16le
  max scr        : 16le
  max snds       : 16le
  max chst       : 16le
  max cost       : 16le
  max glob obj   : 16le

This is used by the VM to ensure resource address validity. So generally it doesn't represent the actual number of resources, in particular for variables, as most low addresses are reserved and might not be used at all in the scripts. max local objs is the maximum number of objects used inside a single room. It's set to 200 in DOTT, and I suppose this limit was simply set arbitrarily. max global obj is the highest valid object address, so usually the number of objects minus 17 (as object addresses 1-16 are used for actors). max bit var must be rounded up to the next multiple of 8, as apparently the original engine just divides by 8 to allocate the memory.

DROO : room idx

  num entries : 16le
  file no     : 8*num
  room off    : 32le*num

This describes where to find each room: in which file and at which offset. So one can split the game into several files named gamename.xxx. The offset is to the beginning of the ROOM block (i.e. right after the LFLF header).

DSCR : script idx

  num entries : 16le
  room no     : 8*num
  room off    : 32le*num

DSOU : sound idx

  num entries : 16le
  room no     : 8*num
  room off    : 32le*num

DCOS : costume idx

  num entries : 16le
  room no     : 8*num
  room off    : 32le*num

DCHR : charset idx

  num entries : 16le
  room no     : 8*num
  room off    : 32le*num

All the above D??? blocks describe where the resources are found. The offset is relative to the beginning of the ROOM (i.e. the one given for the room in the DROO block).

DOBJ : object owners list

  num entries      : 16le
  obj owner/state  : 8*num
    owner          : 4
    state          : 4
  class data       : 32le*num

This block sets the owner, class and initial state of each object. Each object must have an owner and it is usually the room (0xF), but it can be any actor number.

AARY : array list

  vlc (end code 0x0000)
    var no      : 16le
    x size -1   : 16le
    y size -1   : 16le
    type        : 16le (0: words, 1: bytes)

Finally we have a list of pre-allocated arrays, these will be allocated by the engine before running the boot script.

3. Main data file

3.1 Structure

  • *LECF* main container
    • *LOFF* room offset table
    • *LFLF* disk block
      • *ROOM* room block
        • *RMHD* room header
        • *CYCL* color cycle (vlc stop on 0x00)
        • *TRNS* transparent color
        • *PALS* palette data
          • *WRAP* dummy container
            • *OFFS* palette index
            • *APAL* 256 entries rgb palette (include the others blocks size)
        • *RMIM* room image
          • *RMIH* number of z buffers
          • *IM00* image data
            • *SMAP* stripe table + plane 0
            • *ZPnn* stripe table + z planes (nn >= 1)
        • *OBIM* obj image
          • *IMHD* image header
          • *IMnn* image data (image for state nn, start with state 1 as state 0 just display nothing)
            • *SMAP* plane 0 (see above)
            • *ZPnn* other z planes (nn >= 1, again see above)
        • *OBCD* object scripts
          • *CDHD* code header
          • *VERB* verb entries (the object scripts indexed by verbs. Several verbs may have the same code)
          • *OBNA* object name (default object name, it may be overrided by the scripts)
        • *EXCD* exit script (script run when leaving the room)
        • *ENCD* entry script (script run when entering the room)
        • *NLSC* number of local scripts
        • *LSCR* local script
        • *BOXD* box data (box 0 seems to be crap with all the points set to -32000,-32000)
        • *BOXM* box matrix
        • *SCAL*
      • *SCRP* script
      • *SOUN* sound, contain one of:
        • *MIDI* midi data
        • *SOU* container for the different versions
          • *ROL* roland data
          • *ADL* adlib data
          • *GMD* general midi data
      • *COST* costume
      • *CHAR* charset

3.2 Blocks content

All the blocks not described here don't contain anything more than the blocks shown in the above structure. Or they are unknown (to me) atm ;)

3.2.1 LOFF
  num rooms: 8
    room id: 8
    offset : 32le (absolute offset of the ROOM block)
3.2.2 RMHD
  width    : 16le
  height   : 16le
  num objs : 16le
3.2.3 CYCL
  cycles  : variable length
    idx   : 8 (valid range is [1-16])
    unk   : 16 Th
    freq  : 16be (delay = 16384/freq)
    flags : 16be
    start : 8 (start/end entries in the palette)
    end   : 8
  close   : 8 must be set to 0 to end the block

Defines the various parts of the palette that can be cycled. The only known flag is the second bit, which means the cycling should go backwards.

3.2.4 TRNS
  val     : 8
  padding : 8
3.2.5 OFFS
  offset table : block size imply the number of palette ?
    offset     : 32le (offset starting from this OFFS block)
3.2.6 APAL
  colors : 256 times
    r    : 8
    g    : 8
    b    : 8
  APAL   : others APAL blocks may follow

Note that the APAL blocks are constructed recursively. If we have for example 3 palettes, the first APAL block contains its palette followed by 2 other APAL blocks, etc.

3.2.7 RMIH
  num z buf: 16le (number of ZPnn in the IM00 block)
3.2.8 SMAP
  strip off : 32le (offset from this SMAP. 1 per column of 8 pix)
  stripes
    codec   : 8
    data    : variable length
3.2.9 ZPnn
  strip off : 16le (offset from this ZPnn. 1 per column of 8 pix)
  stripes
    data    : variable length

See SCUMM Image Resources for more information on the SMAP and ZPnn blocks.

3.2.10 IMHD
  obj id       : 16le
  num imnn     : 16le
  num zpnn     : 16le
  unknown      : 16
  x            : 16le
  y            : 16le
  width        : 16le
  height       : 16le
  num hotspots : 16le (usually one for each IMnn, but there is one even
                       if no IMnn is present)
  hotspots
    x          : 16le signed
    y          : 16le signed
3.2.11 CDHD
  obj id    : 16le
  x         : 16le (upper left corner)
  y         : 16le
  w         : 16le (size of the active area - it may not match the image
                    size, I think)
  h         : 16le
  flags     : 8
  parent    : 8
  unk       : 2*16
  actor dir : 8 (direction an actor should look at when standing in front
                 of the object)
3.2.12 VERB
  offset table : vlc
    entries    : 8 (0x00 is end, 0xFF is default)
    offset     : 16le
  code

Each offset is relative to the start of the block. For example, offset 0 is the 'V' in 'VERB'.

3.2.13 OBNA
  null terminated string
3.2.14 NLSC
  val     : 8
  padding : 8
3.2.15 LSCR
  idx  : 8 (local script IDs start at 200)
  data
3.2.16 BOXD
  num box : 16 ScummVM only uses the lower 8 bits
    ulx   : 16le
    uly   : 16le
    urx   : 16le
    ury   : 16le
    lrx   : 16le
    lry   : 16le
    llx   : 16le
    lly   : 16le
    mask  : 8
    flags : 8
    scale : 16le

The mask indicates which Z plane should mask this box. The flags are used for certain parameters:

  • 0x08 : X flip
  • 0x10 : Y flip
  • 0x20 : Ignore scale / Player only
  • 0x40 : Locked
  • 0x80 : Invisible

"scale" indicates the scaling to be used for actors in the box. If the highest bit is set, it defines a scale slot instead. See the SCAL block for more details on scaling.

3.2.17 BOXM
  line      : the matrix has one line for each box, terminated by 0xFF.
    box     : list of the directly connected boxes
      start : 8
      end   : 8
      box   : 8
    0xFF    : 8
3.2.18 SCAL
  scale data (repeat 4 times)
    scale1 : 16le
    y1     : 16le
    scale2 : 16le
    y2     : 16le

The SCAL block defines the room's scale slots. These allow the actors to be scaled as a function of their position in the room, instead of just having a fixed scale for a whole box. Each slot represents a linear function ranging from scale1 to scale2 for an input (the y coordinate) ranging from y1 to y2. Scale factors range from 1 to 255, with 255 being unscaled and 1 the minimal size (1 pixel or so :). I dunno yet if the real scale factor is s/255, but that seems the most logical.

  s = ((scale2 - scale1) * (y - y1)) / (y2 - y1) + scale1;
  if(s > 0xFF) s = 0xFF;
  if(s < 1) s = 1;
3.2.19 COST
  size               : 32le can be 0 (see num anim) or the size
                       (sometimes with an offset of one ??)
  header             : 2*8 always contain "CO"
  num anim           : 8  if(size) num_anim++
  format             : 8  bit 7 is a mirror flag, bit 0 is the palette
                       size (0: 16 colors, 1: 32 colors)
  palette            : 8*num colors coded in format
  anim cmds offset   : 16le access the anim cmds array
  limbs offset       : 16*16le access limb picture table
  anim offsets       : 16le*num anim  access anim definitions
  anim
    limb mask        : 16le
    anim definitions : variable length, one definition for each bit set
                       to 1 in the limb mask.
        0xFFFF       : 16le stopped limb code
     OR
        start        : 16le
        noloop       : 1
        len          : 7
  anim cmds
    cmd              : 8
  limbs
    pict  offset     : 16le
  picts
    width            : 16le
    height           : 16le
    rel_x            : s16le
    rel_y            : s16le
    move_x           : s16le
    move_y           : s16le
    redir_limb       : 8 only present if((format & 0x7E) == 0x60)
    redir_pict       : 8 only present if((format & 0x7E) == 0x60)
    rle data

See SCUMM Costume Resources for more informations on costumes.

3.2.20 CHAR
  size           : 32le  size-15
  unknown        : 2*8 always 0x6303 in dott
  palette        : 15*8
  bpp            : 8 (font ptr)
  font height    : 8
  num char       : 16le
  offset table   : num*32le the offsets are relative to font ptr
  chars
    width        : 8
    height       : 8
    x offset     : s8
    y offset     : s8
    data         : raw bit packed data

In ScummVM only bpp 1, 2, 4 and 8 are supported.

3.2.21 SOUN

This is apparently used to store music, and was also used for sound effects in older games. They can contain various data, but it seems only SOU is used in DOTT. Sam'n Max also has some MIDI blocks. A few others exist but they are probably not used in v6.

The SOU block again contains new blocks to store the various versions. In DOTT they generally contain 3 blocks: ROL, ADL and GMD (presumably Roland, AdLib, and General MIDI). Each seems to contain more or less standard MIDI data.

4. Voice file

All the voices and effects are in a separate file. In the original engine, the file is called MONSTER.SOU and uses VOC (creative voice) samples. ScummVM can use compressed samples and supports files named $GAME.SOU. Currently ScummVM supports MP3, Vorbis and FLAC.

4.1 Structure

  main block header   : scumm block header (SOU )
  samples
    sync block header : scumm block header (VCTL)
      sync data       : 16be*num
    voc data          : sample data

The first block header at the start of the file has a zero size. The number of sync points is derived from the VCTL block size. The size of the VOC data can only be found by parsing it.