context: // ZipFile.Read.cs
  // ------------------------------------------------------------------
  //
  // Copyright (c) 2009-2011 Dino Chiesa.
  // All rights reserved.
  //
  // This code module is part of DotNetZip, a zipfile class library.
  //
  // ------------------------------------------------------------------
  //
  // This code is licensed under the Microsoft Public License.
  // See the file License.txt for the license details.
  // More info on: http://dotnetzip.codeplex.com
  //
  // ------------------------------------------------------------------
  //
  // last saved (in emacs):
  // Time-stamp: <2011-August-05 11:38:59>
  //
  // ------------------------------------------------------------------
  //
  // This module defines the methods for Reading zip files.
  //
  // ------------------------------------------------------------------
  //
  
  
  using System;
  using System.Collections.Generic;
  using System.IO;
  
  namespace OfficeOpenXml.Packaging.Ionic.Zip
  {
  	/// <summary>
  	///   A class for collecting the various options that can be used when
  	///   Reading zip files for extraction or update.
  	/// </summary>
  	///
  	/// <remarks>
  	///   <para>
  	///     When reading a zip file, there are several options an
  	///     application can set, to modify how the file is read, or what
  	///     the library does while reading.  This class collects those
  	///     options into one container.
  	///   </para>
  	///
  	///   <para>
  	///     Pass an instance of the <c>ReadOptions</c> class into the
  	///     <c>ZipFile.Read()</c> method.
  	///   </para>
  	///
  	/// <seealso cref="ZipFile.Read(String, ReadOptions)"/>.
  	/// <seealso cref="ZipFile.Read(Stream, ReadOptions)"/>.
  	/// </remarks>
  	internal class ReadOptions
  	{
  		/// <summary>
  		/// An event handler for Read operations.  When opening large zip
  		/// archives, you may want to display a progress bar or other
  		/// indicator of status progress while reading.  This parameter
  		/// allows you to specify a ReadProgress Event Handler directly.
  		/// When you call <c>Read()</c>, the progress event is invoked as
  		/// necessary.
  		/// </summary>
  		public EventHandler<ReadProgressEventArgs> ReadProgress { get; set; }
  
  		/// <summary>
  		/// The <c>System.IO.TextWriter</c> to use for writing verbose status messages
  		/// during operations on the zip archive.  A console application may wish to
  		/// pass <c>System.Console.Out</c> to get messages on the Console. A graphical
  		/// or headless application may wish to capture the messages in a different
  		/// <c>TextWriter</c>, such as a <c>System.IO.StringWriter</c>.
  		/// </summary>
  		public TextWriter StatusMessageWriter { get; set; }
  
  		/// <summary>
  		/// The <c>System.Text.Encoding</c> to use when reading in the zip archive. Be
  		/// careful specifying the encoding.  If the value you use here is not the same
  		/// as the Encoding used when the zip archive was created (possibly by a
  		/// different archiver) you will get unexpected results and possibly exceptions.
  		/// </summary>
  		///
  		/// <seealso cref="ZipFile.ProvisionalAlternateEncoding"/>
  		///
  		public System.Text.Encoding @Encoding { get; set; }
  	}
  
  
  	internal partial class ZipFile
  	{
  		/// <summary>
  		/// Reads a zip file archive and returns the instance.
  		/// </summary>
  		///
  		/// <remarks>
  		/// <para>
  		/// The stream is read using the default <c>System.Text.Encoding</c>, which is the
  		/// <c>IBM437</c> codepage.
  		/// </para>
  		/// </remarks>
  		///
  		/// <exception cref="System.Exception">
  		/// Thrown if the <c>ZipFile</c> cannot be read. The implementation of this method
  		/// relies on <c>System.IO.File.OpenRead</c>, which can throw a variety of exceptions,
  		/// including specific exceptions if a file is not found, an unauthorized access
  		/// exception, exceptions for poorly formatted filenames, and so on.
  		/// </exception>
  		///
  		/// <param name="fileName">
  		/// The name of the zip archive to open.  This can be a fully-qualified or relative
  		/// pathname.
  		/// </param>
  		///
  		/// <seealso cref="ZipFile.Read(String, ReadOptions)"/>.
  		///
  		/// <returns>The instance read from the zip archive.</returns>
  		///
  		public static ZipFile Read(string fileName)
  		{
  			return ZipFile.Read(fileName, null, null, null);
  		}
  
  
  		/// <summary>
  		///   Reads a zip file archive from the named filesystem file using the
  		///   specified options.
  		/// </summary>
  		///
  		/// <remarks>
  		/// <para>
  		///   This version of the <c>Read()</c> method allows the caller to pass
  		///   in a <c>TextWriter</c> an <c>Encoding</c>, via an instance of the
  		///   <c>ReadOptions</c> class.  The <c>ZipFile</c> is read in using the
  		///   specified encoding for entries where UTF-8 encoding is not
  		///   explicitly specified.
  		/// </para>
  		/// </remarks>
  		///
  		/// <example>
  		///
  		/// <para>
  		///   This example shows how to read a zip file using the Big-5 Chinese
  		///   code page (950), and extract each entry in the zip file, while
  		///   sending status messages out to the Console.
  		/// </para>
  		///
  		/// <para>
  		///   For this code to work as intended, the zipfile must have been
  		///   created using the big5 code page (CP950). This is typical, for
  		///   example, when using WinRar on a machine with CP950 set as the
  		///   default code page.  In that case, the names of entries within the
  		///   Zip archive will be stored in that code page, and reading the zip
  		///   archive must be done using that code page.  If the application did
  		///   not use the correct code page in ZipFile.Read(), then names of
  		///   entries within the zip archive would not be correctly retrieved.
  		/// </para>
  		///
  		/// <code lang="C#">
  		/// string zipToExtract = "MyArchive.zip";
  		/// string extractDirectory = "extract";
  		/// var options = new ReadOptions
  		/// {
  		///   StatusMessageWriter = System.Console.Out,
  		///   Encoding = System.Text.Encoding.GetEncoding(950)
  		/// };
  		/// using (ZipFile zip = ZipFile.Read(zipToExtract, options))
  		/// {
  		///   foreach (ZipEntry e in zip)
  		///   {
  		///      e.Extract(extractDirectory);
  		///   }
  		/// }
  		/// </code>
  		///
  		///
  		/// <code lang="VB">
  		/// Dim zipToExtract as String = "MyArchive.zip"
  		/// Dim extractDirectory as String = "extract"
  		/// Dim options as New ReadOptions
  		/// options.Encoding = System.Text.Encoding.GetEncoding(950)
  		/// options.StatusMessageWriter = System.Console.Out
  		/// Using zip As ZipFile = ZipFile.Read(zipToExtract, options)
  		///     Dim e As ZipEntry
  		///     For Each e In zip
  		///      e.Extract(extractDirectory)
  		///     Next
  		/// End Using
  		/// </code>
  		/// </example>
  		///
  		///
  		/// <example>
  		///
  		/// <para>
  		///   This example shows how to read a zip file using the default
  		///   code page, to remove entries that have a modified date before a given threshold,
  		///   sending status messages out to a <c>StringWriter</c>.
  		/// </para>
  		///
  		/// <code lang="C#">
  		/// var options = new ReadOptions
  		/// {
  		///   StatusMessageWriter = new System.IO.StringWriter()
  		/// };
  		/// using (ZipFile zip =  ZipFile.Read("PackedDocuments.zip", options))
  		/// {
  		///   var Threshold = new DateTime(2007,7,4);
  		///   // We cannot remove the entry from the list, within the context of
  		///   // an enumeration of said list.
  		///   // So we add the doomed entry to a list to be removed later.
  		///   // pass 1: mark the entries for removal
  		///   var MarkedEntries = new System.Collections.Generic.List&lt;ZipEntry&gt;();
  		///   foreach (ZipEntry e in zip)
  		///   {
  		///     if (e.LastModified &lt; Threshold)
  		///       MarkedEntries.Add(e);
  		///   }
  		///   // pass 2: actually remove the entry.
  		///   foreach (ZipEntry zombie in MarkedEntries)
  		///      zip.RemoveEntry(zombie);
  		///   zip.Comment = "This archive has been updated.";
  		///   zip.Save();
  		/// }
  		/// // can now use contents of sw, eg store in an audit log
  		/// </code>
  		///
  		/// <code lang="VB">
  		/// Dim options as New ReadOptions
  		/// options.StatusMessageWriter = New System.IO.StringWriter
  		/// Using zip As ZipFile = ZipFile.Read("PackedDocuments.zip", options)
  		///     Dim Threshold As New DateTime(2007, 7, 4)
  		///     ' We cannot remove the entry from the list, within the context of
  		///     ' an enumeration of said list.
  		///     ' So we add the doomed entry to a list to be removed later.
  		///     ' pass 1: mark the entries for removal
  		///     Dim MarkedEntries As New System.Collections.Generic.List(Of ZipEntry)
  		///     Dim e As ZipEntry
  		///     For Each e In zip
  		///         If (e.LastModified &lt; Threshold) Then
  		///             MarkedEntries.Add(e)
  		///         End If
  		///     Next
  		///     ' pass 2: actually remove the entry.
  		///     Dim zombie As ZipEntry
  		///     For Each zombie In MarkedEntries
  		///         zip.RemoveEntry(zombie)
  		///     Next
  		///     zip.Comment = "This archive has been updated."
  		///     zip.Save
  		/// End Using
  		/// ' can now use contents of sw, eg store in an audit log
  		/// </code>
  		/// </example>
  		///
  		/// <exception cref="System.Exception">
  		///   Thrown if the zipfile cannot be read. The implementation of
  		///   this method relies on <c>System.IO.File.OpenRead</c>, which
  		///   can throw a variety of exceptions, including specific
  		///   exceptions if a file is not found, an unauthorized access
  		///   exception, exceptions for poorly formatted filenames, and so
  		///   on.
  		/// </exception>
  		///
  		/// <param name="fileName">
  		/// The name of the zip archive to open.
  		/// This can be a fully-qualified or relative pathname.
  		/// </param>
  		///
  		/// <param name="options">
  		/// The set of options to use when reading the zip file.
  		/// </param>
  		///
  		/// <returns>The ZipFile instance read from the zip archive.</returns>
  		///
  		/// <seealso cref="ZipFile.Read(Stream, ReadOptions)"/>
  		///
  		internal static ZipFile Read(string fileName,
  											ReadOptions options)
  		{
  			if (options == null)
  				throw new ArgumentNullException("options");
  			return Read(fileName,
  							options.StatusMessageWriter,
  							options.Encoding,
  							options.ReadProgress);
  		}
  
  		/// <summary>
  		/// Reads a zip file archive using the specified text encoding,  the specified
  		/// TextWriter for status messages, and the specified ReadProgress event handler,
  		/// and returns the instance.
  		/// </summary>
  		///
  		/// <param name="fileName">
  		/// The name of the zip archive to open.
  		/// This can be a fully-qualified or relative pathname.
  		/// </param>
  		///
  		/// <param name="readProgress">
  		/// An event handler for Read operations.
  		/// </param>
  		///
  		/// <param name="statusMessageWriter">
  		/// The <c>System.IO.TextWriter</c> to use for writing verbose status messages
  		/// during operations on the zip archive.  A console application may wish to
  		/// pass <c>System.Console.Out</c> to get messages on the Console. A graphical
  		/// or headless application may wish to capture the messages in a different
  		/// <c>TextWriter</c>, such as a <c>System.IO.StringWriter</c>.
  		/// </param>
  		///
  		/// <param name="encoding">
  		/// The <c>System.Text.Encoding</c> to use when reading in the zip archive. Be
  		/// careful specifying the encoding.  If the value you use here is not the same
  		/// as the Encoding used when the zip archive was created (possibly by a
  		/// different archiver) you will get unexpected results and possibly exceptions.
  		/// </param>
  		///
  		/// <returns>The instance read from the zip archive.</returns>
  		///
  		private static ZipFile Read(string fileName,
  											TextWriter statusMessageWriter,
  											System.Text.Encoding encoding,
  											EventHandler<ReadProgressEventArgs> readProgress)
  		{
  			ZipFile zf = new ZipFile();
  			zf.AlternateEncoding = encoding ?? DefaultEncoding;
  			zf.AlternateEncodingUsage = ZipOption.Always;
  			zf._StatusMessageTextWriter = statusMessageWriter;
  			zf._name = fileName;
  			if (readProgress != null)
  				zf.ReadProgress = readProgress;
  
  			if (zf.Verbose) zf._StatusMessageTextWriter.WriteLine("reading from {0}...", fileName);
  
  			ReadIntoInstance(zf);
  			zf._fileAlreadyExists = true;
  
  			return zf;
  		}
  
  		/// <summary>
  		///   Reads a zip archive from a stream.
  		/// </summary>
  		///
  		/// <remarks>
  		///
  		/// <para>
  		///   When reading from a file, it's probably easier to just use
  		///   <see cref="ZipFile.Read(String,
  		///   ReadOptions)">ZipFile.Read(String, ReadOptions)</see>.  This
  		///   overload is useful when when the zip archive content is
  		///   available from an already-open stream. The stream must be
  		///   open and readable and seekable when calling this method.  The
  		///   stream is left open when the reading is completed.
  		/// </para>
  		///
  		/// <para>
  		///   Using this overload, the stream is read using the default
  		///   <c>System.Text.Encoding</c>, which is the <c>IBM437</c>
  		///   codepage. If you want to specify the encoding to use when
  		///   reading the zipfile content, see
  		///   <see cref="ZipFile.Read(Stream,
  		///   ReadOptions)">ZipFile.Read(Stream, ReadOptions)</see>.  This
  		/// </para>
  		///
  		/// <para>
  		///   Reading of zip content begins at the current position in the
  		///   stream.  This means if you have a stream that concatenates
  		///   regular data and zip data, if you position the open, readable
  		///   stream at the start of the zip data, you will be able to read
  		///   the zip archive using this constructor, or any of the ZipFile
  		///   constructors that accept a <see cref="System.IO.Stream" /> as
  		///   input. Some examples of where this might be useful: the zip
  		///   content is concatenated at the end of a regular EXE file, as
  		///   some self-extracting archives do.  (Note: SFX files produced
  		///   by DotNetZip do not work this way; they can be read as normal
  		///   ZIP files). Another example might be a stream being read from
  		///   a database, where the zip content is embedded within an
  		///   aggregate stream of data.
  		/// </para>
  		///
  		/// </remarks>
  		///
  		/// <example>
  		/// <para>
  		///   This example shows how to Read zip content from a stream, and
  		///   extract one entry into a different stream. In this example,
  		///   the filename "NameOfEntryInArchive.doc", refers only to the
  		///   name of the entry within the zip archive.  A file by that
  		///   name is not created in the filesystem.  The I/O is done
  		///   strictly with the given streams.
  		/// </para>
  		///
  		/// <code>
  		/// using (ZipFile zip = ZipFile.Read(InputStream))
  		/// {
  		///    zip.Extract("NameOfEntryInArchive.doc", OutputStream);
  		/// }
  		/// </code>
  		///
  		/// <code lang="VB">
  		/// Using zip as ZipFile = ZipFile.Read(InputStream)
  		///    zip.Extract("NameOfEntryInArchive.doc", OutputStream)
  		/// End Using
  		/// </code>
  		/// </example>
  		///
  		/// <param name="zipStream">the stream containing the zip data.</param>
  		///
  		/// <returns>The ZipFile instance read from the stream</returns>
  		///
  		public static ZipFile Read(Stream zipStream)
  		{
  			return Read(zipStream, null, null, null);
  		}
  
  		/// <summary>
  		///   Reads a zip file archive from the given stream using the
  		///   specified options.
  		/// </summary>
  		///
  		/// <remarks>
  		///
  		/// <para>
  		///   When reading from a file, it's probably easier to just use
  		///   <see cref="ZipFile.Read(String,
  		///   ReadOptions)">ZipFile.Read(String, ReadOptions)</see>.  This
  		///   overload is useful when when the zip archive content is
  		///   available from an already-open stream. The stream must be
  		///   open and readable and seekable when calling this method.  The
  		///   stream is left open when the reading is completed.
  		/// </para>
  		///
  		/// <para>
  		///   Reading of zip content begins at the current position in the
  		///   stream.  This means if you have a stream that concatenates
  		///   regular data and zip data, if you position the open, readable
  		///   stream at the start of the zip data, you will be able to read
  		///   the zip archive using this constructor, or any of the ZipFile
  		///   constructors that accept a <see cref="System.IO.Stream" /> as
  		///   input. Some examples of where this might be useful: the zip
  		///   content is concatenated at the end of a regular EXE file, as
  		///   some self-extracting archives do.  (Note: SFX files produced
  		///   by DotNetZip do not work this way; they can be read as normal
  		///   ZIP files). Another example might be a stream being read from
  		///   a database, where the zip content is embedded within an
  		///   aggregate stream of data.
  		/// </para>
  		/// </remarks>
  		///
  		/// <param name="zipStream">the stream containing the zip data.</param>
  		///
  		/// <param name="options">
  		///   The set of options to use when reading the zip file.
  		/// </param>
  		///
  		/// <exception cref="System.Exception">
  		///   Thrown if the zip archive cannot be read.
  		/// </exception>
  		///
  		/// <returns>The ZipFile instance read from the stream.</returns>
  		///
  		/// <seealso cref="ZipFile.Read(String, ReadOptions)"/>
  		///
  		internal static ZipFile Read(Stream zipStream, ReadOptions options)
  		{
  			if (options == null)
  				throw new ArgumentNullException("options");
  
  			return Read(zipStream,
  							options.StatusMessageWriter,
  							options.Encoding,
  							options.ReadProgress);
  		}
  
  
  
  		/// <summary>
  		/// Reads a zip archive from a stream, using the specified text Encoding, the
  		/// specified TextWriter for status messages,
  		/// and the specified ReadProgress event handler.
  		/// </summary>
  		///
  		/// <remarks>
  		/// <para>
  		/// Reading of zip content begins at the current position in the stream.  This
  		/// means if you have a stream that concatenates regular data and zip data, if
  		/// you position the open, readable stream at the start of the zip data, you
  		/// will be able to read the zip archive using this constructor, or any of the
  		/// ZipFile constructors that accept a <see cref="System.IO.Stream" /> as
  		/// input. Some examples of where this might be useful: the zip content is
  		/// concatenated at the end of a regular EXE file, as some self-extracting
  		/// archives do.  (Note: SFX files produced by DotNetZip do not work this
  		/// way). Another example might be a stream being read from a database, where
  		/// the zip content is embedded within an aggregate stream of data.
  		/// </para>
  		/// </remarks>
  		///
  		/// <param name="zipStream">the stream containing the zip data.</param>
  		///
  		/// <param name="statusMessageWriter">
  		/// The <c>System.IO.TextWriter</c> to which verbose status messages are written
  		/// during operations on the <c>ZipFile</c>.  For example, in a console
  		/// application, System.Console.Out works, and will get a message for each entry
  		/// added to the ZipFile.  If the TextWriter is <c>null</c>, no verbose messages
  		/// are written.
  		/// </param>
  		///
  		/// <param name="encoding">
  		/// The text encoding to use when reading entries that do not have the UTF-8
  		/// encoding bit set.  Be careful specifying the encoding.  If the value you use
  		/// here is not the same as the Encoding used when the zip archive was created
  		/// (possibly by a different archiver) you will get unexpected results and
  		/// possibly exceptions.  See the <see cref="ProvisionalAlternateEncoding"/>
  		/// property for more information.
  		/// </param>
  		///
  		/// <param name="readProgress">
  		/// An event handler for Read operations.
  		/// </param>
  		///
  		/// <returns>an instance of ZipFile</returns>
  		private static ZipFile Read(Stream zipStream,
  											TextWriter statusMessageWriter,
  											System.Text.Encoding encoding,
  											EventHandler<ReadProgressEventArgs> readProgress)
  		{
  			if (zipStream == null)
  				throw new ArgumentNullException("zipStream");
  
  			ZipFile zf = new ZipFile();
  			zf._StatusMessageTextWriter = statusMessageWriter;
  			zf._alternateEncoding = encoding ?? ZipFile.DefaultEncoding;
  			zf._alternateEncodingUsage = ZipOption.Always;
  			if (readProgress != null)
  				zf.ReadProgress += readProgress;
  			zf._readstream = (zipStream.Position == 0L)
  				 ? zipStream
  				 : new OffsetStream(zipStream);
  			zf._ReadStreamIsOurs = false;
  			if (zf.Verbose) zf._StatusMessageTextWriter.WriteLine("reading from stream...");
  
  			ReadIntoInstance(zf);
  			return zf;
  		}
  
  
  
  		private static void ReadIntoInstance(ZipFile zf)
  		{
  			Stream s = zf.ReadStream;
  			try
  			{
  				zf._readName = zf._name; // workitem 13915
  				if (!s.CanSeek)
  				{
  					ReadIntoInstance_Orig(zf);
  					return;
  				}
  
  				zf.OnReadStarted();
  
  				// change for workitem 8098
  				//zf._originPosition = s.Position;
  
  				// Try reading the central directory, rather than scanning the file.
  
  				uint datum = ReadFirstFourBytes(s);
  
  				if (datum == ZipConstants.EndOfCentralDirectorySignature)
  					return;
  
  
  				// start at the end of the file...
  				// seek backwards a bit, then look for the EoCD signature.
  				int nTries = 0;
  				bool success = false;
  
  				// The size of the end-of-central-directory-footer plus 2 bytes is 18.
  				// This implies an archive comment length of 0.  We'll add a margin of
  				// safety and start "in front" of that, when looking for the
  				// EndOfCentralDirectorySignature
  				long posn = s.Length - 64;
  				long maxSeekback = Math.Max(s.Length - 0x4000, 10);
  				do
  				{
  					if (posn < 0) posn = 0;  // BOF
  					s.Seek(posn, SeekOrigin.Begin);
  					long bytesRead = SharedUtilities.FindSignature(s, (int)ZipConstants.EndOfCentralDirectorySignature);
  					if (bytesRead != -1)
  						success = true;
  					else
  					{
  						if (posn == 0) break; // started at the BOF and found nothing
  						nTries++;
  						// Weird: with NETCF, negative offsets from SeekOrigin.End DO
  						// NOT WORK. So rather than seek a negative offset, we seek
  						// from SeekOrigin.Begin using a smaller number.
  						posn -= (32 * (nTries + 1) * nTries);
  					}
  				}
  				while (!success && posn > maxSeekback);
  
  				if (success)
  				{
  					// workitem 8299
  					zf._locEndOfCDS = s.Position - 4;
  
  					byte[] block = new byte[16];
  					s.Read(block, 0, block.Length);
  
  					zf._diskNumberWithCd = BitConverter.ToUInt16(block, 2);
  
  					if (zf._diskNumberWithCd == 0xFFFF)
  						throw new ZipException("Spanned archives with more than 65534 segments are not supported at this time.");
  
  					zf._diskNumberWithCd++; // I think the number in the file differs from reality by 1
  
  					int i = 12;
  
  					uint offset32 = (uint)BitConverter.ToUInt32(block, i);
  					if (offset32 == 0xFFFFFFFF)
  					{
  						Zip64SeekToCentralDirectory(zf);
  					}
  					else
  					{
  						zf._OffsetOfCentralDirectory = offset32;
  						// change for workitem 8098
  						s.Seek(offset32, SeekOrigin.Begin);
  					}
  
  					ReadCentralDirectory(zf);
  				}
  				else
  				{
  					// Could not find the central directory.
  					// Fallback to the old method.
  					// workitem 8098: ok
  					//s.Seek(zf._originPosition, SeekOrigin.Begin);
  					s.Seek(0L, SeekOrigin.Begin);
  					ReadIntoInstance_Orig(zf);
  				}
  			}
  			catch (Exception ex1)
  			{
  				if (zf._ReadStreamIsOurs && zf._readstream != null)
  				{
  					try
  					{
  #if NETCF
                          zf._readstream.Close();
  #else
  						zf._readstream.Dispose();
  #endif
  						zf._readstream = null;
  					}
  					finally { }
  				}
  
  				throw new ZipException("Cannot read that as a ZipFile", ex1);
  			}
  
  			// the instance has been read in
  			zf._contentsChanged = false;
  		}
  
  
  
  		private static void Zip64SeekToCentralDirectory(ZipFile zf)
  		{
  			Stream s = zf.ReadStream;
  			byte[] block = new byte[16];
  
  			// seek back to find the ZIP64 EoCD.
  			// I think this might not work for .NET CF ?
  			s.Seek(-40, SeekOrigin.Current);
  			s.Read(block, 0, 16);
  
  			Int64 offset64 = BitConverter.ToInt64(block, 8);
  			zf._OffsetOfCentralDirectory = 0xFFFFFFFF;
  			zf._OffsetOfCentralDirectory64 = offset64;
  			// change for workitem 8098
  			s.Seek(offset64, SeekOrigin.Begin);
  			//zf.SeekFromOrigin(Offset64);
  
  			uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s);
  			if (datum != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature)
  				throw new BadReadException(String.Format("  Bad signature (0x{0:X8}) looking for ZIP64 EoCD Record at position 0x{1:X8}", datum, s.Position));
  
  			s.Read(block, 0, 8);
  			Int64 Size = BitConverter.ToInt64(block, 0);
  
  			block = new byte[Size];
  			s.Read(block, 0, block.Length);
  
  			offset64 = BitConverter.ToInt64(block, 36);
  			// change for workitem 8098
  			s.Seek(offset64, SeekOrigin.Begin);
  			//zf.SeekFromOrigin(Offset64);
  		}
  
  
  		private static uint ReadFirstFourBytes(Stream s)
  		{
  			uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s);
  			return datum;
  		}
  
  
  
  		private static void ReadCentralDirectory(ZipFile zf)
  		{
  			// We must have the central directory footer record, in order to properly
  			// read zip dir entries from the central directory.  This because the logic
  			// knows when to open a spanned file when the volume number for the central
  			// directory differs from the volume number for the zip entry.  The
  			// _diskNumberWithCd was set when originally finding the offset for the
  			// start of the Central Directory.
  
  			// workitem 9214
  			bool inputUsesZip64 = false;
  			ZipEntry de;
  			// in lieu of hashset, use a dictionary
  			var previouslySeen = new Dictionary<String, object>();
  
gt: 			while ((de = ZipEntry.ReadDirEntry(zf, previouslySeen)) != null)