mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 13:14:59 -06:00
919 lines
23 KiB
C++
Executable File
919 lines
23 KiB
C++
Executable File
// DmgHandler.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#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 "Windows/PropVariant.h"
|
|
|
|
#include "../Common/LimitedStreams.h"
|
|
#include "../Common/ProgressUtils.h"
|
|
#include "../Common/RegisterArc.h"
|
|
#include "../Common/StreamUtils.h"
|
|
|
|
#include "../Compress/BZip2Decoder.h"
|
|
#include "../Compress/CopyCoder.h"
|
|
#include "../Compress/ZlibDecoder.h"
|
|
|
|
// #define DMG_SHOW_RAW
|
|
|
|
// #include <stdio.h>
|
|
#define PRF(x) // x
|
|
|
|
#define Get32(p) GetBe32(p)
|
|
#define Get64(p) GetBe64(p)
|
|
|
|
static int Base64ToByte(char c)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static int Base64ToBin(Byte *dest, const char *src, int srcLen)
|
|
{
|
|
int srcPos = 0;
|
|
int destPos = 0;
|
|
while (srcPos < srcLen)
|
|
{
|
|
Byte buf[4];
|
|
int filled = 0;
|
|
while (srcPos < srcLen)
|
|
{
|
|
int n = Base64ToByte(src[srcPos++]);
|
|
if (n >= 0)
|
|
{
|
|
buf[filled++] = (Byte)n;
|
|
if (filled == 4)
|
|
break;
|
|
}
|
|
}
|
|
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++; }
|
|
}
|
|
return destPos;
|
|
}
|
|
|
|
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 {
|
|
|
|
struct CBlock
|
|
{
|
|
UInt32 Type;
|
|
UInt64 UnpPos;
|
|
UInt64 UnpSize;
|
|
UInt64 PackPos;
|
|
UInt64 PackSize;
|
|
|
|
UInt64 GetNextPackOffset() const { return PackPos + PackSize; }
|
|
};
|
|
|
|
struct CFile
|
|
{
|
|
CByteBuffer Raw;
|
|
UInt64 StartPos;
|
|
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;
|
|
};
|
|
|
|
AString Name;
|
|
};
|
|
|
|
class CHandler:
|
|
public IInArchive,
|
|
public CMyUnknownImp
|
|
{
|
|
CMyComPtr<IInStream> _inStream;
|
|
|
|
AString _xml;
|
|
CObjectVector<CFile> _files;
|
|
CRecordVector<int> _fileIndices;
|
|
|
|
HRESULT Open2(IInStream *stream);
|
|
HRESULT Extract(IInStream *stream);
|
|
public:
|
|
MY_UNKNOWN_IMP1(IInArchive)
|
|
INTERFACE_IInArchive(;)
|
|
};
|
|
|
|
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;
|
|
void Update(const CFile &file);
|
|
UString GetString() 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++;
|
|
}
|
|
}
|
|
|
|
UString CMethods::GetString() const
|
|
{
|
|
UString res;
|
|
for (int 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)
|
|
{
|
|
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;
|
|
}
|
|
res += s;
|
|
if (m.NumBlocks != 1)
|
|
{
|
|
res += L'[';
|
|
ConvertUInt64ToString(m.NumBlocks, buf);
|
|
res += buf;
|
|
res += L']';
|
|
}
|
|
res += L'-';
|
|
res += GetSizeString(m.UnpSize);
|
|
if (showPack)
|
|
{
|
|
res += L'-';
|
|
res += GetSizeString(m.PackSize);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
STATPROPSTG kProps[] =
|
|
{
|
|
{ NULL, kpidPath, VT_BSTR},
|
|
{ NULL, kpidSize, VT_UI8},
|
|
{ NULL, kpidPackSize, VT_UI8},
|
|
{ NULL, kpidComment, VT_BSTR},
|
|
{ NULL, kpidMethod, VT_BSTR}
|
|
};
|
|
|
|
IMP_IInArchive_Props
|
|
|
|
STATPROPSTG kArcProps[] =
|
|
{
|
|
{ NULL, kpidMethod, VT_BSTR},
|
|
{ NULL, kpidNumBlocks, VT_UI4}
|
|
};
|
|
|
|
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
|
{
|
|
COM_TRY_BEGIN
|
|
NWindows::NCOM::CPropVariant prop;
|
|
switch(propID)
|
|
{
|
|
case kpidMethod:
|
|
{
|
|
CMethods m;
|
|
for (int i = 0; i < _files.Size(); i++)
|
|
m.Update(_files[i]);
|
|
prop = m.GetString();
|
|
break;
|
|
}
|
|
case kpidNumBlocks:
|
|
{
|
|
UInt64 numBlocks = 0;
|
|
for (int i = 0; i < _files.Size(); i++)
|
|
numBlocks += _files[i].Blocks.Size();
|
|
prop = numBlocks;
|
|
break;
|
|
}
|
|
}
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
IMP_IInArchive_ArcProps
|
|
|
|
static int FindKeyPair(const CXmlItem &item, const AString &key, const AString &nextTag)
|
|
{
|
|
for (int 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))
|
|
return i + 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static 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();
|
|
}
|
|
|
|
HRESULT CHandler::Open2(IInStream *stream)
|
|
{
|
|
const int HEADER_SIZE = 0x1E0;
|
|
|
|
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();
|
|
|
|
CXml xml;
|
|
if (!xml.Parse(_xml))
|
|
return S_FALSE;
|
|
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];
|
|
|
|
int i;
|
|
for (i = 0; i < arrItem.SubItems.Size(); i++)
|
|
{
|
|
const CXmlItem &item = arrItem.SubItems[i];
|
|
if (!item.IsTagged("dict"))
|
|
continue;
|
|
|
|
CFile file;
|
|
file.StartPos = 0;
|
|
|
|
int destLen;
|
|
{
|
|
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());
|
|
}
|
|
|
|
if (destLen > 0xCC && Get32(file.Raw) == 0x6D697368)
|
|
{
|
|
PRF(printf("\n\n index = %d", _files.Size()));
|
|
const int kRecordSize = 40;
|
|
for (int offset = 0xCC; offset + kRecordSize <= destLen; offset += kRecordSize)
|
|
{
|
|
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));
|
|
}
|
|
}
|
|
int itemIndex = _files.Add(file);
|
|
if (file.Blocks.Size() > 0)
|
|
{
|
|
// if (file.Name.Find("HFS") >= 0)
|
|
_fileIndices.Add(itemIndex);
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
for (i = 0; i < _files.Size(); i++)
|
|
{
|
|
CFile &file = _files[i];
|
|
file.StartPos = startPos;
|
|
if (!file.Blocks.IsEmpty())
|
|
startPos += file.Blocks.Back().GetNextPackOffset();
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Open(IInStream *stream,
|
|
const UInt64 * /* maxCheckStartPosition */,
|
|
IArchiveOpenCallback * /* openArchiveCallback */)
|
|
{
|
|
COM_TRY_BEGIN
|
|
{
|
|
Close();
|
|
if (Open2(stream) != S_OK)
|
|
return S_FALSE;
|
|
_inStream = stream;
|
|
}
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Close()
|
|
{
|
|
_inStream.Release();
|
|
_fileIndices.Clear();
|
|
_files.Clear();
|
|
_xml.Empty();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
|
|
{
|
|
*numItems = _fileIndices.Size()
|
|
#ifdef DMG_SHOW_RAW
|
|
+ _files.Size() + 1;
|
|
#endif
|
|
;
|
|
return S_OK;
|
|
}
|
|
|
|
#define RAW_PREFIX L"raw" WSTRING_PATH_SEPARATOR
|
|
|
|
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
|
|
{
|
|
COM_TRY_BEGIN
|
|
NWindows::NCOM::CPropVariant prop;
|
|
|
|
#ifdef DMG_SHOW_RAW
|
|
if ((int)index == _fileIndices.Size())
|
|
{
|
|
switch(propID)
|
|
{
|
|
case kpidPath:
|
|
prop = RAW_PREFIX L"a.xml";
|
|
break;
|
|
case kpidSize:
|
|
case kpidPackSize:
|
|
prop = (UInt64)_xml.Length();
|
|
break;
|
|
}
|
|
}
|
|
else if ((int)index > _fileIndices.Size())
|
|
{
|
|
int rawIndex = (int)index - (_fileIndices.Size() + 1);
|
|
switch(propID)
|
|
{
|
|
case kpidPath:
|
|
{
|
|
wchar_t s[32] = RAW_PREFIX;
|
|
ConvertUInt64ToString(rawIndex, s + MyStringLen(s));
|
|
prop = s;
|
|
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;
|
|
break;
|
|
}
|
|
|
|
// case kpidExtension: prop = L"hfs"; break;
|
|
|
|
case kpidPath:
|
|
{
|
|
// break;
|
|
UString name;
|
|
wchar_t s[32];
|
|
ConvertUInt64ToString(index, s);
|
|
name = s;
|
|
int num = 10;
|
|
int numDigits;
|
|
for (numDigits = 1; num < _fileIndices.Size(); numDigits++)
|
|
num *= 10;
|
|
while (name.Length() < numDigits)
|
|
name = L'0' + name;
|
|
|
|
AString subName;
|
|
int pos1 = item.Name.Find('(');
|
|
if (pos1 >= 0)
|
|
{
|
|
pos1++;
|
|
int pos2 = item.Name.Find(')', pos1);
|
|
if (pos2 >= 0)
|
|
{
|
|
subName = item.Name.Mid(pos1, pos2 - pos1);
|
|
pos1 = subName.Find(':');
|
|
if (pos1 >= 0)
|
|
subName = subName.Left(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";
|
|
UString name2;
|
|
ConvertUTF8ToUnicode(subName, name2);
|
|
name += L'.';
|
|
name += name2;
|
|
}
|
|
else
|
|
{
|
|
UString name2;
|
|
ConvertUTF8ToUnicode(item.Name, name2);
|
|
if (!name2.IsEmpty())
|
|
name += L" - ";
|
|
name += name2;
|
|
}
|
|
prop = name;
|
|
break;
|
|
}
|
|
case kpidComment:
|
|
{
|
|
UString name;
|
|
ConvertUTF8ToUnicode(item.Name, name);
|
|
prop = name;
|
|
break;
|
|
}
|
|
|
|
case kpidSize: prop = item.GetUnpackSize(); break;
|
|
case kpidPackSize: prop = item.GetPackSize(); break;
|
|
}
|
|
}
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
class CAdcDecoder:
|
|
public ICompressCoder,
|
|
public CMyUnknownImp
|
|
{
|
|
CLzOutWindow m_OutWindowStream;
|
|
CInBuffer m_InStream;
|
|
|
|
void ReleaseStreams()
|
|
{
|
|
m_OutWindowStream.ReleaseStream();
|
|
m_InStream.ReleaseStream();
|
|
}
|
|
|
|
class CCoderReleaser
|
|
{
|
|
CAdcDecoder *m_Coder;
|
|
public:
|
|
bool NeedFlush;
|
|
CCoderReleaser(CAdcDecoder *coder): m_Coder(coder), NeedFlush(true) {}
|
|
~CCoderReleaser()
|
|
{
|
|
if (NeedFlush)
|
|
m_Coder->m_OutWindowStream.Flush();
|
|
m_Coder->ReleaseStreams();
|
|
}
|
|
};
|
|
friend class CCoderReleaser;
|
|
|
|
public:
|
|
MY_UNKNOWN_IMP
|
|
|
|
STDMETHOD(CodeReal)(ISequentialInStream *inStream,
|
|
ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
|
|
ICompressProgressInfo *progress);
|
|
|
|
STDMETHOD(Code)(ISequentialInStream *inStream,
|
|
ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
|
|
ICompressProgressInfo *progress);
|
|
};
|
|
|
|
STDMETHODIMP CAdcDecoder::CodeReal(ISequentialInStream *inStream,
|
|
ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
|
|
ICompressProgressInfo *progress)
|
|
{
|
|
if (!m_OutWindowStream.Create(1 << 18))
|
|
return E_OUTOFMEMORY;
|
|
if (!m_InStream.Create(1 << 18))
|
|
return E_OUTOFMEMORY;
|
|
|
|
m_OutWindowStream.SetStream(outStream);
|
|
m_OutWindowStream.Init(false);
|
|
m_InStream.SetStream(inStream);
|
|
m_InStream.Init();
|
|
|
|
CCoderReleaser coderReleaser(this);
|
|
|
|
const UInt32 kStep = (1 << 20);
|
|
UInt64 nextLimit = kStep;
|
|
|
|
UInt64 pos = 0;
|
|
while (pos < *outSize)
|
|
{
|
|
if (pos > nextLimit && progress)
|
|
{
|
|
UInt64 packSize = m_InStream.GetProcessedSize();
|
|
RINOK(progress->SetRatioInfo(&packSize, &pos));
|
|
nextLimit += kStep;
|
|
}
|
|
Byte b;
|
|
if (!m_InStream.ReadByte(b))
|
|
return S_FALSE;
|
|
UInt64 rem = *outSize - pos;
|
|
if (b & 0x80)
|
|
{
|
|
unsigned num = (b & 0x7F) + 1;
|
|
if (num > rem)
|
|
return S_FALSE;
|
|
for (unsigned i = 0; i < num; i++)
|
|
{
|
|
if (!m_InStream.ReadByte(b))
|
|
return S_FALSE;
|
|
m_OutWindowStream.PutByte(b);
|
|
}
|
|
pos += num;
|
|
continue;
|
|
}
|
|
Byte b1;
|
|
if (!m_InStream.ReadByte(b1))
|
|
return S_FALSE;
|
|
|
|
UInt32 len, distance;
|
|
|
|
if (b & 0x40)
|
|
{
|
|
len = ((UInt32)b & 0x3F) + 4;
|
|
Byte b2;
|
|
if (!m_InStream.ReadByte(b2))
|
|
return S_FALSE;
|
|
distance = ((UInt32)b1 << 8) + b2;
|
|
}
|
|
else
|
|
{
|
|
b &= 0x3F;
|
|
len = ((UInt32)b >> 2) + 3;
|
|
distance = (((UInt32)b & 3) << 8) + b1;
|
|
}
|
|
|
|
if (distance >= pos || len > rem)
|
|
return S_FALSE;
|
|
m_OutWindowStream.CopyBlock(distance, len);
|
|
pos += len;
|
|
}
|
|
if (*inSize != m_InStream.GetProcessedSize())
|
|
return S_FALSE;
|
|
coderReleaser.NeedFlush = false;
|
|
return m_OutWindowStream.Flush();
|
|
}
|
|
|
|
STDMETHODIMP CAdcDecoder::Code(ISequentialInStream *inStream,
|
|
ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
|
|
ICompressProgressInfo *progress)
|
|
{
|
|
try { return CodeReal(inStream, outStream, inSize, outSize, progress);}
|
|
catch(const CInBufferException &e) { return e.ErrorCode; }
|
|
catch(const CLzOutWindowException &e) { return e.ErrorCode; }
|
|
catch(...) { return S_FALSE; }
|
|
}
|
|
|
|
|
|
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
|
|
Int32 testMode, IArchiveExtractCallback *extractCallback)
|
|
{
|
|
COM_TRY_BEGIN
|
|
bool allFilesMode = (numItems == (UInt32)-1);
|
|
if (allFilesMode)
|
|
numItems = _files.Size();
|
|
if (numItems == 0)
|
|
return S_OK;
|
|
UInt64 totalSize = 0;
|
|
UInt32 i;
|
|
for (i = 0; i < numItems; i++)
|
|
{
|
|
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();
|
|
else
|
|
#endif
|
|
totalSize += _files[_fileIndices[index]].GetUnpackSize();
|
|
}
|
|
extractCallback->SetTotal(totalSize);
|
|
|
|
UInt64 currentPackTotal = 0;
|
|
UInt64 currentUnpTotal = 0;
|
|
UInt64 currentPackSize = 0;
|
|
UInt64 currentUnpSize = 0;
|
|
|
|
const UInt32 kZeroBufSize = (1 << 14);
|
|
CByteBuffer zeroBuf;
|
|
zeroBuf.SetCapacity(kZeroBufSize);
|
|
memset(zeroBuf, 0, kZeroBufSize);
|
|
|
|
NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
|
|
CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
|
|
|
|
NCompress::NBZip2::CDecoder *bzip2CoderSpec = new NCompress::NBZip2::CDecoder();
|
|
CMyComPtr<ICompressCoder> bzip2Coder = bzip2CoderSpec;
|
|
|
|
NCompress::NZlib::CDecoder *zlibCoderSpec = new NCompress::NZlib::CDecoder();
|
|
CMyComPtr<ICompressCoder> zlibCoder = zlibCoderSpec;
|
|
|
|
CAdcDecoder *adcCoderSpec = new CAdcDecoder();
|
|
CMyComPtr<ICompressCoder> adcCoder = adcCoderSpec;
|
|
|
|
CLocalProgress *lps = new CLocalProgress;
|
|
CMyComPtr<ICompressProgressInfo> progress = lps;
|
|
lps->Init(extractCallback, false);
|
|
|
|
CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
|
|
CMyComPtr<ISequentialInStream> inStream(streamSpec);
|
|
streamSpec->SetStream(_inStream);
|
|
|
|
for (i = 0; i < numItems; i++, currentPackTotal += currentPackSize, currentUnpTotal += currentUnpSize)
|
|
{
|
|
lps->InSize = currentPackTotal;
|
|
lps->OutSize = currentUnpTotal;
|
|
currentPackSize = 0;
|
|
currentUnpSize = 0;
|
|
RINOK(lps->SetCur());
|
|
CMyComPtr<ISequentialOutStream> realOutStream;
|
|
Int32 askMode = testMode ?
|
|
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));
|
|
|
|
CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream;
|
|
CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
|
|
outStreamSpec->SetStream(realOutStream);
|
|
|
|
realOutStream.Release();
|
|
|
|
Int32 opRes = NExtract::NOperationResult::kOK;
|
|
#ifdef DMG_SHOW_RAW
|
|
if (index > _fileIndices.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();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
const CFile &item = _files[_fileIndices[index]];
|
|
currentPackSize = item.GetPackSize();
|
|
currentUnpSize = item.GetUnpackSize();
|
|
|
|
UInt64 unpPos = 0;
|
|
UInt64 packPos = 0;
|
|
{
|
|
for (int j = 0; j < item.Blocks.Size(); j++)
|
|
{
|
|
lps->InSize = currentPackTotal + packPos;
|
|
lps->OutSize = currentUnpTotal + unpPos;
|
|
RINOK(lps->SetCur());
|
|
|
|
const CBlock &block = item.Blocks[j];
|
|
|
|
packPos += block.PackSize;
|
|
if (block.UnpPos != unpPos)
|
|
{
|
|
opRes = NExtract::NOperationResult::kDataError;
|
|
break;
|
|
}
|
|
|
|
RINOK(_inStream->Seek(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)
|
|
{
|
|
case METHOD_ZERO_0:
|
|
case METHOD_ZERO_2:
|
|
realMethod = false;
|
|
if (block.PackSize != 0)
|
|
opRes = NExtract::NOperationResult::kUnSupportedMethod;
|
|
break;
|
|
|
|
case METHOD_COPY:
|
|
if (block.UnpSize != block.PackSize)
|
|
{
|
|
opRes = NExtract::NOperationResult::kUnSupportedMethod;
|
|
break;
|
|
}
|
|
res = copyCoder->Code(inStream, outStream, NULL, NULL, progress);
|
|
break;
|
|
|
|
case METHOD_ADC:
|
|
{
|
|
res = adcCoder->Code(inStream, outStream, &block.PackSize, &block.UnpSize, progress);
|
|
break;
|
|
}
|
|
|
|
case METHOD_ZLIB:
|
|
{
|
|
res = zlibCoder->Code(inStream, outStream, NULL, NULL, progress);
|
|
break;
|
|
}
|
|
|
|
case METHOD_BZIP2:
|
|
{
|
|
res = bzip2Coder->Code(inStream, outStream, NULL, NULL, progress);
|
|
if (res == S_OK)
|
|
if (streamSpec->GetSize() != block.PackSize)
|
|
opRes = NExtract::NOperationResult::kDataError;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
opRes = NExtract::NOperationResult::kUnSupportedMethod;
|
|
break;
|
|
}
|
|
if (res != S_OK)
|
|
{
|
|
if (res != S_FALSE)
|
|
return res;
|
|
if (opRes == NExtract::NOperationResult::kOK)
|
|
opRes = NExtract::NOperationResult::kDataError;
|
|
}
|
|
unpPos += block.UnpSize;
|
|
if (!outStreamSpec->IsFinishedOK())
|
|
{
|
|
if (realMethod && opRes == NExtract::NOperationResult::kOK)
|
|
opRes = NExtract::NOperationResult::kDataError;
|
|
|
|
while (outStreamSpec->GetRem() != 0)
|
|
{
|
|
UInt64 rem = outStreamSpec->GetRem();
|
|
UInt32 size = (UInt32)MyMin(rem, (UInt64)kZeroBufSize);
|
|
RINOK(WriteStream(outStream, zeroBuf, size));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
outStream.Release();
|
|
RINOK(extractCallback->SetOperationResult(opRes));
|
|
}
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
static IInArchive *CreateArc() { return new CHandler; }
|
|
|
|
static CArcInfo g_ArcInfo =
|
|
{ L"Dmg", L"dmg", 0, 0xE4, { 0 }, 0, false, CreateArc, 0 };
|
|
|
|
REGISTER_ARC(Dmg)
|
|
|
|
}}
|