Code cleanup of 2013

Signed-off-by: Connor Tumbleson <connor.tumbleson@gmail.com>
This commit is contained in:
Connor Tumbleson 2013-02-12 21:12:17 -06:00
parent f504ceca43
commit e82c0754de
71 changed files with 7649 additions and 7444 deletions

View File

@ -36,197 +36,200 @@ import java.util.logging.*;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class Main { public class Main {
public static void main(String[] args) public static void main(String[] args) throws IOException,
throws IOException, InterruptedException, BrutException { InterruptedException, BrutException {
try { try {
Verbosity verbosity = Verbosity.NORMAL; Verbosity verbosity = Verbosity.NORMAL;
int i; int i;
for (i = 0; i < args.length; i++) { for (i = 0; i < args.length; i++) {
String opt = args[i]; String opt = args[i];
if (opt.startsWith("--version") || (opt.startsWith("-version"))) {
version_print();
System.exit(1);
}
if (! opt.startsWith("-")) {
break;
}
if ("-v".equals(opt) || "--verbose".equals(opt)) {
if (verbosity != Verbosity.NORMAL) {
throw new InvalidArgsError();
}
verbosity = Verbosity.VERBOSE;
} else if ("-q".equals(opt) || "--quiet".equals(opt)) {
if (verbosity != Verbosity.NORMAL) {
throw new InvalidArgsError();
}
verbosity = Verbosity.QUIET;
} else {
throw new InvalidArgsError();
}
}
setupLogging(verbosity);
if (args.length <= i) { if (opt.startsWith("--version") || (opt.startsWith("-version"))) {
throw new InvalidArgsError(); version_print();
} System.exit(1);
String cmd = args[i]; }
args = Arrays.copyOfRange(args, i + 1, args.length); if (!opt.startsWith("-")) {
break;
}
if ("-v".equals(opt) || "--verbose".equals(opt)) {
if (verbosity != Verbosity.NORMAL) {
throw new InvalidArgsError();
}
verbosity = Verbosity.VERBOSE;
} else if ("-q".equals(opt) || "--quiet".equals(opt)) {
if (verbosity != Verbosity.NORMAL) {
throw new InvalidArgsError();
}
verbosity = Verbosity.QUIET;
} else {
throw new InvalidArgsError();
}
}
setupLogging(verbosity);
if ("d".equals(cmd) || "decode".equals(cmd)) { if (args.length <= i) {
cmdDecode(args); throw new InvalidArgsError();
} else if ("b".equals(cmd) || "build".equals(cmd)) { }
cmdBuild(args); String cmd = args[i];
} else if ("if".equals(cmd) || "install-framework".equals(cmd)) { args = Arrays.copyOfRange(args, i + 1, args.length);
cmdInstallFramework(args);
} else if ("publicize-resources".equals(cmd)) {
cmdPublicizeResources(args);
} else {
throw new InvalidArgsError();
}
} catch (InvalidArgsError ex) {
usage();
System.exit(1);
}
}
private static void cmdDecode(String[] args) throws InvalidArgsError, if ("d".equals(cmd) || "decode".equals(cmd)) {
AndrolibException { cmdDecode(args);
ApkDecoder decoder = new ApkDecoder(); } else if ("b".equals(cmd) || "build".equals(cmd)) {
cmdBuild(args);
} else if ("if".equals(cmd) || "install-framework".equals(cmd)) {
cmdInstallFramework(args);
} else if ("publicize-resources".equals(cmd)) {
cmdPublicizeResources(args);
} else {
throw new InvalidArgsError();
}
} catch (InvalidArgsError ex) {
usage();
System.exit(1);
}
}
int i; private static void cmdDecode(String[] args) throws InvalidArgsError,
for (i = 0; i < args.length; i++) { AndrolibException {
String opt = args[i]; ApkDecoder decoder = new ApkDecoder();
if (! opt.startsWith("-")) {
break;
}
if ("-s".equals(opt) || "--no-src".equals(opt)) {
decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_NONE);
} else if ("-d".equals(opt) || "--debug".equals(opt)) {
decoder.setDebugMode(true);
} else if ("-b".equals(opt) || "--no-debug-info".equals(opt)) {
decoder.setBaksmaliDebugMode(false);
} else if ("-t".equals(opt) || "--frame-tag".equals(opt)) {
i++;
if (i >= args.length) {
throw new InvalidArgsError();
}
decoder.setFrameworkTag(args[i]);
} else if ("-f".equals(opt) || "--force".equals(opt)) {
decoder.setForceDelete(true);
} else if ("-r".equals(opt) || "--no-res".equals(opt)) {
decoder.setDecodeResources(ApkDecoder.DECODE_RESOURCES_NONE);
} else if ("--keep-broken-res".equals(opt)) {
decoder.setKeepBrokenResources(true);
} else if ("--frame-path".equals(opt)) {
i++;
System.out.println("Using Framework Directory: " + args[i]);
decoder.setFrameworkDir(args[i]);
} else {
throw new InvalidArgsError();
}
}
String outName = null; int i;
if (args.length == i + 2) { for (i = 0; i < args.length; i++) {
outName = args[i + 1]; String opt = args[i];
} else if (args.length == i + 1) { if (!opt.startsWith("-")) {
outName = args[i]; break;
outName = outName.endsWith(".apk") ? }
outName.substring(0, outName.length() - 4) : outName + ".out"; if ("-s".equals(opt) || "--no-src".equals(opt)) {
outName = new File(outName).getName(); decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_NONE);
} else { } else if ("-d".equals(opt) || "--debug".equals(opt)) {
throw new InvalidArgsError(); decoder.setDebugMode(true);
} } else if ("-b".equals(opt) || "--no-debug-info".equals(opt)) {
File outDir = new File(outName); decoder.setBaksmaliDebugMode(false);
decoder.setOutDir(outDir); } else if ("-t".equals(opt) || "--frame-tag".equals(opt)) {
decoder.setApkFile(new File(args[i])); i++;
if (i >= args.length) {
throw new InvalidArgsError();
}
decoder.setFrameworkTag(args[i]);
} else if ("-f".equals(opt) || "--force".equals(opt)) {
decoder.setForceDelete(true);
} else if ("-r".equals(opt) || "--no-res".equals(opt)) {
decoder.setDecodeResources(ApkDecoder.DECODE_RESOURCES_NONE);
} else if ("--keep-broken-res".equals(opt)) {
decoder.setKeepBrokenResources(true);
} else if ("--frame-path".equals(opt)) {
i++;
System.out.println("Using Framework Directory: " + args[i]);
decoder.setFrameworkDir(args[i]);
} else {
throw new InvalidArgsError();
}
}
try { String outName = null;
decoder.decode(); if (args.length == i + 2) {
} catch (OutDirExistsException ex) { outName = args[i + 1];
System.out.println( } else if (args.length == i + 1) {
"Destination directory (" + outDir.getAbsolutePath() + ") " + outName = args[i];
"already exists. Use -f switch if you want to overwrite it."); outName = outName.endsWith(".apk") ? outName.substring(0,
System.exit(1); outName.length() - 4) : outName + ".out";
} catch (InFileNotFoundException ex) { outName = new File(outName).getName();
System.out.println( } else {
"Input file (" + args[i] + ") " + throw new InvalidArgsError();
"was not found or was not readable."); }
System.exit(1); File outDir = new File(outName);
} catch (CantFindFrameworkResException ex) { decoder.setOutDir(outDir);
System.out.println( decoder.setApkFile(new File(args[i]));
"Can't find framework resources for package of id: " +
String.valueOf(ex.getPkgId()) + ". You must install proper " +
"framework files, see project website for more info.");
System.exit(1);
} catch (IOException ex) {
System.out.println(
"Could not modify file. Please ensure you have permission.");
System.exit(1);
}
}
private static void cmdBuild(String[] args) throws BrutException { try {
decoder.decode();
// hold all the fields } catch (OutDirExistsException ex) {
HashMap<String, Boolean> flags = new HashMap<String, Boolean>(); System.out
flags.put("forceBuildAll", false); .println("Destination directory ("
flags.put("debug", false); + outDir.getAbsolutePath()
flags.put("verbose", false); + ") "
flags.put("injectOriginal", false); + "already exists. Use -f switch if you want to overwrite it.");
flags.put("framework", false); System.exit(1);
flags.put("update", false); } catch (InFileNotFoundException ex) {
System.out.println("Input file (" + args[i] + ") "
int i; + "was not found or was not readable.");
int skip = 0; System.exit(1);
ExtFile mOrigApk = null; } catch (CantFindFrameworkResException ex) {
String mAaptPath = ""; System.out
for (i = 0; i < args.length; i++) { .println("Can't find framework resources for package of id: "
String opt = args[i]; + String.valueOf(ex.getPkgId())
if (! opt.startsWith("-")) { + ". You must install proper "
break; + "framework files, see project website for more info.");
} System.exit(1);
if ("-f".equals(opt) || "--force-all".equals(opt)) { } catch (IOException ex) {
flags.put("forceBuildAll", true); System.out
} else if ("-d".equals(opt) || "--debug".equals(opt)) { .println("Could not modify file. Please ensure you have permission.");
flags.put("debug", true); System.exit(1);
} else if ("-v".equals(opt) || "--verbose".equals(opt)) { }
flags.put("verbose", true);
} else if ("-a".equals(opt) || "--aapt".equals(opt)) {
mAaptPath = args[i + 1];
skip = 1;
} else if ("-o".equals(opt) || "--original".equals(opt)) {
if (args.length >= 4) {
throw new InvalidArgsError();
} else {
flags.put("injectOriginal", true);
mOrigApk = new ExtFile(args[i + 1]);
skip = 1;
}
} else {
throw new InvalidArgsError();
}
}
String appDirName; }
File outFile = null;
switch (args.length - i - skip) { private static void cmdBuild(String[] args) throws BrutException {
case 0:
appDirName = "."; // hold all the fields
break; HashMap<String, Boolean> flags = new HashMap<String, Boolean>();
case 2: flags.put("forceBuildAll", false);
outFile = new File(args[i + 1 + skip]); flags.put("debug", false);
case 1: flags.put("verbose", false);
appDirName = args[i + skip]; flags.put("injectOriginal", false);
break; flags.put("framework", false);
default: flags.put("update", false);
throw new InvalidArgsError();
} int i;
int skip = 0;
new Androlib().build(new File(appDirName), outFile, flags, mOrigApk, mAaptPath); ExtFile mOrigApk = null;
} String mAaptPath = "";
for (i = 0; i < args.length; i++) {
String opt = args[i];
if (!opt.startsWith("-")) {
break;
}
if ("-f".equals(opt) || "--force-all".equals(opt)) {
flags.put("forceBuildAll", true);
} else if ("-d".equals(opt) || "--debug".equals(opt)) {
flags.put("debug", true);
} else if ("-v".equals(opt) || "--verbose".equals(opt)) {
flags.put("verbose", true);
} else if ("-a".equals(opt) || "--aapt".equals(opt)) {
mAaptPath = args[i + 1];
skip = 1;
} else if ("-o".equals(opt) || "--original".equals(opt)) {
if (args.length >= 4) {
throw new InvalidArgsError();
} else {
flags.put("injectOriginal", true);
mOrigApk = new ExtFile(args[i + 1]);
skip = 1;
}
} else {
throw new InvalidArgsError();
}
}
String appDirName;
File outFile = null;
switch (args.length - i - skip) {
case 0:
appDirName = ".";
break;
case 2:
outFile = new File(args[i + 1 + skip]);
case 1:
appDirName = args[i + skip];
break;
default:
throw new InvalidArgsError();
}
new Androlib().build(new File(appDirName), outFile, flags, mOrigApk,
mAaptPath);
}
private static void cmdInstallFramework(String[] args) private static void cmdInstallFramework(String[] args)
throws AndrolibException { throws AndrolibException {
@ -254,120 +257,123 @@ public class Main {
throw new InvalidArgsError(); throw new InvalidArgsError();
} }
private static void cmdPublicizeResources(String[] args) private static void cmdPublicizeResources(String[] args)
throws InvalidArgsError, AndrolibException { throws InvalidArgsError, AndrolibException {
if (args.length != 1) { if (args.length != 1) {
throw new InvalidArgsError(); throw new InvalidArgsError();
} }
new Androlib().publicizeResources(new File(args[0])); new Androlib().publicizeResources(new File(args[0]));
} }
private static void version_print() { private static void version_print() {
System.out.println( System.out.println(Androlib.getVersion());
Androlib.getVersion()); }
}
private static void usage() {
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 smali v" + ApktoolProperties.get("smaliVersion") +
", and baksmali v" + ApktoolProperties.get("baksmaliVersion") + "\n" +
"Updated by @iBotPeaches <connor.tumbleson@gmail.com> \n" +
"Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" +
"\n" +
"Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n" +
"\n" +
"COMMANDs are:\n" +
"\n" +
" d[ecode] [OPTS] <file.apk> [<dir>]\n" +
" Decode <file.apk> to <dir>.\n" +
"\n" +
" OPTS:\n" +
"\n" +
" -s, --no-src\n" +
" Do not decode sources.\n" +
" -r, --no-res\n" +
" Do not decode resources.\n" +
" -d, --debug\n" +
" Decode in debug mode. Check project page for more info.\n" +
" -b, --no-debug-info\n" +
" Baksmali -- don't write out debug info (.local, .param, .line, etc.)\n" +
" -f, --force\n" +
" Force delete destination directory.\n" +
" -t <tag>, --frame-tag <tag>\n" +
" Try to use framework files tagged by <tag>.\n" +
" --frame-path <dir>\n" +
" Use the specified directory for framework files\n" +
" --keep-broken-res\n" +
" Use if there was an error and some resources were dropped, e.g.:\n" +
" \"Invalid config flags detected. Dropping resources\", but you\n" +
" want to decode them anyway, even with errors. You will have to\n" +
" fix them manually before building." +
"\n\n" +
" b[uild] [OPTS] [<app_path>] [<out_file>]\n" +
" Build an apk from already decoded application located in <app_path>.\n" +
"\n" +
" It will automatically detect, whether files was changed and perform\n" +
" needed steps only.\n" +
"\n" +
" If you omit <app_path> then current directory will be used.\n" +
" If you omit <out_file> then <app_path>/dist/<name_of_original.apk>\n" +
" will be used.\n" +
"\n" +
" OPTS:\n" +
"\n" +
" -f, --force-all\n" +
" Skip changes detection and build all files.\n" +
" -d, --debug\n" +
" Build in debug mode. Check project page for more info.\n" +
" -a, --aapt\n" +
" Loads aapt from specified location.\n" +
"\n" +
" if|install-framework <framework.apk> [<tag>] --frame-path [<location>] \n" +
" Install framework file to your system.\n" +
"\n" +
"For additional info, see: http://code.google.com/p/android-apktool/" +
"\n" +
"For smali/baksmali info, see: http://code.google.com/p/smali/"
);
}
private static void setupLogging(Verbosity verbosity) { private static void usage() {
Logger logger = Logger.getLogger(""); System.out
for (Handler handler : logger.getHandlers()) { .println("Apktool v"
logger.removeHandler(handler); + Androlib.getVersion()
} + " - a tool for reengineering Android apk files\n"
if (verbosity == Verbosity.QUIET) { + "Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>\n"
return; + "with smali v"
} + ApktoolProperties.get("smaliVersion")
+ ", and baksmali v"
+ ApktoolProperties.get("baksmaliVersion")
+ "\n"
+ "Updated by @iBotPeaches <connor.tumbleson@gmail.com> \n"
+ "Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n"
+ "\n"
+ "Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n"
+ "\n"
+ "COMMANDs are:\n"
+ "\n"
+ " d[ecode] [OPTS] <file.apk> [<dir>]\n"
+ " Decode <file.apk> to <dir>.\n"
+ "\n"
+ " OPTS:\n"
+ "\n"
+ " -s, --no-src\n"
+ " Do not decode sources.\n"
+ " -r, --no-res\n"
+ " Do not decode resources.\n"
+ " -d, --debug\n"
+ " Decode in debug mode. Check project page for more info.\n"
+ " -b, --no-debug-info\n"
+ " Baksmali -- don't write out debug info (.local, .param, .line, etc.)\n"
+ " -f, --force\n"
+ " Force delete destination directory.\n"
+ " -t <tag>, --frame-tag <tag>\n"
+ " Try to use framework files tagged by <tag>.\n"
+ " --frame-path <dir>\n"
+ " Use the specified directory for framework files\n"
+ " --keep-broken-res\n"
+ " Use if there was an error and some resources were dropped, e.g.:\n"
+ " \"Invalid config flags detected. Dropping resources\", but you\n"
+ " want to decode them anyway, even with errors. You will have to\n"
+ " fix them manually before building."
+ "\n\n"
+ " b[uild] [OPTS] [<app_path>] [<out_file>]\n"
+ " Build an apk from already decoded application located in <app_path>.\n"
+ "\n"
+ " It will automatically detect, whether files was changed and perform\n"
+ " needed steps only.\n"
+ "\n"
+ " If you omit <app_path> then current directory will be used.\n"
+ " If you omit <out_file> then <app_path>/dist/<name_of_original.apk>\n"
+ " will be used.\n"
+ "\n"
+ " OPTS:\n"
+ "\n"
+ " -f, --force-all\n"
+ " Skip changes detection and build all files.\n"
+ " -d, --debug\n"
+ " Build in debug mode. Check project page for more info.\n"
+ " -a, --aapt\n"
+ " Loads aapt from specified location.\n"
+ "\n"
+ " if|install-framework <framework.apk> [<tag>] --frame-path [<location>] \n"
+ " Install framework file to your system.\n"
+ "\n"
+ "For additional info, see: http://code.google.com/p/android-apktool/"
+ "\n"
+ "For smali/baksmali info, see: http://code.google.com/p/smali/");
}
Handler handler = new ConsoleHandler(); private static void setupLogging(Verbosity verbosity) {
logger.addHandler(handler); Logger logger = Logger.getLogger("");
for (Handler handler : logger.getHandlers()) {
logger.removeHandler(handler);
}
if (verbosity == Verbosity.QUIET) {
return;
}
if (verbosity == Verbosity.VERBOSE) { Handler handler = new ConsoleHandler();
handler.setLevel(Level.ALL); logger.addHandler(handler);
logger.setLevel(Level.ALL);
} else {
handler.setFormatter(new Formatter() {
@Override
public String format(LogRecord record) {
return record.getLevel().toString().charAt(0) + ": "
+ record.getMessage()
+ System.getProperty("line.separator");
}
});
}
}
private static enum Verbosity { if (verbosity == Verbosity.VERBOSE) {
NORMAL, VERBOSE, QUIET; handler.setLevel(Level.ALL);
} logger.setLevel(Level.ALL);
} else {
private static boolean Advanced = false; handler.setFormatter(new Formatter() {
@Override
public String format(LogRecord record) {
return record.getLevel().toString().charAt(0) + ": "
+ record.getMessage()
+ System.getProperty("line.separator");
}
});
}
}
static class InvalidArgsError extends AndrolibException { private static enum Verbosity {
NORMAL, VERBOSE, QUIET;
}
} private static boolean Advanced = false;
static class InvalidArgsError extends AndrolibException {
}
} }

View File

@ -21,16 +21,15 @@ import org.xmlpull.v1.XmlPullParser;
import android.util.AttributeSet; import android.util.AttributeSet;
/** /**
* The XML parsing interface returned for an XML resource. This is a standard * The XML parsing interface returned for an XML resource. This is a standard
* XmlPullParser interface, as well as an extended AttributeSet interface and * XmlPullParser interface, as well as an extended AttributeSet interface and an
* an additional close() method on this interface for the client to indicate * additional close() method on this interface for the client to indicate when
* when it is done reading the resource. * it is done reading the resource.
*/ */
public interface XmlResourceParser extends XmlPullParser, AttributeSet { public interface XmlResourceParser extends XmlPullParser, AttributeSet {
/** /**
* Close this interface to the resource. Calls on the interface are no * Close this interface to the resource. Calls on the interface are no
* longer value after this call. * longer value after this call.
*/ */
public void close(); public void close();
} }

View File

@ -17,33 +17,61 @@ package android.util;
/** /**
* @author Dmitry Skiba * @author Dmitry Skiba
* *
*/ */
public interface AttributeSet { public interface AttributeSet {
int getAttributeCount(); int getAttributeCount();
String getAttributeName(int index);
String getAttributeValue(int index);
String getPositionDescription();
int getAttributeNameResource(int index);
int getAttributeListValue(int index,String options[],int defaultValue);
boolean getAttributeBooleanValue(int index,boolean defaultValue);
int getAttributeResourceValue(int index,int defaultValue);
int getAttributeIntValue(int index,int defaultValue);
int getAttributeUnsignedIntValue(int index,int defaultValue);
float getAttributeFloatValue(int index,float defaultValue);
String getIdAttribute();
String getClassAttribute();
int getIdAttributeResourceValue(int index);
int getStyleAttribute();
String getAttributeValue(String namespace, String attribute);
int getAttributeListValue(String namespace,String attribute,String options[],int defaultValue);
boolean getAttributeBooleanValue(String namespace,String attribute,boolean defaultValue);
int getAttributeResourceValue(String namespace,String attribute,int defaultValue);
int getAttributeIntValue(String namespace,String attribute,int defaultValue);
int getAttributeUnsignedIntValue(String namespace,String attribute,int defaultValue);
float getAttributeFloatValue(String namespace,String attribute,float defaultValue);
//TODO: remove String getAttributeName(int index);
int getAttributeValueType(int index);
int getAttributeValueData(int index); String getAttributeValue(int index);
String getPositionDescription();
int getAttributeNameResource(int index);
int getAttributeListValue(int index, String options[], int defaultValue);
boolean getAttributeBooleanValue(int index, boolean defaultValue);
int getAttributeResourceValue(int index, int defaultValue);
int getAttributeIntValue(int index, int defaultValue);
int getAttributeUnsignedIntValue(int index, int defaultValue);
float getAttributeFloatValue(int index, float defaultValue);
String getIdAttribute();
String getClassAttribute();
int getIdAttributeResourceValue(int index);
int getStyleAttribute();
String getAttributeValue(String namespace, String attribute);
int getAttributeListValue(String namespace, String attribute,
String options[], int defaultValue);
boolean getAttributeBooleanValue(String namespace, String attribute,
boolean defaultValue);
int getAttributeResourceValue(String namespace, String attribute,
int defaultValue);
int getAttributeIntValue(String namespace, String attribute,
int defaultValue);
int getAttributeUnsignedIntValue(String namespace, String attribute,
int defaultValue);
float getAttributeFloatValue(String namespace, String attribute,
float defaultValue);
// TODO: remove
int getAttributeValueType(int index);
int getAttributeValueData(int index);
} }

View File

