use alloc::borrow::Cow;
use alloc::vec::Vec;
use super::PeFile;
use crate::error::Result;
use crate::pe::{ResourceEntry, ResourceType};
use crate::resource::{GroupIconDirectory, Icon, Resources, VsVersionInfo};
struct ResourceLocation<'a> {
id: u32,
offset: usize,
size: usize,
data: &'a [u8],
}
impl<'a> ResourceLocation<'a> {
fn bytes(&self) -> Option<&'a [u8]> {
self.data
.get(self.offset..self.offset.checked_add(self.size)?)
}
}
impl<'a> PeFile<'a> {
pub fn resources(&self) -> Result<Option<Resources<'a>>> {
let versions = self.locate_resources(ResourceType::Version)?;
let icons = self.locate_resources(ResourceType::Icon)?;
let groups = self.locate_resources(ResourceType::GroupIcon)?;
if versions.is_empty() && icons.is_empty() && groups.is_empty() {
return Ok(None);
}
Ok(Some(Resources {
version_info: versions
.first()
.and_then(|location| VsVersionInfo::parse(self.data, location.offset).ok()),
icons: icons
.iter()
.filter_map(|location| {
location.bytes().map(|data| Icon {
id: location.id,
data,
})
})
.collect(),
group_icon_directories: groups
.iter()
.filter_map(|location| {
GroupIconDirectory::parse(self.data, location.offset, location.size).ok()
})
.collect(),
}))
}
pub fn icons(&self) -> Result<impl Iterator<Item = Cow<'a, [u8]>> + use<'a>> {
let icons = self.resources()?.map(|r| r.icons).unwrap_or_default();
Ok(icons.into_iter().filter_map(|icon| icon.to_ico()))
}
pub fn group_icons(&self) -> Result<Vec<Vec<Cow<'a, [u8]>>>> {
let Some(resources) = self.resources()? else {
return Ok(Vec::new());
};
Ok(resources
.group_icon_directories
.iter()
.map(|directory| {
directory
.entries
.iter()
.flat_map(|entry| entry.associated_icons(&resources.icons))
.filter_map(Icon::to_ico)
.collect()
})
.collect())
}
fn locate_resources(&self, resource_type: ResourceType) -> Result<Vec<ResourceLocation<'a>>> {
let Some(root) = self.resource_directory()? else {
return Ok(Vec::new());
};
let sections = self.section_headers()?;
let mut locations = Vec::new();
let type_entries = root
.entries
.iter()
.filter(|e| e.raw.id() == resource_type.value());
for name_entry in children(type_entries)? {
let language_entries = name_entry.directory()?.entries;
locations.extend(language_entries.iter().filter_map(|language_entry| {
let data_entry = language_entry.data_entry().ok()?;
let offset = sections.rva_to_offset(data_entry.offset_to_data()).ok()?;
Some(ResourceLocation {
id: name_entry.raw.id(),
offset: offset.usize(),
size: data_entry.size() as usize,
data: self.data,
})
}));
}
Ok(locations)
}
}
fn children<'e, 'a: 'e>(
entries: impl Iterator<Item = &'e ResourceEntry<'a>>,
) -> Result<Vec<ResourceEntry<'a>>> {
let mut children = Vec::new();
for entry in entries {
children.extend(entry.directory()?.entries);
}
Ok(children)
}