-
Notifications
You must be signed in to change notification settings - Fork 3
Scumm Image formats
The SCUMM engines use loads of different image formats. Most of them use a bitpacked format with a more or less clever compression. Images are generally coded in 2 parts: the color information (SMAP or BOMP) and the ZPnn blocks which contain Z masks. These are one bit depth planes used to mask actors correctly with their surroundings.
LucasHacks! has an intersting article about this at http://scumm.mixnmojo.com/?page=articles/article1
An SMAP contains a picture. This format is used for room backgrounds and objects. The coding is quite strange but it is probably efficient on old crappy hardware :) The picture in encoded in vertical stripes 8 pixels wide. The data starts with an offset table followed by the encoded stripes. It seems that the overall size of the SMAP is rounded to the next multiple of 2.
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 describe the encoder type used for the stripe. In DOTT, 4 types of encoding are used. I kept the names used in ScummVM (and added one): unkA, unkA6, unkB and unkC :) unkA and unkA6 can both be decoded by the unkA decoder.
The structure is a bit different, hopefully that’s the only difference.
- 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);
}
- 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);
}
}
- 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;
}
}
}
- Opaque id: 1
- Transparent id: 3
This is the same as unkB except that it is coded by column instead of by line.
This encoding is very similar to the SMAP. Again we have an offset table followed by the encoded data. However, the offsets are only 16 bits wide and the special offset of 0 is used to code a stripe full of zeros.
offset table : width/8 elements. Offsets are relative to the start of the
ZPnn block (-8 offset)
offset: 16le
....
stripe
data
Decoder
The Z plane decodes to a bit packed, 1 bit per pixel image. The decoder decodes a single stripe. As these are 8 pixels wide, 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);
}
}
The structure is a bit different but hopefully the codec is the same.
The blast object images, these start in v6 with Sam & Max AFAIK. The encoding is pretty straightforward, it’s a simple line by line RLE encoding.
The header is quite simple and very similar in v6 and v8, except v8 uses 32 bits as usual.
unk : 16
width : 16le
height : 16le
padding : 2*16
data : the encoded image
width : 32le
height : 32le
data : the encoded image
Each line starts 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
Well I haven’t written a tested decoder now, so here is the one from ScummVM. This code only decodes 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;
}