pub mod data_directories;
mod fields;
pub use fields::*;
use crate::{
error::PewterError,
io::{ReadData, WriteData},
};
use self::data_directories::{DataDirectories, ImageDataDirectory, SectionName};
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct OptionalHeader {
pub standard_fields: OptionalHeaderStandardFields,
pub windows_specific_fields: OptionalHeaderWindowsSpecific,
pub data_directories: DataDirectories,
}
impl CalulateOptVariantSize<Pe32> for OptionalHeader {
fn calculate_size() -> usize {
OptionalHeaderStandardFields::SIZE_PE + OptionalHeaderWindowsSpecificFields::<Pe32>::SIZE
}
}
impl CalulateOptVariantSize<Pe32Plus> for OptionalHeader {
fn calculate_size() -> usize {
OptionalHeaderStandardFields::SIZE_PE_PLUS
+ OptionalHeaderWindowsSpecificFields::<Pe32Plus>::SIZE
}
}
impl OptionalHeader {
pub const fn size_pe32() -> usize {
OptionalHeaderStandardFields::SIZE_PE + OptionalHeaderWindowsSpecificFields::<Pe32>::SIZE
}
pub const fn size_pe32_plus() -> usize {
OptionalHeaderStandardFields::SIZE_PE_PLUS
+ OptionalHeaderWindowsSpecificFields::<Pe32Plus>::SIZE
}
pub fn size(&self) -> usize {
match &self.windows_specific_fields {
OptionalHeaderWindowsSpecific::PE32(pe) => {
<Self as CalulateOptVariantSize<Pe32>>::calculate_size()
+ (pe.number_of_rva_and_sizes as usize * ImageDataDirectory::SIZE)
}
OptionalHeaderWindowsSpecific::PE32Plus(pe) => {
<Self as CalulateOptVariantSize<Pe32Plus>>::calculate_size()
+ (pe.number_of_rva_and_sizes as usize * ImageDataDirectory::SIZE)
}
}
}
pub fn try_get_data_directory(&self, name: SectionName) -> Option<ImageDataDirectory> {
(self.windows_specific_fields.number_of_rva_and_sizes() < name as u32)
.then(|| self.data_directories.get_directory(name))
}
}
impl ReadData for OptionalHeader {
fn read(reader: &mut impl crate::io::Reader) -> crate::error::Result<Self> {
let standard_fields: OptionalHeaderStandardFields = reader.read()?;
let windows_specific_fields = match &standard_fields.magic {
OptionalHeaderMagic::PE32 => OptionalHeaderWindowsSpecific::PE32(reader.read()?),
OptionalHeaderMagic::PE32Plus => {
OptionalHeaderWindowsSpecific::PE32Plus(reader.read()?)
}
};
let mut data_directories = DataDirectories::default();
for data_dir_name in SectionName::ALL
.into_iter()
.take(windows_specific_fields.number_of_rva_and_sizes() as usize)
{
data_directories.set_directory(data_dir_name, reader.read()?);
}
Ok(Self {
standard_fields,
windows_specific_fields,
data_directories,
})
}
}
impl WriteData for &OptionalHeader {
fn write_to(self, writer: &mut impl crate::io::Writer) -> crate::error::Result<()> {
writer.write(&self.standard_fields)?;
match (self.standard_fields.magic, &self.windows_specific_fields) {
(OptionalHeaderMagic::PE32, OptionalHeaderWindowsSpecific::PE32(pe)) => writer.write(pe)?,
(OptionalHeaderMagic::PE32Plus, OptionalHeaderWindowsSpecific::PE32Plus(pe)) => writer.write(pe)?,
_ => return Err(PewterError::invalid_image_format("Mismatching Optiional Header standard_fields.magic value and windows_specific_fields variant."))
}
for data_dir_name in SectionName::ALL
.into_iter()
.take(self.windows_specific_fields.number_of_rva_and_sizes() as usize)
{
writer.write(self.data_directories.get_directory(data_dir_name))?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn optional_header_pe32_is_96() {
let mut buffer = [0u8; OptionalHeader::size_pe32()];
buffer[..2].copy_from_slice(&OptionalHeaderMagic::PE32.to_u16().to_le_bytes());
let read_ptr = &mut buffer.as_slice();
OptionalHeader::read(read_ptr).unwrap();
assert_eq!(read_ptr.len(), 0);
}
#[test]
fn optional_header_pe32_plus_is_112() {
let mut buffer = [0u8; OptionalHeader::size_pe32_plus()];
buffer[..2].copy_from_slice(&OptionalHeaderMagic::PE32Plus.to_u16().to_le_bytes());
let read_ptr = &mut buffer.as_slice();
OptionalHeader::read(read_ptr).unwrap();
assert_eq!(read_ptr.len(), 0);
}
}