diff options
Diffstat (limited to 'CPP/7zip/Archive/DmgHandler.cpp')
-rw-r--r--[-rwxr-xr-x] | CPP/7zip/Archive/DmgHandler.cpp | 1294 |
1 files changed, 961 insertions, 333 deletions
diff --git a/CPP/7zip/Archive/DmgHandler.cpp b/CPP/7zip/Archive/DmgHandler.cpp index 5040d518..7166f2ce 100755..100644 --- a/CPP/7zip/Archive/DmgHandler.cpp +++ b/CPP/7zip/Archive/DmgHandler.cpp @@ -4,85 +4,115 @@ #include "../../../C/CpuArch.h" -#include "Common/Buffer.h" -#include "Common/ComTry.h" -#include "Common/IntToString.h" -#include "Common/MyXml.h" -#include "Common/UTFConvert.h" +#include "../../Common/ComTry.h" +#include "../../Common/IntToString.h" +#include "../../Common/MyXml.h" +#include "../../Common/UTFConvert.h" -#include "Windows/PropVariant.h" +#include "../../Windows/PropVariant.h" #include "../Common/LimitedStreams.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" #include "../Common/StreamUtils.h" #include "../Compress/BZip2Decoder.h" #include "../Compress/CopyCoder.h" #include "../Compress/ZlibDecoder.h" +#include "Common/OutStreamWithCRC.h" + // #define DMG_SHOW_RAW // #include <stdio.h> #define PRF(x) // x +#define Get16(p) GetBe16(p) #define Get32(p) GetBe32(p) #define Get64(p) GetBe64(p) -static int Base64ToByte(char c) +static const Byte k_Base64Table[256] = { - if (c >= 'A' && c <= 'Z') return c - 'A'; - if (c >= 'a' && c <= 'z') return c - 'a' + 26; - if (c >= '0' && c <= '9') return c - '0' + 52; - if (c == '+') return 62; - if (c == '/') return 63; - if (c == '=') return 0; - return -1; -} + 64,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,62,77,64,77,63,52,53,54,55,56,57,58,59,60,61,77,77,77,77,77,77, + 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,77,77,77,77,77, + 77,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77 +}; -static int Base64ToBin(Byte *dest, const char *src, int srcLen) +static Byte *Base64ToBin(Byte *dest, const char *src) { - int srcPos = 0; - int destPos = 0; - while (srcPos < srcLen) + UInt32 val = 1; + UInt32 c = k_Base64Table[(Byte)(*src++)]; + for (;;) { - Byte buf[4]; - int filled = 0; - while (srcPos < srcLen) + /* + UInt32 c = (Byte)(*src++); + if (c >= 'A') { - int n = Base64ToByte(src[srcPos++]); - if (n >= 0) - { - buf[filled++] = (Byte)n; - if (filled == 4) - break; - } + if (c <= 'Z') c -= 'A'; + else if (c >= 'a' && c <= 'z') c -= 'a' - 26; + else continue; + } + else if (c >= '0') + { + if (c <= '9') c += 52 - '0'; + else if (c == '=') break; + else continue; + } + else if (c == '+') c = 62; + else if (c == '/') c = 63; + else if (c == 0) break; + else continue; + */ + + // UInt32 c = k_Base64Table[(Byte)(*src++)]; + if (c < 64) + { + val = (val << 6) | c; + c = k_Base64Table[(Byte)(*src++)]; + if ((val & ((UInt32)1 << 24)) == 0) + continue; + dest[0] = (Byte)(val >> 16); + dest[1] = (Byte)(val >> 8); + dest[2] = (Byte)(val); + dest += 3; + val = 1; + continue; } - if (filled >= 2) { if (dest) dest[destPos] = (buf[0] << 2) | (buf[1] >> 4); destPos++; } - if (filled >= 3) { if (dest) dest[destPos] = (buf[1] << 4) | (buf[2] >> 2); destPos++; } - if (filled >= 4) { if (dest) dest[destPos] = (buf[2] << 6) | (buf[3] ); destPos++; } + if (c == 64) + break; + c = k_Base64Table[(Byte)(*src++)]; + } + if (val >= ((UInt32)1 << 12)) + { + if (val >= ((UInt32)1 << 18)) + *dest++ = (Byte)(val >> 16); + *dest++ = (Byte)(val); } - return destPos; + return dest; } -static UString GetSizeString(UInt64 value) -{ - wchar_t s[32]; - wchar_t c; - if (value < (UInt64)20000) c = 0; - else if (value < ((UInt64)20000 << 10)) { value >>= 10; c = L'K'; } - else if (value < ((UInt64)20000 << 20)) { value >>= 20; c = L'M'; } - else { value >>= 30; c = L'G'; } - ConvertUInt64ToString(value, s); - int p = MyStringLen(s); - s[p++] = c; - s[p++] = L'\0'; - return s; -} namespace NArchive { namespace NDmg { +enum +{ + METHOD_ZERO_0 = 0, + METHOD_COPY = 1, + METHOD_ZERO_2 = 2, // without file CRC calculation + METHOD_ADC = 0x80000004, + METHOD_ZLIB = 0x80000005, + METHOD_BZIP2 = 0x80000006, + METHOD_COMMENT = 0x7FFFFFFE, // is used to comment "+beg" and "+end" in extra field. + METHOD_END = 0xFFFFFFFF +}; + struct CBlock { UInt32 Type; @@ -92,176 +122,244 @@ struct CBlock UInt64 PackSize; UInt64 GetNextPackOffset() const { return PackPos + PackSize; } + UInt64 GetNextUnpPos() const { return UnpPos + UnpSize; } + + bool IsZeroMethod() const { return Type == METHOD_ZERO_0 || Type == METHOD_ZERO_2; } + bool ThereAreDataInBlock() const { return Type != METHOD_COMMENT && Type != METHOD_END; } +}; + +static const UInt32 kCheckSumType_CRC = 2; + +static const size_t kChecksumSize_Max = 0x80; + +struct CChecksum +{ + UInt32 Type; + UInt32 NumBits; + Byte Data[kChecksumSize_Max]; + + bool IsCrc32() const { return Type == kCheckSumType_CRC && NumBits == 32; } + UInt32 GetCrc32() const { return Get32(Data); } + void Parse(const Byte *p); +}; + +void CChecksum::Parse(const Byte *p) +{ + Type = Get32(p); + NumBits = Get32(p + 4); + memcpy(Data, p + 8, kChecksumSize_Max); }; struct CFile { - CByteBuffer Raw; + UInt64 Size; + UInt64 PackSize; UInt64 StartPos; + AString Name; CRecordVector<CBlock> Blocks; - UInt64 GetUnpackSize() const - { - UInt64 size = 0; - for (int i = 0; i < Blocks.Size(); i++) - size += Blocks[i].UnpSize; - return size; - }; - UInt64 GetPackSize() const - { - UInt64 size = 0; - for (int i = 0; i < Blocks.Size(); i++) - size += Blocks[i].PackSize; - return size; - }; + CChecksum Checksum; + bool FullFileChecksum; + + HRESULT Parse(const Byte *p, UInt32 size); +}; +#ifdef DMG_SHOW_RAW +struct CExtraFile +{ + CByteBuffer Data; AString Name; }; +#endif class CHandler: public IInArchive, + public IInArchiveGetStream, public CMyUnknownImp { CMyComPtr<IInStream> _inStream; - - AString _xml; CObjectVector<CFile> _files; - CRecordVector<int> _fileIndices; + bool _masterCrcError; + + UInt64 _startPos; + UInt64 _phySize; + + #ifdef DMG_SHOW_RAW + CObjectVector<CExtraFile> _extras; + #endif HRESULT Open2(IInStream *stream); HRESULT Extract(IInStream *stream); public: - MY_UNKNOWN_IMP1(IInArchive) + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); }; const UInt32 kXmlSizeMax = ((UInt32)1 << 31) - (1 << 14); -enum -{ - METHOD_ZERO_0 = 0, - METHOD_COPY = 1, - METHOD_ZERO_2 = 2, - METHOD_ADC = 0x80000004, - METHOD_ZLIB = 0x80000005, - METHOD_BZIP2 = 0x80000006, - METHOD_DUMMY = 0x7FFFFFFE, - METHOD_END = 0xFFFFFFFF -}; - -struct CMethodStat -{ - UInt32 NumBlocks; - UInt64 PackSize; - UInt64 UnpSize; - CMethodStat(): NumBlocks(0), PackSize(0), UnpSize(0) {} -}; - struct CMethods { - CRecordVector<CMethodStat> Stats; CRecordVector<UInt32> Types; + CRecordVector<UInt32> ChecksumTypes; + void Update(const CFile &file); - UString GetString() const; + void GetString(AString &s) const; }; void CMethods::Update(const CFile &file) { - for (int i = 0; i < file.Blocks.Size(); i++) - { - const CBlock &b = file.Blocks[i]; - int index = Types.FindInSorted(b.Type); - if (index < 0) - { - index = Types.AddToUniqueSorted(b.Type); - Stats.Insert(index, CMethodStat()); - } - CMethodStat &m = Stats[index]; - m.PackSize += b.PackSize; - m.UnpSize += b.UnpSize; - m.NumBlocks++; - } + ChecksumTypes.AddToUniqueSorted(file.Checksum.Type); + FOR_VECTOR (i, file.Blocks) + Types.AddToUniqueSorted(file.Blocks[i].Type); } -UString CMethods::GetString() const +void CMethods::GetString(AString &res) const { - UString res; - for (int i = 0; i < Types.Size(); i++) + res.Empty(); + unsigned i; + for (i = 0; i < Types.Size(); i++) { - if (i != 0) - res += L' '; - wchar_t buf[32]; - const wchar_t *s; - const CMethodStat &m = Stats[i]; - bool showPack = true; UInt32 type = Types[i]; - switch(type) + if (type == METHOD_COMMENT || type == METHOD_END) + continue; + char buf[16]; + const char *s; + switch (type) { - case METHOD_ZERO_0: s = L"zero0"; showPack = (m.PackSize != 0); break; - case METHOD_ZERO_2: s = L"zero2"; showPack = (m.PackSize != 0); break; - case METHOD_COPY: s = L"copy"; showPack = (m.UnpSize != m.PackSize); break; - case METHOD_ADC: s = L"adc"; break; - case METHOD_ZLIB: s = L"zlib"; break; - case METHOD_BZIP2: s = L"bzip2"; break; - default: ConvertUInt64ToString(type, buf); s = buf; + case METHOD_ZERO_0: s = "Zero0"; break; + case METHOD_ZERO_2: s = "Zero2"; break; + case METHOD_COPY: s = "Copy"; break; + case METHOD_ADC: s = "ADC"; break; + case METHOD_ZLIB: s = "ZLIB"; break; + case METHOD_BZIP2: s = "BZip2"; break; + default: ConvertUInt32ToString(type, buf); s = buf; } + if (!res.IsEmpty()) + res += ' '; res += s; - if (m.NumBlocks != 1) - { - res += L'['; - ConvertUInt64ToString(m.NumBlocks, buf); - res += buf; - res += L']'; - } - res += L'-'; - res += GetSizeString(m.UnpSize); - if (showPack) + } + for (i = 0; i < ChecksumTypes.Size(); i++) + { + UInt32 type = ChecksumTypes[i]; + char buf[32]; + const char *s; + switch (type) { - res += L'-'; - res += GetSizeString(m.PackSize); + case kCheckSumType_CRC: s = "CRC"; break; + default: + ConvertUInt32ToString(type, MyStpCpy(buf, "Check")); + s = buf; } + if (!res.IsEmpty()) + res += ' '; + res += s; } - return res; } -STATPROPSTG kProps[] = +struct CAppleName +{ + bool IsFs; + const char *Ext; + const char *AppleName; +}; + +static const CAppleName k_Names[] = { - { NULL, kpidPath, VT_BSTR}, - { NULL, kpidSize, VT_UI8}, - { NULL, kpidPackSize, VT_UI8}, - { NULL, kpidComment, VT_BSTR}, - { NULL, kpidMethod, VT_BSTR} + { true, "hfs", "Apple_HFS" }, + { true, "hfsx", "Apple_HFSX" }, + { true, "ufs", "Apple_UFS" }, + { false, "free", "Apple_Free" }, + { false, "ddm", "DDM" }, + { false, NULL, "Apple_partition_map" }, + { false, NULL, " GPT " }, + { false, NULL, "MBR" }, + { false, NULL, "Driver" }, + { false, NULL, "Patches" } +}; + +static const unsigned kNumAppleNames = ARRAY_SIZE(k_Names); + +static const Byte kProps[] = +{ + kpidPath, + kpidSize, + kpidPackSize, + kpidCRC, + kpidComment, + kpidMethod }; IMP_IInArchive_Props -STATPROPSTG kArcProps[] = +static const Byte kArcProps[] = { - { NULL, kpidMethod, VT_BSTR}, - { NULL, kpidNumBlocks, VT_UI4} + kpidMethod, + kpidNumBlocks }; STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NWindows::NCOM::CPropVariant prop; - switch(propID) + switch (propID) { case kpidMethod: { CMethods m; - for (int i = 0; i < _files.Size(); i++) + FOR_VECTOR (i, _files) m.Update(_files[i]); - prop = m.GetString(); + AString s; + m.GetString(s); + if (!s.IsEmpty()) + prop = s; break; } case kpidNumBlocks: { UInt64 numBlocks = 0; - for (int i = 0; i < _files.Size(); i++) + FOR_VECTOR (i, _files) numBlocks += _files[i].Blocks.Size(); prop = numBlocks; break; } + case kpidMainSubfile: + { + int mainIndex = -1; + int numFS = 0; + int numUnknown = 0; + FOR_VECTOR (i, _files) + { + const AString &name = _files[i].Name; + unsigned n; + for (n = 0; n < kNumAppleNames; n++) + { + const CAppleName &appleName = k_Names[n]; + if (name.Find(appleName.AppleName) >= 0) + { + if (appleName.IsFs) + { + numFS++; + mainIndex = i; + } + break; + } + } + if (n == kNumAppleNames) + { + mainIndex = i; + numUnknown++; + } + } + if (numFS + numUnknown == 1) + prop = (UInt32)mainIndex; + break; + } + case kpidWarning: + if (_masterCrcError) + prop = "Master CRC error"; + break; + case kpidOffset: prop = _startPos; break; + case kpidPhySize: prop = _phySize; break; } prop.Detach(value); return S_OK; @@ -270,9 +368,83 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) IMP_IInArchive_ArcProps +HRESULT CFile::Parse(const Byte *p, UInt32 size) +{ + const UInt32 kHeadSize = 0xCC; + if (size < kHeadSize) + return S_FALSE; + if (Get32(p) != 0x6D697368) // "mish" signature + return S_FALSE; + if (Get32(p + 4) != 1) // version + return S_FALSE; + // UInt64 firstSectorNumber = Get64(p + 8); + UInt64 numSectors = Get64(p + 0x10); + + StartPos = Get64(p + 0x18); + + // UInt32 decompressedBufRequested = Get32(p + 0x20); // ??? + // UInt32 blocksDescriptor = Get32(p + 0x24); // number starting from -1? + // char Reserved1[24]; + + Checksum.Parse(p + 0x40); + PRF(printf("\n\nChecksum Type = %2d", Checksum.Type)); + + UInt32 numBlocks = Get32(p + 0xC8); + if (numBlocks > ((UInt32)1 << 28)) + return S_FALSE; + + const UInt32 kRecordSize = 40; + if (numBlocks * kRecordSize + kHeadSize != size) + return S_FALSE; + + PackSize = 0; + Size = 0; + Blocks.ClearAndReserve(numBlocks); + FullFileChecksum = true; + + p += kHeadSize; + UInt32 i; + for (i = 0; i < numBlocks; i++, p += kRecordSize) + { + CBlock b; + b.Type = Get32(p); + b.UnpPos = Get64(p + 0x08) << 9; + b.UnpSize = Get64(p + 0x10) << 9; + b.PackPos = Get64(p + 0x18); + b.PackSize = Get64(p + 0x20); + + // b.PackPos can be 0 for some types. So we don't check it + if (!Blocks.IsEmpty()) + if (b.UnpPos != Blocks.Back().GetNextUnpPos()) + return S_FALSE; + + PRF(printf("\nType=%8x m[1]=%8x uPos=%8x uSize=%7x pPos=%8x pSize=%7x", + b.Type, Get32(p + 4), (UInt32)b.UnpPos, (UInt32)b.UnpSize, (UInt32)b.PackPos, (UInt32)b.PackSize)); + + if (b.Type == METHOD_COMMENT) + continue; + if (b.Type == METHOD_END) + break; + PackSize += b.PackSize; + if (b.UnpSize != 0) + { + if (b.Type == METHOD_ZERO_2) + FullFileChecksum = false; + Blocks.AddInReserved(b); + } + } + if (i != numBlocks - 1) + return S_FALSE; + if (!Blocks.IsEmpty()) + Size = Blocks.Back().GetNextUnpPos(); + if (Size != (numSectors << 9)) + return S_FALSE; + return S_OK; +} + static int FindKeyPair(const CXmlItem &item, const AString &key, const AString &nextTag) { - for (int i = 0; i + 1 < item.SubItems.Size(); i++) + for (unsigned i = 0; i + 1 < item.SubItems.Size(); i++) { const CXmlItem &si = item.SubItems[i]; if (si.IsTagged("key") && si.GetSubString() == key && item.SubItems[i + 1].IsTagged(nextTag)) @@ -281,134 +453,308 @@ static int FindKeyPair(const CXmlItem &item, const AString &key, const AString & return -1; } -static AString GetStringFromKeyPair(const CXmlItem &item, const AString &key, const AString &nextTag) +static const AString *GetStringFromKeyPair(const CXmlItem &item, const AString &key, const AString &nextTag) { int index = FindKeyPair(item, key, nextTag); if (index >= 0) - return item.SubItems[index].GetSubString(); - return AString(); + return item.SubItems[index].GetSubStringPtr(); + return NULL; +} + +static const unsigned HEADER_SIZE = 0x200; + +static bool IsKoly(const Byte *p) +{ + if (Get32(p) != 0x6B6F6C79) // "koly" signature + return false; + if (Get32(p + 4) != 4) // version + return false; + if (Get32(p + 8) != HEADER_SIZE) + return false; + return true; } HRESULT CHandler::Open2(IInStream *stream) { - const int HEADER_SIZE = 0x1E0; + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_startPos)); - UInt64 headerPos; - RINOK(stream->Seek(-HEADER_SIZE, STREAM_SEEK_END, &headerPos)); Byte buf[HEADER_SIZE]; RINOK(ReadStream_FALSE(stream, buf, HEADER_SIZE)); - UInt64 address1 = Get64(buf + 0); - UInt64 address2 = Get64(buf + 0xB8); - UInt64 size64 = Get64(buf + 0xC0); - if (address1 != address2 || size64 >= kXmlSizeMax || size64 == 0 || - address1 >= headerPos || address1 + size64 > headerPos) - return S_FALSE; - RINOK(stream->Seek(address1, STREAM_SEEK_SET, NULL)); - size_t size = (size_t)size64; - char *ss = _xml.GetBuffer((int)size + 1); - RINOK(ReadStream_FALSE(stream, ss, size)); - ss[size] = 0; - _xml.ReleaseBuffer(); + UInt64 headerPos; + if (IsKoly(buf)) + headerPos = _startPos; + else + { + RINOK(stream->Seek(0, STREAM_SEEK_END, &headerPos)); + if (headerPos < HEADER_SIZE) + return S_FALSE; + headerPos -= HEADER_SIZE; + RINOK(stream->Seek(headerPos, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, buf, HEADER_SIZE)); + if (!IsKoly(buf)) + return S_FALSE; + } - CXml xml; - if (!xml.Parse(_xml)) - return S_FALSE; - if (xml.Root.Name != "plist") + // UInt32 flags = Get32(buf + 12); + // UInt64 runningDataForkOffset = Get64(buf + 0x10); + UInt64 dataForkOffset = Get64(buf + 0x18); + UInt64 dataForkLen = Get64(buf + 0x20); + UInt64 rsrcOffset = Get64(buf + 0x28); + UInt64 rsrcLen = Get64(buf + 0x30); + // UInt32 segmentNumber = Get32(buf + 0x38); + // UInt32 segmentCount = Get32(buf + 0x3C); + // Byte segmentGUID[16]; + // CChecksum dataForkChecksum; + // dataForkChecksum.Parse(buf + 0x50); + UInt64 xmlOffset = Get64(buf + 0xD8); + UInt64 xmlLen = Get64(buf + 0xE0); + + UInt64 totalLen = dataForkLen + rsrcLen + xmlLen; + if (totalLen > headerPos) return S_FALSE; - - int dictIndex = xml.Root.FindSubTag("dict"); - if (dictIndex < 0) - return S_FALSE; - - const CXmlItem &dictItem = xml.Root.SubItems[dictIndex]; - int rfDictIndex = FindKeyPair(dictItem, "resource-fork", "dict"); - if (rfDictIndex < 0) - return S_FALSE; - - const CXmlItem &rfDictItem = dictItem.SubItems[rfDictIndex]; - int arrIndex = FindKeyPair(rfDictItem, "blkx", "array"); - if (arrIndex < 0) + _startPos = headerPos - totalLen; + _phySize = totalLen + HEADER_SIZE; + headerPos = totalLen; + + if (headerPos < dataForkOffset || + headerPos < dataForkOffset + dataForkLen || + headerPos < rsrcOffset || + headerPos < rsrcOffset + rsrcLen || + headerPos < xmlOffset || + headerPos < xmlOffset + xmlLen) return S_FALSE; - const CXmlItem &arrItem = rfDictItem.SubItems[arrIndex]; + // Byte reserved[0x78] + + CChecksum masterChecksum; + masterChecksum.Parse(buf + 0x160); - int i; - for (i = 0; i < arrItem.SubItems.Size(); i++) + // UInt32 imageVariant = Get32(buf + 0x1E8); + // UInt64 numSectors = Get64(buf + 0x1EC); + // Byte reserved[0x12] + + const UInt32 RSRC_HEAD_SIZE = 0x100; + + // We don't know the size of the field "offset" in rsrc. + // We suppose that it uses 24 bits. So we use Rsrc, only if the rsrcLen < (1 << 24). + bool useRsrc = (rsrcLen > RSRC_HEAD_SIZE && rsrcLen < ((UInt32)1 << 24)); + // useRsrc = false; + + if (useRsrc) { - const CXmlItem &item = arrItem.SubItems[i]; - if (!item.IsTagged("dict")) - continue; + #ifdef DMG_SHOW_RAW + CExtraFile &extra = _extras.AddNew(); + extra.Name = "rsrc.bin"; + CByteBuffer &rsrcBuf = extra.Data; + #else + CByteBuffer rsrcBuf; + #endif + + size_t rsrcLenT = (size_t)rsrcLen; + rsrcBuf.Alloc(rsrcLenT); + RINOK(stream->Seek(_startPos + rsrcOffset, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, rsrcBuf, rsrcLenT)); + + const Byte *p = rsrcBuf; + UInt32 headSize = Get32(p + 0); + UInt32 footerOffset = Get32(p + 4); + UInt32 mainDataSize = Get32(p + 8); + UInt32 footerSize = Get32(p + 12); + if (headSize != RSRC_HEAD_SIZE || + footerOffset >= rsrcLenT || + mainDataSize >= rsrcLenT || + footerOffset + footerSize != rsrcLenT || + footerOffset != headSize + mainDataSize) + return S_FALSE; + if (footerSize < 16) + return S_FALSE; + if (memcmp(p, p + footerOffset, 16) != 0) + return S_FALSE; - CFile file; - file.StartPos = 0; + p += footerOffset; - int destLen; + if ((UInt32)Get16(p + 0x18) != 0x1C) + return S_FALSE; + UInt32 namesOffset = Get16(p + 0x1A); + if (namesOffset > footerSize) + return S_FALSE; + + UInt32 numItems = (UInt32)Get16(p + 0x1C) + 1; + if (numItems * 8 + 0x1E > namesOffset) + return S_FALSE; + + for (UInt32 i = 0; i < numItems; i++) { - AString dataString; - AString name = GetStringFromKeyPair(item, "Name", "string"); - if (name.IsEmpty()) - name = GetStringFromKeyPair(item, "CFName", "string"); - file.Name = name; - dataString = GetStringFromKeyPair(item, "Data", "data"); - - destLen = Base64ToBin(NULL, dataString, dataString.Length()); - file.Raw.SetCapacity(destLen); - Base64ToBin(file.Raw, dataString, dataString.Length()); - } + const Byte *p2 = p + 0x1E + i * 8; + + UInt32 typeId = Get32(p2); + if (typeId != 0x626C6B78) // blkx + continue; + + UInt32 numFiles = (UInt32)Get16(p2 + 4) + 1; + UInt32 offs = Get16(p2 + 6); + if (0x1C + offs + 12 * numFiles > namesOffset) + return S_FALSE; - if (destLen > 0xCC && Get32(file.Raw) == 0x6D697368) + for (UInt32 k = 0; k < numFiles; k++) + { + const Byte *p3 = p + 0x1C + offs + k * 12; + // UInt32 id = Get16(p3); + UInt32 namePos = Get16(p3 + 2); + // Byte attributes = p3[4]; // = 0x50 for blkx + // we don't know how many bits we can use. So we use 24 bits only + UInt32 blockOffset = Get32(p3 + 4); + blockOffset &= (((UInt32)1 << 24) - 1); + // UInt32 unknown2 = Get32(p3 + 8); // ??? + if (blockOffset + 4 >= mainDataSize) + return S_FALSE; + const Byte *pBlock = rsrcBuf + headSize + blockOffset; + UInt32 blockSize = Get32(pBlock); + + #ifdef DMG_SHOW_RAW + { + CExtraFile &extra = _extras.AddNew(); + { + char extraName[16]; + ConvertUInt32ToString(_files.Size(), extraName); + extra.Name = extraName; + } + CByteBuffer &rawBuf = extra.Data; + rawBuf.SetCapacity(blockSize); + memcpy(rawBuf, pBlock + 4, blockSize); + } + #endif + + CFile &file = _files.AddNew(); + if (namePos != 0xFFFF) + { + UInt32 namesBlockSize = footerSize - namesOffset; + if (namePos >= namesBlockSize) + return S_FALSE; + const Byte *namePtr = p + namesOffset + namePos; + UInt32 nameLen = *namePtr; + if (namesBlockSize - namePos <= nameLen) + return S_FALSE; + for (UInt32 r = 1; r <= nameLen; r++) + { + char c = namePtr[r]; + if (c < 0x20 || c >= 0x80) + break; + file.Name += c; + } + } + RINOK(file.Parse(pBlock + 4, blockSize)); + } + } + } + else + { + if (xmlLen >= kXmlSizeMax || xmlLen == 0) + return S_FALSE; + RINOK(stream->Seek(_startPos + dataForkLen, STREAM_SEEK_SET, NULL)); + size_t size = (size_t)xmlLen; + + CXml xml; { - PRF(printf("\n\n index = %d", _files.Size())); - const int kRecordSize = 40; - for (int offset = 0xCC; offset + kRecordSize <= destLen; offset += kRecordSize) + AString xmlStr; + char *ss = xmlStr.GetBuffer((int)size + 1); + RINOK(ReadStream_FALSE(stream, ss, size)); + ss[size] = 0; { - const Byte *p = (const Byte *)file.Raw + offset; - CBlock b; - b.Type = Get32(p); - if (b.Type == METHOD_END) - break; - if (b.Type == METHOD_DUMMY) - continue; - - b.UnpPos = Get64(p + 0x08) << 9; - b.UnpSize = Get64(p + 0x10) << 9; - b.PackPos = Get64(p + 0x18); - b.PackSize = Get64(p + 0x20); - - file.Blocks.Add(b); - - PRF(printf("\nType=%8x m[1]=%8x uPos=%8x uSize=%7x pPos=%8x pSize=%7x", - b.Type, Get32(p + 4), (UInt32)b.UnpPos, (UInt32)b.UnpSize, (UInt32)b.PackPos, (UInt32)b.PackSize)); + const char *p = ss; + for (;;) + { + if (*p == 0) break; p++; + if (*p == 0) break; p++; + if (*p == 0) break; p++; + if (*p == 0) break; p++; + } + xmlStr.ReleaseBuffer((int)(p - ss)); } + if (!xml.Parse(xmlStr)) + return S_FALSE; + + #ifdef DMG_SHOW_RAW + CExtraFile &extra = _extras.AddNew(); + extra.Name = "a.xml"; + extra.Data.SetCapacity(size); + memcpy(extra.Data, ss, size); + #endif } - int itemIndex = _files.Add(file); - if (file.Blocks.Size() > 0) + if (xml.Root.Name != "plist") + return S_FALSE; + + int dictIndex = xml.Root.FindSubTag("dict"); + if (dictIndex < 0) + return S_FALSE; + + const CXmlItem &dictItem = xml.Root.SubItems[dictIndex]; + int rfDictIndex = FindKeyPair(dictItem, "resource-fork", "dict"); + if (rfDictIndex < 0) + return S_FALSE; + + const CXmlItem &rfDictItem = dictItem.SubItems[rfDictIndex]; + int arrIndex = FindKeyPair(rfDictItem, "blkx", "array"); + if (arrIndex < 0) + return S_FALSE; + + const CXmlItem &arrItem = rfDictItem.SubItems[arrIndex]; + + FOR_VECTOR (i, arrItem.SubItems) { - // if (file.Name.Find("HFS") >= 0) - _fileIndices.Add(itemIndex); + const CXmlItem &item = arrItem.SubItems[i]; + if (!item.IsTagged("dict")) + continue; + + CByteBuffer rawBuf; + int destLen = 0; + { + const AString *dataString = GetStringFromKeyPair(item, "Data", "data"); + if (!dataString) + return S_FALSE; + destLen = dataString->Len() / 4 * 3 + 4; + rawBuf.Alloc(destLen); + destLen = (int)(Base64ToBin(rawBuf, *dataString) - rawBuf); + #ifdef DMG_SHOW_RAW + CExtraFile &extra = _extras.AddNew(); + { + char extraName[16]; + ConvertUInt32ToString(_files.Size(), extraName); + extra.Name = extraName; + } + extra.Data.SetCapacity(destLen); + memcpy(extra.Data, rawBuf, destLen); + #endif + } + CFile &file = _files.AddNew(); + { + const AString *name = GetStringFromKeyPair(item, "Name", "string"); + if (!name || name->IsEmpty()) + name = GetStringFromKeyPair(item, "CFName", "string"); + if (name) + file.Name = *name; + } + RINOK(file.Parse(rawBuf, destLen)); } } - - // PackPos for each new file is 0 in some DMG files. So we use additional StartPos - bool allStartAreZeros = true; - for (i = 0; i < _files.Size(); i++) - { - const CFile &file = _files[i]; - if (!file.Blocks.IsEmpty() && file.Blocks[0].PackPos != 0) - allStartAreZeros = false; - } - UInt64 startPos = 0; - if (allStartAreZeros) + if (masterChecksum.IsCrc32()) { + UInt32 crc = CRC_INIT_VAL; + unsigned i; for (i = 0; i < _files.Size(); i++) { - CFile &file = _files[i]; - file.StartPos = startPos; - if (!file.Blocks.IsEmpty()) - startPos += file.Blocks.Back().GetNextPackOffset(); + const CChecksum &cs = _files[i].Checksum; + if ((cs.NumBits & 0x7) != 0) + break; + UInt32 len = cs.NumBits >> 3; + if (len > kChecksumSize_Max) + break; + crc = CrcUpdate(crc, cs.Data, (size_t)len); } + if (i == _files.Size()) + _masterCrcError = (CRC_GET_DIGEST(crc) != masterChecksum.GetCrc32()); } return S_OK; @@ -431,24 +777,27 @@ STDMETHODIMP CHandler::Open(IInStream *stream, STDMETHODIMP CHandler::Close() { + _phySize = 0; _inStream.Release(); - _fileIndices.Clear(); _files.Clear(); - _xml.Empty(); + _masterCrcError = false; + #ifdef DMG_SHOW_RAW + _extras.Clear(); + #endif return S_OK; } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { - *numItems = _fileIndices.Size() + *numItems = _files.Size() #ifdef DMG_SHOW_RAW - + _files.Size() + 1; + + _extras.Size() #endif ; return S_OK; } -#define RAW_PREFIX L"raw" WSTRING_PATH_SEPARATOR +#define RAW_PREFIX "raw" STRING_PATH_SEPARATOR STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { @@ -456,69 +805,58 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val NWindows::NCOM::CPropVariant prop; #ifdef DMG_SHOW_RAW - if ((int)index == _fileIndices.Size()) + if ((int)index >= _files.Size()) { - switch(propID) + const CExtraFile &extra = _extras[index - _files.Size()]; + switch (propID) { case kpidPath: - prop = RAW_PREFIX L"a.xml"; + prop = (AString)RAW_PREFIX + extra.Name; break; case kpidSize: case kpidPackSize: - prop = (UInt64)_xml.Length(); + prop = (UInt64)extra.Data.Size(); break; } } - else if ((int)index > _fileIndices.Size()) + else + #endif { - int rawIndex = (int)index - (_fileIndices.Size() + 1); - switch(propID) + const CFile &item = _files[index]; + switch (propID) { - case kpidPath: + case kpidSize: prop = item.Size; break; + case kpidPackSize: prop = item.PackSize; break; + case kpidCRC: { - wchar_t s[32] = RAW_PREFIX; - ConvertUInt64ToString(rawIndex, s + MyStringLen(s)); - prop = s; + if (item.Checksum.IsCrc32() && item.FullFileChecksum) + prop = item.Checksum.GetCrc32(); break; } - case kpidSize: - case kpidPackSize: - prop = (UInt64)_files[rawIndex].Raw.GetCapacity(); - break; - } - } - else - #endif - { - int itemIndex = _fileIndices[index]; - const CFile &item = _files[itemIndex]; - switch(propID) - { + case kpidMethod: { CMethods m; m.Update(item); - UString resString = m.GetString(); - if (!resString.IsEmpty()) - prop = resString; + AString s; + m.GetString(s); + if (!s.IsEmpty()) + prop = s; break; } - // case kpidExtension: prop = L"hfs"; break; - case kpidPath: { - // break; UString name; - wchar_t s[32]; - ConvertUInt64ToString(index, s); + wchar_t s[16]; + ConvertUInt32ToString(index, s); name = s; - int num = 10; - int numDigits; - for (numDigits = 1; num < _fileIndices.Size(); numDigits++) + unsigned num = 10; + unsigned numDigits; + for (numDigits = 1; num < _files.Size(); numDigits++) num *= 10; - while (name.Length() < numDigits) - name = L'0' + name; + while (name.Len() < numDigits) + name.InsertAtFront(L'0'); AString subName; int pos1 = item.Name.Find('('); @@ -528,23 +866,27 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val int pos2 = item.Name.Find(')', pos1); if (pos2 >= 0) { - subName = item.Name.Mid(pos1, pos2 - pos1); + subName.SetFrom(item.Name.Ptr(pos1), pos2 - pos1); pos1 = subName.Find(':'); if (pos1 >= 0) - subName = subName.Left(pos1); + subName.DeleteFrom(pos1); } } subName.Trim(); if (!subName.IsEmpty()) { - if (subName == "Apple_HFS") - subName = "hfs"; - else if (subName == "Apple_HFSX") - subName = "hfsx"; - else if (subName == "Apple_Free") - subName = "free"; - else if (subName == "DDM") - subName = "ddm"; + for (unsigned n = 0; n < kNumAppleNames; n++) + { + const CAppleName &appleName = k_Names[n]; + if (appleName.Ext) + { + if (subName == appleName.AppleName) + { + subName = appleName.Ext; + break; + } + } + } UString name2; ConvertUTF8ToUnicode(subName, name2); name += L'.'; @@ -561,6 +903,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val prop = name; break; } + case kpidComment: { UString name; @@ -568,9 +911,6 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val prop = name; break; } - - case kpidSize: prop = item.GetUnpackSize(); break; - case kpidPackSize: prop = item.GetPackSize(); break; } } prop.Detach(value); @@ -585,11 +925,13 @@ class CAdcDecoder: CLzOutWindow m_OutWindowStream; CInBuffer m_InStream; + /* void ReleaseStreams() { m_OutWindowStream.ReleaseStream(); m_InStream.ReleaseStream(); } + */ class CCoderReleaser { @@ -601,7 +943,7 @@ class CAdcDecoder: { if (NeedFlush) m_Coder->m_OutWindowStream.Flush(); - m_Coder->ReleaseStreams(); + // m_Coder->ReleaseStreams(); } }; friend class CCoderReleaser; @@ -711,7 +1053,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN - bool allFilesMode = (numItems == (UInt32)-1); + bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = _files.Size(); if (numItems == 0) @@ -722,13 +1064,11 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, { int index = (int)(allFilesMode ? i : indices[i]); #ifdef DMG_SHOW_RAW - if (index == _fileIndices.Size()) - totalSize += _xml.Length(); - else if (index > _fileIndices.Size()) - totalSize += _files[index - (_fileIndices.Size() + 1)].Raw.GetCapacity(); + if (index >= _files.Size()) + totalSize += _extras[index - _files.Size()].Data.Size(); else #endif - totalSize += _files[_fileIndices[index]].GetUnpackSize(); + totalSize += _files[index].Size; } extractCallback->SetTotal(totalSize); @@ -738,8 +1078,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, UInt64 currentUnpSize = 0; const UInt32 kZeroBufSize = (1 << 14); - CByteBuffer zeroBuf; - zeroBuf.SetCapacity(kZeroBufSize); + CByteBuffer zeroBuf(kZeroBufSize); memset(zeroBuf, 0, kZeroBufSize); NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); @@ -774,52 +1113,55 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; Int32 index = allFilesMode ? i : indices[i]; - // const CItemEx &item = _files[index]; RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); - - + if (!testMode && !realOutStream) continue; RINOK(extractCallback->PrepareOperation(askMode)); + + COutStreamWithCRC *outCrcStreamSpec = new COutStreamWithCRC; + CMyComPtr<ISequentialOutStream> outCrcStream = outCrcStreamSpec; + outCrcStreamSpec->SetStream(realOutStream); + bool needCrc = false; + outCrcStreamSpec->Init(needCrc); + CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream; CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); - outStreamSpec->SetStream(realOutStream); + outStreamSpec->SetStream(outCrcStream); realOutStream.Release(); Int32 opRes = NExtract::NOperationResult::kOK; #ifdef DMG_SHOW_RAW - if (index > _fileIndices.Size()) + if (index >= _files.Size()) { - const CByteBuffer &buf = _files[index - (_fileIndices.Size() + 1)].Raw; - outStreamSpec->Init(buf.GetCapacity()); - RINOK(WriteStream(outStream, buf, buf.GetCapacity())); - currentPackSize = currentUnpSize = buf.GetCapacity(); - } - else if (index == _fileIndices.Size()) - { - outStreamSpec->Init(_xml.Length()); - RINOK(WriteStream(outStream, (const char *)_xml, _xml.Length())); - currentPackSize = currentUnpSize = _xml.Length(); + const CByteBuffer &buf = _extras[index - _files.Size()].Data; + outStreamSpec->Init(buf.Size()); + RINOK(WriteStream(outStream, buf, buf.Size())); + currentPackSize = currentUnpSize = buf.Size(); } else #endif { - const CFile &item = _files[_fileIndices[index]]; - currentPackSize = item.GetPackSize(); - currentUnpSize = item.GetUnpackSize(); + const CFile &item = _files[index]; + currentPackSize = item.PackSize; + currentUnpSize = item.Size; + + needCrc = item.Checksum.IsCrc32(); UInt64 unpPos = 0; UInt64 packPos = 0; { - for (int j = 0; j < item.Blocks.Size(); j++) + FOR_VECTOR (j, item.Blocks) { lps->InSize = currentPackTotal + packPos; lps->OutSize = currentUnpTotal + unpPos; RINOK(lps->SetCur()); const CBlock &block = item.Blocks[j]; + if (!block.ThereAreDataInBlock()) + continue; packPos += block.PackSize; if (block.UnpPos != unpPos) @@ -828,26 +1170,28 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, break; } - RINOK(_inStream->Seek(item.StartPos + block.PackPos, STREAM_SEEK_SET, NULL)); + RINOK(_inStream->Seek(_startPos + item.StartPos + block.PackPos, STREAM_SEEK_SET, NULL)); streamSpec->Init(block.PackSize); - // UInt64 startSize = outStreamSpec->GetSize(); bool realMethod = true; outStreamSpec->Init(block.UnpSize); HRESULT res = S_OK; - switch(block.Type) + outCrcStreamSpec->EnableCalc(needCrc); + + switch (block.Type) { case METHOD_ZERO_0: case METHOD_ZERO_2: realMethod = false; if (block.PackSize != 0) - opRes = NExtract::NOperationResult::kUnSupportedMethod; + opRes = NExtract::NOperationResult::kUnsupportedMethod; + outCrcStreamSpec->EnableCalc(block.Type == METHOD_ZERO_0); break; case METHOD_COPY: if (block.UnpSize != block.PackSize) { - opRes = NExtract::NOperationResult::kUnSupportedMethod; + opRes = NExtract::NOperationResult::kUnsupportedMethod; break; } res = copyCoder->Code(inStream, outStream, NULL, NULL, progress); @@ -862,6 +1206,9 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, case METHOD_ZLIB: { res = zlibCoder->Code(inStream, outStream, NULL, NULL, progress); + if (res == S_OK) + if (zlibCoderSpec->GetInputProcessedSize() != block.PackSize) + opRes = NExtract::NOperationResult::kDataError; break; } @@ -869,13 +1216,13 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, { res = bzip2Coder->Code(inStream, outStream, NULL, NULL, progress); if (res == S_OK) - if (streamSpec->GetSize() != block.PackSize) + if (bzip2CoderSpec->GetInputProcessedSize() != block.PackSize) opRes = NExtract::NOperationResult::kDataError; break; } default: - opRes = NExtract::NOperationResult::kUnSupportedMethod; + opRes = NExtract::NOperationResult::kUnsupportedMethod; break; } if (res != S_OK) @@ -900,6 +1247,11 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, } } } + if (needCrc && opRes == NExtract::NOperationResult::kOK) + { + if (outCrcStreamSpec->GetCRC() != item.Checksum.GetCrc32()) + opRes = NExtract::NOperationResult::kCRCError; + } } outStream.Release(); RINOK(extractCallback->SetOperationResult(opRes)); @@ -908,10 +1260,286 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, COM_TRY_END } -static IInArchive *CreateArc() { return new CHandler; } +struct CChunk +{ + int BlockIndex; + UInt64 AccessMark; + CByteBuffer Buf; +}; + +class CInStream: + public IInStream, + public CMyUnknownImp +{ + UInt64 _virtPos; + int _latestChunk; + int _latestBlock; + UInt64 _accessMark; + CObjectVector<CChunk> _chunks; + + NCompress::NBZip2::CDecoder *bzip2CoderSpec; + CMyComPtr<ICompressCoder> bzip2Coder; + + NCompress::NZlib::CDecoder *zlibCoderSpec; + CMyComPtr<ICompressCoder> zlibCoder; + + CAdcDecoder *adcCoderSpec; + CMyComPtr<ICompressCoder> adcCoder; + + CBufPtrSeqOutStream *outStreamSpec; + CMyComPtr<ISequentialOutStream> outStream; + + CLimitedSequentialInStream *limitedStreamSpec; + CMyComPtr<ISequentialInStream> inStream; + +public: + CMyComPtr<IInStream> Stream; + UInt64 Size; + const CFile *File; + UInt64 _startPos; + + HRESULT InitAndSeek(UInt64 startPos) + { + _startPos = startPos; + _virtPos = 0; + _latestChunk = -1; + _latestBlock = -1; + _accessMark = 0; + + limitedStreamSpec = new CLimitedSequentialInStream; + inStream = limitedStreamSpec; + limitedStreamSpec->SetStream(Stream); + + outStreamSpec = new CBufPtrSeqOutStream; + outStream = outStreamSpec; + return S_OK; + } + + MY_UNKNOWN_IMP1(IInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; + + +int FindBlock(const CRecordVector<CBlock> &blocks, UInt64 pos) +{ + int left = 0, right = blocks.Size(); + for (;;) + { + int mid = (left + right) / 2; + if (mid == left) + return left; + if (pos < blocks[mid].UnpPos) + right = mid; + else + left = mid; + } +} + +STDMETHODIMP CInStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + COM_TRY_BEGIN + + if (processedSize) + *processedSize = 0; + if (size == 0) + return S_OK; + if (_virtPos >= Size) + return S_OK; // (Size == _virtPos) ? S_OK: E_FAIL; + { + UInt64 rem = Size - _virtPos; + if (size > rem) + size = (UInt32)rem; + } + + if (_latestBlock >= 0) + { + const CBlock &block = File->Blocks[_latestBlock]; + if (_virtPos < block.UnpPos || (_virtPos - block.UnpPos) >= block.UnpSize) + _latestBlock = -1; + } + if (_latestBlock < 0) + { + _latestChunk = -1; + int blockIndex = FindBlock(File->Blocks, _virtPos); + const CBlock &block = File->Blocks[blockIndex]; + if (!block.IsZeroMethod() && block.Type != METHOD_COPY) + { + unsigned i; + for (i = 0; i < _chunks.Size(); i++) + if (_chunks[i].BlockIndex == blockIndex) + break; + if (i != _chunks.Size()) + _latestChunk = i; + else + { + const int kNumChunksMax = 128; + int chunkIndex; + if (_chunks.Size() != kNumChunksMax) + chunkIndex = _chunks.Add(CChunk()); + else + { + chunkIndex = 0; + for (i = 0; i < _chunks.Size(); i++) + if (_chunks[i].AccessMark < _chunks[chunkIndex].AccessMark) + chunkIndex = i; + } + CChunk &chunk = _chunks[chunkIndex]; + chunk.BlockIndex = -1; + chunk.AccessMark = 0; + if (chunk.Buf.Size() < block.UnpSize) + { + chunk.Buf.Free(); + if (block.UnpSize > ((UInt32)1 << 31)) + return E_FAIL; + chunk.Buf.Alloc((size_t)block.UnpSize); + } + outStreamSpec->Init(chunk.Buf, (size_t)block.UnpSize); + + RINOK(Stream->Seek(_startPos + File->StartPos + block.PackPos, STREAM_SEEK_SET, NULL)); + + limitedStreamSpec->Init(block.PackSize); + HRESULT res = S_OK; + switch (block.Type) + { + case METHOD_COPY: + if (block.PackSize != block.UnpSize) + return E_FAIL; + res = ReadStream_FAIL(inStream, chunk.Buf, (size_t)block.UnpSize); + break; + + case METHOD_ADC: + if (!adcCoder) + { + adcCoderSpec = new CAdcDecoder(); + adcCoder = adcCoderSpec; + } + res = adcCoder->Code(inStream, outStream, &block.PackSize, &block.UnpSize, NULL); + break; + + case METHOD_ZLIB: + if (!zlibCoder) + { + zlibCoderSpec = new NCompress::NZlib::CDecoder(); + zlibCoder = zlibCoderSpec; + } + res = zlibCoder->Code(inStream, outStream, NULL, NULL, NULL); + if (res == S_OK && zlibCoderSpec->GetInputProcessedSize() != block.PackSize) + res = S_FALSE; + break; + + case METHOD_BZIP2: + if (!bzip2Coder) + { + bzip2CoderSpec = new NCompress::NBZip2::CDecoder(); + bzip2Coder = bzip2CoderSpec; + } + res = bzip2Coder->Code(inStream, outStream, NULL, NULL, NULL); + if (res == S_OK && bzip2CoderSpec->GetInputProcessedSize() != block.PackSize) + res = S_FALSE; + break; + + default: + return E_FAIL; + } + if (res != S_OK) + return res; + if (block.Type != METHOD_COPY && outStreamSpec->GetPos() != block.UnpSize) + return E_FAIL; + chunk.BlockIndex = blockIndex; + _latestChunk = chunkIndex; + } + _chunks[_latestChunk].AccessMark = _accessMark++; + } + _latestBlock = blockIndex; + } + + const CBlock &block = File->Blocks[_latestBlock]; + UInt64 offset = _virtPos - block.UnpPos; + UInt64 rem = block.UnpSize - offset; + if (size > rem) + size = (UInt32)rem; + + HRESULT res = S_OK; + if (block.Type == METHOD_COPY) + { + RINOK(Stream->Seek(_startPos + File->StartPos + block.PackPos + offset, STREAM_SEEK_SET, NULL)); + res = Stream->Read(data, size, &size); + } + else if (block.IsZeroMethod()) + memset(data, 0, size); + else + memcpy(data, _chunks[_latestChunk].Buf + offset, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return res; + COM_TRY_END +} + +STDMETHODIMP CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + switch (seekOrigin) + { + case STREAM_SEEK_SET: break; + case STREAM_SEEK_CUR: offset += _virtPos; break; + case STREAM_SEEK_END: offset += Size; break; + default: return STG_E_INVALIDFUNCTION; + } + if (offset < 0) + return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; + _virtPos = offset; + if (newPosition) + *newPosition = offset; + return S_OK; +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + #ifdef DMG_SHOW_RAW + if (index >= (UInt32)_files.Size()) + return S_FALSE; + #endif + CInStream *spec = new CInStream; + CMyComPtr<ISequentialInStream> specStream = spec; + spec->File = &_files[index]; + const CFile &file = *spec->File; + FOR_VECTOR (i, file.Blocks) + { + const CBlock &block = file.Blocks[i]; + switch (block.Type) + { + case METHOD_ZERO_0: + case METHOD_ZERO_2: + case METHOD_COPY: + case METHOD_ADC: + case METHOD_ZLIB: + case METHOD_BZIP2: + case METHOD_END: + break; + default: + return S_FALSE; + } + } + spec->Stream = _inStream; + spec->Size = spec->File->Size; + RINOK(spec->InitAndSeek(_startPos)); + *stream = specStream.Detach(); + return S_OK; + COM_TRY_END +} + +IMP_CreateArcIn static CArcInfo g_ArcInfo = - { L"Dmg", L"dmg", 0, 0xE4, { 0 }, 0, false, CreateArc, 0 }; + { "Dmg", "dmg", 0, 0xE4, + 12, { 'k','o','l','y', 0, 0, 0, 4, 0, 0, 2, 0 }, + 0, + NArcInfoFlags::kBackwardOpen | + NArcInfoFlags::kUseGlobalOffset, + CreateArc }; REGISTER_ARC(Dmg) |