mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 13:14:59 -06:00
372 lines
9.3 KiB
C++
Executable File
372 lines
9.3 KiB
C++
Executable File
// Archive/ComIn.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
extern "C"
|
|
{
|
|
#include "../../../../C/Alloc.h"
|
|
}
|
|
|
|
#include "Common/MyCom.h"
|
|
#include "../../Common/StreamUtils.h"
|
|
#include "Common/IntToString.h"
|
|
|
|
#include "ComIn.h"
|
|
|
|
namespace NArchive{
|
|
namespace NCom{
|
|
|
|
static const UInt32 kSignatureSize = 8;
|
|
static const Byte kSignature[kSignatureSize] = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
|
|
|
|
static HRESULT ReadBytes(ISequentialInStream *inStream, void *data, UInt32 size)
|
|
{
|
|
UInt32 realProcessedSize;
|
|
RINOK(ReadStream(inStream, data, size, &realProcessedSize));
|
|
return (realProcessedSize == size) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
#ifdef LITTLE_ENDIAN_UNALIGN
|
|
#define GetUi16(p) (*(const UInt16 *)(p))
|
|
#define GetUi32(p) (*(const UInt32 *)(p))
|
|
#else
|
|
#define GetUi16(p) ((p)[0] | ((UInt16)(p)[1] << 8))
|
|
#define GetUi32(p) ((p)[0] | ((UInt32)(p)[1] << 8) | ((UInt32)(p)[2] << 16) | ((UInt32)(p)[3] << 24))
|
|
#endif
|
|
|
|
|
|
void CUInt32Buf::Free()
|
|
{
|
|
MyFree(_buf);
|
|
_buf = 0;
|
|
}
|
|
|
|
bool CUInt32Buf::Allocate(UInt32 numItems)
|
|
{
|
|
Free();
|
|
if (numItems == 0)
|
|
return true;
|
|
size_t newSize = (size_t)numItems * sizeof(UInt32);
|
|
if (newSize / sizeof(UInt32) != numItems)
|
|
return false;
|
|
_buf = (UInt32 *)MyAlloc(newSize);
|
|
return (_buf != 0);
|
|
}
|
|
|
|
static HRESULT ReadSector(IInStream *inStream, Byte *buf, int sectorSizeBits, UInt32 sid)
|
|
{
|
|
RINOK(inStream->Seek((((UInt64)sid + 1) << sectorSizeBits), STREAM_SEEK_SET, NULL));
|
|
return ReadBytes(inStream, buf, (UInt32)1 << sectorSizeBits);
|
|
}
|
|
|
|
static HRESULT ReadIDs(IInStream *inStream, Byte *buf, int sectorSizeBits, UInt32 sid, UInt32 *dest)
|
|
{
|
|
RINOK(ReadSector(inStream, buf, sectorSizeBits, sid));
|
|
UInt32 sectorSize = (UInt32)1 << sectorSizeBits;
|
|
for (UInt32 t = 0; t < sectorSize; t += 4)
|
|
*dest++ = GetUi32(buf + t);
|
|
return S_OK;
|
|
}
|
|
|
|
static void GetFileTimeFromMem(const Byte *p, FILETIME *ft)
|
|
{
|
|
ft->dwLowDateTime = GetUi32(p);
|
|
ft->dwHighDateTime = GetUi32(p + 4);
|
|
}
|
|
|
|
static void ReadItem(Byte *p, CItem &item, bool mode64bit)
|
|
{
|
|
memcpy(item.Name, p, 64);
|
|
// item.NameSize = GetUi16(p + 64);
|
|
item.Type = p[66];
|
|
item.LeftDid = GetUi32(p + 68);
|
|
item.RightDid = GetUi32(p + 72);
|
|
item.SonDid = GetUi32(p + 76);
|
|
// item.Flags = GetUi32(p + 96);
|
|
GetFileTimeFromMem(p + 100, &item.CreationTime);
|
|
GetFileTimeFromMem(p + 108, &item.LastWriteTime);
|
|
item.Sid = GetUi32(p + 116);
|
|
item.Size = GetUi32(p + 120);
|
|
if (mode64bit)
|
|
item.Size |= ((UInt64)GetUi32(p + 124) << 32);
|
|
}
|
|
|
|
static const UInt32 kNoDid = 0xFFFFFFFF;
|
|
|
|
HRESULT CDatabase::AddNode(int parent, UInt32 did)
|
|
{
|
|
if (did == kNoDid)
|
|
return S_OK;
|
|
if (did >= (UInt32)Items.Size())
|
|
return S_FALSE;
|
|
const CItem &item = Items[did];
|
|
if (item.IsEmpty())
|
|
return S_FALSE;
|
|
CRef ref;
|
|
ref.Parent = parent;
|
|
ref.Did = did;
|
|
int index = Refs.Add(ref);
|
|
if (Refs.Size() > Items.Size())
|
|
return S_FALSE;
|
|
RINOK(AddNode(parent, item.LeftDid));
|
|
RINOK(AddNode(parent, item.RightDid));
|
|
if (item.IsDir())
|
|
{
|
|
RINOK(AddNode(index, item.SonDid));
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
static const char kCharOpenBracket = '[';
|
|
static const char kCharCloseBracket = ']';
|
|
|
|
UString DWORDToString(UInt32 val)
|
|
{
|
|
wchar_t buf[32];
|
|
ConvertUInt64ToString(val, buf);
|
|
return buf;
|
|
}
|
|
|
|
static UString CompoundNameToFileName(const UString &s)
|
|
{
|
|
UString res;
|
|
for (int i = 0; i < s.Length(); i++)
|
|
{
|
|
wchar_t c = s[i];
|
|
if (c < 0x20)
|
|
{
|
|
res += kCharOpenBracket;
|
|
res += DWORDToString(c);
|
|
res += kCharCloseBracket;
|
|
}
|
|
else
|
|
res += c;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static char g_MsiChars[] =
|
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._";
|
|
|
|
static const wchar_t *kMsi_ID = L""; // L"{msi}";
|
|
|
|
static const int kMsiNumBits = 6;
|
|
static const UInt32 kMsiNumChars = 1 << kMsiNumBits;
|
|
static const UInt32 kMsiCharMask = kMsiNumChars - 1;
|
|
static const UInt32 kMsiStartUnicodeChar = 0x3800;
|
|
static const UInt32 kMsiUnicodeRange = kMsiNumChars * (kMsiNumChars + 1);
|
|
|
|
bool CompoundMsiNameToFileName(const UString &name, UString &resultName)
|
|
{
|
|
resultName.Empty();
|
|
for (int i = 0; i < name.Length(); i++)
|
|
{
|
|
wchar_t c = name[i];
|
|
if (c < kMsiStartUnicodeChar || c > kMsiStartUnicodeChar + kMsiUnicodeRange)
|
|
return false;
|
|
if (i == 0)
|
|
resultName += kMsi_ID;
|
|
c -= kMsiStartUnicodeChar;
|
|
|
|
UInt32 c0 = c & kMsiCharMask;
|
|
UInt32 c1 = c >> kMsiNumBits;
|
|
|
|
if (c1 <= kMsiNumChars)
|
|
{
|
|
resultName += (wchar_t)g_MsiChars[c0];
|
|
if (c1 == kMsiNumChars)
|
|
break;
|
|
resultName += (wchar_t)g_MsiChars[c1];
|
|
}
|
|
else
|
|
resultName += L'!';
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static UString ConvertName(const Byte *p)
|
|
{
|
|
UString s;
|
|
for (int i = 0; i < kNameSizeMax; i += 2)
|
|
{
|
|
wchar_t c = (p[i] | (wchar_t)p[i + 1] << 8);
|
|
if (c == 0)
|
|
break;
|
|
s += c;
|
|
}
|
|
UString msiName;
|
|
if (CompoundMsiNameToFileName(s, msiName))
|
|
return msiName;
|
|
return CompoundNameToFileName(s);
|
|
}
|
|
|
|
UString CDatabase::GetItemPath(UInt32 index) const
|
|
{
|
|
UString s;
|
|
while (index != kNoDid)
|
|
{
|
|
const CRef &ref = Refs[index];
|
|
const CItem &item = Items[ref.Did];
|
|
if (!s.IsEmpty())
|
|
s = (UString)WCHAR_PATH_SEPARATOR + s;
|
|
s = ConvertName(item.Name) + s;
|
|
index = ref.Parent;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
HRESULT OpenArchive(IInStream *inStream, CDatabase &db)
|
|
{
|
|
static const UInt32 kHeaderSize = 512;
|
|
Byte p[kHeaderSize];
|
|
RINOK(ReadBytes(inStream, p, kHeaderSize));
|
|
if (memcmp(p, kSignature, kSignatureSize) != 0)
|
|
return S_FALSE;
|
|
UInt16 majorVer = GetUi16(p + 0x1A);
|
|
if (majorVer > 4)
|
|
return S_FALSE;
|
|
if (GetUi16(p + 0x1C) != 0xFFFE)
|
|
return S_FALSE;
|
|
UInt16 sectorSizeBits = GetUi16(p + 0x1E);
|
|
bool mode64bit = (sectorSizeBits >= 12);
|
|
UInt16 miniSectorSizeBits = GetUi16(p + 0x20);
|
|
db.SectorSizeBits = sectorSizeBits;
|
|
db.MiniSectorSizeBits = miniSectorSizeBits;
|
|
|
|
if (sectorSizeBits > 28 || miniSectorSizeBits > 28 ||
|
|
sectorSizeBits < 7 || miniSectorSizeBits < 2 || miniSectorSizeBits > sectorSizeBits)
|
|
return S_FALSE;
|
|
UInt32 numSectorsForFAT = GetUi32(p + 0x2C);
|
|
db.LongStreamMinSize = GetUi32(p + 0x38);
|
|
|
|
UInt32 sectSize = (UInt32)1 << (int)(sectorSizeBits);
|
|
|
|
CByteBuffer sect;
|
|
sect.SetCapacity(sectSize);
|
|
|
|
int ssb2 = (int)(sectorSizeBits - 2);
|
|
UInt32 numSidsInSec = (UInt32)1 << ssb2;
|
|
UInt32 numFatItems = numSectorsForFAT << ssb2;
|
|
if ((numFatItems >> ssb2) != numSectorsForFAT)
|
|
return S_FALSE;
|
|
db.FatSize = numFatItems;
|
|
|
|
{
|
|
CUInt32Buf bat;
|
|
UInt32 numSectorsForBat = GetUi32(p + 0x48);
|
|
const UInt32 kNumHeaderBatItems = 109;
|
|
UInt32 numBatItems = kNumHeaderBatItems + (numSectorsForBat << ssb2);
|
|
if (numBatItems < kNumHeaderBatItems || ((numBatItems - kNumHeaderBatItems) >> ssb2) != numSectorsForBat)
|
|
return S_FALSE;
|
|
if (!bat.Allocate(numBatItems))
|
|
return S_FALSE;
|
|
UInt32 i;
|
|
for (i = 0; i < kNumHeaderBatItems; i++)
|
|
bat[i] = GetUi32(p + 0x4c + i * 4);
|
|
UInt32 sid = GetUi32(p + 0x44);
|
|
for (UInt32 s = 0; s < numSectorsForBat; s++)
|
|
{
|
|
RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, bat + i));
|
|
i += numSidsInSec - 1;
|
|
sid = bat[i];
|
|
}
|
|
numBatItems = i;
|
|
|
|
if (!db.Fat.Allocate(numFatItems))
|
|
return S_FALSE;
|
|
UInt32 j = 0;
|
|
|
|
for (i = 0; i < numFatItems; j++, i += numSidsInSec)
|
|
{
|
|
if (j >= numBatItems)
|
|
return S_FALSE;
|
|
RINOK(ReadIDs(inStream, sect, sectorSizeBits, bat[j], db.Fat + i));
|
|
}
|
|
}
|
|
|
|
UInt32 numMatItems;
|
|
{
|
|
UInt32 numSectorsForMat = GetUi32(p + 0x40);
|
|
numMatItems = (UInt32)numSectorsForMat << ssb2;
|
|
if ((numMatItems >> ssb2) != numSectorsForMat)
|
|
return S_FALSE;
|
|
if (!db.Mat.Allocate(numMatItems))
|
|
return S_FALSE;
|
|
UInt32 i;
|
|
UInt32 sid = GetUi32(p + 0x3C);
|
|
for (i = 0; i < numMatItems; i += numSidsInSec)
|
|
{
|
|
RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, db.Mat + i));
|
|
if (sid >= numFatItems)
|
|
return S_FALSE;
|
|
sid = db.Fat[sid];
|
|
}
|
|
if (sid != NFatID::kEndOfChain)
|
|
return S_FALSE;
|
|
}
|
|
|
|
{
|
|
UInt32 sid = GetUi32(p + 0x30);
|
|
for (;;)
|
|
{
|
|
if (sid >= numFatItems)
|
|
return S_FALSE;
|
|
RINOK(ReadSector(inStream, sect, sectorSizeBits, sid));
|
|
for (UInt32 i = 0; i < sectSize; i += 128)
|
|
{
|
|
CItem item;
|
|
ReadItem(sect + i, item, mode64bit);
|
|
db.Items.Add(item);
|
|
}
|
|
sid = db.Fat[sid];
|
|
if (sid == NFatID::kEndOfChain)
|
|
break;
|
|
}
|
|
}
|
|
|
|
CItem root = db.Items[0];
|
|
|
|
{
|
|
UInt32 numSectorsInMiniStream;
|
|
{
|
|
UInt64 numSatSects64 = (root.Size + sectSize - 1) >> sectorSizeBits;
|
|
if (numSatSects64 > NFatID::kMaxValue)
|
|
return S_FALSE;
|
|
numSectorsInMiniStream = (UInt32)numSatSects64;
|
|
}
|
|
db.NumSectorsInMiniStream = numSectorsInMiniStream;
|
|
if (!db.MiniSids.Allocate(numSectorsInMiniStream))
|
|
return S_FALSE;
|
|
{
|
|
UInt64 matSize64 = (root.Size + (1 << miniSectorSizeBits) - 1) >> miniSectorSizeBits;
|
|
if (matSize64 > NFatID::kMaxValue)
|
|
return S_FALSE;
|
|
db.MatSize = (UInt32)matSize64;
|
|
if (numMatItems < db.MatSize)
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
UInt32 sid = root.Sid;
|
|
for (UInt32 i = 0; ; i++)
|
|
{
|
|
if (sid == NFatID::kEndOfChain)
|
|
{
|
|
if (i != numSectorsInMiniStream)
|
|
return S_FALSE;
|
|
break;
|
|
}
|
|
if (i >= numSectorsInMiniStream)
|
|
return S_FALSE;
|
|
db.MiniSids[i] = sid;
|
|
if (sid >= numFatItems)
|
|
return S_FALSE;
|
|
sid = db.Fat[sid];
|
|
}
|
|
}
|
|
|
|
return db.AddNode(-1, root.SonDid);
|
|
}
|
|
|
|
}}
|