pelite\resources/
group.rs1use std::prelude::v1::*;
44
45#[cfg(feature = "std")]
46use std::io;
47
48use crate::util::AlignTo;
49use crate::Error;
50use std::{fmt, mem, slice};
51
52use super::{FindError, Resources};
53
54use self::image::*;
55
56#[derive(Copy, Clone, Debug, Eq, PartialEq)]
60pub enum ResourceType {
61 Icon,
62 Cursor,
63}
64impl ResourceType {
65 #[inline]
66 pub fn id(self) -> u16 {
67 match self {
68 ResourceType::Icon => crate::image::RT_ICON,
69 ResourceType::Cursor => crate::image::RT_CURSOR,
70 }
71 }
72}
73impl<'a> From<ResourceType> for super::Name<'a> {
74 fn from(resource_type: ResourceType) -> super::Name<'a> {
75 resource_type.id().into()
76 }
77}
78
79#[derive(Copy, Clone)]
81pub struct GroupResource<'a> {
82 resources: Resources<'a>,
83 image: &'a GRPICONDIR,
84}
85impl<'a> GroupResource<'a> {
86 pub fn new(resources: Resources<'a>, bytes: &'a [u8]) -> Result<GroupResource<'a>, Error> {
90 if !bytes.as_ptr().aligned_to(2) {
91 return Err(Error::Misaligned);
92 }
93 if bytes.len() < mem::size_of::<GRPICONDIR>() {
94 return Err(Error::Bounds);
95 }
96 let image: &'a GRPICONDIR = unsafe { &*(bytes.as_ptr() as *const GRPICONDIR) };
97 if image.idReserved != 0 || !(image.idType == 1 || image.idType == 2) {
98 return Err(Error::BadMagic);
99 }
100 let total_size = mem::size_of::<GRPICONDIR>() + image.idCount as usize * mem::size_of::<GRPICONDIRENTRY>();
101 if bytes.len() != total_size {
102 return Err(Error::Bounds);
103 }
104 Ok(GroupResource { resources, image })
105 }
106 pub fn header(&self) -> &'a GRPICONDIR {
108 self.image
109 }
110 pub fn entries(&self) -> &'a [GRPICONDIRENTRY] {
112 let len = self.image.idCount as usize;
113 unsafe {
115 let ptr = (self.image as *const GRPICONDIR).offset(1) as *const GRPICONDIRENTRY;
116 slice::from_raw_parts(ptr, len)
117 }
118 }
119 pub fn ty(&self) -> ResourceType {
121 match self.image.idType {
122 1 => ResourceType::Icon,
123 2 => ResourceType::Cursor,
124 _ => unreachable!(), }
126 }
127 pub fn image(&self, id: u16) -> Result<&'a [u8], FindError> {
129 self.resources.root()?
130 .get_dir(self.ty().into())?
131 .get_dir(id.into())?
132 .first_data()?
133 .bytes().map_err(FindError::Pe)
134 }
135 #[cfg(feature = "std")]
137 pub fn write(&self, dest: &mut dyn io::Write) -> io::Result<()> {
138 dest.write(dataview::bytes(self.image))?;
140 let entries = self.entries();
142 let mut image_offset = (6 + entries.len() * 16) as u32;
143 for entry in entries {
144 let mut icon_entry = [0u32; 4];
147 dataview::bytes_mut(&mut icon_entry)[..14].copy_from_slice(dataview::bytes(entry));
148 icon_entry[3] = image_offset;
149 image_offset += entry.bytes_in_resource();
150 dest.write(dataview::bytes(&icon_entry))?;
151 }
152 for entry in entries {
154 if let Ok(bytes) = self.image(entry.nId) {
158 dest.write(bytes)?;
160 }
161 }
162 Ok(())
163 }
164}
165
166impl fmt::Debug for GroupResource<'_> {
167 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168 f.debug_struct("GroupResource")
169 .field("type", &self.ty())
170 .field("entries.len", &self.entries().len())
171 .finish()
172 }
173}
174
175#[cfg(feature = "serde")]
176impl serde::Serialize for GroupResource<'_> {
177 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
178 let mut bytes = Vec::new();
179 mem::forget(self.write(&mut bytes));
180 #[cfg(feature = "data-encoding")]
181 {if serializer.is_human_readable() {
182 return serializer.serialize_str(&data_encoding::BASE64.encode(&bytes));
183 }}
184 serializer.serialize_bytes(&bytes)
185 }
186}
187
188pub type GroupIcon<'a> = GroupResource<'a>;
190pub type GroupCursor<'a> = GroupResource<'a>;
192
193#[allow(non_snake_case)]
196pub mod image {
197 use crate::Pod;
198 #[derive(Copy, Clone, Debug)]
199 #[repr(C)]
200 pub struct GRPICONDIR {
201 pub idReserved: u16,
202 pub idType: u16,
203 pub idCount: u16,
204 pub idEntries: [GRPICONDIRENTRY; 0],
205 }
206 #[derive(Copy, Clone, Debug)]
207 #[repr(C)]
208 pub struct GRPICONDIRENTRY {
209 pub bWidth: u8,
210 pub bHeight: u8,
211 pub bColorCount: u8,
212 pub bReserved: u8,
213 pub wPlanes: u16,
214 pub wBitCount: u16,
215 pub dwBytesInResLo: u16,
216 pub dwBytesInResHi: u16,
217 pub nId: u16,
218 }
219 impl GRPICONDIRENTRY {
220 pub fn bytes_in_resource(&self) -> u32 {
221 self.dwBytesInResHi as u32 * 0x10000 + self.dwBytesInResLo as u32
222 }
223 }
224 unsafe impl Pod for GRPICONDIR {}
225 unsafe impl Pod for GRPICONDIRENTRY {}
226}