use std::collections::BTreeMap;
use super::msf::Stream;
use crate::Result;
const LANES: [char; 4] = ['x', 'y', 'z', 'w'];
const MODIFIER: u16 = 0x1518;
const ALIAS: u16 = 0x150a;
const CLASS: u16 = 0x1504;
const STRUCTURE: u16 = 0x1505;
const FIELD_LIST: u16 = 0x1203;
const MEMBER: u16 = 0x150d;
const BASE_CLASS: u16 = 0x1400;
const BASE_INTERFACE: u16 = 0x151a;
const STATIC_MEMBER: u16 = 0x150e;
const METHOD: u16 = 0x150f;
const NESTED_TYPE: u16 = 0x1510;
const ONE_METHOD: u16 = 0x1511;
const STRIDED_ARRAY: u16 = 0x1516;
const HLSL: u16 = 0x1517;
const VECTOR: u16 = 0x151b;
const MATRIX: u16 = 0x151c;
pub(crate) struct Types<'a> {
stream: &'a Stream,
records: BTreeMap<u32, (u16, usize, usize)>,
}
impl<'a> Types<'a> {
pub(crate) fn read(stream: &'a Stream) -> Result<Self> {
let header = stream.size(4)?;
let mut index = stream.u32(8)?;
let end = stream.plus(header, stream.size(16)?)?;
let mut at = header;
let mut records = BTreeMap::new();
while at < end {
let len = usize::from(stream.u16(at)?);
let kind = stream.u16(stream.plus(at, 2)?)?;
let next = stream.plus(at, len.saturating_add(2))?;
if len < 2 || next > end {
return Err(stream.fail(at, format!("a type record of {len} bytes past the records' end")));
}
records.insert(index, (kind, at.saturating_add(4), next));
index = index.saturating_add(1);
at = next;
}
Ok(Self { stream, records })
}
fn numeric(&self, at: usize) -> Result<(u32, usize)> {
let s = self.stream;
match s.u16(at)? {
n if n < 0x8000 => Ok((u32::from(n), s.plus(at, 2)?)),
0x8002 => Ok((u32::from(s.u16(s.plus(at, 2)?)?), s.plus(at, 4)?)),
0x8004 => Ok((s.u32(s.plus(at, 2)?)?, s.plus(at, 6)?)),
leaf => Err(s.fail(
at,
format!("numeric leaf {leaf:#06x}, where 0x8002 and 0x8004 are the ones known"),
)),
}
}
pub(crate) fn path(&self, index: u32, offset: u32) -> Result<String> {
let mut out = String::new();
let (mut index, mut offset) = (index, offset);
for _ in 0..64 {
if index < 0x1000 {
return Ok(out);
}
let s = self.stream;
let &(kind, at, _) = self
.records
.get(&index)
.ok_or_else(|| s.fail(0, format!("type index {index:#x} has no record")))?;
match kind {
MODIFIER | ALIAS => index = s.u32(at)?,
VECTOR => {
let count = s.u32(s.plus(at, 4)?)?;
let (size, _) = self.numeric(s.plus(at, 8)?)?;
let component = size.checked_div(count).unwrap_or(0);
if count > 1 && component > 0 {
let lane =
usize::try_from(offset.checked_div(component).unwrap_or(0)).unwrap_or(usize::MAX);
out.push('.');
out.push(
*LANES
.get(lane)
.ok_or_else(|| s.fail(at, format!("offset {offset} past a vector")))?,
);
}
return Ok(out);
}
STRUCTURE | CLASS => {
let list = s.u32(s.plus(at, 4)?)?;
let list = match s.u16(s.plus(at, 2)?)? & 0x80 != 0 {
true => self.definition(index, kind, at)?,
false => list,
};
let (member, ty, start) = self.member(list, offset)?;
if let Some(member) = member {
out.push('.');
out.push_str(&member);
}
index = ty;
offset = offset.saturating_sub(start);
}
MATRIX => {
let stride = s.u32(s.plus(at, 12)?)?;
let row_major = s.u8(s.plus(at, 16)?)? & 1 != 0;
let major = offset.checked_div(stride).unwrap_or(0);
let minor = offset.checked_rem(stride).unwrap_or(0) / 4;
let (row, column) = if row_major { (major, minor) } else { (minor, major) };
out.push_str(&format!("._m{row}{column}"));
return Ok(out);
}
HLSL => {
if s.u16(s.plus(at, 4)?)? == 0x200 {
let lane = usize::try_from(offset / 4).unwrap_or(usize::MAX);
out.push('.');
out.push(*LANES.get(lane).ok_or_else(|| {
s.fail(at, format!("offset {offset} past an interface pointer"))
})?);
}
return Ok(out);
}
STRIDED_ARRAY => {
let element = s.u32(at)?;
let stride = s.u32(s.plus(at, 8)?)?;
out.push_str(&format!("[{}]", offset.checked_div(stride).unwrap_or(0)));
offset = offset.checked_rem(stride).unwrap_or(0);
index = element;
}
_ => {
return Err(s.fail(
at,
format!("type {index:#x} of kind {kind:#06x}, which the listing does not name"),
));
}
}
}
Err(self
.stream
.fail(0, format!("type {index:#x} nests more than 64 deep")))
}
fn definition(&self, index: u32, kind: u16, at: usize) -> Result<u32> {
let s = self.stream;
let name = |at: usize| -> Result<String> {
let (_, name_at) = self.numeric(s.plus(at, 16)?)?;
Ok(s.string(name_at)?.0)
};
let wanted = name(at)?;
for &(other, data, _) in self.records.values() {
if other == kind && s.u16(s.plus(data, 2)?)? & 0x80 == 0 && name(data)? == wanted {
return s.u32(s.plus(data, 4)?);
}
}
Err(s.fail(
at,
format!("type {index:#x} is a forward reference to {wanted}, which no record defines"),
))
}
fn member(&self, list: u32, offset: u32) -> Result<(Option<String>, u32, u32)> {
let s = self.stream;
let &(kind, mut at, end) = self
.records
.get(&list)
.ok_or_else(|| s.fail(0, format!("field list {list:#x} has no record")))?;
if kind != FIELD_LIST {
return Err(s.fail(
at,
format!("type {list:#x} is of kind {kind:#06x}, where a field list is 0x1203"),
));
}
let mut best = None;
while at < end {
let leaf = s.u16(at)?;
let ty = s.u32(s.plus(at, 4)?)?;
let mut next = match leaf {
MEMBER | BASE_CLASS | BASE_INTERFACE => {
let (start, after) = self.numeric(s.plus(at, 8)?)?;
let (name, after) = match leaf {
MEMBER => {
let (name, after) = s.string(after)?;
(Some(name), after)
}
_ => (None, after),
};
if leaf != BASE_INTERFACE && start <= offset {
best = Some((name, ty, start));
}
after
}
STATIC_MEMBER | METHOD | NESTED_TYPE => s.string(s.plus(at, 8)?)?.1,
ONE_METHOD => {
let virtual_offset = matches!((s.u16(s.plus(at, 2)?)? >> 2) & 7, 4 | 6);
s.string(s.plus(at, if virtual_offset { 12 } else { 8 })?)?.1
}
_ => {
return Err(s.fail(
at,
format!(
"field list member of kind {leaf:#06x}, where 0x1400, 0x150d to 0x1511 and 0x151a are the \
ones known"
),
));
}
};
while next < end && s.u8(next)? >= 0xf0 {
next = next.saturating_add(1);
}
at = next;
}
best.ok_or_else(|| {
s.fail(
at,
format!("field list {list:#x} has no member at offset {offset}"),
)
})
}
}