primase-foundations
A Rust library of shared building blocks for programs that read and convert game files. It is the base layer of primase and the format crates built on it, and it does not read any game's archive or container format itself.
Features
- Bounds-checked
ViewandCursorbyte readers with the byte order stated on every multi-byte read - CRC-32, Adler-32 from any start value, Jenkins one-at-a-time (case-folded and exact), MD5 with its block function exposed, and SDBM
- Decoding of ten DXGI vertex element formats, vertex layouts and vertex streams
- Packed 11/11/10 and 21/21/22 positions, packed tangents, triangle strip to list conversion, and mesh splitting for 16-bit indices
- Texture sampling with wrap, clamp, mirror, mirror-once and border addressing; point, bilinear and trilinear filtering; 2D and 3D textures
- The sRGB transfer curve in
f64andf32, with table-based byte conversions - An ordered parallel
mapand in-placerows_mutover scoped threads with a stated thread count scan, which reads many entries of a few large archive files in file order on several threads (unbuffered on Windows)- Decompression of LZ4 blocks, raw deflate, zlib, Zstandard, LZO1X, LZO2A, LZMA, Brotli, LZX (XNA's content) and Oodle, plus a streaming zlib/deflate inflater (feature
compress) - Compression to LZ4, deflate and zlib, and to Oodle Kraken through the game's own Oodle library
- A walker that checks an Oodle stream's block and quantum headers without decoding it
- Reading whole tables from an SQLite 3 file image (feature
sqlite) - Malformed input returns an error naming the offset instead of panicking, and decoders allocate no more than the input can produce (Oodle excepted)
- Vertex arithmetic uses a fixed
f32operation order without fused multiply-add, so the same bytes give the same bits on every platform - Library code does no file, environment or process access, except
Oodle::loadandscan;unsafeis used only incompress::oodle
Getting Started
Requirements
Rust 1.99 or newer (edition 2024). rust-toolchain.toml pins 1.99.0.
Installation
The crate is published on crates.io.
| feature | what it adds |
|---|---|
| (default) | bytes, hash, vertex, parallel, scan (Unix and Windows), color, sampling |
compress |
the compress module: LZ4, deflate, zlib, Zstandard, LZO1X, LZO2A, LZMA, Brotli, LZX and Oodle |
sqlite |
the sqlite module, through quantydb-sqlite |
reference-decoder |
on Windows, compress::reference: oozextract as a second Oodle decoder for comparing against the game's library |
The Oodle library is not shipped or downloaded. Oodle::load takes the path of the DLL the game ships (oo2core_5_win64.dll, oo2core_6_win64.dll or oo2core_9_win64.dll) and is unsafe because loading it runs native code. Off Windows, Oodle::open(None, version) decodes with the pure-Rust oozextract instead, which cannot compress; on Windows a library path is required.
Usage
Read a small record with a Cursor, then see what a truncated copy reports:
use ;
use crc32;
// "DATA", a little-endian count of 2-byte entries, then the entries
let file = ;
let mut cursor = new;
assert_eq!;
let count = cursor.count?; // refused unless that many entries fit
let mut entries = Vecnew;
for _ in 0..count
assert_eq!;
assert_eq!;
// cut short, the read is refused with the field and the offset
let mut cut = new;
cut.take?;
let refusal = cut.count.field.unwrap_err;
assert_eq!;
Ok::
With compress enabled, every codec decodes through Decompressor::decompress(input, expected_len), where the expected size comes from the container; any other output size, leftover input or failed checksum is an error. There is no format autodetection. docs/guarantees.md lists what the crate guarantees and what it does not do.
Run the tests with cargo test --workspace; they need no game files. Opt-in tests that read your own game files are described in docs/testing.md, and build and contribution rules are in CONTRIBUTING.md. Licensed under GPL-3.0-or-later; see LICENSE.