123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 |
- // ZipHelperStream.cs
- //
- // Copyright 2006, 2007 John Reilly
- //
- // 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.IO;
- using System.Text;
- namespace CommonMPQ.SharpZipLib.Zip
- {
- /// <summary>
- /// Holds data pertinent to a data descriptor.
- /// </summary>
- public class DescriptorData
- {
- /// <summary>
- /// Get /set the compressed size of data.
- /// </summary>
- public long CompressedSize
- {
- get { return compressedSize; }
- set { compressedSize = value; }
- }
- /// <summary>
- /// Get / set the uncompressed size of data
- /// </summary>
- public long Size
- {
- get { return size; }
- set { size = value; }
- }
- /// <summary>
- /// Get /set the crc value.
- /// </summary>
- public long Crc
- {
- get { return crc; }
- set { crc = (value & 0xffffffff); }
- }
- #region Instance Fields
- long size;
- long compressedSize;
- long crc;
- #endregion
- }
- class EntryPatchData
- {
- public long SizePatchOffset
- {
- get { return sizePatchOffset_; }
- set { sizePatchOffset_ = value; }
- }
- public long CrcPatchOffset
- {
- get { return crcPatchOffset_; }
- set { crcPatchOffset_ = value; }
- }
-
- #region Instance Fields
- long sizePatchOffset_;
- long crcPatchOffset_;
- #endregion
- }
-
- /// <summary>
- /// This class assists with writing/reading from Zip files.
- /// </summary>
- internal class ZipHelperStream : Stream
- {
- #region Constructors
- /// <summary>
- /// Initialise an instance of this class.
- /// </summary>
- /// <param name="name">The name of the file to open.</param>
- public ZipHelperStream(string name)
- {
- stream_ = new FileStream(name, FileMode.Open, FileAccess.ReadWrite);
- isOwner_ = true;
- }
- /// <summary>
- /// Initialise a new instance of <see cref="ZipHelperStream"/>.
- /// </summary>
- /// <param name="stream">The stream to use.</param>
- public ZipHelperStream(Stream stream)
- {
- stream_ = stream;
- }
- #endregion
- /// <summary>
- /// Get / set a value indicating wether the the underlying stream is owned or not.
- /// </summary>
- /// <remarks>If the stream is owned it is closed when this instance is closed.</remarks>
- public bool IsStreamOwner
- {
- get { return isOwner_; }
- set { isOwner_ = value; }
- }
- #region Base Stream Methods
- public override bool CanRead
- {
- get { return stream_.CanRead; }
- }
- public override bool CanSeek
- {
- get { return stream_.CanSeek; }
- }
- #if !NET_1_0 && !NET_1_1 && !NETCF_1_0
- public override bool CanTimeout
- {
- get { return stream_.CanTimeout; }
- }
- #endif
- public override long Length
- {
- get { return stream_.Length; }
- }
- public override long Position
- {
- get { return stream_.Position; }
- set { stream_.Position = value; }
- }
- public override bool CanWrite
- {
- get { return stream_.CanWrite; }
- }
- public override void Flush()
- {
- stream_.Flush();
- }
- public override long Seek(long offset, SeekOrigin origin)
- {
- return stream_.Seek(offset, origin);
- }
- public override void SetLength(long value)
- {
- stream_.SetLength(value);
- }
- public override int Read(byte[] buffer, int offset, int count)
- {
- return stream_.Read(buffer, offset, count);
- }
- public override void Write(byte[] buffer, int offset, int count)
- {
- stream_.Write(buffer, offset, count);
- }
- /// <summary>
- /// Close the stream.
- /// </summary>
- /// <remarks>
- /// The underlying stream is closed only if <see cref="IsStreamOwner"/> is true.
- /// </remarks>
- override public void Close()
- {
- Stream toClose = stream_;
- stream_ = null;
- if (isOwner_ && (toClose != null))
- {
- isOwner_ = false;
- toClose.Close();
- }
- }
- #endregion
-
- // Write the local file header
- // TODO: ZipHelperStream.WriteLocalHeader is not yet used and needs checking for ZipFile and ZipOuptutStream usage
- void WriteLocalHeader(ZipEntry entry, EntryPatchData patchData)
- {
- CompressionMethod method = entry.CompressionMethod;
- bool headerInfoAvailable = true; // How to get this?
- bool patchEntryHeader = false;
- WriteLEInt(ZipConstants.LocalHeaderSignature);
-
- WriteLEShort(entry.Version);
- WriteLEShort(entry.Flags);
- WriteLEShort((byte)method);
- WriteLEInt((int)entry.DosTime);
- if (headerInfoAvailable == true) {
- WriteLEInt((int)entry.Crc);
- if ( entry.LocalHeaderRequiresZip64 ) {
- WriteLEInt(-1);
- WriteLEInt(-1);
- }
- else {
- WriteLEInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CryptoHeaderSize : (int)entry.CompressedSize);
- WriteLEInt((int)entry.Size);
- }
- } else {
- if (patchData != null) {
- patchData.CrcPatchOffset = stream_.Position;
- }
- WriteLEInt(0); // Crc
-
- if ( patchData != null ) {
- patchData.SizePatchOffset = stream_.Position;
- }
- // For local header both sizes appear in Zip64 Extended Information
- if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
- WriteLEInt(-1);
- WriteLEInt(-1);
- }
- else {
- WriteLEInt(0); // Compressed size
- WriteLEInt(0); // Uncompressed size
- }
- }
- byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);
-
- if (name.Length > 0xFFFF) {
- throw new ZipException("Entry name too long.");
- }
- ZipExtraData ed = new ZipExtraData(entry.ExtraData);
- if (entry.LocalHeaderRequiresZip64 && (headerInfoAvailable || patchEntryHeader)) {
- ed.StartNewEntry();
- if (headerInfoAvailable) {
- ed.AddLeLong(entry.Size);
- ed.AddLeLong(entry.CompressedSize);
- }
- else {
- ed.AddLeLong(-1);
- ed.AddLeLong(-1);
- }
- ed.AddNewEntry(1);
- if ( !ed.Find(1) ) {
- throw new ZipException("Internal error cant find extra data");
- }
-
- if ( patchData != null ) {
- patchData.SizePatchOffset = ed.CurrentReadIndex;
- }
- }
- else {
- ed.Delete(1);
- }
-
- byte[] extra = ed.GetEntryData();
- WriteLEShort(name.Length);
- WriteLEShort(extra.Length);
- if ( name.Length > 0 ) {
- stream_.Write(name, 0, name.Length);
- }
-
- if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
- patchData.SizePatchOffset += stream_.Position;
- }
- if ( extra.Length > 0 ) {
- stream_.Write(extra, 0, extra.Length);
- }
- }
-
- /// <summary>
- /// Locates a block with the desired <paramref name="signature"/>.
- /// </summary>
- /// <param name="signature">The signature to find.</param>
- /// <param name="endLocation">Location, marking the end of block.</param>
- /// <param name="minimumBlockSize">Minimum size of the block.</param>
- /// <param name="maximumVariableData">The maximum variable data.</param>
- /// <returns>Eeturns the offset of the first byte after the signature; -1 if not found</returns>
- public long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData)
- {
- long pos = endLocation - minimumBlockSize;
- if ( pos < 0 ) {
- return -1;
- }
- long giveUpMarker = Math.Max(pos - maximumVariableData, 0);
- // TODO: This loop could be optimised for speed.
- do {
- if ( pos < giveUpMarker ) {
- return -1;
- }
- Seek(pos--, SeekOrigin.Begin);
- } while ( ReadLEInt() != signature );
- return Position;
- }
- /// <summary>
- /// Write Zip64 end of central directory records (File header and locator).
- /// </summary>
- /// <param name="noOfEntries">The number of entries in the central directory.</param>
- /// <param name="sizeEntries">The size of entries in the central directory.</param>
- /// <param name="centralDirOffset">The offset of the dentral directory.</param>
- public void WriteZip64EndOfCentralDirectory(long noOfEntries, long sizeEntries, long centralDirOffset)
- {
- long centralSignatureOffset = stream_.Position;
- WriteLEInt(ZipConstants.Zip64CentralFileHeaderSignature);
- WriteLELong(44); // Size of this record (total size of remaining fields in header or full size - 12)
- WriteLEShort(ZipConstants.VersionMadeBy); // Version made by
- WriteLEShort(ZipConstants.VersionZip64); // Version to extract
- WriteLEInt(0); // Number of this disk
- WriteLEInt(0); // number of the disk with the start of the central directory
- WriteLELong(noOfEntries); // No of entries on this disk
- WriteLELong(noOfEntries); // Total No of entries in central directory
- WriteLELong(sizeEntries); // Size of the central directory
- WriteLELong(centralDirOffset); // offset of start of central directory
- // zip64 extensible data sector not catered for here (variable size)
- // Write the Zip64 end of central directory locator
- WriteLEInt(ZipConstants.Zip64CentralDirLocatorSignature);
- // no of the disk with the start of the zip64 end of central directory
- WriteLEInt(0);
- // relative offset of the zip64 end of central directory record
- WriteLELong(centralSignatureOffset);
- // total number of disks
- WriteLEInt(1);
- }
- /// <summary>
- /// Write the required records to end the central directory.
- /// </summary>
- /// <param name="noOfEntries">The number of entries in the directory.</param>
- /// <param name="sizeEntries">The size of the entries in the directory.</param>
- /// <param name="startOfCentralDirectory">The start of the central directory.</param>
- /// <param name="comment">The archive comment. (This can be null).</param>
- public void WriteEndOfCentralDirectory(long noOfEntries, long sizeEntries,
- long startOfCentralDirectory, byte[] comment)
- {
- if ( (noOfEntries >= 0xffff) ||
- (startOfCentralDirectory >= 0xffffffff) ||
- (sizeEntries >= 0xffffffff) ) {
- WriteZip64EndOfCentralDirectory(noOfEntries, sizeEntries, startOfCentralDirectory);
- }
- WriteLEInt(ZipConstants.EndOfCentralDirectorySignature);
- // TODO: ZipFile Multi disk handling not done
- WriteLEShort(0); // number of this disk
- WriteLEShort(0); // no of disk with start of central dir
-
- // Number of entries
- if ( noOfEntries >= 0xffff ) {
- WriteLEUshort(0xffff); // Zip64 marker
- WriteLEUshort(0xffff);
- }
- else {
- WriteLEShort(( short )noOfEntries); // entries in central dir for this disk
- WriteLEShort(( short )noOfEntries); // total entries in central directory
- }
- // Size of the central directory
- if ( sizeEntries >= 0xffffffff ) {
- WriteLEUint(0xffffffff); // Zip64 marker
- }
- else {
- WriteLEInt(( int )sizeEntries);
- }
- // offset of start of central directory
- if ( startOfCentralDirectory >= 0xffffffff ) {
- WriteLEUint(0xffffffff); // Zip64 marker
- }
- else {
- WriteLEInt(( int )startOfCentralDirectory);
- }
- int commentLength = (comment != null) ? comment.Length : 0;
- if ( commentLength > 0xffff ) {
- throw new ZipException(string.Format("Comment length({0}) is too long can only be 64K", commentLength));
- }
- WriteLEShort(commentLength);
- if ( commentLength > 0 ) {
- Write(comment, 0, comment.Length);
- }
- }
- #region LE value reading/writing
- /// <summary>
- /// Read an unsigned short in little endian byte order.
- /// </summary>
- /// <returns>Returns the value read.</returns>
- /// <exception cref="IOException">
- /// An i/o error occurs.
- /// </exception>
- /// <exception cref="EndOfStreamException">
- /// The file ends prematurely
- /// </exception>
- public int ReadLEShort()
- {
- int byteValue1 = stream_.ReadByte();
- if (byteValue1 < 0) {
- throw new EndOfStreamException();
- }
- int byteValue2 = stream_.ReadByte();
- if (byteValue2 < 0) {
- throw new EndOfStreamException();
- }
- return byteValue1 | (byteValue2 << 8);
- }
- /// <summary>
- /// Read an int in little endian byte order.
- /// </summary>
- /// <returns>Returns the value read.</returns>
- /// <exception cref="IOException">
- /// An i/o error occurs.
- /// </exception>
- /// <exception cref="System.IO.EndOfStreamException">
- /// The file ends prematurely
- /// </exception>
- public int ReadLEInt()
- {
- return ReadLEShort() | (ReadLEShort() << 16);
- }
- /// <summary>
- /// Read a long in little endian byte order.
- /// </summary>
- /// <returns>The value read.</returns>
- public long ReadLELong()
- {
- return (uint)ReadLEInt() | ((long)ReadLEInt() << 32);
- }
- /// <summary>
- /// Write an unsigned short in little endian byte order.
- /// </summary>
- /// <param name="value">The value to write.</param>
- public void WriteLEShort(int value)
- {
- stream_.WriteByte(( byte )(value & 0xff));
- stream_.WriteByte(( byte )((value >> 8) & 0xff));
- }
- /// <summary>
- /// Write a ushort in little endian byte order.
- /// </summary>
- /// <param name="value">The value to write.</param>
- public void WriteLEUshort(ushort value)
- {
- stream_.WriteByte(( byte )(value & 0xff));
- stream_.WriteByte(( byte )(value >> 8));
- }
- /// <summary>
- /// Write an int in little endian byte order.
- /// </summary>
- /// <param name="value">The value to write.</param>
- public void WriteLEInt(int value)
- {
- WriteLEShort(value);
- WriteLEShort(value >> 16);
- }
- /// <summary>
- /// Write a uint in little endian byte order.
- /// </summary>
- /// <param name="value">The value to write.</param>
- public void WriteLEUint(uint value)
- {
- WriteLEUshort(( ushort )(value & 0xffff));
- WriteLEUshort(( ushort )(value >> 16));
- }
- /// <summary>
- /// Write a long in little endian byte order.
- /// </summary>
- /// <param name="value">The value to write.</param>
- public void WriteLELong(long value)
- {
- WriteLEInt(( int )value);
- WriteLEInt(( int )(value >> 32));
- }
- /// <summary>
- /// Write a ulong in little endian byte order.
- /// </summary>
- /// <param name="value">The value to write.</param>
- public void WriteLEUlong(ulong value)
- {
- WriteLEUint(( uint )(value & 0xffffffff));
- WriteLEUint(( uint )(value >> 32));
- }
- #endregion
- /// <summary>
- /// Write a data descriptor.
- /// </summary>
- /// <param name="entry">The entry to write a descriptor for.</param>
- /// <returns>Returns the number of descriptor bytes written.</returns>
- public int WriteDataDescriptor(ZipEntry entry)
- {
- if (entry == null) {
- throw new ArgumentNullException("entry");
- }
- int result=0;
- // Add data descriptor if flagged as required
- if ((entry.Flags & (int)GeneralBitFlags.Descriptor) != 0)
- {
- // The signature is not PKZIP originally but is now described as optional
- // in the PKZIP Appnote documenting trhe format.
- WriteLEInt(ZipConstants.DataDescriptorSignature);
- WriteLEInt(unchecked((int)(entry.Crc)));
- result+=8;
- if (entry.LocalHeaderRequiresZip64)
- {
- WriteLELong(entry.CompressedSize);
- WriteLELong(entry.Size);
- result+=16;
- }
- else
- {
- WriteLEInt((int)entry.CompressedSize);
- WriteLEInt((int)entry.Size);
- result+=8;
- }
- }
- return result;
- }
- /// <summary>
- /// Read data descriptor at the end of compressed data.
- /// </summary>
- /// <param name="zip64">if set to <c>true</c> [zip64].</param>
- /// <param name="data">The data to fill in.</param>
- /// <returns>Returns the number of bytes read in the descriptor.</returns>
- public void ReadDataDescriptor(bool zip64, DescriptorData data)
- {
- int intValue = ReadLEInt();
- // In theory this may not be a descriptor according to PKZIP appnote.
- // In practise its always there.
- if (intValue != ZipConstants.DataDescriptorSignature) {
- throw new ZipException("Data descriptor signature not found");
- }
- data.Crc = ReadLEInt();
-
- if (zip64) {
- data.CompressedSize = ReadLELong();
- data.Size = ReadLELong();
- }
- else {
- data.CompressedSize = ReadLEInt();
- data.Size = ReadLEInt();
- }
- }
- #region Instance Fields
- bool isOwner_;
- Stream stream_;
- #endregion
- }
- }
|