Extracting Rollercoaster Tycoon sprites
Posted on Mon 10 April 2017 in Software
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).
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.
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!