The RollerCoaster Tycoon 3® Non-OVL File Format

Belgabor


Table of Contents

1. The Header
2. The Variable Declaration
3. The Data
4. License/Credits

Coypright (C) 2007 Belgabor (document), some rights reserved (see below). The file format itself has probably been designed by Frontier, so cannot and do not claim Copyright for it :-).

This document explains what I found out about the file format Frontier used for most files that are not OVLs, which includes for example park files, track files and firework effects. These files all follow basically the same scheme and come in three versions. The files that use the respective versions have led me to suspect that the three versions were introduced in original RCT3, Soaked! and Wild! respectively, although I'm not fully sure about the timing of v2 and v3 as many Soaked! files have are v3.

The first version (v1) has only two sections (Section 2, “The Variable Declaration”, Section 3, “The Data”), the header (Section 1, “The Header”) is missing. Version 2 and 3 have three sections. The versions do not match to any specific file type, for example the game has particle effects of all three versions.

All structures will be shown in C-ish pseudocode.

1. The Header

As written above the header is only present in v2 and v3 files. It consists of a first part that is present in both versions and a second part that's only present in v3 files.

typedef struct {
	uint8 nullbytes[8];     // Always 0x00
	uint8 formatbyte;       // 0x1F for v2 and 0x2F for v3
	uint8 alwaysDA;         // Always 0xDA
	uint8 always1E;         // Always 0x1E
	uint8 alwaysF1;         // Always 0xF1
	uint32 parameters[13];  // Unknown purpose. Some seem to relate to the file coming from Soaked! or Wild!
} Header1;
typedef struct {
	uint32 unknown;      // 0x00000000 for park-ish files (parks, most scenarios and tutorials), 0xFFFFFFFF for other files
	uint32 ffbytes;      // Always 0xFFFFFFFF
	uint8 nullbytes[8];  // Always 0x00
} Header2;

Note

As the difference between a full v2 and a full v3 header are 16 (0x10) bytes, it's highly probable that Header1.formatbyte is an offset or length of some kind.

2. The Variable Declaration

This section describes the classes and structures the data part (Section 3, “The Data”) can use. Version 1 and 2 files seem to declare almost everything RCT3 can use (although the amount is not consistant). On the other hand v3 files seem to only declare what's necessary for the file type at hand.

All strings in this section are AscII long pascal style strings, essentially described by this declaration:

typedef struct {
	uint16 charactercount;
	char content[charactercount];
} DeclarationString;

To emphasize, they are not 0-terminated!

Here is how this section looks like:

uint32 classdeclarationcount;
ClassDeclaration classdec[classdeclarationcount];

Here's how a ClassDeclaration looks like:

typedef struct {
	DeclarationString classname;
	uint32 variablecount;
	VariableDeclaration variable[variablecount];
} ClassDeclaration;

And finally VariableDeclaration:

typedef struct {
	DeclarationString variablename;
	DeclarationString datatypename;
	uint32 datatypesize;       // Size of the data type in bytes
	uint32 subvariablecount;   // Number of subvariables for some data types
	VariableDeclaration subvariable[subvariablecount];
} VariableDeclaration;

The datatypesize parameter is a numerical representation of the data type named in datatypename. A 0 seems to indicate a data type of variable size. Here is a list of data types I found so far:

datatypenamedatatypesizeComment
array0Has 1+ subvariables. Seems to imply a variable amount of struct
bool1 
graphedValue0Seems to indicate graphed coaster test data
float324 
int324 
list0Has subvariable(s? so far only seen one). No idea what makes it different from array.
managedobjectptr8see reference
orientation12 
reference8In-file reference to a class data block (ClassDataHeader.refmarker)
string0Strings in the data section are utf16 encoded
structsum of subvariablesHas 1+ subvariables. datatypesize is 0 if any subvariables are of varible size.
uint81 
vector312 

3. The Data

The data section seems to be nearly identical among versions. It is separated in to class blocks and starts with the number of these blocks:

uint32 classdatablockcount;

Followed by the class data blocks themselves. Each class block starts with a header:

typedef struct {
	uint32 classid;     // Identifies which class declaration applies (classdec[classid])
	char refmarker[8];  // Used for in-file references.
} ClassDataHeader;

The header is followed by the actual data as described in the class' declaration.

All data is represented as a raw little endian data type of the respective length with the following exceptions (incomplete):

array
typedef struct {
	uint32 arraylength;         // in bytes. Includes the 4 bytes of the itemcount
	uint32 itemcount;           // number of items in the array
	ArrayItem item[itemcount];  // ArrayItem as defined in the declaration section
} ArrayData;

An empty array has arraylength = 4 and arraycount = 0.

managedobjectptr, reference
typedef struct {
	char reference[8];  // references a class data block by it's ClassDataHeader.refmarker
} ReferenceData;

Can in some cases be read as a 0-terminated string, but not always.

string
typedef struct {
	uint32 stringlength;                // in bytes. Start marker is included.
	uint8 startmarker[4];               // All 0xEF
	wchar content[(stringlength-4)/2];  // UTF16, little endian
} StringData;

An empty string has stringlength = 4 and the startmarker.

struct
typedef struct {
	uint32 structlength;          // in bytes. Full size of the content
	uint8 content[structlength];  // Data as defined as subvariables in the declaration section
} StructData;

After the data, there is a last value in v2 and v3 files:

uint32 unknownend;

The exact purpose is unknown purpose, maybe some kind of checksum. It is too big to indicate a size inside the file.

4. License/Credits