1use std::collections::HashMap;
2
3use quick_xml::events::{BytesStart, Event};
4use quick_xml::{Decoder, Reader};
5
6use crate::error::AxmlError;
7use crate::proto;
8use crate::public;
9use crate::resource_map::ResourceMap;
10use crate::string_pool::{StringBlocks, StringPool};
11use crate::typed_value::{self, TYPE_STRING};
12use crate::xml_element::{Attribute, XmlElement};
13
14pub(crate) const RES_XML_TYPE: u16 = 0x0003;
15const ANDROID_NS: &str = "http://schemas.android.com/apk/res/android";
18
19pub struct Axml {
35 pub(crate) proto: proto::Axml,
37 pub(crate) stringblocks: StringBlocks,
39 pub(crate) resource_map: Option<ResourceMap>,
40 pub(crate) elements: Vec<XmlElement>,
41 pub(crate) file_type: u16,
42 pub(crate) file_header_size: u16,
43 pub(crate) total_size: u32,
47}
48
49impl Axml {
50 pub fn new() -> Self {
52 Axml {
53 proto: proto::Axml::default(),
54 stringblocks: StringBlocks::new(),
55 resource_map: None,
56 elements: Vec::new(),
57 file_type: RES_XML_TYPE,
58 file_header_size: 8,
59 total_size: 0,
60 }
61 }
62
63 pub fn get_proto(&self) -> &proto::Axml {
67 &self.proto
68 }
69
70 pub fn string_count(&self) -> usize {
72 self.stringblocks.inner.strings.len()
73 }
74
75 pub fn export_stringblocks_json(&self) -> String {
77 crate::json_serde::export_stringblocks(&self.stringblocks.inner)
78 }
79
80 pub fn import_stringblocks_json(&mut self, json: &str) {
83 crate::json_serde::import_stringblocks(json, &mut self.stringblocks.inner);
84 }
85
86 pub fn from_axml(data: &[u8]) -> Result<Self, AxmlError> {
90 if data.len() < 8 {
91 return Err(AxmlError::UnexpectedEof);
92 }
93
94 let file_type = u16::from_le_bytes([data[0], data[1]]);
95 let file_header_size = u16::from_le_bytes([data[2], data[3]]);
96
97 if file_type == crate::arsc::RES_TABLE_TYPE {
100 return Err(AxmlError::InvalidMagic(file_type));
101 }
102
103 let file_size = u32::from_le_bytes([data[4], data[5], data[6], data[7]]) as usize;
104 if file_size > data.len() {
105 return Err(AxmlError::UnexpectedEof);
106 }
107
108 let mut pos = file_header_size as usize;
109 if pos > data.len() {
110 return Err(AxmlError::UnexpectedEof);
111 }
112
113 let (string_pool, consumed) = StringPool::parse(&data[pos..])?;
114 pos += consumed;
115
116 let (resource_map, rmap_consumed) = ResourceMap::parse(&data[pos..])?;
117 pos += rmap_consumed;
118
119 let elements = XmlElement::parse_all(&data[pos..])?;
120
121 let total_size = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
122
123 let mut axml = Axml {
124 proto: proto::Axml::default(),
125 stringblocks: StringBlocks::from_pool_and_proto(
126 string_pool,
127 proto::StringBlocks::default(),
128 ),
129 resource_map,
130 elements,
131 file_type,
132 file_header_size,
133 total_size,
134 };
135 axml.update_proto();
136 Ok(axml)
137 }
138
139 pub fn pack(&self) -> Vec<u8> {
148 let sp = self.stringblocks.inner.pack();
149 let rm = self
150 .resource_map
151 .as_ref()
152 .map(|r| r.pack())
153 .unwrap_or_default();
154 let mut xml = Vec::new();
155 for e in &self.elements {
156 xml.extend_from_slice(&e.pack());
157 }
158
159 let body_len = sp.len() + rm.len() + xml.len();
160 let mut out = Vec::with_capacity(8 + body_len);
161
162 out.extend_from_slice(&self.file_type.to_le_bytes());
163 out.extend_from_slice(&self.file_header_size.to_le_bytes());
164 out.extend_from_slice(&self.total_size.to_le_bytes());
165 out.extend_from_slice(&sp);
166 out.extend_from_slice(&rm);
167 out.extend_from_slice(&xml);
168 out
169 }
170
171 pub fn to_xml(&self) -> Result<String, AxmlError> {
175 let mut ns_order: Vec<(String, String)> = Vec::new(); let mut ns_map: HashMap<String, String> = HashMap::new(); for elt in &self.elements {
180 if let XmlElement::StartNamespace { prefix, uri, .. } = elt {
181 let p = self.decode_safe(*prefix);
182 let u = self.decode_safe(*uri);
183 if !p.is_empty() && !u.is_empty() && !ns_map.contains_key(&u) {
184 ns_map.insert(u.clone(), p.clone());
185 ns_order.push((u, p));
186 }
187 }
188 }
189
190 let mut output = String::from("<?xml version='1.0' encoding='utf-8'?>\n");
191 let mut depth = 0usize;
192 let mut first_element = true;
193 let mut tag_stack: Vec<String> = Vec::new();
194
195 for elt in &self.elements {
196 match elt {
197 XmlElement::StartElement {
198 namespace_uri,
199 name,
200 at_size: _,
201 attributes,
202 ..
203 } => {
204 let ns = self.decode_safe(*namespace_uri);
205 let tag = self.resolve_attr_name(*name)?;
206 let qualified = qualify_name(&ns, &tag, &ns_map);
207
208 indent(&mut output, depth);
209 output.push('<');
210 output.push_str(&qualified);
211
212 if first_element {
213 for (uri, prefix) in &ns_order {
214 output.push_str(" xmlns:");
215 output.push_str(prefix);
216 output.push_str("=\"");
217 xml_escape_into(&mut output, uri);
218 output.push('"');
219 }
220 first_element = false;
221 }
222
223 for attr in attributes {
224 let attr_ns = self.decode_safe(attr.namespace_uri);
225 let attr_name = self.resolve_attr_name(attr.name)?;
226 let qname = qualify_name(&attr_ns, &attr_name, &ns_map);
227
228 let type_byte = typed_value::get_type(attr.type_);
229 let value_str = if type_byte == TYPE_STRING {
230 self.decode_safe(attr.value)
231 } else if let Some(s) = typed_value::coerce_to_string(type_byte, attr.data)
232 {
233 s
234 } else {
235 attr.data.to_string()
236 };
237
238 output.push(' ');
239 output.push_str(&qname);
240 output.push_str("=\"");
241 xml_escape_into(&mut output, &value_str);
242 output.push('"');
243 }
244
245 output.push('>');
246 output.push('\n');
247 depth += 1;
248 tag_stack.push(qualified);
249 }
250
251 XmlElement::EndElement { .. } => {
252 depth = depth.saturating_sub(1);
253 let tag = tag_stack.pop().unwrap_or_default();
254 indent(&mut output, depth);
255 output.push_str("</");
256 output.push_str(&tag);
257 output.push_str(">\n");
258 }
259
260 XmlElement::CData { name, .. } => {
261 if *name != 0xffff_ffff {
262 let text = self.decode_safe(*name);
263 if !text.is_empty() {
264 indent(&mut output, depth);
265 xml_escape_into(&mut output, &text);
266 output.push('\n');
267 }
268 }
269 }
270
271 XmlElement::StartNamespace { .. } | XmlElement::EndNamespace { .. } => {}
272 }
273 }
274
275 Ok(output)
276 }
277
278 fn decode_safe(&self, idx: u32) -> String {
279 if idx == 0xffff_ffff {
280 return String::new();
281 }
282 self.stringblocks.inner.decode(idx).unwrap_or_default()
283 }
284
285 fn resolve_attr_name(&self, name_idx: u32) -> Result<String, AxmlError> {
287 if name_idx == 0xffff_ffff {
288 return Ok(String::new());
289 }
290 if let Some(ref rmap) = self.resource_map {
291 if let Some(res_id) = rmap.get_id(name_idx) {
292 if let Some(name) = public::attr_inverse(res_id) {
293 return Ok(name.to_string());
294 }
295 }
296 }
297 self.stringblocks.inner.decode(name_idx)
298 }
299
300 pub fn from_xml(&mut self, xml: &str) -> Result<(), AxmlError> {
309 self.stringblocks.inner = StringPool::new(false);
310 self.resource_map = None;
311 self.elements = Vec::new();
312
313 let mut all_xmlns: Vec<(String, String)> = Vec::new(); all_xmlns.push(("android".to_string(), ANDROID_NS.to_string()));
317 {
318 let mut pre_reader = Reader::from_str(xml);
319 pre_reader.config_mut().trim_text(true);
320 let mut pre_buf = Vec::new();
321 loop {
322 match pre_reader.read_event_into(&mut pre_buf) {
323 Ok(Event::Start(ref e)) | Ok(Event::Empty(ref e)) => {
324 for attr in e.attributes().flatten() {
325 let k = String::from_utf8_lossy(attr.key.as_ref());
326 if let Some(prefix) = k.strip_prefix("xmlns:") {
327 if prefix != "android"
328 && !all_xmlns.iter().any(|(p, _)| p == prefix)
329 {
330 let uri = String::from_utf8_lossy(&attr.value).into_owned();
331 all_xmlns.push((prefix.to_string(), uri));
332 }
333 }
334 }
335 }
336 Ok(Event::Eof) | Err(_) => break,
337 _ => {}
338 }
339 pre_buf.clear();
340 }
341 }
342
343 let mut ns_indices: Vec<(u32, u32)> = Vec::with_capacity(all_xmlns.len());
346 for (prefix, uri) in &all_xmlns {
347 let p_idx = self.stringblocks.inner.add(prefix.as_str());
348 let u_idx = self.stringblocks.inner.add(uri.as_str());
349 self.elements.push(XmlElement::StartNamespace {
350 header_size: 16,
351 chunk_size: 0,
352 line_number: 0,
353 comment: 0xffff_ffff,
354 prefix: p_idx,
355 uri: u_idx,
356 });
357 ns_indices.push((p_idx, u_idx));
358 }
359
360 let mut res_map_entries: Vec<(usize, u32)> = Vec::new();
362
363 let mut xmlns_map: HashMap<String, String> = all_xmlns.iter().cloned().collect();
365
366 let mut reader = Reader::from_str(xml);
367 reader.config_mut().trim_text(true);
368 let decoder = reader.decoder();
369
370 let mut open_stack: Vec<(u32, u32)> = Vec::new();
372 let mut buf = Vec::new();
373 const MAX_DEPTH: usize = 512;
375 const MAX_ELEMENTS: usize = 100_000;
376 let mut element_count: usize = 0;
377
378 loop {
379 match reader.read_event_into(&mut buf) {
380 Ok(Event::Start(ref e)) => {
381 if open_stack.len() >= MAX_DEPTH {
382 return Err(AxmlError::XmlError(format!(
383 "XML nesting depth exceeds limit ({})",
384 MAX_DEPTH
385 )));
386 }
387 element_count += 1;
388 if element_count > MAX_ELEMENTS {
389 return Err(AxmlError::XmlError(format!(
390 "XML element count exceeds limit ({})",
391 MAX_ELEMENTS
392 )));
393 }
394 collect_xmlns(e, &mut xmlns_map);
395 let (ns_idx, name_idx) = self.parse_element_name(e, &xmlns_map)?;
396 open_stack.push((ns_idx, name_idx));
397 self.push_start_element(
398 e,
399 ns_idx,
400 name_idx,
401 &xmlns_map,
402 decoder,
403 &mut res_map_entries,
404 )?;
405 }
406 Ok(Event::Empty(ref e)) => {
407 collect_xmlns(e, &mut xmlns_map);
408 let (ns_idx, name_idx) = self.parse_element_name(e, &xmlns_map)?;
409 self.push_start_element(
410 e,
411 ns_idx,
412 name_idx,
413 &xmlns_map,
414 decoder,
415 &mut res_map_entries,
416 )?;
417 self.elements.push(XmlElement::EndElement {
418 header_size: 16,
419 chunk_size: 0,
420 line_number: 0,
421 comment: 0xffff_ffff,
422 namespace_uri: 0xffff_ffff,
423 name: name_idx,
424 });
425 }
426 Ok(Event::End(_)) => {
427 let (_, name_idx) = open_stack.pop().unwrap_or((0xffff_ffff, 0xffff_ffff));
428 self.elements.push(XmlElement::EndElement {
429 header_size: 16,
430 chunk_size: 0,
431 line_number: 0,
432 comment: 0xffff_ffff,
433 namespace_uri: 0xffff_ffff,
434 name: name_idx,
435 });
436 }
437 Ok(Event::Text(ref e)) => {
438 let text = e
439 .decode()
440 .map_err(|_err| AxmlError::XmlError("Failed to decode text".to_string()))?;
441 let trimmed = text.trim();
442 if !trimmed.is_empty() {
443 let idx = self.stringblocks.inner.add(trimmed);
444 self.elements.push(XmlElement::CData {
445 header_size: 16,
446 chunk_size: 0,
447 line_number: 0,
448 comment: 0xffff_ffff,
449 name: idx,
450 res_size: 8,
451 res_res0: 0,
452 res_data_type: 0x03,
453 res_data: 0,
454 });
455 }
456 }
457 Ok(Event::Eof) => break,
458 Err(e) => return Err(AxmlError::XmlError(e.to_string())),
459 _ => {}
460 }
461 buf.clear();
462 }
463
464 for (p_idx, u_idx) in ns_indices.into_iter().rev() {
466 self.elements.push(XmlElement::EndNamespace {
467 header_size: 16,
468 chunk_size: 0,
469 line_number: 0,
470 comment: 0xffff_ffff,
471 prefix: p_idx,
472 uri: u_idx,
473 });
474 }
475
476 if let Some(&(max_idx, _)) = res_map_entries.iter().max_by_key(|&&(i, _)| i) {
478 let mut ids = vec![0u32; max_idx + 1];
479 for (idx, id) in res_map_entries {
480 ids[idx] = id;
481 }
482 while ids.last() == Some(&0) {
483 ids.pop();
484 }
485 if !ids.is_empty() {
486 self.resource_map = Some(ResourceMap::new_with_ids(ids));
487 }
488 }
489
490 self.compute();
491 self.update_proto();
492 Ok(())
493 }
494
495 fn parse_element_name(
497 &mut self,
498 e: &BytesStart,
499 xmlns_map: &HashMap<String, String>,
500 ) -> Result<(u32, u32), AxmlError> {
501 let full = String::from_utf8_lossy(e.name().as_ref()).to_string();
502 let (ns, local) = split_clark_name(&full);
503 let resolved_ns = if ns.is_empty() {
504 resolve_prefix_ns(&full, xmlns_map)
505 } else {
506 ns.to_string()
507 };
508 let ns_idx = if resolved_ns.is_empty() {
509 0xffff_ffff
510 } else {
511 self.stringblocks.inner.add(&resolved_ns)
512 };
513 let name_idx = self.stringblocks.inner.add(local);
514 Ok((ns_idx, name_idx))
515 }
516
517 fn push_start_element(
522 &mut self,
523 e: &BytesStart,
524 ns_uri_idx: u32,
525 name_idx: u32,
526 xmlns_map: &HashMap<String, String>,
527 decoder: Decoder,
528 res_map_entries: &mut Vec<(usize, u32)>,
529 ) -> Result<(), AxmlError> {
530 let mut attributes = Vec::new();
531
532 for attr in e.attributes().flatten() {
533 let key = String::from_utf8_lossy(attr.key.as_ref()).to_string();
534
535 if key.starts_with("xmlns") {
537 continue;
538 }
539
540 let val = attr
543 .decode_and_unescape_value(decoder)
544 .map(|v| v.into_owned())
545 .unwrap_or_else(|_| String::from_utf8_lossy(&attr.value).into_owned());
546 let (attr_ns, attr_local) = split_clark_name(&key);
547
548 let resolved_ns = if attr_ns.is_empty() {
551 resolve_prefix_ns(&key, xmlns_map)
552 } else {
553 attr_ns.to_string()
554 };
555
556 let attr_ns_idx = if resolved_ns.is_empty() {
557 0xffff_ffff
558 } else {
559 self.stringblocks.inner.add(&resolved_ns)
560 };
561
562 let attr_name_idx = self.stringblocks.inner.add(attr_local);
564
565 if !resolved_ns.is_empty() || key.starts_with("android:") {
567 if let Some(id) = public::attr_forward(attr_local) {
568 res_map_entries.push((attr_name_idx as usize, id));
569 }
570 }
571
572 let (type_, value_idx, data) =
573 typed_value::encode_attribute_value(&val, attr_local, &mut self.stringblocks.inner);
574
575 attributes.push(Attribute {
576 namespace_uri: attr_ns_idx,
577 name: attr_name_idx,
578 value: value_idx,
579 type_,
580 data,
581 padding: Vec::new(),
582 });
583 }
584
585 self.elements.push(XmlElement::StartElement {
586 header_size: 16,
587 chunk_size: 0,
588 line_number: 0,
589 comment: 0xffff_ffff,
590 namespace_uri: ns_uri_idx,
591 name: name_idx,
592 at_start: 0x14,
593 at_size: 0x14,
594 style_attribute: 0,
595 class_attribute: 0,
596 attributes,
597 });
598
599 Ok(())
600 }
601
602 pub fn compute(&mut self) {
613 self.stringblocks.inner.mark_dirty();
614 let sp = self.stringblocks.inner.pack();
615
616 if let Some(ref mut rm) = self.resource_map {
617 let chunk_size = (8 + rm.ids.len() * 4) as u32;
618 rm.set_chunk_size(chunk_size);
619 }
620 let rm = self
621 .resource_map
622 .as_ref()
623 .map(|r| r.pack())
624 .unwrap_or_default();
625
626 for elem in &mut self.elements {
627 let body = elem.pack_body();
628 let chunk_size = (8 + body.len()) as u32;
629 elem.set_chunk_size(chunk_size);
630 }
631
632 let xml_len: usize = self.elements.iter().map(|e| e.pack().len()).sum();
633 self.total_size = (8 + sp.len() + rm.len() + xml_len) as u32;
634 }
635
636 pub fn apply_stringblocks_no_compute(&mut self, new_strings: Vec<Vec<u8>>) {
646 self.stringblocks.inner.apply_no_compute(new_strings);
647 }
648
649 pub fn to_proto_text(&self) -> String {
655 use prost_reflect::ReflectMessage;
656 self.proto.transcode_to_dynamic().to_text_format()
657 }
658
659 pub fn to_proto_text_pretty(&self) -> String {
661 use prost_reflect::{text_format::FormatOptions, ReflectMessage};
662 self.proto
663 .transcode_to_dynamic()
664 .to_text_format_with_options(&FormatOptions::default().pretty(true))
665 }
666
667 pub fn export_proto_file(&self, path: &str) -> Result<(), crate::error::AxmlError> {
669 std::fs::write(path, self.to_proto_bytes())?;
670 Ok(())
671 }
672
673 pub fn from_proto_file(path: &str) -> Result<Self, crate::error::AxmlError> {
675 let data = std::fs::read(path)?;
676 Self::from_proto_bytes(&data)
677 }
678
679 pub fn export_stringblocks_proto_file(
681 &self,
682 path: &str,
683 ) -> Result<(), crate::error::AxmlError> {
684 use prost::Message as _;
685 let bytes = self.stringblocks.proto.encode_to_vec();
686 std::fs::write(path, bytes)?;
687 Ok(())
688 }
689
690 pub fn import_stringblocks_proto_file(
694 &mut self,
695 path: &str,
696 ) -> Result<(), crate::error::AxmlError> {
697 use prost::Message as _;
698 let data = std::fs::read(path)?;
699 let sb_proto = crate::proto::StringBlocks::decode(data.as_slice())?;
700 let pool = crate::proto_conv::proto_to_string_pool(sb_proto.clone());
701 self.stringblocks = crate::string_pool::StringBlocks::from_pool_and_proto(pool, sb_proto);
702 self.update_proto();
703 Ok(())
704 }
705}
706
707impl Default for Axml {
708 fn default() -> Self {
709 Self::new()
710 }
711}
712
713fn indent(out: &mut String, depth: usize) {
716 for _ in 0..depth {
717 out.push_str(" ");
718 }
719}
720
721fn qualify_name(ns_uri: &str, local: &str, ns_map: &HashMap<String, String>) -> String {
722 if ns_uri.is_empty() {
723 return local.to_string();
724 }
725 match ns_map.get(ns_uri) {
726 Some(prefix) => format!("{}:{}", prefix, local),
727 None => local.to_string(),
728 }
729}
730
731fn xml_escape_into(out: &mut String, s: &str) {
732 for c in s.chars() {
733 match c {
734 '&' => out.push_str("&"),
735 '<' => out.push_str("<"),
736 '>' => out.push_str(">"),
737 '"' => out.push_str("""),
738 c => out.push(c),
739 }
740 }
741}
742
743fn collect_xmlns(e: &BytesStart, xmlns_map: &mut HashMap<String, String>) {
746 for attr in e.attributes().flatten() {
747 let key = String::from_utf8_lossy(attr.key.as_ref());
748 if let Some(prefix) = key.strip_prefix("xmlns:") {
749 let uri = String::from_utf8_lossy(&attr.value).into_owned();
750 xmlns_map.insert(prefix.to_string(), uri);
751 } else if key == "xmlns" {
752 let uri = String::from_utf8_lossy(&attr.value).into_owned();
753 xmlns_map.insert(String::new(), uri);
754 }
755 }
756}
757
758fn resolve_prefix_ns(key: &str, xmlns_map: &HashMap<String, String>) -> String {
762 if let Some(colon) = key.find(':') {
763 let prefix = &key[..colon];
764 if prefix != "xmlns" {
765 if let Some(uri) = xmlns_map.get(prefix) {
766 return uri.clone();
767 }
768 }
769 }
770 String::new()
771}
772
773fn split_clark_name(name: &str) -> (&str, &str) {
775 if name.starts_with('{') {
776 if let Some(end) = name.find('}') {
777 return (&name[1..end], &name[end + 1..]);
778 }
779 }
780 if let Some(colon) = name.find(':') {
782 let prefix = &name[..colon];
783 if prefix != "xmlns" {
784 return ("", &name[colon + 1..]);
785 }
786 }
787 ("", name)
788}