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 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::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 pub ty: u16,
53 pub header_size: u16,
56 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 pub namespace: i32,
199 pub name: i32,
202 pub attribute_start: u16,
205 pub attribute_size: u16,
207 pub attribute_count: u16,
211 pub id_index: u16,
213 pub class_index: u16,
215 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 pub id: u32,
368 pub name: String,
370 pub type_strings: u32,
374 pub last_public_type: u32,
376 pub key_strings: u32,
380 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 pub id: u8,
442 pub res0: u8,
444 pub res1: u16,
446 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 pub id: u8,
479 pub res0: u8,
481 pub res1: u16,
483 pub entry_count: u32,
485 pub entries_start: u32,
487 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 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 let s = String::from_utf8(buf).unwrap_or_default();
914 strings.push(s);
915 if r.read_u8()? != 0 {
916 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 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 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}