The Incredible Machine image format
The Incredible Machine (TIM) bitmap images are stored in the RESOURCE.00? resource files as sub-files. They have the extension .BMP.
(For full-screen images (*.SCR), see Stellar 7 RES Format.)
The files are built up out of chunks, in the typical Dynamix chunk format: a 3-character ID, followed by a ':', followed by the chunk size in UINT32LE format. The highest bit of the chunk size value is not actually part of the chunk size: it is set to 1 if the chunk is a container-type chunk that contains more chunks inside itself. This chunk format is documented on the Stellar 7 RES Format page.
The BMP file is contained inside one BMP chunk. This is a container chunk. None of chunks types embedded inside it are container chunks.
The first chunk inside the BMP data is the INF chunk. Its data starts with a UINT16LE indicating the number of sub-images inside the file, followed by two UINT16LE arrays containing respectively the widths and heights of all images.
Based on the chunk type that contains the image data, there are two types of images:
The majority of the image files in The Incredible Machine, including the graphics for puzzle parts, contain an SCN and an OFF chunk. The image data is stored in the SCN chunk, while the OFF chunk contains an index for each sub-image's data inside the SCN chunk. The SCN chunk has its own compression format, described in the next section.
The SCN chunk
The SCN chunk contains the pixel data for each sub-image. Basically, it has a code-based run-length encoding format with 2-bit commands. This gives four commands; the classic "repeat" and "copy" ones, and two extra commands to skip ahead. These "skips" should always treat the skipped space as transparent. If the output buffer is not cleared in advance, and the decompression is not meant to directly paint the new graphics onto an existing image in the buffer, these commands might need to specifically write 0x00-bytes to fill the skipped space.
The data for each sub-image starts with an
addValue byte that has a special meaning: its value is added to all non-transparent pixel values. It normally corresponds to the lowest pixel value found in the resulting image. The value of this byte is set to 0xFF in some "empty" images that contain only a few transparent pixels. In 8-bit games, like Heart of China, this byte may be greater than 0x0F, in which case the image is actually an 8-bit image. However, since the compression itself is based on storage of 4-bit pixels, the indices on the resulting 8-bit image can only contain values from
addValue up to
addValue + 0x0F, plus the background colour. This does mean that the special handling of transparency makes sure that in 8-bit mode, the full range of the 4 bits can be used for colours, without the need to sacrifice one for transparency.
The actual RLE codes start after this byte. The
command is made from the upper two bits, with the lower six bits serving as the
Note that this
value always denotes a number of pixels, not bytes, which may be tricky to program for 4-bit images. An alternative approach could be to decompress the image to an 8-bit buffer, and post-process the result to compact it to 4-bit (unless the
addValue makes the values too large for that, and it is intended to be 8-bit).
The four different commands are:
Command 0: Line skip
command made from high bits 00 is meant to skip to the next line in the image. This command must be used to terminate a line in the image; the format does not seem to support automatic wraparound for progressing to a new line. This also means none of the other commands will ever write data that goes beyond the end of a line.
The line skip command will move the output pointer down one line on the image (ending up on the same X-coordinate on the next line), and then move back by the amount of pixels specified in
value. With wraparound included, this means the operation simply jumps (or background-fills) to the position
width - value pixels ahead of your current output write position.
There are some special cases in this, though. Since this should be able to move from the end of one line to the start of the next one, it needs to be able to address the entire width of the image, which, on large images, can't be done in one 6-bit value. Because of this, the data may contain two consecutive bytes with a 'line skip' command, and the values of both of these need to be taken together, with the second command providing the more significant seventh to twelfth bits to be added to the first read value, as
value = value1 | (value2 << 6).
However, if an image has several empty lines in it, it can be unclear whether or not such commands should be taken together. Because of this, if the second command has a
value of 0, the rule is to always treat it as separate command. To avoid all confusion in these cases, and because a
value 00 code can be extended by one with a higher
value, if the non-empty pixels on that later line start at an earlier X-coordinate than the first empty pixel on that earlier line, the first line skip command will always align the X-coordinate with the point at which the data restarts after the gap, so that all commands following it can be non-ambiguous byte 00 full line skips. Note that if the pixel appears at a higher X-coordinate, all line skips can be done with 00 bytes, and the remaining distance can simply be filled up with 'short skip' commands (see below), which can perfectly be placed after the 00 bytes.
Since all files obey this pre-aligning rule, the decoding mechanism can be applied the same even if the image width is smaller than 63.
Command 1: Short skip
command you get when the high bits are 01 will skip (or background-fill) the amount of pixels specified in
value. Numbers greater than 0x3F will simply be encoded in multiple 'short skip' commands. A 'short skip' command with a
value of 0, resulting in a Code byte 0x40, is used to indicate the end of the decompression of the current sub-image.
Command 2: Repeat
command made up of bits 10 will create a sequence of pixels of the same color. The repeat count is specified by
value. A single byte follows the Code byte, containing the pixel color. Since this is a 4-bit format, the most significant 4 bits of this byte are always zero. Note that the
addValue from the start of the data needs to be added to this to get the real value to repeat.
Command 3: Copy
command with when you have bits 11 is the Copy command. This simply copies a sequence of pixel values to the output. The amount of pixels is specified in
Since the amount is in pixels, the number of bytes after the Code byte does not match the
value. It can be calculated as
(value + 1) >> 1. Each byte contains two pixels, with the color of the leftmost pixel stored in the most significant nibble. If the number of pixels is odd, the most significant 4 bits of the last byte contains the color of the last pixel, and the less significant nibble is zero. Note that since the
addValue from the start of the data needs to be added to every pixel in this range, and the starting position to write pixels to may be an odd number at the start of the command, this operation requires treating all data as nibbles to process them correctly.
Nearly all sub-images are "terminated" by the byte sequence 0x00 0x40; a full line skip, followed by an empty short skip command. However, there are a few exceptions, where it ends on 0x40 alone. Do note that while a 0x40 can always be considered a terminator command, since it has no real meaning on its own, a 0x00 is a perfectly valid full-line skip, and should never be treated as a signal to end the decompression.
The OFF chunk
The OFF chunk contains the offsets for each sub-image in the SCN chunk, as UINT32LE values. The first one should always be zero.
The end of the current sub-image's compressed data in the SCN chunk can be determined from the start of the next offset in the list, or the length of the SCN chunk, if it's the final image. In practice, though, the data itself will always indicate the end with the 0x40 terminator value.
Since the 'line skip' command is the only valid way to progress to a new line, and this can only address up to 12 bits, images in this format are technically restricted to a width of up to 4095 pixels.
The remaining files in The Incredible Machine use a BIN chunk (e.g. MOUSE.BMP, PARTBIN.BMP).
The BIN chunk has a small header of one byte compression algorithm followed by a UINT32LE giving the uncompressed size. It supports the same compression algorithms as Dynamix fonts. The header is immediately followed by the compressed 4bpp pixel data.
This format is the 4 bit per pixel version of the BIN/VGA-containing BMP format found in several 8-bit Dynamix games.