Upload 2.0.2
[physicsfs] / lzma / CPP / 7zip / Archive / 7z / 7zIn.cpp
1 // 7zIn.cpp
2
3 #include "StdAfx.h"
4
5 #include "7zIn.h"
6 #include "7zDecode.h"
7 #include "../../Common/StreamObjects.h"
8 #include "../../Common/StreamUtils.h"
9 extern "C" 
10
11 #include "../../../../C/7zCrc.h"
12 }
13
14 // define FORMAT_7Z_RECOVERY if you want to recover multivolume archives with empty StartHeader 
15 #ifndef _SFX
16 #define FORMAT_7Z_RECOVERY
17 #endif
18
19 namespace NArchive {
20 namespace N7z {
21
22 class CInArchiveException {};
23
24 static void ThrowException() { throw CInArchiveException(); }
25 static inline void ThrowEndOfData()   { ThrowException(); }
26 static inline void ThrowUnsupported() { ThrowException(); }
27 static inline void ThrowIncorrect()   { ThrowException(); }
28 static inline void ThrowUnsupportedVersion() { ThrowException(); }
29
30 /*
31 class CInArchiveException
32 {
33 public:
34   enum CCauseType
35   {
36     kUnsupportedVersion = 0,
37     kUnsupported,
38     kIncorrect, 
39     kEndOfData,
40   } Cause;
41   CInArchiveException(CCauseType cause): Cause(cause) {};
42 };
43
44 static void ThrowException(CInArchiveException::CCauseType c) { throw CInArchiveException(c); }
45 static void ThrowEndOfData()   { ThrowException(CInArchiveException::kEndOfData); }
46 static void ThrowUnsupported() { ThrowException(CInArchiveException::kUnsupported); }
47 static void ThrowIncorrect()   { ThrowException(CInArchiveException::kIncorrect); }
48 static void ThrowUnsupportedVersion() { ThrowException(CInArchiveException::kUnsupportedVersion); }
49 */
50
51 class CStreamSwitch
52 {
53   CInArchive *_archive;
54   bool _needRemove;
55 public:
56   CStreamSwitch(): _needRemove(false) {}
57   ~CStreamSwitch() { Remove(); }
58   void Remove();
59   void Set(CInArchive *archive, const Byte *data, size_t size);
60   void Set(CInArchive *archive, const CByteBuffer &byteBuffer);
61   void Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector);
62 };
63
64 void CStreamSwitch::Remove()
65 {
66   if (_needRemove)
67   {
68     _archive->DeleteByteStream();
69     _needRemove = false;
70   }
71 }
72
73 void CStreamSwitch::Set(CInArchive *archive, const Byte *data, size_t size)
74 {
75   Remove();
76   _archive = archive;
77   _archive->AddByteStream(data, size);
78   _needRemove = true;
79 }
80
81 void CStreamSwitch::Set(CInArchive *archive, const CByteBuffer &byteBuffer)
82 {
83   Set(archive, byteBuffer, byteBuffer.GetCapacity());
84 }
85
86 void CStreamSwitch::Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector)
87 {
88   Remove();
89   Byte external = archive->ReadByte();
90   if (external != 0)
91   {
92     int dataIndex = (int)archive->ReadNum();
93     if (dataIndex < 0 || dataIndex >= dataVector->Size())
94       ThrowIncorrect();
95     Set(archive, (*dataVector)[dataIndex]);
96   }
97 }
98
99 #if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || defined(__i386__) || defined(__x86_64__)
100 #define SZ_LITTLE_ENDIAN_UNALIGN
101 #endif
102
103 #ifdef SZ_LITTLE_ENDIAN_UNALIGN
104 static inline UInt16 GetUInt16FromMem(const Byte *p) { return *(const UInt16 *)p; }
105 static inline UInt32 GetUInt32FromMem(const Byte *p) { return *(const UInt32 *)p; }
106 static inline UInt64 GetUInt64FromMem(const Byte *p) { return *(const UInt64 *)p; }
107 #else
108 static inline UInt16 GetUInt16FromMem(const Byte *p) { return p[0] | ((UInt16)p[1] << 8); }
109 static inline UInt32 GetUInt32FromMem(const Byte *p) { return p[0] | ((UInt32)p[1] << 8) | ((UInt32)p[2] << 16) | ((UInt32)p[3] << 24); }
110 static inline UInt64 GetUInt64FromMem(const Byte *p) { return GetUInt32FromMem(p) | ((UInt64)GetUInt32FromMem(p + 4) << 32); }
111 #endif
112
113 Byte CInByte2::ReadByte()
114 {
115   if (_pos >= _size)
116     ThrowEndOfData();
117   return _buffer[_pos++];
118 }
119
120 void CInByte2::ReadBytes(Byte *data, size_t size)
121 {
122   if (size > _size - _pos)
123     ThrowEndOfData();
124   for (size_t i = 0; i < size; i++)
125     data[i] = _buffer[_pos++];
126 }
127
128 void CInByte2::SkeepData(UInt64 size)
129 {
130   if (size > _size - _pos)
131     ThrowEndOfData();
132 }
133
134 void CInByte2::SkeepData()
135 {
136   SkeepData(ReadNumber());
137 }
138
139 UInt64 CInByte2::ReadNumber()
140 {
141   if (_pos >= _size)
142     ThrowEndOfData();
143   Byte firstByte = _buffer[_pos++];
144   Byte mask = 0x80;
145   UInt64 value = 0;
146   for (int i = 0; i < 8; i++)
147   {
148     if ((firstByte & mask) == 0)
149     {
150       UInt64 highPart = firstByte & (mask - 1);
151       value += (highPart << (i * 8));
152       return value;
153     }
154     if (_pos >= _size)
155       ThrowEndOfData();
156     value |= ((UInt64)_buffer[_pos++] << (8 * i));
157     mask >>= 1;
158   }
159   return value;
160 }
161
162 CNum CInByte2::ReadNum()
163
164   UInt64 value = ReadNumber(); 
165   if (value > kNumMax)
166     ThrowUnsupported();
167   return (CNum)value;
168 }
169
170 UInt32 CInByte2::ReadUInt32()
171 {
172   if (_pos + 4 > _size)
173     ThrowEndOfData();
174   UInt32 res = GetUInt32FromMem(_buffer + _pos);
175   _pos += 4;
176   return res;
177 }
178
179 UInt64 CInByte2::ReadUInt64()
180 {
181   if (_pos + 8 > _size)
182     ThrowEndOfData();
183   UInt64 res = GetUInt64FromMem(_buffer + _pos);
184   _pos += 8;
185   return res;
186 }
187
188 void CInByte2::ReadString(UString &s)
189 {
190   const Byte *buf = _buffer + _pos;
191   size_t rem = (_size - _pos) / 2 * 2;
192   {
193     size_t i;
194     for (i = 0; i < rem; i += 2)
195       if (buf[i] == 0 && buf[i + 1] == 0)
196         break;
197     if (i == rem)
198       ThrowEndOfData();
199     rem = i;
200   }
201   int len = (int)(rem / 2);
202   if (len < 0 || (size_t)len * 2 != rem)
203     ThrowUnsupported();
204   wchar_t *p = s.GetBuffer(len);
205   int i;
206   for (i = 0; i < len; i++, buf += 2) 
207     p[i] = (wchar_t)GetUInt16FromMem(buf);
208   p[i] = 0;
209   s.ReleaseBuffer(len);
210   _pos += rem + 2;
211 }
212
213 static inline bool TestSignatureCandidate(const Byte *p)
214 {
215   for (int i = 0; i < kSignatureSize; i++)
216     if (p[i] != kSignature[i])
217       return false;
218   return (p[0x1A] == 0 && p[0x1B] == 0);
219 }
220
221 HRESULT CInArchive::FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
222 {
223   UInt32 processedSize; 
224   RINOK(ReadStream(stream, _header, kHeaderSize, &processedSize));
225   if (processedSize != kHeaderSize)
226     return S_FALSE;
227   if (TestSignatureCandidate(_header))
228     return S_OK;
229
230   CByteBuffer byteBuffer;
231   const UInt32 kBufferSize = (1 << 16);
232   byteBuffer.SetCapacity(kBufferSize);
233   Byte *buffer = byteBuffer;
234   UInt32 numPrevBytes = kHeaderSize - 1;
235   memcpy(buffer, _header + 1, numPrevBytes);
236   UInt64 curTestPos = _arhiveBeginStreamPosition + 1;
237   for (;;)
238   {
239     if (searchHeaderSizeLimit != NULL)
240       if (curTestPos - _arhiveBeginStreamPosition > *searchHeaderSizeLimit)
241         break;
242     UInt32 numReadBytes = kBufferSize - numPrevBytes;
243     RINOK(stream->Read(buffer + numPrevBytes, numReadBytes, &processedSize));
244     UInt32 numBytesInBuffer = numPrevBytes + processedSize;
245     if (numBytesInBuffer < kHeaderSize)
246       break;
247     UInt32 numTests = numBytesInBuffer - kHeaderSize + 1;
248     for(UInt32 pos = 0; pos < numTests; pos++, curTestPos++)
249     { 
250       if (TestSignatureCandidate(buffer + pos))
251       {
252         memcpy(_header, buffer + pos, kHeaderSize);
253         _arhiveBeginStreamPosition = curTestPos;
254         return stream->Seek(curTestPos + kHeaderSize, STREAM_SEEK_SET, NULL);
255       }
256     }
257     numPrevBytes = numBytesInBuffer - numTests;
258     memmove(buffer, buffer + numTests, numPrevBytes);
259   }
260   return S_FALSE;
261 }
262
263 // S_FALSE means that file is not archive
264 HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
265 {
266   Close();
267   RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_arhiveBeginStreamPosition))
268   RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit));
269   _stream = stream;
270   return S_OK;
271 }
272   
273 void CInArchive::Close()
274 {
275   _stream.Release();
276 }
277
278 void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */)
279 {
280   for (;;)
281   {
282     if (ReadID() == NID::kEnd)
283       break;
284     SkeepData();
285   }
286 }
287
288 void CInArchive::GetNextFolderItem(CFolder &folder)
289 {
290   CNum numCoders = ReadNum();
291
292   folder.Coders.Clear();
293   folder.Coders.Reserve((int)numCoders);
294   CNum numInStreams = 0;
295   CNum numOutStreams = 0;
296   CNum i;
297   for (i = 0; i < numCoders; i++)
298   {
299     folder.Coders.Add(CCoderInfo());
300     CCoderInfo &coder = folder.Coders.Back();
301
302     {
303       Byte mainByte = ReadByte();
304       int idSize = (mainByte & 0xF);
305       Byte longID[15];
306       ReadBytes(longID, idSize);
307       if (idSize > 8)
308         ThrowUnsupported();
309       UInt64 id = 0;
310       for (int j = 0; j < idSize; j++)
311         id |= (UInt64)longID[idSize - 1 - j] << (8 * j);
312       coder.MethodID = id;
313
314       if ((mainByte & 0x10) != 0)
315       {
316         coder.NumInStreams = ReadNum();
317         coder.NumOutStreams = ReadNum();
318       }
319       else
320       {
321         coder.NumInStreams = 1;
322         coder.NumOutStreams = 1;
323       }
324       if ((mainByte & 0x20) != 0)
325       {
326         CNum propertiesSize = ReadNum();
327         coder.Properties.SetCapacity((size_t)propertiesSize);
328         ReadBytes((Byte *)coder.Properties, (size_t)propertiesSize);
329       }
330       if ((mainByte & 0x80) != 0)
331         ThrowUnsupported();
332     }
333     numInStreams += coder.NumInStreams;
334     numOutStreams += coder.NumOutStreams;
335   }
336
337   CNum numBindPairs;
338   numBindPairs = numOutStreams - 1;
339   folder.BindPairs.Clear();
340   folder.BindPairs.Reserve(numBindPairs);
341   for (i = 0; i < numBindPairs; i++)
342   {
343     CBindPair bindPair;
344     bindPair.InIndex = ReadNum();
345     bindPair.OutIndex = ReadNum(); 
346     folder.BindPairs.Add(bindPair);
347   }
348
349   CNum numPackedStreams = numInStreams - numBindPairs;
350   folder.PackStreams.Reserve(numPackedStreams);
351   if (numPackedStreams == 1)
352   {
353     for (CNum j = 0; j < numInStreams; j++)
354       if (folder.FindBindPairForInStream(j) < 0)
355       {
356         folder.PackStreams.Add(j);
357         break;
358       }
359   }
360   else
361     for(i = 0; i < numPackedStreams; i++)
362       folder.PackStreams.Add(ReadNum());
363 }
364
365 void CInArchive::WaitAttribute(UInt64 attribute)
366 {
367   for (;;)
368   {
369     UInt64 type = ReadID();
370     if (type == attribute)
371       return;
372     if (type == NID::kEnd)
373       ThrowIncorrect();
374     SkeepData();
375   }
376 }
377
378 void CInArchive::ReadHashDigests(int numItems,
379     CRecordVector<bool> &digestsDefined, 
380     CRecordVector<UInt32> &digests)
381 {
382   ReadBoolVector2(numItems, digestsDefined);
383   digests.Clear();
384   digests.Reserve(numItems);
385   for(int i = 0; i < numItems; i++)
386   {
387     UInt32 crc = 0;
388     if (digestsDefined[i])
389       crc = ReadUInt32();
390     digests.Add(crc);
391   }
392 }
393
394 void CInArchive::ReadPackInfo(
395     UInt64 &dataOffset,
396     CRecordVector<UInt64> &packSizes,
397     CRecordVector<bool> &packCRCsDefined,
398     CRecordVector<UInt32> &packCRCs)
399 {
400   dataOffset = ReadNumber();
401   CNum numPackStreams = ReadNum();
402
403   WaitAttribute(NID::kSize);
404   packSizes.Clear();
405   packSizes.Reserve(numPackStreams);
406   for (CNum i = 0; i < numPackStreams; i++)
407     packSizes.Add(ReadNumber());
408
409   UInt64 type;
410   for (;;)
411   {
412     type = ReadID();
413     if (type == NID::kEnd)
414       break;
415     if (type == NID::kCRC)
416     {
417       ReadHashDigests(numPackStreams, packCRCsDefined, packCRCs); 
418       continue;
419     }
420     SkeepData();
421   }
422   if (packCRCsDefined.IsEmpty())
423   {
424     packCRCsDefined.Reserve(numPackStreams);
425     packCRCsDefined.Clear();
426     packCRCs.Reserve(numPackStreams);
427     packCRCs.Clear();
428     for(CNum i = 0; i < numPackStreams; i++)
429     {
430       packCRCsDefined.Add(false);
431       packCRCs.Add(0);
432     }
433   }
434 }
435
436 void CInArchive::ReadUnPackInfo(
437     const CObjectVector<CByteBuffer> *dataVector,
438     CObjectVector<CFolder> &folders)
439 {
440   WaitAttribute(NID::kFolder);
441   CNum numFolders = ReadNum();
442
443   {
444     CStreamSwitch streamSwitch;
445     streamSwitch.Set(this, dataVector);
446     folders.Clear();
447     folders.Reserve(numFolders);
448     for(CNum i = 0; i < numFolders; i++)
449     {
450       folders.Add(CFolder());
451       GetNextFolderItem(folders.Back());
452     }
453   }
454
455   WaitAttribute(NID::kCodersUnPackSize);
456
457   CNum i;
458   for (i = 0; i < numFolders; i++)
459   {
460     CFolder &folder = folders[i];
461     CNum numOutStreams = folder.GetNumOutStreams();
462     folder.UnPackSizes.Reserve(numOutStreams);
463     for (CNum j = 0; j < numOutStreams; j++)
464       folder.UnPackSizes.Add(ReadNumber());
465   }
466
467   for (;;)
468   {
469     UInt64 type = ReadID();
470     if (type == NID::kEnd)
471       return;
472     if (type == NID::kCRC)
473     {
474       CRecordVector<bool> crcsDefined;
475       CRecordVector<UInt32> crcs;
476       ReadHashDigests(numFolders, crcsDefined, crcs); 
477       for(i = 0; i < numFolders; i++)
478       {
479         CFolder &folder = folders[i];
480         folder.UnPackCRCDefined = crcsDefined[i];
481         folder.UnPackCRC = crcs[i];
482       }
483       continue;
484     }
485     SkeepData();
486   }
487 }
488
489 void CInArchive::ReadSubStreamsInfo(
490     const CObjectVector<CFolder> &folders,
491     CRecordVector<CNum> &numUnPackStreamsInFolders,
492     CRecordVector<UInt64> &unPackSizes,
493     CRecordVector<bool> &digestsDefined, 
494     CRecordVector<UInt32> &digests)
495 {
496   numUnPackStreamsInFolders.Clear();
497   numUnPackStreamsInFolders.Reserve(folders.Size());
498   UInt64 type;
499   for (;;)
500   {
501     type = ReadID();
502     if (type == NID::kNumUnPackStream)
503     {
504       for(int i = 0; i < folders.Size(); i++)
505         numUnPackStreamsInFolders.Add(ReadNum());
506       continue;
507     }
508     if (type == NID::kCRC || type == NID::kSize)
509       break;
510     if (type == NID::kEnd)
511       break;
512     SkeepData();
513   }
514
515   if (numUnPackStreamsInFolders.IsEmpty())
516     for(int i = 0; i < folders.Size(); i++)
517       numUnPackStreamsInFolders.Add(1);
518
519   int i;
520   for(i = 0; i < numUnPackStreamsInFolders.Size(); i++)
521   {
522     // v3.13 incorrectly worked with empty folders
523     // v4.07: we check that folder is empty
524     CNum numSubstreams = numUnPackStreamsInFolders[i];
525     if (numSubstreams == 0)
526       continue;
527     UInt64 sum = 0;
528     for (CNum j = 1; j < numSubstreams; j++)
529       if (type == NID::kSize)
530       {
531         UInt64 size = ReadNumber();
532         unPackSizes.Add(size);
533         sum += size;
534       }
535     unPackSizes.Add(folders[i].GetUnPackSize() - sum);
536   }
537   if (type == NID::kSize)
538     type = ReadID();
539
540   int numDigests = 0;
541   int numDigestsTotal = 0;
542   for(i = 0; i < folders.Size(); i++)
543   {
544     CNum numSubstreams = numUnPackStreamsInFolders[i];
545     if (numSubstreams != 1 || !folders[i].UnPackCRCDefined)
546       numDigests += numSubstreams;
547     numDigestsTotal += numSubstreams;
548   }
549
550   for (;;)
551   {
552     if (type == NID::kCRC)
553     {
554       CRecordVector<bool> digestsDefined2; 
555       CRecordVector<UInt32> digests2;
556       ReadHashDigests(numDigests, digestsDefined2, digests2);
557       int digestIndex = 0;
558       for (i = 0; i < folders.Size(); i++)
559       {
560         CNum numSubstreams = numUnPackStreamsInFolders[i];
561         const CFolder &folder = folders[i];
562         if (numSubstreams == 1 && folder.UnPackCRCDefined)
563         {
564           digestsDefined.Add(true);
565           digests.Add(folder.UnPackCRC);
566         }
567         else
568           for (CNum j = 0; j < numSubstreams; j++, digestIndex++)
569           {
570             digestsDefined.Add(digestsDefined2[digestIndex]);
571             digests.Add(digests2[digestIndex]);
572           }
573       }
574     }
575     else if (type == NID::kEnd)
576     {
577       if (digestsDefined.IsEmpty())
578       {
579         digestsDefined.Clear();
580         digests.Clear();
581         for (int i = 0; i < numDigestsTotal; i++)
582         {
583           digestsDefined.Add(false);
584           digests.Add(0);
585         }
586       }
587       return;
588     }
589     else
590       SkeepData();
591     type = ReadID();
592   }
593 }
594
595 void CInArchive::ReadStreamsInfo(
596     const CObjectVector<CByteBuffer> *dataVector,
597     UInt64 &dataOffset,
598     CRecordVector<UInt64> &packSizes,
599     CRecordVector<bool> &packCRCsDefined,
600     CRecordVector<UInt32> &packCRCs,
601     CObjectVector<CFolder> &folders,
602     CRecordVector<CNum> &numUnPackStreamsInFolders,
603     CRecordVector<UInt64> &unPackSizes,
604     CRecordVector<bool> &digestsDefined, 
605     CRecordVector<UInt32> &digests)
606 {
607   for (;;)
608   {
609     UInt64 type = ReadID();
610     if (type > ((UInt32)1 << 30))
611       ThrowIncorrect();
612     switch((UInt32)type)
613     {
614       case NID::kEnd:
615         return;
616       case NID::kPackInfo:
617       {
618         ReadPackInfo(dataOffset, packSizes, packCRCsDefined, packCRCs);
619         break;
620       }
621       case NID::kUnPackInfo:
622       {
623         ReadUnPackInfo(dataVector, folders);
624         break;
625       }
626       case NID::kSubStreamsInfo:
627       {
628         ReadSubStreamsInfo(folders, numUnPackStreamsInFolders,
629             unPackSizes, digestsDefined, digests);
630         break;
631       }
632       default:
633         ThrowIncorrect();
634     }
635   }
636 }
637
638 void CInArchive::ReadBoolVector(int numItems, CBoolVector &v)
639 {
640   v.Clear();
641   v.Reserve(numItems);
642   Byte b = 0;
643   Byte mask = 0;
644   for(int i = 0; i < numItems; i++)
645   {
646     if (mask == 0)
647     {
648       b = ReadByte();
649       mask = 0x80;
650     }
651     v.Add((b & mask) != 0);
652     mask >>= 1;
653   }
654 }
655
656 void CInArchive::ReadBoolVector2(int numItems, CBoolVector &v)
657 {
658   Byte allAreDefined = ReadByte();
659   if (allAreDefined == 0)
660   {
661     ReadBoolVector(numItems, v);
662     return;
663   }
664   v.Clear();
665   v.Reserve(numItems);
666   for (int i = 0; i < numItems; i++)
667     v.Add(true);
668 }
669
670 void CInArchive::ReadTime(const CObjectVector<CByteBuffer> &dataVector,
671     CObjectVector<CFileItem> &files, UInt32 type)
672 {
673   CBoolVector boolVector;
674   ReadBoolVector2(files.Size(), boolVector);
675
676   CStreamSwitch streamSwitch;
677   streamSwitch.Set(this, &dataVector);
678
679   for(int i = 0; i < files.Size(); i++)
680   {
681     CFileItem &file = files[i];
682     CArchiveFileTime fileTime;
683     fileTime.dwLowDateTime = 0;
684     fileTime.dwHighDateTime = 0;
685     bool defined = boolVector[i];
686     if (defined)
687     {
688       fileTime.dwLowDateTime = ReadUInt32();
689       fileTime.dwHighDateTime = ReadUInt32();
690     }
691     switch(type)
692     {
693       case NID::kCreationTime:
694         file.IsCreationTimeDefined = defined;
695         if (defined)
696           file.CreationTime = fileTime;
697         break;
698       case NID::kLastWriteTime:
699         file.IsLastWriteTimeDefined = defined;
700         if (defined)
701           file.LastWriteTime = fileTime;
702         break;
703       case NID::kLastAccessTime:
704         file.IsLastAccessTimeDefined = defined;
705         if (defined)
706           file.LastAccessTime = fileTime;
707         break;
708     }
709   }
710 }
711
712 HRESULT CInArchive::ReadAndDecodePackedStreams(
713     DECL_EXTERNAL_CODECS_LOC_VARS
714     UInt64 baseOffset, 
715     UInt64 &dataOffset, CObjectVector<CByteBuffer> &dataVector
716     #ifndef _NO_CRYPTO
717     , ICryptoGetTextPassword *getTextPassword
718     #endif
719     )
720 {
721   CRecordVector<UInt64> packSizes;
722   CRecordVector<bool> packCRCsDefined;
723   CRecordVector<UInt32> packCRCs;
724   CObjectVector<CFolder> folders;
725   
726   CRecordVector<CNum> numUnPackStreamsInFolders;
727   CRecordVector<UInt64> unPackSizes;
728   CRecordVector<bool> digestsDefined;
729   CRecordVector<UInt32> digests;
730   
731   ReadStreamsInfo(NULL, 
732     dataOffset,
733     packSizes, 
734     packCRCsDefined, 
735     packCRCs, 
736     folders,
737     numUnPackStreamsInFolders,
738     unPackSizes,
739     digestsDefined, 
740     digests);
741   
742   // database.ArchiveInfo.DataStartPosition2 += database.ArchiveInfo.StartPositionAfterHeader;
743   
744   CNum packIndex = 0;
745   CDecoder decoder(
746     #ifdef _ST_MODE
747     false
748     #else
749     true
750     #endif
751     );
752   UInt64 dataStartPos = baseOffset + dataOffset;
753   for(int i = 0; i < folders.Size(); i++)
754   {
755     const CFolder &folder = folders[i];
756     dataVector.Add(CByteBuffer());
757     CByteBuffer &data = dataVector.Back();
758     UInt64 unPackSize64 = folder.GetUnPackSize();
759     size_t unPackSize = (size_t)unPackSize64;
760     if (unPackSize != unPackSize64)
761       ThrowUnsupported();
762     data.SetCapacity(unPackSize);
763     
764     CSequentialOutStreamImp2 *outStreamSpec = new CSequentialOutStreamImp2;
765     CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
766     outStreamSpec->Init(data, unPackSize);
767     
768     HRESULT result = decoder.Decode(
769       EXTERNAL_CODECS_LOC_VARS
770       _stream, dataStartPos, 
771       &packSizes[packIndex], folder, outStream, NULL
772       #ifndef _NO_CRYPTO
773       , getTextPassword
774       #endif
775       #ifdef COMPRESS_MT
776       , false, 1
777       #endif
778       );
779     RINOK(result);
780     
781     if (folder.UnPackCRCDefined)
782       if (CrcCalc(data, unPackSize) != folder.UnPackCRC)
783         ThrowIncorrect();
784       for (int j = 0; j < folder.PackStreams.Size(); j++)
785         dataStartPos += packSizes[packIndex++];
786   }
787   return S_OK;
788 }
789
790 HRESULT CInArchive::ReadHeader(
791     DECL_EXTERNAL_CODECS_LOC_VARS
792     CArchiveDatabaseEx &database
793     #ifndef _NO_CRYPTO
794     , ICryptoGetTextPassword *getTextPassword
795     #endif
796     )
797 {
798   UInt64 type = ReadID();
799
800   if (type == NID::kArchiveProperties)
801   {
802     ReadArchiveProperties(database.ArchiveInfo);
803     type = ReadID();
804   }
805  
806   CObjectVector<CByteBuffer> dataVector;
807   
808   if (type == NID::kAdditionalStreamsInfo)
809   {
810     HRESULT result = ReadAndDecodePackedStreams(
811         EXTERNAL_CODECS_LOC_VARS
812         database.ArchiveInfo.StartPositionAfterHeader, 
813         database.ArchiveInfo.DataStartPosition2,
814         dataVector
815         #ifndef _NO_CRYPTO
816         , getTextPassword
817         #endif
818         );
819     RINOK(result);
820     database.ArchiveInfo.DataStartPosition2 += database.ArchiveInfo.StartPositionAfterHeader;
821     type = ReadID();
822   }
823
824   CRecordVector<UInt64> unPackSizes;
825   CRecordVector<bool> digestsDefined;
826   CRecordVector<UInt32> digests;
827   
828   if (type == NID::kMainStreamsInfo)
829   {
830     ReadStreamsInfo(&dataVector,
831         database.ArchiveInfo.DataStartPosition,
832         database.PackSizes, 
833         database.PackCRCsDefined, 
834         database.PackCRCs, 
835         database.Folders,
836         database.NumUnPackStreamsVector,
837         unPackSizes,
838         digestsDefined,
839         digests);
840     database.ArchiveInfo.DataStartPosition += database.ArchiveInfo.StartPositionAfterHeader;
841     type = ReadID();
842   }
843   else
844   {
845     for(int i = 0; i < database.Folders.Size(); i++)
846     {
847       database.NumUnPackStreamsVector.Add(1);
848       CFolder &folder = database.Folders[i];
849       unPackSizes.Add(folder.GetUnPackSize());
850       digestsDefined.Add(folder.UnPackCRCDefined);
851       digests.Add(folder.UnPackCRC);
852     }
853   }
854
855   database.Files.Clear();
856
857   if (type == NID::kEnd)
858     return S_OK;
859   if (type != NID::kFilesInfo)
860     ThrowIncorrect();
861   
862   CNum numFiles = ReadNum();
863   database.Files.Reserve(numFiles);
864   CNum i;
865   for(i = 0; i < numFiles; i++)
866     database.Files.Add(CFileItem());
867
868   database.ArchiveInfo.FileInfoPopIDs.Add(NID::kSize);
869   if (!database.PackSizes.IsEmpty())
870     database.ArchiveInfo.FileInfoPopIDs.Add(NID::kPackInfo);
871   if (numFiles > 0  && !digests.IsEmpty())
872     database.ArchiveInfo.FileInfoPopIDs.Add(NID::kCRC);
873
874   CBoolVector emptyStreamVector;
875   emptyStreamVector.Reserve((int)numFiles);
876   for(i = 0; i < numFiles; i++)
877     emptyStreamVector.Add(false);
878   CBoolVector emptyFileVector;
879   CBoolVector antiFileVector;
880   CNum numEmptyStreams = 0;
881
882   for (;;)
883   {
884     UInt64 type = ReadID();
885     if (type == NID::kEnd)
886       break;
887     UInt64 size = ReadNumber();
888     bool isKnownType = true;
889     if (type > ((UInt32)1 << 30))
890       isKnownType = false;
891     else switch((UInt32)type)
892     {
893       case NID::kName:
894       {
895         CStreamSwitch streamSwitch;
896         streamSwitch.Set(this, &dataVector);
897         for(int i = 0; i < database.Files.Size(); i++)
898           _inByteBack->ReadString(database.Files[i].Name);
899         break;
900       }
901       case NID::kWinAttributes:
902       {
903         CBoolVector boolVector;
904         ReadBoolVector2(database.Files.Size(), boolVector);
905         CStreamSwitch streamSwitch;
906         streamSwitch.Set(this, &dataVector);
907         for(i = 0; i < numFiles; i++)
908         {
909           CFileItem &file = database.Files[i];
910           file.AreAttributesDefined = boolVector[i];
911           if (file.AreAttributesDefined)
912             file.Attributes = ReadUInt32();
913         }
914         break;
915       }
916       case NID::kStartPos:
917       {
918         CBoolVector boolVector;
919         ReadBoolVector2(database.Files.Size(), boolVector);
920         CStreamSwitch streamSwitch;
921         streamSwitch.Set(this, &dataVector);
922         for(i = 0; i < numFiles; i++)
923         {
924           CFileItem &file = database.Files[i];
925           file.IsStartPosDefined = boolVector[i];
926           if (file.IsStartPosDefined)
927             file.StartPos = ReadUInt64();
928         }
929         break;
930       }
931       case NID::kEmptyStream:
932       {
933         ReadBoolVector(numFiles, emptyStreamVector);
934         for (i = 0; i < (CNum)emptyStreamVector.Size(); i++)
935           if (emptyStreamVector[i])
936             numEmptyStreams++;
937         emptyFileVector.Reserve(numEmptyStreams);
938         antiFileVector.Reserve(numEmptyStreams);
939         for (i = 0; i < numEmptyStreams; i++)
940         {
941           emptyFileVector.Add(false);
942           antiFileVector.Add(false);
943         }
944         break;
945       }
946       case NID::kEmptyFile:
947       {
948         ReadBoolVector(numEmptyStreams, emptyFileVector);
949         break;
950       }
951       case NID::kAnti:
952       {
953         ReadBoolVector(numEmptyStreams, antiFileVector);
954         break;
955       }
956       case NID::kCreationTime:
957       case NID::kLastWriteTime:
958       case NID::kLastAccessTime:
959       {
960         ReadTime(dataVector, database.Files, (UInt32)type);
961         break;
962       }
963       default:
964         isKnownType = false;
965     }
966     if (isKnownType)
967       database.ArchiveInfo.FileInfoPopIDs.Add(type);
968     else
969       SkeepData(size);
970   }
971
972   CNum emptyFileIndex = 0;
973   CNum sizeIndex = 0;
974   for(i = 0; i < numFiles; i++)
975   {
976     CFileItem &file = database.Files[i];
977     file.HasStream = !emptyStreamVector[i];
978     if(file.HasStream)
979     {
980       file.IsDirectory = false;
981       file.IsAnti = false;
982       file.UnPackSize = unPackSizes[sizeIndex];
983       file.FileCRC = digests[sizeIndex];
984       file.IsFileCRCDefined = digestsDefined[sizeIndex];
985       sizeIndex++;
986     }
987     else
988     {
989       file.IsDirectory = !emptyFileVector[emptyFileIndex];
990       file.IsAnti = antiFileVector[emptyFileIndex];
991       emptyFileIndex++;
992       file.UnPackSize = 0;
993       file.IsFileCRCDefined = false;
994     }
995   }
996   return S_OK;
997 }
998
999
1000 void CArchiveDatabaseEx::FillFolderStartPackStream()
1001 {
1002   FolderStartPackStreamIndex.Clear();
1003   FolderStartPackStreamIndex.Reserve(Folders.Size());
1004   CNum startPos = 0;
1005   for(int i = 0; i < Folders.Size(); i++)
1006   {
1007     FolderStartPackStreamIndex.Add(startPos);
1008     startPos += (CNum)Folders[i].PackStreams.Size();
1009   }
1010 }
1011
1012 void CArchiveDatabaseEx::FillStartPos()
1013 {
1014   PackStreamStartPositions.Clear();
1015   PackStreamStartPositions.Reserve(PackSizes.Size());
1016   UInt64 startPos = 0;
1017   for(int i = 0; i < PackSizes.Size(); i++)
1018   {
1019     PackStreamStartPositions.Add(startPos);
1020     startPos += PackSizes[i];
1021   }
1022 }
1023
1024 void CArchiveDatabaseEx::FillFolderStartFileIndex()
1025 {
1026   FolderStartFileIndex.Clear();
1027   FolderStartFileIndex.Reserve(Folders.Size());
1028   FileIndexToFolderIndexMap.Clear();
1029   FileIndexToFolderIndexMap.Reserve(Files.Size());
1030   
1031   int folderIndex = 0;
1032   CNum indexInFolder = 0;
1033   for (int i = 0; i < Files.Size(); i++)
1034   {
1035     const CFileItem &file = Files[i];
1036     bool emptyStream = !file.HasStream;
1037     if (emptyStream && indexInFolder == 0)
1038     {
1039       FileIndexToFolderIndexMap.Add(kNumNoIndex);
1040       continue;
1041     }
1042     if (indexInFolder == 0)
1043     {
1044       // v3.13 incorrectly worked with empty folders
1045       // v4.07: Loop for skipping empty folders
1046       for (;;)
1047       {
1048         if (folderIndex >= Folders.Size())
1049           ThrowIncorrect();
1050         FolderStartFileIndex.Add(i); // check it
1051         if (NumUnPackStreamsVector[folderIndex] != 0)
1052           break;
1053         folderIndex++;
1054       }
1055     }
1056     FileIndexToFolderIndexMap.Add(folderIndex);
1057     if (emptyStream)
1058       continue;
1059     indexInFolder++;
1060     if (indexInFolder >= NumUnPackStreamsVector[folderIndex])
1061     {
1062       folderIndex++;
1063       indexInFolder = 0;
1064     }
1065   }
1066 }
1067
1068 HRESULT CInArchive::ReadDatabase2(
1069     DECL_EXTERNAL_CODECS_LOC_VARS
1070     CArchiveDatabaseEx &database
1071     #ifndef _NO_CRYPTO
1072     , ICryptoGetTextPassword *getTextPassword
1073     #endif
1074     )
1075 {
1076   database.Clear();
1077   database.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition;
1078
1079   database.ArchiveInfo.Version.Major = _header[6];
1080   database.ArchiveInfo.Version.Minor = _header[7];
1081
1082   if (database.ArchiveInfo.Version.Major != kMajorVersion)
1083     ThrowUnsupportedVersion();
1084
1085   UInt32 crcFromArchive = GetUInt32FromMem(_header + 8);
1086   UInt64 nextHeaderOffset = GetUInt64FromMem(_header + 0xC);
1087   UInt64 nextHeaderSize = GetUInt64FromMem(_header + 0x14);
1088   UInt32 nextHeaderCRC =  GetUInt32FromMem(_header + 0x1C);
1089   UInt32 crc = CrcCalc(_header + 0xC, 20);
1090
1091   #ifdef FORMAT_7Z_RECOVERY
1092   if (crcFromArchive == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0)
1093   {
1094     UInt64 cur, cur2;
1095     RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur));
1096     const int kCheckSize = 500;
1097     Byte buf[kCheckSize];
1098     RINOK(_stream->Seek(0, STREAM_SEEK_END, &cur2));
1099     int checkSize = kCheckSize;
1100     if (cur2 - cur < kCheckSize)
1101       checkSize = (int)(cur2 - cur);
1102     RINOK(_stream->Seek(-checkSize, STREAM_SEEK_END, &cur2));
1103     
1104     UInt32 realProcessedSize;
1105     RINOK(_stream->Read(buf, (UInt32)kCheckSize, &realProcessedSize));
1106
1107     int i;
1108     for (i = (int)realProcessedSize - 2; i >= 0; i--)
1109       if (buf[i] == 0x17 && buf[i + 1] == 0x6 || buf[i] == 0x01 && buf[i + 1] == 0x04)
1110         break;
1111     if (i < 0)
1112       return S_FALSE;
1113     nextHeaderSize = realProcessedSize - i;
1114     nextHeaderOffset = cur2 - cur + i;
1115     nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize);
1116     RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL));
1117   }
1118   #endif
1119
1120   #ifdef FORMAT_7Z_RECOVERY
1121   crcFromArchive = crc;
1122   #endif
1123
1124   database.ArchiveInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize;
1125
1126   if (crc != crcFromArchive)
1127     ThrowIncorrect();
1128
1129   if (nextHeaderSize == 0)
1130     return S_OK;
1131
1132   if (nextHeaderSize > (UInt64)0xFFFFFFFF)
1133     return S_FALSE;
1134
1135   RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL));
1136
1137   CByteBuffer buffer2;
1138   buffer2.SetCapacity((size_t)nextHeaderSize);
1139
1140   UInt32 realProcessedSize;
1141   RINOK(_stream->Read(buffer2, (UInt32)nextHeaderSize, &realProcessedSize));
1142   if (realProcessedSize != (UInt32)nextHeaderSize)
1143     return S_FALSE;
1144   if (CrcCalc(buffer2, (UInt32)nextHeaderSize) != nextHeaderCRC)
1145     ThrowIncorrect();
1146   
1147   CStreamSwitch streamSwitch;
1148   streamSwitch.Set(this, buffer2);
1149   
1150   CObjectVector<CByteBuffer> dataVector;
1151   
1152   for (;;)
1153   {
1154     UInt64 type = ReadID();
1155     if (type == NID::kHeader)
1156       break;
1157     if (type != NID::kEncodedHeader)
1158       ThrowIncorrect();
1159     HRESULT result = ReadAndDecodePackedStreams(
1160         EXTERNAL_CODECS_LOC_VARS
1161         database.ArchiveInfo.StartPositionAfterHeader, 
1162         database.ArchiveInfo.DataStartPosition2,
1163         dataVector
1164         #ifndef _NO_CRYPTO
1165         , getTextPassword
1166         #endif
1167         );
1168     RINOK(result);
1169     if (dataVector.Size() == 0)
1170       return S_OK;
1171     if (dataVector.Size() > 1)
1172       ThrowIncorrect();
1173     streamSwitch.Remove();
1174     streamSwitch.Set(this, dataVector.Front());
1175   }
1176
1177   return ReadHeader(
1178     EXTERNAL_CODECS_LOC_VARS
1179     database
1180     #ifndef _NO_CRYPTO
1181     , getTextPassword
1182     #endif
1183     );
1184 }
1185
1186 HRESULT CInArchive::ReadDatabase(
1187     DECL_EXTERNAL_CODECS_LOC_VARS
1188     CArchiveDatabaseEx &database
1189     #ifndef _NO_CRYPTO
1190     , ICryptoGetTextPassword *getTextPassword
1191     #endif
1192     )
1193 {
1194   try
1195   {
1196     return ReadDatabase2(
1197       EXTERNAL_CODECS_LOC_VARS database
1198       #ifndef _NO_CRYPTO
1199       , getTextPassword
1200       #endif
1201       );
1202   }
1203   catch(CInArchiveException &) { return S_FALSE; }
1204 }
1205
1206 }}