| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- // ZipFile.cs
- // Copyright (C) 2001 Mike Krueger
- //
- // This file was translated from java, it was part of the GNU Classpath
- // Copyright (C) 2001 Free Software Foundation, Inc.
- //
- // This program is free software; you can redistribute it and/or
- // modify it under the terms of the GNU General Public License
- // as published by the Free Software Foundation; either version 2
- // of the License, or (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program; if not, write to the Free Software
- // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- //
- // Linking this library statically or dynamically with other modules is
- // making a combined work based on this library. Thus, the terms and
- // conditions of the GNU General Public License cover the whole
- // combination.
- //
- // As a special exception, the copyright holders of this library give you
- // permission to link this library with independent modules to produce an
- // executable, regardless of the license terms of these independent
- // modules, and to copy and distribute the resulting executable under
- // terms of your choice, provided that you also meet, for each linked
- // independent module, the terms and conditions of the license of that
- // module. An independent module is a module which is not derived from
- // or based on this library. If you modify this library, you may extend
- // this exception to your version of the library, but you are not
- // obligated to do so. If you do not wish to do so, delete this
- // exception statement from your version.
- using System;
- using System.Collections;
- using System.IO;
- using System.Text;
- using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
- using ICSharpCode.SharpZipLib.Zip.Compression;
- namespace ICSharpCode.SharpZipLib.Zip
- {
-
- /// <summary>
- /// This class represents a Zip archive. You can ask for the contained
- /// entries, or get an input stream for a file entry. The entry is
- /// automatically decompressed.
- ///
- /// This class is thread safe: You can open input streams for arbitrary
- /// entries in different threads.
- ///
- /// author of the original java version : Jochen Hoenicke
- /// </summary>
- /// <example>
- /// using System;
- /// using System.Text;
- /// using System.Collections;
- /// using System.IO;
- ///
- /// using NZlib.Zip;
- ///
- /// class MainClass
- /// {
- /// static public void Main(string[] args)
- /// {
- /// ZipFile zFile = new ZipFile(args[0]);
- /// //Console.WriteLine("Listing of : " + zFile.Name);
- /// //Console.WriteLine("");
- /// //Console.WriteLine("Raw Size Size Date Time Name");
- /// //Console.WriteLine("-------- -------- -------- ------ ---------");
- /// foreach (ZipEntry e in zFile) {
- /// DateTime d = e.DateTime;
- /// //Console.WriteLine("{0, -10}{1, -10}{2} {3} {4}", e.Size, e.CompressedSize,
- /// d.ToString("dd-MM-yy"), d.ToString("t"),
- /// e.Name);
- /// }
- /// }
- /// }
- /// </example>
- public class ZipFile : IEnumerable
- {
- string name;
- string comment;
- Stream baseStream;
- ZipEntry[] entries;
-
- /// <summary>
- /// Opens a Zip file with the given name for reading.
- /// </summary>
- /// <exception name="System.IO.IOException">
- /// IOException if a i/o error occured.
- /// </exception>
- /// <exception name="ICSharpCode.SharpZipLib.ZipException">
- /// if the file doesn't contain a valid zip archive.
- /// </exception>
- public ZipFile(string name) : this(File.OpenRead(name))
- {
- }
-
- /// <summary>
- /// Opens a Zip file reading the given FileStream
- /// </summary>
- /// <exception name="System.IO.IOException">
- /// IOException if a i/o error occured.
- /// </exception>
- /// <exception name="ICSharpCode.SharpZipLib.ZipException">
- /// if the file doesn't contain a valid zip archive.
- /// </exception>
- public ZipFile(FileStream file)
- {
- this.baseStream = file;
- this.name = file.Name;
- ReadEntries();
- }
-
- /// <summary>
- /// Opens a Zip file reading the given Stream
- /// </summary>
- /// <exception name="System.IO.IOException">
- /// IOException if a i/o error occured.
- /// </exception>
- /// <exception name="ICSharpCode.SharpZipLib.ZipException">
- /// if the file doesn't contain a valid zip archive.
- /// </exception>
- public ZipFile(Stream baseStream)
- {
- this.baseStream = baseStream;
- this.name = null;
- ReadEntries();
- }
-
-
- /// <summary>
- /// Read an unsigned short in little endian byte order.
- /// </summary>
- /// <exception name="System.IO.IOException">
- /// if a i/o error occured.
- /// </exception>
- /// <exception name="System.IO.EndOfStreamException">
- /// if the file ends prematurely
- /// </exception>
- int ReadLeShort()
- {
- return baseStream.ReadByte() | baseStream.ReadByte() << 8;
- }
-
- /// <summary>
- /// Read an int in little endian byte order.
- /// </summary>
- /// <exception name="System.IO.IOException">
- /// if a i/o error occured.
- /// </exception>
- /// <exception name="System.IO.EndOfStreamException">
- /// if the file ends prematurely
- /// </exception>
- int ReadLeInt()
- {
- return ReadLeShort() | ReadLeShort() << 16;
- }
-
- /// <summary>
- /// Read the central directory of a zip file and fill the entries
- /// array. This is called exactly once by the constructors.
- /// </summary>
- /// <exception name="System.IO.IOException">
- /// if a i/o error occured.
- /// </exception>
- /// <exception name="ICSharpCode.SharpZipLib.ZipException">
- /// if the central directory is malformed
- /// </exception>
- void ReadEntries()
- {
- /* Search for the End Of Central Directory. When a zip comment is
- * present the directory may start earlier.
- * FIXME: This searches the whole file in a very slow manner if the
- * file isn't a zip file.
- */
- long pos = baseStream.Length - ZipConstants.ENDHDR;
- do {
- if (pos < 0) {
- throw new ZipException("central directory not found, probably not a zip file");
- }
- baseStream.Seek(pos--, SeekOrigin.Begin);
- } while (ReadLeInt() != ZipConstants.ENDSIG);
-
- long oldPos = baseStream.Position;
- baseStream.Position += ZipConstants.ENDTOT - ZipConstants.ENDNRD;
-
- if (baseStream.Position - oldPos != ZipConstants.ENDTOT - ZipConstants.ENDNRD) {
- throw new EndOfStreamException();
- }
- int count = ReadLeShort();
-
- oldPos = baseStream.Position;
- baseStream.Position += ZipConstants.ENDOFF - ZipConstants.ENDSIZ;
-
- if (baseStream.Position - oldPos != ZipConstants.ENDOFF - ZipConstants.ENDSIZ) {
- throw new EndOfStreamException();
- }
-
- int centralOffset = ReadLeInt();
-
- // GET COMMENT SIZE (COMES AFTER CENTRALOFFSET)
- int commentSize = ReadLeShort();
- byte[] zipComment = new byte[commentSize];
- baseStream.Read(zipComment, 0, zipComment.Length);
- comment = ZipConstants.ConvertToString(zipComment);
-
- entries = new ZipEntry[count];
- baseStream.Seek(centralOffset, SeekOrigin.Begin);
- for (int i = 0; i < count; i++) {
- if (ReadLeInt() != ZipConstants.CENSIG) {
- throw new ZipException("Wrong Central Directory signature");
- }
-
- oldPos = baseStream.Position;
- baseStream.Position += ZipConstants.CENHOW - ZipConstants.CENVEM;
-
- if (baseStream.Position - oldPos != ZipConstants.CENHOW - ZipConstants.CENVEM) {
- throw new EndOfStreamException();
- }
- int method = ReadLeShort();
- int dostime = ReadLeInt();
- int crc = ReadLeInt();
- int csize = ReadLeInt();
- int size = ReadLeInt();
- int nameLen = ReadLeShort();
- int extraLen = ReadLeShort();
- int commentLen = ReadLeShort();
-
- oldPos = baseStream.Position;
- baseStream.Position += ZipConstants.CENOFF - ZipConstants.CENDSK;
- if (baseStream.Position - oldPos != ZipConstants.CENOFF - ZipConstants.CENDSK) {
- throw new EndOfStreamException();
- }
- int offset = ReadLeInt();
-
- byte[] buffer = new byte[Math.Max(nameLen, commentLen)];
-
- baseStream.Read(buffer, 0, nameLen);
- string name = ZipConstants.ConvertToString(buffer);
-
- ZipEntry entry = new ZipEntry(name);
- entry.CompressionMethod = (CompressionMethod)method;
- entry.Crc = crc & 0xffffffffL;
- entry.Size = size & 0xffffffffL;
- entry.CompressedSize = csize & 0xffffffffL;
- entry.DosTime = (uint)dostime;
- if (extraLen > 0) {
- byte[] extra = new byte[extraLen];
- baseStream.Read(extra, 0, extraLen);
- entry.ExtraData = extra;
- }
- if (commentLen > 0) {
- baseStream.Read(buffer, 0, commentLen);
- entry.Comment = ZipConstants.ConvertToString(buffer);
- }
- entry.ZipFileIndex = i;
- entry.Offset = offset;
- entries[i] = entry;
- }
- }
-
- /// <summary>
- /// Closes the ZipFile. This also closes all input streams given by
- /// this class. After this is called, no further method should be
- /// called.
- /// </summary>
- /// <exception name="System.IO.IOException">
- /// if a i/o error occured.
- /// </exception>
- public void Close()
- {
- entries = null;
- lock(baseStream) {
- baseStream.Close();
- }
- }
-
- /// <summary>
- /// Returns an IEnumerator of all Zip entries in this Zip file.
- /// </summary>
- public IEnumerator GetEnumerator()
- {
- if (entries == null) {
- throw new InvalidOperationException("ZipFile has closed");
- }
-
- return new ZipEntryEnumeration(entries);
- }
-
- int GetEntryIndex(string name)
- {
- for (int i = 0; i < entries.Length; i++) {
- if (name.Equals(entries[i].Name)) {
- return i;
- }
- }
- return -1; // ok
- }
-
- /// <summary>
- /// Searches for a zip entry in this archive with the given name.
- /// </summary>
- /// <param name="name">
- /// the name. May contain directory components separated by slashes ('/').
- /// </param>
- /// <returns>
- /// the zip entry, or null if no entry with that name exists.
- /// </returns>
- public ZipEntry GetEntry(string name)
- {
- if (entries == null) {
- throw new InvalidOperationException("ZipFile has closed");
- }
- int index = GetEntryIndex(name);
- return index >= 0 ? (ZipEntry) entries[index].Clone() : null;
- }
-
- /// <summary>
- /// Checks, if the local header of the entry at index i matches the
- /// central directory, and returns the offset to the data.
- /// </summary>
- /// <returns>
- /// the start offset of the (compressed) data.
- /// </returns>
- /// <exception name="System.IO.IOException">
- /// if a i/o error occured.
- /// </exception>
- /// <exception name="ICSharpCode.SharpZipLib.ZipException">
- /// if the local header doesn't match the central directory header
- /// </exception>
- long CheckLocalHeader(ZipEntry entry)
- {
- lock(baseStream) {
- baseStream.Seek(entry.Offset, SeekOrigin.Begin);
- if (ReadLeInt() != ZipConstants.LOCSIG) {
- throw new ZipException("Wrong Local header signature");
- }
-
- /* skip version and flags */
- long oldPos = baseStream.Position;
- baseStream.Position += ZipConstants.LOCHOW - ZipConstants.LOCVER;
- if (baseStream.Position - oldPos != ZipConstants.LOCHOW - ZipConstants.LOCVER) {
- throw new EndOfStreamException();
- }
-
- if (entry.CompressionMethod != (CompressionMethod)ReadLeShort()) {
- throw new ZipException("Compression method mismatch");
- }
-
- /* Skip time, crc, size and csize */
- oldPos = baseStream.Position;
- baseStream.Position += ZipConstants.LOCNAM - ZipConstants.LOCTIM;
-
- if (baseStream.Position - oldPos != ZipConstants.LOCNAM - ZipConstants.LOCTIM) {
- throw new EndOfStreamException();
- }
-
- if (entry.Name.Length != ReadLeShort()) {
- throw new ZipException("file name length mismatch");
- }
-
- int extraLen = entry.Name.Length + ReadLeShort();
- return entry.Offset + ZipConstants.LOCHDR + extraLen;
- }
- }
-
- /// <summary>
- /// Creates an input stream reading the given zip entry as
- /// uncompressed data. Normally zip entry should be an entry
- /// returned by GetEntry().
- /// </summary>
- /// <returns>
- /// the input stream.
- /// </returns>
- /// <exception name="System.IO.IOException">
- /// if a i/o error occured.
- /// </exception>
- /// <exception name="ICSharpCode.SharpZipLib.ZipException">
- /// if the Zip archive is malformed.
- /// </exception>
- public Stream GetInputStream(ZipEntry entry)
- {
- if (entries == null) {
- throw new InvalidOperationException("ZipFile has closed");
- }
-
- int index = entry.ZipFileIndex;
- if (index < 0 || index >= entries.Length || entries[index].Name != entry.Name) {
- index = GetEntryIndex(entry.Name);
- if (index < 0) {
- throw new IndexOutOfRangeException();
- }
- }
-
- long start = CheckLocalHeader(entries[index]);
- CompressionMethod method = entries[index].CompressionMethod;
- Stream istr = new PartialInputStream(baseStream, start, entries[index].CompressedSize);
- switch (method) {
- case CompressionMethod.Stored:
- return istr;
- case CompressionMethod.Deflated:
- return new InflaterInputStream(istr, new Inflater(true));
- default:
- throw new ZipException("Unknown compression method " + method);
- }
- }
-
- /// <summary>
- /// The comment for the whole zip file.
- /// </summary>
- public string ZipFileComment {
- get {
- return comment;
- }
- }
-
- /// <summary>
- /// Returns the name of this zip file.
- /// </summary>
- public string Name {
- get {
- return name;
- }
- }
-
- /// <summary>
- /// Returns the number of entries in this zip file.
- /// </summary>
- public int Size {
- get {
- try {
- return entries.Length;
- } catch (Exception) {
- throw new InvalidOperationException("ZipFile has closed");
- }
- }
- }
-
- class ZipEntryEnumeration : IEnumerator
- {
- ZipEntry[] array;
- int ptr = -1;
-
- public ZipEntryEnumeration(ZipEntry[] arr)
- {
- array = arr;
- }
-
- public object Current {
- get {
- return array[ptr];
- }
- }
-
- public void Reset()
- {
- ptr = -1;
- }
-
- public bool MoveNext()
- {
- return (++ptr < array.Length);
- }
- }
-
- class PartialInputStream : InflaterInputStream
- {
- Stream baseStream;
- long filepos, end;
-
- public PartialInputStream(Stream baseStream, long start, long len) : base(baseStream)
- {
- this.baseStream = baseStream;
- filepos = start;
- end = start + len;
- }
-
- public override int Available
- {
- get {
- long amount = end - filepos;
- if (amount > Int32.MaxValue) {
- return Int32.MaxValue;
- }
-
- return (int) amount;
- }
- }
-
- public override int ReadByte()
- {
- if (filepos == end) {
- return -1; //ok
- }
-
- lock(baseStream) {
- baseStream.Seek(filepos++, SeekOrigin.Begin);
- return baseStream.ReadByte();
- }
- }
-
- public override int Read(byte[] b, int off, int len)
- {
- if (len > end - filepos) {
- len = (int) (end - filepos);
- if (len == 0) {
- return 0;
- }
- }
- lock(baseStream) {
- baseStream.Seek(filepos, SeekOrigin.Begin);
- int count = baseStream.Read(b, off, len);
- if (count > 0) {
- filepos += len;
- }
- return count;
- }
- }
-
- public long SkipBytes(long amount)
- {
- if (amount < 0) {
- throw new ArgumentOutOfRangeException();
- }
- if (amount > end - filepos) {
- amount = end - filepos;
- }
- filepos += amount;
- return amount;
- }
- }
- }
- }
|