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