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 format;
29mod opcodes;
30pub mod reflection;
31mod sampling;
32pub mod statistics;
33
34pub use controls::{GlobalFlags, InfoReturn, SyncFlags};
35pub use decode::{
36 Declaration, Declared, Decoded, Index, Instruction, Operand, ProgramType, Register, Selection, decode,
37 opcode_name,
38};
39pub use format::{OperandClass, OperandFormat, operand_format};
40pub use reflection::{
41 Rdef, RdefBinding, RdefBuffer, RdefMember, RdefType, RdefVariable, SignatureElement,
42 patch_signature_elements, program_type, rdef, rdef_target, signature, signature_elements,
43};
44pub use sampling::{TextureRead, texture_reads};
45pub use statistics::{Statistics, statistics};
46
47#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
49#[error("DXBC at word {word}: {detail}")]
50pub struct Error {
51 pub word: usize,
53 pub detail: String,
55}
56
57pub type Result<T> = std::result::Result<T, Error>;
59
60pub(crate) fn error(word: usize, detail: impl Into<String>) -> Error {
61 Error {
62 word,
63 detail: detail.into(),
64 }
65}
66
67pub fn parts(bytes: &[u8]) -> Result<Vec<dxcontainer::Chunk<'_>>> {
69 dxcontainer::chunks(bytes).map_err(|e| error(e.offset / 4, e.detail))
70}
71
72pub fn chunk<'a>(directory: &[dxcontainer::Chunk<'a>], tag: &[u8; 4]) -> Result<&'a [u8]> {
74 let shown = String::from_utf8_lossy(tag);
75 let mut matching = directory.iter().filter(|c| &c.kind == tag);
76 let data = matching
77 .next()
78 .ok_or_else(|| error(0, format!("the container has no {shown} part")))?
79 .data;
80 if matching.next().is_some() {
81 return Err(error(
82 0,
83 format!("the container has the {shown} part more than once"),
84 ));
85 }
86 Ok(data)
87}
88
89pub fn code(bytes: &[u8]) -> Result<Vec<u32>> {
91 let directory = parts(bytes)?;
92 let code = directory
93 .iter()
94 .find(|c| matches!(&c.kind, b"SHDR" | b"SHEX"))
95 .ok_or_else(|| error(0, "missing shader code chunk"))?;
96 if code.data.len() % 4 != 0 {
97 return Err(error(
98 code.data.len() / 4,
99 format!(
100 "the code part is {} bytes, expected a multiple of 4 (32-bit tokens)",
101 code.data.len()
102 ),
103 ));
104 }
105 Ok(code
106 .data
107 .as_chunks::<4>()
108 .0
109 .iter()
110 .map(|b| u32::from_le_bytes(*b))
111 .collect())
112}