package org.zywx.wbpalmstar.base.zip; import java.io.IOException; import java.io.OutputStream; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import java.util.zip.CRC32; import java.util.zip.Deflater; import java.util.zip.ZipException; public class CnZipOutputStream extends DeflaterOutputStream implements ZipConstants { private ZipEntry entry; @SuppressWarnings("rawtypes") private Vector entries = new Vector(); @SuppressWarnings("rawtypes") private Hashtable names = new Hashtable(); private CRC32 crc = new CRC32(); private long written; private long locoff = 0L; private String comment; private int method = 8; private boolean finished; private boolean closed = false; private String encoding = "UTF-8"; public static final int STORED = 0; public static final int DEFLATED = 8; private void ensureOpen() throws IOException { if (this.closed) throw new IOException("Stream closed"); } public CnZipOutputStream(OutputStream out) { super(out, new Deflater(-1, true)); this.usesDefaultDeflater = true; } public CnZipOutputStream(OutputStream out, String encoding) { this(out); this.encoding = encoding; } public void setComment(String comment) { if ((comment != null) && (comment.length() > 21845) && (getUTF8Length(comment) > 65535)) { throw new IllegalArgumentException("ZIP file comment too long."); } this.comment = comment; } public void setMethod(int method) { if ((method != 8) && (method != 0)) { throw new IllegalArgumentException("invalid compression method"); } this.method = method; } public void setLevel(int level) { this.def.setLevel(level); } @SuppressWarnings("unchecked") public void putNextEntry(ZipEntry e) throws IOException { ensureOpen(); if (this.entry != null) { closeEntry(); } if (e.time == -1L) { e.setTime(System.currentTimeMillis()); } if (e.method == -1) { e.method = this.method; } switch (e.method) { case 8: if ((e.size == -1L) || (e.csize == -1L) || (e.crc == -1L)) { e.flag = 8; } else if ((e.size != -1L) && (e.csize != -1L) && (e.crc != -1L)) { e.flag = 0; } else throw new ZipException( "DEFLATED entry missing size, compressed size, or crc-32"); e.version = 20; break; case 0: if (e.size == -1L) e.size = e.csize; else if (e.csize == -1L) e.csize = e.size; else if (e.size != e.csize) { throw new ZipException( "STORED entry where compressed != uncompressed size"); } if ((e.size == -1L) || (e.crc == -1L)) { throw new ZipException( "STORED entry missing size, compressed size, or crc-32"); } e.version = 10; e.flag = 0; break; default: throw new ZipException("unsupported compression method"); } e.offset = this.written; if (this.names.put(e.name, e) != null) { throw new ZipException("duplicate entry: " + e.name); } writeLOC(e); this.entries.addElement(e); this.entry = e; } public void closeEntry() throws IOException { ensureOpen(); ZipEntry e = this.entry; if (e != null) { switch (e.method) { case 8: this.def.finish(); while (!this.def.finished()) { deflate(); } if ((e.flag & 0x8) == 0) { if (e.size != this.def.getTotalIn()) { throw new ZipException("invalid entry size (expected " + e.size + " but got " + this.def.getTotalIn() + " bytes)"); } if (e.csize != this.def.getTotalOut()) { throw new ZipException( "invalid entry compressed size (expected " + e.csize + " but got " + this.def.getTotalOut() + " bytes)"); } if (e.crc != this.crc.getValue()) throw new ZipException( "invalid entry CRC-32 (expected 0x" + Long.toHexString(e.crc) + " but got 0x" + Long.toHexString(this.crc.getValue()) + ")"); } else { e.size = this.def.getTotalIn(); e.csize = this.def.getTotalOut(); e.crc = this.crc.getValue(); writeEXT(e); } this.def.reset(); this.written += e.csize; break; case 0: if (e.size != this.written - this.locoff) { throw new ZipException("invalid entry size (expected " + e.size + " but got " + (this.written - this.locoff) + " bytes)"); } if (e.crc == this.crc.getValue()) break; throw new ZipException("invalid entry crc-32 (expected 0x" + Long.toHexString(e.crc) + " but got 0x" + Long.toHexString(this.crc.getValue()) + ")"); default: throw new InternalError("invalid compression method"); } this.crc.reset(); this.entry = null; } } public synchronized void write(byte[] b, int off, int len) throws IOException { ensureOpen(); if ((off < 0) || (len < 0) || (off > b.length - len)) throw new IndexOutOfBoundsException(); if (len == 0) { return; } if (this.entry == null) { throw new ZipException("no current ZIP entry"); } switch (this.entry.method) { case 8: super.write(b, off, len); break; case 0: this.written += len; if (this.written - this.locoff > this.entry.size) { throw new ZipException( "attempt to write past end of STORED entry"); } this.out.write(b, off, len); break; default: throw new InternalError("invalid compression method"); } this.crc.update(b, off, len); } @SuppressWarnings("rawtypes") public void finish() throws IOException { ensureOpen(); if (this.finished) { return; } if (this.entry != null) { closeEntry(); } if (this.entries.size() < 1) { throw new ZipException("ZIP file must have at least one entry"); } long off = this.written; Enumeration e = this.entries.elements(); while (e.hasMoreElements()) { writeCEN((ZipEntry) e.nextElement()); } writeEND(off, this.written - off); this.finished = true; } public void close() throws IOException { if (!this.closed) { super.close(); this.closed = true; } } private void writeLOC(ZipEntry e) throws IOException { writeInt(67324752L); writeShort(e.version); writeShort(e.flag); writeShort(e.method); writeInt(e.time); if ((e.flag & 0x8) == 8) { writeInt(0L); writeInt(0L); writeInt(0L); } else { writeInt(e.crc); writeInt(e.csize); writeInt(e.size); } byte[] nameBytes = (byte[]) null; try { if (this.encoding.toUpperCase().equals("UTF-8")) { nameBytes = getUTF8Bytes(e.name); } else nameBytes = e.name.getBytes(this.encoding); } catch (Exception byteE) { nameBytes = getUTF8Bytes(e.name); } writeShort(nameBytes.length); writeShort(e.extra != null ? e.extra.length : 0); writeBytes(nameBytes, 0, nameBytes.length); if (e.extra != null) { writeBytes(e.extra, 0, e.extra.length); } this.locoff = this.written; } private void writeEXT(ZipEntry e) throws IOException { writeInt(134695760L); writeInt(e.crc); writeInt(e.csize); writeInt(e.size); } private void writeCEN(ZipEntry e) throws IOException { writeInt(33639248L); writeShort(e.version); writeShort(e.version); writeShort(e.flag); writeShort(e.method); writeInt(e.time); writeInt(e.crc); writeInt(e.csize); writeInt(e.size); byte[] nameBytes = (byte[]) null; try { if (this.encoding.toUpperCase().equals("UTF-8")) { nameBytes = getUTF8Bytes(e.name); } else nameBytes = e.name.getBytes(this.encoding); } catch (Exception byteE) { nameBytes = getUTF8Bytes(e.name); } writeShort(nameBytes.length); writeShort(e.extra != null ? e.extra.length : 0); byte[] commentBytes; if (e.comment != null) { commentBytes = getUTF8Bytes(e.comment); writeShort(commentBytes.length); } else { commentBytes = (byte[]) null; writeShort(0); } writeShort(0); writeShort(0); writeInt(0L); writeInt(e.offset); writeBytes(nameBytes, 0, nameBytes.length); if (e.extra != null) { writeBytes(e.extra, 0, e.extra.length); } if (commentBytes != null) writeBytes(commentBytes, 0, commentBytes.length); } private void writeEND(long off, long len) throws IOException { writeInt(101010256L); writeShort(0); writeShort(0); writeShort(this.entries.size()); writeShort(this.entries.size()); writeInt(len); writeInt(off); if (this.comment != null) { byte[] b = getUTF8Bytes(this.comment); writeShort(b.length); writeBytes(b, 0, b.length); } else { writeShort(0); } } private void writeShort(int v) throws IOException { OutputStream out = this.out; out.write(v >>> 0 & 0xFF); out.write(v >>> 8 & 0xFF); this.written += 2L; } private void writeInt(long v) throws IOException { OutputStream out = this.out; out.write((int) (v >>> 0 & 0xFF)); out.write((int) (v >>> 8 & 0xFF)); out.write((int) (v >>> 16 & 0xFF)); out.write((int) (v >>> 24 & 0xFF)); this.written += 4L; } private void writeBytes(byte[] b, int off, int len) throws IOException { this.out.write(b, off, len); this.written += len; } static int getUTF8Length(String s) { int count = 0; for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); if (ch <= '') count++; else if (ch <= '߿') count += 2; else { count += 3; } } return count; } private static byte[] getUTF8Bytes(String s) { char[] c = s.toCharArray(); int len = c.length; int count = 0; for (int i = 0; i < len; i++) { int ch = c[i]; if (ch <= 127) count++; else if (ch <= 2047) count += 2; else { count += 3; } } byte[] b = new byte[count]; int off = 0; for (int i = 0; i < len; i++) { int ch = c[i]; if (ch <= 127) { b[(off++)] = (byte) ch; } else if (ch <= 2047) { b[(off++)] = (byte) (ch >> 6 | 0xC0); b[(off++)] = (byte) (ch & 0x3F | 0x80); } else { b[(off++)] = (byte) (ch >> 12 | 0xE0); b[(off++)] = (byte) (ch >> 6 & 0x3F | 0x80); b[(off++)] = (byte) (ch & 0x3F | 0x80); } } return b; } }