Monster Bash Sprite Format
A Monster Bash sprite contains multiple 16-colour EGA images (for example, an enemy sprite contains all the animation frames for that sprite.) This format is trickier than most, as it contains an arbitrary number of colour planes - the EGA contains four colour planes (red, green, blue and intensity) but each plane in the Monster Bash sprite can refer to multiple EGA planes at the same time (so you could have a "purple plane" in the file, which puts data into the EGA red and blue planes at the same time!)
All sprite files begin with a single 0xFF byte. To correctly detect this file format, the following tests can be used:
- The last image block should end at the end of the file, there should be no trailing data (except a possible trailing 0x00 depending on the decompressor used)
- The last byte in each image block should be 0x00 to signal the final plane of image data
A number of image blocks immediately follow the signature byte, repeating until EOF:
|UINT16 iSize||Size of this sprite|
|BYTE cData[iSize]||Block of image data, iSize bytes long|
Each of the blocks of image data in the above structure contains an image header, followed by multiple planes of data. The header looks like this:
|UINT8 iHeight||Height of the image in pixels|
|UINT8 iWidth||Width of the image in pixels|
|UINT8 iReserved||Ignored, always set to zero|
|INT16LE iHotspotX||Image is offset so that this pixel appears at the object position|
|INT16LE iHotspotY||Image is offset so that this pixel appears at the object position|
|UINT16LE iRectX||Right-coordinate of collision rectangle (object position is left-coordinate)|
|UINT16LE iRectY||Bottom-coordinate of collision rectangle (object position is top-coordinate)|
The "object position" as tracked by the game is the top-left corner of the collision rectangle. iRectX and iRectY indicate the bottom-right corner of the collision rectangle. iHotspotX and iHotspotY indicate how far up and to the left the image should be shifted, with the end result being that the "hotspot pixel" will appear at the object location. Tweaking these variables allows the collision rectangle to be set arbitrarily, with the only restriction being that the object location tracked by the game will always be part of the collision rectangle.
After this header, a number of data planes follow one after the other. Each plane of data can contain the image data for multiple EGA planes. For example, if EGA plane 1 is blue and EGA plane 2 is green, then a single Monster Bash plane can have a bit flag for 3, which means the data is for EGA plane 1 and 2 (
1 | 2 == 3).
The very first plane in the image is always transparency, but it can also contain data for the other EGA planes (in fact it must, because a plane identifier of 0x00 means there is no more data to read.) Each plane is of the following structure:
|UINT8 iPlaneBits||Which colours this plane holds|
|BYTE cPlaneData[iPlaneSize]||Plane data, one bit-per-pixel|
iPlaneSize is the size of the data in the plane. In order to calculate this, you must take the width of the image (in pixels, obtained from the frame's header above) and round this up to the nearest byte boundary. For example, if the image is eight pixels wide, it will take up eight bits which fits in one byte. If the image is nine pixels wide, this will take up nine bits, so two bytes are required to hold each scanline of data. The plane size is then the number of bytes in a scanline multiplied by the image height. You can use a formula like one of the following to calculate the various values, where width and height are the image dimensions in pixels:
bits per scanline = width + (8 - (width % 8)) bytes per scanline = (width + 7) / 8 bytes per plane = bytes per scanline * height
An image that's 56x99 pixels, should have a plane size of 693 bytes (7 bytes per scanline.)
iPlaneBits indicates which EGA planes the data applies to. If this value is zero the end of the data has been reached. If for example the value is 0x06, this means the plane should be drawn on EGA plane 0x02 and EGA plane 0x04.
This means a given EGA plane (say red, or green) can be updated by multiple image planes. To handle this, each plane's data should be applied using an XOR operation, so that a one-bit in the incoming plane will flip the bit for that colour in the image (so it may get flipped on in one plane, but flipped off again when the next plane is read in.)
Once the image plane identified by 0x00 has been reached, the rendering of the image is complete. There is no data following iPlaneBits in this case.
- It is unknown whether the number of image planes is stored in the data file. The only known way of precalculating this is by working it out from the size of the image. The alternative of simply reading in image planes until one for bit 0 is reached is also undesirable, as a corrupt image could result in the program reading far too much data out of the file. Even a rough calculation of the number of image planes will act as a safeguard against this.
The following tools are able to work with files in this format.
|Name||Platform||View images in this format?||Convert/export to another image file/format?||Import from another image file/format?||Edit metadata?|
This file format was reverse engineered by Malvineous. If you find this information helpful in a project you're working on, please give credit where credit is due. (A link back to this wiki would be nice too!)