apk/
res.rs

1use anyhow::Result;
2use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
3use std::io::{Read, Seek, SeekFrom, Write};
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6#[repr(u16)]
7pub enum ChunkType {
8    Null = 0x0000,
9    StringPool = 0x0001,
10    Table = 0x0002,
11    Xml = 0x0003,
12    XmlStartNamespace = 0x0100,
13    XmlEndNamespace = 0x0101,
14    XmlStartElement = 0x0102,
15    XmlEndElement = 0x0103,
16    //XmlCdata = 0x0104,
17    //XmlLastChunk = 0x017f,
18    XmlResourceMap = 0x0180,
19    TablePackage = 0x0200,
20    TableType = 0x0201,
21    TableTypeSpec = 0x0202,
22    Unknown = 0x0206,
23}
24
25impl ChunkType {
26    pub fn from_u16(ty: u16) -> Option<Self> {
27        Some(match ty {
28            ty if ty == ChunkType::Null as u16 => ChunkType::Null,
29            ty if ty == ChunkType::StringPool as u16 => ChunkType::StringPool,
30            ty if ty == ChunkType::Table as u16 => ChunkType::Table,
31            ty if ty == ChunkType::Xml as u16 => ChunkType::Xml,
32            ty if ty == ChunkType::XmlStartNamespace as u16 => ChunkType::XmlStartNamespace,
33            ty if ty == ChunkType::XmlEndNamespace as u16 => ChunkType::XmlEndNamespace,
34            ty if ty == ChunkType::XmlStartElement as u16 => ChunkType::XmlStartElement,
35            ty if ty == ChunkType::XmlEndElement as u16 => ChunkType::XmlEndElement,
36            //ty if ty == ChunkType::XmlCdata as u16 => ChunkType::XmlCdata,
37            //ty if ty == ChunkType::XmlLastChunk as u16 => ChunkType::XmlLastChunk,
38            ty if ty == ChunkType::XmlResourceMap as u16 => ChunkType::XmlResourceMap,
39            ty if ty == ChunkType::TablePackage as u16 => ChunkType::TablePackage,
40            ty if ty == ChunkType::TableType as u16 => ChunkType::TableType,
41            ty if ty == ChunkType::TableTypeSpec as u16 => ChunkType::TableTypeSpec,
42            ty if ty == ChunkType::Unknown as u16 => ChunkType::Unknown,
43            _ => return None,
44        })
45    }
46}
47
48#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
49pub struct ResChunkHeader {
50    /// Type identifier for this chunk. The meaning of this value depends
51    /// on the containing chunk.
52    pub ty: u16,
53    /// Size of the chunk header (in bytes). Adding this value to the address
54    /// of the chunk allows you to find its associated data (if any).
55    pub header_size: u16,
56    /// Total size of this chunk (in bytes). This is the header_size plus the
57    /// size of any data associated with the chunk. Adding this value to the
58    /// chunk allows you to completely skip its contents (including any child
59    /// chunks). If this value is the same as header_size, there is no data
60    /// associated with the chunk.
61    pub size: u32,
62}
63
64impl ResChunkHeader {
65    pub fn read(r: &mut impl Read) -> Result<Self> {
66        let ty = r.read_u16::<LittleEndian>()?;
67        let header_size = r.read_u16::<LittleEndian>()?;
68        let size = r.read_u32::<LittleEndian>()?;
69        Ok(Self {
70            ty,
71            header_size,
72            size,
73        })
74    }
75
76    pub fn write(&self, w: &mut impl Write) -> Result<()> {
77        w.write_u16::<LittleEndian>(self.ty)?;
78        w.write_u16::<LittleEndian>(self.header_size)?;
79        w.write_u32::<LittleEndian>(self.size)?;
80        Ok(())
81    }
82}
83
84#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
85pub struct ResStringPoolHeader {
86    pub string_count: u32,
87    pub style_count: u32,
88    pub flags: u32,
89    pub strings_start: u32,
90    pub styles_start: u32,
91}
92
93impl ResStringPoolHeader {
94    pub const SORTED_FLAG: u32 = 1 << 0;
95    pub const UTF8_FLAG: u32 = 1 << 8;
96
97    pub fn read(r: &mut impl Read) -> Result<Self> {
98        let string_count = r.read_u32::<LittleEndian>()?;
99        let style_count = r.read_u32::<LittleEndian>()?;
100        let flags = r.read_u32::<LittleEndian>()?;
101        let strings_start = r.read_u32::<LittleEndian>()?;
102        let styles_start = r.read_u32::<LittleEndian>()?;
103        Ok(Self {
104            string_count,
105            style_count,
106            flags,
107            strings_start,
108            styles_start,
109        })
110    }
111
112    pub fn write(&self, w: &mut impl Write) -> Result<()> {
113        w.write_u32::<LittleEndian>(self.string_count)?;
114        w.write_u32::<LittleEndian>(self.style_count)?;
115        w.write_u32::<LittleEndian>(self.flags)?;
116        w.write_u32::<LittleEndian>(self.strings_start)?;
117        w.write_u32::<LittleEndian>(self.styles_start)?;
118        Ok(())
119    }
120
121    pub fn is_utf8(&self) -> bool {
122        self.flags & Self::UTF8_FLAG > 0
123    }
124}
125
126#[derive(Clone, Copy, Debug, Eq, PartialEq)]
127pub struct ResTableHeader {
128    pub package_count: u32,
129}
130
131impl ResTableHeader {
132    pub fn read(r: &mut impl Read) -> Result<Self> {
133        let package_count = r.read_u32::<LittleEndian>()?;
134        Ok(Self { package_count })
135    }
136
137    pub fn write(&self, w: &mut impl Write) -> Result<()> {
138        w.write_u32::<LittleEndian>(self.package_count)?;
139        Ok(())
140    }
141}
142
143#[derive(Clone, Copy, Debug, Eq, PartialEq)]
144pub struct ResXmlNodeHeader {
145    pub line_number: u32,
146    pub comment: i32,
147}
148
149impl ResXmlNodeHeader {
150    pub fn read(r: &mut impl Read) -> Result<Self> {
151        let _line_number = r.read_u32::<LittleEndian>()?;
152        let _comment = r.read_i32::<LittleEndian>()?;
153        Ok(Self {
154            line_number: 1,
155            comment: -1,
156        })
157    }
158
159    pub fn write(&self, w: &mut impl Write) -> Result<()> {
160        w.write_u32::<LittleEndian>(self.line_number)?;
161        w.write_i32::<LittleEndian>(self.comment)?;
162        Ok(())
163    }
164}
165
166impl Default for ResXmlNodeHeader {
167    fn default() -> Self {
168        Self {
169            line_number: 1,
170            comment: -1,
171        }
172    }
173}
174
175#[derive(Clone, Copy, Debug, Eq, PartialEq)]
176pub struct ResXmlNamespace {
177    pub prefix: i32,
178    pub uri: i32,
179}
180
181impl ResXmlNamespace {
182    pub fn read(r: &mut impl Read) -> Result<Self> {
183        let prefix = r.read_i32::<LittleEndian>()?;
184        let uri = r.read_i32::<LittleEndian>()?;
185        Ok(Self { prefix, uri })
186    }
187
188    pub fn write(&self, w: &mut impl Write) -> Result<()> {
189        w.write_i32::<LittleEndian>(self.prefix)?;
190        w.write_i32::<LittleEndian>(self.uri)?;
191        Ok(())
192    }
193}
194
195#[derive(Clone, Copy, Debug, Eq, PartialEq)]
196pub struct ResXmlStartElement {
197    /// String of the full namespace of this element.
198    pub namespace: i32,
199    /// String name of this node if it is an ELEMENT; the raw
200    /// character data if this is a CDATA node.
201    pub name: i32,
202    /// Byte offset from the start of this structure to where
203    /// the attributes start.
204    pub attribute_start: u16,
205    /// Size of the attribute structures that follow.
206    pub attribute_size: u16,
207    /// Number of attributes associated with an ELEMENT. These are
208    /// available as an array of ResXmlAttribute structures
209    /// immediately following this node.
210    pub attribute_count: u16,
211    /// Index (1-based) of the "id" attribute. 0 if none.
212    pub id_index: u16,
213    /// Index (1-based) of the "class" attribute. 0 if none.
214    pub class_index: u16,
215    /// Index (1-based) of the "style" attribute. 0 if none.
216    pub style_index: u16,
217}
218
219impl Default for ResXmlStartElement {
220    fn default() -> Self {
221        Self {
222            namespace: -1,
223            name: -1,
224            attribute_start: 0x0014,
225            attribute_size: 0x0014,
226            attribute_count: 0,
227            id_index: 0,
228            class_index: 0,
229            style_index: 0,
230        }
231    }
232}
233
234impl ResXmlStartElement {
235    pub fn read(r: &mut impl Read) -> Result<Self> {
236        let namespace = r.read_i32::<LittleEndian>()?;
237        let name = r.read_i32::<LittleEndian>()?;
238        let attribute_start = r.read_u16::<LittleEndian>()?;
239        let attribute_size = r.read_u16::<LittleEndian>()?;
240        let attribute_count = r.read_u16::<LittleEndian>()?;
241        let id_index = r.read_u16::<LittleEndian>()?;
242        let class_index = r.read_u16::<LittleEndian>()?;
243        let style_index = r.read_u16::<LittleEndian>()?;
244        Ok(Self {
245            namespace,
246            name,
247            attribute_start,
248            attribute_size,
249            attribute_count,
250            id_index,
251            class_index,
252            style_index,
253        })
254    }
255
256    pub fn write(&self, w: &mut impl Write) -> Result<()> {
257        w.write_i32::<LittleEndian>(self.namespace)?;
258        w.write_i32::<LittleEndian>(self.name)?;
259        w.write_u16::<LittleEndian>(self.attribute_start)?;
260        w.write_u16::<LittleEndian>(self.attribute_size)?;
261        w.write_u16::<LittleEndian>(self.attribute_count)?;
262        w.write_u16::<LittleEndian>(self.id_index)?;
263        w.write_u16::<LittleEndian>(self.class_index)?;
264        w.write_u16::<LittleEndian>(self.style_index)?;
265        Ok(())
266    }
267}
268
269#[derive(Clone, Copy, Debug, Eq, PartialEq)]
270pub struct ResXmlAttribute {
271    pub namespace: i32,
272    pub name: i32,
273    pub raw_value: i32,
274    pub typed_value: ResValue,
275}
276
277impl ResXmlAttribute {
278    pub fn read(r: &mut impl Read) -> Result<Self> {
279        let namespace = r.read_i32::<LittleEndian>()?;
280        let name = r.read_i32::<LittleEndian>()?;
281        let raw_value = r.read_i32::<LittleEndian>()?;
282        let typed_value = ResValue::read(r)?;
283        Ok(Self {
284            namespace,
285            name,
286            raw_value,
287            typed_value,
288        })
289    }
290
291    pub fn write(&self, w: &mut impl Write) -> Result<()> {
292        w.write_i32::<LittleEndian>(self.namespace)?;
293        w.write_i32::<LittleEndian>(self.name)?;
294        w.write_i32::<LittleEndian>(self.raw_value)?;
295        self.typed_value.write(w)?;
296        Ok(())
297    }
298}
299
300#[derive(Clone, Copy, Debug, Eq, PartialEq)]
301pub struct ResXmlEndElement {
302    pub namespace: i32,
303    pub name: i32,
304}
305
306impl ResXmlEndElement {
307    pub fn read(r: &mut impl Read) -> Result<Self> {
308        let namespace = r.read_i32::<LittleEndian>()?;
309        let name = r.read_i32::<LittleEndian>()?;
310        Ok(Self { namespace, name })
311    }
312
313    pub fn write(&self, w: &mut impl Write) -> Result<()> {
314        w.write_i32::<LittleEndian>(self.namespace)?;
315        w.write_i32::<LittleEndian>(self.name)?;
316        Ok(())
317    }
318}
319
320#[derive(Clone, Copy, Debug, Eq, PartialEq)]
321pub struct ResTableRef(u32);
322
323impl ResTableRef {
324    pub fn new(package: u8, ty: u8, entry: u16) -> Self {
325        let package = (package as u32) << 24;
326        let ty = (ty as u32) << 16;
327        let entry = entry as u32;
328        Self(package | ty | entry)
329    }
330
331    pub fn package(self) -> u8 {
332        (self.0 >> 24) as u8
333    }
334
335    pub fn ty(self) -> u8 {
336        (self.0 >> 16) as u8
337    }
338
339    pub fn entry(self) -> u16 {
340        self.0 as u16
341    }
342}
343
344impl From<u32> for ResTableRef {
345    fn from(r: u32) -> Self {
346        Self(r)
347    }
348}
349
350impl From<ResTableRef> for u32 {
351    fn from(r: ResTableRef) -> u32 {
352        r.0
353    }
354}
355
356impl std::fmt::Display for ResTableRef {
357    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
358        write!(f, "{}", self.0)
359    }
360}
361
362#[derive(Clone, Debug, Eq, PartialEq)]
363pub struct ResTablePackageHeader {
364    /// If this is a base package, its ID. Package IDs start
365    /// at 1 (corresponding to the value of the package bits in a
366    /// resource identifier). 0 means this is not a base package.
367    pub id: u32,
368    /// Actual name of this package, \0-terminated.
369    pub name: String,
370    /// Offset to a ResStringPoolHeader defining the resource
371    /// type symbol table. If zero, this package is inheriting
372    /// from another base package (overriding specific values in it).
373    pub type_strings: u32,
374    /// Last index into type_strings that is for public use by others.
375    pub last_public_type: u32,
376    /// Offset to a ResStringPoolHeader defining the resource key
377    /// symbol table. If zero, this package is inheriting from another
378    /// base package (overriding specific values in it).
379    pub key_strings: u32,
380    /// Last index into key_strings that is for public use by others.
381    pub last_public_key: u32,
382    pub type_id_offset: u32,
383}
384
385impl ResTablePackageHeader {
386    pub fn read<R: Read + Seek>(r: &mut R) -> Result<Self> {
387        let id = r.read_u32::<LittleEndian>()?;
388        let mut name = [0; 128];
389        let mut name_len = 0xff;
390        for (i, item) in name.iter_mut().enumerate() {
391            let c = r.read_u16::<LittleEndian>()?;
392            if name_len < 128 {
393                continue;
394            }
395            if c == 0 {
396                name_len = i;
397            } else {
398                *item = c;
399            }
400        }
401        let name = String::from_utf16(&name[..name_len])?;
402        let type_strings = r.read_u32::<LittleEndian>()?;
403        let last_public_type = r.read_u32::<LittleEndian>()?;
404        let key_strings = r.read_u32::<LittleEndian>()?;
405        let last_public_key = r.read_u32::<LittleEndian>()?;
406        let type_id_offset = r.read_u32::<LittleEndian>()?;
407        Ok(Self {
408            id,
409            name,
410            type_strings,
411            last_public_type,
412            key_strings,
413            last_public_key,
414            type_id_offset,
415        })
416    }
417
418    pub fn write(&self, w: &mut impl Write) -> Result<()> {
419        w.write_u32::<LittleEndian>(self.id)?;
420        let mut name = [0; 128];
421        for (i, c) in self.name.encode_utf16().enumerate() {
422            name[i] = c;
423        }
424        for c in name {
425            w.write_u16::<LittleEndian>(c)?;
426        }
427        w.write_u32::<LittleEndian>(self.type_strings)?;
428        w.write_u32::<LittleEndian>(self.last_public_type)?;
429        w.write_u32::<LittleEndian>(self.key_strings)?;
430        w.write_u32::<LittleEndian>(self.last_public_key)?;
431        w.write_u32::<LittleEndian>(self.type_id_offset)?;
432        Ok(())
433    }
434}
435
436#[derive(Clone, Copy, Debug, Eq, PartialEq)]
437pub struct ResTableTypeSpecHeader {
438    /// The type identifier this chunk is holding. Type IDs start
439    /// at 1 (corresponding to the value of the type bits in a
440    /// resource identifier). 0 is invalid.
441    pub id: u8,
442    /// Must be 0.
443    pub res0: u8,
444    /// Must be 0.
445    pub res1: u16,
446    /// Number of u32 entry configuration masks that follow.
447    pub entry_count: u32,
448}
449
450impl ResTableTypeSpecHeader {
451    pub fn read(r: &mut impl Read) -> Result<Self> {
452        let id = r.read_u8()?;
453        let res0 = r.read_u8()?;
454        let res1 = r.read_u16::<LittleEndian>()?;
455        let entry_count = r.read_u32::<LittleEndian>()?;
456        Ok(Self {
457            id,
458            res0,
459            res1,
460            entry_count,
461        })
462    }
463
464    pub fn write(&self, w: &mut impl Write) -> Result<()> {
465        w.write_u8(self.id)?;
466        w.write_u8(self.res0)?;
467        w.write_u16::<LittleEndian>(self.res1)?;
468        w.write_u32::<LittleEndian>(self.entry_count)?;
469        Ok(())
470    }
471}
472
473#[derive(Clone, Debug, Eq, PartialEq)]
474pub struct ResTableTypeHeader {
475    /// The type identifier this chunk is holding. Type IDs start
476    /// at 1 (corresponding to the value of the type bits in a
477    /// resource identifier). 0 is invalid.
478    pub id: u8,
479    /// Must be 0.
480    pub res0: u8,
481    /// Must be 0.
482    pub res1: u16,
483    /// Number of u32 entry indices that follow.
484    pub entry_count: u32,
485    /// Offset from header where ResTableEntry data starts.
486    pub entries_start: u32,
487    /// Configuration this collection of entries is designed for.
488    pub config: ResTableConfig,
489}
490
491impl ResTableTypeHeader {
492    pub fn read(r: &mut impl Read) -> Result<Self> {
493        let id = r.read_u8()?;
494        let res0 = r.read_u8()?;
495        let res1 = r.read_u16::<LittleEndian>()?;
496        let entry_count = r.read_u32::<LittleEndian>()?;
497        let entries_start = r.read_u32::<LittleEndian>()?;
498        let config = ResTableConfig::read(r)?;
499        Ok(Self {
500            id,
501            res0,
502            res1,
503            entry_count,
504            entries_start,
505            config,
506        })
507    }
508
509    pub fn write(&self, w: &mut impl Write) -> Result<()> {
510        w.write_u8(self.id)?;
511        w.write_u8(self.res0)?;
512        w.write_u16::<LittleEndian>(self.res1)?;
513        w.write_u32::<LittleEndian>(self.entry_count)?;
514        w.write_u32::<LittleEndian>(self.entries_start)?;
515        self.config.write(w)?;
516        Ok(())
517    }
518}
519
520#[derive(Clone, Debug, Eq, PartialEq)]
521pub struct ResTableConfig {
522    pub size: u32,
523    pub imsi: u32,
524    pub locale: u32,
525    pub screen_type: ScreenType,
526    pub input: u32,
527    pub screen_size: u32,
528    pub version: u32,
529    pub unknown: Vec<u8>,
530}
531
532impl ResTableConfig {
533    pub fn read(r: &mut impl Read) -> Result<Self> {
534        let size = r.read_u32::<LittleEndian>()?;
535        let imsi = r.read_u32::<LittleEndian>()?;
536        let locale = r.read_u32::<LittleEndian>()?;
537        let screen_type = ScreenType::read(r)?;
538        let input = r.read_u32::<LittleEndian>()?;
539        let screen_size = r.read_u32::<LittleEndian>()?;
540        let version = r.read_u32::<LittleEndian>()?;
541        let unknown_len = size as usize - 28;
542        let mut unknown = vec![0; unknown_len];
543        r.read_exact(&mut unknown)?;
544        Ok(Self {
545            size,
546            imsi,
547            locale,
548            screen_type,
549            input,
550            screen_size,
551            version,
552            unknown,
553        })
554    }
555
556    pub fn write(&self, w: &mut impl Write) -> Result<()> {
557        w.write_u32::<LittleEndian>(self.size)?;
558        w.write_u32::<LittleEndian>(self.imsi)?;
559        w.write_u32::<LittleEndian>(self.locale)?;
560        self.screen_type.write(w)?;
561        w.write_u32::<LittleEndian>(self.input)?;
562        w.write_u32::<LittleEndian>(self.screen_size)?;
563        w.write_u32::<LittleEndian>(self.version)?;
564        w.write_all(&self.unknown)?;
565        Ok(())
566    }
567}
568
569#[derive(Clone, Copy, Debug, Eq, PartialEq)]
570pub struct ScreenType {
571    pub orientation: u8,
572    pub touchscreen: u8,
573    pub density: u16,
574}
575
576impl ScreenType {
577    pub fn read(r: &mut impl Read) -> Result<Self> {
578        let orientation = r.read_u8()?;
579        let touchscreen = r.read_u8()?;
580        let density = r.read_u16::<LittleEndian>()?;
581        Ok(Self {
582            orientation,
583            touchscreen,
584            density,
585        })
586    }
587
588    pub fn write(&self, w: &mut impl Write) -> Result<()> {
589        w.write_u8(self.orientation)?;
590        w.write_u8(self.touchscreen)?;
591        w.write_u16::<LittleEndian>(self.density)?;
592        Ok(())
593    }
594}
595
596#[derive(Clone, Debug, Eq, PartialEq)]
597pub struct ResTableEntry {
598    pub size: u16,
599    pub flags: u16,
600    pub key: u32,
601    pub value: ResTableValue,
602}
603
604impl ResTableEntry {
605    pub fn is_complex(&self) -> bool {
606        self.flags & 0x1 > 0
607    }
608
609    pub fn is_public(&self) -> bool {
610        self.flags & 0x2 > 0
611    }
612
613    pub fn read(r: &mut impl Read) -> Result<Self> {
614        let size = r.read_u16::<LittleEndian>()?;
615        let flags = r.read_u16::<LittleEndian>()?;
616        let key = r.read_u32::<LittleEndian>()?;
617        let is_complex = flags & 0x1 > 0;
618        if is_complex {
619            debug_assert_eq!(size, 16);
620        } else {
621            debug_assert_eq!(size, 8);
622        }
623        let value = ResTableValue::read(r, is_complex)?;
624        Ok(Self {
625            size,
626            flags,
627            key,
628            value,
629        })
630    }
631
632    pub fn write(&self, w: &mut impl Write) -> Result<()> {
633        w.write_u16::<LittleEndian>(self.size)?;
634        w.write_u16::<LittleEndian>(self.flags)?;
635        w.write_u32::<LittleEndian>(self.key)?;
636        self.value.write(w)?;
637        Ok(())
638    }
639}
640
641#[derive(Clone, Debug, Eq, PartialEq)]
642pub enum ResTableValue {
643    Simple(ResValue),
644    Complex(ResTableMapEntry, Vec<ResTableMap>),
645}
646
647impl ResTableValue {
648    pub fn read(r: &mut impl Read, is_complex: bool) -> Result<Self> {
649        let res = if is_complex {
650            let entry = ResTableMapEntry::read(r)?;
651            let mut map = Vec::with_capacity(entry.count as usize);
652            for _ in 0..entry.count {
653                map.push(ResTableMap::read(r)?);
654            }
655            Self::Complex(entry, map)
656        } else {
657            Self::Simple(ResValue::read(r)?)
658        };
659        Ok(res)
660    }
661
662    pub fn write(&self, w: &mut impl Write) -> Result<()> {
663        match self {
664            Self::Simple(value) => value.write(w)?,
665            Self::Complex(entry, map) => {
666                entry.write(w)?;
667                for entry in map {
668                    entry.write(w)?;
669                }
670            }
671        }
672        Ok(())
673    }
674}
675
676#[derive(Clone, Copy, Debug, Eq, PartialEq)]
677pub struct ResValue {
678    pub size: u16,
679    pub res0: u8,
680    pub data_type: u8,
681    pub data: u32,
682}
683
684impl ResValue {
685    pub fn read(r: &mut impl Read) -> Result<Self> {
686        let size = r.read_u16::<LittleEndian>()?;
687        debug_assert_eq!(size, 8);
688        let res0 = r.read_u8()?;
689        let data_type = r.read_u8()?;
690        let data = r.read_u32::<LittleEndian>()?;
691        Ok(Self {
692            size,
693            res0,
694            data_type,
695            data,
696        })
697    }
698
699    pub fn write(&self, w: &mut impl Write) -> Result<()> {
700        w.write_u16::<LittleEndian>(self.size)?;
701        w.write_u8(self.res0)?;
702        w.write_u8(self.data_type)?;
703        w.write_u32::<LittleEndian>(self.data)?;
704        Ok(())
705    }
706}
707
708#[derive(Clone, Copy, Debug, Eq, PartialEq)]
709#[repr(u8)]
710pub enum ResValueType {
711    Null = 0x00,
712    Reference = 0x01,
713    Attribute = 0x02,
714    String = 0x03,
715    Float = 0x04,
716    Dimension = 0x05,
717    Fraction = 0x06,
718    IntDec = 0x10,
719    IntHex = 0x11,
720    IntBoolean = 0x12,
721    IntColorArgb8 = 0x1c,
722    IntColorRgb8 = 0x1d,
723    IntColorArgb4 = 0x1e,
724    IntColorRgb4 = 0x1f,
725}
726
727impl ResValueType {
728    pub fn from_u8(ty: u8) -> Option<Self> {
729        Some(match ty {
730            x if x == Self::Null as u8 => Self::Null,
731            x if x == Self::Reference as u8 => Self::Reference,
732            x if x == Self::Attribute as u8 => Self::Attribute,
733            x if x == Self::String as u8 => Self::String,
734            x if x == Self::Float as u8 => Self::Float,
735            x if x == Self::Dimension as u8 => Self::Dimension,
736            x if x == Self::Fraction as u8 => Self::Fraction,
737            x if x == Self::IntDec as u8 => Self::IntDec,
738            x if x == Self::IntHex as u8 => Self::IntHex,
739            x if x == Self::IntBoolean as u8 => Self::IntBoolean,
740            x if x == Self::IntColorArgb8 as u8 => Self::IntColorArgb8,
741            x if x == Self::IntColorRgb8 as u8 => Self::IntColorRgb8,
742            x if x == Self::IntColorArgb4 as u8 => Self::IntColorArgb4,
743            x if x == Self::IntColorRgb4 as u8 => Self::IntColorRgb4,
744            _ => return None,
745        })
746    }
747}
748
749#[derive(Clone, Copy, Debug, Eq, PartialEq)]
750#[repr(u32)]
751pub enum ResAttributeType {
752    Any = 0x0000_ffff,
753    Reference = 1 << 0,
754    String = 1 << 1,
755    Integer = 1 << 2,
756    Boolean = 1 << 3,
757    Color = 1 << 4,
758    Float = 1 << 5,
759    Dimension = 1 << 6,
760    Fraction = 1 << 7,
761    Enum = 1 << 16,
762    Flags = 1 << 17,
763}
764
765impl ResAttributeType {
766    pub fn from_u32(ty: u32) -> Option<Self> {
767        Some(match ty {
768            x if x == Self::Any as u32 => Self::Any,
769            x if x == Self::Reference as u32 => Self::Reference,
770            x if x == Self::String as u32 => Self::String,
771            x if x == Self::Integer as u32 => Self::Integer,
772            x if x == Self::Boolean as u32 => Self::Boolean,
773            x if x == Self::Color as u32 => Self::Color,
774            x if x == Self::Float as u32 => Self::Float,
775            x if x == Self::Dimension as u32 => Self::Dimension,
776            x if x == Self::Fraction as u32 => Self::Fraction,
777            x if x == Self::Enum as u32 => Self::Enum,
778            x if x == Self::Flags as u32 => Self::Flags,
779            _ => return None,
780        })
781    }
782}
783
784#[derive(Clone, Copy, Debug, Eq, PartialEq)]
785pub struct ResTableMapEntry {
786    pub parent: u32,
787    pub count: u32,
788}
789
790impl ResTableMapEntry {
791    pub fn read(r: &mut impl Read) -> Result<Self> {
792        let parent = r.read_u32::<LittleEndian>()?;
793        let count = r.read_u32::<LittleEndian>()?;
794        Ok(Self { parent, count })
795    }
796
797    pub fn write(&self, w: &mut impl Write) -> Result<()> {
798        w.write_u32::<LittleEndian>(self.parent)?;
799        w.write_u32::<LittleEndian>(self.count)?;
800        Ok(())
801    }
802}
803
804#[derive(Clone, Copy, Debug, Eq, PartialEq)]
805pub struct ResTableMap {
806    pub name: u32,
807    pub value: ResValue,
808}
809
810impl ResTableMap {
811    pub fn read(r: &mut impl Read) -> Result<Self> {
812        let name = r.read_u32::<LittleEndian>()?;
813        let value = ResValue::read(r)?;
814        Ok(Self { name, value })
815    }
816
817    pub fn write(&self, w: &mut impl Write) -> Result<()> {
818        w.write_u32::<LittleEndian>(self.name)?;
819        self.value.write(w)?;
820        Ok(())
821    }
822}
823
824#[derive(Clone, Copy, Debug, Eq, PartialEq)]
825pub struct ResSpan {
826    pub name: i32,
827    pub first_char: u32,
828    pub last_char: u32,
829}
830
831impl ResSpan {
832    pub fn read(r: &mut impl Read) -> Result<Option<Self>> {
833        let name = r.read_i32::<LittleEndian>()?;
834        if name == -1 {
835            return Ok(None);
836        }
837        let first_char = r.read_u32::<LittleEndian>()?;
838        let last_char = r.read_u32::<LittleEndian>()?;
839        Ok(Some(Self {
840            name,
841            first_char,
842            last_char,
843        }))
844    }
845
846    pub fn write(&self, w: &mut impl Write) -> Result<()> {
847        w.write_i32::<LittleEndian>(self.name)?;
848        w.write_u32::<LittleEndian>(self.first_char)?;
849        w.write_u32::<LittleEndian>(self.last_char)?;
850        Ok(())
851    }
852}
853
854#[derive(Clone, Debug, Eq, PartialEq)]
855pub enum Chunk {
856    Null,
857    StringPool(Vec<String>, Vec<Vec<ResSpan>>),
858    Table(ResTableHeader, Vec<Chunk>),
859    Xml(Vec<Chunk>),
860    XmlStartNamespace(ResXmlNodeHeader, ResXmlNamespace),
861    XmlEndNamespace(ResXmlNodeHeader, ResXmlNamespace),
862    XmlStartElement(ResXmlNodeHeader, ResXmlStartElement, Vec<ResXmlAttribute>),
863    XmlEndElement(ResXmlNodeHeader, ResXmlEndElement),
864    XmlResourceMap(Vec<u32>),
865    TablePackage(ResTablePackageHeader, Vec<Chunk>),
866    TableType(ResTableTypeHeader, Vec<u32>, Vec<Option<ResTableEntry>>),
867    TableTypeSpec(ResTableTypeSpecHeader, Vec<u32>),
868    Unknown,
869}
870
871impl Chunk {
872    pub fn parse<R: Read + Seek>(r: &mut R) -> Result<Self> {
873        let start_pos = r.seek(SeekFrom::Current(0))?;
874        let header = ResChunkHeader::read(r)?;
875        let end_pos = start_pos + header.size as u64;
876        match ChunkType::from_u16(header.ty) {
877            Some(ChunkType::Null) => {
878                tracing::trace!("null");
879                Ok(Chunk::Null)
880            }
881            Some(ChunkType::StringPool) => {
882                tracing::trace!("string pool");
883                let string_pool_header = ResStringPoolHeader::read(r)?;
884                let count =
885                    string_pool_header.string_count as i64 + string_pool_header.style_count as i64;
886                r.seek(SeekFrom::Current(count * 4))?;
887                /*let mut string_indices = Vec::with_capacity(string_pool_header.string_count);
888                for _ in 0..string_pool_header.string_count {
889                    string_indices.push(r.read_u32::<LittleEndian>()?);
890                }
891                let mut style_indices = Vec::with_capacity(string_pool_header.style_count);
892                for _ in 0..string_pool_header.style_count {
893                    style_indices.push(r.read_u32::<LittleEndian>()?);
894                }*/
895                let mut strings = Vec::with_capacity(string_pool_header.string_count as usize);
896                for _ in 0..string_pool_header.string_count {
897                    if string_pool_header.is_utf8() {
898                        let charsh = r.read_u8()? as u16;
899                        let _chars = if charsh > 0x7f {
900                            charsh & 0x7f | r.read_u8()? as u16
901                        } else {
902                            charsh
903                        };
904                        let bytesh = r.read_u8()? as u16;
905                        let bytes = if bytesh > 0x7f {
906                            bytesh & 0x7f | r.read_u8()? as u16
907                        } else {
908                            bytesh
909                        };
910                        let mut buf = vec![0; bytes as usize];
911                        r.read_exact(&mut buf)?;
912                        // some times there is an invalid string?
913                        let s = String::from_utf8(buf).unwrap_or_default();
914                        strings.push(s);
915                        if r.read_u8()? != 0 {
916                            // fails to read some files otherwise
917                            r.seek(SeekFrom::Start(end_pos))?;
918                        }
919                    } else {
920                        let charsh = r.read_u16::<LittleEndian>()? as u32;
921                        let chars = if charsh > 0x7fff {
922                            charsh & 0x7fff | r.read_u16::<LittleEndian>()? as u32
923                        } else {
924                            charsh
925                        };
926                        let mut buf = Vec::with_capacity(chars as usize * 2);
927                        loop {
928                            let code = r.read_u16::<LittleEndian>()?;
929                            if code != 0 {
930                                buf.push(code);
931                            } else {
932                                break;
933                            }
934                        }
935                        let s = String::from_utf16(unsafe { std::mem::transmute(buf.as_slice()) })?;
936                        strings.push(s);
937                    }
938                }
939                let pos = r.seek(SeekFrom::Current(0))? as i64;
940                if pos % 4 != 0 {
941                    r.seek(SeekFrom::Current(4 - pos % 4))?;
942                }
943                let mut styles = Vec::with_capacity(string_pool_header.style_count as usize);
944                for _ in 0..string_pool_header.style_count {
945                    let mut spans = vec![];
946                    while let Some(span) = ResSpan::read(r)? {
947                        spans.push(span);
948                    }
949                    styles.push(spans);
950                }
951                // FIXME: skip some unparsable parts
952                r.seek(SeekFrom::Start(end_pos))?;
953                Ok(Chunk::StringPool(strings, styles))
954            }
955            Some(ChunkType::Table) => {
956                tracing::trace!("table");
957                let table_header = ResTableHeader::read(r)?;
958                let mut chunks = vec![];
959                while r.seek(SeekFrom::Current(0))? < end_pos {
960                    chunks.push(Chunk::parse(r)?);
961                }
962                Ok(Chunk::Table(table_header, chunks))
963            }
964            Some(ChunkType::Xml) => {
965                tracing::trace!("xml");
966                let mut chunks = vec![];
967                while r.seek(SeekFrom::Current(0))? < end_pos {
968                    chunks.push(Chunk::parse(r)?);
969                }
970                Ok(Chunk::Xml(chunks))
971            }
972            Some(ChunkType::XmlStartNamespace) => {
973                tracing::trace!("xml start namespace");
974                let node_header = ResXmlNodeHeader::read(r)?;
975                let namespace = ResXmlNamespace::read(r)?;
976                Ok(Chunk::XmlStartNamespace(node_header, namespace))
977            }
978            Some(ChunkType::XmlEndNamespace) => {
979                tracing::trace!("xml end namespace");
980                let node_header = ResXmlNodeHeader::read(r)?;
981                let namespace = ResXmlNamespace::read(r)?;
982                Ok(Chunk::XmlEndNamespace(node_header, namespace))
983            }
984            Some(ChunkType::XmlStartElement) => {
985                tracing::trace!("xml start element");
986                let node_header = ResXmlNodeHeader::read(r)?;
987                let start_element = ResXmlStartElement::read(r)?;
988                let mut attributes = Vec::with_capacity(start_element.attribute_count as usize);
989                for _ in 0..start_element.attribute_count {
990                    attributes.push(ResXmlAttribute::read(r)?);
991                }
992                Ok(Chunk::XmlStartElement(
993                    node_header,
994                    start_element,
995                    attributes,
996                ))
997            }
998            Some(ChunkType::XmlEndElement) => {
999                tracing::trace!("xml end element");
1000                let node_header = ResXmlNodeHeader::read(r)?;
1001                let end_element = ResXmlEndElement::read(r)?;
1002                Ok(Chunk::XmlEndElement(node_header, end_element))
1003            }
1004            Some(ChunkType::XmlResourceMap) => {
1005                tracing::trace!("xml resource map");
1006                let mut resource_map =
1007                    Vec::with_capacity((header.size as usize - header.header_size as usize) / 4);
1008                for _ in 0..resource_map.capacity() {
1009                    resource_map.push(r.read_u32::<LittleEndian>()?);
1010                }
1011                Ok(Chunk::XmlResourceMap(resource_map))
1012            }
1013            Some(ChunkType::TablePackage) => {
1014                tracing::trace!("table package");
1015                let package_header = ResTablePackageHeader::read(r)?;
1016                let mut chunks = vec![];
1017                while r.seek(SeekFrom::Current(0))? < end_pos {
1018                    chunks.push(Chunk::parse(r)?);
1019                }
1020                Ok(Chunk::TablePackage(package_header, chunks))
1021            }
1022            Some(ChunkType::TableType) => {
1023                tracing::trace!("table type");
1024                let type_header = ResTableTypeHeader::read(r)?;
1025                let mut index = Vec::with_capacity(type_header.entry_count as usize);
1026                for _ in 0..type_header.entry_count {
1027                    let entry = r.read_u32::<LittleEndian>()?;
1028                    index.push(entry);
1029                }
1030                let mut entries = Vec::with_capacity(type_header.entry_count as usize);
1031                for offset in &index {
1032                    if *offset == 0xffff_ffff {
1033                        entries.push(None);
1034                    } else {
1035                        let entry = ResTableEntry::read(r)?;
1036                        entries.push(Some(entry));
1037                    }
1038                }
1039                Ok(Chunk::TableType(type_header, index, entries))
1040            }
1041            Some(ChunkType::TableTypeSpec) => {
1042                tracing::trace!("table type spec");
1043                let type_spec_header = ResTableTypeSpecHeader::read(r)?;
1044                let mut type_spec = vec![0; type_spec_header.entry_count as usize];
1045                for c in type_spec.iter_mut() {
1046                    *c = r.read_u32::<LittleEndian>()?;
1047                }
1048                Ok(Chunk::TableTypeSpec(type_spec_header, type_spec))
1049            }
1050            Some(ChunkType::Unknown) => {
1051                tracing::trace!("unknown");
1052                // FIXME: skip some unparsable parts
1053                r.seek(SeekFrom::Start(end_pos))?;
1054                Ok(Chunk::Unknown)
1055            }
1056            None => {
1057                anyhow::bail!("unrecognized chunk {:?}", header);
1058            }
1059        }
1060    }
1061
1062    pub fn write<W: Seek + Write>(&self, w: &mut W) -> Result<()> {
1063        struct ChunkWriter {
1064            ty: ChunkType,
1065            start_chunk: u64,
1066            end_header: u64,
1067        }
1068        impl ChunkWriter {
1069            fn start_chunk<W: Seek + Write>(ty: ChunkType, w: &mut W) -> Result<Self> {
1070                let start_chunk = w.seek(SeekFrom::Current(0))?;
1071                ResChunkHeader::default().write(w)?;
1072                Ok(Self {
1073                    ty,
1074                    start_chunk,
1075                    end_header: 0,
1076                })
1077            }
1078
1079            fn end_header<W: Seek + Write>(&mut self, w: &mut W) -> Result<()> {
1080                self.end_header = w.seek(SeekFrom::Current(0))?;
1081                Ok(())
1082            }
1083
1084            fn end_chunk<W: Seek + Write>(self, w: &mut W) -> Result<(u64, u64)> {
1085                assert_ne!(self.end_header, 0);
1086                let end_chunk = w.seek(SeekFrom::Current(0))?;
1087                let header = ResChunkHeader {
1088                    ty: self.ty as u16,
1089                    header_size: (self.end_header - self.start_chunk) as u16,
1090                    size: (end_chunk - self.start_chunk) as u32,
1091                };
1092                w.seek(SeekFrom::Start(self.start_chunk))?;
1093                header.write(w)?;
1094                w.seek(SeekFrom::Start(end_chunk))?;
1095                Ok((self.start_chunk, end_chunk))
1096            }
1097        }
1098        match self {
1099            Chunk::Null => {}
1100            Chunk::StringPool(strings, styles) => {
1101                let mut chunk = ChunkWriter::start_chunk(ChunkType::StringPool, w)?;
1102                ResStringPoolHeader::default().write(w)?;
1103                chunk.end_header(w)?;
1104                let indices_count = strings.len() + styles.len();
1105                let mut indices = Vec::with_capacity(indices_count);
1106                for _ in 0..indices_count {
1107                    w.write_u32::<LittleEndian>(0)?;
1108                }
1109                let strings_start = w.seek(SeekFrom::Current(0))?;
1110                for string in strings {
1111                    indices.push(w.seek(SeekFrom::Current(0))? - strings_start);
1112                    assert!(string.len() < 0x7f);
1113                    let chars = string.chars().count();
1114                    w.write_u8(chars as u8)?;
1115                    w.write_u8(string.len() as u8)?;
1116                    w.write_all(string.as_bytes())?;
1117                    w.write_u8(0)?;
1118                }
1119                while w.seek(SeekFrom::Current(0))? % 4 != 0 {
1120                    w.write_u8(0)?;
1121                }
1122                let styles_start = w.seek(SeekFrom::Current(0))?;
1123                for style in styles {
1124                    indices.push(w.seek(SeekFrom::Current(0))? - styles_start);
1125                    for span in style {
1126                        span.write(w)?;
1127                    }
1128                    w.write_i32::<LittleEndian>(-1)?;
1129                }
1130                let (start_chunk, end_chunk) = chunk.end_chunk(w)?;
1131
1132                w.seek(SeekFrom::Start(start_chunk + 8))?;
1133                ResStringPoolHeader {
1134                    string_count: strings.len() as u32,
1135                    style_count: styles.len() as u32,
1136                    flags: ResStringPoolHeader::UTF8_FLAG,
1137                    strings_start: (strings_start - start_chunk) as u32,
1138                    styles_start: (styles_start - start_chunk) as u32,
1139                }
1140                .write(w)?;
1141                for index in indices {
1142                    w.write_u32::<LittleEndian>(index as u32)?;
1143                }
1144                w.seek(SeekFrom::Start(end_chunk))?;
1145            }
1146            Chunk::Table(table_header, chunks) => {
1147                let mut chunk = ChunkWriter::start_chunk(ChunkType::Table, w)?;
1148                table_header.write(w)?;
1149                chunk.end_header(w)?;
1150                for chunk in chunks {
1151                    chunk.write(w)?;
1152                }
1153                chunk.end_chunk(w)?;
1154            }
1155            Chunk::Xml(chunks) => {
1156                let mut chunk = ChunkWriter::start_chunk(ChunkType::Xml, w)?;
1157                chunk.end_header(w)?;
1158                for chunk in chunks {
1159                    chunk.write(w)?;
1160                }
1161                chunk.end_chunk(w)?;
1162            }
1163            Chunk::XmlStartNamespace(node_header, namespace) => {
1164                let mut chunk = ChunkWriter::start_chunk(ChunkType::XmlStartNamespace, w)?;
1165                node_header.write(w)?;
1166                chunk.end_header(w)?;
1167                namespace.write(w)?;
1168                chunk.end_chunk(w)?;
1169            }
1170            Chunk::XmlEndNamespace(node_header, namespace) => {
1171                let mut chunk = ChunkWriter::start_chunk(ChunkType::XmlEndNamespace, w)?;
1172                node_header.write(w)?;
1173                chunk.end_header(w)?;
1174                namespace.write(w)?;
1175                chunk.end_chunk(w)?;
1176            }
1177            Chunk::XmlStartElement(node_header, start_element, attributes) => {
1178                let mut chunk = ChunkWriter::start_chunk(ChunkType::XmlStartElement, w)?;
1179                node_header.write(w)?;
1180                chunk.end_header(w)?;
1181                start_element.write(w)?;
1182                for attr in attributes {
1183                    attr.write(w)?;
1184                }
1185                chunk.end_chunk(w)?;
1186            }
1187            Chunk::XmlEndElement(node_header, end_element) => {
1188                let mut chunk = ChunkWriter::start_chunk(ChunkType::XmlEndElement, w)?;
1189                node_header.write(w)?;
1190                chunk.end_header(w)?;
1191                end_element.write(w)?;
1192                chunk.end_chunk(w)?;
1193            }
1194            Chunk::XmlResourceMap(resource_map) => {
1195                let mut chunk = ChunkWriter::start_chunk(ChunkType::XmlResourceMap, w)?;
1196                chunk.end_header(w)?;
1197                for entry in resource_map {
1198                    w.write_u32::<LittleEndian>(*entry)?;
1199                }
1200                chunk.end_chunk(w)?;
1201            }
1202            Chunk::TablePackage(package_header, chunks) => {
1203                let package_start = w.seek(SeekFrom::Current(0))?;
1204                let mut chunk = ChunkWriter::start_chunk(ChunkType::TablePackage, w)?;
1205                let mut package_header = package_header.clone();
1206                let header_start = w.seek(SeekFrom::Current(0))?;
1207                package_header.write(w)?;
1208                chunk.end_header(w)?;
1209
1210                let type_strings_start = w.seek(SeekFrom::Current(0))?;
1211                package_header.type_strings = (type_strings_start - package_start) as u32;
1212                chunks[0].write(w)?;
1213
1214                let key_strings_start = w.seek(SeekFrom::Current(0))?;
1215                package_header.key_strings = (key_strings_start - package_start) as u32;
1216                chunks[1].write(w)?;
1217
1218                for chunk in &chunks[2..] {
1219                    chunk.write(w)?;
1220                }
1221                chunk.end_chunk(w)?;
1222
1223                let end = w.seek(SeekFrom::Current(0))?;
1224                w.seek(SeekFrom::Start(header_start))?;
1225                package_header.write(w)?;
1226                w.seek(SeekFrom::Start(end))?;
1227            }
1228            Chunk::TableType(type_header, index, entries) => {
1229                let mut chunk = ChunkWriter::start_chunk(ChunkType::TableType, w)?;
1230                type_header.write(w)?;
1231                chunk.end_header(w)?;
1232                for offset in index {
1233                    w.write_u32::<LittleEndian>(*offset)?;
1234                }
1235                for entry in entries.iter().flatten() {
1236                    entry.write(w)?;
1237                }
1238                chunk.end_chunk(w)?;
1239            }
1240            Chunk::TableTypeSpec(type_spec_header, type_spec) => {
1241                let mut chunk = ChunkWriter::start_chunk(ChunkType::TableTypeSpec, w)?;
1242                type_spec_header.write(w)?;
1243                chunk.end_header(w)?;
1244                for spec in type_spec {
1245                    w.write_u32::<LittleEndian>(*spec)?;
1246                }
1247                chunk.end_chunk(w)?;
1248            }
1249            Chunk::Unknown => {}
1250        }
1251        Ok(())
1252    }
1253}
1254
1255#[cfg(test)]
1256mod tests {
1257    use super::*;
1258    use std::fs::File;
1259    use std::io::{BufReader, Cursor};
1260    use std::path::Path;
1261    use zip::ZipArchive;
1262
1263    #[test]
1264    fn test_parse_android_resources() -> Result<()> {
1265        crate::tests::init_logger();
1266        let home = std::env::var("ANDROID_HOME")?;
1267        let platforms = Path::new(&home).join("platforms");
1268        for entry in std::fs::read_dir(platforms)? {
1269            let platform = entry?;
1270            let android = platform.path().join("android.jar");
1271            if !android.exists() {
1272                continue;
1273            }
1274            let mut zip = ZipArchive::new(BufReader::new(File::open(&android)?))?;
1275            let mut f = zip.by_name("resources.arsc")?;
1276            let mut buf = vec![];
1277            f.read_to_end(&mut buf)?;
1278            let mut cursor = Cursor::new(&buf);
1279            tracing::info!("parsing {}", android.display());
1280            Chunk::parse(&mut cursor)?;
1281        }
1282        Ok(())
1283    }
1284}