xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/ComHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // ComHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/Alloc.h"
6 #include "../../../C/CpuArch.h"
7 
8 #include "../../Common/IntToString.h"
9 #include "../../Common/ComTry.h"
10 #include "../../Common/MyCom.h"
11 #include "../../Common/MyBuffer.h"
12 #include "../../Common/MyString.h"
13 
14 #include "../../Windows/PropVariant.h"
15 
16 #include "../Common/LimitedStreams.h"
17 #include "../Common/ProgressUtils.h"
18 #include "../Common/RegisterArc.h"
19 #include "../Common/StreamUtils.h"
20 
21 #include "../Compress/CopyCoder.h"
22 
23 #define Get16(p) GetUi16(p)
24 #define Get32(p) GetUi32(p)
25 
26 namespace NArchive {
27 namespace NCom {
28 
29 static const Byte kSignature[] =
30   { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
31 
32 enum EType
33 {
34   k_Type_Common,
35   k_Type_Msi,
36   k_Type_Msp,
37   k_Type_Doc,
38   k_Type_Ppt,
39   k_Type_Xls
40 };
41 
42 static const char * const kExtensions[] =
43 {
44     "compound"
45   , "msi"
46   , "msp"
47   , "doc"
48   , "ppt"
49   , "xls"
50 };
51 
52 namespace NFatID
53 {
54   // static const UInt32 kFree       = 0xFFFFFFFF;
55   static const UInt32 kEndOfChain = 0xFFFFFFFE;
56   // static const UInt32 kFatSector  = 0xFFFFFFFD;
57   // static const UInt32 kMatSector  = 0xFFFFFFFC;
58   static const UInt32 kMaxValue   = 0xFFFFFFFA;
59 }
60 
61 namespace NItemType
62 {
63   static const Byte kEmpty = 0;
64   static const Byte kStorage = 1;
65   // static const Byte kStream = 2;
66   // static const Byte kLockBytes = 3;
67   // static const Byte kProperty = 4;
68   static const Byte kRootStorage = 5;
69 }
70 
71 static const UInt32 kNameSizeMax = 64;
72 
73 struct CItem
74 {
75   Byte Name[kNameSizeMax];
76   // UInt16 NameSize;
77   // UInt32 Flags;
78   FILETIME CTime;
79   FILETIME MTime;
80   UInt64 Size;
81   UInt32 LeftDid;
82   UInt32 RightDid;
83   UInt32 SonDid;
84   UInt32 Sid;
85   Byte Type;
86 
IsEmptyNArchive::NCom::CItem87   bool IsEmpty() const { return Type == NItemType::kEmpty; }
IsDirNArchive::NCom::CItem88   bool IsDir() const { return Type == NItemType::kStorage || Type == NItemType::kRootStorage; }
89 
90   void Parse(const Byte *p, bool mode64bit);
91 };
92 
93 struct CRef
94 {
95   int Parent;
96   UInt32 Did;
97 };
98 
99 class CDatabase
100 {
101   UInt32 NumSectorsInMiniStream;
102   CObjArray<UInt32> MiniSids;
103 
104   HRESULT AddNode(int parent, UInt32 did);
105 public:
106 
107   CObjArray<UInt32> Fat;
108   UInt32 FatSize;
109 
110   CObjArray<UInt32> Mat;
111   UInt32 MatSize;
112 
113   CObjectVector<CItem> Items;
114   CRecordVector<CRef> Refs;
115 
116   UInt32 LongStreamMinSize;
117   unsigned SectorSizeBits;
118   unsigned MiniSectorSizeBits;
119 
120   Int32 MainSubfile;
121 
122   UInt64 PhySize;
123   UInt64 PhySize_Aligned;
124   EType Type;
125 
IsNotArcType() const126   bool IsNotArcType() const
127   {
128     return
129       Type != k_Type_Msi &&
130       Type != k_Type_Msp;
131   }
132 
UpdatePhySize(UInt64 val,UInt64 val_Aligned)133   void UpdatePhySize(UInt64 val, UInt64 val_Aligned)
134   {
135     if (PhySize < val)
136       PhySize = val;
137     if (PhySize_Aligned < val_Aligned)
138       PhySize_Aligned = val_Aligned;
139   }
140   HRESULT ReadSector(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid);
141   HRESULT ReadIDs(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid, UInt32 *dest);
142 
143   HRESULT Update_PhySize_WithItem(unsigned index);
144 
145   void Clear();
IsLargeStream(UInt64 size) const146   bool IsLargeStream(UInt64 size) const { return size >= LongStreamMinSize; }
147   UString GetItemPath(UInt32 index) const;
148 
GetItemPackSize(UInt64 size) const149   UInt64 GetItemPackSize(UInt64 size) const
150   {
151     UInt64 mask = ((UInt64)1 << (IsLargeStream(size) ? SectorSizeBits : MiniSectorSizeBits)) - 1;
152     return (size + mask) & ~mask;
153   }
154 
GetMiniCluster(UInt32 sid,UInt64 & res) const155   bool GetMiniCluster(UInt32 sid, UInt64 &res) const
156   {
157     unsigned subBits = SectorSizeBits - MiniSectorSizeBits;
158     UInt32 fid = sid >> subBits;
159     if (fid >= NumSectorsInMiniStream)
160       return false;
161     res = (((UInt64)MiniSids[fid] + 1) << subBits) + (sid & ((1 << subBits) - 1));
162     return true;
163   }
164 
165   HRESULT Open(IInStream *inStream);
166 };
167 
168 
ReadSector(IInStream * inStream,Byte * buf,unsigned sectorSizeBits,UInt32 sid)169 HRESULT CDatabase::ReadSector(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid)
170 {
171   const UInt64 end = ((UInt64)sid + 2) << sectorSizeBits;
172   UpdatePhySize(end, end);
173   RINOK(InStream_SeekSet(inStream, (((UInt64)sid + 1) << sectorSizeBits)))
174   return ReadStream_FALSE(inStream, buf, (size_t)1 << sectorSizeBits);
175 }
176 
ReadIDs(IInStream * inStream,Byte * buf,unsigned sectorSizeBits,UInt32 sid,UInt32 * dest)177 HRESULT CDatabase::ReadIDs(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid, UInt32 *dest)
178 {
179   RINOK(ReadSector(inStream, buf, sectorSizeBits, sid))
180   UInt32 sectorSize = (UInt32)1 << sectorSizeBits;
181   for (UInt32 t = 0; t < sectorSize; t += 4)
182     *dest++ = Get32(buf + t);
183   return S_OK;
184 }
185 
GetFileTimeFromMem(const Byte * p,FILETIME * ft)186 static void GetFileTimeFromMem(const Byte *p, FILETIME *ft)
187 {
188   ft->dwLowDateTime = Get32(p);
189   ft->dwHighDateTime = Get32(p + 4);
190 }
191 
Parse(const Byte * p,bool mode64bit)192 void CItem::Parse(const Byte *p, bool mode64bit)
193 {
194   memcpy(Name, p, kNameSizeMax);
195   // NameSize = Get16(p + 64);
196   Type = p[66];
197   LeftDid = Get32(p + 68);
198   RightDid = Get32(p + 72);
199   SonDid = Get32(p + 76);
200   // Flags = Get32(p + 96);
201   GetFileTimeFromMem(p + 100, &CTime);
202   GetFileTimeFromMem(p + 108, &MTime);
203   Sid = Get32(p + 116);
204   Size = Get32(p + 120);
205   if (mode64bit)
206     Size |= ((UInt64)Get32(p + 124) << 32);
207 }
208 
Clear()209 void CDatabase::Clear()
210 {
211   PhySize = 0;
212   PhySize_Aligned = 0;
213 
214   Fat.Free();
215   MiniSids.Free();
216   Mat.Free();
217   Items.Clear();
218   Refs.Clear();
219 }
220 
221 static const UInt32 kNoDid = 0xFFFFFFFF;
222 
AddNode(int parent,UInt32 did)223 HRESULT CDatabase::AddNode(int parent, UInt32 did)
224 {
225   if (did == kNoDid)
226     return S_OK;
227   if (did >= (UInt32)Items.Size())
228     return S_FALSE;
229   const CItem &item = Items[did];
230   if (item.IsEmpty())
231     return S_FALSE;
232   CRef ref;
233   ref.Parent = parent;
234   ref.Did = did;
235   const unsigned index = Refs.Add(ref);
236   if (Refs.Size() > Items.Size())
237     return S_FALSE;
238   RINOK(AddNode(parent, item.LeftDid))
239   RINOK(AddNode(parent, item.RightDid))
240   if (item.IsDir())
241   {
242     RINOK(AddNode((int)index, item.SonDid))
243   }
244   return S_OK;
245 }
246 
CompoundNameToFileName(const UString & s)247 static UString CompoundNameToFileName(const UString &s)
248 {
249   UString res;
250   for (unsigned i = 0; i < s.Len(); i++)
251   {
252     const wchar_t c = s[i];
253     if ((unsigned)(int)c < 0x20)
254     {
255       res.Add_Char('[');
256       res.Add_UInt32((UInt32)(unsigned)(int)c);
257       res.Add_Char(']');
258     }
259     else
260       res += c;
261   }
262   return res;
263 }
264 
265 static const char k_Msi_Chars[] =
266   "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._";
267 
268 // static const char * const k_Msi_ID = ""; // "{msi}";
269 static const char k_Msi_SpecChar = '!';
270 
271 static const unsigned k_Msi_NumBits = 6;
272 static const unsigned k_Msi_NumChars = 1 << k_Msi_NumBits;
273 static const unsigned k_Msi_CharMask = k_Msi_NumChars - 1;
274 static const unsigned k_Msi_StartUnicodeChar = 0x3800;
275 static const unsigned k_Msi_UnicodeRange = k_Msi_NumChars * (k_Msi_NumChars + 1);
276 
277 
IsMsiName(const Byte * p)278 static bool IsMsiName(const Byte *p)
279 {
280   UInt32 c = Get16(p);
281   return
282       c >= k_Msi_StartUnicodeChar &&
283       c <= k_Msi_StartUnicodeChar + k_Msi_UnicodeRange;
284 }
285 
AreEqualNames(const Byte * rawName,const char * asciiName)286 static bool AreEqualNames(const Byte *rawName, const char *asciiName)
287 {
288   for (unsigned i = 0; i < kNameSizeMax / 2; i++)
289   {
290     wchar_t c = Get16(rawName + i * 2);
291     wchar_t c2 = (Byte)asciiName[i];
292     if (c != c2)
293       return false;
294     if (c == 0)
295       return true;
296   }
297   return false;
298 }
299 
CompoundMsiNameToFileName(const UString & name,UString & res)300 static bool CompoundMsiNameToFileName(const UString &name, UString &res)
301 {
302   res.Empty();
303   for (unsigned i = 0; i < name.Len(); i++)
304   {
305     wchar_t c = name[i];
306     if (c < (wchar_t)k_Msi_StartUnicodeChar || c > (wchar_t)(k_Msi_StartUnicodeChar + k_Msi_UnicodeRange))
307       return false;
308     /*
309     if (i == 0)
310       res += k_Msi_ID;
311     */
312     c -= (wchar_t)k_Msi_StartUnicodeChar;
313 
314     const unsigned c0 = (unsigned)c & k_Msi_CharMask;
315     const unsigned c1 = (unsigned)c >> k_Msi_NumBits;
316 
317     if (c1 <= k_Msi_NumChars)
318     {
319       res.Add_Char(k_Msi_Chars[c0]);
320       if (c1 == k_Msi_NumChars)
321         break;
322       res.Add_Char(k_Msi_Chars[c1]);
323     }
324     else
325       res.Add_Char(k_Msi_SpecChar);
326   }
327   return true;
328 }
329 
ConvertName(const Byte * p,bool & isMsi)330 static UString ConvertName(const Byte *p, bool &isMsi)
331 {
332   isMsi = false;
333   UString s;
334 
335   for (unsigned i = 0; i < kNameSizeMax; i += 2)
336   {
337     wchar_t c = Get16(p + i);
338     if (c == 0)
339       break;
340     s += c;
341   }
342 
343   UString msiName;
344   if (CompoundMsiNameToFileName(s, msiName))
345   {
346     isMsi = true;
347     return msiName;
348   }
349   return CompoundNameToFileName(s);
350 }
351 
ConvertName(const Byte * p)352 static UString ConvertName(const Byte *p)
353 {
354   bool isMsi;
355   return ConvertName(p, isMsi);
356 }
357 
GetItemPath(UInt32 index) const358 UString CDatabase::GetItemPath(UInt32 index) const
359 {
360   UString s;
361   while (index != kNoDid)
362   {
363     const CRef &ref = Refs[index];
364     const CItem &item = Items[ref.Did];
365     if (!s.IsEmpty())
366       s.InsertAtFront(WCHAR_PATH_SEPARATOR);
367     s.Insert(0, ConvertName(item.Name));
368     index = (unsigned)ref.Parent;
369   }
370   return s;
371 }
372 
Update_PhySize_WithItem(unsigned index)373 HRESULT CDatabase::Update_PhySize_WithItem(unsigned index)
374 {
375   const CItem &item = Items[index];
376   bool isLargeStream = (index == 0 || IsLargeStream(item.Size));
377   if (!isLargeStream)
378     return S_OK;
379   const unsigned bsLog = isLargeStream ? SectorSizeBits : MiniSectorSizeBits;
380   // streamSpec->Size = item.Size;
381 
382   const UInt32 clusterSize = (UInt32)1 << bsLog;
383   const UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog;
384   if (numClusters64 >= ((UInt32)1 << 31))
385     return S_FALSE;
386   UInt32 sid = item.Sid;
387   UInt64 size = item.Size;
388 
389   if (size != 0)
390   {
391     for (;; size -= clusterSize)
392     {
393       // if (isLargeStream)
394       {
395         if (sid >= FatSize)
396           return S_FALSE;
397         UInt64 end = ((UInt64)sid + 1) << bsLog;
398         const UInt64 end_Aligned = end + clusterSize;
399         if (size < clusterSize)
400           end += size;
401         else
402           end = end_Aligned;
403         UpdatePhySize(end, end_Aligned);
404         sid = Fat[sid];
405       }
406       if (size <= clusterSize)
407         break;
408     }
409   }
410   if (sid != NFatID::kEndOfChain)
411     return S_FALSE;
412   return S_OK;
413 }
414 
415 // There is name "[!]MsiPatchSequence" in msp files
416 static const unsigned kMspSequence_Size = 18;
417 static const Byte kMspSequence[kMspSequence_Size] =
418   { 0x40, 0x48, 0x96, 0x45, 0x6C, 0x3E, 0xE4, 0x45,
419     0xE6, 0x42, 0x16, 0x42, 0x37, 0x41, 0x27, 0x41,
420     0x37, 0x41 };
421 
Open(IInStream * inStream)422 HRESULT CDatabase::Open(IInStream *inStream)
423 {
424   MainSubfile = -1;
425   Type = k_Type_Common;
426   const UInt32 kHeaderSize = 512;
427   Byte p[kHeaderSize];
428   PhySize = kHeaderSize;
429   RINOK(ReadStream_FALSE(inStream, p, kHeaderSize))
430   if (memcmp(p, kSignature, Z7_ARRAY_SIZE(kSignature)) != 0)
431     return S_FALSE;
432   if (Get16(p + 0x1A) > 4) // majorVer
433     return S_FALSE;
434   if (Get16(p + 0x1C) != 0xFFFE) // Little-endian
435     return S_FALSE;
436   unsigned sectorSizeBits = Get16(p + 0x1E);
437   bool mode64bit = (sectorSizeBits >= 12);
438   unsigned miniSectorSizeBits = Get16(p + 0x20);
439   SectorSizeBits = sectorSizeBits;
440   MiniSectorSizeBits = miniSectorSizeBits;
441 
442   if (sectorSizeBits > 24 ||
443       sectorSizeBits < 7 ||
444       miniSectorSizeBits > 24 ||
445       miniSectorSizeBits < 2 ||
446       miniSectorSizeBits > sectorSizeBits)
447     return S_FALSE;
448   UInt32 numSectorsForFAT = Get32(p + 0x2C); // SAT
449   LongStreamMinSize = Get32(p + 0x38);
450 
451   UInt32 sectSize = (UInt32)1 << sectorSizeBits;
452 
453   CByteBuffer sect(sectSize);
454 
455   unsigned ssb2 = sectorSizeBits - 2;
456   UInt32 numSidsInSec = (UInt32)1 << ssb2;
457   UInt32 numFatItems = numSectorsForFAT << ssb2;
458   if ((numFatItems >> ssb2) != numSectorsForFAT)
459     return S_FALSE;
460   FatSize = numFatItems;
461 
462   {
463     UInt32 numSectorsForBat = Get32(p + 0x48); // master sector allocation table
464     const UInt32 kNumHeaderBatItems = 109;
465     UInt32 numBatItems = kNumHeaderBatItems + (numSectorsForBat << ssb2);
466     if (numBatItems < kNumHeaderBatItems || ((numBatItems - kNumHeaderBatItems) >> ssb2) != numSectorsForBat)
467       return S_FALSE;
468     CObjArray<UInt32> bat(numBatItems);
469     UInt32 i;
470     for (i = 0; i < kNumHeaderBatItems; i++)
471       bat[i] = Get32(p + 0x4c + i * 4);
472     UInt32 sid = Get32(p + 0x44);
473     for (UInt32 s = 0; s < numSectorsForBat; s++)
474     {
475       RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, bat + i))
476       i += numSidsInSec - 1;
477       sid = bat[i];
478     }
479     numBatItems = i;
480 
481     Fat.Alloc(numFatItems);
482     UInt32 j = 0;
483 
484     for (i = 0; i < numFatItems; j++, i += numSidsInSec)
485     {
486       if (j >= numBatItems)
487         return S_FALSE;
488       RINOK(ReadIDs(inStream, sect, sectorSizeBits, bat[j], Fat + i))
489     }
490     FatSize = numFatItems = i;
491   }
492 
493   UInt32 numMatItems;
494   {
495     UInt32 numSectorsForMat = Get32(p + 0x40);
496     numMatItems = (UInt32)numSectorsForMat << ssb2;
497     if ((numMatItems >> ssb2) != numSectorsForMat)
498       return S_FALSE;
499     Mat.Alloc(numMatItems);
500     UInt32 i;
501     UInt32 sid = Get32(p + 0x3C); // short-sector table SID
502     for (i = 0; i < numMatItems; i += numSidsInSec)
503     {
504       RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, Mat + i))
505       if (sid >= numFatItems)
506         return S_FALSE;
507       sid = Fat[sid];
508     }
509     if (sid != NFatID::kEndOfChain)
510       return S_FALSE;
511   }
512 
513   {
514     CByteBuffer used(numFatItems);
515     for (UInt32 i = 0; i < numFatItems; i++)
516       used[i] = 0;
517     UInt32 sid = Get32(p + 0x30); // directory stream SID
518     for (;;)
519     {
520       if (sid >= numFatItems)
521         return S_FALSE;
522       if (used[sid])
523         return S_FALSE;
524       used[sid] = 1;
525       RINOK(ReadSector(inStream, sect, sectorSizeBits, sid))
526       for (UInt32 i = 0; i < sectSize; i += 128)
527       {
528         CItem item;
529         item.Parse(sect + i, mode64bit);
530         Items.Add(item);
531       }
532       sid = Fat[sid];
533       if (sid == NFatID::kEndOfChain)
534         break;
535     }
536   }
537 
538   const CItem &root = Items[0];
539 
540   {
541     UInt32 numSectorsInMiniStream;
542     {
543       UInt64 numSatSects64 = (root.Size + sectSize - 1) >> sectorSizeBits;
544       if (numSatSects64 > NFatID::kMaxValue)
545         return S_FALSE;
546       numSectorsInMiniStream = (UInt32)numSatSects64;
547     }
548     NumSectorsInMiniStream = numSectorsInMiniStream;
549     MiniSids.Alloc(numSectorsInMiniStream);
550     {
551       UInt64 matSize64 = (root.Size + ((UInt64)1 << miniSectorSizeBits) - 1) >> miniSectorSizeBits;
552       if (matSize64 > NFatID::kMaxValue)
553         return S_FALSE;
554       MatSize = (UInt32)matSize64;
555       if (numMatItems < MatSize)
556         return S_FALSE;
557     }
558 
559     UInt32 sid = root.Sid;
560     for (UInt32 i = 0; ; i++)
561     {
562       if (sid == NFatID::kEndOfChain)
563       {
564         if (i != numSectorsInMiniStream)
565           return S_FALSE;
566         break;
567       }
568       if (i >= numSectorsInMiniStream)
569         return S_FALSE;
570       MiniSids[i] = sid;
571       if (sid >= numFatItems)
572         return S_FALSE;
573       sid = Fat[sid];
574     }
575   }
576 
577   RINOK(AddNode(-1, root.SonDid))
578 
579   unsigned numCabs = 0;
580 
581   FOR_VECTOR (i, Refs)
582   {
583     const CItem &item = Items[Refs[i].Did];
584     if (item.IsDir() || numCabs > 1)
585       continue;
586     bool isMsiName;
587     const UString msiName = ConvertName(item.Name, isMsiName);
588     if (isMsiName && !msiName.IsEmpty())
589     {
590       // bool isThereExt = (msiName.Find(L'.') >= 0);
591       bool isMsiSpec = (msiName[0] == k_Msi_SpecChar);
592       if ((msiName.Len() >= 4 && StringsAreEqualNoCase_Ascii(msiName.RightPtr(4), ".cab"))
593           || (!isMsiSpec && msiName.Len() >= 3 && StringsAreEqualNoCase_Ascii(msiName.RightPtr(3), "exe"))
594           // || (!isMsiSpec && !isThereExt)
595           )
596       {
597         numCabs++;
598         MainSubfile = (int)i;
599       }
600     }
601   }
602 
603   if (numCabs > 1)
604     MainSubfile = -1;
605 
606   {
607     FOR_VECTOR (t, Items)
608     {
609       Update_PhySize_WithItem(t);
610     }
611   }
612   {
613     if (PhySize != PhySize_Aligned)
614     {
615       /* some msi (in rare cases) have unaligned size of archive,
616          where there is no padding data after payload data in last cluster of archive */
617       UInt64 fileSize;
618       RINOK(InStream_GetSize_SeekToEnd(inStream, fileSize))
619       if (PhySize != fileSize)
620         PhySize = PhySize_Aligned;
621     }
622   }
623   {
624     FOR_VECTOR (t, Items)
625     {
626       const CItem &item = Items[t];
627 
628       if (IsMsiName(item.Name))
629       {
630         Type = k_Type_Msi;
631         if (memcmp(item.Name, kMspSequence, kMspSequence_Size) == 0)
632         {
633           Type = k_Type_Msp;
634           break;
635         }
636         continue;
637       }
638       if (AreEqualNames(item.Name, "WordDocument"))
639       {
640         Type = k_Type_Doc;
641         break;
642       }
643       if (AreEqualNames(item.Name, "PowerPoint Document"))
644       {
645         Type = k_Type_Ppt;
646         break;
647       }
648       if (AreEqualNames(item.Name, "Workbook"))
649       {
650         Type = k_Type_Xls;
651         break;
652       }
653     }
654   }
655 
656   return S_OK;
657 }
658 
659 Z7_CLASS_IMP_CHandler_IInArchive_1(
660   IInArchiveGetStream
661 )
662   CMyComPtr<IInStream> _stream;
663   CDatabase _db;
664 };
665 
666 static const Byte kProps[] =
667 {
668   kpidPath,
669   kpidSize,
670   kpidPackSize,
671   kpidCTime,
672   kpidMTime
673 };
674 
675 static const Byte kArcProps[] =
676 {
677   kpidExtension,
678   kpidClusterSize,
679   kpidSectorSize
680 };
681 
682 IMP_IInArchive_Props
683 IMP_IInArchive_ArcProps
684 
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))685 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
686 {
687   COM_TRY_BEGIN
688   NWindows::NCOM::CPropVariant prop;
689   switch (propID)
690   {
691     case kpidExtension: prop = kExtensions[(unsigned)_db.Type]; break;
692     case kpidPhySize: prop = _db.PhySize; break;
693     case kpidClusterSize: prop = (UInt32)1 << _db.SectorSizeBits; break;
694     case kpidSectorSize: prop = (UInt32)1 << _db.MiniSectorSizeBits; break;
695     case kpidMainSubfile: if (_db.MainSubfile >= 0) prop = (UInt32)_db.MainSubfile; break;
696     case kpidIsNotArcType: if (_db.IsNotArcType()) prop = true; break;
697   }
698   prop.Detach(value);
699   return S_OK;
700   COM_TRY_END
701 }
702 
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))703 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
704 {
705   COM_TRY_BEGIN
706   NWindows::NCOM::CPropVariant prop;
707   const CRef &ref = _db.Refs[index];
708   const CItem &item = _db.Items[ref.Did];
709 
710   switch (propID)
711   {
712     case kpidPath:  prop = _db.GetItemPath(index); break;
713     case kpidIsDir:  prop = item.IsDir(); break;
714     case kpidCTime:  prop = item.CTime; break;
715     case kpidMTime:  prop = item.MTime; break;
716     case kpidPackSize:  if (!item.IsDir()) prop = _db.GetItemPackSize(item.Size); break;
717     case kpidSize:  if (!item.IsDir()) prop = item.Size; break;
718   }
719   prop.Detach(value);
720   return S_OK;
721   COM_TRY_END
722 }
723 
Z7_COM7F_IMF(CHandler::Open (IInStream * inStream,const UInt64 *,IArchiveOpenCallback *))724 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream,
725     const UInt64 * /* maxCheckStartPosition */,
726     IArchiveOpenCallback * /* openArchiveCallback */))
727 {
728   COM_TRY_BEGIN
729   Close();
730   try
731   {
732     if (_db.Open(inStream) != S_OK)
733       return S_FALSE;
734     _stream = inStream;
735   }
736   catch(...) { return S_FALSE; }
737   return S_OK;
738   COM_TRY_END
739 }
740 
Z7_COM7F_IMF(CHandler::Close ())741 Z7_COM7F_IMF(CHandler::Close())
742 {
743   _db.Clear();
744   _stream.Release();
745   return S_OK;
746 }
747 
Z7_COM7F_IMF(CHandler::Extract (const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback))748 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
749     Int32 testMode, IArchiveExtractCallback *extractCallback))
750 {
751   COM_TRY_BEGIN
752   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
753   if (allFilesMode)
754     numItems = _db.Refs.Size();
755   if (numItems == 0)
756     return S_OK;
757   UInt32 i;
758   UInt64 totalSize = 0;
759   for (i = 0; i < numItems; i++)
760   {
761     const CItem &item = _db.Items[_db.Refs[allFilesMode ? i : indices[i]].Did];
762     if (!item.IsDir())
763       totalSize += item.Size;
764   }
765   RINOK(extractCallback->SetTotal(totalSize))
766 
767   UInt64 totalPackSize;
768   totalSize = totalPackSize = 0;
769 
770   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
771   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
772 
773   CLocalProgress *lps = new CLocalProgress;
774   CMyComPtr<ICompressProgressInfo> progress = lps;
775   lps->Init(extractCallback, false);
776 
777   for (i = 0; i < numItems; i++)
778   {
779     lps->InSize = totalPackSize;
780     lps->OutSize = totalSize;
781     RINOK(lps->SetCur())
782     const UInt32 index = allFilesMode ? i : indices[i];
783     const CItem &item = _db.Items[_db.Refs[index].Did];
784 
785     CMyComPtr<ISequentialOutStream> outStream;
786     const Int32 askMode = testMode ?
787         NExtract::NAskMode::kTest :
788         NExtract::NAskMode::kExtract;
789     RINOK(extractCallback->GetStream(index, &outStream, askMode))
790 
791     if (item.IsDir())
792     {
793       RINOK(extractCallback->PrepareOperation(askMode))
794       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
795       continue;
796     }
797 
798     totalPackSize += _db.GetItemPackSize(item.Size);
799     totalSize += item.Size;
800 
801     if (!testMode && !outStream)
802       continue;
803     RINOK(extractCallback->PrepareOperation(askMode))
804     Int32 res = NExtract::NOperationResult::kDataError;
805     CMyComPtr<ISequentialInStream> inStream;
806     HRESULT hres = GetStream(index, &inStream);
807     if (hres == S_FALSE)
808       res = NExtract::NOperationResult::kDataError;
809     else if (hres == E_NOTIMPL)
810       res = NExtract::NOperationResult::kUnsupportedMethod;
811     else
812     {
813       RINOK(hres)
814       if (inStream)
815       {
816         RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress))
817         if (copyCoderSpec->TotalSize == item.Size)
818           res = NExtract::NOperationResult::kOK;
819       }
820     }
821     outStream.Release();
822     RINOK(extractCallback->SetOperationResult(res))
823   }
824   return S_OK;
825   COM_TRY_END
826 }
827 
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))828 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
829 {
830   *numItems = _db.Refs.Size();
831   return S_OK;
832 }
833 
Z7_COM7F_IMF(CHandler::GetStream (UInt32 index,ISequentialInStream ** stream))834 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
835 {
836   COM_TRY_BEGIN
837   *stream = NULL;
838   const UInt32 itemIndex = _db.Refs[index].Did;
839   const CItem &item = _db.Items[itemIndex];
840   CClusterInStream *streamSpec = new CClusterInStream;
841   CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
842   streamSpec->Stream = _stream;
843   streamSpec->StartOffset = 0;
844 
845   const bool isLargeStream = (itemIndex == 0 || _db.IsLargeStream(item.Size));
846   const unsigned bsLog = isLargeStream ? _db.SectorSizeBits : _db.MiniSectorSizeBits;
847   streamSpec->BlockSizeLog = bsLog;
848   streamSpec->Size = item.Size;
849 
850   const UInt32 clusterSize = (UInt32)1 << bsLog;
851   const UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog;
852   if (numClusters64 >= ((UInt32)1 << 31))
853     return E_NOTIMPL;
854   streamSpec->Vector.ClearAndReserve((unsigned)numClusters64);
855   UInt32 sid = item.Sid;
856   UInt64 size = item.Size;
857 
858   if (size != 0)
859   {
860     for (;; size -= clusterSize)
861     {
862       if (isLargeStream)
863       {
864         if (sid >= _db.FatSize)
865           return S_FALSE;
866         streamSpec->Vector.AddInReserved(sid + 1);
867         sid = _db.Fat[sid];
868       }
869       else
870       {
871         UInt64 val = 0;
872         if (sid >= _db.MatSize || !_db.GetMiniCluster(sid, val) || val >= (UInt64)1 << 32)
873           return S_FALSE;
874         streamSpec->Vector.AddInReserved((UInt32)val);
875         sid = _db.Mat[sid];
876       }
877       if (size <= clusterSize)
878         break;
879     }
880   }
881   if (sid != NFatID::kEndOfChain)
882     return S_FALSE;
883   RINOK(streamSpec->InitAndSeek())
884   *stream = streamTemp.Detach();
885   return S_OK;
886   COM_TRY_END
887 }
888 
889 REGISTER_ARC_I(
890   "Compound", "msi msp doc xls ppt", NULL, 0xE5,
891   kSignature,
892   0,
893   0,
894   NULL)
895 
896 }}
897