Apk decode

来源:互联网 发布:数据库修改语句update 编辑:程序博客网 时间:2024/05/18 15:06
# coding: gbk
from struct import *
import sys


output = open("D:\\Trace.txt", "w", encoding = "gbk")


#sys.stdout = output
#sys.stderr = output


def u32_rshift(value, count):
    """
    java >>> operator
    c/c++ unsigned int >> operator
    """
    from ctypes import c_uint
    val = c_uint()
    val.value = value
    val.value >>= count
    return val.value


def i32_lshift(value, count):
    from ctypes import c_int
    val = c_int()
    val.value = value
    val <<= count
    return val.value


class Float:
    @staticmethod
    def intBitsToFloat(value):
        unpack("i", pack("f", value))[0]
class FuncStack:
    levl = 0
    def __init__(self, name, *args):
        self.name = name
        self.args = args
        #print("  " * FuncStack.levl, "%s enter lev = %d %s" % (name, FuncStack.levl, args))
        FuncStack.levl += 1


    def __del__(self):
        FuncStack.levl -= 1
        #print("  " * FuncStack.levl, "%s leave levl = %d" % (self.name, FuncStack.levl))


def log(msg, *args):
    try:
        #print (" " * FuncStack.levl, msg % args)
        pass
    except:
        #print (" " * FuncStack.levl, msg, args)
        pass


class ExtDataInput:
    def __init__(self, strm):
        self.strm = strm


    def __getattr__(self, attr):
        return getattr(self.strm, attr)


    def readShort(self):
        data = self.strm.read(2)
        if len(data) != 2:
            raise EOFError()
        return unpack("h", data)[0]


    def readInt(self):
        data = self.strm.read(4)
        if len(data) != 4:
            raise EOFError()
        return unpack("i", data)[0]


    def readIntArray(self, size):
        array = []
        for i in range(size):
            array.append(self.readInt())
        return array


    def readNulEndedString(self, length, fixed):
        string = []
        while length != 0:
            length -= 1
            ch = self.readShort()
            if ch == 0:
                break;
            string.append(ch)


        if fixed:
            self.skipBytes(length * 2)


        return ''.join(map(chr, string))


    def readByte(self):
        return ord(self.strm.read(1))


    def skipBytes(self, length):
        data = self.strm.read(length)
        if len(data) != length:
            raise EOFError()


    def skipInt(self):
        self.readInt()


    def skipCheckInt(self, excepted):
        data = self.readInt()
        if data != excepted:
            raise Exception("readInt %d is not excepted(%d)!" % (data, excepted))


    def skipCheckShort(self, excepted):
        data = self.readShort()
        if data != excepted:
            raise Exception("readShort %d is not excepted(%d)!" % (data, excepted))


    def skipCheckByte(self, excepted):
        data = self.readByte()
        if data != excepted:
            raise Exception("readByte %d is not excepted(%d)!" % (data, excepted))


    def size(self):
        pos = self.strm.tell()
        self.strm.seek(0, 2)
        size = self.strm.tell()
        self.strm.seek(pos)
        return size


    def getCount(self):
        return self.strm.tell()


class TypedValue:
    TYPE_NULL = 0x00
    TYPE_REFERENCE = 0x01
    TYPE_ATTRIBUTE = 0x02
    TYPE_STRING = 0x03
    TYPE_FLOAT = 0x04
    TYPE_DIMENSION = 0x05
    TYPE_FRACTION = 0x06
    TYPE_FIRST_INT = 0x10
    TYPE_INT_DEC = 0x10
    TYPE_INT_HEX = 0x11
    TYPE_INT_BOOLEAN = 0x12
    TYPE_FIRST_COLOR_INT = 0x1c
    TYPE_INT_COLOR_ARGB8 = 0x1c
    TYPE_INT_COLOR_RGB8 = 0x1d
    TYPE_INT_COLOR_ARGB4 = 0x1e
    TYPE_INT_COLOR_RGB4 = 0x1f
    TYPE_LAST_COLOR_INT = 0x1f
    TYPE_LAST_INT = 0x1f


    COMPLEX_UNIT_SHIFT = 0
    COMPLEX_UNIT_MASK = 0xf
    COMPLEX_UNIT_PX = 0
    COMPLEX_UNIT_DIP = 1
    COMPLEX_UNIT_SP = 2
    COMPLEX_UNIT_PT = 3
    COMPLEX_UNIT_IN = 4
    COMPLEX_UNIT_MM = 5
    COMPLEX_UNIT_FRACTION = 0
    COMPLEX_UNIT_FRACTION_PARENT = 1
    COMPLEX_RADIX_SHIFT = 4
    COMPLEX_RADIX_MASK = 0x3


    COMPLEX_RADIX_23p0 = 0
    COMPLEX_RADIX_16p7 = 1
    COMPLEX_RADIX_8p15 = 2
    COMPLEX_RADIX_0p23 = 3
    COMPLEX_MANTISSA_SHIFT = 8
    COMPLEX_MANTISSA_MASK = 0xffffff
    DENSITY_DEFAULT = 0
    DENSITY_NONE = 0xffff


    MANTISSA_MULT =  1.0 / (1 << COMPLEX_MANTISSA_SHIFT)
    RADIX_MULTS = [1.0 * MANTISSA_MULT, 1.0 / (1 << 7) * MANTISSA_MULT, 1.0 / (1 << 15) * MANTISSA_MULT, 1.0/(1 << 23) * MANTISSA_MULT]


    def __init__(self):
        self.type = 0


    def complexToFloat(self, complex):
        return (complex&(TypedValue.COMPLEX_MANTISSA_MASK << TypedValue.COMPLEX_MANTISSA_SHIFT))  * RADIX_MULTS[(complex >> TypedValue.COMPLEX_RADIX_SHIFT) & TypedValue.COMPLEX_RADIX_MASK]


    DIMENSION_UNIT_STRS = ["px", "dip", "sp", "pt", "in", "mm"]
    FRACTION_UNIT_STRS = ["%", "%p"]


    @staticmethod
    def coerceToString(type, data):
        if type == TypedValue.TYPE_NULL:
            return None;
        if type == TypedValue.TYPE_REFERENCE:
            return "@" + str(data)
        if type == TypedValue.TYPE_ATTRIBUTE:
            return "?" + str(data)
        if type == TypedValue.TYPE_FLOAT:
            return str(Float.intBitsToFloat(data));
        if type == TypedValue.TYPE_DIMENSION:
            return str(complexToFloat(data)) + TypedValue.DIMENSION_UNIT_STRS[
                (data >> TypedValue.COMPLEX_UNIT_SHIFT) & TypedValue.COMPLEX_UNIT_MASK];
        if type == TypedValue.TYPE_FRACTION:
            return str(complexToFloat(data) * 100) + TypedValueFRACTION_UNIT_STRS[
                (data >> TypedValue.COMPLEX_UNIT_SHIFT) & TypedValue.COMPLEX_UNIT_MASK];
        if type == TypedValue.TYPE_INT_HEX:
            return hex(data)
        if type == TypedValue.TYPE_INT_BOOLEAN:
            return  "true" if data != 0 else "false"




        if (type >= TypedValue.TYPE_FIRST_COLOR_INT and type <= TypedValue.TYPE_LAST_COLOR_INT):
            return "#" + hex(data)
        elif (type >= TypedValue.TYPE_FIRST_INT and type <= TypedValue.TYPE_LAST_INT):
            return str(data)


        return  None


class Duo:
    def __init__(self, m1, m2):
        self.m1 = m1
        self.m2 = m2
    def __repr__(self):
        return "<Duo {m1: %s, m2: %s}>" % (self.m1, self.m2)


class ResValue:
    def getValue(self):
        return self.mValue
    def __repr__(self):
        return "<%s %s>" % (self.__class__.__name__, self.__dict__)


class ResFileValue(ResValue):
    def __init__(self, path):
        self.mPath = path


    def getPath(self):
        return self.mPath




class ResScalarValue(ResValue):
    def __init__(self, type, rawValue):
        self.mType = type
        self.mRawValue = rawValue


class ResIntValue(ResScalarValue):
    def __init__(self, value, rawValue, type = "integer"):
        super().__init__(type, rawValue)
        self.mValue = value


class ResDimenValue(ResIntValue):
    def __init__(self, value, rawValue):
        super().__init__(value, rawValue, "dimen")


class ResFractionValue(ResIntValue):
    def __init__(self, value, rawValue):
        super().__init__(value, rawValue, "fraction")


class ResBoolValue(ResScalarValue):
    def __init__(self, value, rawValue):
        super().__init__("bool", rawValue)
        self.mValue = value


class ResColorValue(ResIntValue):
    def __init__(self, value, rawValue):
        super().__init__(value, rawValue, "color")


class ResStringValue(ResScalarValue):
    def __init__(self, value, type = "string"):
        super().__init__(type, value)


class ResFloatValue(ResScalarValue):
    def __init__(self, value, rawValue):
        super().__init__("float", rawValue)
        self.mValue = value


class ResReferenceValue(ResIntValue):
    def __init__(self, package, value, rawValue, theme = False):
        super().__init__(value, rawValue, "reference")
        self.mPackage = package
        self.mTheme = theme


class ResBagValue(ResValue):
    def __init__(self, parent):
        self.mParent = parent


class ResArrayValue(ResBagValue):
    BAG_KEY_ARRAY_START = 0x02000000
    def __init__(self, parent, items):
        self.mItems = []
        if items and isinstance(items[0], Duo):
            for i in items:
                self.mItems.append(i.m2)
        elif items and isinstance(items[0], ResScalarValue):
            self.mItems = items


    def getType(self):
        if len(self.mItems) == 0:
            return None


        type = self.mItems[0].getType()
        if "string" != type and "integer" != type:
            return None


        for i in range(1, len(self.mItems)):
            if type != self.mItems[i].getType():
                return None
        return type


