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