mirror of
https://github.com/Xevion/easy7zip.git
synced 2026-01-31 22:24:08 -06:00
17.00
This commit is contained in:
+256
-77
@@ -183,6 +183,30 @@ struct CExtraFile
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
struct CForkPair
|
||||
{
|
||||
UInt64 Offset;
|
||||
UInt64 Len;
|
||||
|
||||
void Parse(const Byte *p)
|
||||
{
|
||||
Offset = Get64(p);
|
||||
Len = Get64(p + 8);
|
||||
}
|
||||
|
||||
bool UpdateTop(UInt64 limit, UInt64 &top)
|
||||
{
|
||||
if (Offset > limit || Len > limit - Offset)
|
||||
return false;
|
||||
UInt64 top2 = Offset + Len;
|
||||
if (top <= top2)
|
||||
top = top2;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class CHandler:
|
||||
public IInArchive,
|
||||
public IInArchiveGetStream,
|
||||
@@ -191,14 +215,19 @@ class CHandler:
|
||||
CMyComPtr<IInStream> _inStream;
|
||||
CObjectVector<CFile> _files;
|
||||
bool _masterCrcError;
|
||||
bool _headersError;
|
||||
|
||||
UInt64 _startPos;
|
||||
UInt64 _phySize;
|
||||
|
||||
AString _name;
|
||||
|
||||
#ifdef DMG_SHOW_RAW
|
||||
CObjectVector<CExtraFile> _extras;
|
||||
#endif
|
||||
|
||||
HRESULT ReadData(IInStream *stream, const CForkPair &pair, CByteBuffer &buf);
|
||||
bool ParseBlob(const CByteBuffer &data);
|
||||
HRESULT Open2(IInStream *stream);
|
||||
HRESULT Extract(IInStream *stream);
|
||||
public:
|
||||
@@ -249,24 +278,20 @@ void CMethods::GetString(AString &res) const
|
||||
case METHOD_BZIP2: s = "BZip2"; break;
|
||||
default: ConvertUInt32ToString(type, buf); s = buf;
|
||||
}
|
||||
res.Add_Space_if_NotEmpty();
|
||||
res += s;
|
||||
res.Add_OptSpaced(s);
|
||||
}
|
||||
|
||||
for (i = 0; i < ChecksumTypes.Size(); i++)
|
||||
{
|
||||
res.Add_Space_if_NotEmpty();
|
||||
UInt32 type = ChecksumTypes[i];
|
||||
char buf[32];
|
||||
const char *s;
|
||||
switch (type)
|
||||
{
|
||||
case kCheckSumType_CRC: s = "CRC"; break;
|
||||
case kCheckSumType_CRC: res += "CRC"; break;
|
||||
default:
|
||||
ConvertUInt32ToString(type, MyStpCpy(buf, "Check"));
|
||||
s = buf;
|
||||
res += "Check";
|
||||
res.Add_UInt32(type);
|
||||
}
|
||||
res.Add_Space_if_NotEmpty();
|
||||
res += s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +333,8 @@ IMP_IInArchive_Props
|
||||
static const Byte kArcProps[] =
|
||||
{
|
||||
kpidMethod,
|
||||
kpidNumBlocks
|
||||
kpidNumBlocks,
|
||||
kpidComment
|
||||
};
|
||||
|
||||
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
||||
@@ -372,9 +398,30 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
||||
case kpidWarning:
|
||||
if (_masterCrcError)
|
||||
prop = "Master CRC error";
|
||||
|
||||
case kpidWarningFlags:
|
||||
{
|
||||
UInt32 v = 0;
|
||||
if (_headersError) v |= kpv_ErrorFlags_HeadersError;
|
||||
if (v != 0)
|
||||
prop = v;
|
||||
break;
|
||||
}
|
||||
|
||||
case kpidOffset: prop = _startPos; break;
|
||||
case kpidPhySize: prop = _phySize; break;
|
||||
|
||||
case kpidComment:
|
||||
if (!_name.IsEmpty() && _name.Len() < 256)
|
||||
prop = _name;
|
||||
break;
|
||||
|
||||
case kpidName:
|
||||
if (!_name.IsEmpty() && _name.Len() < 256)
|
||||
{
|
||||
prop = _name + ".dmg";
|
||||
}
|
||||
break;
|
||||
}
|
||||
prop.Detach(value);
|
||||
return S_OK;
|
||||
@@ -461,7 +508,7 @@ HRESULT CFile::Parse(const Byte *p, UInt32 size)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static int FindKeyPair(const CXmlItem &item, const AString &key, const AString &nextTag)
|
||||
static int FindKeyPair(const CXmlItem &item, const char *key, const char *nextTag)
|
||||
{
|
||||
for (unsigned i = 0; i + 1 < item.SubItems.Size(); i++)
|
||||
{
|
||||
@@ -472,7 +519,7 @@ static int FindKeyPair(const CXmlItem &item, const AString &key, const AString &
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const AString *GetStringFromKeyPair(const CXmlItem &item, const AString &key, const AString &nextTag)
|
||||
static const AString *GetStringFromKeyPair(const CXmlItem &item, const char *key, const char *nextTag)
|
||||
{
|
||||
int index = FindKeyPair(item, key, nextTag);
|
||||
if (index >= 0)
|
||||
@@ -498,6 +545,83 @@ static inline bool IsKoly(const Byte *p)
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
HRESULT CHandler::ReadData(IInStream *stream, const CForkPair &pair, CByteBuffer &buf)
|
||||
{
|
||||
size_t size = (size_t)pair.Len;
|
||||
if (size != pair.Len)
|
||||
return E_OUTOFMEMORY;
|
||||
buf.Alloc(size);
|
||||
RINOK(stream->Seek(_startPos + pair.Offset, STREAM_SEEK_SET, NULL));
|
||||
return ReadStream_FALSE(stream, buf, size);
|
||||
}
|
||||
|
||||
|
||||
bool CHandler::ParseBlob(const CByteBuffer &data)
|
||||
{
|
||||
if (data.Size() < 12)
|
||||
return false;
|
||||
const Byte *p = (const Byte *)data;
|
||||
if (Get32(p) != 0xFADE0CC0)
|
||||
return true;
|
||||
const UInt32 size = Get32(p + 4);
|
||||
if (size != data.Size())
|
||||
return false;
|
||||
const UInt32 num = Get32(p + 8);
|
||||
if (num > (size - 12) / 8)
|
||||
return false;
|
||||
|
||||
for (UInt32 i = 0; i < num; i++)
|
||||
{
|
||||
// UInt32 type = Get32(p + i * 8 + 12);
|
||||
UInt32 offset = Get32(p + i * 8 + 12 + 4);
|
||||
if (size - offset < 8)
|
||||
return false;
|
||||
const Byte *p2 = (const Byte *)data + offset;
|
||||
const UInt32 magic = Get32(p2);
|
||||
const UInt32 len = Get32(p2 + 4);
|
||||
if (size - offset < len || len < 8)
|
||||
return false;
|
||||
|
||||
#ifdef DMG_SHOW_RAW
|
||||
CExtraFile &extra = _extras.AddNew();
|
||||
extra.Name = "_blob_";
|
||||
extra.Data.CopyFrom(p2, len);
|
||||
#endif
|
||||
|
||||
if (magic == 0xFADE0C02)
|
||||
{
|
||||
#ifdef DMG_SHOW_RAW
|
||||
extra.Name += "codedir";
|
||||
#endif
|
||||
|
||||
if (len < 11 * 4)
|
||||
return false;
|
||||
UInt32 idOffset = Get32(p2 + 0x14);
|
||||
if (idOffset >= len)
|
||||
return false;
|
||||
UInt32 len2 = len - idOffset;
|
||||
if (len2 < (1 << 10))
|
||||
_name.SetFrom_CalcLen((const char *)(p2 + idOffset), len2);
|
||||
}
|
||||
#ifdef DMG_SHOW_RAW
|
||||
else if (magic == 0xFADE0C01)
|
||||
extra.Name += "requirements";
|
||||
else if (magic == 0xFADE0B01)
|
||||
extra.Name += "signed";
|
||||
else
|
||||
{
|
||||
char temp[16];
|
||||
ConvertUInt32ToHex8Digits(magic, temp);
|
||||
extra.Name += temp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
HRESULT CHandler::Open2(IInStream *stream)
|
||||
{
|
||||
RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_startPos));
|
||||
@@ -522,35 +646,69 @@ HRESULT CHandler::Open2(IInStream *stream)
|
||||
|
||||
// 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);
|
||||
|
||||
CForkPair dataForkPair, rsrcPair, xmlPair, blobPair;
|
||||
|
||||
dataForkPair.Parse(buf + 0x18);
|
||||
rsrcPair.Parse(buf + 0x28);
|
||||
xmlPair.Parse(buf + 0xD8);
|
||||
blobPair.Parse(buf + 0x128);
|
||||
|
||||
// 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);
|
||||
|
||||
if ( headerPos < dataForkOffset
|
||||
|| headerPos - dataForkOffset < dataForkLen
|
||||
|| headerPos < rsrcOffset
|
||||
|| headerPos - rsrcOffset < rsrcLen
|
||||
|| headerPos < xmlOffset
|
||||
|| headerPos - xmlOffset < xmlLen)
|
||||
return S_FALSE;
|
||||
_startPos = 0;
|
||||
|
||||
UInt64 totalLen = dataForkLen + rsrcLen + xmlLen;
|
||||
if (totalLen > headerPos)
|
||||
return S_FALSE;
|
||||
_startPos = headerPos - totalLen;
|
||||
_phySize = totalLen + HEADER_SIZE;
|
||||
headerPos = totalLen;
|
||||
UInt64 top = 0;
|
||||
if (!dataForkPair.UpdateTop(headerPos, top)) return S_FALSE;
|
||||
if (!xmlPair.UpdateTop(headerPos, top)) return S_FALSE;
|
||||
if (!rsrcPair.UpdateTop(headerPos, top)) return S_FALSE;
|
||||
|
||||
/* Some old dmg files contain garbage data in blobPair field.
|
||||
So we need to ignore such garbage case;
|
||||
And we still need to detect offset of start of archive for "parser" mode. */
|
||||
|
||||
bool useBlob = blobPair.UpdateTop(headerPos, top);
|
||||
|
||||
_startPos = 0;
|
||||
_phySize = headerPos + HEADER_SIZE;
|
||||
|
||||
if (top != headerPos)
|
||||
{
|
||||
CForkPair xmlPair2 = xmlPair;
|
||||
const char *sz = "<?xml version";
|
||||
const unsigned len = (unsigned)strlen(sz);
|
||||
if (xmlPair2.Len > len)
|
||||
xmlPair2.Len = len;
|
||||
CByteBuffer buf2;
|
||||
if (ReadData(stream, xmlPair2, buf2) != S_OK
|
||||
|| memcmp(buf2, sz, len) != 0)
|
||||
{
|
||||
_startPos = headerPos - top;
|
||||
_phySize = top + HEADER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// Byte reserved[0x78]
|
||||
|
||||
if (useBlob && blobPair.Len != 0)
|
||||
{
|
||||
#ifdef DMG_SHOW_RAW
|
||||
CExtraFile &extra = _extras.AddNew();
|
||||
extra.Name = "_blob.bin";
|
||||
CByteBuffer &blobBuf = extra.Data;
|
||||
#else
|
||||
CByteBuffer blobBuf;
|
||||
#endif
|
||||
RINOK(ReadData(stream, blobPair, blobBuf));
|
||||
if (!ParseBlob(blobBuf))
|
||||
_headersError = true;
|
||||
}
|
||||
|
||||
|
||||
CChecksum masterChecksum;
|
||||
masterChecksum.Parse(buf + 0x160);
|
||||
|
||||
@@ -562,7 +720,7 @@ HRESULT CHandler::Open2(IInStream *stream)
|
||||
|
||||
// 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));
|
||||
bool useRsrc = (rsrcPair.Len > RSRC_HEAD_SIZE && rsrcPair.Len < ((UInt32)1 << 24));
|
||||
// useRsrc = false;
|
||||
|
||||
if (useRsrc)
|
||||
@@ -575,21 +733,18 @@ HRESULT CHandler::Open2(IInStream *stream)
|
||||
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));
|
||||
RINOK(ReadData(stream, rsrcPair, rsrcBuf));
|
||||
|
||||
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)
|
||||
if (headSize != RSRC_HEAD_SIZE
|
||||
|| footerOffset >= rsrcPair.Len
|
||||
|| mainDataSize >= rsrcPair.Len
|
||||
|| footerOffset + footerSize != rsrcPair.Len
|
||||
|| footerOffset != headSize + mainDataSize)
|
||||
return S_FALSE;
|
||||
if (footerSize < 16)
|
||||
return S_FALSE;
|
||||
@@ -600,7 +755,7 @@ HRESULT CHandler::Open2(IInStream *stream)
|
||||
|
||||
if ((UInt32)Get16(p + 0x18) != 0x1C)
|
||||
return S_FALSE;
|
||||
UInt32 namesOffset = Get16(p + 0x1A);
|
||||
const UInt32 namesOffset = Get16(p + 0x1A);
|
||||
if (namesOffset > footerSize)
|
||||
return S_FALSE;
|
||||
|
||||
@@ -612,12 +767,15 @@ HRESULT CHandler::Open2(IInStream *stream)
|
||||
{
|
||||
const Byte *p2 = p + 0x1E + i * 8;
|
||||
|
||||
UInt32 typeId = Get32(p2);
|
||||
const UInt32 typeId = Get32(p2);
|
||||
|
||||
#ifndef DMG_SHOW_RAW
|
||||
if (typeId != 0x626C6B78) // blkx
|
||||
continue;
|
||||
#endif
|
||||
|
||||
UInt32 numFiles = (UInt32)Get16(p2 + 4) + 1;
|
||||
UInt32 offs = Get16(p2 + 6);
|
||||
const UInt32 numFiles = (UInt32)Get16(p2 + 4) + 1;
|
||||
const UInt32 offs = Get16(p2 + 6);
|
||||
if (0x1C + offs + 12 * numFiles > namesOffset)
|
||||
return S_FALSE;
|
||||
|
||||
@@ -625,7 +783,7 @@ HRESULT CHandler::Open2(IInStream *stream)
|
||||
{
|
||||
const Byte *p3 = p + 0x1C + offs + k * 12;
|
||||
// UInt32 id = Get16(p3);
|
||||
UInt32 namePos = Get16(p3 + 2);
|
||||
const 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);
|
||||
@@ -634,21 +792,12 @@ HRESULT CHandler::Open2(IInStream *stream)
|
||||
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;
|
||||
}
|
||||
extra.Data.CopyFrom(pBlock + 4, blockSize);
|
||||
}
|
||||
#endif
|
||||
const UInt32 blockSize = Get32(pBlock);
|
||||
if (mainDataSize - (blockOffset + 4) < blockSize)
|
||||
return S_FALSE;
|
||||
|
||||
AString name;
|
||||
|
||||
CFile &file = _files.AddNew();
|
||||
if (namePos != 0xFFFF)
|
||||
{
|
||||
UInt32 namesBlockSize = footerSize - namesOffset;
|
||||
@@ -663,22 +812,56 @@ HRESULT CHandler::Open2(IInStream *stream)
|
||||
Byte c = namePtr[r];
|
||||
if (c < 0x20 || c >= 0x80)
|
||||
break;
|
||||
file.Name += (char)c;
|
||||
name += (char)c;
|
||||
}
|
||||
}
|
||||
RINOK(file.Parse(pBlock + 4, blockSize));
|
||||
|
||||
if (typeId == 0x626C6B78) // blkx
|
||||
{
|
||||
CFile &file = _files.AddNew();
|
||||
file.Name = name;
|
||||
RINOK(file.Parse(pBlock + 4, blockSize));
|
||||
}
|
||||
|
||||
#ifdef DMG_SHOW_RAW
|
||||
{
|
||||
AString name2;
|
||||
|
||||
name2.Add_UInt32(i);
|
||||
name2 += '_';
|
||||
|
||||
{
|
||||
char temp[4 + 1] = { 0 };
|
||||
memcpy(temp, p2, 4);
|
||||
name2 += temp;
|
||||
}
|
||||
name2.Trim();
|
||||
name2 += '_';
|
||||
name2.Add_UInt32(k);
|
||||
|
||||
if (!name.IsEmpty())
|
||||
{
|
||||
name2 += '_';
|
||||
name2 += name;
|
||||
}
|
||||
|
||||
CExtraFile &extra = _extras.AddNew();
|
||||
extra.Name = name2;
|
||||
extra.Data.CopyFrom(pBlock + 4, blockSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (xmlLen >= kXmlSizeMax || xmlLen == 0)
|
||||
if (xmlPair.Len >= kXmlSizeMax || xmlPair.Len == 0)
|
||||
return S_FALSE;
|
||||
size_t size = (size_t)xmlLen;
|
||||
if (size != xmlLen)
|
||||
size_t size = (size_t)xmlPair.Len;
|
||||
if (size != xmlPair.Len)
|
||||
return S_FALSE;
|
||||
|
||||
RINOK(stream->Seek(_startPos + dataForkLen, STREAM_SEEK_SET, NULL));
|
||||
RINOK(stream->Seek(_startPos + xmlPair.Offset, STREAM_SEEK_SET, NULL));
|
||||
|
||||
CXml xml;
|
||||
{
|
||||
@@ -733,16 +916,12 @@ HRESULT CHandler::Open2(IInStream *stream)
|
||||
const Byte *endPtr = Base64ToBin(rawBuf, *dataString);
|
||||
if (!endPtr)
|
||||
return S_FALSE;
|
||||
destLen = (unsigned)(endPtr - rawBuf);
|
||||
destLen = (unsigned)(endPtr - (const Byte *)rawBuf);
|
||||
}
|
||||
|
||||
#ifdef DMG_SHOW_RAW
|
||||
CExtraFile &extra = _extras.AddNew();
|
||||
{
|
||||
char extraName[16];
|
||||
ConvertUInt32ToString(_files.Size(), extraName);
|
||||
extra.Name = extraName;
|
||||
}
|
||||
extra.Name.Add_UInt32(_files.Size());
|
||||
extra.Data.CopyFrom(rawBuf, destLen);
|
||||
#endif
|
||||
}
|
||||
@@ -800,6 +979,8 @@ STDMETHODIMP CHandler::Close()
|
||||
_inStream.Release();
|
||||
_files.Clear();
|
||||
_masterCrcError = false;
|
||||
_headersError = false;
|
||||
_name.Empty();
|
||||
#ifdef DMG_SHOW_RAW
|
||||
_extras.Clear();
|
||||
#endif
|
||||
@@ -867,9 +1048,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
|
||||
case kpidPath:
|
||||
{
|
||||
UString name;
|
||||
wchar_t s[16];
|
||||
ConvertUInt32ToString(index, s);
|
||||
name = s;
|
||||
name.Add_UInt32(index);
|
||||
unsigned num = 10;
|
||||
unsigned numDigits;
|
||||
for (numDigits = 1; num < _files.Size(); numDigits++)
|
||||
@@ -908,7 +1087,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
|
||||
}
|
||||
UString name2;
|
||||
ConvertUTF8ToUnicode(subName, name2);
|
||||
name += L'.';
|
||||
name += '.';
|
||||
name += name2;
|
||||
}
|
||||
else
|
||||
@@ -916,7 +1095,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
|
||||
UString name2;
|
||||
ConvertUTF8ToUnicode(item.Name, name2);
|
||||
if (!name2.IsEmpty())
|
||||
name.AddAscii(" - ");
|
||||
name += "_";
|
||||
name += name2;
|
||||
}
|
||||
prop = name;
|
||||
|
||||
Reference in New Issue
Block a user