dxbc_reader/disasm/
mod.rs1use crate::reflection::{rdef, signature};
5use crate::{Result, chunk, code, decode, error, parts, statistics};
6
7mod header;
8mod level9;
9mod number;
10mod program;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15#[non_exhaustive]
16pub struct Options {
17 pub header: bool,
20 pub numbering: bool,
23 pub offsets: bool,
26 pub hex_literals: bool,
28 pub debug_info: bool,
33}
34
35impl Default for Options {
36 fn default() -> Self {
37 Self {
38 header: true,
39 numbering: false,
40 offsets: false,
41 hex_literals: false,
42 debug_info: true,
43 }
44 }
45}
46
47impl Options {
48 pub fn from_flags(flags: u32) -> Result<Self> {
53 for (bit, name) in [
54 (0x01, "D3D_DISASM_ENABLE_COLOR_CODE (an HTML listing)"),
55 (
56 0x08,
57 "D3D_DISASM_ENABLE_INSTRUCTION_CYCLE, which Microsoft's disassembler refuses too",
58 ),
59 ] {
60 if flags & bit != 0 {
61 return Err(error(0, format!("flag 0x{bit:02x}, {name}, is not written")));
62 }
63 }
64 let unknown = flags & !0xf6;
65 if unknown != 0 {
66 return Err(error(
67 0,
68 format!(
69 "flag bit 0x{:x} is not a D3D_DISASM flag",
70 1u32 << unknown.trailing_zeros()
71 ),
72 ));
73 }
74 Ok(Self {
75 header: flags & 0x40 == 0,
76 numbering: flags & 0x04 != 0,
77 offsets: flags & 0x20 != 0,
78 hex_literals: flags & 0x80 != 0,
79 debug_info: flags & 0x10 == 0,
80 })
81 }
82}
83
84pub fn disassemble(container: &[u8]) -> Result<String> {
90 disassemble_with(container, &Options::default())
91}
92
93pub fn disassemble_with(container: &[u8], options: &Options) -> Result<String> {
95 let directory = parts(container)?;
96 let program = decode(&code(container)?)?;
97 let reflection = rdef(container)?;
98 let read = |tags: &[&[u8; 4]]| -> Result<Option<(Vec<crate::SignatureElement>, bool)>> {
99 match tags.iter().find(|t| directory.iter().any(|c| &&c.kind == *t)) {
100 Some(tag) => {
101 let elements = signature(chunk(&directory, tag)?, tag)?;
102 let streams = elements.iter().any(|e| e.stream != 0);
104 Ok(Some((elements, streams)))
105 }
106 None => Ok(None),
107 }
108 };
109 let inputs = read(&[b"ISGN", b"ISG1"])?;
110 let outputs = read(&[b"OSGN", b"OSG1", b"OSG5"])?;
111 let patch = read(&[b"PCSG", b"PSG1"])?;
112 let stat = statistics(container)?;
113 let mut out = String::new();
114 if options.header {
115 header::write(
116 &mut out,
117 &header::Header {
118 program: &program,
119 rdef: reflection.as_ref(),
120 features: dxcontainer::feature_flags(container)
121 .map_err(|e| error(e.offset / 4, e.detail))?
122 .map_or(0, |f| f.bits),
123 stat: stat.as_ref(),
124 inputs: elements(&inputs),
125 outputs: elements(&outputs),
126 patch: elements(&patch),
127 interfaces: crate::interfaces(container)?.as_ref(),
128 debug_layer: dxcontainer::checksum(container).as_ref().map(|d| &d[..])
129 != container.get(4..20),
130 },
131 );
132 if let Some(part) = crate::level9(container)? {
133 level9::write(&mut out, &part, options)?;
134 }
135 }
136 let annotations = match directory.iter().any(|c| &c.kind == b"SPDB") && options.debug_info {
137 true => Some(crate::spdb::annotations(chunk(&directory, b"SPDB")?)?),
138 false => None,
139 };
140 program::write(&mut out, &program, options, annotations.as_ref());
141 if !options.header {
142 return Ok(out);
143 }
144 let slots = stat.and_then(|s| s.instructions()).unwrap_or(0);
146 out.push_str(&format!("// Approximately {slots} instruction slots used\n"));
147 Ok(out)
148}
149
150fn elements(part: &Option<(Vec<crate::SignatureElement>, bool)>) -> Option<header::Elements<'_>> {
152 part.as_ref().map(|(elements, streams)| header::Elements {
153 elements,
154 streams: *streams,
155 })
156}