1#![deny(
19 clippy::arithmetic_side_effects,
20 clippy::indexing_slicing,
21 clippy::cast_possible_truncation
22)]
23#![deny(missing_docs)]
24#![deny(clippy::disallowed_methods, clippy::disallowed_types)]
25
26mod controls;
27pub mod decode;
28mod disasm;
29mod format;
30pub mod interfaces;
31pub mod level9;
32mod opcodes;
33pub mod reflection;
34mod sampling;
35mod spdb;
36pub mod statistics;
37
38pub use controls::{GlobalFlags, InfoReturn, SyncFlags};
39pub use decode::{
40 Declaration, Declared, Decoded, Index, Instruction, Operand, ProgramType, Register, RegisterRange,
41 Selection, decode, opcode_name,
42};
43pub use disasm::{Options, disassemble, disassemble_with};
44pub use format::{OperandClass, OperandFormat, operand_format};
45pub use interfaces::{ClassInstance, ClassType, InterfaceSlots, Interfaces, interfaces};
46pub use level9::{ConstantMapping, Level9, RuntimeConstant, SamplerMapping, ScalarMapping, level9};
47pub use reflection::{
48 Rdef, RdefBinding, RdefBuffer, RdefMember, RdefType, RdefVariable, SignatureElement,
49 patch_signature_elements, program_type, rdef, rdef_target, signature, signature_elements,
50};
51pub use sampling::{TextureRead, texture_reads};
52pub use statistics::{Statistics, statistics};
53
54#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
56#[error("DXBC at word {word}: {detail}")]
57pub struct Error {
58 pub word: usize,
60 pub detail: String,
62}
63
64pub type Result<T> = std::result::Result<T, Error>;
66
67pub(crate) fn error(word: usize, detail: impl Into<String>) -> Error {
68 Error {
69 word,
70 detail: detail.into(),
71 }
72}
73
74pub fn parts(bytes: &[u8]) -> Result<Vec<dxcontainer::Chunk<'_>>> {
76 dxcontainer::chunks(bytes).map_err(|e| error(e.offset / 4, e.detail))
77}
78
79pub fn chunk<'a>(directory: &[dxcontainer::Chunk<'a>], tag: &[u8; 4]) -> Result<&'a [u8]> {
81 let shown = String::from_utf8_lossy(tag);
82 let mut matching = directory.iter().filter(|c| &c.kind == tag);
83 let data = matching
84 .next()
85 .ok_or_else(|| error(0, format!("the container has no {shown} part")))?
86 .data;
87 if matching.next().is_some() {
88 return Err(error(
89 0,
90 format!("the container has the {shown} part more than once"),
91 ));
92 }
93 Ok(data)
94}
95
96pub(crate) fn code_part<'a>(directory: &'a [dxcontainer::Chunk<'a>]) -> Result<&'a dxcontainer::Chunk<'a>> {
99 if directory.iter().any(|c| &c.kind == b"DXIL") {
100 return Err(error(
101 0,
102 "a DXIL container (Shader Model 6): its program is the DXIL part's LLVM bitcode, not a Shader Model 4 or 5 \
103 token stream",
104 ));
105 }
106 directory
107 .iter()
108 .find(|c| matches!(&c.kind, b"SHDR" | b"SHEX"))
109 .ok_or_else(|| error(0, "the container has no code part (SHEX, SHDR or DXIL)"))
110}
111
112pub fn code(bytes: &[u8]) -> Result<Vec<u32>> {
115 let directory = parts(bytes)?;
116 let code = code_part(&directory)?;
117 if code.data.len() % 4 != 0 {
118 return Err(error(
119 code.data.len() / 4,
120 format!(
121 "the code part is {} bytes, expected a multiple of 4 (32-bit tokens)",
122 code.data.len()
123 ),
124 ));
125 }
126 Ok(code
127 .data
128 .as_chunks::<4>()
129 .0
130 .iter()
131 .map(|b| u32::from_le_bytes(*b))
132 .collect())
133}