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