use crate::{
error::Error,
util::{read_fixed, read_u16_le, read_u32_le},
};
#[derive(Clone, Copy, Debug)]
pub struct ObjectTable<'a> {
bytes: &'a [u8],
}
impl<'a> ObjectTable<'a> {
pub const SIZE: usize = 0x54;
pub fn parse(data: &'a [u8]) -> Result<Self, Error> {
let bytes = data.get(..Self::SIZE).ok_or(Error::TooShort {
expected: Self::SIZE,
actual: data.len(),
context: "ObjectTable",
})?;
Ok(Self { bytes })
}
#[inline]
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
#[inline]
pub fn heap_link(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x00)
}
#[inline]
pub fn exec_proj_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x04)
}
#[inline]
pub fn project_info2_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x08)
}
#[inline]
pub fn reserved(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x0C)
}
#[inline]
pub fn project_object_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x14)
}
#[inline]
pub fn uuid(&self) -> Result<&'a [u8], Error> {
read_fixed(self.bytes, 0x18, 16)
}
#[inline]
pub fn compile_state(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x28)
}
#[inline]
pub fn total_objects(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x2A)
}
#[inline]
pub fn compiled_objects(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x2C)
}
#[inline]
pub fn objects_in_use(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x2E)
}
#[inline]
pub fn object_array_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x30)
}
#[inline]
pub fn ide_flag(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x34)
}
#[inline]
pub fn project_name_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x40)
}
#[inline]
pub fn lcid(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x44)
}
#[inline]
pub fn lcid2(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x48)
}
#[inline]
pub fn identifier(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x50)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_object_table() -> Vec<u8> {
let mut buf = vec![0u8; ObjectTable::SIZE];
buf[0x0C..0x10].copy_from_slice(&0xFFFFFFFFu32.to_le_bytes());
buf[0x2A..0x2C].copy_from_slice(&4u16.to_le_bytes());
buf[0x30..0x34].copy_from_slice(&0x00403000u32.to_le_bytes());
buf[0x44..0x48].copy_from_slice(&0x0409u32.to_le_bytes());
buf
}
#[test]
fn test_parse_valid() {
let data = make_object_table();
let ot = ObjectTable::parse(&data).unwrap();
assert_eq!(ot.reserved().unwrap(), 0xFFFFFFFF);
assert_eq!(ot.total_objects().unwrap(), 4);
assert_eq!(ot.object_array_va().unwrap(), 0x00403000);
assert_eq!(ot.lcid().unwrap(), 0x0409);
}
#[test]
fn test_parse_too_short() {
let data = vec![0u8; ObjectTable::SIZE - 1];
assert!(matches!(
ObjectTable::parse(&data),
Err(Error::TooShort { .. })
));
}
#[test]
fn test_all_fields() {
let data = make_object_table();
let ot = ObjectTable::parse(&data).unwrap();
let _ = ot.heap_link().unwrap();
let _ = ot.exec_proj_va().unwrap();
let _ = ot.project_info2_va().unwrap();
let _ = ot.project_object_va().unwrap();
let _ = ot.uuid().unwrap();
let _ = ot.compile_state().unwrap();
let _ = ot.compiled_objects().unwrap();
let _ = ot.objects_in_use().unwrap();
let _ = ot.ide_flag().unwrap();
let _ = ot.project_name_va().unwrap();
let _ = ot.lcid2().unwrap();
let _ = ot.identifier().unwrap();
let _ = ot.as_bytes();
}
}