// 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 + ((UInt64)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); } }}