Merge branch 'rover12421-yaml-object'

This commit is contained in:
Connor Tumbleson 2016-01-22 07:17:16 -06:00
commit 114af9799d
15 changed files with 465 additions and 127 deletions

View File

@ -17,6 +17,8 @@
package brut.androlib; package brut.androlib;
import brut.androlib.java.AndrolibJava; import brut.androlib.java.AndrolibJava;
import brut.androlib.meta.MetaInfo;
import brut.androlib.meta.UsesFramework;
import brut.androlib.res.AndrolibResources; import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.ResPackage; import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResTable; import brut.androlib.res.data.ResTable;
@ -30,7 +32,6 @@ import brut.directory.*;
import brut.util.BrutIO; import brut.util.BrutIO;
import brut.util.OS; import brut.util.OS;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -40,9 +41,6 @@ import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FilenameUtils; 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> * @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 { throws AndrolibException {
DumperOptions options = new DumperOptions(); try{
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); meta.save(new File(mOutDir, "apktool.yml"));
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);
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
public Map<String, Object> readMetaFile(ExtFile appDir) public MetaInfo readMetaFile(ExtFile appDir)
throws AndrolibException { throws AndrolibException {
try( try(
InputStream in = appDir.getDirectory().getFileInput("apktool.yml") InputStream in = appDir.getDirectory().getFileInput("apktool.yml")
) { ) {
Yaml yaml = new Yaml(); return MetaInfo.load(in);
Map<String, Object> result = (Map<String, Object>) yaml.load(in);
if (result.containsKey("unknownFiles")) {
result.put("unknownFiles", getUnknownFiles(result));
}
return result;
} catch (DirectoryException | IOException ex) { } catch (DirectoryException | IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
@ -279,22 +265,19 @@ public class Androlib {
throws BrutException { throws BrutException {
LOGGER.info("Using Apktool " + Androlib.getVersion()); LOGGER.info("Using Apktool " + Androlib.getVersion());
Map<String, Object> meta = readMetaFile(appDir); MetaInfo meta = readMetaFile(appDir);
Object t1 = meta.get("isFrameworkApk"); apkOptions.isFramework = meta.isFrameworkApk;
apkOptions.isFramework = (t1 == null ? false : (Boolean) t1); apkOptions.resourcesAreCompressed = meta.compressionType;
apkOptions.resourcesAreCompressed = meta.get("compressionType") == null apkOptions.doNotCompress = meta.doNotCompress;
? false
: Boolean.valueOf(meta.get("compressionType").toString());
apkOptions.doNotCompress = (Collection<String>) meta.get("doNotCompress");
mAndRes.setSdkInfo((Map<String, String>) meta.get("sdkInfo")); mAndRes.setSdkInfo(meta.sdkInfo);
mAndRes.setPackageId((Map<String, String>) meta.get("packageInfo")); mAndRes.setPackageId(meta.packageInfo);
mAndRes.setPackageInfo((Map<String, String>) meta.get("packageInfo")); mAndRes.setPackageRenamed(meta.packageInfo);
mAndRes.setVersionInfo((Map<String, String>) meta.get("versionInfo")); mAndRes.setVersionInfo(meta.versionInfo);
mAndRes.setSharedLibrary((boolean) (meta.get("sharedLibrary") == null ? false : meta.get("sharedLibrary"))); mAndRes.setSharedLibrary(meta.sharedLibrary);
if (outFile == null) { 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)); outFile = new File(appDir, "dist" + File.separator + (outFileName == null ? "out.apk" : outFileName));
} }
@ -302,7 +285,7 @@ public class Androlib {
buildSources(appDir); buildSources(appDir);
buildNonDefaultSources(appDir); buildNonDefaultSources(appDir);
ResXmlPatcher.fixingPublicAttrsInProviderAttributes(new File(appDir, "AndroidManifest.xml")); ResXmlPatcher.fixingPublicAttrsInProviderAttributes(new File(appDir, "AndroidManifest.xml"));
buildResources(appDir, (Map<String, Object>) meta.get("usesFramework")); buildResources(appDir, meta.usesFramework);
buildLib(appDir); buildLib(appDir);
buildLibs(appDir); buildLibs(appDir);
@ -412,7 +395,7 @@ public class Androlib {
return true; return true;
} }
public void buildResources(ExtFile appDir, Map<String, Object> usesFramework) public void buildResources(ExtFile appDir, UsesFramework usesFramework)
throws BrutException { throws BrutException {
if (!buildResourcesRaw(appDir) && !buildResourcesFull(appDir, usesFramework) if (!buildResourcesRaw(appDir) && !buildResourcesFull(appDir, usesFramework)
&& !buildManifest(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 { throws AndrolibException {
try { try {
if (!new File(appDir, "res").exists()) { 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 { throws BrutException {
try { try {
if (!new File(appDir, "AndroidManifest.xml").exists()) { 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 { throws AndrolibException {
if (meta.containsKey("unknownFiles")) { if (meta.unknownFiles != null) {
LOGGER.info("Copying unknown files/dir..."); 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"); File tempFile = new File(outFile.getParent(), outFile.getName() + ".apktool_temp");
boolean renamed = outFile.renameTo(tempFile); boolean renamed = outFile.renameTo(tempFile);
if(!renamed) { if(!renamed) {
@ -703,18 +686,18 @@ public class Androlib {
return ApktoolProperties.get("application.version"); return ApktoolProperties.get("application.version");
} }
private File[] parseUsesFramework(Map<String, Object> usesFramework) private File[] parseUsesFramework(UsesFramework usesFramework)
throws AndrolibException { throws AndrolibException {
if (usesFramework == null) { if (usesFramework == null) {
return null; return null;
} }
List<Integer> ids = (List<Integer>) usesFramework.get("ids"); List<Integer> ids = usesFramework.ids;
if (ids == null || ids.isEmpty()) { if (ids == null || ids.isEmpty()) {
return null; return null;
} }
String tag = (String) usesFramework.get("tag"); String tag = usesFramework.tag;
File[] files = new File[ids.size()]; File[] files = new File[ids.size()];
int i = 0; int i = 0;
for (int id : ids) { for (int id : ids) {
@ -744,15 +727,6 @@ public class Androlib {
return files; 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 Logger LOGGER = Logger.getLogger(Androlib.class.getName());
private final static String SMALI_DIRNAME = "smali"; private final static String SMALI_DIRNAME = "smali";

View File

@ -19,6 +19,9 @@ package brut.androlib;
import brut.androlib.err.InFileNotFoundException; import brut.androlib.err.InFileNotFoundException;
import brut.androlib.err.OutDirExistsException; import brut.androlib.err.OutDirExistsException;
import brut.androlib.err.UndefinedResObject; 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.AndrolibResources;
import brut.androlib.res.data.ResPackage; import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResTable; import brut.androlib.res.data.ResTable;
@ -298,12 +301,12 @@ public class ApkDecoder {
} }
private void writeMetaFile() throws AndrolibException { private void writeMetaFile() throws AndrolibException {
Map<String, Object> meta = new LinkedHashMap<String, Object>(); MetaInfo meta = new MetaInfo();
meta.put("version", Androlib.getVersion()); meta.version = Androlib.getVersion();
meta.put("apkFileName", mApkFile.getName()); meta.apkFileName = mApkFile.getName();
if (mDecodeResources != DECODE_RESOURCES_NONE && (hasManifest() || hasResources())) { if (mDecodeResources != DECODE_RESOURCES_NONE && (hasManifest() || hasResources())) {
meta.put("isFrameworkApk", mAndrolib.isFrameworkApk(getResTable())); meta.isFrameworkApk = mAndrolib.isFrameworkApk(getResTable());
putUsesFramework(meta); putUsesFramework(meta);
putSdkInfo(meta); putSdkInfo(meta);
putPackageInfo(meta); putPackageInfo(meta);
@ -316,7 +319,7 @@ public class ApkDecoder {
mAndrolib.writeMetaFile(mOutDir, meta); 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(); Set<ResPackage> pkgs = getResTable().listFramePackages();
if (pkgs.isEmpty()) { if (pkgs.isEmpty()) {
return; return;
@ -329,24 +332,22 @@ public class ApkDecoder {
} }
Arrays.sort(ids); Arrays.sort(ids);
Map<String, Object> uses = new LinkedHashMap<String, Object>(); meta.usesFramework = new UsesFramework();
uses.put("ids", ids); meta.usesFramework.ids = Arrays.asList(ids);
if (mAndrolib.apkOptions.frameworkTag != null) { 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(); Map<String, String> info = getResTable().getSdkInfo();
if (info.size() > 0) { 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 renamed = getResTable().getPackageRenamed();
String original = getResTable().getPackageOriginal(); String original = getResTable().getPackageOriginal();
@ -355,42 +356,35 @@ public class ApkDecoder {
id = getResTable().getPackage(renamed).getId(); id = getResTable().getPackage(renamed).getId();
} catch (UndefinedResObject ignored) {} } catch (UndefinedResObject ignored) {}
HashMap<String, String> packages = new HashMap<String, String>();
if (Strings.isNullOrEmpty(original)) { if (Strings.isNullOrEmpty(original)) {
return; return;
} }
meta.packageInfo = new PackageInfo();
// only put rename-manifest-package into apktool.yml, if the change will be required // only put rename-manifest-package into apktool.yml, if the change will be required
if (!renamed.equalsIgnoreCase(original)) { if (!renamed.equalsIgnoreCase(original)) {
packages.put("rename-manifest-package", renamed); meta.packageInfo.renameManifestPackage = renamed;
} }
packages.put("forced-package-id", String.valueOf(id)); meta.packageInfo.forcedPackageId = String.valueOf(id);
meta.put("packageInfo", packages);
} }
private void putVersionInfo(Map<String, Object> meta) throws AndrolibException { private void putVersionInfo(MetaInfo meta) throws AndrolibException {
Map<String, String> info = getResTable().getVersionInfo(); meta.versionInfo = getResTable().getVersionInfo();
if (info.size() > 0) {
meta.put("versionInfo", info);
}
} }
private void putUnknownInfo(Map<String, Object> meta) throws AndrolibException { private void putUnknownInfo(MetaInfo meta) throws AndrolibException {
Map<byte[], String> info = mAndrolib.mResUnknownFiles.getUnknownFiles(); meta.unknownFiles = mAndrolib.mResUnknownFiles.getUnknownFiles();
if (info.size() > 0) {
meta.put("unknownFiles", info);
}
} }
private void putFileCompressionInfo(Map<String, Object> meta) throws AndrolibException { private void putFileCompressionInfo(MetaInfo meta) throws AndrolibException {
if (!mUncompressedFiles.isEmpty()) { if (!mUncompressedFiles.isEmpty()) {
meta.put("doNotCompress", mUncompressedFiles); meta.doNotCompress = mUncompressedFiles;
} }
} }
private void putSharedLibraryInfo(Map<String, Object> meta) throws AndrolibException { private void putSharedLibraryInfo(MetaInfo meta) throws AndrolibException {
meta.put("sharedLibrary", mResTable.getSharedLibrary()); meta.sharedLibrary = mResTable.getSharedLibrary();
} }
private final Androlib mAndrolib; private final Androlib mAndrolib;

View File

@ -0,0 +1,78 @@
/**
* Copyright 2014 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.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;
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);
}
}
}

View File

@ -0,0 +1,21 @@
/**
* Copyright 2014 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.meta;
public class PackageInfo {
public String forcedPackageId;
public String renameManifestPackage;
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2014 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.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;
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);
}
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2014 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.meta;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.representer.Representer;
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()));
}
}
}

View File

@ -0,0 +1,23 @@
/**
* Copyright 2014 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.meta;
import java.util.List;
public class UsesFramework {
public List<Integer> ids;
public String tag;
}

View File

@ -0,0 +1,21 @@
/**
* Copyright 2014 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.meta;
public class VersionInfo {
public String versionCode;
public String versionName;
}

View File

@ -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);
}
}

View File

@ -19,26 +19,35 @@ package brut.androlib.res;
import brut.androlib.AndrolibException; import brut.androlib.AndrolibException;
import brut.androlib.ApkOptions; import brut.androlib.ApkOptions;
import brut.androlib.err.CantFindFrameworkResException; import brut.androlib.err.CantFindFrameworkResException;
import brut.androlib.meta.PackageInfo;
import brut.androlib.meta.VersionInfo;
import brut.androlib.res.data.*; import brut.androlib.res.data.*;
import brut.androlib.res.decoder.*; import brut.androlib.res.decoder.*;
import brut.androlib.res.decoder.ARSCDecoder.ARSCData; import brut.androlib.res.decoder.ARSCDecoder.ARSCData;
import brut.androlib.res.decoder.ARSCDecoder.FlagsOffset; 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.ResValuesXmlSerializable;
import brut.androlib.res.xml.ResXmlPatcher; import brut.androlib.res.xml.ResXmlPatcher;
import brut.common.BrutException; import brut.common.BrutException;
import brut.directory.*; import brut.directory.Directory;
import brut.util.*; 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.io.*;
import java.util.*; import java.util.*;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.zip.*; import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.io.File; import java.util.zip.ZipFile;
import java.io.IOException; import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;
import org.xmlpull.v1.XmlSerializer;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
@ -268,22 +277,22 @@ final public class AndrolibResources {
} }
} }
public void setVersionInfo(Map<String, String> map) { public void setVersionInfo(VersionInfo versionInfo) {
if (map != null) { if (versionInfo != null) {
mVersionCode = map.get("versionCode"); mVersionCode = versionInfo.versionCode;
mVersionName = map.get("versionName"); mVersionName = versionInfo.versionName;
} }
} }
public void setPackageInfo(Map<String, String> map) { public void setPackageRenamed(PackageInfo packageInfo) {
if (map != null) { if (packageInfo != null) {
mPackageRenamed = map.get("rename-manifest-package"); mPackageRenamed = packageInfo.renameManifestPackage;
} }
} }
public void setPackageId(Map<String, String> map) { public void setPackageId(PackageInfo packageInfo) {
if (map != null) { if (packageInfo != null) {
mPackageId = map.get("forced-package-id"); mPackageId = packageInfo.forcedPackageId;
} }
} }

View File

@ -18,6 +18,7 @@ package brut.androlib.res.data;
import brut.androlib.AndrolibException; import brut.androlib.AndrolibException;
import brut.androlib.err.UndefinedResObject; import brut.androlib.err.UndefinedResObject;
import brut.androlib.meta.VersionInfo;
import brut.androlib.res.AndrolibResources; import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.value.ResValue; import brut.androlib.res.data.value.ResValue;
import java.util.*; import java.util.*;
@ -39,8 +40,8 @@ public class ResTable {
private boolean mAnalysisMode = false; private boolean mAnalysisMode = false;
private boolean mSharedLibrary = false; private boolean mSharedLibrary = false;
private Map<String, String> mSdkInfo = new LinkedHashMap<String, String>(); private Map<String, String> mSdkInfo = new LinkedHashMap<>();
private Map<String, String> mVersionInfo = new LinkedHashMap<String, String>(); private VersionInfo mVersionInfo = new VersionInfo();
public ResTable() { public ResTable() {
mAndRes = null; mAndRes = null;
@ -174,11 +175,15 @@ public class ResTable {
mSdkInfo.put(key, value); mSdkInfo.put(key, value);
} }
public void addVersionInfo(String key, String value) { public void setVersionName(String versionName) {
mVersionInfo.put(key, value); mVersionInfo.versionName = versionName;
} }
public Map<String, String> getVersionInfo() { public void setVersionCode(String versionCode) {
mVersionInfo.versionCode = versionCode;
}
public VersionInfo getVersionInfo() {
return mVersionInfo; return mVersionInfo;
} }

View File

@ -15,7 +15,6 @@
*/ */
package brut.androlib.res.data; package brut.androlib.res.data;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -24,13 +23,13 @@ import java.util.Map;
*/ */
public class ResUnknownFiles { 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) { 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; return mUnknownFiles;
} }
} }

View File

@ -94,9 +94,9 @@ public class XmlPullStreamDecoder implements ResStreamDecoder {
if (attr_name.equalsIgnoreCase(("package"))) { if (attr_name.equalsIgnoreCase(("package"))) {
resTable.setPackageRenamed(pp.getAttributeValue(i)); resTable.setPackageRenamed(pp.getAttributeValue(i));
} else if (attr_name.equalsIgnoreCase("versionCode")) { } else if (attr_name.equalsIgnoreCase("versionCode")) {
resTable.addVersionInfo("versionCode", pp.getAttributeValue(i)); resTable.setVersionCode(pp.getAttributeValue(i));
} else if (attr_name.equalsIgnoreCase("versionName")) { } else if (attr_name.equalsIgnoreCase("versionName")) {
resTable.addVersionInfo("versionName", pp.getAttributeValue(i)); resTable.setVersionName(pp.getAttributeValue(i));
} }
} }
return true; return true;

View File

@ -15,6 +15,7 @@
*/ */
package brut.androlib; package brut.androlib;
import brut.androlib.meta.MetaInfo;
import brut.androlib.res.util.ExtFile; import brut.androlib.res.util.ExtFile;
import brut.common.BrutException; import brut.common.BrutException;
import brut.directory.FileDirectory; import brut.directory.FileDirectory;
@ -340,13 +341,13 @@ public class BuildAndDecodeTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void compareUnknownFiles() throws BrutException, IOException { private void compareUnknownFiles() throws BrutException, IOException {
Map<String, Object> control = new Androlib().readMetaFile(sTestOrigDir); MetaInfo control = new Androlib().readMetaFile(sTestOrigDir);
Map<String, Object> test = new Androlib().readMetaFile(sTestNewDir); MetaInfo test = new Androlib().readMetaFile(sTestNewDir);
assertTrue(control.containsKey("unknownFiles")); assertNotNull(control.unknownFiles);
assertTrue(test.containsKey("unknownFiles")); assertNotNull(test.unknownFiles);
Map<String, String> control_files = (Map<String, String>)control.get("unknownFiles"); Map<String, String> control_files = control.unknownFiles;
Map<String, String> test_files = (Map<String, String>)test.get("unknownFiles"); Map<String, String> test_files = test.unknownFiles;
assertTrue(control_files.size() == test_files.size()); assertTrue(control_files.size() == test_files.size());
// Make sure that the compression methods are still the same // Make sure that the compression methods are still the same

View File

@ -11,8 +11,8 @@ versionInfo:
versionName: '1.0' versionName: '1.0'
compressionType: false compressionType: false
unknownFiles: unknownFiles:
!!binary "aGlkZGVuLmZpbGU=": '8' hidden.file: '8'
!!binary "bm9uf3ByaW50YWJsZS5maWxl": '8' non\u007Fprintable.file: '8'
!!binary "c3RvcmVkLmZpbGU=": '0' stored.file: '0'
!!binary "dW5rX2ZvbGRlci91bmtub3duX2ZpbGU=": '8' unk_folder/unknown_file: '8'
!!binary "bGliX2J1ZzYwMy9idWc2MDM=": '8' lib_bug603/bug603: '8'