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