#!/usr/bin/python -u # -*- coding: utf-8 -*- from defs import * from stack import NamespaceStack from string import StringBlock from struct import unpack class AXMLParser(): def __init__(self, reader): self._reader = reader magic = reader.readInt() if magic != CHUNK_AXML_FILE: raise Exception('Bad Magic Number (0x%08lx)!' % magic) # chunkSize reader.skipInt() self._strings = StringBlock(reader) self._namespaces = NamespaceStack() self._operational = True self.resetEventInfo() def resetEventInfo(self): self._event = -1 self._line_number = -1 self._name = -1 self._namespace_uri = -1 self._attributes = None self._id_attrib = -1 self._class_attrib = -1 self._style_attrib = -1 self._decreaseDepth = False def next(self): self.doNext() return self._event def doNext(self): event = self._event while True: if self._decreaseDepth: self._decreaseDepth = False self._namespaces.decDepth() # Fake END_DOCUMENT event. if event == END_TAG and self._namespaces.getDepth() == 1 and self._namespaces.count() == 0: self._event = END_DOCUMENT break if event == START_DOCUMENT: # Fake event, see CHUNK_XML_START_TAG handler. chunk = CHUNK_XML_START_TAG else: chunk = self._reader.readInt() if chunk == CHUNK_RESOURCEIDS: size = self._reader.readInt() if size < 8 or size % 4 != 0: raise Exception('Invalid resource ids size (%d).' % size) self._resource_ids = self._reader.readIntArray(size / 4 - 2) continue if chunk < CHUNK_XML_FIRST or chunk > CHUNK_XML_LAST: raise Exception('Invalid chunk type 0x%08lx.' % chunk) # Fake START_DOCUMENT event. if chunk == CHUNK_XML_START_TAG and event == -1: self._event = START_DOCUMENT break # Common header. self._reader.skipInt() # chunkSize self._line_number = self._reader.readInt() self._reader.skipInt() # 0xffffffff if chunk == CHUNK_XML_START_NAMESPACE or chunk == CHUNK_XML_END_NAMESPACE: if chunk == CHUNK_XML_START_NAMESPACE: prefix = self._reader.readInt() uri = self._reader.readInt() self._namespaces.push(prefix, uri) else: self._reader.skipInt() # prefix self._reader.skipInt() # uri self._namespaces.pop() continue elif chunk == CHUNK_XML_START_TAG: self._namespace_uri = self._reader.readInt() self._name = self._reader.readInt() self._reader.skipInt() # flags ? attribs_count = self._reader.readInt() self._id_attrib = (attribs_count >> 16) - 1 attribs_count &= 0xffff self._class_attrib = self._reader.readInt() self._style_attrib = (self._class_attrib >> 16) - 1 self._class_attrib = (self._class_attrib & 0xffff) - 1 self._attributes = self._reader.readIntArray(attribs_count * ATTRIBUTE_LENGHT) for i in range(ATTRIBUTE_IX_VALUE_TYPE, len(self._attributes), ATTRIBUTE_LENGHT): self._attributes[i] >>= 24 self._namespaces.incDepth() self._event = START_TAG break elif chunk == CHUNK_XML_END_TAG: self._namespaceUri = self._reader.readInt() self._name = self._reader.readInt() self._event = END_TAG self._decreaseDepth = True break elif chunk == CHUNK_XML_TEXT: self._name = self._reader.readInt() self._reader.skipInt() # ??? self._reader.skipInt() # ??? self._event=TEXT break else: raise Exception('Unknown chunck (0x%08lx)' % chunk) ### ESPACES ### def getNamespacePrefix(self, index): name = self._namespaces.getPrefix(index) if name == -1: return '' else: return self._strings.getRaw(name) def getNamespaceUri(self, index): name = self._namespaces.getUri(index) if name == -1: return '' else: return self._strings.getRaw(name) ### NAMES ### def getTagPrefix(self): """Provide the prefix linked to START_TAG or END_TAG.""" name = self._namespaces.findPrefix(self._namespace_uri) if name == -1: return '' else: return self._strings.getRaw(name) + ':' def getTagName(self): """Provide the name linked to START_TAG or END_TAG.""" if self._name == -1 or (self._event != START_TAG and self._event != END_TAG): raise Exception('Invalid tag name.') return self._strings.getRaw(self._name) def getText(self): """Provide the content linked to TEXT.""" if self._name == -1 or self._event != START_TEXT: raise Exception('Invalid text content.') return self._strings.getRaw(self._name) ### ATRIBUTES ### def countAttributes(self): """Count the properties of the current tag.""" if self._event != START_TAG: raise Exception('Invalid event.') return len(self._attributes) / ATTRIBUTE_LENGHT def getAttribPrefix(self, index): """Get the prefix of a given attribute.""" index *= ATTRIBUTE_LENGHT if index >= len(self._attributes): raise Exception('Bad attribute index.') uri = self._attributes[index + ATTRIBUTE_IX_NAMESPACE_URI] name = self._namespaces.findPrefix(uri) if name == -1: return '' else: return self._strings.getRaw(name) + ':' def getAttribName(self, index): """Get the name of a given attribute.""" index *= ATTRIBUTE_LENGHT if index >= len(self._attributes): raise Exception('Bad attribute index.') name = self._attributes[index + ATTRIBUTE_IX_NAME] if name == -1: return '???' else: return self._strings.getRaw(name) def getAttribValue(self, index): """Get the value of a given attribute.""" index *= ATTRIBUTE_LENGHT if index >= len(self._attributes): raise Exception('Bad attribute index.') vtype = self._attributes[index + ATTRIBUTE_IX_VALUE_TYPE] vdata = self._attributes[index + ATTRIBUTE_IX_VALUE_DATA] if vtype == TYPE_NULL: return '???' elif vtype == TYPE_REFERENCE: return '@%s%08X' % (self.getPackage(vdata), vdata) elif vtype == TYPE_ATTRIBUTE: return '?%s%08X' % (self.getPackage(vdata), vdata) if vtype == TYPE_STRING: vdata = self._attributes[index + ATTRIBUTE_IX_VALUE_STRING] return self._strings.getRaw(vdata) elif vtype == TYPE_FLOAT: return '%f' % unpack('=f', pack('=L', vdata))[0] elif vtype == TYPE_DIMENSION: return '%f%s' % (self.complexToFloat(vdata), DIMENSION_UNITS[vdata & COMPLEX_UNIT_MASK]) elif vtype == TYPE_FRACTION: return '%f%s' % (self.complexToFloat(vdata), FRACTION_UNITS[vdata & COMPLEX_UNIT_MASK]) elif vtype == TYPE_INT_HEX: return '0x%08x' % vdata elif vtype == TYPE_INT_BOOLEAN: if vdata == 0: return 'false' else: return 'true' elif vtype >= TYPE_FIRST_COLOR_INT and vtype <= TYPE_LAST_COLOR_INT: return '#%08x' % vdata elif vtype >= TYPE_FIRST_INT and vtype <= TYPE_LAST_INT: return str(vdata) return "<0x%x, 0x%02x>" % (vdata, vtype) def complexToFloat(self, xcomplex): return (float)(xcomplex & 0xffffff00) * RADIX_MULTS[(xcomplex >> 4) & 3]; def getPackage(self, id): if id >> 24 == 1: return "android:" else: return ""