@ -17,247 +17,285 @@
package android.util; package android.util;
/** /**
* Container for a dynamically typed data value. Primarily used with * Container for a dynamically typed data value. Primarily used with
* {@link android.content.res.Resources} for holding resource values. * {@link android.content.res.Resources} for holding resource values.
*/ */
public class TypedValue { public class TypedValue {
/** The value contains no data. */ /** The value contains no data. */
public static final int TYPE_NULL = 0x00; public static final int TYPE_NULL = 0x00;
/** The <var>data</var> field holds a resource identifier. */ /** The <var>data</var> field holds a resource identifier. */
public static final int TYPE_REFERENCE = 0x01; public static final int TYPE_REFERENCE = 0x01;
/** The <var>data</var> field holds an attribute resource /**
* identifier (referencing an attribute in the current theme * The <var>data</var> field holds an attribute resource identifier
* style, not a resource entry). */ * (referencing an attribute in the current theme style, not a resource
public static final int TYPE_ATTRIBUTE = 0x02; * entry).
/** The <var>string</var> field holds string data. In addition, if */
* <var>data</var> is non-zero then it is the string block public static final int TYPE_ATTRIBUTE = 0x02;
* index of the string and <var>assetCookie</var> is the set of /**
* assets the string came from. */ * The <var>string</var> field holds string data. In addition, if
public static final int TYPE_STRING = 0x03; * <var>data</var> is non-zero then it is the string block index of the
/** The <var>data</var> field holds an IEEE 754 floating point number. */ * string and <var>assetCookie</var> is the set of assets the string came
public static final int TYPE_FLOAT = 0x04; * from.
/** The <var>data</var> field holds a complex number encoding a */
* dimension value. */ public static final int TYPE_STRING = 0x03;
public static final int TYPE_DIMENSION = 0x05; /** The <var>data</var> field holds an IEEE 754 floating point number. */
/** The <var>data</var> field holds a complex number encoding a fraction public static final int TYPE_FLOAT = 0x04;
* of a container. */ /**
public static final int TYPE_FRACTION = 0x06; * The <var>data</var> field holds a complex number encoding a dimension
* value.
*/
public static final int TYPE_DIMENSION = 0x05;
/**
* The <var>data</var> field holds a complex number encoding a fraction of a
* container.
*/
public static final int TYPE_FRACTION = 0x06;
/** Identifies the start of plain integer values. Any type value /**
* from this to {@link #TYPE_LAST_INT} means the * Identifies the start of plain integer values. Any type value from this to
* <var>data</var> field holds a generic integer value. */ * {@link #TYPE_LAST_INT} means the <var>data</var> field holds a generic
public static final int TYPE_FIRST_INT = 0x10; * integer value.
*/
public static final int TYPE_FIRST_INT = 0x10;
/** The <var>data</var> field holds a number that was /**
* originally specified in decimal. */ * The <var>data</var> field holds a number that was originally specified in
public static final int TYPE_INT_DEC = 0x10; * decimal.
/** The <var>data</var> field holds a number that was */
* originally specified in hexadecimal (0xn). */ public static final int TYPE_INT_DEC = 0x10;
public static final int TYPE_INT_HEX = 0x11; /**
/** The <var>data</var> field holds 0 or 1 that was originally * The <var>data</var> field holds a number that was originally specified in
* specified as "false" or "true". */ * hexadecimal (0xn).
public static final int TYPE_INT_BOOLEAN = 0x12; */
public static final int TYPE_INT_HEX = 0x11;
/**
* The <var>data</var> field holds 0 or 1 that was originally specified as
* "false" or "true".
*/
public static final int TYPE_INT_BOOLEAN = 0x12;
/** Identifies the start of integer values that were specified as /**
* color constants (starting with '#'). */ * Identifies the start of integer values that were specified as color
public static final int TYPE_FIRST_COLOR_INT = 0x1c; * constants (starting with '#').
*/
public static final int TYPE_FIRST_COLOR_INT = 0x1c;
/** The <var>data</var> field holds a color that was originally /**
* specified as #aarrggbb. */ * The <var>data</var> field holds a color that was originally specified as
public static final int TYPE_INT_COLOR_ARGB8 = 0x1c; * #aarrggbb.
/** The <var>data</var> field holds a color that was originally */
* specified as #rrggbb. */ public static final int TYPE_INT_COLOR_ARGB8 = 0x1c;
public static final int TYPE_INT_COLOR_RGB8 = 0x1d; /**
/** The <var>data</var> field holds a color that was originally * The <var>data</var> field holds a color that was originally specified as
* specified as #argb. */ * #rrggbb.
public static final int TYPE_INT_COLOR_ARGB4 = 0x1e; */
/** The <var>data</var> field holds a color that was originally public static final int TYPE_INT_COLOR_RGB8 = 0x1d;
* specified as #rgb. */ /**
public static final int TYPE_INT_COLOR_RGB4 = 0x1f; * The <var>data</var> field holds a color that was originally specified as
* #argb.
*/
public static final int TYPE_INT_COLOR_ARGB4 = 0x1e;
/**
* The <var>data</var> field holds a color that was originally specified as
* #rgb.
*/
public static final int TYPE_INT_COLOR_RGB4 = 0x1f;
/** Identifies the end of integer values that were specified as color /**
* constants. */ * Identifies the end of integer values that were specified as color
public static final int TYPE_LAST_COLOR_INT = 0x1f; * constants.
*/
public static final int TYPE_LAST_COLOR_INT = 0x1f;
/** Identifies the end of plain integer values. */ /** Identifies the end of plain integer values. */
public static final int TYPE_LAST_INT = 0x1f; public static final int TYPE_LAST_INT = 0x1f;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Complex data: bit location of unit information. */ /** Complex data: bit location of unit information. */
public static final int COMPLEX_UNIT_SHIFT = 0; public static final int COMPLEX_UNIT_SHIFT = 0;
/** Complex data: mask to extract unit information (after shifting by /**
* {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as * Complex data: mask to extract unit information (after shifting by
* defined below. */ * {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as defined
public static final int COMPLEX_UNIT_MASK = 0xf; * below.
*/
public static final int COMPLEX_UNIT_MASK = 0xf;
/** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */ /** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */
public static final int COMPLEX_UNIT_PX = 0; public static final int COMPLEX_UNIT_PX = 0;
/** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent /**
* Pixels. */ * {@link #TYPE_DIMENSION} complex unit: Value is Device Independent Pixels.
public static final int COMPLEX_UNIT_DIP = 1; */
/** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */ public static final int COMPLEX_UNIT_DIP = 1;
public static final int COMPLEX_UNIT_SP = 2; /** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */
/** {@link #TYPE_DIMENSION} complex unit: Value is in points. */ public static final int COMPLEX_UNIT_SP = 2;
public static final int COMPLEX_UNIT_PT = 3; /** {@link #TYPE_DIMENSION} complex unit: Value is in points. */
/** {@link #TYPE_DIMENSION} complex unit: Value is in inches. */ public static final int COMPLEX_UNIT_PT = 3;
public static final int COMPLEX_UNIT_IN = 4; /** {@link #TYPE_DIMENSION} complex unit: Value is in inches. */
/** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */ public static final int COMPLEX_UNIT_IN = 4;
public static final int COMPLEX_UNIT_MM = 5; /** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */
public static final int COMPLEX_UNIT_MM = 5;
/** {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall /**
* size. */ * {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall size.
public static final int COMPLEX_UNIT_FRACTION = 0; */
/** {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. */ public static final int COMPLEX_UNIT_FRACTION = 0;
public static final int COMPLEX_UNIT_FRACTION_PARENT = 1; /** {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. */
public static final int COMPLEX_UNIT_FRACTION_PARENT = 1;
/** Complex data: where the radix information is, telling where the decimal /**
* place appears in the mantissa. */ * Complex data: where the radix information is, telling where the decimal
public static final int COMPLEX_RADIX_SHIFT = 4; * place appears in the mantissa.
/** Complex data: mask to extract radix information (after shifting by */
* {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point public static final int COMPLEX_RADIX_SHIFT = 4;
* representations as defined below. */ /**
public static final int COMPLEX_RADIX_MASK = 0x3; * Complex data: mask to extract radix information (after shifting by
* {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point
* representations as defined below.
*/
public static final int COMPLEX_RADIX_MASK = 0x3;
/** Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 */ /** Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 */
public static final int COMPLEX_RADIX_23p0 = 0; public static final int COMPLEX_RADIX_23p0 = 0;
/** Complex data: the mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn */ /** Complex data: the mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn */
public static final int COMPLEX_RADIX_16p7 = 1; public static final int COMPLEX_RADIX_16p7 = 1;
/** Complex data: the mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn */ /** Complex data: the mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn */
public static final int COMPLEX_RADIX_8p15 = 2; public static final int COMPLEX_RADIX_8p15 = 2;
/** Complex data: the mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn */ /** Complex data: the mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn */
public static final int COMPLEX_RADIX_0p23 = 3; public static final int COMPLEX_RADIX_0p23 = 3;
/** Complex data: bit location of mantissa information. */ /** Complex data: bit location of mantissa information. */
public static final int COMPLEX_MANTISSA_SHIFT = 8; public static final int COMPLEX_MANTISSA_SHIFT = 8;
/** Complex data: mask to extract mantissa information (after shifting by /**
* {@link #COMPLEX_MANTISSA_SHIFT}). This gives us 23 bits of precision; * Complex data: mask to extract mantissa information (after shifting by
* the top bit is the sign. */ * {@link #COMPLEX_MANTISSA_SHIFT}). This gives us 23 bits of precision; the
public static final int COMPLEX_MANTISSA_MASK = 0xffffff; * top bit is the sign.
*/
public static final int COMPLEX_MANTISSA_MASK = 0xffffff;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** /**
* If {@link #density} is equal to this value, then the density should be * If {@link #density} is equal to this value, then the density should be
* treated as the system's default density value: {@link DisplayMetrics#DENSITY_DEFAULT}. * treated as the system's default density value:
*/ * {@link DisplayMetrics#DENSITY_DEFAULT}.
public static final int DENSITY_DEFAULT = 0; */
public static final int DENSITY_DEFAULT = 0;
/** /**
* If {@link #density} is equal to this value, then there is no density * If {@link #density} is equal to this value, then there is no density
* associated with the resource and it should not be scaled. * associated with the resource and it should not be scaled.
*/ */
public static final int DENSITY_NONE = 0xffff; public static final int DENSITY_NONE = 0xffff;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** The type held by this value, as defined by the constants here. /**
* This tells you how to interpret the other fields in the object. */ * The type held by this value, as defined by the constants here. This tells
public int type; * you how to interpret the other fields in the object.
*/
public int type;
private static final float MANTISSA_MULT = private static final float MANTISSA_MULT = 1.0f / (1 << TypedValue.COMPLEX_MANTISSA_SHIFT);
1.0f / (1<<TypedValue.COMPLEX_MANTISSA_SHIFT); private static final float[] RADIX_MULTS = new float[] {
private static final float[] RADIX_MULTS = new float[] { 1.0f * MANTISSA_MULT, 1.0f / (1 << 7) * MANTISSA_MULT,
1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT, 1.0f / (1 << 15) * MANTISSA_MULT, 1.0f / (1 << 23) * MANTISSA_MULT };
1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT
};
/** /**
* Retrieve the base value from a complex data integer. This uses the * Retrieve the base value from a complex data integer. This uses the
* {@link #COMPLEX_MANTISSA_MASK} and {@link #COMPLEX_RADIX_MASK} fields of * {@link #COMPLEX_MANTISSA_MASK} and {@link #COMPLEX_RADIX_MASK} fields of
* the data to compute a floating point representation of the number they * the data to compute a floating point representation of the number they
* describe. The units are ignored. * describe. The units are ignored.
* *
* @param complex A complex data value. * @param complex
* * A complex data value.
* @return A floating point value corresponding to the complex data. *
*/ * @return A floating point value corresponding to the complex data.
public static float complexToFloat(int complex) */
{ public static float complexToFloat(int complex) {
return (complex&(TypedValue.COMPLEX_MANTISSA_MASK return (complex & (TypedValue.COMPLEX_MANTISSA_MASK << TypedValue.COMPLEX_MANTISSA_SHIFT))
<<TypedValue.COMPLEX_MANTISSA_SHIFT)) * RADIX_MULTS[(complex >> TypedValue.COMPLEX_RADIX_SHIFT)
* RADIX_MULTS[(complex>>TypedValue.COMPLEX_RADIX_SHIFT) & TypedValue.COMPLEX_RADIX_MASK];
& TypedValue.COMPLEX_RADIX_MASK]; }
}
private static final String[] DIMENSION_UNIT_STRS = new String[] { private static final String[] DIMENSION_UNIT_STRS = new String[] { "px",
"px", "dip", "sp", "pt", "in", "mm" "dip", "sp", "pt", "in", "mm" };
}; private static final String[] FRACTION_UNIT_STRS = new String[] { "%", "%p" };
private static final String[] FRACTION_UNIT_STRS = new String[] {
"%", "%p"
};
/** /**
* Perform type conversion as per {@link #coerceToString()} on an * Perform type conversion as per {@link #coerceToString()} on an explicitly
* explicitly supplied type and data. * supplied type and data.
* *
* @param type The data type identifier. * @param type
* @param data The data value. * The data type identifier.
* * @param data
* @return String The coerced string value. If the value is * The data value.
* null or the type is not known, null is returned. *
*/ * @return String The coerced string value. If the value is null or the type
public static final String coerceToString(int type, int data) * is not known, null is returned.
{ */
switch (type) { public static final String coerceToString(int type, int data) {
case TYPE_NULL: switch (type) {
return null; case TYPE_NULL:
case TYPE_REFERENCE: return null;
return "@" + data; case TYPE_REFERENCE:
case TYPE_ATTRIBUTE: return "@" + data;
return "?" + data; case TYPE_ATTRIBUTE:
case TYPE_FLOAT: return "?" + data;
return Float.toString(Float.intBitsToFloat(data)); case TYPE_FLOAT:
case TYPE_DIMENSION: return Float.toString(Float.intBitsToFloat(data));
return Float.toString(complexToFloat(data)) + DIMENSION_UNIT_STRS[ case TYPE_DIMENSION:
(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; return Float.toString(complexToFloat(data))
case TYPE_FRACTION: + DIMENSION_UNIT_STRS[(data >> COMPLEX_UNIT_SHIFT)
return Float.toString(complexToFloat(data)*100) + FRACTION_UNIT_STRS[ & COMPLEX_UNIT_MASK];
(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; case TYPE_FRACTION:
case TYPE_INT_HEX: return Float.toString(complexToFloat(data) * 100)
return "0x" + Integer.toHexString(data); + FRACTION_UNIT_STRS[(data >> COMPLEX_UNIT_SHIFT)
case TYPE_INT_BOOLEAN: & COMPLEX_UNIT_MASK];
return data != 0 ? "true" : "false"; case TYPE_INT_HEX:
} return "0x" + Integer.toHexString(data);
case TYPE_INT_BOOLEAN:
return data != 0 ? "true" : "false";
}
if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) { if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) {
String res =String.format("%08x", data); String res = String.format("%08x", data);
char[] vals = res.toCharArray(); char[] vals = res.toCharArray();
switch (type) { switch (type) {
default: default:
case TYPE_INT_COLOR_ARGB8://#AaRrGgBb case TYPE_INT_COLOR_ARGB8:// #AaRrGgBb
break; break;
case TYPE_INT_COLOR_RGB8://#FFRrGgBb->#RrGgBb case TYPE_INT_COLOR_RGB8:// #FFRrGgBb->#RrGgBb
res = res.substring(2); res = res.substring(2);
break; break;
case TYPE_INT_COLOR_ARGB4://#AARRGGBB->#ARGB case TYPE_INT_COLOR_ARGB4:// #AARRGGBB->#ARGB
res = new StringBuffer().append(vals[0]).append(vals[2]).append(vals[4]).append(vals[6]).toString(); res = new StringBuffer().append(vals[0]).append(vals[2])
break; .append(vals[4]).append(vals[6]).toString();
case TYPE_INT_COLOR_RGB4://#FFRRGGBB->#RGB break;
res = new StringBuffer().append(vals[2]).append(vals[4]).append(vals[6]).toString(); case TYPE_INT_COLOR_RGB4:// #FFRRGGBB->#RGB
break; res = new StringBuffer().append(vals[2]).append(vals[4])
} .append(vals[6]).toString();
return "#" + res; break;
} else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) { }
String res; return "#" + res;
switch (type) { } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) {
default: String res;
case TYPE_INT_DEC: switch (type) {
res = Integer.toString(data); default:
break; case TYPE_INT_DEC:
//defined before res = Integer.toString(data);
/*case TYPE_INT_HEX: break;
res = "0x" + Integer.toHexString(data); // defined before
break; /*
case TYPE_INT_BOOLEAN: * case TYPE_INT_HEX: res = "0x" + Integer.toHexString(data); break;
res = (data != 0) ? "true":"false"; * case TYPE_INT_BOOLEAN: res = (data != 0) ? "true":"false"; break;
break;*/ */
} }
return res; return res;
} }
return null; return null;
} }
}; };

View File

@ -22,18 +22,18 @@ import brut.common.BrutException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class AndrolibException extends BrutException { public class AndrolibException extends BrutException {
public AndrolibException() { public AndrolibException() {
} }
public AndrolibException(String message) { public AndrolibException(String message) {
super(message); super(message);
} }
public AndrolibException(String message, Throwable cause) { public AndrolibException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
public AndrolibException(Throwable cause) { public AndrolibException(Throwable cause) {
super(cause); super(cause);
} }
} }

View File

@ -95,17 +95,19 @@ public class ApkDecoder {
} }
if (hasResources()) { if (hasResources()) {
// read the resources.arsc checking for STORED vs DEFLATE compression // read the resources.arsc checking for STORED vs DEFLATE
// compression
// this will determine whether we compress on rebuild or not. // this will determine whether we compress on rebuild or not.
JarFile jf = new JarFile(mApkFile.getAbsoluteFile()); JarFile jf = new JarFile(mApkFile.getAbsoluteFile());
JarEntry je = jf.getJarEntry("resources.arsc"); JarEntry je = jf.getJarEntry("resources.arsc");
if (je != null) { if (je != null) {
int compression = je.getMethod(); int compression = je.getMethod();
mCompressResources = (compression != ZipEntry.STORED) && (compression == ZipEntry.DEFLATED); mCompressResources = (compression != ZipEntry.STORED)
&& (compression == ZipEntry.DEFLATED);
} }
jf.close(); jf.close();
switch (mDecodeResources) { switch (mDecodeResources) {
case DECODE_RESOURCES_NONE: case DECODE_RESOURCES_NONE:
mAndrolib.decodeResourcesRaw(mApkFile, outDir); mAndrolib.decodeResourcesRaw(mApkFile, outDir);
@ -286,12 +288,12 @@ public class ApkDecoder {
meta.put("packageInfo", info); meta.put("packageInfo", info);
} }
} }
private void putCompressionInfo(Map<String, Object> meta) private void putCompressionInfo(Map<String, Object> meta)
throws AndrolibException { throws AndrolibException {
meta.put("compressionType", getCompressionType()); meta.put("compressionType", getCompressionType());
} }
private boolean getCompressionType() { private boolean getCompressionType() {
return mCompressResources; return mCompressResources;
} }

View File

@ -20,56 +20,57 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jf.baksmali.baksmali;
import org.jf.smali.main;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ApktoolProperties { public class ApktoolProperties {
public static String get(String key) { public static String get(String key) {
return get().getProperty(key); return get().getProperty(key);
} }
public static Properties get() {
if (sProps == null) {
loadProps();
}
return sProps;
}
private static void loadProps() { public static Properties get() {
InputStream in = ApktoolProperties.class.getResourceAsStream("/properties/apktool.properties"); if (sProps == null) {
sProps = new Properties(); loadProps();
try { }
sProps.load(in); return sProps;
in.close(); }
} catch (IOException ex) {
LOGGER.warning("Can't load properties.");
}
InputStream templateStream = ApktoolProperties.class.getResourceAsStream("/properties/baksmali.properties"); private static void loadProps() {
Properties properties = new Properties(); InputStream in = ApktoolProperties.class
String version = "(unknown)"; .getResourceAsStream("/properties/apktool.properties");
try { sProps = new Properties();
properties.load(templateStream); try {
version = properties.getProperty("application.version"); sProps.load(in);
} catch (IOException ex) { in.close();
} } catch (IOException ex) {
sProps.put("baksmaliVersion", version); LOGGER.warning("Can't load properties.");
templateStream = ApktoolProperties.class.getResourceAsStream("/properties/smali.properties"); }
properties = new Properties();
version = "(unknown)"; InputStream templateStream = ApktoolProperties.class
try { .getResourceAsStream("/properties/baksmali.properties");
properties.load(templateStream); Properties properties = new Properties();
version = properties.getProperty("application.version"); String version = "(unknown)";
} catch (IOException ex) { try {
} properties.load(templateStream);
sProps.put("smaliVersion", version); version = properties.getProperty("application.version");
} } catch (IOException ex) {
}
private static Properties sProps; sProps.put("baksmaliVersion", version);
templateStream = ApktoolProperties.class
private static final Logger LOGGER = .getResourceAsStream("/properties/smali.properties");
Logger.getLogger(ApktoolProperties.class.getName()); 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;
private static final Logger LOGGER = Logger
.getLogger(ApktoolProperties.class.getName());
} }

View File

@ -23,18 +23,18 @@ import brut.androlib.AndrolibException;
*/ */
public class CantFind9PatchChunk extends AndrolibException { public class CantFind9PatchChunk extends AndrolibException {
public CantFind9PatchChunk(Throwable cause) { public CantFind9PatchChunk(Throwable cause) {
super(cause); super(cause);
} }
public CantFind9PatchChunk(String message, Throwable cause) { public CantFind9PatchChunk(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
public CantFind9PatchChunk(String message) { public CantFind9PatchChunk(String message) {
super(message); super(message);
} }
public CantFind9PatchChunk() { public CantFind9PatchChunk() {
} }
} }

View File

@ -23,18 +23,18 @@ import brut.androlib.AndrolibException;
*/ */
public class CantFindFrameworkResException extends AndrolibException { public class CantFindFrameworkResException extends AndrolibException {
public CantFindFrameworkResException(Throwable cause, int id) { public CantFindFrameworkResException(Throwable cause, int id) {
super(cause); super(cause);
mPkgId = id; mPkgId = id;
} }
public CantFindFrameworkResException(int id) { public CantFindFrameworkResException(int id) {
mPkgId = id; mPkgId = id;
} }
public int getPkgId() { public int getPkgId() {
return mPkgId; return mPkgId;
} }
private final int mPkgId; private final int mPkgId;
} }

View File

@ -23,18 +23,18 @@ import brut.androlib.AndrolibException;
*/ */
public class InFileNotFoundException extends AndrolibException { public class InFileNotFoundException extends AndrolibException {
public InFileNotFoundException(Throwable cause) { public InFileNotFoundException(Throwable cause) {
super(cause); super(cause);
} }
public InFileNotFoundException(String message, Throwable cause) { public InFileNotFoundException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
public InFileNotFoundException(String message) { public InFileNotFoundException(String message) {
super(message); super(message);
} }
public InFileNotFoundException() { public InFileNotFoundException() {
} }
} }

View File

@ -23,18 +23,18 @@ import brut.androlib.AndrolibException;
*/ */
public class OutDirExistsException extends AndrolibException { public class OutDirExistsException extends AndrolibException {
public OutDirExistsException(Throwable cause) { public OutDirExistsException(Throwable cause) {
super(cause); super(cause);
} }
public OutDirExistsException(String message, Throwable cause) { public OutDirExistsException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
public OutDirExistsException(String message) { public OutDirExistsException(String message) {
super(message); super(message);
} }
public OutDirExistsException() { public OutDirExistsException() {
} }
} }

View File

@ -22,18 +22,18 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class UndefinedResObject extends AndrolibException { public class UndefinedResObject extends AndrolibException {
public UndefinedResObject(Throwable cause) { public UndefinedResObject(Throwable cause) {
super(cause); super(cause);
} }
public UndefinedResObject(String message, Throwable cause) { public UndefinedResObject(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
public UndefinedResObject(String message) { public UndefinedResObject(String message) {
super(message); super(message);
} }
public UndefinedResObject() { public UndefinedResObject() {
} }
} }

View File

@ -24,11 +24,11 @@ import java.io.File;
*/ */
public class AndrolibJava { public class AndrolibJava {
public void decode(ExtFile apkFile, File outDir) { public void decode(ExtFile apkFile, File outDir) {
throw new UnsupportedOperationException("Not yet implemented"); throw new UnsupportedOperationException("Not yet implemented");
} }
public void build(File javaDir, File dex) { public void build(File javaDir, File dex) {
throw new UnsupportedOperationException("Not yet implemented"); throw new UnsupportedOperationException("Not yet implemented");
} }
} }

View File

@ -23,7 +23,7 @@ import java.io.Writer;
*/ */
public class IndentingWriter extends org.jf.util.IndentingWriter { public class IndentingWriter extends org.jf.util.IndentingWriter {
public IndentingWriter(Writer writer) { public IndentingWriter(Writer writer) {
super(writer); super(writer);
} }
} }

View File

@ -28,57 +28,58 @@ import org.jf.smali.*;
*/ */
public class SmaliMod { public class SmaliMod {
public static boolean assembleSmaliFile(InputStream smaliStream, public static boolean assembleSmaliFile(InputStream smaliStream,
String name, DexFile dexFile, boolean verboseErrors, String name, DexFile dexFile, boolean verboseErrors,
boolean oldLexer, boolean printTokens) boolean oldLexer, boolean printTokens) throws IOException,
throws IOException, RecognitionException { RecognitionException {
CommonTokenStream tokens; CommonTokenStream tokens;
boolean lexerErrors = false;
LexerErrorInterface lexer;
boolean lexerErrors = false; InputStreamReader reader = new InputStreamReader(smaliStream, "UTF-8");
LexerErrorInterface lexer;
InputStreamReader reader = new InputStreamReader(smaliStream, "UTF-8"); lexer = new smaliFlexLexer(reader);
tokens = new CommonTokenStream((TokenSource) lexer);
lexer = new smaliFlexLexer(reader); if (printTokens) {
tokens = new CommonTokenStream((TokenSource)lexer); tokens.getTokens();
if (printTokens) { for (int i = 0; i < tokens.size(); i++) {
tokens.getTokens(); Token token = tokens.get(i);
if (token.getChannel() == BaseRecognizer.HIDDEN) {
continue;
}
for (int i=0; i<tokens.size(); i++) { System.out.println(smaliParser.tokenNames[token.getType()]
Token token = tokens.get(i); + ": " + token.getText());
if (token.getChannel() == smaliParser.HIDDEN) { }
continue; }
}
System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText()); smaliParser parser = new smaliParser(tokens);
} parser.setVerboseErrors(verboseErrors);
}
smaliParser parser = new smaliParser(tokens); smaliParser.smali_file_return result = parser.smali_file();
parser.setVerboseErrors(verboseErrors);
smaliParser.smali_file_return result = parser.smali_file(); if (parser.getNumberOfSyntaxErrors() > 0
|| lexer.getNumberOfSyntaxErrors() > 0) {
return false;
}
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) { CommonTree t = (CommonTree) result.getTree();
return false;
}
CommonTree t = (CommonTree) result.getTree(); CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
treeStream.setTokenStream(tokens);
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
treeStream.setTokenStream(tokens);
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); dexGen.dexFile = dexFile;
dexGen.smali_file();
dexGen.dexFile = dexFile; if (dexGen.getNumberOfSyntaxErrors() > 0) {
dexGen.smali_file(); return false;
}
if (dexGen.getNumberOfSyntaxErrors() > 0) { return true;
return false; }
}
return true;
}
} }

View File

@ -145,25 +145,27 @@ final public class AndrolibResources {
LOGGER.info("Decoding AndroidManifest.xml with only framework resources..."); LOGGER.info("Decoding AndroidManifest.xml with only framework resources...");
fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out, fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out,
"AndroidManifest.xml"); "AndroidManifest.xml");
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
public void adjust_package_manifest(ResTable resTable, String filePath) public void adjust_package_manifest(ResTable resTable, String filePath)
throws AndrolibException { throws AndrolibException {
// check if packages different, and that package is not equal to "android" // check if packages different, and that package is not equal to
// "android"
Map<String, String> packageInfo = resTable.getPackageInfo(); Map<String, String> packageInfo = resTable.getPackageInfo();
if ((packageInfo.get("cur_package").equalsIgnoreCase(packageInfo.get("orig_package")) if ((packageInfo.get("cur_package").equalsIgnoreCase(
|| ("android".equalsIgnoreCase(packageInfo.get("cur_package")) packageInfo.get("orig_package")) || ("android"
|| ("com.htc".equalsIgnoreCase(packageInfo.get("cur_package")))))) { .equalsIgnoreCase(packageInfo.get("cur_package")) || ("com.htc"
.equalsIgnoreCase(packageInfo.get("cur_package")))))) {
LOGGER.info("Regular manifest package...");
} else { LOGGER.info("Regular manifest package...");
} else {
try { try {
LOGGER.info("Renamed manifest package found! Fixing..."); LOGGER.info("Renamed manifest package found! Fixing...");
DocumentBuilderFactory docFactory = DocumentBuilderFactory DocumentBuilderFactory docFactory = DocumentBuilderFactory
.newInstance(); .newInstance();
@ -178,15 +180,16 @@ final public class AndrolibResources {
Node nodeAttr = attr.getNamedItem("package"); Node nodeAttr = attr.getNamedItem("package");
mPackageRenamed = nodeAttr.getNodeValue(); mPackageRenamed = nodeAttr.getNodeValue();
nodeAttr.setNodeValue(packageInfo.get("cur_package")); nodeAttr.setNodeValue(packageInfo.get("cur_package"));
// re-save manifest. // re-save manifest.
// fancy an auto-sort :p // fancy an auto-sort :p
TransformerFactory transformerFactory = TransformerFactory.newInstance(); TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory.newTransformer(); Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc); DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(filePath)); StreamResult result = new StreamResult(new File(filePath));
transformer.transform(source, result); transformer.transform(source, result);
} catch (ParserConfigurationException ex) { } catch (ParserConfigurationException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} catch (TransformerException ex) { } catch (TransformerException ex) {
@ -217,9 +220,10 @@ final public class AndrolibResources {
fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out, fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out,
"AndroidManifest.xml"); "AndroidManifest.xml");
// fix package if needed // fix package if needed
adjust_package_manifest(resTable, outDir.getAbsolutePath() + "/AndroidManifest.xml"); adjust_package_manifest(resTable, outDir.getAbsolutePath()
+ "/AndroidManifest.xml");
if (inApk.containsDir("res")) { if (inApk.containsDir("res")) {
in = inApk.getDir("res"); in = inApk.getDir("res");
@ -268,19 +272,21 @@ final public class AndrolibResources {
public void aaptPackage(File apkFile, File manifest, File resDir, public void aaptPackage(File apkFile, File manifest, File resDir,
File rawDir, File assetDir, File[] include, File rawDir, File assetDir, File[] include,
HashMap<String, Boolean> flags, String aaptPath) throws AndrolibException { HashMap<String, Boolean> flags, String aaptPath)
throws AndrolibException {
List<String> cmd = new ArrayList<String>(); List<String> cmd = new ArrayList<String>();
// path for aapt binary // path for aapt binary
if (!aaptPath.isEmpty()) { if (!aaptPath.isEmpty()) {
File aaptFile = new File(aaptPath); File aaptFile = new File(aaptPath);
if (aaptFile.canRead() && aaptFile.exists()) { if (aaptFile.canRead() && aaptFile.exists()) {
aaptFile.setExecutable(true); aaptFile.setExecutable(true);
cmd.add(aaptFile.getPath()); cmd.add(aaptFile.getPath());
if (flags.get("verbose")) { if (flags.get("verbose")) {
LOGGER.info(aaptFile.getPath() + " being used as aapt location."); LOGGER.info(aaptFile.getPath()
+ " being used as aapt location.");
} }
} else { } else {
LOGGER.warning("aapt location could not be found. Defaulting back to default"); LOGGER.warning("aapt location could not be found. Defaulting back to default");
@ -323,10 +329,10 @@ final public class AndrolibResources {
if (flags.get("framework")) { if (flags.get("framework")) {
cmd.add("-x"); cmd.add("-x");
} }
if (!(flags.get("compression"))) { if (!(flags.get("compression"))) {
cmd.add("-0"); cmd.add("-0");
cmd.add("arsc"); cmd.add("arsc");
} }
if (include != null) { if (include != null) {
@ -420,7 +426,7 @@ final public class AndrolibResources {
" "); " ");
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR, serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR,
System.getProperty("line.separator")); System.getProperty("line.separator"));
serial.setProperty(ExtMXSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8"); serial.setProperty(ExtXmlSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8");
serial.setDisabledAttrEscape(true); serial.setDisabledAttrEscape(true);
return serial; return serial;
} }
@ -665,7 +671,8 @@ final public class AndrolibResources {
if (!dir.exists()) { if (!dir.exists()) {
if (!dir.mkdirs()) { if (!dir.mkdirs()) {
if (sFrameworkFolder != null) { if (sFrameworkFolder != null) {
System.out.println("Can't create Framework directory: " + dir); System.out.println("Can't create Framework directory: "
+ dir);
} }
throw new AndrolibException("Can't create directory: " + dir); throw new AndrolibException("Can't create directory: " + dir);
} }
@ -681,7 +688,7 @@ final public class AndrolibResources {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
public void setFrameworkFolder(String path) { public void setFrameworkFolder(String path) {
sFrameworkFolder = path; sFrameworkFolder = path;
} }

View File

@ -36,132 +36,128 @@ import org.apache.commons.io.IOUtils;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResSmaliUpdater { public class ResSmaliUpdater {
public void tagResIDs(ResTable resTable, File smaliDir) public void tagResIDs(ResTable resTable, File smaliDir)
throws AndrolibException { throws AndrolibException {
Directory dir = null; Directory dir = null;
try { try {
dir = new FileDirectory(smaliDir); dir = new FileDirectory(smaliDir);
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException( throw new AndrolibException("Could not tag res IDs", ex);
"Could not tag res IDs", ex); }
} for (String fileName : dir.getFiles(true)) {
for (String fileName : dir.getFiles(true)) { try {
try { tagResIdsForFile(resTable, dir, fileName);
tagResIdsForFile(resTable, dir, fileName); } catch (IOException ex) {
} catch (IOException ex) { throw new AndrolibException("Could not tag resIDs for file: "
throw new AndrolibException( + fileName, ex);
"Could not tag resIDs for file: " + fileName, ex); } catch (DirectoryException ex) {
} catch (DirectoryException ex) { throw new AndrolibException("Could not tag resIDs for file: "
throw new AndrolibException( + fileName, ex);
"Could not tag resIDs for file: " + fileName, ex); } catch (AndrolibException ex) {
} catch (AndrolibException ex) { throw new AndrolibException("Could not tag resIDs for file: "
throw new AndrolibException( + fileName, ex);
"Could not tag resIDs for file: " + fileName, ex); }
} }
} }
}
public void updateResIDs(ResTable resTable, File smaliDir) public void updateResIDs(ResTable resTable, File smaliDir)
throws AndrolibException { throws AndrolibException {
try { try {
Directory dir = new FileDirectory(smaliDir); Directory dir = new FileDirectory(smaliDir);
for (String fileName : dir.getFiles(true)) { for (String fileName : dir.getFiles(true)) {
Iterator<String> it = Iterator<String> it = IOUtils.readLines(
IOUtils.readLines(dir.getFileInput(fileName)).iterator(); dir.getFileInput(fileName)).iterator();
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
while (it.hasNext()) { while (it.hasNext()) {
String line = it.next(); String line = it.next();
out.println(line); out.println(line);
Matcher m1 = RES_NAME_PATTERN.matcher(line); Matcher m1 = RES_NAME_PATTERN.matcher(line);
if (! m1.matches()) { if (!m1.matches()) {
continue; continue;
} }
Matcher m2 = RES_ID_PATTERN.matcher(it.next()); Matcher m2 = RES_ID_PATTERN.matcher(it.next());
if (! m2.matches()) { if (!m2.matches()) {
throw new AndrolibException(); throw new AndrolibException();
} }
int resID = resTable.getPackage(m1.group(1)) int resID = resTable.getPackage(m1.group(1))
.getType(m1.group(2)).getResSpec(m1.group(3)) .getType(m1.group(2)).getResSpec(m1.group(3))
.getId().id; .getId().id;
if (m2.group(1) != null) { if (m2.group(1) != null) {
out.println(String.format( out.println(String.format(RES_ID_FORMAT_FIELD,
RES_ID_FORMAT_FIELD, m2.group(1), resID)); m2.group(1), resID));
} else { } else {
out.println(String.format( out.println(String.format(RES_ID_FORMAT_CONST,
RES_ID_FORMAT_CONST, m2.group(2), resID)); m2.group(2), resID));
} }
} }
out.close(); out.close();
} }
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException( throw new AndrolibException("Could not tag res IDs for: "
"Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex); + smaliDir.getAbsolutePath(), ex);
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException( throw new AndrolibException("Could not tag res IDs for: "
"Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex); + smaliDir.getAbsolutePath(), ex);
} }
} }
private void tagResIdsForFile(ResTable resTable, Directory dir, private void tagResIdsForFile(ResTable resTable, Directory dir,
String fileName) throws IOException, DirectoryException, String fileName) throws IOException, DirectoryException,
AndrolibException { AndrolibException {
Iterator<String> it = Iterator<String> it = IOUtils.readLines(dir.getFileInput(fileName))
IOUtils.readLines(dir.getFileInput(fileName)).iterator(); .iterator();
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
while (it.hasNext()) { while (it.hasNext()) {
String line = it.next(); String line = it.next();
if (RES_NAME_PATTERN.matcher(line).matches()) { if (RES_NAME_PATTERN.matcher(line).matches()) {
out.println(line); out.println(line);
out.println(it.next()); out.println(it.next());
continue; continue;
} }
Matcher m = RES_ID_PATTERN.matcher(line); Matcher m = RES_ID_PATTERN.matcher(line);
if (m.matches()) { if (m.matches()) {
int resID = parseResID(m.group(3)); int resID = parseResID(m.group(3));
if (resID != -1) { if (resID != -1) {
try { try {
ResResSpec spec = resTable.getResSpec(resID); ResResSpec spec = resTable.getResSpec(resID);
out.println(String.format( out.println(String.format(RES_NAME_FORMAT,
RES_NAME_FORMAT, spec.getFullName())); spec.getFullName()));
} catch (UndefinedResObject ex) { } catch (UndefinedResObject ex) {
if (! R_FILE_PATTERN.matcher(fileName).matches()) { if (!R_FILE_PATTERN.matcher(fileName).matches()) {
LOGGER.warning(String.format( LOGGER.warning(String.format(
"Undefined resource spec in %s: 0x%08x" "Undefined resource spec in %s: 0x%08x",
, fileName, resID)); fileName, resID));
} }
} }
} }
} }
out.println(line); out.println(line);
} }
out.close(); out.close();
} }
private int parseResID(String resIDHex) { private int parseResID(String resIDHex) {
if (resIDHex.endsWith("ff")) { if (resIDHex.endsWith("ff")) {
return -1; return -1;
} }
int resID = Integer.valueOf(resIDHex, 16); int resID = Integer.valueOf(resIDHex, 16);
if (resIDHex.length() == 4) { if (resIDHex.length() == 4) {
resID = resID << 16; resID = resID << 16;
} }
return resID; return resID;
} }
private final static String RES_ID_FORMAT_FIELD = private final static String RES_ID_FORMAT_FIELD = ".field %s:I = 0x%08x";
".field %s:I = 0x%08x"; private final static String RES_ID_FORMAT_CONST = " const %s, 0x%08x";
private final static String RES_ID_FORMAT_CONST = private final static Pattern RES_ID_PATTERN = Pattern
" const %s, 0x%08x"; .compile("^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$");
private final static Pattern RES_ID_PATTERN = Pattern.compile( private final static String RES_NAME_FORMAT = "# APKTOOL/RES_NAME: %s";
"^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$"); private final static Pattern RES_NAME_PATTERN = Pattern
private final static String RES_NAME_FORMAT = .compile("^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$");
"# APKTOOL/RES_NAME: %s";
private final static Pattern RES_NAME_PATTERN = Pattern.compile(
"^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$");
private final static Pattern R_FILE_PATTERN = Pattern.compile( private final static Pattern R_FILE_PATTERN = Pattern
".*R\\$[a-z]+\\.smali$"); .compile(".*R\\$[a-z]+\\.smali$");
private final static Logger LOGGER = private final static Logger LOGGER = Logger.getLogger(ResSmaliUpdater.class
Logger.getLogger(ResSmaliUpdater.class.getName()); .getName());
} }

View File

@ -24,51 +24,49 @@ import java.util.*;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResConfig { public class ResConfig {
private final ResConfigFlags mFlags; private final ResConfigFlags mFlags;
private final Map<ResResSpec, ResResource> mResources = private final Map<ResResSpec, ResResource> mResources = new LinkedHashMap<ResResSpec, ResResource>();
new LinkedHashMap<ResResSpec, ResResource>();
public ResConfig(ResConfigFlags flags) { public ResConfig(ResConfigFlags flags) {
this.mFlags = flags; this.mFlags = flags;
} }
public Set<ResResource> listResources() { public Set<ResResource> listResources() {
return new LinkedHashSet<ResResource>(mResources.values()); return new LinkedHashSet<ResResource>(mResources.values());
} }
public ResResource getResource(ResResSpec spec) throws AndrolibException { public ResResource getResource(ResResSpec spec) throws AndrolibException {
ResResource res = mResources.get(spec); ResResource res = mResources.get(spec);
if (res == null) { if (res == null) {
throw new UndefinedResObject(String.format( throw new UndefinedResObject(String.format(
"resource: spec=%s, config=%s", spec, this)); "resource: spec=%s, config=%s", spec, this));
} }
return res; return res;
} }
public Set<ResResSpec> listResSpecs() { public Set<ResResSpec> listResSpecs() {
return mResources.keySet(); return mResources.keySet();
} }
public ResConfigFlags getFlags() { public ResConfigFlags getFlags() {
return mFlags; return mFlags;
} }
public void addResource(ResResource res) public void addResource(ResResource res) throws AndrolibException {
throws AndrolibException { addResource(res, false);
addResource(res, false); }
}
public void addResource(ResResource res, boolean overwrite) public void addResource(ResResource res, boolean overwrite)
throws AndrolibException { throws AndrolibException {
ResResSpec spec = res.getResSpec(); ResResSpec spec = res.getResSpec();
if (mResources.put(spec, res) != null && ! overwrite) { if (mResources.put(spec, res) != null && !overwrite) {
throw new AndrolibException(String.format( throw new AndrolibException(String.format(
"Multiple resources: spec=%s, config=%s", spec, this)); "Multiple resources: spec=%s, config=%s", spec, this));
} }
} }
@Override @Override
public String toString() { public String toString() {
return mFlags.toString(); return mFlags.toString();
} }
} }

View File

@ -22,457 +22,456 @@ import java.util.logging.Logger;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResConfigFlags { public class ResConfigFlags {
public final short mcc; public final short mcc;
public final short mnc; public final short mnc;
public final char[] language; public final char[] language;
public final char[] country; public final char[] country;
public final short layoutDirection;
public final byte orientation; public final short layoutDirection;
public final byte touchscreen;
public final short density;
public final byte keyboard; public final byte orientation;
public final byte navigation; public final byte touchscreen;
public final byte inputFlags; public final short density;
public final short screenWidth; public final byte keyboard;
public final short screenHeight; public final byte navigation;
public final byte inputFlags;
public final short sdkVersion; public final short screenWidth;
public final short screenHeight;
public final byte screenLayout; public final short sdkVersion;
public final byte uiMode;
public final short smallestScreenWidthDp;
public final short screenWidthDp; public final byte screenLayout;
public final short screenHeightDp; public final byte uiMode;
public final short smallestScreenWidthDp;
public final boolean isInvalid; public final short screenWidthDp;
public final short screenHeightDp;
private final String mQualifiers; public final boolean isInvalid;
public ResConfigFlags() { private final String mQualifiers;
mcc = 0;
mnc = 0;
language = new char[]{'\00', '\00'};
country = new char[]{'\00', '\00'};
layoutDirection = SCREENLAYOUT_LAYOUTDIR_ANY;
orientation = ORIENTATION_ANY;
touchscreen = TOUCHSCREEN_ANY;
density = DENSITY_DEFAULT;
keyboard = KEYBOARD_ANY;
navigation = NAVIGATION_ANY;
inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY;
screenWidth = 0;
screenHeight = 0;
sdkVersion = 0;
screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY;
uiMode = UI_MODE_TYPE_ANY | UI_MODE_NIGHT_ANY;
smallestScreenWidthDp = 0;
screenWidthDp = 0;
screenHeightDp = 0;
isInvalid = false;
mQualifiers = "";
}
public ResConfigFlags(short mcc, short mnc, char[] language, char[] country, public ResConfigFlags() {
short layoutDirection, byte orientation, byte touchscreen, mcc = 0;
short density, byte keyboard, byte navigation, byte inputFlags, mnc = 0;
short screenWidth, short screenHeight, short sdkVersion, byte screenLayout, language = new char[] { '\00', '\00' };
byte uiMode, short smallestScreenWidthDp, short screenWidthDp, country = new char[] { '\00', '\00' };
short screenHeightDp, boolean isInvalid) { layoutDirection = SCREENLAYOUT_LAYOUTDIR_ANY;
if (orientation < 0 || orientation > 3) { orientation = ORIENTATION_ANY;
LOGGER.warning("Invalid orientation value: " + orientation); touchscreen = TOUCHSCREEN_ANY;
orientation = 0; density = DENSITY_DEFAULT;
isInvalid = true; keyboard = KEYBOARD_ANY;
} navigation = NAVIGATION_ANY;
if (touchscreen < 0 || touchscreen > 3) { inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY;
LOGGER.warning("Invalid touchscreen value: " + touchscreen); screenWidth = 0;
touchscreen = 0; screenHeight = 0;
isInvalid = true; sdkVersion = 0;
} screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY;
if (density < -1) { uiMode = UI_MODE_TYPE_ANY | UI_MODE_NIGHT_ANY;
LOGGER.warning("Invalid density value: " + density); smallestScreenWidthDp = 0;
density = 0; screenWidthDp = 0;
isInvalid = true; screenHeightDp = 0;
} isInvalid = false;
if (keyboard < 0 || keyboard > 3) { mQualifiers = "";
LOGGER.warning("Invalid keyboard value: " + keyboard); }
keyboard = 0;
isInvalid = true;
}
if (navigation < 0 || navigation > 4) {
LOGGER.warning("Invalid navigation value: " + navigation);
navigation = 0;
isInvalid = true;
}
this.mcc = mcc; public ResConfigFlags(short mcc, short mnc, char[] language,
this.mnc = mnc; char[] country, short layoutDirection, byte orientation,
this.language = language; byte touchscreen, short density, byte keyboard, byte navigation,
this.country = country; byte inputFlags, short screenWidth, short screenHeight,
this.layoutDirection = layoutDirection; short sdkVersion, byte screenLayout, byte uiMode,
this.orientation = orientation; short smallestScreenWidthDp, short screenWidthDp,
this.touchscreen = touchscreen; short screenHeightDp, boolean isInvalid) {
this.density = density; if (orientation < 0 || orientation > 3) {
this.keyboard = keyboard; LOGGER.warning("Invalid orientation value: " + orientation);
this.navigation = navigation; orientation = 0;
this.inputFlags = inputFlags; isInvalid = true;
this.screenWidth = screenWidth; }
this.screenHeight = screenHeight; if (touchscreen < 0 || touchscreen > 3) {
this.sdkVersion = sdkVersion; LOGGER.warning("Invalid touchscreen value: " + touchscreen);
this.screenLayout = screenLayout; touchscreen = 0;
this.uiMode = uiMode; isInvalid = true;
this.smallestScreenWidthDp = smallestScreenWidthDp; }
this.screenWidthDp = screenWidthDp; if (density < -1) {
this.screenHeightDp = screenHeightDp; LOGGER.warning("Invalid density value: " + density);
this.isInvalid = isInvalid; density = 0;
mQualifiers = generateQualifiers(); isInvalid = true;
} }
if (keyboard < 0 || keyboard > 3) {
public String getQualifiers() { LOGGER.warning("Invalid keyboard value: " + keyboard);
return mQualifiers; keyboard = 0;
} isInvalid = true;
}
private String generateQualifiers() { if (navigation < 0 || navigation > 4) {
StringBuilder ret = new StringBuilder(); LOGGER.warning("Invalid navigation value: " + navigation);
if (mcc != 0) { navigation = 0;
ret.append("-mcc").append(String.format("%03d", mcc)); isInvalid = true;
if (mnc != 0) {
ret.append("-mnc").append(mnc);
}
}
if (language[0] != '\00') {
ret.append('-').append(language);
if (country[0] != '\00') {
ret.append("-r").append(country);
}
}
switch (screenLayout & MASK_LAYOUTDIR) {
case SCREENLAYOUT_LAYOUTDIR_RTL:
ret.append("-ldrtl");
break;
case SCREENLAYOUT_LAYOUTDIR_LTR:
ret.append("-ldltr");
break;
} }
if (smallestScreenWidthDp != 0) {
ret.append("-sw").append(smallestScreenWidthDp).append("dp");
}
if (screenWidthDp != 0) {
ret.append("-w").append(screenWidthDp).append("dp");
}
if (screenHeightDp != 0) {
ret.append("-h").append(screenHeightDp).append("dp");
}
switch (screenLayout & MASK_SCREENSIZE) {
case SCREENSIZE_SMALL:
ret.append("-small");
break;
case SCREENSIZE_NORMAL:
ret.append("-normal");
break;
case SCREENSIZE_LARGE:
ret.append("-large");
break;
case SCREENSIZE_XLARGE:
ret.append("-xlarge");
break;
}
switch (screenLayout & MASK_SCREENLONG) {
case SCREENLONG_YES:
ret.append("-long");
break;
case SCREENLONG_NO:
ret.append("-notlong");
break;
}
switch (orientation) {
case ORIENTATION_PORT:
ret.append("-port");
break;
case ORIENTATION_LAND:
ret.append("-land");
break;
case ORIENTATION_SQUARE:
ret.append("-square");
break;
}
switch (uiMode & MASK_UI_MODE_TYPE) {
case UI_MODE_TYPE_CAR:
ret.append("-car");
break;
case UI_MODE_TYPE_DESK:
ret.append("-desk");
break;
case UI_MODE_TYPE_TELEVISION:
ret.append("-television");
break;
case UI_MODE_TYPE_SMALLUI:
ret.append("-smallui");
break;
case UI_MODE_TYPE_MEDIUMUI:
ret.append("-mediumui");
break;
case UI_MODE_TYPE_LARGEUI:
ret.append("-largeui");
break;
case UI_MODE_TYPE_HUGEUI:
ret.append("-hugeui");
break;
case UI_MODE_TYPE_APPLIANCE:
ret.append("-appliance");
break;
}
switch (uiMode & MASK_UI_MODE_NIGHT) {
case UI_MODE_NIGHT_YES:
ret.append("-night");
break;
case UI_MODE_NIGHT_NO:
ret.append("-notnight");
break;
}
switch (density) {
case DENSITY_DEFAULT:
break;
case DENSITY_LOW:
ret.append("-ldpi");
break;
case DENSITY_MEDIUM:
ret.append("-mdpi");
break;
case DENSITY_HIGH:
ret.append("-hdpi");
break;
case DENSITY_TV:
ret.append("-tvdpi");
break;
case DENSITY_XHIGH:
ret.append("-xhdpi");
break;
case DENSITY_XXHIGH:
ret.append("-xxhdpi");
break;
case DENSITY_NONE:
ret.append("-nodpi");
break;
default:
ret.append('-').append(density).append("dpi");
}
switch (touchscreen) {
case TOUCHSCREEN_NOTOUCH:
ret.append("-notouch");
break;
case TOUCHSCREEN_STYLUS:
ret.append("-stylus");
break;
case TOUCHSCREEN_FINGER:
ret.append("-finger");
break;
}
switch (inputFlags & MASK_KEYSHIDDEN) {
case KEYSHIDDEN_NO:
ret.append("-keysexposed");
break;
case KEYSHIDDEN_YES:
ret.append("-keyshidden");
break;
case KEYSHIDDEN_SOFT:
ret.append("-keyssoft");
break;
}
switch (keyboard) {
case KEYBOARD_NOKEYS:
ret.append("-nokeys");
break;
case KEYBOARD_QWERTY:
ret.append("-qwerty");
break;
case KEYBOARD_12KEY:
ret.append("-12key");
break;
}
switch (inputFlags & MASK_NAVHIDDEN) {
case NAVHIDDEN_NO:
ret.append("-navexposed");
break;
case NAVHIDDEN_YES:
ret.append("-navhidden");
break;
}
switch (navigation) {
case NAVIGATION_NONAV:
ret.append("-nonav");
break;
case NAVIGATION_DPAD:
ret.append("-dpad");
break;
case NAVIGATION_TRACKBALL:
ret.append("-trackball");
break;
case NAVIGATION_WHEEL:
ret.append("-wheel");
break;
}
if (screenWidth != 0 && screenHeight != 0) {
if (screenWidth > screenHeight) {
ret.append(String.format("-%dx%d", screenWidth, screenHeight));
} else {
ret.append(String.format("-%dx%d", screenHeight, screenWidth));
}
}
if (sdkVersion > getNaturalSdkVersionRequirement()) {
ret.append("-v").append(sdkVersion);
}
if (isInvalid) {
ret.append("-ERR" + sErrCounter++);
}
return ret.toString(); this.mcc = mcc;
} this.mnc = mnc;
this.language = language;
this.country = country;
this.layoutDirection = layoutDirection;
this.orientation = orientation;
this.touchscreen = touchscreen;
this.density = density;
this.keyboard = keyboard;
this.navigation = navigation;
this.inputFlags = inputFlags;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
this.sdkVersion = sdkVersion;
this.screenLayout = screenLayout;
this.uiMode = uiMode;
this.smallestScreenWidthDp = smallestScreenWidthDp;
this.screenWidthDp = screenWidthDp;
this.screenHeightDp = screenHeightDp;
this.isInvalid = isInvalid;
mQualifiers = generateQualifiers();
}
private short getNaturalSdkVersionRequirement() { public String getQualifiers() {
if (smallestScreenWidthDp != 0 || screenWidthDp != 0 return mQualifiers;
|| screenHeightDp != 0) { }
return SDK_HONEYCOMB_MR2;
}
if ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != 0) {
return SDK_FROYO;
}
if ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != 0
|| density != DENSITY_DEFAULT) {
return SDK_DONUT;
}
return 0;
}
@Override private String generateQualifiers() {
public String toString() { StringBuilder ret = new StringBuilder();
return ! getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]"; if (mcc != 0) {
} ret.append("-mcc").append(String.format("%03d", mcc));
if (mnc != 0) {
ret.append("-mnc").append(mnc);
}
}
if (language[0] != '\00') {
ret.append('-').append(language);
if (country[0] != '\00') {
ret.append("-r").append(country);
}
}
switch (screenLayout & MASK_LAYOUTDIR) {
case SCREENLAYOUT_LAYOUTDIR_RTL:
ret.append("-ldrtl");
break;
case SCREENLAYOUT_LAYOUTDIR_LTR:
ret.append("-ldltr");
break;
}
if (smallestScreenWidthDp != 0) {
ret.append("-sw").append(smallestScreenWidthDp).append("dp");
}
if (screenWidthDp != 0) {
ret.append("-w").append(screenWidthDp).append("dp");
}
if (screenHeightDp != 0) {
ret.append("-h").append(screenHeightDp).append("dp");
}
switch (screenLayout & MASK_SCREENSIZE) {
case SCREENSIZE_SMALL:
ret.append("-small");
break;
case SCREENSIZE_NORMAL:
ret.append("-normal");
break;
case SCREENSIZE_LARGE:
ret.append("-large");
break;
case SCREENSIZE_XLARGE:
ret.append("-xlarge");
break;
}
switch (screenLayout & MASK_SCREENLONG) {
case SCREENLONG_YES:
ret.append("-long");
break;
case SCREENLONG_NO:
ret.append("-notlong");
break;
}
switch (orientation) {
case ORIENTATION_PORT:
ret.append("-port");
break;
case ORIENTATION_LAND:
ret.append("-land");
break;
case ORIENTATION_SQUARE:
ret.append("-square");
break;
}
switch (uiMode & MASK_UI_MODE_TYPE) {
case UI_MODE_TYPE_CAR:
ret.append("-car");
break;
case UI_MODE_TYPE_DESK:
ret.append("-desk");
break;
case UI_MODE_TYPE_TELEVISION:
ret.append("-television");
break;
case UI_MODE_TYPE_SMALLUI:
ret.append("-smallui");
break;
case UI_MODE_TYPE_MEDIUMUI:
ret.append("-mediumui");
break;
case UI_MODE_TYPE_LARGEUI:
ret.append("-largeui");
break;
case UI_MODE_TYPE_HUGEUI:
ret.append("-hugeui");
break;
case UI_MODE_TYPE_APPLIANCE:
ret.append("-appliance");
break;
}
switch (uiMode & MASK_UI_MODE_NIGHT) {
case UI_MODE_NIGHT_YES:
ret.append("-night");
break;
case UI_MODE_NIGHT_NO:
ret.append("-notnight");
break;
}
switch (density) {
case DENSITY_DEFAULT:
break;
case DENSITY_LOW:
ret.append("-ldpi");
break;
case DENSITY_MEDIUM:
ret.append("-mdpi");
break;
case DENSITY_HIGH:
ret.append("-hdpi");
break;
case DENSITY_TV:
ret.append("-tvdpi");
break;
case DENSITY_XHIGH:
ret.append("-xhdpi");
break;
case DENSITY_XXHIGH:
ret.append("-xxhdpi");
break;
case DENSITY_NONE:
ret.append("-nodpi");
break;
default:
ret.append('-').append(density).append("dpi");
}
switch (touchscreen) {
case TOUCHSCREEN_NOTOUCH:
ret.append("-notouch");
break;
case TOUCHSCREEN_STYLUS:
ret.append("-stylus");
break;
case TOUCHSCREEN_FINGER:
ret.append("-finger");
break;
}
switch (inputFlags & MASK_KEYSHIDDEN) {
case KEYSHIDDEN_NO:
ret.append("-keysexposed");
break;
case KEYSHIDDEN_YES:
ret.append("-keyshidden");
break;
case KEYSHIDDEN_SOFT:
ret.append("-keyssoft");
break;
}
switch (keyboard) {
case KEYBOARD_NOKEYS:
ret.append("-nokeys");
break;
case KEYBOARD_QWERTY:
ret.append("-qwerty");
break;
case KEYBOARD_12KEY:
ret.append("-12key");
break;
}
switch (inputFlags & MASK_NAVHIDDEN) {
case NAVHIDDEN_NO:
ret.append("-navexposed");
break;
case NAVHIDDEN_YES:
ret.append("-navhidden");
break;
}
switch (navigation) {
case NAVIGATION_NONAV:
ret.append("-nonav");
break;
case NAVIGATION_DPAD:
ret.append("-dpad");
break;
case NAVIGATION_TRACKBALL:
ret.append("-trackball");
break;
case NAVIGATION_WHEEL:
ret.append("-wheel");
break;
}
if (screenWidth != 0 && screenHeight != 0) {
if (screenWidth > screenHeight) {
ret.append(String.format("-%dx%d", screenWidth, screenHeight));
} else {
ret.append(String.format("-%dx%d", screenHeight, screenWidth));
}
}
if (sdkVersion > getNaturalSdkVersionRequirement()) {
ret.append("-v").append(sdkVersion);
}
if (isInvalid) {
ret.append("-ERR" + sErrCounter++);
}
@Override return ret.toString();
public boolean equals(Object obj) { }
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResConfigFlags other = (ResConfigFlags) obj;
return this.mQualifiers.equals(other.mQualifiers);
}
@Override private short getNaturalSdkVersionRequirement() {
public int hashCode() { if (smallestScreenWidthDp != 0 || screenWidthDp != 0
int hash = 17; || screenHeightDp != 0) {
hash = 31 * hash + this.mQualifiers.hashCode(); return SDK_HONEYCOMB_MR2;
return hash; }
} if ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != 0) {
return SDK_FROYO;
}
if ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != 0
|| density != DENSITY_DEFAULT) {
return SDK_DONUT;
}
return 0;
}
@Override
public String toString() {
return !getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]";
}
// TODO: Dirty static hack. This counter should be a part of ResPackage, @Override
// but it would be hard right now and this feature is very rarely used. public boolean equals(Object obj) {
private static int sErrCounter = 0; if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResConfigFlags other = (ResConfigFlags) obj;
return this.mQualifiers.equals(other.mQualifiers);
}
public final static byte SDK_BASE = 1; @Override
public final static byte SDK_BASE_1_1 = 2; public int hashCode() {
public final static byte SDK_CUPCAKE = 3; int hash = 17;
public final static byte SDK_DONUT = 4; hash = 31 * hash + this.mQualifiers.hashCode();
public final static byte SDK_ECLAIR = 5; return hash;
public final static byte SDK_ECLAIR_0_1 = 6; }
public final static byte SDK_ECLAIR_MR1 = 7;
public final static byte SDK_FROYO = 8;
public final static byte SDK_GINGERBREAD = 9;
public final static byte SDK_GINGERBREAD_MR1 = 10;
public final static byte SDK_HONEYCOMB = 11;
public final static byte SDK_HONEYCOMB_MR1 = 12;
public final static byte SDK_HONEYCOMB_MR2 = 13;
public final static byte SDK_ICE_CREAM_SANDWICH = 14;
public final static byte SDK_ICE_CREAM_SANDWICH_MR1 = 15;
public final static byte SDK_JELLY_BEAN = 16;
public final static byte SDK_JELLY_BEAN_MR1 = 17;
public final static byte ORIENTATION_ANY = 0; // TODO: Dirty static hack. This counter should be a part of ResPackage,
public final static byte ORIENTATION_PORT = 1; // but it would be hard right now and this feature is very rarely used.
public final static byte ORIENTATION_LAND = 2; private static int sErrCounter = 0;
public final static byte ORIENTATION_SQUARE = 3;
public final static byte TOUCHSCREEN_ANY = 0; public final static byte SDK_BASE = 1;
public final static byte TOUCHSCREEN_NOTOUCH = 1; public final static byte SDK_BASE_1_1 = 2;
public final static byte TOUCHSCREEN_STYLUS = 2; public final static byte SDK_CUPCAKE = 3;
public final static byte TOUCHSCREEN_FINGER = 3; public final static byte SDK_DONUT = 4;
public final static byte SDK_ECLAIR = 5;
public final static byte SDK_ECLAIR_0_1 = 6;
public final static byte SDK_ECLAIR_MR1 = 7;
public final static byte SDK_FROYO = 8;
public final static byte SDK_GINGERBREAD = 9;
public final static byte SDK_GINGERBREAD_MR1 = 10;
public final static byte SDK_HONEYCOMB = 11;
public final static byte SDK_HONEYCOMB_MR1 = 12;
public final static byte SDK_HONEYCOMB_MR2 = 13;
public final static byte SDK_ICE_CREAM_SANDWICH = 14;
public final static byte SDK_ICE_CREAM_SANDWICH_MR1 = 15;
public final static byte SDK_JELLY_BEAN = 16;
public final static byte SDK_JELLY_BEAN_MR1 = 17;
public final static short DENSITY_DEFAULT = 0; public final static byte ORIENTATION_ANY = 0;
public final static short DENSITY_LOW = 120; public final static byte ORIENTATION_PORT = 1;
public final static short DENSITY_MEDIUM = 160; public final static byte ORIENTATION_LAND = 2;
public final static short DENSITY_TV = 213; public final static byte ORIENTATION_SQUARE = 3;
public final static short DENSITY_HIGH = 240;
public final static short DENSITY_XHIGH = 320;
public final static short DENSITY_XXHIGH = 480;
public final static short DENSITY_NONE = -1;
public final static short MASK_LAYOUTDIR = 0xc0;
public final static short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00;
public final static short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40;
public final static short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80;
public final static short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06;
public final static byte KEYBOARD_ANY = 0; public final static byte TOUCHSCREEN_ANY = 0;
public final static byte KEYBOARD_NOKEYS = 1; public final static byte TOUCHSCREEN_NOTOUCH = 1;
public final static byte KEYBOARD_QWERTY = 2; public final static byte TOUCHSCREEN_STYLUS = 2;
public final static byte KEYBOARD_12KEY = 3; public final static byte TOUCHSCREEN_FINGER = 3;
public final static byte NAVIGATION_ANY = 0; public final static short DENSITY_DEFAULT = 0;
public final static byte NAVIGATION_NONAV = 1; public final static short DENSITY_LOW = 120;
public final static byte NAVIGATION_DPAD = 2; public final static short DENSITY_MEDIUM = 160;
public final static byte NAVIGATION_TRACKBALL = 3; public final static short DENSITY_TV = 213;
public final static byte NAVIGATION_WHEEL = 4; public final static short DENSITY_HIGH = 240;
public final static short DENSITY_XHIGH = 320;
public final static short DENSITY_XXHIGH = 480;
public final static short DENSITY_NONE = -1;
public final static byte MASK_KEYSHIDDEN = 0x3; public final static short MASK_LAYOUTDIR = 0xc0;
public final static byte KEYSHIDDEN_ANY = 0x0; public final static short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00;
public final static byte KEYSHIDDEN_NO = 0x1; public final static short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40;
public final static byte KEYSHIDDEN_YES = 0x2; public final static short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80;
public final static byte KEYSHIDDEN_SOFT = 0x3; public final static short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06;
public final static byte MASK_NAVHIDDEN = 0xc; public final static byte KEYBOARD_ANY = 0;
public final static byte NAVHIDDEN_ANY = 0x0; public final static byte KEYBOARD_NOKEYS = 1;
public final static byte NAVHIDDEN_NO = 0x4; public final static byte KEYBOARD_QWERTY = 2;
public final static byte NAVHIDDEN_YES = 0x8; public final static byte KEYBOARD_12KEY = 3;
public final static byte MASK_SCREENSIZE = 0x0f; public final static byte NAVIGATION_ANY = 0;
public final static byte SCREENSIZE_ANY = 0x00; public final static byte NAVIGATION_NONAV = 1;
public final static byte SCREENSIZE_SMALL = 0x01; public final static byte NAVIGATION_DPAD = 2;
public final static byte SCREENSIZE_NORMAL = 0x02; public final static byte NAVIGATION_TRACKBALL = 3;
public final static byte SCREENSIZE_LARGE = 0x03; public final static byte NAVIGATION_WHEEL = 4;
public final static byte SCREENSIZE_XLARGE = 0x04;
public final static byte MASK_SCREENLONG = 0x30;
public final static byte SCREENLONG_ANY = 0x00;
public final static byte SCREENLONG_NO = 0x10;
public final static byte SCREENLONG_YES = 0x20;
public final static byte MASK_UI_MODE_TYPE = 0x0f; public final static byte MASK_KEYSHIDDEN = 0x3;
public final static byte UI_MODE_TYPE_ANY = 0x00; public final static byte KEYSHIDDEN_ANY = 0x0;
public final static byte UI_MODE_TYPE_NORMAL = 0x01; public final static byte KEYSHIDDEN_NO = 0x1;
public final static byte UI_MODE_TYPE_DESK = 0x02; public final static byte KEYSHIDDEN_YES = 0x2;
public final static byte UI_MODE_TYPE_CAR = 0x03; public final static byte KEYSHIDDEN_SOFT = 0x3;
public final static byte UI_MODE_TYPE_TELEVISION = 0x04;
public final static byte UI_MODE_TYPE_APPLIANCE = 0x05;
public final static byte UI_MODE_TYPE_SMALLUI = 0x0c;
public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d;
public final static byte UI_MODE_TYPE_LARGEUI = 0x0e;
public final static byte UI_MODE_TYPE_HUGEUI = 0x0f;
public final static byte MASK_UI_MODE_NIGHT = 0x30; public final static byte MASK_NAVHIDDEN = 0xc;
public final static byte UI_MODE_NIGHT_ANY = 0x00; public final static byte NAVHIDDEN_ANY = 0x0;
public final static byte UI_MODE_NIGHT_NO = 0x10; public final static byte NAVHIDDEN_NO = 0x4;
public final static byte UI_MODE_NIGHT_YES = 0x20; public final static byte NAVHIDDEN_YES = 0x8;
public final static byte MASK_SCREENSIZE = 0x0f;
public final static byte SCREENSIZE_ANY = 0x00;
public final static byte SCREENSIZE_SMALL = 0x01;
public final static byte SCREENSIZE_NORMAL = 0x02;
public final static byte SCREENSIZE_LARGE = 0x03;
public final static byte SCREENSIZE_XLARGE = 0x04;
private static final Logger LOGGER = public final static byte MASK_SCREENLONG = 0x30;
Logger.getLogger(ResConfigFlags.class.getName()); public final static byte SCREENLONG_ANY = 0x00;
public final static byte SCREENLONG_NO = 0x10;
public final static byte SCREENLONG_YES = 0x20;
public final static byte MASK_UI_MODE_TYPE = 0x0f;
public final static byte UI_MODE_TYPE_ANY = 0x00;
public final static byte UI_MODE_TYPE_NORMAL = 0x01;
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 UI_MODE_TYPE_SMALLUI = 0x0c;
public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d;
public final static byte UI_MODE_TYPE_LARGEUI = 0x0e;
public final static byte UI_MODE_TYPE_HUGEUI = 0x0f;
public final static byte MASK_UI_MODE_NIGHT = 0x30;
public final static byte UI_MODE_NIGHT_ANY = 0x00;
public final static byte UI_MODE_NIGHT_NO = 0x10;
public final static byte UI_MODE_NIGHT_YES = 0x20;
private static final Logger LOGGER = Logger.getLogger(ResConfigFlags.class
.getName());
} }

View File

@ -20,51 +20,51 @@ package brut.androlib.res.data;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResID { public class ResID {
public final int package_; public final int package_;
public final int type; public final int type;
public final int entry; public final int entry;
public final int id; public final int id;
public ResID(int package_, int type, int entry) { public ResID(int package_, int type, int entry) {
this(package_, type, entry, (package_ << 24) + (type << 16) + entry); this(package_, type, entry, (package_ << 24) + (type << 16) + entry);
} }
public ResID(int id) { public ResID(int id) {
this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id); this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id);
} }
public ResID(int package_, int type, int entry, int id) { public ResID(int package_, int type, int entry, int id) {
this.package_ = package_; this.package_ = package_;
this.type = type; this.type = type;
this.entry = entry; this.entry = entry;
this.id = id; this.id = id;
} }
@Override @Override
public String toString() { public String toString() {
return String.format("0x%08x", id); return String.format("0x%08x", id);
} }
@Override @Override
public int hashCode() { public int hashCode() {
int hash = 17; int hash = 17;
hash = 31 * hash + this.id; hash = 31 * hash + this.id;
return hash; return hash;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null) { if (obj == null) {
return false; return false;
} }
if (getClass() != obj.getClass()) { if (getClass() != obj.getClass()) {
return false; return false;
} }
final ResID other = (ResID) obj; final ResID other = (ResID) obj;
if (this.id != other.id) { if (this.id != other.id) {
return false; return false;
} }
return true; return true;
} }
} }

View File

@ -28,193 +28,192 @@ import java.util.*;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResPackage { public class ResPackage {
private final ResTable mResTable; private final ResTable mResTable;
private final int mId; private final int mId;
private final String mName; private final String mName;
private final Map<ResID, ResResSpec> mResSpecs = private final Map<ResID, ResResSpec> mResSpecs = new LinkedHashMap<ResID, ResResSpec>();
new LinkedHashMap<ResID, ResResSpec>(); private final Map<ResConfigFlags, ResConfig> mConfigs = new LinkedHashMap<ResConfigFlags, ResConfig>();
private final Map<ResConfigFlags, ResConfig> mConfigs = private final Map<String, ResType> mTypes = new LinkedHashMap<String, ResType>();
new LinkedHashMap<ResConfigFlags, ResConfig>(); private final Set<ResID> mSynthesizedRes = new HashSet<ResID>();
private final Map<String, ResType> mTypes =
new LinkedHashMap<String, ResType>();
private final Set<ResID> mSynthesizedRes = new HashSet<ResID>();
private ResValueFactory mValueFactory; private ResValueFactory mValueFactory;
public ResPackage(ResTable resTable, int id, String name) { public ResPackage(ResTable resTable, int id, String name) {
this.mResTable = resTable; this.mResTable = resTable;
this.mId = id; this.mId = id;
this.mName = name; this.mName = name;
} }
public List<ResResSpec> listResSpecs() { public List<ResResSpec> listResSpecs() {
return new ArrayList<ResResSpec>(mResSpecs.values()); return new ArrayList<ResResSpec>(mResSpecs.values());
} }
public boolean hasResSpec(ResID resID) { public boolean hasResSpec(ResID resID) {
return mResSpecs.containsKey(resID); return mResSpecs.containsKey(resID);
} }
public ResResSpec getResSpec(ResID resID) throws UndefinedResObject { public ResResSpec getResSpec(ResID resID) throws UndefinedResObject {
ResResSpec spec = mResSpecs.get(resID); ResResSpec spec = mResSpecs.get(resID);
if (spec == null) { if (spec == null) {
throw new UndefinedResObject("resource spec: " + resID.toString()); throw new UndefinedResObject("resource spec: " + resID.toString());
} }
return spec; return spec;
} }
public List<ResConfig> getConfigs() { public List<ResConfig> getConfigs() {
return new ArrayList<ResConfig>(mConfigs.values()); return new ArrayList<ResConfig>(mConfigs.values());
} }
public boolean hasConfig(ResConfigFlags flags) { public boolean hasConfig(ResConfigFlags flags) {
return mConfigs.containsKey(flags); return mConfigs.containsKey(flags);
} }
public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException { public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException {
ResConfig config = mConfigs.get(flags); ResConfig config = mConfigs.get(flags);
if (config == null) { if (config == null) {
throw new UndefinedResObject("config: " + flags); throw new UndefinedResObject("config: " + flags);
} }
return config; return config;
} }
public ResConfig getOrCreateConfig(ResConfigFlags flags) public ResConfig getOrCreateConfig(ResConfigFlags flags)
throws AndrolibException { throws AndrolibException {
ResConfig config = mConfigs.get(flags); ResConfig config = mConfigs.get(flags);
if (config == null) { if (config == null) {
config = new ResConfig(flags); config = new ResConfig(flags);
mConfigs.put(flags, config); mConfigs.put(flags, config);
} }
return config; return config;
} }
public List<ResType> listTypes() { public List<ResType> listTypes() {
return new ArrayList<ResType>(mTypes.values()); return new ArrayList<ResType>(mTypes.values());
} }
public boolean hasType(String typeName) { public boolean hasType(String typeName) {
return mTypes.containsKey(typeName); return mTypes.containsKey(typeName);
} }
public ResType getType(String typeName) throws AndrolibException { public ResType getType(String typeName) throws AndrolibException {
ResType type = mTypes.get(typeName); ResType type = mTypes.get(typeName);
if (type == null) { if (type == null) {
throw new UndefinedResObject("type: " + typeName); throw new UndefinedResObject("type: " + typeName);
} }
return type; return type;
} }
public Set<ResResource> listFiles() { public Set<ResResource> listFiles() {
Set<ResResource> ret = new HashSet<ResResource>(); Set<ResResource> ret = new HashSet<ResResource>();
for (ResResSpec spec : mResSpecs.values()) { for (ResResSpec spec : mResSpecs.values()) {
for (ResResource res : spec.listResources()) { for (ResResource res : spec.listResources()) {
if (res.getValue() instanceof ResFileValue) { if (res.getValue() instanceof ResFileValue) {
ret.add(res); ret.add(res);
} }
} }
} }
return ret; return ret;
} }
public Collection<ResValuesFile> listValuesFiles() { public Collection<ResValuesFile> listValuesFiles() {
Map<Duo<ResType, ResConfig>, ResValuesFile> ret = Map<Duo<ResType, ResConfig>, ResValuesFile> ret = new HashMap<Duo<ResType, ResConfig>, ResValuesFile>();
new HashMap<Duo<ResType, ResConfig>, ResValuesFile>(); for (ResResSpec spec : mResSpecs.values()) {
for (ResResSpec spec : mResSpecs.values()) { for (ResResource res : spec.listResources()) {
for (ResResource res : spec.listResources()) { if (res.getValue() instanceof ResValuesXmlSerializable) {
if (res.getValue() instanceof ResValuesXmlSerializable) { ResType type = res.getResSpec().getType();
ResType type = res.getResSpec().getType(); ResConfig config = res.getConfig();
ResConfig config = res.getConfig(); Duo<ResType, ResConfig> key = new Duo<ResType, ResConfig>(
Duo<ResType, ResConfig> key = type, config);
new Duo<ResType, ResConfig>(type, config); ResValuesFile values = ret.get(key);
ResValuesFile values = ret.get(key); if (values == null) {
if (values == null) { values = new ResValuesFile(this, type, config);
values = new ResValuesFile(this, type, config); ret.put(key, values);
ret.put(key, values); }
} values.addResource(res);
values.addResource(res); }
} }
} }
} return ret.values();
return ret.values(); }
}
public ResTable getResTable() { public ResTable getResTable() {
return mResTable; return mResTable;
} }
public int getId() { public int getId() {
return mId; return mId;
} }
public String getName() { public String getName() {
return mName; return mName;
} }
boolean isSynthesized(ResID resId) { boolean isSynthesized(ResID resId) {
return mSynthesizedRes.contains(resId); return mSynthesizedRes.contains(resId);
} }
public void addResSpec(ResResSpec spec) throws AndrolibException { public void addResSpec(ResResSpec spec) throws AndrolibException {
if (mResSpecs.put(spec.getId(), spec) != null) { if (mResSpecs.put(spec.getId(), spec) != null) {
throw new AndrolibException("Multiple resource specs: " + spec); throw new AndrolibException("Multiple resource specs: " + spec);
} }
} }
public void addConfig(ResConfig config) throws AndrolibException { public void addConfig(ResConfig config) throws AndrolibException {
if (mConfigs.put(config.getFlags(), config) != null) { if (mConfigs.put(config.getFlags(), config) != null) {
throw new AndrolibException("Multiple configs: " + config); throw new AndrolibException("Multiple configs: " + config);
} }
} }
public void addType(ResType type) throws AndrolibException { public void addType(ResType type) throws AndrolibException {
if (mTypes.put(type.getName(), type) != null) { if (mTypes.put(type.getName(), type) != null) {
throw new AndrolibException("Multiple types: " + type); throw new AndrolibException("Multiple types: " + type);
} }
} }
public void addResource(ResResource res) { public void addResource(ResResource res) {
} }
public void addSynthesizedRes(int resId) { public void addSynthesizedRes(int resId) {
mSynthesizedRes.add(new ResID(resId)); mSynthesizedRes.add(new ResID(resId));
} }
@Override @Override
public String toString() { public String toString() {
return mName; return mName;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null) { if (obj == null) {
return false; return false;
} }
if (getClass() != obj.getClass()) { if (getClass() != obj.getClass()) {
return false; return false;
} }
final ResPackage other = (ResPackage) obj; final ResPackage other = (ResPackage) obj;
if (this.mResTable != other.mResTable && (this.mResTable == null || !this.mResTable.equals(other.mResTable))) { if (this.mResTable != other.mResTable
return false; && (this.mResTable == null || !this.mResTable
} .equals(other.mResTable))) {
if (this.mId != other.mId) { return false;
return false; }
} if (this.mId != other.mId) {
return true; return false;
} }
return true;
}
@Override @Override
public int hashCode() { public int hashCode() {
int hash = 17; int hash = 17;
hash = 31 * hash + (this.mResTable != null ? this.mResTable.hashCode() : 0); hash = 31 * hash
hash = 31 * hash + this.mId; + (this.mResTable != null ? this.mResTable.hashCode() : 0);
return hash; hash = 31 * hash + this.mId;
} return hash;
}
public ResValueFactory getValueFactory() { public ResValueFactory getValueFactory() {
if (mValueFactory == null) { if (mValueFactory == null) {
mValueFactory = new ResValueFactory(this); mValueFactory = new ResValueFactory(this);
} }
return mValueFactory; return mValueFactory;
} }
} }

View File

@ -25,101 +25,97 @@ import org.apache.commons.lang3.StringUtils;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResResSpec { public class ResResSpec {
private final ResID mId; private final ResID mId;
private final String mName; private final String mName;
private final ResPackage mPackage; private final ResPackage mPackage;
private final ResType mType; private final ResType mType;
private final Map<ResConfigFlags, ResResource> mResources = private final Map<ResConfigFlags, ResResource> mResources = new LinkedHashMap<ResConfigFlags, ResResource>();
new LinkedHashMap<ResConfigFlags, ResResource>();
public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) { public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) {
this.mId = id; this.mId = id;
this.mName = name; this.mName = name;
this.mPackage = pkg; this.mPackage = pkg;
this.mType = type; this.mType = type;
} }
public Set<ResResource> listResources() { public Set<ResResource> listResources() {
return new LinkedHashSet<ResResource>(mResources.values()); return new LinkedHashSet<ResResource>(mResources.values());
} }
public ResResource getResource(ResConfig config) throws AndrolibException { public ResResource getResource(ResConfig config) throws AndrolibException {
return getResource(config.getFlags()); return getResource(config.getFlags());
} }
public ResResource getResource(ResConfigFlags config)
throws AndrolibException {
ResResource res = mResources.get(config);
if (res == null) {
throw new UndefinedResObject(String.format(
"resource: spec=%s, config=%s", this, config));
}
return res;
}
public boolean hasResource(ResConfig config) { public ResResource getResource(ResConfigFlags config)
return hasResource(config.getFlags()); throws AndrolibException {
} ResResource res = mResources.get(config);
if (res == null) {
throw new UndefinedResObject(String.format(
"resource: spec=%s, config=%s", this, config));
}
return res;
}
private boolean hasResource(ResConfigFlags flags) { public boolean hasResource(ResConfig config) {
return mResources.containsKey(flags); return hasResource(config.getFlags());
} }
public ResResource getDefaultResource() throws AndrolibException { private boolean hasResource(ResConfigFlags flags) {
return getResource(new ResConfigFlags()); return mResources.containsKey(flags);
} }
public boolean hasDefaultResource() { public ResResource getDefaultResource() throws AndrolibException {
return mResources.containsKey(new ResConfigFlags()); return getResource(new ResConfigFlags());
} }
public String getFullName() { public boolean hasDefaultResource() {
return getFullName(false, false); return mResources.containsKey(new ResConfigFlags());
} }
public String getFullName(ResPackage relativeToPackage, public String getFullName() {
boolean excludeType) { return getFullName(false, false);
return getFullName( }
getPackage().equals(relativeToPackage), excludeType);
}
public String getFullName(boolean excludePackage, boolean excludeType) { public String getFullName(ResPackage relativeToPackage, boolean excludeType) {
return return getFullName(getPackage().equals(relativeToPackage), excludeType);
(excludePackage ? "" : getPackage().getName() + ":") + }
(excludeType ? "" : getType().getName() + "/") + getName();
}
public ResID getId() { public String getFullName(boolean excludePackage, boolean excludeType) {
return mId; return (excludePackage ? "" : getPackage().getName() + ":")
} + (excludeType ? "" : getType().getName() + "/") + getName();
}
public String getName() { public ResID getId() {
return StringUtils.replace(mName, "\"", "q"); return mId;
} }
public ResPackage getPackage() { public String getName() {
return mPackage; return StringUtils.replace(mName, "\"", "q");
} }
public ResType getType() { public ResPackage getPackage() {
return mType; return mPackage;
} }
public void addResource(ResResource res) public ResType getType() {
throws AndrolibException { return mType;
addResource(res, false); }
}
public void addResource(ResResource res, boolean overwrite) public void addResource(ResResource res) throws AndrolibException {
throws AndrolibException { addResource(res, false);
ResConfigFlags flags = res.getConfig().getFlags(); }
if (mResources.put(flags, res) != null && ! overwrite) {
throw new AndrolibException(String.format("Multiple resources: spec=%s, config=%s", this, flags));
}
}
@Override public void addResource(ResResource res, boolean overwrite)
public String toString() { throws AndrolibException {
return mId.toString() + " " + mType.toString() + "/" + mName; ResConfigFlags flags = res.getConfig().getFlags();
} if (mResources.put(flags, res) != null && !overwrite) {
throw new AndrolibException(String.format(
"Multiple resources: spec=%s, config=%s", this, flags));
}
}
@Override
public String toString() {
return mId.toString() + " " + mType.toString() + "/" + mName;
}
} }

View File

@ -23,42 +23,41 @@ import brut.androlib.res.data.value.ResValue;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResResource { public class ResResource {
private final ResConfig mConfig; private final ResConfig mConfig;
private final ResResSpec mResSpec; private final ResResSpec mResSpec;
private final ResValue mValue; private final ResValue mValue;
public ResResource(ResConfig config, ResResSpec spec, public ResResource(ResConfig config, ResResSpec spec, ResValue value) {
ResValue value) { this.mConfig = config;
this.mConfig = config; this.mResSpec = spec;
this.mResSpec = spec; this.mValue = value;
this.mValue = value; }
}
public String getFilePath() { public String getFilePath() {
return mResSpec.getType().getName() + return mResSpec.getType().getName()
mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName(); + mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName();
} }
public ResConfig getConfig() { public ResConfig getConfig() {
return mConfig; return mConfig;
} }
public ResResSpec getResSpec() { public ResResSpec getResSpec() {
return mResSpec; return mResSpec;
} }
public ResValue getValue() { public ResValue getValue() {
return mValue; return mValue;
} }
public void replace(ResValue value) throws AndrolibException { public void replace(ResValue value) throws AndrolibException {
ResResource res = new ResResource(mConfig, mResSpec, value); ResResource res = new ResResource(mConfig, mResSpec, value);
mConfig.addResource(res, true); mConfig.addResource(res, true);
mResSpec.addResource(res, true); mResSpec.addResource(res, true);
} }
@Override @Override
public String toString() { public String toString() {
return getFilePath(); return getFilePath();
} }
} }

View File

@ -26,125 +26,121 @@ import java.util.*;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResTable { public class ResTable {
private final AndrolibResources mAndRes; private final AndrolibResources mAndRes;
private final Map<Integer, ResPackage> mPackagesById = private final Map<Integer, ResPackage> mPackagesById = new HashMap<Integer, ResPackage>();
new HashMap<Integer, ResPackage>(); private final Map<String, ResPackage> mPackagesByName = new HashMap<String, ResPackage>();
private final Map<String, ResPackage> mPackagesByName = private final Set<ResPackage> mMainPackages = new LinkedHashSet<ResPackage>();
new HashMap<String, ResPackage>(); private final Set<ResPackage> mFramePackages = new LinkedHashSet<ResPackage>();
private final Set<ResPackage> mMainPackages =
new LinkedHashSet<ResPackage>();
private final Set<ResPackage> mFramePackages =
new LinkedHashSet<ResPackage>();
private String mFrameTag; private String mFrameTag;
private Map<String, String> mSdkInfo = new LinkedHashMap<String, String>(); private Map<String, String> mSdkInfo = new LinkedHashMap<String, String>();
private Map<String, String> mPackageInfo = new LinkedHashMap<String, String>(); private Map<String, String> mPackageInfo = new LinkedHashMap<String, String>();
public ResTable() { public ResTable() {
mAndRes = null; mAndRes = null;
} }
public ResTable(AndrolibResources andRes) { public ResTable(AndrolibResources andRes) {
mAndRes = andRes; mAndRes = andRes;
} }
public ResResSpec getResSpec(int resID) throws AndrolibException { public ResResSpec getResSpec(int resID) throws AndrolibException {
return getResSpec(new ResID(resID)); return getResSpec(new ResID(resID));
} }
public ResResSpec getResSpec(ResID resID) throws AndrolibException {
return getPackage(resID.package_).getResSpec(resID);
}
public Set<ResPackage> listMainPackages() { public ResResSpec getResSpec(ResID resID) throws AndrolibException {
return mMainPackages; return getPackage(resID.package_).getResSpec(resID);
} }
public Set<ResPackage> listFramePackages() { public Set<ResPackage> listMainPackages() {
return mFramePackages; return mMainPackages;
} }
public ResPackage getPackage(int id) throws AndrolibException { public Set<ResPackage> listFramePackages() {
ResPackage pkg = mPackagesById.get(id); return mFramePackages;
if (pkg != null) { }
return pkg;
}
if (mAndRes != null) {
return mAndRes.loadFrameworkPkg(this, id, mFrameTag);
}
throw new UndefinedResObject(String.format("package: id=%d", id));
}
public ResPackage getPackage(String name) throws AndrolibException { public ResPackage getPackage(int id) throws AndrolibException {
ResPackage pkg = mPackagesByName.get(name); ResPackage pkg = mPackagesById.get(id);
if (pkg == null) { if (pkg != null) {
throw new UndefinedResObject("package: name=" + name); return pkg;
} }
return pkg; if (mAndRes != null) {
} return mAndRes.loadFrameworkPkg(this, id, mFrameTag);
}
throw new UndefinedResObject(String.format("package: id=%d", id));
}
public boolean hasPackage(int id) { public ResPackage getPackage(String name) throws AndrolibException {
return mPackagesById.containsKey(id); ResPackage pkg = mPackagesByName.get(name);
} if (pkg == null) {
throw new UndefinedResObject("package: name=" + name);
}
return pkg;
}
public boolean hasPackage(String name) { public boolean hasPackage(int id) {
return mPackagesByName.containsKey(name); return mPackagesById.containsKey(id);
} }
public ResValue getValue(String package_, String type, String name) public boolean hasPackage(String name) {
throws AndrolibException { return mPackagesByName.containsKey(name);
return getPackage(package_).getType(type).getResSpec(name) }
.getDefaultResource().getValue();
}
public void addPackage(ResPackage pkg, boolean main) public ResValue getValue(String package_, String type, String name)
throws AndrolibException { throws AndrolibException {
Integer id = pkg.getId(); return getPackage(package_).getType(type).getResSpec(name)
if (mPackagesById.containsKey(id)) { .getDefaultResource().getValue();
throw new AndrolibException( }
"Multiple packages: id=" + id.toString());
}
String name = pkg.getName();
if (mPackagesByName.containsKey(name)) {
throw new AndrolibException("Multiple packages: name=" + name);
}
mPackagesById.put(id, pkg); public void addPackage(ResPackage pkg, boolean main)
mPackagesByName.put(name, pkg); throws AndrolibException {
if (main) { Integer id = pkg.getId();
mMainPackages.add(pkg); if (mPackagesById.containsKey(id)) {
} else { throw new AndrolibException("Multiple packages: id="
mFramePackages.add(pkg); + id.toString());
} }
} String name = pkg.getName();
if (mPackagesByName.containsKey(name)) {
throw new AndrolibException("Multiple packages: name=" + name);
}
public void setFrameTag(String tag) { mPackagesById.put(id, pkg);
mFrameTag = tag; mPackagesByName.put(name, pkg);
} if (main) {
mMainPackages.add(pkg);
} else {
mFramePackages.add(pkg);
}
}
public Map<String, String> getSdkInfo() { public void setFrameTag(String tag) {
return mSdkInfo; mFrameTag = tag;
} }
public void addSdkInfo(String key, String value) { public Map<String, String> getSdkInfo() {
mSdkInfo.put(key, value); return mSdkInfo;
} }
public void clearSdkInfo() { public void addSdkInfo(String key, String value) {
mSdkInfo.clear(); mSdkInfo.put(key, value);
} }
public void addPackageInfo(String key, String value) { public void clearSdkInfo() {
mPackageInfo.put(key, value); mSdkInfo.clear();
} }
public Map<String, String> getPackageInfo() { public void addPackageInfo(String key, String value) {
return mPackageInfo; mPackageInfo.put(key, value);
} }
public boolean isPackageInfoValueSet(String key) { public Map<String, String> getPackageInfo() {
return (mPackageInfo.containsKey(key)); return mPackageInfo;
} }
public boolean isPackageInfoValueSet(String key) {
return (mPackageInfo.containsKey(key));
}
} }

View File

@ -24,47 +24,44 @@ import java.util.*;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public final class ResType { public final class ResType {
private final String mName; private final String mName;
private final Map<String, ResResSpec> mResSpecs = private final Map<String, ResResSpec> mResSpecs = new LinkedHashMap<String, ResResSpec>();
new LinkedHashMap<String, ResResSpec>();
private final ResTable mResTable; private final ResTable mResTable;
private final ResPackage mPackage; private final ResPackage mPackage;
public ResType(String name, ResTable resTable, public ResType(String name, ResTable resTable, ResPackage package_) {
ResPackage package_) { this.mName = name;
this.mName = name; this.mResTable = resTable;
this.mResTable = resTable; this.mPackage = package_;
this.mPackage = package_; }
}
public String getName() { public String getName() {
return mName; return mName;
} }
public Set<ResResSpec> listResSpecs() { public Set<ResResSpec> listResSpecs() {
return new LinkedHashSet<ResResSpec>(mResSpecs.values()); return new LinkedHashSet<ResResSpec>(mResSpecs.values());
} }
public ResResSpec getResSpec(String name) throws AndrolibException { public ResResSpec getResSpec(String name) throws AndrolibException {
ResResSpec spec = mResSpecs.get(name); ResResSpec spec = mResSpecs.get(name);
if (spec == null) { if (spec == null) {
throw new UndefinedResObject(String.format( throw new UndefinedResObject(String.format("resource spec: %s/%s",
"resource spec: %s/%s", getName(), name)); getName(), name));
} }
return spec; return spec;
} }
public void addResSpec(ResResSpec spec) public void addResSpec(ResResSpec spec) throws AndrolibException {
throws AndrolibException { if (mResSpecs.put(spec.getName(), spec) != null) {
if (mResSpecs.put(spec.getName(), spec) != null) { throw new AndrolibException(String.format(
throw new AndrolibException(String.format( "Multiple res specs: %s/%s", getName(), spec.getName()));
"Multiple res specs: %s/%s", getName(), spec.getName())); }
} }
}
@Override @Override
public String toString() { public String toString() {
return mName; return mName;
} }
} }

View File

@ -23,68 +23,68 @@ import java.util.Set;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResValuesFile { public class ResValuesFile {
private final ResPackage mPackage; private final ResPackage mPackage;
private final ResType mType; private final ResType mType;
private final ResConfig mConfig; private final ResConfig mConfig;
private final Set<ResResource> mResources = private final Set<ResResource> mResources = new LinkedHashSet<ResResource>();
new LinkedHashSet<ResResource>();
public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) { public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) {
this.mPackage = pkg; this.mPackage = pkg;
this.mType = type; this.mType = type;
this.mConfig = config; this.mConfig = config;
} }
public String getPath() { public String getPath() {
return "values" + mConfig.getFlags().getQualifiers() return "values" + mConfig.getFlags().getQualifiers() + "/"
+ "/" + mType.getName() + mType.getName() + (mType.getName().endsWith("s") ? "" : "s")
+ (mType.getName().endsWith("s") ? "" : "s") + ".xml";
+ ".xml"; }
}
public Set<ResResource> listResources() { public Set<ResResource> listResources() {
return mResources; return mResources;
} }
public ResType getType() { public ResType getType() {
return mType; return mType;
} }
public ResConfig getConfig() { public ResConfig getConfig() {
return mConfig; return mConfig;
} }
public boolean isSynthesized(ResResource res) { public boolean isSynthesized(ResResource res) {
return mPackage.isSynthesized(res.getResSpec().getId()); return mPackage.isSynthesized(res.getResSpec().getId());
} }
public void addResource(ResResource res) { public void addResource(ResResource res) {
mResources.add(res); mResources.add(res);
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null) { if (obj == null) {
return false; return false;
} }
if (getClass() != obj.getClass()) { if (getClass() != obj.getClass()) {
return false; return false;
} }
final ResValuesFile other = (ResValuesFile) obj; final ResValuesFile other = (ResValuesFile) obj;
if (this.mType != other.mType && (this.mType == null || !this.mType.equals(other.mType))) { if (this.mType != other.mType
return false; && (this.mType == null || !this.mType.equals(other.mType))) {
} return false;
if (this.mConfig != other.mConfig && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) { }
return false; if (this.mConfig != other.mConfig
} && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) {
return true; return false;
} }
return true;
}
@Override @Override
public int hashCode() { public int hashCode() {
int hash = 17; int hash = 17;
hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0); hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0);
hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0); hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0);
return hash; return hash;
} }
} }

View File

@ -22,70 +22,70 @@ import brut.androlib.res.xml.ResValuesXmlSerializable;
import brut.util.Duo; import brut.util.Duo;
import java.io.IOException; import java.io.IOException;
import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlSerializer;
import org.apache.commons.lang3.StringUtils;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializable { public class ResArrayValue extends ResBagValue implements
private String mRawItems; ResValuesXmlSerializable {
ResArrayValue(ResReferenceValue parent, private String mRawItems;
Duo<Integer, ResScalarValue>[] items) {
super(parent);
mItems = new ResScalarValue[items.length]; ResArrayValue(ResReferenceValue parent, Duo<Integer, ResScalarValue>[] items) {
for (int i = 0; i < items.length; i++) { super(parent);
mItems[i] = items[i].m2;
}
}
public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) { mItems = new ResScalarValue[items.length];
super(parent); for (int i = 0; i < items.length; i++) {
mItems = items; mItems[i] = items[i].m2;
} }
}
@Override public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) {
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) super(parent);
throws IOException, AndrolibException { mItems = items;
String type = getType(); }
type = (type == null ? "" : type + "-") + "array";
// reference array (04 10 2012, BurgerZ)
if ("reference-array".equals(type)) {
type = "string-array";
}
// reference array (04 10 2012, BurgerZ)
serializer.startTag(null, type);
serializer.attribute(null, "name", res.getResSpec().getName());
for (int i = 0; i < mItems.length; i++) {
serializer.startTag(null, "item");
serializer.text(mItems[i].encodeAsResXmlItemValue());
serializer.endTag(null, "item");
}
serializer.endTag(null, type);
}
public String getType() throws AndrolibException { @Override
if (mItems.length == 0) { public void serializeToResValuesXml(XmlSerializer serializer,
return null; ResResource res) throws IOException, AndrolibException {
} String type = getType();
String type = mItems[0].getType(); type = (type == null ? "" : type + "-") + "array";
for (int i = 1; i < mItems.length; i++) { // reference array (04 10 2012, BurgerZ)
if ("reference-array".equals(type)) {
if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) { type = "string-array";
return "string"; }
} else if (mItems[i].encodeAsResXmlItemValue().startsWith("@drawable")) { // reference array (04 10 2012, BurgerZ)
return null; serializer.startTag(null, type);
} else if (!"string".equals(type) && !"integer".equals(type)) { serializer.attribute(null, "name", res.getResSpec().getName());
return null; for (int i = 0; i < mItems.length; i++) {
} else if (!type.equals(mItems[i].getType())) { serializer.startTag(null, "item");
return null; serializer.text(mItems[i].encodeAsResXmlItemValue());
} serializer.endTag(null, "item");
} }
return type; serializer.endTag(null, type);
} }
private final ResScalarValue[] mItems; public String getType() throws AndrolibException {
if (mItems.length == 0) {
return null;
}
String type = mItems[0].getType();
for (int i = 1; i < mItems.length; i++) {
if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) {
return "string";
} else if (mItems[i].encodeAsResXmlItemValue().startsWith(
"@drawable")) {
return null;
} else if (!"string".equals(type) && !"integer".equals(type)) {
return null;
} else if (!type.equals(mItems[i].getType())) {
return null;
}
}
return type;
}
public static final int BAG_KEY_ARRAY_START = 0x02000000; private final ResScalarValue[] mItems;
public static final int BAG_KEY_ARRAY_START = 0x02000000;
} }

View File

@ -28,148 +28,148 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResAttr extends ResBagValue implements ResValuesXmlSerializable { public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max,
Boolean l10n) { Boolean l10n) {
super(parentVal); super(parentVal);
mType = type; mType = type;
mMin = min; mMin = min;
mMax = max; mMax = max;
mL10n = l10n; mL10n = l10n;
} }
public String convertToResXmlFormat(ResScalarValue value) public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException { throws AndrolibException {
return null; return null;
} }
@Override @Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) public void serializeToResValuesXml(XmlSerializer serializer,
throws IOException, AndrolibException { ResResource res) throws IOException, AndrolibException {
String type = getTypeAsString(); String type = getTypeAsString();
serializer.startTag(null, "attr"); serializer.startTag(null, "attr");
serializer.attribute(null, "name", res.getResSpec().getName()); serializer.attribute(null, "name", res.getResSpec().getName());
if (type != null) { if (type != null) {
serializer.attribute(null, "format", type); serializer.attribute(null, "format", type);
} }
if (mMin != null) { if (mMin != null) {
serializer.attribute(null, "min", mMin.toString()); serializer.attribute(null, "min", mMin.toString());
} }
if (mMax != null) { if (mMax != null) {
serializer.attribute(null, "max", mMax.toString()); serializer.attribute(null, "max", mMax.toString());
} }
if (mL10n != null && mL10n) { if (mL10n != null && mL10n) {
serializer.attribute(null, "localization", "suggested"); serializer.attribute(null, "localization", "suggested");
} }
serializeBody(serializer, res); serializeBody(serializer, res);
serializer.endTag(null, "attr"); serializer.endTag(null, "attr");
} }
public static ResAttr factory(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory,
ResPackage pkg) throws AndrolibException {
public static ResAttr factory(ResReferenceValue parent, int type = ((ResIntValue) items[0].m2).getValue();
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory, int scalarType = type & 0xffff;
ResPackage pkg) throws AndrolibException { Integer min = null, max = null;
Boolean l10n = null;
int i;
for (i = 1; i < items.length; i++) {
switch (items[i].m1) {
case BAG_KEY_ATTR_MIN:
min = ((ResIntValue) items[i].m2).getValue();
continue;
case BAG_KEY_ATTR_MAX:
max = ((ResIntValue) items[i].m2).getValue();
continue;
case BAG_KEY_ATTR_L10N:
l10n = ((ResIntValue) items[i].m2).getValue() != 0;
continue;
}
break;
}
int type = ((ResIntValue) items[0].m2).getValue(); if (i == items.length) {
int scalarType = type & 0xffff; return new ResAttr(parent, scalarType, min, max, l10n);
Integer min = null, max = null; }
Boolean l10n = null; Duo<ResReferenceValue, ResIntValue>[] attrItems = new Duo[items.length
int i; - i];
for (i = 1; i < items.length; i++) { int j = 0;
switch (items[i].m1) { for (; i < items.length; i++) {
case BAG_KEY_ATTR_MIN: int resId = items[i].m1;
min = ((ResIntValue) items[i].m2).getValue(); pkg.addSynthesizedRes(resId);
continue; attrItems[j++] = new Duo<ResReferenceValue, ResIntValue>(
case BAG_KEY_ATTR_MAX: factory.newReference(resId, null),
max = ((ResIntValue) items[i].m2).getValue(); (ResIntValue) items[i].m2);
continue; }
case BAG_KEY_ATTR_L10N: switch (type & 0xff0000) {
l10n = ((ResIntValue) items[i].m2).getValue() != 0; case TYPE_ENUM:
continue; return new ResEnumAttr(parent, scalarType, min, max, l10n,
} attrItems);
break; case TYPE_FLAGS:
} return new ResFlagsAttr(parent, scalarType, min, max, l10n,
attrItems);
}
if (i == items.length) { throw new AndrolibException("Could not decode attr value");
return new ResAttr(parent, scalarType, min, max, l10n); }
}
Duo<ResReferenceValue, ResIntValue>[] attrItems =
new Duo[items.length - i];
int j = 0;
for (; i < items.length; i++) {
int resId = items[i].m1;
pkg.addSynthesizedRes(resId);
attrItems[j++] = new Duo<ResReferenceValue, ResIntValue>(
factory.newReference(resId, null), (ResIntValue) items[i].m2);
}
switch (type & 0xff0000) {
case TYPE_ENUM:
return new ResEnumAttr(
parent, scalarType, min, max, l10n, attrItems);
case TYPE_FLAGS:
return new ResFlagsAttr(
parent, scalarType, min, max, l10n, attrItems);
}
throw new AndrolibException("Could not decode attr value"); protected void serializeBody(XmlSerializer serializer, ResResource res)
} throws AndrolibException, IOException {
}
protected void serializeBody(XmlSerializer serializer, ResResource res) protected String getTypeAsString() {
throws AndrolibException, IOException {} String s = "";
if ((mType & TYPE_REFERENCE) != 0) {
s += "|reference";
}
if ((mType & TYPE_STRING) != 0) {
s += "|string";
}
if ((mType & TYPE_INT) != 0) {
s += "|integer";
}
if ((mType & TYPE_BOOL) != 0) {
s += "|boolean";
}
if ((mType & TYPE_COLOR) != 0) {
s += "|color";
}
if ((mType & TYPE_FLOAT) != 0) {
s += "|float";
}
if ((mType & TYPE_DIMEN) != 0) {
s += "|dimension";
}
if ((mType & TYPE_FRACTION) != 0) {
s += "|fraction";
}
if (s.isEmpty()) {
return null;
}
return s.substring(1);
}
protected String getTypeAsString() { private final int mType;
String s = ""; private final Integer mMin;
if ((mType & TYPE_REFERENCE) != 0) { private final Integer mMax;
s += "|reference"; private final Boolean mL10n;
}
if ((mType & TYPE_STRING) != 0) {
s += "|string";
}
if ((mType & TYPE_INT) != 0) {
s += "|integer";
}
if ((mType & TYPE_BOOL) != 0) {
s += "|boolean";
}
if ((mType & TYPE_COLOR) != 0) {
s += "|color";
}
if ((mType & TYPE_FLOAT) != 0) {
s += "|float";
}
if ((mType & TYPE_DIMEN) != 0) {
s += "|dimension";
}
if ((mType & TYPE_FRACTION) != 0) {
s += "|fraction";
}
if (s.isEmpty()) {
return null;
}
return s.substring(1);
}
private final int mType; public static final int BAG_KEY_ATTR_TYPE = 0x01000000;
private final Integer mMin; private static final int BAG_KEY_ATTR_MIN = 0x01000001;
private final Integer mMax; private static final int BAG_KEY_ATTR_MAX = 0x01000002;
private final Boolean mL10n; private static final int BAG_KEY_ATTR_L10N = 0x01000003;
private final static int TYPE_REFERENCE = 0x01;
private final static int TYPE_STRING = 0x02;
private final static int TYPE_INT = 0x04;
private final static int TYPE_BOOL = 0x08;
private final static int TYPE_COLOR = 0x10;
private final static int TYPE_FLOAT = 0x20;
private final static int TYPE_DIMEN = 0x40;
private final static int TYPE_FRACTION = 0x80;
private final static int TYPE_ANY_STRING = 0xee;
public static final int BAG_KEY_ATTR_TYPE = 0x01000000; private static final int TYPE_ENUM = 0x00010000;
private static final int BAG_KEY_ATTR_MIN = 0x01000001; private static final int TYPE_FLAGS = 0x00020000;
private static final int BAG_KEY_ATTR_MAX = 0x01000002;
private static final int BAG_KEY_ATTR_L10N = 0x01000003;
private final static int TYPE_REFERENCE = 0x01;
private final static int TYPE_STRING = 0x02;
private final static int TYPE_INT = 0x04;
private final static int TYPE_BOOL = 0x08;
private final static int TYPE_COLOR = 0x10;
private final static int TYPE_FLOAT = 0x20;
private final static int TYPE_DIMEN = 0x40;
private final static int TYPE_FRACTION = 0x80;
private final static int TYPE_ANY_STRING = 0xee;
private static final int TYPE_ENUM = 0x00010000;
private static final int TYPE_FLAGS = 0x00020000;
} }

