DAT File Format

DAT files are Fallout archive containers. They hold most of the game's loose resources: artwork, palettes, fonts, maps, prototypes, scripts, message files, sound effects, music, speech, movies, and assorted text configuration. The archive layer does not change the inner file formats; it only stores paths, compressed or uncompressed byte streams, and lookup metadata.

There are two incompatible archive formats with the same .dat extension:

Name used hereGameEndianCompressionDirectory location
DAT1Fallout 1Big-endian metadataFallout LZSS blocksHeader and directory tables at the start
DAT2Fallout 2Little-endian metadatazlib streamsDirectory tree and footer at the end

save.dat, worldmap.dat, and many other files with a .dat extension are not DAT archives. They are independent binary formats documented separately.

Archive Files

Fallout 2 normally uses these archive/search roots:

Path/sourceRole
master.datMain game resources.
critter.datCritter animation and critter-related resources.
data\ or other patch directoriesLoose-file override roots configured by the game's system paths.
patch000.dat, patch001.dat, ...Optional patch archives opened after the base archives by CE.
f2_res.datHigh-resolution patch resources, when present.

Paths stored inside DAT archives are relative game paths such as art\intrface\iface.frm or text\english\game\worldmap.msg. The same relative paths are used when resources are unpacked under data\.

Resource Loading And Override Order

The Fallout resource layer behaves like a small virtual filesystem. Most game code asks for a relative path such as art\items\stimpak.frm, proto\items\items.lst, or text\english\game\misc.msg. The database layer searches all currently opened resource roots until it finds the first matching file.

The base roots come from fallout2.cfg:

Config keyTypical valueRole
[system] master_datmaster.datMain archive.
[system] master_patchesdataLoose patch directory for master resources.
[system] critter_datcritter.datCritter archive.
[system] critter_patchesdataLoose patch directory for critter resources.

Fallout 2 CE opens those roots in this startup order:

  1. master_dat, if configured.
  2. master_patches, if configured.
  3. critter_dat, if configured.
  4. critter_patches, if configured.
  5. Patch archives matching the sfall [Misc] PatchFile template, normally patch000.dat through patch999.dat, if present.
  6. f2_res.dat, if present.

Every successful xbaseOpen inserts the root at the front of the internal search list. Reopening an already-open root moves it to the front. Relative file lookup then searches that list from front to back. Because of that front-insertion behavior, the effective lookup precedence is the reverse of the opening sequence.

With the usual CE startup flow and default patch directory, effective lookup precedence is:

PriorityRootNotes
Highestf2_res.datOpened last when present.
Highest-numbered patchNNN.datPatch archives are opened from patch000.dat upward, so later numbers sit earlier in the search list.
Lower-numbered patchNNN.datStill above the base archives.
critter_patchesCommonly data\. If the same directory is also master_patches, reopening moves it to this position.
critter.datOpened after the master roots.
master_patchesCommonly data\, unless it was the same root as critter_patches and got moved later.
Lowestmaster.datMain base archive.

In the default configuration, both patch directory keys usually point to data. The first open from master_patches adds it, and the second open from critter_patches moves the same root to the front again. The practical result is that loose files under data\ override both master.dat and critter.dat, but numbered patch archives and f2_res.dat can still override loose data\ files in CE's normal order.

Different executable builds, launchers, or mods can change which roots are opened and in what order, so archive tools should not assume this table is universal. It is the behavior visible in Fallout 2 CE's gameDbInit, dbOpen, xbaseOpen, and xfileOpen flow.

Absolute paths and paths beginning with ., \, or / bypass the DAT search list and are opened directly from the filesystem. Normal game asset references should therefore be relative paths.

Loose filesystem files can be gzip-compressed: CE checks the first two bytes of a plain file for gzip magic 1F 8B and reopens it as a gzip stream. This is separate from DAT2 entry compression. DAT2 compressed entries are zlib streams inside the archive, not gzip files.

Lookup Examples

For a relative path such as art\critters\hmjmpsaa.frm, CE tries the highest-precedence open root first. If f2_res.dat does not contain it, CE tries patch archives from highest number to lowest number, then the loose patch directory, then critter.dat, then master.dat. The first match wins.

For a text path such as text\english\game\misc.msg, the same database search applies. Language-specific code changes the relative path it asks for; the database layer still performs the same root search on that path.

For save-game sidecars and other explicitly absolute or dot-relative paths, the database roots are not used. Those paths are opened directly from the filesystem.

Path Rules

DAT2 Overview

DAT2 is the Fallout 2 archive format. It has no global magic number. A reader identifies it by the footer and by successfully parsing the directory tree near the end of the file.

+-----------------------------+
| File data block             |
| compressed/plain file bytes |
+-----------------------------+
| uint32 file_count           |
| directory entries           |
+-----------------------------+
| uint32 tree_size            |
| uint32 data_size            |
+-----------------------------+
PartLocationDescription
Data blockUsually offset 0Raw stored bytes for every entry.
File countStart of directory treeLittle-endian uint32 count of entries.
Directory entriesAfter file countVariable-length entry metadata.
Tree sizeLast 8 bytes, first fieldLittle-endian uint32 size of file_count + entries.
Data sizeLast 4 bytesLittle-endian uint32 size of the DAT data region.

CE computes the start of the archive data region as:

data_base = physical_file_size - data_size

