use crate::{
error::Error,
util::{read_fixed, read_u32_le},
};
#[derive(Clone, Copy, Debug)]
pub struct ProjectData<'a> {
bytes: &'a [u8],
}
impl<'a> ProjectData<'a> {
pub const SIZE: usize = 0x23C;
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: "ProjectData",
})?;
Ok(Self { bytes })
}
#[inline]
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
#[inline]
pub fn version(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x00)
}
#[inline]
pub fn object_table_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 code_start_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x0C)
}
#[inline]
pub fn code_end_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x10)
}
#[inline]
pub fn data_size(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x14)
}
#[inline]
pub fn thread_space_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x18)
}
#[inline]
pub fn vba_seh_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x1C)
}
#[inline]
pub fn native_code_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x20)
}
#[inline]
pub fn is_pcode(&self) -> Result<bool, Error> {
Ok(self.native_code_va()? == 0)
}
#[inline]
pub fn path_info(&self) -> Result<&'a [u8], Error> {
read_fixed(self.bytes, 0x24, 528)
}
#[inline]
pub fn external_table_va(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x234)
}
#[inline]
pub fn external_count(&self) -> Result<u32, Error> {
read_u32_le(self.bytes, 0x238)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_project_data() -> Vec<u8> {
let mut buf = vec![0u8; ProjectData::SIZE];
buf[0x00..0x04].copy_from_slice(&0x1F4u32.to_le_bytes());
buf[0x04..0x08].copy_from_slice(&0x00402000u32.to_le_bytes());
buf[0x20..0x24].copy_from_slice(&0u32.to_le_bytes());
buf[0x238..0x23C].copy_from_slice(&5u32.to_le_bytes());
buf
}
#[test]
fn test_parse_valid() {
let data = make_project_data();
let pd = ProjectData::parse(&data).unwrap();
assert_eq!(pd.version().unwrap(), 0x1F4);
assert_eq!(pd.object_table_va().unwrap(), 0x00402000);
assert!(pd.is_pcode().unwrap());
assert_eq!(pd.native_code_va().unwrap(), 0);
assert_eq!(pd.external_count().unwrap(), 5);
}
#[test]
fn test_native_code() {
let mut data = make_project_data();
data[0x20..0x24].copy_from_slice(&0x00401000u32.to_le_bytes());
let pd = ProjectData::parse(&data).unwrap();
assert!(!pd.is_pcode().unwrap());
assert_eq!(pd.native_code_va().unwrap(), 0x00401000);
}
#[test]
fn test_parse_too_short() {
let data = vec![0u8; ProjectData::SIZE - 1];
assert!(matches!(
ProjectData::parse(&data),
Err(Error::TooShort { .. })
));
}
#[test]
fn test_all_fields_accessible() {
let data = make_project_data();
let pd = ProjectData::parse(&data).unwrap();
let _ = pd.null_08().unwrap();
let _ = pd.code_start_va().unwrap();
let _ = pd.code_end_va().unwrap();
let _ = pd.data_size().unwrap();
let _ = pd.thread_space_va().unwrap();
let _ = pd.vba_seh_va().unwrap();
let _ = pd.path_info().unwrap();
let _ = pd.external_table_va().unwrap();
let _ = pd.as_bytes();
}
}