Westwood CPS Format

From ModdingWiki
Jump to: navigation, search
Westwood CPS Format
Westwood CPS Format.png
Format typeImage
HardwareVGA, Amiga
Colour depth8-bit (VGA), 5-bit (Amiga)
Minimum size (pixels)320×200
Maximum size (pixels)320×200
PaletteInternal / External / Shared
Plane count1 (PC), 5 (Amiga)
Transparent pixels?Palette-based
Hitmap pixels?No
Games

CPS files are images with fixed 320×200 dimensions. They are 8-bit (256 color) images in the PC version of the format, and 5-bit (32 colors) in the Amiga version. They are used both as sprite sheets and as full-screen background images. Depending on the use, they may use index 0 on the palette as transparent index.

The images can be compressed with different compression methods, and may or may not contain an internal palette. In some games, different elements on the same image may need to be viewed with different palettes. In the Amiga version, there is a system to actually embed up to four different palettes for this purpose.

File format

Header

The file starts with the following header:

Data type Name Description
UINT16LE FileSize File size. For compression method 0 and 4, this only counts the bytes behind this value, making it two bytes less than the actual file size.
UINT16LE CompressionType Compression type.
UINT32LE UncompressedSize Uncompressed size.
UINT16LE PaletteSize Palette size. Always empty in Amiga Eye of the Beholder 1 files.

If PaletteSize is not 0, this is followed by an array of PaletteSize containing the embedded palette(s). The rest of the file is the compressed data.

Note that Westwood uses this same header format for other compressed files as well, often using the .CMP file extension. One notable example is the Lands of Lore SHP format, which is basically just a CPS file containing an entire Dune II v1.07 SHP file instead of raw image data.

Compression types

The five different used compression methods are:

Uncompressed size

The UncompressedSize is the buffer size needed to uncompress the compressed data in, but besides that, it also gives an important indicator of the type of image:

  • For PC files, this should always be 64000, since the data contains a linear 8-bit 320×200 image.
  • For Amiga files, the uncompressed size can be either 40000 or 40064. The first 40000 bytes are the image data, in five 8000-byte bit planes. The optional additional 64 at the end are an Eye of the Beholder 1-style embedded palette.

Embedded colour palette

PaletteSize gives the size of the palette. This can either be 0, for no palette, 768 for a PC 256-colour 6-bit RGB VGA palette, or a multiple of 64 for up to four palettes for Amiga multiple-screen CPS files.

In the Eye of the Beholder 1 Amiga format, this value is never filled in. Instead, the existence of a palette is indicated by having an UncompressedSize of 40064, indicating 64 bytes of palette at the end of the uncompressed data.

Amiga specs

A multi-screen Amiga CPS file. Each of the four images uses a different 64-colour palette.

After image decompression, the size of the image data is 40000 bytes. As mentioned in the previous sections, Eye of the Beholder 1 files may have an additional 64 bytes at the end which should be read as embedded palette.

The 40000 bytes of image data contain five 320×200 bit planes. Each bit plane is 320 * 200 / 8 = 8000 bytes long, which is standard for Amiga graphics data.

Multi-screen files

In the Amiga version of Eye of the Beholder II, there is a special type of CPS file that has multiple 32-colour palettes, up to a maximum of four, and which contains images that each take up a quarter of the full image. Each palette corresponds to one image, and the number of palettes indicates the amount of images. These images are 160×96 in size, meaning an 8-pixel border at the bottom is not used. The border could technically be interpreted as belonging to the lower 2 images, making them 160×104, though in reality all such images are only displayed in a 160×96 area.

Note that not all files with multiple palettes are indeed multi-screen files. A notable example is STREET1.CPS in Eye of the Beholder II, where the four palettes are used on the full frame, and create a lightning flash effect on the image.

Palette data

Palettes in Amiga format are 32 colours each. Every entry in the palette takes up 16 bits, or two bytes, making the full palette 64 bytes long. The colours have the following format:

