The Sims™ Technical Aspects

SPR# 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 SPR# resource format is an older version of the SPR2 resource format. It supports only a single channel (8-bit color), but it can encode some types of image more efficiently than SPR2, so it continues to be used in flat, single-plane objects (such as walls, floors, windows, doors, and thought balloons) where there is no need for the additional channels.

In the description below, integers can be either big-endian or little-endian. All of the integers in a given resource are of the same type, so all that has to be done is initially determine which type is present and decode all remaining integers the same way. The first value in the resource, the version, is always less than 65,535, so it is sufficient to look at the first two bytes. If the first two bytes are zero, it's big-endian, otherwise, it's little-endian.

SPR# 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

At least four versions (with values 502, 503, 504, and 505) have been found. Usually, different versions mean that there are structural differences (fields added or meaning changed). However, in this case, all of the versions appear to have the same structure (that is, they all are decoded identically), so it's not known what the different versions mean.

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.

SPR# Frame
Offset Size Value
0 2 unknown, zero
2 2 unknown, zero
4 2 Height
6 2 Width
8 2 unknown
10 2 Row header
12 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 (05 00)

Two unknown values, always zero. Possibly X and Y locations of initial position.

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

Unknown, either 00 00 or 00 10. The value is the same in both big-endian and little-endian modes, suggesting that this value is part of the byte stream that follows rather than an integer, but the meaning is unknown.

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 two bytes. The first byte of the row header is the encoding. There are two types of encoding, represented by the codes 0x09 and 0x04. The second byte is a count (N).

If the the encoding is 0x09, 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 the encoding is 0x04, 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 value 0x05 in the first byte and the second byte is zero.

SPR# Row Segment
Offset Size Value
0 1 Format code
1 1 Pixel count
2 var Pixel data
var opt Alignment byte

The format code and pixel count describe what to do with the next portion of the image line. Each field is a one-byte value. The format code is described below; the pixel count tells how many pixels are affected. The code and count are followed by zero or more bytes of data, depending on the format code.

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

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

Code two has two bytes of pixel data. The first byte is the palette color index to fill into the next number of pixels. The second byte is always the same value as the first byte; it is apparently unused: tests show that the second color index is not alternated with the first, for example.

Code three has one byte of data per pixel, the palette color index. If the pixel count is odd, an alignment byte with value zero 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:24 UTC.
Made on a Mac