OSDN Git Service

Add new source files
[armadillo/armadillo1.git] / src / jp / sfjp / armadillo / archive / zip / ZipHeader.java
1 package jp.sfjp.armadillo.archive.zip;
2
3 import static java.lang.String.format;
4 import java.io.*;
5 import java.nio.*;
6 import java.nio.channels.*;
7 import java.util.*;
8 import java.util.zip.*;
9
10 /**
11  * ZIP archive header.
12  */
13 public final class ZipHeader {
14
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;
23
24     private final ByteBuffer buffer;
25     private final List<ZipEntry> entries;
26     private long offset;
27
28     public ZipHeader() {
29         this.buffer = ByteBuffer.allocate(LENGTH_CEN).order(ByteOrder.LITTLE_ENDIAN);
30         this.entries = new ArrayList<ZipEntry>();
31         this.offset = 0L;
32     }
33
34     public ZipEntry read(InputStream is) throws IOException {
35         return readLOC(is);
36     }
37
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
52         // ---
53         buffer.clear();
54         buffer.limit(LENGTH_LOC);
55         Channels.newChannel(is).read(buffer);
56         if (buffer.position() == 0)
57             return null;
58         buffer.rewind();
59         signature = buffer.getInt();
60         if (signature == SIGN_CEN || signature == SIGN_END)
61             return null;
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)");
77         if (extlen > 0)
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;
83         entry.flags = flags;
84         entry.method = method;
85         entry.mdate = mdate;
86         entry.mtime = mtime;
87         entry.crc = crc;
88         entry.compsize = compsize;
89         entry.uncompsize = uncompsize;
90         entry.setName(nameb);
91         return entry;
92     }
93
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
115         // ---
116         buffer.clear();
117         buffer.limit(LENGTH_CEN);
118         Channels.newChannel(is).read(buffer);
119         if (buffer.position() == 0)
120             return null;
121         buffer.rewind();
122         signature = buffer.getInt();
123         if (signature == SIGN_END)
124             return null;
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)");
146         if (extlen > 0)
147             if (is.skip(extlen) != extlen)
148                 throw new ZipException("invalid LOC header (extra length)");
149         ZipEntry entry = new ZipEntry();
150         entry.signature = signature;
151         entry.flags = flags;
152         entry.method = method;
153         entry.mtime = mtime;
154         entry.mdate = mdate;
155         entry.crc = crc;
156         entry.compsize = compsize;
157         entry.uncompsize = uncompsize;
158         entry.extlen = extlen;
159         entry.reloff = reloff;
160         entry.setName(nameb);
161         return entry;
162     }
163
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
175         // ---
176         buffer.clear();
177         buffer.limit(LENGTH_END);
178         Channels.newChannel(is).read(buffer);
179         if (buffer.position() == 0)
180             return null;
181         buffer.rewind();
182         signature = buffer.getInt();
183         if (signature != SIGN_END)
184             return null;
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];
193         if (commentlen > 0)
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;
206         return entry;
207     }
208
209     public ZipEntry readEXT(InputStream is) throws IOException {
210         ZipEntry entry = new ZipEntry();
211         readEXT(is, entry);
212         return entry;
213     }
214
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
221         // ---
222         buffer.clear();
223         buffer.limit(LENGTH_EXT);
224         Channels.newChannel(is).read(buffer);
225         if (buffer.position() == 0)
226             return false;
227         buffer.rewind();
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;
235         entry.crc = crc;
236         entry.compsize = compsize;
237         entry.uncompsize = uncompsize;
238         return true;
239     }
240
241     public void write(OutputStream os, ZipEntry entry) throws IOException {
242         writeLOC(os, entry);
243     }
244
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
259         // ---
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;
267         flags = entry.flags;
268         method = entry.method;
269         mtime = entry.mtime;
270         mdate = entry.mdate;
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;
277         buffer.clear();
278         buffer.putInt(signature);
279         buffer.putShort(version);
280         buffer.putShort(flags);
281         buffer.putShort(method);
282         buffer.putShort(mtime);
283         buffer.putShort(mdate);
284         buffer.putInt(crc);
285         buffer.putInt(compsize);
286         buffer.putInt(uncompsize);
287         buffer.putShort(namelen);
288         buffer.putShort(extlen);
289         assert buffer.position() == LENGTH_LOC;
290         buffer.flip();
291         Channels.newChannel(os).write(buffer);
292         os.write(nameb);
293         os.flush();
294         entries.add(entry);
295         assert namelen > 0;
296         assert extlen >= 0;
297         assert compsize >= 0;
298         offset += LENGTH_LOC + namelen + extlen + compsize;
299     }
300
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
321         // ---
322         signature = SIGN_CEN;
323         madever = entry.version;
324         needver = entry.version;
325         flags = entry.flags;
326         method = entry.method;
327         mtime = entry.mtime;
328         mdate = entry.mdate;
329         crc = entry.crc;
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;
342         buffer.clear();
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);
350         buffer.putInt(crc);
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;
361         buffer.flip();
362         Channels.newChannel(os).write(buffer);
363         os.write(nameb);
364         os.flush();
365         offset += LENGTH_CEN + namelen + extlen;
366     }
367
368     public void writeEND(OutputStream os) throws IOException {
369         for (final ZipEntry entry : entries)
370             writeCEN(os, entry);
371         writeEND(os, ZipEndEntry.create(offset, entries));
372     }
373
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
385         // ---
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;
393         commentlen = 0;
394         comment = new byte[0];
395         buffer.clear();
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);
404         buffer.put(comment);
405         assert buffer.position() == LENGTH_END;
406         buffer.flip();
407         Channels.newChannel(os).write(buffer);
408         os.flush();
409         offset += LENGTH_END;
410     }
411
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
418         // ---
419         signature = SIGN_EXT;
420         crc = entry.crc;
421         compsize = entry.compsize;
422         uncompsize = entry.uncompsize;
423         buffer.clear();
424         buffer.putInt(signature);
425         buffer.putInt(crc);
426         buffer.putInt(compsize);
427         buffer.putInt(uncompsize);
428         assert buffer.position() == LENGTH_EXT;
429         buffer.flip();
430         Channels.newChannel(os).write(buffer);
431         os.flush();
432         offset += LENGTH_EXT;
433         assert entry.compsize >= 0;
434         offset += entry.compsize;
435     }
436
437     public void clearEntries() {
438         entries.clear();
439         offset = 0;
440     }
441
442 }