mirror of
https://github.com/revanced/Apktool.git
synced 2025-01-12 04:55:52 +01:00
This commit is contained in:
parent
5174001e5b
commit
321dcfa91b
@ -16,7 +16,9 @@
|
||||
|
||||
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.InFileNotFoundException;
|
||||
import brut.androlib.err.OutDirExistsException;
|
||||
@ -209,6 +211,7 @@ public class Main {
|
||||
private static void usage() {
|
||||
System.out.println(
|
||||
"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" +
|
||||
"Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" +
|
||||
"\n" +
|
||||
@ -236,7 +239,7 @@ public class Main {
|
||||
" \"Invalid config flags detected. Dropping resources\", but you\n" +
|
||||
" want to decode them anyway, even with errors. You will have to\n" +
|
||||
" fix them manually before building." +
|
||||
"\n" +
|
||||
"\n\n" +
|
||||
" b[uild] [OPTS] [<app_path>] [<out_file>]\n" +
|
||||
" Build an apk from already decoded application located in <app_path>.\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>
|
||||
<artifactId>apktool-lib</artifactId>
|
||||
<version>1.4.4-SNAPSHOT</version>
|
||||
<version>1.4.5-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
|
@ -24,7 +24,8 @@ 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.directory.Directory;
|
||||
import brut.directory.DirectoryException;
|
||||
import brut.util.BrutIO;
|
||||
import brut.util.OS;
|
||||
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.util.OS;
|
||||
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>
|
||||
|
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;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import org.antlr.runtime.*;
|
||||
import org.antlr.runtime.tree.CommonTree;
|
||||
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;
|
||||
|
||||
import brut.androlib.err.UndefinedResObject;
|
||||
import brut.androlib.AndrolibException;
|
||||
import brut.androlib.err.UndefinedResObject;
|
||||
import brut.androlib.res.data.value.ResFileValue;
|
||||
import brut.androlib.res.data.value.ResValueFactory;
|
||||
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.directory.Directory;
|
||||
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.Logger;
|
||||
|
||||
|
@ -19,8 +19,7 @@ package brut.androlib.src;
|
||||
import brut.androlib.AndrolibException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import org.jf.baksmali.baksmali;
|
||||
import org.jf.baksmali.main;
|
||||
import org.jf.baksmali.*;
|
||||
import org.jf.dexlib.DexFile;
|
||||
|
||||
/**
|
||||
|
@ -19,11 +19,19 @@ package brut.androlib;
|
||||
import brut.androlib.res.util.ExtFile;
|
||||
import brut.common.BrutException;
|
||||
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 org.custommonkey.xmlunit.*;
|
||||
import org.junit.*;
|
||||
import static org.junit.Assert.*;
|
||||
import org.custommonkey.xmlunit.DetailedDiff;
|
||||
import org.custommonkey.xmlunit.Diff;
|
||||
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;
|
||||
|
||||
|
||||
@ -116,9 +124,7 @@ public class BuildAndDecodeTest {
|
||||
|
||||
@Test
|
||||
public void qualifiersTest() throws BrutException {
|
||||
compareValuesFiles("values-mcc004-mnc4-en-rUS-sw100dp-w200dp-h300dp" +
|
||||
"-xlarge-long-land-television-night-xhdpi-finger-keyssoft-12key" +
|
||||
"-navhidden-dpad/strings.xml");
|
||||
compareValuesFiles("values-mcc004/strings.xml");
|
||||
}
|
||||
|
||||
private void compareValuesFiles(String path) throws BrutException {
|
||||
|
6
pom.xml
6
pom.xml
@ -76,18 +76,18 @@
|
||||
<repositories>
|
||||
<repository>
|
||||
<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>
|
||||
</repositories>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<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>
|
||||
<snapshotRepository>
|
||||
<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>
|
||||
</distributionManagement>
|
||||
</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…
Reference in New Issue
Block a user