use crate::reflection::View;
use crate::{Result, error, parts};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClassType {
pub name: String,
pub stride: u16,
pub textures: u16,
pub samplers: u16,
pub reserved: u16,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClassInstance {
pub name: String,
pub ty: u16,
pub count: u16,
pub buffer: Option<u16>,
pub offset: Option<u16>,
pub texture: Option<u16>,
pub sampler: Option<u16>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InterfaceSlots {
pub span: u32,
pub types: Vec<u16>,
pub tables: Vec<u32>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Interfaces {
pub slot_count: u32,
pub types: Vec<ClassType>,
pub instances: Vec<ClassInstance>,
pub slots: Vec<InterfaceSlots>,
pub reserved: [u32; 2],
}
fn register(value: u16) -> Option<u16> {
(value != 0xffff).then_some(value)
}
pub fn interfaces(bytes: &[u8]) -> Result<Option<Interfaces>> {
let directory = parts(bytes)?;
let Some(chunk) = directory.iter().find(|c| &c.kind == b"IFCE") else {
return Ok(None);
};
let v = View(chunk.data);
let word = |n: usize| v.u32(n.saturating_mul(4));
let count = |n: usize| -> Result<usize> {
usize::try_from(word(n)?)
.map_err(|_| error(n, format!("IFCE word {n}: the count exceeds the address space")))
};
let offset = |n: usize| -> Result<usize> {
usize::try_from(word(n)?)
.map_err(|_| error(n, format!("IFCE word {n}: the offset exceeds the address space")))
};
let (instance_count, type_count, record_count) = (count(0)?, count(1)?, count(2)?);
let (instance_at, type_at, record_at) = (offset(4)?, offset(5)?, offset(6)?);
let mut types = Vec::new();
for i in v.table(type_at, type_count, 12)? {
let p = v.record(type_at, i, 12)?;
types.push(ClassType {
name: v.string(v.u32(p)? as usize)?,
reserved: v.field16(p, 4)?,
stride: v.field16(p, 6)?,
textures: v.field16(p, 8)?,
samplers: v.field16(p, 10)?,
});
}
let mut instances = Vec::new();
for i in v.table(instance_at, instance_count, 16)? {
let p = v.record(instance_at, i, 16)?;
instances.push(ClassInstance {
name: v.string(v.u32(p)? as usize)?,
ty: v.field16(p, 4)?,
count: v.field16(p, 6)?,
buffer: register(v.field16(p, 8)?),
offset: register(v.field16(p, 10)?),
texture: register(v.field16(p, 12)?),
sampler: register(v.field16(p, 14)?),
});
}
let mut slots = Vec::new();
for i in v.table(record_at, record_count, 16)? {
let p = v.record(record_at, i, 16)?;
let entries = v.field(p, 4)? as usize;
let (type_ids, table_ids) = (v.field(p, 8)? as usize, v.field(p, 12)? as usize);
let mut record = InterfaceSlots {
span: v.u32(p)?,
types: Vec::new(),
tables: Vec::new(),
};
for j in v.table(type_ids, entries, 2)? {
record.types.push(v.u16(v.record(type_ids, j, 2)?)?);
}
for j in v.table(table_ids, entries, 4)? {
record.tables.push(v.u32(v.record(table_ids, j, 4)?)?);
}
slots.push(record);
}
Ok(Some(Interfaces {
slot_count: word(3)?,
types,
instances,
slots,
reserved: [word(7)?, word(8)?],
}))
}