diff options
Diffstat (limited to 'src/libs/7zip/win/CPP/7zip/Archive/PeHandler.cpp')
-rw-r--r-- | src/libs/7zip/win/CPP/7zip/Archive/PeHandler.cpp | 1752 |
1 files changed, 1752 insertions, 0 deletions
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/PeHandler.cpp b/src/libs/7zip/win/CPP/7zip/Archive/PeHandler.cpp new file mode 100644 index 000000000..c64067aa5 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/PeHandler.cpp @@ -0,0 +1,1752 @@ +// PeHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/DynamicBuffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariantUtils.h" +#include "Windows/Time.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +using namespace NWindows; + +namespace NArchive { +namespace NPe { + +#define NUM_SCAN_SECTIONS_MAX (1 << 6) + +#define PE_SIG 0x00004550 +#define PE_OptHeader_Magic_32 0x10B +#define PE_OptHeader_Magic_64 0x20B + +static AString GetDecString(UInt32 v) +{ + char sz[32]; + ConvertUInt64ToString(v, sz); + return sz; +} + +struct CVersion +{ + UInt16 Major; + UInt16 Minor; + + void Parse(const Byte *buf); + AString GetString() const { return GetDecString(Major) + '.' + GetDecString(Minor); } +}; + +void CVersion::Parse(const Byte *p) +{ + Major = Get16(p); + Minor = Get16(p + 2); +} + +static const UInt32 kHeaderSize = 4 + 20; + +struct CHeader +{ + UInt16 NumSections; + UInt32 Time; + UInt32 PointerToSymbolTable; + UInt32 NumSymbols; + UInt16 OptHeaderSize; + UInt16 Flags; + UInt16 Machine; + + bool Parse(const Byte *buf); +}; + +bool CHeader::Parse(const Byte *p) +{ + if (Get32(p) != PE_SIG) + return false; + p += 4; + Machine = Get16(p + 0); + NumSections = Get16(p + 2); + Time = Get32(p + 4); + PointerToSymbolTable = Get32(p + 8); + NumSymbols = Get32(p + 12); + OptHeaderSize = Get16(p + 16); + Flags = Get16(p + 18); + return true; +} + +struct CDirLink +{ + UInt32 Va; + UInt32 Size; + void Parse(const Byte *p); +}; + +void CDirLink::Parse(const Byte *p) +{ + Va = Get32(p); + Size = Get32(p + 4); +} + +enum +{ + kDirLink_Certificate = 4, + kDirLink_Debug = 6 +}; + +struct CDebugEntry +{ + UInt32 Flags; + UInt32 Time; + CVersion Ver; + UInt32 Type; + UInt32 Size; + UInt32 Va; + UInt32 Pa; + + void Parse(const Byte *p); +}; + +void CDebugEntry::Parse(const Byte *p) +{ + Flags = Get32(p); + Time = Get32(p + 4); + Ver.Parse(p + 8); + Type = Get32(p + 12); + Size = Get32(p + 16); + Va = Get32(p + 20); + Pa = Get32(p + 24); +} + +static const UInt32 kNumDirItemsMax = 16; + +struct COptHeader +{ + UInt16 Magic; + Byte LinkerVerMajor; + Byte LinkerVerMinor; + + UInt32 CodeSize; + UInt32 InitDataSize; + UInt32 UninitDataSize; + + // UInt32 AddressOfEntryPoint; + // UInt32 BaseOfCode; + // UInt32 BaseOfData32; + UInt64 ImageBase; + + UInt32 SectAlign; + UInt32 FileAlign; + + CVersion OsVer; + CVersion ImageVer; + CVersion SubsysVer; + + UInt32 ImageSize; + UInt32 HeadersSize; + UInt32 CheckSum; + UInt16 SubSystem; + UInt16 DllCharacts; + + UInt64 StackReserve; + UInt64 StackCommit; + UInt64 HeapReserve; + UInt64 HeapCommit; + + UInt32 NumDirItems; + CDirLink DirItems[kNumDirItemsMax]; + + bool Is64Bit() const { return Magic == PE_OptHeader_Magic_64; } + bool Parse(const Byte *p, UInt32 size); + + int GetNumFileAlignBits() const + { + for (int i = 9; i <= 16; i++) + if (((UInt32)1 << i) == FileAlign) + return i; + return -1; + } +}; + +bool COptHeader::Parse(const Byte *p, UInt32 size) +{ + Magic = Get16(p); + switch (Magic) + { + case PE_OptHeader_Magic_32: + case PE_OptHeader_Magic_64: + break; + default: + return false; + } + LinkerVerMajor = p[2]; + LinkerVerMinor = p[3]; + + bool hdr64 = Is64Bit(); + + CodeSize = Get32(p + 4); + InitDataSize = Get32(p + 8); + UninitDataSize = Get32(p + 12); + + // AddressOfEntryPoint = Get32(p + 16); + // BaseOfCode = Get32(p + 20); + // BaseOfData32 = hdr64 ? 0: Get32(p + 24); + ImageBase = hdr64 ? GetUi64(p + 24) : Get32(p + 28); + + SectAlign = Get32(p + 32); + FileAlign = Get32(p + 36); + + OsVer.Parse(p + 40); + ImageVer.Parse(p + 44); + SubsysVer.Parse(p + 48); + + // reserved = Get32(p + 52); + + ImageSize = Get32(p + 56); + HeadersSize = Get32(p + 60); + CheckSum = Get32(p + 64); + SubSystem = Get16(p + 68); + DllCharacts = Get16(p + 70); + + if (hdr64) + { + StackReserve = Get64(p + 72); + StackCommit = Get64(p + 80); + HeapReserve = Get64(p + 88); + HeapCommit = Get64(p + 96); + } + else + { + StackReserve = Get32(p + 72); + StackCommit = Get32(p + 76); + HeapReserve = Get32(p + 80); + HeapCommit = Get32(p + 84); + } + UInt32 pos = (hdr64 ? 108 : 92); + NumDirItems = Get32(p + pos); + pos += 4; + if (pos + 8 * NumDirItems != size) + return false; + for (UInt32 i = 0; i < NumDirItems && i < kNumDirItemsMax; i++) + DirItems[i].Parse(p + pos + i * 8); + return true; +} + +static const UInt32 kSectionSize = 40; + +struct CSection +{ + AString Name; + + UInt32 VSize; + UInt32 Va; + UInt32 PSize; + UInt32 Pa; + UInt32 Flags; + UInt32 Time; + // UInt16 NumRelocs; + bool IsDebug; + bool IsRealSect; + bool IsAdditionalSection; + + CSection(): IsRealSect(false), IsDebug(false), IsAdditionalSection(false) {} + UInt64 GetPackSize() const { return PSize; } + + void UpdateTotalSize(UInt32 &totalSize) + { + UInt32 t = Pa + PSize; + if (t > totalSize) + totalSize = t; + } + void Parse(const Byte *p); +}; + +static bool operator <(const CSection &a1, const CSection &a2) { return (a1.Pa < a2.Pa) || ((a1.Pa == a2.Pa) && (a1.PSize < a2.PSize)) ; } +static bool operator ==(const CSection &a1, const CSection &a2) { return (a1.Pa == a2.Pa) && (a1.PSize == a2.PSize); } + +static AString GetName(const Byte *name) +{ + const int kNameSize = 8; + AString res; + char *p = res.GetBuffer(kNameSize); + memcpy(p, name, kNameSize); + p[kNameSize] = 0; + res.ReleaseBuffer(); + return res; +} + +void CSection::Parse(const Byte *p) +{ + Name = GetName(p); + VSize = Get32(p + 8); + Va = Get32(p + 12); + PSize = Get32(p + 16); + Pa = Get32(p + 20); + // NumRelocs = Get16(p + 32); + Flags = Get32(p + 36); +} + +static const CUInt32PCharPair g_HeaderCharacts[] = +{ + { 1, "Executable" }, + { 13, "DLL" }, + { 8, "32-bit" }, + { 5, "LargeAddress" }, + { 0, "NoRelocs" }, + { 2, "NoLineNums" }, + { 3, "NoLocalSyms" }, + { 4, "AggressiveWsTrim" }, + { 9, "NoDebugInfo" }, + { 10, "RemovableRun" }, + { 11, "NetRun" }, + { 12, "System" }, + { 14, "UniCPU" }, + { 7, "Little-Endian" }, + { 15, "Big-Endian" } +}; + +static const CUInt32PCharPair g_DllCharacts[] = +{ + { 6, "Relocated" }, + { 7, "Integrity" }, + { 8, "NX-Compatible" }, + { 9, "NoIsolation" }, + { 10, "NoSEH" }, + { 11, "NoBind" }, + { 13, "WDM" }, + { 15, "TerminalServerAware" } +}; + +static const CUInt32PCharPair g_SectFlags[] = +{ + { 3, "NoPad" }, + { 5, "Code" }, + { 6, "InitializedData" }, + { 7, "UninitializedData" }, + { 9, "Comments" }, + { 11, "Remove" }, + { 12, "COMDAT" }, + { 15, "GP" }, + { 24, "ExtendedRelocations" }, + { 25, "Discardable" }, + { 26, "NotCached" }, + { 27, "NotPaged" }, + { 28, "Shared" }, + { 29, "Execute" }, + { 30, "Read" }, + { 31, "Write" } +}; + +static const CUInt32PCharPair g_MachinePairs[] = +{ + { 0x014C, "x86" }, + { 0x0162, "MIPS-R3000" }, + { 0x0166, "MIPS-R4000" }, + { 0x0168, "MIPS-R10000" }, + { 0x0169, "MIPS-V2" }, + { 0x0184, "Alpha" }, + { 0x01A2, "SH3" }, + { 0x01A3, "SH3-DSP" }, + { 0x01A4, "SH3E" }, + { 0x01A6, "SH4" }, + { 0x01A8, "SH5" }, + { 0x01C0, "ARM" }, + { 0x01C2, "ARM-Thumb" }, + { 0x01F0, "PPC" }, + { 0x01F1, "PPC-FP" }, + { 0x0200, "IA-64" }, + { 0x0284, "Alpha-64" }, + { 0x0200, "IA-64" }, + { 0x0366, "MIPSFPU" }, + { 0x8664, "x64" }, + { 0x0EBC, "EFI" } +}; + +static const CUInt32PCharPair g_SubSystems[] = +{ + { 0, "Unknown" }, + { 1, "Native" }, + { 2, "Windows GUI" }, + { 3, "Windows CUI" }, + { 7, "Posix" }, + { 9, "Windows CE" }, + { 10, "EFI" }, + { 11, "EFI Boot" }, + { 12, "EFI Runtime" }, + { 13, "EFI ROM" }, + { 14, "XBOX" } +}; + +static const wchar_t *g_ResTypes[] = +{ + NULL, + L"CURSOR", + L"BITMAP", + L"ICON", + L"MENU", + L"DIALOG", + L"STRING", + L"FONTDIR", + L"FONT", + L"ACCELERATOR", + L"RCDATA", + L"MESSAGETABLE", + L"GROUP_CURSOR", + NULL, + L"GROUP_ICON", + NULL, + L"VERSION", + L"DLGINCLUDE", + NULL, + L"PLUGPLAY", + L"VXD", + L"ANICURSOR", + L"ANIICON", + L"HTML", + L"MANIFEST" +}; + +const UInt32 kFlag = (UInt32)1 << 31; +const UInt32 kMask = ~kFlag; + +struct CTableItem +{ + UInt32 Offset; + UInt32 ID; +}; + + +const UInt32 kBmpHeaderSize = 14; +const UInt32 kIconHeaderSize = 22; + +struct CResItem +{ + UInt32 Type; + UInt32 ID; + UInt32 Lang; + + UInt32 Size; + UInt32 Offset; + + UInt32 HeaderSize; + Byte Header[kIconHeaderSize]; // it must be enough for max size header. + bool Enabled; + + bool IsNameEqual(const CResItem &item) const { return Lang == item.Lang; } + UInt32 GetSize() const { return Size + HeaderSize; } + bool IsBmp() const { return Type == 2; } + bool IsIcon() const { return Type == 3; } + bool IsString() const { return Type == 6; } + bool IsRcData() const { return Type == 10; } + bool IsRcDataOrUnknown() const { return IsRcData() || Type > 64; } +}; + +struct CStringItem +{ + UInt32 Lang; + UInt32 Size; + CByteDynamicBuffer Buf; + + void AddChar(Byte c); + void AddWChar(UInt16 c); +}; + +void CStringItem::AddChar(Byte c) +{ + Buf.EnsureCapacity(Size + 2); + Buf[Size++] = c; + Buf[Size++] = 0; +} + +void CStringItem::AddWChar(UInt16 c) +{ + if (c == '\n') + { + AddChar('\\'); + c = 'n'; + } + Buf.EnsureCapacity(Size + 2); + SetUi16(Buf + Size, c); + Size += 2; +} + +struct CMixItem +{ + int SectionIndex; + int ResourceIndex; + int StringIndex; + + bool IsSectionItem() const { return ResourceIndex < 0 && StringIndex < 0; }; +}; + +struct CUsedBitmap +{ + CByteBuffer Buf; +public: + void Alloc(size_t size) + { + size = (size + 7) / 8; + Buf.SetCapacity(size); + memset(Buf, 0, size); + } + void Free() + { + Buf.SetCapacity(0); + } + bool SetRange(size_t from, int size) + { + for (int i = 0; i < size; i++) + { + size_t pos = (from + i) >> 3; + Byte mask = (Byte)(1 << ((from + i) & 7)); + Byte b = Buf[pos]; + if ((b & mask) != 0) + return false; + Buf[pos] = b | mask; + } + return true; + } +}; + + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _stream; + CObjectVector<CSection> _sections; + UInt32 _peOffset; + CHeader _header; + COptHeader _optHeader; + UInt32 _totalSize; + UInt32 _totalSizeLimited; + Int32 _mainSubfile; + + CRecordVector<CResItem> _items; + CObjectVector<CStringItem> _strings; + + CByteBuffer _buf; + bool _oneLang; + UString _resourceFileName; + CUsedBitmap _usedRes; + bool _parseResources; + + CRecordVector<CMixItem> _mixItems; + + HRESULT LoadDebugSections(IInStream *stream, bool &thereIsSection); + HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback); + bool Parse(const Byte *buf, UInt32 size); + + void AddResNameToString(UString &s, UInt32 id) const; + UString GetLangPrefix(UInt32 lang); + HRESULT ReadString(UInt32 offset, UString &dest) const; + HRESULT ReadTable(UInt32 offset, CRecordVector<CTableItem> &items); + bool ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size); + HRESULT OpenResources(int sectIndex, IInStream *stream, IArchiveOpenCallback *callback); + void CloseResources(); + + + bool CheckItem(const CSection §, const CResItem &item, size_t offset) const + { + return item.Offset >= sect.Va && offset <= _buf.GetCapacity() && _buf.GetCapacity() - offset >= item.Size; + } + +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +bool CHandler::Parse(const Byte *buf, UInt32 size) +{ + UInt32 i; + if (size < 512) + return false; + _peOffset = Get32(buf + 0x3C); + if (_peOffset >= 0x1000 || _peOffset + 512 > size || (_peOffset & 7) != 0) + return false; + + UInt32 pos = _peOffset; + if (!_header.Parse(buf + pos)) + return false; + if (_header.OptHeaderSize > 512 || _header.NumSections > NUM_SCAN_SECTIONS_MAX) + return false; + pos += kHeaderSize; + + if (!_optHeader.Parse(buf + pos, _header.OptHeaderSize)) + return false; + + pos += _header.OptHeaderSize; + _totalSize = pos; + + for (i = 0; i < _header.NumSections; i++, pos += kSectionSize) + { + CSection sect; + if (pos + kSectionSize > size) + return false; + sect.Parse(buf + pos); + sect.IsRealSect = true; + sect.UpdateTotalSize(_totalSize); + _sections.Add(sect); + } + + return true; +} + +enum +{ + kpidSectAlign = kpidUserDefined, + kpidFileAlign, + kpidLinkerVer, + kpidOsVer, + kpidImageVer, + kpidSubsysVer, + kpidCodeSize, + kpidImageSize, + kpidInitDataSize, + kpidUnInitDataSize, + kpidHeadersSizeUnInitDataSize, + kpidSubSystem, + kpidDllCharacts, + kpidStackReserve, + kpidStackCommit, + kpidHeapReserve, + kpidHeapCommit, + kpidImageBase + // kpidAddressOfEntryPoint, + // kpidBaseOfCode, + // kpidBaseOfData32, +}; + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidCpu, VT_BSTR}, + { NULL, kpidBit64, VT_BOOL}, + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidPhySize, VT_UI4}, + { NULL, kpidHeadersSize, VT_UI4}, + { NULL, kpidChecksum, VT_UI4}, + { L"Image Size", kpidImageSize, VT_UI4}, + { L"Section Alignment", kpidSectAlign, VT_UI4}, + { L"File Alignment", kpidFileAlign, VT_UI4}, + { L"Code Size", kpidCodeSize, VT_UI4}, + { L"Initialized Data Size", kpidInitDataSize, VT_UI4}, + { L"Uninitialized Data Size", kpidUnInitDataSize, VT_UI4}, + { L"Linker Version", kpidLinkerVer, VT_BSTR}, + { L"OS Version", kpidOsVer, VT_BSTR}, + { L"Image Version", kpidImageVer, VT_BSTR}, + { L"Subsystem Version", kpidSubsysVer, VT_BSTR}, + { L"Subsystem", kpidSubSystem, VT_BSTR}, + { L"DLL Characteristics", kpidDllCharacts, VT_BSTR}, + { L"Stack Reserve", kpidStackReserve, VT_UI8}, + { L"Stack Commit", kpidStackCommit, VT_UI8}, + { L"Heap Reserve", kpidHeapReserve, VT_UI8}, + { L"Heap Commit", kpidHeapCommit, VT_UI8}, + { L"Image Base", kpidImageBase, VT_UI8} + // { L"Address Of Entry Point", kpidAddressOfEntryPoint, VT_UI8}, + // { L"Base Of Code", kpidBaseOfCode, VT_UI8}, + // { L"Base Of Data", kpidBaseOfData32, VT_UI8}, +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidOffset, VT_UI8}, + { NULL, kpidVa, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_WITH_NAME + +static void VerToProp(const CVersion &v, NCOM::CPropVariant &prop) +{ + StringToProp(v.GetString(), prop); +} + +void TimeToProp(UInt32 unixTime, NCOM::CPropVariant &prop) +{ + if (unixTime != 0) + { + FILETIME ft; + NTime::UnixTimeToFileTime(unixTime, ft); + prop = ft; + } +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + switch(propID) + { + case kpidSectAlign: prop = _optHeader.SectAlign; break; + case kpidFileAlign: prop = _optHeader.FileAlign; break; + case kpidLinkerVer: + { + CVersion v = { _optHeader.LinkerVerMajor, _optHeader.LinkerVerMinor }; + VerToProp(v, prop); + break; + } + + case kpidOsVer: VerToProp(_optHeader.OsVer, prop); break; + case kpidImageVer: VerToProp(_optHeader.ImageVer, prop); break; + case kpidSubsysVer: VerToProp(_optHeader.SubsysVer, prop); break; + case kpidCodeSize: prop = _optHeader.CodeSize; break; + case kpidInitDataSize: prop = _optHeader.InitDataSize; break; + case kpidUnInitDataSize: prop = _optHeader.UninitDataSize; break; + case kpidImageSize: prop = _optHeader.ImageSize; break; + case kpidPhySize: prop = _totalSize; break; + case kpidHeadersSize: prop = _optHeader.HeadersSize; break; + case kpidChecksum: prop = _optHeader.CheckSum; break; + + case kpidCpu: PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop); break; + case kpidBit64: if (_optHeader.Is64Bit()) prop = true; break; + case kpidSubSystem: PAIR_TO_PROP(g_SubSystems, _optHeader.SubSystem, prop); break; + + case kpidMTime: + case kpidCTime: TimeToProp(_header.Time, prop); break; + case kpidCharacts: FLAGS_TO_PROP(g_HeaderCharacts, _header.Flags, prop); break; + case kpidDllCharacts: FLAGS_TO_PROP(g_DllCharacts, _optHeader.DllCharacts, prop); break; + case kpidStackReserve: prop = _optHeader.StackReserve; break; + case kpidStackCommit: prop = _optHeader.StackCommit; break; + case kpidHeapReserve: prop = _optHeader.HeapReserve; break; + case kpidHeapCommit: prop = _optHeader.HeapCommit; break; + + case kpidImageBase: prop = _optHeader.ImageBase; break; + // case kpidAddressOfEntryPoint: prop = _optHeader.AddressOfEntryPoint; break; + // case kpidBaseOfCode: prop = _optHeader.BaseOfCode; break; + // case kpidBaseOfData32: if (!_optHeader.Is64Bit()) prop = _optHeader.BaseOfData32; break; + + case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +void CHandler::AddResNameToString(UString &s, UInt32 id) const +{ + if ((id & kFlag) != 0) + { + UString name; + if (ReadString(id & kMask, name) == S_OK) + { + if (name.IsEmpty()) + s += L"[]"; + else + { + if (name.Length() > 1 && name[0] == '"' && name.Back() == '"') + name = name.Mid(1, name.Length() - 2); + s += name; + } + return; + } + } + wchar_t sz[32]; + ConvertUInt32ToString(id, sz); + s += sz; +} + +UString CHandler::GetLangPrefix(UInt32 lang) +{ + UString s = _resourceFileName; + s += WCHAR_PATH_SEPARATOR; + if (!_oneLang) + { + AddResNameToString(s, lang); + s += WCHAR_PATH_SEPARATOR; + } + return s; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + const CMixItem &mixItem = _mixItems[index]; + if (mixItem.StringIndex >= 0) + { + const CStringItem &item = _strings[mixItem.StringIndex]; + switch(propID) + { + case kpidPath: prop = GetLangPrefix(item.Lang) + L"string.txt"; break; + case kpidSize: + case kpidPackSize: + prop = (UInt64)item.Size; break; + } + } + else if (mixItem.ResourceIndex < 0) + { + const CSection &item = _sections[mixItem.SectionIndex]; + switch(propID) + { + case kpidPath: StringToProp(item.Name, prop); break; + case kpidSize: prop = (UInt64)item.VSize; break; + case kpidPackSize: prop = (UInt64)item.GetPackSize(); break; + case kpidOffset: prop = item.Pa; break; + case kpidVa: if (item.IsRealSect) prop = item.Va; break; + case kpidMTime: + case kpidCTime: + TimeToProp(item.IsDebug ? item.Time : _header.Time, prop); break; + case kpidCharacts: if (item.IsRealSect) FLAGS_TO_PROP(g_SectFlags, item.Flags, prop); break; + } + } + else + { + const CResItem &item = _items[mixItem.ResourceIndex]; + switch(propID) + { + case kpidPath: + { + UString s = GetLangPrefix(item.Lang); + { + const wchar_t *p = NULL; + if (item.Type < sizeof(g_ResTypes) / sizeof(g_ResTypes[0])) + p = g_ResTypes[item.Type]; + if (p != 0) + s += p; + else + AddResNameToString(s, item.Type); + } + s += WCHAR_PATH_SEPARATOR; + AddResNameToString(s, item.ID); + if (item.HeaderSize != 0) + { + if (item.IsBmp()) + s += L".bmp"; + else if (item.IsIcon()) + s += L".ico"; + } + prop = s; + break; + } + case kpidSize: prop = (UInt64)item.GetSize(); break; + case kpidPackSize: prop = (UInt64)item.Size; break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +HRESULT CHandler::LoadDebugSections(IInStream *stream, bool &thereIsSection) +{ + thereIsSection = false; + const CDirLink &debugLink = _optHeader.DirItems[kDirLink_Debug]; + if (debugLink.Size == 0) + return S_OK; + const unsigned kEntrySize = 28; + UInt32 numItems = debugLink.Size / kEntrySize; + if (numItems * kEntrySize != debugLink.Size || numItems > 16) + return S_FALSE; + + UInt64 pa = 0; + int i; + for (i = 0; i < _sections.Size(); i++) + { + const CSection § = _sections[i]; + if (sect.Va < debugLink.Va && debugLink.Va + debugLink.Size <= sect.Va + sect.PSize) + { + pa = sect.Pa + (debugLink.Va - sect.Va); + break; + } + } + if (i == _sections.Size()) + { + return S_OK; + // Exe for ARM requires S_OK + // return S_FALSE; + } + + CByteBuffer buffer; + buffer.SetCapacity(debugLink.Size); + Byte *buf = buffer; + + RINOK(stream->Seek(pa, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, buf, debugLink.Size)); + + for (i = 0; i < (int)numItems; i++) + { + CDebugEntry de; + de.Parse(buf); + + if (de.Size == 0) + continue; + + CSection sect; + sect.Name = ".debug" + GetDecString(i); + + sect.IsDebug = true; + sect.Time = de.Time; + sect.Va = de.Va; + sect.Pa = de.Pa; + sect.PSize = sect.VSize = de.Size; + UInt32 totalSize = sect.Pa + sect.PSize; + if (totalSize > _totalSize) + { + _totalSize = totalSize; + _sections.Add(sect); + thereIsSection = true; + } + buf += kEntrySize; + } + + return S_OK; +} + +HRESULT CHandler::ReadString(UInt32 offset, UString &dest) const +{ + if ((offset & 1) != 0 || offset >= _buf.GetCapacity()) + return S_FALSE; + size_t rem = _buf.GetCapacity() - offset; + if (rem < 2) + return S_FALSE; + unsigned length = Get16(_buf + offset); + if ((rem - 2) / 2 < length) + return S_FALSE; + dest.Empty(); + offset += 2; + for (unsigned i = 0; i < length; i++) + dest += (wchar_t)Get16(_buf + offset + i * 2); + return S_OK; +} + +HRESULT CHandler::ReadTable(UInt32 offset, CRecordVector<CTableItem> &items) +{ + if ((offset & 3) != 0 || offset >= _buf.GetCapacity()) + return S_FALSE; + size_t rem = _buf.GetCapacity() - offset; + if (rem < 16) + return S_FALSE; + items.Clear(); + unsigned numNameItems = Get16(_buf + offset + 12); + unsigned numIdItems = Get16(_buf + offset + 14); + unsigned numItems = numNameItems + numIdItems; + if ((rem - 16) / 8 < numItems) + return S_FALSE; + if (!_usedRes.SetRange(offset, 16 + numItems * 8)) + return S_FALSE; + offset += 16; + _oneLang = true; + unsigned i; + for (i = 0; i < numItems; i++) + { + CTableItem item; + const Byte *buf = _buf + offset; + offset += 8; + item.ID = Get32(buf + 0); + if (((item.ID & kFlag) != 0) != (i < numNameItems)) + return S_FALSE; + item.Offset = Get32(buf + 4); + items.Add(item); + } + return S_OK; +} + +static const UInt32 kFileSizeMax = (UInt32)1 << 30; +static const int kNumResItemsMax = (UInt32)1 << 23; +static const int kNumStringLangsMax = 128; + +// BITMAPINFOHEADER +struct CBitmapInfoHeader +{ + // UInt32 HeaderSize; + UInt32 XSize; + Int32 YSize; + UInt16 Planes; + UInt16 BitCount; + UInt32 Compression; + UInt32 SizeImage; + + bool Parse(const Byte *p, size_t size); +}; + +static const UInt32 kBitmapInfoHeader_Size = 0x28; + +bool CBitmapInfoHeader::Parse(const Byte *p, size_t size) +{ + if (size < kBitmapInfoHeader_Size || Get32(p) != kBitmapInfoHeader_Size) + return false; + XSize = Get32(p + 4); + YSize = (Int32)Get32(p + 8); + Planes = Get16(p + 12); + BitCount = Get16(p + 14); + Compression = Get32(p + 16); + SizeImage = Get32(p + 20); + return true; +} + +static UInt32 GetImageSize(UInt32 xSize, UInt32 ySize, UInt32 bitCount) +{ + return ((xSize * bitCount + 7) / 8 + 3) / 4 * 4 * ySize; +} + +static UInt32 SetBitmapHeader(Byte *dest, const Byte *src, UInt32 size) +{ + CBitmapInfoHeader h; + if (!h.Parse(src, size)) + return 0; + if (h.YSize < 0) + h.YSize = -h.YSize; + if (h.XSize > (1 << 26) || h.YSize > (1 << 26) || h.Planes != 1 || h.BitCount > 32 || + h.Compression != 0) // BI_RGB + return 0; + if (h.SizeImage == 0) + h.SizeImage = GetImageSize(h.XSize, h.YSize, h.BitCount); + UInt32 totalSize = kBmpHeaderSize + size; + UInt32 offBits = totalSize - h.SizeImage; + // BITMAPFILEHEADER + SetUi16(dest, 0x4D42); + SetUi32(dest + 2, totalSize); + SetUi32(dest + 6, 0); + SetUi32(dest + 10, offBits); + return kBmpHeaderSize; +} + +static UInt32 SetIconHeader(Byte *dest, const Byte *src, UInt32 size) +{ + CBitmapInfoHeader h; + if (!h.Parse(src, size)) + return 0; + if (h.YSize < 0) + h.YSize = -h.YSize; + if (h.XSize > (1 << 26) || h.YSize > (1 << 26) || h.Planes != 1 || + h.Compression != 0) // BI_RGB + return 0; + + UInt32 numBitCount = h.BitCount; + if (numBitCount != 1 && + numBitCount != 4 && + numBitCount != 8 && + numBitCount != 24 && + numBitCount != 32) + return 0; + + if ((h.YSize & 1) != 0) + return 0; + h.YSize /= 2; + if (h.XSize > 0x100 || h.YSize > 0x100) + return 0; + + UInt32 imageSize; + // imageSize is not correct if AND mask array contains zeros + // in this case it is equal image1Size + + // UInt32 imageSize = h.SizeImage; + // if (imageSize == 0) + // { + UInt32 image1Size = GetImageSize(h.XSize, h.YSize, h.BitCount); + UInt32 image2Size = GetImageSize(h.XSize, h.YSize, 1); + imageSize = image1Size + image2Size; + // } + UInt32 numColors = 0; + if (numBitCount < 16) + numColors = 1 << numBitCount; + + SetUi16(dest, 0); // Reserved + SetUi16(dest + 2, 1); // RES_ICON + SetUi16(dest + 4, 1); // ResCount + + dest[6] = (Byte)h.XSize; // Width + dest[7] = (Byte)h.YSize; // Height + dest[8] = (Byte)numColors; // ColorCount + dest[9] = 0; // Reserved + + SetUi32(dest + 10, 0); // Reserved1 / Reserved2 + + UInt32 numQuadsBytes = numColors * 4; + UInt32 BytesInRes = kBitmapInfoHeader_Size + numQuadsBytes + imageSize; + SetUi32(dest + 14, BytesInRes); + SetUi32(dest + 18, kIconHeaderSize); + + /* + Description = DWORDToString(xSize) + + kDelimiterChar + DWORDToString(ySize) + + kDelimiterChar + DWORDToString(numBitCount); + */ + return kIconHeaderSize; +} + +bool CHandler::ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size) +{ + if ((size & 1) != 0) + return false; + + int i; + for (i = 0; i < _strings.Size(); i++) + if (_strings[i].Lang == lang) + break; + if (i == _strings.Size()) + { + if (_strings.Size() >= kNumStringLangsMax) + return false; + CStringItem item; + item.Size = 0; + item.Lang = lang; + i = _strings.Add(item); + } + + CStringItem &item = _strings[i]; + id = (id - 1) << 4; + UInt32 pos = 0; + for (i = 0; i < 16; i++) + { + if (size - pos < 2) + return false; + UInt32 len = Get16(src + pos); + pos += 2; + if (len != 0) + { + if (size - pos < len * 2) + return false; + char temp[32]; + ConvertUInt32ToString(id + i, temp); + size_t tempLen = strlen(temp); + size_t j; + for (j = 0; j < tempLen; j++) + item.AddChar(temp[j]); + item.AddChar('\t'); + for (j = 0; j < len; j++, pos += 2) + item.AddWChar(Get16(src + pos)); + item.AddChar(0x0D); + item.AddChar(0x0A); + } + } + return (size == pos); +} + +HRESULT CHandler::OpenResources(int sectionIndex, IInStream *stream, IArchiveOpenCallback *callback) +{ + const CSection § = _sections[sectionIndex]; + size_t fileSize = sect.PSize; // Maybe we need sect.VSize here !!! + if (fileSize > kFileSizeMax) + return S_FALSE; + { + UInt64 fileSize64 = fileSize; + if (callback) + RINOK(callback->SetTotal(NULL, &fileSize64)); + RINOK(stream->Seek(sect.Pa, STREAM_SEEK_SET, NULL)); + _buf.SetCapacity(fileSize); + for (size_t pos = 0; pos < fileSize;) + { + UInt64 offset64 = pos; + if (callback) + RINOK(callback->SetCompleted(NULL, &offset64)) + size_t rem = MyMin(fileSize - pos, (size_t)(1 << 20)); + RINOK(ReadStream_FALSE(stream, _buf + pos, rem)); + pos += rem; + } + } + + _usedRes.Alloc(fileSize); + CRecordVector<CTableItem> specItems; + RINOK(ReadTable(0, specItems)); + + _oneLang = true; + bool stringsOk = true; + size_t maxOffset = 0; + for (int i = 0; i < specItems.Size(); i++) + { + const CTableItem &item1 = specItems[i]; + if ((item1.Offset & kFlag) == 0) + return S_FALSE; + + CRecordVector<CTableItem> specItems2; + RINOK(ReadTable(item1.Offset & kMask, specItems2)); + + for (int j = 0; j < specItems2.Size(); j++) + { + const CTableItem &item2 = specItems2[j]; + if ((item2.Offset & kFlag) == 0) + return S_FALSE; + + CRecordVector<CTableItem> specItems3; + RINOK(ReadTable(item2.Offset & kMask, specItems3)); + + CResItem item; + item.Type = item1.ID; + item.ID = item2.ID; + + for (int k = 0; k < specItems3.Size(); k++) + { + if (_items.Size() >= kNumResItemsMax) + return S_FALSE; + const CTableItem &item3 = specItems3[k]; + if ((item3.Offset & kFlag) != 0) + return S_FALSE; + if (item3.Offset >= _buf.GetCapacity() || _buf.GetCapacity() - item3.Offset < 16) + return S_FALSE; + const Byte *buf = _buf + item3.Offset; + item.Lang = item3.ID; + item.Offset = Get32(buf + 0); + item.Size = Get32(buf + 4); + // UInt32 codePage = Get32(buf + 8); + if (Get32(buf + 12) != 0) + return S_FALSE; + if (!_items.IsEmpty() && _oneLang && !item.IsNameEqual(_items.Back())) + _oneLang = false; + + item.HeaderSize = 0; + + size_t offset = item.Offset - sect.Va; + if (offset > maxOffset) + maxOffset = offset; + if (offset + item.Size > maxOffset) + maxOffset = offset + item.Size; + + if (CheckItem(sect, item, offset)) + { + const Byte *data = _buf + offset; + if (item.IsBmp()) + item.HeaderSize = SetBitmapHeader(item.Header, data, item.Size); + else if (item.IsIcon()) + item.HeaderSize = SetIconHeader(item.Header, data, item.Size); + else if (item.IsString()) + { + if (stringsOk) + stringsOk = ParseStringRes(item.ID, item.Lang, data, item.Size); + } + } + + item.Enabled = true; + _items.Add(item); + } + } + } + + if (stringsOk && !_strings.IsEmpty()) + { + int i; + for (i = 0; i < _items.Size(); i++) + { + CResItem &item = _items[i]; + if (item.IsString()) + item.Enabled = false; + } + for (i = 0; i < _strings.Size(); i++) + { + if (_strings[i].Size == 0) + continue; + CMixItem mixItem; + mixItem.ResourceIndex = -1; + mixItem.StringIndex = i; + mixItem.SectionIndex = sectionIndex; + _mixItems.Add(mixItem); + } + } + + _usedRes.Free(); + + int numBits = _optHeader.GetNumFileAlignBits(); + if (numBits >= 0) + { + UInt32 mask = (1 << numBits) - 1; + size_t end = ((maxOffset + mask) & ~mask); + if (end < sect.VSize && end <= sect.PSize) + { + CSection sect2; + sect2.Flags = 0; + + // we skip Zeros to start of aligned block + size_t i; + for (i = maxOffset; i < end; i++) + if (_buf[i] != 0) + break; + if (i == end) + maxOffset = end; + + sect2.Pa = sect.Pa + (UInt32)maxOffset; + sect2.Va = sect.Va + (UInt32)maxOffset; + sect2.PSize = sect.VSize - (UInt32)maxOffset; + sect2.VSize = sect2.PSize; + sect2.Name = ".rsrc_1"; + sect2.Time = 0; + sect2.IsAdditionalSection = true; + _sections.Add(sect2); + } + } + + return S_OK; +} + +HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) +{ + const UInt32 kBufSize = 1 << 18; + const UInt32 kSigSize = 2; + + _mainSubfile = -1; + + CByteBuffer buffer; + buffer.SetCapacity(kBufSize); + Byte *buf = buffer; + + size_t processed = kSigSize; + RINOK(ReadStream_FALSE(stream, buf, processed)); + if (buf[0] != 'M' || buf[1] != 'Z') + return S_FALSE; + processed = kBufSize - kSigSize; + RINOK(ReadStream(stream, buf + kSigSize, &processed)); + processed += kSigSize; + if (!Parse(buf, (UInt32)processed)) + return S_FALSE; + bool thereISDebug; + RINOK(LoadDebugSections(stream, thereISDebug)); + + const CDirLink &certLink = _optHeader.DirItems[kDirLink_Certificate]; + if (certLink.Size != 0) + { + CSection sect; + sect.Name = "CERTIFICATE"; + sect.Va = 0; + sect.Pa = certLink.Va; + sect.PSize = sect.VSize = certLink.Size; + sect.UpdateTotalSize(_totalSize); + _sections.Add(sect); + } + + if (thereISDebug) + { + const UInt32 kAlign = 1 << 12; + UInt32 alignPos = _totalSize & (kAlign - 1); + if (alignPos != 0) + { + UInt32 size = kAlign - alignPos; + RINOK(stream->Seek(_totalSize, STREAM_SEEK_SET, NULL)); + buffer.Free(); + buffer.SetCapacity(kAlign); + Byte *buf = buffer; + size_t processed = size; + RINOK(ReadStream(stream, buf, &processed)); + size_t i; + for (i = 0; i < processed; i++) + { + if (buf[i] != 0) + break; + } + if (processed < size && processed < 100) + _totalSize += (UInt32)processed; + else if (((_totalSize + i) & 0x1FF) == 0 || processed < size) + _totalSize += (UInt32)i; + } + } + + if (_header.NumSymbols > 0 && _header.PointerToSymbolTable >= 512) + { + if (_header.NumSymbols >= (1 << 24)) + return S_FALSE; + CSection sect; + sect.Name = "COFF_SYMBOLS"; + UInt32 size = _header.NumSymbols * 18; + RINOK(stream->Seek((UInt64)_header.PointerToSymbolTable + size, STREAM_SEEK_SET, NULL)); + Byte buf[4]; + RINOK(ReadStream_FALSE(stream, buf, 4)); + UInt32 size2 = Get32(buf); + if (size2 >= (1 << 28)) + return S_FALSE; + size += size2; + + sect.Va = 0; + sect.Pa = _header.PointerToSymbolTable; + sect.PSize = sect.VSize = size; + sect.UpdateTotalSize(_totalSize); + _sections.Add(sect); + } + + UInt64 fileSize; + RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize)); + if (fileSize > _totalSize) + return S_FALSE; + _totalSizeLimited = (_totalSize < fileSize) ? _totalSize : (UInt32)fileSize; + + { + CObjectVector<CSection> sections = _sections; + sections.Sort(); + UInt32 limit = (1 << 12); + int num = 0; + int numSections = sections.Size(); + for (int i = 0; i < numSections; i++) + { + const CSection &s = sections[i]; + if (s.Pa > limit) + { + CSection s2; + s2.Pa = s2.Va = limit; + s2.PSize = s2.VSize = s.Pa - limit; + s2.IsAdditionalSection = true; + s2.Name = '['; + s2.Name += GetDecString(num++); + s2.Name += ']'; + _sections.Add(s2); + limit = s.Pa; + } + UInt32 next = s.Pa + s.PSize; + if (next < s.Pa) + break; + if (next >= limit) + limit = next; + } + } + + _parseResources = true; + + UInt64 mainSize = 0, mainSize2 = 0; + int i; + for (i = 0; i < _sections.Size(); i++) + { + const CSection § = _sections[i]; + CMixItem mixItem; + mixItem.SectionIndex = i; + if (_parseResources && sect.Name == ".rsrc" && _items.IsEmpty()) + { + HRESULT res = OpenResources(i, stream, callback); + if (res == S_OK) + { + _resourceFileName = GetUnicodeString(sect.Name); + for (int j = 0; j < _items.Size(); j++) + { + const CResItem &item = _items[j]; + if (item.Enabled) + { + mixItem.ResourceIndex = j; + mixItem.StringIndex = -1; + if (item.IsRcDataOrUnknown()) + { + if (item.Size >= mainSize) + { + mainSize2 = mainSize; + mainSize = item.Size; + _mainSubfile = _mixItems.Size(); + } + else if (item.Size >= mainSize2) + mainSize2 = item.Size; + } + _mixItems.Add(mixItem); + } + } + if (sect.PSize > sect.VSize) + { + int numBits = _optHeader.GetNumFileAlignBits(); + if (numBits >= 0) + { + UInt32 mask = (1 << numBits) - 1; + UInt32 end = ((sect.VSize + mask) & ~mask); + + if (sect.PSize > end) + { + CSection sect2; + sect2.Flags = 0; + sect2.Pa = sect.Pa + end; + sect2.Va = sect.Va + end; + sect2.PSize = sect.PSize - end; + sect2.VSize = sect2.PSize; + sect2.Name = ".rsrc_2"; + sect2.Time = 0; + sect2.IsAdditionalSection = true; + _sections.Add(sect2); + } + } + } + continue; + } + if (res != S_FALSE) + return res; + CloseResources(); + } + mixItem.StringIndex = -1; + mixItem.ResourceIndex = -1; + if (sect.IsAdditionalSection) + { + if (sect.PSize >= mainSize) + { + mainSize2 = mainSize; + mainSize = sect.PSize; + _mainSubfile = _mixItems.Size(); + } + else + mainSize2 = sect.PSize; + } + _mixItems.Add(mixItem); + } + + if (mainSize2 >= (1 << 20) && mainSize < mainSize2 * 2) + _mainSubfile = -1; + + for (i = 0; i < _mixItems.Size(); i++) + { + const CMixItem &mixItem = _mixItems[i]; + if (mixItem.StringIndex < 0 && mixItem.ResourceIndex < 0 && _sections[mixItem.SectionIndex].Name == "_winzip_") + { + _mainSubfile = i; + break; + } + } + + return S_OK; +} + +HRESULT CalcCheckSum(ISequentialInStream *stream, UInt32 size, UInt32 excludePos, UInt32 &res) +{ + // size &= ~1; + const UInt32 kBufSize = 1 << 23; + CByteBuffer buffer; + buffer.SetCapacity(kBufSize); + Byte *buf = buffer; + + UInt32 sum = 0; + UInt32 pos = 0; + for (;;) + { + UInt32 rem = size - pos; + if (rem > kBufSize) + rem = kBufSize; + if (rem == 0) + break; + size_t processed = rem; + RINOK(ReadStream(stream, buf, &processed)); + + /* + for (; processed < rem; processed++) + buf[processed] = 0; + */ + + if ((processed & 1) != 0) + buf[processed] = 0; + + for (int j = 0; j < 4; j++) + { + UInt32 p = excludePos + j; + if (pos <= p && p < pos + processed) + buf[p - pos] = 0; + } + + for (size_t i = 0; i < processed; i += 2) + { + sum += Get16(buf + i); + sum = (sum + (sum >> 16)) & 0xFFFF; + } + pos += (UInt32)processed; + if (rem != processed) + break; + } + sum += pos; + res = sum; + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + Close(); + RINOK(Open2(inStream, callback)); + _stream = inStream; + return S_OK; + COM_TRY_END +} + +void CHandler::CloseResources() +{ + _usedRes.Free(); + _items.Clear(); + _strings.Clear(); + _buf.SetCapacity(0); +} + +STDMETHODIMP CHandler::Close() +{ + _stream.Release(); + _sections.Clear(); + _mixItems.Clear(); + CloseResources(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _mixItems.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 = _mixItems.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + { + const CMixItem &mixItem = _mixItems[allFilesMode ? i : indices[i]]; + if (mixItem.StringIndex >= 0) + totalSize += _strings[mixItem.StringIndex].Size; + else if (mixItem.ResourceIndex < 0) + totalSize += _sections[mixItem.SectionIndex].GetPackSize(); + else + totalSize += _items[mixItem.ResourceIndex].GetSize(); + } + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + UInt64 currentItemSize; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + bool checkSumOK = true; + if (_optHeader.CheckSum != 0 && (int)numItems == _mixItems.Size()) + { + UInt32 checkSum = 0; + RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); + CalcCheckSum(_stream, _totalSizeLimited, _peOffset + kHeaderSize + 64, checkSum); + checkSumOK = (checkSum == _optHeader.CheckSum); + } + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + + CMyComPtr<ISequentialOutStream> outStream; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + const CMixItem &mixItem = _mixItems[index]; + + const CSection § = _sections[mixItem.SectionIndex]; + bool isOk = true; + if (mixItem.StringIndex >= 0) + { + const CStringItem &item = _strings[mixItem.StringIndex]; + currentItemSize = item.Size; + if (!testMode && !outStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + if (outStream) + RINOK(WriteStream(outStream, item.Buf, item.Size)); + } + else if (mixItem.ResourceIndex < 0) + { + currentItemSize = sect.GetPackSize(); + if (!testMode && !outStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(_stream->Seek(sect.Pa, STREAM_SEEK_SET, NULL)); + streamSpec->Init(currentItemSize); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + isOk = (copyCoderSpec->TotalSize == currentItemSize); + } + else + { + const CResItem &item = _items[mixItem.ResourceIndex]; + currentItemSize = item.GetSize(); + if (!testMode && !outStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + size_t offset = item.Offset - sect.Va; + if (!CheckItem(sect, item, offset)) + isOk = false; + else if (outStream) + { + if (item.HeaderSize != 0) + RINOK(WriteStream(outStream, item.Header, item.HeaderSize)); + RINOK(WriteStream(outStream, _buf + offset, item.Size)); + } + } + + outStream.Release(); + RINOK(extractCallback->SetOperationResult(isOk ? + checkSumOK ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kCRCError: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + *stream = 0; + + const CMixItem &mixItem = _mixItems[index]; + const CSection § = _sections[mixItem.SectionIndex]; + if (mixItem.IsSectionItem()) + return CreateLimitedInStream(_stream, sect.Pa, sect.PSize, stream); + + CBufInStream *inStreamSpec = new CBufInStream; + CMyComPtr<ISequentialInStream> streamTemp = inStreamSpec; + CReferenceBuf *referenceBuf = new CReferenceBuf; + CMyComPtr<IUnknown> ref = referenceBuf; + if (mixItem.StringIndex >= 0) + { + const CStringItem &item = _strings[mixItem.StringIndex]; + referenceBuf->Buf.SetCapacity(item.Size); + memcpy(referenceBuf->Buf, item.Buf, item.Size); + } + else + { + const CResItem &item = _items[mixItem.ResourceIndex]; + size_t offset = item.Offset - sect.Va; + if (!CheckItem(sect, item, offset)) + return S_FALSE; + if (item.HeaderSize == 0) + { + CBufInStream *streamSpec = new CBufInStream; + CMyComPtr<IInStream> streamTemp2 = streamSpec; + streamSpec->Init(_buf + offset, item.Size, (IInArchive *)this); + *stream = streamTemp2.Detach(); + return S_OK; + } + referenceBuf->Buf.SetCapacity(item.HeaderSize + item.Size); + memcpy(referenceBuf->Buf, item.Header, item.HeaderSize); + memcpy(referenceBuf->Buf + item.HeaderSize, _buf + offset, item.Size); + } + inStreamSpec->Init(referenceBuf); + + *stream = streamTemp.Detach(); + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"PE", L"exe dll sys", 0, 0xDD, { 'P', 'E', 0, 0 }, 4, false, CreateArc, 0 }; + +REGISTER_ARC(Pe) + +}} |