use std::fmt;
use crate::{
addressmap::AddressMap,
error::Error,
util::{read_u16_le, read_u32_le},
vb::control::Guid,
};
#[derive(Clone, Copy, Debug)]
pub struct PublicObjectDescriptor<'a> {
bytes: &'a [u8],
}
impl<'a> PublicObjectDescriptor<'a> {
pub const SIZE: usize = 0x30;
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: "PublicObjectDescriptor",
});
}
let bytes = data.get(..Self::SIZE).ok_or(Error::TooShort {
expected: Self::SIZE,
actual: data.len(),
context: "PublicObjectDescriptor",
})?;
Ok(Self { bytes })
}
#[inline]
pub fn object_info_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x00)
}
#[inline]
pub fn reserved(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x04)
}
#[inline]
pub fn public_bytes_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x08)
}
#[inline]
pub fn static_bytes_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x0C)
}
#[inline]
pub fn module_public_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x10)
}
#[inline]
pub fn module_static_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x14)
}
#[inline]
pub fn object_name_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x18)
}
#[inline]
pub fn method_count(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x1C)
}
#[inline]
pub fn method_names_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x20)
}
#[inline]
pub fn static_vars_offset(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x24)
}
#[inline]
pub fn object_type_raw(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x28)
}
#[inline]
pub fn has_optional_info(&self) -> bool {
self.object_type_raw().unwrap_or(0) & 0x01 != 0
}
#[inline]
pub fn is_class(&self) -> bool {
let raw = self.object_type_raw().unwrap_or(0);
raw & 0x02 != 0 && raw & 0x80 == 0
}
#[inline]
pub fn is_form(&self) -> bool {
self.object_type_raw().unwrap_or(0) & 0x82 == 0x82
}
#[inline]
pub fn is_module(&self) -> bool {
self.object_type_raw().unwrap_or(0) & 0x82 == 0x00
}
#[inline]
pub fn null_2c(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x2C)
}
}
#[derive(Clone, Copy, Debug)]
pub struct ObjectInfo<'a> {
bytes: &'a [u8],
}
impl<'a> ObjectInfo<'a> {
pub const SIZE: usize = 0x38;
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: "ObjectInfo",
});
}
let bytes = data.get(..Self::SIZE).ok_or(Error::TooShort {
expected: Self::SIZE,
actual: data.len(),
context: "ObjectInfo",
})?;
Ok(Self { bytes })
}
#[inline]
pub fn ref_count(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x00)
}
#[inline]
pub fn object_index(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x02)
}
#[inline]
pub fn object_table_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x04)
}
#[inline]
pub fn ide_data(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x08)
}
#[inline]
pub fn private_object_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x0C)
}
#[inline]
pub fn public_object_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x18)
}
#[inline]
pub fn object_data_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x1C)
}
#[inline]
pub fn method_count(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x20)
}
#[inline]
pub fn method_count_ide(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x22)
}
#[inline]
pub fn methods_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x24)
}
#[inline]
pub fn constants_count(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x28)
}
#[inline]
pub fn max_constants(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x2A)
}
#[inline]
pub fn constants_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x34)
}
}
#[derive(Clone, Copy, Debug)]
pub struct OptionalObjectInfo<'a> {
bytes: &'a [u8],
}
impl<'a> OptionalObjectInfo<'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: "OptionalObjectInfo",
});
}
let bytes = data.get(..Self::SIZE).ok_or(Error::TooShort {
expected: Self::SIZE,
actual: data.len(),
context: "OptionalObjectInfo",
})?;
Ok(Self { bytes })
}
#[inline]
pub fn gui_guids_count(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x00)
}
#[inline]
pub fn object_clsid_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x04)
}
#[inline]
pub fn null_08(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x08)
}
#[inline]
pub fn gui_guid_table_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x0C)
}
#[inline]
pub fn default_iid_count(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x10)
}
#[inline]
pub fn events_iid_table_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x14)
}
#[inline]
pub fn events_iid_count(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x18)
}
#[inline]
pub fn default_iid_table_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x1C)
}
#[inline]
pub fn control_count(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x20)
}
#[inline]
pub fn controls_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x24)
}
#[inline]
pub fn method_link_count(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x28)
}
pub const PCODE_COUNT_NATIVE_SENTINEL: u16 = 0x1B7;
#[inline]
pub fn pcode_count_raw(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x2A)
}
#[inline]
pub fn pcode_count(&self) -> Result<u16, Error> {
let raw = self.pcode_count_raw()?;
Ok(if raw == Self::PCODE_COUNT_NATIVE_SENTINEL {
0
} else {
raw
})
}
#[inline]
pub fn is_native_sentinel(&self) -> Result<bool, Error> {
Ok(self.pcode_count_raw()? == Self::PCODE_COUNT_NATIVE_SENTINEL)
}
#[inline]
pub fn initialize_event_offset(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x2C)
}
#[inline]
pub fn terminate_event_offset(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x2E)
}
#[inline]
pub fn method_link_table_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x30)
}
#[inline]
pub fn basic_class_object_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x34)
}
#[inline]
pub fn null_38(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x38)
}
#[inline]
pub fn field_3c(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x3C)
}
pub fn resolve_clsid(&self, map: &AddressMap<'_>) -> Option<Guid> {
let va = self.object_clsid_va().ok()?;
if va == 0 {
return None;
}
let data = map.slice_from_va(va, 16).ok()?;
Guid::from_bytes(data)
}
pub fn gui_guids<'b>(&self, map: &'b AddressMap<'_>) -> GuidTableIter<'b> {
GuidTableIter::new(
map,
self.gui_guid_table_va().unwrap_or(0),
self.gui_guids_count().unwrap_or(0),
)
}
pub fn default_iids<'b>(&self, map: &'b AddressMap<'_>) -> GuidTableIter<'b> {
GuidTableIter::new(
map,
self.default_iid_table_va().unwrap_or(0),
self.default_iid_count().unwrap_or(0),
)
}
pub fn events_iids<'b>(&self, map: &'b AddressMap<'_>) -> GuidTableIter<'b> {
GuidTableIter::new(
map,
self.events_iid_table_va().unwrap_or(0),
self.events_iid_count().unwrap_or(0),
)
}
pub fn typed_iids(&self, map: &AddressMap<'_>) -> TypedIidIter {
let mut items = Vec::new();
items.extend(self.gui_guids(map).map(|(_, guid)| (IidKind::Gui, guid)));
items.extend(
self.default_iids(map)
.map(|(_, guid)| (IidKind::Default, guid)),
);
items.extend(
self.events_iids(map)
.map(|(_, guid)| (IidKind::Events, guid)),
);
TypedIidIter {
inner: items.into_iter(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IidKind {
Gui,
Default,
Events,
}
impl IidKind {
pub fn as_str(&self) -> &'static str {
match self {
Self::Gui => "gui",
Self::Default => "default",
Self::Events => "events",
}
}
}
impl fmt::Display for IidKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct TypedIidIter {
inner: std::vec::IntoIter<(IidKind, Guid)>,
}
impl Iterator for TypedIidIter {
type Item = (IidKind, Guid);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct GuidTableIter<'a> {
map: &'a AddressMap<'a>,
ptr_data: &'a [u8],
index: u32,
count: u32,
}
impl<'a> GuidTableIter<'a> {
pub fn new(map: &'a AddressMap<'a>, table_va: u32, count: u32) -> Self {
let ptr_data = if table_va != 0 && count > 0 {
let ptr_size = (count as usize).saturating_mul(4);
map.slice_from_va(table_va, ptr_size).unwrap_or(&[])
} else {
&[]
};
Self {
map,
ptr_data,
index: 0,
count,
}
}
}
impl<'a> Iterator for GuidTableIter<'a> {
type Item = (u32, Guid);
fn next(&mut self) -> Option<Self::Item> {
while self.index < self.count {
let i = self.index as usize;
self.index = self.index.saturating_add(1);
let offset = i.checked_mul(4)?;
let end = offset.checked_add(4)?;
let chunk = self.ptr_data.get(offset..end)?;
let guid_va = u32::from_le_bytes(<[u8; 4]>::try_from(chunk).ok()?);
if guid_va == 0 {
continue;
}
if let Ok(guid_data) = self.map.slice_from_va(guid_va, 16)
&& let Some(guid) = Guid::from_bytes(guid_data)
{
return Some((guid_va, guid));
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.count.saturating_sub(self.index) as usize;
(0, Some(remaining))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_public_object_descriptor_parse() {
let mut data = vec![0u8; PublicObjectDescriptor::SIZE];
data[0x1C..0x20].copy_from_slice(&10u32.to_le_bytes()); data[0x28..0x2C].copy_from_slice(&0x00118003u32.to_le_bytes());
let desc = PublicObjectDescriptor::parse(&data).unwrap();
assert_eq!(desc.method_count().unwrap(), 10);
assert!(desc.has_optional_info());
assert!(desc.is_class());
assert!(!desc.is_form());
assert!(!desc.is_module());
}
#[test]
fn test_object_type_detection() {
let mut data = vec![0u8; PublicObjectDescriptor::SIZE];
data[0x28..0x2C].copy_from_slice(&0x00018001u32.to_le_bytes());
let desc = PublicObjectDescriptor::parse(&data).unwrap();
assert!(desc.is_module());
assert!(!desc.is_class());
assert!(!desc.is_form());
data[0x28..0x2C].copy_from_slice(&0x00018083u32.to_le_bytes());
let desc = PublicObjectDescriptor::parse(&data).unwrap();
assert!(desc.is_form());
assert!(!desc.is_class());
assert!(!desc.is_module());
data[0x28..0x2C].copy_from_slice(&0x00118003u32.to_le_bytes());
let desc = PublicObjectDescriptor::parse(&data).unwrap();
assert!(desc.is_class());
assert!(!desc.is_form());
assert!(!desc.is_module());
}
#[test]
fn test_public_object_descriptor_too_short() {
let data = vec![0u8; PublicObjectDescriptor::SIZE - 1];
assert!(matches!(
PublicObjectDescriptor::parse(&data),
Err(Error::TooShort { .. })
));
}
#[test]
fn test_object_info_parse() {
let mut data = vec![0u8; ObjectInfo::SIZE];
data[0x20..0x22].copy_from_slice(&5u16.to_le_bytes()); data[0x24..0x28].copy_from_slice(&0x00404000u32.to_le_bytes()); let info = ObjectInfo::parse(&data).unwrap();
assert_eq!(info.method_count().unwrap(), 5);
assert_eq!(info.methods_va().unwrap(), 0x00404000);
}
#[test]
fn test_object_info_too_short() {
let data = vec![0u8; ObjectInfo::SIZE - 1];
assert!(matches!(
ObjectInfo::parse(&data),
Err(Error::TooShort { .. })
));
}
#[test]
fn test_optional_object_info_parse() {
let mut data = vec![0u8; OptionalObjectInfo::SIZE];
data[0x2A..0x2C].copy_from_slice(&3u16.to_le_bytes()); data[0x20..0x24].copy_from_slice(&7u32.to_le_bytes()); let opt = OptionalObjectInfo::parse(&data).unwrap();
assert_eq!(opt.pcode_count_raw().unwrap(), 3);
assert_eq!(opt.pcode_count().unwrap(), 3);
assert!(!opt.is_native_sentinel().unwrap());
assert_eq!(opt.control_count().unwrap(), 7);
}
#[test]
fn test_optional_object_info_native_sentinel() {
let mut data = vec![0u8; OptionalObjectInfo::SIZE];
data[0x2A..0x2C]
.copy_from_slice(&OptionalObjectInfo::PCODE_COUNT_NATIVE_SENTINEL.to_le_bytes());
let opt = OptionalObjectInfo::parse(&data).unwrap();
assert_eq!(
opt.pcode_count_raw().unwrap(),
OptionalObjectInfo::PCODE_COUNT_NATIVE_SENTINEL
);
assert_eq!(opt.pcode_count().unwrap(), 0);
assert!(opt.is_native_sentinel().unwrap());
}
#[test]
fn test_optional_object_info_too_short() {
let data = vec![0u8; OptionalObjectInfo::SIZE - 1];
assert!(matches!(
OptionalObjectInfo::parse(&data),
Err(Error::TooShort { .. })
));
}
}