Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / PeHandler.cpp
1 // PeHandler.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "../../../C/CpuArch.h"\r
6 \r
7 #include "Common/DynamicBuffer.h"\r
8 #include "Common/ComTry.h"\r
9 #include "Common/IntToString.h"\r
10 #include "Common/StringConvert.h"\r
11 \r
12 #include "Windows/PropVariantUtils.h"\r
13 #include "Windows/Time.h"\r
14 \r
15 #include "../Common/LimitedStreams.h"\r
16 #include "../Common/ProgressUtils.h"\r
17 #include "../Common/RegisterArc.h"\r
18 #include "../Common/StreamObjects.h"\r
19 #include "../Common/StreamUtils.h"\r
20 \r
21 #include "../Compress/CopyCoder.h"\r
22 \r
23 #define Get16(p) GetUi16(p)\r
24 #define Get32(p) GetUi32(p)\r
25 #define Get64(p) GetUi64(p)\r
26 \r
27 using namespace NWindows;\r
28 \r
29 namespace NArchive {\r
30 namespace NPe {\r
31 \r
32 #define NUM_SCAN_SECTIONS_MAX (1 << 6)\r
33 \r
34 #define PE_SIG 0x00004550\r
35 #define PE_OptHeader_Magic_32 0x10B\r
36 #define PE_OptHeader_Magic_64 0x20B\r
37 \r
38 static AString GetDecString(UInt32 v)\r
39 {\r
40   char sz[32];\r
41   ConvertUInt64ToString(v, sz);\r
42   return sz;\r
43 }\r
44 \r
45 struct CVersion\r
46 {\r
47   UInt16 Major;\r
48   UInt16 Minor;\r
49 \r
50   void Parse(const Byte *buf);\r
51   AString GetString() const { return GetDecString(Major) + '.' + GetDecString(Minor); }\r
52 };\r
53 \r
54 void CVersion::Parse(const Byte *p)\r
55 {\r
56   Major = Get16(p);\r
57   Minor = Get16(p + 2);\r
58 }\r
59 \r
60 static const UInt32 kHeaderSize =  4 + 20;\r
61 \r
62 struct CHeader\r
63 {\r
64   UInt16 NumSections;\r
65   UInt32 Time;\r
66   UInt32 PointerToSymbolTable;\r
67   UInt32 NumSymbols;\r
68   UInt16 OptHeaderSize;\r
69   UInt16 Flags;\r
70   UInt16 Machine;\r
71 \r
72   bool Parse(const Byte *buf);\r
73 };\r
74 \r
75 bool CHeader::Parse(const Byte *p)\r
76 {\r
77   if (Get32(p) != PE_SIG)\r
78     return false;\r
79   p += 4;\r
80   Machine = Get16(p + 0);\r
81   NumSections = Get16(p + 2);\r
82   Time = Get32(p + 4);\r
83   PointerToSymbolTable = Get32(p + 8);\r
84   NumSymbols = Get32(p + 12);\r
85   OptHeaderSize = Get16(p + 16);\r
86   Flags = Get16(p + 18);\r
87   return true;\r
88 }\r
89 \r
90 struct CDirLink\r
91 {\r
92   UInt32 Va;\r
93   UInt32 Size;\r
94   void Parse(const Byte *p);\r
95 };\r
96 \r
97 void CDirLink::Parse(const Byte *p)\r
98 {\r
99   Va = Get32(p);\r
100   Size = Get32(p + 4);\r
101 }\r
102 \r
103 enum\r
104 {\r
105   kDirLink_Certificate = 4,\r
106   kDirLink_Debug = 6\r
107 };\r
108 \r
109 struct CDebugEntry\r
110 {\r
111   UInt32 Flags;\r
112   UInt32 Time;\r
113   CVersion Ver;\r
114   UInt32 Type;\r
115   UInt32 Size;\r
116   UInt32 Va;\r
117   UInt32 Pa;\r
118   \r
119   void Parse(const Byte *p);\r
120 };\r
121 \r
122 void CDebugEntry::Parse(const Byte *p)\r
123 {\r
124   Flags = Get32(p);\r
125   Time = Get32(p + 4);\r
126   Ver.Parse(p + 8);\r
127   Type = Get32(p + 12);\r
128   Size = Get32(p + 16);\r
129   Va = Get32(p + 20);\r
130   Pa = Get32(p + 24);\r
131 }\r
132 \r
133 static const UInt32 kNumDirItemsMax = 16;\r
134 \r
135 struct COptHeader\r
136 {\r
137   UInt16 Magic;\r
138   Byte LinkerVerMajor;\r
139   Byte LinkerVerMinor;\r
140 \r
141   UInt32 CodeSize;\r
142   UInt32 InitDataSize;\r
143   UInt32 UninitDataSize;\r
144   \r
145   // UInt32 AddressOfEntryPoint;\r
146   // UInt32 BaseOfCode;\r
147   // UInt32 BaseOfData32;\r
148   UInt64 ImageBase;\r
149 \r
150   UInt32 SectAlign;\r
151   UInt32 FileAlign;\r
152 \r
153   CVersion OsVer;\r
154   CVersion ImageVer;\r
155   CVersion SubsysVer;\r
156   \r
157   UInt32 ImageSize;\r
158   UInt32 HeadersSize;\r
159   UInt32 CheckSum;\r
160   UInt16 SubSystem;\r
161   UInt16 DllCharacts;\r
162 \r
163   UInt64 StackReserve;\r
164   UInt64 StackCommit;\r
165   UInt64 HeapReserve;\r
166   UInt64 HeapCommit;\r
167 \r
168   UInt32 NumDirItems;\r
169   CDirLink DirItems[kNumDirItemsMax];\r
170 \r
171   bool Is64Bit() const { return Magic == PE_OptHeader_Magic_64; }\r
172   bool Parse(const Byte *p, UInt32 size);\r
173 \r
174   int GetNumFileAlignBits() const\r
175   {\r
176     for (int i = 9; i <= 16; i++)\r
177       if (((UInt32)1 << i) == FileAlign)\r
178         return i;\r
179     return -1;\r
180   }\r
181 };\r
182 \r
183 bool COptHeader::Parse(const Byte *p, UInt32 size)\r
184 {\r
185   Magic = Get16(p);\r
186   switch (Magic)\r
187   {\r
188     case PE_OptHeader_Magic_32:\r
189     case PE_OptHeader_Magic_64:\r
190       break;\r
191     default:\r
192       return false;\r
193   }\r
194   LinkerVerMajor = p[2];\r
195   LinkerVerMinor = p[3];\r
196   \r
197   bool hdr64 = Is64Bit();\r
198   \r
199   CodeSize = Get32(p + 4);\r
200   InitDataSize = Get32(p + 8);\r
201   UninitDataSize = Get32(p + 12);\r
202 \r
203   // AddressOfEntryPoint = Get32(p + 16);\r
204   // BaseOfCode = Get32(p + 20);\r
205   // BaseOfData32 = hdr64 ? 0: Get32(p + 24);\r
206   ImageBase = hdr64 ? GetUi64(p + 24) : Get32(p + 28);\r
207 \r
208   SectAlign = Get32(p + 32);\r
209   FileAlign = Get32(p + 36);\r
210 \r
211   OsVer.Parse(p + 40);\r
212   ImageVer.Parse(p + 44);\r
213   SubsysVer.Parse(p + 48);\r
214 \r
215   // reserved = Get32(p + 52);\r
216 \r
217   ImageSize = Get32(p + 56);\r
218   HeadersSize = Get32(p + 60);\r
219   CheckSum = Get32(p + 64);\r
220   SubSystem = Get16(p + 68);\r
221   DllCharacts = Get16(p + 70);\r
222 \r
223   if (hdr64)\r
224   {\r
225     StackReserve = Get64(p + 72);\r
226     StackCommit = Get64(p + 80);\r
227     HeapReserve = Get64(p + 88);\r
228     HeapCommit = Get64(p + 96);\r
229   }\r
230   else\r
231   {\r
232     StackReserve = Get32(p + 72);\r
233     StackCommit = Get32(p + 76);\r
234     HeapReserve = Get32(p + 80);\r
235     HeapCommit = Get32(p + 84);\r
236   }\r
237   UInt32 pos = (hdr64 ? 108 : 92);\r
238   NumDirItems = Get32(p + pos);\r
239   pos += 4;\r
240   if (pos + 8 * NumDirItems != size)\r
241     return false;\r
242   for (UInt32 i = 0; i < NumDirItems && i < kNumDirItemsMax; i++)\r
243     DirItems[i].Parse(p + pos + i * 8);\r
244   return true;\r
245 }\r
246 \r
247 static const UInt32 kSectionSize = 40;\r
248 \r
249 struct CSection\r
250 {\r
251   AString Name;\r
252 \r
253   UInt32 VSize;\r
254   UInt32 Va;\r
255   UInt32 PSize;\r
256   UInt32 Pa;\r
257   UInt32 Flags;\r
258   UInt32 Time;\r
259   // UInt16 NumRelocs;\r
260   bool IsDebug;\r
261   bool IsRealSect;\r
262   bool IsAdditionalSection;\r
263 \r
264   CSection(): IsRealSect(false), IsDebug(false), IsAdditionalSection(false) {}\r
265   UInt64 GetPackSize() const { return PSize; }\r
266 \r
267   void UpdateTotalSize(UInt32 &totalSize)\r
268   {\r
269     UInt32 t = Pa + PSize;\r
270     if (t > totalSize)\r
271       totalSize = t;\r
272   }\r
273   void Parse(const Byte *p);\r
274 };\r
275 \r
276 static bool operator <(const CSection &a1, const CSection &a2) { return (a1.Pa < a2.Pa) || ((a1.Pa == a2.Pa) && (a1.PSize < a2.PSize)) ; }\r
277 static bool operator ==(const CSection &a1, const CSection &a2) { return (a1.Pa == a2.Pa) && (a1.PSize == a2.PSize); }\r
278 \r
279 static AString GetName(const Byte *name)\r
280 {\r
281   const int kNameSize = 8;\r
282   AString res;\r
283   char *p = res.GetBuffer(kNameSize);\r
284   memcpy(p, name, kNameSize);\r
285   p[kNameSize] = 0;\r
286   res.ReleaseBuffer();\r
287   return res;\r
288 }\r
289 \r
290 void CSection::Parse(const Byte *p)\r
291 {\r
292   Name = GetName(p);\r
293   VSize = Get32(p + 8);\r
294   Va = Get32(p + 12);\r
295   PSize = Get32(p + 16);\r
296   Pa = Get32(p + 20);\r
297   // NumRelocs = Get16(p + 32);\r
298   Flags = Get32(p + 36);\r
299 }\r
300 \r
301 static const CUInt32PCharPair g_HeaderCharacts[] =\r
302 {\r
303   {  1, "Executable" },\r
304   { 13, "DLL" },\r
305   {  8, "32-bit" },\r
306   {  5, "LargeAddress" },\r
307   {  0, "NoRelocs" },\r
308   {  2, "NoLineNums" },\r
309   {  3, "NoLocalSyms" },\r
310   {  4, "AggressiveWsTrim" },\r
311   {  9, "NoDebugInfo" },\r
312   { 10, "RemovableRun" },\r
313   { 11, "NetRun" },\r
314   { 12, "System" },\r
315   { 14, "UniCPU" },\r
316   {  7, "Little-Endian" },\r
317   { 15, "Big-Endian" }\r
318 };\r
319 \r
320 static const CUInt32PCharPair g_DllCharacts[] =\r
321 {\r
322   {  6, "Relocated" },\r
323   {  7, "Integrity" },\r
324   {  8, "NX-Compatible" },\r
325   {  9, "NoIsolation" },\r
326   { 10, "NoSEH" },\r
327   { 11, "NoBind" },\r
328   { 13, "WDM" },\r
329   { 15, "TerminalServerAware" }\r
330 };\r
331 \r
332 static const CUInt32PCharPair g_SectFlags[] =\r
333 {\r
334   {  3, "NoPad" },\r
335   {  5, "Code" },\r
336   {  6, "InitializedData" },\r
337   {  7, "UninitializedData" },\r
338   {  9, "Comments" },\r
339   { 11, "Remove" },\r
340   { 12, "COMDAT" },\r
341   { 15, "GP" },\r
342   { 24, "ExtendedRelocations" },\r
343   { 25, "Discardable" },\r
344   { 26, "NotCached" },\r
345   { 27, "NotPaged" },\r
346   { 28, "Shared" },\r
347   { 29, "Execute" },\r
348   { 30, "Read" },\r
349   { 31, "Write" }\r
350 };\r
351 \r
352 static const CUInt32PCharPair g_MachinePairs[] =\r
353 {\r
354   { 0x014C, "x86" },\r
355   { 0x0162, "MIPS-R3000" },\r
356   { 0x0166, "MIPS-R4000" },\r
357   { 0x0168, "MIPS-R10000" },\r
358   { 0x0169, "MIPS-V2" },\r
359   { 0x0184, "Alpha" },\r
360   { 0x01A2, "SH3" },\r
361   { 0x01A3, "SH3-DSP" },\r
362   { 0x01A4, "SH3E" },\r
363   { 0x01A6, "SH4" },\r
364   { 0x01A8, "SH5" },\r
365   { 0x01C0, "ARM" },\r
366   { 0x01C2, "ARM-Thumb" },\r
367   { 0x01F0, "PPC" },\r
368   { 0x01F1, "PPC-FP" },\r
369   { 0x0200, "IA-64" },\r
370   { 0x0284, "Alpha-64" },\r
371   { 0x0200, "IA-64" },\r
372   { 0x0366, "MIPSFPU" },\r
373   { 0x8664, "x64" },\r
374   { 0x0EBC, "EFI" }\r
375 };\r
376 \r
377 static const CUInt32PCharPair g_SubSystems[] =\r
378 {\r
379   { 0, "Unknown" },\r
380   { 1, "Native" },\r
381   { 2, "Windows GUI" },\r
382   { 3, "Windows CUI" },\r
383   { 7, "Posix" },\r
384   { 9, "Windows CE" },\r
385   { 10, "EFI" },\r
386   { 11, "EFI Boot" },\r
387   { 12, "EFI Runtime" },\r
388   { 13, "EFI ROM" },\r
389   { 14, "XBOX" }\r
390 };\r
391 \r
392 static const wchar_t *g_ResTypes[] =\r
393 {\r
394   NULL,\r
395   L"CURSOR",\r
396   L"BITMAP",\r
397   L"ICON",\r
398   L"MENU",\r
399   L"DIALOG",\r
400   L"STRING",\r
401   L"FONTDIR",\r
402   L"FONT",\r
403   L"ACCELERATOR",\r
404   L"RCDATA",\r
405   L"MESSAGETABLE",\r
406   L"GROUP_CURSOR",\r
407   NULL,\r
408   L"GROUP_ICON",\r
409   NULL,\r
410   L"VERSION",\r
411   L"DLGINCLUDE",\r
412   NULL,\r
413   L"PLUGPLAY",\r
414   L"VXD",\r
415   L"ANICURSOR",\r
416   L"ANIICON",\r
417   L"HTML",\r
418   L"MANIFEST"\r
419 };\r
420 \r
421 const UInt32 kFlag = (UInt32)1 << 31;\r
422 const UInt32 kMask = ~kFlag;\r
423 \r
424 struct CTableItem\r
425 {\r
426   UInt32 Offset;\r
427   UInt32 ID;\r
428 };\r
429 \r
430 \r
431 const UInt32 kBmpHeaderSize = 14;\r
432 const UInt32 kIconHeaderSize = 22;\r
433 \r
434 struct CResItem\r
435 {\r
436   UInt32 Type;\r
437   UInt32 ID;\r
438   UInt32 Lang;\r
439 \r
440   UInt32 Size;\r
441   UInt32 Offset;\r
442 \r
443   UInt32 HeaderSize;\r
444   Byte Header[kIconHeaderSize]; // it must be enough for max size header.\r
445   bool Enabled;\r
446 \r
447   bool IsNameEqual(const CResItem &item) const { return Lang == item.Lang; }\r
448   UInt32 GetSize() const { return Size + HeaderSize; }\r
449   bool IsBmp() const { return Type == 2; }\r
450   bool IsIcon() const { return Type == 3; }\r
451   bool IsString() const { return Type == 6; }\r
452   bool IsRcData() const { return Type == 10; }\r
453   bool IsRcDataOrUnknown() const { return IsRcData() || Type > 64; }\r
454 };\r
455 \r
456 struct CStringItem\r
457 {\r
458   UInt32 Lang;\r
459   UInt32 Size;\r
460   CByteDynamicBuffer Buf;\r
461 \r
462   void AddChar(Byte c);\r
463   void AddWChar(UInt16 c);\r
464 };\r
465 \r
466 void CStringItem::AddChar(Byte c)\r
467 {\r
468   Buf.EnsureCapacity(Size + 2);\r
469   Buf[Size++] = c;\r
470   Buf[Size++] = 0;\r
471 }\r
472 \r
473 void CStringItem::AddWChar(UInt16 c)\r
474 {\r
475   if (c == '\n')\r
476   {\r
477     AddChar('\\');\r
478     c = 'n';\r
479   }\r
480   Buf.EnsureCapacity(Size + 2);\r
481   SetUi16(Buf + Size, c);\r
482   Size += 2;\r
483 }\r
484 \r
485 struct CMixItem\r
486 {\r
487   int SectionIndex;\r
488   int ResourceIndex;\r
489   int StringIndex;\r
490 \r
491   bool IsSectionItem() const { return ResourceIndex < 0 && StringIndex < 0; };\r
492 };\r
493 \r
494 struct CUsedBitmap\r
495 {\r
496   CByteBuffer Buf;\r
497 public:\r
498   void Alloc(size_t size)\r
499   {\r
500     size = (size + 7) / 8;\r
501     Buf.SetCapacity(size);\r
502     memset(Buf, 0, size);\r
503   }\r
504   void Free()\r
505   {\r
506     Buf.SetCapacity(0);\r
507   }\r
508   bool SetRange(size_t from, int size)\r
509   {\r
510     for (int i = 0; i < size; i++)\r
511     {\r
512       size_t pos = (from + i) >> 3;\r
513       Byte mask = (Byte)(1 << ((from + i) & 7));\r
514       Byte b = Buf[pos];\r
515       if ((b & mask) != 0)\r
516         return false;\r
517       Buf[pos] = b | mask;\r
518     }\r
519     return true;\r
520   }\r
521 };\r
522  \r
523 \r
524 class CHandler:\r
525   public IInArchive,\r
526   public IInArchiveGetStream,\r
527   public CMyUnknownImp\r
528 {\r
529   CMyComPtr<IInStream> _stream;\r
530   CObjectVector<CSection> _sections;\r
531   UInt32 _peOffset;\r
532   CHeader _header;\r
533   COptHeader _optHeader;\r
534   UInt32 _totalSize;\r
535   UInt32 _totalSizeLimited;\r
536   Int32 _mainSubfile;\r
537 \r
538   CRecordVector<CResItem> _items;\r
539   CObjectVector<CStringItem> _strings;\r
540 \r
541   CByteBuffer _buf;\r
542   bool _oneLang;\r
543   UString _resourceFileName;\r
544   CUsedBitmap _usedRes;\r
545   bool _parseResources;\r
546 \r
547   CRecordVector<CMixItem> _mixItems;\r
548 \r
549   HRESULT LoadDebugSections(IInStream *stream, bool &thereIsSection);\r
550   HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);\r
551   bool Parse(const Byte *buf, UInt32 size);\r
552 \r
553   void AddResNameToString(UString &s, UInt32 id) const;\r
554   UString GetLangPrefix(UInt32 lang);\r
555   HRESULT ReadString(UInt32 offset, UString &dest) const;\r
556   HRESULT ReadTable(UInt32 offset, CRecordVector<CTableItem> &items);\r
557   bool ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size);\r
558   HRESULT OpenResources(int sectIndex, IInStream *stream, IArchiveOpenCallback *callback);\r
559   void CloseResources();\r
560 \r
561 \r
562   bool CheckItem(const CSection &sect, const CResItem &item, size_t offset) const\r
563   {\r
564     return item.Offset >= sect.Va && offset <= _buf.GetCapacity() && _buf.GetCapacity() - offset >= item.Size;\r
565   }\r
566 \r
567 public:\r
568   MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)\r
569   INTERFACE_IInArchive(;)\r
570   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);\r
571 };\r
572 \r
573 bool CHandler::Parse(const Byte *buf, UInt32 size)\r
574 {\r
575   UInt32 i;\r
576   if (size < 512)\r
577     return false;\r
578   _peOffset = Get32(buf + 0x3C);\r
579   if (_peOffset >= 0x1000 || _peOffset + 512 > size || (_peOffset & 7) != 0)\r
580     return false;\r
581 \r
582   UInt32 pos = _peOffset;\r
583   if (!_header.Parse(buf + pos))\r
584     return false;\r
585   if (_header.OptHeaderSize > 512 || _header.NumSections > NUM_SCAN_SECTIONS_MAX)\r
586     return false;\r
587   pos += kHeaderSize;\r
588 \r
589   if (!_optHeader.Parse(buf + pos, _header.OptHeaderSize))\r
590     return false;\r
591 \r
592   pos += _header.OptHeaderSize;\r
593   _totalSize = pos;\r
594 \r
595   for (i = 0; i < _header.NumSections; i++, pos += kSectionSize)\r
596   {\r
597     CSection sect;\r
598     if (pos + kSectionSize > size)\r
599       return false;\r
600     sect.Parse(buf + pos);\r
601     sect.IsRealSect = true;\r
602     sect.UpdateTotalSize(_totalSize);\r
603     _sections.Add(sect);\r
604   }\r
605 \r
606   return true;\r
607 }\r
608 \r
609 enum\r
610 {\r
611   kpidSectAlign = kpidUserDefined,\r
612   kpidFileAlign,\r
613   kpidLinkerVer,\r
614   kpidOsVer,\r
615   kpidImageVer,\r
616   kpidSubsysVer,\r
617   kpidCodeSize,\r
618   kpidImageSize,\r
619   kpidInitDataSize,\r
620   kpidUnInitDataSize,\r
621   kpidHeadersSizeUnInitDataSize,\r
622   kpidSubSystem,\r
623   kpidDllCharacts,\r
624   kpidStackReserve,\r
625   kpidStackCommit,\r
626   kpidHeapReserve,\r
627   kpidHeapCommit,\r
628   kpidImageBase\r
629   // kpidAddressOfEntryPoint,\r
630   // kpidBaseOfCode,\r
631   // kpidBaseOfData32,\r
632 };\r
633 \r
634 STATPROPSTG kArcProps[] =\r
635 {\r
636   { NULL, kpidCpu, VT_BSTR},\r
637   { NULL, kpidBit64, VT_BOOL},\r
638   { NULL, kpidCharacts, VT_BSTR},\r
639   { NULL, kpidCTime, VT_FILETIME},\r
640   { NULL, kpidPhySize, VT_UI4},\r
641   { NULL, kpidHeadersSize, VT_UI4},\r
642   { NULL, kpidChecksum, VT_UI4},\r
643   { L"Image Size", kpidImageSize, VT_UI4},\r
644   { L"Section Alignment", kpidSectAlign, VT_UI4},\r
645   { L"File Alignment", kpidFileAlign, VT_UI4},\r
646   { L"Code Size", kpidCodeSize, VT_UI4},\r
647   { L"Initialized Data Size", kpidInitDataSize, VT_UI4},\r
648   { L"Uninitialized Data Size", kpidUnInitDataSize, VT_UI4},\r
649   { L"Linker Version", kpidLinkerVer, VT_BSTR},\r
650   { L"OS Version", kpidOsVer, VT_BSTR},\r
651   { L"Image Version", kpidImageVer, VT_BSTR},\r
652   { L"Subsystem Version", kpidSubsysVer, VT_BSTR},\r
653   { L"Subsystem", kpidSubSystem, VT_BSTR},\r
654   { L"DLL Characteristics", kpidDllCharacts, VT_BSTR},\r
655   { L"Stack Reserve", kpidStackReserve, VT_UI8},\r
656   { L"Stack Commit", kpidStackCommit, VT_UI8},\r
657   { L"Heap Reserve", kpidHeapReserve, VT_UI8},\r
658   { L"Heap Commit", kpidHeapCommit, VT_UI8},\r
659   { L"Image Base", kpidImageBase, VT_UI8}\r
660   // { L"Address Of Entry Point", kpidAddressOfEntryPoint, VT_UI8},\r
661   // { L"Base Of Code", kpidBaseOfCode, VT_UI8},\r
662   // { L"Base Of Data", kpidBaseOfData32, VT_UI8},\r
663 };\r
664 \r
665 STATPROPSTG kProps[] =\r
666 {\r
667   { NULL, kpidPath, VT_BSTR},\r
668   { NULL, kpidSize, VT_UI8},\r
669   { NULL, kpidPackSize, VT_UI8},\r
670   { NULL, kpidCharacts, VT_BSTR},\r
671   { NULL, kpidOffset, VT_UI8},\r
672   { NULL, kpidVa, VT_UI8}\r
673 };\r
674 \r
675 IMP_IInArchive_Props\r
676 IMP_IInArchive_ArcProps_WITH_NAME\r
677 \r
678 static void VerToProp(const CVersion &v, NCOM::CPropVariant &prop)\r
679 {\r
680   StringToProp(v.GetString(), prop);\r
681 }\r
682 \r
683 void TimeToProp(UInt32 unixTime, NCOM::CPropVariant &prop)\r
684 {\r
685   if (unixTime != 0)\r
686   {\r
687     FILETIME ft;\r
688     NTime::UnixTimeToFileTime(unixTime, ft);\r
689     prop = ft;\r
690   }\r
691 }\r
692 \r
693 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)\r
694 {\r
695   COM_TRY_BEGIN\r
696   NCOM::CPropVariant prop;\r
697   switch(propID)\r
698   {\r
699     case kpidSectAlign: prop = _optHeader.SectAlign; break;\r
700     case kpidFileAlign: prop = _optHeader.FileAlign; break;\r
701     case kpidLinkerVer:\r
702     {\r
703       CVersion v = { _optHeader.LinkerVerMajor, _optHeader.LinkerVerMinor };\r
704       VerToProp(v, prop);\r
705       break;\r
706     }\r
707   \r
708     case kpidOsVer: VerToProp(_optHeader.OsVer, prop); break;\r
709     case kpidImageVer: VerToProp(_optHeader.ImageVer, prop); break;\r
710     case kpidSubsysVer: VerToProp(_optHeader.SubsysVer, prop); break;\r
711     case kpidCodeSize: prop = _optHeader.CodeSize; break;\r
712     case kpidInitDataSize: prop = _optHeader.InitDataSize; break;\r
713     case kpidUnInitDataSize: prop = _optHeader.UninitDataSize; break;\r
714     case kpidImageSize: prop = _optHeader.ImageSize; break;\r
715     case kpidPhySize: prop = _totalSize; break;\r
716     case kpidHeadersSize: prop = _optHeader.HeadersSize; break;\r
717     case kpidChecksum: prop = _optHeader.CheckSum; break;\r
718       \r
719     case kpidCpu: PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop); break;\r
720     case kpidBit64: if (_optHeader.Is64Bit()) prop = true; break;\r
721     case kpidSubSystem: PAIR_TO_PROP(g_SubSystems, _optHeader.SubSystem, prop); break;\r
722 \r
723     case kpidMTime:\r
724     case kpidCTime: TimeToProp(_header.Time, prop); break;\r
725     case kpidCharacts: FLAGS_TO_PROP(g_HeaderCharacts, _header.Flags, prop); break;\r
726     case kpidDllCharacts: FLAGS_TO_PROP(g_DllCharacts, _optHeader.DllCharacts, prop); break;\r
727     case kpidStackReserve: prop = _optHeader.StackReserve; break;\r
728     case kpidStackCommit: prop = _optHeader.StackCommit; break;\r
729     case kpidHeapReserve: prop = _optHeader.HeapReserve; break;\r
730     case kpidHeapCommit: prop = _optHeader.HeapCommit; break;\r
731 \r
732     case kpidImageBase: prop = _optHeader.ImageBase; break;\r
733     // case kpidAddressOfEntryPoint: prop = _optHeader.AddressOfEntryPoint; break;\r
734     // case kpidBaseOfCode: prop = _optHeader.BaseOfCode; break;\r
735     // case kpidBaseOfData32: if (!_optHeader.Is64Bit()) prop = _optHeader.BaseOfData32; break;\r
736 \r
737     case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break;\r
738   }\r
739   prop.Detach(value);\r
740   return S_OK;\r
741   COM_TRY_END\r
742 }\r
743 \r
744 void CHandler::AddResNameToString(UString &s, UInt32 id) const\r
745 {\r
746   if ((id & kFlag) != 0)\r
747   {\r
748     UString name;\r
749     if (ReadString(id & kMask, name) == S_OK)\r
750     {\r
751       if (name.IsEmpty())\r
752         s += L"[]";\r
753       else\r
754       {\r
755         if (name.Length() > 1 && name[0] == '"' && name.Back() == '"')\r
756           name = name.Mid(1, name.Length() - 2);\r
757         s += name;\r
758       }\r
759       return;\r
760     }\r
761   }\r
762   wchar_t sz[32];\r
763   ConvertUInt32ToString(id, sz);\r
764   s += sz;\r
765 }\r
766 \r
767 UString CHandler::GetLangPrefix(UInt32 lang)\r
768 {\r
769   UString s = _resourceFileName;\r
770   s += WCHAR_PATH_SEPARATOR;\r
771   if (!_oneLang)\r
772   {\r
773     AddResNameToString(s, lang);\r
774     s += WCHAR_PATH_SEPARATOR;\r
775   }\r
776   return s;\r
777 }\r
778 \r
779 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)\r
780 {\r
781   COM_TRY_BEGIN\r
782   NCOM::CPropVariant prop;\r
783   const CMixItem &mixItem = _mixItems[index];\r
784   if (mixItem.StringIndex >= 0)\r
785   {\r
786     const CStringItem &item = _strings[mixItem.StringIndex];\r
787     switch(propID)\r
788     {\r
789       case kpidPath: prop = GetLangPrefix(item.Lang) + L"string.txt"; break;\r
790       case kpidSize:\r
791       case kpidPackSize:\r
792         prop = (UInt64)item.Size; break;\r
793     }\r
794   }\r
795   else if (mixItem.ResourceIndex < 0)\r
796   {\r
797     const CSection &item = _sections[mixItem.SectionIndex];\r
798     switch(propID)\r
799     {\r
800       case kpidPath: StringToProp(item.Name, prop); break;\r
801       case kpidSize: prop = (UInt64)item.VSize; break;\r
802       case kpidPackSize: prop = (UInt64)item.GetPackSize(); break;\r
803       case kpidOffset: prop = item.Pa; break;\r
804       case kpidVa: if (item.IsRealSect) prop = item.Va; break;\r
805       case kpidMTime:\r
806       case kpidCTime:\r
807         TimeToProp(item.IsDebug ? item.Time : _header.Time, prop); break;\r
808       case kpidCharacts: if (item.IsRealSect) FLAGS_TO_PROP(g_SectFlags, item.Flags, prop); break;\r
809     }\r
810   }\r
811   else\r
812   {\r
813     const CResItem &item = _items[mixItem.ResourceIndex];\r
814     switch(propID)\r
815     {\r
816       case kpidPath:\r
817       {\r
818         UString s = GetLangPrefix(item.Lang);\r
819         {\r
820           const wchar_t *p = NULL;\r
821           if (item.Type < sizeof(g_ResTypes) / sizeof(g_ResTypes[0]))\r
822             p = g_ResTypes[item.Type];\r
823           if (p != 0)\r
824             s += p;\r
825           else\r
826             AddResNameToString(s, item.Type);\r
827         }\r
828         s += WCHAR_PATH_SEPARATOR;\r
829         AddResNameToString(s, item.ID);\r
830         if (item.HeaderSize != 0)\r
831         {\r
832           if (item.IsBmp())\r
833             s += L".bmp";\r
834           else if (item.IsIcon())\r
835             s += L".ico";\r
836         }\r
837         prop = s;\r
838         break;\r
839       }\r
840       case kpidSize: prop = (UInt64)item.GetSize(); break;\r
841       case kpidPackSize: prop = (UInt64)item.Size; break;\r
842     }\r
843   }\r
844   prop.Detach(value);\r
845   return S_OK;\r
846   COM_TRY_END\r
847 }\r
848 \r
849 HRESULT CHandler::LoadDebugSections(IInStream *stream, bool &thereIsSection)\r
850 {\r
851   thereIsSection = false;\r
852   const CDirLink &debugLink = _optHeader.DirItems[kDirLink_Debug];\r
853   if (debugLink.Size == 0)\r
854     return S_OK;\r
855   const unsigned kEntrySize = 28;\r
856   UInt32 numItems = debugLink.Size / kEntrySize;\r
857   if (numItems * kEntrySize != debugLink.Size || numItems > 16)\r
858     return S_FALSE;\r
859   \r
860   UInt64 pa = 0;\r
861   int i;\r
862   for (i = 0; i < _sections.Size(); i++)\r
863   {\r
864     const CSection &sect = _sections[i];\r
865     if (sect.Va < debugLink.Va && debugLink.Va + debugLink.Size <= sect.Va + sect.PSize)\r
866     {\r
867       pa = sect.Pa + (debugLink.Va - sect.Va);\r
868       break;\r
869     }\r
870   }\r
871   if (i == _sections.Size())\r
872   {\r
873     return S_OK;\r
874     // Exe for ARM requires S_OK\r
875     // return S_FALSE;\r
876   }\r
877   \r
878   CByteBuffer buffer;\r
879   buffer.SetCapacity(debugLink.Size);\r
880   Byte *buf = buffer;\r
881   \r
882   RINOK(stream->Seek(pa, STREAM_SEEK_SET, NULL));\r
883   RINOK(ReadStream_FALSE(stream, buf, debugLink.Size));\r
884 \r
885   for (i = 0; i < (int)numItems; i++)\r
886   {\r
887     CDebugEntry de;\r
888     de.Parse(buf);\r
889 \r
890     if (de.Size == 0)\r
891       continue;\r
892     \r
893     CSection sect;\r
894     sect.Name = ".debug" + GetDecString(i);\r
895     \r
896     sect.IsDebug = true;\r
897     sect.Time = de.Time;\r
898     sect.Va = de.Va;\r
899     sect.Pa = de.Pa;\r
900     sect.PSize = sect.VSize = de.Size;\r
901     UInt32 totalSize = sect.Pa + sect.PSize;\r
902     if (totalSize > _totalSize)\r
903     {\r
904       _totalSize = totalSize;\r
905       _sections.Add(sect);\r
906       thereIsSection = true;\r
907     }\r
908     buf += kEntrySize;\r
909   }\r
910 \r
911   return S_OK;\r
912 }\r
913 \r
914 HRESULT CHandler::ReadString(UInt32 offset, UString &dest) const\r
915 {\r
916   if ((offset & 1) != 0 || offset >= _buf.GetCapacity())\r
917     return S_FALSE;\r
918   size_t rem = _buf.GetCapacity() - offset;\r
919   if (rem < 2)\r
920     return S_FALSE;\r
921   unsigned length = Get16(_buf + offset);\r
922   if ((rem - 2) / 2 < length)\r
923     return S_FALSE;\r
924   dest.Empty();\r
925   offset += 2;\r
926   for (unsigned i = 0; i < length; i++)\r
927     dest += (wchar_t)Get16(_buf + offset + i * 2);\r
928   return S_OK;\r
929 }\r
930 \r
931 HRESULT CHandler::ReadTable(UInt32 offset, CRecordVector<CTableItem> &items)\r
932 {\r
933   if ((offset & 3) != 0 || offset >= _buf.GetCapacity())\r
934     return S_FALSE;\r
935   size_t rem = _buf.GetCapacity() - offset;\r
936   if (rem < 16)\r
937     return S_FALSE;\r
938   items.Clear();\r
939   unsigned numNameItems = Get16(_buf + offset + 12);\r
940   unsigned numIdItems = Get16(_buf + offset + 14);\r
941   unsigned numItems = numNameItems + numIdItems;\r
942   if ((rem - 16) / 8 < numItems)\r
943     return S_FALSE;\r
944   if (!_usedRes.SetRange(offset, 16 + numItems * 8))\r
945     return S_FALSE;\r
946   offset += 16;\r
947   _oneLang = true;\r
948   unsigned i;\r
949   for (i = 0; i < numItems; i++)\r
950   {\r
951     CTableItem item;\r
952     const Byte *buf = _buf + offset;\r
953     offset += 8;\r
954     item.ID = Get32(buf + 0);\r
955     if (((item.ID & kFlag) != 0) != (i < numNameItems))\r
956       return S_FALSE;\r
957     item.Offset = Get32(buf + 4);\r
958     items.Add(item);\r
959   }\r
960   return S_OK;\r
961 }\r
962 \r
963 static const UInt32 kFileSizeMax = (UInt32)1 << 30;\r
964 static const int kNumResItemsMax = (UInt32)1 << 23;\r
965 static const int kNumStringLangsMax = 128;\r
966 \r
967 // BITMAPINFOHEADER\r
968 struct CBitmapInfoHeader\r
969 {\r
970   // UInt32 HeaderSize;\r
971   UInt32 XSize;\r
972   Int32 YSize;\r
973   UInt16 Planes;\r
974   UInt16 BitCount;\r
975   UInt32 Compression;\r
976   UInt32 SizeImage;\r
977 \r
978   bool Parse(const Byte *p, size_t size);\r
979 };\r
980 \r
981 static const UInt32 kBitmapInfoHeader_Size = 0x28;\r
982 \r
983 bool CBitmapInfoHeader::Parse(const Byte *p, size_t size)\r
984 {\r
985   if (size < kBitmapInfoHeader_Size || Get32(p) != kBitmapInfoHeader_Size)\r
986     return false;\r
987   XSize = Get32(p + 4);\r
988   YSize = (Int32)Get32(p + 8);\r
989   Planes = Get16(p + 12);\r
990   BitCount = Get16(p + 14);\r
991   Compression = Get32(p + 16);\r
992   SizeImage = Get32(p + 20);\r
993   return true;\r
994 }\r
995 \r
996 static UInt32 GetImageSize(UInt32 xSize, UInt32 ySize, UInt32 bitCount)\r
997 {\r
998   return ((xSize * bitCount + 7) / 8 + 3) / 4 * 4 * ySize;\r
999 }\r
1000   \r
1001 static UInt32 SetBitmapHeader(Byte *dest, const Byte *src, UInt32 size)\r
1002 {\r
1003   CBitmapInfoHeader h;\r
1004   if (!h.Parse(src, size))\r
1005     return 0;\r
1006   if (h.YSize < 0)\r
1007     h.YSize = -h.YSize;\r
1008   if (h.XSize > (1 << 26) || h.YSize > (1 << 26) || h.Planes != 1 || h.BitCount > 32 ||\r
1009       h.Compression != 0) // BI_RGB\r
1010     return 0;\r
1011   if (h.SizeImage == 0)\r
1012     h.SizeImage = GetImageSize(h.XSize, h.YSize, h.BitCount);\r
1013   UInt32 totalSize = kBmpHeaderSize + size;\r
1014   UInt32 offBits = totalSize - h.SizeImage;\r
1015   // BITMAPFILEHEADER\r
1016   SetUi16(dest, 0x4D42);\r
1017   SetUi32(dest + 2, totalSize);\r
1018   SetUi32(dest + 6, 0);\r
1019   SetUi32(dest + 10, offBits);\r
1020   return kBmpHeaderSize;\r
1021 }\r
1022 \r
1023 static UInt32 SetIconHeader(Byte *dest, const Byte *src, UInt32 size)\r
1024 {\r
1025   CBitmapInfoHeader h;\r
1026   if (!h.Parse(src, size))\r
1027     return 0;\r
1028   if (h.YSize < 0)\r
1029     h.YSize = -h.YSize;\r
1030   if (h.XSize > (1 << 26) || h.YSize > (1 << 26) || h.Planes != 1 ||\r
1031       h.Compression != 0) // BI_RGB\r
1032     return 0;\r
1033 \r
1034   UInt32 numBitCount = h.BitCount;\r
1035   if (numBitCount != 1 &&\r
1036       numBitCount != 4 &&\r
1037       numBitCount != 8 &&\r
1038       numBitCount != 24 &&\r
1039       numBitCount != 32)\r
1040     return 0;\r
1041 \r
1042   if ((h.YSize & 1) != 0)\r
1043     return 0;\r
1044   h.YSize /= 2;\r
1045   if (h.XSize > 0x100 || h.YSize > 0x100)\r
1046     return 0;\r
1047 \r
1048   UInt32 imageSize;\r
1049   // imageSize is not correct if AND mask array contains zeros\r
1050   // in this case it is equal image1Size\r
1051 \r
1052   // UInt32 imageSize = h.SizeImage;\r
1053   // if (imageSize == 0)\r
1054   // {\r
1055     UInt32 image1Size = GetImageSize(h.XSize, h.YSize, h.BitCount);\r
1056     UInt32 image2Size = GetImageSize(h.XSize, h.YSize, 1);\r
1057     imageSize = image1Size + image2Size;\r
1058   // }\r
1059   UInt32 numColors = 0;\r
1060   if (numBitCount < 16)\r
1061     numColors = 1 << numBitCount;\r
1062 \r
1063   SetUi16(dest, 0); // Reserved\r
1064   SetUi16(dest + 2, 1); // RES_ICON\r
1065   SetUi16(dest + 4, 1); // ResCount\r
1066 \r
1067   dest[6] = (Byte)h.XSize; // Width\r
1068   dest[7] = (Byte)h.YSize; // Height\r
1069   dest[8] = (Byte)numColors; // ColorCount\r
1070   dest[9] = 0; // Reserved\r
1071   \r
1072   SetUi32(dest + 10, 0); // Reserved1 / Reserved2\r
1073 \r
1074   UInt32 numQuadsBytes = numColors * 4;\r
1075   UInt32 BytesInRes = kBitmapInfoHeader_Size + numQuadsBytes + imageSize;\r
1076   SetUi32(dest + 14, BytesInRes);\r
1077   SetUi32(dest + 18, kIconHeaderSize);\r
1078 \r
1079   /*\r
1080   Description = DWORDToString(xSize) +\r
1081       kDelimiterChar + DWORDToString(ySize) +\r
1082       kDelimiterChar + DWORDToString(numBitCount);\r
1083   */\r
1084   return kIconHeaderSize;\r
1085 }\r
1086 \r
1087 bool CHandler::ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size)\r
1088 {\r
1089   if ((size & 1) != 0)\r
1090     return false;\r
1091 \r
1092   int i;\r
1093   for (i = 0; i < _strings.Size(); i++)\r
1094     if (_strings[i].Lang == lang)\r
1095       break;\r
1096   if (i == _strings.Size())\r
1097   {\r
1098     if (_strings.Size() >= kNumStringLangsMax)\r
1099       return false;\r
1100     CStringItem item;\r
1101     item.Size = 0;\r
1102     item.Lang = lang;\r
1103     i = _strings.Add(item);\r
1104   }\r
1105   \r
1106   CStringItem &item = _strings[i];\r
1107   id = (id - 1) << 4;\r
1108   UInt32 pos = 0;\r
1109   for (i = 0; i < 16; i++)\r
1110   {\r
1111     if (size - pos < 2)\r
1112       return false;\r
1113     UInt32 len = Get16(src + pos);\r
1114     pos += 2;\r
1115     if (len != 0)\r
1116     {\r
1117       if (size - pos < len * 2)\r
1118         return false;\r
1119       char temp[32];\r
1120       ConvertUInt32ToString(id  + i, temp);\r
1121       size_t tempLen = strlen(temp);\r
1122       size_t j;\r
1123       for (j = 0; j < tempLen; j++)\r
1124         item.AddChar(temp[j]);\r
1125       item.AddChar('\t');\r
1126       for (j = 0; j < len; j++, pos += 2)\r
1127         item.AddWChar(Get16(src + pos));\r
1128       item.AddChar(0x0D);\r
1129       item.AddChar(0x0A);\r
1130     }\r
1131   }\r
1132   return (size == pos);\r
1133 }\r
1134 \r
1135 HRESULT CHandler::OpenResources(int sectionIndex, IInStream *stream, IArchiveOpenCallback *callback)\r
1136 {\r
1137   const CSection &sect = _sections[sectionIndex];\r
1138   size_t fileSize = sect.PSize; // Maybe we need sect.VSize here !!!\r
1139   if (fileSize > kFileSizeMax)\r
1140     return S_FALSE;\r
1141   {\r
1142     UInt64 fileSize64 = fileSize;\r
1143     if (callback)\r
1144       RINOK(callback->SetTotal(NULL, &fileSize64));\r
1145     RINOK(stream->Seek(sect.Pa, STREAM_SEEK_SET, NULL));\r
1146     _buf.SetCapacity(fileSize);\r
1147     for (size_t pos = 0; pos < fileSize;)\r
1148     {\r
1149       UInt64 offset64 = pos;\r
1150       if (callback)\r
1151         RINOK(callback->SetCompleted(NULL, &offset64))\r
1152       size_t rem = MyMin(fileSize - pos, (size_t)(1 << 20));\r
1153       RINOK(ReadStream_FALSE(stream, _buf + pos, rem));\r
1154       pos += rem;\r
1155     }\r
1156   }\r
1157   \r
1158   _usedRes.Alloc(fileSize);\r
1159   CRecordVector<CTableItem> specItems;\r
1160   RINOK(ReadTable(0, specItems));\r
1161 \r
1162   _oneLang = true;\r
1163   bool stringsOk = true;\r
1164   size_t maxOffset = 0;\r
1165   for (int i = 0; i < specItems.Size(); i++)\r
1166   {\r
1167     const CTableItem &item1 = specItems[i];\r
1168     if ((item1.Offset & kFlag) == 0)\r
1169       return S_FALSE;\r
1170 \r
1171     CRecordVector<CTableItem> specItems2;\r
1172     RINOK(ReadTable(item1.Offset & kMask, specItems2));\r
1173 \r
1174     for (int j = 0; j < specItems2.Size(); j++)\r
1175     {\r
1176       const CTableItem &item2 = specItems2[j];\r
1177       if ((item2.Offset & kFlag) == 0)\r
1178         return S_FALSE;\r
1179       \r
1180       CRecordVector<CTableItem> specItems3;\r
1181       RINOK(ReadTable(item2.Offset & kMask, specItems3));\r
1182       \r
1183       CResItem item;\r
1184       item.Type = item1.ID;\r
1185       item.ID = item2.ID;\r
1186       \r
1187       for (int k = 0; k < specItems3.Size(); k++)\r
1188       {\r
1189         if (_items.Size() >= kNumResItemsMax)\r
1190           return S_FALSE;\r
1191         const CTableItem &item3 = specItems3[k];\r
1192         if ((item3.Offset & kFlag) != 0)\r
1193           return S_FALSE;\r
1194         if (item3.Offset >= _buf.GetCapacity() || _buf.GetCapacity() - item3.Offset < 16)\r
1195           return S_FALSE;\r
1196         const Byte *buf = _buf + item3.Offset;\r
1197         item.Lang = item3.ID;\r
1198         item.Offset = Get32(buf + 0);\r
1199         item.Size = Get32(buf + 4);\r
1200         // UInt32 codePage = Get32(buf + 8);\r
1201         if (Get32(buf + 12) != 0)\r
1202           return S_FALSE;\r
1203         if (!_items.IsEmpty() && _oneLang && !item.IsNameEqual(_items.Back()))\r
1204           _oneLang = false;\r
1205 \r
1206         item.HeaderSize = 0;\r
1207       \r
1208         size_t offset = item.Offset - sect.Va;\r
1209         if (offset > maxOffset)\r
1210           maxOffset = offset;\r
1211         if (offset + item.Size > maxOffset)\r
1212           maxOffset = offset + item.Size;\r
1213 \r
1214         if (CheckItem(sect, item, offset))\r
1215         {\r
1216           const Byte *data = _buf + offset;\r
1217           if (item.IsBmp())\r
1218             item.HeaderSize = SetBitmapHeader(item.Header, data, item.Size);\r
1219           else if (item.IsIcon())\r
1220             item.HeaderSize = SetIconHeader(item.Header, data, item.Size);\r
1221           else if (item.IsString())\r
1222           {\r
1223             if (stringsOk)\r
1224               stringsOk = ParseStringRes(item.ID, item.Lang, data, item.Size);\r
1225           }\r
1226         }\r
1227 \r
1228         item.Enabled = true;\r
1229         _items.Add(item);\r
1230       }\r
1231     }\r
1232   }\r
1233   \r
1234   if (stringsOk && !_strings.IsEmpty())\r
1235   {\r
1236     int i;\r
1237     for (i = 0; i < _items.Size(); i++)\r
1238     {\r
1239       CResItem &item = _items[i];\r
1240       if (item.IsString())\r
1241         item.Enabled = false;\r
1242     }\r
1243     for (i = 0; i < _strings.Size(); i++)\r
1244     {\r
1245       if (_strings[i].Size == 0)\r
1246         continue;\r
1247       CMixItem mixItem;\r
1248       mixItem.ResourceIndex = -1;\r
1249       mixItem.StringIndex = i;\r
1250       mixItem.SectionIndex = sectionIndex;\r
1251       _mixItems.Add(mixItem);\r
1252     }\r
1253   }\r
1254 \r
1255   _usedRes.Free();\r
1256 \r
1257   int numBits = _optHeader.GetNumFileAlignBits();\r
1258   if (numBits >= 0)\r
1259   {\r
1260     UInt32 mask = (1 << numBits) - 1;\r
1261     size_t end = ((maxOffset + mask) & ~mask);\r
1262     if (end < sect.VSize && end <= sect.PSize)\r
1263     {\r
1264       CSection sect2;\r
1265       sect2.Flags = 0;\r
1266 \r
1267       // we skip Zeros to start of aligned block\r
1268       size_t i;\r
1269       for (i = maxOffset; i < end; i++)\r
1270         if (_buf[i] != 0)\r
1271           break;\r
1272       if (i == end)\r
1273         maxOffset = end;\r
1274       \r
1275       sect2.Pa = sect.Pa + (UInt32)maxOffset;\r
1276       sect2.Va = sect.Va + (UInt32)maxOffset;\r
1277       sect2.PSize = sect.VSize - (UInt32)maxOffset;\r
1278       sect2.VSize = sect2.PSize;\r
1279       sect2.Name = ".rsrc_1";\r
1280       sect2.Time = 0;\r
1281       sect2.IsAdditionalSection = true;\r
1282       _sections.Add(sect2);\r
1283     }\r
1284   }\r
1285 \r
1286   return S_OK;\r
1287 }\r
1288 \r
1289 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)\r
1290 {\r
1291   const UInt32 kBufSize = 1 << 18;\r
1292   const UInt32 kSigSize = 2;\r
1293 \r
1294   _mainSubfile = -1;\r
1295 \r
1296   CByteBuffer buffer;\r
1297   buffer.SetCapacity(kBufSize);\r
1298   Byte *buf = buffer;\r
1299 \r
1300   size_t processed = kSigSize;\r
1301   RINOK(ReadStream_FALSE(stream, buf, processed));\r
1302   if (buf[0] != 'M' || buf[1] != 'Z')\r
1303     return S_FALSE;\r
1304   processed = kBufSize - kSigSize;\r
1305   RINOK(ReadStream(stream, buf + kSigSize, &processed));\r
1306   processed += kSigSize;\r
1307   if (!Parse(buf, (UInt32)processed))\r
1308     return S_FALSE;\r
1309   bool thereISDebug;\r
1310   RINOK(LoadDebugSections(stream, thereISDebug));\r
1311 \r
1312   const CDirLink &certLink = _optHeader.DirItems[kDirLink_Certificate];\r
1313   if (certLink.Size != 0)\r
1314   {\r
1315     CSection sect;\r
1316     sect.Name = "CERTIFICATE";\r
1317     sect.Va = 0;\r
1318     sect.Pa = certLink.Va;\r
1319     sect.PSize = sect.VSize = certLink.Size;\r
1320     sect.UpdateTotalSize(_totalSize);\r
1321     _sections.Add(sect);\r
1322   }\r
1323 \r
1324   if (thereISDebug)\r
1325   {\r
1326     const UInt32 kAlign = 1 << 12;\r
1327     UInt32 alignPos = _totalSize & (kAlign - 1);\r
1328     if (alignPos != 0)\r
1329     {\r
1330       UInt32 size = kAlign - alignPos;\r
1331       RINOK(stream->Seek(_totalSize, STREAM_SEEK_SET, NULL));\r
1332       buffer.Free();\r
1333       buffer.SetCapacity(kAlign);\r
1334       Byte *buf = buffer;\r
1335       size_t processed = size;\r
1336       RINOK(ReadStream(stream, buf, &processed));\r
1337       size_t i;\r
1338       for (i = 0; i < processed; i++)\r
1339       {\r
1340         if (buf[i] != 0)\r
1341           break;\r
1342       }\r
1343       if (processed < size && processed < 100)\r
1344         _totalSize += (UInt32)processed;\r
1345       else if (((_totalSize + i) & 0x1FF) == 0 || processed < size)\r
1346         _totalSize += (UInt32)i;\r
1347     }\r
1348   }\r
1349 \r
1350   if (_header.NumSymbols > 0 && _header.PointerToSymbolTable >= 512)\r
1351   {\r
1352     if (_header.NumSymbols >= (1 << 24))\r
1353       return S_FALSE;\r
1354     CSection sect;\r
1355     sect.Name = "COFF_SYMBOLS";\r
1356     UInt32 size = _header.NumSymbols * 18;\r
1357     RINOK(stream->Seek((UInt64)_header.PointerToSymbolTable + size, STREAM_SEEK_SET, NULL));\r
1358     Byte buf[4];\r
1359     RINOK(ReadStream_FALSE(stream, buf, 4));\r
1360     UInt32 size2 = Get32(buf);\r
1361     if (size2 >= (1 << 28))\r
1362       return S_FALSE;\r
1363     size += size2;\r
1364 \r
1365     sect.Va = 0;\r
1366     sect.Pa = _header.PointerToSymbolTable;\r
1367     sect.PSize = sect.VSize = size;\r
1368     sect.UpdateTotalSize(_totalSize);\r
1369     _sections.Add(sect);\r
1370   }\r
1371 \r
1372   UInt64 fileSize;\r
1373   RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize));\r
1374   if (fileSize > _totalSize)\r
1375     return S_FALSE;\r
1376   _totalSizeLimited = (_totalSize < fileSize) ? _totalSize : (UInt32)fileSize;\r
1377 \r
1378   {\r
1379     CObjectVector<CSection> sections = _sections;\r
1380     sections.Sort();\r
1381     UInt32 limit = (1 << 12);\r
1382     int num = 0;\r
1383     int numSections = sections.Size();\r
1384     for (int i = 0; i < numSections; i++)\r
1385     {\r
1386       const CSection &s = sections[i];\r
1387       if (s.Pa > limit)\r
1388       {\r
1389         CSection s2;\r
1390         s2.Pa = s2.Va = limit;\r
1391         s2.PSize = s2.VSize = s.Pa - limit;\r
1392         s2.IsAdditionalSection = true;\r
1393         s2.Name = '[';\r
1394         s2.Name += GetDecString(num++);\r
1395         s2.Name += ']';\r
1396         _sections.Add(s2);\r
1397         limit = s.Pa;\r
1398       }\r
1399       UInt32 next = s.Pa + s.PSize;\r
1400       if (next < s.Pa)\r
1401         break;\r
1402       if (next >= limit)\r
1403         limit = next;\r
1404     }\r
1405   }\r
1406 \r
1407   _parseResources = true;\r
1408 \r
1409   UInt64 mainSize = 0, mainSize2 = 0;\r
1410   int i;\r
1411   for (i = 0; i < _sections.Size(); i++)\r
1412   {\r
1413     const CSection &sect = _sections[i];\r
1414     CMixItem mixItem;\r
1415     mixItem.SectionIndex = i;\r
1416     if (_parseResources && sect.Name == ".rsrc" && _items.IsEmpty())\r
1417     {\r
1418       HRESULT res = OpenResources(i, stream, callback);\r
1419       if (res == S_OK)\r
1420       {\r
1421         _resourceFileName = GetUnicodeString(sect.Name);\r
1422         for (int j = 0; j < _items.Size(); j++)\r
1423         {\r
1424           const CResItem &item = _items[j];\r
1425           if (item.Enabled)\r
1426           {\r
1427             mixItem.ResourceIndex = j;\r
1428             mixItem.StringIndex = -1;\r
1429             if (item.IsRcDataOrUnknown())\r
1430             {\r
1431               if (item.Size >= mainSize)\r
1432               {\r
1433                 mainSize2 = mainSize;\r
1434                 mainSize = item.Size;\r
1435                 _mainSubfile = _mixItems.Size();\r
1436               }\r
1437               else if (item.Size >= mainSize2)\r
1438                 mainSize2 = item.Size;\r
1439             }\r
1440             _mixItems.Add(mixItem);\r
1441           }\r
1442         }\r
1443         if (sect.PSize > sect.VSize)\r
1444         {\r
1445           int numBits = _optHeader.GetNumFileAlignBits();\r
1446           if (numBits >= 0)\r
1447           {\r
1448             UInt32 mask = (1 << numBits) - 1;\r
1449             UInt32 end = ((sect.VSize + mask) & ~mask);\r
1450 \r
1451             if (sect.PSize > end)\r
1452             {\r
1453               CSection sect2;\r
1454               sect2.Flags = 0;\r
1455               sect2.Pa = sect.Pa + end;\r
1456               sect2.Va = sect.Va + end;\r
1457               sect2.PSize = sect.PSize - end;\r
1458               sect2.VSize = sect2.PSize;\r
1459               sect2.Name = ".rsrc_2";\r
1460               sect2.Time = 0;\r
1461               sect2.IsAdditionalSection = true;\r
1462               _sections.Add(sect2);\r
1463             }\r
1464           }\r
1465         }\r
1466         continue;\r
1467       }\r
1468       if (res != S_FALSE)\r
1469         return res;\r
1470       CloseResources();\r
1471     }\r
1472     mixItem.StringIndex = -1;\r
1473     mixItem.ResourceIndex = -1;\r
1474     if (sect.IsAdditionalSection)\r
1475     {\r
1476       if (sect.PSize >= mainSize)\r
1477       {\r
1478         mainSize2 = mainSize;\r
1479         mainSize = sect.PSize;\r
1480         _mainSubfile = _mixItems.Size();\r
1481       }\r
1482       else\r
1483         mainSize2 = sect.PSize;\r
1484     }\r
1485     _mixItems.Add(mixItem);\r
1486   }\r
1487   \r
1488   if (mainSize2 >= (1 << 20) && mainSize < mainSize2 * 2)\r
1489     _mainSubfile = -1;\r
1490 \r
1491   for (i = 0; i < _mixItems.Size(); i++)\r
1492   {\r
1493     const CMixItem &mixItem = _mixItems[i];\r
1494     if (mixItem.StringIndex < 0 && mixItem.ResourceIndex < 0 && _sections[mixItem.SectionIndex].Name == "_winzip_")\r
1495     {\r
1496       _mainSubfile = i;\r
1497       break;\r
1498     }\r
1499   }\r
1500 \r
1501   return S_OK;\r
1502 }\r
1503 \r
1504 HRESULT CalcCheckSum(ISequentialInStream *stream, UInt32 size, UInt32 excludePos, UInt32 &res)\r
1505 {\r
1506   // size &= ~1;\r
1507   const UInt32 kBufSize = 1 << 23;\r
1508   CByteBuffer buffer;\r
1509   buffer.SetCapacity(kBufSize);\r
1510   Byte *buf = buffer;\r
1511 \r
1512   UInt32 sum = 0;\r
1513   UInt32 pos = 0;\r
1514   for (;;)\r
1515   {\r
1516     UInt32 rem = size - pos;\r
1517     if (rem > kBufSize)\r
1518       rem = kBufSize;\r
1519     if (rem == 0)\r
1520       break;\r
1521     size_t processed = rem;\r
1522     RINOK(ReadStream(stream, buf, &processed));\r
1523     \r
1524     /*\r
1525     for (; processed < rem; processed++)\r
1526       buf[processed] = 0;\r
1527     */\r
1528 \r
1529     if ((processed & 1) != 0)\r
1530       buf[processed] = 0;\r
1531 \r
1532     for (int j = 0; j < 4; j++)\r
1533     {\r
1534       UInt32 p = excludePos + j;\r
1535       if (pos <= p && p < pos + processed)\r
1536         buf[p - pos] = 0;\r
1537     }\r
1538 \r
1539     for (size_t i = 0; i < processed; i += 2)\r
1540     {\r
1541       sum += Get16(buf + i);\r
1542       sum = (sum + (sum >> 16)) & 0xFFFF;\r
1543     }\r
1544     pos += (UInt32)processed;\r
1545     if (rem != processed)\r
1546       break;\r
1547   }\r
1548   sum += pos;\r
1549   res = sum;\r
1550   return S_OK;\r
1551 }\r
1552 \r
1553 STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback)\r
1554 {\r
1555   COM_TRY_BEGIN\r
1556   Close();\r
1557   RINOK(Open2(inStream, callback));\r
1558   _stream = inStream;\r
1559   return S_OK;\r
1560   COM_TRY_END\r
1561 }\r
1562 \r
1563 void CHandler::CloseResources()\r
1564 {\r
1565   _usedRes.Free();\r
1566   _items.Clear();\r
1567   _strings.Clear();\r
1568   _buf.SetCapacity(0);\r
1569 }\r
1570 \r
1571 STDMETHODIMP CHandler::Close()\r
1572 {\r
1573   _stream.Release();\r
1574   _sections.Clear();\r
1575   _mixItems.Clear();\r
1576   CloseResources();\r
1577   return S_OK;\r
1578 }\r
1579 \r
1580 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)\r
1581 {\r
1582   *numItems = _mixItems.Size();\r
1583   return S_OK;\r
1584 }\r
1585 \r
1586 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,\r
1587     Int32 testMode, IArchiveExtractCallback *extractCallback)\r
1588 {\r
1589   COM_TRY_BEGIN\r
1590   bool allFilesMode = (numItems == (UInt32)-1);\r
1591   if (allFilesMode)\r
1592     numItems = _mixItems.Size();\r
1593   if (numItems == 0)\r
1594     return S_OK;\r
1595   UInt64 totalSize = 0;\r
1596   UInt32 i;\r
1597   for (i = 0; i < numItems; i++)\r
1598   {\r
1599     const CMixItem &mixItem = _mixItems[allFilesMode ? i : indices[i]];\r
1600     if (mixItem.StringIndex >= 0)\r
1601       totalSize += _strings[mixItem.StringIndex].Size;\r
1602     else if (mixItem.ResourceIndex < 0)\r
1603       totalSize += _sections[mixItem.SectionIndex].GetPackSize();\r
1604     else\r
1605       totalSize += _items[mixItem.ResourceIndex].GetSize();\r
1606   }\r
1607   extractCallback->SetTotal(totalSize);\r
1608 \r
1609   UInt64 currentTotalSize = 0;\r
1610   UInt64 currentItemSize;\r
1611   \r
1612   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();\r
1613   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;\r
1614 \r
1615   CLocalProgress *lps = new CLocalProgress;\r
1616   CMyComPtr<ICompressProgressInfo> progress = lps;\r
1617   lps->Init(extractCallback, false);\r
1618 \r
1619   bool checkSumOK = true;\r
1620   if (_optHeader.CheckSum != 0 && (int)numItems == _mixItems.Size())\r
1621   {\r
1622     UInt32 checkSum = 0;\r
1623     RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));\r
1624     CalcCheckSum(_stream, _totalSizeLimited, _peOffset + kHeaderSize + 64, checkSum);\r
1625     checkSumOK = (checkSum == _optHeader.CheckSum);\r
1626   }\r
1627 \r
1628   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;\r
1629   CMyComPtr<ISequentialInStream> inStream(streamSpec);\r
1630   streamSpec->SetStream(_stream);\r
1631 \r
1632   for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)\r
1633   {\r
1634     lps->InSize = lps->OutSize = currentTotalSize;\r
1635     RINOK(lps->SetCur());\r
1636     Int32 askMode = testMode ?\r
1637         NExtract::NAskMode::kTest :\r
1638         NExtract::NAskMode::kExtract;\r
1639     UInt32 index = allFilesMode ? i : indices[i];\r
1640 \r
1641     CMyComPtr<ISequentialOutStream> outStream;\r
1642     RINOK(extractCallback->GetStream(index, &outStream, askMode));\r
1643     const CMixItem &mixItem = _mixItems[index];\r
1644 \r
1645     const CSection &sect = _sections[mixItem.SectionIndex];\r
1646     bool isOk = true;\r
1647     if (mixItem.StringIndex >= 0)\r
1648     {\r
1649       const CStringItem &item = _strings[mixItem.StringIndex];\r
1650       currentItemSize = item.Size;\r
1651       if (!testMode && !outStream)\r
1652         continue;\r
1653 \r
1654       RINOK(extractCallback->PrepareOperation(askMode));\r
1655       if (outStream)\r
1656         RINOK(WriteStream(outStream, item.Buf, item.Size));\r
1657     }\r
1658     else if (mixItem.ResourceIndex < 0)\r
1659     {\r
1660       currentItemSize = sect.GetPackSize();\r
1661       if (!testMode && !outStream)\r
1662         continue;\r
1663       \r
1664       RINOK(extractCallback->PrepareOperation(askMode));\r
1665       RINOK(_stream->Seek(sect.Pa, STREAM_SEEK_SET, NULL));\r
1666       streamSpec->Init(currentItemSize);\r
1667       RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));\r
1668       isOk = (copyCoderSpec->TotalSize == currentItemSize);\r
1669     }\r
1670     else\r
1671     {\r
1672       const CResItem &item = _items[mixItem.ResourceIndex];\r
1673       currentItemSize = item.GetSize();\r
1674       if (!testMode && !outStream)\r
1675         continue;\r
1676 \r
1677       RINOK(extractCallback->PrepareOperation(askMode));\r
1678       size_t offset = item.Offset - sect.Va;\r
1679       if (!CheckItem(sect, item, offset))\r
1680         isOk = false;\r
1681       else if (outStream)\r
1682       {\r
1683         if (item.HeaderSize != 0)\r
1684           RINOK(WriteStream(outStream, item.Header, item.HeaderSize));\r
1685         RINOK(WriteStream(outStream, _buf + offset, item.Size));\r
1686       }\r
1687     }\r
1688     \r
1689     outStream.Release();\r
1690     RINOK(extractCallback->SetOperationResult(isOk ?\r
1691       checkSumOK ?\r
1692         NExtract::NOperationResult::kOK:\r
1693         NExtract::NOperationResult::kCRCError:\r
1694         NExtract::NOperationResult::kDataError));\r
1695   }\r
1696   return S_OK;\r
1697   COM_TRY_END\r
1698 }\r
1699 \r
1700 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)\r
1701 {\r
1702   COM_TRY_BEGIN\r
1703   *stream = 0;\r
1704 \r
1705   const CMixItem &mixItem = _mixItems[index];\r
1706   const CSection &sect = _sections[mixItem.SectionIndex];\r
1707   if (mixItem.IsSectionItem())\r
1708     return CreateLimitedInStream(_stream, sect.Pa, sect.PSize, stream);\r
1709 \r
1710   CBufInStream *inStreamSpec = new CBufInStream;\r
1711   CMyComPtr<ISequentialInStream> streamTemp = inStreamSpec;\r
1712   CReferenceBuf *referenceBuf = new CReferenceBuf;\r
1713   CMyComPtr<IUnknown> ref = referenceBuf;\r
1714   if (mixItem.StringIndex >= 0)\r
1715   {\r
1716     const CStringItem &item = _strings[mixItem.StringIndex];\r
1717     referenceBuf->Buf.SetCapacity(item.Size);\r
1718     memcpy(referenceBuf->Buf, item.Buf, item.Size);\r
1719   }\r
1720   else\r
1721   {\r
1722     const CResItem &item = _items[mixItem.ResourceIndex];\r
1723     size_t offset = item.Offset - sect.Va;\r
1724     if (!CheckItem(sect, item, offset))\r
1725       return S_FALSE;\r
1726     if (item.HeaderSize == 0)\r
1727     {\r
1728       CBufInStream *streamSpec = new CBufInStream;\r
1729       CMyComPtr<IInStream> streamTemp2 = streamSpec;\r
1730       streamSpec->Init(_buf + offset, item.Size, (IInArchive *)this);\r
1731       *stream = streamTemp2.Detach();\r
1732       return S_OK;\r
1733     }\r
1734     referenceBuf->Buf.SetCapacity(item.HeaderSize + item.Size);\r
1735     memcpy(referenceBuf->Buf, item.Header, item.HeaderSize);\r
1736     memcpy(referenceBuf->Buf + item.HeaderSize, _buf + offset, item.Size);\r
1737   }\r
1738   inStreamSpec->Init(referenceBuf);\r
1739 \r
1740   *stream = streamTemp.Detach();\r
1741   return S_OK;\r
1742   COM_TRY_END\r
1743 }\r
1744 \r
1745 static IInArchive *CreateArc() { return new CHandler; }\r
1746 \r
1747 static CArcInfo g_ArcInfo =\r
1748   { L"PE", L"exe dll sys", 0, 0xDD, { 'P', 'E', 0, 0 }, 4, false, CreateArc, 0 };\r
1749 \r
1750 REGISTER_ARC(Pe)\r
1751 \r
1752 }}\r