1 package jp.sfjp.armadillo.archive.zip;
3 import static java.lang.String.format;
6 import java.nio.channels.*;
8 import java.util.zip.*;
13 public final class ZipHeader {
15 static final int SIGN_LOC = 0x04034B50;
16 static final int SIGN_CEN = 0x02014B50;
17 static final int SIGN_END = 0x06054B50;
18 static final int SIGN_EXT = 0x08074B50;
19 static final int LENGTH_LOC = 30;
20 static final int LENGTH_CEN = 46;
21 static final int LENGTH_END = 22;
22 static final int LENGTH_EXT = 16;
24 private final ByteBuffer buffer;
25 private final List<ZipEntry> entries;
29 this.buffer = ByteBuffer.allocate(LENGTH_CEN).order(ByteOrder.LITTLE_ENDIAN);
30 this.entries = new ArrayList<ZipEntry>();
34 public ZipEntry read(InputStream is) throws IOException {
38 public ZipEntry readLOC(InputStream is) throws IOException {
39 // Local file header (LOC)
40 final int signature; // local file header signature
41 final short version; // version needed to extract
42 final short flags; // general purpose bit flag
43 final short method; // compression method
44 final short mtime; // last mod file time
45 final short mdate; // last mod file date
46 final int crc; // crc-32
47 final int compsize; // compressed size
48 final int uncompsize; // uncompressed size
49 final short namelen; // file name length
50 final short extlen; // extra field length
51 final byte[] nameb; // file name
54 buffer.limit(LENGTH_LOC);
55 Channels.newChannel(is).read(buffer);
56 if (buffer.position() == 0)
59 signature = buffer.getInt();
60 if (signature == SIGN_CEN || signature == SIGN_END)
62 if (signature != SIGN_LOC)
63 throw new ZipException(format("invalid LOC header: signature=0x%X", signature));
64 version = buffer.getShort();
65 flags = buffer.getShort();
66 method = buffer.getShort();
67 mtime = buffer.getShort();
68 mdate = buffer.getShort();
69 crc = buffer.getInt();
70 compsize = buffer.getInt();
71 uncompsize = buffer.getInt();
72 namelen = buffer.getShort();
73 extlen = buffer.getShort();
74 nameb = new byte[namelen];
75 if (is.read(nameb) != namelen)
76 throw new ZipException("invalid LOC header (name length)");
78 if (is.skip(extlen) != extlen)
79 throw new ZipException("invalid LOC header (extra length)");
80 ZipEntry entry = new ZipEntry();
81 entry.signature = signature;
82 entry.version = version;
84 entry.method = method;
88 entry.compsize = compsize;
89 entry.uncompsize = uncompsize;
94 @SuppressWarnings("unused")
95 public ZipEntry readCEN(InputStream is) throws IOException {
96 // Central file header (CEN)
97 final int signature; // central file header signature
98 final short madever; // version made by
99 final short needver; // version needed to extract
100 final short flags; // general purpose bit flag
101 final short method; // compression method
102 final short mtime; // last mod file time
103 final short mdate; // last mod file date
104 final int crc; // crc-32
105 final int compsize; // compressed size
106 final int uncompsize; // uncompressed size
107 final short namelen; // file name length
108 final short extlen; // extra field length
109 final short fcmlen; // file comment length
110 final short dnum; // disk number start
111 final short infattr; // internal file attributes
112 final int exfattr; // external file attributes
113 final int reloff; // relative offset of local header
114 final byte[] nameb; // file name
117 buffer.limit(LENGTH_CEN);
118 Channels.newChannel(is).read(buffer);
119 if (buffer.position() == 0)
122 signature = buffer.getInt();
123 if (signature == SIGN_END)
125 else if (signature != SIGN_CEN)
126 throw new ZipException(format("invalid CEN header: signature=0x%X", signature));
127 madever = buffer.getShort();
128 needver = buffer.getShort();
129 flags = buffer.getShort();
130 method = buffer.getShort();
131 mtime = buffer.getShort();
132 mdate = buffer.getShort();
133 crc = buffer.getInt();
134 compsize = buffer.getInt();
135 uncompsize = buffer.getInt();
136 namelen = buffer.getShort();
137 extlen = buffer.getShort();
138 fcmlen = buffer.getShort();
139 dnum = buffer.getShort();
140 infattr = buffer.getShort();
141 exfattr = buffer.getInt();
142 reloff = buffer.getInt();
143 nameb = new byte[namelen];
144 if (is.read(nameb) != namelen)
145 throw new ZipException("invalid LOC header (name length)");
147 if (is.skip(extlen) != extlen)
148 throw new ZipException("invalid LOC header (extra length)");
149 ZipEntry entry = new ZipEntry();
150 entry.signature = signature;
152 entry.method = method;
156 entry.compsize = compsize;
157 entry.uncompsize = uncompsize;
158 entry.extlen = extlen;
159 entry.reloff = reloff;
160 entry.setName(nameb);
164 public ZipEndEntry readEND(InputStream is) throws IOException {
165 // End of central dir header (END)
166 final int signature; // end of central dir signature
167 final short disknum; // number of this disk
168 final short disknumCEN; // number of the disk with the start of the central directory
169 final short countDiskCENs; // total number of entries in the central directory on this disk
170 final short countCENs; // total number of entries in the central directory
171 final int sizeCENs; // size of the central directory
172 final int offsetStartCEN; // offset of start of central directory with respect to the starting disk number
173 final short commentlen; // .ZIP file comment length
174 final byte[] comment; // .ZIP file comment
177 buffer.limit(LENGTH_END);
178 Channels.newChannel(is).read(buffer);
179 if (buffer.position() == 0)
182 signature = buffer.getInt();
183 if (signature != SIGN_END)
185 disknum = buffer.getShort();
186 disknumCEN = buffer.getShort();
187 countDiskCENs = buffer.getShort();
188 countCENs = buffer.getShort();
189 sizeCENs = buffer.getInt();
190 offsetStartCEN = buffer.getInt();
191 commentlen = buffer.getShort();
192 comment = new byte[commentlen];
194 if (is.skip(commentlen) != commentlen)
195 throw new ZipException("invalid END header (comment length)");
196 ZipEndEntry entry = new ZipEndEntry();
197 entry.signature = signature;
198 entry.disknum = disknum;
199 entry.disknumCEN = disknumCEN;
200 entry.countDiskCENs = countDiskCENs;
201 entry.countCENs = countCENs;
202 entry.sizeCENs = sizeCENs;
203 entry.offsetStartCEN = offsetStartCEN;
204 entry.commentlen = commentlen;
205 entry.comment = comment;
209 public ZipEntry readEXT(InputStream is) throws IOException {
210 ZipEntry entry = new ZipEntry();
215 public boolean readEXT(InputStream is, ZipEntry entry) throws IOException {
216 // Extend header (EXT)
217 final int signature; // extend header signature
218 final int crc; // crc-32
219 final int compsize; // compressed size
220 final int uncompsize; // uncompressed size
223 buffer.limit(LENGTH_EXT);
224 Channels.newChannel(is).read(buffer);
225 if (buffer.position() == 0)
228 signature = buffer.getInt();
229 if (signature != SIGN_EXT)
230 throw new ZipException(format("invalid EXT header: signature=0x%X", signature));
231 crc = buffer.getInt();
232 compsize = buffer.getInt();
233 uncompsize = buffer.getInt();
234 entry.extOverwritten = true;
236 entry.compsize = compsize;
237 entry.uncompsize = uncompsize;
241 public void write(OutputStream os, ZipEntry entry) throws IOException {
245 public void writeLOC(OutputStream os, ZipEntry entry) throws IOException {
246 // Local file header (LOC)
247 final int signature; // local file header signature
248 final short version; // version needed to extract
249 final short flags; // general purpose bit flag
250 final short method; // compression method
251 final short mtime; // last mod file time
252 final short mdate; // last mod file date
253 final int crc; // crc-32
254 final int compsize; // compressed size
255 final int uncompsize; // uncompressed size
256 final short namelen; // file name length
257 final short extlen; // extra field length
258 final byte[] nameb; // file name
260 final boolean hasEXT = entry.hasEXT();
261 nameb = entry.getNameAsBytes();
262 if (nameb.length > Short.MAX_VALUE)
263 throw new ZipException("too long name: length=" + nameb.length);
264 entry.reloff = (int)(offset & 0xFFFFFFFFL);
265 signature = SIGN_LOC;
266 version = entry.version;
268 method = entry.method;
271 crc = hasEXT ? 0 : entry.crc;
272 compsize = hasEXT ? 0 : entry.compsize;
273 uncompsize = hasEXT ? 0 : entry.uncompsize;
274 namelen = (short)(nameb.length & 0xFFFF);
275 extlen = entry.extlen;
276 assert offset <= 0xFFFFFFFFL;
278 buffer.putInt(signature);
279 buffer.putShort(version);
280 buffer.putShort(flags);
281 buffer.putShort(method);
282 buffer.putShort(mtime);
283 buffer.putShort(mdate);
285 buffer.putInt(compsize);
286 buffer.putInt(uncompsize);
287 buffer.putShort(namelen);
288 buffer.putShort(extlen);
289 assert buffer.position() == LENGTH_LOC;
291 Channels.newChannel(os).write(buffer);
297 assert compsize >= 0;
298 offset += LENGTH_LOC + namelen + extlen + compsize;
301 public void writeCEN(OutputStream os, ZipEntry entry) throws IOException {
302 // Central file header (CEN)
303 final int signature; // central file header signature
304 final short madever; // version made by
305 final short needver; // version needed to extract
306 final short flags; // general purpose bit flag
307 final short method; // compression method
308 final short mtime; // last mod file time
309 final short mdate; // last mod file date
310 final int crc; // crc-32
311 final int compsize; // compressed size
312 final int uncompsize; // uncompressed size
313 final short namelen; // file name length
314 final short extlen; // extra field length
315 final short commlen; // file comment length
316 final short disknum; // disk number start
317 final short inattr; // internal file attributes
318 final int exattr; // external file attributes
319 final int reloff; // relative offset of local header
320 final byte[] nameb; // file name
322 signature = SIGN_CEN;
323 madever = entry.version;
324 needver = entry.version;
326 method = entry.method;
330 compsize = entry.compsize;
331 uncompsize = entry.uncompsize;
332 nameb = entry.getNameAsBytes();
333 if (nameb.length > Short.MAX_VALUE)
334 throw new ZipException("too long name: length=" + nameb.length);
335 namelen = (short)nameb.length;
336 extlen = 0; // not support
337 commlen = 0; // not support
338 disknum = 0; // not support
339 inattr = 0; // not support
340 exattr = 0; // not support
341 reloff = entry.reloff;
343 buffer.putInt(signature);
344 buffer.putShort(madever);
345 buffer.putShort(needver);
346 buffer.putShort(flags);
347 buffer.putShort(method);
348 buffer.putShort(mtime);
349 buffer.putShort(mdate);
351 buffer.putInt(compsize);
352 buffer.putInt(uncompsize);
353 buffer.putShort(namelen);
354 buffer.putShort(extlen);
355 buffer.putShort(commlen);
356 buffer.putShort(disknum);
357 buffer.putShort(inattr);
358 buffer.putInt(exattr);
359 buffer.putInt(reloff);
360 assert buffer.position() == LENGTH_CEN;
362 Channels.newChannel(os).write(buffer);
365 offset += LENGTH_CEN + namelen + extlen;
368 public void writeEND(OutputStream os) throws IOException {
369 for (final ZipEntry entry : entries)
371 writeEND(os, ZipEndEntry.create(offset, entries));
374 public void writeEND(OutputStream os, ZipEndEntry entry) throws IOException {
375 // End of central dir header (END)
376 final int signature; // end of central dir signature
377 final short disknum; // number of this disk
378 final short disknumCEN; // number of the disk with the start of the central directory
379 final short countDiskCENs; // total number of entries in the central directory on this disk
380 final short countCENs; // total number of entries in the central directory
381 final int sizeCENs; // size of the central directory
382 final int offsetStartCEN; // offset of start of central directory with respect to the starting disk number
383 final short commentlen; // .ZIP file comment length
384 final byte[] comment; // .ZIP file comment
386 signature = entry.signature;
387 disknum = entry.disknum;
388 disknumCEN = entry.disknumCEN;
389 countDiskCENs = entry.countDiskCENs;
390 countCENs = entry.countCENs;
391 sizeCENs = entry.sizeCENs;
392 offsetStartCEN = entry.offsetStartCEN;
394 comment = new byte[0];
396 buffer.putInt(signature);
397 buffer.putShort(disknum);
398 buffer.putShort(disknumCEN);
399 buffer.putShort(countDiskCENs);
400 buffer.putShort(countCENs);
401 buffer.putInt(sizeCENs);
402 buffer.putInt(offsetStartCEN);
403 buffer.putShort(commentlen);
405 assert buffer.position() == LENGTH_END;
407 Channels.newChannel(os).write(buffer);
409 offset += LENGTH_END;
412 public void writeEXT(OutputStream os, ZipEntry entry) throws IOException {
413 // Extend header (EXT)
414 final int signature; // extend header signature
415 final int crc; // crc-32
416 final int compsize; // compressed size
417 final int uncompsize; // uncompressed size
419 signature = SIGN_EXT;
421 compsize = entry.compsize;
422 uncompsize = entry.uncompsize;
424 buffer.putInt(signature);
426 buffer.putInt(compsize);
427 buffer.putInt(uncompsize);
428 assert buffer.position() == LENGTH_EXT;
430 Channels.newChannel(os).write(buffer);
432 offset += LENGTH_EXT;
433 assert entry.compsize >= 0;
434 offset += entry.compsize;
437 public void clearEntries() {