SCUMM/Technical Reference/Image resources
The scumm engines use loads of different image format. Most of them use some bitpacked format with a more or less clever compression. The following is currently limited to the format used for version 6 and above.
LucasHacks! have an intersting article about this at http://scumm.mixnmojo.com/?page=articles/article1 (it is down as of this writing).
SMAP
A smap contain a picture. This used for the background of the rooms and the objects. The coding is quiet strange but for that it is probably efficient on old crappy hardware :) The picture in encoded in vertical stripes of 8 pixels width. The data start with an offset table followed by each encoded stripe. It seems that the overall size of the smap is rounded to the next multiple of 2. Note that no information about the image size is present in the SMAP, this is found in the room or object header.
SMAP v6
offset table : width/8 elements. Offset are relative to the start of the SMAP block (-8 offset) offset : 32le .... stripe header : 8 data
The stripe header is a bit strange. The lower bits are used to store the coding shift. This can be retrieved with a simple header % 10, the rest of the bits code the encoder type used for the stripe. In dott 4 type of encoding are used, i kept the name used in scummvm (and added one): unkA, unkA6, unkB and unkC :) unkA and unkA6 can both be decoded by the unkA decoder.
SMAP v8
TODO
SMAP Codecs
UnkA
- Opaque id: 10
- Transparent id: 12
Decoder
uint8_t color = read_bits(csh); uint8_t inc = 0,n; write_pixel(color,1); while(pixel_left) { n = 1; if(read_bit()) { if(!read_bit()) color = read_bits(csh); else { inc = (read_bits(3) - 4); if(inc) color += inc; else n = read_bits(8); } } write_pixel(color,n); }
UnkA6
- Opaque id: 6
- Transparent id: 8
A simplified version of the above algorithm. Probably a predecessor. Dunno why some of the images in dott are encoded with this inferior algorithm. Perhaps because it's a bit faster to decode :)
Decoder
uint8_t color = read_bits(csh); uint8_t inc = 0; while(pixel_left) { write_pixel(color,1); if(read_bit()) { if(!read_bit()) color = read_bits(csh); else color += (read_bits(3) - 4); } }
UnkB
- Opaque id: 2
- Transparent id: 4
Decoder
uint8_t color = read_bits(csh); uint8_t inc = -1; while(pixel_left) { write_pixel(color,1); if(read_bit()) { if(!read_bit()) { color = read_bits(csh); inc = -1; } else { if(read_bit()) inc = -inc; color += inc; } } }
UnkC
- Opaque id: 1
- Transparent id: 3
This is the same as unkB except that it is coded by column instead of by line.
ZPnn
ZP0n v6
The encoding is very similar to the SMAP. Again we have an offset table followed by the encoded data. However the offset are only 16 bits width and the special offset of 0 is used to code a stripe full of 0. But the LEC interpreter react strangely to these strides.
offset table : width/8 elements. Offset are relative to the start of the
ZPnn block (-8 offset)
offset: 16le
....
stripe
data
Decoder
The z plane decode to a bit packed 1 bit per pixel image. The decoder decode a single stripe. As these are 8 pixels in width every line is a single byte. So this time the encoding is byte based that's a lot simpler :)
uint8_t b,count; while(line_left) { count = read_byte(); if(count & 0x80) { // write the same byte count times count &= 0x7F; b = read_byte(); do { write_byte(b); count--; } while(count && line_left); } else { // write count bytes as is from the input do { write_byte(read_byte()); count--; } while(count && line_left); } }
ZPLN v8
TODO
BOMP
The blast object images, these start in v6 with S&M afaik. The encoding is pretty straight forward, it's a simple line by line RLE encoding.
The header is quiet simple and very similar in v6 and v8, only v8 use 32 bits as usual.
BOMP v6
unk : 16 width : 16le height : 16le padding : 2*16 data : the encoded image
BOMP v8
width : 32le height : 32le data : the encoded image
Codec
Each line start with a 16le storing the size of the encoded line (without the size header itself) followed by the RLE data.
lines encoded size : 16le line data : size bytes
Decoder
Note that this code only decode a single line.
uint8_t b,count; while(len > 0) { b = read_byte(); count = (b >> 1) + 1; if(count > len) count = len; if(b & 1) write_pixel(read_byte(),count); else { while(count > 0) { write_pixel(read_byte(),1); count--; } } len -= count; }