======================================================================
THE UNOFFICIAL NEED FOR SPEED FILE FORMAT SPECIFICATIONS - Version 0.1
Copyright (c) 1995, Denis AUROUX (MXK) - auroux@clipper.ens.fr
======================================================================
This file contains 18 null-terminated strings of 80 characters each
(total 1440 bytes), describing the paths where NFS looks for its files.
For minimum install, here are the contents (with hex offset):
Note that simdata\soundbnk and simdata\misc are present twice.
Also note that in the CD-ROM, directories whose name starts with a 'G'
correspond to the German version of the game. Don't know why the car
dashboards had to be translated, though :)
Modify these entries if you want to copy some of the CD files to your hard
disk, for example if you want to modify them. Remember that modifying the
files can be hazardous, and that because the file formats are not fully
known it is better to modify an existing file than to create one from scratch.
These are the tournament save files (840 bytes each).
- Offset 0 : your name (null-terminated string followed by zeros)
- Offset 28 : 'Opponent' (")
- Offset EC : 'Player 2' (")
- Offset 114 : 'Opponent 2' (")
- Offset 1D8 : name of the tournament (")
- Offset 20A : 'REPLAY.RPL' (")
- Offsets 23C-26F : ???
- Offset 270 : name of the tournament (")
- Offsets 30E-337 : ???
- Offsets 338, 33A, 33C, 33E, 340, 342, 344 : these contain 01 if the
corresponding race has been won, otherwise 00. The first three bytes
are for open-road races, while the other correspond to closed tracks.
For instance, in order to access the bonus track, save a tournament,
then set all these bytes to 01 except 33E to 00 (Rusty Springs), then
reload the tournament and win Rusty Springs. The odd offsets 339 to 345
are zeros.
- Offsets 346-347 : ???
This file (25552 bytes) contains all the relevant configuration data
(current car, track, display & sound options, ...), as well as the current
scores (best times, top speeds, laps, ...). It also describes whether you
have access to the bonus track and the bonus car, etc...
Anybody got anything more detailed to put here ?
B - SIMDATA directory
=================
B.1 SIMDATA\CARSPECS\*.PBS
----------------------
These files (one per player car) describe the car performance.
Usually, installing these files to the hard disk (by editing PATHS.DAT)
makes Need for Speed inoperable (no acceleration is possible) because the
game modifies SIMDATA\CARSPECS\BY_R&T. However this can be prevented by
making the check file read-only (run ATTRIB +R BY_R&T).
Note that copying one of these files on top of another one will affect the
car performance and handling, but not the engine sound. So if the max rpm's
of the two engines are not the same, the sound can get badly altered because
the correct sound sample is not available.
These files are somehow compressed, because by modifying a single byte in
the file one can reach various results such as : an instant system crash ;
a car that materializes under the road and cannot move, with 11 gears (!) ;
a car without front wheels, that keeps spinning at its initial position...
B.2 SIMDATA\DASH\*.FMM
------------------
These small files (two per player car, corresponding to the hi-res and
low-res dashboards) describe the shape of the car's windshield in each
display resolution. The files are slightly different for 320x200 and 640x480.
a) Low-res .FMMs (*DL.FMM) :
The file structure is the following : first a 24-byte header, then four
chunks.
The header format is the following :
offset len data
------ --- ----
00 4 'wwww'
04 4 4 (number of chunks)
08 4 18h (offset of the first chunk)
0C 4 offset of the second chunk
10 4 offset of the third chunk
14 4 offset of the fourth chunk
The chunks in low-res .FMMs have the following header (22 bytes) :
offset len data
------ --- ----
00 4 'BAMF'
04 2 length of the chunk in bytes, minus two
06 4 ? (usually equals 10000h or 8000h : maybe an image size)
0A 2 maximum "length" value in the chunk's records
0C 4 ? (usually equals 10000h or 8000h : maybe an image size)
10 4 ? (usually equals 10000h or 8000h : maybe an image size)
14 2 number of records in the chunk (length minus 18h, div 8)
This header is in each chunk immediately followed by the 8-byte records.
Each record has the following structure :
offset len data
------ --- ----
00 4 baddr
04 2 vaddr
06 2 length
The last record is followed by a 16-bit value (purpose unknown).
The vaddr and length values are interpreted as follows : starting from
address "vaddr" in video memory (i.e. of the form 320y+x for pixel (x,y)),
"length" pixels do not belong to the dashboard, and must instead be drawn
because they correspond to screen portions where the player must see the
road through the windshield.
The baddr field corresponds to an internal buffer offset, describing
where the data to be displayed at "vaddr" are to be found in the buffer
where NFS draws the view.
- The first chunk corresponds to high detail : vaddr and baddr are equal,
and each line corresponds to 320 bytes in the buffer (every pixel is stored).
- The second chunk corresponds to medium detail : each line corresponds to
320 bytes in the buffer, but only half of the pixels are stored, thus only
the first 160 bytes contain relevant data, the following 160 being unused.
To vaddr=320y+x corresponds baddr=320y+(x>>1) : the horizontal resolution
is decreased.
- The third chunk corresponds to low detail : two consecutive lines have the
same baddr values, and these two lines correspond to 320 bytes in the buffer.
As before, only half of these 320 bytes are used. Only a quarter of the
pixels are stored. vaddr=320y+x corresponds to baddr=320.(y>>1)+(x>>1) :
both horizontal and vertical resolutions are decreased.
- The fourth chunk corresponds to the interlaced mode : only half of the
screen lines are referenced ; as in low detail, two screen lines correspond
to 320 bytes, half of which are unused. vaddr=320y+x still corresponds to
baddr=320.(y>>1)+(x>>1), but only the even-numbered lines are present.
b) Hi-res .FMMs (*DH.FMM) :
The chunk format is slightly different because the video memory is now
segmented in several 64K pages. (Note that the values at offsets 06, 0C and
10 in the chunk header are now larger, e.g. 40000h and 10000h)
Offset 14h in the chunk now contains, instead of the total number of records
in the chunk, only the number of records that correspond to the first page
of video memory (i.e. the first 64K). It is followed by the corresponding
records.
After these records, another 16-bit value describes the number of records
that are located in the second page of video memory (the following 64K);
the records for the second page follow (note that the 32-bit baddr values
at last become useful ; only the 16 last bits of the vaddr are stored,
since it is known to be located in the second page).
The chunk continues with the following pages, until the number of records
for a given page is zero. This zero value is followed by a 16-bit value
(purpose unknown) as in the low-res files.
The four chunks correspond respectively to high, medium, low and interlaced
detail levels, as described above (but the lines are now each 640 pixels
long ; when not in high resolution, only 320 of the 640 bytes are used).
B.3 SIMDATA\DASH\*.FSH
------------------
These store the different bitmaps that make up the dashboard.
The 16-byte header format is the following :
offset len data
------ --- ----
00 4 'SHPI'
04 4 length of the file in bytes
08 4 number of objects in the directory
0C 4 directory identifier string
The directory identifier is 'GIMX' in .FSH dashboard files.
This header is followed by the directory entries, each consisting of a
4-byte identifier string, and a 4-byte offset inside the file pointing to
the beginning of the corresponding data.
Each entry in the directory represents a piece of the dashboard.
There are gaps between the directory and the first bitmap, and between
consecutive bitmaps (significance unknown).
Each directory entry points to a bitmap block with the following structure :
offset len data
------ --- ----
00 1 7Bh
01 3 size of the block (= width x length + 10h)
04 2 width of the bitmap in pixels
06 2 heigth of the bitmap in pixels
08 4 ?
0C 2 x position to display the bitmap
0E 2 y position to display the bitmap
10 w.h bitmap data : 1 byte per pixel
Note that the object called 'dash' in the directory takes the whole screen
(320x200 or 640x480, at position x=0, y=0)."
The various objects, depending on their 4-letter identifier, represent :
the dashboard itself, the steering wheel in various positions, the radar
detector lights, the gauges, and also pieces of the steering wheel to redraw
over the gauges when necessary. Note that value FF in the bitmaps stands for
the background : this is useful when a bitmap is drawn on top of another one.
Also note that some SHPI bitmap directories contain entries that actually
describe the palette to be used with the bitmaps. Typically, entries with
names like '!PAL', and with bitmap dimension 256x3, correspond to palettes.
The palette data consists of 256 3-byte records, each record containing the
red, green and blue components of the corresponding color (1 byte each).
B.4 SIMDATA\CARFAMS\*.CFM
---------------------
These files describe the exterior look of the cars during the race. There
seem to be two different kinds of files :
- full CFMs that can be used to describe any car
- restricted CFMs that can only describe traffic or opponents - note that
yourself and your head-to-head opponent must have full CFMs, whereas
"single race" opponents can be either full or restricted CFMs.
a) Full CFMs on the CD :
Standard cars : ANSX.CFM, CZR1.CFM, DVIPER.CFM, F512TR.CFM, LDIABL.CFM,
MRX7.CFM, P911.CFM, TSUPRA.CFM, TRAFFC.CFM (Warrior)
More or less bugged previous versions of these cars :
F512M.CFM, P911T.CFM, WARIOR.CFM, WARRIOR.CFM
b) Restricted CFMs on the CD :
Vans/jeeps/pickups : JEEP.CFM, PICKUP.CFM, RODEO.CFM, VANDURA.CFM
Station-wagons : AXXESS.CFM, WAGON.CFM
Usual cars : BMW.CFM, JETTA.CFM, LEMANS.CFM, SUNBIRD.CFM
Sports cars : CRX.CFM, PROBE.CFM
The cop's car : COPMUST.CFM
Restricted versions of the player cars (loaded for single race opponents):
MANSX.CFM, MCZR1.CFM, MDVIPER.CFM, MF512TR.CFM, MLDIABL.CFM,
MMRX7.CFM, MP911.CFM, MTSUPRA.CFM, MTRAFFC.CFM
Bugged versions of the player cars :
LDIABLO.CFM, MF512M.CFM, MP911T.CFM, TESTA.CFM
These files are 'wwww' files containing four chunks. The header is as in
.FMM files (see B.2). The first and second chunk correspond to high-detail
car structure, while the third and fourth chunk correspond to low-detail.
The first and third chunks have header 'ORIP', and describe how the car is
to be drawn (position and orientation of each car element). The second
and fourth chunks are 'SHPI' bitmap directories (see B.3), with directory
identifier 'WRAP'. They provide the bitmaps referenced by the ORIP chunks.
Note that the offsets specified in the bitmap directories are relative to the
'SHPI' header.
This seems to be EAC's favorite bitmap format... Basically, it is similar
to the .FSH files described in B.3, however the data are now compressed
using some kind of Huffman algorithm. Outside of this compression thing,
the file still contains a 'SHPI' header, and a directory. The directory
itself is much shorter than in the dashboard FSH's : usually one entry
(the bitmap itself), sometimes two (when a palette is specified).
The big trouble is in finding the details of the decompression algorithm.
The directory header is 'LN32', indicating 32768-color mode. (see C.2).
B.6 SIMDATA\MISC\*.TRI
------------------
Don't know why these are in the MISC directory... they are quite important
indeed ! They describe the track itself, including the shape of the road
and the position of the scenery items. The objects are referenced by numbers
which correspond to entries in the corresponding file in SIMDATA\TRACKFAM.
The beginning of the file looks like a succession of 32-bit offsets ;
however a fair part of the file is made up of records of length 36 bytes.
B.7 SIMDATA\SOUNDBNK\*.BNK
----------------------
These file store the sound samples for the engines, the crashes, etc...
The filenames ending with 'W' correspond to 16-bit sound, while 'B' stands
for 8-bit sound. Logically, 'S' should stand for stereo and 'M' for mono,
but it seems that, at least for cars, the file that gets loaded in 8-bit
mono mode is the one whose name ends with 'SB'. I don't know why. I'm
beginning to think that EAC recruited Microsoft programmers ;-)
Also remember that the 8 bit sound samples are signed data. If you plan to
use WAV files as samples, don't forget to change this.
The file format is the following :
- the first 512 bytes contain 128 long integers, which are either zero if
the corresponding sample is not present, or the offset of a sample header.
Car files contain four samples with header offsets 200h, 248h, 290h and 2D8h,
declared at offsets 04, 08, 0C and 80h respectively. The two first samples
are the engine sound, the third is the car horn, the fourth is the sound
that gets produced when you change gears.
- each sample header consists of 72 bytes (48h). The format is the following:
offset len data
------ --- ----
00 4 ? flags (for 8-bit car sounds, these are 1, 2, 4000h and 10h)
04 4 header's offset + 28h (i.e. offset of the 'EACS' marker)
08 4 ? (0 for cars)
0C 4 ? (0 for cars except gear change)
10 4 ? (0 for cars)
14 1 ?
15 1 ? (80h for cars)
16 1 ? (0 for cars)
17 1 ?
18 4 ? (7F40h for cars)
1C 4 ? (0 for cars)
20 4 ? (0 for cars)
24 4 ? (0 for cars)
28 4 'EACS'
2C 4 3E80h (sampling rate : 16 kHz)
30 1 8/16 bit flag : 1 for 8-bit, 2 for 16-bit
31 1 mono/stereo flag : 1 for mono, 2 for stereo
32 1 ? (0 for cars)
33 1 ? (FFh for cars)
34 4 beginning of the repeat loop (in samples : 1, 2 or 4 bytes)
38 4 length of the repeat loop (in samples)
3C 4 length of the sound (in samples)
40 4 offset of the raw sound data in the .BNK file
44 4 ? (0 for cars)
B.8 SIMDATA\TRACKFAM\*.FAM
----------------------
These files contain the bitmaps that are used for displaying the correspon-
ding track or track segment. They make use of the 'wwww' file format, using
the usual header (see B.2) : first 'wwww', then the number of chunks (as a
long integer), then the offsets of each chunk header (long integers).
The .FAM file itself is a 'wwww' file containing four chunks. The first chunk
corresponds to the background bitmaps, making up the terrain and the road,
while the second chunk is devoted to foreground bitmaps (road signs, etc...)
Each of these two chunks is itself in 'wwww' format, containing sub-chunks.
Note that the offsets of the sub-chunks listed in the headers are relative
to the beginning of the chunks. In the open road .FAM's, the chunks are very
subdivided and contain many small subchunks, while in the closed tracks,
there is only one big subchunk.
Now the subchunks in each of the first two chunks are themselves 'SHPI'
bitmap directories (see B.3), whose elements are a palette and several
bitmaps. Keep in mind that the offsets in the directories are relative to
the beginning of the subchunk (i.e. the 'SHPI' header).
The third chunk of the .FAM file is a SHPI file describing the horizon :
the SHPI directory contains two entries, one for the palette and one for
the bitmap itself.
The fourth chunk describes the three-dimensional objects in the scenery.
It is a 'wwww' structure, containing a subchunk per object. Now each of
these subchunks is quite similar to a .CFM file (see B.4) : it is itself a
'wwww' structure containing two subsubchunks ! The first subsubchunk is an
'ORIP' structure describing the shape of the object, while the second is
a 'SHPI' structure containing the bitmaps referenced by the 'ORIP' part.
If you didn't catch everything, don't worry... look at NFSVIEW.PAS (see D.1)
C - FRONTEND directory
==================
C.1 FRONTEND\ART\*.QFS, FRONTEND\SHOW\*.QFS : see B.5
---------------------------------------
C.2 FRONTEND\ART\*.INV
------------------
These files describe how to reduce the palette from hi-color mode to 256
colors. The corresponding .QFS files (compressed SHPI bitmaps) have directory
identifier 'LN32', indicating the bitmaps are in 32768-color mode (each
red, blue and green component is coded on 5 bits (0 to 31), and the 15 bits
describing the color are stored in a 16-bit word). Since many video cards do
not support these modes, information is provided for palette reduction to
256-color modes. This information is partly stored as a palette object in the
QFS file, describing the RGB components of the 256 colors. In order to help
Need for Speed with the palette conversion, the .INV file (length 32768 bytes)
stores, for each 15-bit value, which palette entry is relevant.
Displaying the 'LN32' bitmaps in 256-color is thus done by first expanding
the .QFS file, then setting the specified palette, and converting each
15-bit pixel in the bitmap to the 8-bit value located at the corresponding
offset in the .INV file.
C.3 FRONTEND\MUSIC\*.ASF
--------------------
These files contain the music for the front end. The 40-byte header is as
follows (note a consistency with the EACS structures described in B.7) :
offset len data
------ --- ----
00 4 '1SNh'
04 4 ?
08 4 'EACS'
0C 4 3E80h (sampling rate 16 kHz)
10 1 2 (16-bit mode flag)
11 1 2 (stereo mode flag)
12 1 ? (0 usually)
13 1 ? (0 usually)
14 4 length of the wave data (expressed in 4-byte samples)
18 4 beginning of the repeat loop (in samples)
1C 4 length of the repeat loop (in samples)
20 4 0 (the start offset is not specified : should be 28h)
24 4 ?
Signed 16-bit stereo wave data follows.
C.4 FRONTEND\SPEECH\*.EAS
---------------------
These file contain the speeches, as an EACS structure (see B.7 and C.3).
The 32-byte header is now as follows :
offset len data
------ --- ----
00 4 'EACS'
04 4 3E80h (sampling rate 16 kHz)
08 1 1 (8-bit mode flag)
09 1 1 (mono mode flag)
0A 1 ? (0 usually)
0B 1 ? (FFh usually)
0C 4 length of wave data (expressed in 1-byte units)
10 4 FFFFFFFFh (no repeat loop)
14 4 0 (no repeat length)
18 4 20h (offset of the wave data)
1C 4 ?
Signed 8-bit mono wave data follows.
D - Appendix
========
D.1 NFSVIEW.PAS
-----------
{NFS file viewer : recurse into 'wwww' structures and view 'SHPI' bitmaps}
uses crt;
var f:file;
scr:array[0..199,0..319] of byte absolute $A000:0;
pal:array[0..767] of byte;
s:string[5];
procedure setpalette; assembler;
asm
mov dx,3c8h
xor cl,cl
mov si,offset pal
cld
@bcl:
mov al,cl
out dx,al
inc dx
lodsb
out dx,al
lodsb
out dx,al
lodsb
out dx,al
dec dx
inc cl
jnz @bcl
end;
function st(x:longint):string;
var s:string[20];
begin
str(x,s);
st:=s;
end;
procedure viewshpi(pre:string;off:longint); {process a SHPI}
var ni,i:integer;
w,h,xp,yp,y:word;
l:longint;
foundpal:boolean;
begin
seek(f,off+8);
blockread(f,ni,2);
writeln(pre,'SHPI ',ni,' bitmaps'#10#13'Looking for a palette...');
foundpal:=false;
for i:=0 to ni-1 do begin {look for a palette}
seek(f,off+20+8*i);
blockread(f,l,4);
seek(f,off+l+4);
blockread(f,w,2);
blockread(f,h,2);
if (w=256) and (h=3) then begin
blockread(f,pal,8);
blockread(f,pal,768);
foundpal:=true;
end;
end;
if foundpal then writeln('Found !') else writeln('No palette.');
readkey;
asm
mov ax,13h
int 10h
end;
if foundpal then setpalette;
for i:=0 to ni-1 do begin {display the bitmaps}
fillchar(scr,64000,0);
seek(f,off+20+8*i);
blockread(f,l,4);
seek(f,off+l+4);
blockread(f,w,2);
blockread(f,h,2);
blockread(f,l,4);
blockread(f,xp,2);
blockread(f,yp,2);
if (w<>256) or (h<>3) then begin
if (xp+w>320) or (yp+h>200) then begin yp:=0; xp:=0; end;
for y:=yp to yp+h-1 do
blockread(f,scr[y,xp],w);
readkey; end;
end;
asm
mov ax,3
int 10h
end;
end;
procedure viewwwww(pre:string;off:longint); {process a wwww block}
var n,i:integer;
l:longint;
s:string[5];
begin
seek(f,off+4);
blockread(f,n,2);
for i:=1 to n do begin {process each chunk}
seek(f,off+4+4*i);
blockread(f,l,4);
s[0]:=#4;
seek(f,off+l);
blockread(f,s[1],4); {read 4-char id}
if s='wwww' then
viewwwww(pre+'chunk '+st(i)+'/'+st(n)+' sub',off+l)
else if s='SHPI' then
viewshpi(pre+'chunk '+st(i)+'/'+st(n)+' ',off+l)
else begin
writeln(pre,'chunk ',i,'/',n,' unrecognized header ',s);
readkey;
end;
end;
end;
begin
if paramcount=0 then begin
writeln('Need For Speed wwww/SHPI Viewer 1.0 (c) Denis Auroux 1995');
writeln('Syntax : NFSVIEW filename'#10#13);
exit;
end;
clrscr;
assign(f,paramstr(1));
reset(f,1);
s[0]:=#4;
blockread(f,s[1],4); {read 4-char id}
if s='wwww' then viewwwww('',0)
else if s='SHPI' then viewshpi('',0)
else writeln('Unrecognized header ',s,#10#13);
close(f);
end.
============================================================================
For any comments and additions to this file, mail to auroux@clipper.ens.fr.
============================================================================
--
+------------------------------------+---------------------------------+
| Denis AUROUX (MXK) | Ecole Normale Superieure |
| 255 rue Saint-Jacques | 45 rue d'Ulm |
| 75005 PARIS FRANCE | 75005 PARIS |