diff options
Diffstat (limited to 'CPP/7zip/Archive/UefiHandler.cpp')
-rwxr-xr-x | CPP/7zip/Archive/UefiHandler.cpp | 1935 |
1 files changed, 1935 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/UefiHandler.cpp b/CPP/7zip/Archive/UefiHandler.cpp new file mode 100755 index 00000000..88739d8f --- /dev/null +++ b/CPP/7zip/Archive/UefiHandler.cpp @@ -0,0 +1,1935 @@ +// UefiHandler.cpp + +#include "StdAfx.h" + +// #define SHOW_DEBUG_INFO + +// #include <stdio.h> + +#ifdef SHOW_DEBUG_INFO +#include <stdio.h> +#endif + +#include "../../../C/7zCrc.h" +#include "../../../C/Alloc.h" +#include "../../../C/CpuArch.h" +#include "../../../C/LzmaDec.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/PropVariantUtils.h" + +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#include "./Common/FindSignature.h" + +#ifdef SHOW_DEBUG_INFO +#define PRF(x) x +#else +#define PRF(x) +#endif + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) +#define Get24(p) (Get32(p) & 0xFFFFFF) + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +namespace NArchive { +namespace NUefi { + +static const UInt32 kBufTotalSizeMax = (1 << 29); +static const UInt32 kNumFilesMax = (1 << 18); +static const int kLevelMax = 64; + +static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } +static void SzFree(void *p, void *address) { p = p; MyFree(address); } +static ISzAlloc g_Alloc = { SzAlloc, SzFree }; + +static const UInt32 kFvHeaderSize = 0x38; +static const UInt32 kGuidSize = 16; +static const UInt32 kCapsuleSigSize = kGuidSize; +#define CAPSULE_SIGNATURE \ + { 0xBD,0x86,0x66,0x3B,0x76,0x0D,0x30,0x40,0xB7,0x0E,0xB5,0x51,0x9E,0x2F,0xC5,0xA0 } +static const Byte kCapsuleSig[kCapsuleSigSize] = CAPSULE_SIGNATURE; + +static const UInt32 kFfsGuidOffset = 16; +#define FFS_SIGNATURE \ + { 0xD9,0x54,0x93,0x7A,0x68,0x04,0x4A,0x44,0x81,0xCE,0x0B,0xF6,0x17,0xD8,0x90,0xDF } +static const Byte k_FFS_Guid[kGuidSize] = FFS_SIGNATURE; + +static const Byte k_MacFS_Guid[kGuidSize] = + { 0xAD,0xEE,0xAD,0x04,0xFF,0x61,0x31,0x4D,0xB6,0xBA,0x64,0xF8,0xBF,0x90,0x1F,0x5A }; + +static const UInt32 kFvSignature = 0x4856465F; + +static const Byte kGuids[][kGuidSize] = +{ + { 0xB0,0xCD,0x1B,0xFC,0x31,0x7D,0xAA,0x49,0x93,0x6A,0xA4,0x60,0x0D,0x9D,0xD0,0x83 }, + { 0x2E,0x06,0xA0,0x1B,0x79,0xC7,0x82,0x45,0x85,0x66,0x33,0x6A,0xE8,0xF7,0x8F,0x09 }, + { 0x25,0x4E,0x37,0x7E,0x01,0x8E,0xEE,0x4F,0x87,0xf2,0x39,0x0C,0x23,0xC6,0x06,0xCD }, + { 0x97,0xE5,0x1B,0x16,0xC5,0xE9,0xDB,0x49,0xAE,0x50,0xC4,0x62,0xAB,0x54,0xEE,0xDA }, + { 0xDB,0x7F,0xAD,0x77,0x2A,0xDF,0x02,0x43,0x88,0x98,0xC7,0x2E,0x4C,0xDB,0xD0,0xF4 }, + { 0xAB,0x71,0xCF,0xF5,0x4B,0xB0,0x7E,0x4B,0x98,0x8A,0xD8,0xA0,0xD4,0x98,0xE6,0x92 }, + { 0x91,0x45,0x53,0x7A,0xCE,0x37,0x81,0x48,0xB3,0xC9,0x71,0x38,0x14,0xF4,0x5D,0x6B }, + { 0x84,0xE6,0x7A,0x36,0x5D,0x33,0x71,0x46,0xA1,0x6D,0x89,0x9D,0xBF,0xEA,0x6B,0x88 }, + { 0x98,0x07,0x40,0x24,0x07,0x38,0x42,0x4A,0xB4,0x13,0xA1,0xEC,0xEE,0x20,0x5D,0xD8 }, + { 0xEE,0xA2,0x3F,0x28,0x2C,0x53,0x4D,0x48,0x93,0x83,0x9F,0x93,0xB3,0x6F,0x0B,0x7E }, + { 0x9B,0xD5,0xB8,0x98,0xBA,0xE8,0xEE,0x48,0x98,0xDD,0xC2,0x95,0x39,0x2F,0x1E,0xDB }, + { 0x09,0x6D,0xE3,0xC3,0x94,0x82,0x97,0x4B,0xA8,0x57,0xD5,0x28,0x8F,0xE3,0x3E,0x28 }, + { 0x18,0x88,0x53,0x4A,0xE0,0x5A,0xB2,0x4E,0xB2,0xEB,0x48,0x8B,0x23,0x65,0x70,0x22 } +}; + + +static const char *kGuidNames[] = +{ + "CRC", + "VolumeTopFile", + "ACPI", + "ACPI2", + "Main", + "Intel32", + "Intel64", + "Intel32c", + "Intel64c", + "MacVolume", + "MacUpdate.txt", + "MacName", + "Insyde" +}; + +enum +{ + kGuidIndex_CRC = 0 +}; + +struct CSigExtPair +{ + const char *ext; + unsigned sigSize; + Byte sig[16]; +}; + +static const CSigExtPair g_Sigs[] = +{ + { "bmp", 2, { 'B','M' } }, + { "riff", 4, { 'R','I','F','F' } }, + { "pe", 2, { 'M','Z'} }, + { "gif", 6, { 'G','I','F','8','9', 'a' } }, + { "png", 8, { 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A } }, + { "jpg", 10, { 0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46 } }, + { "rom", 2, { 0x55,0xAA } } +}; + +enum +{ + kSig_BMP, + kSig_RIFF, + kSig_PE +}; + +static const char *FindExt(const Byte *p, size_t size) +{ + unsigned i; + for (i = 0; i < ARRAY_SIZE(g_Sigs); i++) + { + const CSigExtPair &pair = g_Sigs[i]; + if (size >= pair.sigSize) + if (memcmp(p, pair.sig, pair.sigSize) == 0) + break; + } + if (i == ARRAY_SIZE(g_Sigs)) + return NULL; + switch (i) + { + case kSig_BMP: + if (GetUi32(p + 2) > size || GetUi32(p + 0xA) > size) + return NULL; + break; + case kSig_RIFF: + if (GetUi32(p + 8) == 0x45564157 || GetUi32(p + 0xC) == 0x20746D66 ) + return "wav"; + break; + case kSig_PE: + { + if (size < 512) + return NULL; + UInt32 peOffset = GetUi32(p + 0x3C); + if (peOffset >= 0x1000 || peOffset + 512 > size || (peOffset & 7) != 0) + return NULL; + if (GetUi32(p + peOffset) != 0x00004550) + return NULL; + break; + } + } + return g_Sigs[i].ext; +} + +static bool AreGuidsEq(const Byte *p1, const Byte *p2) +{ + return memcmp(p1, p2, kGuidSize) == 0; +} + +static int FindGuid(const Byte *p) +{ + for (int i = 0; i < ARRAY_SIZE(kGuids); i++) + if (AreGuidsEq(p, kGuids[i])) + return i; + return -1; +} + +static bool IsFfs(const Byte *p) +{ + return (Get32(p + 0x28) == kFvSignature && AreGuidsEq(p + kFfsGuidOffset, k_FFS_Guid)); +} + +#define FVB_ERASE_POLARITY (1 << 11) + +/* +static const CUInt32PCharPair g_FV_Attribs[] = +{ + { 0, "ReadDisabledCap" }, + { 1, "ReadEnabledCap" }, + { 2, "ReadEnabled" }, + { 3, "WriteDisabledCap" }, + { 4, "WriteEnabledCap" }, + { 5, "WriteEnabled" }, + { 6, "LockCap" }, + { 7, "Locked" }, + + { 9, "StickyWrite" }, + { 10, "MemoryMapped" }, + { 11, "ErasePolarity" }, + + { 12, "ReadLockCap" }, + { 13, "WriteLockCap" }, + { 14, "WriteLockCap" } +}; +*/ + +enum +{ + FV_FILETYPE_ALL, + FV_FILETYPE_RAW, + FV_FILETYPE_FREEFORM, + FV_FILETYPE_SECURITY_CORE, + FV_FILETYPE_PEI_CORE, + FV_FILETYPE_DXE_CORE, + FV_FILETYPE_PEIM, + FV_FILETYPE_DRIVER, + FV_FILETYPE_COMBINED_PEIM_DRIVER, + FV_FILETYPE_APPLICATION, + // The value 0x0A is reserved and should not be used + FV_FILETYPE_FIRMWARE_VOLUME_IMAGE = 0x0B, + // types 0xF0 - 0xFF are FFS file types + FV_FILETYPE_FFS_PAD = 0xF0 +}; + +static const char *g_FileTypes[] = +{ + "ALL", + "RAW", + "FREEFORM", + "SECURITY_CORE", + "PEI_CORE", + "DXE_CORE", + "PEIM", + "DRIVER", + "COMBINED_PEIM_DRIVER", + "APPLICATION", + "0xA", + "VOLUME" +}; + +// typedef Byte FFS_FILE_ATTRIBUTES; +// FFS File Attributes +#define FFS_ATTRIB_TAIL_PRESENT 0x01 +// #define FFS_ATTRIB_RECOVERY 0x02 +// #define FFS_ATTRIB_HEADER_EXTENSION 0x04 +// #define FFS_ATTRIB_DATA_ALIGNMENT 0x38 +#define FFS_ATTRIB_CHECKSUM 0x40 + +static const CUInt32PCharPair g_FFS_FILE_ATTRIBUTES[] = +{ + { 0, "" /* "TAIL" */ }, + { 1, "RECOVERY" }, + // { 2, "HEADER_EXTENSION" }, // reserved for future + { 6, "" /* "CHECKSUM" */ } +}; + +// static const Byte g_Allignment[8] = { 3, 4, 7, 9, 10, 12, 15, 16 }; + +// typedef Byte FFS_FILE_STATE; + +// Look also FVB_ERASE_POLARITY. +// Lower-order State bits are superceded by higher-order State bits. + +// #define FILE_HEADER_CONSTRUCTION 0x01 +// #define FILE_HEADER_VALID 0x02 +#define FILE_DATA_VALID 0x04 +// #define FILE_MARKED_FOR_UPDATE 0x08 +// #define FILE_DELETED 0x10 +// #define FILE_HEADER_INVALID 0x20 + +// SECTION_TYPE + +#define SECTION_ALL 0x00 + +#define SECTION_COMPRESSION 0x01 +#define SECTION_GUID_DEFINED 0x02 + +// Leaf section Type values +#define SECTION_PE32 0x10 +#define SECTION_PIC 0x11 +#define SECTION_TE 0x12 +#define SECTION_DXE_DEPEX 0x13 +#define SECTION_VERSION 0x14 +#define SECTION_USER_INTERFACE 0x15 +#define SECTION_COMPATIBILITY16 0x16 +#define SECTION_FIRMWARE_VOLUME_IMAGE 0x17 +#define SECTION_FREEFORM_SUBTYPE_GUID 0x18 +#define SECTION_RAW 0x19 +#define SECTION_PEI_DEPEX 0x1B + + +// #define GUIDED_SECTION_PROCESSING_REQUIRED 0x01 +// #define GUIDED_SECTION_AUTH_STATUS_VALID 0x02 + +static const CUInt32PCharPair g_GUIDED_SECTION_ATTRIBUTES[] = +{ + { 0, "PROCESSING_REQUIRED" }, + { 1, "AUTH" } +}; + +static const CUInt32PCharPair g_SECTION_TYPE[] = +{ + { 0x01, "COMPRESSION" }, + { 0x02, "GUID" }, + { 0x10, "efi" }, + { 0x11, "PIC" }, + { 0x12, "te" }, + { 0x13, "DXE_DEPEX" }, + { 0x14, "VERSION" }, + { 0x15, "USER_INTERFACE" }, + { 0x16, "COMPATIBILITY16" }, + { 0x17, "VOLUME" }, + { 0x18, "FREEFORM_SUBTYPE_GUID" }, + { 0x19, "raw" }, + { 0x1B, "PEI_DEPEX" } +}; + +#define COMPRESSION_TYPE_NONE 0 +#define COMPRESSION_TYPE_LZH 1 +#define COMPRESSION_TYPE_LZMA 2 + +static const char *g_Methods[] = +{ + "COPY", + "LZH", + "LZMA" +}; + +static AString UInt32ToString(UInt32 val) +{ + char sz[16]; + ConvertUInt32ToString(val, sz); + return sz; +} + +static void ConvertByteToHex(unsigned value, char *s) +{ + for (int i = 0; i < 2; i++) + { + unsigned t = value & 0xF; + value >>= 4; + s[1 - i] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10))); + } +} + +static AString GuidToString(const Byte *p, bool full) +{ + char s[16 * 2 + 8]; + int i; + for (i = 0; i < 4; i++) + ConvertByteToHex(p[3 - i], s + i * 2); + s[8] = 0; + + if (full) + { + s[8] = '-'; + for (i = 4; i < kGuidSize; i++) + ConvertByteToHex(p[i], s + 1 + i * 2); + s[32 + 1] = 0; + } + return s; +} + +static const char *kExpressionCommands[] = +{ + "BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR" +}; + +static bool ParseDepedencyExpression(const Byte *p, UInt32 size, AString &res) +{ + res.Empty(); + for (UInt32 i = 0; i < size;) + { + unsigned command = p[i++]; + if (command > ARRAY_SIZE(kExpressionCommands)) + return false; + res += kExpressionCommands[command]; + if (command < 3) + { + if (i + kGuidSize > size) + return false; + res += " "; + res += GuidToString(p + i, false); + i += kGuidSize; + } + res += "; "; + } + return true; +} + +static bool ParseUtf16zString(const Byte *p, UInt32 size, UString &res) +{ + if ((size & 1) != 0) + return false; + res.Empty(); + UInt32 i; + for (i = 0; i < size; i += 2) + { + wchar_t c = Get16(p + i); + if (c == 0) + break; + res += c; + } + return (i == size - 2); +} + +static bool ParseUtf16zString2(const Byte *p, UInt32 size, AString &res) +{ + UString s; + if (!ParseUtf16zString(p, size, s)) + return false; + res = UnicodeStringToMultiByte(s); + return true; +} + +#define FLAGS_TO_STRING(pairs, value) FlagsToString(pairs, ARRAY_SIZE(pairs), value) +#define TYPE_TO_STRING(table, value) TypeToString(table, ARRAY_SIZE(table), value) +#define TYPE_PAIR_TO_STRING(table, value) TypePairToString(table, ARRAY_SIZE(table), value) + +static const UInt32 kFileHeaderSize = 24; + +static void AddSpaceAndString(AString &res, const AString &newString) +{ + if (!res.IsEmpty() && !newString.IsEmpty()) + res += ' '; + res += newString; +} + +class CFfsFileHeader +{ + Byte CheckHeader; + Byte CheckFile; + Byte Attrib; + Byte State; + + UInt16 GetTailReference() const { return CheckHeader | ((UInt16)CheckFile << 8); } + UInt32 GetTailSize() const { return IsThereTail() ? 2 : 0; } + bool IsThereFileChecksum() const { return (Attrib & FFS_ATTRIB_CHECKSUM) != 0; } + bool IsThereTail() const { return (Attrib & FFS_ATTRIB_TAIL_PRESENT) != 0; } +public: + Byte GuidName[kGuidSize]; + Byte Type; + UInt32 Size; + + bool Parse(const Byte *p) + { + int i; + for (i = 0; i < kFileHeaderSize; i++) + if (p[i] != 0xFF) + break; + if (i == kFileHeaderSize) + return false; + memcpy(GuidName, p, kGuidSize); + CheckHeader = p[0x10]; + CheckFile = p[0x11]; + Type = p[0x12]; + Attrib = p[0x13]; + Size = Get24(p + 0x14); + State = p[0x17]; + return true; + } + + UInt32 GetDataSize() const { return Size - kFileHeaderSize - GetTailSize(); } + + bool Check(const Byte *p, UInt32 size) + { + if (Size > size) + return false; + UInt32 tailSize = GetTailSize(); + if (Size < kFileHeaderSize + tailSize) + return false; + + { + unsigned checkSum = 0; + for (UInt32 i = 0; i < kFileHeaderSize; i++) + checkSum += p[i]; + checkSum -= p[0x17]; + checkSum -= p[0x11]; + if ((Byte)checkSum != 0) + return false; + } + + if (IsThereFileChecksum()) + { + unsigned checkSum = 0; + UInt32 checkSize = Size - tailSize; + for (UInt32 i = 0; i < checkSize; i++) + checkSum += p[i]; + checkSum -= p[0x17]; + if ((Byte)checkSum != 0) + return false; + } + + if (IsThereTail()) + if (GetTailReference() != (UInt16)~Get16(p + Size - 2)) + return false; + + int polarity = 0; + int i; + for (i = 5; i >= 0; i--) + if (((State >> i) & 1) == polarity) + { + // AddSpaceAndString(s, g_FFS_FILE_STATE_Flags[i]); + if ((1 << i) != FILE_DATA_VALID) + return false; + break; + } + if (i < 0) + return false; + + return true; + } + + AString GetCharacts() const + { + AString s; + if (Type == FV_FILETYPE_FFS_PAD) + s += "PAD"; + else + s += TYPE_TO_STRING(g_FileTypes, Type); + AddSpaceAndString(s, FLAGS_TO_STRING(g_FFS_FILE_ATTRIBUTES, Attrib & 0xC7)); + /* + int align = (Attrib >> 3) & 7; + if (align != 0) + { + s += " Align:"; + s += UInt32ToString((UInt32)1 << g_Allignment[align]); + } + */ + return s; + } +}; + +#define GET_32(offs, dest) dest = Get32(p + (offs)); +#define GET_64(offs, dest) dest = Get64(p + (offs)); + +struct CCapsuleHeader +{ + UInt32 HeaderSize; + UInt32 Flags; + UInt32 CapsuleImageSize; + UInt32 SequenceNumber; + // Guid InstanceId; + UInt32 OffsetToSplitInformation; + UInt32 OffsetToCapsuleBody; + UInt32 OffsetToOemDefinedHeader; + UInt32 OffsetToAuthorInformation; + UInt32 OffsetToRevisionInformation; + UInt32 OffsetToShortDescription; + UInt32 OffsetToLongDescription; + UInt32 OffsetToApplicableDevices; + + void Clear() { memset(this, 0, sizeof(this)); } + + void Parse(const Byte *p) + { + GET_32(0x10, HeaderSize); + GET_32(0x14, Flags); + GET_32(0x18, CapsuleImageSize); + GET_32(0x1C, SequenceNumber); + GET_32(0x30, OffsetToSplitInformation); + GET_32(0x34, OffsetToCapsuleBody); + GET_32(0x38, OffsetToOemDefinedHeader); + GET_32(0x3C, OffsetToAuthorInformation); + GET_32(0x40, OffsetToRevisionInformation); + GET_32(0x44, OffsetToShortDescription); + GET_32(0x48, OffsetToLongDescription); + GET_32(0x4C, OffsetToApplicableDevices); + } +}; + +struct CItem +{ + AString Name; + AString Characts; + int Parent; + int Method; + int NameIndex; + int NumChilds; + bool IsDir; + bool Skip; + bool ThereAreSubDirs; + bool ThereIsUniqueName; + bool KeepName; + + int BufIndex; + UInt32 Offset; + UInt32 Size; + + CItem(): Parent(-1), Method(-1), NameIndex(-1), NumChilds(0), + IsDir(false), Skip(false), ThereAreSubDirs(false), ThereIsUniqueName(false), KeepName(true) {} + void SetGuid(const Byte *guidName, bool full = false); + AString GetName(int numChildsInParent) const; +}; + +void CItem::SetGuid(const Byte *guidName, bool full) +{ + ThereIsUniqueName = true; + int index = FindGuid(guidName); + if (index >= 0) + Name = kGuidNames[index]; + else + Name = GuidToString(guidName, full); +} + +AString CItem::GetName(int numChildsInParent) const +{ + if (numChildsInParent <= 1 || NameIndex < 0) + return Name; + char sz[32]; + char sz2[32]; + ConvertUInt32ToString(NameIndex, sz); + ConvertUInt32ToString(numChildsInParent - 1, sz2); + int numZeros = (int)strlen(sz2) - (int)strlen(sz); + AString res; + for (int i = 0; i < numZeros; i++) + res += '0'; + return res + (AString)sz + '.' + Name; +} + +struct CItem2 +{ + AString Name; + AString Characts; + int MainIndex; + int Parent; + + CItem2(): Parent(-1) {} +}; + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CObjectVector<CItem> _items; + CObjectVector<CItem2> _items2; + CObjectVector<CByteBuffer> _bufs; + UString _comment; + UInt32 _methodsMask; + bool _capsuleMode; + + UInt32 _totalBufsSize; + CCapsuleHeader _h; + + void AddCommentString(const wchar_t *name, UInt32 pos); + int AddItem(const CItem &item); + int AddFileItemWithIndex(CItem &item); + int AddDirItem(CItem &item); + int AddBuf(UInt32 size); + + HRESULT ParseSections(int bufIndex, UInt32 pos, UInt32 size, int parent, int method, int level); + HRESULT ParseVolume(int bufIndex, UInt32 posBase, UInt32 size, int parent, int method, int level); + HRESULT OpenCapsule(IInStream *stream); + HRESULT OpenFv(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback); + HRESULT Open2(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback); +public: + CHandler(bool capsuleMode): _capsuleMode(capsuleMode) {} + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +static const STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidCharacts, VT_BSTR} +}; + +static const STATPROPSTG kArcProps[] = +{ + { NULL, kpidComment, VT_BSTR}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidCharacts, VT_BSTR} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + const CItem2 &item2 = _items2[index]; + const CItem &item = _items[item2.MainIndex]; + switch(propID) + { + case kpidPath: + { + AString path = item2.Name; + int cur = item2.Parent; + while (cur >= 0) + { + const CItem2 &item2 = _items2[cur]; + path = item2.Name + CHAR_PATH_SEPARATOR + path; + cur = item2.Parent; + } + prop = path; + break; + } + case kpidIsDir: prop = item.IsDir; break; + case kpidMethod: if (item.Method >= 0) prop = g_Methods[item.Method]; break; + case kpidCharacts: if (!item2.Characts.IsEmpty()) prop = item2.Characts; break; + case kpidSize: if (!item.IsDir) prop = (UInt64)item.Size; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +void CHandler::AddCommentString(const wchar_t *name, UInt32 pos) +{ + UString s; + const Byte *buf = _bufs[0]; + if (pos < _h.HeaderSize) + return; + for (UInt32 i = pos;; i += 2) + { + if (s.Length() > (1 << 16) || i >= _h.OffsetToCapsuleBody) + return; + wchar_t c = Get16(buf + i); + if (c == 0) + { + i += 2; + if (i >= _h.OffsetToCapsuleBody) + return; + c = Get16(buf + i); + if (c == 0) + break; + s += L'\n'; + } + s += c; + } + if (s.IsEmpty()) + return; + _comment += L'\n'; + _comment += name; + _comment += L": "; + _comment += s; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidMethod: + { + AString s; + for (int i = 0; i < 32; i++) + if ((_methodsMask & ((UInt32)1 << i)) != 0) + AddSpaceAndString(s, g_Methods[i]); + if (!s.IsEmpty()) + prop = s; + break; + } + case kpidComment: if (!_comment.IsEmpty()) prop = _comment; break; + case kpidPhySize: prop = (UInt64)_h.CapsuleImageSize; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +#ifdef SHOW_DEBUG_INFO +static void PrintLevel(int level) +{ + PRF(printf("\n")); + for (int i = 0; i < level; i++) + PRF(printf(" ")); +} +static void MyPrint(UInt32 posBase, UInt32 size, int level, const char *name) +{ + PrintLevel(level); + PRF(printf("%s, pos = %6x, size = %6d", name, posBase, size)); +} +#else +#define PrintLevel(level) +#define MyPrint(posBase, size, level, name) +#endif + +static const unsigned kNumBigValueBits = 8 * 4; +static const unsigned kNumValueBytes = 3; +static const unsigned kNumValueBits = 8 * kNumValueBytes; +static const UInt32 kMask = (1 << kNumValueBits) - 1; + +class CBitmMemDecoder +{ + unsigned _bitPos; + UInt32 _value; + const Byte *_buf; + size_t _pos; + size_t _size; + size_t _extra; +public: + void Init(const Byte *buf, size_t size) + { + _buf = buf; + _size = size; + _pos = 0; + _extra = 0; + _bitPos = kNumBigValueBits; + Normalize(); + } + + bool IsFullFinished() const { return (_extra * 8) == (kNumBigValueBits - _bitPos); } + + void Normalize() + { + for (; _bitPos >= 8; _bitPos -= 8) + { + Byte b; + if (_pos < _size) + b = _buf[_pos++]; + else + { + b = 0; + _extra++; + } + _value = (_value << 8) | b; + } + } + + UInt32 GetValue(unsigned numBits) const + { + return ((_value >> (8 - _bitPos)) & kMask) >> (kNumValueBits - numBits); + } + + void MovePos(unsigned numBits) + { + _bitPos += numBits; + Normalize(); + } + + UInt32 ReadBitsFast(unsigned numBits) + { + UInt32 res = GetValue(numBits); + MovePos(numBits); + return res; + } + + UInt32 ReadBits(unsigned numBits); + UInt32 ReadAlignBits() { return ReadBits((32 - _bitPos) & 7); } +}; + +UInt32 CBitmMemDecoder::ReadBits(unsigned numBits) +{ + UInt32 res = GetValue(numBits); + MovePos(numBits); + return res; +} + +namespace NHuffman { + +static const int kNumTableBits = 9; +static const int kNumBitsMax = 16; + +class CDecoder +{ + UInt32 m_Limits[kNumBitsMax + 1]; + UInt32 m_Positions[kNumBitsMax + 1]; + Byte m_Lengths[1 << kNumTableBits]; + Int32 m_MainSymbol; + +public: + UInt32 *m_Symbols; + UInt32 m_NumSymbols; + + void SetSingleSymbolMode(UInt32 symbol) { m_MainSymbol = symbol; } + bool SetCodeLengths(const Byte *codeLengths); + UInt32 DecodeSymbol(CBitmMemDecoder *bitStream) + { + if (m_MainSymbol != -1) + return (UInt32)m_MainSymbol; + int numBits; + UInt32 value = bitStream->GetValue(kNumBitsMax); + if (value < m_Limits[kNumTableBits]) + numBits = m_Lengths[value >> (kNumBitsMax - kNumTableBits)]; + else + for (numBits = kNumTableBits + 1; value >= m_Limits[numBits]; numBits++); + bitStream->MovePos(numBits); + return m_Symbols[m_Positions[numBits] + ((value - m_Limits[numBits - 1]) >> (kNumBitsMax - numBits))]; + } +}; + +bool CDecoder::SetCodeLengths(const Byte *codeLengths) +{ + m_MainSymbol = -1; + int lenCounts[kNumBitsMax + 1]; + UInt32 tmpPositions[kNumBitsMax + 1]; + int i; + for (i = 1; i <= kNumBitsMax; i++) + lenCounts[i] = 0; + UInt32 symbol; + for (symbol = 0; symbol < m_NumSymbols; symbol++) + { + int len = codeLengths[symbol]; + if (len > kNumBitsMax) + return false; + lenCounts[len]++; + m_Symbols[symbol] = 0xFFFFFFFF; + } + lenCounts[0] = 0; + m_Positions[0] = m_Limits[0] = 0; + UInt32 startPos = 0; + UInt32 index = 0; + const UInt32 kMaxValue = (1 << kNumBitsMax); + for (i = 1; i <= kNumBitsMax; i++) + { + startPos += lenCounts[i] << (kNumBitsMax - i); + if (startPos > kMaxValue) + return false; + m_Limits[i] = (i == kNumBitsMax) ? kMaxValue : startPos; + m_Positions[i] = m_Positions[i - 1] + lenCounts[i - 1]; + tmpPositions[i] = m_Positions[i]; + if (i <= kNumTableBits) + { + UInt32 limit = (m_Limits[i] >> (kNumBitsMax - kNumTableBits)); + for (; index < limit; index++) + m_Lengths[index] = (Byte)i; + } + } + if (startPos != kMaxValue) + return false; + for (symbol = 0; symbol < m_NumSymbols; symbol++) + { + int len = codeLengths[symbol]; + if (len != 0) + m_Symbols[tmpPositions[len]++] = symbol; + } + return true; +} + +} + +static const int kMaxHuffmanLen = 16; +static const int kExtraSize = kMaxHuffmanLen + 3; +static const int kMinMatchLen = 3; +static const int kMaxMatchLen = 256; +static const int kNumAlphaSymsMax = 256 + kMaxMatchLen - kMinMatchLen + 1; +static const int kNumDistSymsMax = 24 + 2; // it's limited by bit decoder. + +#define HUFF_START_CODE(huff, numSymsMax, numSymBits) \ + UInt32 numSyms = bitDec.ReadBits(numSymBits); \ + Byte lens[numSymsMax]; memset(lens, 0, sizeof(lens)); \ + if (numSyms > (numSymsMax)) return S_FALSE; \ + huff.m_NumSymbols = numSyms; \ + if (numSyms == 0) { \ + numSyms = bitDec.ReadBits(numSymBits); \ + if (numSyms >= (numSymsMax)) return S_FALSE; \ + huff.SetSingleSymbolMode(numSyms); } \ + +static HRESULT LzhDecode(Byte *dest, UInt32 destSize, const Byte *src, UInt32 srcSize) +{ + if (srcSize < 8) + return S_FALSE; + { + UInt32 packSize = Get32(src); + UInt32 unpackSize = Get32(src + 4); + src += 8; + srcSize -= 8; + if (destSize != unpackSize || srcSize != packSize) + return S_FALSE; + } + + CBitmMemDecoder bitDec; + bitDec.Init(src, srcSize); + + UInt32 pos = 0; + for (;;) + { + UInt32 blockSize = bitDec.ReadBits(16); + UInt32 symbols[kExtraSize + kNumAlphaSymsMax + kNumDistSymsMax]; + + NHuffman::CDecoder extraHuff; + extraHuff.m_Symbols = symbols; + { + HUFF_START_CODE(extraHuff, kExtraSize, 5) + else + { + for (UInt32 i = 0; i < numSyms; i++) + { + if (i == 3) + { + UInt32 numZeros = bitDec.ReadBits(2); + if (i + numZeros > numSyms) + return S_FALSE; + for (UInt32 j = 0; j < numZeros; j++, i++) + lens[i] = (Byte)0; + if (i == numSyms) + break; + } + + UInt32 len = bitDec.ReadBits(3); + if (len == 7) + { + for(;; len++) + { + if (len > kMaxHuffmanLen) + return S_FALSE; + if (bitDec.ReadBits(1) == 0) + break; + } + } + lens[i] = (Byte)len; + } + if (!extraHuff.SetCodeLengths(lens)) + return S_FALSE; + } + } + + NHuffman::CDecoder symHuff; + symHuff.m_Symbols = symbols + kExtraSize; + { + HUFF_START_CODE(symHuff, kNumAlphaSymsMax, 9) + else + { + for (UInt32 i = 0; i < numSyms;) + { + UInt32 c = extraHuff.DecodeSymbol(&bitDec); + if (c > 2) + lens[i++] = (Byte)c - 2; + else + { + UInt32 numZeros; + if (c == 0) + numZeros = 1; + else if (c == 1) + numZeros = bitDec.ReadBits(4) + 3; + else + numZeros = bitDec.ReadBits(9) + 20; + if (i + numZeros > numSyms) + return S_FALSE; + for (UInt32 j = 0; j < numZeros; j++, i++) + lens[i] = (Byte)0; + } + } + if (!symHuff.SetCodeLengths(lens)) + return S_FALSE; + } + } + + NHuffman::CDecoder distHuff; + distHuff.m_Symbols = symbols + kExtraSize + kNumAlphaSymsMax; + { + const UInt32 version = 1; + const UInt32 numDistBits = version + 4; + HUFF_START_CODE(distHuff, kNumDistSymsMax, numDistBits) + else + { + for (UInt32 i = 0; i < numSyms; i++) + { + UInt32 len = bitDec.ReadBits(3); + if (len == 7) + { + for(;; len++) + { + if (len > kMaxHuffmanLen) + return S_FALSE; + if (bitDec.ReadBits(1) == 0) + break; + } + } + lens[i] = (Byte)len; + } + if (!distHuff.SetCodeLengths(lens)) + return S_FALSE; + } + } + + while (blockSize) + { + blockSize--; + UInt32 c = symHuff.DecodeSymbol(&bitDec); + if (c < 256) + { + if (destSize == 0) + return S_FALSE; + *dest++ = (Byte)c; + destSize--; + pos++; + continue; + } + c = c - 256 + kMinMatchLen; + if (destSize < c) + return S_FALSE; + UInt32 dist = distHuff.DecodeSymbol(&bitDec); + if (dist > 1) + dist = ((UInt32)1 << (dist - 1)) + bitDec.ReadBits(dist - 1); + dist++; + if (dist > pos) + return S_FALSE; + pos += c; + destSize -= c; + do + { + *dest = dest[0 - (Int32)dist]; + dest++; + } + while (--c); + } + + // PRF(printf("\ndestSize = %6d", destSize)); + if (destSize == 0) + { + if (bitDec.ReadAlignBits() != 0) + return S_FALSE; + if (bitDec.ReadBits(8) != 0) + return S_FALSE; + if (!bitDec.IsFullFinished()) + return S_FALSE; + break; + } + } + return S_OK; +} + +int CHandler::AddItem(const CItem &item) +{ + if (_items.Size() >= kNumFilesMax) + throw 2; + return _items.Add(item); +} + +int CHandler::AddFileItemWithIndex(CItem &item) +{ + int nameIndex = _items.Size(); + if (item.Parent >= 0) + nameIndex = _items[item.Parent].NumChilds++; + item.NameIndex = nameIndex; + return AddItem(item); +} + +int CHandler::AddDirItem(CItem &item) +{ + if (item.Parent >= 0) + _items[item.Parent].ThereAreSubDirs = true; + item.IsDir = true; + item.Size = 0; + return AddItem(item); +} + +int CHandler::AddBuf(UInt32 size) +{ + if (size > kBufTotalSizeMax - _totalBufsSize) + throw 1; + _totalBufsSize += size; + int index = _bufs.Add(CByteBuffer()); + _bufs[index].SetCapacity(size); + return index; +} + +HRESULT CHandler::ParseSections(int bufIndex, UInt32 posBase, UInt32 size, int parent, int method, int level) +{ + if (level > kLevelMax) + return S_FALSE; + MyPrint(posBase, size, level, "Sections"); + level++; + const Byte *bufData = _bufs[bufIndex]; + UInt32 pos = 0; + for (;;) + { + if (size == pos) + return S_OK; + PrintLevel(level); + PRF(printf("%s, pos = %6x", "Sect", pos)); + pos = (pos + 3) & ~(UInt32)3; + if (pos > size) + return S_FALSE; + UInt32 rem = size - pos; + if (rem == 0) + return S_OK; + if (rem < 4) + return S_FALSE; + const Byte *p = bufData + posBase + pos; + UInt32 sectSize = Get24(p); + if (sectSize > rem || sectSize < 4) + return S_FALSE; + + Byte type = p[3]; + PrintLevel(level); + PRF(printf("%s, type = %2x, pos = %6x, size = %6d", "Sect", type, pos, sectSize)); + CItem item; + item.Method = method; + item.BufIndex = bufIndex; + item.Parent = parent; + item.Offset = posBase + pos + 4; + UInt32 sectDataSize = sectSize - 4; + item.Size = sectDataSize; + item.Name = TYPE_PAIR_TO_STRING(g_SECTION_TYPE, type); + + if (type == SECTION_COMPRESSION) + { + if (sectSize < 4 + 5) + return S_FALSE; + UInt32 uncompressedSize = Get32(p + 4); + Byte compressionType = p[8]; + + UInt32 newSectSize = sectSize - 9; + UInt32 newOffset = posBase + pos + 9; + const Byte *pStart = p + 9; + + item.KeepName = false; + if (compressionType > 2) + { + // AddFileItemWithIndex(item); + return S_FALSE; + } + else + { + item.Name = g_Methods[compressionType]; + // int parent = AddDirItem(item); + if (compressionType == COMPRESSION_TYPE_NONE) + { + RINOK(ParseSections(bufIndex, newOffset, newSectSize, parent, method, level)); + } + else if (compressionType == COMPRESSION_TYPE_LZH) + { + int newBufIndex = AddBuf(uncompressedSize); + CByteBuffer &buf = _bufs[newBufIndex]; + RINOK(LzhDecode(buf, uncompressedSize, pStart, newSectSize)); + RINOK(ParseSections(newBufIndex, 0, uncompressedSize, parent, compressionType, level)); + } + else + { + if (newSectSize < 4 + 5 + 8) + return S_FALSE; + unsigned addSize = 4; + if (pStart[0] == 0x5d && pStart[1] == 0 && pStart[2] == 0 && pStart[3] == 0x80 && pStart[4] == 0) + { + addSize = 0; + // some archives have such header + } + else + { + // normal BIOS contains uncompressed size here + // UInt32 uncompressedSize2 = Get24(pStart); + // Byte firstSectType = p[9 + 3]; + // firstSectType can be 0 in some archives + } + pStart += addSize; + UInt64 lzmaUncompressedSize = Get64(pStart + 5); + if (lzmaUncompressedSize > (1 << 30)) + return S_FALSE; + if (lzmaUncompressedSize < uncompressedSize) + return S_FALSE; + SizeT destLen = (SizeT)lzmaUncompressedSize; + int newBufIndex = AddBuf((UInt32)lzmaUncompressedSize); + CByteBuffer &buf = _bufs[newBufIndex]; + ELzmaStatus status; + SizeT srcLen = newSectSize - (addSize + 5 + 8); + SizeT srcLen2 = srcLen; + SRes res = LzmaDecode(buf, &destLen, pStart + 13, &srcLen, + pStart, 5, LZMA_FINISH_END, &status, &g_Alloc); + if (res != 0) + return S_FALSE; + if (srcLen != srcLen2 || destLen != lzmaUncompressedSize || status != LZMA_STATUS_FINISHED_WITH_MARK) + return S_FALSE; + RINOK(ParseSections(newBufIndex, 0, (UInt32)lzmaUncompressedSize, parent, compressionType, level)); + } + _methodsMask |= (1 << compressionType); + } + } + else if (type == SECTION_GUID_DEFINED) + { + const UInt32 kHeaderSize = 4 + kGuidSize + 4; + if (sectSize < kHeaderSize) + return S_FALSE; + item.SetGuid(p + 4); + UInt32 dataOffset = Get16(p + 4 + kGuidSize); + UInt32 attrib = Get16(p + 4 + kGuidSize + 2); + if (dataOffset > sectSize || dataOffset < kHeaderSize) + return S_FALSE; + UInt32 newSectSize = sectSize - dataOffset; + item.Size = newSectSize; + UInt32 newOffset = posBase + pos + dataOffset; + item.Offset = newOffset; + UInt32 propsSize = dataOffset - kHeaderSize; + bool needDir = true; + AddSpaceAndString(item.Characts, FLAGS_TO_STRING(g_GUIDED_SECTION_ATTRIBUTES, attrib)); + if (AreGuidsEq(p + 0x4, kGuids[kGuidIndex_CRC]) && propsSize == 4) + { + needDir = false; + item.KeepName = false; + if (CrcCalc(bufData + newOffset, newSectSize) != Get32(p + kHeaderSize)) + return S_FALSE; + } + else + { + if (propsSize != 0) + { + CItem item2 = item; + item2.Name += ".prop"; + item2.Size = propsSize; + item2.Offset = posBase + pos + kHeaderSize; + AddItem(item2); + } + } + int newParent = parent; + if (needDir) + newParent = AddDirItem(item); + RINOK(ParseSections(bufIndex, newOffset, newSectSize, newParent, method, level)); + } + else if (type == SECTION_FIRMWARE_VOLUME_IMAGE) + { + item.KeepName = false; + int newParent = AddDirItem(item); + RINOK(ParseVolume(bufIndex, posBase + pos + 4, sectSize - 4, newParent, method, level)); + } + else + { + bool needAdd = true; + switch(type) + { + case SECTION_RAW: + { + const UInt32 kInsydeOffset = 12; + if (sectDataSize >= kFvHeaderSize + kInsydeOffset) + { + if (IsFfs(p + 4 + kInsydeOffset) && + sectDataSize - kInsydeOffset == Get64(p + 4 + kInsydeOffset + 0x20)) + { + needAdd = false; + item.Name = "vol"; + int newParent = AddDirItem(item); + RINOK(ParseVolume(bufIndex, posBase + pos + 4 + kInsydeOffset, sectDataSize - kInsydeOffset, newParent, method, level)); + } + + if (needAdd) + { + const char *ext = FindExt(p + 4, sectDataSize); + if (ext) + item.Name = ext; + } + } + break; + } + case SECTION_DXE_DEPEX: + case SECTION_PEI_DEPEX: + { + AString s; + if (ParseDepedencyExpression(p + 4, sectDataSize, s)) + { + if (s.Length() < (1 << 9)) + { + s = '[' + s + ']'; + AddSpaceAndString(_items[item.Parent].Characts, s); + needAdd = false; + } + else + { + item.BufIndex = AddBuf(s.Length()); + CByteBuffer &buf0 = _bufs[item.BufIndex]; + memcpy(buf0, s, s.Length()); + item.Offset = 0; + item.Size = s.Length(); + } + } + break; + } + case SECTION_VERSION: + { + if (sectDataSize > 2) + { + AString s; + if (ParseUtf16zString2(p + 6, sectDataSize - 2, s)) + { + AddSpaceAndString(_items[item.Parent].Characts, (AString)"ver:" + UInt32ToString(Get16(p + 4)) + ' ' + s); + needAdd = false; + } + } + break; + } + case SECTION_USER_INTERFACE: + { + AString s; + if (ParseUtf16zString2(p + 4, sectDataSize, s)) + { + _items[parent].Name = s; + needAdd = false; + } + break; + } + case SECTION_FREEFORM_SUBTYPE_GUID: + { + if (sectDataSize >= kGuidSize) + { + item.SetGuid(p + 4); + item.Size = sectDataSize - kGuidSize; + item.Offset = posBase + pos + 4 + kGuidSize; + } + break; + } + } + + if (needAdd) + AddFileItemWithIndex(item); + } + pos += sectSize; + } +} + +static UInt32 Count_FF_Bytes(const Byte *p, UInt32 size) +{ + UInt32 i; + for (i = 0; i < size && p[i] == 0xFF; i++); + return i; +} + +static bool Is_FF_Stream(const Byte *p, UInt32 size) +{ + return (Count_FF_Bytes(p, size) == size); +} + +HRESULT CHandler::ParseVolume(int bufIndex, UInt32 posBase, UInt32 size, int parent, int method, int level) +{ + if (level > kLevelMax) + return S_FALSE; + MyPrint(posBase, size, level, "Volume"); + level++; + if (size < kFvHeaderSize) + return S_FALSE; + const Byte *p = _bufs[bufIndex] + posBase; + // first 16 bytes must be zeros, but they are not zeros sometimes. + if (!AreGuidsEq(p + kFfsGuidOffset, k_FFS_Guid) && + !AreGuidsEq(p + kFfsGuidOffset, k_MacFS_Guid)) + { + CItem item; + item.Method = method; + item.BufIndex = bufIndex; + item.Parent = parent; + item.Offset = posBase; + item.Size = size; + item.SetGuid(p + kFfsGuidOffset); + item.Name += " [VOLUME]"; + AddItem(item); + return S_OK; + } + + if (Get32(p + 0x28) != kFvSignature) + return S_FALSE; + UInt32 attribs = Get32(p + 0x2C); + if ((attribs & FVB_ERASE_POLARITY) == 0) + return S_FALSE; + // if (parent >= 0) AddSpaceAndString(_items[parent].Characts, FLAGS_TO_STRING(g_FV_Attribs, attribs)); + UInt64 fvLen = Get64(p + 0x20); + UInt32 headerLen = Get16(p + 0x30); + if (headerLen > size || headerLen < kFvHeaderSize || (headerLen & 0x7) != 0 || + fvLen > size || fvLen < headerLen) + return S_FALSE; + + { + UInt32 checkCalc = 0; + for (UInt32 i = 0; i < headerLen; i += 2) + checkCalc += Get16(p + i); + if ((checkCalc & 0xFFFF) != 0) + return S_FALSE; + } + + // 3 reserved bytes are not zeros sometimes. + // UInt16 ExtHeaderOffset; // in new SPECIFICATION? + // Byte revision = p[0x37]; + + UInt32 pos = kFvHeaderSize; + for (;;) + { + if (pos >= headerLen) + return S_FALSE; + UInt32 numBlocks = Get32(p + pos); + UInt32 length = Get32(p + pos + 4); + pos += 8; + if (numBlocks == 0 && length == 0) + break; + } + if (pos != headerLen) + return S_FALSE; + + CRecordVector<UInt32> guidsVector; + + for (;;) + { + UInt32 rem = (UInt32)fvLen - pos; + if (rem < kFileHeaderSize) + break; + pos = (pos + 7) & ~7; + rem = (UInt32)fvLen - pos; + if (rem < kFileHeaderSize) + break; + + CItem item; + item.Method = method; + item.BufIndex = bufIndex; + item.Parent = parent; + + const Byte *pFile = p + pos; + CFfsFileHeader fh; + if (!fh.Parse(pFile)) + { + UInt32 num_FF_bytes = Count_FF_Bytes(pFile, rem); + if (num_FF_bytes != rem) + { + item.Name = "[junk]"; + item.Offset = posBase + pos + num_FF_bytes; + item.Size = rem - num_FF_bytes; + AddItem(item); + } + break; + } + PrintLevel(level); PRF(printf("%s, pos = %6x, size = %6d", "FILE", posBase + pos, fh.Size)); + if (!fh.Check(pFile, rem)) + return S_FALSE; + + UInt32 offset = posBase + pos + kFileHeaderSize; + UInt32 sectSize = fh.GetDataSize(); + item.Offset = offset; + item.Size = sectSize; + + pos += fh.Size; + + if (fh.Type == FV_FILETYPE_FFS_PAD) + if (Is_FF_Stream(pFile + kFileHeaderSize, sectSize)) + continue; + + UInt32 guid32 = Get32(fh.GuidName); + bool full = true; + if (guidsVector.FindInSorted(guid32) < 0) + { + guidsVector.AddToUniqueSorted(guid32); + full = false; + } + item.SetGuid(fh.GuidName, full); + + item.Characts = fh.GetCharacts(); + PrintLevel(level); + PRF(printf("%s", item.Characts)); + + if (fh.Type == FV_FILETYPE_FFS_PAD || + fh.Type == FV_FILETYPE_RAW) + { + bool isVolume = false; + if (fh.Type == FV_FILETYPE_RAW) + { + if (sectSize >= kFvHeaderSize) + if (IsFfs(pFile + kFileHeaderSize)) + isVolume = true; + } + if (isVolume) + { + int newParent = AddDirItem(item); + RINOK(ParseVolume(bufIndex, offset, sectSize, newParent, method, level)); + } + else + AddItem(item); + } + else + { + int newParent = AddDirItem(item); + RINOK(ParseSections(bufIndex, offset, sectSize, newParent, method, level)); + } + } + return S_OK; +} + +HRESULT CHandler::OpenCapsule(IInStream *stream) +{ + const UInt32 kHeaderSize = 80; + Byte buf[kHeaderSize]; + RINOK(ReadStream_FALSE(stream, buf, kHeaderSize)); + _h.Parse(buf); + if (_h.HeaderSize != kHeaderSize || + _h.CapsuleImageSize < kHeaderSize || + _h.OffsetToCapsuleBody < kHeaderSize || + _h.OffsetToCapsuleBody > _h.CapsuleImageSize) + return S_FALSE; + + if (_h.SequenceNumber != 0 || + _h.OffsetToSplitInformation != 0 ) + return E_NOTIMPL; + + int bufIndex = AddBuf(_h.CapsuleImageSize); + CByteBuffer &buf0 = _bufs[bufIndex]; + memcpy(buf0, buf, kHeaderSize); + ReadStream_FALSE(stream, buf0 + kHeaderSize, _h.CapsuleImageSize - kHeaderSize); + + AddCommentString(L"Author", _h.OffsetToAuthorInformation); + AddCommentString(L"Revision", _h.OffsetToRevisionInformation); + AddCommentString(L"Short Description", _h.OffsetToShortDescription); + AddCommentString(L"Long Description", _h.OffsetToLongDescription); + + return ParseVolume(bufIndex, _h.OffsetToCapsuleBody, _h.CapsuleImageSize - _h.OffsetToCapsuleBody, -1, -1, 0); +} + +HRESULT CHandler::OpenFv(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback) +{ + UInt64 fileSize; + RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize)); + if (fileSize > (1 << 27)) + return S_FALSE; + + UInt32 volIndex = 0; + UInt32 pos = 0, prevEnd = 0; + if (callback) + { + RINOK(callback->SetTotal(NULL, &fileSize)); + } + + for (;;) + { + UInt64 limit = 0; + UInt64 *limitPtr = NULL; + if (maxCheckStartPosition) + { + UInt32 directSize = pos - prevEnd; + if (directSize >= *maxCheckStartPosition) + break; + limit = *maxCheckStartPosition - directSize; + limitPtr = &limit; + } + + UInt64 resPos; + RINOK(stream->Seek(pos + kFfsGuidOffset, STREAM_SEEK_SET, NULL)); + if (FindSignatureInStream(stream, k_FFS_Guid, kGuidSize, limitPtr, resPos) == S_FALSE) + break; + + pos += (UInt32)resPos; + UInt64 fvSize; + { + UInt32 rem = (UInt32)fileSize - pos; + if (rem < kFvHeaderSize) + break; + RINOK(stream->Seek(pos, STREAM_SEEK_SET, NULL)); + Byte buf[kFvHeaderSize]; + RINOK(ReadStream_FALSE(stream, buf, kFvHeaderSize)); + fvSize = Get64(buf + 0x20); + if (!IsFfs(buf) || fvSize > rem) + { + pos++; + continue; + } + } + + RINOK(stream->Seek(prevEnd, STREAM_SEEK_SET, NULL)); + + if (pos != prevEnd) + { + CItem item; + item.Offset = 0; + item.Size = pos - prevEnd; + item.BufIndex = AddBuf(item.Size); + CByteBuffer &buf0 = _bufs[item.BufIndex]; + RINOK(ReadStream_FALSE(stream, buf0, item.Size)); + item.Name = UInt32ToString(volIndex++); + AddItem(item); + } + + prevEnd = pos; + RINOK(stream->Seek(pos, STREAM_SEEK_SET, NULL)); + UInt32 fvSize32 = (UInt32)fvSize; + CItem item; + item.BufIndex = AddBuf(fvSize32); + CByteBuffer &buf0 = _bufs[item.BufIndex]; + item.Name = UInt32ToString(volIndex++); + int parent = AddDirItem(item); + ReadStream_FALSE(stream, buf0, fvSize32); + RINOK(ParseVolume(item.BufIndex, 0, fvSize32, parent, -1, 0)); + pos += fvSize32; + prevEnd = pos; + + if (callback) + { + UInt64 pos64 = pos; + RINOK(callback->SetCompleted(NULL, &pos64)); + } + } + if (_items.Size() == 0) + return S_FALSE; + + if (pos <= fileSize) + { + pos = (UInt32)fileSize; + if (prevEnd < pos) + { + CItem item; + item.Offset = 0; + item.Size = pos - prevEnd; + item.BufIndex = AddBuf(item.Size); + CByteBuffer &buf0 = _bufs[item.BufIndex]; + RINOK(stream->Seek(prevEnd, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, buf0, item.Size)); + item.Name = UInt32ToString(volIndex++); + AddItem(item); + } + } + _h.CapsuleImageSize = pos; + return S_OK; +} + +HRESULT CHandler::Open2(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback) +{ + if (_capsuleMode) + { + RINOK(OpenCapsule(stream)); + } + else + { + RINOK(OpenFv(stream, maxCheckStartPosition, callback)); + } + + CIntVector numChilds; + numChilds.Reserve(_items.Size()); + int i; + for (i = 0; i < _items.Size(); i++) + { + numChilds.Add(0); + int parent = _items[i].Parent; + if (parent >= 0) + numChilds[parent]++; + } + + for (i = 0; i < _items.Size(); i++) + { + CItem &item = _items[i]; + int parent = item.Parent; + if (parent >= 0) + { + CItem &parentItem = _items[parent]; + if (numChilds[parent] == 1) + if (!item.ThereIsUniqueName || !parentItem.ThereIsUniqueName || !parentItem.ThereAreSubDirs) + parentItem.Skip = true; + } + } + + CIntVector mainToReduced; + for (i = 0; i < _items.Size(); i++) + { + mainToReduced.Add(_items2.Size()); + const CItem &item = _items[i]; + if (item.Skip) + continue; + AString name; + int numItems = -1; + int parent = item.Parent; + if (parent >= 0) + numItems = numChilds[parent]; + AString name2 = item.GetName(numItems); + AString characts2 = item.Characts; + if (item.KeepName) + name = name2; + while (parent >= 0) + { + const CItem &item3 = _items[parent]; + if (!item3.Skip) + break; + if (item3.KeepName) + { + AString name3 = item3.GetName(-1); + if (name.IsEmpty()) + name = name3; + else + name = name3 + '.' + name; + } + AddSpaceAndString(characts2, item3.Characts); + parent = item3.Parent; + } + if (name.IsEmpty()) + name = name2; + + CItem2 item2; + item2.MainIndex = i; + item2.Name = name; + item2.Characts = characts2; + if (parent >= 0) + item2.Parent = mainToReduced[parent]; + _items2.Add(item2); + /* + CItem2 item2; + item2.MainIndex = i; + item2.Name = item.Name; + item2.Parent = item.Parent; + _items2.Add(item2); + */ + } + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + Close(); + try + { + if (Open2(inStream, maxCheckStartPosition, callback) != S_OK) + return S_FALSE; + } + catch(...) { return S_FALSE; } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _totalBufsSize = 0; + _methodsMask = 0; + _items.Clear(); + _items2.Clear(); + _bufs.Clear(); + _comment.Empty(); + _h.Clear(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items2.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _items2.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _items[_items2[allFilesMode ? i : indices[i]].MainIndex].Size; + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + for (i = 0; i < numItems; i++) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + const CItem &item = _items[_items2[index].MainIndex]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + currentTotalSize += item.Size; + + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + if (testMode || item.IsDir) + { + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + int res = NExtract::NOperationResult::kDataError; + CMyComPtr<ISequentialInStream> inStream; + GetStream(index, &inStream); + if (inStream) + { + RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress)); + if (copyCoderSpec->TotalSize == item.Size) + res = NExtract::NOperationResult::kOK; + } + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(res)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + const CItem &item = _items[_items2[index].MainIndex]; + if (item.IsDir) + return S_FALSE; + CBufInStream *streamSpec = new CBufInStream; + CMyComPtr<IInStream> streamTemp = streamSpec; + const CByteBuffer &buf = _bufs[item.BufIndex]; + /* + if (item.Offset + item.Size > buf.GetCapacity()) + return S_FALSE; + */ + streamSpec->Init(buf + item.Offset, item.Size, (IInArchive *)this); + *stream = streamTemp.Detach(); + return S_OK; + COM_TRY_END +} + + +namespace UEFIc +{ + static IInArchive *CreateArc() { return new CHandler(true); } + static CArcInfo g_ArcInfo = + { L"UEFIc", L"scap", 0, 0xD0, CAPSULE_SIGNATURE, kCapsuleSigSize, false, CreateArc, 0 }; + REGISTER_ARC(UEFIc) +} + +namespace UEFIs +{ + static IInArchive *CreateArc() { return new CHandler(false); } + static CArcInfo g_ArcInfo = + { L"UEFIs", L"", 0, 0xD1, FFS_SIGNATURE, kGuidSize, false, CreateArc, 0 }; + REGISTER_ARC(UEFIs) +} + +}} |