use crate::{
error::Error,
util::{read_u16_le, read_u32_le},
};
#[derive(Clone, Copy, Debug)]
pub struct PrivateObjectDescriptor<'a> {
bytes: &'a [u8],
}
impl<'a> PrivateObjectDescriptor<'a> {
pub const SIZE: usize = 0x40;
pub fn parse(data: &'a [u8]) -> Result<Self, Error> {
if data.len() < Self::SIZE {
return Err(Error::TooShort {
expected: Self::SIZE,
actual: data.len(),
context: "PrivateObjectDescriptor",
});
}
let bytes = data.get(..Self::SIZE).ok_or(Error::TooShort {
expected: Self::SIZE,
actual: data.len(),
context: "PrivateObjectDescriptor",
})?;
Ok(Self { bytes })
}
#[inline]
pub fn object_info_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x04)
}
#[inline]
pub fn func_count(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x10)
}
#[inline]
pub fn func_count2(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x12)
}
#[inline]
pub fn var_count(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x14)
}
#[inline]
pub fn func_type_descs_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x18)
}
#[inline]
pub fn method_name_table_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x20)
}
#[inline]
pub fn param_names_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x24)
}
#[inline]
pub fn var_stubs_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x28)
}
#[inline]
pub fn desc_size(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x38)
}
#[inline]
pub fn flags(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x3C)
}
#[inline]
pub fn is_class(&self) -> bool {
self.flags().is_ok_and(|f| f & 0x0100 != 0)
}
}
#[cfg(test)]
mod tests {
use super::*;
const CLS_ZIP: [u8; 0x40] = [
0x00, 0x00, 0x00, 0x00, 0x1C, 0x28, 0x40, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x0E, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xA8, 0x5C, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x5B, 0x40, 0x00, 0x44, 0x55, 0x40, 0x00, 0x44, 0x56, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00,
0x04, 0x01, 0x00, 0x00,
];
const FORM1: [u8; 0x40] = [
0x00, 0x00, 0x00, 0x00, 0x3C, 0x24, 0x40, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x55, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x44, 0x55, 0x40, 0x00, 0x44, 0x55, 0x40, 0x00, 0x44, 0x55, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00,
];
#[test]
fn test_parse_cls_zip() {
let pod = PrivateObjectDescriptor::parse(&CLS_ZIP).unwrap();
assert_eq!(pod.object_info_va().unwrap(), 0x0040281C);
assert_eq!(pod.func_count().unwrap(), 14);
assert_eq!(pod.var_count().unwrap(), 5);
assert_eq!(pod.func_type_descs_va().unwrap(), 0x00405CA8);
assert_eq!(pod.param_names_va().unwrap(), 0x00405544);
assert_eq!(pod.var_stubs_va().unwrap(), 0x00405644);
assert_eq!(pod.desc_size().unwrap(), 0x4C);
assert_eq!(pod.flags().unwrap(), 0x0104);
assert!(pod.is_class());
}
#[test]
fn test_parse_form1() {
let pod = PrivateObjectDescriptor::parse(&FORM1).unwrap();
assert_eq!(pod.object_info_va().unwrap(), 0x0040243C);
assert_eq!(pod.func_count().unwrap(), 0);
assert_eq!(pod.var_count().unwrap(), 0);
assert_eq!(pod.func_type_descs_va().unwrap(), 0x0040558C);
assert_eq!(pod.desc_size().unwrap(), 0x44);
assert_eq!(pod.flags().unwrap(), 0x0004);
assert!(!pod.is_class());
}
const COOLBAR_OCX: [u8; 0x40] = [
0x00, 0x00, 0x00, 0x00, 0x84, 0x71, 0x08, 0x28, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x1E, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x25, 0x09, 0x28, 0x00, 0x00,
0x00, 0x00, 0x3C, 0x22, 0x09, 0x28, 0xCC, 0x1D, 0x09, 0x28, 0x50, 0x05, 0x09, 0x28, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00,
0x04, 0x01, 0x00, 0x00,
];
#[test]
fn test_parse_coolbar_ocx() {
let pod = PrivateObjectDescriptor::parse(&COOLBAR_OCX).unwrap();
assert_eq!(pod.func_count().unwrap(), 30);
assert_eq!(pod.func_count2().unwrap(), 13);
assert_eq!(pod.var_count().unwrap(), 0);
assert_eq!(pod.flags().unwrap(), 0x0104);
assert!(pod.is_class());
}
#[test]
fn test_parse_too_short() {
let short = [0u8; 0x3F];
assert!(PrivateObjectDescriptor::parse(&short).is_err());
}
}