Merged in "yyj" fixes along with a few of my own. 95% yyj work :)

This commit is contained in:
Connor Tumbleson 2012-07-08 10:33:49 -05:00
parent e9738642b2
commit 282bdae126
17 changed files with 328 additions and 70 deletions

View File

@ -211,8 +211,9 @@ public class Main {
System.out.println(
"Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" +
"Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>\n" +
"with baksmali/smali " + smaliVersion + " (http://smali.googlecode.com)\n" +
"Updated by iBotPeaches (@iBotPeaches) \n" +
"with smali v" + ApktoolProperties.get("smaliVersion") +
", and baksmali v" + ApktoolProperties.get("baksmaliVersion") + "\n" +
"Updated by iBotPeaches (@iBotPeaches) and yyj \n" +
"Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" +
"\n" +
"Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n" +

View File

@ -44,8 +44,6 @@ public class TypedValue {
* of a container. */
public static final int TYPE_FRACTION = 0x06;
public static final int TYPE_LAYOUT = 0x07;
/** Identifies the start of plain integer values. Any type value
* from this to {@link #TYPE_LAST_INT} means the
* <var>data</var> field holds a generic integer value. */
@ -224,13 +222,42 @@ public class TypedValue {
}
if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) {
return "#" + Integer.toHexString(data);
String res =String.format("%08x", data);
char[] vals = res.toCharArray();
switch (type) {
default:
case TYPE_INT_COLOR_ARGB8://#AaRrGgBb
break;
case TYPE_INT_COLOR_RGB8://#FFRrGgBb->#RrGgBb
res = res.substring(2);
break;
case TYPE_INT_COLOR_ARGB4://#AARRGGBB->#ARGB
res = new StringBuffer().append(vals[0]).append(vals[2]).append(vals[4]).append(vals[6]).toString();
break;
case TYPE_INT_COLOR_RGB4://#FFRRGGBB->#RGB
res = new StringBuffer().append(vals[2]).append(vals[4]).append(vals[6]).toString();
break;
}
return "#" + res;
} else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) {
return Integer.toString(data);
String res;
switch (type) {
default:
case TYPE_INT_DEC:
res = Integer.toString(data);
break;
//defined before
/*case TYPE_INT_HEX:
res = "0x" + Integer.toHexString(data);
break;
case TYPE_INT_BOOLEAN:
res = (data != 0) ? "true":"false";
break;*/
}
return res;
}
return null;
}
};

View File

