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 < (wchar_t)k_Msi_StartUnicodeChar || c > (wchar_t)(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 numCabs++;
587 MainSubfile = i;
588 }
589 }
590 }
591
592 if (numCabs > 1)
593 MainSubfile = -1;
594
595 {
596 FOR_VECTOR (t, Items)
597 {
598 Update_PhySize_WithItem(t);
599 }
600 }
601 {
602 FOR_VECTOR (t, Items)
603 {
604 const CItem &item = Items[t];
605
606 if (IsMsiName(item.Name))
607 {
608 Type = k_Type_Msi;
609 if (memcmp(item.Name, kMspSequence, kMspSequence_Size) == 0)
610 {
611 Type = k_Type_Msp;
612 break;
613 }
614 continue;
615 }
616 if (AreEqualNames(item.Name, "WordDocument"))
617 {
618 Type = k_Type_Doc;
619 break;
620 }
621 if (AreEqualNames(item.Name, "PowerPoint Document"))
622 {
623 Type = k_Type_Ppt;
624 break;
625 }
626 if (AreEqualNames(item.Name, "Workbook"))
627 {
628 Type = k_Type_Xls;
629 break;
630 }
631 }
632 }
633
634 return S_OK;
635 }
636
637 class CHandler:
638 public IInArchive,
639 public IInArchiveGetStream,
640 public CMyUnknownImp
641 {
642 CMyComPtr<IInStream> _stream;
643 CDatabase _db;
644 public:
645 MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
646 INTERFACE_IInArchive(;)
647 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
648 };
649
650 static const Byte kProps[] =
651 {
652 kpidPath,
653 kpidSize,
654 kpidPackSize,
655 kpidCTime,
656 kpidMTime
657 };
658
659 static const Byte kArcProps[] =
660 {
661 kpidExtension,
662 kpidClusterSize,
663 kpidSectorSize
664 };
665
666 IMP_IInArchive_Props
667 IMP_IInArchive_ArcProps
668
GetArchiveProperty(PROPID propID,PROPVARIANT * value)669 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
670 {
671 COM_TRY_BEGIN
672 NWindows::NCOM::CPropVariant prop;
673 switch (propID)
674 {
675 case kpidExtension: prop = kExtensions[(unsigned)_db.Type]; break;
676 case kpidPhySize: prop = _db.PhySize; break;
677 case kpidClusterSize: prop = (UInt32)1 << _db.SectorSizeBits; break;
678 case kpidSectorSize: prop = (UInt32)1 << _db.MiniSectorSizeBits; break;
679 case kpidMainSubfile: if (_db.MainSubfile >= 0) prop = (UInt32)_db.MainSubfile; break;
680 case kpidIsNotArcType: if (_db.IsNotArcType()) prop = true; break;
681 }
682 prop.Detach(value);
683 return S_OK;
684 COM_TRY_END
685 }
686
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)687 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
688 {
689 COM_TRY_BEGIN
690 NWindows::NCOM::CPropVariant prop;
691 const CRef &ref = _db.Refs[index];
692 const CItem &item = _db.Items[ref.Did];
693
694 switch (propID)
695 {
696 case kpidPath: prop = _db.GetItemPath(index); break;
697 case kpidIsDir: prop = item.IsDir(); break;
698 case kpidCTime: prop = item.CTime; break;
699 case kpidMTime: prop = item.MTime; break;
700 case kpidPackSize: if (!item.IsDir()) prop = _db.GetItemPackSize(item.Size); break;
701 case kpidSize: if (!item.IsDir()) prop = item.Size; break;
702 }
703 prop.Detach(value);
704 return S_OK;
705 COM_TRY_END
706 }
707
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback *)708 STDMETHODIMP CHandler::Open(IInStream *inStream,
709 const UInt64 * /* maxCheckStartPosition */,
710 IArchiveOpenCallback * /* openArchiveCallback */)
711 {
712 COM_TRY_BEGIN
713 Close();
714 try
715 {
716 if (_db.Open(inStream) != S_OK)
717 return S_FALSE;
718 _stream = inStream;
719 }
720 catch(...) { return S_FALSE; }
721 return S_OK;
722 COM_TRY_END
723 }
724
Close()725 STDMETHODIMP CHandler::Close()
726 {
727 _db.Clear();
728 _stream.Release();
729 return S_OK;
730 }
731
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)732 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
733 Int32 testMode, IArchiveExtractCallback *extractCallback)
734 {
735 COM_TRY_BEGIN
736 bool allFilesMode = (numItems == (UInt32)(Int32)-1);
737 if (allFilesMode)
738 numItems = _db.Refs.Size();
739 if (numItems == 0)
740 return S_OK;
741 UInt32 i;
742 UInt64 totalSize = 0;
743 for (i = 0; i < numItems; i++)
744 {
745 const CItem &item = _db.Items[_db.Refs[allFilesMode ? i : indices[i]].Did];
746 if (!item.IsDir())
747 totalSize += item.Size;
748 }
749 RINOK(extractCallback->SetTotal(totalSize));
750
751 UInt64 totalPackSize;
752 totalSize = totalPackSize = 0;
753
754 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
755 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
756
757 CLocalProgress *lps = new CLocalProgress;
758 CMyComPtr<ICompressProgressInfo> progress = lps;
759 lps->Init(extractCallback, false);
760
761 for (i = 0; i < numItems; i++)
762 {
763 lps->InSize = totalPackSize;
764 lps->OutSize = totalSize;
765 RINOK(lps->SetCur());
766 Int32 index = allFilesMode ? i : indices[i];
767 const CItem &item = _db.Items[_db.Refs[index].Did];
768
769 CMyComPtr<ISequentialOutStream> outStream;
770 Int32 askMode = testMode ?
771 NExtract::NAskMode::kTest :
772 NExtract::NAskMode::kExtract;
773 RINOK(extractCallback->GetStream(index, &outStream, askMode));
774
775 if (item.IsDir())
776 {
777 RINOK(extractCallback->PrepareOperation(askMode));
778 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
779 continue;
780 }
781
782 totalPackSize += _db.GetItemPackSize(item.Size);
783 totalSize += item.Size;
784
785 if (!testMode && !outStream)
786 continue;
787 RINOK(extractCallback->PrepareOperation(askMode));
788 Int32 res = NExtract::NOperationResult::kDataError;
789 CMyComPtr<ISequentialInStream> inStream;
790 HRESULT hres = GetStream(index, &inStream);
791 if (hres == S_FALSE)
792 res = NExtract::NOperationResult::kDataError;
793 else if (hres == E_NOTIMPL)
794 res = NExtract::NOperationResult::kUnsupportedMethod;
795 else
796 {
797 RINOK(hres);
798 if (inStream)
799 {
800 RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
801 if (copyCoderSpec->TotalSize == item.Size)
802 res = NExtract::NOperationResult::kOK;
803 }
804 }
805 outStream.Release();
806 RINOK(extractCallback->SetOperationResult(res));
807 }
808 return S_OK;
809 COM_TRY_END
810 }
811
GetNumberOfItems(UInt32 * numItems)812 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
813 {
814 *numItems = _db.Refs.Size();
815 return S_OK;
816 }
817
GetStream(UInt32 index,ISequentialInStream ** stream)818 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
819 {
820 COM_TRY_BEGIN
821 *stream = 0;
822 UInt32 itemIndex = _db.Refs[index].Did;
823 const CItem &item = _db.Items[itemIndex];
824 CClusterInStream *streamSpec = new CClusterInStream;
825 CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
826 streamSpec->Stream = _stream;
827 streamSpec->StartOffset = 0;
828
829 bool isLargeStream = (itemIndex == 0 || _db.IsLargeStream(item.Size));
830 int bsLog = isLargeStream ? _db.SectorSizeBits : _db.MiniSectorSizeBits;
831 streamSpec->BlockSizeLog = bsLog;
832 streamSpec->Size = item.Size;
833
834 UInt32 clusterSize = (UInt32)1 << bsLog;
835 UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog;
836 if (numClusters64 >= ((UInt32)1 << 31))
837 return E_NOTIMPL;
838 streamSpec->Vector.ClearAndReserve((unsigned)numClusters64);
839 UInt32 sid = item.Sid;
840 UInt64 size = item.Size;
841
842 if (size != 0)
843 {
844 for (;; size -= clusterSize)
845 {
846 if (isLargeStream)
847 {
848 if (sid >= _db.FatSize)
849 return S_FALSE;
850 streamSpec->Vector.AddInReserved(sid + 1);
851 sid = _db.Fat[sid];
852 }
853 else
854 {
855 UInt64 val = 0;
856 if (sid >= _db.MatSize || !_db.GetMiniCluster(sid, val) || val >= (UInt64)1 << 32)
857 return S_FALSE;
858 streamSpec->Vector.AddInReserved((UInt32)val);
859 sid = _db.Mat[sid];
860 }
861 if (size <= clusterSize)
862 break;
863 }
864 }
865 if (sid != NFatID::kEndOfChain)
866 return S_FALSE;
867 RINOK(streamSpec->InitAndSeek());
868 *stream = streamTemp.Detach();
869 return S_OK;
870 COM_TRY_END
871 }
872
873 REGISTER_ARC_I(
874 "Compound", "msi msp doc xls ppt", 0, 0xE5,
875 kSignature,
876 0,
877 0,
878 NULL)
879
880 }}
881