mirror of
https://github.com/revanced/Apktool.git
synced 2024-11-16 09:29:24 +01:00
Java NIO doesn't allow the preservation of the compression method (STORED vs DEFLATED), so unfortunately we need to fall back to ZipEntry-based output for unknown files.
This commit is contained in:
parent
5cf7696839
commit
628286c022
@ -33,9 +33,11 @@ import java.nio.file.*;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import java.util.zip.CRC32;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import org.yaml.snakeyaml.DumperOptions;
|
import org.yaml.snakeyaml.DumperOptions;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
@ -544,41 +546,97 @@ public class Androlib {
|
|||||||
|
|
||||||
public void buildUnknownFiles(File appDir, File outFile, Map<String, Object> meta)
|
public void buildUnknownFiles(File appDir, File outFile, Map<String, Object> meta)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
Path globalPath = Paths.get(appDir.getPath() + File.separatorChar + UNK_DIRNAME);
|
|
||||||
|
|
||||||
if (meta.containsKey("unknownFiles")) {
|
if (meta.containsKey("unknownFiles")) {
|
||||||
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 = (Map<String, String>)meta.get("unknownFiles");
|
||||||
|
File tempFile = new File(outFile.getParent(), outFile.getName() + ".apktool_temp");
|
||||||
|
boolean renamed = outFile.renameTo(tempFile);
|
||||||
|
if(!renamed) {
|
||||||
|
throw new AndrolibException("Unable to rename temporary file");
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try (
|
||||||
// set our filesystem options
|
ZipFile inputFile = new ZipFile(tempFile);
|
||||||
Map<String, String> zip_properties = new HashMap<>();
|
ZipOutputStream actualOutput = new ZipOutputStream(new FileOutputStream(outFile));
|
||||||
zip_properties.put("create", "false");
|
) {
|
||||||
zip_properties.put("encoding", "UTF-8");
|
byte[] buffer = new byte[4096 * 1024];
|
||||||
|
copyExistingFiles(inputFile, actualOutput, buffer);
|
||||||
// create filesystem
|
copyUnknownFiles(appDir, actualOutput, files, buffer);
|
||||||
Path path = Paths.get(outFile.getAbsolutePath());
|
|
||||||
|
|
||||||
try(
|
|
||||||
FileSystem fs = FileSystems.newFileSystem(path, null)
|
|
||||||
) {
|
|
||||||
// loop through files inside
|
|
||||||
for (Map.Entry<String,String> entry : files.entrySet()) {
|
|
||||||
|
|
||||||
File file = new File(globalPath.toFile(), entry.getKey());
|
|
||||||
Path dest = fs.getPath(entry.getKey());
|
|
||||||
Path destParent = dest.getParent();
|
|
||||||
if (destParent != null && !Files.exists(destParent)) {
|
|
||||||
Files.createDirectories(destParent);
|
|
||||||
}
|
|
||||||
Path newFile = Paths.get(file.getAbsolutePath());
|
|
||||||
Files.copy(newFile, dest, StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove our temporary file.
|
||||||
|
tempFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyExistingFiles(ZipFile inputFile, ZipOutputStream outputFile, byte[] buffer) throws IOException {
|
||||||
|
// First, copy the contents from the existing outFile:
|
||||||
|
Enumeration<? extends ZipEntry> entries = inputFile.entries();
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
ZipEntry entry = new ZipEntry(entries.nextElement());
|
||||||
|
// We can't reuse the compressed size because it depends on compression sizes.
|
||||||
|
entry.setCompressedSize(-1);
|
||||||
|
outputFile.putNextEntry(entry);
|
||||||
|
|
||||||
|
// No need to create directory entries in the final apk
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
copy(inputFile.getInputStream(entry), outputFile, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFile.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyUnknownFiles(File appDir, ZipOutputStream outputFile, Map<String, String> files, byte[] buffer) throws IOException {
|
||||||
|
File unknownFileDir = new File(appDir, UNK_DIRNAME);
|
||||||
|
|
||||||
|
// loop through unknown files
|
||||||
|
for (Map.Entry<String,String> unknownFileInfo : files.entrySet()) {
|
||||||
|
File inputFile = new File(unknownFileDir, unknownFileInfo.getKey());
|
||||||
|
if(inputFile.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipEntry newEntry = new ZipEntry(unknownFileInfo.getKey());
|
||||||
|
int method = Integer.valueOf(unknownFileInfo.getValue());
|
||||||
|
LOGGER.fine("Copying unknown file " + unknownFileInfo.getKey() + " with method " + unknownFileInfo.getValue());
|
||||||
|
if(method == ZipEntry.STORED) {
|
||||||
|
newEntry.setMethod(ZipEntry.STORED);
|
||||||
|
newEntry.setSize(inputFile.length());
|
||||||
|
newEntry.setCompressedSize(-1);
|
||||||
|
BufferedInputStream unknownFile = new BufferedInputStream(new FileInputStream(inputFile));
|
||||||
|
CRC32 crc = calculateCrc(unknownFile, buffer);
|
||||||
|
newEntry.setCrc(crc.getValue());
|
||||||
|
|
||||||
|
LOGGER.fine("\tsize: " + newEntry.getSize());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newEntry.setMethod(ZipEntry.DEFLATED);
|
||||||
|
}
|
||||||
|
outputFile.putNextEntry(newEntry);
|
||||||
|
|
||||||
|
BufferedInputStream unknownFile = new BufferedInputStream(new FileInputStream(inputFile));
|
||||||
|
copy(unknownFile, outputFile, buffer);
|
||||||
|
outputFile.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CRC32 calculateCrc(InputStream input, byte[] buffer) throws IOException {
|
||||||
|
CRC32 crc = new CRC32();
|
||||||
|
int bytesRead = 0;
|
||||||
|
while((bytesRead = input.read(buffer)) != -1) {
|
||||||
|
crc.update(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void copy(InputStream input, OutputStream output, byte[] buffer) throws IOException {
|
||||||
|
int bytesRead;
|
||||||
|
while((bytesRead = input.read(buffer)) != -1) {
|
||||||
|
output.write(buffer, 0, bytesRead);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,6 +308,11 @@ public class BuildAndDecodeTest {
|
|||||||
Map<String, String> control_files = (Map<String, String>)control.get("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> test_files = (Map<String, String>)test.get("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
|
||||||
|
for(Map.Entry<String, String> controlEntry : control_files.entrySet()) {
|
||||||
|
assertTrue(controlEntry.getValue().equals(test_files.get(controlEntry.getKey())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean compareBinaryFolder(String path, boolean res)
|
private boolean compareBinaryFolder(String path, boolean res)
|
||||||
|
@ -12,5 +12,6 @@ versionInfo:
|
|||||||
compressionType: false
|
compressionType: false
|
||||||
unknownFiles:
|
unknownFiles:
|
||||||
hidden.file: '8'
|
hidden.file: '8'
|
||||||
|
stored.file: '0'
|
||||||
unk_folder/unknown_file: '8'
|
unk_folder/unknown_file: '8'
|
||||||
lib_bug603/bug603: '8'
|
lib_bug603/bug603: '8'
|
@ -0,0 +1 @@
|
|||||||
|
This file is not compressed.
|
Loading…
Reference in New Issue
Block a user