mirror of
https://github.com/revanced/Apktool.git
synced 2025-05-01 06:34:25 +02:00
219 lines
7.4 KiB
Java
219 lines
7.4 KiB
Java
/*
|
|
* Copyright 2010 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.
|
|
* under the License.
|
|
*/
|
|
|
|
package brut.androlib.res;
|
|
|
|
import brut.androlib.*;
|
|
import brut.androlib.res.data.*;
|
|
import brut.androlib.res.data.value.ResFileValue;
|
|
import brut.androlib.res.data.value.ResXmlSerializable;
|
|
import brut.androlib.res.decoder.*;
|
|
import brut.androlib.res.jni.JniPackage;
|
|
import brut.androlib.res.jni.JniPackageGroup;
|
|
import brut.common.BrutException;
|
|
import brut.directory.Directory;
|
|
import brut.directory.DirectoryException;
|
|
import brut.directory.FileDirectory;
|
|
import brut.directory.ZipRODirectory;
|
|
import brut.util.Jar;
|
|
import brut.util.OS;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.util.Arrays;
|
|
|
|
/**
|
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
|
*/
|
|
final public class AndrolibResources {
|
|
static {
|
|
Jar.load("/libAndroid.so");
|
|
}
|
|
|
|
private final File mAndroidJar;
|
|
|
|
public AndrolibResources(String androidJar) {
|
|
this(new File(androidJar));
|
|
}
|
|
|
|
public AndrolibResources(File androidJar) {
|
|
this.mAndroidJar = androidJar;
|
|
}
|
|
|
|
public ResTable getResTable(File apkFile) throws AndrolibException {
|
|
ResTable resTable = new ResTable();
|
|
loadApk(resTable, new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath()), false);
|
|
loadApk(resTable, apkFile, true);
|
|
return resTable;
|
|
}
|
|
|
|
public void decode(ResTable resTable, File apkFile, File outDir)
|
|
throws AndrolibException {
|
|
ResXmlSerializer serial = getResXmlSerializer(resTable);
|
|
ResFileDecoder fileDecoder = getResFileDecoder(serial);
|
|
serial.setCurrentPackage(
|
|
resTable.listMainPackages().iterator().next());
|
|
|
|
Directory in, out;
|
|
try {
|
|
in = new ZipRODirectory(apkFile);
|
|
out = new FileDirectory(outDir);
|
|
|
|
fileDecoder.decode(
|
|
in, "AndroidManifest.xml", out, "AndroidManifest.xml", "xml");
|
|
|
|
in = in.getDir("res");
|
|
out = out.createDir("res");
|
|
} catch (DirectoryException ex) {
|
|
throw new AndrolibException(ex);
|
|
}
|
|
|
|
for (ResPackage pkg : resTable.listMainPackages()) {
|
|
serial.setCurrentPackage(pkg);
|
|
for (ResResource res : pkg.listFiles()) {
|
|
ResFileValue fileValue = (ResFileValue) res.getValue();
|
|
fileDecoder.decode(in, fileValue.getStrippedPath(),
|
|
out, res.getFilePath());
|
|
}
|
|
for (ResValuesFile valuesFile : pkg.listValuesFiles()) {
|
|
generateValuesFile(valuesFile, out, serial);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void aaptPackage(File apkFile, File manifest, File resDir)
|
|
throws AndrolibException {
|
|
aaptPackage(apkFile, manifest, resDir, null, false);
|
|
}
|
|
|
|
public void aaptPackage(File apkFile, File manifest, File resDir,
|
|
File rawDir, boolean update) throws AndrolibException {
|
|
String[] cmd = new String[12];
|
|
int i = 0;
|
|
cmd[i++] = "aapt";
|
|
cmd[i++] = "p";
|
|
if (update) {
|
|
cmd[i++] = "-u";
|
|
}
|
|
cmd[i++] = "-F";
|
|
cmd[i++] = apkFile.getAbsolutePath();
|
|
cmd[i++] = "-I";
|
|
cmd[i++] = mAndroidJar.getAbsolutePath();
|
|
if (manifest != null) {
|
|
cmd[i++] = "-M";
|
|
cmd[i++] = manifest.getAbsolutePath();
|
|
}
|
|
if (resDir != null) {
|
|
cmd[i++] = "-S";
|
|
cmd[i++] = resDir.getAbsolutePath();
|
|
}
|
|
if (rawDir != null) {
|
|
cmd[i++] = rawDir.getAbsolutePath();
|
|
}
|
|
|
|
try {
|
|
OS.exec(Arrays.copyOf(cmd, i));
|
|
} catch (BrutException ex) {
|
|
throw new AndrolibException(ex);
|
|
}
|
|
}
|
|
|
|
public void tagSmaliResIDs(ResTable resTable, File smaliDir)
|
|
throws AndrolibException {
|
|
new ResSmaliUpdater().tagResIDs(resTable, smaliDir);
|
|
}
|
|
|
|
public void updateSmaliResIDs(ResTable resTable, File smaliDir) throws AndrolibException {
|
|
new ResSmaliUpdater().updateResIDs(resTable, smaliDir);
|
|
}
|
|
|
|
public ResFileDecoder getResFileDecoder(ResXmlSerializer serializer) {
|
|
ResStreamDecoderContainer decoders =
|
|
new ResStreamDecoderContainer();
|
|
decoders.setDecoder("raw", new ResRawStreamDecoder());
|
|
decoders.setDecoder("xml",
|
|
new ResXmlStreamDecoder(serializer));
|
|
return new ResFileDecoder(decoders);
|
|
}
|
|
|
|
public ResXmlSerializer getResXmlSerializer(ResTable resTable) {
|
|
ResXmlSerializer serial = new ResXmlSerializer();
|
|
serial.setProperty(serial.PROPERTY_SERIALIZER_INDENTATION, " ");
|
|
return serial;
|
|
}
|
|
|
|
private void generateValuesFile(ResValuesFile valuesFile, Directory out,
|
|
ResXmlSerializer serial) throws AndrolibException {
|
|
try {
|
|
OutputStream outStream = out.getFileOutput(valuesFile.getPath());
|
|
serial.setOutput((outStream), null);
|
|
serial.setDecodingEnabled(false);
|
|
serial.startDocument(null, null);
|
|
serial.startTag(null, "resources");
|
|
|
|
for (ResResource res : valuesFile.listResources()) {
|
|
|
|
((ResXmlSerializable) res.getValue())
|
|
.serializeToXml(serial, res);
|
|
}
|
|
|
|
serial.endTag(null, "resources");
|
|
serial.endDocument();
|
|
serial.flush();
|
|
outStream.close();
|
|
} catch (IOException ex) {
|
|
throw new AndrolibException(
|
|
"Could not generate: " + valuesFile.getPath(), ex);
|
|
} catch (DirectoryException ex) {
|
|
throw new AndrolibException(
|
|
"Could not generate: " + valuesFile.getPath(), ex);
|
|
}
|
|
}
|
|
|
|
private void loadApk(ResTable resTable, File apkFile, boolean main)
|
|
throws AndrolibException {
|
|
JniPackageGroup[] groups =
|
|
nativeGetPackageGroups(apkFile.getAbsolutePath());
|
|
if (groups.length != 1) {
|
|
throw new AndrolibException(
|
|
"Apk's with multiple or zero package groups not supported");
|
|
}
|
|
for (int i = 0; i < groups.length; i++) {
|
|
// if (groups.length != 1 && i == 0) {
|
|
// continue;
|
|
// }
|
|
for (JniPackage jniPkg : groups[i].packages) {
|
|
ResPackage pkg = new JniPackageDecoder().decode(jniPkg, resTable);
|
|
resTable.addPackage(pkg, main);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static String escapeForResXml(String value) {
|
|
value = value.replace("'", "\\'");
|
|
value = value.replace("\n", "\\n\n");
|
|
char c = value.charAt(0);
|
|
if (c == '@' || c == '#' || c == '?') {
|
|
return '\\' + value;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
private static final native JniPackageGroup[] nativeGetPackageGroups(
|
|
String apkFileName);
|
|
}
|