提交 650cf315 编写于 作者: T tRuNKator 提交者: skylot

fix: resource qualifiers (PR #487)

上级 42b78437
package jadx.core.xmlgen; package jadx.core.xmlgen;
import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
...@@ -143,6 +144,23 @@ public class ParserStream { ...@@ -143,6 +144,23 @@ public class ParserStream {
input.reset(); input.reset();
} }
public void readFully(byte[] b) throws IOException {
readFully(b, 0, b.length);
}
public void readFully(byte[] b, int off, int len) throws IOException {
readPos += len;
if (len < 0)
throw new IndexOutOfBoundsException();
int n = 0;
while (n < len) {
int count = input.read(b, off + n, len - n);
if (count < 0)
throw new EOFException();
n += count;
}
}
@Override @Override
public String toString() { public String toString() {
return "pos: 0x" + Long.toHexString(readPos); return "pos: 0x" + Long.toHexString(readPos);
......
...@@ -2,6 +2,7 @@ package jadx.core.xmlgen; ...@@ -2,6 +2,7 @@ package jadx.core.xmlgen;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
...@@ -21,6 +22,8 @@ public class ResTableParser extends CommonBinaryParser { ...@@ -21,6 +22,8 @@ public class ResTableParser extends CommonBinaryParser {
private static final Logger LOG = LoggerFactory.getLogger(ResTableParser.class); private static final Logger LOG = LoggerFactory.getLogger(ResTableParser.class);
private static final int KNOWN_CONFIG_BYTES = 56;
private static final class PackageChunk { private static final class PackageChunk {
private final int id; private final int id;
private final String name; private final String name;
...@@ -194,6 +197,11 @@ public class ResTableParser extends CommonBinaryParser { ...@@ -194,6 +197,11 @@ public class ResTableParser extends CommonBinaryParser {
EntryConfig config = parseConfig(); EntryConfig config = parseConfig();
if (config.isInvalid) {
String typeName = pkg.getTypeStrings()[id - 1];
LOG.warn("Invalid config flags detected: " + typeName + config.getQualifiers());
}
int[] entryIndexes = new int[entryCount]; int[] entryIndexes = new int[entryCount];
for (int i = 0; i < entryCount; i++) { for (int i = 0; i < entryCount; i++) {
entryIndexes[i] = is.readInt32(); entryIndexes[i] = is.readInt32();
...@@ -248,134 +256,134 @@ public class ResTableParser extends CommonBinaryParser { ...@@ -248,134 +256,134 @@ public class ResTableParser extends CommonBinaryParser {
} }
private EntryConfig parseConfig() throws IOException { private EntryConfig parseConfig() throws IOException {
long start = is.getPos();
int size = is.readInt32(); int size = is.readInt32();
int read = 28;
EntryConfig config = new EntryConfig(); if (size < 28) {
throw new IOException("Config size < 28");
is.readInt16(); //mcc }
is.readInt16(); //mnc
config.setLanguage(parseLocale()); boolean isInvalid = false;
config.setCountry(parseLocale());
int orientation = is.readInt8(); short mcc = (short) is.readInt16();
int touchscreen = is.readInt8(); short mnc = (short) is.readInt16();
int density = is.readInt16();
if (density != 0) { char[] language = unpackLocaleOrRegion((byte) is.readInt8(), (byte) is.readInt8(), 'a');
config.setDensity(parseDensity(density)); char[] country = unpackLocaleOrRegion((byte) is.readInt8(), (byte) is.readInt8(), '0');
}
is.readInt8(); // keyboard byte orientation = (byte) is.readInt8();
is.readInt8(); // navigation byte touchscreen = (byte) is.readInt8();
is.readInt8(); // inputFlags
is.readInt8(); // inputPad0
int screenWidth = is.readInt16(); int density = is.readInt16();
int screenHeight = is.readInt16();
if (screenWidth != 0 && screenHeight != 0) { byte keyboard = (byte) is.readInt8();
config.setScreenSize(screenWidth + "x" + screenHeight); byte navigation = (byte) is.readInt8();
byte inputFlags = (byte) is.readInt8();
/* inputPad0 */is.readInt8();
short screenWidth = (short) is.readInt16();
short screenHeight = (short) is.readInt16();
short sdkVersion = (short) is.readInt16();
/* minorVersion, now must always be 0 */is.readInt16();
byte screenLayout = 0;
byte uiMode = 0;
short smallestScreenWidthDp = 0;
if (size >= 32) {
screenLayout = (byte) is.readInt8();
uiMode = (byte) is.readInt8();
smallestScreenWidthDp = (short) is.readInt16();
read = 32;
} }
int sdkVersion = is.readInt16(); short screenWidthDp = 0;
short screenHeightDp = 0;
if (sdkVersion != 0) { if (size >= 36) {
config.setSdkVersion("v" + sdkVersion); screenWidthDp = (short) is.readInt16();
screenHeightDp = (short) is.readInt16();
read = 36;
} }
int minorVersion = is.readInt16(); char[] localeScript = null;
char[] localeVariant = null;
int screenLayout = is.readInt8(); if (size >= 48) {
int uiMode = is.readInt8(); localeScript = readScriptOrVariantChar(4).toCharArray();
int smallestScreenWidthDp = is.readInt16(); localeVariant = readScriptOrVariantChar(8).toCharArray();
read = 48;
int screenWidthDp = is.readInt16();
int screenHeightDp = is.readInt16();
if (screenLayout != 0) {
config.setScreenLayout(parseScreenLayout(screenLayout));
} }
if (smallestScreenWidthDp != 0) { byte screenLayout2 = 0;
config.setSmallestScreenWidthDp("sw" + smallestScreenWidthDp + "dp"); byte colorMode = 0;
if (size >= 52) {
screenLayout2 = (byte) is.readInt8();
colorMode = (byte) is.readInt8();
is.readInt16(); // reserved padding
read = 52;
} }
if (orientation != 0) { if (size >= 56) {
config.setOrientation(parseOrientation(orientation)); is.readInt32();
read = 56;
} }
if (screenWidthDp != 0) { int exceedingSize = size - KNOWN_CONFIG_BYTES;
config.setScreenWidthDp("w" + screenWidthDp + "dp"); if (exceedingSize > 0) {
byte[] buf = new byte[exceedingSize];
read += exceedingSize;
is.readFully(buf);
BigInteger exceedingBI = new BigInteger(1, buf);
if (exceedingBI.equals(BigInteger.ZERO)) {
LOG.info(String
.format("Config flags size > %d, but exceeding bytes are all zero, so it should be ok.",
KNOWN_CONFIG_BYTES));
} else {
LOG.warn(String.format("Config flags size > %d. Size = %d. Exceeding bytes: 0x%X.",
KNOWN_CONFIG_BYTES, size, exceedingBI));
isInvalid = true;
}
} }
if (screenHeightDp != 0) { int remainingSize = size - read;
config.setScreenHeightDp("h" + screenHeightDp + "dp"); if (remainingSize > 0) {
is.skip(remainingSize);
} }
is.skipToPos(start + size, "Skip config parsing"); return new EntryConfig(mcc, mnc, language, country,
return config; orientation, touchscreen, density, keyboard, navigation,
inputFlags, screenWidth, screenHeight, sdkVersion,
screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,
screenHeightDp, localeScript, localeVariant, screenLayout2,
colorMode, isInvalid, size);
} }
private String parseOrientation(int orientation) { private char[] unpackLocaleOrRegion(byte in0, byte in1, char base) throws IOException {
if (orientation == 1) { // check high bit, if so we have a packed 3 letter code
return "port"; if (((in0 >> 7) & 1) == 1) {
} else if (orientation == 2) { int first = in1 & 0x1F;
return "land"; int second = ((in1 & 0xE0) >> 5) + ((in0 & 0x03) << 3);
} else { int third = (in0 & 0x7C) >> 2;
return "o" + orientation;
}
}
private String parseScreenLayout(int screenLayout) { // since this function handles languages & regions, we add the value(s) to the base char
switch (screenLayout) { // which is usually 'a' or '0' depending on language or region.
case 1: return new char[] { (char) (first + base), (char) (second + base), (char) (third + base) };
return "small";
case 2:
return "normal";
case 3:
return "large";
case 4:
return "xlarge";
case 64:
return "ldltr";
case 128:
return "ldrtl";
default:
return "sl" + screenLayout;
} }
return new char[] { (char) in0, (char) in1 };
} }
private String parseDensity(int density) { private String readScriptOrVariantChar(int length) throws IOException {
if (density == 120) { StringBuilder string = new StringBuilder(16);
return "ldpi";
} else if (density == 160) {
return "mdpi";
} else if (density == 240) {
return "hdpi";
} else if (density == 320) {
return "xhdpi";
} else if (density == 480) {
return "xxhdpi";
} else if (density == 640) {
return "xxxhdpi";
} else {
return density + "dpi";
}
}
private String parseLocale() throws IOException { while(length-- != 0) {
int b1 = is.readInt8(); short ch = (short) is.readInt8();
int b2 = is.readInt8(); if (ch == 0) {
String str = null; break;
if (b1 != 0 && b2 != 0) {
if ((b1 & 0x80) == 0) {
str = new String(new char[]{(char) b1, (char) b2});
} else {
LOG.warn("TODO: parse locale: 0x{}{}", Integer.toHexString(b1), Integer.toHexString(b2));
} }
string.append((char) ch);
} }
return str; is.skip(length);
return string.toString();
} }
} }
...@@ -202,10 +202,10 @@ public class ResXmlGen { ...@@ -202,10 +202,10 @@ public class ResXmlGen {
private String getFileName(ResourceEntry ri) { private String getFileName(ResourceEntry ri) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
String locale = ri.getConfig().getLocale(); String qualifiers = ri.getConfig().getQualifiers();
sb.append("res/values"); sb.append("res/values");
if (!locale.isEmpty()) { if (!qualifiers.isEmpty()) {
sb.append('-').append(locale); sb.append(qualifiers);
} }
sb.append('/'); sb.append('/');
sb.append(ri.getTypeName()); sb.append(ri.getTypeName());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册