View File

@ -27,38 +27,39 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResBagValue extends ResValue implements ResValuesXmlSerializable { public class ResBagValue extends ResValue implements ResValuesXmlSerializable {
protected final ResReferenceValue mParent; protected final ResReferenceValue mParent;
public ResBagValue(ResReferenceValue parent) { public ResBagValue(ResReferenceValue parent) {
this.mParent = parent; this.mParent = parent;
} }
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) @Override
throws IOException, AndrolibException { public void serializeToResValuesXml(XmlSerializer serializer,
String type = res.getResSpec().getType().getName(); ResResource res) throws IOException, AndrolibException {
if ("style".equals(type)) { String type = res.getResSpec().getType().getName();
new ResStyleValue(mParent, new Duo[0], null) if ("style".equals(type)) {
.serializeToResValuesXml(serializer, res); new ResStyleValue(mParent, new Duo[0], null)
return; .serializeToResValuesXml(serializer, res);
} return;
if ("array".equals(type)) { }
new ResArrayValue(mParent, new Duo[0]) if ("array".equals(type)) {
.serializeToResValuesXml(serializer, res); new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml(
return; serializer, res);
} return;
if ("plurals".equals(type)) { }
new ResPluralsValue(mParent, new Duo[0]) if ("plurals".equals(type)) {
.serializeToResValuesXml(serializer, res); new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml(
return; serializer, res);
} return;
}
serializer.startTag(null, "item"); serializer.startTag(null, "item");
serializer.attribute(null, "type", type); serializer.attribute(null, "type", type);
serializer.attribute(null, "name", res.getResSpec().getName()); serializer.attribute(null, "name", res.getResSpec().getName());
serializer.endTag(null, "item"); serializer.endTag(null, "item");
} }
public ResReferenceValue getParent() { public ResReferenceValue getParent() {
return mParent; return mParent;
} }
} }

