# d3dasm
Direct3D shader bytecode disassembler and encoder written in Rust.
Parses DXBC containers, decodes SM4/SM5 shader bytecode into a fully typed intermediate representation, and produces `fxc.exe`-compatible disassembly output. The IR can be re-encoded to byte-identical bytecode, enabling shader inspection and modification workflows.
## Features
- **Full SM4/SM5 bytecode round-trip** — Decode → IR → encode produces bit-identical output across all 217+ opcodes. Verified against 1,152 real-world shader chunks with zero failures.
- **Typed IR with no raw bit hacks** — Every opcode field (GS primitives, topologies, component selection modes, sample counts, test conditions) is represented as a proper Rust enum. No raw bit buckets.
- **18 chunk types parsed** — RDEF, ISGN/ISG1, OSGN/OSG1/OSG5, PCSG/PSG1, SHEX/SHDR, STAT, RTS0, SFI0, HASH/XHSH, ILDN, ILDB, PRIV, DXIL, PSV0, RDAT, LIBF, LFS0, LIBH. Unknown chunks are preserved for lossless container rebuilds.
- **Zero-copy reads, copy-on-write mutation** — Metadata strings and raw blobs borrow directly from the input buffer via `Cow<'a, T>`. Assigning `Cow::Owned(...)` transparently enables shader patching without touching the original data.
- **Lazy chunk parsing** — Non-program chunks (RDEF, signatures, STAT, etc.) are parsed on first access via `OnceCell`. Callers that only need the shader program pay zero overhead for metadata.
- **Container scanning** — Automatically finds DXBC containers in arbitrary byte streams (raw files, `fxb0` wrappers, custom game archives).
- **`no_std` core** — The `dxbc` crate uses only `core` and `alloc`, no filesystem or I/O.
- **All shader stages** — VS, PS, GS, HS, DS, CS.
## Crates
| `dxbc` | lib (`no_std`) | DXBC container parser/writer, chunk decoders, SM4/SM5 bytecode decode/encode/format |
| `d3dasm` | lib | High-level disassembly interface — wraps `dxbc` with typed accessors and `Display` formatting |
| `d3dasm-cli` | bin | Command-line disassembler |
## Usage
```sh
cargo run -- <shader.bin>
```
### As a library
```rust
let data = std::fs::read("shader.bin").unwrap();
for shader in d3dasm::parse(&data) {
// Typed access to parsed chunks.
if let Some(program) = shader.program() {
println!("SM {}.{}", program.version_major, program.version_minor);
}
// fxc.exe-compatible disassembly via Display.
print!("{shader}");
}
```
### Lower-level access via `dxbc`
```rust
let container = dxbc::DxbcContainer::parse(bytes).unwrap();
for chunk in &container.chunks {
match chunk.parse() {
dxbc::ChunkData::Shader(program) => {
// Round-trip: decode → encode → identical bytes.
let encoded = dxbc::shex::encode(&program);
assert_eq!(chunk.data, &encoded[..]);
}
_ => {}
}
}
```
## Example Output
```hlsl
// Compiled with: Microsoft (R) HLSL Shader Compiler 10.0.10011.0
//
// Resource Bindings:
// Name Type Dim Slot Count Flags
// --------------- ------------ -------- ---- ----- -----
// gPointSampler0 sampler NA 0 1
// gPointTexture0 texture 2dMS 0 1 comparisonSampler;texComp0
//
// Input Signature:
// TEXCOORD float v0.xy
// TEXCOORD1 float v0.zw
// SV_Position float v1.xyzw
//
// Output Signature:
// SV_Target float v0.xyzw
//
ps_5_0
dcl_globalFlags refactoringAllowed
dcl_sampler s0, mode_default
dcl_resource_texture2d (float,float,float,float) t0
dcl_input_ps linear v0.xy
dcl_input_ps linear v0.zw
dcl_output o0.xyzw
dcl_temps 1
sample r0.xyzw, v0.xyxx, t0.xyzw, s0
mov o0.xyzw, r0.xyzw
ret
```
## Project Structure
```
d3dasm/
├── Cargo.toml # Workspace root (resolver = "3")
├── crates/
│ ├── dxbc/ # no_std DXBC library
│ │ └── src/
│ │ ├── container.rs # DXBC container parser, writer, scanner
│ │ ├── chunks/ # Per-chunk parsers (rdef, signature, stat, …)
│ │ ├── shex/ # SM4/SM5 bytecode
│ │ │ ├── ir.rs # Typed intermediate representation
│ │ │ ├── decode.rs # Bytes → IR
│ │ │ ├── encode.rs # IR → bytes
│ │ │ ├── fmt.rs # IR → disassembly text
│ │ │ └── opcodes.rs # Opcode enum (217+ opcodes)
│ │ └── util.rs # Shared helpers
│ ├── d3dasm/ # High-level disassembly API
│ │ └── src/lib.rs
│ └── d3dasm-cli/ # CLI binary
│ └── src/main.rs
```
## Building
```sh
cargo build
```
Requires Rust 1.85+ (edition 2024, `core::cell::OnceCell` stable since 1.70).
## Testing
```sh
cargo test --workspace
```
Round-trip verification across all test shaders:
```sh
cargo run --release --example roundtrip -- shaders/shaders-pc/**/*.bin
```