# 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
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