annotate 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
rev   line source
rlm@1 1 // Archive/ZipIn.cpp
rlm@1 2
rlm@1 3 #include "StdAfx.h"
rlm@1 4
rlm@1 5 #include "ZipIn.h"
rlm@1 6 #include "Windows/Defs.h"
rlm@1 7 #include "Common/StringConvert.h"
rlm@1 8 #include "Common/DynamicBuffer.h"
rlm@1 9 #include "../../Common/LimitedStreams.h"
rlm@1 10 #include "../../Common/StreamUtils.h"
rlm@1 11
rlm@1 12 extern "C"
rlm@1 13 {
rlm@1 14 #include "../../../../C/CpuArch.h"
rlm@1 15 }
rlm@1 16
rlm@1 17 #define Get16(p) GetUi16(p)
rlm@1 18 #define Get32(p) GetUi32(p)
rlm@1 19 #define Get64(p) GetUi64(p)
rlm@1 20
rlm@1 21 namespace NArchive {
rlm@1 22 namespace NZip {
rlm@1 23
rlm@1 24 // static const char kEndOfString = '\0';
rlm@1 25
rlm@1 26 HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
rlm@1 27 {
rlm@1 28 Close();
rlm@1 29 RINOK(stream->Seek(0, STREAM_SEEK_CUR, &m_StreamStartPosition));
rlm@1 30 m_Position = m_StreamStartPosition;
rlm@1 31 RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit));
rlm@1 32 RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL));
rlm@1 33 m_Stream = stream;
rlm@1 34 return S_OK;
rlm@1 35 }
rlm@1 36
rlm@1 37 void CInArchive::Close()
rlm@1 38 {
rlm@1 39 m_Stream.Release();
rlm@1 40 }
rlm@1 41
rlm@1 42 HRESULT CInArchive::Seek(UInt64 offset)
rlm@1 43 {
rlm@1 44 return m_Stream->Seek(offset, STREAM_SEEK_SET, NULL);
rlm@1 45 }
rlm@1 46
rlm@1 47 //////////////////////////////////////
rlm@1 48 // Markers
rlm@1 49
rlm@1 50 static inline bool TestMarkerCandidate(const Byte *p, UInt32 &value)
rlm@1 51 {
rlm@1 52 value = Get32(p);
rlm@1 53 return
rlm@1 54 (value == NSignature::kLocalFileHeader) ||
rlm@1 55 (value == NSignature::kEndOfCentralDir);
rlm@1 56 }
rlm@1 57
rlm@1 58 static const UInt32 kNumMarkerAddtionalBytes = 2;
rlm@1 59 static inline bool TestMarkerCandidate2(const Byte *p, UInt32 &value)
rlm@1 60 {
rlm@1 61 value = Get32(p);
rlm@1 62 if (value == NSignature::kEndOfCentralDir)
rlm@1 63 return (Get16(p + 4) == 0);
rlm@1 64 return (value == NSignature::kLocalFileHeader && p[4] < 128);
rlm@1 65 }
rlm@1 66
rlm@1 67 HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
rlm@1 68 {
rlm@1 69 m_ArchiveInfo.Clear();
rlm@1 70 m_Position = m_StreamStartPosition;
rlm@1 71
rlm@1 72 Byte marker[NSignature::kMarkerSize];
rlm@1 73 RINOK(ReadStream_FALSE(stream, marker, NSignature::kMarkerSize));
rlm@1 74 m_Position += NSignature::kMarkerSize;
rlm@1 75 if (TestMarkerCandidate(marker, m_Signature))
rlm@1 76 return S_OK;
rlm@1 77
rlm@1 78 CByteDynamicBuffer dynamicBuffer;
rlm@1 79 const UInt32 kSearchMarkerBufferSize = 0x10000;
rlm@1 80 dynamicBuffer.EnsureCapacity(kSearchMarkerBufferSize);
rlm@1 81 Byte *buffer = dynamicBuffer;
rlm@1 82 UInt32 numBytesPrev = NSignature::kMarkerSize - 1;
rlm@1 83 memcpy(buffer, marker + 1, numBytesPrev);
rlm@1 84 UInt64 curTestPos = m_StreamStartPosition + 1;
rlm@1 85 for (;;)
rlm@1 86 {
rlm@1 87 if (searchHeaderSizeLimit != NULL)
rlm@1 88 if (curTestPos - m_StreamStartPosition > *searchHeaderSizeLimit)
rlm@1 89 break;
rlm@1 90 size_t numReadBytes = kSearchMarkerBufferSize - numBytesPrev;
rlm@1 91 RINOK(ReadStream(stream, buffer + numBytesPrev, &numReadBytes));
rlm@1 92 m_Position += numReadBytes;
rlm@1 93 UInt32 numBytesInBuffer = numBytesPrev + (UInt32)numReadBytes;
rlm@1 94 const UInt32 kMarker2Size = NSignature::kMarkerSize + kNumMarkerAddtionalBytes;
rlm@1 95 if (numBytesInBuffer < kMarker2Size)
rlm@1 96 break;
rlm@1 97 UInt32 numTests = numBytesInBuffer - kMarker2Size + 1;
rlm@1 98 for (UInt32 pos = 0; pos < numTests; pos++)
rlm@1 99 {
rlm@1 100 if (buffer[pos] != 0x50)
rlm@1 101 continue;
rlm@1 102 if (TestMarkerCandidate2(buffer + pos, m_Signature))
rlm@1 103 {
rlm@1 104 curTestPos += pos;
rlm@1 105 m_ArchiveInfo.StartPosition = curTestPos;
rlm@1 106 m_Position = curTestPos + NSignature::kMarkerSize;
rlm@1 107 return S_OK;
rlm@1 108 }
rlm@1 109 }
rlm@1 110 curTestPos += numTests;
rlm@1 111 numBytesPrev = numBytesInBuffer - numTests;
rlm@1 112 memmove(buffer, buffer + numTests, numBytesPrev);
rlm@1 113 }
rlm@1 114 return S_FALSE;
rlm@1 115 }
rlm@1 116
rlm@1 117 HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize)
rlm@1 118 {
rlm@1 119 size_t realProcessedSize = size;
rlm@1 120 HRESULT result = ReadStream(m_Stream, data, &realProcessedSize);
rlm@1 121 if (processedSize != NULL)
rlm@1 122 *processedSize = (UInt32)realProcessedSize;
rlm@1 123 m_Position += realProcessedSize;
rlm@1 124 return result;
rlm@1 125 }
rlm@1 126
rlm@1 127 void CInArchive::IncreaseRealPosition(UInt64 addValue)
rlm@1 128 {
rlm@1 129 if (m_Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position) != S_OK)
rlm@1 130 throw CInArchiveException(CInArchiveException::kSeekStreamError);
rlm@1 131 }
rlm@1 132
rlm@1 133 bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size)
rlm@1 134 {
rlm@1 135 UInt32 realProcessedSize;
rlm@1 136 if (ReadBytes(data, size, &realProcessedSize) != S_OK)
rlm@1 137 throw CInArchiveException(CInArchiveException::kReadStreamError);
rlm@1 138 return (realProcessedSize == size);
rlm@1 139 }
rlm@1 140
rlm@1 141 void CInArchive::SafeReadBytes(void *data, UInt32 size)
rlm@1 142 {
rlm@1 143 if (!ReadBytesAndTestSize(data, size))
rlm@1 144 throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive);
rlm@1 145 }
rlm@1 146
rlm@1 147 void CInArchive::ReadBuffer(CByteBuffer &buffer, UInt32 size)
rlm@1 148 {
rlm@1 149 buffer.SetCapacity(size);
rlm@1 150 if (size > 0)
rlm@1 151 SafeReadBytes(buffer, size);
rlm@1 152 }
rlm@1 153
rlm@1 154 Byte CInArchive::ReadByte()
rlm@1 155 {
rlm@1 156 Byte b;
rlm@1 157 SafeReadBytes(&b, 1);
rlm@1 158 return b;
rlm@1 159 }
rlm@1 160
rlm@1 161 UInt16 CInArchive::ReadUInt16()
rlm@1 162 {
rlm@1 163 UInt16 value = 0;
rlm@1 164 for (int i = 0; i < 2; i++)
rlm@1 165 value |= (((UInt16)ReadByte()) << (8 * i));
rlm@1 166 return value;
rlm@1 167 }
rlm@1 168
rlm@1 169 UInt32 CInArchive::ReadUInt32()
rlm@1 170 {
rlm@1 171 UInt32 value = 0;
rlm@1 172 for (int i = 0; i < 4; i++)
rlm@1 173 value |= (((UInt32)ReadByte()) << (8 * i));
rlm@1 174 return value;
rlm@1 175 }
rlm@1 176
rlm@1 177 UInt64 CInArchive::ReadUInt64()
rlm@1 178 {
rlm@1 179 UInt64 value = 0;
rlm@1 180 for (int i = 0; i < 8; i++)
rlm@1 181 value |= (((UInt64)ReadByte()) << (8 * i));
rlm@1 182 return value;
rlm@1 183 }
rlm@1 184
rlm@1 185 bool CInArchive::ReadUInt32(UInt32 &value)
rlm@1 186 {
rlm@1 187 value = 0;
rlm@1 188 for (int i = 0; i < 4; i++)
rlm@1 189 {
rlm@1 190 Byte b;
rlm@1 191 if (!ReadBytesAndTestSize(&b, 1))
rlm@1 192 return false;
rlm@1 193 value |= (UInt32(b) << (8 * i));
rlm@1 194 }
rlm@1 195 return true;
rlm@1 196 }
rlm@1 197
rlm@1 198
rlm@1 199 AString CInArchive::ReadFileName(UInt32 nameSize)
rlm@1 200 {
rlm@1 201 if (nameSize == 0)
rlm@1 202 return AString();
rlm@1 203 char *p = m_NameBuffer.GetBuffer(nameSize);
rlm@1 204 SafeReadBytes(p, nameSize);
rlm@1 205 p[nameSize] = 0;
rlm@1 206 m_NameBuffer.ReleaseBuffer();
rlm@1 207 return m_NameBuffer;
rlm@1 208 }
rlm@1 209
rlm@1 210 void CInArchive::GetArchiveInfo(CInArchiveInfo &archiveInfo) const
rlm@1 211 {
rlm@1 212 archiveInfo = m_ArchiveInfo;
rlm@1 213 }
rlm@1 214
rlm@1 215 /*
rlm@1 216 void CInArchive::ThrowIncorrectArchiveException()
rlm@1 217 {
rlm@1 218 throw CInArchiveException(CInArchiveException::kIncorrectArchive);
rlm@1 219 }
rlm@1 220 */
rlm@1 221
rlm@1 222 static UInt32 GetUInt32(const Byte *data)
rlm@1 223 {
rlm@1 224 return
rlm@1 225 ((UInt32)(Byte)data[0]) |
rlm@1 226 (((UInt32)(Byte)data[1]) << 8) |
rlm@1 227 (((UInt32)(Byte)data[2]) << 16) |
rlm@1 228 (((UInt32)(Byte)data[3]) << 24);
rlm@1 229 }
rlm@1 230
rlm@1 231 /*
rlm@1 232 static UInt16 GetUInt16(const Byte *data)
rlm@1 233 {
rlm@1 234 return
rlm@1 235 ((UInt16)(Byte)data[0]) |
rlm@1 236 (((UInt16)(Byte)data[1]) << 8);
rlm@1 237 }
rlm@1 238 */
rlm@1 239
rlm@1 240 static UInt64 GetUInt64(const Byte *data)
rlm@1 241 {
rlm@1 242 return GetUInt32(data) | ((UInt64)GetUInt32(data + 4) << 32);
rlm@1 243 }
rlm@1 244
rlm@1 245
rlm@1 246
rlm@1 247 void CInArchive::ReadExtra(UInt32 extraSize, CExtraBlock &extraBlock,
rlm@1 248 UInt64 &unpackSize, UInt64 &packSize, UInt64 &localHeaderOffset, UInt32 &diskStartNumber)
rlm@1 249 {
rlm@1 250 extraBlock.Clear();
rlm@1 251 UInt32 remain = extraSize;
rlm@1 252 while(remain >= 4)
rlm@1 253 {
rlm@1 254 CExtraSubBlock subBlock;
rlm@1 255 subBlock.ID = ReadUInt16();
rlm@1 256 UInt32 dataSize = ReadUInt16();
rlm@1 257 remain -= 4;
rlm@1 258 if (dataSize > remain) // it's bug
rlm@1 259 dataSize = remain;
rlm@1 260 if (subBlock.ID == NFileHeader::NExtraID::kZip64)
rlm@1 261 {
rlm@1 262 if (unpackSize == 0xFFFFFFFF)
rlm@1 263 {
rlm@1 264 if (dataSize < 8)
rlm@1 265 break;
rlm@1 266 unpackSize = ReadUInt64();
rlm@1 267 remain -= 8;
rlm@1 268 dataSize -= 8;
rlm@1 269 }
rlm@1 270 if (packSize == 0xFFFFFFFF)
rlm@1 271 {
rlm@1 272 if (dataSize < 8)
rlm@1 273 break;
rlm@1 274 packSize = ReadUInt64();
rlm@1 275 remain -= 8;
rlm@1 276 dataSize -= 8;
rlm@1 277 }
rlm@1 278 if (localHeaderOffset == 0xFFFFFFFF)
rlm@1 279 {
rlm@1 280 if (dataSize < 8)
rlm@1 281 break;
rlm@1 282 localHeaderOffset = ReadUInt64();
rlm@1 283 remain -= 8;
rlm@1 284 dataSize -= 8;
rlm@1 285 }
rlm@1 286 if (diskStartNumber == 0xFFFF)
rlm@1 287 {
rlm@1 288 if (dataSize < 4)
rlm@1 289 break;
rlm@1 290 diskStartNumber = ReadUInt32();
rlm@1 291 remain -= 4;
rlm@1 292 dataSize -= 4;
rlm@1 293 }
rlm@1 294 for (UInt32 i = 0; i < dataSize; i++)
rlm@1 295 ReadByte();
rlm@1 296 }
rlm@1 297 else
rlm@1 298 {
rlm@1 299 ReadBuffer(subBlock.Data, dataSize);
rlm@1 300 extraBlock.SubBlocks.Add(subBlock);
rlm@1 301 }
rlm@1 302 remain -= dataSize;
rlm@1 303 }
rlm@1 304 IncreaseRealPosition(remain);
rlm@1 305 }
rlm@1 306
rlm@1 307 HRESULT CInArchive::ReadLocalItem(CItemEx &item)
rlm@1 308 {
rlm@1 309 item.ExtractVersion.Version = ReadByte();
rlm@1 310 item.ExtractVersion.HostOS = ReadByte();
rlm@1 311 item.Flags = ReadUInt16();
rlm@1 312 item.CompressionMethod = ReadUInt16();
rlm@1 313 item.Time = ReadUInt32();
rlm@1 314 item.FileCRC = ReadUInt32();
rlm@1 315 item.PackSize = ReadUInt32();
rlm@1 316 item.UnPackSize = ReadUInt32();
rlm@1 317 UInt32 fileNameSize = ReadUInt16();
rlm@1 318 item.LocalExtraSize = ReadUInt16();
rlm@1 319 item.Name = ReadFileName(fileNameSize);
rlm@1 320 item.FileHeaderWithNameSize = 4 + NFileHeader::kLocalBlockSize + fileNameSize;
rlm@1 321 if (item.LocalExtraSize > 0)
rlm@1 322 {
rlm@1 323 UInt64 localHeaderOffset = 0;
rlm@1 324 UInt32 diskStartNumber = 0;
rlm@1 325 ReadExtra(item.LocalExtraSize, item.LocalExtra, item.UnPackSize, item.PackSize,
rlm@1 326 localHeaderOffset, diskStartNumber);
rlm@1 327 }
rlm@1 328 /*
rlm@1 329 if (item.IsDir())
rlm@1 330 item.UnPackSize = 0; // check It
rlm@1 331 */
rlm@1 332 return S_OK;
rlm@1 333 }
rlm@1 334
rlm@1 335 HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item)
rlm@1 336 {
rlm@1 337 if (item.FromLocal)
rlm@1 338 return S_OK;
rlm@1 339 try
rlm@1 340 {
rlm@1 341 RINOK(Seek(m_ArchiveInfo.Base + item.LocalHeaderPosition));
rlm@1 342 CItemEx localItem;
rlm@1 343 if (ReadUInt32() != NSignature::kLocalFileHeader)
rlm@1 344 return S_FALSE;
rlm@1 345 RINOK(ReadLocalItem(localItem));
rlm@1 346 if (item.Flags != localItem.Flags)
rlm@1 347 {
rlm@1 348 if (
rlm@1 349 (item.CompressionMethod != NFileHeader::NCompressionMethod::kDeflated ||
rlm@1 350 (item.Flags & 0x7FF9) != (localItem.Flags & 0x7FF9)) &&
rlm@1 351 (item.CompressionMethod != NFileHeader::NCompressionMethod::kStored ||
rlm@1 352 (item.Flags & 0x7FFF) != (localItem.Flags & 0x7FFF)) &&
rlm@1 353 (item.CompressionMethod != NFileHeader::NCompressionMethod::kImploded ||
rlm@1 354 (item.Flags & 0x7FFF) != (localItem.Flags & 0x7FFF))
rlm@1 355 )
rlm@1 356 return S_FALSE;
rlm@1 357 }
rlm@1 358
rlm@1 359 if (item.CompressionMethod != localItem.CompressionMethod ||
rlm@1 360 // item.Time != localItem.Time ||
rlm@1 361 (!localItem.HasDescriptor() &&
rlm@1 362 (
rlm@1 363 item.FileCRC != localItem.FileCRC ||
rlm@1 364 item.PackSize != localItem.PackSize ||
rlm@1 365 item.UnPackSize != localItem.UnPackSize
rlm@1 366 )
rlm@1 367 ) ||
rlm@1 368 item.Name.Length() != localItem.Name.Length()
rlm@1 369 )
rlm@1 370 return S_FALSE;
rlm@1 371 item.FileHeaderWithNameSize = localItem.FileHeaderWithNameSize;
rlm@1 372 item.LocalExtraSize = localItem.LocalExtraSize;
rlm@1 373 item.LocalExtra = localItem.LocalExtra;
rlm@1 374 item.FromLocal = true;
rlm@1 375 }
rlm@1 376 catch(...) { return S_FALSE; }
rlm@1 377 return S_OK;
rlm@1 378 }
rlm@1 379
rlm@1 380 HRESULT CInArchive::ReadLocalItemDescriptor(CItemEx &item)
rlm@1 381 {
rlm@1 382 if (item.HasDescriptor())
rlm@1 383 {
rlm@1 384 const int kBufferSize = (1 << 12);
rlm@1 385 Byte buffer[kBufferSize];
rlm@1 386
rlm@1 387 UInt32 numBytesInBuffer = 0;
rlm@1 388 UInt32 packedSize = 0;
rlm@1 389
rlm@1 390 bool descriptorWasFound = false;
rlm@1 391 for (;;)
rlm@1 392 {
rlm@1 393 UInt32 processedSize;
rlm@1 394 RINOK(ReadBytes(buffer + numBytesInBuffer, kBufferSize - numBytesInBuffer, &processedSize));
rlm@1 395 numBytesInBuffer += processedSize;
rlm@1 396 if (numBytesInBuffer < NFileHeader::kDataDescriptorSize)
rlm@1 397 return S_FALSE;
rlm@1 398 UInt32 i;
rlm@1 399 for (i = 0; i <= numBytesInBuffer - NFileHeader::kDataDescriptorSize; i++)
rlm@1 400 {
rlm@1 401 // descriptorSignature field is Info-ZIP's extension
rlm@1 402 // to Zip specification.
rlm@1 403 UInt32 descriptorSignature = GetUInt32(buffer + i);
rlm@1 404
rlm@1 405 // !!!! It must be fixed for Zip64 archives
rlm@1 406 UInt32 descriptorPackSize = GetUInt32(buffer + i + 8);
rlm@1 407 if (descriptorSignature== NSignature::kDataDescriptor && descriptorPackSize == packedSize + i)
rlm@1 408 {
rlm@1 409 descriptorWasFound = true;
rlm@1 410 item.FileCRC = GetUInt32(buffer + i + 4);
rlm@1 411 item.PackSize = descriptorPackSize;
rlm@1 412 item.UnPackSize = GetUInt32(buffer + i + 12);
rlm@1 413 IncreaseRealPosition(Int64(Int32(0 - (numBytesInBuffer - i - NFileHeader::kDataDescriptorSize))));
rlm@1 414 break;
rlm@1 415 }
rlm@1 416 }
rlm@1 417 if (descriptorWasFound)
rlm@1 418 break;
rlm@1 419 packedSize += i;
rlm@1 420 int j;
rlm@1 421 for (j = 0; i < numBytesInBuffer; i++, j++)
rlm@1 422 buffer[j] = buffer[i];
rlm@1 423 numBytesInBuffer = j;
rlm@1 424 }
rlm@1 425 }
rlm@1 426 else
rlm@1 427 IncreaseRealPosition(item.PackSize);
rlm@1 428 return S_OK;
rlm@1 429 }
rlm@1 430
rlm@1 431 HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item)
rlm@1 432 {
rlm@1 433 if (item.FromLocal)
rlm@1 434 return S_OK;
rlm@1 435 try
rlm@1 436 {
rlm@1 437 RINOK(ReadLocalItemAfterCdItem(item));
rlm@1 438 if (item.HasDescriptor())
rlm@1 439 {
rlm@1 440 RINOK(Seek(m_ArchiveInfo.Base + item.GetDataPosition() + item.PackSize));
rlm@1 441 if (ReadUInt32() != NSignature::kDataDescriptor)
rlm@1 442 return S_FALSE;
rlm@1 443 UInt32 crc = ReadUInt32();
rlm@1 444 UInt64 packSize, unpackSize;
rlm@1 445
rlm@1 446 /*
rlm@1 447 if (IsZip64)
rlm@1 448 {
rlm@1 449 packSize = ReadUInt64();
rlm@1 450 unpackSize = ReadUInt64();
rlm@1 451 }
rlm@1 452 else
rlm@1 453 */
rlm@1 454 {
rlm@1 455 packSize = ReadUInt32();
rlm@1 456 unpackSize = ReadUInt32();
rlm@1 457 }
rlm@1 458
rlm@1 459 if (crc != item.FileCRC || item.PackSize != packSize || item.UnPackSize != unpackSize)
rlm@1 460 return S_FALSE;
rlm@1 461 }
rlm@1 462 }
rlm@1 463 catch(...) { return S_FALSE; }
rlm@1 464 return S_OK;
rlm@1 465 }
rlm@1 466
rlm@1 467 HRESULT CInArchive::ReadCdItem(CItemEx &item)
rlm@1 468 {
rlm@1 469 item.FromCentral = true;
rlm@1 470 const int kBufSize = 42;
rlm@1 471 Byte p[kBufSize];
rlm@1 472 SafeReadBytes(p, kBufSize);
rlm@1 473 item.MadeByVersion.Version = p[0];
rlm@1 474 item.MadeByVersion.HostOS = p[1];
rlm@1 475 item.ExtractVersion.Version = p[2];
rlm@1 476 item.ExtractVersion.HostOS = p[3];
rlm@1 477 item.Flags = Get16(p + 4);
rlm@1 478 item.CompressionMethod = Get16(p + 6);
rlm@1 479 item.Time = Get32(p + 8);
rlm@1 480 item.FileCRC = Get32(p + 12);
rlm@1 481 item.PackSize = Get32(p + 16);
rlm@1 482 item.UnPackSize = Get32(p + 20);
rlm@1 483 UInt16 headerNameSize = Get16(p + 24);
rlm@1 484 UInt16 headerExtraSize = Get16(p + 26);
rlm@1 485 UInt16 headerCommentSize = Get16(p + 28);
rlm@1 486 UInt32 headerDiskNumberStart = Get16(p + 30);
rlm@1 487 item.InternalAttributes = Get16(p + 32);
rlm@1 488 item.ExternalAttributes = Get32(p + 34);
rlm@1 489 item.LocalHeaderPosition = Get32(p + 38);
rlm@1 490 item.Name = ReadFileName(headerNameSize);
rlm@1 491
rlm@1 492 if (headerExtraSize > 0)
rlm@1 493 {
rlm@1 494 ReadExtra(headerExtraSize, item.CentralExtra, item.UnPackSize, item.PackSize,
rlm@1 495 item.LocalHeaderPosition, headerDiskNumberStart);
rlm@1 496 }
rlm@1 497
rlm@1 498 if (headerDiskNumberStart != 0)
rlm@1 499 throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported);
rlm@1 500
rlm@1 501 // May be these strings must be deleted
rlm@1 502 /*
rlm@1 503 if (item.IsDir())
rlm@1 504 item.UnPackSize = 0;
rlm@1 505 */
rlm@1 506
rlm@1 507 ReadBuffer(item.Comment, headerCommentSize);
rlm@1 508 return S_OK;
rlm@1 509 }
rlm@1 510
rlm@1 511 HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo)
rlm@1 512 {
rlm@1 513 RINOK(Seek(offset));
rlm@1 514 const UInt32 kEcd64Size = 56;
rlm@1 515 Byte buf[kEcd64Size];
rlm@1 516 if (!ReadBytesAndTestSize(buf, kEcd64Size))
rlm@1 517 return S_FALSE;
rlm@1 518 if (GetUInt32(buf) != NSignature::kZip64EndOfCentralDir)
rlm@1 519 return S_FALSE;
rlm@1 520 // cdInfo.NumEntries = GetUInt64(buf + 24);
rlm@1 521 cdInfo.Size = GetUInt64(buf + 40);
rlm@1 522 cdInfo.Offset = GetUInt64(buf + 48);
rlm@1 523 return S_OK;
rlm@1 524 }
rlm@1 525
rlm@1 526 HRESULT CInArchive::FindCd(CCdInfo &cdInfo)
rlm@1 527 {
rlm@1 528 UInt64 endPosition;
rlm@1 529 RINOK(m_Stream->Seek(0, STREAM_SEEK_END, &endPosition));
rlm@1 530 const UInt32 kBufSizeMax = (1 << 16) + kEcdSize + kZip64EcdLocatorSize;
rlm@1 531 Byte buf[kBufSizeMax];
rlm@1 532 UInt32 bufSize = (endPosition < kBufSizeMax) ? (UInt32)endPosition : kBufSizeMax;
rlm@1 533 if (bufSize < kEcdSize)
rlm@1 534 return S_FALSE;
rlm@1 535 UInt64 startPosition = endPosition - bufSize;
rlm@1 536 RINOK(m_Stream->Seek(startPosition, STREAM_SEEK_SET, &m_Position));
rlm@1 537 if (m_Position != startPosition)
rlm@1 538 return S_FALSE;
rlm@1 539 if (!ReadBytesAndTestSize(buf, bufSize))
rlm@1 540 return S_FALSE;
rlm@1 541 for (int i = (int)(bufSize - kEcdSize); i >= 0; i--)
rlm@1 542 {
rlm@1 543 if (GetUInt32(buf + i) == NSignature::kEndOfCentralDir)
rlm@1 544 {
rlm@1 545 if (i >= kZip64EcdLocatorSize)
rlm@1 546 {
rlm@1 547 const Byte *locator = buf + i - kZip64EcdLocatorSize;
rlm@1 548 if (GetUInt32(locator) == NSignature::kZip64EndOfCentralDirLocator)
rlm@1 549 {
rlm@1 550 UInt64 ecd64Offset = GetUInt64(locator + 8);
rlm@1 551 if (TryEcd64(ecd64Offset, cdInfo) == S_OK)
rlm@1 552 return S_OK;
rlm@1 553 if (TryEcd64(m_ArchiveInfo.StartPosition + ecd64Offset, cdInfo) == S_OK)
rlm@1 554 {
rlm@1 555 m_ArchiveInfo.Base = m_ArchiveInfo.StartPosition;
rlm@1 556 return S_OK;
rlm@1 557 }
rlm@1 558 }
rlm@1 559 }
rlm@1 560 if (GetUInt32(buf + i + 4) == 0)
rlm@1 561 {
rlm@1 562 // cdInfo.NumEntries = GetUInt16(buf + i + 10);
rlm@1 563 cdInfo.Size = GetUInt32(buf + i + 12);
rlm@1 564 cdInfo.Offset = GetUInt32(buf + i + 16);
rlm@1 565 UInt64 curPos = endPosition - bufSize + i;
rlm@1 566 UInt64 cdEnd = cdInfo.Size + cdInfo.Offset;
rlm@1 567 if (curPos > cdEnd)
rlm@1 568 m_ArchiveInfo.Base = curPos - cdEnd;
rlm@1 569 return S_OK;
rlm@1 570 }
rlm@1 571 }
rlm@1 572 }
rlm@1 573 return S_FALSE;
rlm@1 574 }
rlm@1 575
rlm@1 576 HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress)
rlm@1 577 {
rlm@1 578 items.Clear();
rlm@1 579 RINOK(m_Stream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position));
rlm@1 580 if (m_Position != cdOffset)
rlm@1 581 return S_FALSE;
rlm@1 582 while(m_Position - cdOffset < cdSize)
rlm@1 583 {
rlm@1 584 if (ReadUInt32() != NSignature::kCentralFileHeader)
rlm@1 585 return S_FALSE;
rlm@1 586 CItemEx cdItem;
rlm@1 587 RINOK(ReadCdItem(cdItem));
rlm@1 588 items.Add(cdItem);
rlm@1 589 if (progress && items.Size() % 1000 == 0)
rlm@1 590 RINOK(progress->SetCompleted(items.Size()));
rlm@1 591 }
rlm@1 592 return (m_Position - cdOffset == cdSize) ? S_OK : S_FALSE;
rlm@1 593 }
rlm@1 594
rlm@1 595 HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress)
rlm@1 596 {
rlm@1 597 m_ArchiveInfo.Base = 0;
rlm@1 598 CCdInfo cdInfo;
rlm@1 599 RINOK(FindCd(cdInfo));
rlm@1 600 HRESULT res = S_FALSE;
rlm@1 601 cdSize = cdInfo.Size;
rlm@1 602 cdOffset = cdInfo.Offset;
rlm@1 603 res = TryReadCd(items, m_ArchiveInfo.Base + cdOffset, cdSize, progress);
rlm@1 604 if (res == S_FALSE && m_ArchiveInfo.Base == 0)
rlm@1 605 {
rlm@1 606 res = TryReadCd(items, cdInfo.Offset + m_ArchiveInfo.StartPosition, cdSize, progress);
rlm@1 607 if (res == S_OK)
rlm@1 608 m_ArchiveInfo.Base = m_ArchiveInfo.StartPosition;
rlm@1 609 }
rlm@1 610 if (!ReadUInt32(m_Signature))
rlm@1 611 return S_FALSE;
rlm@1 612 return res;
rlm@1 613 }
rlm@1 614
rlm@1 615 HRESULT CInArchive::ReadLocalsAndCd(CObjectVector<CItemEx> &items, CProgressVirt *progress, UInt64 &cdOffset)
rlm@1 616 {
rlm@1 617 items.Clear();
rlm@1 618 while (m_Signature == NSignature::kLocalFileHeader)
rlm@1 619 {
rlm@1 620 // FSeek points to next byte after signature
rlm@1 621 // NFileHeader::CLocalBlock localHeader;
rlm@1 622 CItemEx item;
rlm@1 623 item.LocalHeaderPosition = m_Position - m_StreamStartPosition - 4; // points to signature;
rlm@1 624 RINOK(ReadLocalItem(item));
rlm@1 625 item.FromLocal = true;
rlm@1 626 ReadLocalItemDescriptor(item);
rlm@1 627 items.Add(item);
rlm@1 628 if (progress && items.Size() % 100 == 0)
rlm@1 629 RINOK(progress->SetCompleted(items.Size()));
rlm@1 630 if (!ReadUInt32(m_Signature))
rlm@1 631 break;
rlm@1 632 }
rlm@1 633 cdOffset = m_Position - 4;
rlm@1 634 for (int i = 0; i < items.Size(); i++)
rlm@1 635 {
rlm@1 636 if (progress && i % 1000 == 0)
rlm@1 637 RINOK(progress->SetCompleted(items.Size()));
rlm@1 638 if (m_Signature != NSignature::kCentralFileHeader)
rlm@1 639 return S_FALSE;
rlm@1 640
rlm@1 641 CItemEx cdItem;
rlm@1 642 RINOK(ReadCdItem(cdItem));
rlm@1 643
rlm@1 644 if (i == 0)
rlm@1 645 {
rlm@1 646 if (cdItem.LocalHeaderPosition == 0)
rlm@1 647 m_ArchiveInfo.Base = m_ArchiveInfo.StartPosition;
rlm@1 648 }
rlm@1 649
rlm@1 650 int index;
rlm@1 651 int left = 0, right = items.Size();
rlm@1 652 for (;;)
rlm@1 653 {
rlm@1 654 if (left >= right)
rlm@1 655 return S_FALSE;
rlm@1 656 index = (left + right) / 2;
rlm@1 657 UInt64 position = items[index].LocalHeaderPosition - m_ArchiveInfo.Base;
rlm@1 658 if (cdItem.LocalHeaderPosition == position)
rlm@1 659 break;
rlm@1 660 if (cdItem.LocalHeaderPosition < position)
rlm@1 661 right = index;
rlm@1 662 else
rlm@1 663 left = index + 1;
rlm@1 664 }
rlm@1 665 CItemEx &item = items[index];
rlm@1 666 item.LocalHeaderPosition = cdItem.LocalHeaderPosition;
rlm@1 667 item.MadeByVersion = cdItem.MadeByVersion;
rlm@1 668 item.CentralExtra = cdItem.CentralExtra;
rlm@1 669
rlm@1 670 if (
rlm@1 671 // item.ExtractVersion != cdItem.ExtractVersion ||
rlm@1 672 item.Flags != cdItem.Flags ||
rlm@1 673 item.CompressionMethod != cdItem.CompressionMethod ||
rlm@1 674 // item.Time != cdItem.Time ||
rlm@1 675 item.FileCRC != cdItem.FileCRC)
rlm@1 676 return S_FALSE;
rlm@1 677
rlm@1 678 if (item.Name.Length() != cdItem.Name.Length() ||
rlm@1 679 item.PackSize != cdItem.PackSize ||
rlm@1 680 item.UnPackSize != cdItem.UnPackSize
rlm@1 681 )
rlm@1 682 return S_FALSE;
rlm@1 683 item.Name = cdItem.Name;
rlm@1 684 item.InternalAttributes = cdItem.InternalAttributes;
rlm@1 685 item.ExternalAttributes = cdItem.ExternalAttributes;
rlm@1 686 item.Comment = cdItem.Comment;
rlm@1 687 item.FromCentral = cdItem.FromCentral;
rlm@1 688 if (!ReadUInt32(m_Signature))
rlm@1 689 return S_FALSE;
rlm@1 690 }
rlm@1 691 return S_OK;
rlm@1 692 }
rlm@1 693
rlm@1 694 struct CEcd
rlm@1 695 {
rlm@1 696 UInt16 thisDiskNumber;
rlm@1 697 UInt16 startCDDiskNumber;
rlm@1 698 UInt16 numEntriesInCDOnThisDisk;
rlm@1 699 UInt16 numEntriesInCD;
rlm@1 700 UInt32 cdSize;
rlm@1 701 UInt32 cdStartOffset;
rlm@1 702 UInt16 commentSize;
rlm@1 703 void Parse(const Byte *p);
rlm@1 704 };
rlm@1 705
rlm@1 706 void CEcd::Parse(const Byte *p)
rlm@1 707 {
rlm@1 708 thisDiskNumber = Get16(p);
rlm@1 709 startCDDiskNumber = Get16(p + 2);
rlm@1 710 numEntriesInCDOnThisDisk = Get16(p + 4);
rlm@1 711 numEntriesInCD = Get16(p + 6);
rlm@1 712 cdSize = Get32(p + 8);
rlm@1 713 cdStartOffset = Get32(p + 12);
rlm@1 714 commentSize = Get16(p + 16);
rlm@1 715 }
rlm@1 716
rlm@1 717 struct CEcd64
rlm@1 718 {
rlm@1 719 UInt16 versionMade;
rlm@1 720 UInt16 versionNeedExtract;
rlm@1 721 UInt32 thisDiskNumber;
rlm@1 722 UInt32 startCDDiskNumber;
rlm@1 723 UInt64 numEntriesInCDOnThisDisk;
rlm@1 724 UInt64 numEntriesInCD;
rlm@1 725 UInt64 cdSize;
rlm@1 726 UInt64 cdStartOffset;
rlm@1 727 void Parse(const Byte *p);
rlm@1 728 CEcd64() { memset(this, 0, sizeof(*this)); }
rlm@1 729 };
rlm@1 730
rlm@1 731 void CEcd64::Parse(const Byte *p)
rlm@1 732 {
rlm@1 733 versionMade = Get16(p);
rlm@1 734 versionNeedExtract = Get16(p + 2);
rlm@1 735 thisDiskNumber = Get32(p + 4);
rlm@1 736 startCDDiskNumber = Get32(p + 8);
rlm@1 737 numEntriesInCDOnThisDisk = Get64(p + 12);
rlm@1 738 numEntriesInCD = Get64(p + 20);
rlm@1 739 cdSize = Get64(p + 28);
rlm@1 740 cdStartOffset = Get64(p + 36);
rlm@1 741 }
rlm@1 742
rlm@1 743 #define COPY_ECD_ITEM_16(n) if (!isZip64 || ecd. n != 0xFFFF) ecd64. n = ecd. n;
rlm@1 744 #define COPY_ECD_ITEM_32(n) if (!isZip64 || ecd. n != 0xFFFFFFFF) ecd64. n = ecd. n;
rlm@1 745
rlm@1 746 HRESULT CInArchive::ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *progress)
rlm@1 747 {
rlm@1 748 // m_Signature must be kLocalFileHeaderSignature or
rlm@1 749 // kEndOfCentralDirSignature
rlm@1 750 // m_Position points to next byte after signature
rlm@1 751
rlm@1 752 IsZip64 = false;
rlm@1 753 items.Clear();
rlm@1 754
rlm@1 755 UInt64 cdSize, cdStartOffset;
rlm@1 756 HRESULT res = ReadCd(items, cdStartOffset, cdSize, progress);
rlm@1 757 if (res != S_FALSE && res != S_OK)
rlm@1 758 return res;
rlm@1 759
rlm@1 760 /*
rlm@1 761 if (res != S_OK)
rlm@1 762 return res;
rlm@1 763 res = S_FALSE;
rlm@1 764 */
rlm@1 765
rlm@1 766 if (res == S_FALSE)
rlm@1 767 {
rlm@1 768 m_ArchiveInfo.Base = 0;
rlm@1 769 RINOK(m_Stream->Seek(m_ArchiveInfo.StartPosition, STREAM_SEEK_SET, &m_Position));
rlm@1 770 if (m_Position != m_ArchiveInfo.StartPosition)
rlm@1 771 return S_FALSE;
rlm@1 772 if (!ReadUInt32(m_Signature))
rlm@1 773 return S_FALSE;
rlm@1 774 RINOK(ReadLocalsAndCd(items, progress, cdStartOffset));
rlm@1 775 cdSize = (m_Position - 4) - cdStartOffset;
rlm@1 776 cdStartOffset -= m_ArchiveInfo.Base;
rlm@1 777 }
rlm@1 778
rlm@1 779 CEcd64 ecd64;
rlm@1 780 bool isZip64 = false;
rlm@1 781 UInt64 zip64EcdStartOffset = m_Position - 4 - m_ArchiveInfo.Base;
rlm@1 782 if (m_Signature == NSignature::kZip64EndOfCentralDir)
rlm@1 783 {
rlm@1 784 IsZip64 = isZip64 = true;
rlm@1 785 UInt64 recordSize = ReadUInt64();
rlm@1 786
rlm@1 787 const int kBufSize = kZip64EcdSize;
rlm@1 788 Byte buf[kBufSize];
rlm@1 789 SafeReadBytes(buf, kBufSize);
rlm@1 790 ecd64.Parse(buf);
rlm@1 791
rlm@1 792 IncreaseRealPosition(recordSize - kZip64EcdSize);
rlm@1 793 if (!ReadUInt32(m_Signature))
rlm@1 794 return S_FALSE;
rlm@1 795 if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0)
rlm@1 796 throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported);
rlm@1 797 if (ecd64.numEntriesInCDOnThisDisk != items.Size() ||
rlm@1 798 ecd64.numEntriesInCD != items.Size() ||
rlm@1 799 ecd64.cdSize != cdSize ||
rlm@1 800 (ecd64.cdStartOffset != cdStartOffset &&
rlm@1 801 (!items.IsEmpty())))
rlm@1 802 return S_FALSE;
rlm@1 803 }
rlm@1 804 if (m_Signature == NSignature::kZip64EndOfCentralDirLocator)
rlm@1 805 {
rlm@1 806 /* UInt32 startEndCDDiskNumber = */ ReadUInt32();
rlm@1 807 UInt64 endCDStartOffset = ReadUInt64();
rlm@1 808 /* UInt32 numberOfDisks = */ ReadUInt32();
rlm@1 809 if (zip64EcdStartOffset != endCDStartOffset)
rlm@1 810 return S_FALSE;
rlm@1 811 if (!ReadUInt32(m_Signature))
rlm@1 812 return S_FALSE;
rlm@1 813 }
rlm@1 814 if (m_Signature != NSignature::kEndOfCentralDir)
rlm@1 815 return S_FALSE;
rlm@1 816
rlm@1 817 const int kBufSize = kEcdSize - 4;
rlm@1 818 Byte buf[kBufSize];
rlm@1 819 SafeReadBytes(buf, kBufSize);
rlm@1 820 CEcd ecd;
rlm@1 821 ecd.Parse(buf);
rlm@1 822
rlm@1 823 COPY_ECD_ITEM_16(thisDiskNumber);
rlm@1 824 COPY_ECD_ITEM_16(startCDDiskNumber);
rlm@1 825 COPY_ECD_ITEM_16(numEntriesInCDOnThisDisk);
rlm@1 826 COPY_ECD_ITEM_16(numEntriesInCD);
rlm@1 827 COPY_ECD_ITEM_32(cdSize);
rlm@1 828 COPY_ECD_ITEM_32(cdStartOffset);
rlm@1 829
rlm@1 830 ReadBuffer(m_ArchiveInfo.Comment, ecd.commentSize);
rlm@1 831
rlm@1 832 if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0)
rlm@1 833 throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported);
rlm@1 834 if ((UInt16)ecd64.numEntriesInCDOnThisDisk != ((UInt16)items.Size()) ||
rlm@1 835 (UInt16)ecd64.numEntriesInCD != ((UInt16)items.Size()) ||
rlm@1 836 (UInt32)ecd64.cdSize != (UInt32)cdSize ||
rlm@1 837 ((UInt32)(ecd64.cdStartOffset) != (UInt32)cdStartOffset &&
rlm@1 838 (!items.IsEmpty())))
rlm@1 839 return S_FALSE;
rlm@1 840
rlm@1 841 return S_OK;
rlm@1 842 }
rlm@1 843
rlm@1 844 ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 size)
rlm@1 845 {
rlm@1 846 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
rlm@1 847 CMyComPtr<ISequentialInStream> stream(streamSpec);
rlm@1 848 SeekInArchive(m_ArchiveInfo.Base + position);
rlm@1 849 streamSpec->SetStream(m_Stream);
rlm@1 850 streamSpec->Init(size);
rlm@1 851 return stream.Detach();
rlm@1 852 }
rlm@1 853
rlm@1 854 IInStream* CInArchive::CreateStream()
rlm@1 855 {
rlm@1 856 CMyComPtr<IInStream> stream = m_Stream;
rlm@1 857 return stream.Detach();
rlm@1 858 }
rlm@1 859
rlm@1 860 bool CInArchive::SeekInArchive(UInt64 position)
rlm@1 861 {
rlm@1 862 UInt64 newPosition;
rlm@1 863 if (m_Stream->Seek(position, STREAM_SEEK_SET, &newPosition) != S_OK)
rlm@1 864 return false;
rlm@1 865 return (newPosition == position);
rlm@1 866 }
rlm@1 867
rlm@1 868 }}