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