axmldecoder 0.4.0

Decode Android's binary XML format
Documentation
use crate::stringpool::StringPool;
use deku::prelude::*;
use std::rc::Rc;

#[derive(Debug, DekuRead)]
pub(crate) struct BinaryXmlDocument {
    pub(crate) header: ChunkHeader,
    pub(crate) string_pool: StringPool,
    pub(crate) resource_map: ResourceMap,
    #[deku(bytes_read = "header.size -
            u32::try_from(header.header_size).unwrap() -
            string_pool.header.chunk_header.size -
            resource_map.header.size")]
    pub(crate) elements: Vec<XmlNode>,
}

#[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
#[deku(type = "u16")]
pub(crate) enum ResourceType {
    NullType = 0x000,
    StringPool = 0x0001,
    Table = 0x0002,
    Xml = 0x0003,
    XmlStartNameSpace = 0x0100,
    XmlEndNameSpace = 0x101,
    XmlStartElement = 0x0102,
    XmlEndElement = 0x0103,
    XmlCdata = 0x0104,
    XmlLastChunk = 0x017f,
    XmlResourceMap = 0x0180,
    TablePackage = 0x0200,
    TableType = 0x0201,
    TableTypeSpec = 0x0202,
    TableLibrary = 0x0203,
}

#[derive(Clone, Debug, DekuRead, DekuWrite)]
pub(crate) struct ChunkHeader {
    pub(crate) typ: ResourceType,
    pub(crate) header_size: u16,
    pub(crate) size: u32,
}

#[derive(Debug, DekuRead, DekuWrite)]
pub(crate) struct ResourceMap {
    pub(crate) header: ChunkHeader,
    #[deku(count = "(header.size - u32::from(header.header_size)) / 4")]
    pub(crate) resource_ids: Vec<u32>,
}

#[derive(Debug, DekuRead, DekuWrite)]
pub(crate) struct XmlNode {
    pub(crate) header: XmlNodeHeader,
    #[deku(ctx = "header.chunk_header.typ")]
    pub(crate) element: XmlNodeType,
}

#[allow(clippy::enum_variant_names)]
#[derive(Debug, DekuRead, DekuWrite)]
#[deku(ctx = "typ: ResourceType", id = "typ")]
pub(crate) enum XmlNodeType {
    #[deku(id = "ResourceType::XmlStartNameSpace")]
    XmlStartNameSpace(XmlStartNameSpace),
    #[deku(id = "ResourceType::XmlEndNameSpace")]
    XmlEndNameSpace(XmlEndNameSpace),
    #[deku(id = "ResourceType::XmlStartElement")]
    XmlStartElement(XmlStartElement),
    #[deku(id = "ResourceType::XmlEndElement")]
    XmlEndElement(XmlEndElement),
    #[deku(id = "ResourceType::XmlCdata")]
    XmlCdata(XmlCdata),
}

#[derive(Debug, DekuRead, DekuWrite)]
pub(crate) struct XmlNodeHeader {
    pub(crate) chunk_header: ChunkHeader,
    pub(crate) line_no: u32,
    pub(crate) comment: u32,
}

#[derive(Debug, DekuRead, DekuWrite)]
pub(crate) struct XmlStartNameSpace {
    pub(crate) prefix: u32,
    pub(crate) uri: u32,
}

#[derive(Debug, DekuRead, DekuWrite)]
pub(crate) struct XmlEndNameSpace {
    pub(crate) prefix: u32,
    pub(crate) uri: u32,
}

#[derive(Debug, DekuRead, DekuWrite)]
pub(crate) struct XmlAttrExt {
    pub(crate) ns: u32,
    pub(crate) name: u32,
    pub(crate) attribute_start: u16,
    pub(crate) attribute_size: u16,
    pub(crate) attribute_count: u16,
    pub(crate) id_index: u16,
    pub(crate) class_index: u16,
    pub(crate) style_index: u16,
}

#[derive(Debug, DekuRead, DekuWrite)]
pub(crate) struct ResourceValue {
    pub(crate) size: u16,
    pub(crate) res: u8,
    pub(crate) data_type: ResourceValueType,
    pub(crate) data: u32,
}

impl ResourceValue {
    pub(crate) fn get_value(&self, string_pool: &StringPool) -> Rc<String> {
        match &self.data_type {
            ResourceValueType::String => string_pool
                .get(usize::try_from(self.data).unwrap())
                .unwrap(),
            ResourceValueType::Dec => Rc::new(self.data.to_string()),
            ResourceValueType::Hex => Rc::new(format!("0x{}", self.data)),
            ResourceValueType::Boolean => Rc::new(match self.data {
                0 => "false".to_string(),
                _ => "true".to_string(),
            }),
            n => Rc::new(format!("ResourceValueType::{:?}/{}", n, self.data)),
        }
    }
}

#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
#[deku(type = "u8")]
pub(crate) enum ResourceValueType {
    Null = 0x00,
    Reference = 0x01,
    Attribute = 0x02,
    String = 0x03,
    Float = 0x04,
    Dimension = 0x05,
    Fraction = 0x06,
    Dec = 0x10,
    Hex = 0x11,
    Boolean = 0x12,
    ColorArgb8 = 0x1c,
    ColorRgb8 = 0x1d,
    ColorArgb4 = 0x1e,
    ColorRgb4 = 0x1f,
}

#[derive(Debug, DekuRead, DekuWrite)]
pub(crate) struct XmlAttribute {
    pub(crate) ns: u32,
    pub(crate) name: u32,
    pub(crate) raw_value: u32,
    pub(crate) typed_value: ResourceValue,
}

#[derive(Debug, DekuRead, DekuWrite)]
pub(crate) struct XmlStartElement {
    pub(crate) attr_ext: XmlAttrExt,
    #[deku(count = "attr_ext.attribute_count")]
    pub(crate) attributes: Vec<XmlAttribute>,
}

#[derive(Debug, DekuRead, DekuWrite)]
pub(crate) struct XmlEndElement {
    pub(crate) ns: u32,
    pub(crate) name: u32,
}

#[derive(Debug, DekuRead, DekuWrite)]
pub(crate) struct XmlCdata {
    pub(crate) data: u32,
    pub(crate) typed_data: ResourceValue,
}