View File

@ -20,18 +20,19 @@ package brut.androlib.res.data.value;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResBoolValue extends ResScalarValue { public class ResBoolValue extends ResScalarValue {
private final boolean mValue; private final boolean mValue;
public ResBoolValue(boolean value, String rawValue) { public ResBoolValue(boolean value, String rawValue) {
super("bool", rawValue); super("bool", rawValue);
this.mValue = value; this.mValue = value;
} }
public boolean getValue() { public boolean getValue() {
return mValue; return mValue;
} }
protected String encodeAsResXml() { @Override
return mValue ? "true" : "false"; protected String encodeAsResXml() {
} return mValue ? "true" : "false";
}
} }

View File

@ -20,12 +20,12 @@ package brut.androlib.res.data.value;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResColorValue extends ResIntValue { public class ResColorValue extends ResIntValue {
public ResColorValue(int value, String rawValue) { public ResColorValue(int value, String rawValue) {
super(value, rawValue, "color"); super(value, rawValue, "color");
} }
@Override @Override
protected String encodeAsResXml() { protected String encodeAsResXml() {
return String.format("#%08x", mValue); return String.format("#%08x", mValue);
} }
} }

View File

@ -23,12 +23,12 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResDimenValue extends ResIntValue { public class ResDimenValue extends ResIntValue {
public ResDimenValue(int value, String rawValue) { public ResDimenValue(int value, String rawValue) {
super(value, rawValue, "dimen"); super(value, rawValue, "dimen");
} }
@Override @Override
protected String encodeAsResXml() throws AndrolibException { protected String encodeAsResXml() throws AndrolibException {
return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue); return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue);
} }
} }

View File

@ -28,57 +28,55 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResEnumAttr extends ResAttr { public class ResEnumAttr extends ResAttr {
ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max, ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max,
Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) { Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
super(parent, type, min, max, l10n); super(parent, type, min, max, l10n);
mItems = items; mItems = items;
} }
@Override @Override
public String convertToResXmlFormat(ResScalarValue value) public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException { throws AndrolibException {
if (value instanceof ResIntValue) { if (value instanceof ResIntValue) {
String ret = decodeValue(((ResIntValue) value).getValue()); String ret = decodeValue(((ResIntValue) value).getValue());
if (ret != null) { if (ret != null) {
return ret; return ret;
} }
} }
return super.convertToResXmlFormat(value); return super.convertToResXmlFormat(value);
} }
@Override @Override
protected void serializeBody(XmlSerializer serializer, ResResource res) protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException { throws AndrolibException, IOException {
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) { for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
int intVal = duo.m2.getValue(); int intVal = duo.m2.getValue();
serializer.startTag(null, "enum"); serializer.startTag(null, "enum");
serializer.attribute(null, "name", duo.m1.getReferent().getName()); serializer.attribute(null, "name", duo.m1.getReferent().getName());
serializer.attribute(null, "value", String.valueOf(intVal)); serializer.attribute(null, "value", String.valueOf(intVal));
serializer.endTag(null, "enum"); serializer.endTag(null, "enum");
} }
} }
private String decodeValue(int value) throws AndrolibException { private String decodeValue(int value) throws AndrolibException {
String value2 = mItemsCache.get(value); String value2 = mItemsCache.get(value);
if (value2 == null) { if (value2 == null) {
ResReferenceValue ref = null; ResReferenceValue ref = null;
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) { for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
if (duo.m2.getValue() == value) { if (duo.m2.getValue() == value) {
ref = duo.m1; ref = duo.m1;
break; break;
} }
} }
if (ref != null) { if (ref != null) {
value2 = ref.getReferent().getName(); value2 = ref.getReferent().getName();
mItemsCache.put(value, value2); mItemsCache.put(value, value2);
} }
} }
return value2; return value2;
} }
private final Duo<ResReferenceValue, ResIntValue>[] mItems;
private final Duo<ResReferenceValue, ResIntValue>[] mItems; private final Map<Integer, String> mItemsCache = new HashMap<Integer, String>();
private final Map<Integer, String> mItemsCache =
new HashMap<Integer, String>();
} }

View File

@ -22,21 +22,21 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResFileValue extends ResValue { public class ResFileValue extends ResValue {
private final String mPath; private final String mPath;
public ResFileValue(String path) { public ResFileValue(String path) {
this.mPath = path; this.mPath = path;
} }
public String getPath() { public String getPath() {
return mPath; return mPath;
} }
public String getStrippedPath() throws AndrolibException { public String getStrippedPath() throws AndrolibException {
if (! mPath.startsWith("res/")) { if (!mPath.startsWith("res/")) {
throw new AndrolibException( throw new AndrolibException(
"File path does not start with \"res/\": " + mPath); "File path does not start with \"res/\": " + mPath);
} }
return mPath.substring(4); return mPath.substring(4);
} }
} }

View File

@ -28,133 +28,133 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResFlagsAttr extends ResAttr { public class ResFlagsAttr extends ResAttr {
ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) { ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max,
super(parent, type, min, max, l10n); Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
super(parent, type, min, max, l10n);
mItems = new FlagItem[items.length]; mItems = new FlagItem[items.length];
for (int i = 0; i < items.length; i++) { for (int i = 0; i < items.length; i++) {
mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue()); mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue());
} }
} }
@Override @Override
public String convertToResXmlFormat(ResScalarValue value) public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException { throws AndrolibException {
if (! (value instanceof ResIntValue)) { if (!(value instanceof ResIntValue)) {
return super.convertToResXmlFormat(value); return super.convertToResXmlFormat(value);
} }
loadFlags(); loadFlags();
int intVal = ((ResIntValue) value).getValue(); int intVal = ((ResIntValue) value).getValue();
if (intVal == 0) { if (intVal == 0) {
return renderFlags(mZeroFlags); return renderFlags(mZeroFlags);
} }
FlagItem[] flagItems = new FlagItem[mFlags.length]; FlagItem[] flagItems = new FlagItem[mFlags.length];
int[] flags = new int[mFlags.length]; int[] flags = new int[mFlags.length];
int flagsCount = 0; int flagsCount = 0;
for (int i = 0; i < mFlags.length; i++) { for (int i = 0; i < mFlags.length; i++) {
FlagItem flagItem = mFlags[i]; FlagItem flagItem = mFlags[i];
int flag = flagItem.flag; int flag = flagItem.flag;
if ((intVal & flag) != flag) { if ((intVal & flag) != flag) {
continue; continue;
} }
if (! isSubpartOf(flag, flags)) { if (!isSubpartOf(flag, flags)) {
flags[flagsCount] = flag; flags[flagsCount] = flag;
flagItems[flagsCount++] = flagItem; flagItems[flagsCount++] = flagItem;
} }
} }
return renderFlags(Arrays.copyOf(flagItems, flagsCount)); return renderFlags(Arrays.copyOf(flagItems, flagsCount));
} }
@Override @Override
protected void serializeBody(XmlSerializer serializer, ResResource res) protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException { throws AndrolibException, IOException {
for (int i = 0; i < mItems.length; i++) { for (int i = 0; i < mItems.length; i++) {
FlagItem item = mItems[i]; FlagItem item = mItems[i];
serializer.startTag(null, "flag"); serializer.startTag(null, "flag");
serializer.attribute(null, "name", item.getValue()); serializer.attribute(null, "name", item.getValue());
serializer.attribute(null, "value", serializer.attribute(null, "value",
String.format("0x%08x", item.flag)); String.format("0x%08x", item.flag));
serializer.endTag(null, "flag"); serializer.endTag(null, "flag");
} }
} }
private boolean isSubpartOf(int flag, int[] flags) { private boolean isSubpartOf(int flag, int[] flags) {
for (int i = 0; i < flags.length; i++) { for (int i = 0; i < flags.length; i++) {
if ((flags[i] & flag) == flag) { if ((flags[i] & flag) == flag) {
return true; return true;
} }
} }
return false; return false;
} }
private String renderFlags(FlagItem[] flags) throws AndrolibException { private String renderFlags(FlagItem[] flags) throws AndrolibException {
String ret = ""; String ret = "";
for (int i = 0; i < flags.length; i++) { for (int i = 0; i < flags.length; i++) {
ret += "|" + flags[i].getValue(); ret += "|" + flags[i].getValue();
} }
if (ret.isEmpty()) { if (ret.isEmpty()) {
return ret; return ret;
} }
return ret.substring(1); return ret.substring(1);
} }
private void loadFlags() { private void loadFlags() {
if (mFlags != null) { if (mFlags != null) {
return; return;
} }
FlagItem[] zeroFlags = new FlagItem[mItems.length]; FlagItem[] zeroFlags = new FlagItem[mItems.length];
int zeroFlagsCount = 0; int zeroFlagsCount = 0;
FlagItem[] flags = new FlagItem[mItems.length]; FlagItem[] flags = new FlagItem[mItems.length];
int flagsCount = 0; int flagsCount = 0;
for (int i = 0; i < mItems.length; i++) { for (int i = 0; i < mItems.length; i++) {
FlagItem item = mItems[i]; FlagItem item = mItems[i];
if (item.flag == 0) { if (item.flag == 0) {
zeroFlags[zeroFlagsCount++] = item; zeroFlags[zeroFlagsCount++] = item;
} else { } else {
flags[flagsCount++] = item; flags[flagsCount++] = item;
} }
} }
mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount); mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount);
mFlags = Arrays.copyOf(flags, flagsCount); mFlags = Arrays.copyOf(flags, flagsCount);
Arrays.sort(mFlags, new Comparator<FlagItem>() { Arrays.sort(mFlags, new Comparator<FlagItem>() {
public int compare(FlagItem o1, FlagItem o2) { @Override
return Integer.valueOf(Integer.bitCount(o2.flag)) public int compare(FlagItem o1, FlagItem o2) {
.compareTo(Integer.bitCount(o1.flag)); return Integer.valueOf(Integer.bitCount(o2.flag)).compareTo(
} Integer.bitCount(o1.flag));
}); }
} });
}
private final FlagItem[] mItems;
private final FlagItem[] mItems; private FlagItem[] mZeroFlags;
private FlagItem[] mFlags;
private FlagItem[] mZeroFlags; private static class FlagItem {
private FlagItem[] mFlags; public final ResReferenceValue ref;
public final int flag;
public String value;
public FlagItem(ResReferenceValue ref, int flag) {
this.ref = ref;
this.flag = flag;
}
private static class FlagItem { public String getValue() throws AndrolibException {
public final ResReferenceValue ref; if (value == null) {
public final int flag; value = ref.getReferent().getName();
public String value; }
return value;
public FlagItem(ResReferenceValue ref, int flag) { }
this.ref = ref; }
this.flag = flag;
}
public String getValue() throws AndrolibException {
if (value == null) {
value = ref.getReferent().getName();
}
return value;
}
}
} }

View File

