internal static ZipArchiveFile Read(ZipArchive archive)
{
Stream reader = archive.FromStream;
ByteBuffer header = new ByteBuffer(30);
int count = header.ReadContentsFrom(reader);
if (count == 0)
{
return null;
}
// Local file header:
//
// local file header signature 4 bytes (0x04034b50)
// version needed to extract 2 bytes
// general purpose bit flag 2 bytes
// compression method 2 bytes
// last mod file time 2 bytes
// last mod file date 2 bytes
// crc-32 4 bytes
// compressed size 4 bytes
// uncompressed size 4 bytes
// file name length 2 bytes
// extra field length 2 bytes
//
// file name (variable size)
// extra field (variable size)
uint fileSignature = header.ReadUInt32();
if (fileSignature != SignatureFileEntry)
{
if (fileSignature != SignatureArchiveDirectory)
{
throw new InvalidOperationException("Bad ZipFile Header");
}
return null;
}
ushort versionNeededToExtract = header.ReadUInt16();
if (versionNeededToExtract > MaximumVersionExtractable)
{
throw new NotSupportedException("Zip file requires unsupported features");
}
header.SkipBytes(2); // general purpose bit flag
ZipArchiveFile newEntry = new ZipArchiveFile(archive, null);
newEntry._compressionMethod = (CompressionMethod)header.ReadUInt16();
newEntry._lastWriteTime = DosTimeToDateTime(header.ReadUInt32());
newEntry._crc32 = header.ReadUInt32();
newEntry._compressedLength = checked((int)header.ReadUInt32());
newEntry._length = header.ReadUInt32();
int fileNameLength = checked((int)header.ReadUInt16());
byte[] fileNameBuffer = new byte[fileNameLength];
int fileNameCount = reader.Read(fileNameBuffer, 0, fileNameLength);
newEntry._name = Encoding.UTF8.GetString(fileNameBuffer).Replace('/', Path.DirectorySeparatorChar);
archive.Entries[newEntry._name] = newEntry;
if (count != header.Length || fileNameCount != fileNameLength || fileNameLength == 0 || newEntry.LastWriteTime.Ticks == 0)
{
throw new InvalidOperationException("Bad Zip File Header");
}
if (newEntry.Name.IndexOfAny(invalidPathChars) >= 0)
{
throw new InvalidOperationException("Invalid File Name");
}
if (newEntry._compressionMethod != CompressionMethod.None && newEntry._compressionMethod != CompressionMethod.Deflate)
{
throw new NotSupportedException("Unsupported compression mode " + newEntry._compressionMethod);
}
if (archive.IsReadOnly && reader.CanSeek)
{
// Optimization: we can defer reading in the data in the common
// case of a read-only archive by simply remembering where the
// data is and fetching it on demand. This is nice because we
// only hold on to memory of data we are actually operating on,
// instead of the whole archive.
//
// Because uncompresseData is null, we know that we fetch it
// from the archive 'fromStream'.
newEntry._positionOfCompressedDataInArchive = archive.FromStream.Position;
reader.Seek(newEntry._compressedLength, SeekOrigin.Current);
}
else
{
// We may be updating the archive in place, so we need to copy
// the data out.
newEntry._compressedData = new byte[newEntry._compressedLength];
reader.Read(newEntry._compressedData, 0, (int)newEntry._compressedLength);
}
#if DEBUG
newEntry.Validate();
#endif
return newEntry;
}