Fallout and Troika .dat management CLI.
Crossplatform, static Rust re-implementation of DAT2, with minor differences. Also supports Arcanum and The Temple of Elemental Evil .dat archives.
dat3
Fallout and Troika .dat management CLI
Usage: dat3 [OPTIONS] <COMMAND>
Commands:
l List files in a DAT archive
x Extract files preserving directory structure
e Extract files flat (no subdirectories)
a Add files to a DAT archive
d Delete files from a DAT archive
help Print this message or the help of the given subcommand(s)
Options:
--case-sensitive Match, list, extract and add entry names exactly as stored. Without it, names match regardless of case and are listed, extracted and added in lowercase
-h, --help Print help
-V, --version Print version
dat3 x master.datdat3 x master.dat -o ./extracted/# Can use forward or backward slashes
dat3 x master.dat art/critters/HMMAXX.FRM scripts\generic.int
# Extract with glob pattern (quote to prevent shell expansion)
dat3 x master.dat 'art/critters/*.frm'dat3 e master.dat -o ./files/# List all files
dat3 l master.dat
# List specific files. Can use forward or backward slashes. Output always shows OS-native slash.
dat3 l master.dat art/critters/vault.frm text\english\quotes.txt
# List with glob pattern (quote to prevent shell expansion)
dat3 l master.dat 'art/critters/*.frm'
# List files from response file
dat3 l master.dat @files_to_list.txt
# List as JSON, for another program to parse
dat3 l master.dat --json--json prints one entry object per line, and always uses forward slashes so the
output is identical on every platform:
[
{"name": "art/critters/vault.frm", "size": 4096, "packed_size": 1180, "compressed": true},
{"name": "text/english/quotes.txt", "size": 84, "packed_size": 84, "compressed": false}
]The names it prints are the names x, e, and d accept back. A pattern that
matches nothing is still an error: the array on stdout is empty, the unmatched
name goes to stderr, and the exit status is non-zero.
By default l, x and e fail when a requested name or glob matches nothing,
and extract nothing. --ignore-missing turns that failure into a warning:
# Lists and extracts whatever is there, warns about the rest, exits 0
dat3 l master.dat --ignore-missing art/critters/vault.frm no/such/file.txt
dat3 x master.dat --ignore-missing -o ./extracted/ @files.txtThe unmatched names still go to stderr, so a script can log them. Useful when one file list is run against several archives and only some of them hold each file. Every name missing is not an error either - nothing is extracted and the exit status is still 0.
The games look files up regardless of case, so by default dat3 does too:
- names and globs given to
l,x,eanddmatch regardless of case landl --jsonlist names in lowercasexandecreate lowercase files and directoriesastores new entries in lowercase, and replaces an entry that differs only in case
Entries already in an archive keep their stored case until they are added again.
An archive can hold names that differ only in case, such as README.TXT and readme.txt. dat3 keeps those apart:
they are listed and extracted in their stored case, a name matching one of them selects all of them, and every command
that opens the archive prints a warning naming them.
--case-sensitive turns all of this off, for any command: names match exactly, and are listed, extracted and added
as stored or given.
dat3 x master.dat --case-sensitive -o ./extracted/# Create a file listing files to process
echo "art/critters/vault.frm" > files.txt
echo "text\english\quotes.txt" >> files.txt
echo "scripts/generic.int" >> files.txt
# Use with any command (mutually exclusive with explicit file lists)
dat3 l master.dat @files.txt
dat3 x master.dat @files.txt -o extracted/
dat3 e master.dat @files.txt -o flat/
dat3 a master.dat @files.txt
dat3 d master.dat @files.txt# Add single file
dat3 a master.dat myfile.txt
# Add files relative to another directory
dat3 a master.dat -C patch000 file.txt # patch000/file.txt
Adding: file.txt # Added to archive root
# Add directory (automatically recursive)
dat3 a master.dat myfolder/
# Add with max compression level
dat3 a master.dat largefile.txt -c 9
# Add to specific directory in archive
dat3 a master.dat myfile.txt -t "art/graphics"
# Choose the format for a new archive (dat1, dat2, arcanum, toee; default dat2)
dat3 a newarchive.dat myfiles/ --format dat1
dat3 a newarchive.dat myfiles/ --format arcanum
dat3 a newarchive.dat myfiles/ --format toee
# Add files from response file
dat3 a master.dat @files_to_add.txt./and.\prefixes are removed before storing paths in the archive- absolute source paths have only their filesystem root/prefix stripped
- the first real directory name is preserved
a -C DIR ...resolves add operands insideDIRand stores paths relative toDIRa -C DIR ...rejects./..components and paths that resolve outsideDIR
Examples:
dat3 a master.dat ./patch000/file.txt
# stores as patch000/file.txt
dat3 a master.dat ./patch000/*
# stores as patch000/...
dat3 a master.dat /tmp/patch000/file.txt
# stores as tmp/patch000/file.txtWhen a creates a new archive and no --format is given, an optional .bgforge.yml in the current directory picks the default:
dat3:
default_format: arcanumSupported values: dat1, dat2, arcanum, toee. An unrecognized value prints a warning and dat2 is used. An explicit --format always wins, and existing archives always keep their format.
# Delete single file (cross-platform paths supported)
dat3 d master.dat text/english/quotes.txt
# Delete multiple files
dat3 d master.dat file1.txt art\critters\vault.frm
# Delete with glob pattern (quote to prevent shell expansion)
dat3 d master.dat 'art/critters/*.frm'
# Delete files from response file
dat3 d master.dat @files_to_delete.txt- Directories are always processed recursively.
- Shrink (
kcommand) not implemented. - Flat extraction is a separate command,
e. - DAT1 compression (LZSS) not implemented, only decompression. Fallout 1 style .dat files are thus created without compression.
- Glob patterns (
*,?,[...]) supported for list/extract/delete. - Names are matched, listed, extracted and added in lowercase unless
--case-sensitiveis given (see Letter case in entry names).
Every release ships a SHA256SUMS file covering its binaries. Download it
alongside the assets and check them:
sha256sum -c SHA256SUMSOnly the files you downloaded need to be present; sha256sum reports the rest
as missing.
- Rust 1.87 or newer
- Target-specific toolchains (install as needed)
./install-tools.shfor the pinned tooling, including Zig, which the aarch64 target needs: mimalloc is C, and no aarch64-musl C compiler is packaged for common distros- Node 24 or newer, to run the integration suite (
./test.sh): its helpers undertests/are TypeScript, run by Node's own type stripping.npm ci && npm run typechecktypechecks them. Neither is needed to build dat3
./build.shBuilds are static.
Binaries will be at:
target/x86_64-unknown-linux-musl/release/dat3
target/aarch64-unknown-linux-musl/release/dat3
target/x86_64-pc-windows-gnu/release/dat3.exe
target/i686-pc-windows-gnu/release/dat3.exe
target/wasm32-wasip1/release/dat3.wasm