use std::prelude::v1::*;
#[cfg(feature = "std")]
use std::io;
use crate::util::AlignTo;
use crate::Error;
use std::{fmt, mem, slice};
use super::{FindError, Resources};
use self::image::*;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ResourceType {
Icon,
Cursor,
}
impl ResourceType {
#[inline]
pub fn id(self) -> u16 {
match self {
ResourceType::Icon => crate::image::RT_ICON,
ResourceType::Cursor => crate::image::RT_CURSOR,
}
}
}
impl<'a> From<ResourceType> for super::Name<'a> {
fn from(resource_type: ResourceType) -> super::Name<'a> {
resource_type.id().into()
}
}
#[derive(Copy, Clone)]
pub struct GroupResource<'a> {
resources: Resources<'a>,
image: &'a GRPICONDIR,
}
impl<'a> GroupResource<'a> {
pub fn new(resources: Resources<'a>, bytes: &'a [u8]) -> Result<GroupResource<'a>, Error> {
if !bytes.as_ptr().aligned_to(2) {
return Err(Error::Misaligned);
}
if bytes.len() < mem::size_of::<GRPICONDIR>() {
return Err(Error::Bounds);
}
let image: &'a GRPICONDIR = unsafe { &*(bytes.as_ptr() as *const GRPICONDIR) };
if image.idReserved != 0 || !(image.idType == 1 || image.idType == 2) {
return Err(Error::BadMagic);
}
let total_size = mem::size_of::<GRPICONDIR>() + image.idCount as usize * mem::size_of::<GRPICONDIRENTRY>();
if bytes.len() != total_size {
return Err(Error::Bounds);
}
Ok(GroupResource { resources, image })
}
pub fn header(&self) -> &'a GRPICONDIR {
self.image
}
pub fn entries(&self) -> &'a [GRPICONDIRENTRY] {
let len = self.image.idCount as usize;
unsafe {
let ptr = (self.image as *const GRPICONDIR).offset(1) as *const GRPICONDIRENTRY;
slice::from_raw_parts(ptr, len)
}
}
pub fn ty(&self) -> ResourceType {
match self.image.idType {
1 => ResourceType::Icon,
2 => ResourceType::Cursor,
_ => unreachable!(), }
}
pub fn image(&self, id: u16) -> Result<&'a [u8], FindError> {
self.resources.root()?
.get_dir(self.ty().into())?
.get_dir(id.into())?
.first_data()?
.bytes().map_err(FindError::Pe)
}
#[cfg(feature = "std")]
pub fn write(&self, dest: &mut dyn io::Write) -> io::Result<()> {
dest.write(dataview::bytes(self.image))?;
let entries = self.entries();
let mut image_offset = (6 + entries.len() * 16) as u32;
for entry in entries {
let mut icon_entry = [0u32; 4];
dataview::bytes_mut(&mut icon_entry)[..14].copy_from_slice(dataview::bytes(entry));
icon_entry[3] = image_offset;
image_offset += entry.bytes_in_resource();
dest.write(dataview::bytes(&icon_entry))?;
}
for entry in entries {
if let Ok(bytes) = self.image(entry.nId) {
dest.write(bytes)?;
}
}
Ok(())
}
}
impl fmt::Debug for GroupResource<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("GroupResource")
.field("type", &self.ty())
.field("entries.len", &self.entries().len())
.finish()
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for GroupResource<'_> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut bytes = Vec::new();
mem::forget(self.write(&mut bytes));
#[cfg(feature = "data-encoding")]
{if serializer.is_human_readable() {
return serializer.serialize_str(&data_encoding::BASE64.encode(&bytes));
}}
serializer.serialize_bytes(&bytes)
}
}
pub type GroupIcon<'a> = GroupResource<'a>;
pub type GroupCursor<'a> = GroupResource<'a>;
#[allow(non_snake_case)]
pub mod image {
use crate::Pod;
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct GRPICONDIR {
pub idReserved: u16,
pub idType: u16,
pub idCount: u16,
pub idEntries: [GRPICONDIRENTRY; 0],
}
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct GRPICONDIRENTRY {
pub bWidth: u8,
pub bHeight: u8,
pub bColorCount: u8,
pub bReserved: u8,
pub wPlanes: u16,
pub wBitCount: u16,
pub dwBytesInResLo: u16,
pub dwBytesInResHi: u16,
pub nId: u16,
}
impl GRPICONDIRENTRY {
pub fn bytes_in_resource(&self) -> u32 {
self.dwBytesInResHi as u32 * 0x10000 + self.dwBytesInResLo as u32
}
}
unsafe impl Pod for GRPICONDIR {}
unsafe impl Pod for GRPICONDIRENTRY {}
}