====================================================================== THE UNOFFICIAL NEED FOR SPEED FILE FORMAT SPECIFICATIONS - Version 0.1 Copyright (c) 1995, Denis AUROUX (MXK) - auroux@clipper.ens.fr ====================================================================== Last updated : December 10, 1995 This file is available from the following URL : http://www.eleves.ens.fr:8080/home/auroux/nfsspecs.txt Disclaimer : NO WARRANTY of any kind comes with this file. A - GAMEDATA directory ================== A.1 GAMEDATA\CONFIG\PATHS.DAT ------------------------- 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): 000 gamedata/config/ 2D0 F:\simdata\misc\ 050 gamedata/savegame/ 320 F:\simdata\trackfam\ 0A0 F:\frontend\speech\ 370 F:\simdata\slides\ 0F0 F:\simdata\soundbnk\ 3C0 F:\simdata\carFams\ 140 F:\frontend\music\ 410 F:\simdata\soundbnk\ 190 F:\frontend\art\ 460 F:\simdata\carspecs\ 1E0 gamedata/modem/ 4B0 F:\simdata\dash\ 230 F:\frontend\movielow\ 500 F:\simdata\misc\ 280 gamedata/replay/ 550 F:\frontend\show\ 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. A.2 GAMEDATA\SAVEGAME\*.SAV ----------------------- 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 : ??? The other offsets contains zeros. A.3 GAMEDATA\CONFIG\CONFIG.DAT -------------------------- 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. B.5 SIMDATA\MISC\*.QFS, SIMDATA\SLIDES\*.QFS, ... --------------------------------------------- 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 |