@ -20,18 +20,19 @@ package brut.androlib.res.data.value;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResFloatValue extends ResScalarValue { public class ResFloatValue extends ResScalarValue {
private final float mValue; private final float mValue;
public ResFloatValue(float value, String rawValue) { public ResFloatValue(float value, String rawValue) {
super("float", rawValue); super("float", rawValue);
this.mValue = value; this.mValue = value;
} }
public float getValue() { public float getValue() {
return mValue; return mValue;
} }
protected String encodeAsResXml() { @Override
return String.valueOf(mValue); protected String encodeAsResXml() {
} return String.valueOf(mValue);
}
} }

View File

@ -23,12 +23,12 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResFractionValue extends ResIntValue { public class ResFractionValue extends ResIntValue {
public ResFractionValue(int value, String rawValue) { public ResFractionValue(int value, String rawValue) {
super(value, rawValue, "fraction"); super(value, rawValue, "fraction");
} }
@Override @Override
protected String encodeAsResXml() throws AndrolibException { protected String encodeAsResXml() throws AndrolibException {
return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue); return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue);
} }
} }

View File

@ -26,10 +26,13 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResIdValue extends ResValue implements ResValuesXmlSerializable { public class ResIdValue extends ResValue implements ResValuesXmlSerializable {
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) throws IOException, AndrolibException { @Override
serializer.startTag(null, "item"); public void serializeToResValuesXml(XmlSerializer serializer,
serializer.attribute(null, "type", res.getResSpec().getType().getName()); ResResource res) throws IOException, AndrolibException {
serializer.attribute(null, "name", res.getResSpec().getName()); serializer.startTag(null, "item");
serializer.endTag(null, "item"); serializer
} .attribute(null, "type", res.getResSpec().getType().getName());
serializer.attribute(null, "name", res.getResSpec().getName());
serializer.endTag(null, "item");
}
} }

View File

@ -23,24 +23,25 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResIntValue extends ResScalarValue { public class ResIntValue extends ResScalarValue {
protected final int mValue; protected final int mValue;
private int type; private int type;
public ResIntValue(int value, String rawValue, int type) { public ResIntValue(int value, String rawValue, int type) {
this(value, rawValue, "integer"); this(value, rawValue, "integer");
this.type = type; this.type = type;
} }
public ResIntValue(int value, String rawValue, String type) { public ResIntValue(int value, String rawValue, String type) {
super(type, rawValue); super(type, rawValue);
this.mValue = value; this.mValue = value;
} }
public int getValue() { public int getValue() {
return mValue; return mValue;
} }
protected String encodeAsResXml() throws AndrolibException { @Override
return TypedValue.coerceToString(type, mValue); protected String encodeAsResXml() throws AndrolibException {
} return TypedValue.coerceToString(type, mValue);
}
} }

View File

@ -28,54 +28,57 @@ import org.xmlpull.v1.XmlSerializer;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializable { public class ResPluralsValue extends ResBagValue implements
ResPluralsValue(ResReferenceValue parent, ResValuesXmlSerializable {
Duo<Integer, ResScalarValue>[] items) { ResPluralsValue(ResReferenceValue parent,
super(parent); Duo<Integer, ResScalarValue>[] items) {
super(parent);
mItems = new ResScalarValue[6]; mItems = new ResScalarValue[6];
for (int i = 0; i < items.length; i++) { for (int i = 0; i < items.length; i++) {
mItems[items[i].m1 - BAG_KEY_PLURALS_START] = mItems[items[i].m1 - BAG_KEY_PLURALS_START] = items[i].m2;
(ResScalarValue) items[i].m2; }
} }
}
@Override @Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) public void serializeToResValuesXml(XmlSerializer serializer,
throws IOException, AndrolibException { ResResource res) throws IOException, AndrolibException {
serializer.startTag(null, "plurals"); serializer.startTag(null, "plurals");
serializer.attribute(null, "name", res.getResSpec().getName()); serializer.attribute(null, "name", res.getResSpec().getName());
for (int i = 0; i < mItems.length; i++) { for (int i = 0; i < mItems.length; i++) {
ResScalarValue item = mItems[i]; ResScalarValue item = mItems[i];
if (item == null) { if (item == null) {
continue; continue;
} }
ResScalarValue rawValue = item; ResScalarValue rawValue = item;
serializer.startTag(null, "item"); serializer.startTag(null, "item");
serializer.attribute(null, "quantity", QUANTITY_MAP[i]); serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue.encodeAsResXmlValue())) { if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue
serializer.text(item.encodeAsResXmlValueExt()); .encodeAsResXmlValue())) {
} else { serializer.text(item.encodeAsResXmlValueExt());
String recode = item.encodeAsResXmlValue(); } else {
//Dirty, but working fix @miuirussia String recode = item.encodeAsResXmlValue();
for (int j = 0; j < 10; j++) { // Dirty, but working fix @miuirussia
recode = StringUtils.replace(recode, "%" + Integer.toString(j) + "$" + Integer.toString(j) + "$", "%" + Integer.toString(j) + "$"); for (int j = 0; j < 10; j++) {
} recode = StringUtils.replace(
serializer.text(recode); recode,
} "%" + Integer.toString(j) + "$"
serializer.endTag(null, "item"); + Integer.toString(j) + "$",
} "%" + Integer.toString(j) + "$");
serializer.endTag(null, "plurals"); }
} serializer.text(recode);
}
serializer.endTag(null, "item");
}
serializer.endTag(null, "plurals");
}
private final ResScalarValue[] mItems;
private final ResScalarValue[] mItems; public static final int BAG_KEY_PLURALS_START = 0x01000004;
public static final int BAG_KEY_PLURALS_END = 0x01000009;
private static final String[] QUANTITY_MAP = new String[] { "other",
public static final int BAG_KEY_PLURALS_START = 0x01000004; "zero", "one", "two", "few", "many" };
public static final int BAG_KEY_PLURALS_END = 0x01000009;
private static final String[] QUANTITY_MAP =
new String[] {"other", "zero", "one", "two", "few", "many"};
} }

View File

@ -24,44 +24,44 @@ import brut.androlib.res.data.ResResSpec;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResReferenceValue extends ResIntValue { public class ResReferenceValue extends ResIntValue {
private final ResPackage mPackage; private final ResPackage mPackage;
private final boolean mTheme; private final boolean mTheme;
public ResReferenceValue(ResPackage package_, int value, String rawValue) { public ResReferenceValue(ResPackage package_, int value, String rawValue) {
this(package_, value, rawValue, false); this(package_, value, rawValue, false);
} }
public ResReferenceValue(ResPackage package_, int value, String rawValue,
boolean theme) {
super(value, rawValue, "reference");
mPackage = package_;
mTheme = theme;
}
protected String encodeAsResXml() throws AndrolibException { public ResReferenceValue(ResPackage package_, int value, String rawValue,
if (isNull()) { boolean theme) {
return "@null"; super(value, rawValue, "reference");
} mPackage = package_;
mTheme = theme;
}
ResResSpec spec = getReferent(); @Override
boolean newId = protected String encodeAsResXml() throws AndrolibException {
spec.hasDefaultResource() && if (isNull()) {
spec.getDefaultResource().getValue() instanceof ResIdValue; return "@null";
}
// generate the beginning to fix @android ResResSpec spec = getReferent();
String mStart = (mTheme ? '?' : '@') + (newId ? "+" : ""); boolean newId = spec.hasDefaultResource()
//mStart = mStart.replace("@android", "@*android"); && spec.getDefaultResource().getValue() instanceof ResIdValue;
return mStart +
spec.getFullName(mPackage,
mTheme && spec.getType().getName().equals("attr"));
}
public ResResSpec getReferent() throws AndrolibException { // generate the beginning to fix @android
return mPackage.getResTable().getResSpec(getValue()); String mStart = (mTheme ? '?' : '@') + (newId ? "+" : "");
} // mStart = mStart.replace("@android", "@*android");
public boolean isNull() { return mStart
return mValue == 0; + spec.getFullName(mPackage, mTheme
} && spec.getType().getName().equals("attr"));
}
public ResResSpec getReferent() throws AndrolibException {
return mPackage.getResTable().getResSpec(getValue());
}
public boolean isNull() {
return mValue == 0;
}
} }

View File

@ -27,105 +27,107 @@ import org.xmlpull.v1.XmlSerializer;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public abstract class ResScalarValue extends ResValue public abstract class ResScalarValue extends ResValue implements
implements ResXmlEncodable, ResValuesXmlSerializable { ResXmlEncodable, ResValuesXmlSerializable {
protected final String mType; protected final String mType;
protected final String mRawValue; protected final String mRawValue;
protected ResScalarValue(String type, String rawValue) { protected ResScalarValue(String type, String rawValue) {
mType = type; mType = type;
mRawValue = rawValue; mRawValue = rawValue;
} }
public String encodeAsResXmlAttr() throws AndrolibException { @Override
if (mRawValue != null) { public String encodeAsResXmlAttr() throws AndrolibException {
return mRawValue; if (mRawValue != null) {
} return mRawValue;
return encodeAsResXml().replace("@android:", "@*android:"); }
} return encodeAsResXml().replace("@android:", "@*android:");
}
public String encodeAsResXmlItemValue() throws AndrolibException { public String encodeAsResXmlItemValue() throws AndrolibException {
return encodeAsResXmlValue().replace("@android:", "@*android:"); return encodeAsResXmlValue().replace("@android:", "@*android:");
} }
public String encodeAsResXmlValue() throws AndrolibException { @Override
if (mRawValue != null) { public String encodeAsResXmlValue() throws AndrolibException {
return mRawValue; if (mRawValue != null) {
} return mRawValue;
return encodeAsResXmlValueExt().replace("@android:", "@*android:"); }
} return encodeAsResXmlValueExt().replace("@android:", "@*android:");
}
public String encodeAsResXmlValueExt() throws AndrolibException { public String encodeAsResXmlValueExt() throws AndrolibException {
String rawValue = mRawValue; String rawValue = mRawValue;
if (rawValue != null) { if (rawValue != null) {
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) { if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
int count = 1; int count = 1;
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
String tmp1[] = rawValue.split("%%", -1); String tmp1[] = rawValue.split("%%", -1);
int tmp1_sz = tmp1.length; int tmp1_sz = tmp1.length;
for(int i=0;i<tmp1_sz;i++) { for (int i = 0; i < tmp1_sz; i++) {
String cur1 = tmp1[i]; String cur1 = tmp1[i];
String tmp2[] = cur1.split("%", -1); String tmp2[] = cur1.split("%", -1);
int tmp2_sz = tmp2.length; int tmp2_sz = tmp2.length;
for(int j=0;j<tmp2_sz;j++) { for (int j = 0; j < tmp2_sz; j++) {
String cur2 = tmp2[j]; String cur2 = tmp2[j];
result.append(cur2); result.append(cur2);
if(j != (tmp2_sz-1)) { if (j != (tmp2_sz - 1)) {
result.append('%').append(count).append('$'); result.append('%').append(count).append('$');
count++; count++;
} }
} }
if(i != (tmp1_sz-1)) { if (i != (tmp1_sz - 1)) {
result.append("%%"); result.append("%%");
} }
} }
rawValue = result.toString(); rawValue = result.toString();
} }
return rawValue; return rawValue;
} }
return encodeAsResXml(); return encodeAsResXml();
} }
@Override
public void serializeToResValuesXml(XmlSerializer serializer,
ResResource res) throws IOException, AndrolibException {
String type = res.getResSpec().getType().getName();
boolean item = !"reference".equals(mType) && !type.equals(mType);
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) String body = encodeAsResXmlValue();
throws IOException, AndrolibException {
String type = res.getResSpec().getType().getName();
boolean item = !"reference".equals(mType) && !type.equals(mType);
String body = encodeAsResXmlValue();
// check for resource reference
if (body.contains("@")){
if(!res.getFilePath().contains("string")) {
item = true;
}
}
// check for using attrib as node or item
String tagName = item ? "item" : type;
serializer.startTag(null, tagName);
if (item) {
serializer.attribute(null, "type", type);
}
serializer.attribute(null, "name", res.getResSpec().getName());
serializeExtraXmlAttrs(serializer, res); // check for resource reference
if (body.contains("@")) {
if (!res.getFilePath().contains("string")) {
item = true;
}
}
if (! body.isEmpty()) { // check for using attrib as node or item
serializer.ignorableWhitespace(body); String tagName = item ? "item" : type;
}
serializer.endTag(null, tagName); serializer.startTag(null, tagName);
} if (item) {
serializer.attribute(null, "type", type);
}
serializer.attribute(null, "name", res.getResSpec().getName());
public String getType() { serializeExtraXmlAttrs(serializer, res);
return mType;
}
protected void serializeExtraXmlAttrs(XmlSerializer serializer, if (!body.isEmpty()) {
ResResource res) throws IOException { serializer.ignorableWhitespace(body);
} }
protected abstract String encodeAsResXml() throws AndrolibException; serializer.endTag(null, tagName);
}
public String getType() {
return mType;
}
protected void serializeExtraXmlAttrs(XmlSerializer serializer,
ResResource res) throws IOException {
}
protected abstract String encodeAsResXml() throws AndrolibException;
} }

View File

@ -27,40 +27,41 @@ import org.xmlpull.v1.XmlSerializer;
*/ */
public class ResStringValue extends ResScalarValue { public class ResStringValue extends ResScalarValue {
public ResStringValue(String value) { public ResStringValue(String value) {
this(value, "string"); this(value, "string");
} }
public ResStringValue(String value, String type) { public ResStringValue(String value, String type) {
super(type, value); super(type, value);
} }
@Override @Override
public String encodeAsResXmlAttr() { public String encodeAsResXmlAttr() {
return ResXmlEncoders.encodeAsResXmlAttr(mRawValue); return ResXmlEncoders.encodeAsResXmlAttr(mRawValue);
} }
@Override @Override
public String encodeAsResXmlItemValue() { public String encodeAsResXmlItemValue() {
return ResXmlEncoders.enumerateNonPositionalSubstitutions( return ResXmlEncoders
ResXmlEncoders.encodeAsXmlValue(mRawValue)); .enumerateNonPositionalSubstitutions(ResXmlEncoders
} .encodeAsXmlValue(mRawValue));
}
@Override @Override
public String encodeAsResXmlValue() { public String encodeAsResXmlValue() {
return ResXmlEncoders.encodeAsXmlValue(mRawValue); return ResXmlEncoders.encodeAsXmlValue(mRawValue);
} }
@Override @Override
protected String encodeAsResXml() throws AndrolibException { protected String encodeAsResXml() throws AndrolibException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
protected void serializeExtraXmlAttrs(XmlSerializer serializer, protected void serializeExtraXmlAttrs(XmlSerializer serializer,
ResResource res) throws IOException { ResResource res) throws IOException {
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(mRawValue)) { if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(mRawValue)) {
serializer.attribute(null, "formatted", "false"); serializer.attribute(null, "formatted", "false");
} }
} }
} }

View File

@ -27,53 +27,54 @@ import org.xmlpull.v1.XmlSerializer;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializable { public class ResStyleValue extends ResBagValue implements
ResStyleValue(ResReferenceValue parent, ResValuesXmlSerializable {
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory) { ResStyleValue(ResReferenceValue parent,
super(parent); Duo<Integer, ResScalarValue>[] items, ResValueFactory factory) {
super(parent);
mItems = new Duo[items.length]; mItems = new Duo[items.length];
for (int i = 0; i < items.length; i++) { for (int i = 0; i < items.length; i++) {
mItems[i] = new Duo<ResReferenceValue, ResScalarValue>( mItems[i] = new Duo<ResReferenceValue, ResScalarValue>(
factory.newReference(items[i].m1, null), items[i].m2); factory.newReference(items[i].m1, null), items[i].m2);
} }
} }
@Override @Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) public void serializeToResValuesXml(XmlSerializer serializer,
throws IOException, AndrolibException { ResResource res) throws IOException, AndrolibException {
serializer.startTag(null, "style"); serializer.startTag(null, "style");
serializer.attribute(null, "name", res.getResSpec().getName()); serializer.attribute(null, "name", res.getResSpec().getName());
if (! mParent.isNull()) { if (!mParent.isNull()) {
serializer.attribute(null, "parent", mParent.encodeAsResXmlAttr()); serializer.attribute(null, "parent", mParent.encodeAsResXmlAttr());
} }
for (int i = 0; i < mItems.length; i++) { for (int i = 0; i < mItems.length; i++) {
ResResSpec spec = mItems[i].m1.getReferent(); ResResSpec spec = mItems[i].m1.getReferent();
// hacky-fix remove bad ReferenceVars
if (spec.getDefaultResource().getValue().toString().contains("ResReferenceValue@")) {
continue;
}
ResAttr attr = (ResAttr) spec.getDefaultResource().getValue();
String value = attr.convertToResXmlFormat(mItems[i].m2);
if (value == null) { // hacky-fix remove bad ReferenceVars
value = mItems[i].m2.encodeAsResXmlValue(); if (spec.getDefaultResource().getValue().toString()
} .contains("ResReferenceValue@")) {
continue;
}
ResAttr attr = (ResAttr) spec.getDefaultResource().getValue();
String value = attr.convertToResXmlFormat(mItems[i].m2);
if (value == null) { if (value == null) {
continue; value = mItems[i].m2.encodeAsResXmlValue();
} }
serializer.startTag(null, "item"); if (value == null) {
serializer.attribute(null, "name", continue;
spec.getFullName(res.getResSpec().getPackage(), true)); }
serializer.text(value);
serializer.endTag(null, "item");
}
serializer.endTag(null, "style");
}
serializer.startTag(null, "item");
serializer.attribute(null, "name",
spec.getFullName(res.getResSpec().getPackage(), true));
serializer.text(value);
serializer.endTag(null, "item");
}
serializer.endTag(null, "style");
}
private final Duo<ResReferenceValue, ResScalarValue>[] mItems; private final Duo<ResReferenceValue, ResScalarValue>[] mItems;
} }

View File

@ -25,77 +25,77 @@ import brut.util.Duo;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResValueFactory { public class ResValueFactory {
private final ResPackage mPackage; private final ResPackage mPackage;
public ResValueFactory(ResPackage pakage_) { public ResValueFactory(ResPackage pakage_) {
this.mPackage = pakage_; this.mPackage = pakage_;
} }
public ResScalarValue factory(int type, int value, String rawValue) public ResScalarValue factory(int type, int value, String rawValue)
throws AndrolibException { throws AndrolibException {
switch (type) { switch (type) {
case TypedValue.TYPE_REFERENCE: case TypedValue.TYPE_REFERENCE:
return newReference(value, rawValue); return newReference(value, rawValue);
case TypedValue.TYPE_ATTRIBUTE: case TypedValue.TYPE_ATTRIBUTE:
return newReference(value, rawValue, true); return newReference(value, rawValue, true);
case TypedValue.TYPE_STRING: case TypedValue.TYPE_STRING:
return new ResStringValue(rawValue); return new ResStringValue(rawValue);
case TypedValue.TYPE_FLOAT: case TypedValue.TYPE_FLOAT:
return new ResFloatValue(Float.intBitsToFloat(value), rawValue); return new ResFloatValue(Float.intBitsToFloat(value), rawValue);
case TypedValue.TYPE_DIMENSION: case TypedValue.TYPE_DIMENSION:
return new ResDimenValue(value, rawValue); return new ResDimenValue(value, rawValue);
case TypedValue.TYPE_FRACTION: case TypedValue.TYPE_FRACTION:
return new ResFractionValue(value, rawValue); return new ResFractionValue(value, rawValue);
case TypedValue.TYPE_INT_BOOLEAN: case TypedValue.TYPE_INT_BOOLEAN:
return new ResBoolValue(value != 0, rawValue); return new ResBoolValue(value != 0, rawValue);
} }
if (type >= TypedValue.TYPE_FIRST_COLOR_INT if (type >= TypedValue.TYPE_FIRST_COLOR_INT
&& type <= TypedValue.TYPE_LAST_COLOR_INT) { && type <= TypedValue.TYPE_LAST_COLOR_INT) {
return new ResColorValue(value, rawValue); return new ResColorValue(value, rawValue);
} }
if (type >= TypedValue.TYPE_FIRST_INT if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) { && type <= TypedValue.TYPE_LAST_INT) {
return new ResIntValue(value, rawValue, type); return new ResIntValue(value, rawValue, type);
} }
throw new AndrolibException("Invalid value type: "+ type); throw new AndrolibException("Invalid value type: " + type);
} }
public ResValue factory(String value) { public ResValue factory(String value) {
if (value.startsWith("res/")) { if (value.startsWith("res/")) {
return new ResFileValue(value); return new ResFileValue(value);
} }
return new ResStringValue(value); return new ResStringValue(value);
} }
public ResBagValue bagFactory(int parent, public ResBagValue bagFactory(int parent,
Duo<Integer, ResScalarValue>[] items) throws AndrolibException { Duo<Integer, ResScalarValue>[] items) throws AndrolibException {
ResReferenceValue parentVal = newReference(parent, null); ResReferenceValue parentVal = newReference(parent, null);
if (items.length == 0) { if (items.length == 0) {
return new ResBagValue(parentVal); return new ResBagValue(parentVal);
} }
int key = items[0].m1; int key = items[0].m1;
if (key == ResAttr.BAG_KEY_ATTR_TYPE) { if (key == ResAttr.BAG_KEY_ATTR_TYPE) {
return ResAttr.factory(parentVal, items, this, mPackage); return ResAttr.factory(parentVal, items, this, mPackage);
} }
if (key == ResArrayValue.BAG_KEY_ARRAY_START) { if (key == ResArrayValue.BAG_KEY_ARRAY_START) {
return new ResArrayValue(parentVal, items); return new ResArrayValue(parentVal, items);
} }
if (key >= ResPluralsValue.BAG_KEY_PLURALS_START if (key >= ResPluralsValue.BAG_KEY_PLURALS_START
&& key <= ResPluralsValue.BAG_KEY_PLURALS_END) { && key <= ResPluralsValue.BAG_KEY_PLURALS_END) {
return new ResPluralsValue(parentVal, items); return new ResPluralsValue(parentVal, items);
} }
return new ResStyleValue(parentVal, items, this); return new ResStyleValue(parentVal, items, this);
} }
public ResReferenceValue newReference(int resID, String rawValue) { public ResReferenceValue newReference(int resID, String rawValue) {
return newReference(resID, rawValue, false); return newReference(resID, rawValue, false);
} }
public ResReferenceValue newReference(int resID, String rawValue, public ResReferenceValue newReference(int resID, String rawValue,
boolean theme) { boolean theme) {
return new ResReferenceValue(mPackage, resID, rawValue, theme); return new ResReferenceValue(mPackage, resID, rawValue, theme);
} }
} }

View File

@ -34,412 +34,406 @@ import org.apache.commons.io.input.CountingInputStream;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ARSCDecoder { public class ARSCDecoder {
public static ARSCData decode(InputStream arscStream, public static ARSCData decode(InputStream arscStream,
boolean findFlagsOffsets, boolean keepBroken) boolean findFlagsOffsets, boolean keepBroken)
throws AndrolibException { throws AndrolibException {
return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable()); return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable());
} }
public static ARSCData decode(InputStream arscStream, public static ARSCData decode(InputStream arscStream,
boolean findFlagsOffsets, boolean keepBroken, ResTable resTable) boolean findFlagsOffsets, boolean keepBroken, ResTable resTable)
throws AndrolibException { throws AndrolibException {
try { try {
ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable, ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable,
findFlagsOffsets, keepBroken); findFlagsOffsets, keepBroken);
ResPackage[] pkgs = decoder.readTable(); ResPackage[] pkgs = decoder.readTable();
return new ARSCData( return new ARSCData(pkgs, decoder.mFlagsOffsets == null ? null
pkgs, : decoder.mFlagsOffsets.toArray(new FlagsOffset[0]),
decoder.mFlagsOffsets == null ? null : resTable);
decoder.mFlagsOffsets.toArray(new FlagsOffset[0]), } catch (IOException ex) {
resTable); throw new AndrolibException("Could not decode arsc file", ex);
} catch (IOException ex) { }
throw new AndrolibException("Could not decode arsc file", ex); }
}
} private ARSCDecoder(InputStream arscStream, ResTable resTable,
boolean storeFlagsOffsets, boolean keepBroken) {
private ARSCDecoder(InputStream arscStream, ResTable resTable, if (storeFlagsOffsets) {
boolean storeFlagsOffsets, boolean keepBroken) { arscStream = mCountIn = new CountingInputStream(arscStream);
if (storeFlagsOffsets) { mFlagsOffsets = new ArrayList<FlagsOffset>();
arscStream = mCountIn = new CountingInputStream(arscStream); } else {
mFlagsOffsets = new ArrayList<FlagsOffset>(); mCountIn = null;
} else { mFlagsOffsets = null;
mCountIn = null; }
mFlagsOffsets = null; mIn = new ExtDataInput(new LEDataInputStream(arscStream));
} mResTable = resTable;
mIn = new ExtDataInput(new LEDataInputStream(arscStream)); mKeepBroken = keepBroken;
mResTable = resTable; }
mKeepBroken = keepBroken;
} private ResPackage[] readTable() throws IOException, AndrolibException {
nextChunkCheckType(Header.TYPE_TABLE);
private ResPackage[] readTable() throws IOException, AndrolibException { int packageCount = mIn.readInt();
nextChunkCheckType(Header.TYPE_TABLE);
int packageCount = mIn.readInt(); mTableStrings = StringBlock.read(mIn);
ResPackage[] packages = new ResPackage[packageCount];
mTableStrings = StringBlock.read(mIn);
ResPackage[] packages = new ResPackage[packageCount]; nextChunk();
for (int i = 0; i < packageCount; i++) {
nextChunk(); packages[i] = readPackage();
for (int i = 0; i < packageCount; i++) { }
packages[i] = readPackage();
} // store package
if (this.mResTable.isPackageInfoValueSet("cur_package") != true) {
// store package this.mResTable.addPackageInfo("cur_package", packages[0].getName());
if (this.mResTable.isPackageInfoValueSet("cur_package") != true) { }
this.mResTable.addPackageInfo("cur_package", packages[0].getName()); return packages;
} }
return packages;
} private ResPackage readPackage() throws IOException, AndrolibException {
checkChunkType(Header.TYPE_PACKAGE);
private ResPackage readPackage() throws IOException, AndrolibException { int id = (byte) mIn.readInt();
checkChunkType(Header.TYPE_PACKAGE); String name = mIn.readNulEndedString(128, true);
int id = (byte) mIn.readInt(); /* typeNameStrings */mIn.skipInt();
String name = mIn.readNulEndedString(128, true); /* typeNameCount */mIn.skipInt();
/*typeNameStrings*/ mIn.skipInt(); /* specNameStrings */mIn.skipInt();
/*typeNameCount*/ mIn.skipInt(); /* specNameCount */mIn.skipInt();
/*specNameStrings*/ mIn.skipInt();
/*specNameCount*/ mIn.skipInt(); mTypeNames = StringBlock.read(mIn);
mSpecNames = StringBlock.read(mIn);
mTypeNames = StringBlock.read(mIn);
mSpecNames = StringBlock.read(mIn); mResId = id << 24;
mPkg = new ResPackage(mResTable, id, name);
mResId = id << 24;
mPkg = new ResPackage(mResTable, id, name); nextChunk();
while (mHeader.type == Header.TYPE_TYPE) {
nextChunk(); readType();
while (mHeader.type == Header.TYPE_TYPE) { }
readType();
} return mPkg;
}
return mPkg;
} private ResType readType() throws AndrolibException, IOException {
checkChunkType(Header.TYPE_TYPE);
private ResType readType() throws AndrolibException, IOException { byte id = mIn.readByte();
checkChunkType(Header.TYPE_TYPE); mIn.skipBytes(3);
byte id = mIn.readByte(); int entryCount = mIn.readInt();
mIn.skipBytes(3);
int entryCount = mIn.readInt(); mMissingResSpecs = new boolean[entryCount];
Arrays.fill(mMissingResSpecs, true);
mMissingResSpecs = new boolean[entryCount];
Arrays.fill(mMissingResSpecs, true); if (mFlagsOffsets != null) {
mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount));
if (mFlagsOffsets != null) { }
mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount)); /* flags */mIn.skipBytes(entryCount * 4);
}
/*flags*/ mIn.skipBytes(entryCount * 4); mResId = (0xff000000 & mResId) | id << 16;
mType = new ResType(mTypeNames.getString(id - 1), mResTable, mPkg);
mResId = (0xff000000 & mResId) | id << 16; mPkg.addType(mType);
mType = new ResType(mTypeNames.getString(id - 1), mResTable, mPkg);
mPkg.addType(mType); while (nextChunk().type == Header.TYPE_CONFIG) {
readConfig();
while (nextChunk().type == Header.TYPE_CONFIG) { }
readConfig();
} addMissingResSpecs();
addMissingResSpecs(); return mType;
}
return mType;
} private ResConfig readConfig() throws IOException, AndrolibException {
checkChunkType(Header.TYPE_CONFIG);
private ResConfig readConfig() throws IOException, AndrolibException { /* typeId */mIn.skipInt();
checkChunkType(Header.TYPE_CONFIG); int entryCount = mIn.readInt();
/*typeId*/ mIn.skipInt(); /* entriesStart */mIn.skipInt();
int entryCount = mIn.readInt();
/*entriesStart*/ mIn.skipInt(); ResConfigFlags flags = readConfigFlags();
int[] entryOffsets = mIn.readIntArray(entryCount);
ResConfigFlags flags = readConfigFlags();
int[] entryOffsets = mIn.readIntArray(entryCount); if (flags.isInvalid) {
String resName = mType.getName() + flags.getQualifiers();
if (flags.isInvalid) { if (mKeepBroken) {
String resName = mType.getName() + flags.getQualifiers(); LOGGER.warning("Invalid config flags detected: " + resName);
if (mKeepBroken) { } else {
LOGGER.warning( LOGGER.warning("Invalid config flags detected. Dropping resources: "
"Invalid config flags detected: " + resName); + resName);
} else { }
LOGGER.warning( }
"Invalid config flags detected. Dropping resources: " + resName);
} mConfig = flags.isInvalid && !mKeepBroken ? null : mPkg
} .getOrCreateConfig(flags);
mConfig = flags.isInvalid && ! mKeepBroken ? for (int i = 0; i < entryOffsets.length; i++) {
null : mPkg.getOrCreateConfig(flags); if (entryOffsets[i] != -1) {
mMissingResSpecs[i] = false;
for (int i = 0; i < entryOffsets.length; i++) { mResId = (mResId & 0xffff0000) | i;
if (entryOffsets[i] != -1) { readEntry();
mMissingResSpecs[i] = false; }
mResId = (mResId & 0xffff0000) | i; }
readEntry();
} return mConfig;
} }
return mConfig; private void readEntry() throws IOException, AndrolibException {
} /* size */mIn.skipBytes(2);
short flags = mIn.readShort();
private void readEntry() throws IOException, AndrolibException { int specNamesId = mIn.readInt();
/*size*/ mIn.skipBytes(2);
short flags = mIn.readShort(); ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? readValue()
int specNamesId = mIn.readInt(); : readComplexEntry();
ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? if (mConfig == null) {
readValue() : readComplexEntry(); return;
}
if (mConfig == null) {
return; ResID resId = new ResID(mResId);
} ResResSpec spec;
if (mPkg.hasResSpec(resId)) {
ResID resId = new ResID(mResId); spec = mPkg.getResSpec(resId);
ResResSpec spec; } else {
if (mPkg.hasResSpec(resId)) { spec = new ResResSpec(resId, mSpecNames.getString(specNamesId),
spec = mPkg.getResSpec(resId); mPkg, mType);
} else { mPkg.addResSpec(spec);
spec = new ResResSpec( mType.addResSpec(spec);
resId, mSpecNames.getString(specNamesId), mPkg, mType); }
mPkg.addResSpec(spec); ResResource res = new ResResource(mConfig, spec, value);
mType.addResSpec(spec);
} mConfig.addResource(res);
ResResource res = new ResResource(mConfig, spec, value); spec.addResource(res);
mPkg.addResource(res);
mConfig.addResource(res); }
spec.addResource(res);
mPkg.addResource(res); private ResBagValue readComplexEntry() throws IOException,
} AndrolibException {
int parent = mIn.readInt();
private ResBagValue readComplexEntry() throws IOException, int count = mIn.readInt();
AndrolibException {
int parent = mIn.readInt(); ResValueFactory factory = mPkg.getValueFactory();
int count = mIn.readInt(); Duo<Integer, ResScalarValue>[] items = new Duo[count];
for (int i = 0; i < count; i++) {
ResValueFactory factory = mPkg.getValueFactory(); items[i] = new Duo<Integer, ResScalarValue>(mIn.readInt(),
Duo<Integer, ResScalarValue>[] items = new Duo[count]; (ResScalarValue) readValue());
for (int i = 0; i < count; i++) { }
items[i] = new Duo<Integer, ResScalarValue>(
mIn.readInt(), (ResScalarValue) readValue()); return factory.bagFactory(parent, items);
} }
return factory.bagFactory(parent, items); private ResValue readValue() throws IOException, AndrolibException {
} /* size */mIn.skipCheckShort((short) 8);
/* zero */mIn.skipCheckByte((byte) 0);
private ResValue readValue() throws IOException, AndrolibException { byte type = mIn.readByte();
/*size*/ mIn.skipCheckShort((short) 8); int data = mIn.readInt();
/*zero*/ mIn.skipCheckByte((byte) 0);
byte type = mIn.readByte(); return type == TypedValue.TYPE_STRING ? mPkg.getValueFactory().factory(
int data = mIn.readInt(); mTableStrings.getHTML(data)) : mPkg.getValueFactory().factory(
type, data, null);
return type == TypedValue.TYPE_STRING ? }
mPkg.getValueFactory().factory(mTableStrings.getHTML(data)) :
mPkg.getValueFactory().factory(type, data, null); private ResConfigFlags readConfigFlags() throws IOException,
} AndrolibException {
int size = mIn.readInt();
private ResConfigFlags readConfigFlags() throws IOException, AndrolibException { if (size < 28) {
int size = mIn.readInt(); throw new AndrolibException("Config size < 28");
if (size < 28) { }
throw new AndrolibException("Config size < 28");
} boolean isInvalid = false;
boolean isInvalid = false; short mcc = mIn.readShort();
short mnc = mIn.readShort();
short mcc = mIn.readShort();
short mnc = mIn.readShort(); char[] language = new char[] { (char) mIn.readByte(),
(char) mIn.readByte() };
char[] language = new char[]{ char[] country = new char[] { (char) mIn.readByte(),
(char) mIn.readByte(), (char) mIn.readByte()}; (char) mIn.readByte() };
char[] country = new char[]{
(char) mIn.readByte(), (char) mIn.readByte()}; byte orientation = mIn.readByte();
byte touchscreen = mIn.readByte();
byte orientation = mIn.readByte(); short density = mIn.readShort();
byte touchscreen = mIn.readByte();
short density = mIn.readShort(); byte keyboard = mIn.readByte();
byte navigation = mIn.readByte();
byte keyboard = mIn.readByte(); byte inputFlags = mIn.readByte();
byte navigation = mIn.readByte(); /* inputPad0 */mIn.skipBytes(1);
byte inputFlags = mIn.readByte();
/*inputPad0*/ mIn.skipBytes(1); short screenWidth = mIn.readShort();
short screenHeight = mIn.readShort();
short screenWidth = mIn.readShort();
short screenHeight = mIn.readShort(); short sdkVersion = mIn.readShort();
/* minorVersion, now must always be 0 */mIn.skipBytes(2);
short sdkVersion = mIn.readShort();
/*minorVersion, now must always be 0*/ mIn.skipBytes(2); byte screenLayout = 0;
byte uiMode = 0;
byte screenLayout = 0; short smallestScreenWidthDp = 0;
byte uiMode = 0; if (size >= 32) {
short smallestScreenWidthDp = 0; screenLayout = mIn.readByte();
if (size >= 32) { uiMode = mIn.readByte();
screenLayout = mIn.readByte(); smallestScreenWidthDp = mIn.readShort();
uiMode = mIn.readByte(); }
smallestScreenWidthDp = mIn.readShort();
} short screenWidthDp = 0;
short screenHeightDp = 0;
short screenWidthDp = 0;
short screenHeightDp = 0; if (size >= 36) {
screenWidthDp = mIn.readShort();
if (size >= 36) { screenHeightDp = mIn.readShort();
screenWidthDp = mIn.readShort(); }
screenHeightDp = mIn.readShort();
} short layoutDirection = 0;
if (size >= 38 && sdkVersion >= 17
short layoutDirection = 0; && !this.mPkg.getName().equalsIgnoreCase("com.htc")) {
if (size >= 38 && sdkVersion >= 17 && !this.mPkg.getName().equalsIgnoreCase("com.htc")) { layoutDirection = mIn.readShort();
layoutDirection = mIn.readShort(); }
}
int exceedingSize = size - KNOWN_CONFIG_BYTES;
int exceedingSize = size - KNOWN_CONFIG_BYTES; if (exceedingSize > 0) {
if (exceedingSize > 0) { byte[] buf = new byte[exceedingSize];
byte[] buf = new byte[exceedingSize]; mIn.readFully(buf);
mIn.readFully(buf); BigInteger exceedingBI = new BigInteger(1, buf);
BigInteger exceedingBI = new BigInteger(1, buf);
if (exceedingBI.equals(BigInteger.ZERO)) {
if (exceedingBI.equals(BigInteger.ZERO)) { LOGGER.fine(String
LOGGER.fine(String.format( .format("Config flags size > %d, but exceeding bytes are all zero, so it should be ok.",
"Config flags size > %d, but exceeding bytes are all zero, so it should be ok.", KNOWN_CONFIG_BYTES));
KNOWN_CONFIG_BYTES)); } else {
} else { LOGGER.warning(String.format(
LOGGER.warning(String.format( "Config flags size > %d. Exceeding bytes: 0x%X.",
"Config flags size > %d. Exceeding bytes: 0x%X.", KNOWN_CONFIG_BYTES, exceedingBI));
KNOWN_CONFIG_BYTES, exceedingBI)); isInvalid = true;
isInvalid = true; }
} }
}
return new ResConfigFlags(mcc, mnc, language, country, layoutDirection,
return new ResConfigFlags(mcc, mnc, language, country, layoutDirection, orientation, orientation, touchscreen, density, keyboard, navigation,
touchscreen, density, keyboard, navigation, inputFlags, inputFlags, screenWidth, screenHeight, sdkVersion,
screenWidth, screenHeight, sdkVersion, screenLayout, uiMode, screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,
smallestScreenWidthDp, screenWidthDp, screenHeightDp, isInvalid); screenHeightDp, isInvalid);
} }
private void addMissingResSpecs() throws AndrolibException { private void addMissingResSpecs() throws AndrolibException {
int resId = mResId & 0xffff0000; int resId = mResId & 0xffff0000;
for (int i = 0; i < mMissingResSpecs.length; i++) { for (int i = 0; i < mMissingResSpecs.length; i++) {
if (! mMissingResSpecs[i]) { if (!mMissingResSpecs[i]) {
continue; continue;
} }
ResResSpec spec = new ResResSpec(new ResID(resId | i), ResResSpec spec = new ResResSpec(new ResID(resId | i),
String.format("APKTOOL_DUMMY_%04x", i), mPkg, mType); String.format("APKTOOL_DUMMY_%04x", i), mPkg, mType);
mPkg.addResSpec(spec); mPkg.addResSpec(spec);
mType.addResSpec(spec); mType.addResSpec(spec);
ResValue value = new ResBoolValue(false, null); ResValue value = new ResBoolValue(false, null);
ResResource res = new ResResource( ResResource res = new ResResource(
mPkg.getOrCreateConfig(new ResConfigFlags()), spec, value); mPkg.getOrCreateConfig(new ResConfigFlags()), spec, value);
mPkg.addResource(res); mPkg.addResource(res);
mConfig.addResource(res); mConfig.addResource(res);
spec.addResource(res); spec.addResource(res);
} }
} }
private Header nextChunk() throws IOException { private Header nextChunk() throws IOException {
return mHeader = Header.read(mIn); return mHeader = Header.read(mIn);
} }
private void checkChunkType(int expectedType) throws AndrolibException { private void checkChunkType(int expectedType) throws AndrolibException {
if (mHeader.type != expectedType) { if (mHeader.type != expectedType) {
throw new AndrolibException(String.format( throw new AndrolibException(String.format(
"Invalid chunk type: expected=0x%08x, got=0x%08x", "Invalid chunk type: expected=0x%08x, got=0x%08x",
expectedType, mHeader.type)); expectedType, mHeader.type));
} }
} }
private void nextChunkCheckType(int expectedType) private void nextChunkCheckType(int expectedType) throws IOException,
throws IOException, AndrolibException { AndrolibException {
nextChunk(); nextChunk();
checkChunkType(expectedType); checkChunkType(expectedType);
} }
private final ExtDataInput mIn; private final ExtDataInput mIn;
private final ResTable mResTable; private final ResTable mResTable;
private final CountingInputStream mCountIn; private final CountingInputStream mCountIn;
private final List<FlagsOffset> mFlagsOffsets; private final List<FlagsOffset> mFlagsOffsets;
private final boolean mKeepBroken; private final boolean mKeepBroken;
private Header mHeader; private Header mHeader;
private StringBlock mTableStrings; private StringBlock mTableStrings;
private StringBlock mTypeNames; private StringBlock mTypeNames;
private StringBlock mSpecNames; private StringBlock mSpecNames;
private ResPackage mPkg; private ResPackage mPkg;
private ResType mType; private ResType mType;
private ResConfig mConfig; private ResConfig mConfig;
private int mResId; private int mResId;
private boolean[] mMissingResSpecs; private boolean[] mMissingResSpecs;
private final static short ENTRY_FLAG_COMPLEX = 0x0001;
private final static short ENTRY_FLAG_COMPLEX = 0x0001;
public static class Header {
public final short type;
public static class Header { public final int chunkSize;
public final short type;
public final int chunkSize; public Header(short type, int size) {
this.type = type;
public Header(short type, int size) { this.chunkSize = size;
this.type = type; }
this.chunkSize = size;
} public static Header read(ExtDataInput in) throws IOException {
short type;
public static Header read(ExtDataInput in) throws IOException { try {
short type; type = in.readShort();
try { } catch (EOFException ex) {
type = in.readShort(); return new Header(TYPE_NONE, 0);
} catch (EOFException ex) { }
return new Header(TYPE_NONE, 0); in.skipBytes(2);
} return new Header(type, in.readInt());
in.skipBytes(2); }
return new Header(type, in.readInt());
} public final static short TYPE_NONE = -1, TYPE_TABLE = 0x0002,
TYPE_PACKAGE = 0x0200, TYPE_TYPE = 0x0202,
public final static short TYPE_CONFIG = 0x0201;
TYPE_NONE = -1, }
TYPE_TABLE = 0x0002,
TYPE_PACKAGE = 0x0200, public static class FlagsOffset {
TYPE_TYPE = 0x0202, public final int offset;
TYPE_CONFIG = 0x0201; public final int count;
}
public FlagsOffset(int offset, int count) {
public static class FlagsOffset { this.offset = offset;
public final int offset; this.count = count;
public final int count; }
}
public FlagsOffset(int offset, int count) {
this.offset = offset; private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class
this.count = count; .getName());
} private static final int KNOWN_CONFIG_BYTES = 36;
}
public static class ARSCData {
private static final Logger LOGGER =
Logger.getLogger(ARSCDecoder.class.getName()); public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets,
private static final int KNOWN_CONFIG_BYTES = 36; ResTable resTable) {
mPackages = packages;
mFlagsOffsets = flagsOffsets;
public static class ARSCData { mResTable = resTable;
}
public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets,
ResTable resTable) { public FlagsOffset[] getFlagsOffsets() {
mPackages = packages; return mFlagsOffsets;
mFlagsOffsets = flagsOffsets; }
mResTable = resTable;
} public ResPackage[] getPackages() {
return mPackages;
public FlagsOffset[] getFlagsOffsets() { }
return mFlagsOffsets;
} public ResPackage getOnePackage() throws AndrolibException {
if (mPackages.length != 1) {
public ResPackage[] getPackages() { throw new AndrolibException(
return mPackages; "Arsc file contains zero or multiple packages");
} }
return mPackages[0];
public ResPackage getOnePackage() throws AndrolibException { }
if (mPackages.length != 1) {
throw new AndrolibException( public ResTable getResTable() {
"Arsc file contains zero or multiple packages"); return mResTable;
} }
return mPackages[0];
} private final ResPackage[] mPackages;
private final FlagsOffset[] mFlagsOffsets;
public ResTable getResTable() { private final ResTable mResTable;
return mResTable; }
}
private final ResPackage[] mPackages;
private final FlagsOffset[] mFlagsOffsets;
private final ResTable mResTable;
}
} }

