dxcontainer 0.5.1

Parser for the DXBC container that holds Direct3D shader bytecode (DXBC and DXIL, Shader Model 4 to 6): reads the part directory and computes the digest
Documentation

dxcontainer

Reads the part directory of a DXBC container, the file format Direct3D shader programs are stored in (Shader Model 4 to 6), computes its digest, and writes a container again with the digest repaired.

let bytes = std::fs::read("shader.dxbc")?;
for part in dxcontainer::chunks(&bytes)? {
    println!("{}: {} bytes", String::from_utf8_lossy(&part.kind), part.data.len());
}
println!("digest: {:02x?}", dxcontainer::checksum(&bytes));

A shader-patching mod edits a part and writes the container back:

let bytes = std::fs::read("shader.dxbc")?;
let new_program = std::fs::read("replacement.shex")?; // a replacement SHEX part payload
let patched = dxcontainer::replace_part(&bytes, *b"SHEX", &new_program)?; // the other parts as they were, the digest repaired
std::fs::write("patched.dxbc", patched)?;

Run it with cargo run -p dxcontainer --example parts -- shader.dxbc; the source is examples/parts.rs. The crate documentation has a self-contained example with a hand-built container.

What it reads

A header (DXBC, a 16-byte digest, version 1, the container's size, the number of parts), the directory of the parts' offsets, then the parts one after another in the directory's order (each its four-character kind, its length and its bytes), the last ending where the container does. Shader Model 6 parts are not padded, so an offset need not be a multiple of 4. This layout holds for the 137 containers committed under dxbc-reader/tests/fixtures and dxil-reader/tests/fixtures: the tests read each to its exact end and check that checksum equals the digest it stores.

serialize(&parts) writes parts as a container: the header, the directory, the parts in the order given, each exactly as given with nothing between them (a part keeps the padding its producer wrote, or none), and the digest checksum computes. serialize(&chunks(bytes)?) is bytes again for every container that stores the digest checksum computes, which includes all 77,457 stock containers of five games. repair_digest rewrites the digest of a container edited in place, and replace_part replaces (or appends) one part of a container and writes the result. A kind twice is refused, as the reader refuses it. Outside tools agree: Microsoft's DXIL validator signs a container whose digest was zeroed with the digest repair_digest writes, and vkd3d's vkd3d-dxbc accepts the containers written here (it checks the digest) and refuses the same bytes with one digest byte changed.

feature_flags reads the SFI0 part, the features the shader requires, as a u64 with the bytecode (SHDR/SHEX or DXIL) that says what its bits mean; feature_name names a bit the way the compiler lists it. The names were read off fxc and dxc output for programs that need one feature each (tests/fixtures/features, with the sources).

What it guarantees

The reader reads to the exact end and refuses, with an Error that names the byte, a part anywhere else (overlapping, or after a gap), a byte after the last part, a part kind twice, a size other than the container's and a version other than 1. The crate denies indexing and unchecked arithmetic at compile time, so untrusted bytes produce an Error, not a panic.

What it does not do

It does not verify the digest: the digest is not an authenticity check. checksum(bytes) computes it (MD5's block function over the bytes from 20 on, padded as the compiler pads it), for a caller that wants to compare; on all 137 committed fixtures it equals the stored digest, as it does on the 77,457 containers of five games' shipped shaders (none stores a zero digest). It interprets no part but SFI0; dxbc-reader (Shader Model 4 and 5) and dxil-reader (Shader Model 6) do.

Tests

cargo test -p dxcontainer: containers built by hand for each refusal, every truncation of one, and the committed fixtures of dxbc-reader and dxil-reader read to their end. No game files are needed.

Check the examples

From the repository root, cargo test -p dxcontainer --doc checks the crate documentation, and cargo build -p dxcontainer --examples builds its example programs. Examples that read paths need the caller's files; the repository ships no game assets.

License: GPL-3.0-or-later.