mirror of
https://github.com/revanced/Apktool.git
synced 2025-05-16 21:07:06 +02:00
This commit is contained in:
parent
5174001e5b
commit
321dcfa91b
@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package brut.apktool;
|
package brut.apktool;
|
||||||
|
|
||||||
import brut.androlib.*;
|
import brut.androlib.Androlib;
|
||||||
|
import brut.androlib.AndrolibException;
|
||||||
|
import brut.androlib.ApkDecoder;
|
||||||
import brut.androlib.err.CantFindFrameworkResException;
|
import brut.androlib.err.CantFindFrameworkResException;
|
||||||
import brut.androlib.err.InFileNotFoundException;
|
import brut.androlib.err.InFileNotFoundException;
|
||||||
import brut.androlib.err.OutDirExistsException;
|
import brut.androlib.err.OutDirExistsException;
|
||||||
@ -209,6 +211,7 @@ public class Main {
|
|||||||
private static void usage() {
|
private static void usage() {
|
||||||
System.out.println(
|
System.out.println(
|
||||||
"Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" +
|
"Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" +
|
||||||
|
"Edited by iBotPeaches (@iBotPeaches) \n" +
|
||||||
"Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>\n" +
|
"Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>\n" +
|
||||||
"Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" +
|
"Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
@ -236,7 +239,7 @@ public class Main {
|
|||||||
" \"Invalid config flags detected. Dropping resources\", but you\n" +
|
" \"Invalid config flags detected. Dropping resources\", but you\n" +
|
||||||
" want to decode them anyway, even with errors. You will have to\n" +
|
" want to decode them anyway, even with errors. You will have to\n" +
|
||||||
" fix them manually before building." +
|
" fix them manually before building." +
|
||||||
"\n" +
|
"\n\n" +
|
||||||
" b[uild] [OPTS] [<app_path>] [<out_file>]\n" +
|
" b[uild] [OPTS] [<app_path>] [<out_file>]\n" +
|
||||||
" Build an apk from already decoded application located in <app_path>.\n" +
|
" Build an apk from already decoded application located in <app_path>.\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
299
apktool-cli/src/main/java/brut/apktool/Main.java~
Normal file
299
apktool-cli/src/main/java/brut/apktool/Main.java~
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2011 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package brut.apktool;
|
||||||
|
|
||||||
|
import brut.androlib.*;
|
||||||
|
import brut.androlib.err.CantFindFrameworkResException;
|
||||||
|
import brut.androlib.err.InFileNotFoundException;
|
||||||
|
import brut.androlib.err.OutDirExistsException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.logging.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
*/
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args)
|
||||||
|
throws IOException, AndrolibException, InterruptedException {
|
||||||
|
try {
|
||||||
|
Verbosity verbosity = Verbosity.NORMAL;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < args.length; i++) {
|
||||||
|
String opt = args[i];
|
||||||
|
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) {
|
||||||
|
throw new InvalidArgsError();
|
||||||
|
}
|
||||||
|
String cmd = args[i];
|
||||||
|
args = Arrays.copyOfRange(args, i + 1, args.length);
|
||||||
|
|
||||||
|
if ("d".equals(cmd) || "decode".equals(cmd)) {
|
||||||
|
cmdDecode(args);
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void cmdDecode(String[] args) throws InvalidArgsError,
|
||||||
|
AndrolibException {
|
||||||
|
ApkDecoder decoder = new ApkDecoder();
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < args.length; i++) {
|
||||||
|
String opt = args[i];
|
||||||
|
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 ("-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 {
|
||||||
|
throw new InvalidArgsError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String outName = null;
|
||||||
|
if (args.length == i + 2) {
|
||||||
|
outName = args[i + 1];
|
||||||
|
} else if (args.length == i + 1) {
|
||||||
|
outName = args[i];
|
||||||
|
outName = outName.endsWith(".apk") ?
|
||||||
|
outName.substring(0, outName.length() - 4) : outName + ".out";
|
||||||
|
outName = new File(outName).getName();
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgsError();
|
||||||
|
}
|
||||||
|
File outDir = new File(outName);
|
||||||
|
decoder.setOutDir(outDir);
|
||||||
|
decoder.setApkFile(new File(args[i]));
|
||||||
|
|
||||||
|
try {
|
||||||
|
decoder.decode();
|
||||||
|
} catch (OutDirExistsException ex) {
|
||||||
|
System.out.println(
|
||||||
|
"Destination directory (" + outDir.getAbsolutePath() + ") " +
|
||||||
|
"already exists. Use -f switch if you want to overwrite it.");
|
||||||
|
System.exit(1);
|
||||||
|
} catch (InFileNotFoundException ex) {
|
||||||
|
System.out.println(
|
||||||
|
"Input file (" + args[i] + ") " +
|
||||||
|
"was not found or was not readable.");
|
||||||
|
System.exit(1);
|
||||||
|
} catch (CantFindFrameworkResException ex) {
|
||||||
|
System.out.println(
|
||||||
|
"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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void cmdBuild(String[] args) throws InvalidArgsError,
|
||||||
|
AndrolibException {
|
||||||
|
boolean forceBuildAll = false;
|
||||||
|
boolean debug = false;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < args.length; i++) {
|
||||||
|
String opt = args[i];
|
||||||
|
if (! opt.startsWith("-")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ("-f".equals(opt) || "--force-all".equals(opt)) {
|
||||||
|
forceBuildAll = true;
|
||||||
|
} else if ("-d".equals(opt) || "--debug".equals(opt)) {
|
||||||
|
debug = true;
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgsError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String appDirName;
|
||||||
|
File outFile = null;
|
||||||
|
switch (args.length - i) {
|
||||||
|
case 0:
|
||||||
|
appDirName = ".";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
outFile = new File(args[i + 1]);
|
||||||
|
case 1:
|
||||||
|
appDirName = args[i];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidArgsError();
|
||||||
|
}
|
||||||
|
|
||||||
|
new Androlib().build(new File(appDirName), outFile, forceBuildAll,
|
||||||
|
debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void cmdInstallFramework(String[] args)
|
||||||
|
throws AndrolibException {
|
||||||
|
String tag = null;
|
||||||
|
switch (args.length) {
|
||||||
|
case 2:
|
||||||
|
tag = args[1];
|
||||||
|
case 1:
|
||||||
|
new Androlib().installFramework(new File(args[0]), tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidArgsError();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void cmdPublicizeResources(String[] args)
|
||||||
|
throws InvalidArgsError, AndrolibException {
|
||||||
|
if (args.length != 1) {
|
||||||
|
throw new InvalidArgsError();
|
||||||
|
}
|
||||||
|
|
||||||
|
new Androlib().publicizeResources(new File(args[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void usage() {
|
||||||
|
System.out.println(
|
||||||
|
"Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" +
|
||||||
|
"Edited by iBotPeachs of http://miuiandroid.com\n" +
|
||||||
|
"Copyright 2010 Ryszard Wiśniewski <brut.alll@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" +
|
||||||
|
" -f, --force\n" +
|
||||||
|
" Force delete destination directory.\n" +
|
||||||
|
" -t <tag>, --frame-tag <tag>\n" +
|
||||||
|
" Try to use framework files tagged by <tag>.\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" +
|
||||||
|
" 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" +
|
||||||
|
"\n" +
|
||||||
|
" if|install-framework <framework.apk> [<tag>]\n" +
|
||||||
|
" Install framework file to your system.\n" +
|
||||||
|
"\n" +
|
||||||
|
"For additional info, see: http://code.google.com/p/android-apktool/"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setupLogging(Verbosity verbosity) {
|
||||||
|
Logger logger = Logger.getLogger("");
|
||||||
|
for (Handler handler : logger.getHandlers()) {
|
||||||
|
logger.removeHandler(handler);
|
||||||
|
}
|
||||||
|
if (verbosity == Verbosity.QUIET) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handler handler = new ConsoleHandler();
|
||||||
|
logger.addHandler(handler);
|
||||||
|
|
||||||
|
if (verbosity == Verbosity.VERBOSE) {
|
||||||
|
handler.setLevel(Level.ALL);
|
||||||
|
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 {
|
||||||
|
NORMAL, VERBOSE, QUIET;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class InvalidArgsError extends AndrolibException {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<groupId>brut.apktool</groupId>
|
<groupId>brut.apktool</groupId>
|
||||||
<artifactId>apktool-lib</artifactId>
|
<artifactId>apktool-lib</artifactId>
|
||||||
<version>1.4.4-SNAPSHOT</version>
|
<version>1.4.5-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
|
@ -24,7 +24,8 @@ import brut.androlib.res.util.ExtFile;
|
|||||||
import brut.androlib.src.SmaliBuilder;
|
import brut.androlib.src.SmaliBuilder;
|
||||||
import brut.androlib.src.SmaliDecoder;
|
import brut.androlib.src.SmaliDecoder;
|
||||||
import brut.common.BrutException;
|
import brut.common.BrutException;
|
||||||
import brut.directory.*;
|
import brut.directory.Directory;
|
||||||
|
import brut.directory.DirectoryException;
|
||||||
import brut.util.BrutIO;
|
import brut.util.BrutIO;
|
||||||
import brut.util.OS;
|
import brut.util.OS;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
443
apktool-lib/src/main/java/brut/androlib/Androlib.java~
Normal file
443
apktool-lib/src/main/java/brut/androlib/Androlib.java~
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2011 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package brut.androlib;
|
||||||
|
|
||||||
|
import brut.androlib.java.AndrolibJava;
|
||||||
|
import brut.androlib.res.AndrolibResources;
|
||||||
|
import brut.androlib.res.data.ResPackage;
|
||||||
|
import brut.androlib.res.data.ResTable;
|
||||||
|
import brut.androlib.res.util.ExtFile;
|
||||||
|
import brut.androlib.src.SmaliBuilder;
|
||||||
|
import brut.androlib.src.SmaliDecoder;
|
||||||
|
import brut.common.BrutException;
|
||||||
|
import brut.directory.*;
|
||||||
|
import brut.util.BrutIO;
|
||||||
|
import brut.util.OS;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import org.yaml.snakeyaml.DumperOptions;
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
*/
|
||||||
|
public class Androlib {
|
||||||
|
private final AndrolibResources mAndRes = new AndrolibResources();
|
||||||
|
|
||||||
|
public ResTable getResTable(ExtFile apkFile) throws AndrolibException {
|
||||||
|
return mAndRes.getResTable(apkFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decodeSourcesRaw(ExtFile apkFile, File outDir, boolean debug)
|
||||||
|
throws AndrolibException {
|
||||||
|
try {
|
||||||
|
if (debug) {
|
||||||
|
LOGGER.warning("Debug mode not available.");
|
||||||
|
}
|
||||||
|
Directory apk = apkFile.getDirectory();
|
||||||
|
LOGGER.info("Copying raw classes.dex file...");
|
||||||
|
apkFile.getDirectory().copyToDir(outDir, "classes.dex");
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decodeSourcesSmali(File apkFile, File outDir, boolean debug)
|
||||||
|
throws AndrolibException {
|
||||||
|
try {
|
||||||
|
File smaliDir = new File(outDir, SMALI_DIRNAME);
|
||||||
|
OS.rmdir(smaliDir);
|
||||||
|
smaliDir.mkdirs();
|
||||||
|
LOGGER.info("Baksmaling...");
|
||||||
|
SmaliDecoder.decode(apkFile, smaliDir, debug);
|
||||||
|
} catch (BrutException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decodeSourcesJava(ExtFile apkFile, File outDir, boolean debug)
|
||||||
|
throws AndrolibException {
|
||||||
|
LOGGER.info("Decoding Java sources...");
|
||||||
|
new AndrolibJava().decode(apkFile, outDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decodeResourcesRaw(ExtFile apkFile, File outDir)
|
||||||
|
throws AndrolibException {
|
||||||
|
try {
|
||||||
|
Directory apk = apkFile.getDirectory();
|
||||||
|
LOGGER.info("Copying raw resources...");
|
||||||
|
apkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES);
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decodeResourcesFull(ExtFile apkFile, File outDir,
|
||||||
|
ResTable resTable) throws AndrolibException {
|
||||||
|
mAndRes.decode(resTable, apkFile, outDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decodeRawFiles(ExtFile apkFile, File outDir)
|
||||||
|
throws AndrolibException {
|
||||||
|
LOGGER.info("Copying assets and libs...");
|
||||||
|
try {
|
||||||
|
Directory in = apkFile.getDirectory();
|
||||||
|
if (in.containsDir("assets")) {
|
||||||
|
in.copyToDir(outDir, "assets");
|
||||||
|
}
|
||||||
|
if (in.containsDir("lib")) {
|
||||||
|
in.copyToDir(outDir, "lib");
|
||||||
|
}
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeMetaFile(File mOutDir, Map<String, Object> meta)
|
||||||
|
throws AndrolibException {
|
||||||
|
DumperOptions options = new DumperOptions();
|
||||||
|
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||||
|
// options.setIndent(4);
|
||||||
|
Yaml yaml = new Yaml(options);
|
||||||
|
|
||||||
|
FileWriter writer = null;
|
||||||
|
try {
|
||||||
|
writer = new FileWriter(new File(mOutDir, "apktool.yml"));
|
||||||
|
yaml.dump(meta, writer);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
} finally {
|
||||||
|
if (writer != null) {
|
||||||
|
try {
|
||||||
|
writer.close();
|
||||||
|
} catch (IOException ex) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> readMetaFile(ExtFile appDir)
|
||||||
|
throws AndrolibException {
|
||||||
|
InputStream in = null;
|
||||||
|
try {
|
||||||
|
in = appDir.getDirectory().getFileInput("apktool.yml");
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
return (Map<String, Object>) yaml.load(in);
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
} finally {
|
||||||
|
if (in != null) {
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException ex) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void build(File appDir, File outFile, boolean forceBuildAll,
|
||||||
|
boolean debug) throws AndrolibException {
|
||||||
|
build(new ExtFile(appDir), outFile, forceBuildAll, debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void build(ExtFile appDir, File outFile, boolean forceBuildAll,
|
||||||
|
boolean debug) throws AndrolibException {
|
||||||
|
Map<String, Object> meta = readMetaFile(appDir);
|
||||||
|
Object t1 = meta.get("isFrameworkApk");
|
||||||
|
boolean framework = t1 == null ? false : (Boolean) t1;
|
||||||
|
|
||||||
|
if (outFile == null) {
|
||||||
|
String outFileName = (String) meta.get("apkFileName");
|
||||||
|
outFile = new File(appDir, "dist" + File.separator +
|
||||||
|
(outFileName == null ? "out.apk" : outFileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
new File(appDir, APK_DIRNAME).mkdirs();
|
||||||
|
buildSources(appDir, forceBuildAll, debug);
|
||||||
|
buildResources(appDir, forceBuildAll, framework,
|
||||||
|
(Map<String, Object>) meta.get("usesFramework"));
|
||||||
|
buildLib(appDir, forceBuildAll);
|
||||||
|
buildApk(appDir, outFile, framework);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildSources(File appDir, boolean forceBuildAll, boolean debug)
|
||||||
|
throws AndrolibException {
|
||||||
|
if (! buildSourcesRaw(appDir, forceBuildAll, debug)
|
||||||
|
&& ! buildSourcesSmali(appDir, forceBuildAll, debug)
|
||||||
|
&& ! buildSourcesJava(appDir, forceBuildAll, debug)
|
||||||
|
) {
|
||||||
|
LOGGER.warning("Could not find sources");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean buildSourcesRaw(File appDir, boolean forceBuildAll,
|
||||||
|
boolean debug) throws AndrolibException {
|
||||||
|
try {
|
||||||
|
File working = new File(appDir, "classes.dex");
|
||||||
|
if (! working.exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (debug) {
|
||||||
|
LOGGER.warning("Debug mode not available.");
|
||||||
|
}
|
||||||
|
File stored = new File(appDir, APK_DIRNAME + "/classes.dex");
|
||||||
|
if (forceBuildAll || isModified(working, stored)) {
|
||||||
|
LOGGER.info("Copying classes.dex file...");
|
||||||
|
BrutIO.copyAndClose(new FileInputStream(working),
|
||||||
|
new FileOutputStream(stored));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean buildSourcesSmali(File appDir, boolean forceBuildAll,
|
||||||
|
boolean debug) throws AndrolibException {
|
||||||
|
ExtFile smaliDir = new ExtFile(appDir, "smali");
|
||||||
|
if (! smaliDir.exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
File dex = new File(appDir, APK_DIRNAME + "/classes.dex");
|
||||||
|
if (! forceBuildAll) {
|
||||||
|
LOGGER.info("Checking whether sources has changed...");
|
||||||
|
}
|
||||||
|
if (forceBuildAll || isModified(smaliDir, dex)) {
|
||||||
|
LOGGER.info("Smaling...");
|
||||||
|
dex.delete();
|
||||||
|
SmaliBuilder.build(smaliDir, dex, debug);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean buildSourcesJava(File appDir, boolean forceBuildAll,
|
||||||
|
boolean debug) throws AndrolibException {
|
||||||
|
File javaDir = new File(appDir, "src");
|
||||||
|
if (! javaDir.exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
File dex = new File(appDir, APK_DIRNAME + "/classes.dex");
|
||||||
|
if (! forceBuildAll) {
|
||||||
|
LOGGER.info("Checking whether sources has changed...");
|
||||||
|
}
|
||||||
|
if (forceBuildAll || isModified(javaDir, dex)) {
|
||||||
|
LOGGER.info("Building java sources...");
|
||||||
|
dex.delete();
|
||||||
|
new AndrolibJava().build(javaDir, dex);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildResources(ExtFile appDir, boolean forceBuildAll,
|
||||||
|
boolean framework, Map<String, Object> usesFramework)
|
||||||
|
throws AndrolibException {
|
||||||
|
if (! buildResourcesRaw(appDir, forceBuildAll)
|
||||||
|
&& ! buildResourcesFull(appDir, forceBuildAll, framework,
|
||||||
|
usesFramework)) {
|
||||||
|
LOGGER.warning("Could not find resources");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean buildResourcesRaw(ExtFile appDir, boolean forceBuildAll)
|
||||||
|
throws AndrolibException {
|
||||||
|
try {
|
||||||
|
if (! new File(appDir, "resources.arsc").exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
File apkDir = new File(appDir, APK_DIRNAME);
|
||||||
|
if (! forceBuildAll) {
|
||||||
|
LOGGER.info("Checking whether resources has changed...");
|
||||||
|
}
|
||||||
|
if (forceBuildAll || isModified(
|
||||||
|
newFiles(APK_RESOURCES_FILENAMES, appDir),
|
||||||
|
newFiles(APK_RESOURCES_FILENAMES, apkDir))) {
|
||||||
|
LOGGER.info("Copying raw resources...");
|
||||||
|
appDir.getDirectory()
|
||||||
|
.copyToDir(apkDir, APK_RESOURCES_FILENAMES);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean buildResourcesFull(File appDir, boolean forceBuildAll,
|
||||||
|
boolean framework, Map<String, Object> usesFramework)
|
||||||
|
throws AndrolibException {
|
||||||
|
try {
|
||||||
|
if (! new File(appDir, "res").exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (! forceBuildAll) {
|
||||||
|
LOGGER.info("Checking whether resources has changed...");
|
||||||
|
}
|
||||||
|
File apkDir = new File(appDir, APK_DIRNAME);
|
||||||
|
if (forceBuildAll || isModified(
|
||||||
|
newFiles(APP_RESOURCES_FILENAMES, appDir),
|
||||||
|
newFiles(APK_RESOURCES_FILENAMES, apkDir))) {
|
||||||
|
LOGGER.info("Building resources...");
|
||||||
|
|
||||||
|
File apkFile = File.createTempFile("APKTOOL", null);
|
||||||
|
apkFile.delete();
|
||||||
|
|
||||||
|
File ninePatch = new File(appDir, "9patch");
|
||||||
|
if (! ninePatch.exists()) {
|
||||||
|
ninePatch = null;
|
||||||
|
}
|
||||||
|
mAndRes.aaptPackage(
|
||||||
|
apkFile,
|
||||||
|
new File(appDir, "AndroidManifest.xml"),
|
||||||
|
new File(appDir, "res"),
|
||||||
|
ninePatch, null, parseUsesFramework(usesFramework),
|
||||||
|
false, framework
|
||||||
|
);
|
||||||
|
|
||||||
|
Directory tmpDir = new ExtFile(apkFile).getDirectory();
|
||||||
|
tmpDir.copyToDir(apkDir,
|
||||||
|
tmpDir.containsDir("res") ? APK_RESOURCES_FILENAMES :
|
||||||
|
APK_RESOURCES_WITHOUT_RES_FILENAMES);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
//throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildLib(File appDir, boolean forceBuildAll)
|
||||||
|
throws AndrolibException {
|
||||||
|
File working = new File(appDir, "lib");
|
||||||
|
if (! working.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File stored = new File(appDir, APK_DIRNAME + "/lib");
|
||||||
|
if (forceBuildAll || isModified(working, stored)) {
|
||||||
|
LOGGER.info("Copying libs...");
|
||||||
|
try {
|
||||||
|
OS.rmdir(stored);
|
||||||
|
OS.cpdir(working, stored);
|
||||||
|
} catch (BrutException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildApk(File appDir, File outApk, boolean framework)
|
||||||
|
throws AndrolibException {
|
||||||
|
LOGGER.info("Building apk file...");
|
||||||
|
if (outApk.exists()) {
|
||||||
|
outApk.delete();
|
||||||
|
} else {
|
||||||
|
File outDir = outApk.getParentFile();
|
||||||
|
if (outDir != null && ! outDir.exists()) {
|
||||||
|
outDir.mkdirs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File assetDir = new File(appDir, "assets");
|
||||||
|
if (! assetDir.exists()) {
|
||||||
|
assetDir = null;
|
||||||
|
}
|
||||||
|
mAndRes.aaptPackage(outApk, null, null,
|
||||||
|
new File(appDir, APK_DIRNAME), assetDir, null, false, framework);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void publicizeResources(File arscFile) throws AndrolibException {
|
||||||
|
mAndRes.publicizeResources(arscFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void installFramework(File frameFile, String tag)
|
||||||
|
throws AndrolibException {
|
||||||
|
mAndRes.installFramework(frameFile, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFrameworkApk(ResTable resTable) {
|
||||||
|
for (ResPackage pkg : resTable.listMainPackages()) {
|
||||||
|
if (pkg.getId() < 64) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getVersion() {
|
||||||
|
String version = ApktoolProperties.get("version");
|
||||||
|
return version.endsWith("-SNAPSHOT") ?
|
||||||
|
version.substring(0, version.length() - 9) + '.' +
|
||||||
|
ApktoolProperties.get("git.commit.id.abbrev")
|
||||||
|
: version;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File[] parseUsesFramework(Map<String, Object> usesFramework)
|
||||||
|
throws AndrolibException {
|
||||||
|
if (usesFramework == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> ids = (List<Integer>) usesFramework.get("ids");
|
||||||
|
if (ids == null || ids.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String tag = (String) usesFramework.get("tag");
|
||||||
|
File[] files = new File[ids.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (int id : ids) {
|
||||||
|
files[i++] = mAndRes.getFrameworkApk(id, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isModified(File working, File stored) {
|
||||||
|
if (! stored.exists()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return BrutIO.recursiveModifiedTime(working) >
|
||||||
|
BrutIO.recursiveModifiedTime(stored);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isModified(File[] working, File[] stored) {
|
||||||
|
for (int i = 0; i < stored.length; i++) {
|
||||||
|
if (! stored[i].exists()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BrutIO.recursiveModifiedTime(working) >
|
||||||
|
BrutIO.recursiveModifiedTime(stored);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File[] newFiles(String[] names, File dir) {
|
||||||
|
File[] files = new File[names.length];
|
||||||
|
for (int i = 0; i < names.length; i++) {
|
||||||
|
files[i] = new File(dir, names[i]);
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Logger LOGGER =
|
||||||
|
Logger.getLogger(Androlib.class.getName());
|
||||||
|
|
||||||
|
private final static String SMALI_DIRNAME = "smali";
|
||||||
|
private final static String APK_DIRNAME = "build/apk";
|
||||||
|
private final static String[] APK_RESOURCES_FILENAMES =
|
||||||
|
new String[]{"resources.arsc", "AndroidManifest.xml", "res"};
|
||||||
|
private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES =
|
||||||
|
new String[]{"resources.arsc", "AndroidManifest.xml"};
|
||||||
|
private final static String[] APP_RESOURCES_FILENAMES =
|
||||||
|
new String[]{"AndroidManifest.xml", "res"};
|
||||||
|
}
|
@ -26,7 +26,10 @@ import brut.common.BrutException;
|
|||||||
import brut.directory.DirectoryException;
|
import brut.directory.DirectoryException;
|
||||||
import brut.util.OS;
|
import brut.util.OS;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
234
apktool-lib/src/main/java/brut/androlib/ApkDecoder.java~
Normal file
234
apktool-lib/src/main/java/brut/androlib/ApkDecoder.java~
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2011 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package brut.androlib;
|
||||||
|
|
||||||
|
import brut.androlib.err.InFileNotFoundException;
|
||||||
|
import brut.androlib.err.OutDirExistsException;
|
||||||
|
import brut.androlib.res.AndrolibResources;
|
||||||
|
import brut.androlib.res.data.ResPackage;
|
||||||
|
import brut.androlib.res.data.ResTable;
|
||||||
|
import brut.androlib.res.util.ExtFile;
|
||||||
|
import brut.common.BrutException;
|
||||||
|
import brut.directory.DirectoryException;
|
||||||
|
import brut.util.OS;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
*/
|
||||||
|
public class ApkDecoder {
|
||||||
|
public ApkDecoder() {
|
||||||
|
this(new Androlib());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApkDecoder(Androlib androlib) {
|
||||||
|
mAndrolib = androlib;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApkDecoder(File apkFile) {
|
||||||
|
this(apkFile, new Androlib());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApkDecoder(File apkFile, Androlib androlib) {
|
||||||
|
mAndrolib = androlib;
|
||||||
|
setApkFile(apkFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApkFile(File apkFile) {
|
||||||
|
mApkFile = new ExtFile(apkFile);
|
||||||
|
mResTable = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutDir(File outDir) throws AndrolibException {
|
||||||
|
mOutDir = outDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decode() throws AndrolibException {
|
||||||
|
File outDir = getOutDir();
|
||||||
|
|
||||||
|
if (! mForceDelete && outDir.exists()) {
|
||||||
|
throw new OutDirExistsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! mApkFile.isFile() || ! mApkFile.canRead() ) {
|
||||||
|
throw new InFileNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
OS.rmdir(outDir);
|
||||||
|
} catch (BrutException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
outDir.mkdirs();
|
||||||
|
|
||||||
|
if (hasSources()) {
|
||||||
|
switch (mDecodeSources) {
|
||||||
|
case DECODE_SOURCES_NONE:
|
||||||
|
mAndrolib.decodeSourcesRaw(mApkFile, outDir, mDebug);
|
||||||
|
break;
|
||||||
|
case DECODE_SOURCES_SMALI:
|
||||||
|
mAndrolib.decodeSourcesSmali(mApkFile, outDir, mDebug);
|
||||||
|
break;
|
||||||
|
case DECODE_SOURCES_JAVA:
|
||||||
|
mAndrolib.decodeSourcesJava(mApkFile, outDir, mDebug);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasResources()) {
|
||||||
|
switch (mDecodeResources) {
|
||||||
|
case DECODE_RESOURCES_NONE:
|
||||||
|
mAndrolib.decodeResourcesRaw(mApkFile, outDir);
|
||||||
|
break;
|
||||||
|
case DECODE_RESOURCES_FULL:
|
||||||
|
mAndrolib.decodeResourcesFull(mApkFile, outDir,
|
||||||
|
getResTable());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mAndrolib.decodeRawFiles(mApkFile, outDir);
|
||||||
|
writeMetaFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecodeSources(short mode) throws AndrolibException {
|
||||||
|
if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI
|
||||||
|
&& mode != DECODE_SOURCES_JAVA) {
|
||||||
|
throw new AndrolibException("Invalid decode sources mode: " + mode);
|
||||||
|
}
|
||||||
|
mDecodeSources = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecodeResources(short mode) throws AndrolibException {
|
||||||
|
if (mode != DECODE_RESOURCES_NONE && mode != DECODE_RESOURCES_FULL) {
|
||||||
|
throw new AndrolibException("Invalid decode resources mode");
|
||||||
|
}
|
||||||
|
mDecodeResources = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDebugMode(boolean debug) {
|
||||||
|
mDebug = debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setForceDelete(boolean forceDelete) {
|
||||||
|
mForceDelete = forceDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFrameworkTag(String tag) throws AndrolibException {
|
||||||
|
mFrameTag = tag;
|
||||||
|
if (mResTable != null) {
|
||||||
|
getResTable().setFrameTag(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeepBrokenResources(boolean keepBrokenResources) {
|
||||||
|
mKeepBrokenResources = keepBrokenResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResTable getResTable() throws AndrolibException {
|
||||||
|
if (mResTable == null) {
|
||||||
|
if (! hasResources()) {
|
||||||
|
throw new AndrolibException(
|
||||||
|
"Apk doesn't containt resources.arsc file");
|
||||||
|
}
|
||||||
|
AndrolibResources.sKeepBroken = mKeepBrokenResources;
|
||||||
|
mResTable = mAndrolib.getResTable(mApkFile);
|
||||||
|
mResTable.setFrameTag(mFrameTag);
|
||||||
|
}
|
||||||
|
return mResTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSources() throws AndrolibException {
|
||||||
|
try {
|
||||||
|
return mApkFile.getDirectory().containsFile("classes.dex");
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasResources() throws AndrolibException {
|
||||||
|
try {
|
||||||
|
return mApkFile.getDirectory().containsFile("resources.arsc");
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static short DECODE_SOURCES_NONE = 0x0000;
|
||||||
|
public final static short DECODE_SOURCES_SMALI = 0x0001;
|
||||||
|
public final static short DECODE_SOURCES_JAVA = 0x0002;
|
||||||
|
|
||||||
|
public final static short DECODE_RESOURCES_NONE = 0x0100;
|
||||||
|
public final static short DECODE_RESOURCES_FULL = 0x0101;
|
||||||
|
|
||||||
|
|
||||||
|
private File getOutDir() throws AndrolibException {
|
||||||
|
if (mOutDir == null) {
|
||||||
|
throw new AndrolibException("Out dir not set");
|
||||||
|
}
|
||||||
|
return mOutDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeMetaFile() throws AndrolibException {
|
||||||
|
Map<String, Object> meta = new LinkedHashMap<String, Object>();
|
||||||
|
meta.put("version", Androlib.getVersion());
|
||||||
|
meta.put("apkFileName", mApkFile.getName());
|
||||||
|
|
||||||
|
if (mDecodeResources != DECODE_RESOURCES_NONE && hasResources()) {
|
||||||
|
meta.put("isFrameworkApk",
|
||||||
|
Boolean.valueOf(mAndrolib.isFrameworkApk(getResTable())));
|
||||||
|
putUsesFramework(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
mAndrolib.writeMetaFile(mOutDir, meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putUsesFramework(Map<String, Object> meta)
|
||||||
|
throws AndrolibException {
|
||||||
|
Set<ResPackage> pkgs = getResTable().listFramePackages();
|
||||||
|
if (pkgs.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer[] ids = new Integer[pkgs.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (ResPackage pkg : pkgs) {
|
||||||
|
ids[i++] = pkg.getId();
|
||||||
|
}
|
||||||
|
Arrays.sort(ids);
|
||||||
|
|
||||||
|
Map<String, Object> uses = new LinkedHashMap<String, Object>();
|
||||||
|
uses.put("ids", ids);
|
||||||
|
|
||||||
|
if (mFrameTag != null) {
|
||||||
|
uses.put("tag", mFrameTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
meta.put("usesFramework", uses);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Androlib mAndrolib;
|
||||||
|
|
||||||
|
private ExtFile mApkFile;
|
||||||
|
private File mOutDir;
|
||||||
|
private ResTable mResTable;
|
||||||
|
private short mDecodeSources = DECODE_SOURCES_SMALI;
|
||||||
|
private short mDecodeResources = DECODE_RESOURCES_FULL;
|
||||||
|
private boolean mDebug = false;
|
||||||
|
private boolean mForceDelete = false;
|
||||||
|
private String mFrameTag;
|
||||||
|
private boolean mKeepBrokenResources = false;
|
||||||
|
}
|
@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package brut.androlib.mod;
|
package brut.androlib.mod;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import org.antlr.runtime.*;
|
import org.antlr.runtime.*;
|
||||||
import org.antlr.runtime.tree.CommonTree;
|
import org.antlr.runtime.tree.CommonTree;
|
||||||
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||||
|
@ -0,0 +1,167 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2011 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package brut.androlib.res;
|
||||||
|
|
||||||
|
import brut.androlib.AndrolibException;
|
||||||
|
import brut.androlib.err.UndefinedResObject;
|
||||||
|
import brut.androlib.res.data.ResResSpec;
|
||||||
|
import brut.androlib.res.data.ResTable;
|
||||||
|
import brut.directory.Directory;
|
||||||
|
import brut.directory.DirectoryException;
|
||||||
|
import brut.directory.FileDirectory;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
*/
|
||||||
|
public class ResSmaliUpdater {
|
||||||
|
public void tagResIDs(ResTable resTable, File smaliDir)
|
||||||
|
throws AndrolibException {
|
||||||
|
Directory dir = null;
|
||||||
|
try {
|
||||||
|
dir = new FileDirectory(smaliDir);
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(
|
||||||
|
"Could not tag res IDs", ex);
|
||||||
|
}
|
||||||
|
for (String fileName : dir.getFiles(true)) {
|
||||||
|
try {
|
||||||
|
tagResIdsForFile(resTable, dir, fileName);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new AndrolibException(
|
||||||
|
"Could not tag resIDs for file: " + fileName, ex);
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(
|
||||||
|
"Could not tag resIDs for file: " + fileName, ex);
|
||||||
|
} catch (AndrolibException ex) {
|
||||||
|
throw new AndrolibException(
|
||||||
|
"Could not tag resIDs for file: " + fileName, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateResIDs(ResTable resTable, File smaliDir)
|
||||||
|
throws AndrolibException {
|
||||||
|
try {
|
||||||
|
Directory dir = new FileDirectory(smaliDir);
|
||||||
|
for (String fileName : dir.getFiles(true)) {
|
||||||
|
Iterator<String> it =
|
||||||
|
IOUtils.readLines(dir.getFileInput(fileName)).iterator();
|
||||||
|
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
|
||||||
|
while (it.hasNext()) {
|
||||||
|
String line = it.next();
|
||||||
|
out.println(line);
|
||||||
|
Matcher m1 = RES_NAME_PATTERN.matcher(line);
|
||||||
|
if (! m1.matches()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Matcher m2 = RES_ID_PATTERN.matcher(it.next());
|
||||||
|
if (! m2.matches()) {
|
||||||
|
throw new AndrolibException();
|
||||||
|
}
|
||||||
|
int resID = resTable.getPackage(m1.group(1))
|
||||||
|
.getType(m1.group(2)).getResSpec(m1.group(3))
|
||||||
|
.getId().id;
|
||||||
|
if (m2.group(1) != null) {
|
||||||
|
out.println(String.format(
|
||||||
|
RES_ID_FORMAT_FIELD, m2.group(1), resID));
|
||||||
|
} else {
|
||||||
|
out.println(String.format(
|
||||||
|
RES_ID_FORMAT_CONST, m2.group(2), resID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new AndrolibException(
|
||||||
|
"Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex);
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(
|
||||||
|
"Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tagResIdsForFile(ResTable resTable, Directory dir,
|
||||||
|
String fileName) throws IOException, DirectoryException,
|
||||||
|
AndrolibException {
|
||||||
|
Iterator<String> it =
|
||||||
|
IOUtils.readLines(dir.getFileInput(fileName)).iterator();
|
||||||
|
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
|
||||||
|
while (it.hasNext()) {
|
||||||
|
String line = it.next();
|
||||||
|
if (RES_NAME_PATTERN.matcher(line).matches()) {
|
||||||
|
out.println(line);
|
||||||
|
out.println(it.next());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Matcher m = RES_ID_PATTERN.matcher(line);
|
||||||
|
if (m.matches()) {
|
||||||
|
int resID = parseResID(m.group(3));
|
||||||
|
if (resID != -1) {
|
||||||
|
try {
|
||||||
|
ResResSpec spec = resTable.getResSpec(resID);
|
||||||
|
out.println(String.format(
|
||||||
|
RES_NAME_FORMAT, spec.getFullName()));
|
||||||
|
} catch (UndefinedResObject ex) {
|
||||||
|
if (! R_FILE_PATTERN.matcher(fileName).matches()) {
|
||||||
|
LOGGER.warning(String.format(
|
||||||
|
"Undefined resource spec in %s: 0x%08x"
|
||||||
|
, fileName, resID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.println(line);
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int parseResID(String resIDHex) {
|
||||||
|
if (resIDHex.endsWith("ff")) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int resID = Integer.valueOf(resIDHex, 16);
|
||||||
|
if (resIDHex.length() == 4) {
|
||||||
|
resID = resID << 16;
|
||||||
|
}
|
||||||
|
return resID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static String RES_ID_FORMAT_FIELD =
|
||||||
|
".field %s:I = 0x%08x";
|
||||||
|
private final static String RES_ID_FORMAT_CONST =
|
||||||
|
" const %s, 0x%08x";
|
||||||
|
private final static Pattern RES_ID_PATTERN = Pattern.compile(
|
||||||
|
"^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$");
|
||||||
|
private final static String RES_NAME_FORMAT =
|
||||||
|
"# APKTOOL/RES_NAME: %s";
|
||||||
|
private final static Pattern RES_NAME_PATTERN = Pattern.compile(
|
||||||
|
"^# APKTOOL/RES_NAME: (+)$");
|
||||||
|
|
||||||
|
private final static Pattern R_FILE_PATTERN = Pattern.compile(
|
||||||
|
".*R\\$[a-z]+\\.smali$");
|
||||||
|
|
||||||
|
private final static Logger LOGGER =
|
||||||
|
Logger.getLogger(ResSmaliUpdater.class.getName());
|
||||||
|
}
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
package brut.androlib.res.data;
|
package brut.androlib.res.data;
|
||||||
|
|
||||||
import brut.androlib.err.UndefinedResObject;
|
|
||||||
import brut.androlib.AndrolibException;
|
import brut.androlib.AndrolibException;
|
||||||
|
import brut.androlib.err.UndefinedResObject;
|
||||||
import brut.androlib.res.data.value.ResFileValue;
|
import brut.androlib.res.data.value.ResFileValue;
|
||||||
import brut.androlib.res.data.value.ResValueFactory;
|
import brut.androlib.res.data.value.ResValueFactory;
|
||||||
import brut.androlib.res.xml.ResValuesXmlSerializable;
|
import brut.androlib.res.xml.ResValuesXmlSerializable;
|
||||||
|
@ -23,7 +23,9 @@ import brut.androlib.res.data.value.ResBoolValue;
|
|||||||
import brut.androlib.res.data.value.ResFileValue;
|
import brut.androlib.res.data.value.ResFileValue;
|
||||||
import brut.directory.Directory;
|
import brut.directory.Directory;
|
||||||
import brut.directory.DirectoryException;
|
import brut.directory.DirectoryException;
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -19,8 +19,7 @@ package brut.androlib.src;
|
|||||||
import brut.androlib.AndrolibException;
|
import brut.androlib.AndrolibException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.jf.baksmali.baksmali;
|
import org.jf.baksmali.*;
|
||||||
import org.jf.baksmali.main;
|
|
||||||
import org.jf.dexlib.DexFile;
|
import org.jf.dexlib.DexFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,11 +19,19 @@ package brut.androlib;
|
|||||||
import brut.androlib.res.util.ExtFile;
|
import brut.androlib.res.util.ExtFile;
|
||||||
import brut.common.BrutException;
|
import brut.common.BrutException;
|
||||||
import brut.util.OS;
|
import brut.util.OS;
|
||||||
import java.io.*;
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import org.custommonkey.xmlunit.*;
|
import org.custommonkey.xmlunit.DetailedDiff;
|
||||||
import org.junit.*;
|
import org.custommonkey.xmlunit.Diff;
|
||||||
import static org.junit.Assert.*;
|
import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
|
||||||
|
import org.custommonkey.xmlunit.ElementQualifier;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
|
||||||
@ -116,9 +124,7 @@ public class BuildAndDecodeTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void qualifiersTest() throws BrutException {
|
public void qualifiersTest() throws BrutException {
|
||||||
compareValuesFiles("values-mcc004-mnc4-en-rUS-sw100dp-w200dp-h300dp" +
|
compareValuesFiles("values-mcc004/strings.xml");
|
||||||
"-xlarge-long-land-television-night-xhdpi-finger-keyssoft-12key" +
|
|
||||||
"-navhidden-dpad/strings.xml");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void compareValuesFiles(String path) throws BrutException {
|
private void compareValuesFiles(String path) throws BrutException {
|
||||||
|
6
pom.xml
6
pom.xml
@ -76,18 +76,18 @@
|
|||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>android-apktool.googlecode.com</id>
|
<id>android-apktool.googlecode.com</id>
|
||||||
<url>http://android-apktool.googlecode.com/svn/m2-releases</url>
|
<url>http://apktool2.googlecode.com/svn/m2-releases</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
<repository>
|
<repository>
|
||||||
<id>android-apktool.googlecode.com</id>
|
<id>android-apktool.googlecode.com</id>
|
||||||
<url>dav:https://android-apktool.googlecode.com/svn/m2-releases</url>
|
<url>dav:https://apktool2.googlecode.com/svn/m2-releases</url>
|
||||||
</repository>
|
</repository>
|
||||||
<snapshotRepository>
|
<snapshotRepository>
|
||||||
<id>android-apktool.googlecode.com</id>
|
<id>android-apktool.googlecode.com</id>
|
||||||
<url>dav:https://android-apktool.googlecode.com/svn/m2-snapshots</url>
|
<url>dav:https://apktool2.googlecode.com/svn/m2-snapshots</url>
|
||||||
</snapshotRepository>
|
</snapshotRepository>
|
||||||
</distributionManagement>
|
</distributionManagement>
|
||||||
</project>
|
</project>
|
||||||
|
75
pom.xml~
Normal file
75
pom.xml~
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>brut.apktool</groupId>
|
||||||
|
<artifactId>apktool-project</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<name>apktool</name>
|
||||||
|
<url>http://github.com/brutall/brut.apktool</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<root.basedir>${basedir}</root.basedir>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>apktool-lib</module>
|
||||||
|
<module>apktool-cli</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>..</directory>
|
||||||
|
<includes>
|
||||||
|
<include>LICENSE</include>
|
||||||
|
<include>NOTICE</include>
|
||||||
|
<include>NOTICE-smali</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.mycila.maven-license-plugin</groupId>
|
||||||
|
<artifactId>maven-license-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<header>${root.basedir}/src/templates/apache2.0-header.txt</header>
|
||||||
|
<strictCheck>true</strictCheck>
|
||||||
|
<excludes>
|
||||||
|
<exclude>.gitignore</exclude>
|
||||||
|
<exclude>LICENSE</exclude>
|
||||||
|
<exclude>NOTICE</exclude>
|
||||||
|
<exclude>NOTICE-smali</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>6</source>
|
||||||
|
<target>6</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
<extensions>
|
||||||
|
<extension>
|
||||||
|
<groupId>org.apache.maven.wagon</groupId>
|
||||||
|
<artifactId>wagon-webdav</artifactId>
|
||||||
|
<version>1.0-beta-2</version>
|
||||||
|
</extension>
|
||||||
|
</extensions>
|
||||||
|
</build>
|
||||||
|
</project>
|
Loading…
x
Reference in New Issue
Block a user