3. The PACK files

3.1 The PACK files format

The PACK format is used to emulate a Unix directory arborescence, and to avoid putting some hundreds of files on the user's disk. It is not a compressed format, and it's very similar to the WAD format of DOOM.

3.1.1 The PACK Header

The PACK file starts with a header, that indicates where to find the directory, and the size of that directory. The number of entries can be deduced by dividing by sizeof(pakentry_t) = 0x40

typedef struct
{ u_char magic[4]= "PACK";     // Name of the new WAD format
  long diroffset;              // Position of WAD directory from start of file
  long dirsize;                // Number of entries * 0x40 (64 char)
} pakheader_t;

3.1.2 The PACK Directory

The PACK directory is made of a list of consecutive entries, each with the following format:

typedef struct
{ u_char filename[0x38];       // Name of the file, Unix style, with extension,
                               // 50 chars, padded with '\0'.
  long offset;                 // Position of the entry in PACK file
  long size;                   // Size of the entry in PACK file
} pakentry_t;

At offset diroffset in the PACK file, you will find:

pakentry_t dir[dirsize/sizeof(pakentry_t)];   // Directory

The directory is preferably placed at the end of the PACK file, but it could actually be anywhere. The entries could also be scattered all around the PACK file, leaving large gaps. If you write a PACK hacking utility, you must take care not to introduce too many empty space. Also, you should never assume that the entries are stored in the same order as in the directory (they could be in reverse order, for example). If you want to add some data after the last entry, make sure that you are really at the end of the file.

Since PACK files are a bit like WAD, it is possible to use the same tricks that were used by tools such as DeuSF and NWT to modify the PACK file reversibly. It is hoped, however, that Quake is flexible enough so that this trick is not needed.

3.1.3 Determining the type of PACK Entries

Contrary to the WAD2 files, there is no tag giving the type of each entry. However, they can be safely recognized by the extension, and it's the method used by Quake itself.

.WAV Sound files (RIFF/WAVE)
.BSP levels (map and textures)
.MDL 3D models (Alias)
.SPR Sprite models
.DAT Pseudo-code
.RC Resources
.CFG Config Files
.LMP Lump files
.LMP End screen
.WAD WAD2 file

3.2 The resources files (.RC)

Those files are ordinary Text, in Unix format (LF only, no CR), so they won't display correctly under DOS if you are using an old editor. They contain only settings and definitions.

3.3 The sound files (.WAV)

The sound files are ordinary 16-bit RIFF WAVE files (the format commonly used under Microsoft Windows, and now supported by many utilities under different operating systems).

3.4 The code lump (.DAT)

The .DAT file contains some semi-compiled machine independent P-code, instead of the Quake programming language .QC files.

This file contains the behavior associated to each of the entities. For instance, this file contains the frame table that defines how and when each frame of the Alias Models must be displayed.

This file also contains the light styles used to animate the Faces of the BSP models. Those light styles can be found as strings, among the other character strings.

Here is a very partial description of that data lump. Since the source of the Quake C compiler was released, you had better directly look at pr_comp.h for all the details about the structure of a compiled .DAT lump.

the only interesting part is the list of text strings, because it gives the names of possible spawning sequences for the Entities.

// addapted from pr_comp.h

typedef struct
{ long  version;               // 6
  long  crc;                   // CRC over progdefs.h
  long  ofs_statements;        // table of code statements. 
  long  num_statements;        // number of statements
  long  ofs_globaldefs;        // table of definitions of global variables
  long  num_globaldefs;        // number of definitions
  long  ofs_fielddefs;         // table of definitions of fields
  long  num_fielddefs;         // number of definitions
  long  ofs_functions;         // Table of functions definitions
  long  num_functions;         // number of functions 
  long  ofs_strings;           // Character strings, separated by '\0'. First one is \0
  long  size_strings;          // total size of string data
  long  ofs_globals;           // Unstructured list of Constants and variable
  long  num_globals;           // num_globals*4 = total size of global data
  int   entityfields;          // total size of entity definition
} dprograms_t;
// table of statements
typedef struct
{ u_short op;                  // operation code
  short   a;                   //
  short   b;                   // operation dependend parameters
  short   c;                   //
} statement;
// table of definitions
typedef struct                 // globaldef and fielddef
{ u_short type;                // type of value stored in table
  u_short offset;              // offset to value, in the global table
  long    s_name;              // offset to name, in string table
} def;                 
// table of functions
typedef struct
{
  long	first_statement;	// offset to the first statement, in the statements table
                                // negative numbers indicate builtin functions
  long  parm_start;             // start of parameters
  long  locals;			// total size of parmeters and all local variable
  long	profile;		// counter incremented at runtime, for each call
  long	s_name;                 // index to name, in the string table
  long  s_file;			// pointer to the source file name, in the string table
  long	numparms;               // number of parameters
  uchar	parm_size[8];           // size of each parameter in memory
}function [ functions.size];

The CRC in the header is not a protection against possible corruption, it's a protection against misuse. As a matter of fact, since part of the definitions in defs.qc are shared between Quake and the PROGS.DAT, and allocated statically in Quake, running a PROGS.DAT with an incompatible version of Quake could cause serious crashes. Having the same CRC values (calculated over progdefs.h) ensures that the PROGS.DAT and Quake are compatible.

3.5 The lump file (.LMP)

There are three kinds of lumps: Palette, colormap, pictures.

PALETTE

This is the game the color palette:

 
struct { u_char Red, u_char Green, u_char Blue} Palette[256];

COLORMAP

This is the precalculated color map, made of 32 tables. each table contains 256 indexes to the actual colors in the colormap.

                                     
u_char ColorIndex[32][256];

When light level is light and the color is color, the games uses the color index:
c = ColorIndex[ ((light>>3)&0x1F)][color].

Pictures

These are simple flat pictures, with indication of width and height, that are used for menus, status bar and the like.

 
typedef struct
{ long width;
  long height;
  u_char Color[width*height];                                       
} picture_t;

3.6 The end screen (.BIN)

A classical DOS text screen, 80x25 with color tags. Same as the end screen in DOOM.

3.7 The config file (.CFG)

A simple text file, that contains the default configuration of keys.