Update src/axml.rs
This commit is contained in:
+278
@@ -0,0 +1,278 @@
|
||||
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<String>,
|
||||
index: std::collections::HashMap<String, u32>,
|
||||
}
|
||||
|
||||
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<u8> {
|
||||
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<u32>,
|
||||
name: u32,
|
||||
raw_value: i32,
|
||||
typ: u8,
|
||||
data: u32,
|
||||
}
|
||||
|
||||
pub struct AxmlBuilder {
|
||||
pool: StringPool,
|
||||
body: Vec<u8>,
|
||||
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<Attr> = 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<u32> {
|
||||
ATTR_RES_IDS.iter().find(|(n, _)| *n == name).map(|(_, id)| *id)
|
||||
}
|
||||
|
||||
pub fn finish(self, attr_name_to_resid: &[(u32, u32)]) -> Vec<u8> {
|
||||
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<u8>, x: u16) {
|
||||
v.extend_from_slice(&x.to_le_bytes());
|
||||
}
|
||||
fn push_u32(v: &mut Vec<u8>, x: u32) {
|
||||
v.extend_from_slice(&x.to_le_bytes());
|
||||
}
|
||||
|
||||
fn push_utf8_len(v: &mut Vec<u8>, 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user