1mod qualifier;
14mod reader;
15mod v1;
16mod v2;
17mod value;
18
19use std::borrow::Cow;
20use std::fmt;
21use std::ops::Range;
22
23pub use qualifier::{Qualifier, QualifierKey, Qualifiers};
24use reader::Reader;
25pub use value::{Attr, ResourceRef, ResourceType, ResourceValue, UnknownResourceType};
26
27#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
29#[non_exhaustive]
30pub enum ResourceIndexError {
31 #[error("unknown resource index version {version:?} (known: \"Restool <version>\", \"RestoolV2 <version>\")")]
34 UnknownVersion {
35 version: String,
37 },
38 #[error("{what} at {offset:#x} needs {needed} bytes, {available} are left")]
39 Truncated { what: &'static str, offset: usize, needed: usize, available: usize },
40 #[error("{what} offset {offset:#x} is outside the index of {len:#x} bytes")]
41 OutOfBounds { what: &'static str, offset: u32, len: usize },
42 #[error("expected the tag {expected:?} at {offset:#x}, found {found:?}")]
43 BadTag { expected: &'static str, offset: usize, found: [u8; 4] },
44 #[error("the table lists resource {listed:#010x} at {offset:#x}, where {found:#010x} is stored")]
46 IdMismatch { listed: u32, found: u32, offset: usize },
47 #[error("resource {id:#010x} has a value for qualifier set {config}, which the index does not define")]
49 UnknownQualifiers { id: u32, config: u32 },
50 #[error("the tables describe more than {limit} values, more than an index of this size holds")]
53 TooManyValues { limit: usize },
54}
55
56type Result<T> = std::result::Result<T, ResourceIndexError>;
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
60pub enum IndexFormat {
61 V1,
63 V2,
65}
66
67impl IndexFormat {
68 fn terminates_values(self) -> bool {
70 match self {
71 IndexFormat::V1 => true,
72 IndexFormat::V2 => false,
73 }
74 }
75}
76
77#[derive(Debug, Clone, Copy)]
79struct Entry<'a> {
80 id: u32,
81 kind: ResourceType,
82 qualifiers: usize,
84 name: &'a [u8],
85 value: &'a [u8],
86}
87
88struct Entries<'a> {
91 entries: Vec<Entry<'a>>,
92 limit: usize,
93}
94
95impl<'a> Entries<'a> {
96 const MIN_BYTES_PER_VALUE: usize = 8;
98
99 fn for_index(bytes: &[u8]) -> Entries<'a> {
100 Entries { entries: Vec::new(), limit: bytes.len() / Self::MIN_BYTES_PER_VALUE }
101 }
102
103 fn push(&mut self, entry: Entry<'a>) -> Result<()> {
104 if self.entries.len() >= self.limit {
105 return Err(ResourceIndexError::TooManyValues { limit: self.limit });
106 }
107 self.entries.push(entry);
108 Ok(())
109 }
110}
111
112struct Tables<'a> {
114 qualifiers: Vec<Qualifiers>,
115 entries: Vec<Entry<'a>>,
116}
117
118#[derive(Debug, Clone)]
136pub struct ResourceIndex<'a> {
137 version: Cow<'a, str>,
138 format: IndexFormat,
139 qualifiers: Vec<Qualifiers>,
140 entries: Vec<Entry<'a>>,
142 by_name: Vec<usize>,
144}
145
146impl<'a> ResourceIndex<'a> {
147 const VERSION_LEN: usize = 128;
149
150 pub fn parse(bytes: &'a [u8]) -> Result<ResourceIndex<'a>> {
158 let mut reader = Reader::new(bytes);
159 let version = reader.take(Self::VERSION_LEN, "version")?;
160 let version = String::from_utf8_lossy(version.split(|&b| b == 0).next().unwrap_or_default());
161 let format = match version.split(' ').next() {
162 Some("Restool") => IndexFormat::V1,
163 Some("RestoolV2") => IndexFormat::V2,
164 _ => return Err(ResourceIndexError::UnknownVersion { version: version.into_owned() }),
165 };
166 let Tables { qualifiers, mut entries } = match format {
167 IndexFormat::V1 => v1::read(bytes, reader)?,
168 IndexFormat::V2 => v2::read(bytes, reader)?,
169 };
170 entries.sort_by_key(|e| (e.id, e.qualifiers));
171 let mut by_name: Vec<usize> = (0..entries.len()).collect();
172 by_name.sort_by_key(|&i| (entries[i].kind.code(), entries[i].name, i));
173 Ok(ResourceIndex { version, format, qualifiers, entries, by_name })
174 }
175
176 pub fn version(&self) -> &str {
178 &self.version
179 }
180
181 pub fn format(&self) -> IndexFormat {
182 self.format
183 }
184
185 pub fn qualifiers(&self) -> &[Qualifiers] {
187 &self.qualifiers
188 }
189
190 pub fn len(&self) -> usize {
192 self.entries.len()
193 }
194
195 pub fn is_empty(&self) -> bool {
196 self.entries.is_empty()
197 }
198
199 pub fn iter(&self) -> Resources<'_> {
201 Resources { index: self, order: Order::ById(0..self.entries.len()) }
202 }
203
204 pub fn by_id(&self, id: u32) -> Resources<'_> {
208 let start = self.entries.partition_point(|e| e.id < id);
209 let end = start + self.entries[start..].partition_point(|e| e.id == id);
210 Resources { index: self, order: Order::ById(start..end) }
211 }
212
213 pub fn by_name(&self, kind: ResourceType, name: &str) -> Resources<'_> {
215 let key = (kind.code(), name.as_bytes());
216 let of = |&i: &usize| (self.entries[i].kind.code(), self.entries[i].name);
217 let start = self.by_name.partition_point(|i| of(i) < key);
218 let end = start + self.by_name[start..].partition_point(|i| of(i) == key);
219 Resources { index: self, order: Order::ByName(start..end) }
220 }
221
222 pub fn resolve(&self, reference: ResourceRef<'_>) -> Resources<'_> {
226 let named = self.by_name(reference.kind, reference.name);
227 if named.len() > 0 {
228 return named;
229 }
230 match reference.name.parse() {
231 Ok(id) if self.by_id(id).all(|r| r.kind() == reference.kind) => self.by_id(id),
232 _ => named,
233 }
234 }
235}
236
237impl<'r> IntoIterator for &'r ResourceIndex<'_> {
238 type Item = Resource<'r>;
239 type IntoIter = Resources<'r>;
240
241 fn into_iter(self) -> Resources<'r> {
242 self.iter()
243 }
244}
245
246#[derive(Debug, Clone)]
247enum Order {
248 ById(Range<usize>),
250 ByName(Range<usize>),
252}
253
254#[derive(Debug, Clone)]
256pub struct Resources<'r> {
257 index: &'r ResourceIndex<'r>,
258 order: Order,
259}
260
261impl<'r> Resources<'r> {
262 fn resource(&self, position: usize) -> Resource<'r> {
263 let position = match self.order {
264 Order::ById(_) => position,
265 Order::ByName(_) => self.index.by_name[position],
266 };
267 Resource { index: self.index, entry: &self.index.entries[position] }
268 }
269
270 fn range(&mut self) -> &mut Range<usize> {
271 match &mut self.order {
272 Order::ById(range) | Order::ByName(range) => range,
273 }
274 }
275}
276
277impl<'r> Iterator for Resources<'r> {
278 type Item = Resource<'r>;
279
280 fn next(&mut self) -> Option<Resource<'r>> {
281 let position = self.range().next()?;
282 Some(self.resource(position))
283 }
284
285 fn size_hint(&self) -> (usize, Option<usize>) {
286 match &self.order {
287 Order::ById(range) | Order::ByName(range) => range.size_hint(),
288 }
289 }
290}
291
292impl DoubleEndedIterator for Resources<'_> {
293 fn next_back(&mut self) -> Option<Self::Item> {
294 let position = self.range().next_back()?;
295 Some(self.resource(position))
296 }
297}
298
299impl ExactSizeIterator for Resources<'_> {}
300
301#[derive(Debug, Clone, Copy)]
304pub struct Resource<'r> {
305 index: &'r ResourceIndex<'r>,
306 entry: &'r Entry<'r>,
307}
308
309impl<'r> Resource<'r> {
310 pub fn id(&self) -> u32 {
311 self.entry.id
312 }
313
314 pub fn kind(&self) -> ResourceType {
315 self.entry.kind
316 }
317
318 pub fn name(&self) -> Cow<'r, str> {
321 String::from_utf8_lossy(self.entry.name)
322 }
323
324 pub fn qualifiers(&self) -> &'r Qualifiers {
325 &self.index.qualifiers[self.entry.qualifiers]
326 }
327
328 pub fn raw_value(&self) -> &'r [u8] {
330 self.entry.value
331 }
332
333 pub fn value(&self) -> ResourceValue<'r> {
334 ResourceValue::decode(self.entry.kind, self.entry.value, self.index.format.terminates_values())
335 }
336}
337
338impl fmt::Display for Resource<'_> {
341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342 write!(f, "{:#010x} {}/{} [{}] {}", self.id(), self.kind(), self.name(), self.qualifiers(), self.value())
343 }
344}
345
346#[cfg(test)]
347mod tests;