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