xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/PeHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // PeHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 // #include <stdio.h>
6 
7 #include "../../../C/CpuArch.h"
8 
9 #include "../../Common/DynamicBuffer.h"
10 #include "../../Common/ComTry.h"
11 #include "../../Common/IntToString.h"
12 #include "../../Common/StringConvert.h"
13 
14 #include "../../Windows/PropVariantUtils.h"
15 #include "../../Windows/TimeUtils.h"
16 
17 #include "../Common/LimitedStreams.h"
18 #include "../Common/ProgressUtils.h"
19 #include "../Common/RegisterArc.h"
20 #include "../Common/StreamObjects.h"
21 #include "../Common/StreamUtils.h"
22 
23 #include "../Compress/CopyCoder.h"
24 
25 #define Get16(p) GetUi16(p)
26 #define Get32(p) GetUi32(p)
27 #define Get64(p) GetUi64(p)
28 
29 #define G16(offs, v) v = Get16(p + (offs))
30 #define G32(offs, v) v = Get32(p + (offs))
31 #define G32_signed(offs, v) v = (Int32)Get32(p + (offs))
32 #define G64(offs, v) v = Get64(p + (offs))
33 
34 #define RINOZ(x) { int _tt_ = (x); if (_tt_ != 0) return _tt_; }
35 
36 using namespace NWindows;
37 
38 namespace NArchive {
39 namespace NPe {
40 
41 static const UInt32 k_Signature32 = 0x00004550;
42 
CalcCheckSum(ISequentialInStream * stream,UInt32 size,UInt32 excludePos,UInt32 & res)43 static HRESULT CalcCheckSum(ISequentialInStream *stream, UInt32 size, UInt32 excludePos, UInt32 &res)
44 {
45   const UInt32 kBufSizeMax = (UInt32)1 << 15;
46   UInt32 bufSize = kBufSizeMax;
47   CByteBuffer buffer(bufSize);
48   Byte *buf = buffer;
49   UInt32 sum = 0;
50   UInt32 pos = 0;
51   for (;;)
52   {
53     UInt32 rem = size - pos;
54     if (rem > bufSize)
55       rem = bufSize;
56     if (rem == 0)
57       break;
58     size_t processed = rem;
59     RINOK(ReadStream(stream, buf, &processed))
60 
61     for (unsigned j = 0; j < 4; j++)
62     {
63       UInt32 e = excludePos + j;
64       if (pos <= e)
65       {
66         e -= pos;
67         if (e < processed)
68           buf[e] = 0;
69       }
70     }
71 
72     const unsigned kStep = (1 << 4);
73     {
74       for (size_t i = processed; (i & (kStep - 1)) != 0; i++)
75         buf[i] = 0;
76     }
77     {
78       const Byte *buf2 = buf;
79       const Byte *bufLimit = buf + processed;
80       UInt64 sum2 = 0;
81       for (; buf2 < bufLimit; buf2 += kStep)
82       {
83         UInt64 sum3 = (UInt64)Get32(buf2)
84             + Get32(buf2 + 4)
85             + Get32(buf2 + 8)
86             + Get32(buf2 + 12);
87         sum2 += sum3;
88       }
89       sum2 = (UInt32)(sum2) + (UInt64)(sum2 >> 32);
90       UInt32 sum3 = ((UInt32)sum2 + (UInt32)(sum2 >> 32));
91       sum += (sum3 & 0xFFFF) + (sum3 >> 16);
92       sum = (sum & 0xFFFF) + (sum >> 16);
93       sum = (sum & 0xFFFF) + (sum >> 16);
94     }
95 
96     pos += (UInt32)processed;
97     if (rem != processed)
98       break;
99   }
100   res = sum + pos;
101   return S_OK;
102 }
103 
104 
105 struct CVersion
106 {
107   UInt16 Major;
108   UInt16 Minor;
109 
ParseNArchive::NPe::CVersion110   void Parse(const Byte *p)
111   {
112     G16(0, Major);
113     G16(2, Minor);
114   }
115   void ToProp(NCOM::CPropVariant &prop);
116 };
117 
ToProp(NCOM::CPropVariant & prop)118 void CVersion::ToProp(NCOM::CPropVariant &prop)
119 {
120   char sz[32];
121   ConvertUInt32ToString(Major, sz);
122   unsigned len = MyStringLen(sz);
123   sz[len] = '.';
124   ConvertUInt32ToString(Minor, sz + len + 1);
125   prop = sz;
126 }
127 
128 static const unsigned kCoffHeaderSize = 20;
129 static const unsigned kPeHeaderSize = 4 + kCoffHeaderSize;
130 static const unsigned k_OptHeader32_Size_MIN = 96;
131 static const unsigned k_OptHeader64_Size_MIN = 112;
132 
133 static const UInt32 PE_IMAGE_FILE_DLL  = (1 << 13);
134 
135 struct CHeader
136 {
137   UInt16 Machine;
138   UInt16 NumSections;
139   UInt32 Time;
140   UInt32 PointerToSymbolTable;
141   UInt32 NumSymbols;
142   UInt16 OptHeaderSize;
143   UInt16 Flags;
144 
145   void ParseBase(const Byte *p);
146   bool ParseCoff(const Byte *p);
147   bool ParsePe(const Byte *p);
IsDllNArchive::NPe::CHeader148   bool IsDll() const { return (Flags & PE_IMAGE_FILE_DLL) != 0; }
149 };
150 
ParseBase(const Byte * p)151 void CHeader::ParseBase(const Byte *p)
152 {
153   G16( 0, Machine);
154   G16( 2, NumSections);
155   G32( 4, Time);
156   G32( 8, PointerToSymbolTable);
157   G32(12, NumSymbols);
158   G16(16, OptHeaderSize);
159   G16(18, Flags);
160 }
161 
ParsePe(const Byte * p)162 bool CHeader::ParsePe(const Byte *p)
163 {
164   if (Get32(p) != k_Signature32)
165     return false;
166   ParseBase(p + 4);
167   return OptHeaderSize >= k_OptHeader32_Size_MIN;
168 }
169 
170 struct CDirLink
171 {
172   UInt32 Va;
173   UInt32 Size;
174 
CDirLinkNArchive::NPe::CDirLink175   CDirLink(): Va(0), Size(0) {}
ParseNArchive::NPe::CDirLink176   void Parse(const Byte *p)
177   {
178     G32(0, Va);
179     G32(4, Size);
180   }
181 };
182 
183 
184 // IMAGE_DIRECTORY_ENTRY_*
185 static const char * const g_Dir_Names[] =
186 {
187     "EXPORT"
188   , "IMPORT"
189   , "RESOURCE"
190   , "EXCEPTION"
191   , "SECURITY"
192   , "BASERELOC"
193   , "DEBUG"
194   , "ARCHITECTURE" // "COPYRIGHT"
195   , "GLOBALPTR"
196   , "TLS"
197   , "LOAD_CONFIG"
198   , "BOUND_IMPORT"
199   , "IAT"
200   , "DELAY_IMPORT"
201   , "COM_DESCRIPTOR"
202 };
203 
204 enum
205 {
206   kDirLink_EXCEPTION = 3,
207   kDirLink_Certificate = 4,
208   kDirLink_BASERELOC = 5,
209   kDirLink_Debug = 6
210 };
211 
212 static const UInt32 kNumDirItemsMax = 16;
213 
214 struct CDebugEntry
215 {
216   UInt32 Flags;
217   UInt32 Time;
218   CVersion Ver;
219   UInt32 Type;
220   UInt32 Size;
221   UInt32 Va;
222   UInt32 Pa;
223 
ParseNArchive::NPe::CDebugEntry224   void Parse(const Byte *p)
225   {
226     G32(0, Flags);
227     G32(4, Time);
228     Ver.Parse(p + 8);
229     G32(12, Type);
230     G32(16, Size);
231     G32(20, Va);
232     G32(24, Pa);
233   }
234 };
235 
236 static const UInt32 k_CheckSum_Field_Offset = 64;
237 
238 static const UInt32 PE_OptHeader_Magic_32 = 0x10B;
239 static const UInt32 PE_OptHeader_Magic_64 = 0x20B;
240 
241 static const UInt32 k_SubSystems_EFI_First = 10;
242 static const UInt32 k_SubSystems_EFI_Last = 13;
243 
244 struct COptHeader
245 {
246   UInt16 Magic;
247   Byte LinkerVerMajor;
248   Byte LinkerVerMinor;
249 
250   UInt32 CodeSize;
251   UInt32 InitDataSize;
252   UInt32 UninitDataSize;
253 
254   // UInt32 AddressOfEntryPoint;
255   // UInt32 BaseOfCode; //  VA(.text) == 0x1000 in most cases
256   // UInt32 BaseOfData32;
257   UInt64 ImageBase;
258 
259   UInt32 SectAlign;
260   UInt32 FileAlign;
261 
262   CVersion OsVer;
263   CVersion ImageVer;
264   CVersion SubsysVer;
265 
266   UInt32 ImageSize;
267   UInt32 HeadersSize;
268   UInt32 CheckSum;
269   UInt16 SubSystem;
270   UInt16 DllCharacts;
271 
272   UInt64 StackReserve;
273   UInt64 StackCommit;
274   UInt64 HeapReserve;
275   UInt64 HeapCommit;
276 
277   UInt32 NumDirItems;
278   CDirLink DirItems[kNumDirItemsMax];
279 
Is64BitNArchive::NPe::COptHeader280   bool Is64Bit() const { return Magic == PE_OptHeader_Magic_64; }
281   bool Parse(const Byte *p, UInt32 size);
282 
GetNumFileAlignBitsNArchive::NPe::COptHeader283   int GetNumFileAlignBits() const
284   {
285     for (unsigned i = 0; i < 32; i++)
286       if (((UInt32)1 << i) == FileAlign)
287         return (int)i;
288     return -1;
289   }
290 
IsSybSystem_EFINArchive::NPe::COptHeader291   bool IsSybSystem_EFI() const
292   {
293     return
294         SubSystem >= k_SubSystems_EFI_First &&
295         SubSystem <= k_SubSystems_EFI_Last;
296   }
297 };
298 
299 // size is 16-bit
Parse(const Byte * p,UInt32 size)300 bool COptHeader::Parse(const Byte *p, UInt32 size)
301 {
302   if (size < k_OptHeader32_Size_MIN)
303     return false;
304   Magic = Get16(p);
305   switch (Magic)
306   {
307     case PE_OptHeader_Magic_32:
308     case PE_OptHeader_Magic_64:
309       break;
310     default:
311       return false;
312   }
313   LinkerVerMajor = p[2];
314   LinkerVerMinor = p[3];
315 
316   G32( 4, CodeSize);
317   G32( 8, InitDataSize);
318   G32(12, UninitDataSize);
319   // G32(16, AddressOfEntryPoint);
320   // G32(20, BaseOfCode);
321 
322   G32(32, SectAlign);
323   G32(36, FileAlign);
324 
325   OsVer.Parse(p + 40);
326   ImageVer.Parse(p + 44);
327   SubsysVer.Parse(p + 48);
328 
329   // reserved = Get32(p + 52);
330 
331   G32(56, ImageSize);
332   G32(60, HeadersSize);
333   G32(64, CheckSum);
334   G16(68, SubSystem);
335   G16(70, DllCharacts);
336 
337   UInt32 pos;
338   if (Is64Bit())
339   {
340     if (size < k_OptHeader64_Size_MIN)
341       return false;
342     // BaseOfData32 = 0;
343     G64(24, ImageBase);
344     G64(72, StackReserve);
345     G64(80, StackCommit);
346     G64(88, HeapReserve);
347     G64(96, HeapCommit);
348     pos = 108;
349   }
350   else
351   {
352     // G32(24, BaseOfData32);
353     G32(28, ImageBase);
354     G32(72, StackReserve);
355     G32(76, StackCommit);
356     G32(80, HeapReserve);
357     G32(84, HeapCommit);
358     pos = 92;
359   }
360 
361   UInt32 numDirItems;
362   G32(pos, numDirItems);
363   NumDirItems = numDirItems;
364   if (numDirItems > (1 << 13))
365     return false;
366   pos += 4;
367   if (pos + 8 * numDirItems > size)
368     return false;
369   memset((void *)DirItems, 0, sizeof(DirItems));
370   if (numDirItems > kNumDirItemsMax)
371       numDirItems = kNumDirItemsMax;
372   for (UInt32 i = 0; i < numDirItems; i++)
373     DirItems[i].Parse(p + pos + i * 8);
374   return true;
375 }
376 
377 static const UInt32 kSectionSize = 40;
378 
379 struct CSection
380 {
381   AString Name;
382 
383   UInt32 ExtractSize;
384   UInt32 VSize;
385   UInt32 Va;
386   UInt32 PSize;
387   UInt32 Pa;
388   UInt32 Flags;
389   UInt32 Time;
390   // UInt16 NumRelocs; // is set to zero for executable images
391   bool IsRealSect;
392   bool IsDebug;
393   bool IsAdditionalSection;
394 
CSectionNArchive::NPe::CSection395   CSection():
396     ExtractSize(0),
397     IsRealSect(false),
398     IsDebug(false),
399     IsAdditionalSection(false)
400     // , NumRelocs(0)
401     {}
402 
Set_Size_for_allNArchive::NPe::CSection403   void Set_Size_for_all(UInt32 size)
404   {
405     PSize = VSize = ExtractSize = size;
406   }
407 
GetSize_ExtractNArchive::NPe::CSection408   UInt32 GetSize_Extract() const
409   {
410     return ExtractSize;
411   }
412 
UpdateTotalSizeNArchive::NPe::CSection413   void UpdateTotalSize(UInt32 &totalSize) const
414   {
415     const UInt32 t = Pa + PSize;
416     if (totalSize < t)
417         totalSize = t;
418   }
419 
420   void Parse(const Byte *p);
421 
CompareNArchive::NPe::CSection422   int Compare(const CSection &s) const
423   {
424     RINOZ(MyCompare(Pa, s.Pa))
425     const UInt32 size1 = GetSize_Extract();
426     const UInt32 size2 = s.GetSize_Extract();
427     return MyCompare(size1, size2);
428   }
429 };
430 
431 static const unsigned kNameSize = 8;
432 
GetName(const Byte * name,AString & res)433 static void GetName(const Byte *name, AString &res)
434 {
435   res.SetFrom_CalcLen((const char *)name, kNameSize);
436 }
437 
Parse(const Byte * p)438 void CSection::Parse(const Byte *p)
439 {
440   GetName(p, Name);
441   G32( 8, VSize);
442   G32(12, Va);
443   G32(16, PSize);
444   G32(20, Pa);
445   // G16(32, NumRelocs);
446   G32(36, Flags);
447   // v24.08: we extract only useful data (without extra padding bytes).
448   // VSize == 0 is not expected, but we support that case too.
449   // return (VSize && VSize < PSize) ? VSize : PSize;
450   ExtractSize = (VSize && VSize < PSize) ? VSize : PSize;
451 }
452 
453 
454 
455 // IMAGE_FILE_*
456 
457 static const CUInt32PCharPair g_HeaderCharacts[] =
458 {
459   {  1, "Executable" },
460   { 13, "DLL" },
461   {  8, "32-bit" },
462   {  5, "LargeAddress" },
463   {  0, "NoRelocs" },
464   {  2, "NoLineNums" },
465   {  3, "NoLocalSyms" },
466   {  4, "AggressiveWsTrim" },
467   {  9, "NoDebugInfo" },
468   { 10, "RemovableRun" },
469   { 11, "NetRun" },
470   { 12, "System" },
471   { 14, "UniCPU" },
472   {  7, "Little-Endian" },
473   { 15, "Big-Endian" }
474 };
475 
476 // IMAGE_DLLCHARACTERISTICS_*
477 
478 static const char * const g_DllCharacts[] =
479 {
480     NULL
481   , NULL
482   , NULL
483   , NULL
484   , NULL
485   , "HighEntropyVA"
486   , "Relocated"
487   , "Integrity"
488   , "NX-Compatible"
489   , "NoIsolation"
490   , "NoSEH"
491   , "NoBind"
492   , "AppContainer"
493   , "WDM"
494   , "GuardCF"
495   , "TerminalServerAware"
496 };
497 
498 
499 // IMAGE_SCN_* constants:
500 
501 static const char * const g_SectFlags[] =
502 {
503     NULL
504   , NULL
505   , NULL
506   , "NoPad"
507   , NULL
508   , "Code"
509   , "InitializedData"
510   , "UninitializedData"
511   , "Other"
512   , "Comments"
513   , NULL // OVER
514   , "Remove"
515   , "COMDAT"
516   , NULL
517   , "NO_DEFER_SPEC_EXC"
518   , "GP" // MEM_FARDATA
519   , NULL // SYSHEAP
520   , "PURGEABLE" // 16BIT
521   , "LOCKED"
522   , "PRELOAD"
523   , NULL
524   , NULL
525   , NULL
526   , NULL
527   , "ExtendedRelocations"
528   , "Discardable"
529   , "NotCached"
530   , "NotPaged"
531   , "Shared"
532   , "Execute"
533   , "Read"
534   , "Write"
535 };
536 
537 static const CUInt32PCharPair g_MachinePairs[] =
538 {
539   { 0x014C, "x86" },
540   { 0x014D, "I860" },
541   { 0x0162, "MIPS-R3000" },
542   { 0x0166, "MIPS-R4000" },
543   { 0x0168, "MIPS-R10000" },
544   { 0x0169, "MIPS-V2" },
545   { 0x0184, "Alpha" },
546   { 0x01A2, "SH3" },
547   { 0x01A3, "SH3-DSP" },
548   { 0x01A4, "SH3E" },
549   { 0x01A6, "SH4" },
550   { 0x01A8, "SH5" },
551   { 0x01C0, "ARM" },
552   { 0x01C2, "ARM-Thumb" },
553   { 0x01C4, "ARM-NT" },
554   { 0x01D3, "AM33" },
555   { 0x01F0, "PPC" },
556   { 0x01F1, "PPC-FP" },
557   { 0x01F2, "PPC-BE" },
558   { 0x0200, "IA-64" },
559   { 0x0266, "MIPS-16" },
560   { 0x0284, "Alpha-64" },
561   { 0x0366, "MIPS-FPU" },
562   { 0x0466, "MIPS-FPU16" },
563   { 0x0520, "TriCore" },
564   { 0x0CEF, "CEF" },
565   { 0x0EBC, "EFI" },
566   { 0x5032, "RISCV32" },
567   { 0x5064, "RISCV64" },
568 //  { 0x5128, "RISCV128" },
569   { 0x6232, "LOONGARCH32" },
570   { 0x6264, "LOONGARCH64" },
571   { 0x8664, "x64" },
572   { 0x9041, "M32R" },
573   { 0xA641, "ARM64EC" },
574   { 0xA64e, "ARM64X" },
575   { 0xAA64, "ARM64" },
576   { 0xC0EE, "CEE" }
577 };
578 
579 static const char * const g_SubSystems[] =
580 {
581     "Unknown"
582   , "Native"
583   , "Windows GUI"
584   , "Windows CUI"
585   , NULL // "Old Windows CE"
586   , "OS2"
587   , NULL
588   , "Posix"
589   , "Win9x"
590   , "Windows CE"
591   , "EFI"
592   , "EFI Boot"
593   , "EFI Runtime"
594   , "EFI ROM"
595   , "XBOX"
596   , NULL
597   , "Windows Boot"
598   , "XBOX Catalog" // 17
599 };
600 
601 static const char * const g_ResTypes[] =
602 {
603     NULL
604   , "CURSOR"
605   , "BITMAP"
606   , "ICON"
607   , "MENU"
608   , "DIALOG"
609   , "STRING"
610   , "FONTDIR"
611   , "FONT"
612   , "ACCELERATOR"
613   , "RCDATA"
614   , "MESSAGETABLE"
615   , "GROUP_CURSOR"
616   , NULL
617   , "GROUP_ICON"
618   , NULL
619   , "VERSION"
620   , "DLGINCLUDE"
621   , NULL
622   , "PLUGPLAY"
623   , "VXD"
624   , "ANICURSOR"
625   , "ANIICON"
626   , "HTML"
627   , "MANIFEST"
628 };
629 
630 static const UInt32 kFlag = (UInt32)1 << 31;
631 static const UInt32 kMask = ~kFlag;
632 
633 struct CTableItem
634 {
635   UInt32 Offset;
636   UInt32 ID;
637 };
638 
639 
640 static const UInt32 kBmpHeaderSize = 14;
641 static const UInt32 kIconHeaderSize = 22;
642 
643 struct CResItem
644 {
645   UInt32 Type;
646   UInt32 ID;
647   UInt32 Lang;
648 
649   UInt32 Size;
650   UInt32 Offset;
651 
652   UInt32 HeaderSize;
653   Byte Header[kIconHeaderSize]; // it must be enough for max size header.
654   bool Enabled;
655 
IsNameEqualNArchive::NPe::CResItem656   bool IsNameEqual(const CResItem &item) const { return Lang == item.Lang; }
GetSizeNArchive::NPe::CResItem657   UInt32 GetSize() const { return Size + HeaderSize; }
IsBmpNArchive::NPe::CResItem658   bool IsBmp() const { return Type == 2; }
IsIconNArchive::NPe::CResItem659   bool IsIcon() const { return Type == 3; }
IsStringNArchive::NPe::CResItem660   bool IsString() const { return Type == 6; }
IsRcDataNArchive::NPe::CResItem661   bool IsRcData() const { return Type == 10; }
IsVersionNArchive::NPe::CResItem662   bool IsVersion() const { return Type == 16; }
IsRcDataOrUnknownNArchive::NPe::CResItem663   bool IsRcDataOrUnknown() const { return IsRcData() || Type > 64; }
664 };
665 
666 struct CTextFile
667 {
668   CByteDynamicBuffer Buf;
669 
FinalSizeNArchive::NPe::CTextFile670   size_t FinalSize() const { return Buf.GetPos(); }
671 
672   void AddChar(char c);
673   void AddWChar(UInt16 c);
674   void AddWChar_Smart(UInt16 c);
675   void NewLine();
676   void AddString(const char *s);
677   void AddSpaces(int num);
AddBytesNArchive::NPe::CTextFile678   void AddBytes(const Byte *p, size_t size)
679   {
680     Buf.AddData(p, size);
681   }
682 
OpenBlockNArchive::NPe::CTextFile683   void OpenBlock(int num)
684   {
685     AddSpaces(num);
686     AddChar('{');
687     NewLine();
688   }
CloseBlockNArchive::NPe::CTextFile689   void CloseBlock(int num)
690   {
691     AddSpaces(num);
692     AddChar('}');
693     NewLine();
694   }
695 };
696 
AddChar(char c)697 void CTextFile::AddChar(char c)
698 {
699   Byte *p = Buf.GetCurPtrAndGrow(2);
700   p[0] = (Byte)c;
701   p[1] = 0;
702 }
703 
AddWChar(UInt16 c)704 void CTextFile::AddWChar(UInt16 c)
705 {
706   Byte *p = Buf.GetCurPtrAndGrow(2);
707   SetUi16(p, c)
708 }
709 
AddWChar_Smart(UInt16 c)710 void CTextFile::AddWChar_Smart(UInt16 c)
711 {
712   if (c == '\n')
713   {
714     AddChar('\\');
715     c = 'n';
716   }
717   AddWChar(c);
718 }
719 
NewLine()720 void CTextFile::NewLine()
721 {
722   AddChar(0x0D);
723   AddChar(0x0A);
724 }
725 
AddString(const char * s)726 void CTextFile::AddString(const char *s)
727 {
728   for (;; s++)
729   {
730     char c = *s;
731     if (c == 0)
732       return;
733     AddChar(c);
734   }
735 }
736 
AddSpaces(int num)737 void CTextFile::AddSpaces(int num)
738 {
739   for (int i = 0; i < num; i++)
740     AddChar(' ');
741 }
742 
743 struct CStringItem: public CTextFile
744 {
745   UInt32 Lang;
746 };
747 
748 struct CByteBuffer_WithLang: public CByteBuffer
749 {
750   UInt32 Lang;
751 };
752 
753 
754 struct CMixItem
755 {
756   int SectionIndex;
757   int ResourceIndex;
758   int StringIndex;
759   int VersionIndex;
760 
CMixItemNArchive::NPe::CMixItem761   CMixItem(): SectionIndex(-1), ResourceIndex(-1), StringIndex(-1), VersionIndex(-1) {}
IsSectionItemNArchive::NPe::CMixItem762   bool IsSectionItem() const { return ResourceIndex < 0 && StringIndex < 0 && VersionIndex < 0; }
763 };
764 
765 struct CUsedBitmap
766 {
767   CByteBuffer Buf;
768 public:
AllocNArchive::NPe::CUsedBitmap769   void Alloc(size_t size)
770   {
771     size = (size + 7) / 8;
772     Buf.Alloc(size);
773     memset(Buf, 0, size);
774   }
775 
FreeNArchive::NPe::CUsedBitmap776   void Free()
777   {
778     Buf.Free();
779   }
780 
SetRangeNArchive::NPe::CUsedBitmap781   bool SetRange(size_t from, unsigned size)
782   {
783     for (unsigned i = 0; i < size; i++)
784     {
785       size_t pos = (from + i) >> 3;
786       Byte mask = (Byte)(1 << ((from + i) & 7));
787       Byte b = Buf[pos];
788       if ((b & mask) != 0)
789         return false;
790       Buf[pos] = (Byte)(b | mask);
791     }
792     return true;
793   }
794 };
795 
796 struct CStringKeyValue
797 {
798   UString Key;
799   UString Value;
800 };
801 
802 
803 Z7_CLASS_IMP_CHandler_IInArchive_2(
804   IInArchiveGetStream,
805   IArchiveAllowTail
806 )
807   CMyComPtr<IInStream> _stream;
808   CObjectVector<CSection> _sections;
809   CHeader _header;
810   UInt32 _totalSize;
811   Int32 _mainSubfile;
812 
813   CRecordVector<CMixItem> _mixItems;
814   CRecordVector<CResItem> _items;
815   CObjectVector<CStringItem> _strings;
816   CObjectVector<CByteBuffer_WithLang> _versionFiles;
817   UString _versionFullString;
818   UString _versionShortString;
819   UString _originalFilename;
820   CObjectVector<CStringKeyValue> _versionKeys;
821 
822   CByteBuffer _buf;
823   bool _oneLang;
824   UString _resourcesPrefix;
825   CUsedBitmap _usedRes;
826   // bool _parseResources;
827   bool _checksumError;
828   bool _sectionsError;
829 
IsOpt() const830   bool IsOpt() const { return _header.OptHeaderSize != 0; }
831 
832   COptHeader _optHeader;
833 
834   bool _coffMode;
835   bool _allowTail;
836 
837   HRESULT LoadDebugSections(IInStream *stream, bool &thereIsSection);
838   HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
839 
840   void AddResNameToString(UString &s, UInt32 id) const;
841   void AddLangPrefix(UString &s, UInt32 lang) const;
842   HRESULT ReadString(UInt32 offset, UString &dest) const;
843   HRESULT ReadTable(UInt32 offset, CRecordVector<CTableItem> &items);
844   bool ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size);
845   HRESULT OpenResources(unsigned sectIndex, IInStream *stream, IArchiveOpenCallback *callback);
846   void CloseResources();
847 
848 
CheckItem(const CSection & sect,const CResItem & item,size_t offset) const849   bool CheckItem(const CSection &sect, const CResItem &item, size_t offset) const
850   {
851     return item.Offset >= sect.Va && offset <= _buf.Size() && _buf.Size() - offset >= item.Size;
852   }
853 
854 public:
855   CHandler(bool coffMode = false):
856         _coffMode(coffMode),
857         _allowTail(coffMode)
858         {}
859 };
860 
861 
862 enum
863 {
864   kpidSectAlign = kpidUserDefined,
865   kpidFileAlign,
866   kpidLinkerVer,
867   kpidOsVer,
868   kpidImageVer,
869   kpidSubsysVer,
870   kpidCodeSize,
871   kpidImageSize,
872   kpidInitDataSize,
873   kpidUnInitDataSize,
874   kpidHeadersSizeUnInitDataSize,
875   kpidSubSystem,
876   kpidDllCharacts,
877   kpidStackReserve,
878   kpidStackCommit,
879   kpidHeapReserve,
880   kpidHeapCommit
881   // , kpidImageBase
882   // , kpidAddressOfEntryPoint
883   // , kpidBaseOfCode
884   // , kpidBaseOfData32
885 };
886 
887 static const CStatProp kArcProps[] =
888 {
889   // { NULL, kpidWarning, VT_BSTR},
890   { NULL, kpidCpu, VT_BSTR},
891   { NULL, kpidBit64, VT_BOOL},
892   { NULL, kpidCharacts, VT_BSTR},
893   { NULL, kpidCTime, VT_FILETIME},
894   { NULL, kpidHeadersSize, VT_UI4},
895   { NULL, kpidChecksum, VT_UI4},
896   { NULL, kpidName, VT_BSTR},
897 
898   { "Image Size", kpidImageSize, VT_UI4},
899   { "Section Alignment", kpidSectAlign, VT_UI4},
900   { "File Alignment", kpidFileAlign, VT_UI4},
901   { "Code Size", kpidCodeSize, VT_UI4},
902   { "Initialized Data Size", kpidInitDataSize, VT_UI4},
903   { "Uninitialized Data Size", kpidUnInitDataSize, VT_UI4},
904   { "Linker Version", kpidLinkerVer, VT_BSTR},
905   { "OS Version", kpidOsVer, VT_BSTR},
906   { "Image Version", kpidImageVer, VT_BSTR},
907   { "Subsystem Version", kpidSubsysVer, VT_BSTR},
908   { "Subsystem", kpidSubSystem, VT_BSTR},
909   { "DLL Characteristics", kpidDllCharacts, VT_BSTR},
910   { "Stack Reserve", kpidStackReserve, VT_UI8},
911   { "Stack Commit", kpidStackCommit, VT_UI8},
912   { "Heap Reserve", kpidHeapReserve, VT_UI8},
913   { "Heap Commit", kpidHeapCommit, VT_UI8},
914   { NULL, kpidVa, VT_UI8 }, // "Image Base", kpidImageBase, VT_UI8
915   { NULL, kpidComment, VT_BSTR}
916 
917   // , { "Address Of Entry Point", kpidAddressOfEntryPoint, VT_UI8}
918   // , { "Base Of Code", kpidBaseOfCode, VT_UI8}
919   // , { "Base Of Data", kpidBaseOfData32, VT_UI8}
920 };
921 
922 // #define kpid_NumRelocs 250
923 
924 static const Byte kProps[] =
925 {
926   kpidPath,
927   kpidSize,
928   kpidPackSize,
929   kpidVirtualSize,
930   kpidCharacts,
931   kpidOffset,
932   kpidVa
933   // , kpid_NumRelocs
934 };
935 
936 IMP_IInArchive_Props
937 IMP_IInArchive_ArcProps_WITH_NAME
938 
939 static void TimeToProp(UInt32 unixTime, NCOM::CPropVariant &prop)
940 {
941   if (unixTime != 0)
942     PropVariant_SetFrom_UnixTime(prop, unixTime);
943 }
944 
945 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
946 {
947   COM_TRY_BEGIN
948   NCOM::CPropVariant prop;
949   switch (propID)
950   {
951     case kpidPhySize: prop = _totalSize; break;
952     case kpidComment:
953     {
954       UString s (_versionFullString);
955       s.Add_LF();
956       s += "Data Directories: ";
957       s.Add_UInt32(_optHeader.NumDirItems);
958       s.Add_LF();
959       s.Add_Char('{');
960       s.Add_LF();
961       for (unsigned i = 0; i < _optHeader.NumDirItems
962           && i < Z7_ARRAY_SIZE(_optHeader.DirItems); i++)
963       {
964         const CDirLink &di = _optHeader.DirItems[i];
965         if (di.Va == 0 && di.Size == 0)
966           continue;
967         s += "index=";
968         s.Add_UInt32(i);
969 
970         if (i < Z7_ARRAY_SIZE(g_Dir_Names))
971         {
972           s += " name=";
973           s += g_Dir_Names[i];
974         }
975         s += " VA=0x";
976         char temp[16];
977         ConvertUInt32ToHex(di.Va, temp);
978         s += temp;
979         s += " Size=";
980         s.Add_UInt32(di.Size);
981         s.Add_LF();
982       }
983       s.Add_Char('}');
984       s.Add_LF();
985       prop = s;
986       break;
987     }
988     case kpidShortComment:
989       if (!_versionShortString.IsEmpty())
990         prop = _versionShortString;
991       else
992       {
993         PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop);
994       }
995       break;
996 
997     case kpidName: if (!_originalFilename.IsEmpty()) prop = _originalFilename; break;
998 
999     // case kpidIsSelfExe: prop = !_header.IsDll(); break;
1000     // case kpidError:
1001     case kpidWarning: if (_checksumError) prop = "Checksum error"; break;
1002 
1003     case kpidWarningFlags:
1004     {
1005       UInt32 v = 0;
1006       if (_sectionsError) v |= kpv_ErrorFlags_HeadersError;
1007       if (v != 0)
1008         prop = v;
1009       break;
1010     }
1011 
1012     case kpidCpu: PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop); break;
1013     case kpidMTime:
1014     case kpidCTime: TimeToProp(_header.Time, prop); break;
1015     case kpidCharacts: FLAGS_TO_PROP(g_HeaderCharacts, _header.Flags, prop); break;
1016     case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break;
1017 
1018     default:
1019     if (IsOpt())
1020     switch (propID)
1021     {
1022 
1023     case kpidSectAlign: prop = _optHeader.SectAlign; break;
1024     case kpidFileAlign: prop = _optHeader.FileAlign; break;
1025     case kpidLinkerVer:
1026     {
1027       CVersion v = { _optHeader.LinkerVerMajor, _optHeader.LinkerVerMinor };
1028       v.ToProp(prop);
1029       break;
1030     }
1031 
1032     case kpidOsVer: _optHeader.OsVer.ToProp(prop); break;
1033     case kpidImageVer: _optHeader.ImageVer.ToProp(prop); break;
1034     case kpidSubsysVer: _optHeader.SubsysVer.ToProp(prop); break;
1035     case kpidCodeSize: prop = _optHeader.CodeSize; break;
1036     case kpidInitDataSize: prop = _optHeader.InitDataSize; break;
1037     case kpidUnInitDataSize: prop = _optHeader.UninitDataSize; break;
1038     case kpidImageSize: prop = _optHeader.ImageSize; break;
1039     case kpidHeadersSize: prop = _optHeader.HeadersSize; break;
1040     case kpidChecksum: prop = _optHeader.CheckSum; break;
1041 
1042     case kpidExtension:
1043       if (_header.IsDll())
1044         prop = "dll";
1045       else if (_optHeader.IsSybSystem_EFI())
1046         prop = "efi";
1047       break;
1048 
1049     case kpidBit64: if (_optHeader.Is64Bit()) prop = true; break;
1050     case kpidSubSystem: TYPE_TO_PROP(g_SubSystems, _optHeader.SubSystem, prop); break;
1051 
1052     case kpidDllCharacts: FLAGS_TO_PROP(g_DllCharacts, _optHeader.DllCharacts, prop); break;
1053     case kpidStackReserve: prop = _optHeader.StackReserve; break;
1054     case kpidStackCommit: prop = _optHeader.StackCommit; break;
1055     case kpidHeapReserve: prop = _optHeader.HeapReserve; break;
1056     case kpidHeapCommit: prop = _optHeader.HeapCommit; break;
1057     case kpidVa: prop = _optHeader.ImageBase; break; // kpidImageBase:
1058     // case kpidAddressOfEntryPoint: prop = _optHeader.AddressOfEntryPoint; break;
1059     // case kpidBaseOfCode: prop = _optHeader.BaseOfCode; break;
1060     // case kpidBaseOfData32: if (!_optHeader.Is64Bit()) prop = _optHeader.BaseOfData32; break;
1061   }
1062   }
1063   prop.Detach(value);
1064   return S_OK;
1065   COM_TRY_END
1066 }
1067 
1068 HRESULT CHandler::ReadString(UInt32 offset, UString &dest) const
1069 {
1070   if ((offset & 1) != 0 || offset >= _buf.Size())
1071     return S_FALSE;
1072   size_t rem = _buf.Size() - offset;
1073   if (rem < 2)
1074     return S_FALSE;
1075   unsigned len = Get16(_buf + offset);
1076   if ((rem - 2) / 2 < len)
1077     return S_FALSE;
1078   dest.Empty();
1079   wchar_t *destBuf = dest.GetBuf(len);
1080   offset += 2;
1081   const Byte *src = _buf + offset;
1082   unsigned i;
1083   for (i = 0; i < len; i++)
1084   {
1085     wchar_t c = (wchar_t)Get16(src + i * 2);
1086     if (c == 0)
1087       break;
1088     destBuf[i] = c;
1089   }
1090   destBuf[i] = 0;
1091   dest.ReleaseBuf_SetLen(i);
1092   return S_OK;
1093 }
1094 
1095 void CHandler::AddResNameToString(UString &s, UInt32 id) const
1096 {
1097   if ((id & kFlag) != 0)
1098   {
1099     UString name;
1100     if (ReadString(id & kMask, name) == S_OK)
1101     {
1102       const wchar_t *str = L"[]";
1103       if (name.Len() > 1 && name[0] == '"' && name.Back() == '"')
1104       {
1105         if (name.Len() != 2)
1106         {
1107           name.DeleteBack();
1108           str = name.Ptr(1);
1109         }
1110       }
1111       else if (!name.IsEmpty())
1112         str = name;
1113       s += str;
1114       return;
1115     }
1116   }
1117   s.Add_UInt32(id);
1118 }
1119 
1120 void CHandler::AddLangPrefix(UString &s, UInt32 lang) const
1121 {
1122   if (!_oneLang)
1123   {
1124     AddResNameToString(s, lang);
1125     s.Add_PathSepar();
1126   }
1127 }
1128 
1129 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
1130 {
1131   COM_TRY_BEGIN
1132   NCOM::CPropVariant prop;
1133   const CMixItem &mixItem = _mixItems[index];
1134   if (mixItem.StringIndex >= 0)
1135   {
1136     const CStringItem &item = _strings[mixItem.StringIndex];
1137     switch (propID)
1138     {
1139       case kpidPath:
1140       {
1141         UString s = _resourcesPrefix;
1142         AddLangPrefix(s, item.Lang);
1143         s += "string.txt";
1144         prop = s;
1145         break;
1146       }
1147       case kpidSize:
1148       case kpidPackSize:
1149         prop = (UInt64)item.FinalSize(); break;
1150     }
1151   }
1152   else if (mixItem.VersionIndex >= 0)
1153   {
1154     const CByteBuffer_WithLang &item = _versionFiles[mixItem.VersionIndex];
1155     switch (propID)
1156     {
1157       case kpidPath:
1158       {
1159         UString s = _resourcesPrefix;
1160         AddLangPrefix(s, item.Lang);
1161         s += "version.txt";
1162         prop = s;
1163         break;
1164       }
1165       case kpidSize:
1166       case kpidPackSize:
1167         prop = (UInt64)item.Size(); break;
1168     }
1169   }
1170   else if (mixItem.ResourceIndex >= 0)
1171   {
1172     const CResItem &item = _items[mixItem.ResourceIndex];
1173     switch (propID)
1174     {
1175       case kpidPath:
1176       {
1177         UString s = _resourcesPrefix;
1178         AddLangPrefix(s, item.Lang);
1179         {
1180           const char *p = NULL;
1181           if (item.Type < Z7_ARRAY_SIZE(g_ResTypes))
1182             p = g_ResTypes[item.Type];
1183           if (p)
1184             s += p;
1185           else
1186             AddResNameToString(s, item.Type);
1187         }
1188         s.Add_PathSepar();
1189         AddResNameToString(s, item.ID);
1190         if (item.HeaderSize != 0)
1191         {
1192           if (item.IsBmp())
1193             s += ".bmp";
1194           else if (item.IsIcon())
1195             s += ".ico";
1196         }
1197         prop = s;
1198         break;
1199       }
1200       case kpidSize: prop = (UInt64)item.GetSize(); break;
1201       case kpidPackSize: prop = (UInt64)item.Size; break;
1202     }
1203   }
1204   else
1205   {
1206     const CSection &item = _sections[mixItem.SectionIndex];
1207     switch (propID)
1208     {
1209       case kpidPath:
1210       {
1211         AString s = item.Name;
1212         s.Replace('/', '_');
1213         s.Replace('\\', '_');
1214         prop = MultiByteToUnicodeString(s);
1215         break;
1216       }
1217       case kpidSize: prop = (UInt64)item.GetSize_Extract(); break;
1218       // case kpid_NumRelocs: prop = (UInt32)item.NumRelocs; break;
1219       case kpidPackSize: prop = (UInt64)item.PSize; break;
1220       case kpidVirtualSize: prop = (UInt64)item.VSize; break;
1221       case kpidOffset: prop = item.Pa; break;
1222       case kpidVa: if (item.IsRealSect) prop = item.Va; break;
1223       case kpidMTime:
1224       case kpidCTime:
1225         TimeToProp(item.IsDebug ? item.Time : _header.Time, prop); break;
1226       case kpidCharacts:
1227        if (item.IsRealSect)
1228        {
1229          UInt32 flags = item.Flags;
1230          const UInt32 MY_IMAGE_SCN_ALIGN_MASK = 0x00F00000;
1231          AString s = FlagsToString(g_SectFlags, Z7_ARRAY_SIZE(g_SectFlags), item.Flags & ~MY_IMAGE_SCN_ALIGN_MASK);
1232          const UInt32 align = ((flags >> 20) & 0xF);
1233          if (align != 0)
1234          {
1235            char sz[32];
1236            ConvertUInt32ToString(1 << (align - 1), sz);
1237            s.Add_Space();
1238            s += "align_";
1239            s += sz;
1240          }
1241          prop = s;
1242        }
1243        break;
1244       case kpidZerosTailIsAllowed: if (!item.IsRealSect) prop = true; break;
1245     }
1246   }
1247   prop.Detach(value);
1248   return S_OK;
1249   COM_TRY_END
1250 }
1251 
1252 HRESULT CHandler::LoadDebugSections(IInStream *stream, bool &thereIsSection)
1253 {
1254   thereIsSection = false;
1255   const CDirLink &debugLink = _optHeader.DirItems[kDirLink_Debug];
1256   if (debugLink.Size == 0)
1257     return S_OK;
1258   const unsigned kEntrySize = 28;
1259   UInt32 numItems = debugLink.Size / kEntrySize;
1260   if (numItems > 16)
1261     return S_FALSE;
1262 
1263   // MAC's EFI file: numItems can be incorrect. Only first CDebugEntry entry is correct.
1264   // debugLink.Size = kEntrySize + some_data, pointed by entry[0].
1265   if (numItems * kEntrySize != debugLink.Size)
1266   {
1267     // return S_FALSE;
1268     if (numItems > 1)
1269       numItems = 1;
1270   }
1271 
1272   UInt64 pa = 0;
1273   unsigned i;
1274   for (i = 0; i < _sections.Size(); i++)
1275   {
1276     const CSection &sect = _sections[i];
1277     if (sect.Va <= debugLink.Va && debugLink.Va + debugLink.Size <= sect.Va + sect.PSize)
1278     {
1279       pa = sect.Pa + (debugLink.Va - sect.Va);
1280       break;
1281     }
1282   }
1283   if (i == _sections.Size())
1284   {
1285     // Exe for ARM requires S_OK
1286     // return S_FALSE;
1287     return S_OK;
1288   }
1289 
1290   CByteBuffer buffer(debugLink.Size);
1291   Byte *buf = buffer;
1292 
1293   RINOK(InStream_SeekSet(stream, pa))
1294   RINOK(ReadStream_FALSE(stream, buf, debugLink.Size))
1295 
1296   for (i = 0; i < numItems; i++)
1297   {
1298     CDebugEntry de;
1299     de.Parse(buf);
1300 
1301     if (de.Size == 0)
1302       continue;
1303 
1304     UInt32 totalSize = de.Pa + de.Size;
1305     if (totalSize > _totalSize)
1306     {
1307       _totalSize = totalSize;
1308       thereIsSection = true;
1309 
1310       CSection &sect = _sections.AddNew();
1311       sect.Name = ".debug";
1312       sect.Name.Add_UInt32(i);
1313       sect.IsDebug = true;
1314       sect.Time = de.Time;
1315       sect.Va = de.Va;
1316       sect.Pa = de.Pa;
1317       sect.Set_Size_for_all(de.Size);
1318     }
1319     buf += kEntrySize;
1320   }
1321 
1322   return S_OK;
1323 }
1324 
1325 HRESULT CHandler::ReadTable(UInt32 offset, CRecordVector<CTableItem> &items)
1326 {
1327   if ((offset & 3) != 0 || offset >= _buf.Size())
1328     return S_FALSE;
1329   size_t rem = _buf.Size() - offset;
1330   if (rem < 16)
1331     return S_FALSE;
1332   unsigned numNameItems = Get16(_buf + offset + 12);
1333   unsigned numIdItems = Get16(_buf + offset + 14);
1334   unsigned numItems = numNameItems + numIdItems;
1335   if ((rem - 16) / 8 < numItems)
1336     return S_FALSE;
1337   if (!_usedRes.SetRange(offset, 16 + numItems * 8))
1338     return S_FALSE;
1339   offset += 16;
1340   items.ClearAndReserve(numItems);
1341   for (unsigned i = 0; i < numItems; i++, offset += 8)
1342   {
1343     const Byte *buf = _buf + offset;
1344     CTableItem item;
1345     item.ID = Get32(buf + 0);
1346     if ((bool)((item.ID & kFlag) != 0) != (bool)(i < numNameItems))
1347       return S_FALSE;
1348     item.Offset = Get32(buf + 4);
1349     items.AddInReserved(item);
1350   }
1351   return S_OK;
1352 }
1353 
1354 static const UInt32 kFileSizeMax = (UInt32)1 << 31;
1355 static const unsigned kNumResItemsMax = (unsigned)1 << 23;
1356 static const unsigned kNumStringLangsMax = 256;
1357 
1358 // BITMAPINFOHEADER
1359 struct CBitmapInfoHeader
1360 {
1361   // UInt32 HeaderSize;
1362   UInt32 XSize;
1363   Int32 YSize;
1364   UInt16 Planes;
1365   UInt16 BitCount;
1366   UInt32 Compression;
1367   UInt32 SizeImage;
1368 
1369   bool Parse(const Byte *p, size_t size);
1370 };
1371 
1372 static const UInt32 kBitmapInfoHeader_Size = 0x28;
1373 
1374 bool CBitmapInfoHeader::Parse(const Byte *p, size_t size)
1375 {
1376   if (size < kBitmapInfoHeader_Size || Get32(p) != kBitmapInfoHeader_Size)
1377     return false;
1378   G32( 4, XSize);
1379   G32_signed( 8, YSize);
1380   G16(12, Planes);
1381   G16(14, BitCount);
1382   G32(16, Compression);
1383   G32(20, SizeImage);
1384   return true;
1385 }
1386 
1387 static UInt32 GetImageSize(UInt32 xSize, UInt32 ySize, UInt32 bitCount)
1388 {
1389   return ((xSize * bitCount + 7) / 8 + 3) / 4 * 4 * ySize;
1390 }
1391 
1392 static UInt32 SetBitmapHeader(Byte *dest, const Byte *src, UInt32 size)
1393 {
1394   CBitmapInfoHeader h;
1395   if (!h.Parse(src, size))
1396     return 0;
1397   if (h.YSize < 0)
1398     h.YSize = -h.YSize;
1399   if (h.XSize > (1 << 26)
1400       || h.YSize > (1 << 26)
1401       || h.YSize < 0
1402       || h.Planes != 1 || h.BitCount > 32)
1403     return 0;
1404   if (h.SizeImage == 0)
1405   {
1406     if (h.Compression != 0) // BI_RGB
1407       return 0;
1408     h.SizeImage = GetImageSize(h.XSize, (UInt32)h.YSize, h.BitCount);
1409   }
1410   UInt32 totalSize = kBmpHeaderSize + size;
1411   UInt32 offBits = totalSize - h.SizeImage;
1412   // BITMAPFILEHEADER
1413   SetUi16(dest, 0x4D42)
1414   SetUi32(dest + 2, totalSize)
1415   SetUi32(dest + 6, 0)
1416   SetUi32(dest + 10, offBits)
1417   return kBmpHeaderSize;
1418 }
1419 
1420 static UInt32 SetIconHeader(Byte *dest, const Byte *src, UInt32 size)
1421 {
1422   CBitmapInfoHeader h;
1423   if (!h.Parse(src, size))
1424     return 0;
1425   if (h.YSize < 0)
1426     h.YSize = -h.YSize;
1427   if (h.XSize > (1 << 26)
1428       || h.YSize > (1 << 26)
1429       || h.YSize < 0
1430       || h.Planes != 1
1431       || h.Compression != 0) // BI_RGB
1432     return 0;
1433 
1434   const UInt32 numBitCount = h.BitCount;
1435   if (numBitCount != 1 &&
1436       numBitCount != 4 &&
1437       numBitCount != 8 &&
1438       numBitCount != 24 &&
1439       numBitCount != 32)
1440     return 0;
1441 
1442   if ((h.YSize & 1) != 0)
1443     return 0;
1444   h.YSize /= 2;
1445   if (h.XSize > 0x100 || h.YSize > 0x100)
1446     return 0;
1447 
1448   UInt32 imageSize;
1449   // imageSize is not correct if AND mask array contains zeros
1450   // in this case it is equal image1Size
1451 
1452   // UInt32 imageSize = h.SizeImage;
1453   // if (imageSize == 0)
1454   // {
1455     const UInt32 image1Size = GetImageSize(h.XSize, (UInt32)h.YSize, h.BitCount);
1456     const UInt32 image2Size = GetImageSize(h.XSize, (UInt32)h.YSize, 1);
1457     imageSize = image1Size + image2Size;
1458   // }
1459   UInt32 numColors = 0;
1460   if (numBitCount < 16)
1461     numColors = 1 << numBitCount;
1462 
1463   SetUi16(dest, 0) // Reserved
1464   SetUi16(dest + 2, 1) // RES_ICON
1465   SetUi16(dest + 4, 1) // ResCount
1466 
1467   dest[6] = (Byte)h.XSize; // Width
1468   dest[7] = (Byte)h.YSize; // Height
1469   dest[8] = (Byte)numColors; // ColorCount
1470   dest[9] = 0; // Reserved
1471 
1472   SetUi32(dest + 10, 0) // Reserved1 / Reserved2
1473 
1474   UInt32 numQuadsBytes = numColors * 4;
1475   UInt32 BytesInRes = kBitmapInfoHeader_Size + numQuadsBytes + imageSize;
1476   SetUi32(dest + 14, BytesInRes)
1477   SetUi32(dest + 18, kIconHeaderSize)
1478 
1479   /*
1480   Description = DWORDToString(xSize) +
1481       kDelimiterChar + DWORDToString(ySize) +
1482       kDelimiterChar + DWORDToString(numBitCount);
1483   */
1484   return kIconHeaderSize;
1485 }
1486 
1487 bool CHandler::ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size)
1488 {
1489   if ((size & 1) != 0)
1490     return false;
1491 
1492   unsigned i;
1493   for (i = 0; i < _strings.Size(); i++)
1494     if (_strings[i].Lang == lang)
1495       break;
1496   if (i == _strings.Size())
1497   {
1498     if (_strings.Size() >= kNumStringLangsMax)
1499       return false;
1500     CStringItem &item = _strings.AddNew();
1501     item.Lang = lang;
1502   }
1503 
1504   CStringItem &item = _strings[i];
1505   id = (id - 1) << 4;
1506   UInt32 pos = 0;
1507   for (i = 0; i < 16; i++)
1508   {
1509     if (size - pos < 2)
1510       return false;
1511     UInt32 len = Get16(src + pos);
1512     pos += 2;
1513     if (len != 0)
1514     {
1515       if (size - pos < len * 2)
1516         return false;
1517       char temp[32];
1518       ConvertUInt32ToString(id + i, temp);
1519       size_t tempLen = strlen(temp);
1520       size_t j;
1521       for (j = 0; j < tempLen; j++)
1522         item.AddChar(temp[j]);
1523       item.AddChar('\t');
1524       for (j = 0; j < len; j++, pos += 2)
1525         item.AddWChar_Smart(Get16(src + pos));
1526       item.NewLine();
1527     }
1528   }
1529   if (size == pos)
1530     return true;
1531 
1532   // Some rare case files have additional ZERO.
1533   if (size == pos + 2 && Get16(src + pos) == 0)
1534     return true;
1535 
1536   return false;
1537 }
1538 
1539 
1540 // ---------- VERSION ----------
1541 
1542 static const UInt32 kMy_VS_FFI_SIGNATURE = 0xFEEF04BD;
1543 
1544 struct CMy_VS_FIXEDFILEINFO
1545 {
1546   // UInt32 Signature;
1547   // UInt32 StrucVersion;
1548   UInt32 VersionMS;
1549   UInt32 VersionLS;
1550   UInt32 ProductVersionMS;
1551   UInt32 ProductVersionLS;
1552   UInt32 FlagsMask;
1553   UInt32 Flags;
1554   UInt32 OS;
1555   UInt32 Type;
1556   UInt32 Subtype;
1557   UInt32 DateMS;
1558   UInt32 DateLS;
1559 
1560   bool Parse(const Byte *p);
1561   void PrintToTextFile(CTextFile &f, CObjectVector<CStringKeyValue> &keys);
1562 };
1563 
1564 bool CMy_VS_FIXEDFILEINFO::Parse(const Byte *p)
1565 {
1566   if (Get32(p) != kMy_VS_FFI_SIGNATURE) // signature;
1567     return false;
1568   // G32(0x04, StrucVersion);
1569   G32(0x08, VersionMS);
1570   G32(0x0C, VersionLS);
1571   G32(0x10, ProductVersionMS);
1572   G32(0x14, ProductVersionLS);
1573   G32(0x18, FlagsMask);
1574   G32(0x1C, Flags);
1575   G32(0x20, OS);
1576   G32(0x24, Type);
1577   G32(0x28, Subtype);
1578   G32(0x2C, DateMS);
1579   G32(0x40, DateLS);
1580   return true;
1581 }
1582 
1583 static void PrintUInt32(CTextFile &f, UInt32 v)
1584 {
1585   char s[16];
1586   ConvertUInt32ToString(v, s);
1587   f.AddString(s);
1588 }
1589 
1590 static inline void PrintUInt32(UString &dest, UInt32 v)
1591 {
1592   dest.Add_UInt32(v);
1593 }
1594 
1595 static void PrintHex(CTextFile &f, UInt32 val)
1596 {
1597   char temp[16];
1598   temp[0] = '0';
1599   temp[1] = 'x';
1600   ConvertUInt32ToHex(val, temp + 2);
1601   f.AddString(temp);
1602 }
1603 
1604 static void PrintVersion(CTextFile &f, UInt32 ms, UInt32 ls)
1605 {
1606   PrintUInt32(f, HIWORD(ms));  f.AddChar(',');
1607   PrintUInt32(f, LOWORD(ms));  f.AddChar(',');
1608   PrintUInt32(f, HIWORD(ls));  f.AddChar(',');
1609   PrintUInt32(f, LOWORD(ls));
1610 }
1611 
1612 static void PrintVersion(UString &s, UInt32 ms, UInt32 ls)
1613 {
1614   PrintUInt32(s, HIWORD(ms));  s.Add_Dot();
1615   PrintUInt32(s, LOWORD(ms));  s.Add_Dot();
1616   PrintUInt32(s, HIWORD(ls));  s.Add_Dot();
1617   PrintUInt32(s, LOWORD(ls));
1618 }
1619 
1620 static const char * const k_VS_FileFlags[] =
1621 {
1622     "DEBUG"
1623   , "PRERELEASE"
1624   , "PATCHED"
1625   , "PRIVATEBUILD"
1626   , "INFOINFERRED"
1627   , "SPECIALBUILD"
1628 };
1629 
1630 static const CUInt32PCharPair k_VS_FileOS[] =
1631 {
1632   {  0x10001, "VOS_DOS_WINDOWS16" },
1633   {  0x10004, "VOS_DOS_WINDOWS32" },
1634   {  0x20002, "VOS_OS216_PM16" },
1635   {  0x30003, "VOS_OS232_PM32" },
1636   {  0x40004, "VOS_NT_WINDOWS32" }
1637 };
1638 
1639 static const char * const k_VS_FileOS_High[] =
1640 {
1641     "VOS_UNKNOWN"
1642   , "VOS_DOS"
1643   , "VOS_OS216"
1644   , "VOS_OS232"
1645   , "VOS_NT"
1646   , "VOS_WINCE"
1647 };
1648 
1649 static const UInt32 kMY_VFT_DRV  = 3;
1650 static const UInt32 kMY_VFT_FONT = 4;
1651 
1652 static const char * const k_VS_FileOS_Low[] =
1653 {
1654     "VOS__BASE"
1655   , "VOS__WINDOWS16"
1656   , "VOS__PM16"
1657   , "VOS__PM32"
1658   , "VOS__WINDOWS32"
1659 };
1660 
1661 static const char * const k_VS_FileType[] =
1662 {
1663     "VFT_UNKNOWN"
1664   , "VFT_APP"
1665   , "VFT_DLL"
1666   , "VFT_DRV"
1667   , "VFT_FONT"
1668   , "VFT_VXD"
1669   , "0x6"
1670   , "VFT_STATIC_LIB"
1671 };
1672 
1673 // Subtype for VFT_DRV Type
1674 static const char * const k_VS_FileSubType_DRV[] =
1675 {
1676     "0"
1677   , "PRINTER"
1678   , "KEYBOARD"
1679   , "LANGUAGE"
1680   , "DISPLAY"
1681   , "MOUSE"
1682   , "NETWORK"
1683   , "SYSTEM"
1684   , "INSTALLABLE"
1685   , "SOUND"
1686   , "COMM"
1687   , "INPUTMETHOD"
1688   , "VERSIONED_PRINTER"
1689 };
1690 
1691 // Subtype for VFT_FONT Type
1692 static const char * const k_VS_FileSubType_FONT[] =
1693 {
1694     "0"
1695   , "VFT2_FONT_RASTER"
1696   , "VFT2_FONT_VECTOR"
1697   , "VFT2_FONT_TRUETYPE"
1698 };
1699 
1700 static int FindKey(CObjectVector<CStringKeyValue> &v, const char *key)
1701 {
1702   FOR_VECTOR (i, v)
1703     if (v[i].Key.IsEqualTo(key))
1704       return (int)i;
1705   return -1;
1706 }
1707 
1708 static void AddToUniqueUStringVector(CObjectVector<CStringKeyValue> &v, const UString &key, const UString &value)
1709 {
1710   bool needInsert = false;
1711   unsigned i;
1712   for (i = 0; i < v.Size(); i++)
1713   {
1714     if (v[i].Key == key)
1715     {
1716       if (v[i].Value == value)
1717         return;
1718       needInsert = true;
1719     }
1720     else if (needInsert)
1721       break;
1722   }
1723   CStringKeyValue &pair = v.InsertNew(i);
1724   pair.Key = key;
1725   pair.Value = value;
1726 }
1727 
1728 void CMy_VS_FIXEDFILEINFO::PrintToTextFile(CTextFile &f, CObjectVector<CStringKeyValue> &keys)
1729 {
1730   f.AddString("FILEVERSION    ");
1731   PrintVersion(f, VersionMS, VersionLS);
1732   f.NewLine();
1733 
1734   f.AddString("PRODUCTVERSION ");
1735   PrintVersion(f, ProductVersionMS, ProductVersionLS);
1736   f.NewLine();
1737 
1738   {
1739     UString s;
1740     PrintVersion(s, VersionMS, VersionLS);
1741     AddToUniqueUStringVector(keys, L"FileVersion", s);
1742   }
1743   {
1744     UString s;
1745     PrintVersion(s, ProductVersionMS, ProductVersionLS);
1746     AddToUniqueUStringVector(keys, L"ProductVersion", s);
1747   }
1748 
1749   f.AddString("FILEFLAGSMASK  ");
1750   PrintHex(f, FlagsMask);
1751   f.NewLine();
1752 
1753   f.AddString("FILEFLAGS      ");
1754   {
1755     bool wasPrinted = false;
1756     for (unsigned i = 0; i < Z7_ARRAY_SIZE(k_VS_FileFlags); i++)
1757     {
1758       if ((Flags & ((UInt32)1 << i)) != 0)
1759       {
1760         if (wasPrinted)
1761           f.AddString(" | ");
1762         f.AddString("VS_FF_");
1763         f.AddString(k_VS_FileFlags[i]);
1764         wasPrinted = true;
1765       }
1766     }
1767     UInt32 v = Flags & ~(((UInt32)1 << Z7_ARRAY_SIZE(k_VS_FileFlags)) - 1);
1768     if (v != 0 || !wasPrinted)
1769     {
1770       if (wasPrinted)
1771         f.AddString(" | ");
1772       PrintHex(f, v);
1773     }
1774   }
1775   f.NewLine();
1776 
1777   // OS = 0x111230;
1778   f.AddString("FILEOS         ");
1779   unsigned i;
1780   for (i = 0; i < Z7_ARRAY_SIZE(k_VS_FileOS); i++)
1781   {
1782     const CUInt32PCharPair &pair = k_VS_FileOS[i];
1783     if (OS == pair.Value)
1784     {
1785       // continue;
1786       // f.AddString("VOS_");
1787       f.AddString(pair.Name);
1788       break;
1789     }
1790   }
1791   if (i == Z7_ARRAY_SIZE(k_VS_FileOS))
1792   {
1793     UInt32 high = OS >> 16;
1794     if (high < Z7_ARRAY_SIZE(k_VS_FileOS_High))
1795       f.AddString(k_VS_FileOS_High[high]);
1796     else
1797       PrintHex(f, high << 16);
1798     UInt32 low = OS & 0xFFFF;
1799     if (low != 0)
1800     {
1801       f.AddString(" | ");
1802       if (low < Z7_ARRAY_SIZE(k_VS_FileOS_Low))
1803         f.AddString(k_VS_FileOS_Low[low]);
1804       else
1805         PrintHex(f, low);
1806     }
1807   }
1808   f.NewLine();
1809 
1810   f.AddString("FILETYPE       ");
1811   if (Type < Z7_ARRAY_SIZE(k_VS_FileType))
1812     f.AddString(k_VS_FileType[Type]);
1813   else
1814     PrintHex(f, Type);
1815   f.NewLine();
1816 
1817   f.AddString("FILESUBTYPE    ");
1818   bool needPrintSubType = true;
1819   if (Type == kMY_VFT_DRV)
1820   {
1821     if (Subtype != 0 && Subtype < Z7_ARRAY_SIZE(k_VS_FileSubType_DRV))
1822     {
1823       f.AddString("VFT2_DRV_");
1824       f.AddString(k_VS_FileSubType_DRV[Subtype]);
1825       needPrintSubType = false;
1826     }
1827   }
1828   else if (Type == kMY_VFT_FONT)
1829   {
1830     if (Subtype != 0 && Subtype < Z7_ARRAY_SIZE(k_VS_FileSubType_FONT))
1831     {
1832       f.AddString(k_VS_FileSubType_FONT[Subtype]);
1833       needPrintSubType = false;
1834     }
1835   }
1836   if (needPrintSubType)
1837     PrintHex(f, Subtype);
1838   f.NewLine();
1839 }
1840 
1841 static void CopyToUString(const Byte *p, UString &s)
1842 {
1843   for (;;)
1844   {
1845     const wchar_t c = (wchar_t)Get16(p);
1846     p += 2;
1847     if (c == 0)
1848       return;
1849     s += c;
1850   }
1851 }
1852 
1853 static void CopyToUString_ByLen16(const Byte *p, unsigned numChars16, UString &s)
1854 {
1855   for (; numChars16; numChars16--)
1856   {
1857     const wchar_t c = (wchar_t)Get16(p);
1858     p += 2;
1859     s += c;
1860   }
1861 }
1862 
1863 static bool CompareWStrStrings(const Byte *p, const char *s)
1864 {
1865   unsigned pos = 0;
1866   for (;;)
1867   {
1868     const Byte c = (Byte)*s++;
1869     if (Get16(p + pos) != c)
1870       return false;
1871     pos += 2;
1872     if (c == 0)
1873       return true;
1874   }
1875 }
1876 
1877 struct CVersionBlock
1878 {
1879   UInt32 TotalLen;
1880   UInt32 ValueLen;
1881   unsigned IsTextValue;
1882   unsigned StrSize;
1883 
1884   bool Parse(const Byte *p, UInt32 size);
1885 };
1886 
1887 static int Get_Utf16Str_Len_InBytes(const Byte *p, size_t size)
1888 {
1889   unsigned pos = 0;
1890   for (;;)
1891   {
1892     if (pos + 1 >= size)
1893       return -1;
1894     if (Get16(p + pos) == 0)
1895       return (int)pos;
1896     pos += 2;
1897   }
1898 }
1899 
1900 static int Get_Utf16Str_Len_InBytes_AllowNonZeroTail(const Byte *p, size_t size)
1901 {
1902   unsigned pos = 0;
1903   for (;;)
1904   {
1905     if (pos + 1 >= size)
1906     {
1907       if (pos == size)
1908         return (int)pos;
1909       return -1;
1910     }
1911     if (Get16(p + pos) == 0)
1912       return (int)pos;
1913     pos += 2;
1914   }
1915 }
1916 
1917 static const unsigned k_ResoureBlockHeader_Size = 6;
1918 
1919 bool CVersionBlock::Parse(const Byte *p, UInt32 size)
1920 {
1921   if (size < k_ResoureBlockHeader_Size)
1922     return false;
1923   TotalLen = Get16(p);
1924   ValueLen = Get16(p + 2);
1925   if (TotalLen < k_ResoureBlockHeader_Size || TotalLen > size)
1926     return false;
1927   IsTextValue = Get16(p + 4);
1928   if (IsTextValue > 1)
1929     return false;
1930   StrSize = 0;
1931   const int t = Get_Utf16Str_Len_InBytes(p + k_ResoureBlockHeader_Size,
1932       TotalLen - k_ResoureBlockHeader_Size);
1933   if (t < 0)
1934     return false;
1935   StrSize = (unsigned)t;
1936   return true;
1937 }
1938 
1939 static void AddParamString(CTextFile &f, const Byte *p, size_t sLen)
1940 {
1941   f.AddChar(' ');
1942   f.AddChar('\"');
1943   f.AddBytes(p, sLen);
1944   f.AddChar('\"');
1945 }
1946 
1947 static bool ParseVersion(const Byte *p, UInt32 size, CTextFile &f, CObjectVector<CStringKeyValue> &keys)
1948 {
1949   UInt32 pos;
1950   {
1951     const unsigned k_sizeof_VS_FIXEDFILEINFO = 13 * 4;
1952 
1953     CVersionBlock vb;
1954     if (!vb.Parse(p, size))
1955       return false;
1956     if (vb.ValueLen != k_sizeof_VS_FIXEDFILEINFO) // maybe 0 is allowed here?
1957       return false;
1958     if (vb.IsTextValue)
1959       return false;
1960     pos = k_ResoureBlockHeader_Size;
1961     if (!CompareWStrStrings(p + pos, "VS_VERSION_INFO"))
1962       return false;
1963     pos += vb.StrSize + 2;
1964     pos += (4 - pos) & 3;
1965     if (pos + vb.ValueLen > vb.TotalLen)
1966       return false;
1967     /* sometimes resource contains zeros in remainder.
1968        So we don't check that size != vb.TotalLen
1969     // if (size != vb.TotalLen) return false;
1970     */
1971     if (size > vb.TotalLen)
1972         size = vb.TotalLen;
1973     CMy_VS_FIXEDFILEINFO FixedFileInfo;
1974     if (!FixedFileInfo.Parse(p + pos))
1975       return false;
1976     FixedFileInfo.PrintToTextFile(f, keys);
1977     pos += vb.ValueLen;
1978   }
1979 
1980   f.OpenBlock(0);
1981 
1982   for (;;)
1983   {
1984     pos += (4 - pos) & 3;
1985     if (pos >= size)
1986       break;
1987 
1988     CVersionBlock vb;
1989     if (!vb.Parse(p + pos, size - pos))
1990       return false;
1991     if (vb.ValueLen != 0)
1992       return false;
1993     const UInt32 endPos = pos + vb.TotalLen;
1994     pos += k_ResoureBlockHeader_Size;
1995 
1996     f.AddSpaces(2);
1997     f.AddString("BLOCK");
1998     AddParamString(f, p + pos, vb.StrSize);
1999 
2000     f.NewLine();
2001     f.OpenBlock(2);
2002 
2003     if (CompareWStrStrings(p + pos, "VarFileInfo"))
2004     {
2005       pos += vb.StrSize + 2;
2006       for (;;)
2007       {
2008         pos += (4 - pos) & 3;
2009         if (pos >= endPos)
2010           break;
2011         CVersionBlock vb2;
2012         if (!vb2.Parse(p + pos, endPos - pos))
2013           return false;
2014         const UInt32 endPos2 = pos + vb2.TotalLen;
2015         if (vb2.IsTextValue)
2016           return false;
2017         pos += k_ResoureBlockHeader_Size;
2018         f.AddSpaces(4);
2019         f.AddString("VALUE");
2020         AddParamString(f, p + pos, vb2.StrSize);
2021         if (!CompareWStrStrings(p + pos, "Translation"))
2022           return false;
2023         pos += vb2.StrSize + 2;
2024         pos += (4 - pos) & 3;
2025         if (pos + vb2.ValueLen != endPos2)
2026           return false;
2027         if ((vb2.ValueLen & 3) != 0)
2028           return false;
2029         UInt32 num = (vb2.ValueLen >> 2);
2030         for (; num != 0; num--, pos += 4)
2031         {
2032           const UInt32 dw = Get32(p + pos);
2033           const UInt32 lang = LOWORD(dw);
2034           const UInt32 codePage = HIWORD(dw);
2035 
2036           f.AddString(", ");
2037           PrintHex(f, lang);
2038           f.AddString(", ");
2039           PrintUInt32(f, codePage);
2040         }
2041         f.NewLine();
2042       }
2043     }
2044     else
2045     {
2046       if (!CompareWStrStrings(p + pos, "StringFileInfo"))
2047         return false;
2048       pos += vb.StrSize + 2;
2049       for (;;)
2050       {
2051         pos += (4 - pos) & 3;
2052         if (pos >= endPos)
2053           break;
2054         CVersionBlock vb2;
2055         if (!vb2.Parse(p + pos, endPos - pos))
2056           return false;
2057         const UInt32 endPos2 = pos + vb2.TotalLen;
2058         if (vb2.ValueLen != 0)
2059           return false;
2060         pos += k_ResoureBlockHeader_Size;
2061 
2062         f.AddSpaces(4);
2063         f.AddString("BLOCK");
2064         AddParamString(f, p + pos, vb2.StrSize);
2065         pos += vb2.StrSize + 2;
2066 
2067         f.NewLine();
2068         f.OpenBlock(4);
2069 
2070         for (;;)
2071         {
2072           pos += (4 - pos) & 3;
2073           if (pos >= endPos2)
2074             break;
2075 
2076           CVersionBlock vb3;
2077           if (!vb3.Parse(p + pos, endPos2 - pos))
2078             return false;
2079           // ValueLen is a number of 16-bit characters (usually it includes zero tail character).
2080           const UInt32 endPos3 = pos + vb3.TotalLen;
2081           pos += k_ResoureBlockHeader_Size;
2082 
2083           // we don't write string if it's not text
2084           if (vb3.IsTextValue)
2085           {
2086             f.AddSpaces(6);
2087             f.AddString("VALUE");
2088             AddParamString(f, p + pos, vb3.StrSize);
2089             UString key;
2090             UString value;
2091             CopyToUString(p + pos, key);
2092             pos += vb3.StrSize + 2;
2093 
2094             pos += (4 - pos) & 3;
2095             if (vb3.ValueLen != 0 && pos /* + 2 */ <= endPos3)
2096             {
2097               f.AddChar(',');
2098               f.AddSpaces((34 - (int)vb3.StrSize) / 2);
2099               // vb3.TotalLen for some PE files (not from msvc) doesn't include tail zero at the end of Value string.
2100               // we allow that minor error.
2101               const int sLen = Get_Utf16Str_Len_InBytes_AllowNonZeroTail(p + pos, endPos3 - pos);
2102               if (sLen < 0)
2103                 return false;
2104               /*
2105               if (vb3.ValueLen - 1 != (unsigned)sLen / 2 &&
2106                   vb3.ValueLen     != (unsigned)sLen / 2)
2107                 return false;
2108               */
2109               AddParamString(f, p + pos, (unsigned)sLen);
2110               CopyToUString_ByLen16(p + pos, (unsigned)sLen / 2, value);
2111               // pos += (unsigned)sLen + 2;
2112             }
2113             AddToUniqueUStringVector(keys, key, value);
2114           }
2115           pos = endPos3;
2116           f.NewLine();
2117         }
2118         pos = endPos2;
2119         f.CloseBlock(4);
2120       }
2121     }
2122     f.CloseBlock(2);
2123     pos = endPos;
2124   }
2125 
2126   f.CloseBlock(0);
2127   return true;
2128 }
2129 
2130 
2131 HRESULT CHandler::OpenResources(unsigned sectionIndex, IInStream *stream, IArchiveOpenCallback *callback)
2132 {
2133   const CSection &sect = _sections[sectionIndex];
2134   size_t fileSize = sect.PSize;
2135   {
2136     size_t fileSizeMin = sect.PSize;
2137 
2138     if (sect.VSize < sect.PSize)
2139     {
2140       fileSize = fileSizeMin = sect.VSize;
2141       const int numBits = _optHeader.GetNumFileAlignBits();
2142       if (numBits > 0)
2143       {
2144         const UInt32 mask = ((UInt32)1 << numBits) - 1;
2145         const size_t end = (size_t)((sect.VSize + mask) & (UInt32)~mask);
2146         if (end > sect.VSize)
2147         {
2148           if (end <= sect.PSize)
2149             fileSize = end;
2150           else
2151             fileSize = sect.PSize;
2152         }
2153       }
2154     }
2155 
2156     if (fileSize > kFileSizeMax)
2157       return S_FALSE;
2158 
2159     {
2160       const UInt64 fileSize64 = fileSize;
2161       if (callback)
2162         RINOK(callback->SetTotal(NULL, &fileSize64))
2163     }
2164 
2165     RINOK(InStream_SeekSet(stream, sect.Pa))
2166 
2167     _buf.Alloc(fileSize);
2168 
2169     size_t pos;
2170 
2171     for (pos = 0; pos < fileSize;)
2172     {
2173       {
2174         const UInt64 offset64 = pos;
2175         if (callback)
2176           RINOK(callback->SetCompleted(NULL, &offset64))
2177       }
2178       size_t rem = MyMin(fileSize - pos, (size_t)(1 << 22));
2179       RINOK(ReadStream(stream, _buf + pos, &rem))
2180       if (rem == 0)
2181       {
2182         if (pos < fileSizeMin)
2183           return S_FALSE;
2184         break;
2185       }
2186       pos += rem;
2187     }
2188 
2189     if (pos < fileSize)
2190       memset(_buf + pos, 0, fileSize - pos);
2191   }
2192 
2193   _usedRes.Alloc(fileSize);
2194   CRecordVector<CTableItem> specItems;
2195   RINOK(ReadTable(0, specItems))
2196 
2197   _oneLang = true;
2198   bool stringsOk = true;
2199   size_t maxOffset = 0;
2200 
2201   FOR_VECTOR (i, specItems)
2202   {
2203     const CTableItem &item1 = specItems[i];
2204     if ((item1.Offset & kFlag) == 0)
2205       return S_FALSE;
2206 
2207     CRecordVector<CTableItem> specItems2;
2208     RINOK(ReadTable(item1.Offset & kMask, specItems2))
2209 
2210     FOR_VECTOR (j, specItems2)
2211     {
2212       const CTableItem &item2 = specItems2[j];
2213       if ((item2.Offset & kFlag) == 0)
2214         return S_FALSE;
2215 
2216       CRecordVector<CTableItem> specItems3;
2217       RINOK(ReadTable(item2.Offset & kMask, specItems3))
2218 
2219       CResItem item;
2220       item.Type = item1.ID;
2221       item.ID = item2.ID;
2222 
2223       FOR_VECTOR (k, specItems3)
2224       {
2225         if (_items.Size() >= kNumResItemsMax)
2226           return S_FALSE;
2227         const CTableItem &item3 = specItems3[k];
2228         if ((item3.Offset & kFlag) != 0)
2229           return S_FALSE;
2230         if (item3.Offset >= _buf.Size() || _buf.Size() - item3.Offset < 16)
2231           return S_FALSE;
2232         const Byte *buf = _buf + item3.Offset;
2233         item.Lang = item3.ID;
2234         item.Offset = Get32(buf + 0);
2235         item.Size = Get32(buf + 4);
2236         // UInt32 codePage = Get32(buf + 8);
2237         if (Get32(buf + 12) != 0)
2238           return S_FALSE;
2239         if (!_items.IsEmpty() && _oneLang && !item.IsNameEqual(_items.Back()))
2240           _oneLang = false;
2241 
2242         item.HeaderSize = 0;
2243 
2244         size_t offset = item.Offset - sect.Va;
2245         if (offset > maxOffset)
2246           maxOffset = offset;
2247         if (offset + item.Size > maxOffset)
2248           maxOffset = offset + item.Size;
2249 
2250         if (CheckItem(sect, item, offset))
2251         {
2252           const Byte *data = _buf + offset;
2253           if (item.IsBmp())
2254             item.HeaderSize = SetBitmapHeader(item.Header, data, item.Size);
2255           else if (item.IsIcon())
2256             item.HeaderSize = SetIconHeader(item.Header, data, item.Size);
2257           else if (item.IsString())
2258           {
2259             if (stringsOk)
2260               stringsOk = ParseStringRes(item.ID, item.Lang, data, item.Size);
2261           }
2262         }
2263 
2264         if (item.IsVersion())
2265         {
2266           if (offset > _buf.Size() || _buf.Size() - offset < item.Size)
2267             continue;
2268           CTextFile f;
2269           if (ParseVersion((const Byte *)_buf + offset, item.Size, f, _versionKeys))
2270           {
2271             CMixItem mixItem;
2272             mixItem.VersionIndex = (int)_versionFiles.Size();
2273             mixItem.SectionIndex = (int)sectionIndex; // check it !!!!
2274             CByteBuffer_WithLang &vf = _versionFiles.AddNew();
2275             vf.Lang = item.Lang;
2276             vf.CopyFrom(f.Buf, f.Buf.GetPos());
2277             _mixItems.Add(mixItem);
2278             continue;
2279           }
2280           // PrintError("ver.Parse error");
2281         }
2282 
2283         item.Enabled = true;
2284         _items.Add(item);
2285       }
2286     }
2287   }
2288 
2289   if (stringsOk && !_strings.IsEmpty())
2290   {
2291     unsigned i;
2292     for (i = 0; i < _items.Size(); i++)
2293     {
2294       CResItem &item = _items[i];
2295       if (item.IsString())
2296         item.Enabled = false;
2297     }
2298     for (i = 0; i < _strings.Size(); i++)
2299     {
2300       if (_strings[i].FinalSize() == 0)
2301         continue;
2302       CMixItem mixItem;
2303       mixItem.StringIndex = (int)i;
2304       mixItem.SectionIndex = (int)sectionIndex;
2305       _mixItems.Add(mixItem);
2306     }
2307   }
2308 
2309   _usedRes.Free();
2310 
2311   {
2312     // PSize can be much larger than VSize in some exe installers.
2313     // it contains archive data after PE resources.
2314     // So we need to use PSize here!
2315     if (maxOffset < sect.PSize)
2316     {
2317       size_t end = fileSize;
2318 
2319       // we skip Zeros to start of aligned block
2320       size_t i;
2321       for (i = maxOffset; i < end; i++)
2322         if (_buf[i] != 0)
2323           break;
2324       if (i == end)
2325         maxOffset = end;
2326 
2327       CSection sect2;
2328       sect2.Flags = 0;
2329       sect2.Pa = sect.Pa + (UInt32)maxOffset;
2330       sect2.Va = sect.Va + (UInt32)maxOffset;
2331 
2332       // 9.29: we use sect.PSize instead of sect.VSize to support some CAB-SFX
2333       // the code for .rsrc_2 is commented.
2334       sect2.PSize = sect.PSize - (UInt32)maxOffset;
2335 
2336       if (sect2.PSize != 0)
2337       {
2338         sect2.ExtractSize = sect2.VSize = sect2.PSize;
2339         sect2.Name = ".rsrc_1";
2340         sect2.Time = 0;
2341         sect2.IsAdditionalSection = true;
2342         _sections.Add(sect2);
2343       }
2344     }
2345   }
2346 
2347   return S_OK;
2348 }
2349 
2350 
2351 bool CHeader::ParseCoff(const Byte *p)
2352 {
2353   ParseBase(p);
2354   if (PointerToSymbolTable < kCoffHeaderSize)
2355     return false;
2356   if (NumSymbols >= (1 << 24))
2357     return false;
2358   if (OptHeaderSize != 0 && OptHeaderSize < k_OptHeader32_Size_MIN)
2359     return false;
2360 
2361   // 18.04: we reduce false detections
2362   if (NumSections == 0 && OptHeaderSize == 0)
2363     return false;
2364 
2365   for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_MachinePairs); i++)
2366     if (Machine == g_MachinePairs[i].Value)
2367       return true;
2368   if (Machine == 0)
2369     return true;
2370 
2371   return false;
2372 }
2373 
2374 
2375 static inline bool CheckPeOffset(UInt32 pe)
2376 {
2377   // ((pe & 7) == 0) is for most PE files. But there is unusual EFI-PE file that uses unaligned pe value.
2378   return pe >= 0x40 && pe <= 0x1000 /* && (pe & 7) == 0 */ ;
2379 }
2380 
2381 static const unsigned kStartSize = 0x40;
2382 
2383 API_FUNC_static_IsArc IsArc_Pe(const Byte *p, size_t size)
2384 {
2385   if (size < 2)
2386     return k_IsArc_Res_NEED_MORE;
2387   if (p[0] != 'M' || p[1] != 'Z')
2388     return k_IsArc_Res_NO;
2389   if (size < kStartSize)
2390     return k_IsArc_Res_NEED_MORE;
2391   UInt32 pe = Get32(p + 0x3C);
2392   if (!CheckPeOffset(pe))
2393     return k_IsArc_Res_NO;
2394   if (pe + kPeHeaderSize > size)
2395     return k_IsArc_Res_NEED_MORE;
2396   CHeader header;
2397   if (!header.ParsePe(p + pe))
2398     return k_IsArc_Res_NO;
2399   return k_IsArc_Res_YES;
2400 }
2401 }
2402 
2403 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
2404 {
2405   UInt32 coffOffset = 0;
2406   if (_coffMode)
2407   {
2408     Byte h[kCoffHeaderSize];
2409     RINOK(ReadStream_FALSE(stream, h, kCoffHeaderSize))
2410     if (!_header.ParseCoff(h))
2411       return S_FALSE;
2412   }
2413   else
2414   {
2415     UInt32 _peOffset;
2416     {
2417       Byte h[kStartSize];
2418       RINOK(ReadStream_FALSE(stream, h, kStartSize))
2419       if (h[0] != 'M' || h[1] != 'Z')
2420         return S_FALSE;
2421         /* most of PE files contain 0x0090 at offset 2.
2422         But some rare PE files contain another values. So we don't use that check.
2423       if (Get16(h + 2) != 0x90) return false; */
2424       _peOffset = Get32(h + 0x3C);
2425       if (!CheckPeOffset(_peOffset))
2426         return S_FALSE;
2427       coffOffset = _peOffset + 4;
2428     }
2429     {
2430       Byte h[kPeHeaderSize];
2431       RINOK(InStream_SeekSet(stream, _peOffset))
2432       RINOK(ReadStream_FALSE(stream, h, kPeHeaderSize))
2433       if (!_header.ParsePe(h))
2434         return S_FALSE;
2435     }
2436   }
2437 
2438   const UInt32 optStart = coffOffset + kCoffHeaderSize;
2439   const UInt32 bufSize = _header.OptHeaderSize + (UInt32)_header.NumSections * kSectionSize;
2440   _totalSize = optStart + bufSize;
2441   CByteBuffer buffer(bufSize);
2442 
2443   RINOK(ReadStream_FALSE(stream, buffer, bufSize))
2444 
2445   // memset((void *)&_optHeader, 0, sizeof(_optHeader));
2446   if (_header.OptHeaderSize != 0)
2447   if (!_optHeader.Parse(buffer, _header.OptHeaderSize))
2448     return S_FALSE;
2449 
2450   UInt32 pos = _header.OptHeaderSize;
2451   unsigned i;
2452   for (i = 0; i < _header.NumSections; i++, pos += kSectionSize)
2453   {
2454     CSection &sect = _sections.AddNew();
2455     sect.Parse(buffer + pos);
2456     sect.IsRealSect = true;
2457     if (sect.Name.IsEqualTo(".reloc"))
2458     {
2459       const CDirLink &dl = _optHeader.DirItems[kDirLink_BASERELOC];
2460       if (dl.Va == sect.Va &&
2461           dl.Size <= sect.PSize)
2462         sect.ExtractSize = dl.Size;
2463     }
2464     else if (sect.Name.IsEqualTo(".pdata"))
2465     {
2466       const CDirLink &dl = _optHeader.DirItems[kDirLink_EXCEPTION];
2467       if (dl.Va == sect.Va &&
2468           dl.Size <= sect.PSize)
2469         sect.ExtractSize = dl.Size;
2470     }
2471 
2472     /* PE pre-file in .hxs file has errors:
2473          PSize of resource is larger than real size.
2474          So it overlaps next ".its" section.
2475        7-zip before 22.02: we corrected it.
2476 
2477        22.02: another bad case is possible in incorrect pe (exe) file:
2478          PSize in .rsrc section is correct,
2479          but next .reloc section has incorrect (Pa) that overlaps with .rsrc.
2480     */
2481 
2482     if (i != 0)
2483     {
2484       const CSection &prev = _sections[i - 1];
2485       if (prev.Pa < sect.Pa
2486           && prev.Pa + prev.PSize > sect.Pa
2487           && sect.PSize != 0
2488           && prev.PSize != 0)
2489       {
2490         _sectionsError = true;
2491         // PRF(printf("\n !!!! Section correction: %s\n ", prev.Name));
2492 
2493         /* we corrected that case in 7-zip before 22.02: */
2494         // prev.PSize = sect.Pa - prev.Pa;
2495 
2496         /* 22.02: here we can try to change bad section position to expected postion.
2497            but original Windows code probably will not do same things. */
2498         // if (prev.PSize <= sect.Va - prev.Va) sect.Pa = prev.Pa + prev.PSize;
2499       }
2500     }
2501     /* last ".its" section in hxs file has incorrect sect.PSize.
2502        7-zip before 22.02: we reduced section to real sect.VSize */
2503     /*
2504     if (sect.VSize == 24 && sect.PSize == 512 && i == (unsigned)_header.NumSections - 1)
2505       sect.PSize = sect.VSize;
2506     */
2507   }
2508 
2509   for (i = 0; i < _sections.Size(); i++)
2510     _sections[i].UpdateTotalSize(_totalSize);
2511 
2512   bool thereISDebug = false;
2513   if (IsOpt())
2514   {
2515   RINOK(LoadDebugSections(stream, thereISDebug))
2516 
2517   const CDirLink &certLink = _optHeader.DirItems[kDirLink_Certificate];
2518   if (certLink.Size != 0)
2519   {
2520     CSection &sect = _sections.AddNew();
2521     sect.Name = "CERTIFICATE";
2522     sect.Va = 0;
2523     sect.Pa = certLink.Va;
2524     sect.Set_Size_for_all(certLink.Size);
2525     sect.UpdateTotalSize(_totalSize);
2526   }
2527 
2528   if (thereISDebug)
2529   {
2530     /* sometime there is some data after debug section.
2531        We don't see any reference in exe file to that data.
2532        But we suppose that it's part of EXE file */
2533 
2534     const UInt32 kAlign = 1 << 12;
2535     UInt32 alignPos = _totalSize & (kAlign - 1);
2536     if (alignPos != 0)
2537     {
2538       UInt32 size = kAlign - alignPos;
2539       RINOK(InStream_SeekSet(stream, _totalSize))
2540       buffer.Alloc(kAlign);
2541       Byte *buf = buffer;
2542       size_t processed = size;
2543       RINOK(ReadStream(stream, buf, &processed))
2544 
2545       /*
2546       if (processed != 0)
2547       {
2548         printf("\ndata after debug %d, %d \n", (int)size, (int)processed);
2549         fflush(stdout);
2550       }
2551       */
2552 
2553       size_t k;
2554       for (k = 0; k < processed; k++)
2555         if (buf[k] != 0)
2556           break;
2557       if (processed < size && processed < 100)
2558         _totalSize += (UInt32)processed;
2559       else if (((_totalSize + k) & 0x1FF) == 0 || processed < size)
2560         _totalSize += (UInt32)k;
2561     }
2562   }
2563   }
2564 
2565   if (_header.NumSymbols > 0 && _header.PointerToSymbolTable >= optStart)
2566   {
2567     if (_header.NumSymbols >= (1 << 24))
2568       return S_FALSE;
2569     UInt32 size = _header.NumSymbols * 18;
2570     RINOK(InStream_SeekSet(stream, (UInt64)_header.PointerToSymbolTable + size))
2571     Byte buf[4];
2572     RINOK(ReadStream_FALSE(stream, buf, 4))
2573     UInt32 size2 = Get32(buf);
2574     if (size2 >= (1 << 28))
2575       return S_FALSE;
2576     size += size2;
2577 
2578     CSection &sect = _sections.AddNew();
2579     sect.Name = "COFF_SYMBOLS";
2580     sect.Va = 0;
2581     sect.Pa = _header.PointerToSymbolTable;
2582     sect.Set_Size_for_all(size);
2583     sect.UpdateTotalSize(_totalSize);
2584   }
2585 
2586   {
2587     CObjectVector<CSection> sections = _sections;
2588     sections.Sort();
2589     UInt32 limit = (1 << 12);
2590     unsigned num = 0;
2591     FOR_VECTOR (k, sections)
2592     {
2593       const CSection &s = sections[k];
2594       if (s.Pa > limit)
2595       {
2596         CSection &s2 = _sections.AddNew();
2597         s2.Pa = s2.Va = limit;
2598         s2.Set_Size_for_all(s.Pa - limit);
2599         s2.IsAdditionalSection = true;
2600         s2.Name.Add_Char('[');
2601         s2.Name.Add_UInt32(num++);
2602         s2.Name.Add_Char(']');
2603         limit = s.Pa;
2604       }
2605       UInt32 next = s.Pa + s.PSize;
2606       if (next < s.Pa)
2607         break;
2608       if (next >= limit)
2609         limit = next;
2610     }
2611   }
2612 
2613 
2614   if (IsOpt())
2615   if (_optHeader.CheckSum != 0)
2616   {
2617     RINOK(InStream_SeekToBegin(stream))
2618     UInt32 checkSum = 0;
2619     RINOK(CalcCheckSum(stream, _totalSize, optStart + k_CheckSum_Field_Offset, checkSum))
2620     _checksumError = (checkSum != _optHeader.CheckSum);
2621   }
2622 
2623 
2624   if (!_allowTail)
2625   {
2626     UInt64 fileSize;
2627     RINOK(InStream_GetSize_SeekToEnd(stream, fileSize))
2628     if (fileSize > _totalSize)
2629       return S_FALSE;
2630   }
2631 
2632   bool _parseResources = true;
2633   // _parseResources = false; // for debug
2634 
2635   UInt64 mainSize = 0, mainSize2 = 0;
2636 
2637   for (i = 0; i < _sections.Size(); i++)
2638   {
2639     const CSection &sect = _sections[i];
2640     if (IsOpt())
2641     if (_parseResources && sect.Name == ".rsrc")
2642     {
2643       // 20.01: we try to parse only first copy of .rsrc section.
2644       _parseResources = false;
2645       const unsigned numMixItems = _mixItems.Size();
2646       HRESULT res = OpenResources(i, stream, callback);
2647       if (res == S_OK)
2648       {
2649         _resourcesPrefix = sect.Name.Ptr();
2650         _resourcesPrefix.Add_PathSepar();
2651         FOR_VECTOR (j, _items)
2652         {
2653           const CResItem &item = _items[j];
2654           if (item.Enabled)
2655           {
2656             CMixItem mixItem;
2657             mixItem.SectionIndex = (int)i;
2658             mixItem.ResourceIndex = (int)j;
2659             if (item.IsRcDataOrUnknown())
2660             {
2661               if (item.Size >= mainSize)
2662               {
2663                 mainSize2 = mainSize;
2664                 mainSize = item.Size;
2665                 _mainSubfile = (Int32)(int)_mixItems.Size();
2666               }
2667               else if (item.Size >= mainSize2)
2668                 mainSize2 = item.Size;
2669             }
2670             _mixItems.Add(mixItem);
2671           }
2672         }
2673         // 9.29: .rsrc_2 code was commented.
2674         // .rsrc_1 now must include that .rsrc_2 block.
2675         /*
2676         if (sect.PSize > sect.VSize)
2677         {
2678           int numBits = _optHeader.GetNumFileAlignBits();
2679           if (numBits >= 0)
2680           {
2681             UInt32 mask = (1 << numBits) - 1;
2682             UInt32 end = ((sect.VSize + mask) & ~mask);
2683 
2684             if (sect.PSize > end)
2685             {
2686               CSection &sect2 = _sections.AddNew();
2687               sect2.Flags = 0;
2688               sect2.Pa = sect.Pa + end;
2689               sect2.Va = sect.Va + end;
2690               sect2.PSize = sect.PSize - end;
2691               sect2.VSize = sect2.PSize;
2692               sect2.Name = ".rsrc_2";
2693               sect2.Time = 0;
2694               sect2.IsAdditionalSection = true;
2695             }
2696           }
2697         }
2698         */
2699         continue;
2700       }
2701       if (res != S_FALSE)
2702         return res;
2703       _mixItems.DeleteFrom(numMixItems);
2704       CloseResources();
2705     }
2706 
2707     if (sect.IsAdditionalSection)
2708     {
2709       if (sect.PSize >= mainSize)
2710       {
2711         mainSize2 = mainSize;
2712         mainSize = sect.PSize;
2713         _mainSubfile = (Int32)(int)_mixItems.Size();
2714       }
2715       else if (sect.PSize >= mainSize2)
2716         mainSize2 = sect.PSize;
2717     }
2718 
2719     CMixItem mixItem;
2720     mixItem.SectionIndex = (int)i;
2721     _mixItems.Add(mixItem);
2722   }
2723 
2724   if (mainSize2 >= (1 << 20) && mainSize < mainSize2 * 2)
2725     _mainSubfile = -1;
2726 
2727   for (i = 0; i < _mixItems.Size(); i++)
2728   {
2729     const CMixItem &mixItem = _mixItems[i];
2730     if (mixItem.StringIndex < 0 && mixItem.ResourceIndex < 0 && _sections[mixItem.SectionIndex].Name == "_winzip_")
2731     {
2732       _mainSubfile = (Int32)(int)i;
2733       break;
2734     }
2735   }
2736 
2737   for (i = 0; i < _versionKeys.Size(); i++)
2738   {
2739     if (i != 0)
2740       _versionFullString.Add_LF();
2741     const CStringKeyValue &k = _versionKeys[i];
2742     _versionFullString += k.Key;
2743     _versionFullString += ": ";
2744     _versionFullString += k.Value;
2745   }
2746 
2747   {
2748     int keyIndex = FindKey(_versionKeys, "OriginalFilename");
2749     if (keyIndex >= 0)
2750       _originalFilename = _versionKeys[keyIndex].Value;
2751   }
2752   {
2753     int keyIndex = FindKey(_versionKeys, "FileDescription");
2754     if (keyIndex >= 0)
2755       _versionShortString = _versionKeys[keyIndex].Value;
2756   }
2757   {
2758     int keyIndex = FindKey(_versionKeys, "FileVersion");
2759     if (keyIndex >= 0)
2760     {
2761       _versionShortString.Add_Space();
2762       _versionShortString += _versionKeys[keyIndex].Value;
2763     }
2764   }
2765 
2766   return S_OK;
2767 }
2768 
2769 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback))
2770 {
2771   COM_TRY_BEGIN
2772   Close();
2773   RINOK(Open2(inStream, callback))
2774   _stream = inStream;
2775   return S_OK;
2776   COM_TRY_END
2777 }
2778 
2779 void CHandler::CloseResources()
2780 {
2781   _usedRes.Free();
2782   _items.Clear();
2783   _strings.Clear();
2784   _versionFiles.Clear();
2785   _buf.Free();
2786   _versionFullString.Empty();
2787   _versionShortString.Empty();
2788   _originalFilename.Empty();
2789   _versionKeys.Clear();
2790 }
2791 
2792 Z7_COM7F_IMF(CHandler::Close())
2793 {
2794   _totalSize = 0;
2795   _checksumError = false;
2796   _sectionsError = false;
2797   _mainSubfile = -1;
2798 
2799   _stream.Release();
2800   _sections.Clear();
2801   _mixItems.Clear();
2802   CloseResources();
2803   return S_OK;
2804 }
2805 
2806 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
2807 {
2808   *numItems = _mixItems.Size();
2809   return S_OK;
2810 }
2811 
2812 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
2813     Int32 testMode, IArchiveExtractCallback *extractCallback))
2814 {
2815   COM_TRY_BEGIN
2816   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
2817   if (allFilesMode)
2818     numItems = _mixItems.Size();
2819   if (numItems == 0)
2820     return S_OK;
2821   UInt64 totalSize = 0;
2822   UInt32 i;
2823   for (i = 0; i < numItems; i++)
2824   {
2825     const CMixItem &mixItem = _mixItems[allFilesMode ? i : indices[i]];
2826     UInt64 size;
2827     if (mixItem.StringIndex >= 0)
2828       size = _strings[mixItem.StringIndex].FinalSize();
2829     else if (mixItem.VersionIndex >= 0)
2830       size = _versionFiles[mixItem.VersionIndex].Size();
2831     else if (mixItem.ResourceIndex >= 0)
2832       size = _items[mixItem.ResourceIndex].GetSize();
2833     else
2834       size = _sections[mixItem.SectionIndex].GetSize_Extract();
2835     totalSize += size;
2836   }
2837   RINOK(extractCallback->SetTotal(totalSize))
2838 
2839   CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
2840   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
2841   lps->Init(extractCallback, false);
2842   CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> inStream;
2843   inStream->SetStream(_stream);
2844 
2845   totalSize = 0;
2846   UInt64 currentItemSize;
2847 
2848   for (i = 0;; i++, totalSize += currentItemSize)
2849   {
2850     lps->InSize = lps->OutSize = totalSize;
2851     RINOK(lps->SetCur())
2852     if (i >= numItems)
2853       break;
2854     const Int32 askMode = testMode ?
2855         NExtract::NAskMode::kTest :
2856         NExtract::NAskMode::kExtract;
2857     const UInt32 index = allFilesMode ? i : indices[i];
2858 
2859     CMyComPtr<ISequentialOutStream> outStream;
2860     RINOK(extractCallback->GetStream(index, &outStream, askMode))
2861     const CMixItem &mixItem = _mixItems[index];
2862 
2863     const CSection &sect = _sections[mixItem.SectionIndex];
2864     bool isOk = true;
2865     if (mixItem.StringIndex >= 0)
2866     {
2867       const CStringItem &item = _strings[mixItem.StringIndex];
2868       currentItemSize = item.FinalSize();
2869       if (!testMode && !outStream)
2870         continue;
2871 
2872       RINOK(extractCallback->PrepareOperation(askMode))
2873       if (outStream)
2874         RINOK(WriteStream(outStream, item.Buf, item.FinalSize()))
2875     }
2876     else if (mixItem.VersionIndex >= 0)
2877     {
2878       const CByteBuffer &item = _versionFiles[mixItem.VersionIndex];
2879       currentItemSize = item.Size();
2880       if (!testMode && !outStream)
2881         continue;
2882 
2883       RINOK(extractCallback->PrepareOperation(askMode))
2884       if (outStream)
2885         RINOK(WriteStream(outStream, item, item.Size()))
2886     }
2887     else if (mixItem.ResourceIndex >= 0)
2888     {
2889       const CResItem &item = _items[mixItem.ResourceIndex];
2890       currentItemSize = item.GetSize();
2891       if (!testMode && !outStream)
2892         continue;
2893 
2894       RINOK(extractCallback->PrepareOperation(askMode))
2895       size_t offset = item.Offset - sect.Va;
2896       if (!CheckItem(sect, item, offset))
2897         isOk = false;
2898       else if (outStream)
2899       {
2900         if (item.HeaderSize != 0)
2901           RINOK(WriteStream(outStream, item.Header, item.HeaderSize))
2902         RINOK(WriteStream(outStream, _buf + offset, item.Size))
2903       }
2904     }
2905     else
2906     {
2907       currentItemSize = sect.GetSize_Extract();
2908       if (!testMode && !outStream)
2909         continue;
2910 
2911       RINOK(extractCallback->PrepareOperation(askMode))
2912       RINOK(InStream_SeekSet(_stream, sect.Pa))
2913       inStream->Init(currentItemSize);
2914       RINOK(copyCoder.Interface()->Code(inStream, outStream, NULL, NULL, lps))
2915       isOk = (copyCoder->TotalSize == currentItemSize);
2916     }
2917 
2918     outStream.Release();
2919     RINOK(extractCallback->SetOperationResult(isOk ?
2920         NExtract::NOperationResult::kOK :
2921         NExtract::NOperationResult::kDataError))
2922   }
2923   return S_OK;
2924   COM_TRY_END
2925 }
2926 
2927 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
2928 {
2929   COM_TRY_BEGIN
2930   *stream = NULL;
2931 
2932   const CMixItem &mixItem = _mixItems[index];
2933   const CSection &sect = _sections[mixItem.SectionIndex];
2934   if (mixItem.IsSectionItem())
2935     return CreateLimitedInStream(_stream, sect.Pa, sect.GetSize_Extract(), stream);
2936 
2937   CBufInStream *inStreamSpec = new CBufInStream;
2938   CMyComPtr<ISequentialInStream> streamTemp = inStreamSpec;
2939   CReferenceBuf *referenceBuf = new CReferenceBuf;
2940   CMyComPtr<IUnknown> ref = referenceBuf;
2941   if (mixItem.StringIndex >= 0)
2942   {
2943     const CStringItem &item = _strings[mixItem.StringIndex];
2944     referenceBuf->Buf.CopyFrom(item.Buf, item.FinalSize());
2945   }
2946   else if (mixItem.VersionIndex >= 0)
2947   {
2948     const CByteBuffer &item = _versionFiles[mixItem.VersionIndex];
2949     referenceBuf->Buf.CopyFrom(item, item.Size());
2950   }
2951   else
2952   {
2953     const CResItem &item = _items[mixItem.ResourceIndex];
2954     size_t offset = item.Offset - sect.Va;
2955     if (!CheckItem(sect, item, offset))
2956       return S_FALSE;
2957     if (item.HeaderSize == 0)
2958     {
2959       CBufInStream *streamSpec = new CBufInStream;
2960       CMyComPtr<IInStream> streamTemp2 = streamSpec;
2961       streamSpec->Init(_buf + offset, item.Size, (IInArchive *)this);
2962       *stream = streamTemp2.Detach();
2963       return S_OK;
2964     }
2965     referenceBuf->Buf.Alloc(item.HeaderSize + item.Size);
2966     memcpy(referenceBuf->Buf, item.Header, item.HeaderSize);
2967     if (item.Size != 0)
2968       memcpy(referenceBuf->Buf + item.HeaderSize, _buf + offset, item.Size);
2969   }
2970   inStreamSpec->Init(referenceBuf);
2971 
2972   *stream = streamTemp.Detach();
2973   return S_OK;
2974   COM_TRY_END
2975 }
2976 
2977 Z7_COM7F_IMF(CHandler::AllowTail(Int32 allowTail))
2978 {
2979   _allowTail = IntToBool(allowTail);
2980   return S_OK;
2981 }
2982 
2983 static const Byte k_Signature[] = { 'M', 'Z' };
2984 
2985 REGISTER_ARC_I(
2986   "PE", "exe dll sys", NULL, 0xDD,
2987   k_Signature,
2988   0,
2989   NArcInfoFlags::kPreArc,
2990   IsArc_Pe)
2991 
2992 }
2993 
2994 namespace NCoff {
2995 
2996 API_FUNC_static_IsArc IsArc_Coff(const Byte *p, size_t size)
2997 {
2998   if (size < NPe::kCoffHeaderSize)
2999     return k_IsArc_Res_NEED_MORE;
3000   NPe::CHeader header;
3001   if (!header.ParseCoff(p))
3002     return k_IsArc_Res_NO;
3003   return k_IsArc_Res_YES;
3004 }
3005 }
3006 
3007 /*
3008 static const Byte k_Signature[] =
3009 {
3010     2, 0x4C, 0x01, // x86
3011     2, 0x64, 0x86, // x64
3012     2, 0x64, 0xAA  // ARM64
3013 };
3014 REGISTER_ARC_I_CLS(
3015 */
3016 
3017 REGISTER_ARC_I_CLS_NO_SIG(
3018   NPe::CHandler(true),
3019   "COFF", "obj", NULL, 0xC6,
3020   // k_Signature,
3021   0,
3022   // NArcInfoFlags::kMultiSignature |
3023   NArcInfoFlags::kStartOpen,
3024   IsArc_Coff)
3025 }
3026 
3027 
3028 namespace NTe {
3029 
3030 // Terse Executable (TE) image
3031 
3032 struct CDataDir
3033 {
3034   UInt32 Va;
3035   UInt32 Size;
3036 
3037   void Parse(const Byte *p)
3038   {
3039     G32(0, Va);
3040     G32(4, Size);
3041   }
3042 };
3043 
3044 static const UInt32 kHeaderSize = 40;
3045 
3046 static bool FindValue(const CUInt32PCharPair *pairs, unsigned num, UInt32 value)
3047 {
3048   for (unsigned i = 0; i < num; i++)
3049     if (pairs[i].Value == value)
3050       return true;
3051   return false;
3052 }
3053 
3054 #define MY_FIND_VALUE(pairs, val) FindValue(pairs, Z7_ARRAY_SIZE(pairs), val)
3055 #define MY_FIND_VALUE_2(strings, val) (val < Z7_ARRAY_SIZE(strings) && strings[val])
3056 
3057 static const UInt32 kNumSection_MAX = 32;
3058 
3059 struct CHeader
3060 {
3061   UInt16 Machine;
3062   Byte NumSections;
3063   Byte SubSystem;
3064   UInt16 StrippedSize;
3065   /*
3066   UInt32 AddressOfEntryPoint;
3067   UInt32 BaseOfCode;
3068   UInt64 ImageBase;
3069   */
3070   CDataDir DataDir[2]; // base relocation and debug directory
3071 
3072   bool ConvertPa(UInt32 &pa) const
3073   {
3074     if (pa < StrippedSize)
3075       return false;
3076     pa = pa - StrippedSize + kHeaderSize;
3077     return true;
3078   }
3079   bool Parse(const Byte *p);
3080 };
3081 
3082 bool CHeader::Parse(const Byte *p)
3083 {
3084   NumSections = p[4];
3085   if (NumSections > kNumSection_MAX)
3086     return false;
3087   SubSystem = p[5];
3088   G16(2, Machine);
3089   G16(6, StrippedSize);
3090   /*
3091   G32(8, AddressOfEntryPoint);
3092   G32(12, BaseOfCode);
3093   G64(16, ImageBase);
3094   */
3095   for (unsigned i = 0; i < 2; i++)
3096   {
3097     CDataDir &dd = DataDir[i];
3098     dd.Parse(p + 24 + i * 8);
3099     if (dd.Size >= ((UInt32)1 << 28))
3100       return false;
3101   }
3102   return
3103       MY_FIND_VALUE(NPe::g_MachinePairs, Machine) &&
3104       MY_FIND_VALUE_2(NPe::g_SubSystems, SubSystem);
3105 }
3106 
3107 API_FUNC_static_IsArc IsArc_Te(const Byte *p, size_t size)
3108 {
3109   if (size < 2)
3110     return k_IsArc_Res_NEED_MORE;
3111   if (p[0] != 'V' || p[1] != 'Z')
3112     return k_IsArc_Res_NO;
3113   if (size < kHeaderSize)
3114     return k_IsArc_Res_NEED_MORE;
3115 
3116   CHeader h;
3117   if (!h.Parse(p))
3118     return k_IsArc_Res_NO;
3119   return k_IsArc_Res_YES;
3120 }
3121 }
3122 
3123 
3124 struct CSection
3125 {
3126   Byte Name[NPe::kNameSize];
3127 
3128   UInt32 ExtractSize;
3129   UInt32 VSize;
3130   UInt32 Va;
3131   UInt32 PSize;
3132   UInt32 Pa;
3133   UInt32 Flags;
3134   // UInt16 NumRelocs;
3135 
3136   void Parse(const Byte *p)
3137   {
3138     memcpy(Name, p, NPe::kNameSize);
3139     G32(8, VSize);
3140     G32(12, Va);
3141     G32(16, PSize);
3142     G32(20, Pa);
3143     // G32(p + 32, NumRelocs);
3144     G32(36, Flags);
3145     ExtractSize = (VSize && VSize < PSize) ? VSize : PSize;
3146   }
3147 
3148   bool Check() const
3149   {
3150     return
3151         Pa <= ((UInt32)1 << 30) &&
3152         PSize <= ((UInt32)1 << 30);
3153   }
3154 
3155   UInt32 GetSize_Extract() const
3156   {
3157     return ExtractSize;
3158   }
3159 
3160   void UpdateTotalSize(UInt32 &totalSize)
3161   {
3162     const UInt32 t = Pa + PSize;
3163     if (totalSize < t)
3164         totalSize = t;
3165   }
3166 };
3167 
3168 
3169 Z7_CLASS_IMP_CHandler_IInArchive_2(
3170   IInArchiveGetStream,
3171   IArchiveAllowTail
3172 )
3173   CRecordVector<CSection> _items;
3174   CMyComPtr<IInStream> _stream;
3175   UInt32 _totalSize;
3176   bool _allowTail;
3177   CHeader _h;
3178 
3179   HRESULT Open2(IInStream *stream);
3180 public:
3181   CHandler(): _allowTail(false) {}
3182 };
3183 
3184 static const Byte kProps[] =
3185 {
3186   kpidPath,
3187   kpidSize,
3188   kpidPackSize,
3189   kpidVirtualSize,
3190   kpidCharacts,
3191   kpidOffset,
3192   kpidVa
3193 };
3194 
3195 enum
3196 {
3197   kpidSubSystem = kpidUserDefined
3198   // , kpidImageBase
3199 };
3200 
3201 static const CStatProp kArcProps[] =
3202 {
3203   // { NULL, kpidHeadersSize, VT_UI4 },
3204   { NULL, kpidCpu, VT_BSTR},
3205   { "Subsystem", kpidSubSystem, VT_BSTR },
3206   // { "Image Base", kpidImageBase, VT_UI8 }
3207 };
3208 
3209 IMP_IInArchive_Props
3210 IMP_IInArchive_ArcProps_WITH_NAME
3211 
3212 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
3213 {
3214   COM_TRY_BEGIN
3215   NCOM::CPropVariant prop;
3216   switch (propID)
3217   {
3218     case kpidPhySize: prop = _totalSize; break;
3219     case kpidCpu: PAIR_TO_PROP(NPe::g_MachinePairs, _h.Machine, prop); break;
3220     case kpidSubSystem: TYPE_TO_PROP(NPe::g_SubSystems, _h.SubSystem, prop); break;
3221     /*
3222     case kpidImageBase: prop = _h.ImageBase; break;
3223     case kpidAddressOfEntryPoint: prop = _h.AddressOfEntryPoint; break;
3224     case kpidBaseOfCode: prop = _h.BaseOfCode; break;
3225     */
3226   }
3227   prop.Detach(value);
3228   return S_OK;
3229   COM_TRY_END
3230 }
3231 
3232 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
3233 {
3234   COM_TRY_BEGIN
3235   NCOM::CPropVariant prop;
3236   {
3237     const CSection &item = _items[index];
3238     switch (propID)
3239     {
3240       case kpidPath:
3241       {
3242         AString name;
3243         NPe::GetName(item.Name, name);
3244         prop = MultiByteToUnicodeString(name);
3245         break;
3246       }
3247       case kpidSize: prop = (UInt64)item.GetSize_Extract(); break;
3248       case kpidPackSize: prop = (UInt64)item.PSize; break;
3249       case kpidVirtualSize: prop = (UInt64)item.VSize; break;
3250       case kpidOffset: prop = item.Pa; break;
3251       case kpidVa: prop = item.Va; break;
3252       case kpidCharacts: FLAGS_TO_PROP(NPe::g_SectFlags, item.Flags, prop); break;
3253     }
3254   }
3255   prop.Detach(value);
3256   return S_OK;
3257   COM_TRY_END
3258 }
3259 
3260 HRESULT CHandler::Open2(IInStream *stream)
3261 {
3262   Byte h[kHeaderSize];
3263   RINOK(ReadStream_FALSE(stream, h, kHeaderSize))
3264   if (h[0] != 'V' || h[1] != 'Z')
3265     return S_FALSE;
3266   if (!_h.Parse(h))
3267     return S_FALSE;
3268 
3269   UInt32 headerSize = NPe::kSectionSize * (UInt32)_h.NumSections;
3270   CByteArr buf(headerSize);
3271   RINOK(ReadStream_FALSE(stream, buf, headerSize))
3272   headerSize += kHeaderSize;
3273 
3274   _totalSize = headerSize;
3275   _items.ClearAndReserve(_h.NumSections);
3276   for (UInt32 i = 0; i < _h.NumSections; i++)
3277   {
3278     CSection sect;
3279     sect.Parse(buf + i * NPe::kSectionSize);
3280     if (!_h.ConvertPa(sect.Pa))
3281       return S_FALSE;
3282     if (sect.Pa < headerSize)
3283       return S_FALSE;
3284     if (!sect.Check())
3285       return S_FALSE;
3286     _items.AddInReserved(sect);
3287     sect.UpdateTotalSize(_totalSize);
3288   }
3289 
3290   if (!_allowTail)
3291   {
3292     UInt64 fileSize;
3293     RINOK(InStream_GetSize_SeekToEnd(stream, fileSize))
3294     if (fileSize > _totalSize)
3295       return S_FALSE;
3296   }
3297 
3298   return S_OK;
3299 }
3300 
3301 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream,
3302     const UInt64 * /* maxCheckStartPosition */,
3303     IArchiveOpenCallback * /* openArchiveCallback */))
3304 {
3305   COM_TRY_BEGIN
3306   Close();
3307   // try
3308   {
3309     if (Open2(inStream) != S_OK)
3310       return S_FALSE;
3311     _stream = inStream;
3312   }
3313   // catch(...) { return S_FALSE; }
3314   return S_OK;
3315   COM_TRY_END
3316 }
3317 
3318 Z7_COM7F_IMF(CHandler::Close())
3319 {
3320   _totalSize = 0;
3321   _stream.Release();
3322   _items.Clear();
3323   return S_OK;
3324 }
3325 
3326 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
3327 {
3328   *numItems = _items.Size();
3329   return S_OK;
3330 }
3331 
3332 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
3333     Int32 testMode, IArchiveExtractCallback *extractCallback))
3334 {
3335   COM_TRY_BEGIN
3336   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
3337   if (allFilesMode)
3338     numItems = _items.Size();
3339   if (numItems == 0)
3340     return S_OK;
3341   UInt64 totalSize = 0;
3342   UInt32 i;
3343   for (i = 0; i < numItems; i++)
3344     totalSize += _items[allFilesMode ? i : indices[i]].GetSize_Extract();
3345   RINOK(extractCallback->SetTotal(totalSize))
3346 
3347   CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
3348   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
3349   lps->Init(extractCallback, false);
3350   CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> inStream;
3351   inStream->SetStream(_stream);
3352 
3353   totalSize = 0;
3354 
3355   for (i = 0;; i++)
3356   {
3357     lps->InSize = lps->OutSize = totalSize;
3358     RINOK(lps->SetCur())
3359     if (i >= numItems)
3360       break;
3361     int opRes;
3362     {
3363     CMyComPtr<ISequentialOutStream> realOutStream;
3364     const Int32 askMode = testMode ?
3365         NExtract::NAskMode::kTest :
3366         NExtract::NAskMode::kExtract;
3367     const UInt32 index = allFilesMode ? i : indices[i];
3368     const CSection &item = _items[index];
3369     RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
3370     const UInt32 size = item.GetSize_Extract();
3371     totalSize += size;
3372 
3373     if (!testMode && !realOutStream)
3374       continue;
3375     RINOK(extractCallback->PrepareOperation(askMode))
3376     RINOK(InStream_SeekSet(_stream, item.Pa))
3377     inStream->Init(size);
3378     RINOK(copyCoder.Interface()->Code(inStream, realOutStream, NULL, NULL, lps))
3379 
3380       opRes = (copyCoder->TotalSize == size) ?
3381           NExtract::NOperationResult::kOK : (copyCoder->TotalSize < size) ?
3382           NExtract::NOperationResult::kUnexpectedEnd :
3383           NExtract::NOperationResult::kDataError;
3384     }
3385     RINOK(extractCallback->SetOperationResult(opRes))
3386   }
3387   return S_OK;
3388   COM_TRY_END
3389 }
3390 
3391 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
3392 {
3393   COM_TRY_BEGIN
3394   const CSection &item = _items[index];
3395   return CreateLimitedInStream(_stream, item.Pa, item.GetSize_Extract(), stream);
3396   COM_TRY_END
3397 }
3398 
3399 Z7_COM7F_IMF(CHandler::AllowTail(Int32 allowTail))
3400 {
3401   _allowTail = IntToBool(allowTail);
3402   return S_OK;
3403 }
3404 
3405 static const Byte k_Signature[] = { 'V', 'Z' };
3406 
3407 REGISTER_ARC_I(
3408   "TE", "te", NULL, 0xCF,
3409   k_Signature,
3410   0,
3411   NArcInfoFlags::kPreArc,
3412   IsArc_Te)
3413 
3414 }
3415 }
3416