PageRenderTime 201ms CodeModel.GetById 120ms app.highlight 38ms RepoModel.GetById 33ms app.codeStats 0ms

/Utilities/Compression/ZipEntry.cs

#
C# | 984 lines | 568 code | 66 blank | 350 comment | 75 complexity | 1ee0479e05a2d4b1c9554cae8d8731a6 MD5 | raw file
  1// Based on Mike Krueger's SharpZipLib, Copyright (C) 2001 (GNU license).
  2// Authors of the original java version: Jochen Hoenicke, John Leuner
  3// See http://www.ISeeSharpCode.com for more information.
  4
  5using System;
  6using System.IO;
  7using Delta.Utilities.Helpers;
  8
  9namespace Delta.Utilities.Compression
 10{
 11	/// <summary>
 12	/// This class represents an entry in a zip archive. This can be a file
 13	/// or a directory ZipFile and ZipInputStream will give you instances of
 14	/// this class as information about the members in an archive.
 15	/// ZipOutputStream uses an instance of this class when creating an entry
 16	/// in a Zip file.
 17	/// <br/>Author of the original java version : Jochen Hoenicke
 18	/// </summary>
 19	public class ZipEntry //: ICloneable
 20	{
 21		#region Constants
 22		private const int KnownSize = 1;
 23
 24		private const int KnownCSize = 2;
 25
 26		private const int KnownCrc = 4;
 27
 28		private const int KnownTime = 8;
 29
 30		private const int KnownExternAttributes = 16;
 31		#endregion
 32
 33		#region CleanName (Static)
 34		/// <summary>
 35		/// Cleans a name making it conform to Zip file conventions.
 36		/// Devices names ('c:\') and UNC share names ('\\server\share') are
 37		/// removed and forward slashes ('\') are converted to back slashes ('/').
 38		/// </summary>
 39		/// <param name="name">Name to clean</param>
 40		/// <param name="relativePath">Make names relative if true or absolute if
 41		/// false</param>
 42		public static string CleanName(string name, bool relativePath)
 43		{
 44			if (name == null)
 45			{
 46				return "";
 47			}
 48
 49			if (Path.IsPathRooted(name))
 50			{
 51				// NOTE:
 52				// for UNC names...  \\machine\share\zoom\beet.txt gives \zoom\beet.txt
 53				name = name.Substring(Path.GetPathRoot(name).Length);
 54			}
 55
 56			name = name.Replace(@"\", "/");
 57
 58			if (relativePath)
 59			{
 60				if (name.Length > 0 &&
 61				    (name[0] == Path.AltDirectorySeparatorChar ||
 62				     name[0] == Path.DirectorySeparatorChar))
 63				{
 64					name = name.Remove(0, 1);
 65				}
 66			}
 67			else
 68			{
 69				if (name.Length > 0 &&
 70				    name[0] != Path.AltDirectorySeparatorChar &&
 71				    name[0] != Path.DirectorySeparatorChar)
 72				{
 73					name = name.Insert(0, "/");
 74				}
 75			}
 76			return name;
 77		}
 78
 79		/// <summary>
 80		/// Cleans a name making it conform to Zip file conventions.
 81		/// Devices names ('c:\') and UNC share names ('\\server\share') are
 82		/// removed and forward slashes ('\') are converted to back slashes ('/').
 83		/// Names are made relative by trimming leading slashes which is
 84		/// compatible with Windows-XPs built in Zip file handling.
 85		/// </summary>
 86		/// <param name="name">Name to clean</param>
 87		public static string CleanName(string name)
 88		{
 89			return CleanName(name, true);
 90		}
 91		#endregion
 92
 93		#region IsCrypted (Public)
 94		/// <summary>
 95		/// Get/Set flag indicating if entry is encrypted.
 96		/// A simple helper routine to aid interpretation of
 97		/// <see cref="Flags">flags</see>
 98		/// </summary>
 99		public bool IsCrypted
100		{
101			get
102			{
103				return (flags & 1) != 0;
104			} // get
105			set
106			{
107				if (value)
108				{
109					flags |= 1;
110				} // if
111				else
112				{
113					flags &= ~1;
114				} // else
115			} // set
116		}
117		#endregion
118
119		#region Flags (Public)
120		/// <summary>
121		/// Get/Set general purpose bit flag for entry
122		/// </summary>
123		/// <remarks>
124		/// General purpose bit flag<br/>
125		/// Bit 0: If set, indicates the file is encrypted<br/>
126		/// Bit 1-2 Only used for compression type 6 Imploding, and 8, 9
127		/// deflating<br/>
128		/// Imploding:<br/>
129		/// Bit 1 if set indicates an 8K sliding dictionary was used.
130		/// If clear a 4k dictionary was used<br/>
131		/// Bit 2 if set indicates 3 Shannon-Fanno trees were used to encode the
132		/// sliding dictionary, 2 otherwise<br/>
133		/// <br/>
134		/// Deflating:<br/>
135		///   Bit 2    Bit 1<br/>
136		///     0        0       Normal compression was used<br/>
137		///     0        1       Maximum compression was used<br/>
138		///     1        0       Fast compression was used<br/>
139		///     1        1       Super fast compression was used<br/>
140		/// <br/>
141		/// Bit 3: If set, the fields crc-32, compressed size
142		/// and uncompressed size are were not able to be written during zip file
143		/// creation. The correct values are held in a data descriptor immediately
144		/// following the compressed data. <br/>
145		/// Bit 4: Reserved for use by PKZIP for enhanced deflating<br/>
146		/// Bit 5: If set indicates the file contains compressed patch data<br/>
147		/// Bit 6: If set indicates strong encryption was used.<br/>
148		/// Bit 7-15: Unused or reserved<br/>
149		/// </remarks>
150		public int Flags
151		{
152			get
153			{
154				return flags;
155			} // get
156			set
157			{
158				flags = value;
159			} // set
160		}
161		#endregion
162
163		#region ZipFileIndex (Public)
164		/// <summary>
165		/// Get/Set index of this entry in Zip file
166		/// </summary>
167		public int ZipFileIndex
168		{
169			get
170			{
171				return zipFileIndex;
172			} // get
173			set
174			{
175				zipFileIndex = value;
176			} // set
177		}
178		#endregion
179
180		#region Offset (Public)
181		/// <summary>
182		/// Get/set offset for use in central header
183		/// </summary>
184		public int Offset
185		{
186			get
187			{
188				return offset;
189			}
190			set
191			{
192				if (((ulong)value & 0xFFFFFFFF00000000L) != 0)
193				{
194					throw new ArgumentOutOfRangeException("Offset");
195				} // if
196				offset = value;
197			} // set
198		}
199		#endregion
200
201		#region ExternalFileAttributes (Public)
202		/// <summary>
203		/// Get/Set external file attributes as an integer.
204		/// The values of this are operating system dependant see
205		/// <see cref="HostSystemId">HostSystem</see> for details
206		/// </summary>
207		public int ExternalFileAttributes
208		{
209			get
210			{
211				if ((known & KnownExternAttributes) == 0)
212				{
213					return MathHelper.InvalidIndex;
214				} // if
215				else
216				{
217					return externalFileAttributes;
218				} // else
219			} // get
220			set
221			{
222				externalFileAttributes = value;
223				known |= KnownExternAttributes;
224			} // set
225		}
226		#endregion
227
228		#region VersionMadeBy (Public)
229		/// <summary>
230		/// Get the version made by for this entry or zero if unknown.
231		/// The value / 10 indicates the major version number, and 
232		/// the value mod 10 is the minor version number
233		/// </summary>
234		public int VersionMadeBy
235		{
236			get
237			{
238				return versionMadeBy & 0xff;
239			} // get
240		}
241		#endregion
242
243		#region HostSystemId (Public)
244		/// <summary>
245		/// Gets the compatability information for the
246		/// <see cref="ExternalFileAttributes">external file attribute</see>
247		/// If the external file attributes are compatible with MS-DOS and can be
248		/// read by PKZIP for DOS version 2.04g then this value will be zero.
249		/// Otherwise the value will be non-zero and identify the host system on
250		/// which the attributes are compatible.
251		/// </summary>
252		/// <remarks>
253		/// The values for this as defined in the Zip File format and by others
254		/// are shown below. The values are somewhat misleading in some cases as
255		/// they are not all used as shown. You should consult the relevant
256		/// documentation to obtain up to date and correct information. The
257		/// modified appnote by the infozip group is particularly helpful as it
258		/// documents a lot of peculiarities. The document is however a little
259		/// dated.
260		/// <list type="table">
261		/// <item>0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)</item>
262		/// <item>1 - Amiga</item>
263		/// <item>2 - OpenVMS</item>
264		/// <item>3 - Unix</item>
265		/// <item>4 - VM/CMS</item>
266		/// <item>5 - Atari ST</item>
267		/// <item>6 - OS/2 HPFS</item>
268		/// <item>7 - Macintosh</item>
269		/// <item>8 - Z-System</item>
270		/// <item>9 - CP/M</item>
271		/// <item>10 - Windows NTFS</item>
272		/// <item>11 - MVS (OS/390 - Z/OS)</item>
273		/// <item>12 - VSE</item>
274		/// <item>13 - Acorn Risc</item>
275		/// <item>14 - VFAT</item>
276		/// <item>15 - Alternate MVS</item>
277		/// <item>16 - BeOS</item>
278		/// <item>17 - Tandem</item>
279		/// <item>18 - OS/400</item>
280		/// <item>19 - OS/X (Darwin)</item>
281		/// <item>99 - WinZip AES</item>
282		/// <item>remainder - unused</item>
283		/// </list>
284		/// </remarks>
285		public int HostSystemId
286		{
287			get
288			{
289				return (versionMadeBy >> 8) & 0xff;
290			} // get
291		}
292		#endregion
293
294		#region Version (Public)
295		/// <summary>
296		/// Get minimum Zip feature version required to extract this
297		/// entry
298		/// </summary>		
299		/// <remarks>
300		/// Minimum features are defined as:<br/>
301		/// 1.0 - Default value<br/>
302		/// 1.1 - File is a volume label<br/>
303		/// 2.0 - File is a folder/directory<br/>
304		/// 2.0 - File is compressed using Deflate compression<br/>
305		/// 2.0 - File is encrypted using traditional encryption<br/>
306		/// 2.1 - File is compressed using Deflate64<br/>
307		/// 2.5 - File is compressed using PKWARE DCL Implode<br/>
308		/// 2.7 - File is a patch data set<br/>
309		/// 4.5 - File uses Zip64 format extensions<br/>
310		/// 4.6 - File is compressed using BZIP2 compression<br/>
311		/// 5.0 - File is encrypted using DES<br/>
312		/// 5.0 - File is encrypted using 3DES<br/>
313		/// 5.0 - File is encrypted using original RC2 encryption<br/>
314		/// 5.0 - File is encrypted using RC4 encryption<br/>
315		/// 5.1 - File is encrypted using AES encryption<br/>
316		/// 5.1 - File is encrypted using corrected RC2 encryption<br/>
317		/// 5.1 - File is encrypted using corrected RC2-64 encryption<br/>
318		/// 6.1 - File is encrypted using non-OAEP key wrapping<br/>
319		/// 6.2 - Central directory encryption (not confirmed yet)<br/>
320		/// </remarks>
321		public int Version
322		{
323			get
324			{
325				if (versionToExtract != 0)
326				{
327					return versionToExtract;
328				} // if
329				else
330				{
331					int result = 10;
332					if (CompressionMethod.Deflated == method)
333					{
334						result = 20;
335					} // if
336					else if (IsDirectory)
337					{
338						result = 20;
339					} // else if
340					else if (IsCrypted)
341					{
342						result = 20;
343					} // else if
344					else if ((known & KnownExternAttributes) != 0 &&
345					         (externalFileAttributes & 0x08) != 0)
346					{
347						result = 11;
348					} // else if
349					return result;
350				} // else
351			} // get
352		}
353		#endregion
354
355		#region RequiresZip64 (Public)
356		/// <summary>
357		/// Gets a value indicating if the entry requires Zip64 extensions to be
358		/// stored.
359		/// </summary>
360		public bool RequiresZip64
361		{
362			get
363			{
364				return (size > uint.MaxValue) ||
365				       (compressedSize > uint.MaxValue);
366			} // get
367		}
368		#endregion
369
370		#region DosTime (Public)
371		/// <summary>
372		/// Gets/Sets DosTime
373		/// </summary>		
374		public long DosTime
375		{
376			get
377			{
378				if ((known & KnownTime) == 0)
379				{
380					return 0;
381				} // if
382				else
383				{
384					return dosTime;
385				} // else
386			} // get
387			set
388			{
389				dosTime = (uint)value;
390				known |= KnownTime;
391			} // set
392		}
393		#endregion
394
395		#region DateTime (Public)
396		/// <summary>
397		/// Gets/Sets the time of last modification of the entry.
398		/// </summary>
399		public DateTime DateTime
400		{
401			get
402			{
403				// Although technically not valid some archives have dates set to zero.
404				// This mimics some archivers handling and is a good a cludge as any
405				// probably.
406				if (dosTime == 0)
407				{
408					return DateTime.Now;
409				} // if
410				else
411				{
412					uint sec = 2 * (dosTime & 0x1f);
413					uint min = (dosTime >> 5) & 0x3f;
414					uint hrs = (dosTime >> 11) & 0x1f;
415					uint day = (dosTime >> 16) & 0x1f;
416					uint mon = ((dosTime >> 21) & 0xf);
417					uint year = ((dosTime >> 25) & 0x7f) + 1980;
418					return new DateTime((int)year, (int)mon, (int)day,
419						(int)hrs, (int)min, (int)sec);
420				} // else
421			} // get
422			set
423			{
424				DosTime =
425					((uint)value.Year - 1980 & 0x7f) << 25 |
426					((uint)value.Month) << 21 |
427					((uint)value.Day) << 16 |
428					((uint)value.Hour) << 11 |
429					((uint)value.Minute) << 5 |
430					((uint)value.Second) >> 1;
431			} // set
432		}
433		#endregion
434
435		#region Name (Public)
436		/// <summary>
437		/// Returns the entry name.  The path components in the entry should
438		/// always separated by slashes ('/').  Dos device names like C: should
439		/// also be removed. See <see cref="CleanName(string)">CleanName</see>.
440		/// </summary>
441		public string Name
442		{
443			get
444			{
445				return name;
446			} // get
447		}
448		#endregion
449
450		#region Comment (Public)
451		/// <summary>
452		/// Gets/Sets the entry comment.
453		/// </summary>
454		/// <exception cref="System.ArgumentOutOfRangeException">
455		/// If comment is longer than 0xffff.
456		/// </exception>
457		/// <returns>
458		/// The comment or null if not set.
459		/// </returns>
460		public string Comment
461		{
462			get
463			{
464				return comment;
465			}
466			set
467			{
468				// While the test is correct in that a comment of this length or
469				// greater is definitely invalid, shorter comments may also have an
470				// invalid length.
471				if (value != null &&
472				    value.Length > 0xffff)
473				{
474					throw new ArgumentOutOfRangeException();
475				}
476				comment = value;
477			}
478		}
479		#endregion
480
481		#region IsDirectory (Public)
482		/// <summary>
483		/// Gets a value indicating of the if the entry is a directory.
484		/// A directory is determined by an entry name with a trailing slash '/'.
485		/// The external file attributes can also mark a file as a directory.
486		/// The trailing slash convention should always be followed however.
487		/// </summary>
488		public bool IsDirectory
489		{
490			get
491			{
492				int nlen = name.Length;
493				bool result = nlen > 0 && name[nlen - 1] == '/';
494
495				if (result == false &&
496				    (known & KnownExternAttributes) != 0)
497				{
498					if (HostSystemId == 0 &&
499					    (ExternalFileAttributes & 16) != 0)
500					{
501						result = true;
502					}
503				}
504				return result;
505			}
506		}
507		#endregion
508
509		#region IsFile (Public)
510		/// <summary>
511		/// Get a value of true if the entry appears to be a file; false otherwise
512		/// </summary>
513		/// <remarks>
514		/// This only takes account Windows attributes. Other operating systems
515		/// are ignored. For Linux and others the result may be incorrect.
516		/// </remarks>
517		public bool IsFile
518		{
519			get
520			{
521				bool result = !IsDirectory;
522
523				// Exclude volume labels
524				if (result && (known & KnownExternAttributes) != 0)
525				{
526					if (HostSystemId == 0 &&
527					    (ExternalFileAttributes & 8) != 0)
528					{
529						result = false;
530					}
531				}
532				return result;
533			}
534		}
535		#endregion
536
537		#region Size (Public)
538		/// <summary>
539		/// Gets/Sets the size of the uncompressed data.
540		/// </summary>
541		/// <exception cref="System.ArgumentOutOfRangeException">
542		/// If the size is not in the range 0..0xffffffffL
543		/// </exception>
544		/// <returns>
545		/// The size or -1 if unknown.
546		/// </returns>
547		public long Size
548		{
549			get
550			{
551				return (known & KnownSize) != 0
552				       	? (long)size
553				       	: -1L;
554			}
555			set
556			{
557				if (((ulong)value & 0xFFFFFFFF00000000L) != 0)
558				{
559					throw new ArgumentOutOfRangeException("size");
560				}
561				size = (ulong)value;
562				known |= KnownSize;
563			}
564		}
565		#endregion
566
567		#region CompressedSize (Public)
568		/// <summary>
569		/// Gets/Sets the size of the compressed data.
570		/// </summary>
571		/// <exception cref="System.ArgumentOutOfRangeException">
572		/// Size is not in the range 0..0xffffffff
573		/// </exception>
574		/// <returns>
575		/// The size or -1 if unknown.
576		/// </returns>
577		public long CompressedSize
578		{
579			get
580			{
581				return (known & KnownCSize) != 0
582				       	? (long)compressedSize
583				       	: -1L;
584			}
585			set
586			{
587				if (((ulong)value & 0xffffffff00000000L) != 0)
588				{
589					throw new ArgumentOutOfRangeException();
590				}
591				compressedSize = (ulong)value;
592				known |= KnownCSize;
593			}
594		}
595		#endregion
596
597		#region Crc (Public)
598		/// <summary>
599		/// Gets/Sets the crc of the uncompressed data.
600		/// </summary>
601		/// <exception cref="System.ArgumentOutOfRangeException">
602		/// Crc is not in the range 0..0xffffffffL
603		/// </exception>
604		/// <returns>
605		/// The crc value or -1 if unknown.
606		/// </returns>
607		public long Crc
608		{
609			get
610			{
611				return (known & KnownCrc) != 0
612				       	? crc & 0xffffffffL
613				       	: -1L;
614			}
615			set
616			{
617				if ((crc & 0xffffffff00000000L) != 0)
618				{
619					throw new ArgumentOutOfRangeException();
620				}
621				crc = (uint)value;
622				known |= KnownCrc;
623			}
624		}
625		#endregion
626
627		#region CompressionMethod (Public)
628		/// <summary>
629		/// Gets/Sets the compression method. Only Deflated and Stored are
630		/// supported.
631		/// </summary>
632		/// <returns>
633		/// The compression method for this entry
634		/// </returns>
635		/// <see cref="Delta.Utilities.Compression.CompressionMethod.Deflated"/>
636		/// <see cref="Delta.Utilities.Compression.CompressionMethod.Stored"/>
637		public CompressionMethod CompressionMethod
638		{
639			get
640			{
641				return method;
642			}
643			set
644			{
645				method = value;
646			}
647		}
648		#endregion
649
650		#region Private
651
652		#region known (Private)
653		/// <summary>
654		/// Bit flags made up of above bits
655		/// </summary>
656		private ushort known;
657		#endregion
658
659		#region externalFileAttributes (Private)
660		/// <summary>
661		/// contains external attributes (os dependant)
662		/// </summary>
663		private int externalFileAttributes;
664		#endregion
665
666		#region versionMadeBy (Private)
667		/// <summary>
668		/// Contains host system and version information
669		/// only relevant for central header entries
670		/// </summary>
671		private ushort versionMadeBy;
672		#endregion
673
674		#region name (Private)
675		/// <summary>
676		/// Name
677		/// </summary>
678		private string name;
679		#endregion
680
681		#region size (Private)
682		/// <summary>
683		/// Size
684		/// </summary>
685		private ulong size;
686		#endregion
687
688		#region compressedSize (Private)
689		/// <summary>
690		/// Compressed size
691		/// </summary>
692		private ulong compressedSize;
693		#endregion
694
695		#region versionToExtract (Private)
696		/// <summary>
697		/// Version required to extract (library handles &lt;= 2.0)
698		/// </summary>
699		private ushort versionToExtract;
700		#endregion
701
702		#region crc (Private)
703		/// <summary>
704		/// Crc
705		/// </summary>
706		private uint crc;
707		#endregion
708
709		#region dosTime (Private)
710		/// <summary>
711		/// Dos time
712		/// </summary>
713		private uint dosTime;
714		#endregion
715
716		#region method (Private)
717		/// <summary>
718		/// Method
719		/// </summary>
720		private CompressionMethod method;
721		#endregion
722
723		#region extra (Private)
724		/// <summary>
725		/// Extra
726		/// </summary>
727		private byte[] extra;
728		#endregion
729
730		#region comment (Private)
731		/// <summary>
732		/// Comment
733		/// </summary>
734		private string comment;
735		#endregion
736
737		#region flags (Private)
738		/// <summary>
739		/// general purpose bit flags
740		/// </summary>
741		private int flags;
742		#endregion
743
744		#region zipFileIndex (Private)
745		/// <summary>
746		/// used by ZipFile
747		/// </summary>
748		private int zipFileIndex;
749		#endregion
750
751		#region offset (Private)
752		/// <summary>
753		/// used by ZipFile and ZipOutputStream
754		/// </summary>
755		private int offset;
756		#endregion
757
758		#endregion
759
760		#region Constructors
761		/// <summary>
762		/// Creates a zip entry with the given name.
763		/// </summary>
764		/// <param name="name">
765		/// The name for this entry. Can include directory components.
766		/// The convention for names is 'unix'  style paths with no device names
767		/// and path elements separated by '/' characters.  This is not enforced
768		/// see <see cref="CleanName(string)">CleanName</see> on how to ensure
769		/// names are valid if this is desired.
770		/// </param>
771		/// <exception cref="ArgumentNullException">The name passed is null
772		/// </exception>
773		public ZipEntry(string name)
774			: this(name, 0, ZipConstants.VersionMadeBy)
775		{
776		}
777
778		/// <summary>
779		/// Creates a zip entry with the given name and version required to extract
780		/// </summary>
781		/// <param name="name">
782		/// The name for this entry. Can include directory components.
783		/// The convention for names is 'unix'  style paths with no device names
784		/// and path elements separated by '/' characters.  This is not enforced
785		/// see <see cref="CleanName(string)">CleanName</see> on how to ensure
786		/// names are valid if this is desired.
787		/// </param>
788		/// <param name="versionRequiredToExtract">
789		/// The minimum 'feature version' required this entry
790		/// </param>
791		/// <exception cref="ArgumentNullException">The name passed is null
792		/// </exception>
793		internal ZipEntry(string name, int versionRequiredToExtract)
794			: this(name, versionRequiredToExtract, ZipConstants.VersionMadeBy)
795		{
796		}
797
798		/// <summary>
799		/// Initializes an entry with the given name and made by information
800		/// </summary>
801		/// <param name="setName">Name for this entry</param>
802		/// <param name="setMadeByInfo">Version and HostSystem Information</param>
803		/// <param name="setVersionRequiredToExtract">Minimum required zip feature
804		/// version required to extract this entry</param>
805		/// <exception cref="ArgumentNullException">
806		/// The name passed is null
807		/// </exception>
808		/// <exception cref="ArgumentOutOfRangeException">
809		/// versionRequiredToExtract should be 0 (auto-calculate) or > 10
810		/// </exception>
811		/// <remarks>
812		/// This constructor is used by the ZipFile class when reading from the
813		/// central header. It is not generally useful, use the constructor
814		/// specifying the name only.
815		/// </remarks>
816		internal ZipEntry(string setName, int setVersionRequiredToExtract,
817			int setMadeByInfo)
818		{
819			if (setName == null)
820			{
821				throw new ArgumentNullException("ZipEntry name");
822			}
823
824			if (setName.Length == 0)
825			{
826				throw new ArgumentException("ZipEntry name is empty");
827			}
828
829			if (setVersionRequiredToExtract != 0 &&
830			    setVersionRequiredToExtract < 10)
831			{
832				throw new ArgumentOutOfRangeException(
833					"versionRequiredToExtract");
834			}
835
836			DateTime = DateTime.Now;
837			name = setName;
838			versionMadeBy = (ushort)setMadeByInfo;
839			versionToExtract = (ushort)setVersionRequiredToExtract;
840			method = CompressionMethod.Deflated;
841			zipFileIndex = MathHelper.InvalidIndex;
842			externalFileAttributes = MathHelper.InvalidIndex;
843		}
844
845		/// <summary>
846		/// Can be used for the "Load" method (after saving) or to create a clone.
847		/// </summary>
848		private ZipEntry()
849		{
850		}
851		#endregion
852
853		#region GetExtraData (Public)
854		/// <summary>
855		/// Get extra data
856		/// </summary>
857		public byte[] GetExtraData()
858		{
859			return extra;
860		}
861		#endregion
862
863		#region SetExtraData (Public)
864		/// <summary>
865		/// Set extra data
866		/// </summary>
867		/// <param name="value">Value</param>
868		public void SetExtraData(byte[] value)
869		{
870			if (value == null)
871			{
872				extra = null;
873				return;
874			}
875
876			if (value.Length > 0xffff)
877			{
878				throw new ArgumentOutOfRangeException();
879			}
880
881			extra = new byte[value.Length];
882			Array.Copy(value, 0, extra, 0, value.Length);
883
884			try
885			{
886				int pos = 0;
887				while (pos < extra.Length)
888				{
889					int sig = (extra[pos++] & 0xff) | (extra[pos++] & 0xff) << 8;
890					int len = (extra[pos++] & 0xff) | (extra[pos++] & 0xff) << 8;
891
892					if (len < 0 ||
893					    pos + len > extra.Length)
894					{
895						// This is still lenient but the extra data is corrupt.
896						// Note: Drops the extra data. Indicate to user there is a problem.
897						Log.Warning(
898							"Dropping extra data in zip entry '" + name + "': " + len);
899						break;
900					}
901
902					if (sig == 0x5455)
903					{
904						// extended time stamp, unix format by Rainer Prem
905						int extraFlags = extra[pos];
906						// Can include other times but these are ignored.
907						// Length of data should actually be 1 + 4 * no of bits in flags.
908						if ((extraFlags & 1) != 0 &&
909						    len >= 5)
910						{
911							int iTime =
912								((extra[pos + 1] & 0xff) |
913								 (extra[pos + 2] & 0xff) << 8 |
914								 (extra[pos + 3] & 0xff) << 16 |
915								 (extra[pos + 4] & 0xff) << 24);
916
917							DateTime =
918								(new DateTime(1970, 1, 1, 0, 0, 0) +
919								 new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime();
920							known |= KnownTime;
921						}
922					}
923					else if (sig == 0x0001)
924					{
925						// ZIP64 extended information extra field
926						// Of variable size depending on which fields in header are too
927						// small fields appear here if the corresponding local or central
928						// directory record field is set to 0xFFFF or 0xFFFFFFFF and the
929						// entry is in Zip64 format.
930						//
931						// Original Size          8 bytes
932						// Compressed size        8 bytes
933						// Relative header offset 8 bytes
934						// Disk start number      4 bytes
935					}
936					pos += len;
937				}
938			}
939			catch (Exception)
940			{
941				// be lenient
942				return;
943			}
944		}
945		#endregion
946
947		#region Clone (Public)
948		/// <summary>
949		/// Creates a copy of this zip entry.
950		/// </summary>
951		public ZipEntry Clone()
952		{
953			return new ZipEntry
954			{
955				known = known,
956				name = name,
957				size = size,
958				compressedSize = compressedSize,
959				crc = crc,
960				dosTime = dosTime,
961				method = method,
962				extra = extra,
963				comment = comment,
964				versionToExtract = versionToExtract,
965				versionMadeBy = versionMadeBy,
966				externalFileAttributes = externalFileAttributes,
967				flags = flags,
968				zipFileIndex = zipFileIndex,
969				offset = offset,
970			};
971		}
972		#endregion
973
974		#region ToString (Public)
975		/// <summary>
976		/// Gets the string representation of this ZipEntry.
977		/// </summary>
978		public override string ToString()
979		{
980			return name;
981		}
982		#endregion
983	}
984}