xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/Nsis/NsisIn.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // NsisIn.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/IntToString.h"
6 #include "../../../Common/StringToInt.h"
7 
8 #include "../../Common/LimitedStreams.h"
9 
10 #include "NsisIn.h"
11 
12 #define Get16(p) GetUi16(p)
13 #define Get32(p) GetUi32(p)
14 
15 // #define NUM_SPEED_TESTS 1000
16 
17 namespace NArchive {
18 namespace NNsis {
19 
20 static const size_t kInputBufSize = 1 << 20;
21 
22 const Byte kSignature[kSignatureSize] = NSIS_SIGNATURE;
23 static const UInt32 kMask_IsCompressed = (UInt32)1 << 31;
24 
25 static const unsigned kNumCommandParams = 6;
26 static const unsigned kCmdSize = 4 + kNumCommandParams * 4;
27 
28 #ifdef NSIS_SCRIPT
29 #define CR_LF "\x0D\x0A"
30 #endif
31 
32 static const char * const kErrorStr = "$_ERROR_STR_";
33 
34 #define RINOZ(x) { int _tt_ = (x); if (_tt_ != 0) return _tt_; }
35 
36 
37 /* There are several versions of NSIS:
38    1) Original NSIS:
39         NSIS-2 ANSI
40         NSIS-3 ANSI
41         NSIS-3 Unicode
42    2) NSIS from Jim Park that extends old NSIS-2 to Unicode support:
43         NSIS-Park-(1,2,3) ANSI
44         NSIS-Park-(1,2,3) Unicode
45 
46    The command IDs layout is slightly different for different versions.
47    Also there are additional "log" versions of NSIS that support EW_LOG.
48    We use the layout of "NSIS-3 Unicode" without "log" as main layout.
49    And we transfer the command IDs to main layout, if another layout is detected. */
50 
51 
52 enum
53 {
54   EW_INVALID_OPCODE,
55   EW_RET,               // Return
56   EW_NOP,               // Nop, Goto
57   EW_ABORT,             // Abort
58   EW_QUIT,              // Quit
59   EW_CALL,              // Call, InitPluginsDir
60   EW_UPDATETEXT,        // DetailPrint
61   EW_SLEEP,             // Sleep
62   EW_BRINGTOFRONT,      // BringToFront
63   EW_CHDETAILSVIEW,     // SetDetailsView
64   EW_SETFILEATTRIBUTES, // SetFileAttributes
65   EW_CREATEDIR,         // CreateDirectory, SetOutPath
66   EW_IFFILEEXISTS,      // IfFileExists
67   EW_SETFLAG,           // SetRebootFlag, ...
68   EW_IFFLAG,            // IfAbort, IfSilent, IfErrors, IfRebootFlag
69   EW_GETFLAG,           // GetInstDirError, GetErrorLevel
70   EW_RENAME,            // Rename
71   EW_GETFULLPATHNAME,   // GetFullPathName
72   EW_SEARCHPATH,        // SearchPath
73   EW_GETTEMPFILENAME,   // GetTempFileName
74   EW_EXTRACTFILE,       // File
75   EW_DELETEFILE,        // Delete
76   EW_MESSAGEBOX,        // MessageBox
77   EW_RMDIR,             // RMDir
78   EW_STRLEN,            // StrLen
79   EW_ASSIGNVAR,         // StrCpy
80   EW_STRCMP,            // StrCmp
81   EW_READENVSTR,        // ReadEnvStr, ExpandEnvStrings
82   EW_INTCMP,            // IntCmp, IntCmpU
83   EW_INTOP,             // IntOp
84   EW_INTFMT,            // IntFmt/Int64Fmt
85   EW_PUSHPOP,           // Push/Pop/Exchange
86   EW_FINDWINDOW,        // FindWindow
87   EW_SENDMESSAGE,       // SendMessage
88   EW_ISWINDOW,          // IsWindow
89   EW_GETDLGITEM,        // GetDlgItem
90   EW_SETCTLCOLORS,      // SetCtlColors
91   EW_SETBRANDINGIMAGE,  // SetBrandingImage / LoadAndSetImage
92   EW_CREATEFONT,        // CreateFont
93   EW_SHOWWINDOW,        // ShowWindow, EnableWindow, HideWindow
94   EW_SHELLEXEC,         // ExecShell
95   EW_EXECUTE,           // Exec, ExecWait
96   EW_GETFILETIME,       // GetFileTime
97   EW_GETDLLVERSION,     // GetDLLVersion
98 
99   // EW_GETFONTVERSION, // Park : 2.46.2
100   // EW_GETFONTNAME,    // Park : 2.46.3
101 
102   EW_REGISTERDLL,       // RegDLL, UnRegDLL, CallInstDLL
103   EW_CREATESHORTCUT,    // CreateShortCut
104   EW_COPYFILES,         // CopyFiles
105   EW_REBOOT,            // Reboot
106   EW_WRITEINI,          // WriteINIStr, DeleteINISec, DeleteINIStr, FlushINI
107   EW_READINISTR,        // ReadINIStr
108   EW_DELREG,            // DeleteRegValue, DeleteRegKey
109   EW_WRITEREG,          // WriteRegStr, WriteRegExpandStr, WriteRegBin, WriteRegDWORD
110   EW_READREGSTR,        // ReadRegStr, ReadRegDWORD
111   EW_REGENUM,           // EnumRegKey, EnumRegValue
112   EW_FCLOSE,            // FileClose
113   EW_FOPEN,             // FileOpen
114   EW_FPUTS,             // FileWrite, FileWriteByte
115   EW_FGETS,             // FileRead, FileReadByte
116 
117   // Park
118   // EW_FPUTWS,            // FileWriteUTF16LE, FileWriteWord
119   // EW_FGETWS,            // FileReadUTF16LE, FileReadWord
120 
121   EW_FSEEK,             // FileSeek
122   EW_FINDCLOSE,         // FindClose
123   EW_FINDNEXT,          // FindNext
124   EW_FINDFIRST,         // FindFirst
125   EW_WRITEUNINSTALLER,  // WriteUninstaller
126 
127   // Park : since 2.46.3 the log is enabled in main Park version
128   // EW_LOG,               // LogSet, LogText
129 
130   EW_SECTIONSET,        // Get*, Set*
131   EW_INSTTYPESET,       // InstTypeSetText, InstTypeGetText, SetCurInstType, GetCurInstType
132 
133   /*
134   // before v3.06 nsis it was so:
135   // instructions not actually implemented in exehead, but used in compiler.
136   EW_GETLABELADDR,      // both of these get converted to EW_ASSIGNVAR
137   EW_GETFUNCTIONADDR,
138   */
139 
140   // v3.06 and later it was changed to:
141   EW_GETOSINFO,
142   EW_RESERVEDOPCODE,
143 
144   EW_LOCKWINDOW,        // LockWindow
145 
146   // 2 unicode commands available only in Unicode archive
147   EW_FPUTWS,            // FileWriteUTF16LE, FileWriteWord
148   EW_FGETWS,            // FileReadUTF16LE, FileReadWord
149 
150   /*
151   // since v3.06 the fllowing IDs codes was moved here:
152   // Opcodes listed here are not actually used in exehead. No exehead opcodes should be present after these!
153   EW_GETLABELADDR,      // --> EW_ASSIGNVAR
154   EW_GETFUNCTIONADDR,   // --> EW_ASSIGNVAR
155   */
156 
157   // The following IDs are not IDs in real order.
158   // We just need some IDs to translate eny extended layout to main layout.
159 
160   EW_LOG,               // LogSet, LogText
161 
162   // Park
163   EW_FINDPROC,          // FindProc
164 
165   EW_GETFONTVERSION,    // GetFontVersion
166   EW_GETFONTNAME,       // GetFontName
167 
168   kNumCmds
169 };
170 
171 
172 
173 struct CCommandInfo
174 {
175   Byte NumParams;
176 };
177 
178 static const CCommandInfo k_Commands[kNumCmds] =
179 {
180   { 0 }, // "Invalid" },
181   { 0 }, // Return
182   { 1 }, // Nop, Goto
183   { 1 }, // "Abort" },
184   { 0 }, // "Quit" },
185   { 2 }, // Call
186   { 6 }, // "DetailPrint" }, // 1 param in new versions, 6 in old NSIS versions
187   { 1 }, // "Sleep" },
188   { 0 }, // "BringToFront" },
189   { 2 }, // "SetDetailsView" },
190   { 2 }, // "SetFileAttributes" },
191   { 3 }, // CreateDirectory, SetOutPath
192   { 3 }, // "IfFileExists" },
193   { 3 }, // SetRebootFlag, ...
194   { 4 }, // "If" }, // IfAbort, IfSilent, IfErrors, IfRebootFlag
195   { 2 }, // "Get" }, // GetInstDirError, GetErrorLevel
196   { 4 }, // "Rename" },
197   { 3 }, // "GetFullPathName" },
198   { 2 }, // "SearchPath" },
199   { 2 }, // "GetTempFileName" },
200   { 6 }, // "File"
201   { 2 }, // "Delete" },
202   { 6 }, // "MessageBox" },
203   { 2 }, // "RMDir" },
204   { 2 }, // "StrLen" },
205   { 4 }, // StrCpy, GetCurrentAddress
206   { 5 }, // "StrCmp" },
207   { 3 }, // ReadEnvStr, ExpandEnvStrings
208   { 6 }, // "IntCmp" },
209   { 4 }, // "IntOp" },
210   { 4 }, // "IntFmt" }, EW_INTFMT
211   { 6 }, // Push, Pop, Exch // it must be 3 params. But some multi-command write garbage.
212   { 5 }, // "FindWindow" },
213   { 6 }, // "SendMessage" },
214   { 3 }, // "IsWindow" },
215   { 3 }, // "GetDlgItem" },
216   { 2 }, // "SetCtlColors" },
217   { 4 }, // "SetBrandingImage" } // LoadAndSetImage
218   { 5 }, // "CreateFont" },
219   { 4 }, // ShowWindow, EnableWindow, HideWindow
220   { 6 }, // "ExecShell" },
221   { 3 }, // "Exec" }, // Exec, ExecWait
222   { 3 }, // "GetFileTime" },
223   { 4 }, // "GetDLLVersion" },
224   { 6 }, // RegDLL, UnRegDLL, CallInstDLL // it must be 5 params. But some multi-command write garbage.
225   { 6 }, // "CreateShortCut" },
226   { 4 }, // "CopyFiles" },
227   { 1 }, // "Reboot" },
228   { 5 }, // WriteINIStr, DeleteINISec, DeleteINIStr, FlushINI
229   { 4 }, // "ReadINIStr" },
230   { 5 }, // "DeleteReg" }, // DeleteRegKey, DeleteRegValue
231   { 6 }, // "WriteReg" },  // WriteRegStr, WriteRegExpandStr, WriteRegBin, WriteRegDWORD
232   { 5 }, // "ReadReg" }, // ReadRegStr, ReadRegDWORD
233   { 5 }, // "EnumReg" }, // EnumRegKey, EnumRegValue
234   { 1 }, // "FileClose" },
235   { 4 }, // "FileOpen" },
236   { 3 }, // "FileWrite" }, // FileWrite, FileWriteByte
237   { 4 }, // "FileRead" }, // FileRead, FileReadByte
238   { 4 }, // "FileSeek" },
239   { 1 }, // "FindClose" },
240   { 2 }, // "FindNext" },
241   { 3 }, // "FindFirst" },
242   { 4 }, // "WriteUninstaller" },
243   { 5 }, // "Section" },  // ***
244   { 4 }, // InstTypeSetText, InstTypeGetText, SetCurInstType, GetCurInstType
245 
246   // { 6 }, // "GetLabelAddr" }, // before 3.06
247   { 6 }, // "GetOsInfo" }, GetKnownFolderPath, ReadMemory, // v3.06+
248 
249   { 2 }, // "GetFunctionAddress" }, // before 3.06
250 
251   { 1 }, // "LockWindow" },
252   { 4 }, // "FileWrite" }, // FileWriteUTF16LE, FileWriteWord
253   { 4 }, // "FileRead" }, // FileReadUTF16LE, FileReadWord
254 
255   { 2 }, // "Log" }, // LogSet, LogText
256   // Park
257   { 2 }, // "FindProc" },
258   { 2 }, // "GetFontVersion" },
259   { 2 }, // "GetFontName" }
260 };
261 
262 #ifdef NSIS_SCRIPT
263 
264 static const char * const k_CommandNames[kNumCmds] =
265 {
266     "Invalid"
267   , NULL // Return
268   , NULL // Nop, Goto
269   , "Abort"
270   , "Quit"
271   , NULL // Call
272   , "DetailPrint" // 1 param in new versions, 6 in old NSIS versions
273   , "Sleep"
274   , "BringToFront"
275   , "SetDetailsView"
276   , "SetFileAttributes"
277   , NULL // CreateDirectory, SetOutPath
278   , "IfFileExists"
279   , NULL // SetRebootFlag, ...
280   , "If" // IfAbort, IfSilent, IfErrors, IfRebootFlag
281   , "Get" // GetInstDirError, GetErrorLevel
282   , "Rename"
283   , "GetFullPathName"
284   , "SearchPath"
285   , "GetTempFileName"
286   , NULL // File
287   , "Delete"
288   , "MessageBox"
289   , "RMDir"
290   , "StrLen"
291   , NULL // StrCpy, GetCurrentAddress
292   , "StrCmp"
293   , NULL // ReadEnvStr, ExpandEnvStrings
294   , NULL // IntCmp / Int64Cmp / EW_INTCMP
295   , "IntOp"
296   , NULL // IntFmt / Int64Fmt / EW_INTFMT
297   , NULL // Push, Pop, Exch // it must be 3 params. But some multi-command write garbage.
298   , "FindWindow"
299   , "SendMessage"
300   , "IsWindow"
301   , "GetDlgItem"
302   , "SetCtlColors"
303   , "SetBrandingImage"
304   , "CreateFont"
305   , NULL // ShowWindow, EnableWindow, HideWindow
306   , "ExecShell"
307   , "Exec" // Exec, ExecWait
308   , "GetFileTime"
309   , "GetDLLVersion"
310   , NULL // RegDLL, UnRegDLL, CallInstDLL // it must be 5 params. But some multi-command write garbage.
311   , "CreateShortCut"
312   , "CopyFiles"
313   , "Reboot"
314   , NULL // WriteINIStr, DeleteINISec, DeleteINIStr, FlushINI
315   , "ReadINIStr"
316   , "DeleteReg" // DeleteRegKey, DeleteRegValue
317   , "WriteReg"  // WriteRegStr, WriteRegExpandStr, WriteRegBin, WriteRegDWORD
318   , "ReadReg" // ReadRegStr, ReadRegDWORD
319   , "EnumReg" // EnumRegKey, EnumRegValue
320   , "FileClose"
321   , "FileOpen"
322   , "FileWrite" // FileWrite, FileWriteByte
323   , "FileRead" // FileRead, FileReadByte
324   , "FileSeek"
325   , "FindClose"
326   , "FindNext"
327   , "FindFirst"
328   , "WriteUninstaller"
329   , "Section"  // ***
330   , NULL // InstTypeSetText, InstTypeGetText, SetCurInstType, GetCurInstType
331 
332   , NULL // "GetOsInfo" // , "GetLabelAddr" //
333   , "GetFunctionAddress"
334 
335   , "LockWindow"
336   , "FileWrite" // FileWriteUTF16LE, FileWriteWord
337   , "FileRead" // FileReadUTF16LE, FileReadWord
338 
339   , "Log" // LogSet, LogText
340 
341   // Park
342   , "FindProc"
343   , "GetFontVersion"
344   , "GetFontName"
345 };
346 
347 #endif
348 
349 /* NSIS can use one name for two CSIDL_*** and CSIDL_COMMON_*** items (CurrentUser / AllUsers)
350    Some NSIS shell names are not identical to WIN32 CSIDL_* names.
351    NSIS doesn't use some CSIDL_* values. But we add name for all CSIDL_ (marked with '+'). */
352 
353 static const char * const kShellStrings[] =
354 {
355     "DESKTOP"     // +
356   , "INTERNET"    // +
357   , "SMPROGRAMS"  // CSIDL_PROGRAMS
358   , "CONTROLS"    // +
359   , "PRINTERS"    // +
360   , "DOCUMENTS"   // CSIDL_PERSONAL
361   , "FAVORITES"   // CSIDL_FAVORITES
362   , "SMSTARTUP"   // CSIDL_STARTUP
363   , "RECENT"      // CSIDL_RECENT
364   , "SENDTO"      // CSIDL_SENDTO
365   , "BITBUCKET"   // +
366   , "STARTMENU"
367   , NULL          // CSIDL_MYDOCUMENTS = CSIDL_PERSONAL
368   , "MUSIC"       // CSIDL_MYMUSIC
369   , "VIDEOS"      // CSIDL_MYVIDEO
370   , NULL
371   , "DESKTOP"     // CSIDL_DESKTOPDIRECTORY
372   , "DRIVES"      // +
373   , "NETWORK"     // +
374   , "NETHOOD"
375   , "FONTS"
376   , "TEMPLATES"
377   , "STARTMENU"   // CSIDL_COMMON_STARTMENU
378   , "SMPROGRAMS"  // CSIDL_COMMON_PROGRAMS
379   , "SMSTARTUP"   // CSIDL_COMMON_STARTUP
380   , "DESKTOP"     // CSIDL_COMMON_DESKTOPDIRECTORY
381   , "APPDATA"     // CSIDL_APPDATA         !!! "QUICKLAUNCH"
382   , "PRINTHOOD"
383   , "LOCALAPPDATA"
384   , "ALTSTARTUP"
385   , "ALTSTARTUP"  // CSIDL_COMMON_ALTSTARTUP
386   , "FAVORITES"   // CSIDL_COMMON_FAVORITES
387   , "INTERNET_CACHE"
388   , "COOKIES"
389   , "HISTORY"
390   , "APPDATA"     // CSIDL_COMMON_APPDATA
391   , "WINDIR"
392   , "SYSDIR"
393   , "PROGRAM_FILES" // +
394   , "PICTURES"    // CSIDL_MYPICTURES
395   , "PROFILE"
396   , "SYSTEMX86" // +
397   , "PROGRAM_FILESX86" // +
398   , "PROGRAM_FILES_COMMON" // +
399   , "PROGRAM_FILES_COMMONX8" // +  CSIDL_PROGRAM_FILES_COMMONX86
400   , "TEMPLATES"   // CSIDL_COMMON_TEMPLATES
401   , "DOCUMENTS"   // CSIDL_COMMON_DOCUMENTS
402   , "ADMINTOOLS"  // CSIDL_COMMON_ADMINTOOLS
403   , "ADMINTOOLS"  // CSIDL_ADMINTOOLS
404   , "CONNECTIONS" // +
405   , NULL
406   , NULL
407   , NULL
408   , "MUSIC"       // CSIDL_COMMON_MUSIC
409   , "PICTURES"    // CSIDL_COMMON_PICTURES
410   , "VIDEOS"      // CSIDL_COMMON_VIDEO
411   , "RESOURCES"
412   , "RESOURCES_LOCALIZED"
413   , "COMMON_OEM_LINKS" // +
414   , "CDBURN_AREA"
415   , NULL // unused
416   , "COMPUTERSNEARME" // +
417 };
418 
419 
UIntToString(AString & s,UInt32 v)420 static inline void UIntToString(AString &s, UInt32 v)
421 {
422   s.Add_UInt32(v);
423 }
424 
425 #ifdef NSIS_SCRIPT
426 
Add_UInt(UInt32 v)427 void CInArchive::Add_UInt(UInt32 v)
428 {
429   char sz[16];
430   ConvertUInt32ToString(v, sz);
431   Script += sz;
432 }
433 
Add_SignedInt(CDynLimBuf & s,Int32 v)434 static void Add_SignedInt(CDynLimBuf &s, Int32 v)
435 {
436   char sz[32];
437   ConvertInt64ToString(v, sz);
438   s += sz;
439 }
440 
Add_Hex(CDynLimBuf & s,UInt32 v)441 static void Add_Hex(CDynLimBuf &s, UInt32 v)
442 {
443   char sz[16];
444   sz[0] = '0';
445   sz[1] = 'x';
446   ConvertUInt32ToHex(v, sz + 2);
447   s += sz;
448 }
449 
GetUi16Str_Len(const Byte * p)450 static UInt32 GetUi16Str_Len(const Byte *p)
451 {
452   const Byte *pp = p;
453   for (; *pp != 0 || *(pp + 1) != 0; pp += 2);
454   return (UInt32)((pp - p) >> 1);
455 }
456 
AddLicense(UInt32 param,Int32 langID)457 void CInArchive::AddLicense(UInt32 param, Int32 langID)
458 {
459   Space();
460   if (param >= NumStringChars ||
461       param + 1 >= NumStringChars)
462   {
463     Script += kErrorStr;
464     return;
465   }
466   strUsed[param] = 1;
467 
468   const UInt32 start = _stringsPos + (IsUnicode ? param * 2 : param);
469   const UInt32 offset = start + (IsUnicode ? 2 : 1);
470   {
471     FOR_VECTOR (i, LicenseFiles)
472     {
473       const CLicenseFile &lic = LicenseFiles[i];
474       if (offset == lic.Offset)
475       {
476         Script += lic.Name;
477         return;
478       }
479     }
480   }
481   AString fileName ("[LICENSE]");
482   if (langID >= 0)
483   {
484     fileName += "\\license-";
485     // LangId_To_String(fileName, langID);
486     UIntToString(fileName, (UInt32)langID);
487   }
488   else if (++_numRootLicenses > 1)
489   {
490     fileName.Add_Minus();
491     UIntToString(fileName, _numRootLicenses);
492   }
493   const Byte *sz = (_data + start);
494   const unsigned marker = IsUnicode ? Get16(sz) : *sz;
495   const bool isRTF = (marker == 2);
496   fileName += isRTF ? ".rtf" : ".txt"; // if (*sz == 1) it's text;
497   Script += fileName;
498 
499   CLicenseFile &lic = LicenseFiles.AddNew();
500   lic.Name = fileName;
501   lic.Offset = offset;
502   if (!IsUnicode)
503     lic.Size = (UInt32)strlen((const char *)sz + 1);
504   else
505   {
506     sz += 2;
507     const UInt32 len = GetUi16Str_Len(sz);
508     lic.Size = len * 2;
509     if (isRTF)
510     {
511       lic.Text.Alloc((size_t)len);
512       for (UInt32 i = 0; i < len; i++, sz += 2)
513       {
514         unsigned c = Get16(sz);
515         if (c >= 256)
516           c = '?';
517         lic.Text[i] = (Byte)(c);
518       }
519       lic.Size = len;
520       lic.Offset = 0;
521     }
522   }
523 }
524 
525 #endif
526 
527 
528 #ifdef NSIS_SCRIPT
529 #define Z7_NSIS_WIN_GENERIC_READ    ((UInt32)1 << 31)
530 #endif
531 #define Z7_NSIS_WIN_GENERIC_WRITE   ((UInt32)1 << 30)
532 #ifdef NSIS_SCRIPT
533 #define Z7_NSIS_WIN_GENERIC_EXECUTE ((UInt32)1 << 29)
534 #define Z7_NSIS_WIN_GENERIC_ALL     ((UInt32)1 << 28)
535 #endif
536 
537 #ifdef NSIS_SCRIPT
538 #define Z7_NSIS_WIN_CREATE_NEW        1
539 #endif
540 #define Z7_NSIS_WIN_CREATE_ALWAYS     2
541 #ifdef NSIS_SCRIPT
542 #define Z7_NSIS_WIN_OPEN_EXISTING     3
543 #define Z7_NSIS_WIN_OPEN_ALWAYS       4
544 #define Z7_NSIS_WIN_TRUNCATE_EXISTING 5
545 #endif
546 
547 
548 // #define kVar_CMDLINE    20
549 #define kVar_INSTDIR    21
550 #define kVar_OUTDIR     22
551 #define kVar_EXEDIR     23
552 // #define kVar_LANGUAGE   24
553 #define kVar_TEMP       25
554 #define kVar_PLUGINSDIR 26
555 #define kVar_EXEPATH    27  // NSIS 2.26+
556 // #define kVar_EXEFILE    28  // NSIS 2.26+
557 
558 #define kVar_HWNDPARENT_225 27
559 #ifdef NSIS_SCRIPT
560 #define kVar_HWNDPARENT     29
561 #endif
562 
563 // #define kVar__CLICK 30
564 #define kVar_Spec_OUTDIR_225  29  // NSIS 2.04 - 2.25
565 #define kVar_Spec_OUTDIR      31  // NSIS 2.26+
566 
567 
568 static const char * const kVarStrings[] =
569 {
570     "CMDLINE"
571   , "INSTDIR"
572   , "OUTDIR"
573   , "EXEDIR"
574   , "LANGUAGE"
575   , "TEMP"
576   , "PLUGINSDIR"
577   , "EXEPATH"   // NSIS 2.26+
578   , "EXEFILE"   // NSIS 2.26+
579   , "HWNDPARENT"
580   , "_CLICK"    // is set from page->clicknext
581   , "_OUTDIR"   // NSIS 2.04+
582 };
583 
584 static const unsigned kNumInternalVars = 20 + Z7_ARRAY_SIZE(kVarStrings);
585 
586 #define GET_NUM_INTERNAL_VARS (IsNsis200 ? kNumInternalVars - 3 : IsNsis225 ? kNumInternalVars - 2 : kNumInternalVars)
587 
GetVar2(AString & res,UInt32 index)588 void CInArchive::GetVar2(AString &res, UInt32 index)
589 {
590   if (index < 20)
591   {
592     if (index >= 10)
593     {
594       res += 'R';
595       index -= 10;
596     }
597     UIntToString(res, index);
598   }
599   else
600   {
601     unsigned numInternalVars = GET_NUM_INTERNAL_VARS;
602     if (index < numInternalVars)
603     {
604       if (IsNsis225 && index >= kVar_EXEPATH)
605         index += 2;
606       res += kVarStrings[index - 20];
607     }
608     else
609     {
610       res += '_';
611       UIntToString(res, index - numInternalVars);
612       res += '_';
613     }
614   }
615 }
616 
GetVar(AString & res,UInt32 index)617 void CInArchive::GetVar(AString &res, UInt32 index)
618 {
619   res += '$';
620   GetVar2(res, index);
621 }
622 
623 #ifdef NSIS_SCRIPT
624 
Add_Var(UInt32 index)625 void CInArchive::Add_Var(UInt32 index)
626 {
627   _tempString_for_GetVar.Empty();
628   GetVar(_tempString_for_GetVar, index);
629   Script += _tempString_for_GetVar;
630 }
631 
AddParam_Var(UInt32 index)632 void CInArchive::AddParam_Var(UInt32 index)
633 {
634   Space();
635   Add_Var(index);
636 }
637 
AddParam_UInt(UInt32 value)638 void CInArchive::AddParam_UInt(UInt32 value)
639 {
640   Space();
641   Add_UInt(value);
642 }
643 
644 #endif
645 
646 
647 #define NS_CODE_SKIP    252
648 #define NS_CODE_VAR     253
649 #define NS_CODE_SHELL   254
650 // #define NS_CODE_LANG    255
651 
652 // #define NS_3_CODE_LANG  1
653 #define NS_3_CODE_SHELL 2
654 #define NS_3_CODE_VAR   3
655 #define NS_3_CODE_SKIP  4
656 
657 #define PARK_CODE_SKIP  0xE000
658 #define PARK_CODE_VAR   0xE001
659 #define PARK_CODE_SHELL 0xE002
660 #define PARK_CODE_LANG  0xE003
661 
662 #define IS_NS_SPEC_CHAR(c) ((c) >= NS_CODE_SKIP)
663 #define IS_PARK_SPEC_CHAR(c) ((c) >= PARK_CODE_SKIP && (c) <= PARK_CODE_LANG)
664 
665 #define DECODE_NUMBER_FROM_2_CHARS(c0, c1) (((unsigned)(c0) & 0x7F) | (((unsigned)((c1) & 0x7F)) << 7))
666 #define CONVERT_NUMBER_NS_3_UNICODE(n) n = ((n & 0x7F) | (((n >> 8) & 0x7F) << 7))
667 #define CONVERT_NUMBER_PARK(n) n &= 0x7FFF
668 
669 
AreStringsEqual_16and8(const Byte * p16,const char * p8)670 static bool AreStringsEqual_16and8(const Byte *p16, const char *p8)
671 {
672   for (;;)
673   {
674     unsigned c16 = Get16(p16); p16 += 2;
675     unsigned c = (Byte)(*p8++);
676     if (c16 != c)
677       return false;
678     if (c == 0)
679       return true;
680   }
681 }
682 
GetShellString(AString & s,unsigned index1,unsigned index2)683 void CInArchive::GetShellString(AString &s, unsigned index1, unsigned index2)
684 {
685   // zeros are not allowed here.
686   // if (index1 == 0 || index2 == 0) throw 333;
687 
688   if ((index1 & 0x80) != 0)
689   {
690     unsigned offset = (index1 & 0x3F);
691 
692     /* NSIS reads registry string:
693          keyName   = HKLM Software\\Microsoft\\Windows\\CurrentVersion
694          mask      = KEY_WOW64_64KEY, If 64-bit flag in index1 is set
695          valueName = string(offset)
696        If registry reading is failed, NSIS uses second parameter (index2)
697        to read string. The recursion is possible in that case in NSIS.
698        We don't parse index2 string. We only set strUsed status for that
699        string (but without recursion). */
700 
701     if (offset >= NumStringChars)
702     {
703       s += kErrorStr;
704       return;
705     }
706 
707     #ifdef NSIS_SCRIPT
708     strUsed[offset] = 1;
709     if (index2 < NumStringChars)
710       strUsed[index2] = 1;
711     #endif
712 
713     const Byte *p = (const Byte *)(_data + _stringsPos);
714     int id = -1;
715     if (IsUnicode)
716     {
717       p += offset * 2;
718       if (AreStringsEqual_16and8(p, "ProgramFilesDir"))
719         id = 0;
720       else if (AreStringsEqual_16and8(p, "CommonFilesDir"))
721         id = 1;
722     }
723     else
724     {
725       p += offset;
726       if (strcmp((const char *)p, "ProgramFilesDir") == 0)
727         id = 0;
728       else if (strcmp((const char *)p, "CommonFilesDir") == 0)
729         id = 1;
730     }
731 
732     s += ((id >= 0) ? (id == 0 ? "$PROGRAMFILES" : "$COMMONFILES") :
733       "$_ERROR_UNSUPPORTED_VALUE_REGISTRY_");
734     // s += ((index1 & 0x40) != 0) ? "64" : "32";
735     if ((index1 & 0x40) != 0)
736       s += "64";
737 
738     if (id < 0)
739     {
740       s += '(';
741       if (IsUnicode)
742       {
743         for (unsigned i = 0; i < 256; i++)
744         {
745           wchar_t c = Get16(p + i * 2);
746           if (c == 0)
747             break;
748           if (c < 0x80)
749             s += (char)c;
750         }
751       }
752       else
753         s += (const char *)p;
754       s += ')';
755     }
756     return;
757   }
758 
759   s += '$';
760   if (index1 < Z7_ARRAY_SIZE(kShellStrings))
761   {
762     const char *sz = kShellStrings[index1];
763     if (sz)
764     {
765       s += sz;
766       return;
767     }
768   }
769   if (index2 < Z7_ARRAY_SIZE(kShellStrings))
770   {
771     const char *sz = kShellStrings[index2];
772     if (sz)
773     {
774       s += sz;
775       return;
776     }
777   }
778   s += "_ERROR_UNSUPPORTED_SHELL_";
779   s += '[';
780   UIntToString(s, index1);
781   s += ',';
782   UIntToString(s, index2);
783   s += ']';
784 }
785 
786 #ifdef NSIS_SCRIPT
787 
Add_LangStr_Simple(UInt32 id)788 void CInArchive::Add_LangStr_Simple(UInt32 id)
789 {
790   Script += "LSTR_";
791   Add_UInt(id);
792 }
793 
794 #endif
795 
Add_LangStr(AString & res,UInt32 id)796 void CInArchive::Add_LangStr(AString &res, UInt32 id)
797 {
798   #ifdef NSIS_SCRIPT
799   langStrIDs.Add(id);
800   #endif
801   res += "$(LSTR_";
802   UIntToString(res, id);
803   res += ')';
804 }
805 
GetNsisString_Raw(const Byte * s)806 void CInArchive::GetNsisString_Raw(const Byte *s)
807 {
808   Raw_AString.Empty();
809 
810   if (NsisType != k_NsisType_Nsis3)
811   {
812     for (;;)
813     {
814       Byte c = *s++;
815       if (c == 0)
816         return;
817       if (IS_NS_SPEC_CHAR(c))
818       {
819         Byte c0 = *s++;
820         if (c0 == 0)
821           return;
822         if (c != NS_CODE_SKIP)
823         {
824           Byte c1 = *s++;
825           if (c1 == 0)
826             return;
827 
828           if (c == NS_CODE_SHELL)
829             GetShellString(Raw_AString, c0, c1);
830           else
831           {
832             unsigned n = DECODE_NUMBER_FROM_2_CHARS(c0, c1);
833             if (c == NS_CODE_VAR)
834               GetVar(Raw_AString, n);
835             else //  if (c == NS_CODE_LANG)
836               Add_LangStr(Raw_AString, n);
837           }
838           continue;
839         }
840         c = c0;
841       }
842       Raw_AString += (char)c;
843     }
844   }
845 
846   // NSIS-3 ANSI
847   for (;;)
848   {
849     Byte c = *s++;
850     if (c <= NS_3_CODE_SKIP)
851     {
852       if (c == 0)
853         return;
854       Byte c0 = *s++;
855       if (c0 == 0)
856         return;
857       if (c != NS_3_CODE_SKIP)
858       {
859         Byte c1 = *s++;
860         if (c1 == 0)
861           return;
862 
863         if (c == NS_3_CODE_SHELL)
864           GetShellString(Raw_AString, c0, c1);
865         else
866         {
867           unsigned n = DECODE_NUMBER_FROM_2_CHARS(c0, c1);
868           if (c == NS_3_CODE_VAR)
869             GetVar(Raw_AString, n);
870           else // if (c == NS_3_CODE_LANG)
871             Add_LangStr(Raw_AString, n);
872         }
873         continue;
874       }
875       c = c0;
876     }
877     Raw_AString += (char)c;
878   }
879 }
880 
881 #ifdef NSIS_SCRIPT
882 
GetNsisString(AString & res,const Byte * s)883 void CInArchive::GetNsisString(AString &res, const Byte *s)
884 {
885   for (;;)
886   {
887     Byte c = *s++;
888     if (c == 0)
889       return;
890     if (NsisType != k_NsisType_Nsis3)
891     {
892       if (IS_NS_SPEC_CHAR(c))
893       {
894         Byte c0 = *s++;
895         if (c0 == 0)
896           return;
897         if (c != NS_CODE_SKIP)
898         {
899           Byte c1 = *s++;
900           if (c1 == 0)
901             return;
902           if (c == NS_CODE_SHELL)
903             GetShellString(res, c0, c1);
904           else
905           {
906             unsigned n = DECODE_NUMBER_FROM_2_CHARS(c0, c1);
907             if (c == NS_CODE_VAR)
908               GetVar(res, n);
909             else // if (c == NS_CODE_LANG)
910               Add_LangStr(res, n);
911           }
912           continue;
913         }
914         c = c0;
915       }
916     }
917     else
918     {
919       // NSIS-3 ANSI
920       if (c <= NS_3_CODE_SKIP)
921       {
922         Byte c0 = *s++;
923         if (c0 == 0)
924           return;
925         if (c0 == 0)
926           break;
927         if (c != NS_3_CODE_SKIP)
928         {
929           Byte c1 = *s++;
930           if (c1 == 0)
931             return;
932           if (c == NS_3_CODE_SHELL)
933             GetShellString(res, c0, c1);
934           else
935           {
936             unsigned n = DECODE_NUMBER_FROM_2_CHARS(c0, c1);
937             if (c == NS_3_CODE_VAR)
938               GetVar(res, n);
939             else // if (c == NS_3_CODE_LANG)
940               Add_LangStr(res, n);
941           }
942           continue;
943         }
944         c = c0;
945       }
946     }
947 
948     {
949       const char *e;
950            if (c ==   9) e = "$\\t";
951       else if (c ==  10) e = "$\\n";
952       else if (c ==  13) e = "$\\r";
953       else if (c == '"') e = "$\\\"";
954       else if (c == '$') e = "$$";
955       else
956       {
957         res += (char)c;
958         continue;
959       }
960       res += e;
961       continue;
962     }
963   }
964 }
965 
966 #endif
967 
GetNsisString_Unicode_Raw(const Byte * p)968 void CInArchive::GetNsisString_Unicode_Raw(const Byte *p)
969 {
970   Raw_UString.Empty();
971 
972   if (IsPark())
973   {
974     for (;;)
975     {
976       unsigned c = Get16(p);
977       p += 2;
978       if (c == 0)
979         break;
980       if (c < 0x80)
981       {
982         Raw_UString.Add_Char((char)c);
983         continue;
984       }
985 
986       if (IS_PARK_SPEC_CHAR(c))
987       {
988         unsigned n = Get16(p);
989         p += 2;
990         if (n == 0)
991           break;
992         if (c != PARK_CODE_SKIP)
993         {
994           Raw_AString.Empty();
995           if (c == PARK_CODE_SHELL)
996             GetShellString(Raw_AString, n & 0xFF, n >> 8);
997           else
998           {
999             CONVERT_NUMBER_PARK(n);
1000             if (c == PARK_CODE_VAR)
1001               GetVar(Raw_AString, n);
1002             else // if (c == PARK_CODE_LANG)
1003               Add_LangStr(Raw_AString, n);
1004           }
1005           Raw_UString += Raw_AString.Ptr(); // check it !
1006           continue;
1007         }
1008         c = n;
1009       }
1010 
1011       Raw_UString += (wchar_t)c;
1012     }
1013 
1014     return;
1015   }
1016 
1017   // NSIS-3 Unicode
1018   for (;;)
1019   {
1020     unsigned c = Get16(p);
1021     p += 2;
1022     if (c > NS_3_CODE_SKIP)
1023     {
1024       Raw_UString += (wchar_t)c;
1025       continue;
1026     }
1027     if (c == 0)
1028       break;
1029 
1030     unsigned n = Get16(p);
1031     p += 2;
1032     if (n == 0)
1033       break;
1034     if (c == NS_3_CODE_SKIP)
1035     {
1036       Raw_UString += (wchar_t)n;
1037       continue;
1038     }
1039 
1040     Raw_AString.Empty();
1041     if (c == NS_3_CODE_SHELL)
1042       GetShellString(Raw_AString, n & 0xFF, n >> 8);
1043     else
1044     {
1045       CONVERT_NUMBER_NS_3_UNICODE(n);
1046       if (c == NS_3_CODE_VAR)
1047         GetVar(Raw_AString, n);
1048       else // if (c == NS_3_CODE_LANG)
1049         Add_LangStr(Raw_AString, n);
1050     }
1051     Raw_UString += Raw_AString.Ptr();
1052   }
1053 }
1054 
1055 #ifdef NSIS_SCRIPT
1056 
1057 static const Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
1058 
GetNsisString_Unicode(AString & res,const Byte * p)1059 void CInArchive::GetNsisString_Unicode(AString &res, const Byte *p)
1060 {
1061   for (;;)
1062   {
1063     unsigned c = Get16(p);
1064     p += 2;
1065     if (c == 0)
1066       break;
1067     if (IsPark())
1068     {
1069       if (IS_PARK_SPEC_CHAR(c))
1070       {
1071         unsigned n = Get16(p);
1072         p += 2;
1073         if (n == 0)
1074           break;
1075         if (c != PARK_CODE_SKIP)
1076         {
1077           if (c == PARK_CODE_SHELL)
1078             GetShellString(res, n & 0xFF, n >> 8);
1079           else
1080           {
1081             CONVERT_NUMBER_PARK(n);
1082             if (c == PARK_CODE_VAR)
1083               GetVar(res, n);
1084             else // if (c == PARK_CODE_LANG)
1085               Add_LangStr(res, n);
1086           }
1087           continue;
1088         }
1089         c = n;
1090       }
1091     }
1092     else
1093     {
1094       // NSIS-3 Unicode
1095       if (c <= NS_3_CODE_SKIP)
1096       {
1097         unsigned n = Get16(p);
1098         p += 2;
1099         if (n == 0)
1100           break;
1101         if (c != NS_3_CODE_SKIP)
1102         {
1103           if (c == NS_3_CODE_SHELL)
1104             GetShellString(res, n & 0xFF, n >> 8);
1105           else
1106           {
1107             CONVERT_NUMBER_NS_3_UNICODE(n);
1108             if (c == NS_3_CODE_VAR)
1109               GetVar(res, n);
1110             else // if (c == NS_3_CODE_LANG)
1111               Add_LangStr(res, n);
1112           }
1113           continue;
1114         }
1115         c = n;
1116       }
1117     }
1118 
1119     if (c < 0x80)
1120     {
1121       const char *e;
1122            if (c ==   9) e = "$\\t";
1123       else if (c ==  10) e = "$\\n";
1124       else if (c ==  13) e = "$\\r";
1125       else if (c == '"') e = "$\\\"";
1126       else if (c == '$') e = "$$";
1127       else
1128       {
1129         res += (char)c;
1130         continue;
1131       }
1132       res += e;
1133       continue;
1134     }
1135 
1136     UInt32 value = c;
1137     /*
1138     if (value >= 0xD800 && value < 0xE000)
1139     {
1140       UInt32 c2;
1141       if (value >= 0xDC00 || srcPos == srcLen)
1142         break;
1143       c2 = src[srcPos++];
1144       if (c2 < 0xDC00 || c2 >= 0xE000)
1145         break;
1146       value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000;
1147     }
1148     */
1149     unsigned numAdds;
1150     for (numAdds = 1; numAdds < 5; numAdds++)
1151       if (value < (((UInt32)1) << (numAdds * 5 + 6)))
1152         break;
1153     res += (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds)));
1154     do
1155     {
1156       numAdds--;
1157       res += (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F));
1158       // destPos++;
1159     }
1160     while (numAdds != 0);
1161 
1162     // AddToUtf8(res, c);
1163   }
1164 }
1165 
1166 #endif
1167 
ReadString2_Raw(UInt32 pos)1168 void CInArchive::ReadString2_Raw(UInt32 pos)
1169 {
1170   Raw_AString.Empty();
1171   Raw_UString.Empty();
1172   if ((Int32)pos < 0)
1173     Add_LangStr(Raw_AString, (UInt32)-((Int32)pos + 1));
1174   else if (pos >= NumStringChars)
1175   {
1176     Raw_AString += kErrorStr;
1177     // UIntToString(Raw_AString, pos);
1178   }
1179   else
1180   {
1181     if (IsUnicode)
1182       GetNsisString_Unicode_Raw(_data + _stringsPos + pos * 2);
1183     else
1184       GetNsisString_Raw(_data + _stringsPos + pos);
1185     return;
1186   }
1187   Raw_UString = Raw_AString.Ptr();
1188 }
1189 
IsGoodString(UInt32 param) const1190 bool CInArchive::IsGoodString(UInt32 param) const
1191 {
1192   if (param >= NumStringChars)
1193     return false;
1194   if (param == 0)
1195     return true;
1196   const Byte *p = _data + _stringsPos;
1197   unsigned c;
1198   if (IsUnicode)
1199     c = Get16(p + param * 2 - 2);
1200   else
1201     c = p[param - 1];
1202   // some files have '\\' character before string?
1203   return (c == 0 || c == '\\');
1204 }
1205 
AreTwoParamStringsEqual(UInt32 param1,UInt32 param2) const1206 bool CInArchive::AreTwoParamStringsEqual(UInt32 param1, UInt32 param2) const
1207 {
1208   if (param1 == param2)
1209     return true;
1210 
1211   /* NSIS-3.0a1 probably contains bug, so it can use 2 different strings
1212      with same content. So we check real string also.
1213      Also it's possible to check identical postfix parts of strings. */
1214 
1215   if (param1 >= NumStringChars ||
1216       param2 >= NumStringChars)
1217     return false;
1218 
1219   const Byte *p = _data + _stringsPos;
1220 
1221   if (IsUnicode)
1222   {
1223     const Byte *p1 = p + param1 * 2;
1224     const Byte *p2 = p + param2 * 2;
1225     for (;;)
1226     {
1227       UInt16 c = Get16(p1);
1228       if (c != Get16(p2))
1229         return false;
1230       if (c == 0)
1231         return true;
1232       p1 += 2;
1233       p2 += 2;
1234     }
1235   }
1236   else
1237   {
1238     const Byte *p1 = p + param1;
1239     const Byte *p2 = p + param2;
1240     for (;;)
1241     {
1242       Byte c = *p1++;
1243       if (c != *p2++)
1244         return false;
1245       if (c == 0)
1246         return true;
1247     }
1248   }
1249 }
1250 
1251 #ifdef NSIS_SCRIPT
1252 
GetNumUsedVars() const1253 UInt32 CInArchive::GetNumUsedVars() const
1254 {
1255   UInt32 numUsedVars = 0;
1256   const Byte *data = (const Byte *)_data + _stringsPos;
1257   unsigned npi = 0;
1258   for (UInt32 i = 0; i < NumStringChars;)
1259   {
1260     bool process = true;
1261     if (npi < noParseStringIndexes.Size() && noParseStringIndexes[npi] == i)
1262     {
1263       process = false;
1264       npi++;
1265     }
1266 
1267     if (IsUnicode)
1268     {
1269       if (IsPark())
1270       {
1271         for (;;)
1272         {
1273           unsigned c = Get16(data + i * 2);
1274           i++;
1275           if (c == 0)
1276             break;
1277           if (IS_PARK_SPEC_CHAR(c))
1278           {
1279             UInt32 n = Get16(data + i * 2);
1280             i++;
1281             if (n == 0)
1282               break;
1283             if (process && c == PARK_CODE_VAR)
1284             {
1285               CONVERT_NUMBER_PARK(n);
1286               n++;
1287               if (numUsedVars < n)
1288                 numUsedVars = n;
1289             }
1290           }
1291         }
1292       }
1293       else // NSIS-3 Unicode
1294       {
1295         for (;;)
1296         {
1297           unsigned c = Get16(data + i * 2);
1298           i++;
1299           if (c == 0)
1300             break;
1301           if (c > NS_3_CODE_SKIP)
1302             continue;
1303           UInt32 n = Get16(data + i * 2);
1304           i++;
1305           if (n == 0)
1306             break;
1307           if (process && c == NS_3_CODE_VAR)
1308           {
1309             CONVERT_NUMBER_NS_3_UNICODE(n);
1310             n++;
1311             if (numUsedVars < n)
1312               numUsedVars = n;
1313           }
1314         }
1315       }
1316     }
1317     else // not Unicode (ANSI)
1318     {
1319       if (NsisType != k_NsisType_Nsis3)
1320       {
1321         for (;;)
1322         {
1323           Byte c = data[i++];
1324           if (c == 0)
1325             break;
1326           if (IS_NS_SPEC_CHAR(c))
1327           {
1328             Byte c0 = data[i++];
1329             if (c0 == 0)
1330               break;
1331             if (c == NS_CODE_SKIP)
1332               continue;
1333             Byte c1 = data[i++];
1334             if (c1 == 0)
1335               break;
1336             if (process && c == NS_CODE_VAR)
1337             {
1338               UInt32 n = DECODE_NUMBER_FROM_2_CHARS(c0, c1);
1339               n++;
1340               if (numUsedVars < n)
1341                 numUsedVars = n;
1342             }
1343           }
1344         }
1345       }
1346       else
1347       {
1348         // NSIS-3 ANSI
1349         for (;;)
1350         {
1351           Byte c = data[i++];
1352           if (c == 0)
1353             break;
1354           if (c > NS_3_CODE_SKIP)
1355             continue;
1356 
1357           Byte c0 = data[i++];
1358           if (c0 == 0)
1359             break;
1360           if (c == NS_3_CODE_SKIP)
1361             continue;
1362           Byte c1 = data[i++];
1363           if (c1 == 0)
1364             break;
1365           if (process && c == NS_3_CODE_VAR)
1366           {
1367             UInt32 n = DECODE_NUMBER_FROM_2_CHARS(c0, c1);
1368             n++;
1369             if (numUsedVars < n)
1370               numUsedVars = n;
1371           }
1372         }
1373       }
1374     }
1375   }
1376   return numUsedVars;
1377 }
1378 
ReadString2(AString & s,UInt32 pos)1379 void CInArchive::ReadString2(AString &s, UInt32 pos)
1380 {
1381   if ((Int32)pos < 0)
1382   {
1383     Add_LangStr(s, (UInt32)-((Int32)pos + 1));
1384     return;
1385   }
1386 
1387   if (pos >= NumStringChars)
1388   {
1389     s += kErrorStr;
1390     // UIntToString(s, pos);
1391     return;
1392   }
1393 
1394   #ifdef NSIS_SCRIPT
1395   strUsed[pos] = 1;
1396   #endif
1397 
1398   if (IsUnicode)
1399     GetNsisString_Unicode(s, _data + _stringsPos + pos * 2);
1400   else
1401     GetNsisString(s, _data + _stringsPos + pos);
1402 }
1403 
1404 #endif
1405 
1406 #ifdef NSIS_SCRIPT
1407 
1408 // #define DEL_DIR     1
1409 #define DEL_RECURSE 2
1410 #define DEL_REBOOT  4
1411 // #define DEL_SIMPLE  8
1412 
AddRegRoot(UInt32 val)1413 void CInArchive::AddRegRoot(UInt32 val)
1414 {
1415   Space();
1416   const char *s;
1417   switch (val)
1418   {
1419     case 0:  s = "SHCTX"; break;
1420     case 0x80000000:  s = "HKCR"; break;
1421     case 0x80000001:  s = "HKCU"; break;
1422     case 0x80000002:  s = "HKLM"; break;
1423     case 0x80000003:  s = "HKU";  break;
1424     case 0x80000004:  s = "HKPD"; break;
1425     case 0x80000005:  s = "HKCC"; break;
1426     case 0x80000006:  s = "HKDD"; break;
1427     case 0x80000050:  s = "HKPT"; break;
1428     case 0x80000060:  s = "HKPN"; break;
1429     default:
1430       // Script += " RRRRR ";
1431       // throw 1;
1432       Add_Hex(Script, val); return;
1433   }
1434   Script += s;
1435 }
1436 
1437 static const char * const g_WinAttrib[] =
1438 {
1439     "READONLY"
1440   , "HIDDEN"
1441   , "SYSTEM"
1442   , NULL
1443   , "DIRECTORY"
1444   , "ARCHIVE"
1445   , "DEVICE"
1446   , "NORMAL"
1447   , "TEMPORARY"
1448   , "SPARSE_FILE"
1449   , "REPARSE_POINT"
1450   , "COMPRESSED"
1451   , "OFFLINE"
1452   , "NOT_CONTENT_INDEXED"
1453   , "ENCRYPTED"
1454   , NULL
1455   , "VIRTUAL"
1456 };
1457 
1458 #define FLAGS_DELIMITER '|'
1459 
FlagsToString2(CDynLimBuf & s,const char * const * table,unsigned num,UInt32 flags)1460 static void FlagsToString2(CDynLimBuf &s, const char * const *table, unsigned num, UInt32 flags)
1461 {
1462   bool filled = false;
1463   for (unsigned i = 0; i < num; i++)
1464   {
1465     UInt32 f = (UInt32)1 << i;
1466     if ((flags & f) != 0)
1467     {
1468       const char *name = table[i];
1469       if (name)
1470       {
1471         if (filled)
1472           s += FLAGS_DELIMITER;
1473         filled = true;
1474         s += name;
1475         flags &= ~f;
1476       }
1477     }
1478   }
1479   if (flags != 0)
1480   {
1481     if (filled)
1482       s += FLAGS_DELIMITER;
1483     Add_Hex(s, flags);
1484   }
1485 }
1486 
DoesNeedQuotes(const char * s)1487 static bool DoesNeedQuotes(const char *s)
1488 {
1489   {
1490     char c = s[0];
1491     if (c == 0 || c == '#' || c == ';' || (c == '/' && s[1] == '*'))
1492       return true;
1493   }
1494   for (;;)
1495   {
1496     char c = *s++;
1497     if (c == 0)
1498       return false;
1499     if (c == ' ')
1500       return true;
1501   }
1502 }
1503 
Add_QuStr(const AString & s)1504 void CInArchive::Add_QuStr(const AString &s)
1505 {
1506   bool needQuotes = DoesNeedQuotes(s);
1507   if (needQuotes)
1508     Script += '\"';
1509   Script += s;
1510   if (needQuotes)
1511     Script += '\"';
1512 }
1513 
SpaceQuStr(const AString & s)1514 void CInArchive::SpaceQuStr(const AString &s)
1515 {
1516   Space();
1517   Add_QuStr(s);
1518 }
1519 
AddParam(UInt32 pos)1520 void CInArchive::AddParam(UInt32 pos)
1521 {
1522   _tempString.Empty();
1523   ReadString2(_tempString, pos);
1524   SpaceQuStr(_tempString);
1525 }
1526 
AddParams(const UInt32 * params,unsigned num)1527 void CInArchive::AddParams(const UInt32 *params, unsigned num)
1528 {
1529   for (unsigned i = 0; i < num; i++)
1530     AddParam(params[i]);
1531 }
1532 
AddOptionalParam(UInt32 pos)1533 void CInArchive::AddOptionalParam(UInt32 pos)
1534 {
1535   if (pos != 0)
1536     AddParam(pos);
1537 }
1538 
GetNumParams(const UInt32 * params,unsigned num)1539 static unsigned GetNumParams(const UInt32 *params, unsigned num)
1540 {
1541   for (; num > 0 && params[num - 1] == 0; num--);
1542   return num;
1543 }
1544 
AddOptionalParams(const UInt32 * params,unsigned num)1545 void CInArchive::AddOptionalParams(const UInt32 *params, unsigned num)
1546 {
1547   AddParams(params, GetNumParams(params, num));
1548 }
1549 
1550 
1551 static const UInt32 CMD_REF_Goto    = (1 << 0);
1552 static const UInt32 CMD_REF_Call    = (1 << 1);
1553 static const UInt32 CMD_REF_Pre     = (1 << 2);
1554 static const UInt32 CMD_REF_Show    = (1 << 3);
1555 static const UInt32 CMD_REF_Leave   = (1 << 4);
1556 static const UInt32 CMD_REF_OnFunc  = (1 << 5);
1557 static const UInt32 CMD_REF_Section = (1 << 6);
1558 static const UInt32 CMD_REF_InitPluginDir = (1 << 7);
1559 // static const UInt32 CMD_REF_Creator = (1 << 5); // CMD_REF_Pre is used instead
1560 static const unsigned CMD_REF_OnFunc_NumShifts = 28; // it uses for onFunc too
1561 static const unsigned CMD_REF_Page_NumShifts = 16; // it uses for onFunc too
1562 static const UInt32 CMD_REF_Page_Mask   = 0x0FFF0000;
1563 static const UInt32 CMD_REF_OnFunc_Mask = 0xF0000000;
1564 
IsPageFunc(UInt32 flag)1565 inline bool IsPageFunc(UInt32 flag)
1566 {
1567   return (flag & (CMD_REF_Pre | CMD_REF_Show | CMD_REF_Leave)) != 0;
1568 }
1569 
IsFunc(UInt32 flag)1570 inline bool IsFunc(UInt32 flag)
1571 {
1572   // return (flag & (CMD_REF_Pre | CMD_REF_Show | CMD_REF_Leave | CMD_REF_OnFunc)) != 0;
1573   return (flag & (CMD_REF_Call | CMD_REF_Pre | CMD_REF_Show | CMD_REF_Leave | CMD_REF_OnFunc)) != 0;
1574 }
1575 
IsProbablyEndOfFunc(UInt32 flag)1576 inline bool IsProbablyEndOfFunc(UInt32 flag)
1577 {
1578   return (flag != 0 && flag != CMD_REF_Goto);
1579 }
1580 
1581 static const char * const kOnFunc[] =
1582 {
1583     "Init"
1584   , "InstSuccess"
1585   , "InstFailed"
1586   , "UserAbort"
1587   , "GUIInit"
1588   , "GUIEnd"
1589   , "MouseOverSection"
1590   , "VerifyInstDir"
1591   , "SelChange"
1592   , "RebootFailed"
1593 };
1594 
Add_FuncName(const UInt32 * labels,UInt32 index)1595 void CInArchive::Add_FuncName(const UInt32 *labels, UInt32 index)
1596 {
1597   UInt32 mask = labels[index];
1598   if (mask & CMD_REF_OnFunc)
1599   {
1600     Script += ".on";
1601     Script += kOnFunc[labels[index] >> CMD_REF_OnFunc_NumShifts];
1602   }
1603   else if (mask & CMD_REF_InitPluginDir)
1604   {
1605     /*
1606     if (!IsInstaller)
1607       Script += "un."
1608     */
1609     Script += "Initialize_____Plugins";
1610   }
1611   else
1612   {
1613     Script += "func_";
1614     Add_UInt(index);
1615   }
1616 }
1617 
AddParam_Func(const UInt32 * labels,UInt32 index)1618 void CInArchive::AddParam_Func(const UInt32 *labels, UInt32 index)
1619 {
1620   Space();
1621   if ((Int32)index >= 0)
1622     Add_FuncName(labels, index);
1623   else
1624     AddQuotes();
1625 }
1626 
1627 
Add_LabelName(UInt32 index)1628 void CInArchive::Add_LabelName(UInt32 index)
1629 {
1630   Script += "label_";
1631   Add_UInt(index);
1632 }
1633 
1634 // param != 0
Add_GotoVar(UInt32 param)1635 void CInArchive::Add_GotoVar(UInt32 param)
1636 {
1637   Space();
1638   if ((Int32)param < 0)
1639     Add_Var((UInt32)-((Int32)param + 1));
1640   else
1641     Add_LabelName(param - 1);
1642 }
1643 
Add_GotoVar1(UInt32 param)1644 void CInArchive::Add_GotoVar1(UInt32 param)
1645 {
1646   if (param == 0)
1647     Script += " 0";
1648   else
1649     Add_GotoVar(param);
1650 }
1651 
Add_GotoVars2(const UInt32 * params)1652 void CInArchive::Add_GotoVars2(const UInt32 *params)
1653 {
1654   Add_GotoVar1(params[0]);
1655   if (params[1] != 0)
1656     Add_GotoVar(params[1]);
1657 }
1658 
NoLabels(const UInt32 * labels,UInt32 num)1659 static bool NoLabels(const UInt32 *labels, UInt32 num)
1660 {
1661   for (UInt32 i = 0; i < num; i++)
1662     if (labels[i] != 0)
1663       return false;
1664   return true;
1665 }
1666 
1667 static const char * const k_REBOOTOK = " /REBOOTOK";
1668 
1669 #define Z7_NSIS_WIN_MB_ABORTRETRYIGNORE 2
1670 #define Z7_NSIS_WIN_MB_RETRYCANCEL      5
1671 
1672 static const char * const k_MB_Buttons[] =
1673 {
1674     "OK"
1675   , "OKCANCEL"
1676   , "ABORTRETRYIGNORE"
1677   , "YESNOCANCEL"
1678   , "YESNO"
1679   , "RETRYCANCEL"
1680   , "CANCELTRYCONTINUE"
1681 };
1682 
1683 #define Z7_NSIS_WIN_MB_ICONSTOP   (1 << 4)
1684 
1685 static const char * const k_MB_Icons[] =
1686 {
1687     NULL
1688   , "ICONSTOP"
1689   , "ICONQUESTION"
1690   , "ICONEXCLAMATION"
1691   , "ICONINFORMATION"
1692 };
1693 
1694 static const char * const k_MB_Flags[] =
1695 {
1696     "HELP"
1697   , "NOFOCUS"
1698   , "SETFOREGROUND"
1699   , "DEFAULT_DESKTOP_ONLY"
1700   , "TOPMOST"
1701   , "RIGHT"
1702   , "RTLREADING"
1703   // , "SERVICE_NOTIFICATION" // unsupported. That bit is used for NSIS purposes
1704 };
1705 
1706 #define Z7_NSIS_WIN_IDCANCEL 2
1707 #define Z7_NSIS_WIN_IDIGNORE 5
1708 
1709 static const char * const k_Button_IDs[] =
1710 {
1711     "0"
1712   , "IDOK"
1713   , "IDCANCEL"
1714   , "IDABORT"
1715   , "IDRETRY"
1716   , "IDIGNORE"
1717   , "IDYES"
1718   , "IDNO"
1719   , "IDCLOSE"
1720   , "IDHELP"
1721   , "IDTRYAGAIN"
1722   , "IDCONTINUE"
1723 };
1724 
Add_ButtonID(UInt32 buttonID)1725 void CInArchive::Add_ButtonID(UInt32 buttonID)
1726 {
1727   Space();
1728   if (buttonID < Z7_ARRAY_SIZE(k_Button_IDs))
1729     Script += k_Button_IDs[buttonID];
1730   else
1731   {
1732     Script += "Button_";
1733     Add_UInt(buttonID);
1734   }
1735 }
1736 
IsDirectString_Equal(UInt32 offset,const char * s) const1737 bool CInArchive::IsDirectString_Equal(UInt32 offset, const char *s) const
1738 {
1739   if (offset >= NumStringChars)
1740     return false;
1741   if (IsUnicode)
1742     return AreStringsEqual_16and8(_data + _stringsPos + offset * 2, s);
1743   else
1744     return strcmp((const char *)(const Byte *)_data + _stringsPos + offset, s) == 0;
1745 }
1746 
StringToUInt32(const char * s,UInt32 & res)1747 static bool StringToUInt32(const char *s, UInt32 &res)
1748 {
1749   const char *end;
1750   if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
1751     res = ConvertHexStringToUInt32(s + 2, &end);
1752   else
1753     res = ConvertStringToUInt32(s, &end);
1754   return (*end == 0);
1755 }
1756 
1757 static const unsigned k_CtlColors32_Size = 24;
1758 static const unsigned k_CtlColors64_Size = 28;
1759 
1760 #define GET_CtlColors_SIZE(is64) ((is64) ? k_CtlColors64_Size : k_CtlColors32_Size)
1761 
1762 struct CNsis_CtlColors
1763 {
1764   UInt32 text; // COLORREF
1765   UInt32 bkc;  // COLORREF
1766   UInt32 lbStyle;
1767   UInt32 bkb; // HBRUSH
1768   Int32 bkmode;
1769   Int32 flags;
1770   UInt32 bkb_hi32;
1771 
1772   void Parse(const Byte *p, bool is64);
1773 };
1774 
Parse(const Byte * p,bool is64)1775 void CNsis_CtlColors::Parse(const Byte *p, bool is64)
1776 {
1777   text = Get32(p);
1778   bkc = Get32(p + 4);
1779   if (is64)
1780   {
1781     bkb = Get32(p + 8);
1782     bkb_hi32 = Get32(p + 12);
1783     lbStyle = Get32(p + 16);
1784     p += 4;
1785   }
1786   else
1787   {
1788     lbStyle = Get32(p + 8);
1789     bkb = Get32(p + 12);
1790   }
1791   bkmode = (Int32)Get32(p + 16);
1792   flags = (Int32)Get32(p + 20);
1793 }
1794 
1795 // Win32 constants
1796 #define Z7_NSIS_WIN_TRANSPARENT 1
1797 // #define Z7_NSIS_WIN_OPAQUE      2
1798 
1799 // text/bg colors
1800 #define kColorsFlags_TEXT     1
1801 #define kColorsFlags_TEXT_SYS 2
1802 #define kColorsFlags_BK       4
1803 #define kColorsFlags_BK_SYS   8
1804 #define kColorsFlags_BKB     16
1805 
Add_Color2(UInt32 v)1806 void CInArchive::Add_Color2(UInt32 v)
1807 {
1808   v = ((v & 0xFF) << 16) | (v & 0xFF00) | ((v >> 16) & 0xFF);
1809   char sz[32];
1810   for (int i = 5; i >= 0; i--)
1811   {
1812     unsigned t = v & 0xF;
1813     v >>= 4;
1814     sz[i] = (char)(((t < 10) ? ('0' + t) : ('A' + (t - 10))));
1815   }
1816   sz[6] = 0;
1817   Script += sz;
1818 }
1819 
Add_ColorParam(UInt32 v)1820 void CInArchive::Add_ColorParam(UInt32 v)
1821 {
1822   Space();
1823   Add_Color2(v);
1824 }
1825 
Add_Color(UInt32 v)1826 void CInArchive::Add_Color(UInt32 v)
1827 {
1828   Script += "0x";
1829   Add_Color2(v);
1830 }
1831 
1832 #define Z7_NSIS_WIN_SW_HIDE 0
1833 #define Z7_NSIS_WIN_SW_SHOWNORMAL 1
1834 
1835 #define Z7_NSIS_WIN_SW_SHOWMINIMIZED 2
1836 #define Z7_NSIS_WIN_SW_SHOWMINNOACTIVE 7
1837 #define Z7_NSIS_WIN_SW_SHOWNA 8
1838 
1839 static const char * const kShowWindow_Commands[] =
1840 {
1841     "HIDE"
1842   , "SHOWNORMAL"     // "NORMAL"
1843   , "SHOWMINIMIZED"
1844   , "SHOWMAXIMIZED"  // "MAXIMIZE"
1845   , "SHOWNOACTIVATE"
1846   , "SHOW"
1847   , "MINIMIZE"
1848   , "SHOWMINNOACTIVE"
1849   , "SHOWNA"
1850   , "RESTORE"
1851   , "SHOWDEFAULT"
1852   , "FORCEMINIMIZE"  // "MAX"
1853 };
1854 
Add_ShowWindow_Cmd_2(AString & s,UInt32 cmd)1855 static void Add_ShowWindow_Cmd_2(AString &s, UInt32 cmd)
1856 {
1857   if (cmd < Z7_ARRAY_SIZE(kShowWindow_Commands))
1858   {
1859     s += "SW_";
1860     s += kShowWindow_Commands[cmd];
1861   }
1862   else
1863     UIntToString(s, cmd);
1864 }
1865 
Add_ShowWindow_Cmd(UInt32 cmd)1866 void CInArchive::Add_ShowWindow_Cmd(UInt32 cmd)
1867 {
1868   if (cmd < Z7_ARRAY_SIZE(kShowWindow_Commands))
1869   {
1870     Script += "SW_";
1871     Script += kShowWindow_Commands[cmd];
1872   }
1873   else
1874     Add_UInt(cmd);
1875 }
1876 
Add_TypeFromList(const char * const * table,unsigned tableSize,UInt32 type)1877 void CInArchive::Add_TypeFromList(const char * const *table, unsigned tableSize, UInt32 type)
1878 {
1879   if (type < tableSize)
1880     Script += table[type];
1881   else
1882   {
1883     Script += '_';
1884     Add_UInt(type);
1885   }
1886 }
1887 
1888 #define ADD_TYPE_FROM_LIST(table, type) Add_TypeFromList(table, Z7_ARRAY_SIZE(table), type)
1889 
1890 enum
1891 {
1892   k_ExecFlags_AutoClose,
1893   k_ExecFlags_ShellVarContext,
1894   k_ExecFlags_Errors,
1895   k_ExecFlags_Abort,
1896   k_ExecFlags_RebootFlag,
1897   k_ExecFlags_reboot_called,
1898   k_ExecFlags_cur_insttype,
1899   k_ExecFlags_plugin_api_version,
1900   k_ExecFlags_Silent,
1901   k_ExecFlags_InstDirError,
1902   k_ExecFlags_rtl,
1903   k_ExecFlags_ErrorLevel,
1904   k_ExecFlags_RegView,
1905   k_ExecFlags_DetailsPrint = 13
1906 };
1907 
1908 // Names for NSIS exec_flags_t structure vars
1909 static const char * const kExecFlags_VarsNames[] =
1910 {
1911     "AutoClose" // autoclose;
1912   , "ShellVarContext" // all_user_var;
1913   , "Errors" // exec_error;
1914   , "Abort" // abort;
1915   , "RebootFlag" // exec_reboot; // NSIS_SUPPORT_REBOOT
1916   , "reboot_called" // reboot_called; // NSIS_SUPPORT_REBOOT
1917   , "cur_insttype" // XXX_cur_insttype; // depreacted
1918   , "plugin_api_version" // plugin_api_version; // see NSISPIAPIVER_CURR
1919                           // used to be XXX_insttype_changed
1920   , "Silent" // silent; // NSIS_CONFIG_SILENT_SUPPORT
1921   , "InstDirError" // instdir_error;
1922   , "rtl" // rtl;
1923   , "ErrorLevel" // errlvl;
1924   , "RegView" // alter_reg_view;
1925   , "DetailsPrint" // status_update;
1926 };
1927 
Add_ExecFlags(UInt32 flagsType)1928 void CInArchive::Add_ExecFlags(UInt32 flagsType)
1929 {
1930   ADD_TYPE_FROM_LIST(kExecFlags_VarsNames, flagsType);
1931 }
1932 
1933 
1934 // ---------- Page ----------
1935 
1936 // page flags
1937 #define PF_CANCEL_ENABLE 4
1938 #define PF_LICENSE_FORCE_SELECTION 32
1939 #define PF_LICENSE_NO_FORCE_SELECTION 64
1940 #define PF_PAGE_EX 512
1941 #define PF_DIR_NO_BTN_DISABLE 1024
1942 /*
1943 #define PF_LICENSE_SELECTED 1
1944 #define PF_NEXT_ENABLE 2
1945 #define PF_BACK_SHOW 8
1946 #define PF_LICENSE_STREAM 16
1947 #define PF_NO_NEXT_FOCUS 128
1948 #define PF_BACK_ENABLE 256
1949 */
1950 
1951 // page window proc
1952 enum
1953 {
1954   PWP_LICENSE,
1955   PWP_SELCOM,
1956   PWP_DIR,
1957   PWP_INSTFILES,
1958   PWP_UNINST,
1959   PWP_COMPLETED,
1960   PWP_CUSTOM
1961 };
1962 
1963 static const char * const kPageTypes[] =
1964 {
1965     "license"
1966   , "components"
1967   , "directory"
1968   , "instfiles"
1969   , "uninstConfirm"
1970   , "COMPLETED"
1971   , "custom"
1972 };
1973 
1974 #define SET_FUNC_REF(x, flag) if ((Int32)(x) >= 0 && (x) < bh.Num) \
1975   { labels[x] = (labels[x] & ~CMD_REF_Page_Mask) | ((flag) | (pageIndex << CMD_REF_Page_NumShifts)); }
1976 
1977 // #define IDD_LICENSE  102
1978 #define IDD_LICENSE_FSRB 108
1979 #define IDD_LICENSE_FSCB 109
1980 
AddPageOption1(UInt32 param,const char * name)1981 void CInArchive::AddPageOption1(UInt32 param, const char *name)
1982 {
1983   if (param == 0)
1984     return;
1985   TabString(name);
1986   AddParam(param);
1987   NewLine();
1988 }
1989 
AddPageOption(const UInt32 * params,unsigned num,const char * name)1990 void CInArchive::AddPageOption(const UInt32 *params, unsigned num, const char *name)
1991 {
1992   num = GetNumParams(params, num);
1993   if (num == 0)
1994     return;
1995   TabString(name);
1996   AddParams(params, num);
1997   NewLine();
1998 }
1999 
Separator()2000 void CInArchive::Separator()
2001 {
2002   AddLF();
2003   AddCommentAndString("--------------------");
2004   AddLF();
2005 }
2006 
Space()2007 void CInArchive::Space()
2008 {
2009   Script += ' ';
2010 }
2011 
Tab()2012 void CInArchive::Tab()
2013 {
2014   Script += "  ";
2015 }
2016 
Tab(bool commented)2017 void CInArchive::Tab(bool commented)
2018 {
2019   Script += commented ? "    ; " : "  ";
2020 }
2021 
BigSpaceComment()2022 void CInArchive::BigSpaceComment()
2023 {
2024   Script += "    ; ";
2025 }
2026 
SmallSpaceComment()2027 void CInArchive::SmallSpaceComment()
2028 {
2029   Script += " ; ";
2030 }
2031 
AddCommentAndString(const char * s)2032 void CInArchive::AddCommentAndString(const char *s)
2033 {
2034   Script += "; ";
2035   Script += s;
2036 }
2037 
AddError(const char * s)2038 void CInArchive::AddError(const char *s)
2039 {
2040   BigSpaceComment();
2041   Script += "!!! ERROR: ";
2042   Script += s;
2043 }
2044 
AddErrorLF(const char * s)2045 void CInArchive::AddErrorLF(const char *s)
2046 {
2047   AddError(s);
2048   AddLF();
2049 }
2050 
CommentOpen()2051 void CInArchive::CommentOpen()
2052 {
2053   AddStringLF("/*");
2054 }
2055 
CommentClose()2056 void CInArchive::CommentClose()
2057 {
2058   AddStringLF("*/");
2059 }
2060 
AddLF()2061 void CInArchive::AddLF()
2062 {
2063   Script += CR_LF;
2064 }
2065 
AddQuotes()2066 void CInArchive::AddQuotes()
2067 {
2068   Script += "\"\"";
2069 }
2070 
TabString(const char * s)2071 void CInArchive::TabString(const char *s)
2072 {
2073   Tab();
2074   Script += s;
2075 }
2076 
AddStringLF(const char * s)2077 void CInArchive::AddStringLF(const char *s)
2078 {
2079   Script += s;
2080   AddLF();
2081 }
2082 
2083 // ---------- Section ----------
2084 
2085 static const char * const kSection_VarsNames[] =
2086 {
2087     "Text"
2088   , "InstTypes"
2089   , "Flags"
2090   , "Code"
2091   , "CodeSize"
2092   , "Size" // size in KB
2093 };
2094 
Add_SectOp(UInt32 opType)2095 void CInArchive::Add_SectOp(UInt32 opType)
2096 {
2097   ADD_TYPE_FROM_LIST(kSection_VarsNames, opType);
2098 }
2099 
Parse(const Byte * p)2100 void CSection::Parse(const Byte *p)
2101 {
2102   Name = Get32(p);
2103   InstallTypes = Get32(p + 4);
2104   Flags = Get32(p + 8);
2105   StartCmdIndex = Get32(p + 12);
2106   NumCommands = Get32(p + 16);
2107   SizeKB = Get32(p + 20);
2108 }
2109 
2110 // used for section->flags
2111 #define SF_SELECTED   (1 << 0)
2112 #define SF_SECGRP     (1 << 1)
2113 #define SF_SECGRPEND  (1 << 2)
2114 #define SF_BOLD       (1 << 3)
2115 #define SF_RO         (1 << 4)
2116 #define SF_EXPAND     (1 << 5)
2117 /*
2118 #define SF_PSELECTED  (1 << 6)
2119 #define SF_TOGGLED    (1 << 7)
2120 #define SF_NAMECHG    (1 << 8)
2121 */
2122 
PrintSectionBegin(const CSection & sect,unsigned index)2123 bool CInArchive::PrintSectionBegin(const CSection &sect, unsigned index)
2124 {
2125   AString name;
2126   if (sect.Flags & SF_BOLD)
2127     name += '!';
2128   AString s2;
2129   ReadString2(s2, sect.Name);
2130   if (!IsInstaller)
2131   {
2132     if (!StringsAreEqualNoCase_Ascii(s2, "uninstall"))
2133       name += "un.";
2134   }
2135   name += s2;
2136 
2137   if (sect.Flags & SF_SECGRPEND)
2138   {
2139     AddStringLF("SectionGroupEnd");
2140     return true;
2141   }
2142 
2143   if (sect.Flags & SF_SECGRP)
2144   {
2145     Script += "SectionGroup";
2146     if (sect.Flags & SF_EXPAND)
2147       Script += " /e";
2148     SpaceQuStr(name);
2149     Script += "    ; Section";
2150     AddParam_UInt(index);
2151     NewLine();
2152     return true;
2153   }
2154 
2155   Script += "Section";
2156   if ((sect.Flags & SF_SELECTED) == 0)
2157     Script += " /o";
2158   if (!name.IsEmpty())
2159     SpaceQuStr(name);
2160 
2161   /*
2162   if (!name.IsEmpty())
2163     Script += ' ';
2164   else
2165   */
2166   SmallSpaceComment();
2167   Script += "Section_";
2168   Add_UInt(index);
2169 
2170   /*
2171   Script += " ; flags = ";
2172   Add_Hex(Script, sect.Flags);
2173   */
2174 
2175   NewLine();
2176 
2177   if (sect.SizeKB != 0)
2178   {
2179     // probably we must show AddSize, only if there is additional size.
2180     Tab();
2181     AddCommentAndString("AddSize");
2182     AddParam_UInt(sect.SizeKB);
2183     AddLF();
2184   }
2185 
2186   bool needSectionIn =
2187       (sect.Name != 0 && sect.InstallTypes != 0) ||
2188       (sect.Name == 0 && sect.InstallTypes != 0xFFFFFFFF);
2189   if (needSectionIn || (sect.Flags & SF_RO) != 0)
2190   {
2191     TabString("SectionIn");
2192     UInt32 instTypes = sect.InstallTypes;
2193     for (unsigned i = 0; i < 32; i++, instTypes >>= 1)
2194       if ((instTypes & 1) != 0)
2195       {
2196         AddParam_UInt(i + 1);
2197       }
2198     if ((sect.Flags & SF_RO) != 0)
2199       Script += " RO";
2200     AddLF();
2201   }
2202   return false;
2203 }
2204 
PrintSectionEnd()2205 void CInArchive::PrintSectionEnd()
2206 {
2207   AddStringLF("SectionEnd");
2208   AddLF();
2209 }
2210 
2211 // static const unsigned kOnFuncShift = 4;
2212 
ClearLangComment()2213 void CInArchive::ClearLangComment()
2214 {
2215   langStrIDs.Clear();
2216 }
2217 
PrintNumComment(const char * name,UInt32 value)2218 void CInArchive::PrintNumComment(const char *name, UInt32 value)
2219 {
2220   // size_t len = Script.Len();
2221   AddCommentAndString(name);
2222   Script += ": ";
2223   Add_UInt(value);
2224   AddLF();
2225   /*
2226   len = Script.Len() - len;
2227   char sz[16];
2228   ConvertUInt32ToString(value, sz);
2229   len += MyStringLen(sz);
2230   for (; len < 20; len++)
2231     Space();
2232   AddStringLF(sz);
2233   */
2234 }
2235 
2236 
NewLine()2237 void CInArchive::NewLine()
2238 {
2239   if (!langStrIDs.IsEmpty())
2240   {
2241     BigSpaceComment();
2242     for (unsigned i = 0; i < langStrIDs.Size() && i < 20; i++)
2243     {
2244       /*
2245       if (i != 0)
2246         Script += ' ';
2247       */
2248       UInt32 langStr = langStrIDs[i];
2249       if (langStr >= _numLangStrings)
2250       {
2251         AddError("langStr");
2252         break;
2253       }
2254       UInt32 param = Get32(_mainLang + langStr * 4);
2255       if (param != 0)
2256         AddParam(param);
2257     }
2258     ClearLangComment();
2259   }
2260   AddLF();
2261 }
2262 
2263 static const UInt32 kPageSize = 16 * 4;
2264 
2265 static const char * const k_SetOverwrite_Modes[] =
2266 {
2267     "on"
2268   , "off"
2269   , "try"
2270   , "ifnewer"
2271   , "ifdiff"
2272   // "lastused"
2273 };
2274 
2275 
MessageBox_MB_Part(UInt32 param)2276 void CInArchive::MessageBox_MB_Part(UInt32 param)
2277 {
2278   {
2279     UInt32 v = param & 0xF;
2280     Script += " MB_";
2281     if (v < Z7_ARRAY_SIZE(k_MB_Buttons))
2282       Script += k_MB_Buttons[v];
2283     else
2284     {
2285       Script += "Buttons_";
2286       Add_UInt(v);
2287     }
2288   }
2289   {
2290     UInt32 icon = (param >> 4) & 0x7;
2291     if (icon != 0)
2292     {
2293       Script += "|MB_";
2294       if (icon < Z7_ARRAY_SIZE(k_MB_Icons) && k_MB_Icons[icon])
2295         Script += k_MB_Icons[icon];
2296       else
2297       {
2298         Script += "Icon_";
2299         Add_UInt(icon);
2300       }
2301     }
2302   }
2303   if ((param & 0x80) != 0)
2304     Script += "|MB_USERICON";
2305   {
2306     UInt32 defButton = (param >> 8) & 0xF;
2307     if (defButton != 0)
2308     {
2309       Script += "|MB_DEFBUTTON";
2310       Add_UInt(defButton + 1);
2311     }
2312   }
2313   {
2314     UInt32 modal = (param >> 12) & 0x3;
2315     if (modal == 1) Script += "|MB_SYSTEMMODAL";
2316     else if (modal == 2) Script += "|MB_TASKMODAL";
2317     else if (modal == 3) Script += "|0x3000";
2318     UInt32 flags = (param >> 14);
2319     for (unsigned i = 0; i < Z7_ARRAY_SIZE(k_MB_Flags); i++)
2320       if ((flags & (1 << i)) != 0)
2321       {
2322         Script += "|MB_";
2323         Script += k_MB_Flags[i];
2324       }
2325   }
2326 }
2327 
2328 #define GET_CMD_PARAM(ppp, index) Get32((ppp) + 4 + (index) * 4)
2329 
2330 static const Byte k_InitPluginDir_Commands[] =
2331   { 13, 26, 31, 13, 19, 21, 11, 14, 25, 31, 1, 22, 4, 1 };
2332 
CompareCommands(const Byte * rawCmds,const Byte * sequence,size_t numCommands)2333 bool CInArchive::CompareCommands(const Byte *rawCmds, const Byte *sequence, size_t numCommands)
2334 {
2335   for (UInt32 kkk = 0; kkk < numCommands; kkk++, rawCmds += kCmdSize)
2336     if (GetCmd(Get32(rawCmds)) != sequence[kkk])
2337       return false;
2338   return true;
2339 }
2340 
2341 
2342 static const UInt32 kSectionSize_base = 6 * 4;
2343 // static const UInt32 kSectionSize_8bit = kSectionSize_base + 1024;
2344 // static const UInt32 kSectionSize_16bit = kSectionSize_base + 1024 * 2;
2345 // static const UInt32 kSectionSize_16bit_Big = kSectionSize_base + 8196 * 2;
2346 // 8196 is default string length in NSIS-Unicode since 2.37.3
2347 
2348 #endif
2349 
2350 
AddString(AString & dest,const char * src)2351 static void AddString(AString &dest, const char *src)
2352 {
2353   dest.Add_Space_if_NotEmpty();
2354   dest += src;
2355 }
2356 
GetFormatDescription() const2357 AString CInArchive::GetFormatDescription() const
2358 {
2359   AString s ("NSIS-");
2360   char c;
2361   if (IsPark())
2362   {
2363     s += "Park-";
2364     c = '1';
2365          if (NsisType == k_NsisType_Park2) c = '2';
2366     else if (NsisType == k_NsisType_Park3) c = '3';
2367   }
2368   else
2369   {
2370     c = '2';
2371     if (NsisType == k_NsisType_Nsis3)
2372       c = '3';
2373   }
2374   s += c;
2375   if (IsNsis200)
2376     s += ".00";
2377   else if (IsNsis225)
2378     s += ".25";
2379 
2380   if (IsUnicode)
2381     AddString(s, "Unicode");
2382 
2383   if (Is64Bit)
2384     AddString(s, "64-bit");
2385 
2386   if (LogCmdIsEnabled)
2387     AddString(s, "log");
2388 
2389   if (BadCmd >= 0)
2390   {
2391     AddString(s, "BadCmd=");
2392     UIntToString(s, (UInt32)BadCmd);
2393   }
2394   return s;
2395 }
2396 
2397 #ifdef NSIS_SCRIPT
2398 
2399 static const unsigned kNumAdditionalParkCmds = 3;
2400 
GetNumSupportedCommands() const2401 unsigned CInArchive::GetNumSupportedCommands() const
2402 {
2403   unsigned numCmds = IsPark() ? (unsigned)kNumCmds : (unsigned)(kNumCmds) - kNumAdditionalParkCmds;
2404   if (!LogCmdIsEnabled)
2405     numCmds--;
2406   if (!IsUnicode)
2407     numCmds -= 2;
2408   return numCmds;
2409 }
2410 
2411 #endif
2412 
GetCmd(UInt32 a)2413 UInt32 CInArchive::GetCmd(UInt32 a)
2414 {
2415   if (!IsPark())
2416   {
2417     if (!LogCmdIsEnabled)
2418       return a;
2419     if (a < EW_SECTIONSET)
2420       return a;
2421     if (a == EW_SECTIONSET)
2422       return EW_LOG;
2423     return a - 1;
2424   }
2425 
2426   if (a < EW_REGISTERDLL)
2427     return a;
2428   if (NsisType >= k_NsisType_Park2)
2429   {
2430     if (a == EW_REGISTERDLL) return EW_GETFONTVERSION;
2431     a--;
2432   }
2433   if (NsisType >= k_NsisType_Park3)
2434   {
2435     if (a == EW_REGISTERDLL) return EW_GETFONTNAME;
2436     a--;
2437   }
2438   if (a >= EW_FSEEK)
2439   {
2440     if (IsUnicode)
2441     {
2442       if (a == EW_FSEEK) return EW_FPUTWS;
2443       if (a == EW_FSEEK + 1) return EW_FPUTWS + 1;
2444       a -= 2;
2445     }
2446 
2447     if (a >= EW_SECTIONSET && LogCmdIsEnabled)
2448     {
2449       if (a == EW_SECTIONSET)
2450         return EW_LOG;
2451       return a - 1;
2452     }
2453     if (a == EW_FPUTWS)
2454       return EW_FINDPROC;
2455     // if (a > EW_FPUTWS) return 0;
2456   }
2457   return a;
2458 }
2459 
FindBadCmd(const CBlockHeader & bh,const Byte * p)2460 void CInArchive::FindBadCmd(const CBlockHeader &bh, const Byte *p)
2461 {
2462   BadCmd = -1;
2463 
2464   for (UInt32 kkk = 0; kkk < bh.Num; kkk++, p += kCmdSize)
2465   {
2466     const UInt32 id = GetCmd(Get32(p));
2467     if (id >= kNumCmds)
2468       continue;
2469     if (BadCmd >= 0 && id >= (unsigned)BadCmd)
2470       continue;
2471     unsigned i;
2472     if (IsNsis3_OrHigher())
2473     {
2474       if (id == EW_RESERVEDOPCODE)
2475       {
2476         BadCmd = (int)id;
2477         continue;
2478       }
2479     }
2480     else
2481     {
2482       // if (id == EW_GETLABELADDR || id == EW_GETFUNCTIONADDR)
2483       if (id == EW_RESERVEDOPCODE || id == EW_GETOSINFO)
2484       {
2485         BadCmd = (int)id;
2486         continue;
2487       }
2488     }
2489     for (i = 6; i != 0; i--)
2490     {
2491       const UInt32 param = Get32(p + i * 4);
2492       if (param != 0)
2493         break;
2494     }
2495     if (id == EW_FINDPROC && i == 0)
2496     {
2497       BadCmd = (int)id;
2498       continue;
2499     }
2500     if (k_Commands[id].NumParams < i)
2501       BadCmd = (int)id;
2502   }
2503 }
2504 
2505 /* We calculate the number of parameters in commands to detect
2506    layout of commands. It's not very good way.
2507    If you know simpler and more robust way to detect Version and layout,
2508    please write to 7-Zip forum */
2509 
DetectNsisType(const CBlockHeader & bh,const Byte * p)2510 void CInArchive::DetectNsisType(const CBlockHeader &bh, const Byte *p)
2511 {
2512   bool strongPark = false;
2513   bool strongNsis = false;
2514 
2515   if (NumStringChars > 2)
2516   {
2517     const Byte *strData = _data + _stringsPos;
2518     if (IsUnicode)
2519     {
2520       UInt32 num = NumStringChars - 2;
2521       for (UInt32 i = 0; i < num; i++)
2522       {
2523         if (Get16(strData + i * 2) == 0)
2524         {
2525           unsigned c2 = Get16(strData + 2 + i * 2);
2526           // it can be TXT/RTF with marker char (1 or 2). so we must check next char
2527           // if (c2 <= NS_3_CODE_SKIP && c2 != NS_3_CODE_SHELL)
2528           if (c2 == NS_3_CODE_VAR)
2529           {
2530             // 18.06: fixed: is it correct ?
2531             // if ((Get16(strData + 3 + i * 2) & 0x8000) != 0)
2532             if ((Get16(strData + 4 + i * 2) & 0x8080) == 0x8080)
2533             {
2534               NsisType = k_NsisType_Nsis3;
2535               strongNsis = true;
2536               break;
2537             }
2538           }
2539         }
2540       }
2541       if (!strongNsis)
2542       {
2543         NsisType = k_NsisType_Park1;
2544         strongPark = true;
2545       }
2546     }
2547     else
2548     {
2549       UInt32 num = NumStringChars - 2;
2550       for (UInt32 i = 0; i < num; i++)
2551       {
2552         if (strData[i] == 0)
2553         {
2554           Byte c2 = strData[i + 1];
2555           // it can be TXT/RTF with marker char (1 or 2). so we must check next char
2556           // for marker=1 (txt)
2557           if (c2 == NS_3_CODE_VAR)
2558             // if (c2 <= NS_3_CODE_SKIP && c2 != NS_3_CODE_SHELL && c2 != 1)
2559           {
2560             if ((strData[i + 2] & 0x80) != 0)
2561             {
2562               // const char *p2 = (const char *)(strData + i + 1);
2563               // p2 = p2;
2564               NsisType = k_NsisType_Nsis3;
2565               strongNsis = true;
2566               break;
2567             }
2568           }
2569         }
2570       }
2571     }
2572   }
2573 
2574   if (NsisType == k_NsisType_Nsis2 && !IsUnicode)
2575   {
2576     const Byte *p2 = p;
2577 
2578     for (UInt32 kkk = 0; kkk < bh.Num; kkk++, p2 += kCmdSize)
2579     {
2580       UInt32 cmd = GetCmd(Get32(p2));
2581       if (cmd != EW_GETDLGITEM &&
2582           cmd != EW_ASSIGNVAR)
2583         continue;
2584 
2585       UInt32 params[kNumCommandParams];
2586       for (unsigned i = 0; i < kNumCommandParams; i++)
2587         params[i] = Get32(p2 + 4 + 4 * i);
2588 
2589       if (cmd == EW_GETDLGITEM)
2590       {
2591         // we can use also EW_SETCTLCOLORS
2592         if (IsVarStr(params[1], kVar_HWNDPARENT_225))
2593         {
2594           IsNsis225 = true;
2595           if (params[0] == kVar_Spec_OUTDIR_225)
2596           {
2597             IsNsis200 = true;
2598             break;
2599           }
2600         }
2601       }
2602       else // if (cmd == EW_ASSIGNVAR)
2603       {
2604         if (params[0] == kVar_Spec_OUTDIR_225 &&
2605             params[2] == 0 &&
2606             params[3] == 0 &&
2607             IsVarStr(params[1], kVar_OUTDIR))
2608           IsNsis225 = true;
2609       }
2610     }
2611   }
2612 
2613   bool parkVer_WasDetected = false;
2614 
2615   if (!strongNsis && !IsNsis225 && !IsNsis200)
2616   {
2617     // it must be before FindBadCmd(bh, p);
2618     unsigned mask = 0;
2619 
2620     unsigned numInsertMax = IsUnicode ? 4 : 2;
2621 
2622     const Byte *p2 = p;
2623 
2624     for (UInt32 kkk = 0; kkk < bh.Num; kkk++, p2 += kCmdSize)
2625     {
2626       const UInt32 cmd = Get32(p2); // we use original (not converted) command
2627 
2628       if (cmd < EW_WRITEUNINSTALLER ||
2629           cmd > EW_WRITEUNINSTALLER + numInsertMax)
2630         continue;
2631 
2632       UInt32 params[kNumCommandParams];
2633       for (unsigned i = 0; i < kNumCommandParams; i++)
2634         params[i] = Get32(p2 + 4 + 4 * i);
2635 
2636       if (params[4] != 0 ||
2637           params[5] != 0 ||
2638           params[0] <= 1 ||
2639           params[3] <= 1)
2640         continue;
2641 
2642       const UInt32 altParam = params[3];
2643       if (!IsGoodString(params[0]) ||
2644           !IsGoodString(altParam))
2645         continue;
2646 
2647       UInt32 additional = 0;
2648       if (GetVarIndexFinished(altParam, '\\', additional) != kVar_INSTDIR)
2649         continue;
2650       if (AreTwoParamStringsEqual(altParam + additional, params[0]))
2651       {
2652         const unsigned numInserts = cmd - EW_WRITEUNINSTALLER;
2653         mask |= ((unsigned)1 << numInserts);
2654       }
2655     }
2656 
2657     if (mask == 1)
2658     {
2659       parkVer_WasDetected = true; // it can be original NSIS or Park-1
2660     }
2661     else if (mask != 0)
2662     {
2663       ENsisType newType = NsisType;
2664       if (IsUnicode)
2665         switch (mask)
2666         {
2667           case (1 << 3): newType = k_NsisType_Park2; break;
2668           case (1 << 4): newType = k_NsisType_Park3; break;
2669         }
2670       else
2671         switch (mask)
2672         {
2673           case (1 << 1): newType = k_NsisType_Park2; break;
2674           case (1 << 2): newType = k_NsisType_Park3; break;
2675         }
2676       if (newType != NsisType)
2677       {
2678         parkVer_WasDetected = true;
2679         NsisType = newType;
2680       }
2681     }
2682   }
2683 
2684   FindBadCmd(bh, p);
2685 
2686   /*
2687   if (strongNsis)
2688     return;
2689   */
2690 
2691   if (BadCmd < EW_REGISTERDLL)
2692     return;
2693 
2694   /*
2695   // in ANSI archive we don't check Park and log version
2696   if (!IsUnicode)
2697     return;
2698   */
2699 
2700   // We can support Park-ANSI archives, if we remove if (strongPark) check
2701   if (strongPark && !parkVer_WasDetected)
2702   {
2703     if (BadCmd < EW_SECTIONSET)
2704     {
2705       NsisType = k_NsisType_Park3;
2706       LogCmdIsEnabled = true; // version 3 is provided with log enabled
2707       FindBadCmd(bh, p);
2708       if (BadCmd > 0 && BadCmd < EW_SECTIONSET)
2709       {
2710         NsisType = k_NsisType_Park2;
2711         LogCmdIsEnabled = false;
2712         FindBadCmd(bh, p);
2713         if (BadCmd > 0 && BadCmd < EW_SECTIONSET)
2714         {
2715           NsisType = k_NsisType_Park1;
2716           FindBadCmd(bh, p);
2717         }
2718       }
2719     }
2720   }
2721 
2722   if (BadCmd >= EW_SECTIONSET)
2723   {
2724     LogCmdIsEnabled = !LogCmdIsEnabled;
2725     FindBadCmd(bh, p);
2726     if (BadCmd >= EW_SECTIONSET && LogCmdIsEnabled)
2727     {
2728       LogCmdIsEnabled = false;
2729       FindBadCmd(bh, p);
2730     }
2731   }
2732 }
2733 
GetVarIndex(UInt32 strPos) const2734 Int32 CInArchive::GetVarIndex(UInt32 strPos) const
2735 {
2736   if (strPos >= NumStringChars)
2737     return -1;
2738 
2739   if (IsUnicode)
2740   {
2741     if (NumStringChars - strPos < 3 * 2)
2742       return -1;
2743     const Byte *p = _data + _stringsPos + strPos * 2;
2744     unsigned code = Get16(p);
2745     if (IsPark())
2746     {
2747       if (code != PARK_CODE_VAR)
2748         return -1;
2749       UInt32 n = Get16(p + 2);
2750       if (n == 0)
2751         return -1;
2752       CONVERT_NUMBER_PARK(n);
2753       return (Int32)n;
2754     }
2755 
2756     // NSIS-3
2757     {
2758       if (code != NS_3_CODE_VAR)
2759         return -1;
2760       UInt32 n = Get16(p + 2);
2761       if (n == 0)
2762         return -1;
2763       CONVERT_NUMBER_NS_3_UNICODE(n);
2764       return (Int32)n;
2765     }
2766   }
2767 
2768   if (NumStringChars - strPos < 4)
2769     return -1;
2770 
2771   const Byte *p = _data + _stringsPos + strPos;
2772   unsigned c = *p;
2773   if (NsisType == k_NsisType_Nsis3)
2774   {
2775     if (c != NS_3_CODE_VAR)
2776       return -1;
2777   }
2778   else if (c != NS_CODE_VAR)
2779     return -1;
2780 
2781   const unsigned c0 = p[1];
2782   if (c0 == 0)
2783     return -1;
2784   const unsigned c1 = p[2];
2785   if (c1 == 0)
2786     return -1;
2787   return (Int32)DECODE_NUMBER_FROM_2_CHARS(c0, c1);
2788 }
2789 
GetVarIndex(UInt32 strPos,UInt32 & resOffset) const2790 Int32 CInArchive::GetVarIndex(UInt32 strPos, UInt32 &resOffset) const
2791 {
2792   resOffset = 0;
2793   Int32 varIndex = GetVarIndex(strPos);
2794   if (varIndex < 0)
2795     return varIndex;
2796   if (IsUnicode)
2797   {
2798     if (NumStringChars - strPos < 2 * 2)
2799       return -1;
2800     resOffset = 2;
2801   }
2802   else
2803   {
2804     if (NumStringChars - strPos < 3)
2805       return -1;
2806     resOffset = 3;
2807   }
2808   return varIndex;
2809 }
2810 
GetVarIndexFinished(UInt32 strPos,Byte endChar,UInt32 & resOffset) const2811 Int32 CInArchive::GetVarIndexFinished(UInt32 strPos, Byte endChar, UInt32 &resOffset) const
2812 {
2813   resOffset = 0;
2814   Int32 varIndex = GetVarIndex(strPos);
2815   if (varIndex < 0)
2816     return varIndex;
2817   if (IsUnicode)
2818   {
2819     if (NumStringChars - strPos < 3 * 2)
2820       return -1;
2821     const Byte *p = _data + _stringsPos + strPos * 2;
2822     if (Get16(p + 4) != endChar)
2823       return -1;
2824     resOffset = 3;
2825   }
2826   else
2827   {
2828     if (NumStringChars - strPos < 4)
2829       return -1;
2830     const Byte *p = _data + _stringsPos + strPos;
2831     if (p[3] != endChar)
2832       return -1;
2833     resOffset = 4;
2834   }
2835   return varIndex;
2836 }
2837 
IsVarStr(UInt32 strPos,UInt32 varIndex) const2838 bool CInArchive::IsVarStr(UInt32 strPos, UInt32 varIndex) const
2839 {
2840   if (varIndex > (UInt32)0x7FFF)
2841     return false;
2842   UInt32 resOffset;
2843   return GetVarIndexFinished(strPos, 0, resOffset) == (Int32)varIndex;
2844 }
2845 
IsAbsolutePathVar(UInt32 strPos) const2846 bool CInArchive::IsAbsolutePathVar(UInt32 strPos) const
2847 {
2848   Int32 varIndex = GetVarIndex(strPos);
2849   if (varIndex < 0)
2850     return false;
2851   switch (varIndex)
2852   {
2853     case kVar_INSTDIR:
2854     case kVar_EXEDIR:
2855     case kVar_TEMP:
2856     case kVar_PLUGINSDIR:
2857       return true;
2858   }
2859   return false;
2860 }
2861 
2862 #define IS_LETTER_CHAR(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
2863 
2864 // We use same check as in NSIS decoder
IsDrivePath(const wchar_t * s)2865 static bool IsDrivePath(const wchar_t *s) { return IS_LETTER_CHAR(s[0]) && s[1] == ':' /* && s[2] == '\\' */ ; }
IsDrivePath(const char * s)2866 static bool IsDrivePath(const char *s)    { return IS_LETTER_CHAR(s[0]) && s[1] == ':' /* && s[2] == '\\' */ ; }
2867 
IsAbsolutePath(const wchar_t * s)2868 static bool IsAbsolutePath(const wchar_t *s)
2869 {
2870   return (s[0] == WCHAR_PATH_SEPARATOR && s[1] == WCHAR_PATH_SEPARATOR) || IsDrivePath(s);
2871 }
2872 
IsAbsolutePath(const char * s)2873 static bool IsAbsolutePath(const char *s)
2874 {
2875   return (s[0] == CHAR_PATH_SEPARATOR && s[1] == CHAR_PATH_SEPARATOR) || IsDrivePath(s);
2876 }
2877 
SetItemName(CItem & item,UInt32 strPos)2878 void CInArchive::SetItemName(CItem &item, UInt32 strPos)
2879 {
2880   ReadString2_Raw(strPos);
2881   const bool isAbs = IsAbsolutePathVar(strPos);
2882   if (IsUnicode)
2883   {
2884     item.NameU = Raw_UString;
2885     if (!isAbs && !IsAbsolutePath(Raw_UString))
2886       item.Prefix = (int)UPrefixes.Size() - 1;
2887   }
2888   else
2889   {
2890     item.NameA = Raw_AString;
2891     if (!isAbs && !IsAbsolutePath(Raw_AString))
2892       item.Prefix = (int)APrefixes.Size() - 1;
2893   }
2894 }
2895 
ReadEntries(const CBlockHeader & bh)2896 HRESULT CInArchive::ReadEntries(const CBlockHeader &bh)
2897 {
2898   #ifdef NSIS_SCRIPT
2899   CDynLimBuf &s = Script;
2900 
2901   CObjArray<UInt32> labels;
2902   labels.Alloc(bh.Num);
2903   memset(labels, 0, bh.Num * sizeof(UInt32));
2904 
2905   {
2906     const Byte *p = _data;
2907     UInt32 i;
2908     for (i = 0; i < numOnFunc; i++)
2909     {
2910       UInt32 func = Get32(p + onFuncOffset + 4 * i);
2911       if (func < bh.Num)
2912         labels[func] = (labels[func] & ~CMD_REF_OnFunc_Mask) | (CMD_REF_OnFunc | (i << CMD_REF_OnFunc_NumShifts));
2913     }
2914   }
2915 
2916   /*
2917   {
2918     for (int i = 0; i < OnFuncs.Size(); i++)
2919     {
2920       UInt32 address = OnFuncs[i] >> kOnFuncShift;
2921       if (address < bh.Num)
2922     }
2923   }
2924   */
2925 
2926   if (bhPages.Num != 0)
2927   {
2928     Separator();
2929     PrintNumComment("PAGES", bhPages.Num);
2930 
2931     if (bhPages.Num > (1 << 12)
2932         || bhPages.Offset > _size
2933         || bhPages.Num * kPageSize > _size - bhPages.Offset)
2934     {
2935       AddErrorLF("Pages error");
2936     }
2937     else
2938     {
2939 
2940     AddLF();
2941     const Byte *p = _data + bhPages.Offset;
2942 
2943     for (UInt32 pageIndex = 0; pageIndex < bhPages.Num; pageIndex++, p += kPageSize)
2944     {
2945       UInt32 dlgID = Get32(p);
2946       UInt32 wndProcID = Get32(p + 4);
2947       UInt32 preFunc = Get32(p + 8);
2948       UInt32 showFunc = Get32(p + 12);
2949       UInt32 leaveFunc = Get32(p + 16);
2950       UInt32 flags = Get32(p + 20);
2951       UInt32 caption = Get32(p + 24);
2952       // UInt32 back = Get32(p + 28);
2953       UInt32 next = Get32(p + 32);
2954       // UInt32 clickNext = Get32(p + 36);
2955       // UInt32 cancel = Get32(p + 40);
2956       UInt32 params[5];
2957       for (int i = 0; i < 5; i++)
2958         params[i] = Get32(p + 44 + 4 * i);
2959 
2960       SET_FUNC_REF(preFunc, CMD_REF_Pre)
2961       SET_FUNC_REF(showFunc, CMD_REF_Show)
2962       SET_FUNC_REF(leaveFunc, CMD_REF_Leave)
2963 
2964       if (wndProcID == PWP_COMPLETED)
2965         CommentOpen();
2966 
2967       AddCommentAndString("Page ");
2968       Add_UInt(pageIndex);
2969       AddLF();
2970 
2971       if (flags & PF_PAGE_EX)
2972       {
2973         s += "PageEx ";
2974         if (!IsInstaller)
2975           s += "un.";
2976       }
2977       else
2978         s += IsInstaller ? "Page " : "UninstPage ";
2979 
2980       if (wndProcID < Z7_ARRAY_SIZE(kPageTypes))
2981         s += kPageTypes[wndProcID];
2982       else
2983         Add_UInt(wndProcID);
2984 
2985 
2986       bool needCallbacks = (
2987           (Int32)preFunc >= 0 ||
2988           (Int32)showFunc >= 0 ||
2989           (Int32)leaveFunc >= 0);
2990 
2991       if (flags & PF_PAGE_EX)
2992       {
2993         AddLF();
2994         if (needCallbacks)
2995           TabString("PageCallbacks");
2996       }
2997 
2998       if (needCallbacks)
2999       {
3000         AddParam_Func(labels, preFunc); // it's creator_function for PWP_CUSTOM
3001         if (wndProcID != PWP_CUSTOM)
3002         {
3003           AddParam_Func(labels, showFunc);
3004         }
3005         AddParam_Func(labels, leaveFunc);
3006       }
3007 
3008       if ((flags & PF_PAGE_EX) == 0)
3009       {
3010         // AddOptionalParam(caption);
3011         if (flags & PF_CANCEL_ENABLE)
3012           s += " /ENABLECANCEL";
3013         AddLF();
3014       }
3015       else
3016       {
3017         AddLF();
3018         AddPageOption1(caption, "Caption");
3019       }
3020 
3021         if (wndProcID == PWP_LICENSE)
3022         {
3023           if ((flags & PF_LICENSE_NO_FORCE_SELECTION) != 0 ||
3024               (flags & PF_LICENSE_FORCE_SELECTION) != 0)
3025           {
3026             TabString("LicenseForceSelection ");
3027             if (flags & PF_LICENSE_NO_FORCE_SELECTION)
3028               s += "off";
3029             else
3030             {
3031               if (dlgID == IDD_LICENSE_FSCB)
3032                 s += "checkbox";
3033               else if (dlgID == IDD_LICENSE_FSRB)
3034                 s += "radiobuttons";
3035               else
3036                 Add_UInt(dlgID);
3037               AddOptionalParams(params + 2, 2);
3038             }
3039             NewLine();
3040           }
3041 
3042           if (params[0] != 0 || next != 0)
3043           {
3044             TabString("LicenseText");
3045             AddParam(params[0]);
3046             AddOptionalParam(next);
3047             NewLine();
3048           }
3049           if (params[1] != 0)
3050           {
3051             TabString("LicenseData");
3052             if ((Int32)params[1] < 0)
3053               AddParam(params[1]);
3054             else
3055               AddLicense(params[1], -1);
3056             ClearLangComment();
3057             NewLine();
3058           }
3059         }
3060         else if (wndProcID == PWP_SELCOM)
3061           AddPageOption(params, 3, "ComponentsText");
3062         else if (wndProcID == PWP_DIR)
3063         {
3064           AddPageOption(params, 4, "DirText");
3065           if (params[4] != 0)
3066           {
3067             TabString("DirVar");
3068             AddParam_Var(params[4] - 1);
3069             AddLF();
3070           }
3071           if (flags & PF_DIR_NO_BTN_DISABLE)
3072           {
3073             TabString("DirVerify leave");
3074             AddLF();
3075           }
3076 
3077         }
3078         else if (wndProcID == PWP_INSTFILES)
3079         {
3080           AddPageOption1(params[2], "CompletedText");
3081           AddPageOption1(params[1], "DetailsButtonText");
3082         }
3083         else if (wndProcID == PWP_UNINST)
3084         {
3085           if (params[4] != 0)
3086           {
3087             TabString("DirVar");
3088             AddParam_Var(params[4] - 1);
3089             AddLF();
3090           }
3091           AddPageOption(params, 2, "UninstallText");
3092         }
3093 
3094       if (flags & PF_PAGE_EX)
3095       {
3096         s += "PageExEnd";
3097         NewLine();
3098       }
3099       if (wndProcID == PWP_COMPLETED)
3100         CommentClose();
3101       NewLine();
3102     }
3103     }
3104   }
3105 
3106   CObjArray<CSection> Sections;
3107 
3108   {
3109     Separator();
3110     PrintNumComment("SECTIONS", bhSections.Num);
3111     PrintNumComment("COMMANDS", bh.Num);
3112     AddLF();
3113 
3114     if (bhSections.Num > (1 << 15)
3115         // || bhSections.Offset > _size
3116         // || (bhSections.Num * SectionSize > _size - bhSections.Offset)
3117       )
3118     {
3119       AddErrorLF("Sections error");
3120     }
3121     else if (bhSections.Num != 0)
3122     {
3123       Sections.Alloc((unsigned)bhSections.Num);
3124       const Byte *p = _data + bhSections.Offset;
3125       for (UInt32 i = 0; i < bhSections.Num; i++, p += SectionSize)
3126       {
3127         CSection &section = Sections[i];
3128         section.Parse(p);
3129         if (section.StartCmdIndex < bh.Num)
3130           labels[section.StartCmdIndex] |= CMD_REF_Section;
3131       }
3132     }
3133   }
3134 
3135   #endif
3136 
3137   const Byte *p;
3138   UInt32 kkk;
3139 
3140   #ifdef NSIS_SCRIPT
3141 
3142   p = _data + bh.Offset;
3143 
3144   for (kkk = 0; kkk < bh.Num; kkk++, p += kCmdSize)
3145   {
3146     UInt32 commandId = GetCmd(Get32(p));
3147     UInt32 mask;
3148     switch (commandId)
3149     {
3150       case EW_NOP:          mask = 1 << 0; break;
3151       case EW_IFFILEEXISTS: mask = 3 << 1; break;
3152       case EW_IFFLAG:       mask = 3 << 0; break;
3153       case EW_MESSAGEBOX:   mask = 5 << 3; break;
3154       case EW_STRCMP:       mask = 3 << 2; break;
3155       case EW_INTCMP:       mask = 7 << 2; break;
3156       case EW_ISWINDOW:     mask = 3 << 1; break;
3157       case EW_CALL:
3158       {
3159         if (Get32(p + 4 + 4) == 1) // it's Call :Label
3160         {
3161           mask = 1 << 0;
3162           break;
3163         }
3164         UInt32 param0 = Get32(p + 4);
3165         if ((Int32)param0 > 0)
3166           labels[param0 - 1] |= CMD_REF_Call;
3167         continue;
3168       }
3169       default: continue;
3170     }
3171     for (unsigned i = 0; mask != 0; i++, mask >>= 1)
3172       if (mask & 1)
3173       {
3174         UInt32 param = Get32(p + 4 + 4 * i);
3175         if ((Int32)param > 0 && (Int32)param <= (Int32)bh.Num)
3176           labels[param - 1] |= CMD_REF_Goto;
3177       }
3178   }
3179 
3180   int InitPluginsDir_Start = -1;
3181   int InitPluginsDir_End = -1;
3182   p = _data + bh.Offset;
3183   for (kkk = 0; kkk < bh.Num; kkk++, p += kCmdSize)
3184   {
3185     UInt32 flg = labels[kkk];
3186     /*
3187     if (IsFunc(flg))
3188     {
3189       AddLF();
3190       for (int i = 0; i < 14; i++)
3191       {
3192         UInt32 commandId = GetCmd(Get32(p + kCmdSize * i));
3193         s += ", ";
3194         UIntToString(s, commandId);
3195       }
3196       AddLF();
3197     }
3198     */
3199     if (IsFunc(flg)
3200         && bh.Num - kkk >= Z7_ARRAY_SIZE(k_InitPluginDir_Commands)
3201         && CompareCommands(p, k_InitPluginDir_Commands, Z7_ARRAY_SIZE(k_InitPluginDir_Commands)))
3202     {
3203       InitPluginsDir_Start = (int)kkk;
3204       InitPluginsDir_End = (int)(kkk + Z7_ARRAY_SIZE(k_InitPluginDir_Commands));
3205       labels[kkk] |= CMD_REF_InitPluginDir;
3206       break;
3207     }
3208   }
3209 
3210   #endif
3211 
3212   // AString prefixA_Temp;
3213   // UString prefixU_Temp;
3214 
3215 
3216   // const UInt32 kFindS = 158;
3217 
3218   #ifdef NSIS_SCRIPT
3219 
3220   UInt32 curSectionIndex = 0;
3221   // UInt32 lastSectionEndCmd = 0xFFFFFFFF;
3222   bool sectionIsOpen = false;
3223   // int curOnFunc = 0;
3224   bool onFuncIsOpen = false;
3225 
3226   /*
3227   for (unsigned yyy = 0; yyy + 3 < _data.Size(); yyy++)
3228   {
3229     UInt32 val = Get32(_data + yyy);
3230     if (val == kFindS)
3231       val = val;
3232   }
3233   */
3234 
3235   UInt32 overwrite_State = 0; // "SetOverwrite on"
3236   Int32 allowSkipFiles_State = -1; // -1: on, -2: off, >=0 : RAW value
3237   UInt32 endCommentIndex = 0;
3238 
3239   unsigned numSupportedCommands = GetNumSupportedCommands();
3240 
3241   #endif
3242 
3243   p = _data + bh.Offset;
3244 
3245   UString spec_outdir_U;
3246   AString spec_outdir_A;
3247 
3248   UPrefixes.Add(UString("$INSTDIR"));
3249   APrefixes.Add(AString("$INSTDIR"));
3250 
3251   p = _data + bh.Offset;
3252 
3253   unsigned spec_outdir_VarIndex = IsNsis225 ?
3254       kVar_Spec_OUTDIR_225 :
3255       kVar_Spec_OUTDIR;
3256 
3257   for (kkk = 0; kkk < bh.Num; kkk++, p += kCmdSize)
3258   {
3259     UInt32 commandId;
3260     UInt32 params[kNumCommandParams];
3261     commandId = GetCmd(Get32(p));
3262     {
3263       for (unsigned i = 0; i < kNumCommandParams; i++)
3264       {
3265         params[i] = Get32(p + 4 + 4 * i);
3266         /*
3267         if (params[i] == kFindS)
3268           i = i;
3269         */
3270       }
3271     }
3272 
3273     #ifdef NSIS_SCRIPT
3274 
3275     bool IsSectionGroup = false;
3276     while (curSectionIndex < bhSections.Num)
3277     {
3278       const CSection &sect = Sections[curSectionIndex];
3279       if (sectionIsOpen)
3280       {
3281         if (sect.StartCmdIndex + sect.NumCommands + 1 != kkk)
3282           break;
3283         PrintSectionEnd();
3284         sectionIsOpen = false;
3285         // lastSectionEndCmd = kkk;
3286         curSectionIndex++;
3287         continue;
3288       }
3289       if (sect.StartCmdIndex != kkk)
3290         break;
3291       if (PrintSectionBegin(sect, curSectionIndex))
3292       {
3293         IsSectionGroup = true;
3294         curSectionIndex++;
3295         // do we need to flush prefixes in new section?
3296         // FlushOutPathPrefixes();
3297       }
3298       else
3299         sectionIsOpen = true;
3300     }
3301 
3302     /*
3303     if (curOnFunc < OnFuncs.Size())
3304     {
3305       if ((OnFuncs[curOnFunc] >> kOnFuncShift) == kkk)
3306       {
3307         s += "Function .on";
3308         s += kOnFunc[OnFuncs[curOnFunc++] & ((1 << kOnFuncShift) - 1)];
3309         AddLF();
3310         onFuncIsOpen = true;
3311       }
3312     }
3313     */
3314 
3315     if (labels[kkk] != 0 && labels[kkk] != CMD_REF_Section)
3316     {
3317       UInt32 flg = labels[kkk];
3318       if (IsFunc(flg))
3319       {
3320         if ((int)kkk == InitPluginsDir_Start)
3321           CommentOpen();
3322 
3323         onFuncIsOpen = true;
3324         s += "Function ";
3325         Add_FuncName(labels, kkk);
3326         if (IsPageFunc(flg))
3327         {
3328           BigSpaceComment();
3329           s += "Page ";
3330           Add_UInt((flg & CMD_REF_Page_Mask) >> CMD_REF_Page_NumShifts);
3331           // if (flg & CMD_REF_Creator) s += ", Creator";
3332           if (flg & CMD_REF_Leave) s += ", Leave";
3333           if (flg & CMD_REF_Pre) s += ", Pre";
3334           if (flg & CMD_REF_Show) s += ", Show";
3335         }
3336         AddLF();
3337       }
3338       if (flg & CMD_REF_Goto)
3339       {
3340         Add_LabelName(kkk);
3341         s += ':';
3342         AddLF();
3343       }
3344     }
3345 
3346     if (commandId != EW_RET)
3347     {
3348       Tab(kkk < endCommentIndex);
3349     }
3350 
3351     /*
3352     UInt32 originalCmd = Get32(p);
3353     if (originalCmd >= EW_REGISTERDLL)
3354     {
3355       UIntToString(s, originalCmd);
3356       s += ' ';
3357       if (originalCmd != commandId)
3358       {
3359         UIntToString(s, commandId);
3360         s += ' ';
3361       }
3362     }
3363     */
3364 
3365     unsigned numSkipParams = 0;
3366 
3367     if (commandId < Z7_ARRAY_SIZE(k_Commands) && commandId < numSupportedCommands)
3368     {
3369       numSkipParams = k_Commands[commandId].NumParams;
3370       const char *sz = k_CommandNames[commandId];
3371       if (sz)
3372         s += sz;
3373     }
3374     else
3375     {
3376       s += "Command";
3377       Add_UInt(commandId);
3378       /* We don't show wrong commands that use overlapped ids.
3379          So we change commandId to big value */
3380       if (commandId < (1 << 12))
3381         commandId += (1 << 12);
3382     }
3383 
3384     #endif
3385 
3386     switch (commandId)
3387     {
3388       case EW_CREATEDIR:
3389       {
3390         bool isSetOutPath = (params[1] != 0);
3391 
3392         if (isSetOutPath)
3393         {
3394           UInt32 par0 = params[0];
3395 
3396           UInt32 resOffset;
3397           Int32 idx = GetVarIndex(par0, resOffset);
3398           if (idx == (Int32)spec_outdir_VarIndex ||
3399               idx == kVar_OUTDIR)
3400             par0 += resOffset;
3401 
3402           ReadString2_Raw(par0);
3403 
3404           if (IsUnicode)
3405           {
3406             if (idx == (Int32)spec_outdir_VarIndex)
3407               Raw_UString.Insert(0, spec_outdir_U);
3408             else if (idx == kVar_OUTDIR)
3409               Raw_UString.Insert(0, UPrefixes.Back());
3410             UPrefixes.Add(Raw_UString);
3411           }
3412           else
3413           {
3414             if (idx == (Int32)spec_outdir_VarIndex)
3415               Raw_AString.Insert(0, spec_outdir_A);
3416             else if (idx == kVar_OUTDIR)
3417               Raw_AString.Insert(0, APrefixes.Back());
3418             APrefixes.Add(Raw_AString);
3419           }
3420         }
3421 
3422         #ifdef NSIS_SCRIPT
3423         s += isSetOutPath ? "SetOutPath" : "CreateDirectory";
3424         AddParam(params[0]);
3425         if (params[2] != 0) // 2.51+ & 3.0b3+
3426         {
3427           SmallSpaceComment();
3428           s += "CreateRestrictedDirectory";
3429         }
3430         #endif
3431 
3432         break;
3433       }
3434 
3435 
3436       case EW_ASSIGNVAR:
3437       {
3438         if (params[0] == spec_outdir_VarIndex)
3439         {
3440           spec_outdir_U.Empty();
3441           spec_outdir_A.Empty();
3442           if (IsVarStr(params[1], kVar_OUTDIR) &&
3443               params[2] == 0 &&
3444               params[3] == 0)
3445           {
3446             spec_outdir_U = UPrefixes.Back(); // outdir_U;
3447             spec_outdir_A = APrefixes.Back(); // outdir_A;
3448           }
3449         }
3450 
3451         #ifdef NSIS_SCRIPT
3452 
3453         if (params[2] == 0 &&
3454             params[3] == 0 &&
3455             params[4] == 0 &&
3456             params[5] == 0 &&
3457             params[1] != 0 &&
3458             params[1] < NumStringChars)
3459         {
3460           char sz[16];
3461           ConvertUInt32ToString(kkk + 1, sz);
3462           if (IsDirectString_Equal(params[1], sz))
3463           {
3464             // we suppose that it's GetCurrentAddress command
3465             // but there is probability that it's StrCpy command
3466             s += "GetCurrentAddress";
3467             AddParam_Var(params[0]);
3468             SmallSpaceComment();
3469           }
3470         }
3471         s += "StrCpy";
3472         AddParam_Var(params[0]);
3473         AddParam(params[1]);
3474 
3475         AddOptionalParams(params + 2, 2);
3476 
3477         #endif
3478 
3479         break;
3480       }
3481 
3482       case EW_EXTRACTFILE:
3483       {
3484         CItem &item = Items.AddNew();
3485 
3486         UInt32 par1 = params[1];
3487 
3488         SetItemName(item, par1);
3489 
3490         item.Pos = params[2];
3491         item.MTime.dwLowDateTime = params[3];
3492         item.MTime.dwHighDateTime = params[4];
3493 
3494         #ifdef NSIS_SCRIPT
3495 
3496         {
3497           UInt32 overwrite = params[0] & 0x7;
3498           if (overwrite != overwrite_State)
3499           {
3500             s += "SetOverwrite ";
3501             ADD_TYPE_FROM_LIST(k_SetOverwrite_Modes, overwrite);
3502             overwrite_State = overwrite;
3503             AddLF();
3504             Tab(kkk < endCommentIndex);
3505           }
3506         }
3507 
3508         {
3509           UInt32 nsisMB = params[0] >> 3;
3510           if ((Int32)nsisMB != allowSkipFiles_State)
3511           {
3512             UInt32 mb = nsisMB & ((1 << 20) - 1);  // old/new NSIS
3513             UInt32 b1 = nsisMB >> 21;  // NSIS 2.06+
3514             UInt32 b2 = nsisMB >> 20;  // NSIS old
3515             Int32 asf = (Int32)nsisMB;
3516             if (mb == (Z7_NSIS_WIN_MB_ABORTRETRYIGNORE | Z7_NSIS_WIN_MB_ICONSTOP) && (b1 == Z7_NSIS_WIN_IDIGNORE || b2 == Z7_NSIS_WIN_IDIGNORE))
3517               asf = -1;
3518             else if (mb == (Z7_NSIS_WIN_MB_RETRYCANCEL | Z7_NSIS_WIN_MB_ICONSTOP) && (b1 == Z7_NSIS_WIN_IDCANCEL || b2 == Z7_NSIS_WIN_IDCANCEL))
3519               asf = -2;
3520             else
3521             {
3522               AddCommentAndString("AllowSkipFiles [Overwrite]: ");
3523               MessageBox_MB_Part(mb);
3524               if (b1 != 0)
3525               {
3526                 s += " /SD";
3527                 Add_ButtonID(b1);
3528               }
3529             }
3530             if (asf != allowSkipFiles_State)
3531             {
3532               if (asf < 0)
3533               {
3534                 s += "AllowSkipFiles ";
3535                 s += (asf == -1) ? "on" : "off";
3536               }
3537               AddLF();
3538               Tab(kkk < endCommentIndex);
3539             }
3540             allowSkipFiles_State = (Int32)nsisMB;
3541           }
3542         }
3543 
3544         s += "File";
3545         AddParam(params[1]);
3546 
3547         /* params[5] contains link to LangString (negative value)
3548            with NLF_FILE_ERROR or NLF_FILE_ERROR_NOIGNORE message for MessageBox.
3549            We don't need to print it. */
3550 
3551         #endif
3552 
3553         if (IsVarStr(par1, 10)) // is $R0
3554         {
3555           // we parse InstallLib macro in 7-Zip installers
3556           unsigned kBackOffset = 28;
3557           if (kkk > 1)
3558           {
3559             // detect old version of InstallLib macro
3560             if (Get32(p - 1 * kCmdSize) == EW_NOP) // goto command
3561               kBackOffset -= 2;
3562           }
3563 
3564           if (kkk > kBackOffset)
3565           {
3566             const Byte *p2 = p - kBackOffset * kCmdSize;
3567             UInt32 cmd = Get32(p2);
3568             if (cmd == EW_ASSIGNVAR)
3569             {
3570               UInt32 pars[6];
3571               for (int i = 0; i < 6; i++)
3572                 pars[i] = Get32(p2 + i * 4 + 4);
3573               if (pars[0] == 10 + 4 && pars[2] == 0 && pars[3] == 0) // 10 + 4 means $R4
3574               {
3575                 item.Prefix = -1;
3576                 item.NameA.Empty();
3577                 item.NameU.Empty();
3578                 SetItemName(item, pars[1]);
3579                 // maybe here we can restore original item name, if new name is empty
3580               }
3581             }
3582           }
3583         }
3584         /* UInt32 allowIgnore = params[5]; */
3585         break;
3586       }
3587 
3588       case EW_SETFILEATTRIBUTES:
3589       {
3590         if (kkk > 0 && Get32(p - kCmdSize) == EW_EXTRACTFILE)
3591         {
3592           if (params[0] == Get32(p - kCmdSize + 4 + 4 * 1)) // compare with PrevCmd.Params[1]
3593           {
3594             CItem &item = Items.Back();
3595             item.Attrib_Defined = true;
3596             item.Attrib = params[1];
3597           }
3598         }
3599         #ifdef NSIS_SCRIPT
3600         AddParam(params[0]);
3601         Space();
3602         FlagsToString2(s, g_WinAttrib, Z7_ARRAY_SIZE(g_WinAttrib), params[1]);
3603         #endif
3604         break;
3605       }
3606 
3607       case EW_WRITEUNINSTALLER:
3608       {
3609         /* NSIS 2.29+ writes alternative path to params[3]
3610              "$INSTDIR\\" + Str(params[0])
3611            NSIS installer uses alternative path, if main path
3612            from params[0] is not absolute path */
3613 
3614         const bool pathOk = (params[0] > 0) && IsGoodString(params[0]);
3615 
3616         if (!pathOk)
3617         {
3618           #ifdef NSIS_SCRIPT
3619           AddError("bad path");
3620           #endif
3621           break;
3622         }
3623 
3624       #ifdef NSIS_SCRIPT
3625 
3626         bool altPathOk = true;
3627 
3628         const UInt32 altParam = params[3];
3629         if (altParam != 0)
3630         {
3631           altPathOk = false;
3632           UInt32 additional = 0;
3633           if (GetVarIndexFinished(altParam, '\\', additional) == kVar_INSTDIR)
3634             altPathOk = AreTwoParamStringsEqual(altParam + additional, params[0]);
3635         }
3636 
3637         AddParam(params[0]);
3638 
3639         /*
3640         for (int i = 1; i < 3; i++)
3641           AddParam_UInt(params[i]);
3642         */
3643 
3644         if (params[3] != 0)
3645         {
3646           SmallSpaceComment();
3647           AddParam(params[3]);
3648         }
3649 
3650         if (!altPathOk)
3651         {
3652           #ifdef NSIS_SCRIPT
3653           AddError("alt path error");
3654           #endif
3655         }
3656 
3657       #endif
3658 
3659         if (BadCmd >= 0 && BadCmd <= EW_WRITEUNINSTALLER)
3660         {
3661           /* We don't support cases with incorrect installer commands.
3662              Such bad installer item can break unpacking for other items. */
3663           #ifdef NSIS_SCRIPT
3664           AddError("SKIP possible BadCmd");
3665           #endif
3666           break;
3667         }
3668 
3669         CItem &item = Items.AddNew();
3670 
3671         SetItemName(item, params[0]);
3672 
3673         item.Pos = params[1];
3674         item.PatchSize = params[2];
3675         item.IsUninstaller = true;
3676         const UInt32 param3 = params[3];
3677         if (param3 != 0 && item.Prefix != -1)
3678         {
3679           /* (item.Prefix != -1) case means that param[0] path was not absolute.
3680               So we use params[3] in that case, as original nsis */
3681           SetItemName(item, param3);
3682         }
3683         /* UNINSTALLER file doesn't use directory prefixes.
3684            So we remove prefix: */
3685         item.Prefix = -1;
3686 
3687         /*
3688         // we can add second time to test the code
3689         CItem item2 = item;
3690         item2.NameU += L'2';
3691         item2.NameA += '2';
3692         Items.Add(item2);
3693         */
3694 
3695         break;
3696       }
3697 
3698       #ifdef NSIS_SCRIPT
3699 
3700       case EW_RET:
3701       {
3702         // bool needComment = false;
3703         if (onFuncIsOpen)
3704         {
3705           if (kkk == bh.Num - 1 || IsProbablyEndOfFunc(labels[kkk + 1]))
3706           {
3707             AddStringLF("FunctionEnd");
3708 
3709             if ((int)kkk + 1 == InitPluginsDir_End)
3710               CommentClose();
3711             AddLF();
3712             onFuncIsOpen = false;
3713             // needComment = true;
3714             break;
3715           }
3716         }
3717         // if (!needComment)
3718             if (IsSectionGroup)
3719               break;
3720           if (sectionIsOpen)
3721           {
3722             const CSection &sect = Sections[curSectionIndex];
3723             if (sect.StartCmdIndex + sect.NumCommands == kkk)
3724             {
3725               PrintSectionEnd();
3726               sectionIsOpen = false;
3727               curSectionIndex++;
3728               break;
3729             }
3730 
3731             // needComment = true;
3732             // break;
3733           }
3734 
3735         /*
3736         if (needComment)
3737           s += "  ;";
3738         */
3739         TabString("Return");
3740         AddLF();
3741         break;
3742       }
3743 
3744       case EW_NOP:
3745       {
3746         if (params[0] == 0)
3747           s += "Nop";
3748         else
3749         {
3750           s += "Goto";
3751           Add_GotoVar(params[0]);
3752         }
3753         break;
3754       }
3755 
3756       case EW_ABORT:
3757       {
3758         AddOptionalParam(params[0]);
3759         break;
3760       }
3761 
3762       case EW_CALL:
3763       {
3764         if (kkk + 1 < bh.Num && GetCmd(Get32(p + kCmdSize)) == EW_EXTRACTFILE)
3765         {
3766           UInt32 par1 = GET_CMD_PARAM(p + kCmdSize, 1);
3767 
3768           UInt32 pluginPar = 0;
3769 
3770           if (GetVarIndexFinished(par1, '\\', pluginPar) == kVar_PLUGINSDIR)
3771           {
3772             pluginPar += par1;
3773             UInt32 commandId2 = GetCmd(Get32(p + kCmdSize * 2));
3774             if (commandId2 == EW_SETFLAG || commandId2 == EW_UPDATETEXT)
3775             {
3776               UInt32 i;
3777               for (i = kkk + 3; i < bh.Num; i++)
3778               {
3779                 const Byte *pCmd = p + kCmdSize * (i - kkk);
3780                 UInt32 commandId3 = GetCmd(Get32(pCmd));
3781                 if (commandId3 != EW_PUSHPOP
3782                     || GET_CMD_PARAM(pCmd, 1) != 0
3783                     || GET_CMD_PARAM(pCmd, 2) != 0)
3784                   break;
3785               }
3786               if (i < bh.Num)
3787               {
3788                 const Byte *pCmd = p + kCmdSize * (i - kkk);
3789 
3790                 // UInt32 callDll_Param = GET_CMD_PARAM(pCmd, 0);
3791                 // UInt32 file_Param = GET_CMD_PARAM(p + kCmdSize, 1);
3792 
3793                 if (GetCmd(Get32(pCmd)) == EW_REGISTERDLL &&
3794                     AreTwoParamStringsEqual(
3795                       GET_CMD_PARAM(pCmd, 0),
3796                       GET_CMD_PARAM(p + kCmdSize, 1)))
3797                 {
3798                   // params[4] = 1 means GetModuleHandle attempt before default LoadLibraryEx;
3799                   /// new versions of NSIS use params[4] = 1 for Plugin command
3800                   if (GET_CMD_PARAM(pCmd, 2) == 0
3801                     // && GET_CMD_PARAM(pCmd, 4) != 0
3802                     )
3803                   {
3804                     {
3805                       AString s2;
3806                       ReadString2(s2, pluginPar);
3807                       if (s2.Len() >= 4 &&
3808                           StringsAreEqualNoCase_Ascii(s2.RightPtr(4), ".dll"))
3809                         s2.DeleteFrom(s2.Len() - 4);
3810                       s2 += "::";
3811                       AString func;
3812                       ReadString2(func, GET_CMD_PARAM(pCmd, 1));
3813                       s2 += func;
3814                       Add_QuStr(s2);
3815 
3816                       if (GET_CMD_PARAM(pCmd, 3) == 1)
3817                         s += " /NOUNLOAD";
3818 
3819                       for (UInt32 j = i - 1; j >= kkk + 3; j--)
3820                       {
3821                         const Byte *pCmd2 = p + kCmdSize * (j - kkk);
3822                         AddParam(GET_CMD_PARAM(pCmd2, 0));
3823                       }
3824                       NewLine();
3825                       Tab(true);
3826                       endCommentIndex = i + 1;
3827                     }
3828                   }
3829                 }
3830               }
3831             }
3832           }
3833         }
3834         {
3835           const Byte *nextCmd = p + kCmdSize;
3836           UInt32 commandId2 = GetCmd(Get32(nextCmd));
3837           if (commandId2 == EW_SETFLAG
3838               && GET_CMD_PARAM(nextCmd, 0) == k_ExecFlags_DetailsPrint
3839               && GET_CMD_PARAM(nextCmd, 2) != 0) // is "lastused"
3840             // || commandId2 == EW_UPDATETEXT)
3841           {
3842             if ((Int32)params[0] > 0 && labels[params[0] - 1] & CMD_REF_InitPluginDir)
3843             {
3844               s += "InitPluginsDir";
3845               AddLF();
3846               Tab(true);
3847               endCommentIndex = kkk + 2;
3848             }
3849           }
3850         }
3851 
3852         s += "Call ";
3853         if ((Int32)params[0] < 0)
3854           Add_Var((UInt32)-((Int32)params[0] + 1));
3855         else if (params[0] == 0)
3856           s += '0';
3857         else
3858         {
3859           const UInt32 val = params[0] - 1;
3860           if (params[1] == 1) // it's Call :Label
3861           {
3862             s += ':';
3863             Add_LabelName(val);
3864           }
3865           else // if (params[1] == 0) // it's Call Func
3866             Add_FuncName(labels, val);
3867         }
3868         break;
3869       }
3870 
3871       case EW_UPDATETEXT:
3872       case EW_SLEEP:
3873       {
3874         AddParam(params[0]);
3875         break;
3876       }
3877 
3878       case EW_CHDETAILSVIEW:
3879       {
3880              if (params[0] == Z7_NSIS_WIN_SW_SHOWNA && params[1] == Z7_NSIS_WIN_SW_HIDE) s += " show";
3881         else if (params[1] == Z7_NSIS_WIN_SW_SHOWNA && params[0] == Z7_NSIS_WIN_SW_HIDE) s += " hide";
3882         else
3883           for (int i = 0; i < 2; i++)
3884           {
3885             Space();
3886             Add_ShowWindow_Cmd(params[i]);
3887           }
3888         break;
3889       }
3890 
3891       case EW_IFFILEEXISTS:
3892       {
3893         AddParam(params[0]);
3894         Add_GotoVars2(&params[1]);
3895         break;
3896       }
3897 
3898       case EW_SETFLAG:
3899       {
3900         AString temp;
3901         ReadString2(temp, params[1]);
3902         if (params[0] == k_ExecFlags_Errors && params[2] == 0)
3903         {
3904           s += (temp.Len() == 1 && temp[0] == '0') ? "ClearErrors" : "SetErrors";
3905           break;
3906         }
3907         s += "Set";
3908         Add_ExecFlags(params[0]);
3909 
3910         if (params[2] != 0)
3911         {
3912           s += " lastused";
3913           break;
3914         }
3915         UInt32 v;
3916         if (StringToUInt32(temp, v))
3917         {
3918           const char *s2 = NULL;
3919           switch (params[0])
3920           {
3921             case k_ExecFlags_AutoClose:
3922             case k_ExecFlags_RebootFlag:
3923               if (v < 2) { s2 = (v == 0) ? "false" : "true"; }  break;
3924             case k_ExecFlags_ShellVarContext:
3925               if (v < 2) { s2 = (v == 0) ? "current" : "all"; }  break;
3926             case k_ExecFlags_Silent:
3927               if (v < 2) { s2 = (v == 0) ? "normal" : "silent"; }  break;
3928             case k_ExecFlags_RegView:
3929                    if (v ==   0) s2 = "32";
3930               else if (v == 256) s2 = "64";
3931               break;
3932             case k_ExecFlags_DetailsPrint:
3933                    if (v == 0) s2 = "both";
3934               else if (v == 2) s2 = "textonly";
3935               else if (v == 4) s2 = "listonly";
3936               else if (v == 6) s2 = "none";
3937               break;
3938           }
3939           if (s2)
3940           {
3941             s += ' ';
3942             s += s2;
3943             break;
3944           }
3945         }
3946         SpaceQuStr(temp);
3947         break;
3948       }
3949 
3950       case EW_IFFLAG:
3951       {
3952         Add_ExecFlags(params[2]);
3953         Add_GotoVars2(&params[0]);
3954         /*
3955         const unsigned kIfErrors = 2;
3956         if (params[2] != kIfErrors && params[3] != 0xFFFFFFFF ||
3957             params[2] == kIfErrors && params[3] != 0)
3958         {
3959           s += " # FLAG &= ";
3960           AddParam_UInt(params[3]);
3961         }
3962         */
3963         break;
3964       }
3965 
3966       case EW_GETFLAG:
3967       {
3968         Add_ExecFlags(params[1]);
3969         AddParam_Var(params[0]);
3970         break;
3971       }
3972 
3973       case EW_RENAME:
3974       {
3975         if (params[2] != 0)
3976           s += k_REBOOTOK;
3977         AddParams(params, 2);
3978         if (params[3] != 0)
3979         {
3980           SmallSpaceComment();
3981           AddParam(params[3]); // rename comment for log file
3982         }
3983         break;
3984       }
3985 
3986       case EW_GETFULLPATHNAME:
3987       {
3988         if (params[2] == 0)
3989           s += " /SHORT";
3990         AddParam_Var(params[1]);
3991         AddParam(params[0]);
3992         break;
3993       }
3994 
3995       case EW_SEARCHPATH:
3996       case EW_STRLEN:
3997       {
3998         AddParam_Var(params[0]);
3999         AddParam(params[1]);
4000         break;
4001       }
4002 
4003       case EW_GETTEMPFILENAME:
4004       {
4005         AddParam_Var(params[0]);
4006         AString temp;
4007         ReadString2(temp, params[1]);
4008         if (temp != "$TEMP")
4009           SpaceQuStr(temp);
4010         break;
4011       }
4012 
4013       case EW_DELETEFILE:
4014       {
4015         UInt32 flag = params[1];
4016         if ((flag & DEL_REBOOT) != 0)
4017           s += k_REBOOTOK;
4018         AddParam(params[0]);
4019         break;
4020       }
4021 
4022       case EW_MESSAGEBOX:
4023       {
4024         MessageBox_MB_Part(params[0]);
4025         AddParam(params[1]);
4026         {
4027           UInt32 buttonID = (params[0] >> 21); // NSIS 2.06+
4028           if (buttonID != 0)
4029           {
4030             s += " /SD";
4031             Add_ButtonID(buttonID);
4032           }
4033         }
4034         for (int i = 2; i < 6; i += 2)
4035           if (params[i] != 0)
4036           {
4037             Add_ButtonID(params[i]);
4038             Add_GotoVar1(params[i + 1]);
4039           }
4040         break;
4041       }
4042 
4043       case EW_RMDIR:
4044       {
4045         UInt32 flag = params[1];
4046         if ((flag & DEL_RECURSE) != 0)
4047           s += " /r";
4048         if ((flag & DEL_REBOOT) != 0)
4049           s += k_REBOOTOK;
4050         AddParam(params[0]);
4051         break;
4052       }
4053 
4054       case EW_STRCMP:
4055       {
4056         if (params[4] != 0)
4057           s += 'S';
4058         AddParams(params, 2);
4059         Add_GotoVars2(&params[2]);
4060         break;
4061       }
4062 
4063       case EW_READENVSTR:
4064       {
4065         s += (params[2] != 0) ?
4066           "ReadEnvStr" :
4067           "ExpandEnvStrings";
4068         AddParam_Var(params[0]);
4069         AString temp;
4070         ReadString2(temp, params[1]);
4071         if (params[2] != 0 &&temp.Len() >= 2 && temp[0] == '%' && temp.Back() == '%')
4072         {
4073           temp.DeleteBack();
4074           temp.Delete(0);
4075         }
4076         SpaceQuStr(temp);
4077         break;
4078       }
4079 
4080       case EW_INTCMP:
4081       {
4082         s += "Int";
4083         const UInt32 param5 = params[5];
4084         if (param5 & 0x8000)
4085           s += "64"; // v3.03+
4086         s += "Cmp";
4087         if (IsNsis3_OrHigher() ? (param5 & 1) : (param5 != 0))
4088           s += 'U';
4089         AddParams(params, 2);
4090         Add_GotoVar1(params[2]);
4091         if (params[3] != 0 || params[4] != 0)
4092           Add_GotoVars2(params + 3);
4093         break;
4094       }
4095 
4096       case EW_INTOP:
4097       {
4098         AddParam_Var(params[0]);
4099         const char * const kOps = "+-*/|&^!|&%<>>"; // NSIS 2.01+
4100                         // "+-*/|&^!|&%";   // NSIS 2.0b4+
4101                         // "+-*/|&^~!|&%";  // NSIS old
4102         const UInt32 opIndex = params[3];
4103         const char c = (opIndex < 14) ? kOps[opIndex] : '?';
4104         const char c2 = (opIndex < 8 || opIndex == 10) ? (char)0 : c;
4105         const int numOps = (opIndex == 7) ? 1 : 2;
4106         AddParam(params[1]);
4107         if (numOps == 2 && c == '^' && IsDirectString_Equal(params[2], "0xFFFFFFFF"))
4108           s += " ~    ;";
4109         Space();
4110         s += c;
4111         if (numOps != 1)
4112         {
4113           if (opIndex == 13) // v3.03+ : operation ">>>"
4114             s += c;
4115           if (c2 != 0)
4116             s += c2;
4117           AddParam(params[2]);
4118         }
4119         break;
4120       }
4121 
4122       case EW_INTFMT:
4123       {
4124         if (params[3])
4125           s += "Int64Fmt";  // v3.03+
4126         else
4127           s += "IntFmt";
4128         AddParam_Var(params[0]);
4129         AddParams(params + 1, 2);
4130         break;
4131       }
4132 
4133       case EW_PUSHPOP:
4134       {
4135         if (params[2] != 0)
4136         {
4137           s += "Exch";
4138           if (params[2] != 1)
4139             AddParam_UInt(params[2]);
4140         }
4141         else if (params[1] != 0)
4142         {
4143           s += "Pop";
4144           AddParam_Var(params[0]);
4145         }
4146         else
4147         {
4148           if (NoLabels(labels + kkk + 1, 2)
4149               && Get32(p + kCmdSize) == EW_PUSHPOP // Exch"
4150               && GET_CMD_PARAM(p + kCmdSize, 2) == 1
4151               && Get32(p + kCmdSize * 2) == EW_PUSHPOP // Pop $VAR
4152               && GET_CMD_PARAM(p + kCmdSize * 2, 1) != 0)
4153           {
4154             if (IsVarStr(params[0], GET_CMD_PARAM(p + kCmdSize * 2, 0)))
4155             {
4156               s += "Exch";
4157               AddParam(params[0]);
4158               NewLine();
4159               Tab(true);
4160               endCommentIndex = kkk + 3;
4161             }
4162           }
4163           s += "Push";
4164           AddParam(params[0]);
4165         }
4166         break;
4167       }
4168 
4169       case EW_FINDWINDOW:
4170       {
4171         AddParam_Var(params[0]);
4172         AddParam(params[1]);
4173         AddOptionalParams(params + 2, 3);
4174         break;
4175       }
4176 
4177       case EW_SENDMESSAGE:
4178       {
4179         // SendMessage: 6 [output, hwnd, msg, wparam, lparam, [wparamstring?1:0 | lparamstring?2:0 | timeout<<2]
4180         AddParam(params[1]);
4181 
4182         const char *w = NULL;
4183         AString t;
4184         ReadString2(t, params[2]);
4185         UInt32 wm;
4186         if (StringToUInt32(t, wm))
4187         {
4188           switch (wm)
4189           {
4190             case 0x0C: w = "SETTEXT"; break;
4191             case 0x10: w = "CLOSE"; break;
4192             case 0x30: w = "SETFONT"; break;
4193           }
4194         }
4195         if (w)
4196         {
4197           s += " ${WM_";
4198           s += w;
4199           s += '}';
4200         }
4201         else
4202           SpaceQuStr(t);
4203 
4204         UInt32 spec = params[5];
4205         for (unsigned i = 0; i < 2; i++)
4206         {
4207           AString s2;
4208           if (spec & ((UInt32)1 << i))
4209             s2 += "STR:";
4210           ReadString2(s2, params[3 + i]);
4211           SpaceQuStr(s2);
4212         }
4213 
4214         if ((Int32)params[0] >= 0)
4215           AddParam_Var(params[0]);
4216 
4217         spec >>= 2;
4218         if (spec != 0)
4219         {
4220           s += " /TIMEOUT=";
4221           Add_UInt(spec);
4222         }
4223         break;
4224       }
4225 
4226       case EW_ISWINDOW:
4227       {
4228         AddParam(params[0]);
4229         Add_GotoVars2(&params[1]);
4230         break;
4231       }
4232 
4233       case EW_GETDLGITEM:
4234       {
4235         AddParam_Var(params[0]);
4236         AddParams(params + 1, 2);
4237         break;
4238       }
4239 
4240       case EW_SETCTLCOLORS:
4241       {
4242         AddParam(params[0]);
4243 
4244         UInt32 offset = params[1];
4245 
4246         if (_size < bhCtlColors.Offset
4247            || _size - bhCtlColors.Offset < offset
4248            || _size - bhCtlColors.Offset - offset < GET_CtlColors_SIZE(Is64Bit))
4249         {
4250           AddError("bad offset");
4251           break;
4252         }
4253 
4254         const Byte *p2 = _data + bhCtlColors.Offset + offset;
4255         CNsis_CtlColors colors;
4256         colors.Parse(p2, Is64Bit);
4257 
4258         if ((colors.flags & kColorsFlags_BK_SYS) != 0 ||
4259             (colors.flags & kColorsFlags_TEXT_SYS) != 0)
4260           s += " /BRANDING";
4261 
4262         AString bk;
4263         bool bkc = false;
4264         if (colors.bkmode == Z7_NSIS_WIN_TRANSPARENT)
4265           bk += " transparent";
4266         else if (colors.flags & kColorsFlags_BKB)
4267         {
4268           if ((colors.flags & kColorsFlags_BK_SYS) == 0 &&
4269               (colors.flags & kColorsFlags_BK) != 0)
4270             bkc = true;
4271         }
4272         if ((colors.flags & kColorsFlags_TEXT) != 0 || !bk.IsEmpty() || bkc)
4273         {
4274           Space();
4275           if ((colors.flags & kColorsFlags_TEXT_SYS) != 0 || (colors.flags & kColorsFlags_TEXT) == 0)
4276             AddQuotes();
4277           else
4278             Add_Color(colors.text);
4279         }
4280         s += bk;
4281         if (bkc)
4282         {
4283           Space();
4284           Add_Color(colors.bkc);
4285         }
4286 
4287         break;
4288       }
4289 
4290       // case EW_LOADANDSETIMAGE:
4291       case EW_SETBRANDINGIMAGE:
4292       {
4293         s += " /IMGID=";
4294         Add_UInt(params[1]);
4295         if (params[2] == 1)
4296           s += " /RESIZETOFIT";
4297         AddParam(params[0]);
4298         break;
4299       }
4300 
4301       case EW_CREATEFONT:
4302       {
4303         AddParam_Var(params[0]);
4304         AddParam(params[1]);
4305         AddOptionalParams(params + 2, 2);
4306         if (params[4] & 1) s += " /ITALIC";
4307         if (params[4] & 2) s += " /UNDERLINE";
4308         if (params[4] & 4) s += " /STRIKE";
4309         break;
4310       }
4311 
4312       case EW_SHOWWINDOW:
4313       {
4314         AString hw, sw;
4315         ReadString2(hw, params[0]);
4316         ReadString2(sw, params[1]);
4317         if (params[3] != 0)
4318           s += "EnableWindow";
4319         else
4320         {
4321           UInt32 val;
4322           bool valDefined = false;
4323           if (StringToUInt32(sw, val))
4324           {
4325             if (val < Z7_ARRAY_SIZE(kShowWindow_Commands))
4326             {
4327               sw.Empty();
4328               sw += "${";
4329               Add_ShowWindow_Cmd_2(sw, val);
4330               sw += '}';
4331               valDefined = true;
4332             }
4333           }
4334           bool isHwndParent = IsVarStr(params[0], IsNsis225 ? kVar_HWNDPARENT_225 : kVar_HWNDPARENT);
4335           if (params[2] != 0)
4336           {
4337             if (valDefined && val == 0 && isHwndParent)
4338             {
4339               s += "HideWindow";
4340               break;
4341             }
4342           }
4343           if (valDefined && val == 5 && isHwndParent &&
4344               kkk + 1 < bh.Num && GetCmd(Get32(p + kCmdSize)) == EW_BRINGTOFRONT)
4345           {
4346             s += "  ; ";
4347           }
4348           s += "ShowWindow";
4349         }
4350         SpaceQuStr(hw);
4351         SpaceQuStr(sw);
4352         break;
4353       }
4354 
4355       case EW_SHELLEXEC:
4356       {
4357         AddParams(params, 2);
4358         if (params[2] != 0 || params[3] != Z7_NSIS_WIN_SW_SHOWNORMAL)
4359         {
4360           AddParam(params[2]);
4361           if (params[3] != Z7_NSIS_WIN_SW_SHOWNORMAL)
4362           {
4363             Space();
4364             Add_ShowWindow_Cmd(params[3]);
4365           }
4366         }
4367         if (params[5] != 0)
4368         {
4369           s += "    ;";
4370           AddParam(params[5]); // it's tatus text update
4371         }
4372         break;
4373       }
4374 
4375       case EW_EXECUTE:
4376       {
4377         if (params[2] != 0)
4378           s += "Wait";
4379         AddParam(params[0]);
4380         if (params[2] != 0)
4381           if ((Int32)params[1] >= 0)
4382             AddParam_Var(params[1]);
4383         break;
4384       }
4385 
4386       case EW_GETFILETIME:
4387       case EW_GETDLLVERSION:
4388       {
4389         if (commandId == EW_GETDLLVERSION)
4390           if (params[3] == 2)
4391             s += " /ProductVersion";  // v3.08+
4392         AddParam(params[2]);
4393         AddParam_Var(params[0]);
4394         AddParam_Var(params[1]);
4395         break;
4396       }
4397 
4398       case EW_REGISTERDLL:
4399       {
4400         AString func;
4401         ReadString2(func, params[1]);
4402         bool printFunc = true;
4403         // params[4] = 1; for plugin command
4404         if (params[2] == 0)
4405         {
4406           s += "CallInstDLL";
4407           AddParam(params[0]);
4408           if (params[3] == 1)
4409             s += " /NOUNLOAD";
4410         }
4411         else
4412         {
4413           if (func == "DllUnregisterServer")
4414           {
4415             s += "UnRegDLL";
4416             printFunc = false;
4417           }
4418           else
4419           {
4420             s += "RegDLL";
4421             if (func == "DllRegisterServer")
4422               printFunc = false;
4423           }
4424           AddParam(params[0]);
4425         }
4426         if (printFunc)
4427           SpaceQuStr(func);
4428         break;
4429       }
4430 
4431       case EW_CREATESHORTCUT:
4432       {
4433         unsigned numParams;
4434         #define IsNsis3d0b3_OrHigher() 0 // change it
4435         const unsigned v3_0b3 = IsNsis3d0b3_OrHigher();
4436         for (numParams = 6; numParams > 2; numParams--)
4437           if (params[numParams - 1] != 0)
4438             break;
4439 
4440         const UInt32 spec = params[4];
4441         const unsigned sw_shift = v3_0b3 ? 12 : 8;
4442         const UInt32 sw_mask = v3_0b3 ? 0x7000 : 0x7F;
4443         if (spec & 0x8000) // NSIS 3.0b0
4444           s += " /NoWorkingDir";
4445 
4446         AddParams(params, numParams > 4 ? 4 : numParams);
4447         if (numParams <= 4)
4448           break;
4449 
4450         UInt32 icon = (spec & (v3_0b3 ? 0xFFF : 0xFF));
4451         Space();
4452         if (icon != 0)
4453           Add_UInt(icon);
4454         else
4455           AddQuotes();
4456 
4457         if ((spec >> sw_shift) == 0 && numParams < 6)
4458           break;
4459         UInt32 sw = (spec >> sw_shift) & sw_mask;
4460         Space();
4461         // NSIS encoder replaces these names:
4462         if (sw == Z7_NSIS_WIN_SW_SHOWMINNOACTIVE)
4463           sw = Z7_NSIS_WIN_SW_SHOWMINIMIZED;
4464         if (sw == 0)
4465           AddQuotes();
4466         else
4467           Add_ShowWindow_Cmd(sw);
4468 
4469         UInt32 modKey = spec >> 24;
4470         UInt32 key = (spec >> 16) & 0xFF;
4471 
4472         if (modKey == 0 && key == 0)
4473         {
4474           if (numParams < 6)
4475             break;
4476           Space();
4477           AddQuotes();
4478         }
4479         else
4480         {
4481           Space();
4482           if (modKey & 1) s += "SHIFT|"; // HOTKEYF_SHIFT
4483           if (modKey & 2) s += "CONTROL|";
4484           if (modKey & 4) s += "ALT|";
4485           if (modKey & 8) s += "EXT|";
4486 
4487           const unsigned kMy_VK_F1 = 0x70;
4488           if (key >= kMy_VK_F1 && key <= kMy_VK_F1 + 23)
4489           {
4490             s += 'F';
4491             Add_UInt(key - kMy_VK_F1 + 1);
4492           }
4493           else if ((key >= 'A' && key <= 'Z') || (key >= '0' && key <= '9'))
4494             s += (char)key;
4495           else
4496           {
4497             s += "Char_";
4498             Add_UInt(key);
4499           }
4500         }
4501         AddOptionalParam(params[5]); // description
4502         break;
4503       }
4504 
4505       case EW_COPYFILES:
4506       {
4507         if (params[2] & 0x04) s += " /SILENT"; // FOF_SILENT
4508         if (params[2] & 0x80) s += " /FILESONLY"; // FOF_FILESONLY
4509         AddParams(params, 2);
4510         if (params[3] != 0)
4511         {
4512           s += "    ;";
4513           AddParam(params[3]); // status text update
4514         }
4515         break;
4516       }
4517 
4518       case EW_REBOOT:
4519       {
4520         if (params[0] != 0xbadf00d)
4521           s += " ; Corrupted ???";
4522         else if (kkk + 1 < bh.Num && GetCmd(Get32(p + kCmdSize)) == EW_QUIT)
4523           endCommentIndex = kkk + 2;
4524         break;
4525       }
4526 
4527       case EW_WRITEINI:
4528       {
4529         unsigned numAlwaysParams = 0;
4530         if (params[0] == 0)  // Section
4531           s += "FlushINI";
4532         else if (params[4] != 0)
4533         {
4534           s += "WriteINIStr";
4535           numAlwaysParams = 3;
4536         }
4537         else
4538         {
4539           s += "DeleteINI";
4540           s += (params[1] == 0) ? "Sec" : "Str";
4541           numAlwaysParams = 1;
4542         }
4543         AddParam(params[3]); // filename
4544         // Section, EntryName, Value
4545         AddParams(params, numAlwaysParams);
4546         AddOptionalParams(params + numAlwaysParams, 3 - numAlwaysParams);
4547         break;
4548       }
4549 
4550       case EW_READINISTR:
4551       {
4552         AddParam_Var(params[0]);
4553         AddParam(params[3]); // FileName
4554         AddParams(params +1, 2); // Section, EntryName
4555         break;
4556       }
4557 
4558       case EW_DELREG:
4559       {
4560         // NSIS 2.00 used another scheme!
4561 
4562         if (params[4] == 0)
4563           s += "Value";
4564         else
4565         {
4566           s += "Key";
4567           if (params[4] & 2)
4568             s += " /ifempty";
4569           // TODO: /ifnosubkeys, /ifnovalues
4570         }
4571         AddRegRoot(params[1]);
4572         AddParam(params[2]);
4573         AddOptionalParam(params[3]);
4574         break;
4575       }
4576 
4577       case EW_WRITEREG:
4578       {
4579         const char *s2 = NULL;
4580         switch (params[4])
4581         {
4582           case 1: s2 = "Str"; break;
4583           case 2: s2 = "ExpandStr"; break; // maybe unused
4584           case 3: s2 = "Bin"; break;
4585           case 4: s2 = "DWORD"; break;
4586           default:
4587             s += '?';
4588             Add_UInt(params[4]);
4589         }
4590         if (params[4] == 1 && params[5] == 2)
4591           s2 = "ExpandStr";
4592         if (params[4] == 3 && params[5] == 7)
4593           s2 = "MultiStr"; // v3.02+
4594         if (s2)
4595           s += s2;
4596         AddRegRoot(params[0]);
4597         AddParams(params + 1, 2); // keyName, valueName
4598         if (params[4] != 3)
4599           AddParam(params[3]); // value
4600         else
4601         {
4602           // Binary data.
4603           Space();
4604           UInt32 offset = params[3];
4605           bool isSupported = false;
4606           if (AfterHeaderSize >= 4
4607               && bhData.Offset <= AfterHeaderSize - 4
4608               && offset <= AfterHeaderSize - 4 - bhData.Offset)
4609           {
4610             // we support it for solid archives.
4611             const Byte *p2 = _afterHeader + bhData.Offset + offset;
4612             UInt32 size = Get32(p2);
4613             if (size <= AfterHeaderSize - 4 - bhData.Offset - offset)
4614             {
4615               for (UInt32 i = 0; i < size; i++)
4616               {
4617                 Byte b = (p2 + 4)[i];
4618                 unsigned t;
4619                 t = (b >> 4); s += (char)(((t < 10) ? ('0' + t) : ('A' + (t - 10))));
4620                 t = (b & 15); s += (char)(((t < 10) ? ('0' + t) : ('A' + (t - 10))));
4621               }
4622               isSupported = true;
4623             }
4624           }
4625           if (!isSupported)
4626           {
4627             // we must read from file here;
4628             s += "data[";
4629             Add_UInt(offset);
4630             s += " ... ]";
4631             s += "  ; !!! Unsupported";
4632           }
4633         }
4634         break;
4635       }
4636 
4637       case EW_READREGSTR:
4638       {
4639         s += (params[4] == 1) ? "DWORD" : "Str";
4640         AddParam_Var(params[0]);
4641         AddRegRoot(params[1]);
4642         AddParams(params + 2, 2);
4643         break;
4644       }
4645 
4646       case EW_REGENUM:
4647       {
4648         s += (params[4] != 0) ? "Key" : "Value";
4649         AddParam_Var(params[0]);
4650         AddRegRoot(params[1]);
4651         AddParams(params + 2, 2);
4652         break;
4653       }
4654 
4655       case EW_FCLOSE:
4656       case EW_FINDCLOSE:
4657       {
4658         AddParam_Var(params[0]);
4659         break;
4660       }
4661 
4662     #endif
4663 
4664       case EW_FOPEN:
4665       {
4666         /*
4667           the pattern for empty files is following:
4668           FileOpen $0 "file_name" w
4669           FileClose $0
4670         */
4671 
4672         const UInt32 acc = params[1]; // dwDesiredAccess
4673         const UInt32 creat = params[2]; // dwCreationDisposition
4674         if (creat == Z7_NSIS_WIN_CREATE_ALWAYS && acc == Z7_NSIS_WIN_GENERIC_WRITE)
4675         {
4676           if (kkk + 1 < bh.Num)
4677           if (Get32(p + kCmdSize) == EW_FCLOSE)
4678           if (Get32(p + kCmdSize + 4) == params[0])
4679           {
4680             CItem &item = Items.AddNew();
4681             item.IsEmptyFile = true;
4682             SetItemName(item, params[3]);
4683           }
4684         }
4685 
4686       #ifdef NSIS_SCRIPT
4687 
4688         AddParam_Var(params[0]);
4689         AddParam(params[3]);
4690         if (acc == 0 && creat == 0)
4691           break;
4692         char cc = 0;
4693              if (creat == Z7_NSIS_WIN_OPEN_EXISTING && acc == Z7_NSIS_WIN_GENERIC_READ)
4694           cc = 'r';
4695         else if (creat == Z7_NSIS_WIN_CREATE_ALWAYS && acc == Z7_NSIS_WIN_GENERIC_WRITE)
4696           cc = 'w';
4697         else if (creat == Z7_NSIS_WIN_OPEN_ALWAYS && (acc == (Z7_NSIS_WIN_GENERIC_WRITE | Z7_NSIS_WIN_GENERIC_READ)))
4698           cc = 'a';
4699         // cc = 0;
4700         if (cc != 0)
4701         {
4702           Space();
4703           s += cc;
4704           break;
4705         }
4706 
4707         if (acc & Z7_NSIS_WIN_GENERIC_READ)     s += " GENERIC_READ";
4708         if (acc & Z7_NSIS_WIN_GENERIC_WRITE)    s += " GENERIC_WRITE";
4709         if (acc & Z7_NSIS_WIN_GENERIC_EXECUTE)  s += " GENERIC_EXECUTE";
4710         if (acc & Z7_NSIS_WIN_GENERIC_ALL)      s += " GENERIC_ALL";
4711 
4712         const char *s2 = NULL;
4713         switch (creat)
4714         {
4715           case Z7_NSIS_WIN_CREATE_NEW:        s2 = "CREATE_NEW"; break;
4716           case Z7_NSIS_WIN_CREATE_ALWAYS:     s2 = "CREATE_ALWAYS"; break;
4717           case Z7_NSIS_WIN_OPEN_EXISTING:     s2 = "OPEN_EXISTING"; break;
4718           case Z7_NSIS_WIN_OPEN_ALWAYS:       s2 = "OPEN_ALWAYS"; break;
4719           case Z7_NSIS_WIN_TRUNCATE_EXISTING: s2 = "TRUNCATE_EXISTING"; break;
4720         }
4721         Space();
4722         if (s2)
4723           s += s2;
4724         else
4725           Add_UInt(creat);
4726       #endif
4727 
4728         break;
4729       }
4730 
4731     #ifdef NSIS_SCRIPT
4732 
4733       case EW_FPUTS:
4734       case EW_FPUTWS:
4735       {
4736         if (commandId == EW_FPUTWS)
4737           s += (params[2] == 0) ? "UTF16LE" : "Word";
4738         else if (params[2] != 0)
4739           s += "Byte";
4740         if (params[2] == 0 && params[3])
4741           s += " /BOM"; // v3.0b3+
4742         AddParam_Var(params[0]);
4743         AddParam(params[1]);
4744         break;
4745       }
4746 
4747       case EW_FGETS:
4748       case EW_FGETWS:
4749       {
4750         if (commandId == EW_FPUTWS)
4751           s += (params[3] == 0) ? "UTF16LE" : "Word";
4752         if (params[3] != 0)
4753           s += "Byte";
4754         AddParam_Var(params[0]);
4755         AddParam_Var(params[1]);
4756         AString maxLenStr;
4757         ReadString2(maxLenStr, params[2]);
4758         UInt32 maxLen;
4759         if (StringToUInt32(maxLenStr, maxLen))
4760         {
4761           if (maxLen == 1 && params[3] != 0)
4762             break;
4763           if (maxLen == 1023 && params[3] == 0) // NSIS_MAX_STRLEN - 1; can be other value!!
4764             break;
4765         }
4766         SpaceQuStr(maxLenStr);
4767         break;
4768       }
4769 
4770       case EW_FSEEK:
4771       {
4772         AddParam_Var(params[0]);
4773         AddParam(params[2]);
4774         if (params[3] == 1) s += " CUR"; // FILE_CURRENT
4775         if (params[3] == 2) s += " END"; // FILE_END
4776         if ((Int32)params[1] >= 0)
4777         {
4778           if (params[3] == 0) s += " SET"; // FILE_BEGIN
4779           AddParam_Var(params[1]);
4780         }
4781         break;
4782       }
4783 
4784       case EW_FINDNEXT:
4785       {
4786         AddParam_Var(params[1]);
4787         AddParam_Var(params[0]);
4788         break;
4789       }
4790 
4791       case EW_FINDFIRST:
4792       {
4793         AddParam_Var(params[1]);
4794         AddParam_Var(params[0]);
4795         AddParam(params[2]);
4796         break;
4797       }
4798 
4799       case EW_LOG:
4800       {
4801         if (params[0] != 0)
4802         {
4803           s += "Set ";
4804           s += (params[1] == 0) ? "off" : "on";
4805         }
4806         else
4807         {
4808           s += "Text";
4809           AddParam(params[1]);
4810         }
4811         break;
4812       }
4813 
4814       case EW_SECTIONSET:
4815       {
4816         if ((Int32)params[2] >= 0)
4817         {
4818           s += "Get";
4819           Add_SectOp(params[2]);
4820           AddParam(params[0]);
4821           AddParam_Var(params[1]);
4822         }
4823         else
4824         {
4825           s += "Set";
4826           const UInt32 t = (UInt32)(-(Int32)params[2] - 1);
4827           Add_SectOp(t);
4828           AddParam(params[0]);
4829           AddParam(params[t == 0 ? 4 : 1]);
4830 
4831           // params[3] != 0 means call SectionFlagsChanged in installer
4832           // used by SECTIONSETFLAGS command
4833         }
4834         break;
4835       }
4836 
4837       case EW_INSTTYPESET:
4838       {
4839         unsigned numQwParams = 0;
4840         const char *s2;
4841         if (params[3] == 0)
4842         {
4843           if (params[2] == 0)
4844           {
4845             s2 = "InstTypeGetText";
4846             numQwParams = 1;
4847           }
4848           else
4849           {
4850             s2 = "InstTypeSetText";
4851             numQwParams = 2;
4852           }
4853         }
4854         else
4855         {
4856           if (params[2] == 0)
4857             s2 = "GetCurInstType";
4858           else
4859           {
4860             s2 = "SetCurInstType";
4861             numQwParams = 1;
4862           }
4863         }
4864         s += s2;
4865         AddParams(params, numQwParams);
4866         if (params[2] == 0)
4867           AddParam_Var(params[1]);
4868         break;
4869       }
4870 
4871       case EW_GETOSINFO:
4872       {
4873         if (IsNsis3_OrHigher())
4874         {
4875           // v3.06+
4876           if (params[3] == 0) // GETOSINFO_KNOWNFOLDER
4877           {
4878             s += "GetKnownFolderPath";
4879             AddParam_Var(params[1]);
4880             AddParam(params[2]);
4881             break;
4882           }
4883           else if (params[3] == 1) // GETOSINFO_READMEMORY
4884           {
4885             s += "ReadMemory";
4886             AddParam_Var(params[1]);
4887             AddParam(params[2]);
4888             AddParam(params[4]);
4889             // if (params[2] == "0") AddCommentAndString("GetWinVer");
4890           }
4891           else
4892             s += "GetOsInfo";
4893           break;
4894         }
4895         s += "GetLabelAddr"; //  before v3.06+
4896         break;
4897       }
4898 
4899       case EW_LOCKWINDOW:
4900       {
4901         s += (params[0] == 0) ? " on" : " off";
4902         break;
4903       }
4904 
4905       case EW_FINDPROC:
4906       {
4907         AddParam_Var(params[0]);
4908         AddParam(params[1]);
4909         break;
4910       }
4911 
4912       default:
4913       {
4914         numSkipParams = 0;
4915       }
4916       #endif
4917     }
4918 
4919     #ifdef NSIS_SCRIPT
4920 
4921     unsigned numParams = kNumCommandParams;
4922 
4923     for (; numParams > 0; numParams--)
4924       if (params[numParams - 1] != 0)
4925         break;
4926 
4927     if (numParams > numSkipParams)
4928     {
4929       s += " ; !!!! Unknown Params: ";
4930       unsigned i;
4931       for (i = 0; i < numParams; i++)
4932         AddParam(params[i]);
4933 
4934       s += "   ;";
4935 
4936       for (i = 0; i < numParams; i++)
4937       {
4938         Space();
4939         UInt32 v = params[i];
4940         if (v > 0xFFF00000)
4941           Add_SignedInt(s, (Int32)v);
4942         else
4943           Add_UInt(v);
4944       }
4945     }
4946 
4947     NewLine();
4948 
4949     #endif
4950   }
4951 
4952   #ifdef NSIS_SCRIPT
4953 
4954   if (sectionIsOpen)
4955   {
4956     if (curSectionIndex < bhSections.Num)
4957     {
4958       const CSection &sect = Sections[curSectionIndex];
4959       if (sect.StartCmdIndex + sect.NumCommands + 1 == kkk)
4960       {
4961         PrintSectionEnd();
4962         sectionIsOpen = false;
4963         // lastSectionEndCmd = kkk;
4964         curSectionIndex++;
4965       }
4966     }
4967   }
4968 
4969   while (curSectionIndex < bhSections.Num)
4970   {
4971     const CSection &sect = Sections[curSectionIndex];
4972     if (sectionIsOpen)
4973     {
4974       if (sect.StartCmdIndex + sect.NumCommands != kkk)
4975         AddErrorLF("SECTION ERROR");
4976       PrintSectionEnd();
4977       sectionIsOpen = false;
4978       curSectionIndex++;
4979     }
4980     else
4981     {
4982       if (PrintSectionBegin(sect, curSectionIndex))
4983         curSectionIndex++;
4984       else
4985         sectionIsOpen = true;
4986     }
4987   }
4988 
4989   #endif
4990 
4991   return S_OK;
4992 }
4993 
CompareItems(void * const * p1,void * const * p2,void * param)4994 static int CompareItems(void *const *p1, void *const *p2, void *param)
4995 {
4996   const CItem &i1 = **(const CItem *const *)p1;
4997   const CItem &i2 = **(const CItem *const *)p2;
4998   RINOZ(MyCompare(i1.Pos, i2.Pos))
4999 
5000   /* In another code we check CItem::Pos after each solid item.
5001      So here we place empty files before all non empty files */
5002   if (i1.IsEmptyFile)
5003   {
5004     if (!i2.IsEmptyFile)
5005       return -1;
5006   }
5007   else if (i2.IsEmptyFile)
5008     return 1;
5009 
5010   const CInArchive *inArchive = (const CInArchive *)param;
5011   if (inArchive->IsUnicode)
5012   {
5013     if (i1.Prefix != i2.Prefix)
5014     {
5015       if (i1.Prefix < 0) return -1;
5016       if (i2.Prefix < 0) return 1;
5017       RINOZ(
5018           inArchive->UPrefixes[i1.Prefix].Compare(
5019           inArchive->UPrefixes[i2.Prefix]))
5020     }
5021     RINOZ(i1.NameU.Compare(i2.NameU))
5022   }
5023   else
5024   {
5025     if (i1.Prefix != i2.Prefix)
5026     {
5027       if (i1.Prefix < 0) return -1;
5028       if (i2.Prefix < 0) return 1;
5029       RINOZ(strcmp(
5030           inArchive->APrefixes[i1.Prefix],
5031           inArchive->APrefixes[i2.Prefix]))
5032     }
5033     RINOZ(strcmp(i1.NameA, i2.NameA))
5034   }
5035   return 0;
5036 }
5037 
SortItems()5038 HRESULT CInArchive::SortItems()
5039 {
5040   {
5041     Items.Sort(CompareItems, (void *)this);
5042     unsigned i;
5043 
5044     for (i = 0; i + 1 < Items.Size(); i++)
5045     {
5046       const CItem &i1 = Items[i];
5047       if (i1.IsEmptyFile)
5048         continue;
5049       const CItem &i2 = Items[i + 1];
5050       if (i1.Pos != i2.Pos)
5051         continue;
5052 
5053       if (IsUnicode)
5054       {
5055         if (i1.NameU != i2.NameU) continue;
5056         if (i1.Prefix != i2.Prefix)
5057         {
5058           if (i1.Prefix < 0 || i2.Prefix < 0) continue;
5059           if (UPrefixes[i1.Prefix] != UPrefixes[i2.Prefix]) continue;
5060         }
5061       }
5062       else
5063       {
5064         if (i1.NameA != i2.NameA) continue;
5065         if (i1.Prefix != i2.Prefix)
5066         {
5067           if (i1.Prefix < 0 || i2.Prefix < 0) continue;
5068           if (APrefixes[i1.Prefix] != APrefixes[i2.Prefix]) continue;
5069         }
5070       }
5071       Items.Delete(i + 1);
5072       i--;
5073     }
5074 
5075     for (i = 0; i < Items.Size(); i++)
5076     {
5077       CItem &item = Items[i];
5078       if (item.IsEmptyFile)
5079         continue;
5080       const UInt32 curPos = item.Pos + 4;
5081       for (unsigned nextIndex = i + 1; nextIndex < Items.Size(); nextIndex++)
5082       {
5083         const CItem &nextItem = Items[nextIndex];
5084         // if (nextItem.IsEmptyFile) continue;
5085         const UInt32 nextPos = nextItem.Pos;
5086         if (curPos <= nextPos)
5087         {
5088           item.EstimatedSize_Defined = true;
5089           item.EstimatedSize = nextPos - curPos;
5090           break;
5091         }
5092       }
5093     }
5094 
5095     if (!IsSolid)
5096     {
5097       for (i = 0; i < Items.Size(); i++)
5098       {
5099         CItem &item = Items[i];
5100         if (item.IsEmptyFile)
5101           continue;
5102         RINOK(SeekToNonSolidItem(i))
5103         const UInt32 kSigSize = 4 + 1 + 1 + 4; // size,[flag],prop,dict
5104         BYTE sig[kSigSize];
5105         size_t processedSize = kSigSize;
5106         RINOK(ReadStream(_stream, sig, &processedSize))
5107         if (processedSize < 4)
5108           return S_FALSE;
5109         UInt32 size = Get32(sig);
5110         if ((size & kMask_IsCompressed) != 0)
5111         {
5112           item.IsCompressed = true;
5113           size &= ~kMask_IsCompressed;
5114           if (Method == NMethodType::kLZMA)
5115           {
5116             if (processedSize < 9)
5117               return S_FALSE;
5118             /*
5119             if (FilterFlag)
5120               item.UseFilter = (sig[4] != 0);
5121             */
5122             item.DictionarySize = Get32(sig + 4 + 1 + (FilterFlag ? 1 : 0));
5123           }
5124         }
5125         else
5126         {
5127           item.IsCompressed = false;
5128           item.Size = size;
5129           item.Size_Defined = true;
5130         }
5131         item.CompressedSize = size;
5132         item.CompressedSize_Defined = true;
5133       }
5134     }
5135   }
5136   return S_OK;
5137 }
5138 
5139 #ifdef NSIS_SCRIPT
5140 // Flags for common_header.flags
5141 // #define CH_FLAGS_DETAILS_SHOWDETAILS 1
5142 // #define CH_FLAGS_DETAILS_NEVERSHOW 2
5143 #define CH_FLAGS_PROGRESS_COLORED 4
5144 #define CH_FLAGS_SILENT 8
5145 #define CH_FLAGS_SILENT_LOG 16
5146 #define CH_FLAGS_AUTO_CLOSE 32
5147 // #define CH_FLAGS_DIR_NO_SHOW 64  // unused now
5148 #define CH_FLAGS_NO_ROOT_DIR 128
5149 #define CH_FLAGS_COMP_ONLY_ON_CUSTOM 256
5150 #define CH_FLAGS_NO_CUSTOM 512
5151 
5152 static const char * const k_PostStrings[] =
5153 {
5154     "install_directory_auto_append"
5155   , "uninstchild"     // NSIS 2.25+, used by uninstaller:
5156   , "uninstcmd"       // NSIS 2.25+, used by uninstaller:
5157   , "wininit"         // NSIS 2.25+, used by move file on reboot
5158 };
5159 #endif
5160 
5161 
Parse(const Byte * p,unsigned bhoSize)5162 void CBlockHeader::Parse(const Byte *p, unsigned bhoSize)
5163 {
5164   if (bhoSize == 12)
5165   {
5166     // UInt64 a = GetUi64(p);
5167     if (GetUi32(p + 4) != 0)
5168       throw 1;
5169   }
5170   Offset = GetUi32(p);
5171   Num = GetUi32(p + bhoSize - 4);
5172 }
5173 
5174 #define PARSE_BH(k, bh) bh.Parse (p1 + 4 + bhoSize * k, bhoSize)
5175 
5176 
Parse()5177 HRESULT CInArchive::Parse()
5178 {
5179   // UInt32 offset = ReadUInt32();
5180   // ???? offset == FirstHeader.HeaderSize
5181   const Byte * const p1 = _data;
5182 
5183   if (_size < 4 + 12 * 8)
5184     Is64Bit = false;
5185   else
5186   {
5187     Is64Bit = true;
5188     // here we test high 32-bit of possible UInt64 CBlockHeader::Offset field
5189     for (int k = 0; k < 8; k++)
5190       if (GetUi32(p1 + 4 + 12 * k + 4) != 0)
5191         Is64Bit = false;
5192   }
5193 
5194   const unsigned bhoSize = Is64Bit ? 12 : 8;
5195   if (_size < 4 + bhoSize * 8)
5196     return S_FALSE;
5197 
5198   CBlockHeader bhEntries, bhStrings, bhLangTables;
5199 
5200   PARSE_BH (2, bhEntries);
5201   PARSE_BH (3, bhStrings);
5202   PARSE_BH (4, bhLangTables);
5203 
5204   #ifdef NSIS_SCRIPT
5205 
5206   CBlockHeader bhFont;
5207   PARSE_BH (0, bhPages);
5208   PARSE_BH (1, bhSections);
5209   PARSE_BH (5, bhCtlColors);
5210   PARSE_BH (6, bhFont);
5211   PARSE_BH (7, bhData);
5212 
5213   #endif
5214 
5215   _stringsPos = bhStrings.Offset;
5216   if (_stringsPos > _size
5217       || bhLangTables.Offset > _size
5218       || bhEntries.Offset > _size)
5219     return S_FALSE;
5220   {
5221     if (bhLangTables.Offset < bhStrings.Offset)
5222       return S_FALSE;
5223     const UInt32 stringTableSize = bhLangTables.Offset - bhStrings.Offset;
5224     if (stringTableSize < 2)
5225       return S_FALSE;
5226     const Byte *strData = _data + _stringsPos;
5227     if (strData[stringTableSize - 1] != 0)
5228       return S_FALSE;
5229     IsUnicode = (Get16(strData) == 0);
5230     NumStringChars = stringTableSize;
5231     if (IsUnicode)
5232     {
5233       if ((stringTableSize & 1) != 0)
5234         return S_FALSE;
5235       NumStringChars >>= 1;
5236       if (strData[stringTableSize - 2] != 0)
5237         return S_FALSE;
5238     }
5239   }
5240 
5241   if (bhEntries.Num > (1 << 25))
5242     return S_FALSE;
5243   if (bhEntries.Num * kCmdSize > _size - bhEntries.Offset)
5244     return S_FALSE;
5245 
5246   DetectNsisType(bhEntries, _data + bhEntries.Offset);
5247 
5248   Decoder.IsNsisDeflate = (NsisType != k_NsisType_Nsis3);
5249 
5250   // some NSIS files (that are not detected as k_NsisType_Nsis3)
5251   // use original (non-NSIS) Deflate
5252   // How to detect these cases?
5253 
5254   // Decoder.IsNsisDeflate = false;
5255 
5256 
5257   #ifdef NSIS_SCRIPT
5258 
5259   {
5260     AddCommentAndString("NSIS script");
5261     if (IsUnicode)
5262       Script += " (UTF-8)";
5263     Space();
5264     Script += GetFormatDescription();
5265     AddLF();
5266   }
5267   {
5268     AddCommentAndString(IsInstaller ? "Install" : "Uninstall");
5269     AddLF();
5270   }
5271 
5272   AddLF();
5273   if (Is64Bit)
5274     AddStringLF("Target AMD64-Unicode"); // TODO: Read PE machine type and use the correct CPU type
5275   else if (IsUnicode)
5276     AddStringLF("Unicode true");
5277   else if (IsNsis3_OrHigher())
5278     AddStringLF("Unicode false"); // Unicode is the default in 3.07+
5279 
5280   if (Method != NMethodType::kCopy)
5281   {
5282     const char *m = NULL;
5283     switch ((int)Method)
5284     {
5285       case NMethodType::kDeflate: m = "zlib"; break;
5286       case NMethodType::kBZip2: m = "bzip2"; break;
5287       case NMethodType::kLZMA: m = "lzma"; break;
5288       default: break;
5289     }
5290     Script += "SetCompressor";
5291     if (IsSolid)
5292       Script += " /SOLID";
5293     if (m)
5294     {
5295       Space();
5296       Script += m;
5297     }
5298     AddLF();
5299   }
5300   if (Method == NMethodType::kLZMA)
5301   {
5302     // if (DictionarySize != (8 << 20))
5303     {
5304       Script += "SetCompressorDictSize";
5305       AddParam_UInt(DictionarySize >> 20);
5306       AddLF();
5307     }
5308   }
5309 
5310   Separator();
5311   PrintNumComment("HEADER SIZE", FirstHeader.HeaderSize);
5312   // if (bhPages.Offset != 300 && bhPages.Offset != 288)
5313   if (bhPages.Offset != 0)
5314   {
5315     PrintNumComment("START HEADER SIZE", bhPages.Offset);
5316   }
5317 
5318   if (bhSections.Num > 0)
5319   {
5320     if (bhEntries.Offset < bhSections.Offset)
5321       return S_FALSE;
5322     SectionSize = (bhEntries.Offset - bhSections.Offset) / bhSections.Num;
5323     if (bhSections.Offset + bhSections.Num * SectionSize != bhEntries.Offset)
5324       return S_FALSE;
5325     if (SectionSize < kSectionSize_base)
5326       return S_FALSE;
5327     UInt32 maxStringLen = SectionSize - kSectionSize_base;
5328     if (IsUnicode)
5329     {
5330       if ((maxStringLen & 1) != 0)
5331         return S_FALSE;
5332       maxStringLen >>= 1;
5333     }
5334     // if (maxStringLen != 1024)
5335     {
5336       if (maxStringLen == 0)
5337         PrintNumComment("SECTION SIZE", SectionSize);
5338       else
5339         PrintNumComment("MAX STRING LENGTH", maxStringLen);
5340     }
5341   }
5342 
5343   PrintNumComment("STRING CHARS", NumStringChars);
5344   // PrintNumComment("LANG TABLE SIZE", bhCtlColors.Offset - bhLangTables.Offset);
5345 
5346   if (bhCtlColors.Offset > _size)
5347     AddErrorLF("Bad COLORS TABLE");
5348   // PrintNumComment("COLORS TABLE SIZE", bhFont.Offset - bhCtlColors.Offset);
5349   if (bhCtlColors.Num != 0)
5350     PrintNumComment("COLORS Num", bhCtlColors.Num);
5351 
5352   // bhData uses offset in _afterHeader (not in _data)
5353   // PrintNumComment("FONT TABLE SIZE", bhData.Offset - bhFont.Offset);
5354   if (bhFont.Num != 0)
5355     PrintNumComment("FONTS Num", bhFont.Num);
5356 
5357   // PrintNumComment("DATA SIZE", FirstHeader.HeaderSize - bhData.Offset);
5358   if (bhData.Num != 0)
5359     PrintNumComment("DATA NUM", bhData.Num);
5360 
5361   AddLF();
5362 
5363   AddStringLF("OutFile [NSIS].exe");
5364   AddStringLF("!include WinMessages.nsh");
5365 
5366   AddLF();
5367 
5368   strUsed.Alloc(NumStringChars);
5369   memset(strUsed, 0, NumStringChars);
5370 
5371   {
5372     UInt32 ehFlags = Get32(p1);
5373     UInt32 showDetails = ehFlags & 3;// CH_FLAGS_DETAILS_SHOWDETAILS & CH_FLAGS_DETAILS_NEVERSHOW;
5374     if (showDetails >= 1 && showDetails <= 2)
5375     {
5376       Script += IsInstaller ? "ShowInstDetails" : "ShowUninstDetails";
5377       Script += (showDetails == 1) ? " show" : " nevershow";
5378       AddLF();
5379     }
5380     if (ehFlags & CH_FLAGS_PROGRESS_COLORED) AddStringLF("InstProgressFlags colored" );
5381     if ((ehFlags & (CH_FLAGS_SILENT | CH_FLAGS_SILENT_LOG)) != 0)
5382     {
5383       Script += IsInstaller ? "SilentInstall " : "SilentUnInstall ";
5384       Script += (ehFlags & CH_FLAGS_SILENT_LOG) ? "silentlog" : "silent";
5385       AddLF();
5386     }
5387     if (ehFlags & CH_FLAGS_AUTO_CLOSE) AddStringLF("AutoCloseWindow true");
5388     if ((ehFlags & CH_FLAGS_NO_ROOT_DIR) == 0) AddStringLF("AllowRootDirInstall true");
5389     if (ehFlags & CH_FLAGS_NO_CUSTOM) AddStringLF("InstType /NOCUSTOM");
5390     if (ehFlags & CH_FLAGS_COMP_ONLY_ON_CUSTOM) AddStringLF("InstType /COMPONENTSONLYONCUSTOM");
5391   }
5392 
5393   // Separator();
5394   // AddLF();
5395 
5396   Int32 licenseLangIndex = -1;
5397   {
5398     const Byte *pp = _data + bhPages.Offset;
5399 
5400     for (UInt32 pageIndex = 0; pageIndex < bhPages.Num; pageIndex++, pp += kPageSize)
5401     {
5402       UInt32 wndProcID = Get32(pp + 4);
5403       UInt32 param1 = Get32(pp + 44 + 4 * 1);
5404       if (wndProcID != PWP_LICENSE || param1 == 0)
5405         continue;
5406       if ((Int32)param1 < 0)
5407         licenseLangIndex = - ((Int32)param1 + 1);
5408       else
5409         noParseStringIndexes.AddToUniqueSorted(param1);
5410     }
5411   }
5412 
5413   unsigned paramsOffset;
5414   {
5415     unsigned numBhs = 8;
5416     // probably its for old NSIS?
5417     if (bhoSize == 8 && bhPages.Offset == 276)
5418       numBhs = 7;
5419     paramsOffset = 4 + bhoSize * numBhs;
5420   }
5421 
5422   const Byte *p2 = p1 + paramsOffset;
5423 
5424   {
5425     UInt32 rootKey = Get32(p2); // (rootKey = -1) in uninstaller by default (the bug in NSIS)
5426     UInt32 subKey = Get32(p2 + 4);
5427     UInt32 value = Get32(p2 + 8);
5428     if ((rootKey != 0 && rootKey != (UInt32)(Int32)-1) || subKey != 0 || value != 0)
5429     {
5430       Script += "InstallDirRegKey";
5431       AddRegRoot(rootKey);
5432       AddParam(subKey);
5433       AddParam(value);
5434       AddLF();
5435     }
5436   }
5437 
5438 
5439   {
5440     UInt32 bg_color1 = Get32(p2 + 12);
5441     UInt32 bg_color2 = Get32(p2 + 16);
5442     UInt32 bg_textcolor = Get32(p2 + 20);
5443     if (bg_color1 != (UInt32)(Int32)-1 || bg_color2 != (UInt32)(Int32)-1 || bg_textcolor != (UInt32)(Int32)-1)
5444     {
5445       Script += "BGGradient";
5446       if (bg_color1 != 0 || bg_color2 != 0xFF0000 || bg_textcolor != (UInt32)(Int32)-1)
5447       {
5448         Add_ColorParam(bg_color1);
5449         Add_ColorParam(bg_color2);
5450         if (bg_textcolor != (UInt32)(Int32)-1)
5451           Add_ColorParam(bg_textcolor);
5452       }
5453       AddLF();
5454     }
5455   }
5456 
5457   {
5458     UInt32 lb_bg = Get32(p2 + 24);
5459     UInt32 lb_fg = Get32(p2 + 28);
5460     if ((lb_bg != (UInt32)(Int32)-1 || lb_fg != (UInt32)(Int32)-1) &&
5461       (lb_bg != 0 || lb_fg != 0xFF00))
5462     {
5463       Script += "InstallColors";
5464       Add_ColorParam(lb_fg);
5465       Add_ColorParam(lb_bg);
5466       AddLF();
5467     }
5468   }
5469 
5470   UInt32 license_bg = Get32(p2 + 36);
5471   if (license_bg != (UInt32)(Int32)-1 &&
5472       license_bg != (UInt32)(Int32)-15) // COLOR_BTNFACE
5473   {
5474     Script += "LicenseBkColor";
5475     if ((Int32)license_bg == -5)  // COLOR_WINDOW
5476       Script += " /windows";
5477     /*
5478     else if ((Int32)license_bg == -15)
5479       Script += " /grey";
5480     */
5481     else
5482       Add_ColorParam(license_bg);
5483     AddLF();
5484   }
5485 
5486   if (bhLangTables.Num > 0)
5487   {
5488     const UInt32 langtable_size = Get32(p2 + 32);
5489 
5490     if (langtable_size == (UInt32)(Int32)-1)
5491       return E_NOTIMPL; // maybe it's old NSIS archive()
5492 
5493     if (langtable_size < 10)
5494       return S_FALSE;
5495     if (bhLangTables.Num > (_size - bhLangTables.Offset) / langtable_size)
5496       return S_FALSE;
5497 
5498     const UInt32 numStrings = (langtable_size - 10) / 4;
5499     _numLangStrings = numStrings;
5500     AddLF();
5501     Separator();
5502     PrintNumComment("LANG TABLES", bhLangTables.Num);
5503     PrintNumComment("LANG STRINGS", numStrings);
5504     AddLF();
5505 
5506     if (licenseLangIndex >= 0 && (unsigned)licenseLangIndex < numStrings)
5507     {
5508       for (UInt32 i = 0; i < bhLangTables.Num; i++)
5509       {
5510         const Byte * const p = _data + bhLangTables.Offset + langtable_size * i;
5511         const UInt16 langID = Get16(p);
5512         UInt32 val = Get32(p + 10 + (UInt32)licenseLangIndex * 4);
5513         if (val != 0)
5514         {
5515           Script += "LicenseLangString ";
5516           Add_LangStr_Simple((UInt32)licenseLangIndex);
5517           AddParam_UInt(langID);
5518           AddLicense(val, langID);
5519           noParseStringIndexes.AddToUniqueSorted(val);
5520           NewLine();
5521         }
5522       }
5523       AddLF();
5524     }
5525 
5526     UInt32 names[3] = { 0 };
5527 
5528     UInt32 i;
5529     for (i = 0; i < bhLangTables.Num; i++)
5530     {
5531       const Byte * const p = _data + bhLangTables.Offset + langtable_size * i;
5532       const UInt16 langID = Get16(p);
5533       if (i == 0 || langID == 1033)
5534         _mainLang = p + 10;
5535       for (unsigned k = 0; k < Z7_ARRAY_SIZE(names) && k < numStrings; k++)
5536       {
5537         UInt32 v = Get32(p + 10 + k * 4);
5538         if (v != 0 && (langID == 1033 || names[k] == 0))
5539           names[k] = v;
5540       }
5541     }
5542 
5543     const UInt32 name = names[2];
5544     if (name != 0)
5545     {
5546       Script += "Name";
5547       AddParam(name);
5548       NewLine();
5549 
5550       ReadString2(Name, name);
5551     }
5552 
5553     /*
5554     const UInt32 caption = names[1];
5555     if (caption != 0)
5556     {
5557       Script += "Caption";
5558       AddParam(caption);
5559       NewLine();
5560     }
5561     */
5562 
5563     const UInt32 brandingText = names[0];
5564     if (brandingText != 0)
5565     {
5566       Script += "BrandingText";
5567       AddParam(brandingText);
5568       NewLine();
5569 
5570       ReadString2(BrandingText, brandingText);
5571     }
5572 
5573     for (i = 0; i < bhLangTables.Num; i++)
5574     {
5575       const Byte * const p = _data + bhLangTables.Offset + langtable_size * i;
5576       const UInt16 langID = Get16(p);
5577 
5578       AddLF();
5579       AddCommentAndString("LANG:");
5580       AddParam_UInt(langID);
5581       /*
5582       Script += " (";
5583       LangId_To_String(Script, langID);
5584       Script += ')';
5585       */
5586       AddLF();
5587       // UInt32 dlg_offset = Get32(p + 2);
5588       // UInt32 g_exec_flags_rtl = Get32(p + 6);
5589 
5590 
5591       for (UInt32 j = 0; j < numStrings; j++)
5592       {
5593         UInt32 val = Get32(p + 10 + j * 4);
5594         if (val != 0)
5595         {
5596           if ((Int32)j != licenseLangIndex)
5597           {
5598             Script += "LangString ";
5599             Add_LangStr_Simple(j);
5600             AddParam_UInt(langID);
5601             AddParam(val);
5602             AddLF();
5603           }
5604         }
5605       }
5606       AddLF();
5607     }
5608     ClearLangComment();
5609   }
5610 
5611   {
5612     unsigned numInternalVars = GET_NUM_INTERNAL_VARS;
5613     UInt32 numUsedVars = GetNumUsedVars();
5614     if (numUsedVars > numInternalVars)
5615     {
5616       Separator();
5617       PrintNumComment("VARIABLES", numUsedVars - numInternalVars);
5618       AddLF();
5619       AString temp;
5620       for (UInt32 i = numInternalVars; i < numUsedVars; i++)
5621       {
5622         Script += "Var ";
5623         temp.Empty();
5624         GetVar2(temp, i);
5625         AddStringLF(temp);
5626       }
5627       AddLF();
5628     }
5629   }
5630 
5631   onFuncOffset = paramsOffset + 40;
5632   numOnFunc = Z7_ARRAY_SIZE(kOnFunc);
5633   if (bhPages.Offset == 276)
5634     numOnFunc--;
5635   p2 += 40 + numOnFunc * 4;
5636 
5637   #define NSIS_MAX_INST_TYPES 32
5638 
5639   AddLF();
5640 
5641   UInt32 i;
5642   for (i = 0; i < NSIS_MAX_INST_TYPES + 1; i++, p2 += 4)
5643   {
5644     UInt32 instType = Get32(p2);
5645     if (instType != 0)
5646     {
5647       Script += "InstType";
5648       AString s2;
5649       if (!IsInstaller)
5650         s2 += "un.";
5651       ReadString2(s2, instType);
5652       SpaceQuStr(s2);
5653       NewLine();
5654     }
5655   }
5656 
5657   {
5658     UInt32 installDir = Get32(p2);
5659     p2 += 4;
5660     if (installDir != 0)
5661     {
5662       Script += "InstallDir";
5663       AddParam(installDir);
5664       NewLine();
5665     }
5666   }
5667 
5668   if (bhPages.Offset >= 288)
5669     for (i = 0; i < 4; i++)
5670     {
5671       if (i != 0 && bhPages.Offset < 300)
5672         break;
5673       UInt32 param = Get32(p2 + 4 * i);
5674       if (param == 0 || param == (UInt32)(Int32)-1)
5675         continue;
5676 
5677       /*
5678       uninstaller:
5679       UInt32 uninstChild = Get32(p2 + 8); // "$TEMP\\$1u_.exe"
5680       UInt32 uninstCmd = Get32(p2 + 12); // "\"$TEMP\\$1u_.exe\" $0 _?=$INSTDIR\\"
5681       int str_wininit = Get32(p2 + 16); // "$WINDIR\\wininit.ini"
5682       */
5683 
5684       AddCommentAndString(k_PostStrings[i]);
5685       Script += " =";
5686       AddParam(param);
5687       NewLine();
5688     }
5689 
5690   AddLF();
5691 
5692   #endif
5693 
5694   RINOK(ReadEntries(bhEntries))
5695 
5696   #ifdef NSIS_SCRIPT
5697 
5698   Separator();
5699   AddCommentAndString("UNREFERENCED STRINGS:");
5700   AddLF();
5701   AddLF();
5702   CommentOpen();
5703 
5704   for (i = 0; i < NumStringChars;)
5705   {
5706     if (!strUsed[i] && i != 0)
5707     // Script += "!!! ";
5708     {
5709       Add_UInt(i);
5710       AddParam(i);
5711       NewLine();
5712     }
5713     if (IsUnicode)
5714       i += GetUi16Str_Len((const Byte *)_data + _stringsPos + i * 2);
5715     else
5716       i += (UInt32)strlen((const char *)(const Byte *)_data + _stringsPos + i);
5717     i++;
5718   }
5719   CommentClose();
5720   #endif
5721 
5722   return SortItems();
5723 }
5724 
IsLZMA(const Byte * p,UInt32 & dictionary)5725 static bool IsLZMA(const Byte *p, UInt32 &dictionary)
5726 {
5727   dictionary = Get32(p + 1);
5728   return (p[0] == 0x5D &&
5729       p[1] == 0x00 && p[2] == 0x00 &&
5730       p[5] == 0x00 && (p[6] & 0x80) == 0x00);
5731 }
5732 
IsLZMA(const Byte * p,UInt32 & dictionary,bool & thereIsFlag)5733 static bool IsLZMA(const Byte *p, UInt32 &dictionary, bool &thereIsFlag)
5734 {
5735   if (IsLZMA(p, dictionary))
5736   {
5737     thereIsFlag = false;
5738     return true;
5739   }
5740   if (p[0] <= 1 && IsLZMA(p + 1, dictionary))
5741   {
5742     thereIsFlag = true;
5743     return true;
5744   }
5745   return false;
5746 }
5747 
IsBZip2(const Byte * p)5748 static bool IsBZip2(const Byte *p)
5749 {
5750   return (p[0] == 0x31 && p[1] < 14);
5751 }
5752 
Open2(const Byte * sig,size_t size)5753 HRESULT CInArchive::Open2(const Byte *sig, size_t size)
5754 {
5755   const UInt32 kSigSize = 4 + 1 + 5 + 2; // size, flag, 5 - lzma props, 2 - lzma first bytes
5756   if (size < kSigSize)
5757     return S_FALSE;
5758 
5759   _headerIsCompressed = true;
5760   IsSolid = true;
5761   FilterFlag = false;
5762   UseFilter = false;
5763   DictionarySize = 1;
5764 
5765   #ifdef NSIS_SCRIPT
5766   AfterHeaderSize = 0;
5767   #endif
5768 
5769   UInt32 compressedHeaderSize = Get32(sig);
5770 
5771 
5772   /*
5773     XX XX XX XX             XX XX XX XX == FirstHeader.HeaderSize, nonsolid, uncompressed
5774     5D 00 00 dd dd 00       solid LZMA
5775     00 5D 00 00 dd dd 00    solid LZMA, empty filter (there are no such archives)
5776     01 5D 00 00 dd dd 00    solid LZMA, BCJ filter   (only 7-Zip installer used that format)
5777 
5778     SS SS SS 80 00 5D 00 00 dd dd 00     non-solid LZMA, empty filter
5779     SS SS SS 80 01 5D 00 00 dd dd 00     non-solid LZMA, BCJ filte
5780     SS SS SS 80 01 tt         non-solid BZip (tt < 14
5781     SS SS SS 80               non-solid  deflate
5782 
5783     01 tt         solid BZip (tt < 14
5784     other         solid Deflate
5785   */
5786 
5787   if (compressedHeaderSize == FirstHeader.HeaderSize)
5788   {
5789     _headerIsCompressed = false;
5790     IsSolid = false;
5791     Method = NMethodType::kCopy;
5792   }
5793   else if (IsLZMA(sig, DictionarySize, FilterFlag))
5794     Method = NMethodType::kLZMA;
5795   else if (sig[3] == 0x80)
5796   {
5797     IsSolid = false;
5798     if (IsLZMA(sig + 4, DictionarySize, FilterFlag) && sig[3] == 0x80)
5799       Method = NMethodType::kLZMA;
5800     else if (IsBZip2(sig + 4))
5801       Method = NMethodType::kBZip2;
5802     else
5803       Method = NMethodType::kDeflate;
5804   }
5805   else if (IsBZip2(sig))
5806     Method = NMethodType::kBZip2;
5807   else
5808     Method = NMethodType::kDeflate;
5809 
5810   if (IsSolid)
5811   {
5812     RINOK(SeekTo_DataStreamOffset())
5813   }
5814   else
5815   {
5816     _headerIsCompressed = ((compressedHeaderSize & kMask_IsCompressed) != 0);
5817     compressedHeaderSize &= ~kMask_IsCompressed;
5818     _nonSolidStartOffset = compressedHeaderSize;
5819     RINOK(SeekTo(DataStreamOffset + 4))
5820   }
5821 
5822   if (FirstHeader.HeaderSize == 0)
5823     return S_FALSE;
5824 
5825   _data.Alloc(FirstHeader.HeaderSize);
5826   _size = (size_t)FirstHeader.HeaderSize;
5827 
5828   Decoder.Method = Method;
5829   Decoder.FilterFlag = FilterFlag;
5830   Decoder.Solid = IsSolid;
5831 
5832   Decoder.IsNsisDeflate = true; // we need some smart check that NSIS is not NSIS3 here.
5833 
5834   Decoder.InputStream = _stream;
5835   Decoder.Buffer.Alloc(kInputBufSize);
5836   Decoder.StreamPos = 0;
5837 
5838   if (_headerIsCompressed)
5839   {
5840     RINOK(Decoder.Init(_stream, UseFilter))
5841     if (IsSolid)
5842     {
5843       size_t processedSize = 4;
5844       Byte buf[4];
5845       RINOK(Decoder.Read(buf, &processedSize))
5846       if (processedSize != 4)
5847         return S_FALSE;
5848       if (Get32((const Byte *)buf) != FirstHeader.HeaderSize)
5849         return S_FALSE;
5850     }
5851     {
5852       size_t processedSize = FirstHeader.HeaderSize;
5853       RINOK(Decoder.Read(_data, &processedSize))
5854       if (processedSize != FirstHeader.HeaderSize)
5855         return S_FALSE;
5856     }
5857 
5858     #ifdef NSIS_SCRIPT
5859     if (IsSolid)
5860     {
5861       /* we need additional bytes for data for WriteRegBin */
5862       AfterHeaderSize = (1 << 12);
5863       _afterHeader.Alloc(AfterHeaderSize);
5864       size_t processedSize = AfterHeaderSize;
5865       RINOK(Decoder.Read(_afterHeader, &processedSize))
5866       AfterHeaderSize = (UInt32)processedSize;
5867     }
5868     #endif
5869   }
5870   else
5871   {
5872     size_t processedSize = FirstHeader.HeaderSize;
5873     RINOK(ReadStream(_stream, (Byte *)_data, &processedSize))
5874     if (processedSize < FirstHeader.HeaderSize)
5875       return S_FALSE;
5876   }
5877 
5878   #ifdef NUM_SPEED_TESTS
5879   for (unsigned i = 0; i < NUM_SPEED_TESTS; i++)
5880   {
5881     RINOK(Parse());
5882     Clear2();
5883   }
5884   #endif
5885 
5886   return Parse();
5887 }
5888 
5889 /*
5890 NsisExe =
5891 {
5892   ExeStub
5893   Archive  // must start from 512 * N
5894   #ifndef NSIS_CONFIG_CRC_ANAL
5895   {
5896     Some additional data
5897   }
5898 }
5899 
5900 Archive
5901 {
5902   FirstHeader
5903   Data
5904   #ifdef NSIS_CONFIG_CRC_SUPPORT && FirstHeader.ThereIsCrc()
5905   {
5906     CRC
5907   }
5908 }
5909 
5910 FirstHeader
5911 {
5912   UInt32 Flags;
5913   Byte Signature[16];
5914   // points to the header+sections+entries+stringtable in the datablock
5915   UInt32 HeaderSize;
5916   UInt32 ArcSize;
5917 }
5918 */
5919 
5920 
5921 // ---------- PE (EXE) parsing ----------
5922 
5923 static const unsigned k_PE_StartSize = 0x40;
5924 static const unsigned k_PE_HeaderSize = 4 + 20;
5925 static const unsigned k_PE_OptHeader32_Size_MIN = 96;
5926 
CheckPeOffset(UInt32 pe)5927 static inline bool CheckPeOffset(UInt32 pe)
5928 {
5929   return (pe >= 0x40 && pe <= 0x1000 && (pe & 7) == 0);
5930 }
5931 
5932 
IsArc_Pe(const Byte * p,size_t size)5933 static bool IsArc_Pe(const Byte *p, size_t size)
5934 {
5935   if (size < 2)
5936     return false;
5937   if (p[0] != 'M' || p[1] != 'Z')
5938     return false;
5939   if (size < k_PE_StartSize)
5940     return false; // k_IsArc_Res_NEED_MORE;
5941   UInt32 pe = Get32(p + 0x3C);
5942   if (!CheckPeOffset(pe))
5943     return false;
5944   if (pe + k_PE_HeaderSize > size)
5945     return false; // k_IsArc_Res_NEED_MORE;
5946 
5947   p += pe;
5948   if (Get32(p) != 0x00004550)
5949     return false;
5950   return Get16(p + 4 + 16) >= k_PE_OptHeader32_Size_MIN;
5951 }
5952 
Open(IInStream * inStream,const UInt64 * maxCheckStartPosition)5953 HRESULT CInArchive::Open(IInStream *inStream, const UInt64 *maxCheckStartPosition)
5954 {
5955   Clear();
5956 
5957   RINOK(InStream_GetPos(inStream, StartOffset))
5958 
5959   const UInt32 kStartHeaderSize = 4 * 7;
5960   const unsigned kStep = 512; // nsis start is aligned for 512
5961   Byte buf[kStep];
5962   UInt64 pos = StartOffset;
5963   size_t bufSize = 0;
5964   UInt64 pePos = (UInt64)(Int64)-1;
5965 
5966   for (;;)
5967   {
5968     bufSize = kStep;
5969     RINOK(ReadStream(inStream, buf, &bufSize))
5970     if (bufSize < kStartHeaderSize)
5971       return S_FALSE;
5972     if (memcmp(buf + 4, kSignature, kSignatureSize) == 0)
5973       break;
5974     if (IsArc_Pe(buf, bufSize))
5975       pePos = pos;
5976     pos += kStep;
5977     UInt64 proc = pos - StartOffset;
5978     if (maxCheckStartPosition && proc > *maxCheckStartPosition)
5979     {
5980       if (pePos == 0)
5981       {
5982         if (proc > (1 << 20))
5983           return S_FALSE;
5984       }
5985       else
5986         return S_FALSE;
5987     }
5988   }
5989 
5990   if (pePos == (UInt64)(Int64)-1)
5991   {
5992     UInt64 posCur = StartOffset;
5993     for (;;)
5994     {
5995       if (posCur < kStep)
5996         break;
5997       posCur -= kStep;
5998       if (pos - posCur > (1 << 20))
5999         break;
6000       bufSize = kStep;
6001       RINOK(InStream_SeekSet(inStream, posCur))
6002       RINOK(ReadStream(inStream, buf, &bufSize))
6003       if (bufSize < kStep)
6004         break;
6005       if (IsArc_Pe(buf, bufSize))
6006       {
6007         pePos = posCur;
6008         break;
6009       }
6010     }
6011 
6012     // restore buf to nsis header
6013     bufSize = kStep;
6014     RINOK(InStream_SeekSet(inStream, pos))
6015     RINOK(ReadStream(inStream, buf, &bufSize))
6016     if (bufSize < kStartHeaderSize)
6017       return S_FALSE;
6018   }
6019 
6020   StartOffset = pos;
6021   UInt32 peSize = 0;
6022 
6023   if (pePos != (UInt64)(Int64)-1)
6024   {
6025     UInt64 peSize64 = (pos - pePos);
6026     if (peSize64 < (1 << 20))
6027     {
6028       peSize = (UInt32)peSize64;
6029       StartOffset = pePos;
6030     }
6031   }
6032 
6033   DataStreamOffset = pos + kStartHeaderSize;
6034   FirstHeader.Flags = Get32(buf);
6035   if ((FirstHeader.Flags & (~kFlagsMask)) != 0)
6036   {
6037     // return E_NOTIMPL;
6038     return S_FALSE;
6039   }
6040   IsInstaller = (FirstHeader.Flags & NFlags::kUninstall) == 0;
6041 
6042   FirstHeader.HeaderSize = Get32(buf + kSignatureSize + 4);
6043   FirstHeader.ArcSize = Get32(buf + kSignatureSize + 8);
6044   if (FirstHeader.ArcSize <= kStartHeaderSize)
6045     return S_FALSE;
6046 
6047   /*
6048   if ((FirstHeader.Flags & NFlags::k_BI_ExternalFileSupport) != 0)
6049   {
6050     UInt32 datablock_low = Get32(buf + kSignatureSize + 12);
6051     UInt32 datablock_high = Get32(buf + kSignatureSize + 16);
6052   }
6053   */
6054 
6055   RINOK(InStream_GetSize_SeekToEnd(inStream, _fileSize))
6056 
6057   IsArc = true;
6058 
6059   if (peSize != 0)
6060   {
6061     ExeStub.Alloc(peSize);
6062     RINOK(InStream_SeekSet(inStream, pePos))
6063     RINOK(ReadStream_FALSE(inStream, ExeStub, peSize))
6064   }
6065 
6066   HRESULT res = S_FALSE;
6067   try
6068   {
6069     CLimitedInStream *_limitedStreamSpec = new CLimitedInStream;
6070     _stream = _limitedStreamSpec;
6071     _limitedStreamSpec->SetStream(inStream);
6072     _limitedStreamSpec->InitAndSeek(pos, FirstHeader.ArcSize);
6073     DataStreamOffset -= pos;
6074     res = Open2(buf + kStartHeaderSize, bufSize - kStartHeaderSize);
6075   }
6076   catch(...)
6077   {
6078     _stream.Release();
6079     throw;
6080     // res = S_FALSE;
6081   }
6082   if (res != S_OK)
6083   {
6084     _stream.Release();
6085     // Clear();
6086   }
6087   return res;
6088 }
6089 
ConvertToUnicode(const AString & s) const6090 UString CInArchive::ConvertToUnicode(const AString &s) const
6091 {
6092   if (IsUnicode)
6093   {
6094     UString res;
6095     // if (
6096       ConvertUTF8ToUnicode(s, res);
6097       return res;
6098   }
6099   return MultiByteToUnicodeString(s);
6100 }
6101 
Clear2()6102 void CInArchive::Clear2()
6103 {
6104   IsUnicode = false;
6105   NsisType = k_NsisType_Nsis2;
6106   IsNsis225 = false;
6107   IsNsis200 = false;
6108   LogCmdIsEnabled = false;
6109   BadCmd = -1;
6110   Is64Bit = false;
6111 
6112   #ifdef NSIS_SCRIPT
6113   Name.Empty();
6114   BrandingText.Empty();
6115   Script.Empty();
6116   LicenseFiles.Clear();
6117   _numRootLicenses = 0;
6118   _numLangStrings = 0;
6119   langStrIDs.Clear();
6120   LangComment.Empty();
6121   noParseStringIndexes.Clear();
6122   #endif
6123 
6124   APrefixes.Clear();
6125   UPrefixes.Clear();
6126   Items.Clear();
6127   IsUnicode = false;
6128   ExeStub.Free();
6129 }
6130 
Clear()6131 void CInArchive::Clear()
6132 {
6133   Clear2();
6134   IsArc = false;
6135   _stream.Release();
6136 }
6137 
6138 }}
6139