1 // DmgHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../C/CpuArch.h"
6
7 #include "../../Common/ComTry.h"
8 #include "../../Common/IntToString.h"
9 #include "../../Common/MyXml.h"
10 #include "../../Common/UTFConvert.h"
11
12 #include "../../Windows/PropVariant.h"
13
14 #include "../Common/LimitedStreams.h"
15 #include "../Common/ProgressUtils.h"
16 #include "../Common/RegisterArc.h"
17 #include "../Common/StreamObjects.h"
18 #include "../Common/StreamUtils.h"
19
20 #include "../Compress/BZip2Decoder.h"
21 #include "../Compress/CopyCoder.h"
22 #include "../Compress/LzfseDecoder.h"
23 #include "../Compress/ZlibDecoder.h"
24
25 #include "Common/OutStreamWithCRC.h"
26
27 // #define DMG_SHOW_RAW
28
29 // #include <stdio.h>
30 #define PRF(x) // x
31
32 #define Get16(p) GetBe16(p)
33 #define Get32(p) GetBe32(p)
34 #define Get64(p) GetBe64(p)
35
36 static const Byte k_Base64Table[256] =
37 {
38 66,77,77,77,77,77,77,77,77,65,65,77,77,65,77,77,
39 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
40 65,77,77,77,77,77,77,77,77,77,77,62,77,77,77,63,
41 52,53,54,55,56,57,58,59,60,61,77,77,77,64,77,77,
42 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
43 15,16,17,18,19,20,21,22,23,24,25,77,77,77,77,77,
44 77,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
45 41,42,43,44,45,46,47,48,49,50,51,77,77,77,77,77,
46 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
47 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
48 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
49 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
50 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
51 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
52 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,
53 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77
54 };
55
Base64ToBin(Byte * dest,const char * src)56 static Byte *Base64ToBin(Byte *dest, const char *src)
57 {
58 UInt32 val = 1;
59
60 for (;;)
61 {
62 UInt32 c = k_Base64Table[(Byte)(*src++)];
63
64 if (c < 64)
65 {
66 val = (val << 6) | c;
67 if ((val & ((UInt32)1 << 24)) == 0)
68 continue;
69 dest[0] = (Byte)(val >> 16);
70 dest[1] = (Byte)(val >> 8);
71 dest[2] = (Byte)(val);
72 dest += 3;
73 val = 1;
74 continue;
75 }
76
77 if (c == 65) // space
78 continue;
79
80 if (c == 64) // '='
81 break;
82
83 if (c == 66 && val == 1) // end of string
84 return dest;
85
86 return NULL;
87 }
88
89 if (val < (1 << 12))
90 return NULL;
91
92 if (val & (1 << 18))
93 {
94 *dest++ = (Byte)(val >> 10);
95 *dest++ = (Byte)(val >> 2);
96 }
97 else if (k_Base64Table[(Byte)(*src++)] != 64) // '='
98 return NULL;
99 else
100 *dest++ = (Byte)(val >> 4);
101
102 for (;;)
103 {
104 Byte c = k_Base64Table[(Byte)(*src++)];
105 if (c == 65) // space
106 continue;
107 if (c == 66) // end of string
108 return dest;
109 return NULL;
110 }
111 }
112
113
114 namespace NArchive {
115 namespace NDmg {
116
117 enum
118 {
119 METHOD_ZERO_0 = 0,
120 METHOD_COPY = 1,
121 METHOD_ZERO_2 = 2, // without file CRC calculation
122 METHOD_ADC = 0x80000004,
123 METHOD_ZLIB = 0x80000005,
124 METHOD_BZIP2 = 0x80000006,
125 METHOD_LZFSE = 0x80000007,
126 METHOD_COMMENT = 0x7FFFFFFE, // is used to comment "+beg" and "+end" in extra field.
127 METHOD_END = 0xFFFFFFFF
128 };
129
130 struct CBlock
131 {
132 UInt32 Type;
133 UInt64 UnpPos;
134 UInt64 UnpSize;
135 UInt64 PackPos;
136 UInt64 PackSize;
137
GetNextPackOffsetNArchive::NDmg::CBlock138 UInt64 GetNextPackOffset() const { return PackPos + PackSize; }
GetNextUnpPosNArchive::NDmg::CBlock139 UInt64 GetNextUnpPos() const { return UnpPos + UnpSize; }
140
IsZeroMethodNArchive::NDmg::CBlock141 bool IsZeroMethod() const { return Type == METHOD_ZERO_0 || Type == METHOD_ZERO_2; }
ThereAreDataInBlockNArchive::NDmg::CBlock142 bool ThereAreDataInBlock() const { return Type != METHOD_COMMENT && Type != METHOD_END; }
143 };
144
145 static const UInt32 kCheckSumType_CRC = 2;
146
147 static const size_t kChecksumSize_Max = 0x80;
148
149 struct CChecksum
150 {
151 UInt32 Type;
152 UInt32 NumBits;
153 Byte Data[kChecksumSize_Max];
154
IsCrc32NArchive::NDmg::CChecksum155 bool IsCrc32() const { return Type == kCheckSumType_CRC && NumBits == 32; }
GetCrc32NArchive::NDmg::CChecksum156 UInt32 GetCrc32() const { return Get32(Data); }
157 void Parse(const Byte *p);
158 };
159
Parse(const Byte * p)160 void CChecksum::Parse(const Byte *p)
161 {
162 Type = Get32(p);
163 NumBits = Get32(p + 4);
164 memcpy(Data, p + 8, kChecksumSize_Max);
165 };
166
167 struct CFile
168 {
169 UInt64 Size;
170 UInt64 PackSize;
171 UInt64 StartPos;
172 AString Name;
173 CRecordVector<CBlock> Blocks;
174 CChecksum Checksum;
175 bool FullFileChecksum;
176
177 HRESULT Parse(const Byte *p, UInt32 size);
178 };
179
180 #ifdef DMG_SHOW_RAW
181 struct CExtraFile
182 {
183 CByteBuffer Data;
184 AString Name;
185 };
186 #endif
187
188
189 struct CForkPair
190 {
191 UInt64 Offset;
192 UInt64 Len;
193
ParseNArchive::NDmg::CForkPair194 void Parse(const Byte *p)
195 {
196 Offset = Get64(p);
197 Len = Get64(p + 8);
198 }
199
UpdateTopNArchive::NDmg::CForkPair200 bool UpdateTop(UInt64 limit, UInt64 &top)
201 {
202 if (Offset > limit || Len > limit - Offset)
203 return false;
204 UInt64 top2 = Offset + Len;
205 if (top <= top2)
206 top = top2;
207 return true;
208 }
209 };
210
211
212 class CHandler:
213 public IInArchive,
214 public IInArchiveGetStream,
215 public CMyUnknownImp
216 {
217 CMyComPtr<IInStream> _inStream;
218 CObjectVector<CFile> _files;
219 bool _masterCrcError;
220 bool _headersError;
221
222 UInt64 _startPos;
223 UInt64 _phySize;
224
225 AString _name;
226
227 #ifdef DMG_SHOW_RAW
228 CObjectVector<CExtraFile> _extras;
229 #endif
230
231 HRESULT ReadData(IInStream *stream, const CForkPair &pair, CByteBuffer &buf);
232 bool ParseBlob(const CByteBuffer &data);
233 HRESULT Open2(IInStream *stream);
234 HRESULT Extract(IInStream *stream);
235 public:
236 MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
237 INTERFACE_IInArchive(;)
238 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
239 };
240
241 // that limit can be increased, if there are such dmg files
242 static const size_t kXmlSizeMax = 0xFFFF0000; // 4 GB - 64 KB;
243
244 struct CMethods
245 {
246 CRecordVector<UInt32> Types;
247 CRecordVector<UInt32> ChecksumTypes;
248
249 void Update(const CFile &file);
250 void GetString(AString &s) const;
251 };
252
Update(const CFile & file)253 void CMethods::Update(const CFile &file)
254 {
255 ChecksumTypes.AddToUniqueSorted(file.Checksum.Type);
256 FOR_VECTOR (i, file.Blocks)
257 Types.AddToUniqueSorted(file.Blocks[i].Type);
258 }
259
GetString(AString & res) const260 void CMethods::GetString(AString &res) const
261 {
262 res.Empty();
263
264 unsigned i;
265
266 for (i = 0; i < Types.Size(); i++)
267 {
268 UInt32 type = Types[i];
269 if (type == METHOD_COMMENT || type == METHOD_END)
270 continue;
271 char buf[16];
272 const char *s;
273 switch (type)
274 {
275 case METHOD_ZERO_0: s = "Zero0"; break;
276 case METHOD_ZERO_2: s = "Zero2"; break;
277 case METHOD_COPY: s = "Copy"; break;
278 case METHOD_ADC: s = "ADC"; break;
279 case METHOD_ZLIB: s = "ZLIB"; break;
280 case METHOD_BZIP2: s = "BZip2"; break;
281 case METHOD_LZFSE: s = "LZFSE"; break;
282 default: ConvertUInt32ToString(type, buf); s = buf;
283 }
284 res.Add_OptSpaced(s);
285 }
286
287 for (i = 0; i < ChecksumTypes.Size(); i++)
288 {
289 res.Add_Space_if_NotEmpty();
290 UInt32 type = ChecksumTypes[i];
291 switch (type)
292 {
293 case kCheckSumType_CRC: res += "CRC"; break;
294 default:
295 res += "Check";
296 res.Add_UInt32(type);
297 }
298 }
299 }
300
301 struct CAppleName
302 {
303 bool IsFs;
304 const char *Ext;
305 const char *AppleName;
306 };
307
308 static const CAppleName k_Names[] =
309 {
310 { true, "hfs", "Apple_HFS" },
311 { true, "hfsx", "Apple_HFSX" },
312 { true, "ufs", "Apple_UFS" },
313
314 // efi_sys partition is FAT32, but it's not main file. So we use (IsFs = false)
315 { false, "efi_sys", "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" },
316
317 { false, "free", "Apple_Free" },
318 { false, "ddm", "DDM" },
319 { false, NULL, "Apple_partition_map" },
320 { false, NULL, " GPT " },
321 { false, NULL, "MBR" },
322 { false, NULL, "Driver" },
323 { false, NULL, "Patches" }
324 };
325
326 static const unsigned kNumAppleNames = ARRAY_SIZE(k_Names);
327
328 static const Byte kProps[] =
329 {
330 kpidPath,
331 kpidSize,
332 kpidPackSize,
333 kpidCRC,
334 kpidComment,
335 kpidMethod
336 };
337
338 IMP_IInArchive_Props
339
340 static const Byte kArcProps[] =
341 {
342 kpidMethod,
343 kpidNumBlocks,
344 kpidComment
345 };
346
GetArchiveProperty(PROPID propID,PROPVARIANT * value)347 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
348 {
349 COM_TRY_BEGIN
350 NWindows::NCOM::CPropVariant prop;
351 switch (propID)
352 {
353 case kpidMethod:
354 {
355 CMethods m;
356 FOR_VECTOR (i, _files)
357 m.Update(_files[i]);
358 AString s;
359 m.GetString(s);
360 if (!s.IsEmpty())
361 prop = s;
362 break;
363 }
364 case kpidNumBlocks:
365 {
366 UInt64 numBlocks = 0;
367 FOR_VECTOR (i, _files)
368 numBlocks += _files[i].Blocks.Size();
369 prop = numBlocks;
370 break;
371 }
372 case kpidMainSubfile:
373 {
374 int mainIndex = -1;
375 unsigned numFS = 0;
376 unsigned numUnknown = 0;
377 FOR_VECTOR (i, _files)
378 {
379 const AString &name = _files[i].Name;
380 unsigned n;
381 for (n = 0; n < kNumAppleNames; n++)
382 {
383 const CAppleName &appleName = k_Names[n];
384 // if (name.Find(appleName.AppleName) >= 0)
385 if (strstr(name, appleName.AppleName))
386 {
387 if (appleName.IsFs)
388 {
389 numFS++;
390 mainIndex = i;
391 }
392 break;
393 }
394 }
395 if (n == kNumAppleNames)
396 {
397 mainIndex = i;
398 numUnknown++;
399 }
400 }
401 if (numFS + numUnknown == 1)
402 prop = (UInt32)mainIndex;
403 break;
404 }
405 case kpidWarning:
406 if (_masterCrcError)
407 prop = "Master CRC error";
408
409 case kpidWarningFlags:
410 {
411 UInt32 v = 0;
412 if (_headersError) v |= kpv_ErrorFlags_HeadersError;
413 if (v != 0)
414 prop = v;
415 break;
416 }
417
418 case kpidOffset: prop = _startPos; break;
419 case kpidPhySize: prop = _phySize; break;
420
421 case kpidComment:
422 if (!_name.IsEmpty() && _name.Len() < 256)
423 prop = _name;
424 break;
425
426 case kpidName:
427 if (!_name.IsEmpty() && _name.Len() < 256)
428 {
429 prop = _name + ".dmg";
430 }
431 break;
432 }
433 prop.Detach(value);
434 return S_OK;
435 COM_TRY_END
436 }
437
438 IMP_IInArchive_ArcProps
439
Parse(const Byte * p,UInt32 size)440 HRESULT CFile::Parse(const Byte *p, UInt32 size)
441 {
442 const UInt32 kHeadSize = 0xCC;
443 if (size < kHeadSize)
444 return S_FALSE;
445 if (Get32(p) != 0x6D697368) // "mish" signature
446 return S_FALSE;
447 if (Get32(p + 4) != 1) // version
448 return S_FALSE;
449 // UInt64 firstSectorNumber = Get64(p + 8);
450 UInt64 numSectors = Get64(p + 0x10);
451
452 StartPos = Get64(p + 0x18);
453
454 // UInt32 decompressedBufRequested = Get32(p + 0x20); // ???
455 // UInt32 blocksDescriptor = Get32(p + 0x24); // number starting from -1?
456 // char Reserved1[24];
457
458 Checksum.Parse(p + 0x40);
459 PRF(printf("\n\nChecksum Type = %2d", Checksum.Type));
460
461 UInt32 numBlocks = Get32(p + 0xC8);
462 if (numBlocks > ((UInt32)1 << 28))
463 return S_FALSE;
464
465 const UInt32 kRecordSize = 40;
466 if (numBlocks * kRecordSize + kHeadSize != size)
467 return S_FALSE;
468
469 PackSize = 0;
470 Size = 0;
471 Blocks.ClearAndReserve(numBlocks);
472 FullFileChecksum = true;
473
474 p += kHeadSize;
475 UInt32 i;
476
477 for (i = 0; i < numBlocks; i++, p += kRecordSize)
478 {
479 CBlock b;
480 b.Type = Get32(p);
481 b.UnpPos = Get64(p + 0x08) << 9;
482 b.UnpSize = Get64(p + 0x10) << 9;
483 b.PackPos = Get64(p + 0x18);
484 b.PackSize = Get64(p + 0x20);
485
486 // b.PackPos can be 0 for some types. So we don't check it
487 if (!Blocks.IsEmpty())
488 if (b.UnpPos != Blocks.Back().GetNextUnpPos())
489 return S_FALSE;
490
491 PRF(printf("\nType=%8x m[1]=%8x uPos=%8x uSize=%7x pPos=%8x pSize=%7x",
492 b.Type, Get32(p + 4), (UInt32)b.UnpPos, (UInt32)b.UnpSize, (UInt32)b.PackPos, (UInt32)b.PackSize));
493
494 if (b.Type == METHOD_COMMENT)
495 continue;
496 if (b.Type == METHOD_END)
497 break;
498 PackSize += b.PackSize;
499
500 if (b.UnpSize != 0)
501 {
502 if (b.Type == METHOD_ZERO_2)
503 FullFileChecksum = false;
504 Blocks.AddInReserved(b);
505 }
506 }
507
508 if (i != numBlocks - 1)
509 return S_FALSE;
510 if (!Blocks.IsEmpty())
511 Size = Blocks.Back().GetNextUnpPos();
512 if (Size != (numSectors << 9))
513 return S_FALSE;
514
515 return S_OK;
516 }
517
FindKeyPair(const CXmlItem & item,const char * key,const char * nextTag)518 static int FindKeyPair(const CXmlItem &item, const char *key, const char *nextTag)
519 {
520 for (unsigned i = 0; i + 1 < item.SubItems.Size(); i++)
521 {
522 const CXmlItem &si = item.SubItems[i];
523 if (si.IsTagged("key") && si.GetSubString() == key && item.SubItems[i + 1].IsTagged(nextTag))
524 return i + 1;
525 }
526 return -1;
527 }
528
GetStringFromKeyPair(const CXmlItem & item,const char * key,const char * nextTag)529 static const AString *GetStringFromKeyPair(const CXmlItem &item, const char *key, const char *nextTag)
530 {
531 int index = FindKeyPair(item, key, nextTag);
532 if (index >= 0)
533 return item.SubItems[index].GetSubStringPtr();
534 return NULL;
535 }
536
537 static const unsigned HEADER_SIZE = 0x200;
538
539 static const Byte k_Signature[] = { 'k','o','l','y', 0, 0, 0, 4, 0, 0, 2, 0 };
540
IsKoly(const Byte * p)541 static inline bool IsKoly(const Byte *p)
542 {
543 return memcmp(p, k_Signature, ARRAY_SIZE(k_Signature)) == 0;
544 /*
545 if (Get32(p) != 0x6B6F6C79) // "koly" signature
546 return false;
547 if (Get32(p + 4) != 4) // version
548 return false;
549 if (Get32(p + 8) != HEADER_SIZE)
550 return false;
551 return true;
552 */
553 }
554
555
ReadData(IInStream * stream,const CForkPair & pair,CByteBuffer & buf)556 HRESULT CHandler::ReadData(IInStream *stream, const CForkPair &pair, CByteBuffer &buf)
557 {
558 size_t size = (size_t)pair.Len;
559 if (size != pair.Len)
560 return E_OUTOFMEMORY;
561 buf.Alloc(size);
562 RINOK(stream->Seek(_startPos + pair.Offset, STREAM_SEEK_SET, NULL));
563 return ReadStream_FALSE(stream, buf, size);
564 }
565
566
ParseBlob(const CByteBuffer & data)567 bool CHandler::ParseBlob(const CByteBuffer &data)
568 {
569 if (data.Size() < 12)
570 return false;
571 const Byte *p = (const Byte *)data;
572 if (Get32(p) != 0xFADE0CC0)
573 return true;
574 const UInt32 size = Get32(p + 4);
575 if (size != data.Size())
576 return false;
577 const UInt32 num = Get32(p + 8);
578 if (num > (size - 12) / 8)
579 return false;
580
581 for (UInt32 i = 0; i < num; i++)
582 {
583 // UInt32 type = Get32(p + i * 8 + 12);
584 UInt32 offset = Get32(p + i * 8 + 12 + 4);
585 if (size - offset < 8)
586 return false;
587 const Byte *p2 = (const Byte *)data + offset;
588 const UInt32 magic = Get32(p2);
589 const UInt32 len = Get32(p2 + 4);
590 if (size - offset < len || len < 8)
591 return false;
592
593 #ifdef DMG_SHOW_RAW
594 CExtraFile &extra = _extras.AddNew();
595 extra.Name = "_blob_";
596 extra.Data.CopyFrom(p2, len);
597 #endif
598
599 if (magic == 0xFADE0C02)
600 {
601 #ifdef DMG_SHOW_RAW
602 extra.Name += "codedir";
603 #endif
604
605 if (len < 11 * 4)
606 return false;
607 UInt32 idOffset = Get32(p2 + 0x14);
608 if (idOffset >= len)
609 return false;
610 UInt32 len2 = len - idOffset;
611 if (len2 < (1 << 10))
612 _name.SetFrom_CalcLen((const char *)(p2 + idOffset), len2);
613 }
614 #ifdef DMG_SHOW_RAW
615 else if (magic == 0xFADE0C01)
616 extra.Name += "requirements";
617 else if (magic == 0xFADE0B01)
618 extra.Name += "signed";
619 else
620 {
621 char temp[16];
622 ConvertUInt32ToHex8Digits(magic, temp);
623 extra.Name += temp;
624 }
625 #endif
626 }
627
628 return true;
629 }
630
631
Open2(IInStream * stream)632 HRESULT CHandler::Open2(IInStream *stream)
633 {
634 RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_startPos));
635
636 Byte buf[HEADER_SIZE];
637 RINOK(ReadStream_FALSE(stream, buf, HEADER_SIZE));
638
639 UInt64 headerPos;
640 if (IsKoly(buf))
641 headerPos = _startPos;
642 else
643 {
644 RINOK(stream->Seek(0, STREAM_SEEK_END, &headerPos));
645 if (headerPos < HEADER_SIZE)
646 return S_FALSE;
647 headerPos -= HEADER_SIZE;
648 RINOK(stream->Seek(headerPos, STREAM_SEEK_SET, NULL));
649 RINOK(ReadStream_FALSE(stream, buf, HEADER_SIZE));
650 if (!IsKoly(buf))
651 return S_FALSE;
652 }
653
654 // UInt32 flags = Get32(buf + 12);
655 // UInt64 runningDataForkOffset = Get64(buf + 0x10);
656
657 CForkPair dataForkPair, rsrcPair, xmlPair, blobPair;
658
659 dataForkPair.Parse(buf + 0x18);
660 rsrcPair.Parse(buf + 0x28);
661 xmlPair.Parse(buf + 0xD8);
662 blobPair.Parse(buf + 0x128);
663
664 // UInt32 segmentNumber = Get32(buf + 0x38);
665 // UInt32 segmentCount = Get32(buf + 0x3C);
666 // Byte segmentGUID[16];
667 // CChecksum dataForkChecksum;
668 // dataForkChecksum.Parse(buf + 0x50);
669
670 _startPos = 0;
671
672 UInt64 top = 0;
673 if (!dataForkPair.UpdateTop(headerPos, top)) return S_FALSE;
674 if (!xmlPair.UpdateTop(headerPos, top)) return S_FALSE;
675 if (!rsrcPair.UpdateTop(headerPos, top)) return S_FALSE;
676
677 /* Some old dmg files contain garbage data in blobPair field.
678 So we need to ignore such garbage case;
679 And we still need to detect offset of start of archive for "parser" mode. */
680
681 bool useBlob = blobPair.UpdateTop(headerPos, top);
682
683 _startPos = 0;
684 _phySize = headerPos + HEADER_SIZE;
685
686 if (top != headerPos)
687 {
688 CForkPair xmlPair2 = xmlPair;
689 const char *sz = "<?xml version";
690 const unsigned len = (unsigned)strlen(sz);
691 if (xmlPair2.Len > len)
692 xmlPair2.Len = len;
693 CByteBuffer buf2;
694 if (ReadData(stream, xmlPair2, buf2) != S_OK
695 || memcmp(buf2, sz, len) != 0)
696 {
697 _startPos = headerPos - top;
698 _phySize = top + HEADER_SIZE;
699 }
700 }
701
702 // Byte reserved[0x78]
703
704 if (useBlob && blobPair.Len != 0)
705 {
706 #ifdef DMG_SHOW_RAW
707 CExtraFile &extra = _extras.AddNew();
708 extra.Name = "_blob.bin";
709 CByteBuffer &blobBuf = extra.Data;
710 #else
711 CByteBuffer blobBuf;
712 #endif
713 RINOK(ReadData(stream, blobPair, blobBuf));
714 if (!ParseBlob(blobBuf))
715 _headersError = true;
716 }
717
718
719 CChecksum masterChecksum;
720 masterChecksum.Parse(buf + 0x160);
721
722 // UInt32 imageVariant = Get32(buf + 0x1E8);
723 // UInt64 numSectors = Get64(buf + 0x1EC);
724 // Byte reserved[0x12]
725
726 const UInt32 RSRC_HEAD_SIZE = 0x100;
727
728 // We don't know the size of the field "offset" in rsrc.
729 // We suppose that it uses 24 bits. So we use Rsrc, only if the rsrcLen < (1 << 24).
730 bool useRsrc = (rsrcPair.Len > RSRC_HEAD_SIZE && rsrcPair.Len < ((UInt32)1 << 24));
731 // useRsrc = false;
732
733 if (useRsrc)
734 {
735 #ifdef DMG_SHOW_RAW
736 CExtraFile &extra = _extras.AddNew();
737 extra.Name = "rsrc.bin";
738 CByteBuffer &rsrcBuf = extra.Data;
739 #else
740 CByteBuffer rsrcBuf;
741 #endif
742
743 RINOK(ReadData(stream, rsrcPair, rsrcBuf));
744
745 const Byte *p = rsrcBuf;
746 UInt32 headSize = Get32(p + 0);
747 UInt32 footerOffset = Get32(p + 4);
748 UInt32 mainDataSize = Get32(p + 8);
749 UInt32 footerSize = Get32(p + 12);
750 if (headSize != RSRC_HEAD_SIZE
751 || footerOffset >= rsrcPair.Len
752 || mainDataSize >= rsrcPair.Len
753 || footerOffset + footerSize != rsrcPair.Len
754 || footerOffset != headSize + mainDataSize)
755 return S_FALSE;
756 if (footerSize < 16)
757 return S_FALSE;
758 if (memcmp(p, p + footerOffset, 16) != 0)
759 return S_FALSE;
760
761 p += footerOffset;
762
763 if ((UInt32)Get16(p + 0x18) != 0x1C)
764 return S_FALSE;
765 const UInt32 namesOffset = Get16(p + 0x1A);
766 if (namesOffset > footerSize)
767 return S_FALSE;
768
769 UInt32 numItems = (UInt32)Get16(p + 0x1C) + 1;
770 if (numItems * 8 + 0x1E > namesOffset)
771 return S_FALSE;
772
773 for (UInt32 i = 0; i < numItems; i++)
774 {
775 const Byte *p2 = p + 0x1E + i * 8;
776
777 const UInt32 typeId = Get32(p2);
778
779 #ifndef DMG_SHOW_RAW
780 if (typeId != 0x626C6B78) // blkx
781 continue;
782 #endif
783
784 const UInt32 numFiles = (UInt32)Get16(p2 + 4) + 1;
785 const UInt32 offs = Get16(p2 + 6);
786 if (0x1C + offs + 12 * numFiles > namesOffset)
787 return S_FALSE;
788
789 for (UInt32 k = 0; k < numFiles; k++)
790 {
791 const Byte *p3 = p + 0x1C + offs + k * 12;
792 // UInt32 id = Get16(p3);
793 const UInt32 namePos = Get16(p3 + 2);
794 // Byte attributes = p3[4]; // = 0x50 for blkx
795 // we don't know how many bits we can use. So we use 24 bits only
796 UInt32 blockOffset = Get32(p3 + 4);
797 blockOffset &= (((UInt32)1 << 24) - 1);
798 // UInt32 unknown2 = Get32(p3 + 8); // ???
799 if (blockOffset + 4 >= mainDataSize)
800 return S_FALSE;
801 const Byte *pBlock = rsrcBuf + headSize + blockOffset;
802 const UInt32 blockSize = Get32(pBlock);
803 if (mainDataSize - (blockOffset + 4) < blockSize)
804 return S_FALSE;
805
806 AString name;
807
808 if (namePos != 0xFFFF)
809 {
810 UInt32 namesBlockSize = footerSize - namesOffset;
811 if (namePos >= namesBlockSize)
812 return S_FALSE;
813 const Byte *namePtr = p + namesOffset + namePos;
814 UInt32 nameLen = *namePtr;
815 if (namesBlockSize - namePos <= nameLen)
816 return S_FALSE;
817 for (UInt32 r = 1; r <= nameLen; r++)
818 {
819 Byte c = namePtr[r];
820 if (c < 0x20 || c >= 0x80)
821 break;
822 name += (char)c;
823 }
824 }
825
826 if (typeId == 0x626C6B78) // blkx
827 {
828 CFile &file = _files.AddNew();
829 file.Name = name;
830 RINOK(file.Parse(pBlock + 4, blockSize));
831 }
832
833 #ifdef DMG_SHOW_RAW
834 {
835 AString name2;
836
837 name2.Add_UInt32(i);
838 name2 += '_';
839
840 {
841 char temp[4 + 1] = { 0 };
842 memcpy(temp, p2, 4);
843 name2 += temp;
844 }
845 name2.Trim();
846 name2 += '_';
847 name2.Add_UInt32(k);
848
849 if (!name.IsEmpty())
850 {
851 name2 += '_';
852 name2 += name;
853 }
854
855 CExtraFile &extra = _extras.AddNew();
856 extra.Name = name2;
857 extra.Data.CopyFrom(pBlock + 4, blockSize);
858 }
859 #endif
860 }
861 }
862 }
863 else
864 {
865 if (xmlPair.Len >= kXmlSizeMax || xmlPair.Len == 0)
866 return S_FALSE;
867 size_t size = (size_t)xmlPair.Len;
868 if (size != xmlPair.Len)
869 return S_FALSE;
870
871 RINOK(stream->Seek(_startPos + xmlPair.Offset, STREAM_SEEK_SET, NULL));
872
873 CXml xml;
874 {
875 CObjArray<char> xmlStr(size + 1);
876 RINOK(ReadStream_FALSE(stream, xmlStr, size));
877 xmlStr[size] = 0;
878 // if (strlen(xmlStr) != size) return S_FALSE;
879 if (!xml.Parse(xmlStr))
880 return S_FALSE;
881
882 #ifdef DMG_SHOW_RAW
883 CExtraFile &extra = _extras.AddNew();
884 extra.Name = "a.xml";
885 extra.Data.CopyFrom((const Byte *)(const char *)xmlStr, size);
886 #endif
887 }
888
889 if (xml.Root.Name != "plist")
890 return S_FALSE;
891
892 int dictIndex = xml.Root.FindSubTag("dict");
893 if (dictIndex < 0)
894 return S_FALSE;
895
896 const CXmlItem &dictItem = xml.Root.SubItems[dictIndex];
897 int rfDictIndex = FindKeyPair(dictItem, "resource-fork", "dict");
898 if (rfDictIndex < 0)
899 return S_FALSE;
900
901 const CXmlItem &rfDictItem = dictItem.SubItems[rfDictIndex];
902 int arrIndex = FindKeyPair(rfDictItem, "blkx", "array");
903 if (arrIndex < 0)
904 return S_FALSE;
905
906 const CXmlItem &arrItem = rfDictItem.SubItems[arrIndex];
907
908 FOR_VECTOR (i, arrItem.SubItems)
909 {
910 const CXmlItem &item = arrItem.SubItems[i];
911 if (!item.IsTagged("dict"))
912 continue;
913
914 CByteBuffer rawBuf;
915 unsigned destLen = 0;
916 {
917 const AString *dataString = GetStringFromKeyPair(item, "Data", "data");
918 if (!dataString)
919 return S_FALSE;
920 destLen = dataString->Len() / 4 * 3 + 4;
921 rawBuf.Alloc(destLen);
922 {
923 const Byte *endPtr = Base64ToBin(rawBuf, *dataString);
924 if (!endPtr)
925 return S_FALSE;
926 destLen = (unsigned)(endPtr - (const Byte *)rawBuf);
927 }
928
929 #ifdef DMG_SHOW_RAW
930 CExtraFile &extra = _extras.AddNew();
931 extra.Name.Add_UInt32(_files.Size());
932 extra.Data.CopyFrom(rawBuf, destLen);
933 #endif
934 }
935 CFile &file = _files.AddNew();
936 {
937 const AString *name = GetStringFromKeyPair(item, "Name", "string");
938 if (!name || name->IsEmpty())
939 name = GetStringFromKeyPair(item, "CFName", "string");
940 if (name)
941 file.Name = *name;
942 }
943 RINOK(file.Parse(rawBuf, destLen));
944 }
945 }
946
947 if (masterChecksum.IsCrc32())
948 {
949 UInt32 crc = CRC_INIT_VAL;
950 unsigned i;
951 for (i = 0; i < _files.Size(); i++)
952 {
953 const CChecksum &cs = _files[i].Checksum;
954 if ((cs.NumBits & 0x7) != 0)
955 break;
956 UInt32 len = cs.NumBits >> 3;
957 if (len > kChecksumSize_Max)
958 break;
959 crc = CrcUpdate(crc, cs.Data, (size_t)len);
960 }
961 if (i == _files.Size())
962 _masterCrcError = (CRC_GET_DIGEST(crc) != masterChecksum.GetCrc32());
963 }
964
965 return S_OK;
966 }
967
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)968 STDMETHODIMP CHandler::Open(IInStream *stream,
969 const UInt64 * /* maxCheckStartPosition */,
970 IArchiveOpenCallback * /* openArchiveCallback */)
971 {
972 COM_TRY_BEGIN
973 {
974 Close();
975 if (Open2(stream) != S_OK)
976 return S_FALSE;
977 _inStream = stream;
978 }
979 return S_OK;
980 COM_TRY_END
981 }
982
Close()983 STDMETHODIMP CHandler::Close()
984 {
985 _phySize = 0;
986 _inStream.Release();
987 _files.Clear();
988 _masterCrcError = false;
989 _headersError = false;
990 _name.Empty();
991 #ifdef DMG_SHOW_RAW
992 _extras.Clear();
993 #endif
994 return S_OK;
995 }
996
GetNumberOfItems(UInt32 * numItems)997 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
998 {
999 *numItems = _files.Size()
1000 #ifdef DMG_SHOW_RAW
1001 + _extras.Size()
1002 #endif
1003 ;
1004 return S_OK;
1005 }
1006
1007 #define RAW_PREFIX "raw" STRING_PATH_SEPARATOR
1008
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)1009 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
1010 {
1011 COM_TRY_BEGIN
1012 NWindows::NCOM::CPropVariant prop;
1013
1014 #ifdef DMG_SHOW_RAW
1015 if (index >= _files.Size())
1016 {
1017 const CExtraFile &extra = _extras[index - _files.Size()];
1018 switch (propID)
1019 {
1020 case kpidPath:
1021 prop = (AString)RAW_PREFIX + extra.Name;
1022 break;
1023 case kpidSize:
1024 case kpidPackSize:
1025 prop = (UInt64)extra.Data.Size();
1026 break;
1027 }
1028 }
1029 else
1030 #endif
1031 {
1032 const CFile &item = _files[index];
1033 switch (propID)
1034 {
1035 case kpidSize: prop = item.Size; break;
1036 case kpidPackSize: prop = item.PackSize; break;
1037 case kpidCRC:
1038 {
1039 if (item.Checksum.IsCrc32() && item.FullFileChecksum)
1040 prop = item.Checksum.GetCrc32();
1041 break;
1042 }
1043
1044 case kpidMethod:
1045 {
1046 CMethods m;
1047 m.Update(item);
1048 AString s;
1049 m.GetString(s);
1050 if (!s.IsEmpty())
1051 prop = s;
1052 break;
1053 }
1054
1055 case kpidPath:
1056 {
1057 UString name;
1058 name.Add_UInt32(index);
1059 unsigned num = 10;
1060 unsigned numDigits;
1061 for (numDigits = 1; num < _files.Size(); numDigits++)
1062 num *= 10;
1063 while (name.Len() < numDigits)
1064 name.InsertAtFront(L'0');
1065
1066 AString subName;
1067 int pos1 = item.Name.Find('(');
1068 if (pos1 >= 0)
1069 {
1070 pos1++;
1071 int pos2 = item.Name.Find(')', pos1);
1072 if (pos2 >= 0)
1073 {
1074 subName.SetFrom(item.Name.Ptr(pos1), pos2 - pos1);
1075 pos1 = subName.Find(':');
1076 if (pos1 >= 0)
1077 subName.DeleteFrom(pos1);
1078 }
1079 }
1080 subName.Trim();
1081 if (!subName.IsEmpty())
1082 {
1083 for (unsigned n = 0; n < kNumAppleNames; n++)
1084 {
1085 const CAppleName &appleName = k_Names[n];
1086 if (appleName.Ext)
1087 {
1088 if (subName == appleName.AppleName)
1089 {
1090 subName = appleName.Ext;
1091 break;
1092 }
1093 }
1094 }
1095 UString name2;
1096 ConvertUTF8ToUnicode(subName, name2);
1097 name += '.';
1098 name += name2;
1099 }
1100 else
1101 {
1102 UString name2;
1103 ConvertUTF8ToUnicode(item.Name, name2);
1104 if (!name2.IsEmpty())
1105 name += "_";
1106 name += name2;
1107 }
1108 prop = name;
1109 break;
1110 }
1111
1112 case kpidComment:
1113 {
1114 UString name;
1115 ConvertUTF8ToUnicode(item.Name, name);
1116 prop = name;
1117 break;
1118 }
1119 }
1120 }
1121 prop.Detach(value);
1122 return S_OK;
1123 COM_TRY_END
1124 }
1125
1126 class CAdcDecoder:
1127 public ICompressCoder,
1128 public CMyUnknownImp
1129 {
1130 CLzOutWindow m_OutWindowStream;
1131 CInBuffer m_InStream;
1132
1133 /*
1134 void ReleaseStreams()
1135 {
1136 m_OutWindowStream.ReleaseStream();
1137 m_InStream.ReleaseStream();
1138 }
1139 */
1140
1141 class CCoderReleaser
1142 {
1143 CAdcDecoder *m_Coder;
1144 public:
1145 bool NeedFlush;
CCoderReleaser(CAdcDecoder * coder)1146 CCoderReleaser(CAdcDecoder *coder): m_Coder(coder), NeedFlush(true) {}
~CCoderReleaser()1147 ~CCoderReleaser()
1148 {
1149 if (NeedFlush)
1150 m_Coder->m_OutWindowStream.Flush();
1151 // m_Coder->ReleaseStreams();
1152 }
1153 };
1154 friend class CCoderReleaser;
1155
1156 public:
1157 MY_UNKNOWN_IMP
1158
1159 STDMETHOD(CodeReal)(ISequentialInStream *inStream,
1160 ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
1161 ICompressProgressInfo *progress);
1162
1163 STDMETHOD(Code)(ISequentialInStream *inStream,
1164 ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
1165 ICompressProgressInfo *progress);
1166 };
1167
CodeReal(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 * inSize,const UInt64 * outSize,ICompressProgressInfo * progress)1168 STDMETHODIMP CAdcDecoder::CodeReal(ISequentialInStream *inStream,
1169 ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
1170 ICompressProgressInfo *progress)
1171 {
1172 if (!m_OutWindowStream.Create(1 << 18))
1173 return E_OUTOFMEMORY;
1174 if (!m_InStream.Create(1 << 18))
1175 return E_OUTOFMEMORY;
1176
1177 m_OutWindowStream.SetStream(outStream);
1178 m_OutWindowStream.Init(false);
1179 m_InStream.SetStream(inStream);
1180 m_InStream.Init();
1181
1182 CCoderReleaser coderReleaser(this);
1183
1184 const UInt32 kStep = (1 << 20);
1185 UInt64 nextLimit = kStep;
1186
1187 UInt64 pos = 0;
1188 while (pos < *outSize)
1189 {
1190 if (pos > nextLimit && progress)
1191 {
1192 UInt64 packSize = m_InStream.GetProcessedSize();
1193 RINOK(progress->SetRatioInfo(&packSize, &pos));
1194 nextLimit += kStep;
1195 }
1196 Byte b;
1197 if (!m_InStream.ReadByte(b))
1198 return S_FALSE;
1199 UInt64 rem = *outSize - pos;
1200 if (b & 0x80)
1201 {
1202 unsigned num = (b & 0x7F) + 1;
1203 if (num > rem)
1204 return S_FALSE;
1205 for (unsigned i = 0; i < num; i++)
1206 {
1207 if (!m_InStream.ReadByte(b))
1208 return S_FALSE;
1209 m_OutWindowStream.PutByte(b);
1210 }
1211 pos += num;
1212 continue;
1213 }
1214 Byte b1;
1215 if (!m_InStream.ReadByte(b1))
1216 return S_FALSE;
1217
1218 UInt32 len, distance;
1219
1220 if (b & 0x40)
1221 {
1222 len = ((UInt32)b & 0x3F) + 4;
1223 Byte b2;
1224 if (!m_InStream.ReadByte(b2))
1225 return S_FALSE;
1226 distance = ((UInt32)b1 << 8) + b2;
1227 }
1228 else
1229 {
1230 b &= 0x3F;
1231 len = ((UInt32)b >> 2) + 3;
1232 distance = (((UInt32)b & 3) << 8) + b1;
1233 }
1234
1235 if (distance >= pos || len > rem)
1236 return S_FALSE;
1237 m_OutWindowStream.CopyBlock(distance, len);
1238 pos += len;
1239 }
1240 if (*inSize != m_InStream.GetProcessedSize())
1241 return S_FALSE;
1242 coderReleaser.NeedFlush = false;
1243 return m_OutWindowStream.Flush();
1244 }
1245
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 * inSize,const UInt64 * outSize,ICompressProgressInfo * progress)1246 STDMETHODIMP CAdcDecoder::Code(ISequentialInStream *inStream,
1247 ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
1248 ICompressProgressInfo *progress)
1249 {
1250 try { return CodeReal(inStream, outStream, inSize, outSize, progress);}
1251 catch(const CInBufferException &e) { return e.ErrorCode; }
1252 catch(const CLzOutWindowException &e) { return e.ErrorCode; }
1253 catch(...) { return S_FALSE; }
1254 }
1255
1256
1257
1258
1259
1260
1261
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)1262 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1263 Int32 testMode, IArchiveExtractCallback *extractCallback)
1264 {
1265 COM_TRY_BEGIN
1266 bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1267 if (allFilesMode)
1268 numItems = _files.Size();
1269 if (numItems == 0)
1270 return S_OK;
1271 UInt64 totalSize = 0;
1272 UInt32 i;
1273
1274 for (i = 0; i < numItems; i++)
1275 {
1276 UInt32 index = (allFilesMode ? i : indices[i]);
1277 #ifdef DMG_SHOW_RAW
1278 if (index >= _files.Size())
1279 totalSize += _extras[index - _files.Size()].Data.Size();
1280 else
1281 #endif
1282 totalSize += _files[index].Size;
1283 }
1284 extractCallback->SetTotal(totalSize);
1285
1286 UInt64 currentPackTotal = 0;
1287 UInt64 currentUnpTotal = 0;
1288 UInt64 currentPackSize = 0;
1289 UInt64 currentUnpSize = 0;
1290
1291 const UInt32 kZeroBufSize = (1 << 14);
1292 CByteBuffer zeroBuf(kZeroBufSize);
1293 memset(zeroBuf, 0, kZeroBufSize);
1294
1295 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
1296 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
1297
1298 NCompress::NBZip2::CDecoder *bzip2CoderSpec = new NCompress::NBZip2::CDecoder();
1299 CMyComPtr<ICompressCoder> bzip2Coder = bzip2CoderSpec;
1300
1301 NCompress::NZlib::CDecoder *zlibCoderSpec = new NCompress::NZlib::CDecoder();
1302 CMyComPtr<ICompressCoder> zlibCoder = zlibCoderSpec;
1303
1304 CAdcDecoder *adcCoderSpec = new CAdcDecoder();
1305 CMyComPtr<ICompressCoder> adcCoder = adcCoderSpec;
1306
1307 NCompress::NLzfse::CDecoder *lzfseCoderSpec = new NCompress::NLzfse::CDecoder();
1308 CMyComPtr<ICompressCoder> lzfseCoder = lzfseCoderSpec;
1309
1310 CLocalProgress *lps = new CLocalProgress;
1311 CMyComPtr<ICompressProgressInfo> progress = lps;
1312 lps->Init(extractCallback, false);
1313
1314 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
1315 CMyComPtr<ISequentialInStream> inStream(streamSpec);
1316 streamSpec->SetStream(_inStream);
1317
1318 for (i = 0; i < numItems; i++, currentPackTotal += currentPackSize, currentUnpTotal += currentUnpSize)
1319 {
1320 lps->InSize = currentPackTotal;
1321 lps->OutSize = currentUnpTotal;
1322 currentPackSize = 0;
1323 currentUnpSize = 0;
1324 RINOK(lps->SetCur());
1325 CMyComPtr<ISequentialOutStream> realOutStream;
1326 Int32 askMode = testMode ?
1327 NExtract::NAskMode::kTest :
1328 NExtract::NAskMode::kExtract;
1329 UInt32 index = allFilesMode ? i : indices[i];
1330 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
1331
1332 if (!testMode && !realOutStream)
1333 continue;
1334 RINOK(extractCallback->PrepareOperation(askMode));
1335
1336
1337 COutStreamWithCRC *outCrcStreamSpec = new COutStreamWithCRC;
1338 CMyComPtr<ISequentialOutStream> outCrcStream = outCrcStreamSpec;
1339 outCrcStreamSpec->SetStream(realOutStream);
1340 bool needCrc = false;
1341 outCrcStreamSpec->Init(needCrc);
1342
1343 CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream;
1344 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
1345 outStreamSpec->SetStream(outCrcStream);
1346
1347 realOutStream.Release();
1348
1349 Int32 opRes = NExtract::NOperationResult::kOK;
1350 #ifdef DMG_SHOW_RAW
1351 if (index >= _files.Size())
1352 {
1353 const CByteBuffer &buf = _extras[index - _files.Size()].Data;
1354 outStreamSpec->Init(buf.Size());
1355 RINOK(WriteStream(outStream, buf, buf.Size()));
1356 currentPackSize = currentUnpSize = buf.Size();
1357 }
1358 else
1359 #endif
1360 {
1361 const CFile &item = _files[index];
1362 currentPackSize = item.PackSize;
1363 currentUnpSize = item.Size;
1364
1365 needCrc = item.Checksum.IsCrc32();
1366
1367 UInt64 unpPos = 0;
1368 UInt64 packPos = 0;
1369 {
1370 FOR_VECTOR (j, item.Blocks)
1371 {
1372 lps->InSize = currentPackTotal + packPos;
1373 lps->OutSize = currentUnpTotal + unpPos;
1374 RINOK(lps->SetCur());
1375
1376 const CBlock &block = item.Blocks[j];
1377 if (!block.ThereAreDataInBlock())
1378 continue;
1379
1380 packPos += block.PackSize;
1381 if (block.UnpPos != unpPos)
1382 {
1383 opRes = NExtract::NOperationResult::kDataError;
1384 break;
1385 }
1386
1387 RINOK(_inStream->Seek(_startPos + item.StartPos + block.PackPos, STREAM_SEEK_SET, NULL));
1388 streamSpec->Init(block.PackSize);
1389 bool realMethod = true;
1390 outStreamSpec->Init(block.UnpSize);
1391 HRESULT res = S_OK;
1392
1393 outCrcStreamSpec->EnableCalc(needCrc);
1394
1395 switch (block.Type)
1396 {
1397 case METHOD_ZERO_0:
1398 case METHOD_ZERO_2:
1399 realMethod = false;
1400 if (block.PackSize != 0)
1401 opRes = NExtract::NOperationResult::kUnsupportedMethod;
1402 outCrcStreamSpec->EnableCalc(block.Type == METHOD_ZERO_0);
1403 break;
1404
1405 case METHOD_COPY:
1406 if (block.UnpSize != block.PackSize)
1407 {
1408 opRes = NExtract::NOperationResult::kUnsupportedMethod;
1409 break;
1410 }
1411 res = copyCoder->Code(inStream, outStream, NULL, NULL, progress);
1412 break;
1413
1414 case METHOD_ADC:
1415 {
1416 res = adcCoder->Code(inStream, outStream, &block.PackSize, &block.UnpSize, progress);
1417 break;
1418 }
1419
1420 case METHOD_ZLIB:
1421 {
1422 res = zlibCoder->Code(inStream, outStream, NULL, NULL, progress);
1423 if (res == S_OK)
1424 if (zlibCoderSpec->GetInputProcessedSize() != block.PackSize)
1425 opRes = NExtract::NOperationResult::kDataError;
1426 break;
1427 }
1428
1429 case METHOD_BZIP2:
1430 {
1431 res = bzip2Coder->Code(inStream, outStream, NULL, NULL, progress);
1432 if (res == S_OK)
1433 if (bzip2CoderSpec->GetInputProcessedSize() != block.PackSize)
1434 opRes = NExtract::NOperationResult::kDataError;
1435 break;
1436 }
1437
1438 case METHOD_LZFSE:
1439 {
1440 res = lzfseCoder->Code(inStream, outStream, &block.PackSize, &block.UnpSize, progress);
1441 break;
1442 }
1443
1444 default:
1445 opRes = NExtract::NOperationResult::kUnsupportedMethod;
1446 break;
1447 }
1448
1449 if (res != S_OK)
1450 {
1451 if (res != S_FALSE)
1452 return res;
1453 if (opRes == NExtract::NOperationResult::kOK)
1454 opRes = NExtract::NOperationResult::kDataError;
1455 }
1456
1457 unpPos += block.UnpSize;
1458
1459 if (!outStreamSpec->IsFinishedOK())
1460 {
1461 if (realMethod && opRes == NExtract::NOperationResult::kOK)
1462 opRes = NExtract::NOperationResult::kDataError;
1463
1464 while (outStreamSpec->GetRem() != 0)
1465 {
1466 UInt64 rem = outStreamSpec->GetRem();
1467 UInt32 size = (UInt32)MyMin(rem, (UInt64)kZeroBufSize);
1468 RINOK(WriteStream(outStream, zeroBuf, size));
1469 }
1470 }
1471 }
1472 }
1473
1474 if (needCrc && opRes == NExtract::NOperationResult::kOK)
1475 {
1476 if (outCrcStreamSpec->GetCRC() != item.Checksum.GetCrc32())
1477 opRes = NExtract::NOperationResult::kCRCError;
1478 }
1479 }
1480 outStream.Release();
1481 RINOK(extractCallback->SetOperationResult(opRes));
1482 }
1483
1484 return S_OK;
1485 COM_TRY_END
1486 }
1487
1488 struct CChunk
1489 {
1490 int BlockIndex;
1491 UInt64 AccessMark;
1492 CByteBuffer Buf;
1493 };
1494
1495 class CInStream:
1496 public IInStream,
1497 public CMyUnknownImp
1498 {
1499 UInt64 _virtPos;
1500 int _latestChunk;
1501 int _latestBlock;
1502 UInt64 _accessMark;
1503 CObjectVector<CChunk> _chunks;
1504
1505 NCompress::NBZip2::CDecoder *bzip2CoderSpec;
1506 CMyComPtr<ICompressCoder> bzip2Coder;
1507
1508 NCompress::NZlib::CDecoder *zlibCoderSpec;
1509 CMyComPtr<ICompressCoder> zlibCoder;
1510
1511 CAdcDecoder *adcCoderSpec;
1512 CMyComPtr<ICompressCoder> adcCoder;
1513
1514 NCompress::NLzfse::CDecoder *lzfseCoderSpec;
1515 CMyComPtr<ICompressCoder> lzfseCoder;
1516
1517 CBufPtrSeqOutStream *outStreamSpec;
1518 CMyComPtr<ISequentialOutStream> outStream;
1519
1520 CLimitedSequentialInStream *limitedStreamSpec;
1521 CMyComPtr<ISequentialInStream> inStream;
1522
1523 public:
1524 CMyComPtr<IInStream> Stream;
1525 UInt64 Size;
1526 const CFile *File;
1527 UInt64 _startPos;
1528
InitAndSeek(UInt64 startPos)1529 HRESULT InitAndSeek(UInt64 startPos)
1530 {
1531 _startPos = startPos;
1532 _virtPos = 0;
1533 _latestChunk = -1;
1534 _latestBlock = -1;
1535 _accessMark = 0;
1536
1537 limitedStreamSpec = new CLimitedSequentialInStream;
1538 inStream = limitedStreamSpec;
1539 limitedStreamSpec->SetStream(Stream);
1540
1541 outStreamSpec = new CBufPtrSeqOutStream;
1542 outStream = outStreamSpec;
1543 return S_OK;
1544 }
1545
1546 MY_UNKNOWN_IMP1(IInStream)
1547
1548 STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
1549 STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
1550 };
1551
1552
FindBlock(const CRecordVector<CBlock> & blocks,UInt64 pos)1553 unsigned FindBlock(const CRecordVector<CBlock> &blocks, UInt64 pos)
1554 {
1555 unsigned left = 0, right = blocks.Size();
1556 for (;;)
1557 {
1558 unsigned mid = (left + right) / 2;
1559 if (mid == left)
1560 return left;
1561 if (pos < blocks[mid].UnpPos)
1562 right = mid;
1563 else
1564 left = mid;
1565 }
1566 }
1567
Read(void * data,UInt32 size,UInt32 * processedSize)1568 STDMETHODIMP CInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
1569 {
1570 COM_TRY_BEGIN
1571
1572 if (processedSize)
1573 *processedSize = 0;
1574 if (size == 0)
1575 return S_OK;
1576 if (_virtPos >= Size)
1577 return S_OK; // (Size == _virtPos) ? S_OK: E_FAIL;
1578 {
1579 UInt64 rem = Size - _virtPos;
1580 if (size > rem)
1581 size = (UInt32)rem;
1582 }
1583
1584 if (_latestBlock >= 0)
1585 {
1586 const CBlock &block = File->Blocks[_latestBlock];
1587 if (_virtPos < block.UnpPos || (_virtPos - block.UnpPos) >= block.UnpSize)
1588 _latestBlock = -1;
1589 }
1590
1591 if (_latestBlock < 0)
1592 {
1593 _latestChunk = -1;
1594 unsigned blockIndex = FindBlock(File->Blocks, _virtPos);
1595 const CBlock &block = File->Blocks[blockIndex];
1596
1597 if (!block.IsZeroMethod() && block.Type != METHOD_COPY)
1598 {
1599 unsigned i;
1600 for (i = 0; i < _chunks.Size(); i++)
1601 if (_chunks[i].BlockIndex == (int)blockIndex)
1602 break;
1603
1604 if (i != _chunks.Size())
1605 _latestChunk = i;
1606 else
1607 {
1608 const unsigned kNumChunksMax = 128;
1609 unsigned chunkIndex;
1610
1611 if (_chunks.Size() != kNumChunksMax)
1612 chunkIndex = _chunks.Add(CChunk());
1613 else
1614 {
1615 chunkIndex = 0;
1616 for (i = 0; i < _chunks.Size(); i++)
1617 if (_chunks[i].AccessMark < _chunks[chunkIndex].AccessMark)
1618 chunkIndex = i;
1619 }
1620
1621 CChunk &chunk = _chunks[chunkIndex];
1622 chunk.BlockIndex = -1;
1623 chunk.AccessMark = 0;
1624
1625 if (chunk.Buf.Size() < block.UnpSize)
1626 {
1627 chunk.Buf.Free();
1628 if (block.UnpSize > ((UInt32)1 << 31))
1629 return E_FAIL;
1630 chunk.Buf.Alloc((size_t)block.UnpSize);
1631 }
1632
1633 outStreamSpec->Init(chunk.Buf, (size_t)block.UnpSize);
1634
1635 RINOK(Stream->Seek(_startPos + File->StartPos + block.PackPos, STREAM_SEEK_SET, NULL));
1636
1637 limitedStreamSpec->Init(block.PackSize);
1638 HRESULT res = S_OK;
1639
1640 switch (block.Type)
1641 {
1642 case METHOD_COPY:
1643 if (block.PackSize != block.UnpSize)
1644 return E_FAIL;
1645 res = ReadStream_FAIL(inStream, chunk.Buf, (size_t)block.UnpSize);
1646 break;
1647
1648 case METHOD_ADC:
1649 if (!adcCoder)
1650 {
1651 adcCoderSpec = new CAdcDecoder();
1652 adcCoder = adcCoderSpec;
1653 }
1654 res = adcCoder->Code(inStream, outStream, &block.PackSize, &block.UnpSize, NULL);
1655 break;
1656
1657 case METHOD_ZLIB:
1658 if (!zlibCoder)
1659 {
1660 zlibCoderSpec = new NCompress::NZlib::CDecoder();
1661 zlibCoder = zlibCoderSpec;
1662 }
1663 res = zlibCoder->Code(inStream, outStream, NULL, NULL, NULL);
1664 if (res == S_OK && zlibCoderSpec->GetInputProcessedSize() != block.PackSize)
1665 res = S_FALSE;
1666 break;
1667
1668 case METHOD_BZIP2:
1669 if (!bzip2Coder)
1670 {
1671 bzip2CoderSpec = new NCompress::NBZip2::CDecoder();
1672 bzip2Coder = bzip2CoderSpec;
1673 }
1674 res = bzip2Coder->Code(inStream, outStream, NULL, NULL, NULL);
1675 if (res == S_OK && bzip2CoderSpec->GetInputProcessedSize() != block.PackSize)
1676 res = S_FALSE;
1677 break;
1678
1679 case METHOD_LZFSE:
1680 if (!lzfseCoder)
1681 {
1682 lzfseCoderSpec = new NCompress::NLzfse::CDecoder();
1683 lzfseCoder = lzfseCoderSpec;
1684 }
1685 res = lzfseCoder->Code(inStream, outStream, &block.PackSize, &block.UnpSize, NULL);
1686 break;
1687
1688 default:
1689 return E_FAIL;
1690 }
1691
1692 if (res != S_OK)
1693 return res;
1694 if (block.Type != METHOD_COPY && outStreamSpec->GetPos() != block.UnpSize)
1695 return E_FAIL;
1696 chunk.BlockIndex = blockIndex;
1697 _latestChunk = chunkIndex;
1698 }
1699
1700 _chunks[_latestChunk].AccessMark = _accessMark++;
1701 }
1702
1703 _latestBlock = blockIndex;
1704 }
1705
1706 const CBlock &block = File->Blocks[_latestBlock];
1707 UInt64 offset = _virtPos - block.UnpPos;
1708 UInt64 rem = block.UnpSize - offset;
1709 if (size > rem)
1710 size = (UInt32)rem;
1711
1712 HRESULT res = S_OK;
1713
1714 if (block.Type == METHOD_COPY)
1715 {
1716 RINOK(Stream->Seek(_startPos + File->StartPos + block.PackPos + offset, STREAM_SEEK_SET, NULL));
1717 res = Stream->Read(data, size, &size);
1718 }
1719 else if (block.IsZeroMethod())
1720 memset(data, 0, size);
1721 else if (size != 0)
1722 memcpy(data, _chunks[_latestChunk].Buf + offset, size);
1723
1724 _virtPos += size;
1725 if (processedSize)
1726 *processedSize = size;
1727
1728 return res;
1729 COM_TRY_END
1730 }
1731
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)1732 STDMETHODIMP CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
1733 {
1734 switch (seekOrigin)
1735 {
1736 case STREAM_SEEK_SET: break;
1737 case STREAM_SEEK_CUR: offset += _virtPos; break;
1738 case STREAM_SEEK_END: offset += Size; break;
1739 default: return STG_E_INVALIDFUNCTION;
1740 }
1741 if (offset < 0)
1742 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
1743 _virtPos = offset;
1744 if (newPosition)
1745 *newPosition = offset;
1746 return S_OK;
1747 }
1748
GetStream(UInt32 index,ISequentialInStream ** stream)1749 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
1750 {
1751 COM_TRY_BEGIN
1752
1753 #ifdef DMG_SHOW_RAW
1754 if (index >= (UInt32)_files.Size())
1755 return S_FALSE;
1756 #endif
1757
1758 CInStream *spec = new CInStream;
1759 CMyComPtr<ISequentialInStream> specStream = spec;
1760 spec->File = &_files[index];
1761 const CFile &file = *spec->File;
1762
1763 FOR_VECTOR (i, file.Blocks)
1764 {
1765 const CBlock &block = file.Blocks[i];
1766 switch (block.Type)
1767 {
1768 case METHOD_ZERO_0:
1769 case METHOD_ZERO_2:
1770 case METHOD_COPY:
1771 case METHOD_ADC:
1772 case METHOD_ZLIB:
1773 case METHOD_BZIP2:
1774 case METHOD_LZFSE:
1775 case METHOD_END:
1776 break;
1777 default:
1778 return S_FALSE;
1779 }
1780 }
1781
1782 spec->Stream = _inStream;
1783 spec->Size = spec->File->Size;
1784 RINOK(spec->InitAndSeek(_startPos));
1785 *stream = specStream.Detach();
1786 return S_OK;
1787
1788 COM_TRY_END
1789 }
1790
1791 REGISTER_ARC_I(
1792 "Dmg", "dmg", 0, 0xE4,
1793 k_Signature,
1794 0,
1795 NArcInfoFlags::kBackwardOpen |
1796 NArcInfoFlags::kUseGlobalOffset,
1797 NULL)
1798
1799 }}
1800