1use std::prelude::v1::*;
6
7use std::{char, fmt, iter, mem, slice};
8
9use crate::{Pod, Error, Result};
10use crate::image::*;
11
12mod find;
15pub use self::find::FindError;
16
17mod art;
18
19pub mod version_info;
20pub mod group;
21
22#[derive(Copy, Clone)]
26pub struct Resources<'a> {
27 section: &'a [u8],
28 dir: &'a IMAGE_DATA_DIRECTORY,
29}
30impl<'a> Resources<'a> {
31 pub fn new(section: &'a [u8], dir: &'a IMAGE_DATA_DIRECTORY) -> Resources<'a> {
35 Resources { section, dir }
39 }
40 pub fn root(&self) -> Result<Directory<'a>> {
42 Directory::try_from(*self, 0)
43 }
44 pub fn fsck(&self) -> Result<()> {
48 self.root()?.fsck()
49 }
50
51 #[inline]
52 fn slice<T>(&self, offset: u32) -> Result<&'a T> where T: Pod {
53 let start = offset as usize;
54 let end = mem::size_of::<T>().wrapping_add(start);
55 if !cfg!(feature = "unsafe_alignment") && start & (mem::align_of::<T>() - 1) != 0 {
57 return Err(Error::Misaligned);
58 }
59 let bytes = self.section.get(start..end).ok_or(Error::Bounds)?;
61 Ok(unsafe { &*(bytes.as_ptr() as *const T) })
63 }
64 #[inline]
65 #[allow(dead_code)] fn slice_len<T>(&self, offset: u32, len: usize) -> Result<&'a [T]> where T: Pod {
67 let start = offset as usize;
68 let size_of = mem::size_of::<T>().checked_mul(len).ok_or(Error::Overflow)?;
69 let end = start.wrapping_add(size_of);
70 if !cfg!(feature = "unsafe_alignment") && start & (mem::align_of::<T>() - 1) != 0 {
72 return Err(Error::Misaligned);
73 }
74 let bytes = self.section.get(start..end).ok_or(Error::Bounds)?;
76 Ok(unsafe { slice::from_raw_parts(bytes.as_ptr() as *const T, len) })
77 }
78 #[inline]
79 fn slice_ws(&self, offset: u32) -> Result<&'a [u16]> {
80 let offset = offset as usize;
81 if !cfg!(feature = "unsafe_alignment") && offset & 1 != 0 {
83 return Err(Error::Misaligned);
84 }
85 let len = self.section.get(offset..offset + 2).ok_or(Error::Bounds)?;
87 let len = unsafe { *(len.as_ptr() as *const u16) } as usize;
88 let name = self.section.get(offset + 2..offset + 2 + len * 2).ok_or(Error::Bounds)?;
90 let name = unsafe { slice::from_raw_parts(name.as_ptr() as *const u16, len) };
91 Ok(name)
92 }
93}
94impl<'a> fmt::Debug for Resources<'a> {
95 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96 f.write_str("Resources { .. }")
97 }
98}
99
100#[derive(Copy, Clone)]
104pub struct Directory<'a> {
105 resources: Resources<'a>,
106 image: &'a IMAGE_RESOURCE_DIRECTORY,
107}
108impl<'a> Directory<'a> {
109 fn try_from(resources: Resources<'a>, offset: u32) -> Result<Directory<'a>> {
110 let image: &IMAGE_RESOURCE_DIRECTORY = resources.slice(offset)?;
111 let entries_size = (image.NumberOfNamedEntries as usize + image.NumberOfIdEntries as usize) * mem::size_of::<IMAGE_RESOURCE_DIRECTORY_ENTRY>();
115 let entries_offset = offset as usize + mem::size_of::<IMAGE_RESOURCE_DIRECTORY>();
116 if entries_size > resources.section.len() - entries_offset {
117 return Err(Error::Bounds);
118 }
119 Ok(Directory { resources, image })
120 }
121 pub fn resources(&self) -> Resources<'a> {
123 self.resources
124 }
125 pub fn image(&self) -> &'a IMAGE_RESOURCE_DIRECTORY {
127 self.image
128 }
129 pub fn entries(&self) -> Entries<'a, impl Clone + FnMut(&'a IMAGE_RESOURCE_DIRECTORY_ENTRY) -> DirectoryEntry<'a>> {
131 let slice = unsafe {
133 let p = (self.image as *const IMAGE_RESOURCE_DIRECTORY).offset(1) as *const IMAGE_RESOURCE_DIRECTORY_ENTRY;
134 let len = self.image.NumberOfNamedEntries as usize + self.image.NumberOfIdEntries as usize;
135 slice::from_raw_parts(p, len)
136 };
137 let resources = self.resources;
138 slice.iter().map(move |image| DirectoryEntry { resources, image })
139 }
140 pub fn named_entries(&self) -> Entries<'a, impl Clone + FnMut(&'a IMAGE_RESOURCE_DIRECTORY_ENTRY) -> DirectoryEntry<'a>> {
144 let slice = unsafe {
146 let p = (self.image as *const IMAGE_RESOURCE_DIRECTORY).offset(1) as *const IMAGE_RESOURCE_DIRECTORY_ENTRY;
148 let len = self.image.NumberOfNamedEntries as usize;
149 slice::from_raw_parts(p, len)
150 };
151 let resources = self.resources;
152 slice.iter().map(move |image| DirectoryEntry { resources, image })
153 }
154 pub fn id_entries(&self) -> Entries<'a, impl Clone + FnMut(&'a IMAGE_RESOURCE_DIRECTORY_ENTRY) -> DirectoryEntry<'a>> {
158 let slice = unsafe {
160 let p = ((self.image as *const IMAGE_RESOURCE_DIRECTORY).offset(1) as *const IMAGE_RESOURCE_DIRECTORY_ENTRY).offset(self.image.NumberOfNamedEntries as isize);
162 let len = self.image.NumberOfIdEntries as usize;
163 slice::from_raw_parts(p, len)
164 };
165 let resources = self.resources;
166 slice.iter().map(move |image| DirectoryEntry { resources, image })
167 }
168 pub fn fsck(&self) -> Result<()> {
172 self.entries().try_for_each(|e| e.fsck())
173 }
174}
175impl<'a> fmt::Debug for Directory<'a> {
176 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
177 f.debug_struct("Directory")
178 .field("entries", &self.entries())
179 .finish()
180 }
181}
182
183pub type Entries<'a, F> = iter::Map<slice::Iter<'a, IMAGE_RESOURCE_DIRECTORY_ENTRY>, F>;
187
188#[derive(Copy, Clone, Debug, Eq)]
192pub enum Name<'a> {
193 Id(u32),
197 Wide(&'a [u16]),
199 Str(&'a str),
205}
206impl<'a> Name<'a> {
208 pub const MANIFEST: Name<'a> = Name::Id(crate::image::RT_MANIFEST as u32);
209 pub const VERSION: Name<'a> = Name::Id(crate::image::RT_VERSION as u32);
210 pub const GROUP_ICON: Name<'a> = Name::Id(crate::image::RT_GROUP_ICON as u32);
211 pub const GROUP_CURSOR: Name<'a> = Name::Id(crate::image::RT_GROUP_CURSOR as u32);
212}
213impl<'a> Name<'a> {
214 #[inline(never)]
215 fn eq_string(&self, string: &str) -> bool {
216 match self {
217 &Name::Id(id) => {
218 if !(string.len() >= 2 && string.as_bytes()[0] == b'#') {
220 false
221 }
222 else if string.as_bytes()[1] > b'0' && string.as_bytes()[1] <= b'9' {
224 match string[1..].parse::<u32>() {
225 Ok(string_id) if id == string_id => true,
226 _ => false,
227 }
228 }
229 else {
231 match RSRC_TYPES.get(id as usize) {
232 Some(&Some(name)) if string == name => true,
233 _ => false,
234 }
235 }
236 },
237 &Name::Wide(words) => char::decode_utf16(words.iter().cloned()).eq(string.chars().map(Ok)),
238 &Name::Str(name) => string == name,
239 }
240 }
241 fn rename_id(self, names: &[Option<&'a str>]) -> Name<'a> {
242 if let Name::Id(id) = self {
243 if let Some(&Some(name)) = names.get(id as usize) {
244 return Name::Str(name);
245 }
246 }
247 self
248 }
249}
250impl<'a> From<u16> for Name<'a> {
251 fn from(id: u16) -> Name<'a> {
252 Name::Id(id as u32)
253 }
254}
255impl<'a> From<&'a [u16]> for Name<'a> {
256 fn from(words: &'a [u16]) -> Name<'a> {
257 Name::Wide(words)
258 }
259}
260impl<'a> From<&'a str> for Name<'a> {
261 fn from(name: &'a str) -> Name<'a> {
262 Name::Str(name)
263 }
264}
265impl PartialEq for Name<'_> {
266 #[inline(never)]
267 fn eq(&self, rhs: &Name<'_>) -> bool {
268 match (*self, *rhs) {
269 (Name::Id(lhs), Name::Id(rhs)) => lhs == rhs,
271 (Name::Id(_), Name::Wide(_)) => false,
272 (Name::Wide(lhs), Name::Wide(rhs)) => lhs == rhs,
273 (Name::Wide(_), Name::Id(_)) => false,
274 (Name::Str(lhs), rhs) => rhs.eq_string(lhs),
276 (lhs, Name::Str(rhs)) => lhs.eq_string(rhs),
277 }
278 }
279}
280impl PartialEq<str> for Name<'_> {
281 fn eq(&self, rhs: &str) -> bool {
282 self.eq_string(rhs)
283 }
284}
285impl PartialEq<u32> for Name<'_> {
286 fn eq(&self, &rhs: &u32) -> bool {
287 match self {
288 &Name::Id(id) => id == rhs,
289 _ => false,
290 }
291 }
292}
293impl fmt::Display for Name<'_> {
294 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
295 match self {
296 Name::Id(id) => write!(f, "#{}", id),
297 Name::Wide(words) => {
298 for chr in char::decode_utf16(words.iter().cloned()) {
299 let chr = chr.unwrap_or(char::REPLACEMENT_CHARACTER);
300 fmt::Write::write_char(f, chr)?;
301 }
302 Ok(())
303 },
304 Name::Str(string) => f.write_str(string),
305 }
306 }
307}
308
309#[derive(Copy, Clone, Debug)]
313#[cfg_attr(feature = "serde", derive(::serde::Serialize), serde(untagged))]
314pub enum Entry<'a> {
315 Directory(Directory<'a>),
316 DataEntry(DataEntry<'a>),
317}
318impl<'a> Entry<'a> {
319 pub fn dir(self) -> Option<Directory<'a>> {
321 match self {
322 Entry::Directory(dir) => Some(dir),
323 Entry::DataEntry(_) => None,
324 }
325 }
326 pub fn data(self) -> Option<DataEntry<'a>> {
328 match self {
329 Entry::Directory(_) => None,
330 Entry::DataEntry(data) => Some(data),
331 }
332 }
333}
334
335#[derive(Copy, Clone)]
341pub struct DirectoryEntry<'a> {
342 resources: Resources<'a>,
343 image: &'a IMAGE_RESOURCE_DIRECTORY_ENTRY,
344}
345impl<'a> DirectoryEntry<'a> {
346 pub fn resources(&self) -> Resources<'a> {
348 self.resources
349 }
350 pub fn image(&self) -> &'a IMAGE_RESOURCE_DIRECTORY_ENTRY {
352 self.image
353 }
354 pub fn name(&self) -> Result<Name<'a>> {
356 if self.image.Name & 0x80000000 != 0 {
357 let offset = self.image.Name & !0x80000000;
358 let words = self.resources.slice_ws(offset)?;
359 Ok(Name::Wide(words))
360 }
361 else {
362 Ok(Name::Id(self.image.Name))
364 }
365 }
366 pub fn is_dir(&self) -> bool {
368 self.image.Offset & 0x80000000 != 0
369 }
370 pub fn entry(&self) -> Result<Entry<'a>> {
372 if self.is_dir() {
373 let offset = self.image.Offset & !0x80000000;
374 Directory::try_from(self.resources, offset).map(Entry::Directory)
375 }
376 else {
377 let offset = self.image.Offset;
378 DataEntry::try_from(self.resources, offset).map(Entry::DataEntry)
379 }
380 }
381 pub fn fsck(&self) -> Result<()> {
385 self.name()?;
386 match self.entry()? {
387 Entry::Directory(dir) => dir.fsck(),
388 Entry::DataEntry(data) => data.fsck(),
389 }
390 }
391}
392impl<'a> fmt::Debug for DirectoryEntry<'a> {
393 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
394 f.debug_struct("DirectoryEntry")
395 .field("name", &format_args!("{:?}", self.name()))
396 .field("entry", &if self.is_dir() { "Directory(..)" } else { "DataEntry(..)" })
397 .finish()
398 }
399}
400
401#[derive(Copy, Clone)]
405pub struct DataEntry<'a> {
406 resources: Resources<'a>,
407 image: &'a IMAGE_RESOURCE_DATA_ENTRY,
408}
409impl<'a> DataEntry<'a> {
410 fn try_from(resources: Resources<'a>, offset: u32) -> Result<DataEntry<'a>> {
411 let image = resources.slice(offset)?;
412 Ok(DataEntry { resources, image })
413 }
414 pub fn resources(&self) -> Resources<'a> {
416 self.resources
417 }
418 pub fn image(&self) -> &'a IMAGE_RESOURCE_DATA_ENTRY {
420 self.image
421 }
422 pub fn bytes(&self) -> Result<&'a [u8]> {
424 let start = u32::checked_sub(self.image.OffsetToData, self.resources.dir.VirtualAddress).ok_or(Error::Overflow)?;
425 let end = u32::checked_add(start, self.image.Size).ok_or(Error::Overflow)?;
426 self.resources.section.get(start as usize..end as usize).ok_or(Error::Bounds)
427 }
428 pub fn size(&self) -> usize {
430 self.image.Size as usize
431 }
432 pub fn code_page(&self) -> u32 {
434 self.image.CodePage
435 }
436 pub fn fsck(&self) -> Result<()> {
440 self.bytes()?;
441 Ok(())
442 }
443}
444impl<'a> fmt::Debug for DataEntry<'a> {
445 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
446 f.debug_struct("DataEntry")
447 .field("data.len", &self.image.Size)
448 .finish()
449 }
450}
451
452static RSRC_TYPES: [Option<&str>; 25] = [
455 None, Some("#CURSOR"), Some("#BITMAP"), Some("#ICON"), Some("#MENU"),
456 Some("#DIALOG"), Some("#STRING"), Some("#FONTDIR"), Some("#FONT"), Some("#ACCELERATOR"),
457 Some("#RCDATA"), Some("#MESSAGETABLE"), Some("#GROUP_CURSOR"), None, Some("#GROUP_ICON"),
458 None, Some("#VERSION"), Some("#DLGINCLUDE"), None, Some("#PLUGPLAY"),
459 Some("#VXD"), Some("#ANICURSOR"), Some("#ANIICON"), Some("#HTML"), Some("#MANIFEST"),
460];
461
462#[cfg(feature = "serde")]
483mod serde {
484 use crate::util::serde_helper::*;
485 use super::{Resources, Directory, Name, DirectoryEntry, DataEntry};
486
487 struct NamedDirectoryEntry<'a>(DirectoryEntry<'a>);
489 impl<'a> Serialize for NamedDirectoryEntry<'a> {
490 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
491 let mut state = serializer.serialize_struct("DirectoryEntry", 2)?;
492 state.serialize_field("name", &self.0.name().ok().map(|name| name.rename_id(&super::RSRC_TYPES)))?;
493 state.serialize_field(if self.0.is_dir() { "directory" } else { "data" }, &self.0.entry().ok())?;
494 state.end()
495 }
496 }
497
498 impl<'a> Serialize for Resources<'a> {
499 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
500 match self.root() {
501 Ok(root) => serializer.collect_seq(root.entries().map(NamedDirectoryEntry)),
502 Err(_) => serializer.serialize_none(),
503 }
504 }
505 }
506 impl<'a> Serialize for Directory<'a> {
507 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
508 serializer.collect_seq(self.entries())
509 }
510 }
511 impl<'a> Serialize for Name<'a> {
512 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
513 match self {
514 Name::Id(id) => id.serialize(serializer),
515 Name::Wide(words) => serializer.serialize_str(&String::from_utf16_lossy(words)),
516 Name::Str(name) => serializer.serialize_str(name),
517 }
518 }
519 }
520 impl<'a> Serialize for DirectoryEntry<'a> {
521 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
522 let mut state = serializer.serialize_struct("DirectoryEntry", 2)?;
523 state.serialize_field("name", &self.name().ok())?;
524 state.serialize_field(if self.is_dir() { "directory" } else { "data" }, &self.entry().ok())?;
525 state.end()
526 }
527 }
528 impl<'a> Serialize for DataEntry<'a> {
529 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
530 let mut state = serializer.serialize_struct("DataEntry", 3)?;
531 state.serialize_field("address", &self.image().OffsetToData)?;
532 state.serialize_field("size", &self.size())?;
533 state.serialize_field("code_page", &self.code_page())?;
534 state.end()
535 }
536 }
537}
538
539#[cfg(test)]
542pub(crate) fn test(resources: Resources<'_>) -> Result<()> {
543 fn test_dir(dir: Directory<'_>) {
544 let _ = format!("{}", dir);
545 let _ = format!("{:?}", dir);
546
547 for entry in dir.entries() {
548 let _ = format!("{:?}\n{:?}", entry.name(), entry);
549
550 if let Ok(name) = entry.name() {
552 let mut id_entries;
553 let mut named_entries;
554 let mut entries: &mut dyn Iterator<Item = _> = match name {
555 Name::Id(_) => { id_entries = dir.id_entries(); &mut id_entries },
556 Name::Wide(_) => { named_entries = dir.named_entries(); &mut named_entries },
557 Name::Str(_) => unreachable!()
558 };
559 assert!((&mut entries).any(|entry| entry.name() == Ok(name)));
560 }
561
562 match entry.entry() {
564 Ok(Entry::DataEntry(data)) => {
565 assert!(!entry.is_dir());
566 let _ = format!("{:?}", data);
567 let _size = data.size();
568 let _code_page = data.code_page();
569 let _bytes = data.bytes();
570 },
571 Ok(Entry::Directory(dir)) => {
572 assert!(entry.is_dir());
573 let _ = test_dir(dir);
574 },
575 Err(_) => (),
576 }
577 }
578 }
579 let _ = resources.fsck();
580 println!("{}", resources);
581 if let Ok(version_info) = resources.version_info() {
582 self::version_info::test(version_info)
583 }
584 resources.root().map(test_dir)
585}