For normal DAT2 files, data_base is 0. The formula allows arbitrary leading bytes before the DAT data region, because entry offsets are relative to data_base, not always to the physical start of the file.

To locate the directory tree, read the last 8 physical bytes:

tree_size = read_u32_le(file_size - 8)
data_size = read_u32_le(file_size - 4)

data_base = file_size - data_size
tree_offset = file_size - tree_size - 8

The first 4 bytes at tree_offset are file_count. The remaining tree_size - 4 bytes are directory entries.

DAT2 Entry Structure

FieldSizeDescription
path_length4Little-endian length of the path string in bytes.
pathpath_lengthEntry path bytes. No null terminator is stored in the DAT.
compressed11 for zlib-compressed data, 0 for plain stored data.
uncompressed_size4Size returned to callers after decompression.
data_size4Stored byte count in the data block. For compressed entries, this is the compressed zlib stream length.
data_offset4Offset of the stored bytes relative to data_base.
struct Dat2Entry {
    uint32_t path_length;
    char     path[path_length];
    uint8_t  compressed;
    uint32_t uncompressed_size;
    uint32_t data_size;
    uint32_t data_offset;
};

The physical byte range for an entry is:

stored_start = data_base + data_offset
stored_end   = stored_start + data_size

For uncompressed entries, data_size should match uncompressed_size. For compressed entries, data_size is usually smaller, but a validator should still check both the stored byte range and the inflated byte count.

DAT2 Compression

Compressed DAT2 entries are zlib streams. They are not gzip streams, even though older documentation sometimes says "gzipped". Common compressed entries begin with zlib header bytes such as 78 DA, but a reader should trust the directory entry's compressed flag and use zlib inflate rather than only sniffing the first bytes.

CE reads compressed entries with a 0x400-byte input buffer and zlib inflate. Stream position is tracked in uncompressed bytes. Seeking backward in a compressed entry rewinds the stream and inflates forward to the requested offset; seeking forward inflates and discards bytes until the destination is reached.

DAT2 Text Reads

When a DAT entry is opened in text mode, CE normalizes Windows CRLF line endings to a single LF character while reading. The same logic is used for compressed and uncompressed DAT entries. This matters for text formats such as MSG, LST, and world-map text configuration files.

Text-mode seeks are restricted. CE only supports rewinding or seeking to an absolute offset; arbitrary relative/end seeks in text streams are rejected because CRLF normalization makes byte positions ambiguous.

DAT2 Reader Algorithm

  1. Open the file as binary and get its physical size.
  2. Read tree_size and data_size from the last 8 bytes.
  3. Set data_base = physical_size - data_size.
  4. Seek to physical_size - tree_size - 8.
  5. Read little-endian file_count.
  6. Read file_count variable-length entries.
  7. Sort-check entries case-insensitively if the archive will be used by an engine that binary-searches the directory.
  8. For each entry, read from data_base + data_offset for data_size bytes.
  9. If compressed == 1, inflate with zlib and require uncompressed_size output bytes.

DAT2 Writer Notes

DAT2 Validation Checklist

DAT1 Overview

DAT1 is the Fallout 1 archive format. It is structurally unrelated to DAT2. Metadata values are big-endian and the archive uses a directory-oriented layout followed by file data.

DAT1 Header

FieldSizeDescription
directory_count4Number of directory records.
folder_allocation_hint4Allocation hint used by the engine. It must be at least directory_count or classic code can overrun its allocation.
reserved4Usually 0.
timestamp4Unix-style creation timestamp in known archives. Read by tools, not normally validated by the game.

DAT1 Directory Name Block

The header is followed by directory_count directory names:

uint8  name_length
char   name[name_length]

Fallout 1 master.dat includes a root directory named .. Do not discard it as padding; it contains files such as color.pal and fonts.

DAT1 Directory Content Block

After the directory names, each directory has a content block:

FieldSizeDescription
file_count4Number of files in the directory.
file_allocation_hint4Allocation hint; should be at least file_count.
fixed_metadata_size4Usually 16, matching the four 32-bit metadata fields in each file entry.
timestamp4Directory timestamp.

Each file entry then contains:

FieldSizeDescription
name_length1Filename length.
nameVariableFilename within the current directory.
attributes40x20 for plain data, 0x40 for LZSS-compressed data in common docs/tools.
offset4Physical offset of file data from the beginning of the DAT.
size4Uncompressed size.
packed_size4Compressed size. Usually 0 for plain entries.

DAT1 LZSS Blocks

DAT1 compressed entries use a Fallout LZSS block stream. This is the file-entry decompression layer, not the archive directory parser.

The token flags are consumed least-significant bit first. If the current flag bit is set, the next input byte is literal data and is also written into the dictionary. If the bit is clear, the next two bytes encode a dictionary offset and a length nibble; length + 3 bytes are copied from the dictionary to output and back into the dictionary. The implementation used by community tools allows copied bytes to overwrite dictionary positions that may still be read later in the same match, which is normal for LZSS-style overlap.

Practical Modding Notes

Source References

Tools

Credits

Original DAT1 format notes are credited by older community documents to Shadowbird. Original DAT2 format notes are credited to MatuX. This page also incorporates behavior verified against Fallout 2 Community Edition source.

History

2019-12-15 - Ported from Vault-Tec Labs DAT file format by ghost.