diff src/win32/7zip/7z/CPP/7zip/Archive/Zip/ZipIn.cpp @ 1:f9f4f1b99eed

importing src directory
author Robert McIntyre <rlm@mit.edu>
date Sat, 03 Mar 2012 10:31:27 -0600
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/win32/7zip/7z/CPP/7zip/Archive/Zip/ZipIn.cpp	Sat Mar 03 10:31:27 2012 -0600
     1.3 @@ -0,0 +1,868 @@
     1.4 +// Archive/ZipIn.cpp
     1.5 +
     1.6 +#include "StdAfx.h"
     1.7 +
     1.8 +#include "ZipIn.h"
     1.9 +#include "Windows/Defs.h"
    1.10 +#include "Common/StringConvert.h"
    1.11 +#include "Common/DynamicBuffer.h"
    1.12 +#include "../../Common/LimitedStreams.h"
    1.13 +#include "../../Common/StreamUtils.h"
    1.14 +
    1.15 +extern "C"
    1.16 +{
    1.17 +  #include "../../../../C/CpuArch.h"
    1.18 +}
    1.19 +
    1.20 +#define Get16(p) GetUi16(p)
    1.21 +#define Get32(p) GetUi32(p)
    1.22 +#define Get64(p) GetUi64(p)
    1.23 +
    1.24 +namespace NArchive {
    1.25 +namespace NZip {
    1.26 + 
    1.27 +// static const char kEndOfString = '\0';
    1.28 +
    1.29 +HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
    1.30 +{
    1.31 +  Close();
    1.32 +  RINOK(stream->Seek(0, STREAM_SEEK_CUR, &m_StreamStartPosition));
    1.33 +  m_Position = m_StreamStartPosition;
    1.34 +  RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit));
    1.35 +  RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL));
    1.36 +  m_Stream = stream;
    1.37 +  return S_OK;
    1.38 +}
    1.39 +
    1.40 +void CInArchive::Close()
    1.41 +{
    1.42 +  m_Stream.Release();
    1.43 +}
    1.44 +
    1.45 +HRESULT CInArchive::Seek(UInt64 offset)
    1.46 +{
    1.47 +  return m_Stream->Seek(offset, STREAM_SEEK_SET, NULL);
    1.48 +}
    1.49 +
    1.50 +//////////////////////////////////////
    1.51 +// Markers
    1.52 +
    1.53 +static inline bool TestMarkerCandidate(const Byte *p, UInt32 &value)
    1.54 +{
    1.55 +  value = Get32(p);
    1.56 +  return
    1.57 +    (value == NSignature::kLocalFileHeader) ||
    1.58 +    (value == NSignature::kEndOfCentralDir);
    1.59 +}
    1.60 +
    1.61 +static const UInt32 kNumMarkerAddtionalBytes = 2;
    1.62 +static inline bool TestMarkerCandidate2(const Byte *p, UInt32 &value)
    1.63 +{
    1.64 +  value = Get32(p);
    1.65 +  if (value == NSignature::kEndOfCentralDir)
    1.66 +    return (Get16(p + 4) == 0);
    1.67 +  return (value == NSignature::kLocalFileHeader && p[4] < 128);
    1.68 +}
    1.69 +
    1.70 +HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
    1.71 +{
    1.72 +  m_ArchiveInfo.Clear();
    1.73 +  m_Position = m_StreamStartPosition;
    1.74 +
    1.75 +  Byte marker[NSignature::kMarkerSize];
    1.76 +  RINOK(ReadStream_FALSE(stream, marker, NSignature::kMarkerSize));
    1.77 +  m_Position += NSignature::kMarkerSize;
    1.78 +  if (TestMarkerCandidate(marker, m_Signature))
    1.79 +    return S_OK;
    1.80 +
    1.81 +  CByteDynamicBuffer dynamicBuffer;
    1.82 +  const UInt32 kSearchMarkerBufferSize = 0x10000;
    1.83 +  dynamicBuffer.EnsureCapacity(kSearchMarkerBufferSize);
    1.84 +  Byte *buffer = dynamicBuffer;
    1.85 +  UInt32 numBytesPrev = NSignature::kMarkerSize - 1;
    1.86 +  memcpy(buffer, marker + 1, numBytesPrev);
    1.87 +  UInt64 curTestPos = m_StreamStartPosition + 1;
    1.88 +  for (;;)
    1.89 +  {
    1.90 +    if (searchHeaderSizeLimit != NULL)
    1.91 +      if (curTestPos - m_StreamStartPosition > *searchHeaderSizeLimit)
    1.92 +        break;
    1.93 +    size_t numReadBytes = kSearchMarkerBufferSize - numBytesPrev;
    1.94 +    RINOK(ReadStream(stream, buffer + numBytesPrev, &numReadBytes));
    1.95 +    m_Position += numReadBytes;
    1.96 +    UInt32 numBytesInBuffer = numBytesPrev + (UInt32)numReadBytes;
    1.97 +    const UInt32 kMarker2Size = NSignature::kMarkerSize + kNumMarkerAddtionalBytes;
    1.98 +    if (numBytesInBuffer < kMarker2Size)
    1.99 +      break;
   1.100 +    UInt32 numTests = numBytesInBuffer - kMarker2Size + 1;
   1.101 +    for (UInt32 pos = 0; pos < numTests; pos++)
   1.102 +    {
   1.103 +      if (buffer[pos] != 0x50)
   1.104 +        continue;
   1.105 +      if (TestMarkerCandidate2(buffer + pos, m_Signature))
   1.106 +      {
   1.107 +        curTestPos += pos;
   1.108 +        m_ArchiveInfo.StartPosition = curTestPos;
   1.109 +        m_Position = curTestPos + NSignature::kMarkerSize;
   1.110 +        return S_OK;
   1.111 +      }
   1.112 +    }
   1.113 +    curTestPos += numTests;
   1.114 +    numBytesPrev = numBytesInBuffer - numTests;
   1.115 +    memmove(buffer, buffer + numTests, numBytesPrev);
   1.116 +  }
   1.117 +  return S_FALSE;
   1.118 +}
   1.119 +
   1.120 +HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize)
   1.121 +{
   1.122 +  size_t realProcessedSize = size;
   1.123 +  HRESULT result = ReadStream(m_Stream, data, &realProcessedSize);
   1.124 +  if (processedSize != NULL)
   1.125 +    *processedSize = (UInt32)realProcessedSize;
   1.126 +  m_Position += realProcessedSize;
   1.127 +  return result;
   1.128 +}
   1.129 +
   1.130 +void CInArchive::IncreaseRealPosition(UInt64 addValue)
   1.131 +{
   1.132 +  if (m_Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position) != S_OK)
   1.133 +    throw CInArchiveException(CInArchiveException::kSeekStreamError);
   1.134 +}
   1.135 +
   1.136 +bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size)
   1.137 +{
   1.138 +  UInt32 realProcessedSize;
   1.139 +  if (ReadBytes(data, size, &realProcessedSize) != S_OK)
   1.140 +    throw CInArchiveException(CInArchiveException::kReadStreamError);
   1.141 +  return (realProcessedSize == size);
   1.142 +}
   1.143 +
   1.144 +void CInArchive::SafeReadBytes(void *data, UInt32 size)
   1.145 +{
   1.146 +  if (!ReadBytesAndTestSize(data, size))
   1.147 +    throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive);
   1.148 +}
   1.149 +
   1.150 +void CInArchive::ReadBuffer(CByteBuffer &buffer, UInt32 size)
   1.151 +{
   1.152 +  buffer.SetCapacity(size);
   1.153 +  if (size > 0)
   1.154 +    SafeReadBytes(buffer, size);
   1.155 +}
   1.156 +
   1.157 +Byte CInArchive::ReadByte()
   1.158 +{
   1.159 +  Byte b;
   1.160 +  SafeReadBytes(&b, 1);
   1.161 +  return b;
   1.162 +}
   1.163 +
   1.164 +UInt16 CInArchive::ReadUInt16()
   1.165 +{
   1.166 +  UInt16 value = 0;
   1.167 +  for (int i = 0; i < 2; i++)
   1.168 +    value |= (((UInt16)ReadByte()) << (8 * i));
   1.169 +  return value;
   1.170 +}
   1.171 +
   1.172 +UInt32 CInArchive::ReadUInt32()
   1.173 +{
   1.174 +  UInt32 value = 0;
   1.175 +  for (int i = 0; i < 4; i++)
   1.176 +    value |= (((UInt32)ReadByte()) << (8 * i));
   1.177 +  return value;
   1.178 +}
   1.179 +
   1.180 +UInt64 CInArchive::ReadUInt64()
   1.181 +{
   1.182 +  UInt64 value = 0;
   1.183 +  for (int i = 0; i < 8; i++)
   1.184 +    value |= (((UInt64)ReadByte()) << (8 * i));
   1.185 +  return value;
   1.186 +}
   1.187 +
   1.188 +bool CInArchive::ReadUInt32(UInt32 &value)
   1.189 +{
   1.190 +  value = 0;
   1.191 +  for (int i = 0; i < 4; i++)
   1.192 +  {
   1.193 +    Byte b;
   1.194 +    if (!ReadBytesAndTestSize(&b, 1))
   1.195 +      return false;
   1.196 +    value |= (UInt32(b) << (8 * i));
   1.197 +  }
   1.198 +  return true;
   1.199 +}
   1.200 +
   1.201 +
   1.202 +AString CInArchive::ReadFileName(UInt32 nameSize)
   1.203 +{
   1.204 +  if (nameSize == 0)
   1.205 +    return AString();
   1.206 +  char *p = m_NameBuffer.GetBuffer(nameSize);
   1.207 +  SafeReadBytes(p, nameSize);
   1.208 +  p[nameSize] = 0;
   1.209 +  m_NameBuffer.ReleaseBuffer();
   1.210 +  return m_NameBuffer;
   1.211 +}
   1.212 +
   1.213 +void CInArchive::GetArchiveInfo(CInArchiveInfo &archiveInfo) const
   1.214 +{
   1.215 +  archiveInfo = m_ArchiveInfo;
   1.216 +}
   1.217 +
   1.218 +/*
   1.219 +void CInArchive::ThrowIncorrectArchiveException()
   1.220 +{
   1.221 +  throw CInArchiveException(CInArchiveException::kIncorrectArchive);
   1.222 +}
   1.223 +*/
   1.224 +
   1.225 +static UInt32 GetUInt32(const Byte *data)
   1.226 +{
   1.227 +  return
   1.228 +      ((UInt32)(Byte)data[0]) |
   1.229 +      (((UInt32)(Byte)data[1]) << 8) |
   1.230 +      (((UInt32)(Byte)data[2]) << 16) |
   1.231 +      (((UInt32)(Byte)data[3]) << 24);
   1.232 +}
   1.233 +
   1.234 +/*
   1.235 +static UInt16 GetUInt16(const Byte *data)
   1.236 +{
   1.237 +  return
   1.238 +      ((UInt16)(Byte)data[0]) |
   1.239 +      (((UInt16)(Byte)data[1]) << 8);
   1.240 +}
   1.241 +*/
   1.242 +
   1.243 +static UInt64 GetUInt64(const Byte *data)
   1.244 +{
   1.245 +  return GetUInt32(data) | ((UInt64)GetUInt32(data + 4) << 32);
   1.246 +}
   1.247 +
   1.248 +
   1.249 +
   1.250 +void CInArchive::ReadExtra(UInt32 extraSize, CExtraBlock &extraBlock,
   1.251 +    UInt64 &unpackSize, UInt64 &packSize, UInt64 &localHeaderOffset, UInt32 &diskStartNumber)
   1.252 +{
   1.253 +  extraBlock.Clear();
   1.254 +  UInt32 remain = extraSize;
   1.255 +  while(remain >= 4)
   1.256 +  {
   1.257 +    CExtraSubBlock subBlock;
   1.258 +    subBlock.ID = ReadUInt16();
   1.259 +    UInt32 dataSize = ReadUInt16();
   1.260 +    remain -= 4;
   1.261 +    if (dataSize > remain) // it's bug
   1.262 +      dataSize = remain;
   1.263 +    if (subBlock.ID == NFileHeader::NExtraID::kZip64)
   1.264 +    {
   1.265 +      if (unpackSize == 0xFFFFFFFF)
   1.266 +      {
   1.267 +        if (dataSize < 8)
   1.268 +          break;
   1.269 +        unpackSize = ReadUInt64();
   1.270 +        remain -= 8;
   1.271 +        dataSize -= 8;
   1.272 +      }
   1.273 +      if (packSize == 0xFFFFFFFF)
   1.274 +      {
   1.275 +        if (dataSize < 8)
   1.276 +          break;
   1.277 +        packSize = ReadUInt64();
   1.278 +        remain -= 8;
   1.279 +        dataSize -= 8;
   1.280 +      }
   1.281 +      if (localHeaderOffset == 0xFFFFFFFF)
   1.282 +      {
   1.283 +        if (dataSize < 8)
   1.284 +          break;
   1.285 +        localHeaderOffset = ReadUInt64();
   1.286 +        remain -= 8;
   1.287 +        dataSize -= 8;
   1.288 +      }
   1.289 +      if (diskStartNumber == 0xFFFF)
   1.290 +      {
   1.291 +        if (dataSize < 4)
   1.292 +          break;
   1.293 +        diskStartNumber = ReadUInt32();
   1.294 +        remain -= 4;
   1.295 +        dataSize -= 4;
   1.296 +      }
   1.297 +      for (UInt32 i = 0; i < dataSize; i++)
   1.298 +        ReadByte();
   1.299 +    }
   1.300 +    else
   1.301 +    {
   1.302 +      ReadBuffer(subBlock.Data, dataSize);
   1.303 +      extraBlock.SubBlocks.Add(subBlock);
   1.304 +    }
   1.305 +    remain -= dataSize;
   1.306 +  }
   1.307 +  IncreaseRealPosition(remain);
   1.308 +}
   1.309 +
   1.310 +HRESULT CInArchive::ReadLocalItem(CItemEx &item)
   1.311 +{
   1.312 +  item.ExtractVersion.Version = ReadByte();
   1.313 +  item.ExtractVersion.HostOS = ReadByte();
   1.314 +  item.Flags = ReadUInt16();
   1.315 +  item.CompressionMethod = ReadUInt16();
   1.316 +  item.Time =  ReadUInt32();
   1.317 +  item.FileCRC = ReadUInt32();
   1.318 +  item.PackSize = ReadUInt32();
   1.319 +  item.UnPackSize = ReadUInt32();
   1.320 +  UInt32 fileNameSize = ReadUInt16();
   1.321 +  item.LocalExtraSize = ReadUInt16();
   1.322 +  item.Name = ReadFileName(fileNameSize);
   1.323 +  item.FileHeaderWithNameSize = 4 + NFileHeader::kLocalBlockSize + fileNameSize;
   1.324 +  if (item.LocalExtraSize > 0)
   1.325 +  {
   1.326 +    UInt64 localHeaderOffset = 0;
   1.327 +    UInt32 diskStartNumber = 0;
   1.328 +    ReadExtra(item.LocalExtraSize, item.LocalExtra, item.UnPackSize, item.PackSize,
   1.329 +      localHeaderOffset, diskStartNumber);
   1.330 +  }
   1.331 +  /*
   1.332 +  if (item.IsDir())
   1.333 +    item.UnPackSize = 0;       // check It
   1.334 +  */
   1.335 +  return S_OK;
   1.336 +}
   1.337 +
   1.338 +HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item)
   1.339 +{
   1.340 +  if (item.FromLocal)
   1.341 +    return S_OK;
   1.342 +  try
   1.343 +  {
   1.344 +    RINOK(Seek(m_ArchiveInfo.Base + item.LocalHeaderPosition));
   1.345 +    CItemEx localItem;
   1.346 +    if (ReadUInt32() != NSignature::kLocalFileHeader)
   1.347 +      return S_FALSE;
   1.348 +    RINOK(ReadLocalItem(localItem));
   1.349 +    if (item.Flags != localItem.Flags)
   1.350 +    {
   1.351 +      if (
   1.352 +          (item.CompressionMethod != NFileHeader::NCompressionMethod::kDeflated ||
   1.353 +            (item.Flags & 0x7FF9) != (localItem.Flags & 0x7FF9)) &&
   1.354 +          (item.CompressionMethod != NFileHeader::NCompressionMethod::kStored ||
   1.355 +            (item.Flags & 0x7FFF) != (localItem.Flags & 0x7FFF)) &&
   1.356 +          (item.CompressionMethod != NFileHeader::NCompressionMethod::kImploded ||
   1.357 +            (item.Flags & 0x7FFF) != (localItem.Flags & 0x7FFF))
   1.358 +        )
   1.359 +        return S_FALSE;
   1.360 +    }
   1.361 +
   1.362 +    if (item.CompressionMethod != localItem.CompressionMethod ||
   1.363 +        // item.Time != localItem.Time ||
   1.364 +        (!localItem.HasDescriptor() &&
   1.365 +          (
   1.366 +            item.FileCRC != localItem.FileCRC ||
   1.367 +            item.PackSize != localItem.PackSize ||
   1.368 +            item.UnPackSize != localItem.UnPackSize
   1.369 +          )
   1.370 +        ) ||
   1.371 +        item.Name.Length() != localItem.Name.Length()
   1.372 +        )
   1.373 +      return S_FALSE;
   1.374 +    item.FileHeaderWithNameSize = localItem.FileHeaderWithNameSize;
   1.375 +    item.LocalExtraSize = localItem.LocalExtraSize;
   1.376 +    item.LocalExtra = localItem.LocalExtra;
   1.377 +    item.FromLocal = true;
   1.378 +  }
   1.379 +  catch(...) { return S_FALSE; }
   1.380 +  return S_OK;
   1.381 +}
   1.382 +
   1.383 +HRESULT CInArchive::ReadLocalItemDescriptor(CItemEx &item)
   1.384 +{
   1.385 +  if (item.HasDescriptor())
   1.386 +  {
   1.387 +    const int kBufferSize = (1 << 12);
   1.388 +    Byte buffer[kBufferSize];
   1.389 +    
   1.390 +    UInt32 numBytesInBuffer = 0;
   1.391 +    UInt32 packedSize = 0;
   1.392 +    
   1.393 +    bool descriptorWasFound = false;
   1.394 +    for (;;)
   1.395 +    {
   1.396 +      UInt32 processedSize;
   1.397 +      RINOK(ReadBytes(buffer + numBytesInBuffer, kBufferSize - numBytesInBuffer, &processedSize));
   1.398 +      numBytesInBuffer += processedSize;
   1.399 +      if (numBytesInBuffer < NFileHeader::kDataDescriptorSize)
   1.400 +        return S_FALSE;
   1.401 +      UInt32 i;
   1.402 +      for (i = 0; i <= numBytesInBuffer - NFileHeader::kDataDescriptorSize; i++)
   1.403 +      {
   1.404 +        // descriptorSignature field is Info-ZIP's extension
   1.405 +        // to Zip specification.
   1.406 +        UInt32 descriptorSignature = GetUInt32(buffer + i);
   1.407 +        
   1.408 +        // !!!! It must be fixed for Zip64 archives
   1.409 +        UInt32 descriptorPackSize = GetUInt32(buffer + i + 8);
   1.410 +        if (descriptorSignature== NSignature::kDataDescriptor && descriptorPackSize == packedSize + i)
   1.411 +        {
   1.412 +          descriptorWasFound = true;
   1.413 +          item.FileCRC = GetUInt32(buffer + i + 4);
   1.414 +          item.PackSize = descriptorPackSize;
   1.415 +          item.UnPackSize = GetUInt32(buffer + i + 12);
   1.416 +          IncreaseRealPosition(Int64(Int32(0 - (numBytesInBuffer - i - NFileHeader::kDataDescriptorSize))));
   1.417 +          break;
   1.418 +        }
   1.419 +      }
   1.420 +      if (descriptorWasFound)
   1.421 +        break;
   1.422 +      packedSize += i;
   1.423 +      int j;
   1.424 +      for (j = 0; i < numBytesInBuffer; i++, j++)
   1.425 +        buffer[j] = buffer[i];
   1.426 +      numBytesInBuffer = j;
   1.427 +    }
   1.428 +  }
   1.429 +  else
   1.430 +    IncreaseRealPosition(item.PackSize);
   1.431 +  return S_OK;
   1.432 +}
   1.433 +
   1.434 +HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item)
   1.435 +{
   1.436 +  if (item.FromLocal)
   1.437 +    return S_OK;
   1.438 +  try
   1.439 +  {
   1.440 +    RINOK(ReadLocalItemAfterCdItem(item));
   1.441 +    if (item.HasDescriptor())
   1.442 +    {
   1.443 +      RINOK(Seek(m_ArchiveInfo.Base + item.GetDataPosition() + item.PackSize));
   1.444 +      if (ReadUInt32() != NSignature::kDataDescriptor)
   1.445 +        return S_FALSE;
   1.446 +      UInt32 crc = ReadUInt32();
   1.447 +      UInt64 packSize, unpackSize;
   1.448 +
   1.449 +      /*
   1.450 +      if (IsZip64)
   1.451 +      {
   1.452 +        packSize = ReadUInt64();
   1.453 +        unpackSize = ReadUInt64();
   1.454 +      }
   1.455 +      else
   1.456 +      */
   1.457 +      {
   1.458 +        packSize = ReadUInt32();
   1.459 +        unpackSize = ReadUInt32();
   1.460 +      }
   1.461 +
   1.462 +      if (crc != item.FileCRC || item.PackSize != packSize || item.UnPackSize != unpackSize)
   1.463 +        return S_FALSE;
   1.464 +    }
   1.465 +  }
   1.466 +  catch(...) { return S_FALSE; }
   1.467 +  return S_OK;
   1.468 +}
   1.469 +  
   1.470 +HRESULT CInArchive::ReadCdItem(CItemEx &item)
   1.471 +{
   1.472 +  item.FromCentral = true;
   1.473 +  const int kBufSize = 42;
   1.474 +  Byte p[kBufSize];
   1.475 +  SafeReadBytes(p, kBufSize);
   1.476 +  item.MadeByVersion.Version = p[0];
   1.477 +  item.MadeByVersion.HostOS = p[1];
   1.478 +  item.ExtractVersion.Version = p[2];
   1.479 +  item.ExtractVersion.HostOS = p[3];
   1.480 +  item.Flags = Get16(p + 4);
   1.481 +  item.CompressionMethod = Get16(p + 6);
   1.482 +  item.Time = Get32(p + 8);
   1.483 +  item.FileCRC = Get32(p + 12);
   1.484 +  item.PackSize = Get32(p + 16);
   1.485 +  item.UnPackSize = Get32(p + 20);
   1.486 +  UInt16 headerNameSize = Get16(p + 24);
   1.487 +  UInt16 headerExtraSize = Get16(p + 26);
   1.488 +  UInt16 headerCommentSize = Get16(p + 28);
   1.489 +  UInt32 headerDiskNumberStart = Get16(p + 30);
   1.490 +  item.InternalAttributes = Get16(p + 32);
   1.491 +  item.ExternalAttributes = Get32(p + 34);
   1.492 +  item.LocalHeaderPosition = Get32(p + 38);
   1.493 +  item.Name = ReadFileName(headerNameSize);
   1.494 +  
   1.495 +  if (headerExtraSize > 0)
   1.496 +  {
   1.497 +    ReadExtra(headerExtraSize, item.CentralExtra, item.UnPackSize, item.PackSize,
   1.498 +        item.LocalHeaderPosition, headerDiskNumberStart);
   1.499 +  }
   1.500 +
   1.501 +  if (headerDiskNumberStart != 0)
   1.502 +    throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported);
   1.503 +  
   1.504 +  // May be these strings must be deleted
   1.505 +  /*
   1.506 +  if (item.IsDir())
   1.507 +    item.UnPackSize = 0;
   1.508 +  */
   1.509 +  
   1.510 +  ReadBuffer(item.Comment, headerCommentSize);
   1.511 +  return S_OK;
   1.512 +}
   1.513 +
   1.514 +HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo)
   1.515 +{
   1.516 +  RINOK(Seek(offset));
   1.517 +  const UInt32 kEcd64Size = 56;
   1.518 +  Byte buf[kEcd64Size];
   1.519 +  if (!ReadBytesAndTestSize(buf, kEcd64Size))
   1.520 +    return S_FALSE;
   1.521 +  if (GetUInt32(buf) != NSignature::kZip64EndOfCentralDir)
   1.522 +    return S_FALSE;
   1.523 +  // cdInfo.NumEntries = GetUInt64(buf + 24);
   1.524 +  cdInfo.Size = GetUInt64(buf + 40);
   1.525 +  cdInfo.Offset = GetUInt64(buf + 48);
   1.526 +  return S_OK;
   1.527 +}
   1.528 +
   1.529 +HRESULT CInArchive::FindCd(CCdInfo &cdInfo)
   1.530 +{
   1.531 +  UInt64 endPosition;
   1.532 +  RINOK(m_Stream->Seek(0, STREAM_SEEK_END, &endPosition));
   1.533 +  const UInt32 kBufSizeMax = (1 << 16) + kEcdSize + kZip64EcdLocatorSize;
   1.534 +  Byte buf[kBufSizeMax];
   1.535 +  UInt32 bufSize = (endPosition < kBufSizeMax) ? (UInt32)endPosition : kBufSizeMax;
   1.536 +  if (bufSize < kEcdSize)
   1.537 +    return S_FALSE;
   1.538 +  UInt64 startPosition = endPosition - bufSize;
   1.539 +  RINOK(m_Stream->Seek(startPosition, STREAM_SEEK_SET, &m_Position));
   1.540 +  if (m_Position != startPosition)
   1.541 +    return S_FALSE;
   1.542 +  if (!ReadBytesAndTestSize(buf, bufSize))
   1.543 +    return S_FALSE;
   1.544 +  for (int i = (int)(bufSize - kEcdSize); i >= 0; i--)
   1.545 +  {
   1.546 +    if (GetUInt32(buf + i) == NSignature::kEndOfCentralDir)
   1.547 +    {
   1.548 +      if (i >= kZip64EcdLocatorSize)
   1.549 +      {
   1.550 +        const Byte *locator = buf + i - kZip64EcdLocatorSize;
   1.551 +        if (GetUInt32(locator) == NSignature::kZip64EndOfCentralDirLocator)
   1.552 +        {
   1.553 +          UInt64 ecd64Offset = GetUInt64(locator + 8);
   1.554 +          if (TryEcd64(ecd64Offset, cdInfo) == S_OK)
   1.555 +            return S_OK;
   1.556 +          if (TryEcd64(m_ArchiveInfo.StartPosition + ecd64Offset, cdInfo) == S_OK)
   1.557 +          {
   1.558 +            m_ArchiveInfo.Base = m_ArchiveInfo.StartPosition;
   1.559 +            return S_OK;
   1.560 +          }
   1.561 +        }
   1.562 +      }
   1.563 +      if (GetUInt32(buf + i + 4) == 0)
   1.564 +      {
   1.565 +        // cdInfo.NumEntries = GetUInt16(buf + i + 10);
   1.566 +        cdInfo.Size = GetUInt32(buf + i + 12);
   1.567 +        cdInfo.Offset = GetUInt32(buf + i + 16);
   1.568 +        UInt64 curPos = endPosition - bufSize + i;
   1.569 +        UInt64 cdEnd = cdInfo.Size + cdInfo.Offset;
   1.570 +        if (curPos > cdEnd)
   1.571 +          m_ArchiveInfo.Base = curPos - cdEnd;
   1.572 +        return S_OK;
   1.573 +      }
   1.574 +    }
   1.575 +  }
   1.576 +  return S_FALSE;
   1.577 +}
   1.578 +
   1.579 +HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress)
   1.580 +{
   1.581 +  items.Clear();
   1.582 +  RINOK(m_Stream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position));
   1.583 +  if (m_Position != cdOffset)
   1.584 +    return S_FALSE;
   1.585 +  while(m_Position - cdOffset < cdSize)
   1.586 +  {
   1.587 +    if (ReadUInt32() != NSignature::kCentralFileHeader)
   1.588 +      return S_FALSE;
   1.589 +    CItemEx cdItem;
   1.590 +    RINOK(ReadCdItem(cdItem));
   1.591 +    items.Add(cdItem);
   1.592 +    if (progress && items.Size() % 1000 == 0)
   1.593 +      RINOK(progress->SetCompleted(items.Size()));
   1.594 +  }
   1.595 +  return (m_Position - cdOffset == cdSize) ? S_OK : S_FALSE;
   1.596 +}
   1.597 +
   1.598 +HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress)
   1.599 +{
   1.600 +  m_ArchiveInfo.Base = 0;
   1.601 +  CCdInfo cdInfo;
   1.602 +  RINOK(FindCd(cdInfo));
   1.603 +  HRESULT res = S_FALSE;
   1.604 +  cdSize = cdInfo.Size;
   1.605 +  cdOffset = cdInfo.Offset;
   1.606 +  res = TryReadCd(items, m_ArchiveInfo.Base + cdOffset, cdSize, progress);
   1.607 +  if (res == S_FALSE && m_ArchiveInfo.Base == 0)
   1.608 +  {
   1.609 +    res = TryReadCd(items, cdInfo.Offset + m_ArchiveInfo.StartPosition, cdSize, progress);
   1.610 +    if (res == S_OK)
   1.611 +      m_ArchiveInfo.Base = m_ArchiveInfo.StartPosition;
   1.612 +  }
   1.613 +  if (!ReadUInt32(m_Signature))
   1.614 +    return S_FALSE;
   1.615 +  return res;
   1.616 +}
   1.617 +
   1.618 +HRESULT CInArchive::ReadLocalsAndCd(CObjectVector<CItemEx> &items, CProgressVirt *progress, UInt64 &cdOffset)
   1.619 +{
   1.620 +  items.Clear();
   1.621 +  while (m_Signature == NSignature::kLocalFileHeader)
   1.622 +  {
   1.623 +    // FSeek points to next byte after signature
   1.624 +    // NFileHeader::CLocalBlock localHeader;
   1.625 +    CItemEx item;
   1.626 +    item.LocalHeaderPosition = m_Position - m_StreamStartPosition - 4; // points to signature;
   1.627 +    RINOK(ReadLocalItem(item));
   1.628 +    item.FromLocal = true;
   1.629 +    ReadLocalItemDescriptor(item);
   1.630 +    items.Add(item);
   1.631 +    if (progress && items.Size() % 100 == 0)
   1.632 +      RINOK(progress->SetCompleted(items.Size()));
   1.633 +    if (!ReadUInt32(m_Signature))
   1.634 +      break;
   1.635 +  }
   1.636 +  cdOffset = m_Position - 4;
   1.637 +  for (int i = 0; i < items.Size(); i++)
   1.638 +  {
   1.639 +    if (progress && i % 1000 == 0)
   1.640 +      RINOK(progress->SetCompleted(items.Size()));
   1.641 +    if (m_Signature != NSignature::kCentralFileHeader)
   1.642 +      return S_FALSE;
   1.643 +
   1.644 +    CItemEx cdItem;
   1.645 +    RINOK(ReadCdItem(cdItem));
   1.646 +
   1.647 +    if (i == 0)
   1.648 +    {
   1.649 +      if (cdItem.LocalHeaderPosition == 0)
   1.650 +        m_ArchiveInfo.Base = m_ArchiveInfo.StartPosition;
   1.651 +    }
   1.652 +
   1.653 +    int index;
   1.654 +    int left = 0, right = items.Size();
   1.655 +    for (;;)
   1.656 +    {
   1.657 +      if (left >= right)
   1.658 +        return S_FALSE;
   1.659 +      index = (left + right) / 2;
   1.660 +      UInt64 position = items[index].LocalHeaderPosition - m_ArchiveInfo.Base;
   1.661 +      if (cdItem.LocalHeaderPosition == position)
   1.662 +        break;
   1.663 +      if (cdItem.LocalHeaderPosition < position)
   1.664 +        right = index;
   1.665 +      else
   1.666 +        left = index + 1;
   1.667 +    }
   1.668 +    CItemEx &item = items[index];
   1.669 +    item.LocalHeaderPosition = cdItem.LocalHeaderPosition;
   1.670 +    item.MadeByVersion = cdItem.MadeByVersion;
   1.671 +    item.CentralExtra = cdItem.CentralExtra;
   1.672 +
   1.673 +    if (
   1.674 +        // item.ExtractVersion != cdItem.ExtractVersion ||
   1.675 +        item.Flags != cdItem.Flags ||
   1.676 +        item.CompressionMethod != cdItem.CompressionMethod ||
   1.677 +        // item.Time != cdItem.Time ||
   1.678 +        item.FileCRC != cdItem.FileCRC)
   1.679 +      return S_FALSE;
   1.680 +
   1.681 +    if (item.Name.Length() != cdItem.Name.Length() ||
   1.682 +        item.PackSize != cdItem.PackSize ||
   1.683 +        item.UnPackSize != cdItem.UnPackSize
   1.684 +      )
   1.685 +      return S_FALSE;
   1.686 +    item.Name = cdItem.Name;
   1.687 +    item.InternalAttributes = cdItem.InternalAttributes;
   1.688 +    item.ExternalAttributes = cdItem.ExternalAttributes;
   1.689 +    item.Comment = cdItem.Comment;
   1.690 +    item.FromCentral = cdItem.FromCentral;
   1.691 +    if (!ReadUInt32(m_Signature))
   1.692 +      return S_FALSE;
   1.693 +  }
   1.694 +  return S_OK;
   1.695 +}
   1.696 +
   1.697 +struct CEcd
   1.698 +{
   1.699 +  UInt16 thisDiskNumber;
   1.700 +  UInt16 startCDDiskNumber;
   1.701 +  UInt16 numEntriesInCDOnThisDisk;
   1.702 +  UInt16 numEntriesInCD;
   1.703 +  UInt32 cdSize;
   1.704 +  UInt32 cdStartOffset;
   1.705 +  UInt16 commentSize;
   1.706 +  void Parse(const Byte *p);
   1.707 +};
   1.708 +
   1.709 +void CEcd::Parse(const Byte *p)
   1.710 +{
   1.711 +  thisDiskNumber = Get16(p);
   1.712 +  startCDDiskNumber = Get16(p + 2);
   1.713 +  numEntriesInCDOnThisDisk = Get16(p + 4);
   1.714 +  numEntriesInCD = Get16(p + 6);
   1.715 +  cdSize = Get32(p + 8);
   1.716 +  cdStartOffset = Get32(p + 12);
   1.717 +  commentSize = Get16(p + 16);
   1.718 +}
   1.719 +
   1.720 +struct CEcd64
   1.721 +{
   1.722 +  UInt16 versionMade;
   1.723 +  UInt16 versionNeedExtract;
   1.724 +  UInt32 thisDiskNumber;
   1.725 +  UInt32 startCDDiskNumber;
   1.726 +  UInt64 numEntriesInCDOnThisDisk;
   1.727 +  UInt64 numEntriesInCD;
   1.728 +  UInt64 cdSize;
   1.729 +  UInt64 cdStartOffset;
   1.730 +  void Parse(const Byte *p);
   1.731 +  CEcd64() { memset(this, 0, sizeof(*this)); }
   1.732 +};
   1.733 +
   1.734 +void CEcd64::Parse(const Byte *p)
   1.735 +{
   1.736 +  versionMade = Get16(p);
   1.737 +  versionNeedExtract = Get16(p + 2);
   1.738 +  thisDiskNumber = Get32(p + 4);
   1.739 +  startCDDiskNumber = Get32(p + 8);
   1.740 +  numEntriesInCDOnThisDisk = Get64(p + 12);
   1.741 +  numEntriesInCD = Get64(p + 20);
   1.742 +  cdSize = Get64(p + 28);
   1.743 +  cdStartOffset = Get64(p + 36);
   1.744 +}
   1.745 +
   1.746 +#define COPY_ECD_ITEM_16(n) if (!isZip64 || ecd. n != 0xFFFF)     ecd64. n = ecd. n;
   1.747 +#define COPY_ECD_ITEM_32(n) if (!isZip64 || ecd. n != 0xFFFFFFFF) ecd64. n = ecd. n;
   1.748 +
   1.749 +HRESULT CInArchive::ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *progress)
   1.750 +{
   1.751 +  // m_Signature must be kLocalFileHeaderSignature or
   1.752 +  // kEndOfCentralDirSignature
   1.753 +  // m_Position points to next byte after signature
   1.754 +
   1.755 +  IsZip64 = false;
   1.756 +  items.Clear();
   1.757 +
   1.758 +  UInt64 cdSize, cdStartOffset;
   1.759 +  HRESULT res = ReadCd(items, cdStartOffset, cdSize, progress);
   1.760 +  if (res != S_FALSE && res != S_OK)
   1.761 +    return res;
   1.762 +
   1.763 +  /*
   1.764 +  if (res != S_OK)
   1.765 +    return res;
   1.766 +  res = S_FALSE;
   1.767 +  */
   1.768 +
   1.769 +  if (res == S_FALSE)
   1.770 +  {
   1.771 +    m_ArchiveInfo.Base = 0;
   1.772 +    RINOK(m_Stream->Seek(m_ArchiveInfo.StartPosition, STREAM_SEEK_SET, &m_Position));
   1.773 +    if (m_Position != m_ArchiveInfo.StartPosition)
   1.774 +      return S_FALSE;
   1.775 +    if (!ReadUInt32(m_Signature))
   1.776 +      return S_FALSE;
   1.777 +    RINOK(ReadLocalsAndCd(items, progress, cdStartOffset));
   1.778 +    cdSize = (m_Position - 4) - cdStartOffset;
   1.779 +    cdStartOffset -= m_ArchiveInfo.Base;
   1.780 +  }
   1.781 +
   1.782 +  CEcd64 ecd64;
   1.783 +  bool isZip64 = false;
   1.784 +  UInt64 zip64EcdStartOffset = m_Position - 4 - m_ArchiveInfo.Base;
   1.785 +  if (m_Signature == NSignature::kZip64EndOfCentralDir)
   1.786 +  {
   1.787 +    IsZip64 = isZip64 = true;
   1.788 +    UInt64 recordSize = ReadUInt64();
   1.789 +
   1.790 +    const int kBufSize = kZip64EcdSize;
   1.791 +    Byte buf[kBufSize];
   1.792 +    SafeReadBytes(buf, kBufSize);
   1.793 +    ecd64.Parse(buf);
   1.794 +
   1.795 +    IncreaseRealPosition(recordSize - kZip64EcdSize);
   1.796 +    if (!ReadUInt32(m_Signature))
   1.797 +      return S_FALSE;
   1.798 +    if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0)
   1.799 +      throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported);
   1.800 +    if (ecd64.numEntriesInCDOnThisDisk != items.Size() ||
   1.801 +        ecd64.numEntriesInCD != items.Size() ||
   1.802 +        ecd64.cdSize != cdSize ||
   1.803 +        (ecd64.cdStartOffset != cdStartOffset &&
   1.804 +        (!items.IsEmpty())))
   1.805 +      return S_FALSE;
   1.806 +  }
   1.807 +  if (m_Signature == NSignature::kZip64EndOfCentralDirLocator)
   1.808 +  {
   1.809 +    /* UInt32 startEndCDDiskNumber = */ ReadUInt32();
   1.810 +    UInt64 endCDStartOffset = ReadUInt64();
   1.811 +    /* UInt32 numberOfDisks = */ ReadUInt32();
   1.812 +    if (zip64EcdStartOffset != endCDStartOffset)
   1.813 +      return S_FALSE;
   1.814 +    if (!ReadUInt32(m_Signature))
   1.815 +      return S_FALSE;
   1.816 +  }
   1.817 +  if (m_Signature != NSignature::kEndOfCentralDir)
   1.818 +      return S_FALSE;
   1.819 +
   1.820 +  const int kBufSize = kEcdSize - 4;
   1.821 +  Byte buf[kBufSize];
   1.822 +  SafeReadBytes(buf, kBufSize);
   1.823 +  CEcd ecd;
   1.824 +  ecd.Parse(buf);
   1.825 +
   1.826 +  COPY_ECD_ITEM_16(thisDiskNumber);
   1.827 +  COPY_ECD_ITEM_16(startCDDiskNumber);
   1.828 +  COPY_ECD_ITEM_16(numEntriesInCDOnThisDisk);
   1.829 +  COPY_ECD_ITEM_16(numEntriesInCD);
   1.830 +  COPY_ECD_ITEM_32(cdSize);
   1.831 +  COPY_ECD_ITEM_32(cdStartOffset);
   1.832 +
   1.833 +  ReadBuffer(m_ArchiveInfo.Comment, ecd.commentSize);
   1.834 +
   1.835 +  if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0)
   1.836 +    throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported);
   1.837 +  if ((UInt16)ecd64.numEntriesInCDOnThisDisk != ((UInt16)items.Size()) ||
   1.838 +      (UInt16)ecd64.numEntriesInCD != ((UInt16)items.Size()) ||
   1.839 +      (UInt32)ecd64.cdSize != (UInt32)cdSize ||
   1.840 +      ((UInt32)(ecd64.cdStartOffset) != (UInt32)cdStartOffset &&
   1.841 +        (!items.IsEmpty())))
   1.842 +      return S_FALSE;
   1.843 +  
   1.844 +  return S_OK;
   1.845 +}
   1.846 +
   1.847 +ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 size)
   1.848 +{
   1.849 +  CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
   1.850 +  CMyComPtr<ISequentialInStream> stream(streamSpec);
   1.851 +  SeekInArchive(m_ArchiveInfo.Base + position);
   1.852 +  streamSpec->SetStream(m_Stream);
   1.853 +  streamSpec->Init(size);
   1.854 +  return stream.Detach();
   1.855 +}
   1.856 +
   1.857 +IInStream* CInArchive::CreateStream()
   1.858 +{
   1.859 +  CMyComPtr<IInStream> stream = m_Stream;
   1.860 +  return stream.Detach();
   1.861 +}
   1.862 +
   1.863 +bool CInArchive::SeekInArchive(UInt64 position)
   1.864 +{
   1.865 +  UInt64 newPosition;
   1.866 +  if (m_Stream->Seek(position, STREAM_SEEK_SET, &newPosition) != S_OK)
   1.867 +    return false;
   1.868 +  return (newPosition == position);
   1.869 +}
   1.870 +
   1.871 +}}