Extracting Rollercoaster Tycoon sprites

Posted on Mon 10 April 2017 in Software

Example of a palette

Recently I wanted to extract the sprites from Rollercoaster Tycoon 2. For those that don’t know the game, it is an older tycoon game, where you need to build up a theme park, with the specific focus on roller coasters. It is a 2D isometric game, but the view can be rotated.

The image data for most of the sprites are stored in a file called g1.dat. This files uses its own special format to store the image data. Luckily, the format is documented here. This guide in particular was invaluable in writing the script. I’m repeating the info given there, but a bit more concise.

To obtain the g1.dat file you will have to buy the game from Amazon or GOG. There is also an open source implementation called OpenRCT, but you still need the original files.

Extracting the sprites

The g1.dat file starts with 2 integers, containing 4 bytes each. The first integer is the number of sprites in the file, the second is the length of the file.

After these two integers there is an entry for each of the sprites. Each entry is 16 bytes long and contains:

Start Address Size (bytes)
Sprite Width 4
Sprite Height 2
X Offset 2
Y Offset 2
Flags 2
Padding 2

The start address is offset from the end of all the sprite entries. So the first sprite will have an address of 0. The width and height are obvious; they are the width and height of the final sprite. The X Offset and Y Offset are used when calculating where to draw a sprite occupying a specific tile. The flag is used to determine the sprite type and encoding, and the padding is to align the entry to 16 bytes. There are 5 flag types that I found in the file:

Flag Meaning
1 or 17 Plain sprite
5 or 21 Compressed sprite
8 Palette

Plain Sprites

Plain sprites are easy: The pixel data starts at the end of the entry data plus the offset. The pixel data contains sprite_width x sprite_height pixels, all of which are stored as a single byte. The values are indexes to the palette being used. To obtain the RGB value for the sprite, look up the RGB value in the palette.

Compressed Sprites

Compressed sprites are a bit harder:

The sprite is stored as a series of scan elements. There can be more than one element per line of the image, or even none at all.

The image definition starts with a list of offsets for each scan element. The addresses are 2 bytes long and are relative to the start address of the sprite.

After the offset positions the scan elements are stored. Each element begins with two bytes: The first indicates the number of pixels, the second the offset from the start of the row. The top bit of the first byte also indicates if it is the last element of the row. If the bit is set, it is the last element. After the two bytes there are single bytes which are used to look up the RGB values.

Palettes

The file contains various palettes. The length of the palette is calculated by Sprite Width x 3. The palettes are stored as Blue, Green and Red values (BRG).

Example of a palette

This is one of the palettes in the file, which is also used for decoding the sprites. The other palettes are smaller, often only containing one colour. I have no idea what they are for.

The Script

I wrote a script to extract any subset of the sprites to a png image. The script also writes the sprite metadata to a file, and writes the sprite number next to the sprite. It is available at https://github.com/KolijnWolfaardt/RCT_tools/blob/master/exportSprites.py.

Some track pieces

The script requires numpy and PIL to run. The script has a bunch of flags that can be set:

Filename -f or --file

This sets the location of the g1.data file. Per default, the script will just search in the current directory.

Sprite Start -s or --sprite-start Sets the sprite to start from

Sprite End -s or --sprite-end

Set the sprite to end. Must be larger than Sprite Start

Output File Width --out-width

Sets the width of the ouput file.

Output File Height --out-height

Sets the Height of the output file.

Palette -p

This can be used to set a customized palette file. The file has to be a set of comma seperated interger values. Each line represent one color, with a total of 255 in the file.

Custom Colors -c1 and -c2

There are certain sprites with colours determined by the user. These include parts such as the rollercoasters, tracks and their supports. There are two colours that can be customized, and these can also be set, by using the -c1 and -c2 flags.

Verbose -v

You can tell the script to produce more output by setting this flag.

First Run

The palettes that are used for the images are stored inside the g1.dat file. The first time the script is run it will store the palettes in the palettes folder. These will be re-used later to determine the colours.

NFO File

The script also stores the following details about the sprites in an .nfo file in the ouput directory.

  • Output File Number
  • Sprite number in the file
  • Total Sprite number
  • Address in the file (as a hex value)
  • Sprite Width
  • Sprite Height
  • xOffset
  • yOffset
  • flags
  • imagePosX
  • imagePosY

These values are stored as comma seperated values. For example, the output for the image above looks like this:

These values are stored as comma seperated values. For example, the output for the image above looks like this:

0, 0, 1915, 106b34, 64, 31, -32,  0, 21,   3, 7,
0, 1, 1916, 106fb0, 64, 32, -32, -1, 21,  99, 7,
0, 2, 1917, 107440, 64, 16, -32,  0, 21, 195, 7,
0, 3, 1918, 1076a0, 62, 16, -32, -1, 21, 291, 7,
0, 4, 1919, 1078e0, 64, 32, -32, -1, 21, 387, 7,
0, 5, 1920, 107d70, 64, 32, -32, -1, 21, 483, 7,
0, 6, 1921, 108210, 62, 16, -30, -1, 21, 579, 7,

Anyway, that's it!

End Image