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")]
40 Truncated {
41 what: &'static str,
43 offset: usize,
45 needed: usize,
47 available: usize,
49 },
50 #[error("{what} offset {offset:#x} is outside the index of {len:#x} bytes")]
52 OutOfBounds {
53 what: &'static str,
55 offset: u32,
57 len: usize,
59 },
60 #[error("expected the tag {expected:?} at {offset:#x}, found {found:?}")]
62 BadTag {
63 expected: &'static str,
65 offset: usize,
67 found: [u8; 4],
69 },
70 #[error("the table lists resource {listed:#010x} at {offset:#x}, where {found:#010x} is stored")]
72 IdMismatch {
73 listed: u32,
75 found: u32,
77 offset: usize,
79 },
80 #[error("resource {id:#010x} has a value for qualifier set {config}, which the index does not define")]
82 UnknownQualifiers {
83 id: u32,
85 config: u32,
87 },
88 #[error("the tables describe more than {limit} values, more than an index of this size holds")]
91 TooManyValues {
92 limit: usize,
94 },
95}
96
97type Result<T> = std::result::Result<T, ResourceIndexError>;
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
101#[non_exhaustive]
102pub enum IndexFormat {
103 V1,
105 V2,
107}
108
109impl IndexFormat {
110 fn terminates_values(self) -> bool {
112 match self {
113 IndexFormat::V1 => true,
114 IndexFormat::V2 => false,
115 }
116 }
117}
118
119#[derive(Debug, Clone, Copy)]
121struct Entry<'a> {
122 id: u32,
123 kind: ResourceType,
124 qualifiers: usize,
126 name: &'a [u8],
127 value: &'a [u8],
128}
129
130struct Entries<'a> {
133 entries: Vec<Entry<'a>>,
134 limit: usize,
135}
136
137impl<'a> Entries<'a> {
138 const MIN_BYTES_PER_VALUE: usize = 8;
140
141 fn for_index(bytes: &[u8]) -> Entries<'a> {
142 Entries { entries: Vec::new(), limit: bytes.len() / Self::MIN_BYTES_PER_VALUE }
143 }
144
145 fn push(&mut self, entry: Entry<'a>) -> Result<()> {
146 if self.entries.len() >= self.limit {
147 return Err(ResourceIndexError::TooManyValues { limit: self.limit });
148 }
149 self.entries.push(entry);
150 Ok(())
151 }
152}
153
154struct Tables<'a> {
156 qualifiers: Vec<Qualifiers>,
157 entries: Vec<Entry<'a>>,
158}
159
160#[derive(Debug, Clone)]
178pub struct ResourceIndex<'a> {
179 version: Cow<'a, str>,
180 format: IndexFormat,
181 qualifiers: Vec<Qualifiers>,
182 entries: Vec<Entry<'a>>,
184 by_name: Vec<usize>,
186}
187
188impl<'a> ResourceIndex<'a> {
189 const VERSION_LEN: usize = 128;
191
192 pub fn parse(bytes: &'a [u8]) -> Result<ResourceIndex<'a>> {
200 let mut reader = Reader::new(bytes);
201 let version = reader.take(Self::VERSION_LEN, "version")?;
202 let version = String::from_utf8_lossy(version.split(|&b| b == 0).next().unwrap_or_default());
203 let format = match version.split(' ').next() {
204 Some("Restool") => IndexFormat::V1,
205 Some("RestoolV2") => IndexFormat::V2,
206 _ => return Err(ResourceIndexError::UnknownVersion { version: version.into_owned() }),
207 };
208 let Tables { qualifiers, mut entries } = match format {
209 IndexFormat::V1 => v1::read(bytes, reader)?,
210 IndexFormat::V2 => v2::read(bytes, reader)?,
211 };
212 entries.sort_by_key(|e| (e.id, e.qualifiers));
213 let mut by_name: Vec<usize> = (0..entries.len()).collect();
214 by_name.sort_by_key(|&i| (entries[i].kind.code(), entries[i].name, i));
215 Ok(ResourceIndex { version, format, qualifiers, entries, by_name })
216 }
217
218 pub fn version(&self) -> &str {
220 &self.version
221 }
222
223 pub fn format(&self) -> IndexFormat {
225 self.format
226 }
227
228 pub fn qualifiers(&self) -> &[Qualifiers] {
230 &self.qualifiers
231 }
232
233 pub fn len(&self) -> usize {
235 self.entries.len()
236 }
237
238 pub fn is_empty(&self) -> bool {
240 self.entries.is_empty()
241 }
242
243 pub fn iter(&self) -> Resources<'_> {
245 Resources { index: self, order: Order::ById(0..self.entries.len()) }
246 }
247
248 pub fn by_id(&self, id: u32) -> Resources<'_> {
252 let start = self.entries.partition_point(|e| e.id < id);
253 let end = start + self.entries[start..].partition_point(|e| e.id == id);
254 Resources { index: self, order: Order::ById(start..end) }
255 }
256
257 pub fn by_name(&self, kind: ResourceType, name: &str) -> Resources<'_> {
259 let key = (kind.code(), name.as_bytes());
260 let of = |&i: &usize| (self.entries[i].kind.code(), self.entries[i].name);
261 let start = self.by_name.partition_point(|i| of(i) < key);
262 let end = start + self.by_name[start..].partition_point(|i| of(i) == key);
263 Resources { index: self, order: Order::ByName(start..end) }
264 }
265
266 pub fn resolve(&self, reference: ResourceRef<'_>) -> Resources<'_> {
270 let named = self.by_name(reference.kind, reference.name);
271 if named.len() > 0 {
272 return named;
273 }
274 match reference.name.parse() {
275 Ok(id) if self.by_id(id).all(|r| r.kind() == reference.kind) => self.by_id(id),
276 _ => named,
277 }
278 }
279}
280
281impl<'r> IntoIterator for &'r ResourceIndex<'_> {
282 type Item = Resource<'r>;
283 type IntoIter = Resources<'r>;
284
285 fn into_iter(self) -> Resources<'r> {
286 self.iter()
287 }
288}
289
290#[derive(Debug, Clone)]
291enum Order {
292 ById(Range<usize>),
294 ByName(Range<usize>),
296}
297
298#[derive(Debug, Clone)]
300pub struct Resources<'r> {
301 index: &'r ResourceIndex<'r>,
302 order: Order,
303}
304
305impl<'r> Resources<'r> {
306 fn resource(&self, position: usize) -> Resource<'r> {
307 let position = match self.order {
308 Order::ById(_) => position,
309 Order::ByName(_) => self.index.by_name[position],
310 };
311 Resource { index: self.index, entry: &self.index.entries[position] }
312 }
313
314 fn range(&mut self) -> &mut Range<usize> {
315 match &mut self.order {
316 Order::ById(range) | Order::ByName(range) => range,
317 }
318 }
319}
320
321impl<'r> Iterator for Resources<'r> {
322 type Item = Resource<'r>;
323
324 fn next(&mut self) -> Option<Resource<'r>> {
325 let position = self.range().next()?;
326 Some(self.resource(position))
327 }
328
329 fn size_hint(&self) -> (usize, Option<usize>) {
330 match &self.order {
331 Order::ById(range) | Order::ByName(range) => range.size_hint(),
332 }
333 }
334}
335
336impl DoubleEndedIterator for Resources<'_> {
337 fn next_back(&mut self) -> Option<Self::Item> {
338 let position = self.range().next_back()?;
339 Some(self.resource(position))
340 }
341}
342
343impl ExactSizeIterator for Resources<'_> {}
344
345#[derive(Debug, Clone, Copy)]
348pub struct Resource<'r> {
349 index: &'r ResourceIndex<'r>,
350 entry: &'r Entry<'r>,
351}
352
353impl<'r> Resource<'r> {
354 pub fn id(&self) -> u32 {
356 self.entry.id
357 }
358
359 pub fn kind(&self) -> ResourceType {
361 self.entry.kind
362 }
363
364 pub fn name(&self) -> Cow<'r, str> {
367 String::from_utf8_lossy(self.entry.name)
368 }
369
370 pub fn qualifiers(&self) -> &'r Qualifiers {
372 &self.index.qualifiers[self.entry.qualifiers]
373 }
374
375 pub fn raw_value(&self) -> &'r [u8] {
377 self.entry.value
378 }
379
380 pub fn value(&self) -> ResourceValue<'r> {
382 ResourceValue::decode(self.entry.kind, self.entry.value, self.index.format.terminates_values())
383 }
384}
385
386impl fmt::Display for Resource<'_> {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 write!(f, "{:#010x} {}/{} [{}] {}", self.id(), self.kind(), self.name(), self.qualifiers(), self.value())
391 }
392}
393
394#[cfg(test)]
395mod tests;