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