graypause/tools/ZipAlign.java

55 lines
2.4 KiB
Java
Raw Normal View History

import java.io.*;
import java.util.*;
import java.util.zip.*;
/**
* Minimal zipalign: copies a zip, padding STORED entries so their data is 4-byte aligned,
* and appends any extra files given after the output path (compressed, at the zip root).
*/
public class ZipAlign {
public static void main(String[] a) throws IOException {
try (ZipFile in = new ZipFile(a[0]);
CountingOut cnt = new CountingOut(new BufferedOutputStream(new FileOutputStream(a[1])));
ZipOutputStream out = new ZipOutputStream(cnt)) {
for (ZipEntry e : Collections.list(in.entries())) {
ZipEntry n = new ZipEntry(e.getName());
n.setMethod(e.getMethod());
n.setTime(946684800000L); // 2000-01-01, inside the DOS date range (no extra timestamp field)
if (e.getMethod() == ZipEntry.STORED) {
n.setSize(e.getSize());
n.setCompressedSize(e.getSize());
n.setCrc(e.getCrc());
out.flush();
long dataStart = cnt.count + 30 + n.getName().getBytes("UTF-8").length;
int pad = (int) ((4 - dataStart % 4) % 4);
if (pad > 0) n.setExtra(padExtra(pad));
}
out.putNextEntry(n);
in.getInputStream(e).transferTo(out);
out.closeEntry();
}
for (int i = 2; i < a.length; i++) {
File f = new File(a[i]);
ZipEntry n = new ZipEntry(f.getName());
n.setTime(946684800000L);
out.putNextEntry(n);
try (InputStream fin = new FileInputStream(f)) { fin.transferTo(out); }
out.closeEntry();
}
}
}
// An extra field needs a 4-byte header, so use one field of pad+4 bytes (Android's 0xD935 alignment tag).
static byte[] padExtra(int pad) {
byte[] x = new byte[pad + 4];
x[0] = 0x35; x[1] = (byte) 0xD9; x[2] = (byte) pad;
return x;
}
static class CountingOut extends FilterOutputStream {
long count;
CountingOut(OutputStream o) { super(o); }
@Override public void write(int b) throws IOException { out.write(b); count++; }
@Override public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); count += len; }
}
}