1use prost::Message;
5
6use crate::axml::Axml;
7use crate::error::AxmlError;
8use crate::proto;
9use crate::resource_map::ResourceMap;
10use crate::string_pool::{StringBlocks, StringPool};
11use crate::xml_element::{Attribute, XmlElement};
12
13impl Axml {
16 pub fn to_proto_bytes(&self) -> Vec<u8> {
19 axml_to_proto(self).encode_to_vec()
20 }
21
22 pub fn from_proto_bytes(data: &[u8]) -> Result<Self, AxmlError> {
25 let p = proto::Axml::decode(data)?;
26 proto_to_axml(p)
27 }
28
29 pub fn to_proto(&self) -> proto::Axml {
32 axml_to_proto(self)
33 }
34
35 pub fn from_proto(p: proto::Axml) -> Result<Self, AxmlError> {
37 proto_to_axml(p)
38 }
39
40 pub fn update_proto(&mut self) {
45 let sb_proto: proto::StringBlocks = string_pool_to_proto(&self.stringblocks.inner);
46 self.stringblocks.proto = sb_proto.clone();
47 self.proto = proto::Axml {
48 header_xml: Some(proto::AxmlHeader {
49 r#type: proto::ResType::ResXmlType as i32,
50 header_size: self.file_header_size as u32,
51 size: self.total_size,
52 }),
53 stringblocks: Some(sb_proto),
54 resourcemap: self.resource_map.as_ref().map(resource_map_to_proto),
55 resourcexml: Some(elements_to_proto(&self.elements)),
56 };
57 }
58}
59
60pub(crate) fn axml_to_proto(axml: &Axml) -> proto::Axml {
63 proto::Axml {
64 header_xml: Some(proto::AxmlHeader {
65 r#type: proto::ResType::ResXmlType as i32,
66 header_size: axml.file_header_size as u32,
67 size: axml.total_size,
68 }),
69 stringblocks: Some(string_pool_to_proto(&axml.stringblocks.inner)),
70 resourcemap: axml.resource_map.as_ref().map(resource_map_to_proto),
71 resourcexml: Some(elements_to_proto(&axml.elements)),
72 }
73}
74
75pub(crate) fn string_pool_to_proto(sp: &StringPool) -> proto::StringBlocks {
76 let string_count = sp.strings.len() as u32;
78 let style_count = sp.style_offsets.len() as u32;
79
80 let (pool_size, strings_start, styles_start) =
84 if let Some(raw) = sp.raw.as_ref().filter(|_| !sp.dirty) {
85 if raw.len() >= 28 {
86 (
87 raw.len() as u32,
88 u32::from_le_bytes([raw[20], raw[21], raw[22], raw[23]]),
89 u32::from_le_bytes([raw[24], raw[25], raw[26], raw[27]]),
90 )
91 } else {
92 (0u32, 28 + 4 * string_count, 0u32)
93 }
94 } else {
95 (0u32, 28 + 4 * string_count + 4 * style_count, 0u32)
97 };
98
99 let hnd = proto::AxmlHeaderStringpool {
100 hnd: Some(proto::AxmlHeader {
101 r#type: proto::ResType::ResStringPoolType as i32,
102 header_size: 28,
103 size: pool_size,
104 }),
105 len_stringblocks: string_count,
106 len_styleblocks: style_count,
107 flag: sp.flags,
108 stringoffset: strings_start,
109 styleoffset: styles_start,
110 };
111
112 let stringoffsets = if !sp.dirty && !sp.string_offsets.is_empty() {
115 sp.string_offsets.clone()
116 } else {
117 Vec::new()
118 };
119
120 let styleoffsets = sp.style_offsets.clone();
121
122 let stringblocks: Vec<proto::StringBlock> = sp
124 .strings
125 .iter()
126 .map(|raw| {
127 let (size, redundant_size, padding) = if sp.is_utf8 {
128 let char_count = String::from_utf8_lossy(raw).chars().count() as u32;
129 let byte_count = raw.len() as u32;
130 (char_count, Some(byte_count), vec![0u8])
131 } else {
132 (raw.len() as u32 / 2, None, vec![0u8, 0u8])
133 };
134 proto::StringBlock {
135 size,
136 redundant_size,
137 data: raw.clone(),
138 padding,
139 }
140 })
141 .collect();
142
143 proto::StringBlocks {
144 hnd: Some(hnd),
145 stringoffsets,
146 styleoffsets,
147 stringblocks,
148 pad_stringblocks: vec![],
149 styleblocks: vec![],
150 pad_styleblocks: vec![],
151 }
152}
153
154fn resource_map_to_proto(rm: &ResourceMap) -> proto::ResourceMap {
155 proto::ResourceMap {
156 header: Some(proto::AxmlHeader {
157 r#type: proto::ResType::ResXmlResourceMapType as i32,
158 header_size: 8,
159 size: (8 + rm.ids.len() * 4) as u32,
160 }),
161 res: rm.ids.clone(),
162 }
163}
164
165fn elements_to_proto(elements: &[XmlElement]) -> proto::ResourceXml {
166 proto::ResourceXml {
167 elts: elements.iter().map(xml_element_to_proto).collect(),
168 }
169}
170
171fn xml_element_to_proto(elt: &XmlElement) -> proto::XmlElement {
172 let chunk_size = elt.pack().len() as u32;
173 let chunk_type = elt.chunk_type() as i32;
174
175 let _header = Some(proto::AxmlHeader {
176 r#type: chunk_type,
177 header_size: 16,
178 size: chunk_size,
179 });
180
181 match elt {
182 XmlElement::StartNamespace {
183 header_size,
184 chunk_size,
185 line_number,
186 comment,
187 prefix,
188 uri,
189 ..
190 } => proto::XmlElement {
191 header: Some(proto::AxmlHeader {
192 r#type: proto::ResType::ResXmlFirstChunkType as i32,
193 header_size: *header_size as u32,
194 size: *chunk_size,
195 }),
196 start_ns: Some(proto::ResXmlStartNamespace {
197 generic: Some(res_xml_generic(*line_number, *comment)),
198 prefix: *prefix,
199 uri: *uri,
200 }),
201 ..Default::default()
202 },
203
204 XmlElement::EndNamespace {
205 header_size,
206 chunk_size,
207 line_number,
208 comment,
209 prefix,
210 uri,
211 ..
212 } => proto::XmlElement {
213 header: Some(proto::AxmlHeader {
214 r#type: proto::ResType::ResXmlEndNamespaceType as i32,
215 header_size: *header_size as u32,
216 size: *chunk_size,
217 }),
218 end_ns: Some(proto::ResXmlEndNamespace {
219 generic: Some(res_xml_generic(*line_number, *comment)),
220 prefix: *prefix,
221 uri: *uri,
222 }),
223 ..Default::default()
224 },
225
226 XmlElement::StartElement {
227 header_size,
228 chunk_size,
229 line_number,
230 comment,
231 namespace_uri,
232 name,
233 at_start,
234 at_size,
235 style_attribute,
236 class_attribute,
237 attributes,
238 ..
239 } => {
240 let proto_attrs = attributes
241 .iter()
242 .map(|a| proto::Attribute {
243 namespace_uri: a.namespace_uri,
244 name: a.name,
245 value: a.value,
246 r#type: a.type_,
247 data: a.data,
248 padding: a.padding.clone(),
249 })
250 .collect();
251
252 proto::XmlElement {
253 header: Some(proto::AxmlHeader {
254 r#type: proto::ResType::ResXmlStartElementType as i32,
255 header_size: *header_size as u32,
256 size: *chunk_size,
257 }),
258 start_elt: Some(proto::ResXmlStartElement {
259 generic: Some(res_xml_generic(*line_number, *comment)),
260 namespace_uri: *namespace_uri,
261 name: *name,
262 at_start: *at_start as u32,
263 at_size: *at_size as u32,
264 attributevalue: 0,
265 len_attributes: attributes.len() as u32,
266 style_attribute: *style_attribute as i32,
267 class_attribute: *class_attribute as i32,
268 attributes: proto_attrs,
269 }),
270 ..Default::default()
271 }
272 }
273
274 XmlElement::EndElement {
275 header_size,
276 chunk_size,
277 line_number,
278 comment,
279 namespace_uri,
280 name,
281 ..
282 } => proto::XmlElement {
283 header: Some(proto::AxmlHeader {
284 r#type: proto::ResType::ResXmlEndElementType as i32,
285 header_size: *header_size as u32,
286 size: *chunk_size,
287 }),
288 end_elt: Some(proto::ResXmlEndElement {
289 generic: Some(res_xml_generic(*line_number, *comment)),
290 namespace_uri: *namespace_uri,
291 name: *name,
292 }),
293 ..Default::default()
294 },
295
296 XmlElement::CData {
297 header_size,
298 chunk_size,
299 line_number,
300 comment,
301 name,
302 res_size,
303 res_res0,
304 res_data_type,
305 res_data,
306 ..
307 } => proto::XmlElement {
308 header: Some(proto::AxmlHeader {
309 r#type: proto::ResType::ResXmlCdataType as i32,
310 header_size: *header_size as u32,
311 size: *chunk_size,
312 }),
313 cdata: Some(proto::ResXmlcdata {
314 generic: Some(res_xml_generic(*line_number, *comment)),
315 name: *name,
316 res: Some(proto::ArscResStringPoolRef {
317 size: *res_size as u32,
318 res0: *res_res0 as u32,
319 data_type: *res_data_type as u32,
320 data: *res_data,
321 }),
322 }),
323 ..Default::default()
324 },
325 }
326}
327
328#[inline]
329fn res_xml_generic(line_number: u32, comment: u32) -> proto::ResXml {
330 proto::ResXml {
331 line_number,
332 comment,
333 }
334}
335
336fn proto_to_axml(p: proto::Axml) -> Result<Axml, AxmlError> {
339 let (file_type, file_header_size, total_size) = p
340 .header_xml
341 .map(|h| (h.r#type as u16, h.header_size as u16, h.size))
342 .unwrap_or((crate::axml::RES_XML_TYPE, 8, 0));
343
344 let sb_proto = p.stringblocks.unwrap_or_default();
345 let string_pool = proto_to_string_pool(sb_proto.clone());
346
347 let resource_map = p.resourcemap.map(proto_to_resource_map);
348
349 let elements = p
350 .resourcexml
351 .map(proto_to_elements)
352 .transpose()?
353 .unwrap_or_default();
354
355 let mut axml = Axml {
356 proto: proto::Axml::default(),
357 stringblocks: StringBlocks::from_pool_and_proto(string_pool, sb_proto),
358 resource_map,
359 elements,
360 file_type,
361 file_header_size,
362 total_size,
363 };
364 axml.update_proto();
365 Ok(axml)
366}
367
368pub(crate) fn proto_to_string_pool(sb: proto::StringBlocks) -> StringPool {
369 let (is_utf8, flags) = sb
370 .hnd
371 .as_ref()
372 .map(|h| {
373 let f = h.flag;
374 ((f & crate::string_pool::UTF8_FLAG) != 0, f)
375 })
376 .unwrap_or((false, 0));
377
378 let strings: Vec<Vec<u8>> = sb.stringblocks.into_iter().map(|s| s.data).collect();
379 let string_offsets = sb.stringoffsets;
380 let style_offsets = sb.styleoffsets;
381
382 let index = strings
384 .iter()
385 .enumerate()
386 .map(|(i, data)| (data.clone(), i as u32))
387 .collect();
388
389 StringPool {
390 raw: None,
391 dirty: true,
392 is_utf8,
393 flags,
394 strings,
395 index,
396 string_offsets,
397 string_data_start: 0,
398 style_offsets,
399 }
400}
401
402fn proto_to_resource_map(rm: proto::ResourceMap) -> ResourceMap {
403 let header_size = rm
404 .header
405 .as_ref()
406 .map(|h| h.header_size as u16)
407 .unwrap_or(8);
408 let chunk_size = rm.header.as_ref().map(|h| h.size).unwrap_or(0);
409 ResourceMap {
410 ids: rm.res,
411 header_size,
412 chunk_size,
413 }
414}
415
416fn proto_to_elements(rxml: proto::ResourceXml) -> Result<Vec<XmlElement>, AxmlError> {
417 rxml.elts
418 .into_iter()
419 .filter_map(proto_to_xml_element)
420 .collect::<Result<Vec<_>, _>>()
421}
422
423pub(crate) fn proto_to_xml_element(
424 elt: proto::XmlElement,
425) -> Option<Result<XmlElement, AxmlError>> {
426 let header_size = elt
427 .header
428 .as_ref()
429 .map(|h| h.header_size as u16)
430 .unwrap_or(16);
431 let chunk_size = elt.header.as_ref().map(|h| h.size).unwrap_or(0);
432
433 if let Some(ns) = elt.start_ns {
434 let (ln, cm) = line_comment(&ns.generic);
435 return Some(Ok(XmlElement::StartNamespace {
436 header_size,
437 chunk_size,
438 line_number: ln,
439 comment: cm,
440 prefix: ns.prefix,
441 uri: ns.uri,
442 }));
443 }
444 if let Some(ns) = elt.end_ns {
445 let (ln, cm) = line_comment(&ns.generic);
446 return Some(Ok(XmlElement::EndNamespace {
447 header_size,
448 chunk_size,
449 line_number: ln,
450 comment: cm,
451 prefix: ns.prefix,
452 uri: ns.uri,
453 }));
454 }
455 if let Some(se) = elt.start_elt {
456 let (ln, cm) = line_comment(&se.generic);
457 let attrs = se
458 .attributes
459 .into_iter()
460 .map(|a| Attribute {
461 namespace_uri: a.namespace_uri,
462 name: a.name,
463 value: a.value,
464 type_: a.r#type,
465 data: a.data,
466 padding: a.padding,
467 })
468 .collect();
469 return Some(Ok(XmlElement::StartElement {
470 header_size,
471 chunk_size,
472 line_number: ln,
473 comment: cm,
474 namespace_uri: se.namespace_uri,
475 name: se.name,
476 at_start: se.at_start as i16,
477 at_size: se.at_size as i16,
478 style_attribute: se.style_attribute as i16,
479 class_attribute: se.class_attribute as i16,
480 attributes: attrs,
481 }));
482 }
483 if let Some(ee) = elt.end_elt {
484 let (ln, cm) = line_comment(&ee.generic);
485 return Some(Ok(XmlElement::EndElement {
486 header_size,
487 chunk_size,
488 line_number: ln,
489 comment: cm,
490 namespace_uri: ee.namespace_uri,
491 name: ee.name,
492 }));
493 }
494 if let Some(cd) = elt.cdata {
495 let (ln, cm) = line_comment(&cd.generic);
496 let (res_size, res_res0, res_data_type, res_data) = cd
497 .res
498 .map(|r| (r.size as u16, r.res0 as u8, r.data_type as u8, r.data))
499 .unwrap_or((0, 0, 0, 0));
500 return Some(Ok(XmlElement::CData {
501 header_size,
502 chunk_size,
503 line_number: ln,
504 comment: cm,
505 name: cd.name,
506 res_size,
507 res_res0,
508 res_data_type,
509 res_data,
510 }));
511 }
512 None
513}
514
515#[inline]
516fn line_comment(g: &Option<proto::ResXml>) -> (u32, u32) {
517 g.as_ref()
518 .map(|r| (r.line_number, r.comment))
519 .unwrap_or((0, 0xffff_ffff))
520}
521
522use crate::arsc::{
525 Arsc, ArscEntry, ArscPackage, ArscResChunk, ArscTypeSpec, ArscTypeType, ArscValue,
526};
527
528impl Arsc {
529 pub fn to_proto_bytes(&self) -> Vec<u8> {
531 self.proto.encode_to_vec()
532 }
533
534 pub fn from_proto_bytes(data: &[u8]) -> Result<Self, AxmlError> {
536 let p = proto::Arsc::decode(data)?;
537 proto_to_arsc(p)
538 }
539
540 pub fn from_proto(p: proto::Arsc) -> Result<Self, AxmlError> {
542 proto_to_arsc(p)
543 }
544
545 pub fn to_proto(&self) -> proto::Arsc {
547 self.proto.clone()
548 }
549
550 pub fn update_proto(&mut self) {
554 let sb_proto = string_pool_to_proto(&self.stringblocks.inner);
555 self.stringblocks.proto = sb_proto.clone();
556
557 let restablespackage: Vec<proto::AxmlResTablePackage> = self
558 .packages
559 .iter_mut()
560 .map(|pkg| {
561 let p = arsc_package_to_proto(pkg);
562 pkg.proto = p.clone();
563 p
564 })
565 .collect();
566
567 self.proto = proto::Arsc {
568 header_res: Some(proto::AxmlHeaderRestable {
569 hnd: Some(proto::AxmlHeader {
570 r#type: proto::ResType::ResTableType as i32,
571 header_size: 12,
572 size: self.total_size,
573 }),
574 package_count: self.package_count,
575 }),
576 stringblocks: Some(sb_proto),
577 restablespackage,
578 };
579 }
580}
581
582fn arsc_package_to_proto(pkg: &ArscPackage) -> proto::AxmlResTablePackage {
583 let type_sp = string_pool_to_proto(&pkg.type_strings);
584 let key_sp = string_pool_to_proto(&pkg.key_strings);
585
586 let restypes: Vec<proto::ArscResType> = pkg.chunks.iter().map(arsc_chunk_to_proto).collect();
587
588 proto::AxmlResTablePackage {
589 hnd: Some(proto::AxmlHeader {
590 r#type: crate::arsc::RES_TABLE_PACKAGE_TYPE as i32,
591 header_size: 8,
592 size: pkg.chunk_size,
593 }),
594 id: pkg.id,
595 name: pkg.name.clone(),
596 type_strings: 0,
597 last_public_type: 0,
598 key_strings: 0,
599 last_public_key: 0,
600 padding: vec![],
601 type_sp_string: Some(type_sp),
602 key_sp_string: Some(key_sp),
603 restypes,
604 }
605}
606
607fn arsc_chunk_to_proto(chunk: &ArscResChunk) -> proto::ArscResType {
608 match chunk {
609 ArscResChunk::Spec(spec) => proto::ArscResType {
610 hnd: Some(proto::AxmlHeader {
611 r#type: crate::arsc::RES_TABLE_TYPE_SPEC_TYPE as i32,
612 header_size: 8,
613 size: (8 + 8 + spec.entries.len() * 4) as u32,
614 }),
615 typespec: Some(proto::ArscResTypeSpec {
616 id: spec.id as u32,
617 res0: 0,
618 res1: 0,
619 entry_count: spec.entries.len() as u32,
620 entries: spec.entries.clone(),
621 }),
622 typetype: None,
623 },
624 ArscResChunk::Type(tt) => {
625 let tables: Vec<proto::ArscResTableEntry> =
626 tt.tables.iter().map(arsc_entry_to_proto).collect();
627 proto::ArscResType {
628 hnd: Some(proto::AxmlHeader {
629 r#type: crate::arsc::RES_TABLE_TYPE_TYPE as i32,
630 header_size: 8,
631 size: 0,
632 }),
633 typespec: None,
634 typetype: Some(proto::ArscResTypeType {
635 id: tt.id as u32,
636 flags: 0,
637 reserved: 0,
638 entry_count: tt.tables.len() as u32,
639 entry_start: 0,
640 config: None,
641 entries: vec![],
642 tables,
643 }),
644 }
645 }
646 }
647}
648
649fn arsc_entry_to_proto(entry_opt: &Option<ArscEntry>) -> proto::ArscResTableEntry {
650 match entry_opt {
651 None => proto::ArscResTableEntry {
652 size: 0,
653 flags: 0,
654 index: 0,
655 present: false,
656 item: None,
657 key: None,
658 },
659 Some(entry) => {
660 let key = entry.value.as_ref().map(|v| proto::ArscResStringPoolRef {
661 size: v.size as u32,
662 res0: v.res0 as u32,
663 data_type: v.data_type as u32,
664 data: v.data,
665 });
666 proto::ArscResTableEntry {
667 size: 8,
668 flags: 0,
669 index: entry.key_index,
670 present: true,
671 item: None,
672 key,
673 }
674 }
675 }
676}
677
678pub(crate) fn proto_to_arsc(p: proto::Arsc) -> Result<Arsc, AxmlError> {
681 let (total_size, package_count) = p
682 .header_res
683 .as_ref()
684 .map(|h| {
685 let sz = h.hnd.as_ref().map(|hnd| hnd.size).unwrap_or(0);
686 (sz, h.package_count)
687 })
688 .unwrap_or((0, 0));
689
690 let sb_proto = p.stringblocks.unwrap_or_default();
691 let string_pool = proto_to_string_pool(sb_proto.clone());
692 let stringblocks = StringBlocks::from_pool_and_proto(string_pool, sb_proto);
693
694 let packages: Vec<ArscPackage> = p
695 .restablespackage
696 .into_iter()
697 .map(proto_to_arsc_package)
698 .collect();
699
700 let mut arsc = Arsc {
701 proto: proto::Arsc::default(),
702 stringblocks,
703 packages,
704 total_size,
705 package_count,
706 };
707 arsc.update_proto();
708 Ok(arsc)
709}
710
711pub(crate) fn proto_to_arsc_package(p: proto::AxmlResTablePackage) -> ArscPackage {
712 let type_strings = p
713 .type_sp_string
714 .map(proto_to_string_pool)
715 .unwrap_or_default();
716 let key_strings = p
717 .key_sp_string
718 .map(proto_to_string_pool)
719 .unwrap_or_default();
720
721 let chunks: Vec<ArscResChunk> = p
722 .restypes
723 .into_iter()
724 .filter_map(proto_to_arsc_chunk)
725 .collect();
726
727 let chunk_size = p.hnd.as_ref().map(|h| h.size).unwrap_or(0);
728
729 ArscPackage {
730 proto: proto::AxmlResTablePackage::default(),
731 id: p.id,
732 name: p.name,
733 type_strings,
734 key_strings,
735 chunks,
736 chunk_size,
737 last_public_type: 0,
738 last_public_key: 0,
739 }
740}
741
742pub(crate) fn proto_to_arsc_chunk(p: proto::ArscResType) -> Option<ArscResChunk> {
743 if let Some(spec) = p.typespec {
744 return Some(ArscResChunk::Spec(ArscTypeSpec {
745 id: spec.id as u8,
746 res0: 0,
747 res1: 0,
748 entries: spec.entries,
749 }));
750 }
751 if let Some(tt) = p.typetype {
752 let tables: Vec<Option<ArscEntry>> =
753 tt.tables.into_iter().map(proto_to_arsc_entry).collect();
754 let config_raw = {
755 let mut cfg = vec![0u8; 32];
756 cfg[0] = 32;
757 cfg
758 };
759 return Some(ArscResChunk::Type(ArscTypeType {
760 id: tt.id as u8,
761 flags: 0,
762 reserved: 0,
763 language: String::new(),
764 region: String::new(),
765 config_raw,
766 tables,
767 }));
768 }
769 None
770}
771
772fn proto_to_arsc_entry(p: proto::ArscResTableEntry) -> Option<ArscEntry> {
773 if !p.present {
774 return None;
775 }
776 let value = p.key.map(|k| ArscValue {
777 size: k.size as u16,
778 res0: k.res0 as u8,
779 data_type: k.data_type as u8,
780 data: k.data,
781 });
782 let mut raw_tail = Vec::new();
783 let size = if let Some(ref v) = value {
784 raw_tail.extend_from_slice(&v.size.to_le_bytes());
785 raw_tail.push(v.res0);
786 raw_tail.push(v.data_type);
787 raw_tail.extend_from_slice(&v.data.to_le_bytes());
788 16 } else {
790 8 };
792 Some(ArscEntry {
793 size: size as u16,
794 flags: 0,
795 key_index: p.index,
796 value,
797 raw_tail,
798 })
799}