@ -24,8 +24,7 @@ import brut.androlib.res.util.ExtFile;
import brut.androlib.src.SmaliBuilder;
import brut.androlib.src.SmaliDecoder;
import brut.common.BrutException;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import brut.directory.*;
import brut.util.BrutIO;
import brut.util.OS;
import java.io.*;
@ -42,7 +41,11 @@ public class Androlib {
private final AndrolibResources mAndRes = new AndrolibResources();
public ResTable getResTable(ExtFile apkFile) throws AndrolibException {
return mAndRes.getResTable(apkFile);
return mAndRes.getResTable(apkFile, true);
}
public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) throws AndrolibException {
return mAndRes.getResTable(apkFile, loadMainPkg);
}
public void decodeSourcesRaw(ExtFile apkFile, File outDir, boolean debug)
@ -78,6 +81,22 @@ public class Androlib {
new AndrolibJava().decode(apkFile, outDir);
}
public void decodeManifestRaw(ExtFile apkFile, File outDir)
throws AndrolibException {
try {
Directory apk = apkFile.getDirectory();
LOGGER.info("Copying raw manifest...");
apkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
public void decodeManifestFull(ExtFile apkFile, File outDir,
ResTable resTable) throws AndrolibException {
mAndRes.decodeManifest(resTable, apkFile, outDir);
}
public void decodeResourcesRaw(ExtFile apkFile, File outDir)
throws AndrolibException {
try {
@ -248,6 +267,8 @@ public class Androlib {
throws AndrolibException {
if (! buildResourcesRaw(appDir, forceBuildAll)
&& ! buildResourcesFull(appDir, forceBuildAll, framework,
usesFramework)
&& ! buildManifest(appDir, forceBuildAll, framework,
usesFramework)) {
LOGGER.warning("Could not find resources");
}
@ -320,6 +341,65 @@ public class Androlib {
}
}
public boolean buildManifestRaw(ExtFile appDir, boolean forceBuildAll)
throws AndrolibException {
try {
File apkDir = new File(appDir, APK_DIRNAME);
LOGGER.info("Copying raw AndroidManifest.xml...");
appDir.getDirectory()
.copyToDir(apkDir, APK_MANIFEST_FILENAMES);
return true;
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
public boolean buildManifest(ExtFile appDir, boolean forceBuildAll,
boolean framework, Map<String, Object> usesFramework)
throws AndrolibException {
try {
if (! new File(appDir, "AndroidManifest.xml").exists()) {
return false;
}
if (! forceBuildAll) {
LOGGER.info("Checking whether resources has changed...");
}
File apkDir = new File(appDir, APK_DIRNAME);
if (forceBuildAll || isModified(
newFiles(APK_MANIFEST_FILENAMES, appDir),
newFiles(APK_MANIFEST_FILENAMES, apkDir))) {
LOGGER.info("Building AndroidManifest.xml...");
File apkFile = File.createTempFile("APKTOOL", null);
apkFile.delete();
File ninePatch = new File(appDir, "9patch");
if (! ninePatch.exists()) {
ninePatch = null;
}
mAndRes.aaptPackage(
apkFile,
new File(appDir, "AndroidManifest.xml"),
null,
ninePatch, null, parseUsesFramework(usesFramework),
false, framework
);
Directory tmpDir = new ExtFile(apkFile).getDirectory();
tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES);
}
return true;
} catch (IOException ex) {
throw new AndrolibException(ex);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
} catch (AndrolibException ex) {
LOGGER.warning("Parse AndroidManifest.xml failed, treat it as raw file.");
return buildManifestRaw(appDir, forceBuildAll);
}
}
public void buildLib(File appDir, boolean forceBuildAll)
throws AndrolibException {
File working = new File(appDir, "lib");
@ -441,4 +521,6 @@ public class Androlib {
new String[]{"resources.arsc", "AndroidManifest.xml"};
private final static String[] APP_RESOURCES_FILENAMES =
new String[]{"AndroidManifest.xml", "res"};
private final static String[] APK_MANIFEST_FILENAMES =
new String[]{"AndroidManifest.xml"};
}

View File

@ -26,10 +26,7 @@ import brut.common.BrutException;
import brut.directory.DirectoryException;
import brut.util.OS;
import java.io.File;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
@ -92,6 +89,7 @@ public class ApkDecoder {
break;
}
}
if (hasResources()) {
switch (mDecodeResources) {
case DECODE_RESOURCES_NONE:
@ -102,7 +100,22 @@ public class ApkDecoder {
getResTable());
break;
}
} else {
// if there's no resources.asrc, decode the manifest without looking up
// attribute references
if (hasManifest()) {
switch (mDecodeResources) {
case DECODE_RESOURCES_NONE:
mAndrolib.decodeManifestRaw(mApkFile, outDir);
break;
case DECODE_RESOURCES_FULL:
mAndrolib.decodeManifestFull(mApkFile, outDir,
getResTable());
break;
}
}
}
mAndrolib.decodeRawFiles(mApkFile, outDir);
writeMetaFile();
}
@ -143,12 +156,14 @@ public class ApkDecoder {
public ResTable getResTable() throws AndrolibException {
if (mResTable == null) {
if (! hasResources()) {
boolean hasResources = hasResources();
boolean hasManifest = hasManifest();
if (! (hasManifest || hasResources)) {
throw new AndrolibException(
"Apk doesn't containt resources.arsc file");
"Apk doesn't contain either AndroidManifest.xml file or resources.arsc file");
}
AndrolibResources.sKeepBroken = mKeepBrokenResources;
mResTable = mAndrolib.getResTable(mApkFile);
mResTable = mAndrolib.getResTable(mApkFile, hasResources);
mResTable.setFrameTag(mFrameTag);
}
return mResTable;
@ -162,6 +177,14 @@ public class ApkDecoder {
}
}
public boolean hasManifest() throws AndrolibException {
try {
return mApkFile.getDirectory().containsFile("AndroidManifest.xml");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
public boolean hasResources() throws AndrolibException {
try {
return mApkFile.getDirectory().containsFile("resources.arsc");
@ -190,7 +213,7 @@ public class ApkDecoder {
meta.put("version", Androlib.getVersion());
meta.put("apkFileName", mApkFile.getName());
if (mDecodeResources != DECODE_RESOURCES_NONE && hasResources()) {
if (mDecodeResources != DECODE_RESOURCES_NONE && (hasManifest() || hasResources())) {
meta.put("isFrameworkApk",
Boolean.valueOf(mAndrolib.isFrameworkApk(getResTable())));
putUsesFramework(meta);

View File

@ -21,6 +21,9 @@ import java.io.InputStream;
import java.util.Properties;
import java.util.logging.Logger;
import org.jf.baksmali.baksmali;
import org.jf.smali.main;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
@ -46,6 +49,25 @@ public class ApktoolProperties {
} catch (IOException ex) {
LOGGER.warning("Can't load properties.");
}
InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties");
Properties properties = new Properties();
String version = "(unknown)";
try {
properties.load(templateStream);
version = properties.getProperty("application.version");
} catch (IOException ex) {
}
sProps.put("baksmaliVersion", version);
templateStream = main.class.getClassLoader().getResourceAsStream("smali.properties");
properties = new Properties();
version = "(unknown)";
try {
properties.load(templateStream);
version = properties.getProperty("application.version");
} catch (IOException ex) {
}
sProps.put("smaliVersion", version);
}
private static Properties sProps;

View File

@ -39,8 +39,14 @@ import org.xmlpull.v1.XmlSerializer;
*/
final public class AndrolibResources {
public ResTable getResTable(ExtFile apkFile) throws AndrolibException {
return getResTable(apkFile, true);
}
public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) throws AndrolibException {
ResTable resTable = new ResTable(this);
loadMainPkg(resTable, apkFile);
if (loadMainPkg) {
loadMainPkg(resTable, apkFile);
}
return resTable;
}
@ -97,6 +103,36 @@ final public class AndrolibResources {
return pkg;
}
public void decodeManifest(ResTable resTable, ExtFile apkFile, File outDir)
throws AndrolibException {
Duo<ResFileDecoder, AXmlResourceParser> duo = getManifestFileDecoder();
ResFileDecoder fileDecoder = duo.m1;
// Set ResAttrDecoder
duo.m2.setAttrDecoder(new ResAttrDecoder());
ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder();
// Fake ResPackage
attrDecoder.setCurrentPackage(new ResPackage(resTable, 0, null));
Directory inApk, out;
try {
inApk = apkFile.getDirectory();
out = new FileDirectory(outDir);
LOGGER.info("Decoding AndroidManifest.xml with only framework resources...");
fileDecoder.decode(
inApk, "AndroidManifest.xml", out, "AndroidManifest.xml",
"xml");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
public void decode(ResTable resTable, ExtFile apkFile, File outDir)
throws AndrolibException {
Duo<ResFileDecoder, AXmlResourceParser> duo = getResFileDecoder();
@ -111,6 +147,7 @@ final public class AndrolibResources {
inApk = apkFile.getDirectory();
out = new FileDirectory(outDir);
LOGGER.info("Decoding AndroidManifest.xml with resources...");
fileDecoder.decode(
inApk, "AndroidManifest.xml", out, "AndroidManifest.xml",
"xml");
@ -238,6 +275,19 @@ final public class AndrolibResources {
new ResFileDecoder(decoders), axmlParser);
}
public Duo<ResFileDecoder, AXmlResourceParser> getManifestFileDecoder() {
ResStreamDecoderContainer decoders =
new ResStreamDecoderContainer();
AXmlResourceParser axmlParser = new AXmlResourceParser();
decoders.setDecoder("xml",
new XmlPullStreamDecoder(axmlParser, getResXmlSerializer()));
return new Duo<ResFileDecoder, AXmlResourceParser>(
new ResFileDecoder(decoders), axmlParser);
}
public ExtMXSerializer getResXmlSerializer() {
ExtMXSerializer serial = new ExtMXSerializer();
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_INDENTATION

View File

@ -196,6 +196,10 @@ public class ResConfigFlags {
case UI_MODE_TYPE_DESK:
ret.append("-desk");
break;
case UI_MODE_TYPE_APPLIANCE:
ret.append("-appliance");
break;
}
switch (uiMode & MASK_UI_MODE_NIGHT) {
case UI_MODE_NIGHT_YES:
@ -410,6 +414,7 @@ public class ResConfigFlags {
public final static byte UI_MODE_TYPE_DESK = 0x02;
public final static byte UI_MODE_TYPE_CAR = 0x03;
public final static byte UI_MODE_TYPE_TELEVISION = 0x04;
public final static byte UI_MODE_TYPE_APPLIANCE = 0x05;
public final static byte MASK_UI_MODE_NIGHT = 0x30;
public final static byte UI_MODE_NIGHT_ANY = 0x00;

View File

@ -16,16 +16,22 @@
package brut.androlib.res.data.value;
import android.util.TypedValue;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResColorValue extends ResIntValue {
public ResColorValue(int value, String rawValue) {
private int type;
public ResColorValue(int value, String rawValue, int type) {
super(value, rawValue, "color");
this.type = type;
}
@Override
protected String encodeAsResXml() {
return String.format("#%08x", mValue);
return TypedValue.coerceToString(type, mValue);
}
}

View File

@ -16,6 +16,7 @@
package brut.androlib.res.data.value;
import android.util.TypedValue;
import brut.androlib.AndrolibException;
/**
@ -23,9 +24,11 @@ import brut.androlib.AndrolibException;
*/
public class ResIntValue extends ResScalarValue {
protected final int mValue;
private int type;
public ResIntValue(int value, String rawValue) {
public ResIntValue(int value, String rawValue, int type) {
this(value, rawValue, "integer");
this.type = type;
}
public ResIntValue(int value, String rawValue, String type) {
@ -38,6 +41,6 @@ public class ResIntValue extends ResScalarValue {
}
protected String encodeAsResXml() throws AndrolibException {
return String.valueOf(mValue);
return TypedValue.coerceToString(type, mValue);
}
}

View File

@ -16,10 +16,9 @@
package brut.androlib.res.data.value;
import brut.androlib.res.xml.ResValuesXmlSerializable;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import brut.androlib.res.xml.ResXmlEncoders;
import brut.androlib.res.xml.ResValuesXmlSerializable;
import brut.util.Duo;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
@ -52,15 +51,13 @@ public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializ
serializer.startTag(null, "item");
serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
String item2 = item.encodeAsResXmlValue();
/*
* peaches fix regarding formatted=false
*/
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(item2)) {
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(item2);
}*/
serializer.text(rawValue);
serializer.endTag(null, "item");
}

View File

@ -20,6 +20,7 @@ import brut.androlib.res.xml.ResValuesXmlSerializable;
import brut.androlib.res.xml.ResXmlEncodable;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import brut.androlib.res.xml.ResXmlEncoders;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
@ -54,6 +55,38 @@ public abstract class ResScalarValue extends ResValue
return encodeAsResXml();
}
public String encodeAsResXmlValueExt() throws AndrolibException {
String rawValue = mRawValue;
if (rawValue != null) {
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
int count = 1;
StringBuffer result = new StringBuffer();
String tmp1[] = rawValue.split("%%", -1);
int tmp1_sz = tmp1.length;
for(int i=0;i<tmp1_sz;i++) {
String cur1 = tmp1[i];
String tmp2[] = cur1.split("%", -1);
int tmp2_sz = tmp2.length;
for(int j=0;j<tmp2_sz;j++) {
String cur2 = tmp2[j];
result.append(cur2);
if(j != (tmp2_sz-1)) {
result.append('%').append(count).append('$');
count++;
}
}
if(i != (tmp1_sz-1)) {
result.append("%%");
}
}
rawValue = result.toString();
}
return rawValue;
}
return encodeAsResXml();
}
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
String type = res.getResSpec().getType().getName();

View File

@ -52,11 +52,11 @@ public class ResValueFactory {
if (type >= TypedValue.TYPE_FIRST_COLOR_INT
&& type <= TypedValue.TYPE_LAST_COLOR_INT) {
return new ResColorValue(value, rawValue);
return new ResColorValue(value, rawValue, type);
}
if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
return new ResIntValue(value, rawValue);
return new ResIntValue(value, rawValue, type);
}
throw new AndrolibException("Invalid value type: "+ type);

View File

@ -325,6 +325,10 @@ public class AXmlResourceParser implements XmlResourceParser {
valueData
), ex);
}
} else {
if (valueType==TypedValue.TYPE_STRING) {
return m_strings.getString(valueRaw);
}
}
return TypedValue.coerceToString(valueType, valueData);
@ -370,7 +374,7 @@ public class AXmlResourceParser implements XmlResourceParser {
public String getAttributeValue(String namespace, String attribute) {
int index = findAttribute(namespace, attribute);
if (index == -1) {
return null;
return "";
}
return getAttributeValue(index);
}
@ -755,7 +759,7 @@ public class AXmlResourceParser implements XmlResourceParser {
if (m_event != START_TAG) {
throw new IndexOutOfBoundsException("Current event is not START_TAG.");
}
int offset = index * 5;
int offset = index * ATTRIBUTE_LENGHT;
if (offset >= m_attributes.length) {
throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ").");
}

View File

@ -34,35 +34,31 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
throws AndrolibException {
try {
byte[] data = IOUtils.toByteArray(in);
NinePatch np = getNinePatch(data);
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
int w = im.getWidth(), h = im.getHeight();
BufferedImage im2 = new BufferedImage(w + 2, h + 2, BufferedImage.TYPE_4BYTE_ABGR);
BufferedImage im2 = new BufferedImage(
w + 2, h + 2, BufferedImage.TYPE_4BYTE_ABGR);
Raster src = im.getRaster();
NinePatch np = getNinePatch(data);
WritableRaster dst = im2.getRaster();
int nbands = im.getSampleModel().getNumBands();
int[] bands = new int[4];
if (nbands == 2) {
bands[0] = bands[1] = bands[2] = 0;
bands[3] = 1;
bands[0] = bands[1] = bands[2] = 0;
bands[3] = 1;
} else {
bands[0] = 0;
bands[1] = 1;
bands[2] = 2;
bands[3] = 3;
bands[0] = 0; bands[1] = 1; bands[2] = 2; bands[3] = 3;
}
int[] band = null;
for (int y = 0; y < h; y++) {
for (int bi = 0; bi < 4; bi++) {
band = src.getSamples(0, y, w, 1, bands[bi], band);
dst.setSamples(1, y + 1, w, 1, bi, band);
}
for (int bi = 0; bi < 4; bi++) {
band = src.getSamples(0, y, w, 1, bands[bi], band);
dst.setSamples(1, y + 1, w, 1, bi, band);
}
}
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);

View File

@ -94,16 +94,25 @@ public class ResFileDecoder {
public void decode(Directory inDir, String inFileName, Directory outDir,
String outFileName, String decoder) throws AndrolibException {
InputStream in = null;
OutputStream out = null;
try {
InputStream in = inDir.getFileInput(inFileName);
OutputStream out = outDir.getFileOutput(outFileName);
in = inDir.getFileInput(inFileName);
out = outDir.getFileOutput(outFileName);
mDecoders.decode(in, out, decoder);
in.close();
out.close();
} catch (IOException ex) {
throw new AndrolibException(ex);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
} finally {
try{
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
}