3063 lines
72 KiB
C++
3063 lines
72 KiB
C++
|
// PeHandler.cpp
|
||
|
|
||
|
#include "StdAfx.h"
|
||
|
|
||
|
// #include <stdio.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/TimeUtils.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)
|
||
|
|
||
|
#define G16(offs, v) v = Get16(p + (offs))
|
||
|
#define G32(offs, v) v = Get32(p + (offs))
|
||
|
#define G64(offs, v) v = Get64(p + (offs))
|
||
|
|
||
|
#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
|
||
|
|
||
|
using namespace NWindows;
|
||
|
|
||
|
namespace NArchive {
|
||
|
namespace NPe {
|
||
|
|
||
|
static const UInt32 k_Signature32 = 0x00004550;
|
||
|
|
||
|
static HRESULT CalcCheckSum(ISequentialInStream *stream, UInt32 size, UInt32 excludePos, UInt32 &res)
|
||
|
{
|
||
|
const UInt32 kBufSizeMax = (UInt32)1 << 16;
|
||
|
UInt32 bufSize = MyMin(kBufSizeMax, size);
|
||
|
bufSize += (bufSize & 1);
|
||
|
CByteBuffer buffer(bufSize);
|
||
|
Byte *buf = buffer;
|
||
|
UInt32 sum = 0;
|
||
|
UInt32 pos = 0;
|
||
|
for (;;)
|
||
|
{
|
||
|
UInt32 rem = size - pos;
|
||
|
if (rem > bufSize)
|
||
|
rem = bufSize;
|
||
|
if (rem == 0)
|
||
|
break;
|
||
|
size_t processed = rem;
|
||
|
RINOK(ReadStream(stream, buf, &processed));
|
||
|
|
||
|
if ((processed & 1) != 0)
|
||
|
buf[processed] = 0;
|
||
|
|
||
|
for (unsigned j = 0; j < 4; j++)
|
||
|
{
|
||
|
UInt32 e = excludePos + j;
|
||
|
if (pos <= e)
|
||
|
{
|
||
|
e -= pos;
|
||
|
if (e < processed)
|
||
|
buf[e] = 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;
|
||
|
}
|
||
|
res = sum + pos;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static AString GetDecString(UInt32 v)
|
||
|
{
|
||
|
char sz[16];
|
||
|
ConvertUInt32ToString(v, sz);
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
struct CVersion
|
||
|
{
|
||
|
UInt16 Major;
|
||
|
UInt16 Minor;
|
||
|
|
||
|
void Parse(const Byte *p)
|
||
|
{
|
||
|
G16(0, Major);
|
||
|
G16(2, Minor);
|
||
|
}
|
||
|
void ToProp(NCOM::CPropVariant &prop);
|
||
|
};
|
||
|
|
||
|
void CVersion::ToProp(NCOM::CPropVariant &prop)
|
||
|
{
|
||
|
char sz[32];
|
||
|
ConvertUInt32ToString(Major, sz);
|
||
|
unsigned len = MyStringLen(sz);
|
||
|
sz[len] = '.';
|
||
|
ConvertUInt32ToString(Minor, sz + len + 1);
|
||
|
prop = sz;
|
||
|
}
|
||
|
|
||
|
static const unsigned kHeaderSize = 4 + 20;
|
||
|
static const unsigned k_OptHeader32_Size_MIN = 96;
|
||
|
static const unsigned k_OptHeader64_Size_MIN = 112;
|
||
|
|
||
|
static const UInt32 PE_IMAGE_FILE_DLL = (1 << 13);
|
||
|
|
||
|
struct CHeader
|
||
|
{
|
||
|
UInt16 Machine;
|
||
|
UInt16 NumSections;
|
||
|
UInt32 Time;
|
||
|
UInt32 PointerToSymbolTable;
|
||
|
UInt32 NumSymbols;
|
||
|
UInt16 OptHeaderSize;
|
||
|
UInt16 Flags;
|
||
|
|
||
|
bool Parse(const Byte *p);
|
||
|
bool IsDll() const { return (Flags & PE_IMAGE_FILE_DLL) != 0; }
|
||
|
};
|
||
|
|
||
|
bool CHeader::Parse(const Byte *p)
|
||
|
{
|
||
|
if (Get32(p) != k_Signature32)
|
||
|
return false;
|
||
|
p += 4;
|
||
|
G16( 0, Machine);
|
||
|
G16( 2, NumSections);
|
||
|
G32( 4, Time);
|
||
|
G32( 8, PointerToSymbolTable);
|
||
|
G32(12, NumSymbols);
|
||
|
G16(16, OptHeaderSize);
|
||
|
G16(18, Flags);
|
||
|
return OptHeaderSize >= k_OptHeader32_Size_MIN;
|
||
|
}
|
||
|
|
||
|
struct CDirLink
|
||
|
{
|
||
|
UInt32 Va;
|
||
|
UInt32 Size;
|
||
|
|
||
|
CDirLink(): Va(0), Size(0) {}
|
||
|
void Parse(const Byte *p)
|
||
|
{
|
||
|
G32(0, Va);
|
||
|
G32(4, Size);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
kDirLink_Certificate = 4,
|
||
|
kDirLink_Debug = 6
|
||
|
};
|
||
|
|
||
|
static const UInt32 kNumDirItemsMax = 16;
|
||
|
|
||
|
struct CDebugEntry
|
||
|
{
|
||
|
UInt32 Flags;
|
||
|
UInt32 Time;
|
||
|
CVersion Ver;
|
||
|
UInt32 Type;
|
||
|
UInt32 Size;
|
||
|
UInt32 Va;
|
||
|
UInt32 Pa;
|
||
|
|
||
|
void Parse(const Byte *p)
|
||
|
{
|
||
|
G32(0, Flags);
|
||
|
G32(4, Time);
|
||
|
Ver.Parse(p + 8);
|
||
|
G32(12, Type);
|
||
|
G32(16, Size);
|
||
|
G32(20, Va);
|
||
|
G32(24, Pa);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static const UInt32 k_CheckSum_Field_Offset = 64;
|
||
|
|
||
|
static const UInt32 PE_OptHeader_Magic_32 = 0x10B;
|
||
|
static const UInt32 PE_OptHeader_Magic_64 = 0x20B;
|
||
|
|
||
|
static const UInt32 k_SubSystems_EFI_First = 10;
|
||
|
static const UInt32 k_SubSystems_EFI_Last = 13;
|
||
|
|
||
|
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 (unsigned i = 0; i <= 31; i++)
|
||
|
if (((UInt32)1 << i) == FileAlign)
|
||
|
return i;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
bool IsSybSystem_EFI() const
|
||
|
{
|
||
|
return
|
||
|
SubSystem >= k_SubSystems_EFI_First &&
|
||
|
SubSystem <= k_SubSystems_EFI_Last;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
bool COptHeader::Parse(const Byte *p, UInt32 size)
|
||
|
{
|
||
|
if (size < k_OptHeader32_Size_MIN)
|
||
|
return false;
|
||
|
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];
|
||
|
|
||
|
G32( 4, CodeSize);
|
||
|
G32( 8, InitDataSize);
|
||
|
G32(12, UninitDataSize);
|
||
|
// G32(16, AddressOfEntryPoint);
|
||
|
// G32(20, BaseOfCode);
|
||
|
|
||
|
G32(32, SectAlign);
|
||
|
G32(36, FileAlign);
|
||
|
|
||
|
OsVer.Parse(p + 40);
|
||
|
ImageVer.Parse(p + 44);
|
||
|
SubsysVer.Parse(p + 48);
|
||
|
|
||
|
// reserved = Get32(p + 52);
|
||
|
|
||
|
G32(56, ImageSize);
|
||
|
G32(60, HeadersSize);
|
||
|
G32(64, CheckSum);
|
||
|
G16(68, SubSystem);
|
||
|
G16(70, DllCharacts);
|
||
|
|
||
|
UInt32 pos;
|
||
|
if (Is64Bit())
|
||
|
{
|
||
|
if (size < k_OptHeader64_Size_MIN)
|
||
|
return false;
|
||
|
// BaseOfData32 = 0;
|
||
|
G64(24, ImageBase);
|
||
|
G64(72, StackReserve);
|
||
|
G64(80, StackCommit);
|
||
|
G64(88, HeapReserve);
|
||
|
G64(96, HeapCommit);
|
||
|
pos = 108;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// G32(24, BaseOfData32);
|
||
|
G32(28, ImageBase);
|
||
|
G32(72, StackReserve);
|
||
|
G32(76, StackCommit);
|
||
|
G32(80, HeapReserve);
|
||
|
G32(84, HeapCommit);
|
||
|
pos = 92;
|
||
|
}
|
||
|
|
||
|
G32(pos, NumDirItems);
|
||
|
if (NumDirItems > (1 << 16))
|
||
|
return false;
|
||
|
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 IsRealSect;
|
||
|
bool IsDebug;
|
||
|
bool IsAdditionalSection;
|
||
|
|
||
|
CSection(): IsRealSect(false), IsDebug(false), IsAdditionalSection(false) {}
|
||
|
|
||
|
const UInt32 GetSizeExtract() const { return PSize; }
|
||
|
const UInt32 GetSizeMin() const { return MyMin(PSize, VSize); }
|
||
|
|
||
|
void UpdateTotalSize(UInt32 &totalSize) const
|
||
|
{
|
||
|
UInt32 t = Pa + PSize;
|
||
|
if (totalSize < t)
|
||
|
totalSize = t;
|
||
|
}
|
||
|
|
||
|
void Parse(const Byte *p);
|
||
|
|
||
|
int Compare(const CSection &s) const
|
||
|
{
|
||
|
RINOZ(MyCompare(Pa, s.Pa));
|
||
|
UInt32 size1 = GetSizeExtract();
|
||
|
UInt32 size2 = s.GetSizeExtract();
|
||
|
return MyCompare(size1, size2);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static const unsigned kNameSize = 8;
|
||
|
|
||
|
static void GetName(const Byte *name, AString &res)
|
||
|
{
|
||
|
res.SetFrom_CalcLen((const char *)name, kNameSize);
|
||
|
}
|
||
|
|
||
|
void CSection::Parse(const Byte *p)
|
||
|
{
|
||
|
GetName(p, Name);
|
||
|
G32( 8, VSize);
|
||
|
G32(12, Va);
|
||
|
G32(16, PSize);
|
||
|
G32(20, Pa);
|
||
|
// G16(32, NumRelocs);
|
||
|
G32(36, Flags);
|
||
|
}
|
||
|
|
||
|
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" },
|
||
|
{ 0x014D, "I860" },
|
||
|
{ 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" },
|
||
|
{ 0x01C4, "ARM-NT" },
|
||
|
{ 0x01D3, "AM33" },
|
||
|
{ 0x01F0, "PPC" },
|
||
|
{ 0x01F1, "PPC-FP" },
|
||
|
{ 0x0200, "IA-64" },
|
||
|
{ 0x0266, "MIPS-16" },
|
||
|
{ 0x0284, "Alpha-64" },
|
||
|
{ 0x0366, "MIPS-FPU" },
|
||
|
{ 0x0466, "MIPS-FPU16" },
|
||
|
{ 0x0520, "TriCore" },
|
||
|
{ 0x0CEF, "CEF" },
|
||
|
{ 0x0EBC, "EFI" },
|
||
|
{ 0x8664, "x64" },
|
||
|
{ 0x9041, "M32R" },
|
||
|
{ 0xC0EE, "CEE" }
|
||
|
};
|
||
|
|
||
|
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 char * const g_ResTypes[] =
|
||
|
{
|
||
|
NULL
|
||
|
, "CURSOR"
|
||
|
, "BITMAP"
|
||
|
, "ICON"
|
||
|
, "MENU"
|
||
|
, "DIALOG"
|
||
|
, "STRING"
|
||
|
, "FONTDIR"
|
||
|
, "FONT"
|
||
|
, "ACCELERATOR"
|
||
|
, "RCDATA"
|
||
|
, "MESSAGETABLE"
|
||
|
, "GROUP_CURSOR"
|
||
|
, NULL
|
||
|
, "GROUP_ICON"
|
||
|
, NULL
|
||
|
, "VERSION"
|
||
|
, "DLGINCLUDE"
|
||
|
, NULL
|
||
|
, "PLUGPLAY"
|
||
|
, "VXD"
|
||
|
, "ANICURSOR"
|
||
|
, "ANIICON"
|
||
|
, "HTML"
|
||
|
, "MANIFEST"
|
||
|
};
|
||
|
|
||
|
static const UInt32 kFlag = (UInt32)1 << 31;
|
||
|
static const UInt32 kMask = ~kFlag;
|
||
|
|
||
|
struct CTableItem
|
||
|
{
|
||
|
UInt32 Offset;
|
||
|
UInt32 ID;
|
||
|
};
|
||
|
|
||
|
|
||
|
static const UInt32 kBmpHeaderSize = 14;
|
||
|
static 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 IsVersion() const { return Type == 16; }
|
||
|
bool IsRcDataOrUnknown() const { return IsRcData() || Type > 64; }
|
||
|
};
|
||
|
|
||
|
struct CTextFile
|
||
|
{
|
||
|
CByteDynamicBuffer Buf;
|
||
|
|
||
|
size_t FinalSize() const { return Buf.GetPos(); }
|
||
|
|
||
|
void AddChar(Byte c);
|
||
|
void AddWChar(UInt16 c);
|
||
|
void AddWChar_Smart(UInt16 c);
|
||
|
void NewLine();
|
||
|
void AddString(const char *s);
|
||
|
void AddSpaces(int num);
|
||
|
void AddBytes(const Byte *p, size_t size)
|
||
|
{
|
||
|
Buf.AddData(p, size);
|
||
|
}
|
||
|
|
||
|
void OpenBlock(int num)
|
||
|
{
|
||
|
AddSpaces(num);
|
||
|
AddChar('{');
|
||
|
NewLine();
|
||
|
}
|
||
|
void CloseBlock(int num)
|
||
|
{
|
||
|
AddSpaces(num);
|
||
|
AddChar('}');
|
||
|
NewLine();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void CTextFile::AddChar(Byte c)
|
||
|
{
|
||
|
Byte *p = Buf.GetCurPtrAndGrow(2);
|
||
|
p[0] = c;
|
||
|
p[1] = 0;
|
||
|
}
|
||
|
|
||
|
void CTextFile::AddWChar(UInt16 c)
|
||
|
{
|
||
|
Byte *p = Buf.GetCurPtrAndGrow(2);
|
||
|
SetUi16(p, c);
|
||
|
}
|
||
|
|
||
|
void CTextFile::AddWChar_Smart(UInt16 c)
|
||
|
{
|
||
|
if (c == '\n')
|
||
|
{
|
||
|
AddChar('\\');
|
||
|
c = 'n';
|
||
|
}
|
||
|
AddWChar(c);
|
||
|
}
|
||
|
|
||
|
void CTextFile::NewLine()
|
||
|
{
|
||
|
AddChar(0x0D);
|
||
|
AddChar(0x0A);
|
||
|
}
|
||
|
|
||
|
void CTextFile::AddString(const char *s)
|
||
|
{
|
||
|
for (;; s++)
|
||
|
{
|
||
|
char c = *s;
|
||
|
if (c == 0)
|
||
|
return;
|
||
|
AddChar(c);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CTextFile::AddSpaces(int num)
|
||
|
{
|
||
|
for (int i = 0; i < num; i++)
|
||
|
AddChar(' ');
|
||
|
}
|
||
|
|
||
|
struct CStringItem: public CTextFile
|
||
|
{
|
||
|
UInt32 Lang;
|
||
|
};
|
||
|
|
||
|
struct CByteBuffer_WithLang: public CByteBuffer
|
||
|
{
|
||
|
UInt32 Lang;
|
||
|
};
|
||
|
|
||
|
|
||
|
struct CMixItem
|
||
|
{
|
||
|
int SectionIndex;
|
||
|
int ResourceIndex;
|
||
|
int StringIndex;
|
||
|
int VersionIndex;
|
||
|
|
||
|
CMixItem(): SectionIndex(-1), ResourceIndex(-1), StringIndex(-1), VersionIndex(-1) {}
|
||
|
bool IsSectionItem() const { return ResourceIndex < 0 && StringIndex < 0 && VersionIndex < 0; }
|
||
|
};
|
||
|
|
||
|
struct CUsedBitmap
|
||
|
{
|
||
|
CByteBuffer Buf;
|
||
|
public:
|
||
|
void Alloc(size_t size)
|
||
|
{
|
||
|
size = (size + 7) / 8;
|
||
|
Buf.Alloc(size);
|
||
|
memset(Buf, 0, size);
|
||
|
}
|
||
|
|
||
|
void Free()
|
||
|
{
|
||
|
Buf.Free();
|
||
|
}
|
||
|
|
||
|
bool SetRange(size_t from, unsigned size)
|
||
|
{
|
||
|
for (unsigned 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] = (Byte)(b | mask);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct CStringKeyValue
|
||
|
{
|
||
|
UString Key;
|
||
|
UString Value;
|
||
|
};
|
||
|
|
||
|
class CHandler:
|
||
|
public IInArchive,
|
||
|
public IInArchiveGetStream,
|
||
|
public IArchiveAllowTail,
|
||
|
public CMyUnknownImp
|
||
|
{
|
||
|
CMyComPtr<IInStream> _stream;
|
||
|
CObjectVector<CSection> _sections;
|
||
|
UInt32 _peOffset;
|
||
|
CHeader _header;
|
||
|
UInt32 _totalSize;
|
||
|
Int32 _mainSubfile;
|
||
|
|
||
|
CRecordVector<CMixItem> _mixItems;
|
||
|
CRecordVector<CResItem> _items;
|
||
|
CObjectVector<CStringItem> _strings;
|
||
|
CObjectVector<CByteBuffer_WithLang> _versionFiles;
|
||
|
UString _versionFullString;
|
||
|
UString _versionShortString;
|
||
|
UString _originalFilename;
|
||
|
CObjectVector<CStringKeyValue> _versionKeys;
|
||
|
|
||
|
CByteBuffer _buf;
|
||
|
bool _oneLang;
|
||
|
UString _resourcesPrefix;
|
||
|
CUsedBitmap _usedRes;
|
||
|
bool _parseResources;
|
||
|
bool _checksumError;
|
||
|
|
||
|
COptHeader _optHeader;
|
||
|
|
||
|
bool _allowTail;
|
||
|
|
||
|
HRESULT LoadDebugSections(IInStream *stream, bool &thereIsSection);
|
||
|
HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
|
||
|
|
||
|
void AddResNameToString(UString &s, UInt32 id) const;
|
||
|
void AddLangPrefix(UString &s, UInt32 lang) const;
|
||
|
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(unsigned 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.Size() && _buf.Size() - offset >= item.Size;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
CHandler(): _allowTail(false) {}
|
||
|
|
||
|
MY_UNKNOWN_IMP3(IInArchive, IInArchiveGetStream, IArchiveAllowTail)
|
||
|
INTERFACE_IInArchive(;)
|
||
|
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
|
||
|
STDMETHOD(AllowTail)(Int32 allowTail);
|
||
|
};
|
||
|
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
kpidSectAlign = kpidUserDefined,
|
||
|
kpidFileAlign,
|
||
|
kpidLinkerVer,
|
||
|
kpidOsVer,
|
||
|
kpidImageVer,
|
||
|
kpidSubsysVer,
|
||
|
kpidCodeSize,
|
||
|
kpidImageSize,
|
||
|
kpidInitDataSize,
|
||
|
kpidUnInitDataSize,
|
||
|
kpidHeadersSizeUnInitDataSize,
|
||
|
kpidSubSystem,
|
||
|
kpidDllCharacts,
|
||
|
kpidStackReserve,
|
||
|
kpidStackCommit,
|
||
|
kpidHeapReserve,
|
||
|
kpidHeapCommit,
|
||
|
kpidImageBase
|
||
|
// kpidAddressOfEntryPoint,
|
||
|
// kpidBaseOfCode,
|
||
|
// kpidBaseOfData32,
|
||
|
};
|
||
|
|
||
|
static const CStatProp kArcProps[] =
|
||
|
{
|
||
|
// { NULL, kpidWarning, VT_BSTR},
|
||
|
{ NULL, kpidCpu, VT_BSTR},
|
||
|
{ NULL, kpidBit64, VT_BOOL},
|
||
|
{ NULL, kpidCharacts, VT_BSTR},
|
||
|
{ NULL, kpidCTime, VT_FILETIME},
|
||
|
{ NULL, kpidHeadersSize, VT_UI4},
|
||
|
{ NULL, kpidChecksum, VT_UI4},
|
||
|
{ NULL, kpidName, VT_BSTR},
|
||
|
|
||
|
{ "Image Size", kpidImageSize, VT_UI4},
|
||
|
{ "Section Alignment", kpidSectAlign, VT_UI4},
|
||
|
{ "File Alignment", kpidFileAlign, VT_UI4},
|
||
|
{ "Code Size", kpidCodeSize, VT_UI4},
|
||
|
{ "Initialized Data Size", kpidInitDataSize, VT_UI4},
|
||
|
{ "Uninitialized Data Size", kpidUnInitDataSize, VT_UI4},
|
||
|
{ "Linker Version", kpidLinkerVer, VT_BSTR},
|
||
|
{ "OS Version", kpidOsVer, VT_BSTR},
|
||
|
{ "Image Version", kpidImageVer, VT_BSTR},
|
||
|
{ "Subsystem Version", kpidSubsysVer, VT_BSTR},
|
||
|
{ "Subsystem", kpidSubSystem, VT_BSTR},
|
||
|
{ "DLL Characteristics", kpidDllCharacts, VT_BSTR},
|
||
|
{ "Stack Reserve", kpidStackReserve, VT_UI8},
|
||
|
{ "Stack Commit", kpidStackCommit, VT_UI8},
|
||
|
{ "Heap Reserve", kpidHeapReserve, VT_UI8},
|
||
|
{ "Heap Commit", kpidHeapCommit, VT_UI8},
|
||
|
{ "Image Base", kpidImageBase, VT_UI8},
|
||
|
{ NULL, kpidComment, VT_BSTR},
|
||
|
|
||
|
// { "Address Of Entry Point", kpidAddressOfEntryPoint, VT_UI8},
|
||
|
// { "Base Of Code", kpidBaseOfCode, VT_UI8},
|
||
|
// { "Base Of Data", kpidBaseOfData32, VT_UI8},
|
||
|
};
|
||
|
|
||
|
static const Byte kProps[] =
|
||
|
{
|
||
|
kpidPath,
|
||
|
kpidSize,
|
||
|
kpidPackSize,
|
||
|
kpidVirtualSize,
|
||
|
kpidCharacts,
|
||
|
kpidOffset,
|
||
|
kpidVa,
|
||
|
};
|
||
|
|
||
|
IMP_IInArchive_Props
|
||
|
IMP_IInArchive_ArcProps_WITH_NAME
|
||
|
|
||
|
static 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 };
|
||
|
v.ToProp(prop);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case kpidOsVer: _optHeader.OsVer.ToProp(prop); break;
|
||
|
case kpidImageVer: _optHeader.ImageVer.ToProp(prop); break;
|
||
|
case kpidSubsysVer: _optHeader.SubsysVer.ToProp(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 kpidComment: if (!_versionFullString.IsEmpty()) prop = _versionFullString; break;
|
||
|
case kpidShortComment:
|
||
|
if (!_versionShortString.IsEmpty())
|
||
|
prop = _versionShortString;
|
||
|
else
|
||
|
{
|
||
|
PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case kpidName: if (!_originalFilename.IsEmpty()) prop = _originalFilename; break;
|
||
|
case kpidExtension:
|
||
|
if (_header.IsDll())
|
||
|
prop = _optHeader.IsSybSystem_EFI() ? "efi" : "dll";
|
||
|
break;
|
||
|
|
||
|
// case kpidIsSelfExe: prop = !_header.IsDll(); break;
|
||
|
|
||
|
// case kpidError:
|
||
|
case kpidWarning: if (_checksumError) prop = "Checksum error"; 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
|
||
|
}
|
||
|
|
||
|
HRESULT CHandler::ReadString(UInt32 offset, UString &dest) const
|
||
|
{
|
||
|
if ((offset & 1) != 0 || offset >= _buf.Size())
|
||
|
return S_FALSE;
|
||
|
size_t rem = _buf.Size() - offset;
|
||
|
if (rem < 2)
|
||
|
return S_FALSE;
|
||
|
unsigned len = Get16(_buf + offset);
|
||
|
if ((rem - 2) / 2 < len)
|
||
|
return S_FALSE;
|
||
|
dest.Empty();
|
||
|
wchar_t *destBuf = dest.GetBuf(len);
|
||
|
offset += 2;
|
||
|
const Byte *src = _buf + offset;
|
||
|
unsigned i;
|
||
|
for (i = 0; i < len; i++)
|
||
|
{
|
||
|
wchar_t c = (wchar_t)Get16(src + i * 2);
|
||
|
if (c == 0)
|
||
|
break;
|
||
|
destBuf[i] = c;
|
||
|
}
|
||
|
destBuf[i] = 0;
|
||
|
dest.ReleaseBuf_SetLen(i);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
void CHandler::AddResNameToString(UString &s, UInt32 id) const
|
||
|
{
|
||
|
if ((id & kFlag) != 0)
|
||
|
{
|
||
|
UString name;
|
||
|
if (ReadString(id & kMask, name) == S_OK)
|
||
|
{
|
||
|
const wchar_t *str = L"[]";
|
||
|
if (name.Len() > 1 && name[0] == '"' && name.Back() == '"')
|
||
|
{
|
||
|
if (name.Len() != 2)
|
||
|
{
|
||
|
name.DeleteBack();
|
||
|
str = name.Ptr(1);
|
||
|
}
|
||
|
}
|
||
|
else if (!name.IsEmpty())
|
||
|
str = name;
|
||
|
s += str;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
wchar_t sz[16];
|
||
|
ConvertUInt32ToString(id, sz);
|
||
|
s += sz;
|
||
|
}
|
||
|
|
||
|
void CHandler::AddLangPrefix(UString &s, UInt32 lang) const
|
||
|
{
|
||
|
if (!_oneLang)
|
||
|
{
|
||
|
AddResNameToString(s, lang);
|
||
|
s.Add_PathSepar();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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:
|
||
|
{
|
||
|
UString s = _resourcesPrefix;
|
||
|
AddLangPrefix(s, item.Lang);
|
||
|
s.AddAscii("string.txt");
|
||
|
prop = s;
|
||
|
break;
|
||
|
}
|
||
|
case kpidSize:
|
||
|
case kpidPackSize:
|
||
|
prop = (UInt64)item.FinalSize(); break;
|
||
|
}
|
||
|
}
|
||
|
else if (mixItem.VersionIndex >= 0)
|
||
|
{
|
||
|
const CByteBuffer_WithLang &item = _versionFiles[mixItem.VersionIndex];
|
||
|
switch (propID)
|
||
|
{
|
||
|
case kpidPath:
|
||
|
{
|
||
|
UString s = _resourcesPrefix;
|
||
|
AddLangPrefix(s, item.Lang);
|
||
|
s.AddAscii("version.txt");
|
||
|
prop = s;
|
||
|
break;
|
||
|
}
|
||
|
case kpidSize:
|
||
|
case kpidPackSize:
|
||
|
prop = (UInt64)item.Size(); break;
|
||
|
}
|
||
|
}
|
||
|
else if (mixItem.ResourceIndex >= 0)
|
||
|
{
|
||
|
const CResItem &item = _items[mixItem.ResourceIndex];
|
||
|
switch (propID)
|
||
|
{
|
||
|
case kpidPath:
|
||
|
{
|
||
|
UString s = _resourcesPrefix;
|
||
|
AddLangPrefix(s, item.Lang);
|
||
|
{
|
||
|
const char *p = NULL;
|
||
|
if (item.Type < ARRAY_SIZE(g_ResTypes))
|
||
|
p = g_ResTypes[item.Type];
|
||
|
if (p)
|
||
|
s.AddAscii(p);
|
||
|
else
|
||
|
AddResNameToString(s, item.Type);
|
||
|
}
|
||
|
s.Add_PathSepar();
|
||
|
AddResNameToString(s, item.ID);
|
||
|
if (item.HeaderSize != 0)
|
||
|
{
|
||
|
if (item.IsBmp())
|
||
|
s.AddAscii(".bmp");
|
||
|
else if (item.IsIcon())
|
||
|
s.AddAscii(".ico");
|
||
|
}
|
||
|
prop = s;
|
||
|
break;
|
||
|
}
|
||
|
case kpidSize: prop = (UInt64)item.GetSize(); break;
|
||
|
case kpidPackSize: prop = (UInt64)item.Size; break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
const CSection &item = _sections[mixItem.SectionIndex];
|
||
|
switch (propID)
|
||
|
{
|
||
|
case kpidPath: prop = MultiByteToUnicodeString(item.Name); break;
|
||
|
case kpidSize: prop = (UInt64)item.PSize; break;
|
||
|
case kpidPackSize: prop = (UInt64)item.PSize; break;
|
||
|
case kpidVirtualSize: prop = (UInt64)item.VSize; 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;
|
||
|
case kpidZerosTailIsAllowed: if (!item.IsRealSect) prop = true; 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;
|
||
|
unsigned 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())
|
||
|
{
|
||
|
// Exe for ARM requires S_OK
|
||
|
// return S_FALSE;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
CByteBuffer buffer(debugLink.Size);
|
||
|
Byte *buf = buffer;
|
||
|
|
||
|
RINOK(stream->Seek(pa, STREAM_SEEK_SET, NULL));
|
||
|
RINOK(ReadStream_FALSE(stream, buf, debugLink.Size));
|
||
|
|
||
|
for (i = 0; i < numItems; i++)
|
||
|
{
|
||
|
CDebugEntry de;
|
||
|
de.Parse(buf);
|
||
|
|
||
|
if (de.Size == 0)
|
||
|
continue;
|
||
|
|
||
|
UInt32 totalSize = de.Pa + de.Size;
|
||
|
if (totalSize > _totalSize)
|
||
|
{
|
||
|
_totalSize = totalSize;
|
||
|
thereIsSection = true;
|
||
|
|
||
|
CSection § = _sections.AddNew();
|
||
|
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;
|
||
|
}
|
||
|
buf += kEntrySize;
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CHandler::ReadTable(UInt32 offset, CRecordVector<CTableItem> &items)
|
||
|
{
|
||
|
if ((offset & 3) != 0 || offset >= _buf.Size())
|
||
|
return S_FALSE;
|
||
|
size_t rem = _buf.Size() - offset;
|
||
|
if (rem < 16)
|
||
|
return S_FALSE;
|
||
|
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;
|
||
|
items.ClearAndReserve(numItems);
|
||
|
for (unsigned i = 0; i < numItems; i++, offset += 8)
|
||
|
{
|
||
|
const Byte *buf = _buf + offset;
|
||
|
CTableItem item;
|
||
|
item.ID = Get32(buf + 0);
|
||
|
if ((bool)((item.ID & kFlag) != 0) != (bool)(i < numNameItems))
|
||
|
return S_FALSE;
|
||
|
item.Offset = Get32(buf + 4);
|
||
|
items.AddInReserved(item);
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static const UInt32 kFileSizeMax = (UInt32)1 << 31;
|
||
|
static const unsigned kNumResItemsMax = (unsigned)1 << 23;
|
||
|
static const unsigned kNumStringLangsMax = 256;
|
||
|
|
||
|
// 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;
|
||
|
G32( 4, XSize);
|
||
|
G32( 8, YSize);
|
||
|
G16(12, Planes);
|
||
|
G16(14, BitCount);
|
||
|
G32(16, Compression);
|
||
|
G32(20, SizeImage);
|
||
|
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)
|
||
|
return 0;
|
||
|
if (h.SizeImage == 0)
|
||
|
{
|
||
|
if (h.Compression != 0) // BI_RGB
|
||
|
return 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;
|
||
|
|
||
|
unsigned 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 = _strings.AddNew();
|
||
|
item.Lang = lang;
|
||
|
}
|
||
|
|
||
|
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_Smart(Get16(src + pos));
|
||
|
item.NewLine();
|
||
|
}
|
||
|
}
|
||
|
if (size == pos)
|
||
|
return true;
|
||
|
|
||
|
// Some rare case files have additional ZERO.
|
||
|
if (size == pos + 2 && Get16(src + pos) == 0)
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
// ---------- VERSION ----------
|
||
|
|
||
|
static const UInt32 kMy_VS_FFI_SIGNATURE = 0xFEEF04BD;
|
||
|
|
||
|
struct CMy_VS_FIXEDFILEINFO
|
||
|
{
|
||
|
// UInt32 Signature;
|
||
|
// UInt32 StrucVersion;
|
||
|
UInt32 VersionMS;
|
||
|
UInt32 VersionLS;
|
||
|
UInt32 ProductVersionMS;
|
||
|
UInt32 ProductVersionLS;
|
||
|
UInt32 FlagsMask;
|
||
|
UInt32 Flags;
|
||
|
UInt32 OS;
|
||
|
UInt32 Type;
|
||
|
UInt32 Subtype;
|
||
|
UInt32 DateMS;
|
||
|
UInt32 DateLS;
|
||
|
|
||
|
bool Parse(const Byte *p);
|
||
|
void PrintToTextFile(CTextFile &f, CObjectVector<CStringKeyValue> &keys);
|
||
|
};
|
||
|
|
||
|
bool CMy_VS_FIXEDFILEINFO::Parse(const Byte *p)
|
||
|
{
|
||
|
if (Get32(p) != kMy_VS_FFI_SIGNATURE) // signature;
|
||
|
return false;
|
||
|
// G32(0x04, StrucVersion);
|
||
|
G32(0x08, VersionMS);
|
||
|
G32(0x0C, VersionLS);
|
||
|
G32(0x10, ProductVersionMS);
|
||
|
G32(0x14, ProductVersionLS);
|
||
|
G32(0x18, FlagsMask);
|
||
|
G32(0x1C, Flags);
|
||
|
G32(0x20, OS);
|
||
|
G32(0x24, Type);
|
||
|
G32(0x28, Subtype);
|
||
|
G32(0x2C, DateMS);
|
||
|
G32(0x40, DateLS);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void PrintUInt32(CTextFile &f, UInt32 v)
|
||
|
{
|
||
|
char s[16];
|
||
|
ConvertUInt32ToString(v, s);
|
||
|
f.AddString(s);
|
||
|
}
|
||
|
|
||
|
static void PrintUInt32(UString &dest, UInt32 v)
|
||
|
{
|
||
|
wchar_t s[16];
|
||
|
ConvertUInt32ToString(v, s);
|
||
|
dest += s;
|
||
|
}
|
||
|
|
||
|
static void PrintHex(CTextFile &f, UInt32 val)
|
||
|
{
|
||
|
char temp[16];
|
||
|
temp[0] = '0';
|
||
|
temp[1] = 'x';
|
||
|
ConvertUInt32ToHex(val, temp + 2);
|
||
|
f.AddString(temp);
|
||
|
}
|
||
|
|
||
|
static void PrintVersion(CTextFile &f, UInt32 ms, UInt32 ls)
|
||
|
{
|
||
|
PrintUInt32(f, HIWORD(ms)); f.AddChar(',');
|
||
|
PrintUInt32(f, LOWORD(ms)); f.AddChar(',');
|
||
|
PrintUInt32(f, HIWORD(ls)); f.AddChar(',');
|
||
|
PrintUInt32(f, LOWORD(ls));
|
||
|
}
|
||
|
|
||
|
static void PrintVersion(UString &s, UInt32 ms, UInt32 ls)
|
||
|
{
|
||
|
PrintUInt32(s, HIWORD(ms)); s += L'.';
|
||
|
PrintUInt32(s, LOWORD(ms)); s += L'.';
|
||
|
PrintUInt32(s, HIWORD(ls)); s += L'.';
|
||
|
PrintUInt32(s, LOWORD(ls));
|
||
|
}
|
||
|
|
||
|
static const char * const k_VS_FileFlags[] =
|
||
|
{
|
||
|
"DEBUG"
|
||
|
, "PRERELEASE"
|
||
|
, "PATCHED"
|
||
|
, "PRIVATEBUILD"
|
||
|
, "INFOINFERRED"
|
||
|
, "SPECIALBUILD"
|
||
|
};
|
||
|
|
||
|
static const CUInt32PCharPair k_VS_FileOS[] =
|
||
|
{
|
||
|
{ 0x10001, "VOS_DOS_WINDOWS16" },
|
||
|
{ 0x10004, "VOS_DOS_WINDOWS32" },
|
||
|
{ 0x20002, "VOS_OS216_PM16" },
|
||
|
{ 0x30003, "VOS_OS232_PM32" },
|
||
|
{ 0x40004, "VOS_NT_WINDOWS32" }
|
||
|
};
|
||
|
|
||
|
static const char * const k_VS_FileOS_High[] =
|
||
|
{
|
||
|
"VOS_UNKNOWN"
|
||
|
, "VOS_DOS"
|
||
|
, "VOS_OS216"
|
||
|
, "VOS_OS232"
|
||
|
, "VOS_NT"
|
||
|
, "VOS_WINCE"
|
||
|
};
|
||
|
|
||
|
static const UInt32 kMY_VFT_DRV = 3;
|
||
|
static const UInt32 kMY_VFT_FONT = 4;
|
||
|
|
||
|
static const char * const k_VS_FileOS_Low[] =
|
||
|
{
|
||
|
"VOS__BASE"
|
||
|
, "VOS__WINDOWS16"
|
||
|
, "VOS__PM16"
|
||
|
, "VOS__PM32"
|
||
|
, "VOS__WINDOWS32"
|
||
|
};
|
||
|
|
||
|
static const char * const k_VS_FileType[] =
|
||
|
{
|
||
|
"VFT_UNKNOWN"
|
||
|
, "VFT_APP"
|
||
|
, "VFT_DLL"
|
||
|
, "VFT_DRV"
|
||
|
, "VFT_FONT"
|
||
|
, "VFT_VXD"
|
||
|
, "0x6"
|
||
|
, "VFT_STATIC_LIB"
|
||
|
};
|
||
|
|
||
|
// Subtype for VFT_DRV Type
|
||
|
static const char * const k_VS_FileSubType_DRV[] =
|
||
|
{
|
||
|
"0"
|
||
|
, "PRINTER"
|
||
|
, "KEYBOARD"
|
||
|
, "LANGUAGE"
|
||
|
, "DISPLAY"
|
||
|
, "MOUSE"
|
||
|
, "NETWORK"
|
||
|
, "SYSTEM"
|
||
|
, "INSTALLABLE"
|
||
|
, "SOUND"
|
||
|
, "COMM"
|
||
|
, "INPUTMETHOD"
|
||
|
, "VERSIONED_PRINTER"
|
||
|
};
|
||
|
|
||
|
// Subtype for VFT_FONT Type
|
||
|
static const char * const k_VS_FileSubType_FONT[] =
|
||
|
{
|
||
|
"0"
|
||
|
, "VFT2_FONT_RASTER"
|
||
|
, "VFT2_FONT_VECTOR"
|
||
|
, "VFT2_FONT_TRUETYPE"
|
||
|
};
|
||
|
|
||
|
static int FindKey(CObjectVector<CStringKeyValue> &v, const char *key)
|
||
|
{
|
||
|
FOR_VECTOR (i, v)
|
||
|
if (v[i].Key.IsEqualTo(key))
|
||
|
return i;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static void AddToUniqueUStringVector(CObjectVector<CStringKeyValue> &v, const UString &key, const UString &value)
|
||
|
{
|
||
|
bool needInsert = false;
|
||
|
unsigned i;
|
||
|
for (i = 0; i < v.Size(); i++)
|
||
|
{
|
||
|
if (v[i].Key == key)
|
||
|
{
|
||
|
if (v[i].Value == value)
|
||
|
return;
|
||
|
needInsert = true;
|
||
|
}
|
||
|
else if (needInsert)
|
||
|
break;
|
||
|
}
|
||
|
CStringKeyValue &pair = v.InsertNew(i);
|
||
|
pair.Key = key;
|
||
|
pair.Value = value;
|
||
|
}
|
||
|
|
||
|
void CMy_VS_FIXEDFILEINFO::PrintToTextFile(CTextFile &f, CObjectVector<CStringKeyValue> &keys)
|
||
|
{
|
||
|
f.AddString("FILEVERSION ");
|
||
|
PrintVersion(f, VersionMS, VersionLS);
|
||
|
f.NewLine();
|
||
|
|
||
|
f.AddString("PRODUCTVERSION ");
|
||
|
PrintVersion(f, ProductVersionMS, ProductVersionLS);
|
||
|
f.NewLine();
|
||
|
|
||
|
{
|
||
|
UString s;
|
||
|
PrintVersion(s, VersionMS, VersionLS);
|
||
|
AddToUniqueUStringVector(keys, L"FileVersion", s);
|
||
|
}
|
||
|
{
|
||
|
UString s;
|
||
|
PrintVersion(s, ProductVersionMS, ProductVersionLS);
|
||
|
AddToUniqueUStringVector(keys, L"ProductVersion", s);
|
||
|
}
|
||
|
|
||
|
f.AddString("FILEFLAGSMASK ");
|
||
|
PrintHex(f, FlagsMask);
|
||
|
f.NewLine();
|
||
|
|
||
|
f.AddString("FILEFLAGS ");
|
||
|
{
|
||
|
bool wasPrinted = false;
|
||
|
for (unsigned i = 0; i < ARRAY_SIZE(k_VS_FileFlags); i++)
|
||
|
{
|
||
|
if ((Flags & ((UInt32)1 << i)) != 0)
|
||
|
{
|
||
|
if (wasPrinted)
|
||
|
f.AddString(" | ");
|
||
|
f.AddString("VS_FF_");
|
||
|
f.AddString(k_VS_FileFlags[i]);
|
||
|
wasPrinted = true;
|
||
|
}
|
||
|
}
|
||
|
UInt32 v = Flags & ~(((UInt32)1 << ARRAY_SIZE(k_VS_FileFlags)) - 1);
|
||
|
if (v != 0 || !wasPrinted)
|
||
|
{
|
||
|
if (wasPrinted)
|
||
|
f.AddString(" | ");
|
||
|
PrintHex(f, v);
|
||
|
}
|
||
|
}
|
||
|
f.NewLine();
|
||
|
|
||
|
// OS = 0x111230;
|
||
|
f.AddString("FILEOS ");
|
||
|
unsigned i;
|
||
|
for (i = 0; i < ARRAY_SIZE(k_VS_FileOS); i++)
|
||
|
{
|
||
|
const CUInt32PCharPair &pair = k_VS_FileOS[i];
|
||
|
if (OS == pair.Value)
|
||
|
{
|
||
|
// continue;
|
||
|
// f.AddString("VOS_");
|
||
|
f.AddString(pair.Name);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (i == ARRAY_SIZE(k_VS_FileOS))
|
||
|
{
|
||
|
UInt32 high = OS >> 16;
|
||
|
if (high < ARRAY_SIZE(k_VS_FileOS_High))
|
||
|
f.AddString(k_VS_FileOS_High[high]);
|
||
|
else
|
||
|
PrintHex(f, high << 16);
|
||
|
UInt32 low = OS & 0xFFFF;
|
||
|
if (low != 0)
|
||
|
{
|
||
|
f.AddString(" | ");
|
||
|
if (low < ARRAY_SIZE(k_VS_FileOS_Low))
|
||
|
f.AddString(k_VS_FileOS_Low[low]);
|
||
|
else
|
||
|
PrintHex(f, low);
|
||
|
}
|
||
|
}
|
||
|
f.NewLine();
|
||
|
|
||
|
f.AddString("FILETYPE ");
|
||
|
if (Type < ARRAY_SIZE(k_VS_FileType))
|
||
|
f.AddString(k_VS_FileType[Type]);
|
||
|
else
|
||
|
PrintHex(f, Type);
|
||
|
f.NewLine();
|
||
|
|
||
|
f.AddString("FILESUBTYPE ");
|
||
|
bool needPrintSubType = true;
|
||
|
if (Type == kMY_VFT_DRV)
|
||
|
{
|
||
|
if (Subtype != 0 && Subtype < ARRAY_SIZE(k_VS_FileSubType_DRV))
|
||
|
{
|
||
|
f.AddString("VFT2_DRV_");
|
||
|
f.AddString(k_VS_FileSubType_DRV[Subtype]);
|
||
|
needPrintSubType = false;
|
||
|
}
|
||
|
}
|
||
|
else if (Type == kMY_VFT_FONT)
|
||
|
{
|
||
|
if (Subtype != 0 && Subtype < ARRAY_SIZE(k_VS_FileSubType_FONT))
|
||
|
{
|
||
|
f.AddString(k_VS_FileSubType_FONT[Subtype]);
|
||
|
needPrintSubType = false;
|
||
|
}
|
||
|
}
|
||
|
if (needPrintSubType)
|
||
|
PrintHex(f, Subtype);
|
||
|
f.NewLine();
|
||
|
}
|
||
|
|
||
|
static void CopyToUString(const Byte *p, UString &s)
|
||
|
{
|
||
|
for (;;)
|
||
|
{
|
||
|
wchar_t c = (wchar_t)Get16(p);
|
||
|
p += 2;
|
||
|
if (c == 0)
|
||
|
return;
|
||
|
s += c;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool CompareWStrStrings(const Byte *p, const char *s)
|
||
|
{
|
||
|
unsigned pos = 0;
|
||
|
for (;;)
|
||
|
{
|
||
|
Byte c = *s++;
|
||
|
if (Get16(p + pos) != c)
|
||
|
return false;
|
||
|
pos += 2;
|
||
|
if (c == 0)
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct CVersionBlock
|
||
|
{
|
||
|
UInt32 TotalLen;
|
||
|
UInt32 ValueLen;
|
||
|
bool IsTextValue;
|
||
|
unsigned StrSize;
|
||
|
|
||
|
bool Parse(const Byte *p, UInt32 size);
|
||
|
};
|
||
|
|
||
|
static int Get_Utf16Str_Len_InBytes(const Byte *p, size_t size)
|
||
|
{
|
||
|
unsigned pos = 0;
|
||
|
for (;;)
|
||
|
{
|
||
|
if (pos + 1 >= size)
|
||
|
return -1;
|
||
|
if (Get16(p + pos) == 0)
|
||
|
return pos;
|
||
|
pos += 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static const unsigned k_ResoureBlockHeader_Size = 6;
|
||
|
|
||
|
bool CVersionBlock::Parse(const Byte *p, UInt32 size)
|
||
|
{
|
||
|
if (size < k_ResoureBlockHeader_Size)
|
||
|
return false;
|
||
|
TotalLen = Get16(p);
|
||
|
ValueLen = Get16(p + 2);
|
||
|
if (TotalLen == 0 || TotalLen > size)
|
||
|
return false;
|
||
|
switch (Get16(p + 4))
|
||
|
{
|
||
|
case 0: IsTextValue = false; break;
|
||
|
case 1: IsTextValue = true; break;
|
||
|
default: return false;
|
||
|
}
|
||
|
StrSize = 0;
|
||
|
int t = Get_Utf16Str_Len_InBytes(p + k_ResoureBlockHeader_Size, TotalLen - k_ResoureBlockHeader_Size);
|
||
|
if (t < 0)
|
||
|
return false;
|
||
|
StrSize = t;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void AddParamString(CTextFile &f, const Byte *p, size_t sLen)
|
||
|
{
|
||
|
f.AddChar(' ');
|
||
|
f.AddChar('\"');
|
||
|
f.AddBytes(p, sLen);
|
||
|
f.AddChar('\"');
|
||
|
}
|
||
|
|
||
|
static bool ParseVersion(const Byte *p, UInt32 size, CTextFile &f, CObjectVector<CStringKeyValue> &keys)
|
||
|
{
|
||
|
UInt32 pos;
|
||
|
{
|
||
|
const unsigned k_sizeof_VS_FIXEDFILEINFO = 13 * 4;
|
||
|
|
||
|
CVersionBlock vb;
|
||
|
if (!vb.Parse(p, size))
|
||
|
return false;
|
||
|
if (vb.ValueLen != k_sizeof_VS_FIXEDFILEINFO) // maybe 0 is allowed here?
|
||
|
return false;
|
||
|
if (vb.IsTextValue)
|
||
|
return false;
|
||
|
pos = k_ResoureBlockHeader_Size;
|
||
|
if (!CompareWStrStrings(p + pos, "VS_VERSION_INFO"))
|
||
|
return false;
|
||
|
pos += vb.StrSize + 2;
|
||
|
pos += (4 - pos) & 3;
|
||
|
if (pos + vb.ValueLen > vb.TotalLen)
|
||
|
return false;
|
||
|
/* sometimes resource contains zeros in remainder.
|
||
|
So we don't check that size != vb.TotalLen
|
||
|
// if (size != vb.TotalLen) return false;
|
||
|
*/
|
||
|
if (size > vb.TotalLen)
|
||
|
size = vb.TotalLen;
|
||
|
CMy_VS_FIXEDFILEINFO FixedFileInfo;
|
||
|
if (!FixedFileInfo.Parse(p + pos))
|
||
|
return false;
|
||
|
FixedFileInfo.PrintToTextFile(f, keys);
|
||
|
pos += vb.ValueLen;
|
||
|
}
|
||
|
|
||
|
f.OpenBlock(0);
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
pos += (4 - pos) & 3;
|
||
|
if (pos >= size)
|
||
|
break;
|
||
|
|
||
|
CVersionBlock vb;
|
||
|
if (!vb.Parse(p + pos, size - pos))
|
||
|
return false;
|
||
|
if (vb.ValueLen != 0)
|
||
|
return false;
|
||
|
UInt32 endPos = pos + vb.TotalLen;
|
||
|
pos += k_ResoureBlockHeader_Size;
|
||
|
|
||
|
f.AddSpaces(2);
|
||
|
f.AddString("BLOCK");
|
||
|
AddParamString(f, p + pos, vb.StrSize);
|
||
|
|
||
|
f.NewLine();
|
||
|
f.OpenBlock(2);
|
||
|
|
||
|
if (CompareWStrStrings(p + pos, "VarFileInfo"))
|
||
|
{
|
||
|
pos += vb.StrSize + 2;
|
||
|
for (;;)
|
||
|
{
|
||
|
pos += (4 - pos) & 3;
|
||
|
if (pos >= endPos)
|
||
|
break;
|
||
|
CVersionBlock vb2;
|
||
|
if (!vb2.Parse(p + pos, endPos - pos))
|
||
|
return false;
|
||
|
UInt32 endPos2 = pos + vb2.TotalLen;
|
||
|
if (vb2.IsTextValue)
|
||
|
return false;
|
||
|
pos += k_ResoureBlockHeader_Size;
|
||
|
f.AddSpaces(4);
|
||
|
f.AddString("VALUE");
|
||
|
AddParamString(f, p + pos, vb2.StrSize);
|
||
|
if (!CompareWStrStrings(p + pos, "Translation"))
|
||
|
return false;
|
||
|
pos += vb2.StrSize + 2;
|
||
|
pos += (4 - pos) & 3;
|
||
|
if (pos + vb2.ValueLen != endPos2)
|
||
|
return false;
|
||
|
if ((vb2.ValueLen & 3) != 0)
|
||
|
return false;
|
||
|
UInt32 num = (vb2.ValueLen >> 2);
|
||
|
for (; num != 0; num--, pos += 4)
|
||
|
{
|
||
|
UInt32 dw = Get32(p + pos);
|
||
|
UInt32 lang = LOWORD(dw);
|
||
|
UInt32 codePage = HIWORD(dw);
|
||
|
|
||
|
f.AddString(", ");
|
||
|
PrintHex(f, lang);
|
||
|
f.AddString(", ");
|
||
|
PrintUInt32(f, codePage);
|
||
|
}
|
||
|
f.NewLine();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!CompareWStrStrings(p + pos, "StringFileInfo"))
|
||
|
return false;
|
||
|
pos += vb.StrSize + 2;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
pos += (4 - pos) & 3;
|
||
|
if (pos >= endPos)
|
||
|
break;
|
||
|
CVersionBlock vb2;
|
||
|
if (!vb2.Parse(p + pos, endPos - pos))
|
||
|
return false;
|
||
|
UInt32 endPos2 = pos + vb2.TotalLen;
|
||
|
if (vb2.ValueLen != 0)
|
||
|
return false;
|
||
|
pos += k_ResoureBlockHeader_Size;
|
||
|
|
||
|
f.AddSpaces(4);
|
||
|
f.AddString("BLOCK");
|
||
|
AddParamString(f, p + pos, vb2.StrSize);
|
||
|
pos += vb2.StrSize + 2;
|
||
|
|
||
|
f.NewLine();
|
||
|
f.OpenBlock(4);
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
pos += (4 - pos) & 3;
|
||
|
if (pos >= endPos2)
|
||
|
break;
|
||
|
|
||
|
CVersionBlock vb3;
|
||
|
if (!vb3.Parse(p + pos, endPos2 - pos))
|
||
|
return false;
|
||
|
// ValueLen sometimes is a number of characters (not bytes)?
|
||
|
// So we don't use it.
|
||
|
UInt32 endPos3 = pos + vb3.TotalLen;
|
||
|
pos += k_ResoureBlockHeader_Size;
|
||
|
|
||
|
// we don't write string if it's not text
|
||
|
if (vb3.IsTextValue)
|
||
|
{
|
||
|
f.AddSpaces(6);
|
||
|
f.AddString("VALUE");
|
||
|
AddParamString(f, p + pos, vb3.StrSize);
|
||
|
UString key;
|
||
|
UString value;
|
||
|
CopyToUString(p + pos, key);
|
||
|
pos += vb3.StrSize + 2;
|
||
|
|
||
|
pos += (4 - pos) & 3;
|
||
|
if (vb3.ValueLen > 0 && pos + 2 <= endPos3)
|
||
|
{
|
||
|
f.AddChar(',');
|
||
|
f.AddSpaces((34 - (int)vb3.StrSize) / 2);
|
||
|
int sLen = Get_Utf16Str_Len_InBytes(p + pos, endPos3 - pos);
|
||
|
if (sLen < 0)
|
||
|
return false;
|
||
|
AddParamString(f, p + pos, (unsigned)sLen);
|
||
|
CopyToUString(p + pos, value);
|
||
|
pos += sLen + 2;
|
||
|
}
|
||
|
AddToUniqueUStringVector(keys, key, value);
|
||
|
}
|
||
|
pos = endPos3;
|
||
|
f.NewLine();
|
||
|
}
|
||
|
f.CloseBlock(4);
|
||
|
}
|
||
|
}
|
||
|
f.CloseBlock(2);
|
||
|
}
|
||
|
|
||
|
f.CloseBlock(0);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CHandler::OpenResources(unsigned sectionIndex, IInStream *stream, IArchiveOpenCallback *callback)
|
||
|
{
|
||
|
const CSection § = _sections[sectionIndex];
|
||
|
size_t fileSize = sect.PSize;
|
||
|
{
|
||
|
size_t fileSizeMin = sect.PSize;
|
||
|
|
||
|
if (sect.VSize < sect.PSize)
|
||
|
{
|
||
|
fileSize = fileSizeMin = sect.VSize;
|
||
|
const int numBits = _optHeader.GetNumFileAlignBits();
|
||
|
if (numBits > 0)
|
||
|
{
|
||
|
const UInt32 mask = ((UInt32)1 << numBits) - 1;
|
||
|
const size_t end = (size_t)((sect.VSize + mask) & (UInt32)~mask);
|
||
|
if (end > sect.VSize)
|
||
|
if (end <= sect.PSize)
|
||
|
fileSize = end;
|
||
|
else
|
||
|
fileSize = sect.PSize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (fileSize > kFileSizeMax)
|
||
|
return S_FALSE;
|
||
|
|
||
|
{
|
||
|
const UInt64 fileSize64 = fileSize;
|
||
|
if (callback)
|
||
|
RINOK(callback->SetTotal(NULL, &fileSize64));
|
||
|
}
|
||
|
|
||
|
RINOK(stream->Seek(sect.Pa, STREAM_SEEK_SET, NULL));
|
||
|
|
||
|
_buf.Alloc(fileSize);
|
||
|
|
||
|
size_t pos;
|
||
|
|
||
|
for (pos = 0; pos < fileSize;)
|
||
|
{
|
||
|
{
|
||
|
const UInt64 offset64 = pos;
|
||
|
if (callback)
|
||
|
RINOK(callback->SetCompleted(NULL, &offset64))
|
||
|
}
|
||
|
size_t rem = MyMin(fileSize - pos, (size_t)(1 << 22));
|
||
|
RINOK(ReadStream(stream, _buf + pos, &rem));
|
||
|
if (rem == 0)
|
||
|
{
|
||
|
if (pos < fileSizeMin)
|
||
|
return S_FALSE;
|
||
|
break;
|
||
|
}
|
||
|
pos += rem;
|
||
|
}
|
||
|
|
||
|
if (pos < fileSize)
|
||
|
memset(_buf + pos, 0, fileSize - pos);
|
||
|
}
|
||
|
|
||
|
_usedRes.Alloc(fileSize);
|
||
|
CRecordVector<CTableItem> specItems;
|
||
|
RINOK(ReadTable(0, specItems));
|
||
|
|
||
|
_oneLang = true;
|
||
|
bool stringsOk = true;
|
||
|
size_t maxOffset = 0;
|
||
|
|
||
|
FOR_VECTOR (i, specItems)
|
||
|
{
|
||
|
const CTableItem &item1 = specItems[i];
|
||
|
if ((item1.Offset & kFlag) == 0)
|
||
|
return S_FALSE;
|
||
|
|
||
|
CRecordVector<CTableItem> specItems2;
|
||
|
RINOK(ReadTable(item1.Offset & kMask, specItems2));
|
||
|
|
||
|
FOR_VECTOR (j, specItems2)
|
||
|
{
|
||
|
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_VECTOR (k, specItems3)
|
||
|
{
|
||
|
if (_items.Size() >= kNumResItemsMax)
|
||
|
return S_FALSE;
|
||
|
const CTableItem &item3 = specItems3[k];
|
||
|
if ((item3.Offset & kFlag) != 0)
|
||
|
return S_FALSE;
|
||
|
if (item3.Offset >= _buf.Size() || _buf.Size() - 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (item.IsVersion())
|
||
|
{
|
||
|
if (offset > _buf.Size() || _buf.Size() - offset < item.Size)
|
||
|
continue;
|
||
|
CTextFile f;
|
||
|
if (ParseVersion((const Byte *)_buf + offset, item.Size, f, _versionKeys))
|
||
|
{
|
||
|
CMixItem mixItem;
|
||
|
mixItem.VersionIndex = _versionFiles.Size();
|
||
|
mixItem.SectionIndex = sectionIndex; // check it !!!!
|
||
|
CByteBuffer_WithLang &vf = _versionFiles.AddNew();
|
||
|
vf.Lang = item.Lang;
|
||
|
vf.CopyFrom(f.Buf, f.Buf.GetPos());
|
||
|
_mixItems.Add(mixItem);
|
||
|
continue;
|
||
|
}
|
||
|
// PrintError("ver.Parse error");
|
||
|
}
|
||
|
|
||
|
item.Enabled = true;
|
||
|
_items.Add(item);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (stringsOk && !_strings.IsEmpty())
|
||
|
{
|
||
|
unsigned 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].FinalSize() == 0)
|
||
|
continue;
|
||
|
CMixItem mixItem;
|
||
|
mixItem.StringIndex = i;
|
||
|
mixItem.SectionIndex = sectionIndex;
|
||
|
_mixItems.Add(mixItem);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_usedRes.Free();
|
||
|
|
||
|
{
|
||
|
// PSize can be much larger than VSize in some exe installers.
|
||
|
// it contains archive data after PE resources.
|
||
|
// So we need to use PSize here!
|
||
|
if (maxOffset < sect.PSize)
|
||
|
{
|
||
|
size_t end = fileSize;
|
||
|
|
||
|
// 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;
|
||
|
|
||
|
CSection sect2;
|
||
|
sect2.Flags = 0;
|
||
|
sect2.Pa = sect.Pa + (UInt32)maxOffset;
|
||
|
sect2.Va = sect.Va + (UInt32)maxOffset;
|
||
|
|
||
|
// 9.29: we use sect.PSize instead of sect.VSize to support some CAB-SFX
|
||
|
// the code for .rsrc_2 is commented.
|
||
|
sect2.PSize = sect.PSize - (UInt32)maxOffset;
|
||
|
|
||
|
if (sect2.PSize != 0)
|
||
|
{
|
||
|
sect2.VSize = sect2.PSize;
|
||
|
sect2.Name = ".rsrc_1";
|
||
|
sect2.Time = 0;
|
||
|
sect2.IsAdditionalSection = true;
|
||
|
_sections.Add(sect2);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static inline bool CheckPeOffset(UInt32 pe)
|
||
|
{
|
||
|
return (pe >= 0x40 && pe <= 0x1000 && (pe & 7) == 0);
|
||
|
}
|
||
|
|
||
|
static const unsigned kStartSize = 0x40;
|
||
|
|
||
|
API_FUNC_static_IsArc IsArc_Pe(const Byte *p, size_t size)
|
||
|
{
|
||
|
if (size < 2)
|
||
|
return k_IsArc_Res_NEED_MORE;
|
||
|
if (p[0] != 'M' || p[1] != 'Z')
|
||
|
return k_IsArc_Res_NO;
|
||
|
if (size < kStartSize)
|
||
|
return k_IsArc_Res_NEED_MORE;
|
||
|
UInt32 pe = Get32(p + 0x3C);
|
||
|
if (!CheckPeOffset(pe))
|
||
|
return k_IsArc_Res_NO;
|
||
|
if (pe + kHeaderSize > size)
|
||
|
return k_IsArc_Res_NEED_MORE;
|
||
|
CHeader header;
|
||
|
if (!header.Parse(p + pe))
|
||
|
return k_IsArc_Res_NO;
|
||
|
return k_IsArc_Res_YES;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
|
||
|
{
|
||
|
{
|
||
|
Byte h[kStartSize];
|
||
|
_mainSubfile = -1;
|
||
|
RINOK(ReadStream_FALSE(stream, h, kStartSize));
|
||
|
if (h[0] != 'M' || h[1] != 'Z')
|
||
|
return S_FALSE;
|
||
|
/* most of PE files contain 0x0090 at offset 2.
|
||
|
But some rare PE files contain another values. So we don't use that check.
|
||
|
if (Get16(h + 2) != 0x90) return false; */
|
||
|
_peOffset = Get32(h + 0x3C);
|
||
|
if (!CheckPeOffset(_peOffset))
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
{
|
||
|
Byte h[kHeaderSize];
|
||
|
RINOK(stream->Seek(_peOffset, STREAM_SEEK_SET, NULL));
|
||
|
RINOK(ReadStream_FALSE(stream, h, kHeaderSize));
|
||
|
if (!_header.Parse(h))
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
UInt32 bufSize = _header.OptHeaderSize + (UInt32)_header.NumSections * kSectionSize;
|
||
|
_totalSize = _peOffset + kHeaderSize + bufSize;
|
||
|
CByteBuffer buffer(bufSize);
|
||
|
|
||
|
RINOK(ReadStream_FALSE(stream, buffer, bufSize));
|
||
|
if (!_optHeader.Parse(buffer, _header.OptHeaderSize))
|
||
|
return S_FALSE;
|
||
|
|
||
|
UInt32 pos = _header.OptHeaderSize;
|
||
|
unsigned i;
|
||
|
for (i = 0; i < _header.NumSections; i++, pos += kSectionSize)
|
||
|
{
|
||
|
CSection § = _sections.AddNew();
|
||
|
sect.Parse(buffer + pos);
|
||
|
sect.IsRealSect = true;
|
||
|
|
||
|
/* PE pre-file in .hxs file has errors:
|
||
|
PSize of resource is larger tnan real size.
|
||
|
So it overlaps next ".its" section.
|
||
|
We correct it. */
|
||
|
|
||
|
if (i > 0)
|
||
|
{
|
||
|
CSection &prev = _sections[i - 1];
|
||
|
if (prev.Pa < sect.Pa &&
|
||
|
prev.Pa + prev.PSize > sect.Pa &&
|
||
|
sect.PSize > 0)
|
||
|
{
|
||
|
// printf("\n !!!! Section correction: %s\n ", prev.Name);
|
||
|
// fflush(stdout);
|
||
|
prev.PSize = sect.Pa - prev.Pa;
|
||
|
}
|
||
|
}
|
||
|
/* last ".its" section in hxs file has incorrect sect.PSize.
|
||
|
So we reduce it to real sect.VSize */
|
||
|
if (sect.VSize == 24 && sect.PSize == 512 && i == (unsigned)_header.NumSections - 1)
|
||
|
sect.PSize = sect.VSize;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < _sections.Size(); i++)
|
||
|
_sections[i].UpdateTotalSize(_totalSize);
|
||
|
|
||
|
bool thereISDebug;
|
||
|
RINOK(LoadDebugSections(stream, thereISDebug));
|
||
|
|
||
|
const CDirLink &certLink = _optHeader.DirItems[kDirLink_Certificate];
|
||
|
if (certLink.Size != 0)
|
||
|
{
|
||
|
CSection § = _sections.AddNew();
|
||
|
sect.Name = "CERTIFICATE";
|
||
|
sect.Va = 0;
|
||
|
sect.Pa = certLink.Va;
|
||
|
sect.PSize = sect.VSize = certLink.Size;
|
||
|
sect.UpdateTotalSize(_totalSize);
|
||
|
}
|
||
|
|
||
|
if (thereISDebug)
|
||
|
{
|
||
|
/* sometime there is some data after debug section.
|
||
|
We don't see any reference in exe file to that data.
|
||
|
But we suppose that it's part of EXE file */
|
||
|
|
||
|
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.Alloc(kAlign);
|
||
|
Byte *buf = buffer;
|
||
|
size_t processed = size;
|
||
|
RINOK(ReadStream(stream, buf, &processed));
|
||
|
|
||
|
/*
|
||
|
if (processed != 0)
|
||
|
{
|
||
|
printf("\ndata after debug %d, %d \n", (int)size, (int)processed);
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
size_t k;
|
||
|
for (k = 0; k < processed; k++)
|
||
|
if (buf[k] != 0)
|
||
|
break;
|
||
|
if (processed < size && processed < 100)
|
||
|
_totalSize += (UInt32)processed;
|
||
|
else if (((_totalSize + k) & 0x1FF) == 0 || processed < size)
|
||
|
_totalSize += (UInt32)k;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (_header.NumSymbols > 0 && _header.PointerToSymbolTable >= 512)
|
||
|
{
|
||
|
if (_header.NumSymbols >= (1 << 24))
|
||
|
return S_FALSE;
|
||
|
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;
|
||
|
|
||
|
CSection § = _sections.AddNew();
|
||
|
sect.Name = "COFF_SYMBOLS";
|
||
|
sect.Va = 0;
|
||
|
sect.Pa = _header.PointerToSymbolTable;
|
||
|
sect.PSize = sect.VSize = size;
|
||
|
sect.UpdateTotalSize(_totalSize);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
CObjectVector<CSection> sections = _sections;
|
||
|
sections.Sort();
|
||
|
UInt32 limit = (1 << 12);
|
||
|
unsigned num = 0;
|
||
|
FOR_VECTOR (k, sections)
|
||
|
{
|
||
|
const CSection &s = sections[k];
|
||
|
if (s.Pa > limit)
|
||
|
{
|
||
|
CSection &s2 = _sections.AddNew();
|
||
|
s2.Pa = s2.Va = limit;
|
||
|
s2.PSize = s2.VSize = s.Pa - limit;
|
||
|
s2.IsAdditionalSection = true;
|
||
|
s2.Name = '[';
|
||
|
s2.Name += GetDecString(num++);
|
||
|
s2.Name += ']';
|
||
|
limit = s.Pa;
|
||
|
}
|
||
|
UInt32 next = s.Pa + s.PSize;
|
||
|
if (next < s.Pa)
|
||
|
break;
|
||
|
if (next >= limit)
|
||
|
limit = next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (_optHeader.CheckSum != 0)
|
||
|
{
|
||
|
RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
|
||
|
UInt32 checkSum = 0;
|
||
|
RINOK(CalcCheckSum(stream, _totalSize, _peOffset + kHeaderSize + k_CheckSum_Field_Offset, checkSum));
|
||
|
_checksumError = (checkSum != _optHeader.CheckSum);
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!_allowTail)
|
||
|
{
|
||
|
UInt64 fileSize;
|
||
|
RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize));
|
||
|
if (fileSize > _totalSize)
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
_parseResources = true;
|
||
|
// _parseResources = false;
|
||
|
|
||
|
UInt64 mainSize = 0, mainSize2 = 0;
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
_resourcesPrefix.SetFromAscii(sect.Name);
|
||
|
_resourcesPrefix.Add_PathSepar();
|
||
|
FOR_VECTOR (j, _items)
|
||
|
{
|
||
|
const CResItem &item = _items[j];
|
||
|
if (item.Enabled)
|
||
|
{
|
||
|
mixItem.ResourceIndex = j;
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
// 9.29: .rsrc_2 code was commented.
|
||
|
// .rsrc_1 now must include that .rsrc_2 block.
|
||
|
/*
|
||
|
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 §2 = _sections.AddNew();
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
continue;
|
||
|
}
|
||
|
if (res != S_FALSE)
|
||
|
return res;
|
||
|
CloseResources();
|
||
|
}
|
||
|
if (sect.IsAdditionalSection)
|
||
|
{
|
||
|
if (sect.PSize >= mainSize)
|
||
|
{
|
||
|
mainSize2 = mainSize;
|
||
|
mainSize = sect.PSize;
|
||
|
_mainSubfile = _mixItems.Size();
|
||
|
}
|
||
|
else if (sect.PSize >= mainSize2)
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < _versionKeys.Size(); i++)
|
||
|
{
|
||
|
if (i != 0)
|
||
|
_versionFullString.Add_LF();
|
||
|
const CStringKeyValue &k = _versionKeys[i];
|
||
|
_versionFullString += k.Key;
|
||
|
_versionFullString += L": ";
|
||
|
_versionFullString += k.Value;
|
||
|
}
|
||
|
|
||
|
{
|
||
|
int keyIndex = FindKey(_versionKeys, "OriginalFilename");
|
||
|
if (keyIndex >= 0)
|
||
|
_originalFilename = _versionKeys[keyIndex].Value;
|
||
|
}
|
||
|
{
|
||
|
int keyIndex = FindKey(_versionKeys, "FileDescription");
|
||
|
if (keyIndex >= 0)
|
||
|
_versionShortString = _versionKeys[keyIndex].Value;
|
||
|
}
|
||
|
{
|
||
|
int keyIndex = FindKey(_versionKeys, "FileVersion");
|
||
|
if (keyIndex >= 0)
|
||
|
{
|
||
|
_versionShortString.Add_Space();
|
||
|
_versionShortString += _versionKeys[keyIndex].Value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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();
|
||
|
_versionFiles.Clear();
|
||
|
_buf.Free();
|
||
|
_versionFullString.Empty();
|
||
|
_versionShortString.Empty();
|
||
|
_originalFilename.Empty();
|
||
|
_versionKeys.Clear();
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::Close()
|
||
|
{
|
||
|
_totalSize = 0;
|
||
|
_checksumError = false;
|
||
|
_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)(Int32)-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]];
|
||
|
UInt64 size;
|
||
|
if (mixItem.StringIndex >= 0)
|
||
|
size = _strings[mixItem.StringIndex].FinalSize();
|
||
|
else if (mixItem.VersionIndex >= 0)
|
||
|
size = _versionFiles[mixItem.VersionIndex].Size();
|
||
|
else if (mixItem.ResourceIndex >= 0)
|
||
|
size = _items[mixItem.ResourceIndex].GetSize();
|
||
|
else
|
||
|
size = _sections[mixItem.SectionIndex].GetSizeExtract();
|
||
|
totalSize += size;
|
||
|
}
|
||
|
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);
|
||
|
|
||
|
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.FinalSize();
|
||
|
if (!testMode && !outStream)
|
||
|
continue;
|
||
|
|
||
|
RINOK(extractCallback->PrepareOperation(askMode));
|
||
|
if (outStream)
|
||
|
RINOK(WriteStream(outStream, item.Buf, item.FinalSize()));
|
||
|
}
|
||
|
else if (mixItem.VersionIndex >= 0)
|
||
|
{
|
||
|
const CByteBuffer &item = _versionFiles[mixItem.VersionIndex];
|
||
|
currentItemSize = item.Size();
|
||
|
if (!testMode && !outStream)
|
||
|
continue;
|
||
|
|
||
|
RINOK(extractCallback->PrepareOperation(askMode));
|
||
|
if (outStream)
|
||
|
RINOK(WriteStream(outStream, item, item.Size()));
|
||
|
}
|
||
|
else if (mixItem.ResourceIndex >= 0)
|
||
|
{
|
||
|
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));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
currentItemSize = sect.GetSizeExtract();
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
outStream.Release();
|
||
|
RINOK(extractCallback->SetOperationResult(isOk ?
|
||
|
NExtract::NOperationResult::kOK :
|
||
|
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.CopyFrom(item.Buf, item.FinalSize());
|
||
|
}
|
||
|
else if (mixItem.VersionIndex >= 0)
|
||
|
{
|
||
|
const CByteBuffer &item = _versionFiles[mixItem.VersionIndex];
|
||
|
referenceBuf->Buf.CopyFrom(item, 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.Alloc(item.HeaderSize + item.Size);
|
||
|
memcpy(referenceBuf->Buf, item.Header, item.HeaderSize);
|
||
|
if (item.Size != 0)
|
||
|
memcpy(referenceBuf->Buf + item.HeaderSize, _buf + offset, item.Size);
|
||
|
}
|
||
|
inStreamSpec->Init(referenceBuf);
|
||
|
|
||
|
*stream = streamTemp.Detach();
|
||
|
return S_OK;
|
||
|
COM_TRY_END
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::AllowTail(Int32 allowTail)
|
||
|
{
|
||
|
_allowTail = IntToBool(allowTail);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static const Byte k_Signature[] = { 'M', 'Z' };
|
||
|
|
||
|
REGISTER_ARC_I(
|
||
|
"PE", "exe dll sys", 0, 0xDD,
|
||
|
k_Signature,
|
||
|
0,
|
||
|
NArcInfoFlags::kPreArc,
|
||
|
IsArc_Pe)
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
namespace NTe {
|
||
|
|
||
|
// Terse Executable (TE) image
|
||
|
|
||
|
struct CDataDir
|
||
|
{
|
||
|
UInt32 Va;
|
||
|
UInt32 Size;
|
||
|
|
||
|
void Parse(const Byte *p)
|
||
|
{
|
||
|
G32(0, Va);
|
||
|
G32(4, Size);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static const UInt32 kHeaderSize = 40;
|
||
|
|
||
|
static bool FindValue(const CUInt32PCharPair *pairs, unsigned num, UInt32 value)
|
||
|
{
|
||
|
for (unsigned i = 0; i < num; i++)
|
||
|
if (pairs[i].Value == value)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#define MY_FIND_VALUE(pairs, value) FindValue(pairs, ARRAY_SIZE(pairs), value)
|
||
|
|
||
|
static const UInt32 kNumSection_MAX = 32;
|
||
|
|
||
|
struct CHeader
|
||
|
{
|
||
|
UInt16 Machine;
|
||
|
Byte NumSections;
|
||
|
Byte SubSystem;
|
||
|
UInt16 StrippedSize;
|
||
|
/*
|
||
|
UInt32 AddressOfEntryPoint;
|
||
|
UInt32 BaseOfCode;
|
||
|
UInt64 ImageBase;
|
||
|
*/
|
||
|
CDataDir DataDir[2]; // base relocation and debug directory
|
||
|
|
||
|
bool ConvertPa(UInt32 &pa) const
|
||
|
{
|
||
|
if (pa < StrippedSize)
|
||
|
return false;
|
||
|
pa = pa - StrippedSize + kHeaderSize;
|
||
|
return true;
|
||
|
}
|
||
|
bool Parse(const Byte *p);
|
||
|
};
|
||
|
|
||
|
bool CHeader::Parse(const Byte *p)
|
||
|
{
|
||
|
NumSections = p[4];
|
||
|
if (NumSections > kNumSection_MAX)
|
||
|
return false;
|
||
|
SubSystem = p[5];
|
||
|
G16(2, Machine);
|
||
|
G16(6, StrippedSize);
|
||
|
/*
|
||
|
G32(8, AddressOfEntryPoint);
|
||
|
G32(12, BaseOfCode);
|
||
|
G64(16, ImageBase);
|
||
|
*/
|
||
|
for (int i = 0; i < 2; i++)
|
||
|
{
|
||
|
CDataDir &dd = DataDir[i];
|
||
|
dd.Parse(p + 24 + i * 8);
|
||
|
if (dd.Size >= ((UInt32)1 << 28))
|
||
|
return false;
|
||
|
}
|
||
|
return
|
||
|
MY_FIND_VALUE(NPe::g_MachinePairs, Machine) &&
|
||
|
MY_FIND_VALUE(NPe::g_SubSystems, SubSystem);
|
||
|
}
|
||
|
|
||
|
API_FUNC_static_IsArc IsArc_Te(const Byte *p, size_t size)
|
||
|
{
|
||
|
if (size < 2)
|
||
|
return k_IsArc_Res_NEED_MORE;
|
||
|
if (p[0] != 'V' || p[1] != 'Z')
|
||
|
return k_IsArc_Res_NO;
|
||
|
if (size < kHeaderSize)
|
||
|
return k_IsArc_Res_NEED_MORE;
|
||
|
|
||
|
CHeader h;
|
||
|
if (!h.Parse(p))
|
||
|
return k_IsArc_Res_NO;
|
||
|
return k_IsArc_Res_YES;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
struct CSection
|
||
|
{
|
||
|
Byte Name[NPe::kNameSize];
|
||
|
|
||
|
UInt32 VSize;
|
||
|
UInt32 Va;
|
||
|
UInt32 PSize;
|
||
|
UInt32 Pa;
|
||
|
UInt32 Flags;
|
||
|
// UInt16 NumRelocs;
|
||
|
|
||
|
void Parse(const Byte *p)
|
||
|
{
|
||
|
memcpy(Name, p, NPe::kNameSize);
|
||
|
G32(8, VSize);
|
||
|
G32(12, Va);
|
||
|
G32(16, PSize);
|
||
|
G32(20, Pa);
|
||
|
// G32(p + 32, NumRelocs);
|
||
|
G32(36, Flags);
|
||
|
}
|
||
|
|
||
|
bool Check() const
|
||
|
{
|
||
|
return
|
||
|
Pa <= ((UInt32)1 << 30) &&
|
||
|
PSize <= ((UInt32)1 << 30);
|
||
|
}
|
||
|
|
||
|
void UpdateTotalSize(UInt32 &totalSize)
|
||
|
{
|
||
|
UInt32 t = Pa + PSize;
|
||
|
if (t > totalSize)
|
||
|
totalSize = t;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class CHandler:
|
||
|
public IInArchive,
|
||
|
public IInArchiveGetStream,
|
||
|
public IArchiveAllowTail,
|
||
|
public CMyUnknownImp
|
||
|
{
|
||
|
CRecordVector<CSection> _items;
|
||
|
CMyComPtr<IInStream> _stream;
|
||
|
UInt32 _totalSize;
|
||
|
bool _allowTail;
|
||
|
CHeader _h;
|
||
|
|
||
|
HRESULT Open2(IInStream *stream);
|
||
|
public:
|
||
|
MY_UNKNOWN_IMP3(IInArchive, IInArchiveGetStream, IArchiveAllowTail)
|
||
|
INTERFACE_IInArchive(;)
|
||
|
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
|
||
|
STDMETHOD(AllowTail)(Int32 allowTail);
|
||
|
CHandler(): _allowTail(false) {}
|
||
|
};
|
||
|
|
||
|
static const Byte kProps[] =
|
||
|
{
|
||
|
kpidPath,
|
||
|
kpidSize,
|
||
|
kpidVirtualSize,
|
||
|
kpidCharacts,
|
||
|
kpidOffset,
|
||
|
kpidVa
|
||
|
};
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
kpidSubSystem = kpidUserDefined,
|
||
|
// , kpidImageBase
|
||
|
};
|
||
|
|
||
|
static const CStatProp kArcProps[] =
|
||
|
{
|
||
|
// { NULL, kpidHeadersSize, VT_UI4 },
|
||
|
{ NULL, kpidCpu, VT_BSTR},
|
||
|
{ "Subsystem", kpidSubSystem, VT_BSTR },
|
||
|
// { "Image Base", kpidImageBase, VT_UI8 }
|
||
|
};
|
||
|
|
||
|
IMP_IInArchive_Props
|
||
|
IMP_IInArchive_ArcProps_WITH_NAME
|
||
|
|
||
|
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
||
|
{
|
||
|
COM_TRY_BEGIN
|
||
|
NCOM::CPropVariant prop;
|
||
|
switch (propID)
|
||
|
{
|
||
|
case kpidPhySize: prop = _totalSize; break;
|
||
|
case kpidCpu: PAIR_TO_PROP(NPe::g_MachinePairs, _h.Machine, prop); break;
|
||
|
case kpidSubSystem: PAIR_TO_PROP(NPe::g_SubSystems, _h.SubSystem, prop); break;
|
||
|
/*
|
||
|
case kpidImageBase: prop = _h.ImageBase; break;
|
||
|
case kpidAddressOfEntryPoint: prop = _h.AddressOfEntryPoint; break;
|
||
|
case kpidBaseOfCode: prop = _h.BaseOfCode; break;
|
||
|
*/
|
||
|
}
|
||
|
prop.Detach(value);
|
||
|
return S_OK;
|
||
|
COM_TRY_END
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
|
||
|
{
|
||
|
COM_TRY_BEGIN
|
||
|
NCOM::CPropVariant prop;
|
||
|
{
|
||
|
const CSection &item = _items[index];
|
||
|
switch (propID)
|
||
|
{
|
||
|
case kpidPath:
|
||
|
{
|
||
|
AString name;
|
||
|
NPe::GetName(item.Name, name);
|
||
|
prop = MultiByteToUnicodeString(name);
|
||
|
break;
|
||
|
}
|
||
|
case kpidSize:
|
||
|
case kpidPackSize: prop = (UInt64)item.PSize; break;
|
||
|
case kpidVirtualSize: prop = (UInt64)item.VSize; break;
|
||
|
case kpidOffset: prop = item.Pa; break;
|
||
|
case kpidVa: prop = item.Va; break;
|
||
|
case kpidCharacts: FLAGS_TO_PROP(NPe::g_SectFlags, item.Flags, prop); break;
|
||
|
}
|
||
|
}
|
||
|
prop.Detach(value);
|
||
|
return S_OK;
|
||
|
COM_TRY_END
|
||
|
}
|
||
|
|
||
|
HRESULT CHandler::Open2(IInStream *stream)
|
||
|
{
|
||
|
Byte h[kHeaderSize];
|
||
|
RINOK(ReadStream_FALSE(stream, h, kHeaderSize));
|
||
|
if (h[0] != 'V' || h[1] != 'Z')
|
||
|
return S_FALSE;
|
||
|
if (!_h.Parse(h))
|
||
|
return S_FALSE;
|
||
|
|
||
|
UInt32 headerSize = NPe::kSectionSize * (UInt32)_h.NumSections;
|
||
|
CByteArr buf(headerSize);
|
||
|
RINOK(ReadStream_FALSE(stream, buf, headerSize));
|
||
|
headerSize += kHeaderSize;
|
||
|
|
||
|
_totalSize = headerSize;
|
||
|
_items.ClearAndReserve(_h.NumSections);
|
||
|
for (UInt32 i = 0; i < _h.NumSections; i++)
|
||
|
{
|
||
|
CSection sect;
|
||
|
sect.Parse(buf + i * NPe::kSectionSize);
|
||
|
if (!_h.ConvertPa(sect.Pa))
|
||
|
return S_FALSE;
|
||
|
if (sect.Pa < headerSize)
|
||
|
return S_FALSE;
|
||
|
if (!sect.Check())
|
||
|
return S_FALSE;
|
||
|
_items.AddInReserved(sect);
|
||
|
sect.UpdateTotalSize(_totalSize);
|
||
|
}
|
||
|
|
||
|
if (!_allowTail)
|
||
|
{
|
||
|
UInt64 fileSize;
|
||
|
RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize));
|
||
|
if (fileSize > _totalSize)
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::Open(IInStream *inStream,
|
||
|
const UInt64 * /* maxCheckStartPosition */,
|
||
|
IArchiveOpenCallback * /* openArchiveCallback */)
|
||
|
{
|
||
|
COM_TRY_BEGIN
|
||
|
Close();
|
||
|
try
|
||
|
{
|
||
|
if (Open2(inStream) != S_OK)
|
||
|
return S_FALSE;
|
||
|
_stream = inStream;
|
||
|
}
|
||
|
catch(...) { return S_FALSE; }
|
||
|
return S_OK;
|
||
|
COM_TRY_END
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::Close()
|
||
|
{
|
||
|
_totalSize = 0;
|
||
|
_stream.Release();
|
||
|
_items.Clear();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
|
||
|
{
|
||
|
*numItems = _items.Size();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
|
||
|
Int32 testMode, IArchiveExtractCallback *extractCallback)
|
||
|
{
|
||
|
COM_TRY_BEGIN
|
||
|
bool allFilesMode = (numItems == (UInt32)(Int32)-1);
|
||
|
if (allFilesMode)
|
||
|
numItems = _items.Size();
|
||
|
if (numItems == 0)
|
||
|
return S_OK;
|
||
|
UInt64 totalSize = 0;
|
||
|
UInt32 i;
|
||
|
for (i = 0; i < numItems; i++)
|
||
|
totalSize += _items[allFilesMode ? i : indices[i]].PSize;
|
||
|
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);
|
||
|
|
||
|
CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
|
||
|
CMyComPtr<ISequentialInStream> inStream(streamSpec);
|
||
|
streamSpec->SetStream(_stream);
|
||
|
|
||
|
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 CSection &item = _items[index];
|
||
|
RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
|
||
|
currentTotalSize += item.PSize;
|
||
|
|
||
|
if (!testMode && !realOutStream)
|
||
|
continue;
|
||
|
RINOK(extractCallback->PrepareOperation(askMode));
|
||
|
int res = NExtract::NOperationResult::kDataError;
|
||
|
|
||
|
RINOK(_stream->Seek(item.Pa, STREAM_SEEK_SET, NULL));
|
||
|
streamSpec->Init(item.PSize);
|
||
|
RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
|
||
|
if (copyCoderSpec->TotalSize == item.PSize)
|
||
|
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 CSection &item = _items[index];
|
||
|
return CreateLimitedInStream(_stream, item.Pa, item.PSize, stream);
|
||
|
COM_TRY_END
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::AllowTail(Int32 allowTail)
|
||
|
{
|
||
|
_allowTail = IntToBool(allowTail);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static const Byte k_Signature[] = { 'V', 'Z' };
|
||
|
|
||
|
REGISTER_ARC_I(
|
||
|
"TE", "te", 0, 0xCF,
|
||
|
k_Signature,
|
||
|
0,
|
||
|
NArcInfoFlags::kPreArc,
|
||
|
IsArc_Te)
|
||
|
|
||
|
}
|
||
|
}
|