Bit FEDCBA98 76543210
    ____RRRR GGGGBBBB

In other words, they are big-endian 12 bit RGB colours, in XRGB format.

Image data

The resolution of the images is the same as in the PC version. However, since we only use one of 32 colors for a pixel, we only need 5 bits to store it. These bits are split up in five bit planes, meaning the 40000 bytes are actually five blocks of 8000 bytes. The color index of each pixel is made up of the corresponding bits at the same bit position in all five bit planes.

Each bit plane could be interpreted as a 1 bit per pixel 320×200 image, meaning each byte in a plane contains the data of 8 pixels, and the width of one line in a bit plane is 320/8 = 40.

To get a pixel at location (x,y), you would need to take the byte at x / 8 + y * 40 in each bit plane, take the bit at 7 - (x % 8) from those, and combine those bits to one byte, with the plane number giving the index of each bit. However, if seen as a compact byte array of 8-bit pixels, a simple pixel index can be used instead of x and y, which makes the byte index in the bit planes i / 8, and the bit index 7 - (i % 8).

Reading code

Pseudo-code for a 5-bit planar to 8-bit indexed implementation. The final data needs to use the correct 64-colour palette to correctly show the image.

// Filled in from the file
unsigned char bitPlanes[5][8000];
// New buffer for 8-bit indexed image
unsigned char imageData8bit[64000];
// Loop over all pixels
for (int i = 0; i < 64000; i++)
{
    int bytePos = i >> 3; // Bitwise optimisation of 'i / 8'
    int bitPos = 7 - (i & 7); // Bitwise optimisation of '7 - (i % 8)'
    imageData8bit[i] = (((bitPlanes[0][bytePos] >> bitPos) & 1) << 0) |
                       (((bitPlanes[1][bytePos] >> bitPos) & 1) << 1) |
                       (((bitPlanes[2][bytePos] >> bitPos) & 1) << 2) |
                       (((bitPlanes[3][bytePos] >> bitPos) & 1) << 3) |
                       (((bitPlanes[4][bytePos] >> bitPos) & 1) << 4);
}

Writing code

Pseudo-code for an 8-bit indexed to 5-bit planar implementation. Obviously, this conversion only works if the data in the 8-bit source image doesn't contain any values larger than 63.

// From loaded data
unsigned char imageData8bit[64000];
// New buffer for bit planes
unsigned char bitPlanes[5][8000];
// Loop over all pixels
for (int i = 0; i < 64000; i++)
{
    int bytePos = i >> 3; // Bitwise optimisation of 'i / 8'
    int bitPos = 7 - (i & 7); // Bitwise optimisation of '7 - (i % 8)'
    unsigned char curByte = imageData[i];
    bitPlanes[0][bytepos] = bitPlanes[0][bytepos] | (((curByte >> 0) & 1) << bitPos);
    bitPlanes[1][bytepos] = bitPlanes[1][bytepos] | (((curByte >> 1) & 1) << bitPos);
    bitPlanes[2][bytepos] = bitPlanes[2][bytepos] | (((curByte >> 2) & 1) << bitPos);
    bitPlanes[3][bytepos] = bitPlanes[3][bytepos] | (((curByte >> 3) & 1) << bitPos);
    bitPlanes[4][bytepos] = bitPlanes[4][bytepos] | (((curByte >> 4) & 1) << bitPos);
}

Tools

The following tools are able to work with files in this format.

Name PlatformView images in this format? Convert/export to another file/format? Import from another file/format? Access hidden data? Edit metadata? Notes
Engie File Converter WindowsYesYesYesN/AN/A Supports the multi-palette Amiga files as a type with multiple frames. Does not handle them as multi-palette full images, as is required for STREET1.CPS.
XCC Mixer WindowsYesYesYesN/AN/A Only supports the LCW-compressed PC type. Can only convert to PC CPS with included palette.
Mix Manager DOSYesYesYesN/AN/A Only supports the LCW-compressed PC type.