View File

@ -28,112 +28,112 @@ import org.apache.commons.io.IOUtils;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class Res9patchStreamDecoder implements ResStreamDecoder { public class Res9patchStreamDecoder implements ResStreamDecoder {
public void decode(InputStream in, OutputStream out) @Override
throws AndrolibException { public void decode(InputStream in, OutputStream out)
try { throws AndrolibException {
byte[] data = IOUtils.toByteArray(in); try {
byte[] data = IOUtils.toByteArray(in);
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
int w = im.getWidth(), h = im.getHeight(); int w = im.getWidth(), h = im.getHeight();
BufferedImage im2 = new BufferedImage( BufferedImage im2 = new BufferedImage(w + 2, h + 2,
w + 2, h + 2, BufferedImage.TYPE_4BYTE_ABGR); BufferedImage.TYPE_4BYTE_ABGR);
if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) { if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) {
im2.getRaster().setRect(1, 1, im.getRaster()); im2.getRaster().setRect(1, 1, im.getRaster());
} else { } else {
im2.getGraphics().drawImage(im, 1, 1, null); im2.getGraphics().drawImage(im, 1, 1, null);
} }
NinePatch np = getNinePatch(data); NinePatch np = getNinePatch(data);
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight); drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom); drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
int[] xDivs = np.xDivs; int[] xDivs = np.xDivs;
for (int i = 0; i < xDivs.length; i += 2) { for (int i = 0; i < xDivs.length; i += 2) {
drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]); drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);
} }
int[] yDivs = np.yDivs; int[] yDivs = np.yDivs;
for (int i = 0; i < yDivs.length; i += 2) { for (int i = 0; i < yDivs.length; i += 2) {
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]); drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
} }
ImageIO.write(im2, "png", out); ImageIO.write(im2, "png", out);
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
private NinePatch getNinePatch(byte[] data) private NinePatch getNinePatch(byte[] data) throws AndrolibException,
throws AndrolibException, IOException { IOException {
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
find9patchChunk(di); find9patchChunk(di);
return NinePatch.decode(di); return NinePatch.decode(di);
} }
private void find9patchChunk(DataInput di) private void find9patchChunk(DataInput di) throws AndrolibException,
throws AndrolibException, IOException { IOException {
di.skipBytes(8); di.skipBytes(8);
while (true) { while (true) {
int size; int size;
try { try {
size = di.readInt(); size = di.readInt();
} catch (IOException ex) { } catch (IOException ex) {
throw new CantFind9PatchChunk("Cant find nine patch chunk", ex); throw new CantFind9PatchChunk("Cant find nine patch chunk", ex);
} }
if (di.readInt() == NP_CHUNK_TYPE) { if (di.readInt() == NP_CHUNK_TYPE) {
return; return;
} }
di.skipBytes(size + 4); di.skipBytes(size + 4);
} }
} }
private void drawHLine(BufferedImage im, int y, int x1, int x2) { private void drawHLine(BufferedImage im, int y, int x1, int x2) {
for (int x = x1; x <= x2; x++) { for (int x = x1; x <= x2; x++) {
im.setRGB(x, y, NP_COLOR); im.setRGB(x, y, NP_COLOR);
} }
} }
private void drawVLine(BufferedImage im, int x, int y1, int y2) { private void drawVLine(BufferedImage im, int x, int y1, int y2) {
for (int y = y1; y <= y2; y++) { for (int y = y1; y <= y2; y++) {
im.setRGB(x, y, NP_COLOR); im.setRGB(x, y, NP_COLOR);
} }
} }
private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc
private static final int NP_COLOR = 0xff000000; private static final int NP_COLOR = 0xff000000;
private static class NinePatch {
public final int padLeft, padRight, padTop, padBottom;
public final int[] xDivs, yDivs;
private static class NinePatch { public NinePatch(int padLeft, int padRight, int padTop, int padBottom,
public final int padLeft, padRight, padTop, padBottom; int[] xDivs, int[] yDivs) {
public final int[] xDivs, yDivs; this.padLeft = padLeft;
this.padRight = padRight;
this.padTop = padTop;
this.padBottom = padBottom;
this.xDivs = xDivs;
this.yDivs = yDivs;
}
public NinePatch(int padLeft, int padRight, int padTop, int padBottom, public static NinePatch decode(ExtDataInput di) throws IOException {
int[] xDivs, int[] yDivs) { di.skipBytes(1);
this.padLeft = padLeft; byte numXDivs = di.readByte();
this.padRight = padRight; byte numYDivs = di.readByte();
this.padTop = padTop; di.skipBytes(1);
this.padBottom = padBottom; di.skipBytes(8);
this.xDivs = xDivs; int padLeft = di.readInt();
this.yDivs = yDivs; int padRight = di.readInt();
} int padTop = di.readInt();
int padBottom = di.readInt();
di.skipBytes(4);
int[] xDivs = di.readIntArray(numXDivs);
int[] yDivs = di.readIntArray(numYDivs);
public static NinePatch decode(ExtDataInput di) throws IOException { return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs,
di.skipBytes(1); yDivs);
byte numXDivs = di.readByte(); }
byte numYDivs = di.readByte(); }
di.skipBytes(1);
di.skipBytes(8);
int padLeft = di.readInt();
int padRight = di.readInt();
int padTop = di.readInt();
int padBottom = di.readInt();
di.skipBytes(4);
int[] xDivs = di.readIntArray(numXDivs);
int[] yDivs = di.readIntArray(numYDivs);
return new NinePatch(padLeft, padRight, padTop, padBottom,
xDivs, yDivs);
}
}
} }

View File

@ -25,31 +25,31 @@ import brut.androlib.res.data.value.ResScalarValue;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResAttrDecoder { public class ResAttrDecoder {
public String decode(int type, int value, String rawValue, int attrResId) public String decode(int type, int value, String rawValue, int attrResId)
throws AndrolibException { throws AndrolibException {
ResScalarValue resValue = mCurrentPackage.getValueFactory() ResScalarValue resValue = mCurrentPackage.getValueFactory().factory(
.factory(type, value, rawValue); type, value, rawValue);
String decoded = null; String decoded = null;
if (attrResId != 0) { if (attrResId != 0) {
ResAttr attr = (ResAttr) getCurrentPackage().getResTable() ResAttr attr = (ResAttr) getCurrentPackage().getResTable()
.getResSpec(attrResId).getDefaultResource().getValue(); .getResSpec(attrResId).getDefaultResource().getValue();
decoded = attr.convertToResXmlFormat(resValue); decoded = attr.convertToResXmlFormat(resValue);
} }
return decoded != null ? decoded : resValue.encodeAsResXmlAttr(); return decoded != null ? decoded : resValue.encodeAsResXmlAttr();
} }
public ResPackage getCurrentPackage() throws AndrolibException { public ResPackage getCurrentPackage() throws AndrolibException {
if (mCurrentPackage == null) { if (mCurrentPackage == null) {
throw new AndrolibException("Current package not set"); throw new AndrolibException("Current package not set");
} }
return mCurrentPackage; return mCurrentPackage;
} }
public void setCurrentPackage(ResPackage currentPackage) { public void setCurrentPackage(ResPackage currentPackage) {
mCurrentPackage = currentPackage; mCurrentPackage = currentPackage;
} }
private ResPackage mCurrentPackage; private ResPackage mCurrentPackage;
} }

View File

@ -31,120 +31,121 @@ import java.util.logging.Logger;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResFileDecoder { public class ResFileDecoder {
private final ResStreamDecoderContainer mDecoders; private final ResStreamDecoderContainer mDecoders;
public ResFileDecoder(ResStreamDecoderContainer decoders) { public ResFileDecoder(ResStreamDecoderContainer decoders) {
this.mDecoders = decoders; this.mDecoders = decoders;
} }
public void decode(ResResource res, Directory inDir, Directory outDir) public void decode(ResResource res, Directory inDir, Directory outDir)
throws AndrolibException { throws AndrolibException {
ResFileValue fileValue = (ResFileValue) res.getValue(); ResFileValue fileValue = (ResFileValue) res.getValue();
String inFileName = fileValue.getStrippedPath(); String inFileName = fileValue.getStrippedPath();
String outResName = res.getFilePath(); String outResName = res.getFilePath();
String typeName = res.getResSpec().getType().getName(); String typeName = res.getResSpec().getType().getName();
String ext = null; String ext = null;
String outFileName; String outFileName;
int extPos = inFileName.lastIndexOf("."); int extPos = inFileName.lastIndexOf(".");
if (extPos == -1) { if (extPos == -1) {
outFileName = outResName; outFileName = outResName;
} else { } else {
ext = inFileName.substring(extPos); ext = inFileName.substring(extPos);
outFileName = outResName + ext; outFileName = outResName + ext;
} }
try { try {
if (typeName.equals("raw")) { if (typeName.equals("raw")) {
decode(inDir, inFileName, outDir, outFileName, "raw"); decode(inDir, inFileName, outDir, outFileName, "raw");
return; return;
} }
if (typeName.equals("drawable") || typeName.equals("mipmap")) { if (typeName.equals("drawable") || typeName.equals("mipmap")) {
if (inFileName.toLowerCase().endsWith(".9.png")) { if (inFileName.toLowerCase().endsWith(".9.png")) {
outFileName = outResName + ".9" + ext; outFileName = outResName + ".9" + ext;
// check for htc .r.9.png
if (inFileName.toLowerCase().endsWith(".r.9.png")) {
outFileName = outResName + ".r.9" + ext;
}
try { // check for htc .r.9.png
decode( if (inFileName.toLowerCase().endsWith(".r.9.png")) {
inDir, inFileName, outDir, outFileName, "9patch"); outFileName = outResName + ".r.9" + ext;
return; }
} catch (CantFind9PatchChunk ex) {
LOGGER.log(Level.WARNING, String.format(
"Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.",
inFileName
), ex);
outDir.removeFile(outFileName);
outFileName = outResName + ext;
}
}
if (! ".xml".equals(ext)) {
decode(inDir, inFileName, outDir, outFileName, "raw");
return;
}
}
decode(inDir, inFileName, outDir, outFileName, "xml"); try {
} catch (AndrolibException ex) { decode(inDir, inFileName, outDir, outFileName, "9patch");
LOGGER.log(Level.SEVERE, String.format( return;
"Could not decode file, replacing by FALSE value: %s", } catch (CantFind9PatchChunk ex) {
inFileName, outFileName), ex); LOGGER.log(
res.replace(new ResBoolValue(false, null)); Level.WARNING,
} String.format(
} "Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.",
inFileName), ex);
outDir.removeFile(outFileName);
outFileName = outResName + ext;
}
}
if (!".xml".equals(ext)) {
decode(inDir, inFileName, outDir, outFileName, "raw");
return;
}
}
public void decode(Directory inDir, String inFileName, Directory outDir, decode(inDir, inFileName, outDir, outFileName, "xml");
String outFileName, String decoder) throws AndrolibException { } catch (AndrolibException ex) {
InputStream in = null; LOGGER.log(Level.SEVERE, String.format(
OutputStream out = null; "Could not decode file, replacing by FALSE value: %s",
try { inFileName, outFileName), ex);
in = inDir.getFileInput(inFileName); res.replace(new ResBoolValue(false, null));
out = outDir.getFileOutput(outFileName); }
mDecoders.decode(in, out, decoder); }
} 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);
}
}
}
public void decodeManifest(Directory inDir, String inFileName, Directory outDir, public void decode(Directory inDir, String inFileName, Directory outDir,
String outFileName) throws AndrolibException { String outFileName, String decoder) throws AndrolibException {
InputStream in = null; InputStream in = null;
OutputStream out = null; OutputStream out = null;
try { try {
in = inDir.getFileInput(inFileName); in = inDir.getFileInput(inFileName);
out = outDir.getFileOutput(outFileName); out = outDir.getFileOutput(outFileName);
((XmlPullStreamDecoder)mDecoders.getDecoder("xml")).decodeManifest(in, out); mDecoders.decode(in, out, decoder);
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} finally { } finally {
try{ try {
if (in != null) { if (in != null) {
in.close(); in.close();
} }
if (out != null) { if (out != null) {
out.close(); out.close();
} }
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
} }
private final static Logger LOGGER = public void decodeManifest(Directory inDir, String inFileName,
Logger.getLogger(ResFileDecoder.class.getName()); Directory outDir, String outFileName) throws AndrolibException {
InputStream in = null;
OutputStream out = null;
try {
in = inDir.getFileInput(inFileName);
out = outDir.getFileOutput(outFileName);
((XmlPullStreamDecoder) mDecoders.getDecoder("xml"))
.decodeManifest(in, out);
} 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);
}
}
}
private final static Logger LOGGER = Logger.getLogger(ResFileDecoder.class
.getName());
} }

View File

@ -26,12 +26,13 @@ import org.apache.commons.io.IOUtils;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResRawStreamDecoder implements ResStreamDecoder { public class ResRawStreamDecoder implements ResStreamDecoder {
public void decode(InputStream in, OutputStream out) @Override
throws AndrolibException { public void decode(InputStream in, OutputStream out)
try { throws AndrolibException {
IOUtils.copy(in, out); try {
} catch (IOException ex) { IOUtils.copy(in, out);
throw new AndrolibException("Could not decode raw stream", ex); } catch (IOException ex) {
} throw new AndrolibException("Could not decode raw stream", ex);
} }
}
} }

View File

@ -24,6 +24,6 @@ import java.io.OutputStream;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public interface ResStreamDecoder { public interface ResStreamDecoder {
public void decode(InputStream in, OutputStream out) public void decode(InputStream in, OutputStream out)
throws AndrolibException; throws AndrolibException;
} }

View File

@ -26,23 +26,22 @@ import java.util.Map;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResStreamDecoderContainer { public class ResStreamDecoderContainer {
private final Map<String, ResStreamDecoder> mDecoders = private final Map<String, ResStreamDecoder> mDecoders = new HashMap<String, ResStreamDecoder>();
new HashMap<String, ResStreamDecoder>();
public void decode(InputStream in, OutputStream out, String decoderName) public void decode(InputStream in, OutputStream out, String decoderName)
throws AndrolibException { throws AndrolibException {
getDecoder(decoderName).decode(in, out); getDecoder(decoderName).decode(in, out);
} }
public ResStreamDecoder getDecoder(String name) throws AndrolibException { public ResStreamDecoder getDecoder(String name) throws AndrolibException {
ResStreamDecoder decoder = mDecoders.get(name); ResStreamDecoder decoder = mDecoders.get(name);
if (decoder == null) { if (decoder == null) {
throw new AndrolibException("Undefined decoder: " + name); throw new AndrolibException("Undefined decoder: " + name);
} }
return decoder; return decoder;
} }
public void setDecoder(String name, ResStreamDecoder decoder) { public void setDecoder(String name, ResStreamDecoder decoder) {
mDecoders.put(name, decoder); mDecoders.put(name, decoder);
} }
} }

View File

@ -28,320 +28,313 @@ import java.util.logging.Logger;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
* @author Dmitry Skiba * @author Dmitry Skiba
* *
* Block of strings, used in binary xml and arsc. * Block of strings, used in binary xml and arsc.
*
* TODO: - implement get()
* *
* TODO:
* - implement get()
*
*/ */
public class StringBlock { public class StringBlock {
/** /**
* Reads whole (including chunk type) string block from stream. * Reads whole (including chunk type) string block from stream. Stream must
* Stream must be at the chunk type. * be at the chunk type.
*/ */
public static StringBlock read(ExtDataInput reader) throws IOException { public static StringBlock read(ExtDataInput reader) throws IOException {
reader.skipCheckInt(CHUNK_TYPE); reader.skipCheckInt(CHUNK_TYPE);
int chunkSize = reader.readInt(); int chunkSize = reader.readInt();
int stringCount = reader.readInt(); int stringCount = reader.readInt();
int styleOffsetCount = reader.readInt(); int styleOffsetCount = reader.readInt();
int flags = reader.readInt(); int flags = reader.readInt();
int stringsOffset = reader.readInt(); int stringsOffset = reader.readInt();
int stylesOffset = reader.readInt(); int stylesOffset = reader.readInt();
StringBlock block = new StringBlock(); StringBlock block = new StringBlock();
block.m_isUTF8 = (flags & UTF8_FLAG) != 0; block.m_isUTF8 = (flags & UTF8_FLAG) != 0;
block.m_stringOffsets = reader.readIntArray(stringCount); block.m_stringOffsets = reader.readIntArray(stringCount);
block.m_stringOwns = new int[stringCount]; block.m_stringOwns = new int[stringCount];
for (int i=0;i<stringCount;i++) { for (int i = 0; i < stringCount; i++) {
block.m_stringOwns[i] = -1; block.m_stringOwns[i] = -1;
} }
if (styleOffsetCount != 0) { if (styleOffsetCount != 0) {
block.m_styleOffsets = reader.readIntArray(styleOffsetCount); block.m_styleOffsets = reader.readIntArray(styleOffsetCount);
} }
{ {
int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) - stringsOffset; int size = ((stylesOffset == 0) ? chunkSize : stylesOffset)
if ((size % 4) != 0) { - stringsOffset;
throw new IOException("String data size is not multiple of 4 (" + size + ")."); if ((size % 4) != 0) {
} throw new IOException("String data size is not multiple of 4 ("
block.m_strings = new byte[size]; + size + ").");
reader.readFully(block.m_strings); }
} block.m_strings = new byte[size];
if (stylesOffset != 0) { reader.readFully(block.m_strings);
int size = (chunkSize - stylesOffset); }
if ((size % 4) != 0) { if (stylesOffset != 0) {
throw new IOException("Style data size is not multiple of 4 (" + size + ")."); int size = (chunkSize - stylesOffset);
} if ((size % 4) != 0) {
block.m_styles = reader.readIntArray(size / 4); throw new IOException("Style data size is not multiple of 4 ("
} + size + ").");
}
block.m_styles = reader.readIntArray(size / 4);
}
return block; return block;
} }
/** /**
* Returns number of strings in block. * Returns number of strings in block.
*/ */
public int getCount() { public int getCount() {
return m_stringOffsets != null return m_stringOffsets != null ? m_stringOffsets.length : 0;
? m_stringOffsets.length }
: 0;
}
/** /**
* Returns raw string (without any styling information) at specified index. * Returns raw string (without any styling information) at specified index.
*/ */
public String getString(int index) { public String getString(int index) {
if (index < 0 if (index < 0 || m_stringOffsets == null
|| m_stringOffsets == null || index >= m_stringOffsets.length) {
|| index >= m_stringOffsets.length) { return null;
return null; }
} int offset = m_stringOffsets[index];
int offset = m_stringOffsets[index]; int length;
int length;
if (! m_isUTF8) { if (!m_isUTF8) {
length = getShort(m_strings, offset) * 2; length = getShort(m_strings, offset) * 2;
offset += 2; offset += 2;
} else { } else {
offset += getVarint(m_strings, offset)[1]; offset += getVarint(m_strings, offset)[1];
int[] varint = getVarint(m_strings, offset); int[] varint = getVarint(m_strings, offset);
offset += varint[1]; offset += varint[1];
length = varint[0]; length = varint[0];
} }
return decodeString(offset, length); return decodeString(offset, length);
} }
/** /**
* Not yet implemented. * Not yet implemented.
* *
* Returns string with style information (if any). * Returns string with style information (if any).
*/ */
public CharSequence get(int index) { public CharSequence get(int index) {
return getString(index); return getString(index);
} }
/** /**
* Returns string with style tags (html-like). * Returns string with style tags (html-like).
*/ */
public String getHTML(int index) { public String getHTML(int index) {
String raw = getString(index); String raw = getString(index);
if (raw == null) { if (raw == null) {
return raw; return raw;
} }
int[] style = getStyle(index); int[] style = getStyle(index);
if (style == null) { if (style == null) {
return ResXmlEncoders.escapeXmlChars(raw); return ResXmlEncoders.escapeXmlChars(raw);
} }
StringBuilder html = new StringBuilder(raw.length() + 32); StringBuilder html = new StringBuilder(raw.length() + 32);
int[] opened = new int[style.length / 3]; int[] opened = new int[style.length / 3];
int offset = 0, depth = 0; int offset = 0, depth = 0;
while (true) { while (true) {
int i = -1, j; int i = -1, j;
for (j = 0; j != style.length; j += 3) { for (j = 0; j != style.length; j += 3) {
if (style[j + 1] == -1) { if (style[j + 1] == -1) {
continue; continue;
} }
if (i == -1 || style[i + 1] > style[j + 1]) { if (i == -1 || style[i + 1] > style[j + 1]) {
i = j; i = j;
} }
} }
int start = ((i != -1) ? style[i + 1] : raw.length()); int start = ((i != -1) ? style[i + 1] : raw.length());
for (j = depth - 1; j >= 0; j--) { for (j = depth - 1; j >= 0; j--) {
int last = opened[j]; int last = opened[j];
int end = style[last + 2]; int end = style[last + 2];
if (end >= start) { if (end >= start) {
break; break;
} }
if (offset <= end) { if (offset <= end) {
html.append(ResXmlEncoders.escapeXmlChars( html.append(ResXmlEncoders.escapeXmlChars(raw.substring(
raw.substring(offset, end + 1))); offset, end + 1)));
offset = end + 1; offset = end + 1;
} }
outputStyleTag(getString(style[last]), html, true); outputStyleTag(getString(style[last]), html, true);
} }
depth = j + 1; depth = j + 1;
if (offset < start) { if (offset < start) {
html.append(ResXmlEncoders.escapeXmlChars( html.append(ResXmlEncoders.escapeXmlChars(raw.substring(offset,
raw.substring(offset, start))); start)));
offset = start; offset = start;
} }
if (i == -1) { if (i == -1) {
break; break;
} }
outputStyleTag(getString(style[i]), html, false); outputStyleTag(getString(style[i]), html, false);
style[i + 1] = -1; style[i + 1] = -1;
opened[depth++] = i; opened[depth++] = i;
} }
return html.toString(); return html.toString();
} }
private void outputStyleTag(String tag, StringBuilder builder, private void outputStyleTag(String tag, StringBuilder builder, boolean close) {
boolean close) { builder.append('<');
builder.append('<'); if (close) {
if (close) { builder.append('/');
builder.append('/'); }
}
int pos = tag.indexOf(';'); int pos = tag.indexOf(';');
if (pos == -1) { if (pos == -1) {
builder.append(tag); builder.append(tag);
} else { } else {
builder.append(tag.substring(0, pos)); builder.append(tag.substring(0, pos));
if (! close) { if (!close) {
boolean loop = true; boolean loop = true;
while (loop) { while (loop) {
int pos2 = tag.indexOf('=', pos + 1); int pos2 = tag.indexOf('=', pos + 1);
builder.append(' ').append(tag.substring(pos + 1, pos2)) builder.append(' ').append(tag.substring(pos + 1, pos2))
.append("=\""); .append("=\"");
pos = tag.indexOf(';', pos2 + 1); pos = tag.indexOf(';', pos2 + 1);
String val; String val;
if (pos != -1) { if (pos != -1) {
val = tag.substring(pos2 + 1, pos); val = tag.substring(pos2 + 1, pos);
} else { } else {
loop = false; loop = false;
val = tag.substring(pos2 + 1); val = tag.substring(pos2 + 1);
} }
builder.append(ResXmlEncoders.escapeXmlChars(val)) builder.append(ResXmlEncoders.escapeXmlChars(val)).append(
.append('"'); '"');
} }
} }
} }
builder.append('>'); builder.append('>');
} }
/** /**
* Finds index of the string. * Finds index of the string. Returns -1 if the string was not found.
* Returns -1 if the string was not found. */
*/ public int find(String string) {
public int find(String string) { if (string == null) {
if (string == null) { return -1;
return -1; }
} for (int i = 0; i != m_stringOffsets.length; ++i) {
for (int i = 0; i != m_stringOffsets.length; ++i) { int offset = m_stringOffsets[i];
int offset = m_stringOffsets[i]; int length = getShort(m_strings, offset);
int length = getShort(m_strings, offset); if (length != string.length()) {
if (length != string.length()) { continue;
continue; }
} int j = 0;
int j = 0; for (; j != length; ++j) {
for (; j != length; ++j) { offset += 2;
offset += 2; if (string.charAt(j) != getShort(m_strings, offset)) {
if (string.charAt(j) != getShort(m_strings, offset)) { break;
break; }
} }
} if (j == length) {
if (j == length) { return i;
return i; }
} }
} return -1;
return -1; }
}
///////////////////////////////////////////// implementation // /////////////////////////////////////////// implementation
private StringBlock() { private StringBlock() {
} }
/** /**
* Returns style information - array of int triplets, * Returns style information - array of int triplets, where in each triplet:
* where in each triplet: * * first int is index of tag name ('b','i', etc.) * second int is tag
* * first int is index of tag name ('b','i', etc.) * start index in string * third int is tag end index in string
* * second int is tag start index in string */
* * third int is tag end index in string private int[] getStyle(int index) {
*/ if (m_styleOffsets == null || m_styles == null
private int[] getStyle(int index) { || index >= m_styleOffsets.length) {
if (m_styleOffsets == null || m_styles == null return null;
|| index >= m_styleOffsets.length) { }
return null; int offset = m_styleOffsets[index] / 4;
} int style[];
int offset = m_styleOffsets[index] / 4; {
int style[]; int count = 0;
{ for (int i = offset; i < m_styles.length; ++i) {
int count = 0; if (m_styles[i] == -1) {
for (int i = offset; i < m_styles.length; ++i) { break;
if (m_styles[i] == -1) { }
break; count += 1;
} }
count += 1; if (count == 0 || (count % 3) != 0) {
} return null;
if (count == 0 || (count % 3) != 0) { }
return null; style = new int[count];
} }
style = new int[count]; for (int i = offset, j = 0; i < m_styles.length;) {
} if (m_styles[i] == -1) {
for (int i = offset, j = 0; i < m_styles.length;) { break;
if (m_styles[i] == -1) { }
break; style[j++] = m_styles[i++];
} }
style[j++] = m_styles[i++]; return style;
} }
return style;
}
private String decodeString(int offset, int length) { private String decodeString(int offset, int length) {
try { try {
return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode( return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode(
ByteBuffer.wrap(m_strings, offset, length)).toString(); ByteBuffer.wrap(m_strings, offset, length)).toString();
} catch (CharacterCodingException ex) { } catch (CharacterCodingException ex) {
LOGGER.log(Level.WARNING, null, ex); LOGGER.log(Level.WARNING, null, ex);
return null; return null;
} }
} }
private static final int getShort(byte[] array, int offset) { private static final int getShort(byte[] array, int offset) {
return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff; return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff;
} }
private static final int getShort(int[] array, int offset) { private static final int getShort(int[] array, int offset) {
int value = array[offset / 4]; int value = array[offset / 4];
if ((offset % 4) / 2 == 0) { if ((offset % 4) / 2 == 0) {
return (value & 0xFFFF); return (value & 0xFFFF);
} else { } else {
return (value >>> 16); return (value >>> 16);
} }
} }
private static final int[] getVarint(byte[] array, int offset) { private static final int[] getVarint(byte[] array, int offset) {
int val = array[offset]; int val = array[offset];
boolean more = (val & 0x80) != 0; boolean more = (val & 0x80) != 0;
val &= 0x7f; val &= 0x7f;
if (! more) { if (!more) {
return new int[]{val, 1}; return new int[] { val, 1 };
} else { } else {
return new int[]{val << 8 | array[offset + 1] & 0xff, 2}; return new int[] { val << 8 | array[offset + 1] & 0xff, 2 };
} }
} }
public boolean touch(int index, int own) { public boolean touch(int index, int own) {
if (index < 0 if (index < 0 || m_stringOwns == null || index >= m_stringOwns.length) {
|| m_stringOwns == null return false;
|| index >= m_stringOwns.length) { }
return false; if (m_stringOwns[index] == -1) {
} m_stringOwns[index] = own;
if(m_stringOwns[index] == -1) { return true;
m_stringOwns[index] = own; } else if (m_stringOwns[index] == own) {
return true; return true;
} else if (m_stringOwns[index] == own) { } else {
return true; return false;
} else { }
return false; }
}
}
private int[] m_stringOffsets; private int[] m_stringOffsets;
private byte[] m_strings; private byte[] m_strings;
private int[] m_styleOffsets; private int[] m_styleOffsets;
private int[] m_styles; private int[] m_styles;
private boolean m_isUTF8; private boolean m_isUTF8;
private int[] m_stringOwns; private int[] m_stringOwns;
private static final CharsetDecoder UTF16LE_DECODER = private static final CharsetDecoder UTF16LE_DECODER = Charset.forName(
Charset.forName("UTF-16LE").newDecoder(); "UTF-16LE").newDecoder();
private static final CharsetDecoder UTF8_DECODER = private static final CharsetDecoder UTF8_DECODER = Charset.forName("UTF-8")
Charset.forName("UTF-8").newDecoder(); .newDecoder();
private static final Logger LOGGER = private static final Logger LOGGER = Logger.getLogger(StringBlock.class
Logger.getLogger(StringBlock.class.getName()); .getName());
private static final int CHUNK_TYPE = 0x001C0001; private static final int CHUNK_TYPE = 0x001C0001;
private static final int UTF8_FLAG = 0x00000100; private static final int UTF8_FLAG = 0x00000100;
} }

