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