use crate::ole::binary::{read_u16_le, read_i16_le, read_u32_le};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SprmOperation {
Toggle,
Byte,
Word,
DWord,
Word2,
Word3,
Variable,
ThreeByte,
}
impl From<u8> for SprmOperation {
fn from(size_code: u8) -> Self {
match size_code {
0 => SprmOperation::Toggle,
1 => SprmOperation::Byte,
2 => SprmOperation::Word,
3 => SprmOperation::DWord,
4 => SprmOperation::Word2,
5 => SprmOperation::Word3,
6 => SprmOperation::Variable,
7 => SprmOperation::ThreeByte,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone)]
pub struct Sprm {
pub opcode: u16,
pub operation: SprmOperation,
pub operand: Vec<u8>,
}
impl Sprm {
#[inline]
pub fn operand_byte(&self) -> Option<u8> {
self.operand.first().copied()
}
#[inline]
pub fn operand_word(&self) -> Option<u16> {
read_u16_le(&self.operand, 0).ok()
}
#[inline]
pub fn operand_i16(&self) -> Option<i16> {
read_i16_le(&self.operand, 0).ok()
}
#[inline]
pub fn operand_dword(&self) -> Option<u32> {
read_u32_le(&self.operand, 0).ok()
}
#[inline]
pub fn operand_bytes(&self) -> &[u8] {
&self.operand
}
}
pub fn parse_sprms(grpprl: &[u8]) -> Vec<Sprm> {
parse_sprms_two_byte(grpprl)
}
fn parse_sprms_two_byte(grpprl: &[u8]) -> Vec<Sprm> {
let mut sprms = Vec::new();
let mut offset = 0;
while offset + 2 <= grpprl.len() {
let opcode = read_u16_le(grpprl, offset).unwrap_or(0);
offset += 2;
let size_code = ((opcode & 0xe000) >> 13) as u8;
let operation = SprmOperation::from(size_code);
let operand_size = match size_code {
0 | 1 => 1, 2 | 4 | 5 => 2, 3 => 4, 6 => {
if offset + 1 < grpprl.len() {
if opcode == 0xc615 || opcode == 0xd608 {
if offset + 3 <= grpprl.len() {
read_u16_le(grpprl, offset).unwrap_or(0) as usize
} else {
break;
}
} else {
grpprl[offset] as usize
}
} else {
break;
}
}
7 => 3, _ => unreachable!(),
};
if offset + operand_size > grpprl.len() {
break;
}
let operand = grpprl[offset..offset + operand_size].to_vec();
offset += operand_size;
sprms.push(Sprm {
opcode,
operation,
operand,
});
}
sprms
}
#[inline]
pub fn find_sprm(sprms: &[Sprm], opcode: u16) -> Option<&Sprm> {
sprms.iter().find(|sprm| sprm.opcode == opcode)
}
#[inline]
pub fn get_bool_from_sprm(sprm: &Sprm) -> bool {
sprm.operand_byte().unwrap_or(0) != 0
}
#[inline]
pub fn get_int_from_sprm(sprm: &Sprm) -> Option<i32> {
match sprm.operation {
SprmOperation::Byte | SprmOperation::Toggle => sprm.operand_byte().map(|b| b as i32),
SprmOperation::Word | SprmOperation::Word2 | SprmOperation::Word3 => {
sprm.operand_i16().map(|w| w as i32)
}
SprmOperation::DWord => sprm.operand_dword().map(|d| d as i32),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sprm_operation_from() {
assert_eq!(SprmOperation::from(0), SprmOperation::Toggle);
assert_eq!(SprmOperation::from(1), SprmOperation::Byte);
assert_eq!(SprmOperation::from(2), SprmOperation::Word);
assert_eq!(SprmOperation::from(4), SprmOperation::Word2);
assert_eq!(SprmOperation::from(5), SprmOperation::Word3);
}
#[test]
fn test_parse_sprms() {
let grpprl = vec![
0x35, 0x08, 0x01, 0x43, 0x4A, 0x18, 0x00, ];
let sprms = parse_sprms(&grpprl);
assert_eq!(sprms.len(), 2);
assert_eq!(sprms[0].opcode, 0x0835); assert_eq!(sprms[1].opcode, 0x4A43); }
#[test]
fn test_find_sprm() {
let sprms = vec![
Sprm {
opcode: 0x0835,
operation: SprmOperation::Byte,
operand: vec![1],
},
Sprm {
opcode: 0x4A43,
operation: SprmOperation::Word,
operand: vec![24, 0],
},
];
assert!(find_sprm(&sprms, 0x0835).is_some());
assert!(find_sprm(&sprms, 0x4A43).is_some());
assert!(find_sprm(&sprms, 0xFFFF).is_none());
}
#[test]
fn test_get_bool_from_sprm() {
let sprm = Sprm {
opcode: 0x0835,
operation: SprmOperation::Byte,
operand: vec![1],
};
assert!(get_bool_from_sprm(&sprm));
let sprm_false = Sprm {
opcode: 0x0835,
operation: SprmOperation::Byte,
operand: vec![0],
};
assert!(!get_bool_from_sprm(&sprm_false));
}
}