mod annotate;
mod asm;
mod header;
mod layout;
#[rustfmt::skip] mod ops;
use crate::{Module, Result, bitstream, error, psv, rdat};
pub fn disassemble(container: &[u8]) -> Result<String> {
Ok(listing(container)?.text)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Positioned {
pub text: String,
pub instructions: Vec<Listed>,
pub part: [u8; 4],
pub bitcode: usize,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Listed {
pub line: usize,
pub at: usize,
pub end: usize,
}
pub fn disassemble_with_positions(container: &[u8]) -> Result<Positioned> {
let l = listing(container)?;
let mut records = Vec::new();
for b in &l.blocks {
function_records(b, &mut records);
}
records.sort_unstable();
let mut instructions = Vec::with_capacity(l.marks.len());
let (mut line, mut counted) = (0usize, 0usize);
for mark in &l.marks {
let before = l
.text
.get(counted..mark.byte)
.ok_or_else(|| error(mark.at, "an instruction's text"))?;
line = line.saturating_add(before.bytes().filter(|&c| c == b'\n').count());
counted = mark.byte;
let end = records
.binary_search_by_key(&mark.at, |&(at, _)| at)
.ok()
.and_then(|i| records.get(i))
.map(|&(_, end)| end)
.ok_or_else(|| error(mark.at, "an instruction that is not a record of a function block"))?;
instructions.push(Listed {
line,
at: mark.at,
end,
});
}
Ok(Positioned {
text: l.text,
instructions,
part: l.part,
bitcode: l.bitcode,
})
}
fn function_records(block: &bitstream::Block, out: &mut Vec<(usize, usize)>) {
if block.id == 12 {
out.extend(block.records().map(|r| (r.at, r.end)));
}
for b in block.blocks() {
function_records(b, out);
}
}
struct Listing {
text: String,
marks: Vec<asm::Mark>,
blocks: Vec<bitstream::Block>,
part: [u8; 4],
bitcode: usize,
}
fn listing(container: &[u8]) -> Result<Listing> {
let parts = crate::parts(container)?;
let find = |kind: &[u8; 4]| parts.iter().find(|c| &c.kind == kind).map(|c| c.data);
let mut s = String::new();
if let Some(sfi0) = find(b"SFI0") {
header::feature_info(&mut s, sfi0)?;
}
for (kind, name, input) in [
(b"ISG1", "Input", true),
(b"OSG1", "Output", false),
(b"PSG1", "Patch Constant", false),
] {
if let Some(part) = find(kind) {
header::part_signature(&mut s, name, part, input)?;
}
}
if let Some(ildn) = find(b"ILDN") {
header::debug_name(&mut s, ildn);
}
if let Some(hash) = find(b"HASH") {
header::hash(&mut s, hash)?;
}
let (part, program) = [*b"ILDB", *b"DXIL"]
.into_iter()
.find_map(|kind| find(&kind).map(|data| (kind, data)))
.ok_or_else(|| error(0, "the container has no DXIL part"))?;
let program = crate::program(program)?;
let bitcode = program
.bitcode
.as_ptr()
.addr()
.checked_sub(container.as_ptr().addr())
.filter(|&b| b < container.len())
.ok_or_else(|| error(0, "the program's bitcode outside the container"))?;
if let Some(part) = find(b"PSV0") {
let kind = u8::try_from(program.program_version >> 16 & 0xFF).unwrap_or(u8::MAX);
header::psv_runtime(&mut s, &psv::read(part)?, kind)?;
}
let blocks = bitstream::read(program.bitcode, *b"BC\xc0\xde")?;
let module = Module::read(&blocks)?;
let reflection = match find(b"STAT").map(crate::program) {
Some(Ok(part)) => Some(read(part.bitcode)?),
_ => None,
};
if module.named_metadata.iter().any(|(n, _)| n == "dx.version") {
let kind = crate::reflection::shader_model(&module)?
.map(|(k, _, _)| k)
.ok_or_else(|| error(module.at, "the module has no dx.shaderModel metadata"))?;
if kind != "lib" {
header::dxil_signatures(&mut s, &module, &kind)?;
}
header::reflection_sections(&mut s, reflection.as_ref().unwrap_or(&module))?;
let subobjects = match find(b"RDAT") {
Some(part) => rdat::subobjects(part)?,
None => Vec::new(),
};
if subobjects.is_empty() && module.named_metadata.iter().any(|(n, _)| n == "dx.subobjects") {
return Err(error(
module.at,
"subobjects in dx.subobjects metadata are not supported",
));
}
header::subobjects(&mut s, &subobjects);
}
let marks = asm::write(&mut s, &module)?;
s.push('\n');
Ok(Listing {
text: s,
marks,
blocks,
part,
bitcode,
})
}
pub fn module_text(m: &Module) -> Result<String> {
let mut s = String::new();
asm::write(&mut s, m)?;
Ok(s)
}
fn read(bitcode: &[u8]) -> Result<Module> {
Module::read(&crate::bitstream::read(bitcode, *b"BC\xc0\xde")?)
}