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