class ResPluralsValue(ResBagValue):
    BAG_KEY_PLURALS_START = 0x01000004
    BAG_KEY_PLURALS_END = 0x01000009
    QUANTITY_MAP = ["other", "zero", "one", "two", "few", "many"]


    def __init__(self, parent, items):
        super().__init__(parent)
        self.mItems = [None] * 6
        for i in range(len(items)):
            self.mItems[items[i].m1 - self.BAG_KEY_PLURALS_START] = items[i].m2


class ResStyleValue(ResBagValue):
    def __init__(self, parent, items, factory):
        super().__init__(parent)
        self.mItems = [None] * len(items)
        for i in range(len(items)):
            self.mItems[i] = Duo(factory.newReference(items[i].m1, None), items[i].m2)


class ResAttr(ResBagValue):
    BAG_KEY_ATTR_TYPE = 0x01000000
    BAG_KEY_ATTR_MIN = 0x01000001
    BAG_KEY_ATTR_MAX = 0x01000002
    BAG_KEY_ATTR_L10N = 0x01000003


    TYPE_REFERENCE = 0x01
    TYPE_STRING = 0x02
    TYPE_INT = 0x04
    TYPE_BOOL = 0x08
    TYPE_COLOR = 0x10
    TYPE_FLOAT = 0x20
    TYPE_DIMEN = 0x40
    TYPE_FRACTION = 0x80
    TYPE_ANY_STRING = 0xee


    TYPE_ENUM = 0x00010000
    TYPE_FLAGS = 0x00020000


    def __init__(self, parentVal, type, min, max, l10n):
        super().__init__(parentVal)
        self.mType = type
        self.mMin = min
        self.mMax = max
        self.mL10n = l10n


    @staticmethod
    def factory(parent, items, factory, pkg):
        type = items[0].m2.getValue()
        scalarType = type & 0xffff
        min = None
        max = None
        l10n = None


        i = 1
        for i in range(1, len(items)):
            if items[i].m1 == ResAttr.BAG_KEY_ATTR_MIN:
                min = items[i].m2.getValue()
                continue
            if items[i].m1 == ResAttr.BAG_KEY_ATTR_MAX:
                max = items[i].m2.getValue()
                continue
            if items[i].m1 == ResAttr.BAG_KEY_ATTR_L10N:
                l10n = items[i].m2.getValue() != 0
                continue
            break


        if i == len(items):
            return ResAttr(parent, scalarType, min, max, l10n)


        attrItems = [None] * (len(items) - i)


        j = 0
        while i < len(items):
            resId = items[i].m1
            pkg.addSynthesizedRes(resId)
            attrItems[j] = Duo(factory.newReference(resId, None), items[i].m2)
            j += 1


            i += 1


        if type & 0xff0000 == ResAttr.TYPE_ENUM:
            return ResEnumAttr(parent, scalarType, min, max, l10n, attrItems)
        elif type & 0xff0000 == ResAttr.TYPE_FLAGS:
            return ResFlagsAttr(parent, scalarType, min, max, l10n, attrItems)


        raise Exception("Could not decode attr value %s" % locals())


class ResEnumAttr(ResAttr):
    def __init__(self, parent, type, min, max, l10n, items):
        super().__init__(parent, type, min, max, l10n)
        self.mItems = items


class ResFlagsAttr(ResAttr):
    class FlagItem:
        def __init__(self, ref, flag):
            self.ref = ref
            self.flag = flag
            self.value = None


        def getValue(self):
            if value is None:
                value = ref.getReferent().getName()
                return value


    def __init__(self, parent, type, min, max, l10n, items):
        super().__init__(parent, type, min, max, l10n)


        self.mItems = FlagItem[items.length]
        for i in range(len(items)):
            self.mItems[i] = FlagItem(items[i].m1, items[i].m2.getValue())




class ResValueFactory:
    mPackage = None


    def __init__(self, package):
        self.mPackage = package


    def factoryByType(self, type, value, rawValue):
        if type == TypedValue.TYPE_REFERENCE:
            return self.newReference(value, rawValue)
        if type == TypedValue.TYPE_ATTRIBUTE:
            return newReference(value, rawValue, true)
        if type == TypedValue.TYPE_STRING:
            return ResStringValue(rawValue)
        if type == TypedValue.TYPE_FLOAT:
            return ResFloatValue(Float.intBitsToFloat(value), rawValue)
        if type == TypedValue.TYPE_DIMENSION:
            return ResDimenValue(value, rawValue)
        if type == TypedValue.TYPE_FRACTION:
            return ResFractionValue(value, rawValue)
        if type == TypedValue.TYPE_INT_BOOLEAN:
            return ResBoolValue(value != 0, rawValue)




        if (type >= TypedValue.TYPE_FIRST_COLOR_INT
                and type <= TypedValue.TYPE_LAST_COLOR_INT):
            return ResColorValue(value, rawValue)


        if (type >= TypedValue.TYPE_FIRST_INT
                and type <= TypedValue.TYPE_LAST_INT):
            return ResIntValue(value, rawValue)


        raise Exception("Invalid value type: %s" % type)




    def factoryByValue(self, value):
        if value.startswith("res/"):
            return ResFileValue(value)


        return ResStringValue(value)


    def factory(self, type, value = None, rawValue = None):
        #log("factory(%s, %s, %s, %s)" % (self, type, value, rawValue))
        if (value is None and rawValue is None):
            value = type
            return self.factoryByValue(value)
        else:
            return self.factoryByType(type, value, rawValue)


    def bagFactory(self, parent, items):
        parentVal = self.newReference(parent, None)


        if len(items) == 0:
            return ResBagValue(parentVal)


        key = items[0].m1
        if key == ResAttr.BAG_KEY_ATTR_TYPE:
            return ResAttr.factory(parentVal, items, self, self.mPackage)


        if key == ResArrayValue.BAG_KEY_ARRAY_START:
            return ResArrayValue(parentVal, items)


        if key >= ResPluralsValue.BAG_KEY_PLURALS_START and key <= ResPluralsValue.BAG_KEY_PLURALS_END:
            return ResPluralsValue(parentVal, items)


        return ResStyleValue(parentVal, items, self)


    def newReference(self, resID, rawValue, theme = False):
        return ResReferenceValue(self.mPackage, resID, rawValue, theme)


class Header:
    TYPE_NONE = -1
    TYPE_TABLE = 0x0002
    TYPE_PACKAGE = 0x0200
    TYPE_TYPE = 0x0202
    TYPE_CONFIG = 0x0201
    types = {
        TYPE_NONE : "TYPE_NONE",
        TYPE_TABLE: "TYPE_TABLE",
        TYPE_PACKAGE: "TYPE_PACKAGE",
        TYPE_TYPE: "TYPE_TYPE",
        TYPE_CONFIG: "TYPE_CONFIG"
    }


    def __init__(self, type, size, len):
        self.type = type
        self.size = size
        self.len = len


    def __repr__(self):
        return "Header(type=%s, size=%d, len=%d)"%(self.types.get(self.type, "ERROR"), self.size, self.len)


    @staticmethod
    def read(strm):
        try:
            type = strm.readShort()
            size = strm.readShort()
            len = strm.readInt()
            return Header(type, size, len)
        except EOFError:
            return Header(Header.TYPE_NONE, 0, 0)


