| 1 | use alloc::string::String; | 
| 2 | use core::char; | 
| 3 |  | 
| 4 | use crate::read::{ReadError, ReadRef, Result}; | 
| 5 | use crate::{pe, LittleEndian as LE, U16Bytes}; | 
| 6 |  | 
| 7 | /// The `.rsrc` section of a PE file. | 
| 8 | /// | 
| 9 | /// Returned by [`DataDirectories::resource_directory`](super::DataDirectories::resource_directory). | 
| 10 | #[derive (Debug, Clone, Copy)] | 
| 11 | pub struct ResourceDirectory<'data> { | 
| 12 |     data: &'data [u8], | 
| 13 | } | 
| 14 |  | 
| 15 | impl<'data> ResourceDirectory<'data> { | 
| 16 |     /// Construct from the data of the `.rsrc` section. | 
| 17 |     pub fn new(data: &'data [u8]) -> Self { | 
| 18 |         ResourceDirectory { data } | 
| 19 |     } | 
| 20 |  | 
| 21 |     /// Parses the root resource directory. | 
| 22 |     pub fn root(&self) -> Result<ResourceDirectoryTable<'data>> { | 
| 23 |         ResourceDirectoryTable::parse(self.data, offset:0) | 
| 24 |     } | 
| 25 | } | 
| 26 |  | 
| 27 | /// A table of resource entries. | 
| 28 | #[derive (Debug, Clone)] | 
| 29 | pub struct ResourceDirectoryTable<'data> { | 
| 30 |     /// The table header. | 
| 31 |     pub header: &'data pe::ImageResourceDirectory, | 
| 32 |     /// The table entries. | 
| 33 |     pub entries: &'data [pe::ImageResourceDirectoryEntry], | 
| 34 | } | 
| 35 |  | 
| 36 | impl<'data> ResourceDirectoryTable<'data> { | 
| 37 |     fn parse(data: &'data [u8], offset: u32) -> Result<Self> { | 
| 38 |         let mut offset: u64 = u64::from(offset); | 
| 39 |         let header: &ImageResourceDirectory = dataResult<&ImageResourceDirectory, …> | 
| 40 |             .read::<pe::ImageResourceDirectory>(&mut offset) | 
| 41 |             .read_error("Invalid resource table header" )?; | 
| 42 |         let entries_count: usize = header.number_of_id_entries.get(LE) as usize | 
| 43 |             + header.number_of_named_entries.get(LE) as usize; | 
| 44 |         let entries: &[ImageResourceDirectoryEntry] = dataResult<&[ImageResourceDirectoryEntry], …> | 
| 45 |             .read_slice::<pe::ImageResourceDirectoryEntry>(&mut offset, entries_count) | 
| 46 |             .read_error("Invalid resource table entries" )?; | 
| 47 |         Ok(Self { header, entries }) | 
| 48 |     } | 
| 49 | } | 
| 50 |  | 
| 51 | impl pe::ImageResourceDirectoryEntry { | 
| 52 |     /// Returns true if the entry has a name, rather than an ID. | 
| 53 |     pub fn has_name(&self) -> bool { | 
| 54 |         self.name_or_id.get(LE) & pe::IMAGE_RESOURCE_NAME_IS_STRING != 0 | 
| 55 |     } | 
| 56 |  | 
| 57 |     /// Returns the section offset of the name. | 
| 58 |     /// | 
| 59 |     /// Valid if `has_name()` returns true. | 
| 60 |     fn name(&self) -> ResourceName { | 
| 61 |         let offset = self.name_or_id.get(LE) & !pe::IMAGE_RESOURCE_NAME_IS_STRING; | 
| 62 |         ResourceName { offset } | 
| 63 |     } | 
| 64 |  | 
| 65 |     /// Returns the ID. | 
| 66 |     /// | 
| 67 |     /// Valid if `has_string_name()` returns false. | 
| 68 |     fn id(&self) -> u16 { | 
| 69 |         (self.name_or_id.get(LE) & 0x0000_FFFF) as u16 | 
| 70 |     } | 
| 71 |  | 
| 72 |     /// Returns the entry name | 
| 73 |     pub fn name_or_id(&self) -> ResourceNameOrId { | 
| 74 |         if self.has_name() { | 
| 75 |             ResourceNameOrId::Name(self.name()) | 
| 76 |         } else { | 
| 77 |             ResourceNameOrId::Id(self.id()) | 
| 78 |         } | 
| 79 |     } | 
| 80 |  | 
| 81 |     /// Returns true if the entry is a subtable. | 
| 82 |     pub fn is_table(&self) -> bool { | 
| 83 |         self.offset_to_data_or_directory.get(LE) & pe::IMAGE_RESOURCE_DATA_IS_DIRECTORY != 0 | 
| 84 |     } | 
| 85 |  | 
| 86 |     /// Returns the section offset of the associated table or data. | 
| 87 |     pub fn data_offset(&self) -> u32 { | 
| 88 |         self.offset_to_data_or_directory.get(LE) & !pe::IMAGE_RESOURCE_DATA_IS_DIRECTORY | 
| 89 |     } | 
| 90 |  | 
| 91 |     /// Returns the data associated to this directory entry. | 
| 92 |     pub fn data<'data>( | 
| 93 |         &self, | 
| 94 |         section: ResourceDirectory<'data>, | 
| 95 |     ) -> Result<ResourceDirectoryEntryData<'data>> { | 
| 96 |         if self.is_table() { | 
| 97 |             ResourceDirectoryTable::parse(section.data, self.data_offset()) | 
| 98 |                 .map(ResourceDirectoryEntryData::Table) | 
| 99 |         } else { | 
| 100 |             section | 
| 101 |                 .data | 
| 102 |                 .read_at::<pe::ImageResourceDataEntry>(self.data_offset().into()) | 
| 103 |                 .read_error("Invalid resource entry" ) | 
| 104 |                 .map(ResourceDirectoryEntryData::Data) | 
| 105 |         } | 
| 106 |     } | 
| 107 | } | 
| 108 |  | 
| 109 | /// Data associated with a resource directory entry. | 
| 110 | #[derive (Debug, Clone)] | 
| 111 | pub enum ResourceDirectoryEntryData<'data> { | 
| 112 |     /// A subtable entry. | 
| 113 |     Table(ResourceDirectoryTable<'data>), | 
| 114 |     /// A resource data entry. | 
| 115 |     Data(&'data pe::ImageResourceDataEntry), | 
| 116 | } | 
| 117 |  | 
| 118 | impl<'data> ResourceDirectoryEntryData<'data> { | 
| 119 |     /// Converts to an option of table. | 
| 120 |     /// | 
| 121 |     /// Helper for iterator filtering. | 
| 122 |     pub fn table(self) -> Option<ResourceDirectoryTable<'data>> { | 
| 123 |         match self { | 
| 124 |             Self::Table(dir: ResourceDirectoryTable<'data>) => Some(dir), | 
| 125 |             _ => None, | 
| 126 |         } | 
| 127 |     } | 
| 128 |  | 
| 129 |     /// Converts to an option of data entry. | 
| 130 |     /// | 
| 131 |     /// Helper for iterator filtering. | 
| 132 |     pub fn data(self) -> Option<&'data pe::ImageResourceDataEntry> { | 
| 133 |         match self { | 
| 134 |             Self::Data(rsc: &'data ImageResourceDataEntry) => Some(rsc), | 
| 135 |             _ => None, | 
| 136 |         } | 
| 137 |     } | 
| 138 | } | 
| 139 |  | 
| 140 | /// A resource name. | 
| 141 | #[derive (Debug, Clone, Copy)] | 
| 142 | pub struct ResourceName { | 
| 143 |     offset: u32, | 
| 144 | } | 
| 145 |  | 
| 146 | impl ResourceName { | 
| 147 |     /// Converts to a `String`. | 
| 148 |     pub fn to_string_lossy(&self, directory: ResourceDirectory<'_>) -> Result<String> { | 
| 149 |         let d = self.data(directory)?.iter().map(|c| c.get(LE)); | 
| 150 |  | 
| 151 |         Ok(char::decode_utf16(d) | 
| 152 |             .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) | 
| 153 |             .collect::<String>()) | 
| 154 |     } | 
| 155 |  | 
| 156 |     /// Returns the string unicode buffer. | 
| 157 |     pub fn data<'data>( | 
| 158 |         &self, | 
| 159 |         directory: ResourceDirectory<'data>, | 
| 160 |     ) -> Result<&'data [U16Bytes<LE>]> { | 
| 161 |         let mut offset = u64::from(self.offset); | 
| 162 |         let len = directory | 
| 163 |             .data | 
| 164 |             .read::<U16Bytes<LE>>(&mut offset) | 
| 165 |             .read_error("Invalid resource name offset" )?; | 
| 166 |         directory | 
| 167 |             .data | 
| 168 |             .read_slice::<U16Bytes<LE>>(&mut offset, len.get(LE).into()) | 
| 169 |             .read_error("Invalid resource name length" ) | 
| 170 |     } | 
| 171 |  | 
| 172 |     /// Returns the string buffer as raw bytes. | 
| 173 |     pub fn raw_data<'data>(&self, directory: ResourceDirectory<'data>) -> Result<&'data [u8]> { | 
| 174 |         self.data(directory).map(crate::pod::bytes_of_slice) | 
| 175 |     } | 
| 176 | } | 
| 177 |  | 
| 178 | /// A resource name or ID. | 
| 179 | /// | 
| 180 | /// Can be either a string or a numeric ID. | 
| 181 | #[derive (Debug)] | 
| 182 | pub enum ResourceNameOrId { | 
| 183 |     /// A resource name. | 
| 184 |     Name(ResourceName), | 
| 185 |     /// A resource ID. | 
| 186 |     Id(u16), | 
| 187 | } | 
| 188 |  | 
| 189 | impl ResourceNameOrId { | 
| 190 |     /// Converts to an option of name. | 
| 191 |     /// | 
| 192 |     /// Helper for iterator filtering. | 
| 193 |     pub fn name(self) -> Option<ResourceName> { | 
| 194 |         match self { | 
| 195 |             Self::Name(name: ResourceName) => Some(name), | 
| 196 |             _ => None, | 
| 197 |         } | 
| 198 |     } | 
| 199 |  | 
| 200 |     /// Converts to an option of ID. | 
| 201 |     /// | 
| 202 |     /// Helper for iterator filtering. | 
| 203 |     pub fn id(self) -> Option<u16> { | 
| 204 |         match self { | 
| 205 |             Self::Id(id: u16) => Some(id), | 
| 206 |             _ => None, | 
| 207 |         } | 
| 208 |     } | 
| 209 | } | 
| 210 |  |