View File

@ -36,122 +36,133 @@ import brut.androlib.res.util.ExtXmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class XmlPullStreamDecoder implements ResStreamDecoder { public class XmlPullStreamDecoder implements ResStreamDecoder {
public XmlPullStreamDecoder(XmlPullParser parser, public XmlPullStreamDecoder(XmlPullParser parser,
ExtXmlSerializer serializer) { ExtXmlSerializer serializer) {
this.mParser = parser; this.mParser = parser;
this.mSerial = serializer; this.mSerial = serializer;
} }
public void decode(InputStream in, OutputStream out) @Override
throws AndrolibException { public void decode(InputStream in, OutputStream out)
try { throws AndrolibException {
XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance(); try {
XmlPullParserWrapper par = factory.newPullParserWrapper(mParser); XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance();
final ResTable resTable = ((AXmlResourceParser)mParser).getAttrDecoder().getCurrentPackage().getResTable(); XmlPullParserWrapper par = factory.newPullParserWrapper(mParser);
final ResTable resTable = ((AXmlResourceParser) mParser)
XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, factory){ .getAttrDecoder().getCurrentPackage().getResTable();
boolean hideSdkInfo = false;
boolean hidePackageInfo = false;
@Override
public void event(XmlPullParser pp) throws XmlPullParserException, IOException {
int type = pp.getEventType();
if (type == XmlPullParser.START_TAG) {
if ("manifest".equalsIgnoreCase(pp.getName())) {
try {
hidePackageInfo = parseManifest(pp);
} catch (AndrolibException e) {}
}else if ("uses-sdk".equalsIgnoreCase(pp.getName())) {
try {
hideSdkInfo = parseAttr(pp);
if(hideSdkInfo) {
return;
}
} catch (AndrolibException e) {}
}
} else if (hideSdkInfo && type == XmlPullParser.END_TAG &&
"uses-sdk".equalsIgnoreCase(pp.getName())) {
return;
} else if (hidePackageInfo && type == XmlPullParser.END_TAG &&
"manifest".equalsIgnoreCase(pp.getName())) {
super.event(pp);
return;
}
super.event(pp);
}
private boolean parseManifest(XmlPullParser pp) throws AndrolibException {
ResTable restable = resTable;
// read <manifest> for package:
for (int i = 0; i < pp.getAttributeCount(); i++) {
if (pp.getAttributeName(i).equalsIgnoreCase(("package"))) {
restable.addPackageInfo("orig_package", pp.getAttributeValue(i));
}
}
return true;
}
private boolean parseAttr(XmlPullParser pp) throws AndrolibException {
ResTable restable = resTable;
for (int i = 0; i < pp.getAttributeCount(); i++) {
final String a_ns = "http://schemas.android.com/apk/res/android";
String ns = pp.getAttributeNamespace (i);
if (a_ns.equalsIgnoreCase(ns)) {
String name = pp.getAttributeName (i);
String value = pp.getAttributeValue (i);
if (name != null && value != null) {
if (name.equalsIgnoreCase("minSdkVersion") ||
name.equalsIgnoreCase("targetSdkVersion") ||
name.equalsIgnoreCase("maxSdkVersion")) {
restable.addSdkInfo(name, value);
} else {
restable.clearSdkInfo();
return false;//Found unknown flags
}
}
} else {
resTable.clearSdkInfo();
if (i >= pp.getAttributeCount()) {
return false;//Found unknown flags
}
}
}
return true;
}
};
par.setInput(in, null); XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial,
ser.setOutput(out, null); factory) {
boolean hideSdkInfo = false;
boolean hidePackageInfo = false;
while (par.nextToken() != XmlPullParser.END_DOCUMENT) { @Override
ser.event(par); public void event(XmlPullParser pp)
} throws XmlPullParserException, IOException {
ser.flush(); int type = pp.getEventType();
} catch (XmlPullParserException ex) {
throw new AndrolibException("Could not decode XML", ex);
} catch (IOException ex) {
throw new AndrolibException("Could not decode XML", ex);
}
}
public void decodeManifest(InputStream in, OutputStream out) if (type == XmlPullParser.START_TAG) {
throws AndrolibException { if ("manifest".equalsIgnoreCase(pp.getName())) {
mOptimizeForManifest = true; try {
try { hidePackageInfo = parseManifest(pp);
decode(in, out); } catch (AndrolibException e) {
} finally { }
mOptimizeForManifest = false; } else if ("uses-sdk".equalsIgnoreCase(pp.getName())) {
} try {
} hideSdkInfo = parseAttr(pp);
if (hideSdkInfo) {
return;
}
} catch (AndrolibException e) {
}
}
} else if (hideSdkInfo && type == XmlPullParser.END_TAG
&& "uses-sdk".equalsIgnoreCase(pp.getName())) {
return;
} else if (hidePackageInfo && type == XmlPullParser.END_TAG
&& "manifest".equalsIgnoreCase(pp.getName())) {
super.event(pp);
return;
}
super.event(pp);
}
private final XmlPullParser mParser; private boolean parseManifest(XmlPullParser pp)
private final ExtXmlSerializer mSerial; throws AndrolibException {
ResTable restable = resTable;
private boolean mOptimizeForManifest = false; // read <manifest> for package:
for (int i = 0; i < pp.getAttributeCount(); i++) {
if (pp.getAttributeName(i)
.equalsIgnoreCase(("package"))) {
restable.addPackageInfo("orig_package",
pp.getAttributeValue(i));
}
}
return true;
}
private final static Logger LOGGER = private boolean parseAttr(XmlPullParser pp)
Logger.getLogger(XmlPullStreamDecoder.class.getName()); throws AndrolibException {
ResTable restable = resTable;
for (int i = 0; i < pp.getAttributeCount(); i++) {
final String a_ns = "http://schemas.android.com/apk/res/android";
String ns = pp.getAttributeNamespace(i);
if (a_ns.equalsIgnoreCase(ns)) {
String name = pp.getAttributeName(i);
String value = pp.getAttributeValue(i);
if (name != null && value != null) {
if (name.equalsIgnoreCase("minSdkVersion")
|| name.equalsIgnoreCase("targetSdkVersion")
|| name.equalsIgnoreCase("maxSdkVersion")) {
restable.addSdkInfo(name, value);
} else {
restable.clearSdkInfo();
return false;// Found unknown flags
}
}
} else {
resTable.clearSdkInfo();
if (i >= pp.getAttributeCount()) {
return false;// Found unknown flags
}
}
}
return true;
}
};
par.setInput(in, null);
ser.setOutput(out, null);
while (par.nextToken() != XmlPullParser.END_DOCUMENT) {
ser.event(par);
}
ser.flush();
} catch (XmlPullParserException ex) {
throw new AndrolibException("Could not decode XML", ex);
} catch (IOException ex) {
throw new AndrolibException("Could not decode XML", ex);
}
}
public void decodeManifest(InputStream in, OutputStream out)
throws AndrolibException {
mOptimizeForManifest = true;
try {
decode(in, out);
} finally {
mOptimizeForManifest = false;
}
}
private final XmlPullParser mParser;
private final ExtXmlSerializer mSerial;
private boolean mOptimizeForManifest = false;
private final static Logger LOGGER = Logger
.getLogger(XmlPullStreamDecoder.class.getName());
} }

View File

@ -27,37 +27,36 @@ import java.net.URI;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ExtFile extends File { public class ExtFile extends File {
public ExtFile(File file) { public ExtFile(File file) {
super(file.getPath()); super(file.getPath());
} }
public ExtFile(URI uri) { public ExtFile(URI uri) {
super(uri); super(uri);
} }
public ExtFile(File parent, String child) { public ExtFile(File parent, String child) {
super(parent, child); super(parent, child);
} }
public ExtFile(String parent, String child) { public ExtFile(String parent, String child) {
super(parent, child); super(parent, child);
} }
public ExtFile(String pathname) { public ExtFile(String pathname) {
super(pathname); super(pathname);
} }
public Directory getDirectory() throws DirectoryException { public Directory getDirectory() throws DirectoryException {
if (mDirectory == null) { if (mDirectory == null) {
if (isDirectory()) { if (isDirectory()) {
mDirectory = new FileDirectory(this); mDirectory = new FileDirectory(this);
} else { } else {
mDirectory = new ZipRODirectory(this); mDirectory = new ZipRODirectory(this);
} }
} }
return mDirectory; return mDirectory;
} }
private Directory mDirectory;
private Directory mDirectory;
} }

View File

@ -23,57 +23,59 @@ import org.xmlpull.mxp1_serializer.MXSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer { public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer {
@Override @Override
public void startDocument(String encoding, Boolean standalone) throws public void startDocument(String encoding, Boolean standalone)
IOException, IllegalArgumentException, IllegalStateException { throws IOException, IllegalArgumentException, IllegalStateException {
super.startDocument(encoding != null ? encoding : mDefaultEncoding, super.startDocument(encoding != null ? encoding : mDefaultEncoding,
standalone); standalone);
this.newLine(); this.newLine();
} }
@Override @Override
protected void writeAttributeValue(String value, Writer out) protected void writeAttributeValue(String value, Writer out)
throws IOException { throws IOException {
if (mIsDisabledAttrEscape) { if (mIsDisabledAttrEscape) {
out.write(value); out.write(value);
return; return;
} }
super.writeAttributeValue(value, out); super.writeAttributeValue(value, out);
} }
@Override @Override
public void setOutput(OutputStream os, String encoding) throws IOException { public void setOutput(OutputStream os, String encoding) throws IOException {
super.setOutput(os, encoding != null ? encoding : mDefaultEncoding); super.setOutput(os, encoding != null ? encoding : mDefaultEncoding);
} }
@Override @Override
public Object getProperty(String name) throws IllegalArgumentException { public Object getProperty(String name) throws IllegalArgumentException {
if (PROPERTY_DEFAULT_ENCODING.equals(name)) { if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
return mDefaultEncoding; return mDefaultEncoding;
} }
return super.getProperty(name); return super.getProperty(name);
} }
@Override @Override
public void setProperty(String name, Object value) public void setProperty(String name, Object value)
throws IllegalArgumentException, IllegalStateException { throws IllegalArgumentException, IllegalStateException {
if (PROPERTY_DEFAULT_ENCODING.equals(name)) { if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
mDefaultEncoding = (String) value; mDefaultEncoding = (String) value;
} else { } else {
super.setProperty(name, value); super.setProperty(name, value);
} }
} }
public ExtXmlSerializer newLine() throws IOException { @Override
super.out.write(lineSeparator); public ExtXmlSerializer newLine() throws IOException {
return this; super.out.write(lineSeparator);
} return this;
}
public void setDisabledAttrEscape(boolean disabled) { @Override
mIsDisabledAttrEscape = disabled; public void setDisabledAttrEscape(boolean disabled) {
} mIsDisabledAttrEscape = disabled;
}
private String mDefaultEncoding;
private boolean mIsDisabledAttrEscape = false;
private String mDefaultEncoding;
private boolean mIsDisabledAttrEscape = false;
} }

View File

@ -24,12 +24,11 @@ import org.xmlpull.v1.XmlSerializer;
*/ */
public interface ExtXmlSerializer extends XmlSerializer { public interface ExtXmlSerializer extends XmlSerializer {
public ExtXmlSerializer newLine() throws IOException; public ExtXmlSerializer newLine() throws IOException;
public void setDisabledAttrEscape(boolean disabled);
public static final String PROPERTY_SERIALIZER_INDENTATION = public void setDisabledAttrEscape(boolean disabled);
"http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = public static final String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
"http://xmlpull.org/v1/doc/properties.html#serializer-line-separator"; public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING"; public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING";
} }

View File

@ -25,6 +25,6 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public interface ResValuesXmlSerializable { public interface ResValuesXmlSerializable {
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) public void serializeToResValuesXml(XmlSerializer serializer,
throws IOException, AndrolibException; ResResource res) throws IOException, AndrolibException;
} }

View File

@ -22,6 +22,7 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public interface ResXmlEncodable { public interface ResXmlEncodable {
public String encodeAsResXmlAttr() throws AndrolibException; public String encodeAsResXmlAttr() throws AndrolibException;
public String encodeAsResXmlValue() throws AndrolibException;
public String encodeAsResXmlValue() throws AndrolibException;
} }

View File

