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