mirror of
https://github.com/revanced/Apktool.git
synced 2025-05-05 00:04:26 +02:00
deleting those buggers ~
This commit is contained in:
parent
94aceb381d
commit
5e49d31f41
@ -1,299 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,443 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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"};
|
|
||||||
}
|
|
@ -1,234 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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());
|
|
||||||
}
|
|
75
pom.xml~
75
pom.xml~
@ -1,75 +0,0 @@
|
|||||||
|
|
||||||
<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