Skip to main content

dxbc_reader/
interfaces.rs

1//! The `IFCE` part of a Shader Model 5 program with class linkage (dynamic shader linkage): the classes that
2//! implement its interfaces, the class instances its constant buffers hold, and which function tables each interface
3//! slot can be bound to.
4//!
5//! The layout below was read from containers fxc 10.0.26100.4654 built with one to thirty classes per interface,
6//! interface arrays, several interfaces, classes with data, textures and samplers, instance arrays, and pixel and hull
7//! programs, each checked against `D3DDisassemble`'s printing of the same tables. Words, from the start of the part:
8//!
9//! - 0 to 3: the counts of class instances, class types and slot records, and the number of interface slots;
10//! - 4 to 6: the byte offsets of the instance table (16 bytes a record), the type table (12) and the slot records
11//!   (16);
12//! - 7 and 8: 0 in every container seen.
13//!
14//! Offsets are from the start of the part; names are zero-terminated strings after the tables.
15use crate::reflection::View;
16use crate::{Result, error, parts};
17
18/// A class that implements an interface, by its place in the type table (its id).
19#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct ClassType {
21    /// The class's name.
22    pub name: String,
23    /// The constant buffer registers one instance's data takes.
24    pub stride: u16,
25    /// The textures one instance holds.
26    pub textures: u16,
27    /// The samplers one instance holds.
28    pub samplers: u16,
29    /// The low half of the record's second word, 0 in every container seen.
30    pub reserved: u16,
31}
32
33/// A class instance in a constant buffer, or an array of them (`suns[3]`).
34#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct ClassInstance {
36    /// The instance's name.
37    pub name: String,
38    /// The class type's id.
39    pub ty: u16,
40    /// The number of instances, more than 1 for an array.
41    pub count: u16,
42    /// The constant buffer its data is in; `None` for a class without data.
43    pub buffer: Option<u16>,
44    /// The register of that buffer its data starts at; `None` for a class without data.
45    pub offset: Option<u16>,
46    /// The first texture register it holds; `None` where it holds none.
47    pub texture: Option<u16>,
48    /// The first sampler register it holds; `None` where it holds none.
49    pub sampler: Option<u16>,
50}
51
52/// A run of interface slots (one interface pointer, or an array of them) and the classes that can be bound to it.
53#[derive(Clone, Debug, PartialEq, Eq)]
54pub struct InterfaceSlots {
55    /// The number of slots it takes.
56    pub span: u32,
57    /// The class type ids that can be bound to it.
58    pub types: Vec<u16>,
59    /// The function table of each, at the same place as its type id.
60    pub tables: Vec<u32>,
61}
62
63/// The `IFCE` part.
64#[derive(Clone, Debug, PartialEq, Eq)]
65pub struct Interfaces {
66    /// The number of interface slots (word 3).
67    pub slot_count: u32,
68    /// The class types, by id.
69    pub types: Vec<ClassType>,
70    /// The class instances.
71    pub instances: Vec<ClassInstance>,
72    /// The slot records, in slot order.
73    pub slots: Vec<InterfaceSlots>,
74    /// Words 7 and 8.
75    pub reserved: [u32; 2],
76}
77
78/// A 16-bit register field, where `0xffff` is none.
79fn register(value: u16) -> Option<u16> {
80    (value != 0xffff).then_some(value)
81}
82
83/// The container's `IFCE` part; `None` without one. Refuses a container [`parts`] refuses and a part whose tables or
84/// names lie outside it, naming the word.
85pub fn interfaces(bytes: &[u8]) -> Result<Option<Interfaces>> {
86    let directory = parts(bytes)?;
87    let Some(chunk) = directory.iter().find(|c| &c.kind == b"IFCE") else {
88        return Ok(None);
89    };
90    let v = View(chunk.data);
91    let word = |n: usize| v.u32(n.saturating_mul(4));
92    let count = |n: usize| -> Result<usize> {
93        usize::try_from(word(n)?)
94            .map_err(|_| error(n, format!("IFCE word {n}: the count exceeds the address space")))
95    };
96    let offset = |n: usize| -> Result<usize> {
97        usize::try_from(word(n)?)
98            .map_err(|_| error(n, format!("IFCE word {n}: the offset exceeds the address space")))
99    };
100    let (instance_count, type_count, record_count) = (count(0)?, count(1)?, count(2)?);
101    let (instance_at, type_at, record_at) = (offset(4)?, offset(5)?, offset(6)?);
102    let mut types = Vec::new();
103    for i in v.table(type_at, type_count, 12)? {
104        let p = v.record(type_at, i, 12)?;
105        types.push(ClassType {
106            name: v.string(v.u32(p)? as usize)?,
107            reserved: v.field16(p, 4)?,
108            stride: v.field16(p, 6)?,
109            textures: v.field16(p, 8)?,
110            samplers: v.field16(p, 10)?,
111        });
112    }
113    let mut instances = Vec::new();
114    for i in v.table(instance_at, instance_count, 16)? {
115        let p = v.record(instance_at, i, 16)?;
116        instances.push(ClassInstance {
117            name: v.string(v.u32(p)? as usize)?,
118            ty: v.field16(p, 4)?,
119            count: v.field16(p, 6)?,
120            buffer: register(v.field16(p, 8)?),
121            offset: register(v.field16(p, 10)?),
122            texture: register(v.field16(p, 12)?),
123            sampler: register(v.field16(p, 14)?),
124        });
125    }
126    let mut slots = Vec::new();
127    for i in v.table(record_at, record_count, 16)? {
128        let p = v.record(record_at, i, 16)?;
129        let entries = v.field(p, 4)? as usize;
130        let (type_ids, table_ids) = (v.field(p, 8)? as usize, v.field(p, 12)? as usize);
131        let mut record = InterfaceSlots {
132            span: v.u32(p)?,
133            types: Vec::new(),
134            tables: Vec::new(),
135        };
136        for j in v.table(type_ids, entries, 2)? {
137            record.types.push(v.u16(v.record(type_ids, j, 2)?)?);
138        }
139        for j in v.table(table_ids, entries, 4)? {
140            record.tables.push(v.u32(v.record(table_ids, j, 4)?)?);
141        }
142        slots.push(record);
143    }
144    Ok(Some(Interfaces {
145        slot_count: word(3)?,
146        types,
147        instances,
148        slots,
149        reserved: [word(7)?, word(8)?],
150    }))
151}