#![deny(
clippy::arithmetic_side_effects,
clippy::indexing_slicing,
clippy::cast_possible_truncation
)]
#![deny(missing_docs)]
#![deny(clippy::disallowed_methods, clippy::disallowed_types)]
mod controls;
pub mod decode;
mod disasm;
mod format;
pub mod interfaces;
pub mod level9;
mod opcodes;
pub mod reflection;
mod sampling;
mod spdb;
pub mod statistics;
pub use controls::{GlobalFlags, InfoReturn, SyncFlags};
pub use decode::{
Declaration, Declared, Decoded, Index, Instruction, Operand, ProgramType, Register, RegisterRange,
Selection, decode, opcode_name,
};
pub use disasm::{Options, disassemble, disassemble_with};
pub use format::{OperandClass, OperandFormat, operand_format};
pub use interfaces::{ClassInstance, ClassType, InterfaceSlots, Interfaces, interfaces};
pub use level9::{ConstantMapping, Level9, RuntimeConstant, SamplerMapping, ScalarMapping, level9};
pub use reflection::{
Rdef, RdefBinding, RdefBuffer, RdefMember, RdefType, RdefVariable, SignatureElement,
patch_signature_elements, program_type, rdef, rdef_target, signature, signature_elements,
};
pub use sampling::{TextureRead, texture_reads};
pub use statistics::{Statistics, statistics};
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[error("DXBC at word {word}: {detail}")]
pub struct Error {
pub word: usize,
pub detail: String,
}
pub type Result<T> = std::result::Result<T, Error>;
pub(crate) fn error(word: usize, detail: impl Into<String>) -> Error {
Error {
word,
detail: detail.into(),
}
}
pub fn parts(bytes: &[u8]) -> Result<Vec<dxcontainer::Chunk<'_>>> {
dxcontainer::chunks(bytes).map_err(|e| error(e.offset / 4, e.detail))
}
pub fn chunk<'a>(directory: &[dxcontainer::Chunk<'a>], tag: &[u8; 4]) -> Result<&'a [u8]> {
let shown = String::from_utf8_lossy(tag);
let mut matching = directory.iter().filter(|c| &c.kind == tag);
let data = matching
.next()
.ok_or_else(|| error(0, format!("the container has no {shown} part")))?
.data;
if matching.next().is_some() {
return Err(error(
0,
format!("the container has the {shown} part more than once"),
));
}
Ok(data)
}
pub(crate) fn code_part<'a>(directory: &'a [dxcontainer::Chunk<'a>]) -> Result<&'a dxcontainer::Chunk<'a>> {
if directory.iter().any(|c| &c.kind == b"DXIL") {
return Err(error(
0,
"a DXIL container (Shader Model 6): its program is the DXIL part's LLVM bitcode, not a Shader Model 4 or 5 \
token stream",
));
}
directory
.iter()
.find(|c| matches!(&c.kind, b"SHDR" | b"SHEX"))
.ok_or_else(|| error(0, "the container has no code part (SHEX, SHDR or DXIL)"))
}
pub fn code(bytes: &[u8]) -> Result<Vec<u32>> {
let directory = parts(bytes)?;
let code = code_part(&directory)?;
if code.data.len() % 4 != 0 {
return Err(error(
code.data.len() / 4,
format!(
"the code part is {} bytes, expected a multiple of 4 (32-bit tokens)",
code.data.len()
),
));
}
Ok(code
.data
.as_chunks::<4>()
.0
.iter()
.map(|b| u32::from_le_bytes(*b))
.collect())
}