The Sims™ Technical Aspects

SPR2 Resource Format


The following information is not based on any proprietary knowledge or restricted documentation—it was entirely derived from observation, experiment, and public information, thus it may be inaccurate or incomplete.

Analyzed by Greg Noel.

The SPR2 resource format is an enhanced version of the SPR# resource format. It supports up to three channels of information (eight-bit color, eight-bit depth, and alpha blending) allowing more complex objects, with contour and translucency, than are provided by the SPR# format.

In the description below, integers are in little-endian order (least significant byte first).

SPR2 layout
Offset Size Value
0 4 Version
4 4 Frame count (N)
8 4 Palette ID
12 4 * N Offset table
var var Frame 0
var var Frame 1
var var . . .
var var Frame N-1

Version is always 1000.

The frame count says how many sprite frames (separate graphical image pieces) there are in this sprite. Each image is effectively distinct, but the frames within a single sprite are traditionally the same image at different scales. The DGRP resource combines multiple frames from multiple sprites into a single composite image; it is this image that appears in the game.

A sprite can use a maximum of 256 colors. The palette ID is the ID of an associated PALT resource within the same .iff file as the SPR# resource that is used to convert the 8-bit color index into a full 24-bit color.

The Nth offset table entry is the number of bytes from the begining of the resource to the data for the Nth frame. Since frames are variable length, this makes it possible to go directly to the the Nth frame without having to scan over the intervening entries.

Each sprite frame is a distinct image. Traditionally, the different frames within a sprite are different sizes (zoom factors) of the same item, but this is not a requirement and it would be possible to put completely unrelated images together if desired.

SPR2 Frame
Offset Size Value
0 2 Width
2 2 Height
4 2 Flags
6 2 unknown, zero
8 2 Palette ID
10 2 Transparent pixel
12 2 Y loc
14 2 X loc
16 2 Row header
18 var Row segments
var 2 Row header
var var Row segments
var var . . .
var 2 Row header
var var Row segments
var 2 End marker (0xA000)

The width and height tell how big the image is. There are height rows of width pixels in the image.

The flags are almost certainly which channels were encoded. Bit 0x01 says that the image channel was encoded, bit 0x02 says that the depth channel was encoded, and bit 0x04 says that the alpha channel was encoded. Realistically, this means that the only possible flag values are 1, 3, or 7, since the earlier channels are prerequsits for the latter channels.

The unknown value is always zero.

The palette ID gives the frame a chance to override the default palette ID, but this is never done. This value is always either the same as the default or the null value 0xA3A3. It is not known if the occurances of the null value were intentional.

The transparent pixel value is the index into the palette to use when filling background.

If this frame is a part of a composite image, the X and Y location can be used to position this piece of the image within that larger image.

The rest of the resource describes how to fill in the sprite frame image. It consists of information for exactly height rows plus an end marker.

Each row header is a two-byte little-endian integer. This integer probably has a code in the high-order three bits and a count (N) in the low-order thirteen bits. The only two valid codes are zero and four, so the sign bit can be used to distinguish the two cases. The alternative is that the the sign bit distinguishes the two cases and the count is the remaining fifteen bits. This alternative is less satisfying for reasons described below in the section on Decoder Strategies.

If the code is four, the count is the number of rows to fill in with background. In other words, the entirety of the next N rows are transparent. There are no row segments associated with this encoding, so the next row header occurs immediately.

If the code is zero, the count is the number of bytes of compressed data (including the two bytes of the row header) to describe the image information for one row, so that the next row header occurs N bytes after this one.

The compressed data consists of one or more row segments each of which describes successive parts of the image. If the row segments do not describe the full width of a row, the remainder of the row is filled with background.

The end marker contains the little-endian value 0xA000. That is, the high-order three bits contain the value five and the remaining bits are zero.

SPR2 Row Segment
Offset Size Value
0 2 Code and count
2 var Pixel data
var opt Alignment byte

The code and count field describes what to do with the next portion of the image line. It is a two-byte little-endian integer. The high-order three bits are a format code (described below) and the low-order thirteen bits are a count of how many pixels are affected. The code and count field are followed by zero or more bytes of data, depending on the format code.

The (known) fnown format codes and their meanings are given in this list:

Code one has two bytes of data per pixel, the depth and the palette color index.

Code two has three bytes of data per pixel, the depth, the palette color index, and the alpha blending. If the pixel count is odd, an alignment byte with value 0xB0 pads the length to even.

Code three has no pixel data. The pixel count is the number of pixels that are transparent (show the background).

Code six has one byte of data per pixel, the palette color index. If the pixel count is odd, an alignment byte with value 0xB0 pads the length to even.

Decoder strategies

When writing a decoder for this format, there are two basic strategies that can be followed: Either go through each row, rendering all the segments in the row before going to the next row, or go through each code, rendering each segment as it is encountered and using the line codes as psuedo-segments to keep the image aligned.

Let's consider those two strategies in more detail.

Each strategy has its strengths and weaknesses. Whichever strategy is used, the time to transfer the pixels will dominate performance, so the choice of which to use will likely be driven by the interface requirements—an application that just displays an image may want the whole image instantiated at once, while an application that incorporates several images into a larger image may want to look at the images all together line-by-line in order to minimize the space impact.


Reminder: This information is not based on any proprietary knowledge or restricted documentation—it was entirely derived from observation, experiment, and public information, thus it may be inaccurate or incomplete.

Valid XHTML 1.1! Valid CSS!
Copyright © 2001-2008 Dave Baum and Greg Noel. All rights reserved.
The Sims™ is a trademark of Maxis and Electronic Arts.
This page was last modified Sunday, 27-Oct-2002 12:50:25 UTC.
Made on a Mac
SourceForge