mirror of
https://github.com/revanced/Apktool.git
synced 2024-09-05 05:55:29 +02:00
Apktool.yml file (MetaInfo) using object serialization
non-printable character more intuitive, does not affect printable character view
This commit is contained in:
parent
81404c8904
commit
123912d226
@ -17,6 +17,8 @@
|
||||
package brut.androlib;
|
||||
|
||||
import brut.androlib.java.AndrolibJava;
|
||||
import brut.androlib.meta.MetaInfo;
|
||||
import brut.androlib.meta.UsesFramework;
|
||||
import brut.androlib.res.AndrolibResources;
|
||||
import brut.androlib.res.data.ResPackage;
|
||||
import brut.androlib.res.data.ResTable;
|
||||
@ -30,7 +32,6 @@ import brut.directory.*;
|
||||
import brut.util.BrutIO;
|
||||
import brut.util.OS;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
@ -40,9 +41,6 @@ import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
@ -239,33 +237,21 @@ public class Androlib {
|
||||
}
|
||||
}
|
||||
|
||||
public void writeMetaFile(File mOutDir, Map<String, Object> meta)
|
||||
public void writeMetaFile(File mOutDir, MetaInfo meta)
|
||||
throws AndrolibException {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
Yaml yaml = new Yaml(options);
|
||||
|
||||
try (
|
||||
Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
|
||||
new File(mOutDir, "apktool.yml")), "UTF-8"))
|
||||
) {
|
||||
yaml.dump(meta, writer);
|
||||
try{
|
||||
meta.save(new File(mOutDir, "apktool.yml"));
|
||||
} catch (IOException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> readMetaFile(ExtFile appDir)
|
||||
public MetaInfo readMetaFile(ExtFile appDir)
|
||||
throws AndrolibException {
|
||||
try(
|
||||
InputStream in = appDir.getDirectory().getFileInput("apktool.yml")
|
||||
) {
|
||||
Yaml yaml = new Yaml();
|
||||
Map<String, Object> result = (Map<String, Object>) yaml.load(in);
|
||||
if (result.containsKey("unknownFiles")) {
|
||||
result.put("unknownFiles", getUnknownFiles(result));
|
||||
}
|
||||
return result;
|
||||
return MetaInfo.load(in);
|
||||
} catch (DirectoryException | IOException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
@ -279,22 +265,19 @@ public class Androlib {
|
||||
throws BrutException {
|
||||
LOGGER.info("Using Apktool " + Androlib.getVersion());
|
||||
|
||||
Map<String, Object> meta = readMetaFile(appDir);
|
||||
Object t1 = meta.get("isFrameworkApk");
|
||||
apkOptions.isFramework = (t1 == null ? false : (Boolean) t1);
|
||||
apkOptions.resourcesAreCompressed = meta.get("compressionType") == null
|
||||
? false
|
||||
: Boolean.valueOf(meta.get("compressionType").toString());
|
||||
apkOptions.doNotCompress = (Collection<String>) meta.get("doNotCompress");
|
||||
MetaInfo meta = readMetaFile(appDir);
|
||||
apkOptions.isFramework = meta.isFrameworkApk;
|
||||
apkOptions.resourcesAreCompressed = meta.compressionType;
|
||||
apkOptions.doNotCompress = meta.doNotCompress;
|
||||
|
||||
mAndRes.setSdkInfo((Map<String, String>) meta.get("sdkInfo"));
|
||||
mAndRes.setPackageId((Map<String, String>) meta.get("packageInfo"));
|
||||
mAndRes.setPackageInfo((Map<String, String>) meta.get("packageInfo"));
|
||||
mAndRes.setVersionInfo((Map<String, String>) meta.get("versionInfo"));
|
||||
mAndRes.setSharedLibrary((boolean) (meta.get("sharedLibrary") == null ? false : meta.get("sharedLibrary")));
|
||||
mAndRes.setSdkInfo(meta.sdkInfo);
|
||||
mAndRes.setPackageId(meta.packageInfo);
|
||||
mAndRes.setPackageRenamed(meta.packageInfo);
|
||||
mAndRes.setVersionInfo(meta.versionInfo);
|
||||
mAndRes.setSharedLibrary(meta.sharedLibrary);
|
||||
|
||||
if (outFile == null) {
|
||||
String outFileName = (String) meta.get("apkFileName");
|
||||
String outFileName = meta.apkFileName;
|
||||
outFile = new File(appDir, "dist" + File.separator + (outFileName == null ? "out.apk" : outFileName));
|
||||
}
|
||||
|
||||
@ -302,7 +285,7 @@ public class Androlib {
|
||||
buildSources(appDir);
|
||||
buildNonDefaultSources(appDir);
|
||||
ResXmlPatcher.fixingPublicAttrsInProviderAttributes(new File(appDir, "AndroidManifest.xml"));
|
||||
buildResources(appDir, (Map<String, Object>) meta.get("usesFramework"));
|
||||
buildResources(appDir, meta.usesFramework);
|
||||
|
||||
buildLib(appDir);
|
||||
buildLibs(appDir);
|
||||
@ -412,7 +395,7 @@ public class Androlib {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void buildResources(ExtFile appDir, Map<String, Object> usesFramework)
|
||||
public void buildResources(ExtFile appDir, UsesFramework usesFramework)
|
||||
throws BrutException {
|
||||
if (!buildResourcesRaw(appDir) && !buildResourcesFull(appDir, usesFramework)
|
||||
&& !buildManifest(appDir, usesFramework)) {
|
||||
@ -441,7 +424,7 @@ public class Androlib {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean buildResourcesFull(File appDir, Map<String, Object> usesFramework)
|
||||
public boolean buildResourcesFull(File appDir, UsesFramework usesFramework)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
if (!new File(appDir, "res").exists()) {
|
||||
@ -492,7 +475,7 @@ public class Androlib {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean buildManifest(ExtFile appDir, Map<String, Object> usesFramework)
|
||||
public boolean buildManifest(ExtFile appDir, UsesFramework usesFramework)
|
||||
throws BrutException {
|
||||
try {
|
||||
if (!new File(appDir, "AndroidManifest.xml").exists()) {
|
||||
@ -587,12 +570,12 @@ public class Androlib {
|
||||
}
|
||||
}
|
||||
|
||||
public void buildUnknownFiles(File appDir, File outFile, Map<String, Object> meta)
|
||||
public void buildUnknownFiles(File appDir, File outFile, MetaInfo meta)
|
||||
throws AndrolibException {
|
||||
if (meta.containsKey("unknownFiles")) {
|
||||
if (meta.unknownFiles != null) {
|
||||
LOGGER.info("Copying unknown files/dir...");
|
||||
|
||||
Map<String, String> files = (Map<String, String>)meta.get("unknownFiles");
|
||||
Map<String, String> files = meta.unknownFiles;
|
||||
File tempFile = new File(outFile.getParent(), outFile.getName() + ".apktool_temp");
|
||||
boolean renamed = outFile.renameTo(tempFile);
|
||||
if(!renamed) {
|
||||
@ -703,18 +686,18 @@ public class Androlib {
|
||||
return ApktoolProperties.get("application.version");
|
||||
}
|
||||
|
||||
private File[] parseUsesFramework(Map<String, Object> usesFramework)
|
||||
private File[] parseUsesFramework(UsesFramework usesFramework)
|
||||
throws AndrolibException {
|
||||
if (usesFramework == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Integer> ids = (List<Integer>) usesFramework.get("ids");
|
||||
List<Integer> ids = usesFramework.ids;
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String tag = (String) usesFramework.get("tag");
|
||||
String tag = usesFramework.tag;
|
||||
File[] files = new File[ids.size()];
|
||||
int i = 0;
|
||||
for (int id : ids) {
|
||||
@ -744,15 +727,6 @@ public class Androlib {
|
||||
return files;
|
||||
}
|
||||
|
||||
private static Map<String, String> getUnknownFiles(Map<String, Object> meta) {
|
||||
Map<byte[], String> unknownFiles = (Map<byte[], String>) meta.get("unknownFiles");
|
||||
Map<String, String> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<byte[], String> entry : unknownFiles.entrySet()) {
|
||||
result.put(new String(entry.getKey(), StandardCharsets.UTF_8), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(Androlib.class.getName());
|
||||
|
||||
private final static String SMALI_DIRNAME = "smali";
|
||||
|
@ -19,6 +19,9 @@ package brut.androlib;
|
||||
import brut.androlib.err.InFileNotFoundException;
|
||||
import brut.androlib.err.OutDirExistsException;
|
||||
import brut.androlib.err.UndefinedResObject;
|
||||
import brut.androlib.meta.MetaInfo;
|
||||
import brut.androlib.meta.PackageInfo;
|
||||
import brut.androlib.meta.UsesFramework;
|
||||
import brut.androlib.res.AndrolibResources;
|
||||
import brut.androlib.res.data.ResPackage;
|
||||
import brut.androlib.res.data.ResTable;
|
||||
@ -298,12 +301,12 @@ public class ApkDecoder {
|
||||
}
|
||||
|
||||
private void writeMetaFile() throws AndrolibException {
|
||||
Map<String, Object> meta = new LinkedHashMap<String, Object>();
|
||||
meta.put("version", Androlib.getVersion());
|
||||
meta.put("apkFileName", mApkFile.getName());
|
||||
MetaInfo meta = new MetaInfo();
|
||||
meta.version = Androlib.getVersion();
|
||||
meta.apkFileName = mApkFile.getName();
|
||||
|
||||
if (mDecodeResources != DECODE_RESOURCES_NONE && (hasManifest() || hasResources())) {
|
||||
meta.put("isFrameworkApk", mAndrolib.isFrameworkApk(getResTable()));
|
||||
meta.isFrameworkApk = mAndrolib.isFrameworkApk(getResTable());
|
||||
putUsesFramework(meta);
|
||||
putSdkInfo(meta);
|
||||
putPackageInfo(meta);
|
||||
@ -316,7 +319,7 @@ public class ApkDecoder {
|
||||
mAndrolib.writeMetaFile(mOutDir, meta);
|
||||
}
|
||||
|
||||
private void putUsesFramework(Map<String, Object> meta) throws AndrolibException {
|
||||
private void putUsesFramework(MetaInfo meta) throws AndrolibException {
|
||||
Set<ResPackage> pkgs = getResTable().listFramePackages();
|
||||
if (pkgs.isEmpty()) {
|
||||
return;
|
||||
@ -329,24 +332,22 @@ public class ApkDecoder {
|
||||
}
|
||||
Arrays.sort(ids);
|
||||
|
||||
Map<String, Object> uses = new LinkedHashMap<String, Object>();
|
||||
uses.put("ids", ids);
|
||||
meta.usesFramework = new UsesFramework();
|
||||
meta.usesFramework.ids = Arrays.asList(ids);
|
||||
|
||||
if (mAndrolib.apkOptions.frameworkTag != null) {
|
||||
uses.put("tag", mAndrolib.apkOptions.frameworkTag);
|
||||
meta.usesFramework.tag = mAndrolib.apkOptions.frameworkTag;
|
||||
}
|
||||
|
||||
meta.put("usesFramework", uses);
|
||||
}
|
||||
|
||||
private void putSdkInfo(Map<String, Object> meta) throws AndrolibException {
|
||||
private void putSdkInfo(MetaInfo meta) throws AndrolibException {
|
||||
Map<String, String> info = getResTable().getSdkInfo();
|
||||
if (info.size() > 0) {
|
||||
meta.put("sdkInfo", info);
|
||||
meta.sdkInfo = info;
|
||||
}
|
||||
}
|
||||
|
||||
private void putPackageInfo(Map<String, Object> meta) throws AndrolibException {
|
||||
private void putPackageInfo(MetaInfo meta) throws AndrolibException {
|
||||
String renamed = getResTable().getPackageRenamed();
|
||||
String original = getResTable().getPackageOriginal();
|
||||
|
||||
@ -355,42 +356,35 @@ public class ApkDecoder {
|
||||
id = getResTable().getPackage(renamed).getId();
|
||||
} catch (UndefinedResObject ignored) {}
|
||||
|
||||
HashMap<String, String> packages = new HashMap<String, String>();
|
||||
|
||||
if (Strings.isNullOrEmpty(original)) {
|
||||
return;
|
||||
}
|
||||
|
||||
meta.packageInfo = new PackageInfo();
|
||||
|
||||
// only put rename-manifest-package into apktool.yml, if the change will be required
|
||||
if (!renamed.equalsIgnoreCase(original)) {
|
||||
packages.put("rename-manifest-package", renamed);
|
||||
meta.packageInfo.renameManifestPackage = renamed;
|
||||
}
|
||||
packages.put("forced-package-id", String.valueOf(id));
|
||||
meta.put("packageInfo", packages);
|
||||
meta.packageInfo.forcedPackageId = String.valueOf(id);
|
||||
}
|
||||
|
||||
private void putVersionInfo(Map<String, Object> meta) throws AndrolibException {
|
||||
Map<String, String> info = getResTable().getVersionInfo();
|
||||
if (info.size() > 0) {
|
||||
meta.put("versionInfo", info);
|
||||
}
|
||||
private void putVersionInfo(MetaInfo meta) throws AndrolibException {
|
||||
meta.versionInfo = getResTable().getVersionInfo();
|
||||
}
|
||||
|
||||
private void putUnknownInfo(Map<String, Object> meta) throws AndrolibException {
|
||||
Map<byte[], String> info = mAndrolib.mResUnknownFiles.getUnknownFiles();
|
||||
if (info.size() > 0) {
|
||||
meta.put("unknownFiles", info);
|
||||
}
|
||||
private void putUnknownInfo(MetaInfo meta) throws AndrolibException {
|
||||
meta.unknownFiles = mAndrolib.mResUnknownFiles.getUnknownFiles();
|
||||
}
|
||||
|
||||
private void putFileCompressionInfo(Map<String, Object> meta) throws AndrolibException {
|
||||
private void putFileCompressionInfo(MetaInfo meta) throws AndrolibException {
|
||||
if (!mUncompressedFiles.isEmpty()) {
|
||||
meta.put("doNotCompress", mUncompressedFiles);
|
||||
meta.doNotCompress = mUncompressedFiles;
|
||||
}
|
||||
}
|
||||
|
||||
private void putSharedLibraryInfo(Map<String, Object> meta) throws AndrolibException {
|
||||
meta.put("sharedLibrary", mResTable.getSharedLibrary());
|
||||
private void putSharedLibraryInfo(MetaInfo meta) throws AndrolibException {
|
||||
meta.sharedLibrary = mResTable.getSharedLibrary();
|
||||
}
|
||||
|
||||
private final Androlib mAndrolib;
|
||||
|
@ -0,0 +1,66 @@
|
||||
package brut.androlib.meta;
|
||||
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.introspector.PropertyUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by rover12421 on 1/6/16.
|
||||
*/
|
||||
public class MetaInfo {
|
||||
public String version;
|
||||
public String apkFileName;
|
||||
public boolean isFrameworkApk;
|
||||
public UsesFramework usesFramework;
|
||||
public Map<String, String> sdkInfo;
|
||||
public PackageInfo packageInfo;
|
||||
public VersionInfo versionInfo;
|
||||
public boolean compressionType;
|
||||
public boolean sharedLibrary;
|
||||
public Map<String, String> unknownFiles;
|
||||
public Collection<String> doNotCompress;
|
||||
|
||||
private static Yaml getYaml() {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
|
||||
StringExRepresent representer = new StringExRepresent();
|
||||
PropertyUtils propertyUtils = representer.getPropertyUtils();
|
||||
propertyUtils.setSkipMissingProperties(true);
|
||||
|
||||
return new Yaml(new StringExConstructor(), representer, options);
|
||||
}
|
||||
|
||||
public void save(Writer output) {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
getYaml().dump(this, output);
|
||||
}
|
||||
|
||||
public void save(File file) throws IOException {
|
||||
try(
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
|
||||
Writer writer = new BufferedWriter(outputStreamWriter)
|
||||
){
|
||||
save(writer);
|
||||
}
|
||||
}
|
||||
|
||||
public static MetaInfo load(InputStream is) {
|
||||
return getYaml().loadAs(is, MetaInfo.class);
|
||||
}
|
||||
|
||||
public static MetaInfo load(File file) throws IOException {
|
||||
try (
|
||||
InputStream fis = new FileInputStream(file)
|
||||
){
|
||||
return load(fis);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package brut.androlib.meta;
|
||||
|
||||
/**
|
||||
* Created by rover12421 on 1/6/16.
|
||||
*/
|
||||
public class PackageInfo {
|
||||
public String forcedPackageId;
|
||||
public String renameManifestPackage;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package brut.androlib.meta;
|
||||
|
||||
import org.yaml.snakeyaml.constructor.AbstractConstruct;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.ScalarNode;
|
||||
import org.yaml.snakeyaml.nodes.Tag;
|
||||
|
||||
/**
|
||||
* Created by rover12421 on 1/7/16.
|
||||
*/
|
||||
public class StringExConstructor extends Constructor {
|
||||
public StringExConstructor() {
|
||||
this.yamlConstructors.put(Tag.STR, new ConstructStringEx());
|
||||
}
|
||||
|
||||
private class ConstructStringEx extends AbstractConstruct {
|
||||
public Object construct(Node node) {
|
||||
String val = (String) constructScalar((ScalarNode)node);
|
||||
return YamlStringEscapeUtils.unescapeString(val);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package brut.androlib.meta;
|
||||
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
/**
|
||||
* Created by rover12421 on 1/7/16.
|
||||
*/
|
||||
public class StringExRepresent extends Representer {
|
||||
public StringExRepresent() {
|
||||
RepresentStringEx representStringEx = new RepresentStringEx();
|
||||
multiRepresenters.put(String.class, representStringEx);
|
||||
representers.put(String.class, representStringEx);
|
||||
}
|
||||
|
||||
private class RepresentStringEx extends RepresentString {
|
||||
|
||||
@Override
|
||||
public Node representData(Object data) {
|
||||
return super.representData(YamlStringEscapeUtils.escapeString(data.toString()));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package brut.androlib.meta;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by rover12421 on 1/6/16.
|
||||
*/
|
||||
public class UsesFramework {
|
||||
public List<Integer> ids;
|
||||
public String tag;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package brut.androlib.meta;
|
||||
|
||||
/**
|
||||
* Created by rover12421 on 1/6/16.
|
||||
*/
|
||||
public class VersionInfo {
|
||||
public String versionCode;
|
||||
public String versionName;
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.meta;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.text.translate.CharSequenceTranslator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
public class YamlStringEscapeUtils {
|
||||
|
||||
public static String escapeString(String str) {
|
||||
return escapeJavaStyleString(str, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param str String to escape values in, may be null
|
||||
* @param escapeSingleQuotes escapes single quotes if <code>true</code>
|
||||
* @param escapeForwardSlash TODO
|
||||
* @return the escaped string
|
||||
*/
|
||||
private static String escapeJavaStyleString(String str, boolean escapeSingleQuotes, boolean escapeForwardSlash) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
StringWriter writer = new StringWriter(str.length() * 2);
|
||||
escapeJavaStyleString(writer, str, escapeSingleQuotes, escapeForwardSlash);
|
||||
return writer.toString();
|
||||
} catch (IOException ioe) {
|
||||
// this should never ever happen while writing to a StringWriter
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param out write to receieve the escaped string
|
||||
* @param str String to escape values in, may be null
|
||||
* @param escapeSingleQuote escapes single quotes if <code>true</code>
|
||||
* @param escapeForwardSlash TODO
|
||||
* @throws IOException if an IOException occurs
|
||||
*/
|
||||
private static void escapeJavaStyleString(Writer out, String str, boolean escapeSingleQuote,
|
||||
boolean escapeForwardSlash) throws IOException {
|
||||
if (out == null) {
|
||||
throw new IllegalArgumentException("The Writer must not be null");
|
||||
}
|
||||
if (str == null) {
|
||||
return;
|
||||
}
|
||||
int sz;
|
||||
sz = str.length();
|
||||
for (int i = 0; i < sz; i++) {
|
||||
char ch = str.charAt(i);
|
||||
// "[^\t\n\r\u0020-\u007E\u0085\u00A0-\uD7FF\uE000-\uFFFD]"
|
||||
// handle unicode
|
||||
if (ch > 0xFFFD) {
|
||||
out.write("\\u" + CharSequenceTranslator.hex(ch));
|
||||
} else if (ch > 0xD7FF && ch < 0xE000) {
|
||||
out.write("\\u" + CharSequenceTranslator.hex(ch));
|
||||
} else if (ch > 0x7E && ch != 0x85 && ch < 0xA0) {
|
||||
out.write("\\u00" + CharSequenceTranslator.hex(ch));
|
||||
} else if (ch < 32) {
|
||||
switch (ch) {
|
||||
case '\t' :
|
||||
out.write('\\');
|
||||
out.write('t');
|
||||
break;
|
||||
case '\n' :
|
||||
out.write('\\');
|
||||
out.write('n');
|
||||
break;
|
||||
case '\r' :
|
||||
out.write('\\');
|
||||
out.write('r');
|
||||
break;
|
||||
default :
|
||||
if (ch > 0xf) {
|
||||
out.write("\\u00" + CharSequenceTranslator.hex(ch));
|
||||
} else {
|
||||
out.write("\\u000" + CharSequenceTranslator.hex(ch));
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (ch) {
|
||||
case '\'' :
|
||||
if (escapeSingleQuote) {
|
||||
out.write('\\');
|
||||
}
|
||||
out.write('\'');
|
||||
break;
|
||||
case '"' :
|
||||
out.write('\\');
|
||||
out.write('"');
|
||||
break;
|
||||
case '\\' :
|
||||
out.write('\\');
|
||||
out.write('\\');
|
||||
break;
|
||||
case '/' :
|
||||
if (escapeForwardSlash) {
|
||||
out.write('\\');
|
||||
}
|
||||
out.write('/');
|
||||
break;
|
||||
default :
|
||||
out.write(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Unescapes any Java literals found in the <code>String</code>.
|
||||
* For example, it will turn a sequence of <code>'\'</code> and
|
||||
* <code>'n'</code> into a newline character, unless the <code>'\'</code>
|
||||
* is preceded by another <code>'\'</code>.</p>
|
||||
*
|
||||
* @param str the <code>String</code> to unescape, may be null
|
||||
* @return a new unescaped <code>String</code>, <code>null</code> if null string input
|
||||
*/
|
||||
public static String unescapeString(String str) {
|
||||
return StringEscapeUtils.unescapeJava(str);
|
||||
}
|
||||
}
|
@ -19,26 +19,35 @@ package brut.androlib.res;
|
||||
import brut.androlib.AndrolibException;
|
||||
import brut.androlib.ApkOptions;
|
||||
import brut.androlib.err.CantFindFrameworkResException;
|
||||
import brut.androlib.meta.PackageInfo;
|
||||
import brut.androlib.meta.VersionInfo;
|
||||
import brut.androlib.res.data.*;
|
||||
import brut.androlib.res.decoder.*;
|
||||
import brut.androlib.res.decoder.ARSCDecoder.ARSCData;
|
||||
import brut.androlib.res.decoder.ARSCDecoder.FlagsOffset;
|
||||
import brut.androlib.res.util.*;
|
||||
import brut.androlib.res.util.ExtFile;
|
||||
import brut.androlib.res.util.ExtMXSerializer;
|
||||
import brut.androlib.res.util.ExtXmlSerializer;
|
||||
import brut.androlib.res.xml.ResValuesXmlSerializable;
|
||||
import brut.androlib.res.xml.ResXmlPatcher;
|
||||
import brut.common.BrutException;
|
||||
import brut.directory.*;
|
||||
import brut.util.*;
|
||||
import brut.directory.Directory;
|
||||
import brut.directory.DirectoryException;
|
||||
import brut.directory.FileDirectory;
|
||||
import brut.util.Duo;
|
||||
import brut.util.Jar;
|
||||
import brut.util.OS;
|
||||
import brut.util.OSDetection;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.zip.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
@ -268,22 +277,22 @@ final public class AndrolibResources {
|
||||
}
|
||||
}
|
||||
|
||||
public void setVersionInfo(Map<String, String> map) {
|
||||
if (map != null) {
|
||||
mVersionCode = map.get("versionCode");
|
||||
mVersionName = map.get("versionName");
|
||||
public void setVersionInfo(VersionInfo versionInfo) {
|
||||
if (versionInfo != null) {
|
||||
mVersionCode = versionInfo.versionCode;
|
||||
mVersionName = versionInfo.versionName;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPackageInfo(Map<String, String> map) {
|
||||
if (map != null) {
|
||||
mPackageRenamed = map.get("rename-manifest-package");
|
||||
public void setPackageRenamed(PackageInfo packageInfo) {
|
||||
if (packageInfo != null) {
|
||||
mPackageRenamed = packageInfo.renameManifestPackage;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPackageId(Map<String, String> map) {
|
||||
if (map != null) {
|
||||
mPackageId = map.get("forced-package-id");
|
||||
public void setPackageId(PackageInfo packageInfo) {
|
||||
if (packageInfo != null) {
|
||||
mPackageId = packageInfo.forcedPackageId;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ package brut.androlib.res.data;
|
||||
|
||||
import brut.androlib.AndrolibException;
|
||||
import brut.androlib.err.UndefinedResObject;
|
||||
import brut.androlib.meta.VersionInfo;
|
||||
import brut.androlib.res.AndrolibResources;
|
||||
import brut.androlib.res.data.value.ResValue;
|
||||
import java.util.*;
|
||||
@ -39,8 +40,8 @@ public class ResTable {
|
||||
private boolean mAnalysisMode = false;
|
||||
private boolean mSharedLibrary = false;
|
||||
|
||||
private Map<String, String> mSdkInfo = new LinkedHashMap<String, String>();
|
||||
private Map<String, String> mVersionInfo = new LinkedHashMap<String, String>();
|
||||
private Map<String, String> mSdkInfo = new LinkedHashMap<>();
|
||||
private VersionInfo mVersionInfo = new VersionInfo();
|
||||
|
||||
public ResTable() {
|
||||
mAndRes = null;
|
||||
@ -174,11 +175,15 @@ public class ResTable {
|
||||
mSdkInfo.put(key, value);
|
||||
}
|
||||
|
||||
public void addVersionInfo(String key, String value) {
|
||||
mVersionInfo.put(key, value);
|
||||
public void setVersionName(String versionName) {
|
||||
mVersionInfo.versionName = versionName;
|
||||
}
|
||||
|
||||
public Map<String, String> getVersionInfo() {
|
||||
public void setVersionCode(String versionCode) {
|
||||
mVersionInfo.versionCode = versionCode;
|
||||
}
|
||||
|
||||
public VersionInfo getVersionInfo() {
|
||||
return mVersionInfo;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package brut.androlib.res.data;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -24,13 +23,13 @@ import java.util.Map;
|
||||
*/
|
||||
public class ResUnknownFiles {
|
||||
|
||||
private final Map<byte[], String> mUnknownFiles = new LinkedHashMap<byte[], String>();
|
||||
private final Map<String, String> mUnknownFiles = new LinkedHashMap<>();
|
||||
|
||||
public void addUnknownFileInfo(String file, String value) {
|
||||
mUnknownFiles.put(file.getBytes(StandardCharsets.UTF_8), value);
|
||||
mUnknownFiles.put(file, value);
|
||||
}
|
||||
|
||||
public Map<byte[], String> getUnknownFiles() {
|
||||
public Map<String, String> getUnknownFiles() {
|
||||
return mUnknownFiles;
|
||||
}
|
||||
}
|
||||
|
@ -21,15 +21,20 @@ import brut.androlib.AndrolibException;
|
||||
import brut.androlib.res.data.*;
|
||||
import brut.androlib.res.data.value.*;
|
||||
import brut.util.Duo;
|
||||
import brut.androlib.res.data.ResTable;
|
||||
import brut.util.ExtDataInput;
|
||||
import com.mindprod.ledatastream.LEDataInputStream;
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.commons.io.input.CountingInputStream;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
|
@ -94,9 +94,9 @@ public class XmlPullStreamDecoder implements ResStreamDecoder {
|
||||
if (attr_name.equalsIgnoreCase(("package"))) {
|
||||
resTable.setPackageRenamed(pp.getAttributeValue(i));
|
||||
} else if (attr_name.equalsIgnoreCase("versionCode")) {
|
||||
resTable.addVersionInfo("versionCode", pp.getAttributeValue(i));
|
||||
resTable.setVersionCode(pp.getAttributeValue(i));
|
||||
} else if (attr_name.equalsIgnoreCase("versionName")) {
|
||||
resTable.addVersionInfo("versionName", pp.getAttributeValue(i));
|
||||
resTable.setVersionName(pp.getAttributeValue(i));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -20,17 +20,19 @@ import brut.androlib.AndrolibException;
|
||||
import brut.androlib.mod.SmaliMod;
|
||||
import brut.androlib.res.util.ExtFile;
|
||||
import brut.directory.DirectoryException;
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.antlr.runtime.RecognitionException;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jf.dexlib2.writer.builder.DexBuilder;
|
||||
import org.jf.dexlib2.writer.io.FileDataStore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package brut.androlib;
|
||||
|
||||
import brut.androlib.meta.MetaInfo;
|
||||
import brut.androlib.res.util.ExtFile;
|
||||
import brut.common.BrutException;
|
||||
import brut.directory.FileDirectory;
|
||||
@ -340,13 +341,13 @@ public class BuildAndDecodeTest {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void compareUnknownFiles() throws BrutException, IOException {
|
||||
Map<String, Object> control = new Androlib().readMetaFile(sTestOrigDir);
|
||||
Map<String, Object> test = new Androlib().readMetaFile(sTestNewDir);
|
||||
assertTrue(control.containsKey("unknownFiles"));
|
||||
assertTrue(test.containsKey("unknownFiles"));
|
||||
MetaInfo control = new Androlib().readMetaFile(sTestOrigDir);
|
||||
MetaInfo test = new Androlib().readMetaFile(sTestNewDir);
|
||||
assertNotNull(control.unknownFiles);
|
||||
assertNotNull(test.unknownFiles);
|
||||
|
||||
Map<String, String> control_files = (Map<String, String>)control.get("unknownFiles");
|
||||
Map<String, String> test_files = (Map<String, String>)test.get("unknownFiles");
|
||||
Map<String, String> control_files = control.unknownFiles;
|
||||
Map<String, String> test_files = test.unknownFiles;
|
||||
assertTrue(control_files.size() == test_files.size());
|
||||
|
||||
// Make sure that the compression methods are still the same
|
||||
|
@ -11,8 +11,8 @@ versionInfo:
|
||||
versionName: '1.0'
|
||||
compressionType: false
|
||||
unknownFiles:
|
||||
!!binary "aGlkZGVuLmZpbGU=": '8'
|
||||
!!binary "bm9uf3ByaW50YWJsZS5maWxl": '8'
|
||||
!!binary "c3RvcmVkLmZpbGU=": '0'
|
||||
!!binary "dW5rX2ZvbGRlci91bmtub3duX2ZpbGU=": '8'
|
||||
!!binary "bGliX2J1ZzYwMy9idWc2MDM=": '8'
|
||||
hidden.file: '8'
|
||||
non\u007Fprintable.file: '8'
|
||||
stored.file: '0'
|
||||
unk_folder/unknown_file: '8'
|
||||
lib_bug603/bug603: '8'
|
Loading…
Reference in New Issue
Block a user