NFS HS Track File Formats - V0.03, (c) Denis Auroux and Vitaly Kootin 1999
This help page describes the structure of NFS High Stakes track files.
DISCLAIMER : The information below is unofficial, and may be inexact by some aspects. It is only intended as reference information to help other NFS developers, and comes with no warranty. It may not be used for commercial purposes. It is forbidden to distribute a modified version of this document.
Contents :
1. FLOATPT structure (size : 12 bytes)
typedef struct FLOATPT {
float x,z,y;
} FLOATPT;
This structure is used to store the three coordinates of a vertex. Each of the components is stored as a floating-point number (IEEE single-precision format). The x and y axes are horizontal, while the z axis is vertical and points upwards.
2. INTPT structure (size : 12 bytes)
typedef struct INTPT {
long x,z,y;
} INTPT;
This is another way to store the three coordinates of a vertex (less frequently used than the FLOATPT structure). Each of the components is stored as a 32-bit integer, and must be divided by 2^16 in order to obtain the corresponding floating-point value (in other words, the coordinates are 16.16 fixed precision numbers).
3. POLYGONDATA structure (size : 13 bytes)
typedef struct POLYGONDATA {
short vertex[4];
short texture;
short flags;
unsigned char animInfo;
} POLYGONDATA;
This structure describes a polygon. The vertex
member refers to four entries in the vertex table associated with the polygon (either the vertex table of the TRKBLOCK structure in the case of a POLYBLOCK polygon, or that of the XOBJDATA structure in the case of an XOBJDATA polygon), describing the coordinates of the four polygon vertices. The texture
member directly references a texture in the QFS file.
If texture
is greater than 2047, it is to be interpreted as a road lane texture (dotted or solid line,
yellow or white) : after subtracting 2048 to the texture number one gets the code of the road lane texture (see NFS3). Otherwise, the texture number directly points to an entry in the QFS file. However, some of the textures in the QFS file are to be skipped, namely those which contain after their local palette a comment (i.e. a piece starting with header ID 0x6F) whose text is the string "<mirrored>" ; the correct texture is therefore the texture
-th bitmap of the QFS file which does not have the "<mirrored>" comment.
Unlike NFS3 where the first vertex of a track polygon is forward left, followed by forward right, backward right and backward left, the NFS HS ordering is forward right, forward left, backward left, backward right (the mirror image).
The flags are as follows : (flags>>2)&3
indicates the multiple of 90° by which the texture should be rotated (0 for no rotation, 1 for 90°, 2 for 180°, 3 for 270°) ; a non-zero value of flags&0x10
indicates that the texture is horizontally flipped ; a non-zero value of flags&0x20
indicates that the texture is vertically flipped. The value of (flags>>6)&7
indicates the scaling factor : 0 is no scaling ; 1 means that the texture is tiled twice horizontally ; 2 that the texture is tile twice vertically ; 3 indicates 4x horizontal tiling, 4 indicates 4x vertical tiling. Finally, a non-zero value of flags&0x8000
indicates that the polygon is one-sided.
The texture is animated if the animInfo
member is non-zero ; in that case the LSB part indicates the length of the animation, and the MSB part indicates the periodicity.
4. ARRAYPTR structure (size : 8 bytes)
typedef struct ARRAYPTR {
long nObjects;
void* pObjects;
} ARRAYPTR;
This structure is used to describe a collection of objects : the nObjects
member contains the number of objects, while the pObjects
member merely contains a 32-bit space to be filled with a pointer to the objects themselves (which are stored elsewhere in the file).
1. VROADBLOCK structure (size : 84 bytes)
This structure stores the virtual road data (road position, width, orientation) for a track position.
typedef struct VROADBLOCK {
struct FLOATPT nodePt;
struct FLOATPT normal,forward,right;
float leftWall,rightWall;
float unknown[2];
short extraNeighbors[2];
long unknown[4]; // first 2 are lane widths, last 2 are flags
} VROADBLOCK;
There is one virtual-road block record for each track node ; there are usually 8 track nodes (and therefore 8 VROADBLOCK records) for each FRD track block.
The refPt
member stores the coordinates of the node‚s reference point (a point along the road). The normal
vector points upwards, the forward
vector points forward, the right
vector points to the right ; their components are now three floating-point numbers, with normalization to unit length (note that these values are multiples of 1/128). The leftWall
and rightWall
members indicate the distance between the node‚s reference point and the left and right track walls (this distance is counted along the direction pointed by the right
vector).
The two extraNeighbor
values are usually equal to -1, but can be used to specify up to two neighbouring nodes other than the previous node and the following node (e.g. if there are shortcuts).
2. TRKBLOCK structure (size : 1512 bytes)
typedef struct TRKBLOCK
{
long nPolygons[11];
POLYGONDATA* pPolygons[11];
long nVertices;
long nHiResVert,nLoResVert,nMedResVert;
long nVerticesDup;
long nObjectVert;
FLOATPT* vertices;
long* shadingVertices;
struct FLOATPT ptCentre;
struct FLOATPT ptBounding[4];
struct NEIGHBORDATA nbdData[300];
struct ARRAYPTR xobj[4];
long nVroad;
struct FLOATPT minPt,maxPt;
POLYVROADDATA* pVroad;
long nPositions;
struct ARRAYPTR refxobj;
struct ARRAYPTR refobj0;
struct ARRAYPTR soundsrc;
struct ARRAYPTR lightsrc;
long neighbors[8];
} TRKBLOCK;
This structure describes a track block ; it describes how many items of each type the block contains, and provides pointers to these data. Note that in order to speed up file access the data structure stored in the FRD file contains 32-bit memory pointers ; these values are to be ignored and replaced by return values of subsequent malloc() calls at load time.
The structure starts with eleven polygon chunks : seven for the track structure, and four for the track‚s polygon objects. Polygon chunk 0 lists the low-resolution track polygons ; chunk 2 lists the medium-resolution track polygons ; chunk 4 lists the high-resolution track polygons ; and chunk 6 lists the polygons making up the lines between road lanes. Chunks 1, 3 and 5 contain polygons making up fences and other transparent stuff : chunk 1 corresponds to low resolution, chunk 3 to medium resolution, and chunk 5 to high resolution. Chunks 7, 8, 9 and 10 correspond to the polygon objects. The nPolygons
member contains the number of polygons in each chunk, while the pPolygons
member provides room for pointer storage.
The nVertices
and nVerticesDup
values are always equal, and give the total number of vertices to be stored in the vertices
member of the TRKBLOCK structure. Vertices 0
to nObjectVert-1
are the vertices used by the polygon objects (chunks 7,8,9,10). Vertices nObjectVert
to nLoResVert-1
are used in polygon chunks 0 and 1, and correspond to the track vertices in low resolution. Vertices nLoResVert
to nMedResVert-1
are the additional vertices used in chunks 2 and 3, and correspond to the track vertices in medium resolution (the low-resolution vertices are also used in medium resolution). Vertices nMedResVert
to nHiResVert-1
are the additional vertices used in chunks 4 and 5, and correspond to the track vertices in high resolution (the low- and medium-resolution vertices are also used in high resolution). Finally, vertices nHiResVert
to nVertices-1
are used in chunk 6, and correspond to the road lanes. The shadingVertices
member describes the shading applied to each vertex : for each vertex, the least significant byte is the blue component, the next byte is the green component, the next byte is the red component, and finally the most significant byte is the alpha value. The structure contains two pointers used to access the vertex coordinate table and the vertex shading table.
The ptCentre
member provides the coordinates of a central reference point around which the block is built. The ptBounding
member provides the coordinates of four bounding points for the block vertices : these points delimit a horizontal rectangle inside which all the block‚s elements fit.
The four xobj
array-pointers indicate how many extra-objects are contained in each of the four extra-object chunks associated with the track block, and provide the corresponding pointer space.
The nVroad
member indicates how many POLYVROADDATA structures are used in the block to describe the virtual road parameters of the track polygons ; the pVroad
member provides pointer space.
Normally each block corresponds to 8 node positions, except the last one : the number of nodes in the block is given by the nPositions
member. The node positions refer to the VROADBLOCK records at the beginning of the FRD file.
The four array-pointers refxobj, refobj0, soundsrc
and lightsrc
are used to access the various corresponding substructures of types REFXOBJ, REFPOLYOBJ, SOUNDSRC and LIGHTSRC respectively (see below). The number of REFXOBJ entries is the number of non-animated (type 4) extra-objects stored in the four extra-object chunks associated with the track block. The number of REFPOLYOBJ entries is the total number of objects (both polygon-objects and extra-objects) in object chunk 0.
The neighbors
member lists blocks with which the current block is in direct contact. Usually these are the previous block and the following block. The unused entries contain -1.
The minPt
and maxPt
members, whose z coordinates are unused, store in their x components respectively the minimum and maximum x coordinates of any vertex belonging to a non-type14 high-resolution road polygon (i.e. a polygon for which there is a POLYVROADDATA entry). Similarly the y components of minPt
and maxPt
contain the minimum and maximum y coordinates of any vertex belonging to a non-type14 high resolution road polygon. These two members are used by the POLYVROADDATA entries.
3. NEIGHBORDATA structure (size : 4 bytes)
typedef struct NEIGHBORDATA {
short block;
short unused;
} NEIGHBORDATA;
This structure is used to list the block numbers corresponding to the neighbors of the currently described block, starting with the closest ones. There are always 300 such entries in the TRKBLOCK structure, but for obvious reasons most of these entries are unused and just contain -1 in the block
member.
1. TRKBLOCKDATA structure (size : variable)
This structure stores the various data blocks referenced by the TRKBLOCK structure (above).
typedef struct TRKBLOCKDATA {
struct FLOATPT vertices[];
long shadingVertices[];
struct POLYVROADDATA vroad[];
struct REFXOBJ refxobj[];
struct REFPOLYOBJ refobj0[];
char padding[];
struct SOUNDSRC soundsrc[];
struct LIGHTSRC lightsrc[];
struct POLYGONDATA polygons[11][];
struct XOBJCHUNK xobj[4];
} TRKBLOCKDATA;
The number of vertices
and shadingVertices
entries corresponds to the nVertices
member of the TRKBLOCK structure. These contain respectively the coordinates and shading RGBA values of the track vertices.
The number of POLYVROADDATA structures (see below) is given by the nVroad
member of the TRKBLOCK structure.
The number of REFXOBJ, REFPOLYOBJ, SOUNDSRC and LIGHTSRC structures (see below) is given by the corresponding array-pointers in the TRKBLOCK structure.
The REFPOLYOBJ structures have variable size (16 or 20 bytes) ; the .FRD file contains padding just after the REFPOLYOBJ structures, in order to obtain a total size of 20*nPolyobj
in all cases for the REFPOLYOBJ table.
The polygons
member contains 11 series of POLYGONDATA structures, making up the 11 polygon chunks of the track block. The length of each chunk is given at the beginning of the TRKBLOCK structure. These structures contain all polygons both for the track structure and polygon objects.
The four xobj
members contain the block‚s extra-objects (4 chunks). The number of XOBJHEAD and XOBJDATA structures for each chunk are the number of objects of each xobj chunk, stored in the xobj
array-pointers of the TRKBLOCK structure.
2. POLYVROADDATA structure (size : 24 bytes)
typedef struct POLYVROADDATA {
unsigned char minY,maxY,minX,maxX;
char isEdge[4];
char flags;
char unknown;
short polygon;
short xNorm,zNorm,yNorm;
short xForw,zForw,yForw;
} POLYVROADDATA;
This structure describes the virtual road properties of a track polygon (i.e. a polygon in chunk 4). There is one entry for each driveable polygon (non-driveable polygons of type 14 do not have an entry). The polygon‚s position in chunk 4 is given by the polygon
member. The xNorm,zNorm,yNorm
members describe the coordinates of the normal vector (normalized to length 2^15) while the xForw,zForw,yForw
members contain the coordinates of the forward vector (normalized to length 2^15).
The minY,maxY,minX,maxX
members give bounds on the x and y coordinates of the four vertices making up the polygon, and are used by NFSHS to determine which polygon the car hits as it moves. These values are scaled relatively to the minPt
and maxPt
members of the corresponding TRKBLOCK structure, by a linear transformation : 0 stands for the minPt
value, and 255 stands for the maxPt
value. For example, denoting by v[i] the four vertices of the polygon,
minX = 255*(min(v[i].x,i=0..3) - minPt.x) / (maxPt.x - minPt.x)
The isEdge
array indicates along which of the sides of the polygon there are neighboring track polygons : the first value corresponds to the front edge, followed by the left edge, the back edge, and the right edge. These values are 0xFF if there is no adjacent track polygon along this edge, 0 if there is one.
The flags
member describes the behavior of the polygon : a non-zero value of flags&0x80
indicates that collisions with walls should be handled, i.e. that some of the neighboring polygons are not passable. A non-zero value of flags&0x40
indicates that collisions with extra-objects should be handled, i.e. that some extra-objects might be present over the polygon. Flags&0x20
seems to be set for the first row of polygons in a track block. Finally, flags&0x0f
describes the behavior of the polygon when driven over : a value of 0 corresponds to a polygon over which cars cannot pass (14 is unused in NFSHS, see above) ; a value of 2 corresponds to a polygon which emits gravel when driven over ; values 4 and 11 correspond to emission of leaves, 5 and 13 to emission of dust, 9 and 15 to emission of snow. All other values correspond to plain passable polygons.
3. REFXOBJ structure (size : 20 bytes)
typedef struct REFXOBJ {
struct INTPT pt;
short unknown;
short globalno;
char unknown[4];
} REFXOBJ;
This structure lists the properties of an extra-object. There is one entry for each non-animated (type 4) extra-object, regardless of the chunk to which it belongs ; there are no entries for animated objects. The pt
member contains the coordinates of the object‚s reference point (see XOBJDATA structure). The globalno
member is a unique sequence number characterizing the object among all of the track‚s extra-objects. There are also entries for type 6 objects ; however these objects are not stored among the track block‚s extra-objects, so type 6 entries should mostly be ignored.
4. REFPOLYOBJ structure (size : 16 or 20 bytes)
This structure, which lists the properties of an object, exists in two versions : the version for polygon objects is 16-bytes long and has the following structure :
typedef struct REFPOLYOBJ {
short head;
char type;
char no;
struct INTPT pt;
} REFPOLYOBJ;
For polygon objects, the head
member equals 4 and the type
member equals 1. The no
member is a serial number among the block‚s objects ; it is apparently not used by NFS. The pt
member contains the reference position of the object.
In the case of non-animated extra-objects, the structure is 20-bytes long :
typedef struct REFPOLYOBJ {
short head;
char type;
char no;
struct INTPT pt;
char crossindex; // ?
char unknown[3];
} REFPOLYOBJ;
The head
member is still equal to 4, and the type
member equals 2 or 4. The crossindex
member refers to the corresponding REFXOBJ entry (?)
There is one REFPOLYOBJ structure (of either type) for every object (either polygon object or extra-object) in the first object chunk. The three other chunks are not listed. The various REFPOLYOBJ structures of a track block are stored consecutively in the .FRD file ; the .FRD files contains padding just after the REFPOLYOBJ structures, in order to obtain a total size of 20*nPolyobj
in all cases for the REFPOLYOBJ table.
There are also variants for type 3 and 6 extra-objects ; type 6 extra-objects have 16 bytes like for polygon objects ; type 3 objects contain animation data (see XOBJDATA below) which quickly fills up the space for REFPOLYOBJ structures, so that most of the table is actually lost. In any case, this table is not used by NFS, so it can safely be erased.
5. SOUNDSRC structure (size : 16 bytes)
typedef struct SOUNDSRC {
struct INTPT refpoint;
long type;
} SOUNDSRC;
This structure describes the position and type of a sound source (water stream, etc...).
6. LIGHTSRC structure (size : 16 bytes)
typedef struct LIGHTSRC {
struct INTPT refpoint;
long type;
} LIGHTSRC;
This structure describes the position and type of a light source (special lighting effect).
7. XOBJCHUNK structure (variable size)
This structure stores the data of an extra-object chunk (4 chunks per track block, plus two more chunks at the end of the file for global objects). It has the following structure :
typedef struct XOBJCHUNK {
struct XOBJHEAD xobj[];
struct XOBJDATA xobjdata[];
} XOBJCHUNK;
The number of XOBJHEAD and XOBJDATA structures are given in the TRKBLOCK header structure.
8. XOBJHEAD structure (52 bytes long)
This structure is used to describe an extra-object ; it exists in different versions, depending on the object‚s type : static object (type 2 or 4), animated object (type 3), static global object (type 1), complex-behavior global object (type 6). The structure is the following (52 bytes long) :
typedef struct XOBJHEAD {
long type;
long crossno;
long unknown;
struct FLOATPT ptRef;
long dataSize;
void *data;
long nVertices;
FLOATPT* vertices;
long* shadingVertices;
long nPolygons;
POLYGONDATA* polygons;
} XOBJHEAD;
The type
member equals 2 or 4 for static extra-objects, 3 for animated objects, 1 for static global objects, 6 for complex-behavior objects. The only difference between type 2 and type 4 objects is that type 2 objects do not appear in the REFXOBJ table (they cannot be collided with). Type 3 objects do not appear in the REFXOBJ table either ; they are non-interactive animated objects, i.e. they follow a prescribed trajectory that can‚t be affected by cars. Type 1 objects are static objects like type 2 objects, but do not belong to any track block (they‚re used in the final xobj chunks of the FRD file). Finally, type 6 objects are static objects which can be knocked over by the cars, resulting in a complex behavior : e.g. the crates in Route Adonf, cones in raceway tracks, etc... ; these objects are stored in the final xobj chunks of the FRD file, although they are referred to in the REFXOBJ tables of the individual track blocks (they‚re not present in the xobj data of the block but postponed to the end of the file instead).
The crossno
member is only used for objects which appear in the REFXOBJ table, i.e. type 4 static objects (and type 6 global objects) ; it then references the entry in the REFXOBJ structure corresponding to the object.
The ptRef
member contains the coordinates of the object‚s reference point (only in the case of type 2, 4 or 6 ; for type 3 a sequence of animation positions is used instead, and type 1 objects have global coordinates).
The dataSize
member contains the length in bytes of the object‚s type-specific extra data, only non-zero for type 3 and 6 objects ; the data is to be accessed via the data
pointer member. For type 3 objects this data contains the animation‚s successive positions, and for type 6 objects this data is always 72 bytes long and stores the object‚s position and orientation.
The nVertices
value is the number of vertices contained in the extra-object ; the coordinates of these vertices are to be stored in the vertices
member (pointer space only). All coordinates are relative to the reference point, except for type 1 global objects where these are absolute coordinates. The shadingVertices
member is pointer space to store the shading applied to each vertex. The nPolygon
value is the number of polygons making up the extra-object, and the polygons themselves are to be accessed via the polygons
pointer.
9. XOBJDATA structure (variable size)
The XOBJDATA structure is :
typedef struct XOBJDATA {
struct SPECIFICDATA data;
struct FLOATPT vertices[];
long shadingVertices[];
struct POLYGONDATA polygons[];
} XOBJDATA;
The sizes of these tables are contained in the corresponding XOBJHEAD structure (nVertices
and nPolygons
members). All coordinates are relative to the reference point (except for type 1 objects).
The data
member is empty for type 1, 2 or 4 objects (static objects). In the case of an animated (type 3) object, the specific data structure is :
typedef struct SPECIFICDATA {
short head;
char type;
char no;
short animLength;
short unknown;
struct ANIMDATA animData[animLength];
} SPECIFICDATA;
The extra information describing the animation looks much like a REFPOLYOBJ structure : head
equals 4, type
equals 3, no
is an object number. The animLength
member indicates how many ANIMDATA structures are stored.
In the case of a type 6 object (last chunk only), it becomes :
typedef struct SPECIFICDATA {
struct FLOATPT ptRef;
float weight;
struct FLOATPT Xdir,Ydir,Zdir;
struct FLOATPT dimensions;
long unknown;
short unknown2,unknown3;
} SPECIFICDATA;
The ptRef
field contains the object‚s reference point (it is used instead of the one given in the XOBJHEAD) ; the weight
member contains the object‚s weight. The three vectors Xdir, Ydir, Zdir
indicate the orientation of the object and contain respectively the world coordinates of the X, Y and Z axes of the object‚s coordinate frame. These vectors are mutually orthogonal and have unit length. The components of the dimensions
member provide information on the object‚s size along the various axes. The value of unknown2
is related to the object‚s shape (cube, pyramid, ...)
9. ANIMDATA structure (size : 20 bytes)
typedef struct ANIMDATA {
struct INTPT pt;
float costheta,sintheta;
} ANIMDATA;
This structure describes the position of an animated extra-object. The pt
member provides the position of the object‚s reference point (it is similar to the ptRef
member of a static extra-object XOBJDATA structure, but in the case of an animated object the reference point moves during animation). The costheta
and sintheta
members describe the rotation to be applied to the object (this rotation always occurs in the XY plane, the Z coordinate is not affected).
typedef struct FRDFILE {
char header[28];
long nLastBlock;
long nNodes;
struct VROADBLOCK vroad[nNodes];
struct TRKBLOCK trk[nLastBlock+1];
struct TRKBLOCKDATA data[nLastBlock+1];
struct GLOBALXOBJ global_xobj[2];
// anything else ?
} FRDFILE;
FRD files start with a 28-byte header, followed by the number of the last track block. The actual number of blocks is nLastBlock+1
. Besides the data corresponding to blocks (as described above), the final part of the FRD file is devoted to global (animated) objects. The following structure is used :
typedef struct GLOBALXOBJ {
long nObj;
struct XOBJHEAD xobj[nObj];
struct XOBJDATA xobjdata[nObj];
} GLOBALXOBJ;
This is the typical place where animated objects are stored ; object types are widely variable in these two final XOBJ chunks. The first chunk can contain objects of type 1 or 3 ; the second one can contain objects of type 6.