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