1use alloc::string::String;
2use core::char;
3
4use crate::read::{ReadError, ReadRef, Result};
5use 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)]
11pub struct ResourceDirectory<'data> {
12 data: &'data [u8],
13}
14
15impl<'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)]
29pub 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
36impl<'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
51impl 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)]
111pub enum ResourceDirectoryEntryData<'data> {
112 /// A subtable entry.
113 Table(ResourceDirectoryTable<'data>),
114 /// A resource data entry.
115 Data(&'data pe::ImageResourceDataEntry),
116}
117
118impl<'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<'_>) => 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: &ImageResourceDataEntry) => Some(rsc),
135 _ => None,
136 }
137 }
138}
139
140/// A resource name.
141#[derive(Debug, Clone, Copy)]
142pub struct ResourceName {
143 offset: u32,
144}
145
146impl 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)]
182pub enum ResourceNameOrId {
183 /// A resource name.
184 Name(ResourceName),
185 /// A resource ID.
186 Id(u16),
187}
188
189impl 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