class StringBlock:
    CHUNK_TYPE = 0x001C0001
    UTF8_FLAG = 0x00000100
    def __init__(self):
        self.m_stringOffsets = None
        self.m_strings = bytes()
        self.m_styleOffsets = None
        self.m_styles = None
        self.m_isUTF8 = None


    def __repr__(self):
        return "StringBlock({isUTF8: %s, m_stringOffsets: %d, m_styleOffsets: %d, m_styles: %d, m_strings: %d})" \
            %(self.m_isUTF8, len(self.m_stringOffsets) if self.m_stringOffsets else 0, len(self.m_styleOffsets) if self.m_styleOffsets else 0,
            len(self.m_styles) if self.m_styles else 0, len(self.m_strings))


    def getString(self, index):
        if (index < 0 or self.m_stringOffsets is None or index >= len(self.m_stringOffsets)):
            return None
        offset = self.m_stringOffsets[index]
        length = 0
        if not self.m_isUTF8:
            length = self.getShort(self.m_strings, offset) * 2
            offset += 2
        else:
            offset += self.getVariant(self.m_strings, offset)[1]
            variant = self.getVariant(self.m_strings, offset)
            offset += variant[1]
            length = variant[0]
        return self.decodeString(offset, length)
    def get(self, index):
        return self.getString(index)


    def getShort(self, array, offset):
        return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff


    def decodeString(self, offset, length):
        #print("decodeString(%d, %d)"%(offset, length))
        val =  self.m_strings[offset: offset + length] .decode("UTF8" if self.m_isUTF8 else "UTF16", "ignore")
        log("decodeString(%s)" % val)
        return val


    def getVariant(self, array, offset):
        val = array[offset]
        more = (val & 0x80) != 0
        if not more:
            return [val, 1]
        else:
            return [val << 8 | array[offset + 1] & 0xff, 2]


    def getStyle(self, index):
        if self.m_styleOffsets is None or self.m_styles is None or index >= len(self.m_styleOffsets):
            return None


        offset = m_styleOffsets[index] // 4
        style = None


        count = 0
        for i in range(offset, len(self.m_styles)):
            if self.m_styles[i] == -1:
                break


            count += 1


        if count == 0 or (count % 3) != 0:
            return None


        style = [0] * count


        i = offset
        j = 0
        while i < len(self.m_styles):
            if self.m_styles[i] == -1:
                break


            style[j] = m_styles[i]
            j += 1
            i += 1


        return style


    def getHTML(self, index):
        log("getHTML(%d)", index)
        return self.getString(index)


        #TODO:
        raw = self.getString(index)
        if raw is None:
            return raw


        style = getStyle(index)
        if style is None:
            return ResXmlEncoders.escapeXmlChars(raw)


        #html = new StringBuilder(raw.length() + 32);
        opened = [0] * (style.length // 3)
        offset = 0
        depth = 0
        while True:
            i = -1
            j = None
            for j in range(0, len(style), 3):
                if style[j + 1] == -1:
                    continue


                if i == -1 or style[i + 1] > style[j + 1]:
                    i = j


            start = style[i + 1] if (i != -1) else len(raw)


            for j in range(depth - 1, -1, -1):
                last = opened[j]
                end = style[last + 2]
                if end >= start:
                    break


                if offset <= end:
                    html.append(ResXmlEncoders.escapeXmlChars(raw[offset: end + 1]))
                    offset = end + 1


                self.outputStyleTag(getString(self.style[last]), html, True)


            depth = j + 1
            if offset < start:
                html.append(ResXmlEncoders.escapeXmlChars(raw[offset: start]))
                offset = start


            if i == -1:
                break


            outputStyleTag(getString(style[i]), html, False)
            style[i + 1] = -1
            opened[depth] = i
            depth += 1


        return html.toString()


    @staticmethod
    def read(strm):
        type = strm.readInt()
        assert(type == StringBlock.CHUNK_TYPE)
        chunkSize = strm.readInt()
        stringCount = strm.readInt()
        styleOffsetCount = strm.readInt()
        flags = strm.readInt()
        stringsOffset = strm.readInt()
        stylesOffset = strm.readInt()
        block = StringBlock()
        block.m_isUTF8 = flags & StringBlock.UTF8_FLAG
        block.m_stringOffsets = strm.readIntArray(stringCount)
        if styleOffsetCount != 0:
            block.m_styleOffsets = strm.readIntArray(styleOffsetCount)
        size = (chunkSize if stylesOffset == 0 else stylesOffset) - stringsOffset
        if size % 4 != 0:
            raise Exception("String data size is not multiple of 4 (%d)."%size)
        block.m_strings = strm.read(size)
        assert(len(block.m_strings) == size)
        if stylesOffset != 0:
            size = chunkSize - stylesOffset
            if size % 4 != 0:
                raise Exception("Style data size is not multiple of 4 (%d)."%size)
            block.m_styles = strm.readIntArray(size // 4)
        return block


class ResID:
    def __init__(self, *args):
        try:
            self.__init_depth += 1
        except:
            self.__init_depth = 0


        #print("ResID(" +  ", ".join(str(arg) for arg in args) + ") levl = %d" % self.__init_depth)
        if (self.__init_depth > 2):
            raise Exception("ResID()")


        package, type, entry, id = (None, None, None, None)
        if len(args) == 1:
            id = args[0]
            self.__init__(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id)
        elif len(args) == 3:
            package, type, entry = args
            self.__init__(package, type, entry, ((package << 24) + (type << 16)) + entry)
        elif len(args) == 4:
            package, type, entry, id = args


            self.package = package
            self.type = type
            self.entry = entry
            self.id = id
        else:
            raise Exception("invalid args: ", args)


    def __repr__(self):
        return "ResID<{package: %d, type: %d, entry: %d, id: %d}>" % (self.package, self.type, self.entry, self.id)


class FlagsOffset:
    def __init__(self, offset, count):
        self.offset = offset
        self.count = count
    def __repr__(self):
        return "FlagsOffset<{offset: %d, count: %d}>" % (self.offset, self.count)


class ResTable:
    """
    AndrolibResources mAndRes;
    Map<Integer, ResPackage> mPackagesById ;
    Map<String, ResPackage> mPackagesByName;
    Set<ResPackage> mMainPackages;
    Set<ResPackage> mFramePackages;
    String mFrameTag;
    """
    def __init__(self, andRes = None):
        self.mAndRes = andRes
        self.mPackagesById = {}
        self.mPackagesByName = {}
        self.mMainPackages = set()
        self.mFramePackages = set()
        self.mFrameTag = None


    def listMainPackage(self):
        return self.mMainPackages


    def getPackage(self, arg):
        if isinstance(arg, str):
            name = arg
            return self.getPackageByName(name)
        elif isinstance(arg, int):
            id = arg
            return self.getPackageById(id)


    def getPackageByName(self, name):
        pkg = self.mPackagesByName.get(name)
        if pkg is None:
            raise Exception("package: name=%s" % name)
        return pkg


    def getPackageById(self, id):
        pkg = self.mPackagesById.get(id)
        if pkg is not None:
            return pkg
        if self.mAndRes is not None:
            return self.mAndRes.loadFrameworkPkg(self, id, self.mFrameTag)
        raise Exception("package: id=%d" % id)


    def getResSpec(self, resID):
        if isinstance(resID, int):
            return self.getResSpec(ResID(resID))
        else:
            return self.getPackage(resID.package).getResSpec(resID)


    def addPackage(self, pkg, main):
        id = pkg.getId()
        if id in self.mPackagesById:
            raise Exception("Multiple packages: id=%s" % id)
        name = pkg.getName()
        if name in self.mPackagesByName:
            raise Exception("Multiple packages: name=%s" % name)


        self.mPackagesById[id] = pkg
        self.mPackagesByName[name] = pkg
        if main:
            self.mMainPackages.add(pkg)
        else:
            self.mFramePackages.add(pkg)




class ResType:
    """
    String mName;
    Map<String, ResResSpec> mResSpecs;
    ResTable mResTable;
    ResPackage mPackage;
    """
    def __init__(self, name, resTable, package):
        self.mName = name
        self.mResTable = resTable
        self.mPackage = package
        self.mResSpecs = {}


    def getName(self):
        return self.mName


    def addResSpec(self, spec):
        if self.mResSpecs.get(spec.getName(), None) is None:
            self.mResSpecs[spec.getName()] = spec
        else:
            log("Multiple res specs: %s/%s \nold = %s,\n new = %s" % (self.getName(), spec.getName(), self.mResSpecs[spec.getName()], spec))
            #raise Exception("Multiple res specs: %s/%s \nold = %s,\n new = %s" % (self.getName(), spec.getName(), self.mResSpecs[spec.getName()], spec))


class ResConfigFlags:
    ORIENTATION_ANY  = 0
    ORIENTATION_PORT = 1
    ORIENTATION_LAND = 2
    ORIENTATION_SQUARE = 3


    TOUCHSCREEN_ANY  = 0
    TOUCHSCREEN_NOTOUCH  = 1
    TOUCHSCREEN_STYLUS  = 2
    TOUCHSCREEN_FINGER  = 3


    DENSITY_DEFAULT = 0
    DENSITY_LOW = 120
    DENSITY_MEDIUM = 160
    DENSITY_HIGH = 240
    DENSITY_XHIGH = 320
    DENSITY_NONE = -1


    KEYBOARD_ANY  = 0
    KEYBOARD_NOKEYS  = 1
    KEYBOARD_QWERTY  = 2
    KEYBOARD_12KEY  = 3


    NAVIGATION_ANY  = 0
    NAVIGATION_NONAV  = 1
    NAVIGATION_DPAD  = 2
    NAVIGATION_TRACKBALL  = 3
    NAVIGATION_WHEEL  = 4


    MASK_KEYSHIDDEN = 0x3
    KEYSHIDDEN_ANY = 0x0
    KEYSHIDDEN_NO = 0x1
    KEYSHIDDEN_YES = 0x2
    KEYSHIDDEN_SOFT = 0x3


    MASK_NAVHIDDEN = 0xc
    NAVHIDDEN_ANY = 0x0
    NAVHIDDEN_NO = 0x4
    NAVHIDDEN_YES = 0x8


    MASK_SCREENSIZE = 0x0f
    SCREENSIZE_ANY  = 0x00
    SCREENSIZE_SMALL = 0x01
    SCREENSIZE_NORMAL = 0x02
    SCREENSIZE_LARGE = 0x03
    SCREENSIZE_XLARGE = 0x04


    MASK_SCREENLONG = 0x30
    SCREENLONG_ANY = 0x00
    SCREENLONG_NO = 0x10
    SCREENLONG_YES = 0x20


    MASK_UI_MODE_TYPE = 0x0f
    UI_MODE_TYPE_ANY = 0x00
    UI_MODE_TYPE_NORMAL = 0x01
    UI_MODE_TYPE_DESK = 0x02
    UI_MODE_TYPE_CAR = 0x03
    UI_MODE_TYPE_TELEVISION = 0x04


    MASK_UI_MODE_NIGHT = 0x30
    UI_MODE_NIGHT_ANY = 0x00
    UI_MODE_NIGHT_NO = 0x10
    UI_MODE_NIGHT_YES = 0x20


    def __hash__(self):
        _hash = 3
        _hash = 97 * _hash + hash(self.mQualifiers)
        return _hash


    def __repr__(self):
        return "<ResType %s>" % self.generateQualifiers()


    def __init__(self, mcc, mnc, language, country, orientation,
            touchscreen, density, keyboard, navigation, inputFlags,
            screenWidth, screenHeight, sdkVersion, screenLayout, uiMode,
            smallestScreenWidthDp, screenWidthDp, screenHeightDp, isInvalid):


#        self.mcc = 0
#        self.mnc = 0
#        self.language = bytearray([0, 0])
#        self.country = bytearray([0, 0])
#        self.orientation = self.ORIENTATION_ANY
#        self.touchscreen = self.TOUCHSCREEN_ANY
#        self.density = self.DENSITY_DEFAULT
#        self.keyboard = self.KEYBOARD_ANY
#        self.navigation = self.NAVIGATION_ANY
#        self.inputFlags = self.KEYSHIDDEN_ANY | self.NAVHIDDEN_ANY
#        self.screenWidth = 0
#        self.screenHeight = 0
#        self.sdkVersion = 0
#        self.screenLayout = self.SCREENLONG_ANY | self.SCREENSIZE_ANY
#        self.uiMode = self.UI_MODE_TYPE_ANY | self.UI_MODE_NIGHT_ANY
#        self.smallestScreenWidthDp = 0
#        self.screenWidthDp = 0
#        self.screenHeightDp = 0
#        self.isInvalid = False
#        self.mQualifiers = ""
        if orientation < 0 or orientation > 3:
            log("Invalid orientation value: %s", orientation)
            orientation = 0
            isInvalid = True


        if touchscreen < 0 or touchscreen > 3:
            log("Invalid touchscreen value: %s", touchscreen)
            touchscreen = 0
            isInvalid = True


        if density < -1:
            log("Invalid density value: %s", density)
            density = 0
            isInvalid = True


        if keyboard < 0 or keyboard > 3:
            log("Invalid keyboard value: %s", keyboard)
            keyboard = 0
            isInvalid = True


        if navigation < 0 or navigation > 4:
            log("Invalid navigation value: ", navigation)
            navigation = 0
            isInvalid = True


        self.mcc = mcc
        self.mnc = mnc
        self.language = language
        self.country = country
        self.orientation = orientation
        self.touchscreen = touchscreen
        self.density = density
        self.keyboard = keyboard
        self.navigation = navigation
        self.inputFlags = inputFlags
        self.screenWidth = screenWidth
        self.screenHeight = screenHeight
        self.sdkVersion = sdkVersion
        self.screenLayout = screenLayout
        self.uiMode = uiMode
        self.smallestScreenWidthDp = smallestScreenWidthDp
        self.screenWidthDp = screenWidthDp
        self.screenHeightDp = screenHeightDp
        self.isInvalid = isInvalid
        self.mQualifiers = self.generateQualifiers()


    def getNaturalSdkVersionRequirement(self):
        if self.smallestScreenWidthDp != 0 or self.screenWidthDp != 0 or self.screenHeightDp != 0:
            return 13


        if self.uiMode & self.MASK_UI_MODE_TYPE | self.MASK_UI_MODE_NIGHT != 0:
            return 8


        if self.screenLayout & (self.MASK_SCREENSIZE | self.MASK_SCREENLONG) != 0 or self.density != self.DENSITY_DEFAULT:
            return 4


        return 0


    def generateQualifiers(self):
        class StringBuffer:
            def __init__(self):
                import io
                self.buff = io.StringIO()


            def __str__(self):
                return self.buff.getvalue()


            def append(self, data):
                data = str(data)
                self.buff.write(data)
                return self
            def toString(self):
                return self.buff.getvalue()


        ret = StringBuffer()
        if self.mcc != 0:
            ret.append("-mcc").append("%03d" % mcc)
            if mnc != 0:
                ret.append("-mnc").append(mnc)


        if self.language[0] != '\00':
            ret.append('-').append(self.language)
            if self.country[0] != '\00':
                ret.append("-r").append(self.country)


        if self.smallestScreenWidthDp != 0:
            ret.append("-sw").append(self.smallestScreenWidthDp).append("dp")


        if self.screenWidthDp != 0:
            ret.append("-w").append(self.screenWidthDp).append("dp")


        if self.screenHeightDp != 0:
            ret.append("-h").append(self.screenHeightDp).append("dp")


        if self.screenLayout & self.MASK_SCREENSIZE == self.SCREENSIZE_SMALL:
            ret.append("-small")
        elif self.screenLayout & self.MASK_SCREENSIZE == self.SCREENSIZE_NORMAL:
            ret.append("-normal")
        elif self.screenLayout & self.MASK_SCREENSIZE == self.SCREENSIZE_LARGE:
                ret.append("-large")
        elif self.screenLayout & self.MASK_SCREENSIZE == self.SCREENSIZE_XLARGE:
            ret.append("-xlarge")


        if self.screenLayout & self.MASK_SCREENLONG == self.SCREENLONG_YES:
            ret.append("-long")
        elif self.screenLayout & self.MASK_SCREENLONG == self.SCREENLONG_NO:
            ret.append("-notlong")


        if self.orientation == self.ORIENTATION_PORT:
            ret.append("-port")
        elif self.orientation == self.ORIENTATION_LAND:
            ret.append("-land")
        elif self.orientation == self.ORIENTATION_SQUARE:
            ret.append("-square")


        if self.uiMode & self.MASK_UI_MODE_TYPE == self.UI_MODE_TYPE_CAR:
            ret.append("-car")
        elif self.uiMode & self.MASK_UI_MODE_TYPE == self.UI_MODE_TYPE_DESK:
            ret.append("-desk")
        elif self.uiMode & self.MASK_UI_MODE_TYPE == self.UI_MODE_TYPE_TELEVISION:
            ret.append("-television")


        if self.uiMode & self.MASK_UI_MODE_NIGHT == self.UI_MODE_NIGHT_YES:
            ret.append("-night")
        elif self.uiMode & self.MASK_UI_MODE_NIGHT == self.UI_MODE_NIGHT_NO:
            ret.append("-notnight")


        if self.density == self.DENSITY_DEFAULT:
            pass
        elif self.density == self.DENSITY_LOW:
            ret.append("-ldpi")
        elif self.density == self.DENSITY_MEDIUM:
            ret.append("-mdpi")
        elif self.density == self.DENSITY_HIGH:
            ret.append("-hdpi")
        elif self.density == self.DENSITY_XHIGH:
            ret.append("-xhdpi")
        elif self.density == self.DENSITY_NONE:
            ret.append("-nodpi")
        else:
            ret.append('-').append(density).append("dpi")




        if self.touchscreen == self.TOUCHSCREEN_NOTOUCH:
            ret.append("-notouch")
        elif self.touchscreen == self.TOUCHSCREEN_STYLUS:
            ret.append("-stylus")
        elif self.touchscreen == self.TOUCHSCREEN_FINGER:
            ret.append("-finger")


        if self.inputFlags & self.MASK_KEYSHIDDEN == self.KEYSHIDDEN_NO:
            ret.append("-keysexposed")
        elif self.inputFlags & self.MASK_KEYSHIDDEN == self.KEYSHIDDEN_YES:
            ret.append("-keyshidden")
        elif self.inputFlags & self.MASK_KEYSHIDDEN == self.KEYSHIDDEN_SOFT:
            ret.append("-keyssoft")


        if self.keyboard == self.KEYBOARD_NOKEYS:
            ret.append("-nokeys")
        elif self.keyboard == self.KEYBOARD_QWERTY:
            ret.append("-qwerty")
        elif self.keyboard == self.KEYBOARD_12KEY:
            ret.append("-12key")


        if self.inputFlags & self.MASK_NAVHIDDEN == self.NAVHIDDEN_NO:
            ret.append("-navexposed")
        elif self.inputFlags & self.MASK_NAVHIDDEN == self.NAVHIDDEN_YES:
            ret.append("-navhidden")


        if self.navigation == self.NAVIGATION_NONAV:
            ret.append("-nonav")
        elif self.navigation == self.NAVIGATION_DPAD:
            ret.append("-dpad")
        elif self.navigation == self.NAVIGATION_TRACKBALL:
            ret.append("-trackball")
        elif self.navigation == self.NAVIGATION_WHEEL:
            ret.append("-wheel")


        if self.screenWidth != 0 and self.screenHeight != 0:
            if self.screenWidth > self.screenHeight:
                ret.append("-%dx%d" % (self.screenWidth, self.screenHeight))
            else:
                ret.append("-%dx%d" % (self.screenHeight, self.screenWidth))


        if self.sdkVersion > self.getNaturalSdkVersionRequirement():
            ret.append("-v").append(self.sdkVersion)


        if self.isInvalid:
            ret.append("-ERR" + sErrCounter)
            sErrCounter += 1


        return ret.toString()


class ResConfig:
    """
    ResConfigFlags mFlags;
    Map<ResResSpec, ResResource> mResources;
    """


    def __init__(self, flags):
        self.mFlags = flags
        self.mResources = {}


    def addResource(self, res, overwrite = False):
        spec = res.getResSpec()
        exists = self.mResources.get(spec, None) is not None
        if exists and not overwrite:
            raise Exception("Multiple resources: spec=%s, config=%s" % (spec, self))
        else:
            self.mResources[spec] = res
    def getFlags(self):
        return self.mFlags


class ResResource:
    """
    ResConfig mConfig;
    ResResSpec mResSpec;
    ResValue mValue;
    """
    def __init__(self, config, spec, value):
        self.mConfig = config
        self.mResSpec = spec
        self.mValue = value


    def getResSpec(self):
        return self.mResSpec


    def getConfig(self):
        return self.mConfig


class ResResSpec:
    """
    ResID mId;
    String mName;
    ResPackage mPackage;
    ResType mType;
    Map<ResConfigFlags, ResResource> mResources;
    """
    def __init__(self, id, name, pkg, type):
        self.mId = id
        self.mName = name
        self.mPackage = pkg
        self.mType = type
        self.mResources = {}


    def getId(self):
        return self.mId


    def getName(self):
        return self.mName


    def addResource(self, res, overwrite = False):
        flags = res.getConfig().getFlags()
        exists = self.mResources.get(flags, None) is not None
        if exists and not overwrite:
            raise  Exception("Multiple resources: spec=%s, config=%s" % (self, flags))
        else:
            self.mResources[flags] = res


class ResPackage:
    """
    ResTable mResTable;
    int mId;
    String mName;
    Map<ResID, ResResSpec> mResSpecs;
    Map<ResConfigFlags, ResConfig> mConfigs;
    Map<String, ResType> mTypes;
    Set<ResID> mSynthesizedRes;
    ResValueFactory mValueFactory;
    """
    def __init__(self, resTable, id, name):
        self.mResTable = resTable
        self.mId = id
        self.mName = name
        self.mResSpecs = {}
        self.mConfigs = {}
        self.mTypes = {}
        self.mSynthesizedRes = set()
        self.mValueFactory = None




    def __hash__(self):
        _hash = 7
        _hash = 37 * _hash + (hash(self.mResTable) if self.mResTable is not None  else 0)
        _hash = 37 * _hash + self.mId
        return _hash


    def addType(self, type):
        if self.mTypes.get(type.getName(), None) is not None:
            raise Exception("Multiple types: %s" % type)
        self.mTypes[type.getName()] = type


    def getOrCreateConfig(self, flags):
        config = self.mConfigs.get(flags)
        if config is None:
            config = ResConfig(flags)
            self.mConfigs[flags] = config


        return config


    def getValueFactory(self):
        if self.mValueFactory is None:
            self.mValueFactory = ResValueFactory(self)


        return self.mValueFactory;


    def hasResSpec(self, resID):
        return resID in self.mResSpecs


    def addResSpec(self, spec):
        if self.mResSpecs.get(spec.getId(), None) is None:
            self.mResSpecs[spec.getId()] = spec
        else:
            raise Exception("Multiple resource specs: %s" % spec)


    def addResource(self, res):
        pass


    def addSynthesizedRes(self, resId):
        self.mSynthesizedRes.add(ResID(resId))


    def getResTable(self):
        return self.mResTable


    def getId(self):
        return self.mId


    def getName(self):
        return self.mName


class ARSCData:
    """
    ResPackage [] mPackages;
    FlagsOffset[] mFlagsOffsets;
    ResTable mResTable;
    """
    def __init__(self, packages, flagsOffsets, resTable):
        self.mPackages = packages
        self.mFlagsOffsets = flagsOffsets
        self.mResTable = resTable


    def __repr__(self):
        return ("<ARSCData {mPackages: %s, mFlagsFossets: %s, mResTable: %s}>" % (self.mPackages, self.mFlagsOffsets, self.mResTable)).replace(",", ",\r\n")




class ARSCDecoder:
    """
    ResPackage[] mPackages;
    FlagsOffset[] mFlagsOffsets;
    ResTable mResTable;
    """


    ENTRY_FLAG_COMPLEX = 0x0001
    KNOWN_CONFIG_BYTES = 36


    def __init__(self, arscStream, resTable, storeFlagsOffsets, keepBroken):
        self.mIn = None
        self.mResTable = None
        self.mCountIn = None
        self.mFlagsOffsets = None
        self.mKeepBroken = None


        self.mHeader = None
        self.mTableStrings = None
        self.mTypeNames = None
        self.mSpecNames = None
        self.mPkg = None
        self.mType = None
        self.mConfig = None
        self.mResId = 0
        self.mMissingResSpecs = None




        self.mPackages = None
        self.mFlagsOffsets = None
        self.ResTable = None


        if storeFlagsOffsets:
            mCountIn = arscStream
            self.mFlagsOffsets = []
        else:
            self.mCountIn = None
            self.mFlagsOffsets = None


        self.mIn = arscStream
        self.mResTable = resTable
        self.mKeepBroken = keepBroken


    @staticmethod
    def decode(arscStream, findFlagsOffsets, keepBroken, resTable):
        stack = FuncStack("decode()", arscStream.tell())
        if resTable is None:
            resTable = ResTable()


        decoder = ARSCDecoder(arscStream, resTable, findFlagsOffsets, keepBroken)
        pkgs = decoder.readTable()
        return ARSCData(pkgs, None if decoder.mFlagsOffsets is None else decoder.mFlagsOffsets[:], resTable)


    def readTable(self):
        stack = FuncStack("readTable()", self.mIn.tell())
        self.nextChunkCheckType(Header.TYPE_TABLE)
        packageCount = self.mIn.readInt()


        log("packageCount = %d", packageCount)


        self.mTableStrings = StringBlock.read(self.mIn)
        packages = [None] * packageCount


        self.nextChunk()
        for i in range(packageCount):
            packages[i] = self.readPackage()


        return packages


    def readPackage(self):
        stack = FuncStack("readPackage()", self.mIn.tell())
        self.checkChunkType(Header.TYPE_PACKAGE)
        id = self.mIn.readInt() % 256
        name = self.mIn.readNulEndedString(128, True)
        log("package name = %s",name)
        log("typeNameStrings: %s", self.mIn.readInt()) #typeNameStrings
        log("typeNameCount: %s", self.mIn.readInt()) #typeNameCount
        log("specNameStrings: %s", self.mIn.readInt()) #specNameStrings
        log("specNameCount: %s", self.mIn.readInt()) #specNameCount


        self.mTypeNames = StringBlock.read(self.mIn)
        self.mSpecNames = StringBlock.read(self.mIn)


        log("mTypeNames: %s", self.mTypeNames)
        log("mSpecNames: %s", self.mSpecNames)


        self.mResId = id << 24
        self.mPkg = ResPackage(self.mResTable, id, name)


        self.nextChunk()
        while self.mHeader.type == Header.TYPE_TYPE:
            self.readType()


        return self.mPkg;


    def readType(self):
        stack = FuncStack("readType()", self.mIn.tell())
        self.checkChunkType(Header.TYPE_TYPE);
        id =  self.mIn.readByte()
        self.mIn.skipBytes(3)
        entryCount = self.mIn.readInt();


        self.mMissingResSpecs = [True] * entryCount


        if self.mFlagsOffsets is not None:
            self.mFlagsOffsets.append(FlagsOffset(self.mCountIn.getCount(), entryCount));


        self.mIn.skipBytes(entryCount * 4)


        self.mResId = (0xff000000 & self.mResId) | id << 16
        self.mType = ResType(self.mTypeNames.getString(id - 1), self.mResTable, self.mPkg)
        self.mPkg.addType(self.mType)


        while self.nextChunk().type == Header.TYPE_CONFIG:
            self.readConfig()


        self.addMissingResSpecs()


        return self.mType


    def readConfig(self):
        stack = FuncStack("readConfig()", self.mIn.tell())
        self.checkChunkType(Header.TYPE_CONFIG)
        self.mIn.skipInt()
        entryCount = self.mIn.readInt()
        self.mIn.skipInt()


        flags = self.readConfigFlags()
        entryOffsets = self.mIn.readIntArray(entryCount)


        if flags.isInvalid:
            resName = mType.getName() + flags.getQualifiers()
            if self.mKeepBroken:
                log("Invalid config flags detected: " + resName)
            else:
                log("Invalid config flags detected. Dropping resources: " + resName)


        self.mConfig = None if (flags.isInvalid and not self.mKeepBroken) else self.mPkg.getOrCreateConfig(flags)
        for i in range(len(entryOffsets)):
            if entryOffsets[i] != -1:
                log("entryOffsets[%d] = %d is OK!", i, entryOffsets[i])
                self.mMissingResSpecs[i] = False;
                self.mResId = (self.mResId & 0xffff0000) | i
                self.readEntry()
            else:
                log("entryOffsets[%d] = %d is invalid!", i, entryOffsets[i])
        return self.mConfig


    def readEntry(self):
        stack = FuncStack("readEntry()", self.mIn.tell())
        log("entry.size = %d", self.mIn.readShort())
        flags = self.mIn.readShort()
        specNamesId = self.mIn.readInt()


        value = self.readValue() if (flags & self.ENTRY_FLAG_COMPLEX) == 0 else self.readComplexEntry()


        if self.mConfig is None:
            return


        resId = ResID(self.mResId)
        spec = None
        if self.mPkg.hasResSpec(resId):
            spec = self.mPkg.getResSpec(resId)
        else:
            spec = ResResSpec(resId, self.mSpecNames.getString(specNamesId), self.mPkg, self.mType)
            self.mPkg.addResSpec(spec)
            self.mType.addResSpec(spec)


        res = ResResource(self.mConfig, spec, value)


        self.mConfig.addResource(res)
        spec.addResource(res)
        self.mPkg.addResource(res)


    def readComplexEntry(self):
        stack = FuncStack("readComplexEntry()", self.mIn.tell())
        parent = self.mIn.readInt()
        count = self.mIn.readInt()


        factory = self.mPkg.getValueFactory()
        items = [None] * count;
        for i in range(count):
            items[i] = Duo(self.mIn.readInt(), self.readValue())


        return factory.bagFactory(parent, items)


    def readValue(self):
        stack = FuncStack("readValue()", self.mIn.tell())
        self.mIn.skipCheckShort(8)
        self.mIn.skipCheckByte(0)
        type = self.mIn.readByte()
        data = self.mIn.readInt()


        return  self.mPkg.getValueFactory().factory(self.mTableStrings.getHTML(data)) if type == TypedValue.TYPE_STRING else  self.mPkg.getValueFactory().factory(type, data, None)


    def readConfigFlags(self):
        stack = FuncStack("readConfigFlags()", self.mIn.tell())
        size = self.mIn.readInt()
        if size < 28:
            raise Exception("Config size < 28")


        isInvalid = False


        mcc = self.mIn.readShort()
        mnc = self.mIn.readShort()


        language = bytes((self.mIn.readByte(), self.mIn.readByte()))
        country =  bytes((self.mIn.readByte(), self.mIn.readByte()))


        orientation = self.mIn.readByte()
        touchscreen = self.mIn.readByte()
        density = self.mIn.readShort()


        keyboard = self.mIn.readByte()
        navigation = self.mIn.readByte()
        inputFlags = self.mIn.readByte()
        self.mIn.skipBytes(1)


        screenWidth = self.mIn.readShort()
        screenHeight = self.mIn.readShort()


        sdkVersion = self.mIn.readShort()
        self.mIn.skipBytes(2)


        screenLayout = 0
        uiMode = 0
        smallestScreenWidthDp = 0
        if size >= 32:
            screenLayout = self.mIn.readByte()
            uiMode = self.mIn.readByte()
            smallestScreenWidthDp = self.mIn.readShort()


        screenWidthDp = 0
        screenHeightDp = 0
        if size >= 36:
            self.screenWidthDp = mIn.readShort()
            self.screenHeightDp = mIn.readShort()




        exceedingSize = size - self.KNOWN_CONFIG_BYTES
        if exceedingSize > 0:
            buf = bytearray(exceedingSize)
            self.mIn.readFully(buf)
            exceedingBI = int(buf)


            if exceedingBI == 0:
                log("Config flags size > %d, but exceeding bytes are all zero, so it should be ok." % KNOWN_CONFIG_BYTES)
            else:
                log("Config flags size > %d. Exceeding bytes: 0x%X." % (KNOWN_CONFIG_BYTES, exceedingBI))
                isInvalid = True


        return ResConfigFlags(mcc, mnc, language, country, orientation,
            touchscreen, density, keyboard, navigation, inputFlags,
            screenWidth, screenHeight, sdkVersion, screenLayout, uiMode,
            smallestScreenWidthDp, screenWidthDp, screenHeightDp, isInvalid)


    def addMissingResSpecs(self):
        stack = FuncStack("addMissingResSpecs()", self.mIn.tell())
        resId = self.mResId & 0xffff0000


        for i in range(len(self.mMissingResSpecs)):
            if not self.mMissingResSpecs[i]:
                continue;


            spec = ResResSpec(ResID(resId | i), "APKTOOL_DUMMY_%04x" % i, self.mPkg, self.mType)
            self.mPkg.addResSpec(spec)
            self.mType.addResSpec(spec)


            value = ResBoolValue(False, None);
            res = ResResource(self.mPkg.getOrCreateConfig(ResConfigFlags()), spec, value)
            self.mPkg.addResource(res)
            self.mConfig.addResource(res)
            spec.addResource(res)


    def nextChunk(self):
        stack = FuncStack("nextChunk()", self.mIn.tell())
        self.mHeader = Header.read(self.mIn)
        log("chunk = %s", self.mHeader)
        return self.mHeader


    def checkChunkType(self, expectedType):
        FuncStack("checkChunkType()", self.mIn.tell())
        if self.mHeader.type != expectedType:
            raise Exception("Invalid chunk type: expected=0x%08x, got=0x%08x" % (expectedType, mHeader.type))


    def nextChunkCheckType(self, expectedType):
        stack = FuncStack("nextChunkCheckType()", self.mIn.tell())
        self.nextChunk()
        self.checkChunkType(expectedType)


class ResAttrDecoder:
    def __init__(self):
        self.mCurrentPackage = None


    def decode(self, type, value, rawValue, attrResId):
        resValue = self.mCurrentPackage.getValueFactory().factory(type, value, rawValue)
        decoded = None
        if attrResId != 0:
            attr = self.getCurrentPackage().getResTable().getResSpec(attrResId).getDefaultResource().getValue()
            decoded = attr.convertToResXmlFormat(resValue)


        return decoded if decoded is not None else resValue.encodeAsResXmlAttr()


    def getCurrentPackage(self):
        if self.mCurrentPackage is None:
            raise Exception("Current package not set")


        return self.mCurrentPackage


    def setCurrentPackage(self, currentPackage):
        self.mCurrentPackage = currentPackage


class AXmlResourceParser:
    """ExtDataInput m_reader;
    ResAttrDecoder mAttrDecoder;
    AndrolibException mFirstError;


    boolean m_operational = false;
    StringBlock m_strings;
    int[] m_resourceIDs;
    NamespaceStack m_namespaces = new NamespaceStack();
    boolean m_decreaseDepth;
    int m_event;
    int m_lineNumber;
    int m_name;
    int m_namespaceUri;
    int[] m_attributes;
    int m_idAttribute;
    int m_classAttribute;
    int m_styleAttribute;
    """


    E_NOT_SUPPORTED = "Method is not supported."


    ATTRIBUTE_IX_NAMESPACE_URI =    0
    ATTRIBUTE_IX_NAME =             1
    ATTRIBUTE_IX_VALUE_STRING =     2
    ATTRIBUTE_IX_VALUE_TYPE =       3
    ATTRIBUTE_IX_VALUE_DATA =       4
    ATTRIBUTE_LENGHT =              5




    CHUNK_AXML_FILE =           0x00080003
    CHUNK_RESOURCEIDS =         0x00080180
    CHUNK_XML_FIRST =           0x00100100
    CHUNK_XML_START_NAMESPACE = 0x00100100
    CHUNK_XML_END_NAMESPACE =   0x00100101
    CHUNK_XML_START_TAG =       0x00100102
    CHUNK_XML_END_TAG =         0x00100103
    CHUNK_XML_TEXT =            0x00100104
    CHUNK_XML_LAST =            0x00100104


    def __init__(self, stream = None):
        self.m_reader = None
        self.mAttrDecoder = None
        self.mFirstError = None
        self.m_operational = False
        self.m_strings = None
        self.m_resourceIDs = None
        self.m_namespaces = AXmlResourceParser.NamespaceStack()
        self.m_decreaseDepth = False
        self.m_event = 0
        self.m_lineNumber = 0
        self.m_name = 0
        self.m_namespaceUri = 0
        self.m_attributes = None
        self.m_idAttribute = None
        self.m_classAttribute = None
        self.m_styleAttribute = None


        self.resetEventInfo()
        self.open(stream)


    def __repr__(self):
        def get_type(index):
            return ["namespaceUri", "name", "string", "type", "data", "length"][index % 5]


        return "<AXmlResourceParser {event: %s,\t\n name: %s, \t\nattributesRaw: %s, \t\nattributes: %s\t\n}>" % (
            self.TYPE_NAMES[self.m_event],
            self.getName(),
            [{get_type(i): self.m_strings.get(d)} for i, d in enumerate(self.m_attributes)] if self.m_attributes else None,
            [{self.getAttributeName(i): self.getAttributeValue(i)} for i in range(self.getAttributeCount())] if self.getAttributeCount() > 0 else None)


    def getFirstError(self):
        return self.mFirstError


    def getAttrDecoder(self):
        return self.mAttrDecoder


    def setAttrDecoder(self, attrDecoder):
        self.mAttrDecoder = attrDecoder


    def open(self, stream):
        self.close()
        if stream is not None:
            self.m_reader = ExtDataInput(stream)


    def close(self):
        if not self.m_operational:
            return


        self.m_operational = False;
        self.m_reader = None
        self.m_strings = None
        self.m_resourceIDs = None
        self.m_namespaces.reset()
        self.resetEventInfo()


    def next(self):
        if self.m_reader is None:
            raise Exception("Parser is not opened.", self, None)
        try:
            self.doNext()
            return self.m_event
        except Exception as e:
            self.close()
            raise e


    def nextToken(self):
        return self.next()


    def nextTag(self):
        eventType = self.next()
        if eventType == TEXT and self.isWhitespace():
            eventType = next()


        if eventType != START_TAG and eventType != self.END_TAG:
            raise Exception("Expected start or end tag.", self, None)


        return eventType


    def nextText(self):
        if self.getEventType() != self.START_TAG:
            raise Exception("Parser must be on START_TAG to read next text.", self, None)


        eventType = self.next()
        if eventType == self.TEXT:
            result = getText()
            eventType = self.next()
            if eventType != self.END_TAG:
                raise Exception("Event TEXT must be immediately followed by END_TAG.", self, None)


            return result
        elif eventType == END_TAG:
            return ""
        else:
            raise Exception("Parser must be on START_TAG or TEXT to read text.", self, None)




    def require(self, type, namespace, name):
        if type != self.getEventType() or (namespace is not None and namespace != self.getNamespace()) or (name is not None and name != self.ggetName()):
            raise Exception(TYPES[type] + " is expected.", self, None)


    def getDepth(self):
        return self.m_namespaces.getDepth() - 1


    def getEventType(self):
        return self.m_event


    def getLineNumber(self):
        return self.m_lineNumber


    def getName(self):
        if self.m_name == -1 or self.m_event != self.START_TAG and self.m_event != self.END_TAG:
            return None


        return self.m_strings.getString(self.m_name)


    def getText(self):
        if self.m_name == -1 or self.m_event != TEXT:
            return None


        return self.m_strings.getString(m_name)


    def getTextCharacters(self, holderForStartAndLength):
        text = self.getText()
        if text is None:
            return None


        holderForStartAndLength[0] = 0;
        holderForStartAndLength[1] = text.length();
        chars = text[:]
        return text


    def getNamespace(self):
        return self.m_strings.getString(m_namespaceUri)


    def getPrefix(self):
        prefix = self.m_namespaces.findPrefix(self.m_namespaceUri)
        return self.m_strings.getString(prefix)


    def getPositionDescription(self):
        return "XML line #" + getLineNumber()


    def getNamespaceCount(self, depth):
        return self.m_namespaces.getAccumulatedCount(depth)


    def getNamespacePrefix(self, pos):
        prefix = self.m_namespaces.getPrefix(pos)
        return self.m_strings.getString(prefix)


    def getNamespaceUri(self, pos):
        uri = self.m_namespaces.getUri(pos)
        return self.m_strings.getString(uri)




    def getClassAttribute(self):
        if self.m_classAttribute == -1:
            return None


        offset = self.getAttributeOffset(self.m_classAttribute)
        value = self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_STRING]
        return self.m_strings.getString(value)


    def getIdAttribute(self):
        if self.m_idAttribute == -1:
            return None


        offset = self.getAttributeOffset(self.m_idAttribute)
        value = self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_STRING]
        return self.m_strings.getString(value)


    def getIdAttributeResourceValue(self, defaultValue):
        if self.m_idAttribute == -1:
            return defaultValue


        offset = self.getAttributeOffset(self.m_idAttribute)
        valueType = self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_TYPE]
        if valueType != TypedValue.TYPE_REFERENCE:
            return defaultValue


        return self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_DATA]


    def getStyleAttribute(self):
        if self.m_styleAttribute == -1:
            return 0


        offset = self.getAttributeOffset(self.m_styleAttribute)
        return self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_DATA]




    def getAttributeCount(self):
        if self.m_event != self.START_TAG:
            return -1


        return len(self.m_attributes) // self.ATTRIBUTE_LENGHT




    def getAttributeNamespace(self, index):
        offset = self.getAttributeOffset(index)
        namespace = self.m_attributes[offset + self.ATTRIBUTE_IX_NAMESPACE_URI]
        if namespace == -1:
            return ""


        return self.m_strings.getString(namespace)


    def getAttributePrefix(self, index):
        offset = self.getAttributeOffset(index)
        uri = self.m_attributes[offset + self.ATTRIBUTE_IX_NAMESPACE_URI]
        prefix = self.m_namespaces.findPrefix(uri)
        if prefix == -1:
            return ""


        return self.m_strings.getString(prefix)


    def getAttributeName(self, index):
        offset = self.getAttributeOffset(index)
        name = self.m_attributes[offset + self.ATTRIBUTE_IX_NAME]
        if name == -1:
            return ""


        return self.m_strings.getString(name)


    def getAttributeNameResource(self, index):
        offset = self.getAttributeOffset(index)
        name = self.m_attributes[offset + self.ATTRIBUTE_IX_NAME]
        if self.m_resourceIDs is None or name < 0 or name >= len(self.m_resourceIDs):
            return 0
        print("len(self.m_resourceIDs) = " , len(self.m_resourceIDs))
        return self.m_resourceIDs[name]


    def getAttributeValueType(self, index):
        offset = self.getAttributeOffset(index)
        return self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_TYPE]


    def getAttributeValueData(self, index):
        offset = self.getAttributeOffset(index)
        return self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_DATA]


    def getAttributeValue(self, *args):
        if len(args) == 1:
            print(args)
            return self.getAttributeValueByIndex(*args)
        else:
            return self.getAttributeValueByNameSpaceAttr(*args)


    def getAttributeValueByNameSpaceAttr(self, namespace, attribute):
        index = self.findAttribute(namespace, attribute)
        if index == -1:
            return None


        return self.getAttributeValue(index)


    def getAttributeValueByIndex(self, index):
        offset = self.getAttributeOffset(index)
        valueType = self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_TYPE]
        valueData = self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_DATA]
        valueRaw = self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_STRING]


        if self.mAttrDecoder is not None:
            try:
                return self.mAttrDecoder.decode(valueType, valueData,
                    None if valueRaw == -1 else ResXmlEncoders.escapeXmlChars(self.m_strings.getString(valueRaw)),
                  self.getAttributeNameResource(index))
            except Exception as ex:
                self.setFirstError(ex)
                log("Could not decode attr value, using undecoded value " +  "instead: ns=%s, name=%s, value=0x%08x",
                    self.getAttributePrefix(index), self.getAttributeName(index),  valueData)


        return TypedValue.coerceToString(valueType, valueData)


    def getAttributeBooleanValue(self, index, defaultValue):
        return self.getAttributeIntValue(index, 1 if defaultValue else 0) != 0


    def getAttributeFloatValue(self, index, defaultValue):
        offset = self.getAttributeOffset(index)
        valueType = self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_TYPE]
        if valueType == TypedValue.TYPE_FLOAT:
            valueData = self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_DATA]
            return Float.intBitsToFloat(valueData)
        return defaultValue


    def getAttributeIntValue(self, index, defaultValue):
        offset = self.getAttributeOffset(index)
        valueType = self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_TYPE]
        if (valueType >= TypedValue.TYPE_FIRST_INT and valueType <= TypedValue.TYPE_LAST_INT):
            return self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_DATA]


        return defaultValue


    def getAttributeUnsignedIntValue(self, index, defaultValue):
        return self.getAttributeIntValue(index, defaultValue)


    def getAttributeResourceValue(self, index, defaultValue):
        offset = self.getAttributeOffset(index)
        valueType = self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_TYPE]
        if valueType == TypedValue.TYPE_REFERENCE:
            return self.m_attributes[offset + self.ATTRIBUTE_IX_VALUE_DATA]


        return defaultValue


    def getAttributeBooleanValue(self, namespace, attribute, defaultValue):
        index = self.findAttribute(namespace, attribute)
        if index == -1:
            return defaultValue
        return self.getAttributeBooleanValue(index, defaultValue)




    def getAttributeFloatValue(self, namespace, attribute, defaultValue):
        index = self.findAttribute(namespace, attribute)
        if index == -1:
            return defaultValue
        return self.getAttributeFloatValue(index, defaultValue)


    def getAttributeIntValue(self, namespace, attribute, defaultValue):
        index = self.findAttribute(namespace, attribute)
        if index == -1:
            return defaultValue
        return self.getAttributeIntValue(index, defaultValue)


    def getAttributeUnsignedIntValue(self, namespace, attribute, defaultValue):
        index = self.findAttribute(namespace, attribute)
        if index == -1:
            return defaultValue
        return self.getAttributeUnsignedIntValue(index, defaultValue)


    def getAttributeResourceValue(self, namespace, attribute, defaultValue):
        index = self.findAttribute(namespace, attribute)
        if index == -1:
            return defaultValue
        return self.getAttributeResourceValue(index, defaultValue)


    def getAttributeListValue(self, index, options, defaultValue):
        return 0


    def getAttributeListValue(self, namespace, attribute, options, defaultValue):
        return 0


    def getAttributeType(self, index):
        return "CDATA"


    def isAttributeDefault(self, index):
        return False


    def setInput(self, stream, inputEncoding):
        self.open(stream)


    def setInput(self, reader):
        raise Exception("E_NOT_SUPPORTED")


    def getInputEncoding(self):
        return None


    def getColumnNumber(self):
        return -1


    def isEmptyElementTag(self):
        return False


    def isWhitespace(self):
        return False


    def defineEntityReplacementText(self, entityName, replacementText):
        raise Exception("E_NOT_SUPPORTED")


    def getNamespace(self, prefix):
        raise Exception("E_NOT_SUPPORTED")


    def getProperty(self, name):
        return None


    def setProperty(self, name, value):
        raise Exception("E_NOT_SUPPORTED")


    def getFeature(self, feature):
        return False


    def setFeature(self, name, value):
        raise Exception("E_NOT_SUPPORTED")


    class NamespaceStack:
        """
        int[] m_data;
        int m_dataLength;
        int m_count;
        int m_depth;
        """
        def __init__(self):
            self.m_data = [0] * 32
            self.m_dataLength = 0
            self.m_count = 0
            self.m_depth = 0


        def reset(self):
            self.m_dataLength = 0
            self.m_count = 0
            self.m_depth = 0


        def getTotalCount(self):
            return self.m_count


        def getCurrentCount(self):
            if self.m_dataLength == 0:
                return 0
            offset = self.m_dataLength - 1
            return self.m_data[offset]


        def getAccumulatedCount(self, depth):
            if self.m_dataLength == 0 or depth < 0:
                return 0
            if depth > self.m_depth:
                depth = self.m_depth
            accumulatedCount = 0
            offset = 0
            while depth != 0:
                count = self.m_data[offset]
                accumulatedCount += count
                offset += (2 + count * 2)


                depth -= 1
            return accumulatedCount


        def push(self, prefix, uri):
            if self.m_depth == 0:
                self.increaseDepth()
            self.ensureDataCapacity(2)
            offset = self.m_dataLength - 1
            count = self.m_data[offset]
            self.m_data[offset - 1 - count * 2] = count + 1
            self.m_data[offset] = prefix
            self.m_data[offset + 1] = uri
            self.m_data[offset + 2] = count + 1
            self.m_dataLength += 2
            self.m_count += 1


        def pop(self, prefix, uri):
            if self.m_dataLength == 0:
                return False
            offset = self.m_dataLength - 1
            count = self.m_data[offset]


            #for (int i = 0, o = offset - 2; i != count; ++i, o -= 2) {
            i = 0
            o = offset - 2
            while i != count:
                if self.m_data[o] != prefix or self.m_data[o + 1] != uri:
                    continue
                count -= 1
                if i == 0:
                    self.m_data[o] = count
                    o -= (1 + count * 2)
                    self.m_data[o] = count
                else:
                    self.m_data[offset] = count
                    offset -= (1 + 2 + count * 2)
                    self.m_data[offset] = count
                    arraycopy(m_data, o + 2, m_data, o, m_dataLength - o)
                self.m_dataLength -= 2
                self.m_count -= 1
                return True
            return False


        def pop(self):
            if self.m_dataLength == 0:
                return False
            offset = self.m_dataLength - 1
            count = self.m_data[offset]
            if count == 0:
                return False
            count -= 1
            offset -= 2
            self.m_data[offset] = count
            offset -= (1 + count * 2)
            self.m_data[offset] = count
            self.m_dataLength -= 2
            self.m_count -= 1
            return True


        def getPrefix(self, index):
            return self.get(index, True)


        def getUri(self, index):
            return self.get(index, False)


        def findPrefix(self, uri):
            return self.find(uri, False)


        def findUri(self, prefix):
            return self.find(prefix, True)


        def getDepth(self):
            return self.m_depth


        def increaseDepth(self):
            self.ensureDataCapacity(2)
            offset = self.m_dataLength
            self.m_data[offset] = 0
            self.m_data[offset + 1] = 0
            self.m_dataLength += 2
            self.m_depth += 1


        def decreaseDepth(self):
            if self.m_dataLength == 0:
                return
            offset = self.m_dataLength - 1
            count = self.m_data[offset]
            if (offset - 1 - count * 2) == 0:
                return
            self.m_dataLength -= 2 + count * 2
            self.m_count -= count
            self.m_depth -= 1


        def ensureDataCapacity(self, capacity):
            available = (len(self.m_data) - self.m_dataLength)
            if available > capacity:
                return
            newLength = (len(self.m_data) + available) * 2
            newData = [0] * newLength
            arraycopy(m_data, 0, newData, 0, m_dataLength)
            self.m_data = newData


        def find(self, prefixOrUri, prefix):
            if self.m_dataLength == 0:
                return -1
            offset = self.m_dataLength - 1
            for i in range(self.m_depth, -1, -1):
                count = self.m_data[offset]
                offset -= 2
                while count != 0:
                    if prefix:
                        if self.m_data[offset] == prefixOrUri:
                            return self.m_data[offset + 1]
                    else:
                        if self.m_data[offset + 1] == prefixOrUri:
                            return self.m_data[offset]
                    offset -= 2


                    count -= 1
            return -1


        def get(self, index, prefix):
            if self.m_dataLength == 0 or index < 0:
                return -1
            offset = 0
            for i in range(m_depth, -1, -1):
                count = self.m_data[offset]
                if index >= count:
                    index -= count
                    offset += (2 + count * 2)
                    continue
                offset += (1 + index * 2)
                if not prefix:
                    offset += 1
                return self.m_data[offset]
            return -1
        # END namespacestack


    def getStrings(self):
        return self.m_strings


    def getAttributeOffset(self, index):
        if self.m_event != self.START_TAG:
            raise Exception("Current event is not START_TAG.")
        offset = index * 5
        if offset >= len(self.m_attributes):
            raise Exception("Invalid attribute index (%s)." % index)
        return offset


    def findAttribute(self, namespace, attribute):
        if self.m_strings is None or attribute is None:
            return -1


        name = self.m_strings.find(attribute)
        if name == -1:
            return -1


        uri = self.m_strings.find(namespace) if namespace is not None else -1


        for i in range(len(self.m_attributes)):
            if name == self.m_attributes[o + self.ATTRIBUTE_IX_NAME] and (uri == -1 or uri == m_attributes[o + self.ATTRIBUTE_IX_NAMESPACE_URI]):
                return o // self.ATTRIBUTE_LENGHT
        return -1


    def resetEventInfo(self):
        self.m_event = -1
        self.m_lineNumber = -1
        self.m_name = -1
        self.m_namespaceUri = -1
        self.m_attributes = None
        self.m_idAttribute = -1
        self.m_classAttribute = -1
        self.m_styleAttribute = -1




    def doNext(self):
        if self.m_strings is None:
            self.m_reader.skipCheckInt(self.CHUNK_AXML_FILE)
            self.m_reader.skipInt() # chunkSize
            self.m_strings = StringBlock.read(self.m_reader)
            self.m_namespaces.increaseDepth()
            self.m_operational = True


        if self.m_event == self.END_DOCUMENT:
            return


        event = self.m_event
        self.resetEventInfo()


        while True:
            if self.m_decreaseDepth:
                self.m_decreaseDepth = False
                self.m_namespaces.decreaseDepth()


            #Fake END_DOCUMENT event.
            if event == self.END_TAG and self.m_namespaces.getDepth() == 1 and self.m_namespaces.getCurrentCount() == 0:
                self.m_event = self.END_DOCUMENT
                break


            chunkType = None
            if event == self.START_DOCUMENT:
                # Fake event, see CHUNK_XML_START_TAG handler.
                chunkType = self.CHUNK_XML_START_TAG
            else:
                chunkType = self.m_reader.readInt()


            if chunkType == self.CHUNK_RESOURCEIDS:
                chunkSize = self.m_reader.readInt()
                if chunkSize < 8 or (chunkSize % 4) != 0:
                    raise Exception("Invalid resource ids size (%s)." % chunkSize)


                self.m_resourceIDs = self.m_reader.readIntArray(chunkSize // 4 - 2)
                continue


            if chunkType < self.CHUNK_XML_FIRST or chunkType > self.CHUNK_XML_LAST:
                raise Exception("Invalid chunk type (%s)." % chunkType)


            # Fake START_DOCUMENT event.
            if chunkType == self.CHUNK_XML_START_TAG and event == -1:
                self.m_event = self.START_DOCUMENT
                break


            # Common header.
            self.m_reader.skipInt() # chunkSize
            lineNumber = self.m_reader.readInt()
            self.m_reader.skipInt() # /*0xFFFFFFFF*/


            if chunkType == self.CHUNK_XML_START_NAMESPACE or chunkType == self.CHUNK_XML_END_NAMESPACE:
                if chunkType == self.CHUNK_XML_START_NAMESPACE:
                    prefix = self.m_reader.readInt()
                    uri = self.m_reader.readInt()
                    self.m_namespaces.push(prefix, uri)
                else:
                    self.m_reader.skipInt() # prefix
                    self.m_reader.skipInt() # uri
                    self.m_namespaces.pop()
                continue


            self.m_lineNumber = lineNumber;


            if chunkType == self.CHUNK_XML_START_TAG:
                self.m_namespaceUri = self.m_reader.readInt()
                self.m_name = self.m_reader.readInt();
                self.m_reader.skipInt() # flags?
                attributeCount = self.m_reader.readInt()
                self.m_idAttribute = u32_rshift(attributeCount, 16) - 1
                attributeCount &= 0xFFFF
                self.m_classAttribute = self.m_reader.readInt()
                self.m_styleAttribute = u32_rshift(self.m_classAttribute, 16) - 1
                self.m_classAttribute = (self.m_classAttribute & 0xFFFF) - 1
                self.m_attributes = self.m_reader.readIntArray(attributeCount * self.ATTRIBUTE_LENGHT);
                i = self.ATTRIBUTE_IX_VALUE_TYPE
                while i < len(self.m_attributes):
                    self.m_attributes[i] = u32_rshift(self.m_attributes[i], 24)
                    i += self.ATTRIBUTE_LENGHT


                self.m_namespaces.increaseDepth()
                self.m_event = self.START_TAG
                break


            if chunkType == self.CHUNK_XML_END_TAG:
                self.m_namespaceUri = self.m_reader.readInt()
                self.m_name = self.m_reader.readInt()
                self.m_event = self.END_TAG
                self.m_decreaseDepth = True
                break


            if chunkType == self.CHUNK_XML_TEXT:
                self.m_name = self.m_reader.readInt()
                self.m_reader.skipInt() # ?
                self.m_reader.skipInt() # ?
                self.m_event = self.TEXT
                break


    def setFirstError(self, error):
        if self.mFirstError is None:
            self.mFirstError = error


    CDSECT = 0x00000005
    COMMENT = 0x00000009
    DOCDECL = 0x0000000a
    END_DOCUMENT = 0x00000001
    END_TAG = 0x00000003
    ENTITY_REF = 0x00000006
    IGNORABLE_WHITESPACE = 0x00000007
    PROCESSING_INSTRUCTION = 0x00000008
    START_DOCUMENT = 0x00000000
    START_TAG = 0x00000002
    TEXT = 0x00000004


    TYPE_NAMES = {
        0x00000005: "CDSECT",
        0x00000009: "COMMENT",
        0x0000000a: "DOCDECL",
        0x00000001: "END_DOCUMENT",
        0x00000003: "END_TAG",
        0x00000006: "ENTITY_REF",
        0x00000007: "IGNORABLE_WHITESPACE",
        0x00000008: "PROCESSING_INSTRUCTION",
        0x00000000: "START_DOCUMENT",
        0x00000002: "START_TAG",
        0x00000004: "TEXT"
    }




apkFile = "D:\\test\\KingReader.apk"


import zipfile
import io
zip = zipfile.ZipFile(apkFile, "r")
resFile = io.BytesIO()
resFile.write(zip.read("resources.arsc"))
resFile.seek(0)
xmlFile = io.BytesIO()
xmlFile.write(zip.read("AndroidManifest.xml"))
xmlFile.seek(0)


resTable = ResTable()
data = ARSCDecoder.decode(ExtDataInput(resFile), False, False, resTable)
for pkg in data.mPackages:
    print(pkg.getId(), pkg.getName())


pkg = None
if len(data.mPackages) == 1:
    pkg = data.mPackages[0]
elif len(data.mPackages) == 2:
    pkg = data.mPackages[1]
if pkg is None:
    raise Exception("Arsc files with zero or multiple packages")


resTable.addPackage(pkg, True)


for pkg in resTable.listMainPackage():
    resDecoder = ResAttrDecoder()
    resDecoder.setCurrentPackage(pkg)
    parser = AXmlResourceParser()
    parser.setAttrDecoder(resDecoder)
    parser.open(xmlFile)
    while 1:
        try:
            t = parser.next()
            print(AXmlResourceParser.TYPE_NAMES[t])
            print(parser)
            if t == AXmlResourceParser.END_DOCUMENT:
                break
        except Exception as e:
            raise e
            break