const RES_XML_TYPE: u16 = 0x0003; const RES_STRING_POOL_TYPE: u16 = 0x0001; const RES_XML_RESOURCE_MAP_TYPE: u16 = 0x0180; const RES_XML_START_NAMESPACE_TYPE: u16 = 0x0100; const RES_XML_END_NAMESPACE_TYPE: u16 = 0x0101; const RES_XML_START_ELEMENT_TYPE: u16 = 0x0102; const RES_XML_END_ELEMENT_TYPE: u16 = 0x0103; const NO_COMMENT: u32 = 0; const ATTR_RES_IDS: &[(&str, u32)] = &[ ("versionCode", 0x0101021b), ("versionName", 0x0101021c), ("minSdkVersion", 0x0101020c), ("targetSdkVersion", 0x01010270), ("label", 0x01010001), ("name", 0x01010003), ("exported", 0x01010010), ("hasCode", 0x0101000c), ("configChanges", 0x0101001f), ]; pub const TYPE_STRING: u8 = 0x03; pub const TYPE_INT_DEC: u8 = 0x10; pub const TYPE_INT_BOOL: u8 = 0x12; struct StringPool { strings: Vec, index: std::collections::HashMap, } impl StringPool { fn new() -> Self { StringPool { strings: Vec::new(), index: std::collections::HashMap::new(), } } fn intern(&mut self, s: &str) -> u32 { if let Some(&i) = self.index.get(s) { return i; } let i = self.strings.len() as u32; self.strings.push(s.to_string()); self.index.insert(s.to_string(), i); i } fn encode(&self) -> Vec { const UTF8_FLAG: u32 = 1 << 8; let mut offsets = Vec::new(); let mut string_data = Vec::new(); for s in &self.strings { offsets.push(string_data.len() as u32); let utf16_units = s.encode_utf16().count(); let bytes = s.as_bytes(); push_utf8_len(&mut string_data, utf16_units); push_utf8_len(&mut string_data, bytes.len()); string_data.extend_from_slice(bytes); string_data.push(0); } while string_data.len() % 4 != 0 { string_data.push(0); } let string_count = self.strings.len() as u32; let header_size = 28u32; let offsets_size = string_count * 4; let strings_start = header_size + offsets_size; let chunk_size = strings_start + string_data.len() as u32; let mut out = Vec::new(); push_u16(&mut out, RES_STRING_POOL_TYPE); push_u16(&mut out, 28); push_u32(&mut out, chunk_size); push_u32(&mut out, string_count); push_u32(&mut out, 0); push_u32(&mut out, UTF8_FLAG); push_u32(&mut out, strings_start); push_u32(&mut out, 0); for o in offsets { push_u32(&mut out, o); } out.extend_from_slice(&string_data); out } } struct Attr { ns: Option, name: u32, raw_value: i32, typ: u8, data: u32, } pub struct AxmlBuilder { pool: StringPool, body: Vec, android_ns_uri: u32, android_ns_prefix: u32, line: u32, } impl AxmlBuilder { pub fn new() -> Self { AxmlBuilder { pool: StringPool::new(), body: Vec::new(), android_ns_uri: 0xFFFF_FFFF, android_ns_prefix: 0xFFFF_FFFF, line: 1, } } fn ns_uri(&mut self) -> u32 { if self.android_ns_uri == 0xFFFF_FFFF { self.android_ns_uri = self.pool.intern("http://schemas.android.com/apk/res/android"); } self.android_ns_uri } pub fn intern(&mut self, s: &str) -> u32 { self.pool.intern(s) } pub const EMIT_NAMESPACE_CHUNKS: bool = false; pub fn start(&mut self) { if !Self::EMIT_NAMESPACE_CHUNKS { return; } let mut c = Vec::new(); push_u16(&mut c, RES_XML_START_NAMESPACE_TYPE); push_u16(&mut c, 16); push_u32(&mut c, 24); push_u32(&mut c, self.line); push_u32(&mut c, NO_COMMENT); push_u32(&mut c, self.android_ns_prefix); push_u32(&mut c, self.android_ns_uri); self.body.extend_from_slice(&c); } pub fn end(&mut self) { if !Self::EMIT_NAMESPACE_CHUNKS { return; } let mut c = Vec::new(); push_u16(&mut c, RES_XML_END_NAMESPACE_TYPE); push_u16(&mut c, 16); push_u32(&mut c, 24); push_u32(&mut c, self.line); push_u32(&mut c, 0xFFFF_FFFF); push_u32(&mut c, self.android_ns_prefix); push_u32(&mut c, self.android_ns_uri); self.body.extend_from_slice(&c); } pub fn open(&mut self, tag: u32, attrs: &[(u32, u8, u32, i32)]) { let with_ns: Vec<(u32, u8, u32, i32, bool)> = attrs.iter().map(|&(n, t, d, r)| (n, t, d, r, true)).collect(); self.open_ns(tag, &with_ns); } pub fn open_ns(&mut self, tag: u32, attrs: &[(u32, u8, u32, i32, bool)]) { let uri = if attrs.iter().any(|a| a.4) { Some(self.ns_uri()) } else { None }; let attr_structs: Vec = attrs .iter() .map(|&(name, typ, data, raw, android_ns)| Attr { ns: if android_ns { uri } else { None }, name, raw_value: raw, typ, data, }) .collect(); let mut c = Vec::new(); let attr_count = attr_structs.len() as u32; let chunk_size = 16 + 20 + attr_count * 20; push_u16(&mut c, RES_XML_START_ELEMENT_TYPE); push_u16(&mut c, 16); push_u32(&mut c, chunk_size); push_u32(&mut c, self.line); push_u32(&mut c, NO_COMMENT); push_u32(&mut c, 0); push_u32(&mut c, tag); push_u16(&mut c, 20); push_u16(&mut c, 20); push_u16(&mut c, attr_count as u16); push_u16(&mut c, 0); push_u16(&mut c, 0); push_u16(&mut c, 0); for a in &attr_structs { push_u32(&mut c, a.ns.unwrap_or(0xFFFF_FFFF)); push_u32(&mut c, a.name); push_u32(&mut c, a.raw_value as u32); push_u16(&mut c, 8); c.push(0); c.push(a.typ); push_u32(&mut c, a.data); } self.body.extend_from_slice(&c); } pub fn close(&mut self, tag: u32) { let mut c = Vec::new(); push_u16(&mut c, RES_XML_END_ELEMENT_TYPE); push_u16(&mut c, 16); push_u32(&mut c, 24); push_u32(&mut c, self.line); push_u32(&mut c, NO_COMMENT); push_u32(&mut c, 0); push_u32(&mut c, tag); self.body.extend_from_slice(&c); } pub fn res_id_for(name: &str) -> Option { ATTR_RES_IDS.iter().find(|(n, _)| *n == name).map(|(_, id)| *id) } pub fn finish(self, attr_name_to_resid: &[(u32, u32)]) -> Vec { let pool_chunk = self.pool.encode(); let max_attr_idx = attr_name_to_resid .iter() .map(|&(str_idx, _)| str_idx as usize) .max() .unwrap_or(0); let mut resmap = vec![0u32; max_attr_idx + 1]; for &(str_idx, resid) in attr_name_to_resid { if (str_idx as usize) < resmap.len() { resmap[str_idx as usize] = resid; } } let mut resmap_chunk = Vec::new(); let resmap_size = 8 + resmap.len() as u32 * 4; push_u16(&mut resmap_chunk, RES_XML_RESOURCE_MAP_TYPE); push_u16(&mut resmap_chunk, 8); push_u32(&mut resmap_chunk, resmap_size); for r in &resmap { push_u32(&mut resmap_chunk, *r); } let inner_len = pool_chunk.len() + resmap_chunk.len() + self.body.len(); let total = 8 + inner_len; let mut out = Vec::new(); push_u16(&mut out, RES_XML_TYPE); push_u16(&mut out, 8); push_u32(&mut out, total as u32); out.extend_from_slice(&pool_chunk); out.extend_from_slice(&resmap_chunk); out.extend_from_slice(&self.body); out } } pub mod val { #[allow(unused_imports)] pub use super::{TYPE_INT_BOOL, TYPE_INT_DEC, TYPE_STRING}; } fn push_u16(v: &mut Vec, x: u16) { v.extend_from_slice(&x.to_le_bytes()); } fn push_u32(v: &mut Vec, x: u32) { v.extend_from_slice(&x.to_le_bytes()); } fn push_utf8_len(v: &mut Vec, len: usize) { if len < 0x80 { v.push(len as u8); } else { v.push(((len >> 8) & 0x7f) as u8 | 0x80); v.push((len & 0xff) as u8); } }