| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 | using System;using System.IO;using System.Text;namespace ICSharpCode.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; }		}		public override bool CanTimeout {			get { return stream_.CanTimeout; }		}		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>		protected override void Dispose(bool disposing)		{			Stream toClose = stream_;			stream_ = null;			if (isOwner_ && (toClose != null)) {				isOwner_ = false;				toClose.Dispose();			}		}		#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.");			}			var 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("nameof(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	}}
 |