use std::fmt;
use crate::{
error::Error,
util::{read_u16_le, read_u32_le},
};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Guid {
pub bytes: [u8; 16],
}
impl Guid {
pub fn from_bytes(data: &[u8]) -> Option<Self> {
let slice = data.get(..16)?;
let mut bytes = [0u8; 16];
bytes.copy_from_slice(slice);
Some(Self { bytes })
}
pub fn control_class_name(&self) -> Option<&'static str> {
generated::lookup_control_name(&self.bytes)
}
}
impl fmt::Debug for Guid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
impl fmt::Display for Guid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let b = &self.bytes;
let &[
b0,
b1,
b2,
b3,
b4,
b5,
b6,
b7,
b8,
b9,
b10,
b11,
b12,
b13,
b14,
b15,
] = b;
let d1 = u32::from_le_bytes([b0, b1, b2, b3]);
let d2 = u16::from_le_bytes([b4, b5]);
let d3 = u16::from_le_bytes([b6, b7]);
write!(
f,
"{{{d1:08X}-{d2:04X}-{d3:04X}-{b8:02X}{b9:02X}-{b10:02X}{b11:02X}{b12:02X}{b13:02X}{b14:02X}{b15:02X}}}"
)
}
}
pub(crate) mod generated {
include!(concat!(env!("OUT_DIR"), "/vb6_data_generated.rs"));
}
#[derive(Clone, Copy, Debug)]
pub struct ControlInfo<'a> {
bytes: &'a [u8],
}
impl<'a> ControlInfo<'a> {
pub const MIN_SIZE: usize = 0x28;
pub fn parse(data: &'a [u8]) -> Result<Self, Error> {
let bytes = data.get(..Self::MIN_SIZE).ok_or(Error::TooShort {
expected: Self::MIN_SIZE,
actual: data.len(),
context: "ControlInfo",
})?;
Ok(Self { bytes })
}
#[inline]
pub fn flags(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x00)
}
#[inline]
pub fn event_handler_slots(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x02)
}
#[inline]
pub fn control_type(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x00)
}
#[inline]
pub fn dispatch_offset(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x04)
}
#[inline]
pub fn guid_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x08)
}
#[inline]
pub fn index(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x0C)
}
#[inline]
pub fn member_type(&self) -> Result<u16, Error> {
read_u16_le(self.bytes, 0x0E)
}
#[inline]
pub fn dispid_count_or_zero(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x10)
}
#[inline]
pub fn dispid_table_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x14)
}
#[inline]
pub fn event_sink_vtable_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x18)
}
#[inline]
pub fn name_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x20)
}
#[inline]
pub fn linker_type_data_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x1C)
}
#[inline]
pub fn control_id(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x24)
}
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct ControlIterator<'a> {
data: &'a [u8],
offset: usize,
remaining: u32,
}
impl<'a> ControlIterator<'a> {
pub fn new(data: &'a [u8], count: u32) -> Self {
Self {
data,
offset: 0,
remaining: count,
}
}
}
impl<'a> Iterator for ControlIterator<'a> {
type Item = Result<ControlInfo<'a>, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
self.remaining = self.remaining.checked_sub(1)?;
let entry_end = match self.offset.checked_add(ControlInfo::MIN_SIZE) {
Some(e) => e,
None => {
return Some(Err(Error::ArithmeticOverflow {
context: "ControlIterator offset+MIN_SIZE",
}));
}
};
if entry_end > self.data.len() {
return Some(Err(Error::TooShort {
expected: ControlInfo::MIN_SIZE,
actual: self.data.len().saturating_sub(self.offset),
context: "ControlInfo",
}));
}
let slice = match self.data.get(self.offset..) {
Some(s) => s,
None => {
return Some(Err(Error::Truncated {
needed: ControlInfo::MIN_SIZE,
available: self.data.len().saturating_sub(self.offset),
}));
}
};
match ControlInfo::parse(slice) {
Ok(ctrl) => {
self.offset = entry_end;
Some(Ok(ctrl))
}
Err(e) => Some(Err(e)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_control_info() -> Vec<u8> {
let mut buf = vec![0u8; ControlInfo::MIN_SIZE];
buf[0x00..0x02].copy_from_slice(&0x0040u16.to_le_bytes()); buf[0x02..0x04].copy_from_slice(&0x0014u16.to_le_bytes()); buf[0x04..0x06].copy_from_slice(&52u16.to_le_bytes()); buf[0x08..0x0C].copy_from_slice(&0x00405000u32.to_le_bytes()); buf[0x0C..0x0E].copy_from_slice(&1u16.to_le_bytes()); buf[0x0E..0x10].copy_from_slice(&3u16.to_le_bytes()); buf[0x10..0x14].copy_from_slice(&0x00406000u32.to_le_bytes()); buf[0x20..0x24].copy_from_slice(&0x00407000u32.to_le_bytes()); buf
}
#[test]
fn test_parse_valid() {
let data = make_control_info();
let ctrl = ControlInfo::parse(&data).unwrap();
assert_eq!(ctrl.flags().unwrap(), 0x0040);
assert_eq!(ctrl.event_handler_slots().unwrap(), 0x0014);
assert_eq!(ctrl.control_type().unwrap(), 0x00140040);
assert_eq!(ctrl.dispatch_offset().unwrap(), 52);
assert_eq!(ctrl.guid_va().unwrap(), 0x00405000);
assert_eq!(ctrl.index().unwrap(), 1);
assert_eq!(ctrl.member_type().unwrap(), 3);
assert_eq!(ctrl.dispid_count_or_zero().unwrap(), 0x00406000);
assert_eq!(ctrl.name_va().unwrap(), 0x00407000);
}
#[test]
fn test_parse_too_short() {
let data = vec![0u8; ControlInfo::MIN_SIZE - 1];
assert!(matches!(
ControlInfo::parse(&data),
Err(Error::TooShort { .. })
));
}
#[test]
fn test_control_iterator() {
let mut data = make_control_info();
let mut ctrl2 = make_control_info();
ctrl2[0x0C..0x0E].copy_from_slice(&2u16.to_le_bytes()); data.extend_from_slice(&ctrl2);
let iter = ControlIterator::new(&data, 2);
let results: Vec<_> = iter.collect();
assert_eq!(results.len(), 2);
assert_eq!(results[0].as_ref().unwrap().index().unwrap(), 1);
assert_eq!(results[1].as_ref().unwrap().index().unwrap(), 2);
}
#[test]
fn test_control_iterator_empty() {
let data = vec![0u8; 0];
let iter = ControlIterator::new(&data, 0);
let results: Vec<_> = iter.collect();
assert!(results.is_empty());
}
#[test]
fn test_control_iterator_truncated() {
let data = make_control_info();
let iter = ControlIterator::new(&data, 2);
let results: Vec<_> = iter.collect();
assert_eq!(results.len(), 2);
assert!(results[0].is_ok());
assert!(results[1].is_err());
}
}