diff options
Diffstat (limited to 'CPP/7zip/Archive/ComHandler.cpp')
-rw-r--r-- | CPP/7zip/Archive/ComHandler.cpp | 880 |
1 files changed, 880 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/ComHandler.cpp b/CPP/7zip/Archive/ComHandler.cpp new file mode 100644 index 0000000..a1f643b --- /dev/null +++ b/CPP/7zip/Archive/ComHandler.cpp | |||
@@ -0,0 +1,880 @@ | |||
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 | |||
87 | bool IsEmpty() const { return Type == NItemType::kEmpty; } | ||
88 | 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 | |||
125 | bool IsNotArcType() const | ||
126 | { | ||
127 | return | ||
128 | Type != k_Type_Msi && | ||
129 | Type != k_Type_Msp; | ||
130 | } | ||
131 | |||
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(); | ||
143 | bool IsLargeStream(UInt64 size) const { return size >= LongStreamMinSize; } | ||
144 | UString GetItemPath(UInt32 index) const; | ||
145 | |||
146 | UInt64 GetItemPackSize(UInt64 size) const | ||
147 | { | ||
148 | UInt64 mask = ((UInt64)1 << (IsLargeStream(size) ? SectorSizeBits : MiniSectorSizeBits)) - 1; | ||
149 | return (size + mask) & ~mask; | ||
150 | } | ||
151 | |||
152 | 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 | |||
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 | |||
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 | |||
182 | static void GetFileTimeFromMem(const Byte *p, FILETIME *ft) | ||
183 | { | ||
184 | ft->dwLowDateTime = Get32(p); | ||
185 | ft->dwHighDateTime = Get32(p + 4); | ||
186 | } | ||
187 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
347 | static UString ConvertName(const Byte *p) | ||
348 | { | ||
349 | bool isMsi; | ||
350 | return ConvertName(p, isMsi); | ||
351 | } | ||
352 | |||
353 | 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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
725 | STDMETHODIMP CHandler::Close() | ||
726 | { | ||
727 | _db.Clear(); | ||
728 | _stream.Release(); | ||
729 | return S_OK; | ||
730 | } | ||
731 | |||
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 | |||
812 | STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) | ||
813 | { | ||
814 | *numItems = _db.Refs.Size(); | ||
815 | return S_OK; | ||
816 | } | ||
817 | |||
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 | }} | ||