Skip to content

Latest commit

 

History

History
126 lines (84 loc) · 5.98 KB

ResourceFiles.md

File metadata and controls

126 lines (84 loc) · 5.98 KB

Resource Files

Resource files are structured archives. These files contain one or more "resource" entries. Resources are identified and contain one or more data blocks. A resource with more than one data block is called a "compound resource".

Identifier

A resource identifier is a uint16 value, and a compound resource uses a uint16 value as index.

Resource identifier are "valid" from value 0x0003 and above. 0x0000 is the NULL identifier (this was quick approach to erase resources from file for developers), and 0x0001 and 0x0002 are used engine-internally (first used to point to head ptr in LRU cache, second - to tail ptr in LRU).

General Format

A resource file consists of a header, resources and a file directory. The header points to the file offset of the file directory and the file directory points to the file offset of the first resource.

|        /--------------------------------------v                      |
| Header.dirOffset | Res0 | Res1 | ...  | Res N | Directory.dataOffset |
|                  ^----------------------------------------/          |

All resources, as well as the file directory, start at a 4-byte boundary.

To calculate the file offset of a resource, the file directory has to be read first to determine the offsets (and lengths) of the previous resources.

Although the format would allow for a different order, all resource files have the directory at the end of the file.

File Header

The file header has a length of 128 bytes and has the following format:

File Header (ResFileHeader struct, 128 bytes)

0000  [16]byte  signature  signature "LG Res File v2\r\n"
0010  [96]byte  comment    comment, terminated with "\x1A" (remainder 0x00)
0070  [12]byte  reserved   reserved, set to 0x00
007C  sint32    dirOffset  file offset to directory

No resource file has been found containing a comment. It always only contains the terminator byte 0x1A. The comment is terminated with 0x1A as this represents the Ctrl+Z code. This was a little trick to enable people to type the file on the command line and not be spammed with binary garbage.

File Directory

The file directory consists of a 6 byte header followed directly by its directory entries:

Resource file directory header (ResDirHeader struct, 6 bytes)

0000  uint16  numEntries  number of resources in the file
0002  sint32  dataOffset  file offset to beginning of first resource

Resource file directory entry (ResDirEntry struct, 10 bytes)

0000  uint16  id     resource ID
0002  sint24  size   resource length (unpacked)
0005  sint8   flags  resource flags (see below)
0006  sint24  csize  resource length (packed in file)
0009  sint8   type   resource type (see below)

Resource Flags

The resource flag marks extra information about the resource.

Resource Flag (1 byte)

0x01  RDF_LZW         compressed
0x02  RDF_COMPOUND    compound
0x04  RDF_RESERVED    reserved (unused)
0x08  RDF_LOADONOPEN  load on open (unused)

The original source lists several more resource flags, which are all unused in System Shock. Presumably this is the result of re-used code.

"Flat" resources contain only one data block, which is the complete resource data itself. Compound resources contain 0, one, or more blocks.

Resource Types

The following resource types appears in System Shock:

0x00  UNKNOWN  Palette (although originally called "unknown", it is used for palettes exclusively)
0x01  STRING   String
0x02  IMAGE    Image
0x03  FONT     Font
0x04  ANIM     Animation Script (Video clip)
0x07  VOC      Sound effects and voices (.voc)
0x0F  OBJ3D    3D model geometry
0x11  MOVIE    Movie
0x30  APP1     Application specific resource (used in archive.dat for storing maps)

The original source lists several more resource types, which are all unused in System Shock. Presumably this is the result of re-used code.

There exists 0x05 (PALL) resource type specially for palettes, but never used in System Shock.

Compound Resources

If a resource is flagged to be compound, the resource data starts with a directory. This directory contains relative offsets to the first bytes of the contained blocks, following the directory.

Resource directory (2 bytes + 4 bytes per block + 4 bytes)

0000    uint16  number of blocks in resource
0002    sint32  resource offset to first block
....
nnnn    sint32  resource offset to last block
nnnn+4  sint32  total length of resource

To calculate the length of a block N, take the start offset of N+1 and subtract the start offset of N. This is why there is one extra offset entry in the directory.

If a compound resource is compressed, the directory itself is not compressed. Resource offsets always point into the uncompressed data.

Some compound resources have two zero bytes between the directory and the first block.

Resource Compression

Resource compression is based on the LZW algorithm. It uses a fixed word-size of 14 bits. Two words are reserved for control.

The words are serialized sequentially big-endian as a continuous stream. For example, if the first 7 bytes in the resource are

aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee ffffffff gggggggg

then the corresponding words are

aaaaaaaabbbbbb bbccccccccdddd ddddeeeeeeeeff ffffffgggggggg

with the high bit to the left in each case.

The first 256 words of the dictionary are initialized with length 1 for the corresponding byte values. The reserved words are:

  • 0x3FFE Reset dictionary to initial state
  • 0x3FFF End-Of-Stream

The End-Of-Stream marker is always issued. The serialized byte stream always contains a trailing 0x00 byte after the End-Of-Stream word.

The compressor issues a dictionary reset when it is exhausting the dictionary and can't add a new word to it for the 1001st time.