Fixing the mess-ups that was apktool v1.4.8 :(

This commit is contained in:
Connor Tumbleson 2012-07-08 17:46:02 -05:00
parent b5a476bb1b
commit 97451619b8
6 changed files with 169 additions and 187 deletions

View File

@ -149,10 +149,9 @@ final public class AndrolibResources {
inApk = apkFile.getDirectory();
out = new FileDirectory(outDir);
LOGGER.info("Decoding AndroidManifest.xml with resources...");
fileDecoder.decodeManifest(
inApk, "AndroidManifest.xml", out, "AndroidManifest.xml");
fileDecoder.decode(
inApk, "AndroidManifest.xml", out, "AndroidManifest.xml",
"xml");
if (inApk.containsDir("res")) {
in = inApk.getDir("res");

View File

@ -51,15 +51,7 @@ public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializ
}
serializer.startTag(null, "item");
serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
String rawValue = item.encodeAsResXmlValueExt();
//AAPT don`t parse formatted for item tag(only for string and string-array tag),
// so adding "formatted='fasle'" is useless.
/*if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
serializer.attribute(null, "formatted", "false");
}*/
serializer.text(rawValue);
serializer.text(item.encodeAsResXmlValue());
serializer.endTag(null, "item");
}
serializer.endTag(null, "plurals");

View File

@ -52,7 +52,7 @@ public abstract class ResScalarValue extends ResValue
if (mRawValue != null) {
return mRawValue;
}
return encodeAsResXml();
return encodeAsResXmlValueExt();
}
public String encodeAsResXmlValueExt() throws AndrolibException {
@ -60,7 +60,7 @@ public abstract class ResScalarValue extends ResValue
if (rawValue != null) {
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
int count = 1;
StringBuffer result = new StringBuffer();
StringBuilder result = new StringBuilder();
String tmp1[] = rawValue.split("%%", -1);
int tmp1_sz = tmp1.length;
for(int i=0;i<tmp1_sz;i++) {

View File

@ -247,13 +247,13 @@ public class ARSCDecoder {
byte keyboard = mIn.readByte();
byte navigation = mIn.readByte();
byte inputFlags = mIn.readByte();
/*inputPad0*/ mIn.skipBytes(1);
mIn.skipBytes(1);
short screenWidth = mIn.readShort();
short screenHeight = mIn.readShort();
short sdkVersion = mIn.readShort();
/*minorVersion, now must always be 0*/ mIn.skipBytes(2);
mIn.skipBytes(2);
byte screenLayout = 0;
byte uiMode = 0;

View File

@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package brut.androlib.res.decoder;
import android.content.res.XmlResourceParser;
@ -40,14 +39,13 @@ import java.util.logging.Logger;
*
* Binary xml files parser.
*
* Parser has only two states:
* (1) Operational state, which parser obtains after first successful call
* to next() and retains until open(), close(), or failed call to next().
* (2) Closed state, which parser obtains after open(), close(), or failed
* call to next(). In this state methods return invalid values or throw exceptions.
* Parser has only two states: (1) Operational state, which parser obtains after
* first successful call to next() and retains until open(), close(), or failed
* call to next(). (2) Closed state, which parser obtains after open(), close(),
* or failed call to next(). In this state methods return invalid values or
* throw exceptions.
*
* TODO:
* * check all methods in closed state
* TODO: * check all methods in closed state
*
*/
public class AXmlResourceParser implements XmlResourceParser {
@ -319,20 +317,19 @@ public class AXmlResourceParser implements XmlResourceParser {
try {
return mAttrDecoder.decode(valueType, valueData,
valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(
m_strings.getString(valueRaw)),
m_strings.getString(valueRaw)),
getAttributeNameResource(index));
} catch (AndrolibException ex) {
setFirstError(ex);
LOGGER.log(Level.WARNING, String.format(
"Could not decode attr value, using undecoded value " +
"instead: ns=%s, name=%s, value=0x%08x",
getAttributePrefix(index), getAttributeName(index),
valueData
), ex);
"Could not decode attr value, using undecoded value "
+ "instead: ns=%s, name=%s, value=0x%08x",
getAttributePrefix(index), getAttributeName(index),
valueData), ex);
}
} else {
if (valueType==TypedValue.TYPE_STRING) {
return m_strings.getString(valueRaw);
if (valueType == TypedValue.TYPE_STRING) {
return ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw));
}
}
@ -493,20 +490,16 @@ public class AXmlResourceParser implements XmlResourceParser {
///////////////////////////////////////////// implementation
/**
* Namespace stack, holds prefix+uri pairs, as well as
* depth information.
* All information is stored in one int[] array.
* Array consists of depth frames:
* Data=DepthFrame*;
* DepthFrame=Count+[Prefix+Uri]*+Count;
* Count='count of Prefix+Uri pairs';
* Yes, count is stored twice, to enable bottom-up traversal.
* increaseDepth adds depth frame, decreaseDepth removes it.
* push/pop operations operate only in current depth frame.
* decreaseDepth removes any remaining (not pop'ed) namespace pairs.
* findXXX methods search all depth frames starting
* from the last namespace pair of current depth frame.
* All functions that operate with int, use -1 as 'invalid value'.
* Namespace stack, holds prefix+uri pairs, as well as depth information.
* All information is stored in one int[] array. Array consists of depth
* frames: Data=DepthFrame*; DepthFrame=Count+[Prefix+Uri]*+Count;
* Count='count of Prefix+Uri pairs'; Yes, count is stored twice, to enable
* bottom-up traversal. increaseDepth adds depth frame, decreaseDepth
* removes it. push/pop operations operate only in current depth frame.
* decreaseDepth removes any remaining (not pop'ed) namespace pairs. findXXX
* methods search all depth frames starting from the last namespace pair of
* current depth frame. All functions that operate with int, use -1 as
* 'invalid value'.
*
* !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !!
*
@ -764,7 +757,7 @@ public class AXmlResourceParser implements XmlResourceParser {
if (m_event != START_TAG) {
throw new IndexOutOfBoundsException("Current event is not START_TAG.");
}
int offset = index * ATTRIBUTE_LENGHT;
int offset = index * ATTRIBUTE_LENGHT;
if (offset >= m_attributes.length) {
throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ").");
}
@ -806,7 +799,9 @@ public class AXmlResourceParser implements XmlResourceParser {
// Delayed initialization.
if (m_strings == null) {
m_reader.skipCheckInt(CHUNK_AXML_FILE);
/*chunkSize*/ m_reader.skipInt();
/*
* chunkSize
*/ m_reader.skipInt();
m_strings = StringBlock.read(m_reader);
m_namespaces.increaseDepth();
m_operational = true;
@ -856,9 +851,13 @@ public class AXmlResourceParser implements XmlResourceParser {
}
// Common header.
/*chunkSize*/ m_reader.skipInt();
/*
* chunkSize
*/ m_reader.skipInt();
int lineNumber = m_reader.readInt();
/*0xFFFFFFFF*/ m_reader.skipInt();
/*
* 0xFFFFFFFF
*/ m_reader.skipInt();
// Fake START_DOCUMENT event.
if (chunkType == CHUNK_XML_START_TAG && event == -1) {
@ -874,8 +873,12 @@ public class AXmlResourceParser implements XmlResourceParser {
int uri = m_reader.readInt();
m_namespaces.push(prefix, uri);
} else {
/*prefix*/ m_reader.skipInt();
/*uri*/ m_reader.skipInt();
/*
* prefix
*/ m_reader.skipInt();
/*
* uri
*/ m_reader.skipInt();
m_namespaces.pop();
}
continue;
@ -888,7 +891,9 @@ public class AXmlResourceParser implements XmlResourceParser {
if (chunkType == CHUNK_XML_START_TAG) {
m_namespaceUri = m_reader.readInt();
m_name = m_reader.readInt();
/*flags?*/ m_reader.skipInt();
/*
* flags?
*/ m_reader.skipInt();
int attributeCount = m_reader.readInt();
m_idAttribute = (attributeCount >>> 16) - 1;
attributeCount &= 0xFFFF;
@ -903,7 +908,7 @@ public class AXmlResourceParser implements XmlResourceParser {
m_namespaces.increaseDepth();
m_event = START_TAG;
m_strings.touch(m_name, m_name);
for(int i = 0; i<attributeCount; i++) {
for (int i = 0; i < attributeCount; i++) {
m_strings.touch(m_attributes[ATTRIBUTE_IX_NAME], m_name);
m_strings.touch(m_attributes[ATTRIBUTE_IX_VALUE_STRING], m_name);
}
@ -922,119 +927,122 @@ public class AXmlResourceParser implements XmlResourceParser {
if (chunkType == CHUNK_XML_TEXT) {
m_name = m_reader.readInt();
/*?*/ m_reader.skipInt();
/*?*/ m_reader.skipInt();
/*
* ?
*/ m_reader.skipInt();
/*
* ?
*/ m_reader.skipInt();
m_event = TEXT;
break;
}
}
}
private static String formatArray(int[] array, int min, int max) {
if(max > array.length)
max = array.length;
if(min < 0)
min = 0;
StringBuffer sb = new StringBuffer("[");
int i = min;
while(true) {
sb.append(array[i]);
i++;
if(i < max) {
sb.append(", ");
} else {
sb.append("]");
break;
}
}
return sb.toString();
}
private boolean compareAttr(int[] attr1, int[] attr2) {
//TODO: sort Attrs
/*
* ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING
* : ATTRIBUTE_IX_NAMESPACE_URI
* ATTRIBUTE_IX_NAMESPACE_URI : ATTRIBUTE_IX_NAME
* id
*
*/
if(attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING &&
attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE] &&
//(m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) ||
// m_strings.touch(attr2[ATTRIBUTE_IX_VALUE_STRING], m_name)) &&
//m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) &&
attr1[ATTRIBUTE_IX_VALUE_STRING] != attr2[ATTRIBUTE_IX_VALUE_STRING]) {
return (attr1[ATTRIBUTE_IX_VALUE_STRING] < attr2[ATTRIBUTE_IX_VALUE_STRING]);
} else if ((attr1[ATTRIBUTE_IX_NAMESPACE_URI] == attr2[ATTRIBUTE_IX_NAMESPACE_URI]) && (attr1[ATTRIBUTE_IX_NAMESPACE_URI] != -1) &&
//(m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) ||
// m_strings.touch(attr2[ATTRIBUTE_IX_NAME], m_name)) &&
//m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) &&
(attr1[ATTRIBUTE_IX_NAME] != attr2[ATTRIBUTE_IX_NAME])) {
return (attr1[ATTRIBUTE_IX_NAME] < attr2[ATTRIBUTE_IX_NAME]);
//} else if (attr1[ATTRIBUTE_IX_NAMESPACE_URI] < attr2[ATTRIBUTE_IX_NAMESPACE_URI]) {
// return true;
} else {
return false;
}
}
private void sortAttrs() {
int attributeCount = m_attributes.length/ATTRIBUTE_LENGHT;
int tmp1[][] = new int[attributeCount][];
int tmp2[] = null;
for (int i = 0; i < attributeCount;i++) {
tmp1[i] = new int[ATTRIBUTE_LENGHT+1];
for(int j = 0; j < ATTRIBUTE_LENGHT; j++) {
tmp1[i][j] = m_attributes[i*ATTRIBUTE_LENGHT+j];
}
tmp1[i][ATTRIBUTE_LENGHT] = i;
if(DBG) {
try {
if (dbgOut == null) {
dbgOut = new BufferedWriter(new FileWriter("C:\\res.log",false));
}
dbgOut.write("Namespace: " + getAttributeNamespace (i) +
", Name: " + getAttributeName (i)+
", Value: " + getAttributeValue (i) + ", Array: " +
formatArray(tmp1[i], 0, ATTRIBUTE_LENGHT) + "\n");
dbgOut.flush();
} catch (IOException e) {
e.printStackTrace();
}
private static String formatArray(int[] array, int min, int max) {
if (max > array.length) {
max = array.length;
}
if (min < 0) {
min = 0;
}
StringBuffer sb = new StringBuffer("[");
int i = min;
while (true) {
sb.append(array[i]);
i++;
if (i < max) {
sb.append(", ");
} else {
sb.append("]");
break;
}
}
for (int j = 1; j < attributeCount;j++) {
for (int i = 1; i < attributeCount;i++) {
if(compareAttr(tmp1[i], tmp1[i-1])) {
tmp2 = tmp1[i-1];
tmp1[i-1] = tmp1[i];
return sb.toString();
}
private boolean compareAttr(int[] attr1, int[] attr2) {
//TODO: sort Attrs
/*
* ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING :
* ATTRIBUTE_IX_NAMESPACE_URI ATTRIBUTE_IX_NAMESPACE_URI :
* ATTRIBUTE_IX_NAME id
*
*/
if (attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING
&& attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE]
&& //(m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) ||
// m_strings.touch(attr2[ATTRIBUTE_IX_VALUE_STRING], m_name)) &&
//m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) &&
attr1[ATTRIBUTE_IX_VALUE_STRING] != attr2[ATTRIBUTE_IX_VALUE_STRING]) {
return (attr1[ATTRIBUTE_IX_VALUE_STRING] < attr2[ATTRIBUTE_IX_VALUE_STRING]);
} else if ((attr1[ATTRIBUTE_IX_NAMESPACE_URI] == attr2[ATTRIBUTE_IX_NAMESPACE_URI]) && (attr1[ATTRIBUTE_IX_NAMESPACE_URI] != -1)
&& //(m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) ||
// m_strings.touch(attr2[ATTRIBUTE_IX_NAME], m_name)) &&
//m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) &&
(attr1[ATTRIBUTE_IX_NAME] != attr2[ATTRIBUTE_IX_NAME])) {
return (attr1[ATTRIBUTE_IX_NAME] < attr2[ATTRIBUTE_IX_NAME]);
//} else if (attr1[ATTRIBUTE_IX_NAMESPACE_URI] < attr2[ATTRIBUTE_IX_NAMESPACE_URI]) {
// return true;
} else {
return false;
}
}
private void sortAttrs() {
int attributeCount = m_attributes.length / ATTRIBUTE_LENGHT;
int tmp1[][] = new int[attributeCount][];
int tmp2[] = null;
for (int i = 0; i < attributeCount; i++) {
tmp1[i] = new int[ATTRIBUTE_LENGHT + 1];
for (int j = 0; j < ATTRIBUTE_LENGHT; j++) {
tmp1[i][j] = m_attributes[i * ATTRIBUTE_LENGHT + j];
}
tmp1[i][ATTRIBUTE_LENGHT] = i;
if (DBG) {
try {
if (dbgOut == null) {
dbgOut = new BufferedWriter(new FileWriter("C:\\res.log", false));
}
dbgOut.write("Namespace: " + getAttributeNamespace(i)
+ ", Name: " + getAttributeName(i)
+ ", Value: " + getAttributeValue(i) + ", Array: "
+ formatArray(tmp1[i], 0, ATTRIBUTE_LENGHT) + "\n");
dbgOut.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
for (int j = 1; j < attributeCount; j++) {
for (int i = 1; i < attributeCount; i++) {
if (compareAttr(tmp1[i], tmp1[i - 1])) {
tmp2 = tmp1[i - 1];
tmp1[i - 1] = tmp1[i];
tmp1[i] = tmp2;
}
}
}
for (int i = 0; i < attributeCount;i++) {
for(int j = 0; j < ATTRIBUTE_LENGHT; j++) {
m_attributes[i*ATTRIBUTE_LENGHT+j] = tmp1[i][j];
for (int i = 0; i < attributeCount; i++) {
for (int j = 0; j < ATTRIBUTE_LENGHT; j++) {
m_attributes[i * ATTRIBUTE_LENGHT + j] = tmp1[i][j];
}
}
}
}
private void setFirstError(AndrolibException error) {
private void setFirstError(AndrolibException error) {
if (mFirstError == null) {
mFirstError = error;
}
}
/////////////////////////////////// data
/*
* All values are essentially indices, e.g. m_name is
* an index of name in m_strings.
* All values are essentially indices, e.g. m_name is an index of name in
* m_strings.
*/
private ExtDataInput m_reader;
private ResAttrDecoder mAttrDecoder;
private AndrolibException mFirstError;
private boolean m_operational = false;
private StringBlock m_strings;
private int[] m_resourceIDs;
@ -1048,28 +1056,24 @@ public class AXmlResourceParser implements XmlResourceParser {
private int m_idAttribute;
private int m_classAttribute;
private int m_styleAttribute;
private final static Logger LOGGER =
Logger.getLogger(AXmlResourceParser.class.getName());
Logger.getLogger(AXmlResourceParser.class.getName());
private static final String E_NOT_SUPPORTED = "Method is not supported.";
private static final int
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;
private static final int
CHUNK_AXML_FILE = 0x00080003,
CHUNK_RESOURCEIDS = 0x00080180,
CHUNK_XML_FIRST = 0x00100100,
private static final int 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;
private static final int 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;
CHUNK_XML_END_NAMESPACE = 0x00100101,
CHUNK_XML_START_TAG = 0x00100102,
CHUNK_XML_END_TAG = 0x00100103,
CHUNK_XML_TEXT = 0x00100104,
CHUNK_XML_LAST = 0x00100104;
private Writer dbgOut = null;
private final static boolean DBG = false;
}

View File

@ -156,37 +156,24 @@ public final class ResXmlEncoders {
return out.toString();
}
/**
* It searches for "%", but not "%%" nor "%(\d)+\$"
*/
private static List<Integer> findNonPositionalSubstitutions(String str,
private static List<Integer> findNonPositionalSubstitutions(String str,
int max) {
int pos = 0;
int pos2 = 0;
int count = 0;
int length = str.length();
List<Integer> ret = new ArrayList<Integer>();
while((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) {
if (pos2 == length) {
while((pos = str.indexOf('%', pos)) != -1) {
if (pos + 1 == length) {
break;
}
char c = str.charAt(pos2++);
if (c == '%') {
continue;
}
if (c >= '0' && c <= '9' && pos2 < length) {
do {
c = str.charAt(pos2++);
} while (c >= '0' && c <= '9' && pos2 < length);
if (c == '$') {
continue;
char c = str.charAt(pos + 1);
if (c >= 'a' && c <= 'z') {
ret.add(pos);
if (max != -1 && ++count >= max) {
break;
}
}
ret.add(pos);
if (max != -1 && ++count >= max) {
break;
}
pos += 2;
}
return ret;