@ -25,178 +25,176 @@ import java.util.List;
*/ */
public final class ResXmlEncoders { public final class ResXmlEncoders {
public static String escapeXmlChars(String str) { public static String escapeXmlChars(String str) {
return str.replace("&", "&amp;").replace("<", "&lt;"); return str.replace("&", "&amp;").replace("<", "&lt;");
} }
public static String encodeAsResXmlAttr(String str) { public static String encodeAsResXmlAttr(String str) {
if (str.isEmpty()) { if (str.isEmpty()) {
return str; return str;
} }
char[] chars = str.toCharArray(); char[] chars = str.toCharArray();
StringBuilder out = new StringBuilder(str.length() + 10); StringBuilder out = new StringBuilder(str.length() + 10);
switch (chars[0]) { switch (chars[0]) {
case '#': case '#':
case '@': case '@':
case '?': case '?':
out.append('\\'); out.append('\\');
} }
for (char c : chars) { for (char c : chars) {
switch (c) { switch (c) {
case '\\': case '\\':
out.append('\\'); out.append('\\');
break; break;
case '"': case '"':
out.append("&quot;"); out.append("&quot;");
continue; continue;
case '\n': case '\n':
out.append("\\n"); out.append("\\n");
continue; continue;
default: default:
if (!isPrintableChar(c)) { if (!isPrintableChar(c)) {
out.append(String.format("\\u%04x", (int) c)); out.append(String.format("\\u%04x", (int) c));
continue; continue;
} }
} }
out.append(c); out.append(c);
} }
return out.toString(); return out.toString();
} }
public static String encodeAsXmlValue(String str) { public static String encodeAsXmlValue(String str) {
if (str.isEmpty()) { if (str.isEmpty()) {
return str; return str;
} }
char[] chars = str.toCharArray(); char[] chars = str.toCharArray();
StringBuilder out = new StringBuilder(str.length() + 10); StringBuilder out = new StringBuilder(str.length() + 10);
switch (chars[0]) { switch (chars[0]) {
case '#': case '#':
case '@': case '@':
case '?': case '?':
out.append('\\'); out.append('\\');
} }
boolean isInStyleTag = false; boolean isInStyleTag = false;
int startPos = 0; int startPos = 0;
boolean enclose = false; boolean enclose = false;
boolean wasSpace = true; boolean wasSpace = true;
for (char c : chars) { for (char c : chars) {
if (isInStyleTag) { if (isInStyleTag) {
if (c == '>') { if (c == '>') {
isInStyleTag = false; isInStyleTag = false;
startPos = out.length() + 1; startPos = out.length() + 1;
enclose = false; enclose = false;
} }
} else if (c == ' ') { } else if (c == ' ') {
if (wasSpace) { if (wasSpace) {
enclose = true; enclose = true;
} }
wasSpace = true; wasSpace = true;
} else { } else {
wasSpace = false; wasSpace = false;
switch (c) { switch (c) {
case '\\': case '\\':
out.append('\\'); out.append('\\');
break; break;
case '\'': case '\'':
case '\n': case '\n':
enclose = true; enclose = true;
break; break;
case '"': case '"':
out.append('\\'); out.append('\\');
break; break;
case '<': case '<':
isInStyleTag = true; isInStyleTag = true;
if (enclose) { if (enclose) {
out.insert(startPos, '"').append('"'); out.insert(startPos, '"').append('"');
} }
break; break;
default: default:
if (!isPrintableChar(c)) { if (!isPrintableChar(c)) {
out.append(String.format("\\u%04x", (int) c)); out.append(String.format("\\u%04x", (int) c));
continue; continue;
} }
} }
} }
out.append(c); out.append(c);
} }
if (enclose || wasSpace) { if (enclose || wasSpace) {
out.insert(startPos, '"').append('"'); out.insert(startPos, '"').append('"');
} }
return out.toString(); return out.toString();
} }
public static boolean hasMultipleNonPositionalSubstitutions(String str) { public static boolean hasMultipleNonPositionalSubstitutions(String str) {
return findNonPositionalSubstitutions(str, 2).size() > 1; return findNonPositionalSubstitutions(str, 2).size() > 1;
} }
public static String enumerateNonPositionalSubstitutions(String str) { public static String enumerateNonPositionalSubstitutions(String str) {
List<Integer> subs = findNonPositionalSubstitutions(str, -1); List<Integer> subs = findNonPositionalSubstitutions(str, -1);
if (subs.size() < 2) { if (subs.size() < 2) {
return str; return str;
} }
StringBuilder out = new StringBuilder(); StringBuilder out = new StringBuilder();
int pos = 0; int pos = 0;
int count = 0; int count = 0;
for (Integer sub : subs) { for (Integer sub : subs) {
out.append(str.substring(pos, ++sub)).append(++count).append('$'); out.append(str.substring(pos, ++sub)).append(++count).append('$');
pos = sub; pos = sub;
} }
out.append(str.substring(pos)); out.append(str.substring(pos));
return out.toString(); return out.toString();
} }
/** /**
* It searches for "%", but not "%%" nor "%(\d)+\$" * It searches for "%", but not "%%" nor "%(\d)+\$"
*/ */
private static List<Integer> findNonPositionalSubstitutions(String str, private static List<Integer> findNonPositionalSubstitutions(String str,
int max) { int max) {
int pos = 0; int pos = 0;
int pos2 = 0; int pos2 = 0;
int count = 0; int count = 0;
int length = str.length(); int length = str.length();
List<Integer> ret = new ArrayList<Integer>(); List<Integer> ret = new ArrayList<Integer>();
while((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) { while ((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) {
if (pos2 == length) { if (pos2 == length) {
break; break;
} }
char c = str.charAt(pos2++); char c = str.charAt(pos2++);
if (c == '%') { if (c == '%') {
continue; continue;
} }
if (c >= '0' && c <= '9' && pos2 < length) { if (c >= '0' && c <= '9' && pos2 < length) {
do { do {
c = str.charAt(pos2++); c = str.charAt(pos2++);
} while (c >= '0' && c <= '9' && pos2 < length); } while (c >= '0' && c <= '9' && pos2 < length);
if (c == '$') { if (c == '$') {
continue; continue;
} }
} }
ret.add(pos); ret.add(pos);
if (max != -1 && ++count >= max) { if (max != -1 && ++count >= max) {
break; break;
} }
} }
return ret; return ret;
} }
private static boolean isPrintableChar(char c) { private static boolean isPrintableChar(char c) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(c); Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
return !Character.isISOControl(c) return !Character.isISOControl(c) && c != KeyEvent.CHAR_UNDEFINED
&& c != KeyEvent.CHAR_UNDEFINED && block != null && block != Character.UnicodeBlock.SPECIALS;
&& block != null }
&& block != Character.UnicodeBlock.SPECIALS;
}
} }

View File

@ -21,207 +21,197 @@ import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.jf.dexlib.Code.Analysis.RegisterType; import org.jf.dexlib.Code.Analysis.RegisterType;
import org.jf.dexlib.Code.Opcode;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class DebugInjector { public class DebugInjector {
public static void inject(ListIterator<String> it, StringBuilder out) public static void inject(ListIterator<String> it, StringBuilder out)
throws AndrolibException { throws AndrolibException {
new DebugInjector(it, out).inject(); new DebugInjector(it, out).inject();
} }
private DebugInjector(ListIterator<String> it, StringBuilder out) { private DebugInjector(ListIterator<String> it, StringBuilder out) {
mIt = it; mIt = it;
mOut = out; mOut = out;
} }
private void inject() throws AndrolibException { private void inject() throws AndrolibException {
String definition = nextAndAppend(); String definition = nextAndAppend();
if ( if (definition.contains(" abstract ")
definition.contains(" abstract ") || || definition.contains(" native ")) {
definition.contains(" native ") nextAndAppend();
) { return;
nextAndAppend(); }
return; injectParameters(definition);
}
injectParameters(definition);
boolean end = false; boolean end = false;
while (!end) { while (!end) {
end = step(); end = step();
} }
} }
private void injectParameters(String definition) throws AndrolibException { private void injectParameters(String definition) throws AndrolibException {
int pos = definition.indexOf('('); int pos = definition.indexOf('(');
if (pos == -1) { if (pos == -1) {
throw new AndrolibException(); throw new AndrolibException();
} }
int pos2 = definition.indexOf(')', pos); int pos2 = definition.indexOf(')', pos);
if (pos2 == -1) { if (pos2 == -1) {
throw new AndrolibException(); throw new AndrolibException();
} }
String params = definition.substring(pos + 1, pos2); String params = definition.substring(pos + 1, pos2);
int i = definition.contains(" static ") ? 0 : 1; int i = definition.contains(" static ") ? 0 : 1;
int argc = TypeName.listFromInternalName(params).size() + i; int argc = TypeName.listFromInternalName(params).size() + i;
while(i < argc) { while (i < argc) {
mOut.append(".parameter \"p").append(i).append("\"\n"); mOut.append(".parameter \"p").append(i).append("\"\n");
i++; i++;
} }
} }
private boolean step() { private boolean step() {
String line = next(); String line = next();
if (line.isEmpty()) { if (line.isEmpty()) {
return false; return false;
} }
switch (line.charAt(0)) { switch (line.charAt(0)) {
case '#': case '#':
return processComment(line); return processComment(line);
case ':': case ':':
append(line); append(line);
return false; return false;
case '.': case '.':
return processDirective(line); return processDirective(line);
default: default:
return processInstruction(line); return processInstruction(line);
} }
} }
private boolean processComment(String line) { private boolean processComment(String line) {
if (mFirstInstruction) { if (mFirstInstruction) {
return false; return false;
} }
Matcher m = REGISTER_INFO_PATTERN.matcher(line); Matcher m = REGISTER_INFO_PATTERN.matcher(line);
while (m.find()) { while (m.find()) {
String localName = m.group(1); String localName = m.group(1);
String localType = null; String localType = null;
switch (RegisterType.Category.valueOf(m.group(2))) { switch (RegisterType.Category.valueOf(m.group(2))) {
case Reference: case Reference:
case Null: case Null:
case UninitRef: case UninitRef:
case UninitThis: case UninitThis:
localType = "Ljava/lang/Object;"; localType = "Ljava/lang/Object;";
break; break;
case Boolean: case Boolean:
localType = "Z"; localType = "Z";
break; break;
case Integer: case Integer:
case One: case One:
case Unknown: case Unknown:
localType = "I"; localType = "I";
break; break;
case Uninit: case Uninit:
case Conflicted: case Conflicted:
if (mInitializedRegisters.remove(localName)) { if (mInitializedRegisters.remove(localName)) {
mOut.append(".end local ").append(localName) mOut.append(".end local ").append(localName).append('\n');
.append('\n'); }
} continue;
continue; case Short:
case Short: case PosShort:
case PosShort: localType = "S";
localType = "S"; break;
break; case Byte:
case Byte: case PosByte:
case PosByte: localType = "B";
localType = "B"; break;
break; case Char:
case Char: localType = "C";
localType = "C"; break;
break; case Float:
case Float: localType = "F";
localType = "F"; break;
break; case LongHi:
case LongHi: case LongLo:
case LongLo: localType = "J";
localType = "J"; break;
break; case DoubleHi:
case DoubleHi: case DoubleLo:
case DoubleLo: localType = "D";
localType = "D"; break;
break; default:
default: assert false;
assert false; }
}
mInitializedRegisters.add(localName); mInitializedRegisters.add(localName);
mOut.append(".local ").append(localName).append(", ") mOut.append(".local ").append(localName).append(", ")
.append(localName).append(':').append(localType).append('\n'); .append(localName).append(':').append(localType)
} .append('\n');
}
return false; return false;
} }
private boolean processDirective(String line) { private boolean processDirective(String line) {
String line2 = line.substring(1); String line2 = line.substring(1);
if ( if (line2.startsWith("line ") || line2.equals("prologue")
line2.startsWith("line ") || || line2.startsWith("parameter") || line2.startsWith("local ")
line2.equals("prologue") || || line2.startsWith("end local ")) {
line2.startsWith("parameter") || return false;
line2.startsWith("local ") || }
line2.startsWith("end local ")
) {
return false;
}
append(line); append(line);
if (line2.equals("end method")) { if (line2.equals("end method")) {
return true; return true;
} }
if ( if (line2.startsWith("annotation ") || line2.equals("sparse-switch")
line2.startsWith("annotation ") || || line2.startsWith("packed-switch ")
line2.equals("sparse-switch") || || line2.startsWith("array-data ")) {
line2.startsWith("packed-switch ") || while (true) {
line2.startsWith("array-data ") line2 = nextAndAppend();
) { if (line2.startsWith(".end ")) {
while(true) { break;
line2 = nextAndAppend(); }
if (line2.startsWith(".end ")) { }
break; }
} return false;
} }
}
return false;
}
private boolean processInstruction(String line) { private boolean processInstruction(String line) {
if (mFirstInstruction) { if (mFirstInstruction) {
mOut.append(".prologue\n"); mOut.append(".prologue\n");
mFirstInstruction = false; mFirstInstruction = false;
} }
mOut.append(".line ").append(mIt.nextIndex()).append('\n') mOut.append(".line ").append(mIt.nextIndex()).append('\n').append(line)
.append(line).append('\n'); .append('\n');
return false; return false;
} }
private String next() { private String next() {
return mIt.next().trim(); return mIt.next().trim();
} }
private String nextAndAppend() { private String nextAndAppend() {
String line = next(); String line = next();
append(line); append(line);
return line; return line;
} }
private void append(String append) { private void append(String append) {
mOut.append(append).append('\n'); mOut.append(append).append('\n');
} }
private final ListIterator<String> mIt; private final ListIterator<String> mIt;
private final StringBuilder mOut; private final StringBuilder mOut;
private boolean mFirstInstruction = true; private boolean mFirstInstruction = true;
private final Set<String> mInitializedRegisters = new HashSet<String>(); private final Set<String> mInitializedRegisters = new HashSet<String>();
private static final Pattern REGISTER_INFO_PATTERN = private static final Pattern REGISTER_INFO_PATTERN = Pattern
Pattern.compile("((?:p|v)\\d+)=\\(([^)]+)\\);"); .compile("((?:p|v)\\d+)=\\(([^)]+)\\);");
} }

View File

@ -28,58 +28,57 @@ import org.jf.dexlib.Util.ByteArrayAnnotatedOutput;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class DexFileBuilder { public class DexFileBuilder {
public void addSmaliFile(File smaliFile) throws AndrolibException { public void addSmaliFile(File smaliFile) throws AndrolibException {
try { try {
addSmaliFile(new FileInputStream(smaliFile), addSmaliFile(new FileInputStream(smaliFile),
smaliFile.getAbsolutePath()); smaliFile.getAbsolutePath());
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
public void addSmaliFile(InputStream smaliStream, String name) public void addSmaliFile(InputStream smaliStream, String name)
throws AndrolibException { throws AndrolibException {
try { try {
if (! SmaliMod.assembleSmaliFile( if (!SmaliMod.assembleSmaliFile(smaliStream, name, mDexFile, false,
smaliStream, name, mDexFile, false, false, false)) { false, false)) {
throw new AndrolibException( throw new AndrolibException("Could not smali file: " + name);
"Could not smali file: " + name); }
} } catch (IOException ex) {
} catch (IOException ex) { throw new AndrolibException(ex);
throw new AndrolibException(ex); } catch (RecognitionException ex) {
} catch (RecognitionException ex) { throw new AndrolibException(ex);
throw new AndrolibException(ex); }
} }
}
public void writeTo(File dexFile) throws AndrolibException { public void writeTo(File dexFile) throws AndrolibException {
try { try {
OutputStream out = new FileOutputStream(dexFile); OutputStream out = new FileOutputStream(dexFile);
out.write(getAsByteArray()); out.write(getAsByteArray());
out.close(); out.close();
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException( throw new AndrolibException("Could not write dex to file: "
"Could not write dex to file: " + dexFile, ex); + dexFile, ex);
} }
} }
public byte[] getAsByteArray() { public byte[] getAsByteArray() {
mDexFile.place(); mDexFile.place();
for (CodeItem codeItem: mDexFile.CodeItemsSection.getItems()) { for (CodeItem codeItem : mDexFile.CodeItemsSection.getItems()) {
codeItem.fixInstructions(true, true); codeItem.fixInstructions(true, true);
} }
mDexFile.place(); mDexFile.place();
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
mDexFile.writeTo(out); mDexFile.writeTo(out);
byte[] bytes = out.toByteArray(); byte[] bytes = out.toByteArray();
DexFile.calcSignature(bytes); DexFile.calcSignature(bytes);
DexFile.calcChecksum(bytes); DexFile.calcChecksum(bytes);
return bytes; return bytes;
} }
private final DexFile mDexFile = new DexFile(); private final DexFile mDexFile = new DexFile();
} }

View File

@ -31,86 +31,85 @@ import org.apache.commons.io.IOUtils;
*/ */
public class SmaliBuilder { public class SmaliBuilder {
public static void build(ExtFile smaliDir, File dexFile, public static void build(ExtFile smaliDir, File dexFile,
HashMap<String, Boolean> flags) HashMap<String, Boolean> flags) throws AndrolibException {
throws AndrolibException { new SmaliBuilder(smaliDir, dexFile, flags).build();
new SmaliBuilder(smaliDir, dexFile, flags).build(); }
}
private SmaliBuilder(ExtFile smaliDir, File dexFile, HashMap<String, Boolean> flags) { private SmaliBuilder(ExtFile smaliDir, File dexFile,
mSmaliDir = smaliDir; HashMap<String, Boolean> flags) {
mDexFile = dexFile; mSmaliDir = smaliDir;
mFlags = flags; mDexFile = dexFile;
} mFlags = flags;
}
private void build() throws AndrolibException { private void build() throws AndrolibException {
try { try {
mDexBuilder = new DexFileBuilder(); mDexBuilder = new DexFileBuilder();
for (String fileName : mSmaliDir.getDirectory().getFiles(true)) { for (String fileName : mSmaliDir.getDirectory().getFiles(true)) {
buildFile(fileName); buildFile(fileName);
} }
mDexBuilder.writeTo(mDexFile); mDexBuilder.writeTo(mDexFile);
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
private void buildFile(String fileName) throws AndrolibException, private void buildFile(String fileName) throws AndrolibException,
IOException { IOException {
File inFile = new File(mSmaliDir, fileName); File inFile = new File(mSmaliDir, fileName);
InputStream inStream = new FileInputStream(inFile); InputStream inStream = new FileInputStream(inFile);
if (fileName.endsWith(".smali")) { if (fileName.endsWith(".smali")) {
mDexBuilder.addSmaliFile(inFile); mDexBuilder.addSmaliFile(inFile);
return; return;
} }
if (! fileName.endsWith(".java")) { if (!fileName.endsWith(".java")) {
LOGGER.warning("Unknown file type, ignoring: " + inFile); LOGGER.warning("Unknown file type, ignoring: " + inFile);
return; return;
} }
StringBuilder out = new StringBuilder(); StringBuilder out = new StringBuilder();
List<String> lines = IOUtils.readLines(inStream); List<String> lines = IOUtils.readLines(inStream);
if (!mFlags.containsKey("debug")) { if (!mFlags.containsKey("debug")) {
final String[] linesArray = lines.toArray(new String[0]); final String[] linesArray = lines.toArray(new String[0]);
for (int i = 2; i < linesArray.length - 2; i++) { for (int i = 2; i < linesArray.length - 2; i++) {
out.append(linesArray[i]).append('\n'); out.append(linesArray[i]).append('\n');
} }
} else { } else {
lines.remove(lines.size() - 1); lines.remove(lines.size() - 1);
lines.remove(lines.size() - 1); lines.remove(lines.size() - 1);
ListIterator<String> it = lines.listIterator(2); ListIterator<String> it = lines.listIterator(2);
out.append(".source \"").append(inFile.getName()).append("\"\n"); out.append(".source \"").append(inFile.getName()).append("\"\n");
while (it.hasNext()) { while (it.hasNext()) {
String line = it.next().trim(); String line = it.next().trim();
if (line.isEmpty() || line.charAt(0) == '#' || if (line.isEmpty() || line.charAt(0) == '#'
line.startsWith(".source")) { || line.startsWith(".source")) {
continue; continue;
} }
if (line.startsWith(".method ")) { if (line.startsWith(".method ")) {
it.previous(); it.previous();
DebugInjector.inject(it, out); DebugInjector.inject(it, out);
continue; continue;
} }
out.append(line).append('\n'); out.append(line).append('\n');
} }
} }
mDexBuilder.addSmaliFile( mDexBuilder.addSmaliFile(IOUtils.toInputStream(out.toString()),
IOUtils.toInputStream(out.toString()), fileName); fileName);
} }
private final ExtFile mSmaliDir; private final ExtFile mSmaliDir;
private final File mDexFile; private final File mDexFile;
private final HashMap<String, Boolean> mFlags; private final HashMap<String, Boolean> mFlags;
private DexFileBuilder mDexBuilder; private DexFileBuilder mDexBuilder;
private final static Logger LOGGER = Logger.getLogger(SmaliBuilder.class
private final static Logger LOGGER = .getName());
Logger.getLogger(SmaliBuilder.class.getName());
} }

View File

@ -29,34 +29,35 @@ import org.jf.dexlib.DexFile;
*/ */
public class SmaliDecoder { public class SmaliDecoder {
public static void decode(File apkFile, File outDir, boolean debug, boolean bakdeb) public static void decode(File apkFile, File outDir, boolean debug,
throws AndrolibException { boolean bakdeb) throws AndrolibException {
new SmaliDecoder(apkFile, outDir, debug, bakdeb).decode(); new SmaliDecoder(apkFile, outDir, debug, bakdeb).decode();
} }
private SmaliDecoder(File apkFile, File outDir, boolean debug, boolean bakdeb) { private SmaliDecoder(File apkFile, File outDir, boolean debug,
mApkFile = apkFile; boolean bakdeb) {
mOutDir = outDir; mApkFile = apkFile;
mDebug = debug; mOutDir = outDir;
mBakDeb = bakdeb; mDebug = debug;
} mBakDeb = bakdeb;
}
private void decode() throws AndrolibException { private void decode() throws AndrolibException {
if (mDebug) { if (mDebug) {
ClassPath.dontLoadClassPath = true; ClassPath.dontLoadClassPath = true;
} }
try { try {
baksmali.disassembleDexFile(mApkFile.getAbsolutePath(), baksmali.disassembleDexFile(mApkFile.getAbsolutePath(),
new DexFile(mApkFile), false, mOutDir.getAbsolutePath(), null, new DexFile(mApkFile), false, mOutDir.getAbsolutePath(),
null, null, false, true, true, mBakDeb, false, false, null, null, null, false, true, true, mBakDeb, false, false,
mDebug ? main.DIFFPRE: 0, false, false, null, false); mDebug ? main.DIFFPRE : 0, false, false, null, false);
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
private final File mApkFile; private final File mApkFile;
private final File mOutDir; private final File mOutDir;
private final boolean mDebug; private final boolean mDebug;
private final boolean mBakDeb; private final boolean mBakDeb;
} }

View File

@ -26,184 +26,182 @@ import java.util.List;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class TypeName { public class TypeName {
public final String package_; public final String package_;
public final String type; public final String type;
public final String innerType; public final String innerType;
public final int array; public final int array;
public TypeName(String type, int array) { public TypeName(String type, int array) {
this(null, type, null, array); this(null, type, null, array);
} }
public TypeName(String package_, String type, String innerType, int array) { public TypeName(String package_, String type, String innerType, int array) {
this.package_ = package_; this.package_ = package_;
this.type = type; this.type = type;
this.innerType = innerType; this.innerType = innerType;
this.array = array; this.array = array;
} }
public String getShortenedName() { public String getShortenedName() {
return getName("java.lang".equals(package_), isFileOwner()); return getName("java.lang".equals(package_), isFileOwner());
} }
public String getName() { public String getName() {
return getName(false, false); return getName(false, false);
} }
public String getName(boolean excludePackage, boolean separateInner) { public String getName(boolean excludePackage, boolean separateInner) {
String name = String name = (package_ == null || excludePackage ? "" : package_ + '.')
(package_ == null || excludePackage ? "" : package_ + '.') + + type
type + + (innerType != null ? (separateInner ? '$' : '.') + innerType
(innerType != null ? (separateInner ? '$' : '.') + innerType : ""); : "");
for (int i = 0; i < array; i++) { for (int i = 0; i < array; i++) {
name += "[]"; name += "[]";
} }
return name; return name;
} }
public String getJavaFilePath() { public String getJavaFilePath() {
return getFilePath(isFileOwner()) + ".java"; return getFilePath(isFileOwner()) + ".java";
} }
public String getSmaliFilePath() { public String getSmaliFilePath() {
return getFilePath(true) + ".smali"; return getFilePath(true) + ".smali";
} }
public String getFilePath(boolean separateInner) { public String getFilePath(boolean separateInner) {
return package_.replace('.', File.separatorChar) + File.separatorChar return package_.replace('.', File.separatorChar) + File.separatorChar
+ type + (separateInner && isInner() ? "$" + innerType : ""); + type + (separateInner && isInner() ? "$" + innerType : "");
} }
public boolean isInner() { public boolean isInner() {
return innerType != null; return innerType != null;
} }
public boolean isArray() { public boolean isArray() {
return array != 0; return array != 0;
} }
public boolean isFileOwner() { public boolean isFileOwner() {
if (mIsFileOwner == null) { if (mIsFileOwner == null) {
mIsFileOwner = true; mIsFileOwner = true;
if (isInner()) { if (isInner()) {
char c = innerType.charAt(0); char c = innerType.charAt(0);
if (c < '0' || c > '9') { if (c < '0' || c > '9') {
mIsFileOwner = false; mIsFileOwner = false;
} }
} }
} }
return mIsFileOwner; return mIsFileOwner;
} }
@Override @Override
public String toString() { public String toString() {
return getName(); return getName();
} }
public static TypeName fromInternalName(String internal) public static TypeName fromInternalName(String internal)
throws AndrolibException { throws AndrolibException {
Duo<TypeName, Integer> duo = fetchFromInternalName(internal); Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
if (duo.m2 != internal.length()) { if (duo.m2 != internal.length()) {
throw new AndrolibException( throw new AndrolibException("Invalid internal name: " + internal);
"Invalid internal name: " + internal); }
} return duo.m1;
return duo.m1; }
}
public static List<TypeName> listFromInternalName(String internal) public static List<TypeName> listFromInternalName(String internal)
throws AndrolibException { throws AndrolibException {
List<TypeName> types = new ArrayList<TypeName>(); List<TypeName> types = new ArrayList<TypeName>();
while (! internal.isEmpty()) { while (!internal.isEmpty()) {
Duo<TypeName, Integer> duo = fetchFromInternalName(internal); Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
types.add(duo.m1); types.add(duo.m1);
internal = internal.substring(duo.m2); internal = internal.substring(duo.m2);
} }
return types; return types;
} }
public static Duo<TypeName, Integer> fetchFromInternalName(String internal) public static Duo<TypeName, Integer> fetchFromInternalName(String internal)
throws AndrolibException { throws AndrolibException {
String origInternal = internal; String origInternal = internal;
int array = 0; int array = 0;
boolean isArray = false; boolean isArray = false;
do { do {
if (internal.isEmpty()) { if (internal.isEmpty()) {
throw new AndrolibException( throw new AndrolibException("Invalid internal name: "
"Invalid internal name: " + origInternal); + origInternal);
} }
isArray = internal.charAt(0) == '['; isArray = internal.charAt(0) == '[';
if (isArray) { if (isArray) {
array++; array++;
internal = internal.substring(1); internal = internal.substring(1);
} }
} while (isArray); } while (isArray);
int length = array + 1; int length = array + 1;
String package_ = null; String package_ = null;
String type = null; String type = null;
String innerType = null; String innerType = null;
switch (internal.charAt(0)) { switch (internal.charAt(0)) {
case 'B': case 'B':
type = "byte"; type = "byte";
break; break;
case 'C': case 'C':
type = "char"; type = "char";
break; break;
case 'D': case 'D':
type = "double"; type = "double";
break; break;
case 'F': case 'F':
type = "float"; type = "float";
break; break;
case 'I': case 'I':
type = "int"; type = "int";
break; break;
case 'J': case 'J':
type = "long"; type = "long";
break; break;
case 'S': case 'S':
type = "short"; type = "short";
break; break;
case 'Z': case 'Z':
type = "boolean"; type = "boolean";
break; break;
case 'V': case 'V':
type = "void"; type = "void";
break; break;
case 'L': case 'L':
int pos = internal.indexOf(';'); int pos = internal.indexOf(';');
if (pos == -1) { if (pos == -1) {
throw new AndrolibException( throw new AndrolibException("Invalid internal name: "
"Invalid internal name: " + origInternal); + origInternal);
} }
length += pos; length += pos;
internal = internal.substring(1, pos); internal = internal.substring(1, pos);
pos = internal.lastIndexOf('/'); pos = internal.lastIndexOf('/');
if (pos == -1) { if (pos == -1) {
package_ = ""; package_ = "";
type = internal; type = internal;
} else { } else {
package_ = internal.substring(0, pos).replace('/', '.'); package_ = internal.substring(0, pos).replace('/', '.');
type = internal.substring(pos + 1); type = internal.substring(pos + 1);
} }
pos = type.indexOf('$'); pos = type.indexOf('$');
if (pos != -1) { if (pos != -1) {
innerType = type.substring(pos + 1); innerType = type.substring(pos + 1);
type = type.substring(0, pos); type = type.substring(0, pos);
} }
break; break;
default: default:
throw new AndrolibException( throw new AndrolibException("Invalid internal name: "
"Invalid internal name: " + origInternal); + origInternal);
} }
return new Duo<TypeName, Integer>( return new Duo<TypeName, Integer>(new TypeName(package_, type,
new TypeName(package_, type, innerType, array), length); innerType, array), length);
} }
private Boolean mIsFileOwner;
private Boolean mIsFileOwner;
} }

View File

@ -25,295 +25,306 @@ import java.io.InputStream;
/** /**
* Little-Endian version of DataInputStream. * Little-Endian version of DataInputStream.
* <p/> * <p/>
* Very similar to DataInputStream except it reads * Very similar to DataInputStream except it reads little-endian instead of
* little-endian instead of big-endian binary data. We can't extend * big-endian binary data. We can't extend DataInputStream directly since it has
* DataInputStream directly since it has only final methods, though * only final methods, though DataInputStream itself is not final. This forces
* DataInputStream itself is not final. This forces us implement * us implement LEDataInputStream with a DataInputStream object, and use wrapper
* LEDataInputStream with a DataInputStream object, and use wrapper methods. * methods.
* *
* @author Roedy Green, Canadian Mind Products * @author Roedy Green, Canadian Mind Products
* @version 1.8 2007-05-24 * @version 1.8 2007-05-24
* @since 1998 * @since 1998
*/ */
public final class LEDataInputStream implements DataInput public final class LEDataInputStream implements DataInput {
{ // ------------------------------ CONSTANTS ------------------------------
// ------------------------------ CONSTANTS ------------------------------
/** /**
* undisplayed copyright notice. * undisplayed copyright notice.
* *
* @noinspection UnusedDeclaration * @noinspection UnusedDeclaration
*/ */
private static final String EMBEDDED_COPYRIGHT = private static final String EMBEDDED_COPYRIGHT = "copyright (c) 1999-2010 Roedy Green, Canadian Mind Products, http://mindprod.com";
"copyright (c) 1999-2010 Roedy Green, Canadian Mind Products, http://mindprod.com";
// ------------------------------ FIELDS ------------------------------ // ------------------------------ FIELDS ------------------------------
/** /**
* to get at the big-Endian methods of a basic DataInputStream * to get at the big-Endian methods of a basic DataInputStream
* *
* @noinspection WeakerAccess * @noinspection WeakerAccess
*/ */
protected final DataInputStream dis; protected final DataInputStream dis;
/** /**
* to get at the a basic readBytes method. * to get at the a basic readBytes method.
* *
* @noinspection WeakerAccess * @noinspection WeakerAccess
*/ */
protected final InputStream is; protected final InputStream is;
/** /**
* work array for buffering input. * work array for buffering input.
* *
* @noinspection WeakerAccess * @noinspection WeakerAccess
*/ */
protected final byte[] work; protected final byte[] work;
// -------------------------- PUBLIC STATIC METHODS --------------------------
/** // -------------------------- PUBLIC STATIC METHODS
* Note. This is a STATIC method! // --------------------------
*
* @param in stream to read UTF chars from (endian irrelevant)
*
* @return string from stream
* @throws IOException if read fails.
*/
public static String readUTF( DataInput in ) throws IOException
{
return DataInputStream.readUTF( in );
}
// -------------------------- PUBLIC INSTANCE METHODS -------------------------- /**
* Note. This is a STATIC method!
*
* @param in
* stream to read UTF chars from (endian irrelevant)
*
* @return string from stream
* @throws IOException
* if read fails.
*/
public static String readUTF(DataInput in) throws IOException {
return DataInputStream.readUTF(in);
}
/** // -------------------------- PUBLIC INSTANCE METHODS
* constructor. // --------------------------
*
* @param in binary inputstream of little-endian data.
*/
public LEDataInputStream( InputStream in )
{
this.is = in;
this.dis = new DataInputStream( in );
work = new byte[8];
}
/** /**
* close. * constructor.
* *
* @throws IOException if close fails. * @param in
*/ * binary inputstream of little-endian data.
public final void close() throws IOException */
{ public LEDataInputStream(InputStream in) {
dis.close(); this.is = in;
} this.dis = new DataInputStream(in);
work = new byte[8];
}
/** /**
* Read bytes. Watch out, read may return fewer bytes than requested. * close.
* *
* @param ba where the bytes go. * @throws IOException
* @param off offset in buffer, not offset in file. * if close fails.
* @param len count of bytes to read. */
* public final void close() throws IOException {
* @return how many bytes read. dis.close();
* @throws IOException if read fails. }
*/
public final int read( byte ba[], int off, int len ) throws IOException
{
// For efficiency, we avoid one layer of wrapper
return is.read( ba, off, len );
}
/** /**
* read only a one-byte boolean. * Read bytes. Watch out, read may return fewer bytes than requested.
* *
* @return true or false. * @param ba
* @throws IOException if read fails. * where the bytes go.
* @see java.io.DataInput#readBoolean() * @param off
*/ * offset in buffer, not offset in file.
public final boolean readBoolean() throws IOException * @param len
{ * count of bytes to read.
return dis.readBoolean(); *
} * @return how many bytes read.
* @throws IOException
* if read fails.
*/
public final int read(byte ba[], int off, int len) throws IOException {
// For efficiency, we avoid one layer of wrapper
return is.read(ba, off, len);
}
/** /**
* read byte. * read only a one-byte boolean.
* *
* @return the byte read. * @return true or false.
* @throws IOException if read fails. * @throws IOException
* @see java.io.DataInput#readByte() * if read fails.
*/ * @see java.io.DataInput#readBoolean()
public final byte readByte() throws IOException */
{ @Override
return dis.readByte(); public final boolean readBoolean() throws IOException {
} return dis.readBoolean();
}
/** /**
* Read on char. like DataInputStream.readChar except little endian. * read byte.
* *
* @return little endian 16-bit unicode char from the stream. * @return the byte read.
* @throws IOException if read fails. * @throws IOException
*/ * if read fails.
public final char readChar() throws IOException * @see java.io.DataInput#readByte()
{ */
dis.readFully( work, 0, 2 ); @Override
return ( char ) ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) ); public final byte readByte() throws IOException {
} return dis.readByte();
}
/** /**
* Read a double. like DataInputStream.readDouble except little endian. * Read on char. like DataInputStream.readChar except little endian.
* *
* @return little endian IEEE double from the datastream. * @return little endian 16-bit unicode char from the stream.
* @throws IOException * @throws IOException
*/ * if read fails.
public final double readDouble() throws IOException */
{ @Override
return Double.longBitsToDouble( readLong() ); public final char readChar() throws IOException {
} dis.readFully(work, 0, 2);
return (char) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
}
/** /**
* Read one float. Like DataInputStream.readFloat except little endian. * Read a double. like DataInputStream.readDouble except little endian.
* *
* @return little endian IEEE float from the datastream. * @return little endian IEEE double from the datastream.
* @throws IOException if read fails. * @throws IOException
*/ */
public final float readFloat() throws IOException @Override
{ public final double readDouble() throws IOException {
return Float.intBitsToFloat( readInt() ); return Double.longBitsToDouble(readLong());
} }
/** /**
* Read bytes until the array is filled. * Read one float. Like DataInputStream.readFloat except little endian.
* *
* @see java.io.DataInput#readFully(byte[]) * @return little endian IEEE float from the datastream.
*/ * @throws IOException
public final void readFully( byte ba[] ) throws IOException * if read fails.
{ */
dis.readFully( ba, 0, ba.length ); @Override
} public final float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
/** /**
* Read bytes until the count is satisfied. * Read bytes until the array is filled.
* *
* @throws IOException if read fails. * @see java.io.DataInput#readFully(byte[])
* @see java.io.DataInput#readFully(byte[],int,int) */
*/ @Override
public final void readFully( byte ba[], public final void readFully(byte ba[]) throws IOException {
int off, dis.readFully(ba, 0, ba.length);
int len ) throws IOException }
{
dis.readFully( ba, off, len );
}
/** /**
* Read an int, 32-bits. Like DataInputStream.readInt except little endian. * Read bytes until the count is satisfied.
* *
* @return little-endian binary int from the datastream * @throws IOException
* @throws IOException if read fails. * if read fails.
*/ * @see java.io.DataInput#readFully(byte[],int,int)
public final int readInt() throws IOException */
{ @Override
dis.readFully( work, 0, 4 ); public final void readFully(byte ba[], int off, int len) throws IOException {
return ( work[ 3 ] ) << 24 dis.readFully(ba, off, len);
| ( work[ 2 ] & 0xff ) << 16 }
| ( work[ 1 ] & 0xff ) << 8
| ( work[ 0 ] & 0xff );
}
/** /**
* Read a line. * Read an int, 32-bits. Like DataInputStream.readInt except little endian.
* *
* @return a rough approximation of the 8-bit stream as a 16-bit unicode string * @return little-endian binary int from the datastream
* @throws IOException * @throws IOException
* @noinspection deprecation * if read fails.
* @deprecated This method does not properly convert bytes to characters. Use a Reader instead with a little-endian */
* encoding. @Override
*/ public final int readInt() throws IOException {
public final String readLine() throws IOException dis.readFully(work, 0, 4);
{ return (work[3]) << 24 | (work[2] & 0xff) << 16 | (work[1] & 0xff) << 8
return dis.readLine(); | (work[0] & 0xff);
} }
/** /**
* read a long, 64-bits. Like DataInputStream.readLong except little endian. * Read a line.
* *
* @return little-endian binary long from the datastream. * @return a rough approximation of the 8-bit stream as a 16-bit unicode
* @throws IOException * string
*/ * @throws IOException
public final long readLong() throws IOException * @noinspection deprecation
{ * @deprecated This method does not properly convert bytes to characters.
dis.readFully( work, 0, 8 ); * Use a Reader instead with a little-endian encoding.
return ( long ) ( work[ 7 ] ) << 56 */
| @Deprecated
/* long cast needed or shift done modulo 32 */ @Override
( long ) ( work[ 6 ] & 0xff ) << 48 public final String readLine() throws IOException {
| ( long ) ( work[ 5 ] & 0xff ) << 40 return dis.readLine();
| ( long ) ( work[ 4 ] & 0xff ) << 32 }
| ( long ) ( work[ 3 ] & 0xff ) << 24
| ( long ) ( work[ 2 ] & 0xff ) << 16
| ( long ) ( work[ 1 ] & 0xff ) << 8
| ( long ) ( work[ 0 ] & 0xff );
}
/** /**
* Read short, 16-bits. Like DataInputStream.readShort except little endian. * read a long, 64-bits. Like DataInputStream.readLong except little endian.
* *
* @return little endian binary short from stream. * @return little-endian binary long from the datastream.
* @throws IOException if read fails. * @throws IOException
*/ */
public final short readShort() throws IOException @Override
{ public final long readLong() throws IOException {
dis.readFully( work, 0, 2 ); dis.readFully(work, 0, 8);
return ( short ) ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) ); return (long) (work[7]) << 56 |
} /* long cast needed or shift done modulo 32 */
(long) (work[6] & 0xff) << 48 | (long) (work[5] & 0xff) << 40
| (long) (work[4] & 0xff) << 32 | (long) (work[3] & 0xff) << 24
| (long) (work[2] & 0xff) << 16 | (long) (work[1] & 0xff) << 8
| work[0] & 0xff;
}
/** /**
* Read UTF counted string. * Read short, 16-bits. Like DataInputStream.readShort except little endian.
* *
* @return String read. * @return little endian binary short from stream.
*/ * @throws IOException
public final String readUTF() throws IOException * if read fails.
{ */
return dis.readUTF(); @Override
} public final short readShort() throws IOException {
dis.readFully(work, 0, 2);
return (short) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
}
/** /**
* Read an unsigned byte. Note: returns an int, even though says Byte (non-Javadoc) * Read UTF counted string.
* *
* @throws IOException if read fails. * @return String read.
* @see java.io.DataInput#readUnsignedByte() */
*/ @Override
public final int readUnsignedByte() throws IOException public final String readUTF() throws IOException {
{ return dis.readUTF();
return dis.readUnsignedByte(); }
}
/** /**
* Read an unsigned short, 16 bits. Like DataInputStream.readUnsignedShort except little endian. Note, returns int * Read an unsigned byte. Note: returns an int, even though says Byte
* even though it reads a short. * (non-Javadoc)
* *
* @return little-endian int from the stream. * @throws IOException
* @throws IOException if read fails. * if read fails.
*/ * @see java.io.DataInput#readUnsignedByte()
public final int readUnsignedShort() throws IOException */
{ @Override
dis.readFully( work, 0, 2 ); public final int readUnsignedByte() throws IOException {
return ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) ); return dis.readUnsignedByte();
} }
/** /**
* Skip over bytes in the stream. See the general contract of the <code>skipBytes</code> method of * Read an unsigned short, 16 bits. Like DataInputStream.readUnsignedShort
* <code>DataInput</code>. * except little endian. Note, returns int even though it reads a short.
* <p/> *
* Bytes for this operation are read from the contained input stream. * @return little-endian int from the stream.
* * @throws IOException
* @param n the number of bytes to be skipped. * if read fails.
* */
* @return the actual number of bytes skipped. @Override
* @throws IOException if an I/O error occurs. public final int readUnsignedShort() throws IOException {
*/ dis.readFully(work, 0, 2);
public final int skipBytes( int n ) throws IOException return ((work[1] & 0xff) << 8 | (work[0] & 0xff));
{ }
return dis.skipBytes( n );
} /**
} * Skip over bytes in the stream. See the general contract of the
* <code>skipBytes</code> method of <code>DataInput</code>.
* <p/>
* Bytes for this operation are read from the contained input stream.
*
* @param n
* the number of bytes to be skipped.
*
* @return the actual number of bytes skipped.
* @throws IOException
* if an I/O error occurs.
*/
@Override
public final int skipBytes(int n) throws IOException {
return dis.skipBytes(n);
}
}

View File

@ -26,175 +26,173 @@ import org.junit.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class BuildAndDecodeTest { public class BuildAndDecodeTest {
@BeforeClass @BeforeClass
public static void beforeClass() throws Exception, BrutException { public static void beforeClass() throws Exception, BrutException {
sTmpDir = new ExtFile(OS.createTempDirectory()); sTmpDir = new ExtFile(OS.createTempDirectory());
sTestOrigDir = new ExtFile(sTmpDir, "testapp-orig"); sTestOrigDir = new ExtFile(sTmpDir, "testapp-orig");
sTestNewDir = new ExtFile(sTmpDir, "testapp-new"); sTestNewDir = new ExtFile(sTmpDir, "testapp-new");
LOGGER.info("Unpacking testapp..."); LOGGER.info("Unpacking testapp...");
TestUtils.copyResourceDir(BuildAndDecodeTest.class, TestUtils.copyResourceDir(BuildAndDecodeTest.class,
"brut/apktool/testapp/", sTestOrigDir); "brut/apktool/testapp/", sTestOrigDir);
} }
@AfterClass
public static void afterClass() throws BrutException {
OS.rmdir(sTmpDir);
}
@Test
public void isAaptInstalledTest() throws Exception {
assertEquals(true, isAaptPresent());
}
@Test
public void encodeAndDecodeTest() throws BrutException, IOException {
LOGGER.info("Building testapp.apk..."); @AfterClass
File testApk = new File(sTmpDir, "testapp.apk"); public static void afterClass() throws BrutException {
ExtFile blank = null; OS.rmdir(sTmpDir);
new Androlib().build(sTestOrigDir, testApk, BuildAndDecodeTest.returnStock(),blank,""); }
LOGGER.info("Decoding testapp.apk..."); @Test
ApkDecoder apkDecoder = new ApkDecoder(testApk); public void isAaptInstalledTest() throws Exception {
apkDecoder.setOutDir(sTestNewDir); assertEquals(true, isAaptPresent());
apkDecoder.decode(); }
}
@Test
public void encodeAndDecodeTest() throws BrutException, IOException {
@Test LOGGER.info("Building testapp.apk...");
public void valuesArraysTest() throws BrutException { File testApk = new File(sTmpDir, "testapp.apk");
compareValuesFiles("values-mcc001/arrays.xml"); ExtFile blank = null;
compareValuesFiles("values-mcc002/arrays.xml"); new Androlib().build(sTestOrigDir, testApk,
} BuildAndDecodeTest.returnStock(), blank, "");
@Test LOGGER.info("Decoding testapp.apk...");
public void valuesBoolsTest() throws BrutException { ApkDecoder apkDecoder = new ApkDecoder(testApk);
compareValuesFiles("values-mcc001/bools.xml"); apkDecoder.setOutDir(sTestNewDir);
} apkDecoder.decode();
}
@Test @Test
public void valuesColorsTest() throws BrutException { public void valuesArraysTest() throws BrutException {
compareValuesFiles("values-mcc001/colors.xml"); compareValuesFiles("values-mcc001/arrays.xml");
} compareValuesFiles("values-mcc002/arrays.xml");
}
@Test @Test
public void valuesDimensTest() throws BrutException { public void valuesBoolsTest() throws BrutException {
compareValuesFiles("values-mcc001/dimens.xml"); compareValuesFiles("values-mcc001/bools.xml");
} }
@Test @Test
public void valuesIdsTest() throws BrutException { public void valuesColorsTest() throws BrutException {
compareValuesFiles("values-mcc001/ids.xml"); compareValuesFiles("values-mcc001/colors.xml");
} }
@Test @Test
public void valuesIntegersTest() throws BrutException { public void valuesDimensTest() throws BrutException {
compareValuesFiles("values-mcc001/integers.xml"); compareValuesFiles("values-mcc001/dimens.xml");
} }
@Test @Test
public void valuesStringsTest() throws BrutException { public void valuesIdsTest() throws BrutException {
compareValuesFiles("values-mcc001/strings.xml"); compareValuesFiles("values-mcc001/ids.xml");
} }
@Test @Test
public void valuesReferencesTest() throws BrutException { public void valuesIntegersTest() throws BrutException {
compareValuesFiles("values-mcc002/strings.xml"); compareValuesFiles("values-mcc001/integers.xml");
} }
@Test @Test
public void crossTypeTest() throws BrutException { public void valuesStringsTest() throws BrutException {
compareValuesFiles("values-mcc003/strings.xml"); compareValuesFiles("values-mcc001/strings.xml");
compareValuesFiles("values-mcc003/integers.xml"); }
compareValuesFiles("values-mcc003/bools.xml");
}
@Test @Test
public void xmlLiteralsTest() throws BrutException { public void valuesReferencesTest() throws BrutException {
compareXmlFiles("res/xml/literals.xml"); compareValuesFiles("values-mcc002/strings.xml");
} }
@Test @Test
public void xmlReferencesTest() throws BrutException { public void crossTypeTest() throws BrutException {
compareXmlFiles("res/xml/references.xml"); compareValuesFiles("values-mcc003/strings.xml");
} compareValuesFiles("values-mcc003/integers.xml");
compareValuesFiles("values-mcc003/bools.xml");
@Test }
public void qualifiersTest() throws BrutException {
compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp" +
"-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key" +
"-navhidden-dpad/strings.xml");
}
private static boolean isAaptPresent() throws Exception {
boolean result = true;
try
{
Process proc = Runtime.getRuntime().exec("aapt");
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
String line = null;
while ( (line = br.readLine()) != null){}
} catch (Exception ex){
result = false;
}
return result;
}
private void compareValuesFiles(String path) throws BrutException { @Test
compareXmlFiles("res/" + path, public void xmlLiteralsTest() throws BrutException {
new ElementNameAndAttributeQualifier("name")); compareXmlFiles("res/xml/literals.xml");
} }
private void compareXmlFiles(String path) throws BrutException { @Test
compareXmlFiles(path, null); public void xmlReferencesTest() throws BrutException {
} compareXmlFiles("res/xml/references.xml");
}
private void compareXmlFiles(String path, @Test
ElementQualifier qualifier) throws BrutException { public void qualifiersTest() throws BrutException {
DetailedDiff diff; compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp"
try { + "-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key"
Reader control = new FileReader( + "-navhidden-dpad/strings.xml");
new File(sTestOrigDir, path)); }
Reader test = new FileReader(new File(sTestNewDir, path));
diff = new DetailedDiff(new Diff(control, test)); private static boolean isAaptPresent() throws Exception {
} catch (SAXException ex) { boolean result = true;
throw new BrutException(ex); try {
} catch (IOException ex) { Process proc = Runtime.getRuntime().exec("aapt");
throw new BrutException(ex); BufferedReader br = new BufferedReader(new InputStreamReader(
} proc.getErrorStream()));
String line = null;
while ((line = br.readLine()) != null) {
}
} catch (Exception ex) {
result = false;
}
return result;
}
if (qualifier != null) { private void compareValuesFiles(String path) throws BrutException {
diff.overrideElementQualifier(qualifier); compareXmlFiles("res/" + path, new ElementNameAndAttributeQualifier(
} "name"));
}
assertTrue(path + ": " + private void compareXmlFiles(String path) throws BrutException {
diff.getAllDifferences().toString(), diff.similar()); compareXmlFiles(path, null);
} }
private static HashMap<String, Boolean> returnStock() throws BrutException {
HashMap<String, Boolean> tmp = new HashMap<String, Boolean>();
tmp.put("forceBuildAll", false);
tmp.put("debug", false);
tmp.put("verbose", false);
tmp.put("injectOriginal", false);
tmp.put("framework", false);
tmp.put("update", false);
return tmp;
}
private static ExtFile sTmpDir; private void compareXmlFiles(String path, ElementQualifier qualifier)
private static ExtFile sTestOrigDir; throws BrutException {
private static ExtFile sTestNewDir; DetailedDiff diff;
try {
private final static Logger LOGGER = Reader control = new FileReader(new File(sTestOrigDir, path));
Logger.getLogger(BuildAndDecodeTest.class.getName()); Reader test = new FileReader(new File(sTestNewDir, path));
diff = new DetailedDiff(new Diff(control, test));
} catch (SAXException ex) {
throw new BrutException(ex);
} catch (IOException ex) {
throw new BrutException(ex);
}
if (qualifier != null) {
diff.overrideElementQualifier(qualifier);
}
assertTrue(path + ": " + diff.getAllDifferences().toString(),
diff.similar());
}
private static HashMap<String, Boolean> returnStock() throws BrutException {
HashMap<String, Boolean> tmp = new HashMap<String, Boolean>();
tmp.put("forceBuildAll", false);
tmp.put("debug", false);
tmp.put("verbose", false);
tmp.put("injectOriginal", false);
tmp.put("framework", false);
tmp.put("update", false);
return tmp;
}
private static ExtFile sTmpDir;
private static ExtFile sTestOrigDir;
private static ExtFile sTestNewDir;
private final static Logger LOGGER = Logger
.getLogger(BuildAndDecodeTest.class.getName());
} }

View File

@ -1,4 +1,4 @@
/** /**
* Copyright 2011 Ryszard Wiśniewski <brut.alll@gmail.com> * Copyright 2011 Ryszard Wiśniewski <brut.alll@gmail.com>
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -32,107 +32,110 @@ import org.xmlpull.v1.*;
*/ */
public abstract class TestUtils { public abstract class TestUtils {
public static Map<String, String> parseStringsXml(File file) public static Map<String, String> parseStringsXml(File file)
throws BrutException { throws BrutException {
try { try {
XmlPullParser xpp = XmlPullParserFactory.newInstance().newPullParser(); XmlPullParser xpp = XmlPullParserFactory.newInstance()
xpp.setInput(new FileReader(file)); .newPullParser();
xpp.setInput(new FileReader(file));
int eventType; int eventType;
String key = null; String key = null;
Map<String, String> map = new HashMap<String, String>(); Map<String, String> map = new HashMap<String, String>();
while ((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) { while ((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) {
switch (eventType) { switch (eventType) {
case XmlPullParser.START_TAG: case XmlPullParser.START_TAG:
if ("string".equals(xpp.getName())) { if ("string".equals(xpp.getName())) {
int attrCount = xpp.getAttributeCount(); int attrCount = xpp.getAttributeCount();
for (int i = 0; i < attrCount; i++) { for (int i = 0; i < attrCount; i++) {
if ("name".equals(xpp.getAttributeName(i))) { if ("name".equals(xpp.getAttributeName(i))) {
key = xpp.getAttributeValue(i); key = xpp.getAttributeValue(i);
break; break;
} }
} }
} }
break; break;
case XmlPullParser.END_TAG: case XmlPullParser.END_TAG:
if ("string".equals(xpp.getName())) { if ("string".equals(xpp.getName())) {
key = null; key = null;
} }
break; break;
case XmlPullParser.TEXT: case XmlPullParser.TEXT:
if (key != null) { if (key != null) {
map.put(key, xpp.getText()); map.put(key, xpp.getText());
} }
break; break;
} }
} }
return map; return map;
} catch (IOException ex) { } catch (IOException ex) {
throw new BrutException(ex); throw new BrutException(ex);
} catch (XmlPullParserException ex) { } catch (XmlPullParserException ex) {
throw new BrutException(ex); throw new BrutException(ex);
} }
} }
/* TODO: move to brut.util.Jar - it's not possible for now, because below /*
* implementation uses brut.dir. I think I should merge all my projects to * TODO: move to brut.util.Jar - it's not possible for now, because below
* single brut.common . * implementation uses brut.dir. I think I should merge all my projects to
*/ * single brut.common .
public static void copyResourceDir(Class class_, String dirPath, File out) */
throws BrutException { public static void copyResourceDir(Class class_, String dirPath, File out)
if (! out.exists()) { throws BrutException {
out.mkdirs(); if (!out.exists()) {
} out.mkdirs();
copyResourceDir(class_, dirPath, new FileDirectory(out)); }
} copyResourceDir(class_, dirPath, new FileDirectory(out));
}
public static void copyResourceDir(Class class_, String dirPath, public static void copyResourceDir(Class class_, String dirPath,
Directory out) throws BrutException { Directory out) throws BrutException {
if (class_ == null) { if (class_ == null) {
class_ = Class.class; class_ = Class.class;
} }
URL dirURL = class_.getClassLoader().getResource(dirPath); URL dirURL = class_.getClassLoader().getResource(dirPath);
if (dirURL != null && dirURL.getProtocol().equals("file")) { if (dirURL != null && dirURL.getProtocol().equals("file")) {
DirUtil.copyToDir(new FileDirectory(dirURL.getFile()), out); DirUtil.copyToDir(new FileDirectory(dirURL.getFile()), out);
return; return;
} }
if (dirURL == null) { if (dirURL == null) {
String className = class_.getName().replace(".", "/") + ".class"; String className = class_.getName().replace(".", "/") + ".class";
dirURL = class_.getClassLoader().getResource(className); dirURL = class_.getClassLoader().getResource(className);
} }
if (dirURL.getProtocol().equals("jar")) {
String jarPath;
try {
jarPath = URLDecoder.decode(
dirURL.getPath().substring(5,
dirURL.getPath().indexOf("!")), "UTF-8");
} catch (UnsupportedEncodingException ex) {
throw new BrutException(ex);
}
DirUtil.copyToDir(new FileDirectory(jarPath), out);
}
}
if (dirURL.getProtocol().equals("jar")) { public static class ResValueElementQualifier implements ElementQualifier {
String jarPath;
try {
jarPath = URLDecoder.decode(dirURL.getPath().substring(
5, dirURL.getPath().indexOf("!")), "UTF-8");
} catch (UnsupportedEncodingException ex) {
throw new BrutException(ex);
}
DirUtil.copyToDir(new FileDirectory(jarPath), out);
}
}
@Override
public boolean qualifyForComparison(Element control, Element test) {
String controlType = control.getTagName();
if ("item".equals(controlType)) {
controlType = control.getAttribute("type");
}
public static class ResValueElementQualifier implements ElementQualifier { String testType = test.getTagName();
if ("item".equals(testType)) {
testType = test.getAttribute("type");
}
public boolean qualifyForComparison(Element control, Element test) { return controlType.equals(testType)
String controlType = control.getTagName(); && control.getAttribute("name").equals(
if ("item".equals(controlType)) { test.getAttribute("name"));
controlType = control.getAttribute("type"); }
} }
String testType = test.getTagName();
if ("item".equals(testType)) {
testType = test.getAttribute("type");
}
return controlType.equals(testType) && control.getAttribute("name")
.equals(test.getAttribute("name"));
}
}
} }