// NsisIn.cpp #include "StdAfx.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringToInt.h" #include "../../Common/LimitedStreams.h" #include "../../Common/StreamUtils.h" #include "NsisIn.h" #define Get16(p) GetUi16(p) #define Get32(p) GetUi32(p) // #define NUM_SPEED_TESTS 1000 namespace NArchive { namespace NNsis { static const size_t kInputBufSize = 1 << 20; const Byte kSignature[kSignatureSize] = NSIS_SIGNATURE; static const UInt32 kMask_IsCompressed = (UInt32)1 << 31; static const unsigned kNumCommandParams = 6; static const unsigned kCmdSize = 4 + kNumCommandParams * 4; #ifdef NSIS_SCRIPT #define CR_LF "\x0D\x0A" #endif static const char * const kErrorStr = "$_ERROR_STR_"; #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } /* There are several versions of NSIS: 1) Original NSIS: NSIS-2 ANSI NSIS-3 ANSI NSIS-3 Unicode 2) NSIS from Jim Park that extends old NSIS-2 to Unicode support: NSIS-Park-(1,2,3) ANSI NSIS-Park-(1,2,3) Unicode The command IDs layout is slightly different for different versions. Also there are additional "log" versions of NSIS that support EW_LOG. We use the layout of "NSIS-3 Unicode" without "log" as main layout. And we transfer the command IDs to main layout, if another layout is detected. */ enum { EW_INVALID_OPCODE, EW_RET, // Return EW_NOP, // Nop, Goto EW_ABORT, // Abort EW_QUIT, // Quit EW_CALL, // Call, InitPluginsDir EW_UPDATETEXT, // DetailPrint EW_SLEEP, // Sleep EW_BRINGTOFRONT, // BringToFront EW_CHDETAILSVIEW, // SetDetailsView EW_SETFILEATTRIBUTES, // SetFileAttributes EW_CREATEDIR, // CreateDirectory, SetOutPath EW_IFFILEEXISTS, // IfFileExists EW_SETFLAG, // SetRebootFlag, ... EW_IFFLAG, // IfAbort, IfSilent, IfErrors, IfRebootFlag EW_GETFLAG, // GetInstDirError, GetErrorLevel EW_RENAME, // Rename EW_GETFULLPATHNAME, // GetFullPathName EW_SEARCHPATH, // SearchPath EW_GETTEMPFILENAME, // GetTempFileName EW_EXTRACTFILE, // File EW_DELETEFILE, // Delete EW_MESSAGEBOX, // MessageBox EW_RMDIR, // RMDir EW_STRLEN, // StrLen EW_ASSIGNVAR, // StrCpy EW_STRCMP, // StrCmp EW_READENVSTR, // ReadEnvStr, ExpandEnvStrings EW_INTCMP, // IntCmp, IntCmpU EW_INTOP, // IntOp EW_INTFMT, // IntFmt EW_PUSHPOP, // Push/Pop/Exchange EW_FINDWINDOW, // FindWindow EW_SENDMESSAGE, // SendMessage EW_ISWINDOW, // IsWindow EW_GETDLGITEM, // GetDlgItem EW_SETCTLCOLORS, // SetCtlColors EW_SETBRANDINGIMAGE, // SetBrandingImage EW_CREATEFONT, // CreateFont EW_SHOWWINDOW, // ShowWindow, EnableWindow, HideWindow EW_SHELLEXEC, // ExecShell EW_EXECUTE, // Exec, ExecWait EW_GETFILETIME, // GetFileTime EW_GETDLLVERSION, // GetDLLVersion // EW_GETFONTVERSION, // Park : 2.46.2 // EW_GETFONTNAME, // Park : 2.46.3 EW_REGISTERDLL, // RegDLL, UnRegDLL, CallInstDLL EW_CREATESHORTCUT, // CreateShortCut EW_COPYFILES, // CopyFiles EW_REBOOT, // Reboot EW_WRITEINI, // WriteINIStr, DeleteINISec, DeleteINIStr, FlushINI EW_READINISTR, // ReadINIStr EW_DELREG, // DeleteRegValue, DeleteRegKey EW_WRITEREG, // WriteRegStr, WriteRegExpandStr, WriteRegBin, WriteRegDWORD EW_READREGSTR, // ReadRegStr, ReadRegDWORD EW_REGENUM, // EnumRegKey, EnumRegValue EW_FCLOSE, // FileClose EW_FOPEN, // FileOpen EW_FPUTS, // FileWrite, FileWriteByte EW_FGETS, // FileRead, FileReadByte // Park // EW_FPUTWS, // FileWriteUTF16LE, FileWriteWord // EW_FGETWS, // FileReadUTF16LE, FileReadWord EW_FSEEK, // FileSeek EW_FINDCLOSE, // FindClose EW_FINDNEXT, // FindNext EW_FINDFIRST, // FindFirst EW_WRITEUNINSTALLER, // WriteUninstaller // Park : since 2.46.3 the log is enabled in main Park version // EW_LOG, // LogSet, LogText EW_SECTIONSET, // Get*, Set* EW_INSTTYPESET, // InstTypeSetText, InstTypeGetText, SetCurInstType, GetCurInstType // instructions not actually implemented in exehead, but used in compiler. EW_GETLABELADDR, // both of these get converted to EW_ASSIGNVAR EW_GETFUNCTIONADDR, EW_LOCKWINDOW, // LockWindow // 2 unicode commands available only in Unicode archive EW_FPUTWS, // FileWriteUTF16LE, FileWriteWord EW_FGETWS, // FileReadUTF16LE, FileReadWord // The following IDs are not IDs in real order. // We just need some IDs to translate eny extended layout to main layout. EW_LOG, // LogSet, LogText // Park EW_FINDPROC, // FindProc EW_GETFONTVERSION, // GetFontVersion EW_GETFONTNAME, // GetFontName kNumCmds }; static const unsigned kNumAdditionalParkCmds = 3; struct CCommandInfo { Byte NumParams; }; static const CCommandInfo k_Commands[kNumCmds] = { { 0 }, // "Invalid" }, { 0 }, // Return { 1 }, // Nop, Goto { 1 }, // "Abort" }, { 0 }, // "Quit" }, { 2 }, // Call { 6 }, // "DetailPrint" }, // 1 param in new versions, 6 in old NSIS versions { 1 }, // "Sleep" }, { 0 }, // "BringToFront" }, { 2 }, // "SetDetailsView" }, { 2 }, // "SetFileAttributes" }, { 2 }, // CreateDirectory, SetOutPath { 3 }, // "IfFileExists" }, { 3 }, // SetRebootFlag, ... { 4 }, // "If" }, // IfAbort, IfSilent, IfErrors, IfRebootFlag { 2 }, // "Get" }, // GetInstDirError, GetErrorLevel { 4 }, // "Rename" }, { 3 }, // "GetFullPathName" }, { 2 }, // "SearchPath" }, { 2 }, // "GetTempFileName" }, { 6 }, // "File" { 2 }, // "Delete" }, { 6 }, // "MessageBox" }, { 2 }, // "RMDir" }, { 2 }, // "StrLen" }, { 4 }, // StrCpy, GetCurrentAddress { 5 }, // "StrCmp" }, { 3 }, // ReadEnvStr, ExpandEnvStrings { 6 }, // "IntCmp" }, { 4 }, // "IntOp" }, { 3 }, // "IntFmt" }, { 6 }, // Push, Pop, Exch // it must be 3 params. But some multi-command write garbage. { 5 }, // "FindWindow" }, { 6 }, // "SendMessage" }, { 3 }, // "IsWindow" }, { 3 }, // "GetDlgItem" }, { 2 }, // "SetCtlColors" }, { 3 }, // "SetBrandingImage" }, { 5 }, // "CreateFont" }, { 4 }, // ShowWindow, EnableWindow, HideWindow { 6 }, // "ExecShell" }, { 3 }, // "Exec" }, // Exec, ExecWait { 3 }, // "GetFileTime" }, { 3 }, // "GetDLLVersion" }, { 6 }, // RegDLL, UnRegDLL, CallInstDLL // it must be 5 params. But some multi-command write garbage. { 6 }, // "CreateShortCut" }, { 4 }, // "CopyFiles" }, { 1 }, // "Reboot" }, { 5 }, // WriteINIStr, DeleteINISec, DeleteINIStr, FlushINI { 4 }, // "ReadINIStr" }, { 5 }, // "DeleteReg" }, // DeleteRegKey, DeleteRegValue { 6 }, // "WriteReg" }, // WriteRegStr, WriteRegExpandStr, WriteRegBin, WriteRegDWORD { 5 }, // "ReadReg" }, // ReadRegStr, ReadRegDWORD { 5 }, // "EnumReg" }, // EnumRegKey, EnumRegValue { 1 }, // "FileClose" }, { 4 }, // "FileOpen" }, { 3 }, // "FileWrite" }, // FileWrite, FileWriteByte { 4 }, // "FileRead" }, // FileRead, FileReadByte { 4 }, // "FileSeek" }, { 1 }, // "FindClose" }, { 2 }, // "FindNext" }, { 3 }, // "FindFirst" }, { 4 }, // "WriteUninstaller" }, { 5 }, // "Section" }, // *** { 4 }, // InstTypeSetText, InstTypeGetText, SetCurInstType, GetCurInstType { 6 }, // "GetLabelAddr" }, { 2 }, // "GetFunctionAddress" }, { 1 }, // "LockWindow" }, { 3 }, // "FileWrite" }, // FileWriteUTF16LE, FileWriteWord { 4 }, // "FileRead" }, // FileReadUTF16LE, FileReadWord { 2 }, // "Log" }, // LogSet, LogText // Park { 2 }, // "FindProc" }, { 2 }, // "GetFontVersion" }, { 2 }, // "GetFontName" } }; #ifdef NSIS_SCRIPT static const char * const k_CommandNames[kNumCmds] = { "Invalid" , NULL // Return , NULL // Nop, Goto , "Abort" , "Quit" , NULL // Call , "DetailPrint" // 1 param in new versions, 6 in old NSIS versions , "Sleep" , "BringToFront" , "SetDetailsView" , "SetFileAttributes" , NULL // CreateDirectory, SetOutPath , "IfFileExists" , NULL // SetRebootFlag, ... , "If" // IfAbort, IfSilent, IfErrors, IfRebootFlag , "Get" // GetInstDirError, GetErrorLevel , "Rename" , "GetFullPathName" , "SearchPath" , "GetTempFileName" , NULL // File , "Delete" , "MessageBox" , "RMDir" , "StrLen" , NULL // StrCpy, GetCurrentAddress , "StrCmp" , NULL // ReadEnvStr, ExpandEnvStrings , "IntCmp" , "IntOp" , "IntFmt" , NULL // Push, Pop, Exch // it must be 3 params. But some multi-command write garbage. , "FindWindow" , "SendMessage" , "IsWindow" , "GetDlgItem" , "SetCtlColors" , "SetBrandingImage" , "CreateFont" , NULL // ShowWindow, EnableWindow, HideWindow , "ExecShell" , "Exec" // Exec, ExecWait , "GetFileTime" , "GetDLLVersion" , NULL // RegDLL, UnRegDLL, CallInstDLL // it must be 5 params. But some multi-command write garbage. , "CreateShortCut" , "CopyFiles" , "Reboot" , NULL // WriteINIStr, DeleteINISec, DeleteINIStr, FlushINI , "ReadINIStr" , "DeleteReg" // DeleteRegKey, DeleteRegValue , "WriteReg" // WriteRegStr, WriteRegExpandStr, WriteRegBin, WriteRegDWORD , "ReadReg" // ReadRegStr, ReadRegDWORD , "EnumReg" // EnumRegKey, EnumRegValue , "FileClose" , "FileOpen" , "FileWrite" // FileWrite, FileWriteByte , "FileRead" // FileRead, FileReadByte , "FileSeek" , "FindClose" , "FindNext" , "FindFirst" , "WriteUninstaller" , "Section" // *** , NULL // InstTypeSetText, InstTypeGetText, SetCurInstType, GetCurInstType , "GetLabelAddr" , "GetFunctionAddress" , "LockWindow" , "FileWrite" // FileWriteUTF16LE, FileWriteWord , "FileRead" // FileReadUTF16LE, FileReadWord , "Log" // LogSet, LogText // Park , "FindProc" , "GetFontVersion" , "GetFontName" }; #endif /* NSIS can use one name for two CSIDL_*** and CSIDL_COMMON_*** items (CurrentUser / AllUsers) Some NSIS shell names are not identical to WIN32 CSIDL_* names. NSIS doesn't use some CSIDL_* values. But we add name for all CSIDL_ (marked with '+'). */ static const char * const kShellStrings[] = { "DESKTOP" // + , "INTERNET" // + , "SMPROGRAMS" // CSIDL_PROGRAMS , "CONTROLS" // + , "PRINTERS" // + , "DOCUMENTS" // CSIDL_PERSONAL , "FAVORITES" // CSIDL_FAVORITES , "SMSTARTUP" // CSIDL_STARTUP , "RECENT" // CSIDL_RECENT , "SENDTO" // CSIDL_SENDTO , "BITBUCKET" // + , "STARTMENU" , NULL // CSIDL_MYDOCUMENTS = CSIDL_PERSONAL , "MUSIC" // CSIDL_MYMUSIC , "VIDEOS" // CSIDL_MYVIDEO , NULL , "DESKTOP" // CSIDL_DESKTOPDIRECTORY , "DRIVES" // + , "NETWORK" // + , "NETHOOD" , "FONTS" , "TEMPLATES" , "STARTMENU" // CSIDL_COMMON_STARTMENU , "SMPROGRAMS" // CSIDL_COMMON_PROGRAMS , "SMSTARTUP" // CSIDL_COMMON_STARTUP , "DESKTOP" // CSIDL_COMMON_DESKTOPDIRECTORY , "APPDATA" // CSIDL_APPDATA !!! "QUICKLAUNCH" , "PRINTHOOD" , "LOCALAPPDATA" , "ALTSTARTUP" , "ALTSTARTUP" // CSIDL_COMMON_ALTSTARTUP , "FAVORITES" // CSIDL_COMMON_FAVORITES , "INTERNET_CACHE" , "COOKIES" , "HISTORY" , "APPDATA" // CSIDL_COMMON_APPDATA , "WINDIR" , "SYSDIR" , "PROGRAM_FILES" // + , "PICTURES" // CSIDL_MYPICTURES , "PROFILE" , "SYSTEMX86" // + , "PROGRAM_FILESX86" // + , "PROGRAM_FILES_COMMON" // + , "PROGRAM_FILES_COMMONX8" // + CSIDL_PROGRAM_FILES_COMMONX86 , "TEMPLATES" // CSIDL_COMMON_TEMPLATES , "DOCUMENTS" // CSIDL_COMMON_DOCUMENTS , "ADMINTOOLS" // CSIDL_COMMON_ADMINTOOLS , "ADMINTOOLS" // CSIDL_ADMINTOOLS , "CONNECTIONS" // + , NULL , NULL , NULL , "MUSIC" // CSIDL_COMMON_MUSIC , "PICTURES" // CSIDL_COMMON_PICTURES , "VIDEOS" // CSIDL_COMMON_VIDEO , "RESOURCES" , "RESOURCES_LOCALIZED" , "COMMON_OEM_LINKS" // + , "CDBURN_AREA" , NULL // unused , "COMPUTERSNEARME" // + }; static void UIntToString(AString &s, UInt32 v) { char sz[16]; ConvertUInt32ToString(v, sz); s += sz; } #ifdef NSIS_SCRIPT void CInArchive::Add_UInt(UInt32 v) { char sz[16]; ConvertUInt32ToString(v, sz); Script += sz; } static void Add_SignedInt(CDynLimBuf &s, Int32 v) { char sz[32]; ConvertInt64ToString(v, sz); s += sz; } static void Add_Hex(CDynLimBuf &s, UInt32 v) { char sz[16]; sz[0] = '0'; sz[1] = 'x'; ConvertUInt32ToHex(v, sz + 2); s += sz; } static UInt32 GetUi16Str_Len(const Byte *p) { const Byte *pp = p; for (; *pp != 0 || *(pp + 1) != 0; pp += 2); return (UInt32)((pp - p) >> 1); } void CInArchive::AddLicense(UInt32 param, Int32 langID) { Space(); if (param >= NumStringChars || param + 1 >= NumStringChars) { Script += kErrorStr; return; } strUsed[param] = 1; UInt32 start = _stringsPos + (IsUnicode ? param * 2 : param); UInt32 offset = start + (IsUnicode ? 2 : 1); { FOR_VECTOR (i, LicenseFiles) { const CLicenseFile &lic = LicenseFiles[i]; if (offset == lic.Offset) { Script += lic.Name; return; } } } AString fileName = "[LICENSE]"; if (langID >= 0) { fileName += "\\license-"; // LangId_To_String(fileName, langID); UIntToString(fileName, langID); } else if (++_numRootLicenses > 1) { fileName += '-'; UIntToString(fileName, _numRootLicenses); } const Byte *sz = (_data + start); unsigned marker = IsUnicode ? Get16(sz) : *sz; bool isRTF = (marker == 2); fileName += isRTF ? ".rtf" : ".txt"; // if (*sz == 1) it's text; Script += fileName; CLicenseFile &lic = LicenseFiles.AddNew(); lic.Name = fileName; lic.Offset = offset; if (!IsUnicode) lic.Size = (UInt32)strlen((const char *)sz + 1); else { sz += 2; UInt32 len = GetUi16Str_Len(sz); lic.Size = len * 2; if (isRTF) { lic.Text.Alloc((size_t)len); for (UInt32 i = 0; i < len; i++, sz += 2) { unsigned c = Get16(sz); if (c >= 256) c = '?'; lic.Text[i] = (Byte)(c); } lic.Size = len; lic.Offset = 0; } } } #endif #define kVar_CMDLINE 20 #define kVar_INSTDIR 21 #define kVar_OUTDIR 22 #define kVar_EXEDIR 23 #define kVar_LANGUAGE 24 #define kVar_TEMP 25 #define kVar_PLUGINSDIR 26 #define kVar_EXEPATH 27 // NSIS 2.26+ #define kVar_EXEFILE 28 // NSIS 2.26+ #define kVar_HWNDPARENT_225 27 #define kVar_HWNDPARENT 29 // #define kVar__CLICK 30 #define kVar_Spec_OUTDIR_225 29 // NSIS 2.04 - 2.25 #define kVar_Spec_OUTDIR 31 // NSIS 2.26+ static const char * const kVarStrings[] = { "CMDLINE" , "INSTDIR" , "OUTDIR" , "EXEDIR" , "LANGUAGE" , "TEMP" , "PLUGINSDIR" , "EXEPATH" // NSIS 2.26+ , "EXEFILE" // NSIS 2.26+ , "HWNDPARENT" , "_CLICK" // is set from page->clicknext , "_OUTDIR" // NSIS 2.04+ }; static const unsigned kNumInternalVars = 20 + ARRAY_SIZE(kVarStrings); #define GET_NUM_INTERNAL_VARS (IsNsis200 ? kNumInternalVars - 3 : IsNsis225 ? kNumInternalVars - 2 : kNumInternalVars); void CInArchive::GetVar2(AString &res, UInt32 index) { if (index < 20) { if (index >= 10) { res += 'R'; index -= 10; } UIntToString(res, index); } else { unsigned numInternalVars = GET_NUM_INTERNAL_VARS; if (index < numInternalVars) { if (IsNsis225 && index >= kVar_EXEPATH) index += 2; res += kVarStrings[index - 20]; } else { res += '_'; UIntToString(res, index - numInternalVars); res += '_'; } } } void CInArchive::GetVar(AString &res, UInt32 index) { res += '$'; GetVar2(res, index); } #ifdef NSIS_SCRIPT void CInArchive::Add_Var(UInt32 index) { _tempString_for_GetVar.Empty(); GetVar(_tempString_for_GetVar, index); Script += _tempString_for_GetVar; } void CInArchive::AddParam_Var(UInt32 index) { Space(); Add_Var(index); } void CInArchive::AddParam_UInt(UInt32 value) { Space(); Add_UInt(value); } #endif #define NS_CODE_SKIP 252 #define NS_CODE_VAR 253 #define NS_CODE_SHELL 254 #define NS_CODE_LANG 255 #define NS_3_CODE_LANG 1 #define NS_3_CODE_SHELL 2 #define NS_3_CODE_VAR 3 #define NS_3_CODE_SKIP 4 #define PARK_CODE_SKIP 0xE000 #define PARK_CODE_VAR 0xE001 #define PARK_CODE_SHELL 0xE002 #define PARK_CODE_LANG 0xE003 #define IS_NS_SPEC_CHAR(c) ((c) >= NS_CODE_SKIP) #define IS_PARK_SPEC_CHAR(c) ((c) >= PARK_CODE_SKIP && (c) <= PARK_CODE_LANG) #define DECODE_NUMBER_FROM_2_CHARS(c0, c1) (((c0) & 0x7F) | (((unsigned)((c1) & 0x7F)) << 7)) #define CONVERT_NUMBER_NS_3_UNICODE(n) n = ((n & 0x7F) | (((n >> 8) & 0x7F) << 7)) #define CONVERT_NUMBER_PARK(n) n &= 0x7FFF static bool AreStringsEqual_16and8(const Byte *p16, const char *p8) { for (;;) { unsigned c16 = Get16(p16); p16 += 2; unsigned c = (Byte)(*p8++); if (c16 != c) return false; if (c == 0) return true; } } void CInArchive::GetShellString(AString &s, unsigned index1, unsigned index2) { // zeros are not allowed here. // if (index1 == 0 || index2 == 0) throw 333; if ((index1 & 0x80) != 0) { unsigned offset = (index1 & 0x3F); /* NSIS reads registry string: keyName = HKLM Software\\Microsoft\\Windows\\CurrentVersion mask = KEY_WOW64_64KEY, If 64-bit flag in index1 is set valueName = string(offset) If registry reading is failed, NSIS uses second parameter (index2) to read string. The recursion is possible in that case in NSIS. We don't parse index2 string. We only set strUsed status for that string (but without recursion). */ if (offset >= NumStringChars) { s += kErrorStr; return; } #ifdef NSIS_SCRIPT strUsed[offset] = 1; if (index2 < NumStringChars) strUsed[index2] = 1; #endif const Byte *p = (const Byte *)(_data + _stringsPos); int id = -1; if (IsUnicode) { p += offset * 2; if (AreStringsEqual_16and8(p, "ProgramFilesDir")) id = 0; else if (AreStringsEqual_16and8(p, "CommonFilesDir")) id = 1; } else { p += offset; if (strcmp((const char *)p, "ProgramFilesDir") == 0) id = 0; else if (strcmp((const char *)p, "CommonFilesDir") == 0) id = 1; } s += ((id >= 0) ? (id == 0 ? "$PROGRAMFILES" : "$COMMONFILES") : "$_ERROR_UNSUPPORTED_VALUE_REGISTRY_"); // s += ((index1 & 0x40) != 0) ? "64" : "32"; if ((index1 & 0x40) != 0) s += "64"; if (id < 0) { s += '('; if (IsUnicode) { for (unsigned i = 0; i < 256; i++) { wchar_t c = Get16(p + i * 2); if (c == 0) break; if (c < 0x80) s += (char)c; } } else s += (const char *)p; s += ')'; } return; } s += '$'; if (index1 < ARRAY_SIZE(kShellStrings)) { const char *sz = kShellStrings[index1]; if (sz) { s += sz; return; } } if (index2 < ARRAY_SIZE(kShellStrings)) { const char *sz = kShellStrings[index2]; if (sz) { s += sz; return; } } s += "_ERROR_UNSUPPORTED_SHELL_"; s += '['; UIntToString(s, index1); s += ','; UIntToString(s, index2); s += ']'; } #ifdef NSIS_SCRIPT void CInArchive::Add_LangStr_Simple(UInt32 id) { Script += "LSTR_"; Add_UInt(id); } #endif void CInArchive::Add_LangStr(AString &res, UInt32 id) { #ifdef NSIS_SCRIPT langStrIDs.Add(id); #endif res += "$(LSTR_"; UIntToString(res, id); res += ')'; } void CInArchive::GetNsisString_Raw(const Byte *s) { Raw_AString.Empty(); if (NsisType != k_NsisType_Nsis3) { for (;;) { Byte c = *s++; if (c == 0) return; if (IS_NS_SPEC_CHAR(c)) { Byte c0 = *s++; if (c0 == 0) return; if (c != NS_CODE_SKIP) { Byte c1 = *s++; if (c1 == 0) return; if (c == NS_CODE_SHELL) GetShellString(Raw_AString, c0, c1); else { unsigned n = DECODE_NUMBER_FROM_2_CHARS(c0, c1); if (c == NS_CODE_VAR) GetVar(Raw_AString, n); else // if (c == NS_CODE_LANG) Add_LangStr(Raw_AString, n); } continue; } c = c0; } Raw_AString += (char)c; } } // NSIS-3 ANSI for (;;) { Byte c = *s++; if (c <= NS_3_CODE_SKIP) { if (c == 0) return; Byte c0 = *s++; if (c0 == 0) return; if (c != NS_3_CODE_SKIP) { Byte c1 = *s++; if (c1 == 0) return; if (c == NS_3_CODE_SHELL) GetShellString(Raw_AString, c0, c1); else { unsigned n = DECODE_NUMBER_FROM_2_CHARS(c0, c1); if (c == NS_3_CODE_VAR) GetVar(Raw_AString, n); else // if (c == NS_3_CODE_LANG) Add_LangStr(Raw_AString, n); } continue; } c = c0; } Raw_AString += (char)c; } } #ifdef NSIS_SCRIPT void CInArchive::GetNsisString(AString &res, const Byte *s) { for (;;) { Byte c = *s++; if (c == 0) return; if (NsisType != k_NsisType_Nsis3) { if (IS_NS_SPEC_CHAR(c)) { Byte c0 = *s++; if (c0 == 0) return; if (c != NS_CODE_SKIP) { Byte c1 = *s++; if (c1 == 0) return; if (c == NS_CODE_SHELL) GetShellString(res, c0, c1); else { unsigned n = DECODE_NUMBER_FROM_2_CHARS(c0, c1); if (c == NS_CODE_VAR) GetVar(res, n); else // if (c == NS_CODE_LANG) Add_LangStr(res, n); } continue; } c = c0; } } else { // NSIS-3 ANSI if (c <= NS_3_CODE_SKIP) { Byte c0 = *s++; if (c0 == 0) return; if (c0 == 0) break; if (c != NS_3_CODE_SKIP) { Byte c1 = *s++; if (c1 == 0) return; if (c == NS_3_CODE_SHELL) GetShellString(res, c0, c1); else { unsigned n = DECODE_NUMBER_FROM_2_CHARS(c0, c1); if (c == NS_3_CODE_VAR) GetVar(res, n); else // if (c == NS_3_CODE_LANG) Add_LangStr(res, n); } continue; } c = c0; } } { const char *e; if (c == 9) e = "$\\t"; else if (c == 10) e = "$\\n"; else if (c == 13) e = "$\\r"; else if (c == '"') e = "$\\\""; else if (c == '$') e = "$$"; else { res += (char)c; continue; } res += e; continue; } } } #endif void CInArchive::GetNsisString_Unicode_Raw(const Byte *p) { Raw_UString.Empty(); if (IsPark()) { for (;;) { unsigned c = Get16(p); p += 2; if (c == 0) break; if (c < 0x80) { Raw_UString += (wchar_t)c; continue; } if (IS_PARK_SPEC_CHAR(c)) { unsigned n = Get16(p); p += 2; if (n == 0) break; if (c != PARK_CODE_SKIP) { Raw_AString.Empty(); if (c == PARK_CODE_SHELL) GetShellString(Raw_AString, n & 0xFF, n >> 8); else { CONVERT_NUMBER_PARK(n); if (c == PARK_CODE_VAR) GetVar(Raw_AString, n); else // if (c == PARK_CODE_LANG) Add_LangStr(Raw_AString, n); } Raw_UString.AddAscii(Raw_AString); continue; } c = n; } Raw_UString += (wchar_t)c; } return; } // NSIS-3 Unicode for (;;) { unsigned c = Get16(p); p += 2; if (c > NS_3_CODE_SKIP) { Raw_UString += (wchar_t)c; continue; } if (c == 0) break; unsigned n = Get16(p); p += 2; if (n == 0) break; if (c == NS_3_CODE_SKIP) { Raw_UString += (wchar_t)n; continue; } Raw_AString.Empty(); if (c == NS_3_CODE_SHELL) GetShellString(Raw_AString, n & 0xFF, n >> 8); else { CONVERT_NUMBER_NS_3_UNICODE(n); if (c == NS_3_CODE_VAR) GetVar(Raw_AString, n); else // if (c == NS_3_CODE_LANG) Add_LangStr(Raw_AString, n); } Raw_UString.AddAscii(Raw_AString); } } #ifdef NSIS_SCRIPT static const Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; void CInArchive::GetNsisString_Unicode(AString &res, const Byte *p) { for (;;) { unsigned c = Get16(p); p += 2; if (c == 0) break; if (IsPark()) { if (IS_PARK_SPEC_CHAR(c)) { unsigned n = Get16(p); p += 2; if (n == 0) break; if (c != PARK_CODE_SKIP) { if (c == PARK_CODE_SHELL) GetShellString(res, n & 0xFF, n >> 8); else { CONVERT_NUMBER_PARK(n); if (c == PARK_CODE_VAR) GetVar(res, n); else // if (c == PARK_CODE_LANG) Add_LangStr(res, n); } continue; } c = n; } } else { // NSIS-3 Unicode if (c <= NS_3_CODE_SKIP) { unsigned n = Get16(p); p += 2; if (n == 0) break; if (c != NS_3_CODE_SKIP) { if (c == NS_3_CODE_SHELL) GetShellString(res, n & 0xFF, n >> 8); else { CONVERT_NUMBER_NS_3_UNICODE(n); if (c == NS_3_CODE_VAR) GetVar(res, n); else // if (c == NS_3_CODE_LANG) Add_LangStr(res, n); } continue; } c = n; } } if (c < 0x80) { const char *e; if (c == 9) e = "$\\t"; else if (c == 10) e = "$\\n"; else if (c == 13) e = "$\\r"; else if (c == '"') e = "$\\\""; else if (c == '$') e = "$$"; else { res += (char)c; continue; } res += e; continue; } UInt32 value = c; /* if (value >= 0xD800 && value < 0xE000) { UInt32 c2; if (value >= 0xDC00 || srcPos == srcLen) break; c2 = src[srcPos++]; if (c2 < 0xDC00 || c2 >= 0xE000) break; value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000; } */ unsigned numAdds; for (numAdds = 1; numAdds < 5; numAdds++) if (value < (((UInt32)1) << (numAdds * 5 + 6))) break; res += (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds))); do { numAdds--; res += (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F)); // destPos++; } while (numAdds != 0); // AddToUtf8(res, c); } } #endif void CInArchive::ReadString2_Raw(UInt32 pos) { Raw_AString.Empty(); Raw_UString.Empty(); if ((Int32)pos < 0) Add_LangStr(Raw_AString, -((Int32)pos + 1)); else if (pos >= NumStringChars) { Raw_AString += kErrorStr; // UIntToString(Raw_AString, pos); } else { if (IsUnicode) GetNsisString_Unicode_Raw(_data + _stringsPos + pos * 2); else GetNsisString_Raw(_data + _stringsPos + pos); return; } Raw_UString.SetFromAscii(Raw_AString); } bool CInArchive::IsGoodString(UInt32 param) const { if (param >= NumStringChars) return false; if (param == 0) return true; const Byte *p = _data + _stringsPos; unsigned c; if (IsUnicode) c = Get16(p + param * 2 - 2); else c = p[param - 1]; // some files have '\\' character before string? return (c == 0 || c == '\\'); } bool CInArchive::AreTwoParamStringsEqual(UInt32 param1, UInt32 param2) const { if (param1 == param2) return true; /* NSIS-3.0a1 probably contains bug, so it can use 2 different strings with same content. So we check real string also. Also it's possible to check identical postfix parts of strings. */ if (param1 >= NumStringChars || param2 >= NumStringChars) return false; const Byte *p = _data + _stringsPos; if (IsUnicode) { const Byte *p1 = p + param1 * 2; const Byte *p2 = p + param2 * 2; for (;;) { UInt16 c = Get16(p1); if (c != Get16(p2)) return false; if (c == 0) return true; p1 += 2; p2 += 2; } } else { const Byte *p1 = p + param1; const Byte *p2 = p + param2; for (;;) { Byte c = *p1++; if (c != *p2++) return false; if (c == 0) return true; } } } #ifdef NSIS_SCRIPT UInt32 CInArchive::GetNumUsedVars() const { UInt32 numUsedVars = 0; const Byte *data = (const Byte *)_data + _stringsPos; unsigned npi = 0; for (UInt32 i = 0; i < NumStringChars;) { bool process = true; if (npi < noParseStringIndexes.Size() && noParseStringIndexes[npi] == i) { process = false; npi++; } if (IsUnicode) { if (IsPark()) { for (;;) { unsigned c = Get16(data + i * 2); i++; if (c == 0) break; if (IS_PARK_SPEC_CHAR(c)) { UInt32 n = Get16(data + i * 2); i++; if (n == 0) break; if (process && c == PARK_CODE_VAR) { CONVERT_NUMBER_PARK(n); n++; if (numUsedVars < n) numUsedVars = n; } } } } else // NSIS-3 Unicode { for (;;) { unsigned c = Get16(data + i * 2); i++; if (c == 0) break; if (c > NS_3_CODE_SKIP) continue; UInt32 n = Get16(data + i * 2); i++; if (n == 0) break; if (process && c == NS_3_CODE_VAR) { CONVERT_NUMBER_NS_3_UNICODE(n); n++; if (numUsedVars < n) numUsedVars = n; } } } } else // not Unicode (ANSI) { if (NsisType != k_NsisType_Nsis3) { for (;;) { Byte c = data[i++]; if (c == 0) break; if (IS_NS_SPEC_CHAR(c)) { Byte c0 = data[i++]; if (c0 == 0) break; if (c == NS_CODE_SKIP) continue; Byte c1 = data[i++]; if (c1 == 0) break; if (process && c == NS_CODE_VAR) { UInt32 n = DECODE_NUMBER_FROM_2_CHARS(c0, c1); n++; if (numUsedVars < n) numUsedVars = n; } } } } else { // NSIS-3 ANSI for (;;) { Byte c = data[i++]; if (c == 0) break; if (c > NS_3_CODE_SKIP) continue; Byte c0 = data[i++]; if (c0 == 0) break; if (c == NS_3_CODE_SKIP) continue; Byte c1 = data[i++]; if (c1 == 0) break; if (process && c == NS_3_CODE_VAR) { UInt32 n = DECODE_NUMBER_FROM_2_CHARS(c0, c1); n++; if (numUsedVars < n) numUsedVars = n; } } } } } return numUsedVars; } void CInArchive::ReadString2(AString &s, UInt32 pos) { if ((Int32)pos < 0) { Add_LangStr(s, -((Int32)pos + 1)); return; } if (pos >= NumStringChars) { s += kErrorStr; // UIntToString(s, pos); return; } #ifdef NSIS_SCRIPT strUsed[pos] = 1; #endif if (IsUnicode) GetNsisString_Unicode(s, _data + _stringsPos + pos * 2); else GetNsisString(s, _data + _stringsPos + pos); } #endif #ifdef NSIS_SCRIPT #define DEL_DIR 1 #define DEL_RECURSE 2 #define DEL_REBOOT 4 // #define DEL_SIMPLE 8 void CInArchive::AddRegRoot(UInt32 val) { Space(); const char *s; switch (val) { case 0: s = "SHCTX"; break; case 0x80000000: s = "HKCR"; break; case 0x80000001: s = "HKCU"; break; case 0x80000002: s = "HKLM"; break; case 0x80000003: s = "HKU"; break; case 0x80000004: s = "HKPD"; break; case 0x80000005: s = "HKCC"; break; case 0x80000006: s = "HKDD"; break; case 0x80000050: s = "HKPT"; break; case 0x80000060: s = "HKPN"; break; default: // Script += " RRRRR "; // throw 1; Add_Hex(Script, val); return; } Script += s; } static const char *g_WinAttrib[] = { "READONLY" , "HIDDEN" , "SYSTEM" , NULL , "DIRECTORY" , "ARCHIVE" , "DEVICE" , "NORMAL" , "TEMPORARY" , "SPARSE_FILE" , "REPARSE_POINT" , "COMPRESSED" , "OFFLINE" , "NOT_CONTENT_INDEXED" , "ENCRYPTED" , NULL , "VIRTUAL" }; #define FLAGS_DELIMITER '|' static void FlagsToString2(CDynLimBuf &s, const char * const *table, unsigned num, UInt32 flags) { bool filled = false; for (unsigned i = 0; i < num; i++) { UInt32 f = (UInt32)1 << i; if ((flags & f) != 0) { const char *name = table[i]; if (name) { if (filled) s += FLAGS_DELIMITER; filled = true; s += name; flags &= ~f; } } } if (flags != 0) { if (filled) s += FLAGS_DELIMITER; Add_Hex(s, flags); } } static bool DoesNeedQuotes(const char *s) { char c = s[0]; if (c == 0 || c == '#' || c == ';' || (c == '/' && s[1] == '*')) return true; for (;;) { char c = *s++; if (c == 0) return false; if (c == ' ') return true; } } void CInArchive::Add_QuStr(const AString &s) { bool needQuotes = DoesNeedQuotes(s); if (needQuotes) Script += '\"'; Script += s; if (needQuotes) Script += '\"'; } void CInArchive::SpaceQuStr(const AString &s) { Space(); Add_QuStr(s); } void CInArchive::AddParam(UInt32 pos) { _tempString.Empty(); ReadString2(_tempString, pos); SpaceQuStr(_tempString); } void CInArchive::AddParams(const UInt32 *params, unsigned num) { for (unsigned i = 0; i < num; i++) AddParam(params[i]); } void CInArchive::AddOptionalParam(UInt32 pos) { if (pos != 0) AddParam(pos); } static unsigned GetNumParams(const UInt32 *params, unsigned num) { for (; num > 0 && params[num - 1] == 0; num--); return num; } void CInArchive::AddOptionalParams(const UInt32 *params, unsigned num) { AddParams(params, GetNumParams(params, num)); } static const UInt32 CMD_REF_Goto = (1 << 0); static const UInt32 CMD_REF_Call = (1 << 1); static const UInt32 CMD_REF_Pre = (1 << 2); static const UInt32 CMD_REF_Show = (1 << 3); static const UInt32 CMD_REF_Leave = (1 << 4); static const UInt32 CMD_REF_OnFunc = (1 << 5); static const UInt32 CMD_REF_Section = (1 << 6); static const UInt32 CMD_REF_InitPluginDir = (1 << 7); // static const UInt32 CMD_REF_Creator = (1 << 5); // _Pre is used instead static const unsigned CMD_REF_OnFunc_NumShifts = 28; // it uses for onFunc too static const unsigned CMD_REF_Page_NumShifts = 16; // it uses for onFunc too static const UInt32 CMD_REF_Page_Mask = 0x0FFF0000; static const UInt32 CMD_REF_OnFunc_Mask = 0xF0000000; inline bool IsPageFunc(UInt32 flag) { return (flag & (CMD_REF_Pre | CMD_REF_Show | CMD_REF_Leave)) != 0; } inline bool IsFunc(UInt32 flag) { // return (flag & (CMD_REF_Pre | CMD_REF_Show | CMD_REF_Leave | CMD_REF_OnFunc)) != 0; return (flag & (CMD_REF_Call | CMD_REF_Pre | CMD_REF_Show | CMD_REF_Leave | CMD_REF_OnFunc)) != 0; } inline bool IsProbablyEndOfFunc(UInt32 flag) { return (flag != 0 && flag != CMD_REF_Goto); } static const char * const kOnFunc[] = { "Init" , "InstSuccess" , "InstFailed" , "UserAbort" , "GUIInit" , "GUIEnd" , "MouseOverSection" , "VerifyInstDir" , "SelChange" , "RebootFailed" }; void CInArchive::Add_FuncName(const UInt32 *labels, UInt32 index) { UInt32 mask = labels[index]; if (mask & CMD_REF_OnFunc) { Script += ".on"; Script += kOnFunc[labels[index] >> CMD_REF_OnFunc_NumShifts]; } else if (mask & CMD_REF_InitPluginDir) { /* if (!IsInstaller) Script += "un." */ Script += "Initialize_____Plugins"; } else { Script += "func_"; Add_UInt(index); } } void CInArchive::AddParam_Func(const UInt32 *labels, UInt32 index) { Space(); if ((Int32)index >= 0) Add_FuncName(labels, index); else AddQuotes(); } void CInArchive::Add_LabelName(UInt32 index) { Script += "label_"; Add_UInt(index); } // param != 0 void CInArchive::Add_GotoVar(UInt32 param) { Space(); if ((Int32)param < 0) Add_Var(-((Int32)param + 1)); else Add_LabelName(param - 1); } void CInArchive::Add_GotoVar1(UInt32 param) { if (param == 0) Script += " 0"; else Add_GotoVar(param); } void CInArchive::Add_GotoVars2(const UInt32 *params) { Add_GotoVar1(params[0]); if (params[1] != 0) Add_GotoVar(params[1]); } static bool NoLabels(const UInt32 *labels, UInt32 num) { for (UInt32 i = 0; i < num; i++) if (labels[i] != 0) return false; return true; } static const char * const k_REBOOTOK = " /REBOOTOK"; #define MY__MB_ABORTRETRYIGNORE 2 #define MY__MB_RETRYCANCEL 5 static const char * const k_MB_Buttons[] = { "OK" , "OKCANCEL" , "ABORTRETRYIGNORE" , "YESNOCANCEL" , "YESNO" , "RETRYCANCEL" , "CANCELTRYCONTINUE" }; #define MY__MB_ICONSTOP (1 << 4) static const char * const k_MB_Icons[] = { NULL , "ICONSTOP" , "ICONQUESTION" , "ICONEXCLAMATION" , "ICONINFORMATION" }; static const char * const k_MB_Flags[] = { "HELP" , "NOFOCUS" , "SETFOREGROUND" , "DEFAULT_DESKTOP_ONLY" , "TOPMOST" , "RIGHT" , "RTLREADING" // , "SERVICE_NOTIFICATION" // unsupported. That bit is used for NSIS purposes }; #define MY__IDCANCEL 2 #define MY__IDIGNORE 5 static const char * const k_Button_IDs[] = { "0" , "IDOK" , "IDCANCEL" , "IDABORT" , "IDRETRY" , "IDIGNORE" , "IDYES" , "IDNO" , "IDCLOSE" , "IDHELP" , "IDTRYAGAIN" , "IDCONTINUE" }; void CInArchive::Add_ButtonID(UInt32 buttonID) { Space(); if (buttonID < ARRAY_SIZE(k_Button_IDs)) Script += k_Button_IDs[buttonID]; else { Script += "Button_"; Add_UInt(buttonID); } } bool CInArchive::IsDirectString_Equal(UInt32 offset, const char *s) const { if (offset >= NumStringChars) return false; if (IsUnicode) return AreStringsEqual_16and8(_data + _stringsPos + offset * 2, s); else return strcmp((const char *)(const Byte *)_data + _stringsPos + offset, s) == 0; } static bool StringToUInt32(const char *s, UInt32 &res) { const char *end; if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) res = ConvertHexStringToUInt32(s + 2, &end); else res = ConvertStringToUInt32(s, &end); return (*end == 0); } static const unsigned k_CtlColors_Size = 24; struct CNsis_CtlColors { UInt32 text; // COLORREF UInt32 bkc; // COLORREF UInt32 lbStyle; UInt32 bkb; // HBRUSH Int32 bkmode; Int32 flags; void Parse(const Byte *p); }; void CNsis_CtlColors::Parse(const Byte *p) { text = Get32(p); bkc = Get32(p + 4); lbStyle = Get32(p + 8); bkb = Get32(p + 12); bkmode = (Int32)Get32(p + 16); flags = (Int32)Get32(p + 20); } // Win32 constants #define MY__TRANSPARENT 1 #define MY__OPAQUE 2 #define MY__GENERIC_READ (1 << 31) #define MY__GENERIC_WRITE (1 << 30) #define MY__GENERIC_EXECUTE (1 << 29) #define MY__GENERIC_ALL (1 << 28) #define MY__CREATE_NEW 1 #define MY__CREATE_ALWAYS 2 #define MY__OPEN_EXISTING 3 #define MY__OPEN_ALWAYS 4 #define MY__TRUNCATE_EXISTING 5 // text/bg colors #define kColorsFlags_TEXT 1 #define kColorsFlags_TEXT_SYS 2 #define kColorsFlags_BK 4 #define kColorsFlags_BK_SYS 8 #define kColorsFlags_BKB 16 void CInArchive::Add_Color2(UInt32 v) { v = ((v & 0xFF) << 16) | (v & 0xFF00) | ((v >> 16) & 0xFF); char sz[32]; for (int i = 5; i >= 0; i--) { unsigned t = v & 0xF; v >>= 4; sz[i] = (char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))); } sz[6] = 0; Script += sz; } void CInArchive::Add_ColorParam(UInt32 v) { Space(); Add_Color2(v); } void CInArchive::Add_Color(UInt32 v) { Script += "0x"; Add_Color2(v); } #define MY__SW_HIDE 0 #define MY__SW_SHOWNORMAL 1 #define MY__SW_SHOWMINIMIZED 2 #define MY__SW_SHOWMINNOACTIVE 7 #define MY__SW_SHOWNA 8 static const char * const kShowWindow_Commands[] = { "HIDE" , "SHOWNORMAL" // "NORMAL" , "SHOWMINIMIZED" , "SHOWMAXIMIZED" // "MAXIMIZE" , "SHOWNOACTIVATE" , "SHOW" , "MINIMIZE" , "SHOWMINNOACTIVE" , "SHOWNA" , "RESTORE" , "SHOWDEFAULT" , "FORCEMINIMIZE" // "MAX" }; static void Add_ShowWindow_Cmd_2(AString &s, UInt32 cmd) { if (cmd < ARRAY_SIZE(kShowWindow_Commands)) { s += "SW_"; s += kShowWindow_Commands[cmd]; } else UIntToString(s, cmd); } void CInArchive::Add_ShowWindow_Cmd(UInt32 cmd) { if (cmd < ARRAY_SIZE(kShowWindow_Commands)) { Script += "SW_"; Script += kShowWindow_Commands[cmd]; } else Add_UInt(cmd); } void CInArchive::Add_TypeFromList(const char * const *table, unsigned tableSize, UInt32 type) { if (type < tableSize) Script += table[type]; else { Script += '_'; Add_UInt(type); } } #define ADD_TYPE_FROM_LIST(table, type) Add_TypeFromList(table, ARRAY_SIZE(table), type) enum { k_ExecFlags_AutoClose, k_ExecFlags_ShellVarContext, k_ExecFlags_Errors, k_ExecFlags_Abort, k_ExecFlags_RebootFlag, k_ExecFlags_reboot_called, k_ExecFlags_cur_insttype, k_ExecFlags_plugin_api_version, k_ExecFlags_Silent, k_ExecFlags_InstDirError, k_ExecFlags_rtl, k_ExecFlags_ErrorLevel, k_ExecFlags_RegView, k_ExecFlags_DetailsPrint = 13, }; // Names for NSIS exec_flags_t structure vars static const char * const kExecFlags_VarsNames[] = { "AutoClose" // autoclose; , "ShellVarContext" // all_user_var; , "Errors" // exec_error; , "Abort" // abort; , "RebootFlag" // exec_reboot; // NSIS_SUPPORT_REBOOT , "reboot_called" // reboot_called; // NSIS_SUPPORT_REBOOT , "cur_insttype" // XXX_cur_insttype; // depreacted , "plugin_api_version" // plugin_api_version; // see NSISPIAPIVER_CURR // used to be XXX_insttype_changed , "Silent" // silent; // NSIS_CONFIG_SILENT_SUPPORT , "InstDirError" // instdir_error; , "rtl" // rtl; , "ErrorLevel" // errlvl; , "RegView" // alter_reg_view; , "DetailsPrint" // status_update; }; void CInArchive::Add_ExecFlags(UInt32 flagsType) { ADD_TYPE_FROM_LIST(kExecFlags_VarsNames, flagsType); } // ---------- Page ---------- // page flags #define PF_CANCEL_ENABLE 4 #define PF_LICENSE_FORCE_SELECTION 32 #define PF_LICENSE_NO_FORCE_SELECTION 64 #define PF_PAGE_EX 512 #define PF_DIR_NO_BTN_DISABLE 1024 /* #define PF_LICENSE_SELECTED 1 #define PF_NEXT_ENABLE 2 #define PF_BACK_SHOW 8 #define PF_LICENSE_STREAM 16 #define PF_NO_NEXT_FOCUS 128 #define PF_BACK_ENABLE 256 */ // page window proc enum { PWP_LICENSE, PWP_SELCOM, PWP_DIR, PWP_INSTFILES, PWP_UNINST, PWP_COMPLETED, PWP_CUSTOM }; static const char * const kPageTypes[] = { "license" , "components" , "directory" , "instfiles" , "uninstConfirm" , "COMPLETED" , "custom" }; #define SET_FUNC_REF(x, flag) if ((Int32)(x) >= 0 && (x) < bh.Num) \ { labels[x] = (labels[x] & ~CMD_REF_Page_Mask) | ((flag) | (pageIndex << CMD_REF_Page_NumShifts)); } // #define IDD_LICENSE 102 #define IDD_LICENSE_FSRB 108 #define IDD_LICENSE_FSCB 109 void CInArchive::AddPageOption1(UInt32 param, const char *name) { if (param == 0) return; TabString(name); AddParam(param); NewLine(); } void CInArchive::AddPageOption(const UInt32 *params, unsigned num, const char *name) { num = GetNumParams(params, num); if (num == 0) return; TabString(name); AddParams(params, num); NewLine(); } void CInArchive::Separator() { AddLF(); AddCommentAndString("--------------------"); AddLF(); } void CInArchive::Space() { Script += ' '; } void CInArchive::Tab() { Script += " "; } void CInArchive::Tab(bool commented) { Script += commented ? " ; " : " "; } void CInArchive::BigSpaceComment() { Script += " ; "; } void CInArchive::SmallSpaceComment() { Script += " ; "; } void CInArchive::AddCommentAndString(const char *s) { Script += "; "; Script += s; } void CInArchive::AddError(const char *s) { BigSpaceComment(); Script += "!!! ERROR: "; Script += s; } void CInArchive::AddErrorLF(const char *s) { AddError(s); AddLF(); } void CInArchive::CommentOpen() { AddStringLF("/*"); } void CInArchive::CommentClose() { AddStringLF("*/"); } void CInArchive::AddLF() { Script += CR_LF; } void CInArchive::AddQuotes() { Script += "\"\""; } void CInArchive::TabString(const char *s) { Tab(); Script += s; } void CInArchive::AddStringLF(const char *s) { Script += s; AddLF(); } // ---------- Section ---------- static const char * const kSection_VarsNames[] = { "Text" , "InstTypes" , "Flags" , "Code" , "CodeSize" , "Size" // size in KB }; void CInArchive::Add_SectOp(UInt32 opType) { ADD_TYPE_FROM_LIST(kSection_VarsNames, opType); } void CSection::Parse(const Byte *p) { Name = Get32(p); InstallTypes = Get32(p + 4); Flags = Get32(p + 8); StartCmdIndex = Get32(p + 12); NumCommands = Get32(p + 16); SizeKB = Get32(p + 20); }; // used for section->flags #define SF_SELECTED (1 << 0) #define SF_SECGRP (1 << 1) #define SF_SECGRPEND (1 << 2) #define SF_BOLD (1 << 3) #define SF_RO (1 << 4) #define SF_EXPAND (1 << 5) #define SF_PSELECTED (1 << 6) #define SF_TOGGLED (1 << 7) #define SF_NAMECHG (1 << 8) bool CInArchive::PrintSectionBegin(const CSection §, unsigned index) { AString name; if (sect.Flags & SF_BOLD) name += '!'; AString s2; ReadString2(s2, sect.Name); if (!IsInstaller) { if (!StringsAreEqualNoCase_Ascii(s2, "uninstall")) name += "un."; } name += s2; if (sect.Flags & SF_SECGRPEND) { AddStringLF("SectionGroupEnd"); return true; } if (sect.Flags & SF_SECGRP) { Script += "SectionGroup"; if (sect.Flags & SF_EXPAND) Script += " /e"; SpaceQuStr(name); Script += " ; Section"; AddParam_UInt(index); NewLine(); return true; } Script += "Section"; if ((sect.Flags & SF_SELECTED) == 0) Script += " /o"; if (!name.IsEmpty()) SpaceQuStr(name); /* if (!name.IsEmpty()) Script += ' '; else */ SmallSpaceComment(); Script += "Section_"; Add_UInt(index); /* Script += " ; flags = "; Add_Hex(Script, sect.Flags); */ NewLine(); if (sect.SizeKB != 0) { // probably we must show AddSize, only if there is additional size. Tab(); AddCommentAndString("AddSize"); AddParam_UInt(sect.SizeKB); AddLF(); } bool needSectionIn = (sect.Name != 0 && sect.InstallTypes != 0) || (sect.Name == 0 && sect.InstallTypes != 0xFFFFFFFF); if (needSectionIn || (sect.Flags & SF_RO) != 0) { TabString("SectionIn"); UInt32 instTypes = sect.InstallTypes; for (int i = 0; i < 32; i++, instTypes >>= 1) if ((instTypes & 1) != 0) { AddParam_UInt(i + 1); } if ((sect.Flags & SF_RO) != 0) Script += " RO"; AddLF(); } return false; } void CInArchive::PrintSectionEnd() { AddStringLF("SectionEnd"); AddLF(); } // static const unsigned kOnFuncShift = 4; void CInArchive::ClearLangComment() { langStrIDs.Clear(); } void CInArchive::PrintNumComment(const char *name, UInt32 value) { // size_t len = Script.Len(); AddCommentAndString(name); Script += ": "; Add_UInt(value); AddLF(); /* len = Script.Len() - len; char sz[16]; ConvertUInt32ToString(value, sz); len += MyStringLen(sz); for (; len < 20; len++) Space(); AddStringLF(sz); */ } void CInArchive::NewLine() { if (!langStrIDs.IsEmpty()) { BigSpaceComment(); for (unsigned i = 0; i < langStrIDs.Size() && i < 20; i++) { /* if (i != 0) Script += ' '; */ UInt32 langStr = langStrIDs[i]; if (langStr >= _numLangStrings) { AddError("langStr"); break; } UInt32 param = Get32(_mainLang + langStr * 4); if (param != 0) AddParam(param); } ClearLangComment(); } AddLF(); } static const UInt32 kPageSize = 16 * 4; static const char * const k_SetOverwrite_Modes[] = { "on" , "off" , "try" , "ifnewer" , "ifdiff" // "lastused" }; void CInArchive::MessageBox_MB_Part(UInt32 param) { { UInt32 v = param & 0xF; Script += " MB_"; if (v < ARRAY_SIZE(k_MB_Buttons)) Script += k_MB_Buttons[v]; else { Script += "Buttons_"; Add_UInt(v); } } { UInt32 icon = (param >> 4) & 0x7; if (icon != 0) { Script += "|MB_"; if (icon < ARRAY_SIZE(k_MB_Icons) && k_MB_Icons[icon] != 0) Script += k_MB_Icons[icon]; else { Script += "Icon_"; Add_UInt(icon); } } } if ((param & 0x80) != 0) Script += "|MB_USERICON"; { UInt32 defButton = (param >> 8) & 0xF; if (defButton != 0) { Script += "|MB_DEFBUTTON"; Add_UInt(defButton + 1); } } { UInt32 modal = (param >> 12) & 0x3; if (modal == 1) Script += "|MB_SYSTEMMODAL"; else if (modal == 2) Script += "|MB_TASKMODAL"; else if (modal == 3) Script += "|0x3000"; UInt32 flags = (param >> 14); for (unsigned i = 0; i < ARRAY_SIZE(k_MB_Flags); i++) if ((flags & (1 << i)) != 0) { Script += "|MB_"; Script += k_MB_Flags[i]; } } } #define GET_CMD_PARAM(ppp, index) Get32((ppp) + 4 + (index) * 4) static const Byte k_InitPluginDir_Commands[] = { 13, 26, 31, 13, 19, 21, 11, 14, 25, 31, 1, 22, 4, 1 }; bool CInArchive::CompareCommands(const Byte *rawCmds, const Byte *sequence, size_t numCommands) { for (UInt32 kkk = 0; kkk < numCommands; kkk++, rawCmds += kCmdSize) if (GetCmd(Get32(rawCmds)) != sequence[kkk]) return false; return true; } #endif static const UInt32 kSectionSize_base = 6 * 4; static const UInt32 kSectionSize_8bit = kSectionSize_base + 1024; static const UInt32 kSectionSize_16bit = kSectionSize_base + 1024 * 2; static const UInt32 kSectionSize_16bit_Big = kSectionSize_base + 8196 * 2; // 8196 is default string length in NSIS-Unicode since 2.37.3 static void AddString(AString &dest, const char *src) { dest.Add_Space_if_NotEmpty(); dest += src; } AString CInArchive::GetFormatDescription() const { AString s = "NSIS-"; char c; if (IsPark()) { s += "Park-"; c = '1'; if (NsisType == k_NsisType_Park2) c = '2'; else if (NsisType == k_NsisType_Park3) c = '3'; } else { c = '2'; if (NsisType == k_NsisType_Nsis3) c = '3'; } s += c; if (IsNsis200) s += ".00"; else if (IsNsis225) s += ".25"; if (IsUnicode) AddString(s, "Unicode"); if (LogCmdIsEnabled) AddString(s, "log"); if (BadCmd >= 0) { AddString(s, "BadCmd="); UIntToString(s, BadCmd); } return s; } #ifdef NSIS_SCRIPT unsigned CInArchive::GetNumSupportedCommands() const { unsigned numCmds = IsPark() ? kNumCmds : kNumCmds - kNumAdditionalParkCmds; if (!LogCmdIsEnabled) numCmds--; if (!IsUnicode) numCmds -= 2; return numCmds; } #endif UInt32 CInArchive::GetCmd(UInt32 a) { if (!IsPark()) { if (!LogCmdIsEnabled) return a; if (a < EW_SECTIONSET) return a; if (a == EW_SECTIONSET) return EW_LOG; return a - 1; } if (a < EW_REGISTERDLL) return a; if (NsisType >= k_NsisType_Park2) { if (a == EW_REGISTERDLL) return EW_GETFONTVERSION; a--; } if (NsisType >= k_NsisType_Park3) { if (a == EW_REGISTERDLL) return EW_GETFONTNAME; a--; } if (a >= EW_FSEEK) { if (IsUnicode) { if (a == EW_FSEEK) return EW_FPUTWS; if (a == EW_FSEEK + 1) return EW_FPUTWS + 1; a -= 2; } if (a >= EW_SECTIONSET && LogCmdIsEnabled) { if (a == EW_SECTIONSET) return EW_LOG; return a - 1; } if (a == EW_FPUTWS) return EW_FINDPROC; // if (a > EW_FPUTWS) return 0; } return a; } void CInArchive::FindBadCmd(const CBlockHeader &bh, const Byte *p) { BadCmd = -1; for (UInt32 kkk = 0; kkk < bh.Num; kkk++, p += kCmdSize) { UInt32 id = GetCmd(Get32(p)); if (id >= kNumCmds) continue; if (BadCmd >= 0 && id >= (unsigned)BadCmd) continue; unsigned i; if (id == EW_GETLABELADDR || id == EW_GETFUNCTIONADDR) { BadCmd = id; continue; } for (i = 6; i != 0; i--) { UInt32 param = Get32(p + i * 4); if (param != 0) break; } if (id == EW_FINDPROC && i == 0) { BadCmd = id; continue; } if (k_Commands[id].NumParams < i) BadCmd = id; } } /* We calculate the number of parameters in commands to detect layout of commands. It's not very good way. If you know simpler and more robust way to detect Version and layout, please write to 7-Zip forum */ void CInArchive::DetectNsisType(const CBlockHeader &bh, const Byte *p) { bool strongPark = false; bool strongNsis = false; { const Byte *strData = _data + _stringsPos; if (IsUnicode) { UInt32 num = NumStringChars; for (UInt32 i = 0; i < num; i++) { if (Get16(strData + i * 2) == 0) { unsigned c2 = Get16(strData + 2 + i * 2); // if (c2 <= NS_3_CODE_SKIP && c2 != NS_3_CODE_SHELL) if (c2 == NS_3_CODE_VAR) { // it can be TXT/RTF string with marker char (1 or 2). so we must next char // const wchar_t *p2 = (const wchar_t *)(strData + i * 2 + 2); // p2 = p2; if ((Get16(strData + 3 + i * 2) & 0x8000) != 0) { NsisType = k_NsisType_Nsis3; strongNsis = true; break; } } } } if (!strongNsis) { NsisType = k_NsisType_Park1; strongPark = true; } } else { UInt32 num = NumStringChars; for (UInt32 i = 0; i < num; i++) { if (strData[i] == 0) { Byte c2 = strData[i + 1]; // it can be TXT/RTF with marker char (1 or 2). so we must check next char // for marker=1 (txt) if (c2 == NS_3_CODE_VAR) // if (c2 <= NS_3_CODE_SKIP && c2 != NS_3_CODE_SHELL && c2 != 1) { if ((strData[i+ 2] & 0x80) != 0) { // const char *p2 = (const char *)(strData + i + 1); // p2 = p2; NsisType = k_NsisType_Nsis3; strongNsis = true; break; } } } } } } if (NsisType == k_NsisType_Nsis2 && !IsUnicode) { const Byte *p2 = p; for (UInt32 kkk = 0; kkk < bh.Num; kkk++, p2 += kCmdSize) { UInt32 cmd = GetCmd(Get32(p2)); if (cmd != EW_GETDLGITEM && cmd != EW_ASSIGNVAR) continue; UInt32 params[kNumCommandParams]; for (unsigned i = 0; i < kNumCommandParams; i++) params[i] = Get32(p2 + 4 + 4 * i); if (cmd == EW_GETDLGITEM) { // we can use also EW_SETCTLCOLORS if (IsVarStr(params[1], kVar_HWNDPARENT_225)) { IsNsis225 = true; if (params[0] == kVar_Spec_OUTDIR_225) { IsNsis200 = true; break; } } } else // if (cmd == EW_ASSIGNVAR) { if (params[0] == kVar_Spec_OUTDIR_225 && params[2] == 0 && params[3] == 0 && IsVarStr(params[1], kVar_OUTDIR)) IsNsis225 = true; } } } bool parkVer_WasDetected = false; if (!strongNsis && !IsNsis225 && !IsNsis200) { // it must be before FindBadCmd(bh, p); unsigned mask = 0; unsigned numInsertMax = IsUnicode ? 4 : 2; const Byte *p2 = p; for (UInt32 kkk = 0; kkk < bh.Num; kkk++, p2 += kCmdSize) { UInt32 cmd = Get32(p2); // we use original (not converted) command if (cmd < EW_WRITEUNINSTALLER || cmd > EW_WRITEUNINSTALLER + numInsertMax) continue; UInt32 params[kNumCommandParams]; for (unsigned i = 0; i < kNumCommandParams; i++) params[i] = Get32(p2 + 4 + 4 * i); if (params[4] != 0 || params[5] != 0 || params[0] <= 1 || params[3] <= 1) continue; UInt32 altParam = params[3]; if (!IsGoodString(params[0]) || !IsGoodString(altParam)) continue; UInt32 additional = 0; if (GetVarIndexFinished(altParam, '\\', additional) != kVar_INSTDIR) continue; if (AreTwoParamStringsEqual(altParam + additional, params[0])) { unsigned numInserts = cmd - EW_WRITEUNINSTALLER; mask |= (1 << numInserts); } } if (mask == 1) { parkVer_WasDetected = true; // it can be original NSIS or Park-1 } else if (mask != 0) { ENsisType newType = NsisType; if (IsUnicode) switch (mask) { case (1 << 3): newType = k_NsisType_Park2; break; case (1 << 4): newType = k_NsisType_Park3; break; } else switch (mask) { case (1 << 1): newType = k_NsisType_Park2; break; case (1 << 2): newType = k_NsisType_Park3; break; } if (newType != NsisType) { parkVer_WasDetected = true; NsisType = newType; } } } FindBadCmd(bh, p); /* if (strongNsis) return; */ if (BadCmd < EW_REGISTERDLL) return; /* // in ANSI archive we don't check Park and log version if (!IsUnicode) return; */ // We can support Park-ANSI archives, if we remove if (strongPark) check if (strongPark && !parkVer_WasDetected) { if (BadCmd < EW_SECTIONSET) { NsisType = k_NsisType_Park3; LogCmdIsEnabled = true; // version 3 is provided with log enabled FindBadCmd(bh, p); if (BadCmd > 0 && BadCmd < EW_SECTIONSET) { NsisType = k_NsisType_Park2; LogCmdIsEnabled = false; FindBadCmd(bh, p); if (BadCmd > 0 && BadCmd < EW_SECTIONSET) { NsisType = k_NsisType_Park1; FindBadCmd(bh, p); } } } } if (BadCmd >= EW_SECTIONSET) { LogCmdIsEnabled = !LogCmdIsEnabled; FindBadCmd(bh, p); if (BadCmd >= EW_SECTIONSET && LogCmdIsEnabled) { LogCmdIsEnabled = false; FindBadCmd(bh, p); } } } Int32 CInArchive::GetVarIndex(UInt32 strPos) const { if (strPos >= NumStringChars) return -1; if (IsUnicode) { if (NumStringChars - strPos < 3 * 2) return -1; const Byte *p = _data + _stringsPos + strPos * 2; unsigned code = Get16(p); if (IsPark()) { if (code != PARK_CODE_VAR) return -1; UInt32 n = Get16(p + 2); if (n == 0) return -1; CONVERT_NUMBER_PARK(n); return (Int32)n; } // NSIS-3 { if (code != NS_3_CODE_VAR) return -1; UInt32 n = Get16(p + 2); if (n == 0) return -1; CONVERT_NUMBER_NS_3_UNICODE(n); return (Int32)n; } } if (NumStringChars - strPos < 4) return -1; const Byte *p = _data + _stringsPos + strPos; unsigned c = *p; if (NsisType == k_NsisType_Nsis3) { if (c != NS_3_CODE_VAR) return -1; } else if (c != NS_CODE_VAR) return -1; unsigned c0 = p[1]; if (c0 == 0) return -1; unsigned c1 = p[2]; if (c1 == 0) return -1; return DECODE_NUMBER_FROM_2_CHARS(c0, c1); } Int32 CInArchive::GetVarIndex(UInt32 strPos, UInt32 &resOffset) const { resOffset = 0; Int32 varIndex = GetVarIndex(strPos); if (varIndex < 0) return varIndex; if (IsUnicode) { if (NumStringChars - strPos < 2 * 2) return -1; resOffset = 2; } else { if (NumStringChars - strPos < 3) return -1; resOffset = 3; } return varIndex; } Int32 CInArchive::GetVarIndexFinished(UInt32 strPos, Byte endChar, UInt32 &resOffset) const { resOffset = 0; Int32 varIndex = GetVarIndex(strPos); if (varIndex < 0) return varIndex; if (IsUnicode) { if (NumStringChars - strPos < 3 * 2) return -1; const Byte *p = _data + _stringsPos + strPos * 2; if (Get16(p + 4) != endChar) return -1; resOffset = 3; } else { if (NumStringChars - strPos < 4) return -1; const Byte *p = _data + _stringsPos + strPos; if (p[3] != endChar) return -1; resOffset = 4; } return varIndex; } bool CInArchive::IsVarStr(UInt32 strPos, UInt32 varIndex) const { if (varIndex > (UInt32)0x7FFF) return false; UInt32 resOffset; return GetVarIndexFinished(strPos, 0, resOffset) == (Int32)varIndex; } bool CInArchive::IsAbsolutePathVar(UInt32 strPos) const { Int32 varIndex = GetVarIndex(strPos); if (varIndex < 0) return false; switch (varIndex) { case kVar_INSTDIR: case kVar_EXEDIR: case kVar_TEMP: case kVar_PLUGINSDIR: return true; } return false; } #define IS_LETTER_CHAR(c) ((c) >= 'a' && (c) <= 'z' || (c) >= 'A' && (c) <= 'Z') // We use same check as in NSIS decoder bool IsDrivePath(const wchar_t *s) { return IS_LETTER_CHAR(s[0]) && s[1] == ':' /* && s[2] == '\\' */ ; } bool IsDrivePath(const char *s) { return IS_LETTER_CHAR(s[0]) && s[1] == ':' /* && s[2] == '\\' */ ; } static bool IsAbsolutePath(const wchar_t *s) { return s[0] == WCHAR_PATH_SEPARATOR && s[1] == WCHAR_PATH_SEPARATOR || IsDrivePath(s); } static bool IsAbsolutePath(const char *s) { return s[0] == CHAR_PATH_SEPARATOR && s[1] == CHAR_PATH_SEPARATOR || IsDrivePath(s); } void CInArchive::SetItemName(CItem &item, UInt32 strPos) { ReadString2_Raw(strPos); bool isAbs = IsAbsolutePathVar(strPos); if (IsUnicode) { item.NameU = Raw_UString; if (!isAbs && !IsAbsolutePath(Raw_UString)) item.Prefix = UPrefixes.Size() - 1; } else { item.NameA = Raw_AString; if (!isAbs && !IsAbsolutePath(Raw_AString)) item.Prefix = APrefixes.Size() - 1; } } HRESULT CInArchive::ReadEntries(const CBlockHeader &bh) { #ifdef NSIS_SCRIPT CDynLimBuf &s = Script; CObjArray labels; labels.Alloc(bh.Num); memset(labels, 0, bh.Num * sizeof(UInt32)); { const Byte *p = _data; UInt32 i; for (i = 0; i < numOnFunc; i++) { UInt32 func = Get32(p + onFuncOffset + 4 * i); if (func < bh.Num) labels[func] = (labels[func] & ~CMD_REF_OnFunc_Mask) | (CMD_REF_OnFunc | (i << CMD_REF_OnFunc_NumShifts)); } } /* { for (int i = 0; i < OnFuncs.Size(); i++) { UInt32 address = OnFuncs[i] >> kOnFuncShift; if (address < bh.Num) } } */ if (bhPages.Num != 0) { Separator(); PrintNumComment("PAGES", bhPages.Num); if (bhPages.Num > (1 << 12) || bhPages.Offset > _size || bhPages.Num * kPageSize > _size - bhPages.Offset) { AddErrorLF("Pages error"); } else { AddLF(); const Byte *p = _data + bhPages.Offset; for (UInt32 pageIndex = 0; pageIndex < bhPages.Num; pageIndex++, p += kPageSize) { UInt32 dlgID = Get32(p); UInt32 wndProcID = Get32(p + 4); UInt32 preFunc = Get32(p + 8); UInt32 showFunc = Get32(p + 12); UInt32 leaveFunc = Get32(p + 16); UInt32 flags = Get32(p + 20); UInt32 caption = Get32(p + 24); // UInt32 back = Get32(p + 28); UInt32 next = Get32(p + 32); // UInt32 clickNext = Get32(p + 36); // UInt32 cancel = Get32(p + 40); UInt32 params[5]; for (int i = 0; i < 5; i++) params[i] = Get32(p + 44 + 4 * i); SET_FUNC_REF(preFunc, CMD_REF_Pre); SET_FUNC_REF(showFunc, CMD_REF_Show); SET_FUNC_REF(leaveFunc, CMD_REF_Leave); if (wndProcID == PWP_COMPLETED) CommentOpen(); AddCommentAndString("Page "); Add_UInt(pageIndex); AddLF(); if (flags & PF_PAGE_EX) { s += "PageEx "; if (!IsInstaller) s += "un."; } else s += IsInstaller ? "Page " : "UninstPage "; if (wndProcID < ARRAY_SIZE(kPageTypes)) s += kPageTypes[wndProcID]; else Add_UInt(wndProcID); bool needCallbacks = ( (Int32)preFunc >= 0 || (Int32)showFunc >= 0 || (Int32)leaveFunc >= 0); if (flags & PF_PAGE_EX) { AddLF(); if (needCallbacks) TabString("PageCallbacks"); } if (needCallbacks) { AddParam_Func(labels, preFunc); // it's creator_function for PWP_CUSTOM if (wndProcID != PWP_CUSTOM) { AddParam_Func(labels, showFunc); } AddParam_Func(labels, leaveFunc); } if ((flags & PF_PAGE_EX) == 0) { // AddOptionalParam(caption); if (flags & PF_CANCEL_ENABLE) s += " /ENABLECANCEL"; AddLF(); } else { AddLF(); AddPageOption1(caption, "Caption"); } if (wndProcID == PWP_LICENSE) { if ((flags & PF_LICENSE_NO_FORCE_SELECTION) != 0 || (flags & PF_LICENSE_FORCE_SELECTION) != 0) { TabString("LicenseForceSelection "); if (flags & PF_LICENSE_NO_FORCE_SELECTION) s += "off"; else { if (dlgID == IDD_LICENSE_FSCB) s += "checkbox"; else if (dlgID == IDD_LICENSE_FSRB) s += "radiobuttons"; else Add_UInt(dlgID); AddOptionalParams(params + 2, 2); } NewLine(); } if (params[0] != 0 || next != 0) { TabString("LicenseText"); AddParam(params[0]); AddOptionalParam(next); NewLine(); } if (params[1] != 0) { TabString("LicenseData"); if ((Int32)params[1] < 0) AddParam(params[1]); else AddLicense(params[1], -1); ClearLangComment(); NewLine(); } } else if (wndProcID == PWP_SELCOM) AddPageOption(params, 3, "ComponentsText"); else if (wndProcID == PWP_DIR) { AddPageOption(params, 4, "DirText"); if (params[4] != 0) { TabString("DirVar"); AddParam_Var(params[4] - 1); AddLF(); } if (flags & PF_DIR_NO_BTN_DISABLE) { TabString("DirVerify leave"); AddLF(); } } else if (wndProcID == PWP_INSTFILES) { AddPageOption1(params[2], "CompletedText"); AddPageOption1(params[1], "DetailsButtonText"); } else if (wndProcID == PWP_UNINST) { if (params[4] != 0) { TabString("DirVar"); AddParam_Var(params[4] - 1); AddLF(); } AddPageOption(params, 2, "UninstallText"); } if (flags & PF_PAGE_EX) { s += "PageExEnd"; NewLine(); } if (wndProcID == PWP_COMPLETED) CommentClose(); NewLine(); } } } CObjArray Sections; { Separator(); PrintNumComment("SECTIONS", bhSections.Num); PrintNumComment("COMMANDS", bh.Num); AddLF(); if (bhSections.Num > (1 << 15) // || bhSections.Offset > _size // || (bhSections.Num * SectionSize > _size - bhSections.Offset) ) { AddErrorLF("Sections error"); } else if (bhSections.Num != 0) { Sections.Alloc((unsigned)bhSections.Num); const Byte *p = _data + bhSections.Offset; for (UInt32 i = 0; i < bhSections.Num; i++, p += SectionSize) { CSection §ion = Sections[i]; section.Parse(p); if (section.StartCmdIndex < bh.Num) labels[section.StartCmdIndex] |= CMD_REF_Section; } } } #endif const Byte *p; UInt32 kkk; #ifdef NSIS_SCRIPT p = _data + bh.Offset; for (kkk = 0; kkk < bh.Num; kkk++, p += kCmdSize) { UInt32 commandId = GetCmd(Get32(p)); UInt32 mask; switch (commandId) { case EW_NOP: mask = 1 << 0; break; case EW_IFFILEEXISTS: mask = 3 << 1; break; case EW_IFFLAG: mask = 3 << 0; break; case EW_MESSAGEBOX: mask = 5 << 3; break; case EW_STRCMP: mask = 3 << 2; break; case EW_INTCMP: mask = 7 << 2; break; case EW_ISWINDOW: mask = 3 << 1; break; case EW_CALL: { if (Get32(p + 4 + 4) == 1) // it's Call :Label { mask = 1 << 0; break; } UInt32 param0 = Get32(p + 4); if ((Int32)param0 > 0) labels[param0 - 1] |= CMD_REF_Call; continue; } default: continue; } for (unsigned i = 0; mask != 0; i++, mask >>= 1) if (mask & 1) { UInt32 param = Get32(p + 4 + 4 * i); if ((Int32)param > 0 && (Int32)param <= (Int32)bh.Num) labels[param - 1] |= CMD_REF_Goto; } } int InitPluginsDir_Start = -1; int InitPluginsDir_End = -1; p = _data + bh.Offset; for (kkk = 0; kkk < bh.Num; kkk++, p += kCmdSize) { UInt32 flg = labels[kkk]; /* if (IsFunc(flg)) { AddLF(); for (int i = 0; i < 14; i++) { UInt32 commandId = GetCmd(Get32(p + kCmdSize * i)); s += ", "; UIntToString(s, commandId); } AddLF(); } */ if (IsFunc(flg) && bh.Num - kkk >= ARRAY_SIZE(k_InitPluginDir_Commands) && CompareCommands(p, k_InitPluginDir_Commands, ARRAY_SIZE(k_InitPluginDir_Commands))) { InitPluginsDir_Start = kkk; InitPluginsDir_End = kkk + ARRAY_SIZE(k_InitPluginDir_Commands); labels[kkk] |= CMD_REF_InitPluginDir; break; } } #endif // AString prefixA_Temp; // UString prefixU_Temp; // const UInt32 kFindS = 158; #ifdef NSIS_SCRIPT UInt32 curSectionIndex = 0; // UInt32 lastSectionEndCmd = 0xFFFFFFFF; bool sectionIsOpen = false; // int curOnFunc = 0; bool onFuncIsOpen = false; /* for (unsigned yyy = 0; yyy + 3 < _data.Size(); yyy++) { UInt32 val = Get32(_data + yyy); if (val == kFindS) val = val; } */ UInt32 overwrite_State = 0; // "SetOverwrite on" Int32 allowSkipFiles_State = -1; // -1: on, -2: off, >=0 : RAW value UInt32 endCommentIndex = 0; unsigned numSupportedCommands = GetNumSupportedCommands(); #endif p = _data + bh.Offset; UString spec_outdir_U; AString spec_outdir_A; UPrefixes.Add(L"$INSTDIR"); APrefixes.Add("$INSTDIR"); p = _data + bh.Offset; unsigned spec_outdir_VarIndex = IsNsis225 ? kVar_Spec_OUTDIR_225 : kVar_Spec_OUTDIR; for (kkk = 0; kkk < bh.Num; kkk++, p += kCmdSize) { UInt32 commandId; UInt32 params[kNumCommandParams]; commandId = GetCmd(Get32(p)); { for (unsigned i = 0; i < kNumCommandParams; i++) { params[i] = Get32(p + 4 + 4 * i); /* if (params[i] == kFindS) i = i; */ } } #ifdef NSIS_SCRIPT bool IsSectionGroup = false; while (curSectionIndex < bhSections.Num) { const CSection § = Sections[curSectionIndex]; if (sectionIsOpen) { if (sect.StartCmdIndex + sect.NumCommands + 1 != kkk) break; PrintSectionEnd(); sectionIsOpen = false; // lastSectionEndCmd = kkk; curSectionIndex++; continue; } if (sect.StartCmdIndex != kkk) break; if (PrintSectionBegin(sect, curSectionIndex)) { IsSectionGroup = true; curSectionIndex++; // do we need to flush prefixes in new section? // FlushOutPathPrefixes(); } else sectionIsOpen = true; } /* if (curOnFunc < OnFuncs.Size()) { if ((OnFuncs[curOnFunc] >> kOnFuncShift) == kkk) { s += "Function .on"; s += kOnFunc[OnFuncs[curOnFunc++] & ((1 << kOnFuncShift) - 1)]; AddLF(); onFuncIsOpen = true; } } */ if (labels[kkk] != 0 && labels[kkk] != CMD_REF_Section) { UInt32 flg = labels[kkk]; if (IsFunc(flg)) { if ((int)kkk == InitPluginsDir_Start) CommentOpen(); onFuncIsOpen = true; s += "Function "; Add_FuncName(labels, kkk); if (IsPageFunc(flg)) { BigSpaceComment(); s += "Page "; Add_UInt((flg & CMD_REF_Page_Mask) >> CMD_REF_Page_NumShifts); // if (flg & CMD_REF_Creator) s += ", Creator"; if (flg & CMD_REF_Leave) s += ", Leave"; if (flg & CMD_REF_Pre) s += ", Pre"; if (flg & CMD_REF_Show) s += ", Show"; } AddLF(); } if (flg & CMD_REF_Goto) { Add_LabelName(kkk); s += ':'; AddLF(); } } if (commandId != EW_RET) { Tab(kkk < endCommentIndex); } /* UInt32 originalCmd = Get32(p); if (originalCmd >= EW_REGISTERDLL) { UIntToString(s, originalCmd); s += ' '; if (originalCmd != commandId) { UIntToString(s, commandId); s += ' '; } } */ unsigned numSkipParams = 0; if (commandId < ARRAY_SIZE(k_Commands) && commandId < numSupportedCommands) { numSkipParams = k_Commands[commandId].NumParams; const char *sz = k_CommandNames[commandId]; if (sz) s += sz; } else { s += "Command"; Add_UInt(commandId); /* We don't show wrong commands that use overlapped ids. So we change commandId to big value */ if (commandId < (1 << 12)) commandId += (1 << 12); } #endif switch (commandId) { case EW_CREATEDIR: { bool isSetOutPath = (params[1] != 0); if (isSetOutPath) { UInt32 par0 = params[0]; UInt32 resOffset; Int32 idx = GetVarIndex(par0, resOffset); if (idx == (Int32)spec_outdir_VarIndex || idx == kVar_OUTDIR) par0 += resOffset; ReadString2_Raw(par0); if (IsUnicode) { if (idx == (Int32)spec_outdir_VarIndex) Raw_UString.Insert(0, spec_outdir_U); else if (idx == kVar_OUTDIR) Raw_UString.Insert(0, UPrefixes.Back()); UPrefixes.Add(Raw_UString); } else { if (idx == (Int32)spec_outdir_VarIndex) Raw_AString.Insert(0, spec_outdir_A); else if (idx == kVar_OUTDIR) Raw_AString.Insert(0, APrefixes.Back()); APrefixes.Add(Raw_AString); } } #ifdef NSIS_SCRIPT s += isSetOutPath ? "SetOutPath" : "CreateDirectory"; AddParam(params[0]); #endif break; } case EW_ASSIGNVAR: { if (params[0] == spec_outdir_VarIndex) { spec_outdir_U.Empty(); spec_outdir_A.Empty(); if (IsVarStr(params[1], kVar_OUTDIR) && params[2] == 0 && params[3] == 0) { if (IsVarStr(params[1], kVar_OUTDIR)) { spec_outdir_U = UPrefixes.Back(); // outdir_U; spec_outdir_A = APrefixes.Back();// outdir_A; } } } #ifdef NSIS_SCRIPT if (params[2] == 0 && params[3] == 0 && params[4] == 0 && params[5] == 0 && params[1] != 0 && params[1] < NumStringChars) { char sz[16]; ConvertUInt32ToString(kkk + 1, sz); if (IsDirectString_Equal(params[1], sz)) { // we suppose that it's GetCurrentAddress command // but there is probability that it's StrCpy command s += "GetCurrentAddress"; AddParam_Var(params[0]); SmallSpaceComment(); } } s += "StrCpy"; AddParam_Var(params[0]); AddParam(params[1]); AddOptionalParams(params + 2, 2); #endif break; } case EW_EXTRACTFILE: { CItem &item = Items.AddNew(); UInt32 par1 = params[1]; SetItemName(item, par1); item.Pos = params[2]; item.MTime.dwLowDateTime = params[3]; item.MTime.dwHighDateTime = params[4]; #ifdef NSIS_SCRIPT { UInt32 overwrite = params[0] & 0x7; if (overwrite != overwrite_State) { s += "SetOverwrite "; ADD_TYPE_FROM_LIST(k_SetOverwrite_Modes, overwrite); overwrite_State = overwrite; AddLF(); Tab(kkk < endCommentIndex); } } { UInt32 nsisMB = params[0] >> 3; if ((Int32)nsisMB != allowSkipFiles_State) { UInt32 mb = nsisMB & ((1 << 20) - 1); // old/new NSIS UInt32 b1 = nsisMB >> 21; // NSIS 2.06+ UInt32 b2 = nsisMB >> 20; // NSIS old Int32 asf = (Int32)nsisMB; if (mb == (MY__MB_ABORTRETRYIGNORE | MY__MB_ICONSTOP) && (b1 == MY__IDIGNORE || b2 == MY__IDIGNORE)) asf = -1; else if (mb == (MY__MB_RETRYCANCEL | MY__MB_ICONSTOP) && (b1 == MY__IDCANCEL || b2 == MY__IDCANCEL)) asf = -2; else { AddCommentAndString("AllowSkipFiles [Overwrite]: "); MessageBox_MB_Part(mb); if (b1 != 0) { s += " /SD"; Add_ButtonID(b1); } } if (asf != allowSkipFiles_State) { if (asf < 0) { s += "AllowSkipFiles "; s += (asf == -1) ? "on" : "off"; } AddLF(); Tab(kkk < endCommentIndex); } allowSkipFiles_State = (Int32)nsisMB; } } s += "File"; AddParam(params[1]); /* params[5] contains link to LangString (negative value) with NLF_FILE_ERROR or NLF_FILE_ERROR_NOIGNORE message for MessageBox. We don't need to print it. */ #endif if (IsVarStr(par1, 10)) // is $R0 { // we parse InstallLib macro in 7-Zip installers unsigned kBackOffset = 28; if (kkk > 1) { // detect old version of InstallLib macro if (Get32(p - 1 * kCmdSize) == EW_NOP) // goto command kBackOffset -= 2; } if (kkk > kBackOffset) { const Byte *p2 = p - kBackOffset * kCmdSize; UInt32 cmd = Get32(p2); if (cmd == EW_ASSIGNVAR) { UInt32 pars[6]; for (int i = 0; i < 6; i++) pars[i] = Get32(p2 + i * 4 + 4); if (pars[0] == 10 + 4 && pars[2] == 0 && pars[3] == 0) // 10 + 4 means $R4 { item.Prefix = -1; item.NameA.Empty(); item.NameU.Empty(); SetItemName(item, pars[1]); // maybe here we can restore original item name, if new name is empty } } } } /* UInt32 allowIgnore = params[5]; */ break; } case EW_SETFILEATTRIBUTES: { if (kkk > 0 && Get32(p - kCmdSize) == EW_EXTRACTFILE) { if (params[0] == Get32(p - kCmdSize + 4 + 4 * 1)) // compare with PrevCmd.Params[1] { CItem &item = Items.Back(); item.Attrib_Defined = true; item.Attrib = params[1]; } } #ifdef NSIS_SCRIPT AddParam(params[0]); Space(); FlagsToString2(s, g_WinAttrib, ARRAY_SIZE(g_WinAttrib), params[1]); #endif break; } case EW_WRITEUNINSTALLER: { /* NSIS 2.29+ writes alternative path to params[3] "$INSTDIR\\" + Str(params[0]) NSIS installer uses alternative path, if main path from params[0] is not absolute path */ bool pathOk = (params[0] > 0) && IsGoodString(params[0]); if (!pathOk) { #ifdef NSIS_SCRIPT AddError("bad path"); #endif break; } bool altPathOk = true; UInt32 altParam = params[3]; if (altParam != 0) { altPathOk = false; UInt32 additional = 0; if (GetVarIndexFinished(altParam, '\\', additional) == kVar_INSTDIR) altPathOk = AreTwoParamStringsEqual(altParam + additional, params[0]); } #ifdef NSIS_SCRIPT AddParam(params[0]); /* for (int i = 1; i < 3; i++) AddParam_UInt(params[i]); */ if (params[3] != 0) { SmallSpaceComment(); AddParam(params[3]); } #endif if (!altPathOk) { #ifdef NSIS_SCRIPT AddError("alt path error"); #endif } if (BadCmd >= 0 && BadCmd <= EW_WRITEUNINSTALLER) { /* We don't cases with incorrect installer commands. Such bad installer item can break unpacking for other items. */ #ifdef NSIS_SCRIPT AddError("SKIP possible BadCmd"); #endif break; } CItem &item = Items.AddNew();; SetItemName(item, params[0]); item.Pos = params[1]; item.PatchSize = params[2]; item.IsUninstaller = true; /* // we can add second time to test the code CItem item2 = item; item2.NameU += L'2'; item2.NameA += '2'; Items.Add(item2); */ break; } #ifdef NSIS_SCRIPT case EW_RET: { // bool needComment = false; if (onFuncIsOpen) { if (kkk == bh.Num - 1 || IsProbablyEndOfFunc(labels[kkk + 1])) { AddStringLF("FunctionEnd"); if ((int)kkk + 1 == InitPluginsDir_End) CommentClose(); AddLF(); onFuncIsOpen = false; // needComment = true; break; } } // if (!needComment) if (IsSectionGroup) break; if (sectionIsOpen) { const CSection § = Sections[curSectionIndex]; if (sect.StartCmdIndex + sect.NumCommands == kkk) { PrintSectionEnd(); sectionIsOpen = false; curSectionIndex++; break; } // needComment = true; // break; } /* if (needComment) s += " ;"; */ TabString("Return"); AddLF(); break; } case EW_NOP: { if (params[0] == 0) s += "Nop"; else { s += "Goto"; Add_GotoVar(params[0]); } break; } case EW_ABORT: { AddOptionalParam(params[0]); break; } case EW_CALL: { if (kkk + 1 < bh.Num && GetCmd(Get32(p + kCmdSize)) == EW_EXTRACTFILE) { UInt32 par1 = GET_CMD_PARAM(p + kCmdSize, 1); UInt32 pluginPar = 0; if (GetVarIndexFinished(par1, '\\', pluginPar) == kVar_PLUGINSDIR) { pluginPar += par1; UInt32 commandId2 = GetCmd(Get32(p + kCmdSize * 2)); if (commandId2 == EW_SETFLAG || commandId2 == EW_UPDATETEXT) { UInt32 i; for (i = kkk + 3; i < bh.Num; i++) { const Byte *pCmd = p + kCmdSize * (i - kkk); UInt32 commandId3 = GetCmd(Get32(pCmd)); if (commandId3 != EW_PUSHPOP || GET_CMD_PARAM(pCmd, 1) != 0 || GET_CMD_PARAM(pCmd, 2) != 0) break; } if (i < bh.Num) { const Byte *pCmd = p + kCmdSize * (i - kkk); // UInt32 callDll_Param = GET_CMD_PARAM(pCmd, 0); // UInt32 file_Param = GET_CMD_PARAM(p + kCmdSize, 1); if (GetCmd(Get32(pCmd)) == EW_REGISTERDLL && AreTwoParamStringsEqual( GET_CMD_PARAM(pCmd, 0), GET_CMD_PARAM(p + kCmdSize, 1))) { // params[4] = 1 means GetModuleHandle attempt before default LoadLibraryEx; /// new versions of NSIS use params[4] = 1 for Plugin command if (GET_CMD_PARAM(pCmd, 2) == 0 // && GET_CMD_PARAM(pCmd, 4) != 0 ) { { AString s2; ReadString2(s2, pluginPar); if (s2.Len() >= 4 && StringsAreEqualNoCase_Ascii(s2.RightPtr(4), ".dll")) s2.DeleteFrom(s2.Len() - 4); s2 += "::"; AString func; ReadString2(func, GET_CMD_PARAM(pCmd, 1)); s2 += func; Add_QuStr(s2); if (GET_CMD_PARAM(pCmd, 3) == 1) s += " /NOUNLOAD"; for (UInt32 j = i - 1; j >= kkk + 3; j--) { const Byte *pCmd = p + kCmdSize * (j - kkk); AddParam(GET_CMD_PARAM(pCmd, 0)); } NewLine(); Tab(true); endCommentIndex = i + 1; } } } } } } } { const Byte *nextCmd = p + kCmdSize; UInt32 commandId2 = GetCmd(Get32(nextCmd)); if (commandId2 == EW_SETFLAG && GET_CMD_PARAM(nextCmd, 0) == k_ExecFlags_DetailsPrint && GET_CMD_PARAM(nextCmd, 2) != 0) // is "lastused" // || commandId2 == EW_UPDATETEXT) { if ((Int32)params[0] > 0 && labels[params[0] - 1] & CMD_REF_InitPluginDir) { s += "InitPluginsDir"; AddLF(); Tab(true); endCommentIndex = kkk + 2; } } } s += "Call "; if ((Int32)params[0] < 0) Add_Var(-((Int32)params[0] + 1)); else if (params[0] == 0) s += '0'; else { UInt32 val = params[0] - 1; if (params[1] == 1) // it's Call :Label { s += ':'; Add_LabelName(val); } else // if (params[1] == 0) // it's Call Func Add_FuncName(labels, val); } break; } case EW_UPDATETEXT: case EW_SLEEP: { AddParam(params[0]); break; } case EW_CHDETAILSVIEW: { if (params[0] == MY__SW_SHOWNA && params[1] == MY__SW_HIDE) s += " show"; else if (params[1] == MY__SW_SHOWNA && params[0] == MY__SW_HIDE) s += " hide"; else for (int i = 0; i < 2; i++) { Space(); Add_ShowWindow_Cmd(params[i]); } break; } case EW_IFFILEEXISTS: { AddParam(params[0]); Add_GotoVars2(¶ms[1]); break; } case EW_SETFLAG: { AString temp; ReadString2(temp, params[1]); if (params[0] == k_ExecFlags_Errors && params[2] == 0) { s += (temp.Len() == 1 && temp[0] == '0') ? "ClearErrors" : "SetErrors"; break; } s += "Set"; Add_ExecFlags(params[0]); if (params[2] != 0) { s += " lastused"; break; } UInt32 v; if (StringToUInt32(temp, v)) { const char *s2 = NULL; switch (params[0]) { case k_ExecFlags_AutoClose: case k_ExecFlags_RebootFlag: if (v < 2) s2 = (v == 0) ? "false" : "true"; break; case k_ExecFlags_ShellVarContext: if (v < 2) s2 = (v == 0) ? "current" : "all"; break; case k_ExecFlags_Silent: if (v < 2) s2 = (v == 0) ? "normal" : "silent"; break; case k_ExecFlags_RegView: if (v == 0) s2 = "32"; else if (v == 256) s2 = "64"; break; case k_ExecFlags_DetailsPrint: if (v == 0) s2 = "both"; else if (v == 2) s2 = "textonly"; else if (v == 4) s2 = "listonly"; else if (v == 6) s2 = "none"; } if (s2) { s += ' '; s += s2; break; } } SpaceQuStr(temp); break; } case EW_IFFLAG: { Add_ExecFlags(params[2]); Add_GotoVars2(¶ms[0]); /* static const unsigned kIfErrors = 2; if (params[2] != kIfErrors && params[3] != 0xFFFFFFFF || params[2] == kIfErrors && params[3] != 0) { s += " # FLAG &= "; AddParam_UInt(params[3]); } */ break; } case EW_GETFLAG: { Add_ExecFlags(params[1]); AddParam_Var(params[0]); break; } case EW_RENAME: { if (params[2] != 0) s += k_REBOOTOK; AddParams(params, 2); if (params[3] != 0) { SmallSpaceComment(); AddParam(params[3]); // rename comment for log file } break; } case EW_GETFULLPATHNAME: { if (params[2] == 0) s += " /SHORT"; AddParam_Var(params[1]); AddParam(params[0]); break; } case EW_SEARCHPATH: case EW_STRLEN: { AddParam_Var(params[0]); AddParam(params[1]); break; } case EW_GETTEMPFILENAME: { AddParam_Var(params[0]); AString temp; ReadString2(temp, params[1]); if (temp != "$TEMP") SpaceQuStr(temp); break; } case EW_DELETEFILE: { UInt32 flag = params[1]; if ((flag & DEL_REBOOT) != 0) s += k_REBOOTOK; AddParam(params[0]); break; } case EW_MESSAGEBOX: { MessageBox_MB_Part(params[0]); AddParam(params[1]); { UInt32 buttonID = (params[0] >> 21); // NSIS 2.06+ if (buttonID != 0) { s += " /SD"; Add_ButtonID(buttonID); } } for (int i = 2; i < 6; i += 2) if (params[i] != 0) { Add_ButtonID(params[i]); Add_GotoVar1(params[i + 1]); } break; } case EW_RMDIR: { UInt32 flag = params[1]; if ((flag & DEL_RECURSE) != 0) s += " /r"; if ((flag & DEL_REBOOT) != 0) s += k_REBOOTOK; AddParam(params[0]); break; } case EW_STRCMP: { if (params[4] != 0) s += 'S'; AddParams(params, 2); Add_GotoVars2(¶ms[2]); break; } case EW_READENVSTR: { s += (params[2] != 0) ? "ReadEnvStr" : "ExpandEnvStrings"; AddParam_Var(params[0]); AString temp; ReadString2(temp, params[1]); if (params[2] != 0 &&temp.Len() >= 2 && temp[0] == '%' && temp.Back() == '%') { temp.DeleteBack(); temp.Delete(0); } SpaceQuStr(temp); break; } case EW_INTCMP: { if (params[5] != 0) s += 'U'; AddParams(params, 2); Add_GotoVar1(params[2]); if (params[3] != 0 || params[4] != 0) Add_GotoVars2(params + 3); break; } case EW_INTOP: { AddParam_Var(params[0]); const char *kOps = "+-*/|&^!|&%<>"; // NSIS 2.01+ // "+-*/|&^!|&%"; // NSIS 2.0b4+ // "+-*/|&^~!|&%"; // NSIS old UInt32 opIndex = params[3]; char c = (opIndex < 13) ? kOps[opIndex] : '?'; char c2 = (opIndex < 8 || opIndex == 10) ? (char)0 : c; int numOps = (opIndex == 7) ? 1 : 2; AddParam(params[1]); if (numOps == 2 && c == '^' && IsDirectString_Equal(params[2], "0xFFFFFFFF")) s += " ~ ;"; Space(); s += c; if (numOps != 1) { if (c2 != 0) s += c2; AddParam(params[2]); } break; } case EW_INTFMT: { AddParam_Var(params[0]); AddParams(params + 1, 2); break; } case EW_PUSHPOP: { if (params[2] != 0) { s += "Exch"; if (params[2] != 1) AddParam_UInt(params[2]); } else if (params[1] != 0) { s += "Pop"; AddParam_Var(params[0]); } else { if (NoLabels(labels + kkk + 1, 2) && Get32(p + kCmdSize) == EW_PUSHPOP // Exch" && GET_CMD_PARAM(p + kCmdSize, 2) == 1 && Get32(p + kCmdSize * 2) == EW_PUSHPOP // Pop $VAR && GET_CMD_PARAM(p + kCmdSize * 2, 1) != 0) { if (IsVarStr(params[0], GET_CMD_PARAM(p + kCmdSize * 2, 0))) { s += "Exch"; AddParam(params[0]); NewLine(); Tab(true); endCommentIndex = kkk + 3; } } s += "Push"; AddParam(params[0]); } break; } case EW_FINDWINDOW: { AddParam_Var(params[0]); AddParam(params[1]); AddOptionalParams(params + 2, 3); break; } case EW_SENDMESSAGE: { // SendMessage: 6 [output, hwnd, msg, wparam, lparam, [wparamstring?1:0 | lparamstring?2:0 | timeout<<2] AddParam(params[1]); const char *w = NULL; AString t; ReadString2(t, params[2]); UInt32 wm; if (StringToUInt32(t, wm)) { switch (wm) { case 0x0C: w = "SETTEXT"; break; case 0x10: w = "CLOSE"; break; case 0x30: w = "SETFONT"; break; } } if (w) { s += " ${WM_"; s += w; s += '}'; } else SpaceQuStr(t); UInt32 spec = params[5]; for (unsigned i = 0; i < 2; i++) { AString s2; if (spec & ((UInt32)1 << i)) s2 += "STR:"; ReadString2(s2, params[3 + i]); SpaceQuStr(s2); } if ((Int32)params[0] >= 0) AddParam_Var(params[0]); spec >>= 2; if (spec != 0) { s += " /TIMEOUT="; Add_UInt(spec); } break; } case EW_ISWINDOW: { AddParam(params[0]); Add_GotoVars2(¶ms[1]); break; } case EW_GETDLGITEM: { AddParam_Var(params[0]); AddParams(params + 1, 2); break; } case EW_SETCTLCOLORS: { AddParam(params[0]); UInt32 offset = params[1]; if (_size < bhCtlColors.Offset || _size - bhCtlColors.Offset < offset || _size - bhCtlColors.Offset - offset < k_CtlColors_Size) { AddError("bad offset"); break; } const Byte *p2 = _data + bhCtlColors.Offset + offset; CNsis_CtlColors colors; colors.Parse(p2); if ((colors.flags & kColorsFlags_BK_SYS) != 0 || (colors.flags & kColorsFlags_TEXT_SYS) != 0) s += " /BRANDING"; AString bk; bool bkc = false; if (colors.bkmode == MY__TRANSPARENT) bk += " transparent"; else if (colors.flags & kColorsFlags_BKB) { if ((colors.flags & kColorsFlags_BK_SYS) == 0 && (colors.flags & kColorsFlags_BK) != 0) bkc = true; } if ((colors.flags & kColorsFlags_TEXT) != 0 || !bk.IsEmpty() || bkc) { Space(); if ((colors.flags & kColorsFlags_TEXT_SYS) != 0 || (colors.flags & kColorsFlags_TEXT) == 0) AddQuotes(); else Add_Color(colors.text); } s += bk; if (bkc) { Space(); Add_Color(colors.bkc); } break; } case EW_SETBRANDINGIMAGE: { s += " /IMGID="; Add_UInt(params[1]); if (params[2] == 1) s += " /RESIZETOFIT"; AddParam(params[0]); break; } case EW_CREATEFONT: { AddParam_Var(params[0]); AddParam(params[1]); AddOptionalParams(params + 2, 2); if (params[4] & 1) s += " /ITALIC"; if (params[4] & 2) s += " /UNDERLINE"; if (params[4] & 4) s += " /STRIKE"; break; } case EW_SHOWWINDOW: { AString hw, sw; ReadString2(hw, params[0]); ReadString2(sw, params[1]); if (params[3] != 0) s += "EnableWindow"; else { UInt32 val; bool valDefined = false; if (StringToUInt32(sw, val)) { if (val < ARRAY_SIZE(kShowWindow_Commands)) { sw.Empty(); sw += "${"; Add_ShowWindow_Cmd_2(sw, val); sw += '}'; valDefined = true; } } bool isHwndParent = IsVarStr(params[0], IsNsis225 ? kVar_HWNDPARENT_225 : kVar_HWNDPARENT); if (params[2] != 0) { if (valDefined && val == 0 && isHwndParent) { s += "HideWindow"; break; } } if (valDefined && val == 5 && isHwndParent && kkk + 1 < bh.Num && GetCmd(Get32(p + kCmdSize)) == EW_BRINGTOFRONT) { s += " ; "; } s += "ShowWindow"; } SpaceQuStr(hw); SpaceQuStr(sw); break; } case EW_SHELLEXEC: { AddParams(params, 2); if (params[2] != 0 || params[3] != MY__SW_SHOWNORMAL) { AddParam(params[2]); if (params[3] != MY__SW_SHOWNORMAL) { Space(); Add_ShowWindow_Cmd(params[3]); } } if (params[5] != 0) { s += " ;"; AddParam(params[5]); // it's tatus text update } break; } case EW_EXECUTE: { if (params[2] != 0) s += "Wait"; AddParam(params[0]); if (params[2] != 0) if ((Int32)params[1] >= 0) AddParam_Var(params[1]); break; } case EW_GETFILETIME: case EW_GETDLLVERSION: { AddParam(params[2]); AddParam_Var(params[0]); AddParam_Var(params[1]); break; } case EW_REGISTERDLL: { AString func; ReadString2(func, params[1]); bool printFunc = true; // params[4] = 1; for plugin command if (params[2] == 0) { s += "CallInstDLL"; AddParam(params[0]); if (params[3] == 1) s += " /NOUNLOAD"; } else { if (func == "DllUnregisterServer") { s += "UnRegDLL"; printFunc = false; } else { s += "RegDLL"; if (func == "DllRegisterServer") printFunc = false; } AddParam(params[0]); } if (printFunc) SpaceQuStr(func); break; } case EW_CREATESHORTCUT: { unsigned numParams; for (numParams = 6; numParams > 2; numParams--) if (params[numParams - 1] != 0) break; UInt32 spec = params[4]; if (spec & 0x8000) // NSIS 3.0b0 s += " /NoWorkingDir"; AddParams(params, numParams > 4 ? 4 : numParams); if (numParams <= 4) break; UInt32 icon = (spec & 0xFF); Space(); if (icon != 0) Add_UInt(icon); else AddQuotes(); if ((spec >> 8) == 0 && numParams < 6) break; UInt32 sw = (spec >> 8) & 0x7F; Space(); // NSIS encoder replaces these names: if (sw == MY__SW_SHOWMINNOACTIVE) sw = MY__SW_SHOWMINIMIZED; if (sw == 0) AddQuotes(); else Add_ShowWindow_Cmd(sw); UInt32 modKey = spec >> 24; UInt32 key = (spec >> 16) & 0xFF; if (modKey == 0 && key == 0) { if (numParams < 6) break; Space(); AddQuotes(); } else { Space(); if (modKey & 1) s += "SHIFT|"; // HOTKEYF_SHIFT if (modKey & 2) s += "CONTROL|"; if (modKey & 4) s += "ALT|"; if (modKey & 8) s += "EXT|"; static const unsigned kMy_VK_F1 = 0x70; if (key >= kMy_VK_F1 && key <= kMy_VK_F1 + 23) { s += 'F'; Add_UInt(key - kMy_VK_F1 + 1); } else if (key >= 'A' && key <= 'Z' || key >= '0' && key <= '9') s += (char)key; else { s += "Char_"; Add_UInt(key); } } AddOptionalParam(params[5]); // description break; } case EW_COPYFILES: { if (params[2] & 0x04) s += " /SILENT"; // FOF_SILENT if (params[2] & 0x80) s += " /FILESONLY"; // FOF_FILESONLY AddParams(params, 2); if (params[3] != 0) { s += " ;"; AddParam(params[3]); // status text update } break; } case EW_REBOOT: { if (params[0] != 0xbadf00d) s += " ; Corrupted ???"; else if (kkk + 1 < bh.Num && GetCmd(Get32(p + kCmdSize)) == EW_QUIT) endCommentIndex = kkk + 2; break; } case EW_WRITEINI: { unsigned numAlwaysParams = 0; if (params[0] == 0) // Section s += "FlushINI"; else if (params[4] != 0) { s += "WriteINIStr"; numAlwaysParams = 3; } else { s += "DeleteINI"; s += (params[1] == 0) ? "Sec" : "Str"; numAlwaysParams = 1; } AddParam(params[3]); // filename // Section, EntryName, Value AddParams(params, numAlwaysParams); AddOptionalParams(params + numAlwaysParams, 3 - numAlwaysParams); break; } case EW_READINISTR: { AddParam_Var(params[0]); AddParam(params[3]); // FileName AddParams(params +1, 2); // Section, EntryName break; } case EW_DELREG: { // NSIS 2.00 used another scheme! if (params[4] == 0) s += "Value"; else { s += "Key"; if (params[4] & 2) s += " /ifempty"; } AddRegRoot(params[1]); AddParam(params[2]); AddOptionalParam(params[3]); break; } case EW_WRITEREG: { const char *s2 = 0; switch (params[4]) { case 1: s2 = "Str"; break; case 2: s2 = "ExpandStr"; break; // maybe unused case 3: s2 = "Bin"; break; case 4: s2 = "DWORD"; break; default: s += '?'; Add_UInt(params[4]); } if (params[4] == 1 && params[5] == 2) s2 = "ExpandStr"; if (s2) s += s2; AddRegRoot(params[0]); AddParams(params + 1, 2); // keyName, valueName if (params[4] != 3) AddParam(params[3]); // value else { // Binary data. Space(); UInt32 offset = params[3]; bool isSupported = false; if (AfterHeaderSize >= 4 && bhData.Offset <= AfterHeaderSize - 4 && offset <= AfterHeaderSize - 4 - bhData.Offset) { // we support it for solid archives. const Byte *p2 = _afterHeader + bhData.Offset + offset; UInt32 size = Get32(p2); if (size <= AfterHeaderSize - 4 - bhData.Offset - offset) { for (UInt32 i = 0; i < size; i++) { Byte b = (p2 + 4)[i]; unsigned t; t = (b >> 4); s += (char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))); t = (b & 15); s += (char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))); } isSupported = true; } } if (!isSupported) { // we must read from file here; s += "data["; Add_UInt(offset); s += " ... ]"; s += " ; !!! Unsupported"; } } break; } case EW_READREGSTR: { s += (params[4] == 1) ? "DWORD" : "Str"; AddParam_Var(params[0]); AddRegRoot(params[1]); AddParams(params + 2, 2); break; } case EW_REGENUM: { s += (params[4] != 0) ? "Key" : "Value"; AddParam_Var(params[0]); AddRegRoot(params[1]); AddParams(params + 2, 2); break; } case EW_FCLOSE: case EW_FINDCLOSE: { AddParam_Var(params[0]); break; } case EW_FOPEN: { AddParam_Var(params[0]); AddParam(params[3]); UInt32 acc = params[1]; // dwDesiredAccess UInt32 creat = params[2]; // dwCreationDisposition if (acc == 0 && creat == 0) break; char cc = 0; if (acc == MY__GENERIC_READ && creat == OPEN_EXISTING) cc = 'r'; else if (creat == CREATE_ALWAYS && acc == MY__GENERIC_WRITE) cc = 'w'; else if (creat == OPEN_ALWAYS && (acc == (MY__GENERIC_WRITE | MY__GENERIC_READ))) cc = 'a'; // cc = 0; if (cc != 0) { Space(); s += cc; break; } if (acc & MY__GENERIC_READ) s += " GENERIC_READ"; if (acc & MY__GENERIC_WRITE) s += " GENERIC_WRITE"; if (acc & MY__GENERIC_EXECUTE) s += " GENERIC_EXECUTE"; if (acc & MY__GENERIC_ALL) s += " GENERIC_ALL"; const char *s2 = NULL; switch (creat) { case MY__CREATE_NEW: s2 = "CREATE_NEW"; break; case MY__CREATE_ALWAYS: s2 = "CREATE_ALWAYS"; break; case MY__OPEN_EXISTING: s2 = "OPEN_EXISTING"; break; case MY__OPEN_ALWAYS: s2 = "OPEN_ALWAYS"; break; case MY__TRUNCATE_EXISTING: s2 = "TRUNCATE_EXISTING"; break; } Space(); if (s2) s += s2; else Add_UInt(creat); break; } case EW_FPUTS: case EW_FPUTWS: { if (commandId == EW_FPUTWS) s += (params[2] == 0) ? "UTF16LE" : "Word"; else if (params[2] != 0) s += "Byte"; AddParam_Var(params[0]); AddParam(params[1]); break; } case EW_FGETS: case EW_FGETWS: { if (commandId == EW_FPUTWS) s += (params[3] == 0) ? "UTF16LE" : "Word"; if (params[3] != 0) s += "Byte"; AddParam_Var(params[0]); AddParam_Var(params[1]); AString maxLenStr; ReadString2(maxLenStr, params[2]); UInt32 maxLen; if (StringToUInt32(maxLenStr, maxLen)) { if (maxLen == 1 && params[3] != 0) break; if (maxLen == 1023 && params[3] == 0) // NSIS_MAX_STRLEN - 1; can be other value!! break; } SpaceQuStr(maxLenStr); break; } case EW_FSEEK: { AddParam_Var(params[0]); AddParam(params[2]); if (params[3] == 1) s += " CUR"; // FILE_CURRENT if (params[3] == 2) s += " END"; // FILE_END if ((Int32)params[1] >= 0) { if (params[3] == 0) s += " SET"; // FILE_BEGIN AddParam_Var(params[1]); } break; } case EW_FINDNEXT: { AddParam_Var(params[1]); AddParam_Var(params[0]); break; } case EW_FINDFIRST: { AddParam_Var(params[1]); AddParam_Var(params[0]); AddParam(params[2]); break; } case EW_LOG: { if (params[0] != 0) { s += "Set "; s += (params[1] == 0) ? "off" : "on"; } else { s += "Text"; AddParam(params[1]); } } case EW_SECTIONSET: { if ((Int32)params[2] >= 0) { s += "Get"; Add_SectOp(params[2]); AddParam(params[0]); AddParam_Var(params[1]); } else { s += "Set"; UInt32 t = -(Int32)params[2] - 1; Add_SectOp(t); AddParam(params[0]); AddParam(params[t == 0 ? 4 : 1]); // params[3] != 0 means call SectionFlagsChanged in installer // used by SECTIONSETFLAGS command } break; } case EW_INSTTYPESET: { int numQwParams = 0; const char *s2; if (params[3] == 0) { if (params[2] == 0) { s2 = "InstTypeGetText"; numQwParams = 1; } else { s2 = "InstTypeSetText"; numQwParams = 2; } } else { if (params[2] == 0) s2 = "GetCurInstType"; else { s2 = "SetCurInstType"; numQwParams = 1; } } s += s2; AddParams(params, numQwParams); if (params[2] == 0) AddParam_Var(params[1]); break; } case EW_LOCKWINDOW: { s += (params[0] == 0) ? " on" : " off"; break; } case EW_FINDPROC: { AddParam_Var(params[0]); AddParam(params[1]); break; } default: { numSkipParams = 0; } #endif } #ifdef NSIS_SCRIPT unsigned numParams = kNumCommandParams; for (; numParams > 0; numParams--) if (params[numParams - 1] != 0) break; if (numParams > numSkipParams) { s += " ; !!!! Unknown Params: "; unsigned i; for (i = 0; i < numParams; i++) AddParam(params[i]); s += " ;"; for (i = 0; i < numParams; i++) { Space(); UInt32 v = params[i]; if (v > 0xFFF00000) Add_SignedInt(s, (Int32)v); else Add_UInt(v); } } NewLine(); #endif } #ifdef NSIS_SCRIPT if (sectionIsOpen) { if (curSectionIndex < bhSections.Num) { const CSection § = Sections[curSectionIndex]; if (sect.StartCmdIndex + sect.NumCommands + 1 == kkk) { PrintSectionEnd(); sectionIsOpen = false; // lastSectionEndCmd = kkk; curSectionIndex++; } } } while (curSectionIndex < bhSections.Num) { const CSection § = Sections[curSectionIndex]; if (sectionIsOpen) { if (sect.StartCmdIndex + sect.NumCommands != kkk) AddErrorLF("SECTION ERROR"); PrintSectionEnd(); sectionIsOpen = false; curSectionIndex++; } else { if (curSectionIndex == 49) curSectionIndex = curSectionIndex; if (PrintSectionBegin(sect, curSectionIndex)) curSectionIndex++; else sectionIsOpen = true; } } #endif return S_OK; } static int CompareItems(void *const *p1, void *const *p2, void *param) { const CItem &i1 = **(CItem **)p1; const CItem &i2 = **(CItem **)p2; RINOZ(MyCompare(i1.Pos, i2.Pos)); const CInArchive *inArchive = (const CInArchive *)param; if (inArchive->IsUnicode) { if (i1.Prefix != i2.Prefix) { if (i1.Prefix < 0) return -1; if (i2.Prefix < 0) return 1; RINOZ(wcscmp( inArchive->UPrefixes[i1.Prefix], inArchive->UPrefixes[i2.Prefix])); } RINOZ(wcscmp(i1.NameU, i2.NameU)); } else { if (i1.Prefix != i2.Prefix) { if (i1.Prefix < 0) return -1; if (i2.Prefix < 0) return 1; RINOZ(strcmp( inArchive->APrefixes[i1.Prefix], inArchive->APrefixes[i2.Prefix])); } RINOZ(strcmp(i1.NameA, i2.NameA)); } return 0; } HRESULT CInArchive::SortItems() { { Items.Sort(CompareItems, (void *)this); unsigned i; for (i = 0; i + 1 < Items.Size(); i++) { const CItem &i1 = Items[i]; const CItem &i2 = Items[i + 1]; if (i1.Pos != i2.Pos) continue; if (IsUnicode) { if (i1.NameU != i2.NameU) continue; if (i1.Prefix != i2.Prefix) { if (i1.Prefix < 0 || i2.Prefix < 0) continue; if (UPrefixes[i1.Prefix] != UPrefixes[i2.Prefix]) continue; } } else { if (i1.NameA != i2.NameA) continue; if (i1.Prefix != i2.Prefix) { if (i1.Prefix < 0 || i2.Prefix < 0) continue; if (APrefixes[i1.Prefix] != APrefixes[i2.Prefix]) continue; } } Items.Delete(i + 1); i--; } for (i = 0; i < Items.Size(); i++) { CItem &item = Items[i]; UInt32 curPos = item.Pos + 4; for (unsigned nextIndex = i + 1; nextIndex < Items.Size(); nextIndex++) { UInt32 nextPos = Items[nextIndex].Pos; if (curPos <= nextPos) { item.EstimatedSize_Defined = true; item.EstimatedSize = nextPos - curPos; break; } } } if (!IsSolid) { for (i = 0; i < Items.Size(); i++) { CItem &item = Items[i]; RINOK(_stream->Seek(GetPosOfNonSolidItem(i), STREAM_SEEK_SET, NULL)); const UInt32 kSigSize = 4 + 1 + 1 + 4; // size,[flag],prop,dict BYTE sig[kSigSize]; size_t processedSize = kSigSize; RINOK(ReadStream(_stream, sig, &processedSize)); if (processedSize < 4) return S_FALSE; UInt32 size = Get32(sig); if ((size & kMask_IsCompressed) != 0) { item.IsCompressed = true; size &= ~kMask_IsCompressed; if (Method == NMethodType::kLZMA) { if (processedSize < 9) return S_FALSE; /* if (FilterFlag) item.UseFilter = (sig[4] != 0); */ item.DictionarySize = Get32(sig + 4 + 1 + (FilterFlag ? 1 : 0)); } } else { item.IsCompressed = false; item.Size = size; item.Size_Defined = true; } item.CompressedSize = size; item.CompressedSize_Defined = true; } } } return S_OK; } // Flags for common_header.flags #define CH_FLAGS_DETAILS_SHOWDETAILS 1 #define CH_FLAGS_DETAILS_NEVERSHOW 2 #define CH_FLAGS_PROGRESS_COLORED 4 #define CH_FLAGS_SILENT 8 #define CH_FLAGS_SILENT_LOG 16 #define CH_FLAGS_AUTO_CLOSE 32 #define CH_FLAGS_DIR_NO_SHOW 64 // unused now #define CH_FLAGS_NO_ROOT_DIR 128 #define CH_FLAGS_COMP_ONLY_ON_CUSTOM 256 #define CH_FLAGS_NO_CUSTOM 512 static const char * const k_PostStrings[] = { "install_directory_auto_append" , "uninstchild" // NSIS 2.25+, used by uninstaller: , "uninstcmd" // NSIS 2.25+, used by uninstaller: , "wininit" // NSIS 2.25+, used by move file on reboot }; HRESULT CInArchive::Parse() { // UInt32 offset = ReadUInt32(); // ???? offset == FirstHeader.HeaderSize const Byte *p = _data; CBlockHeader bhEntries, bhStrings, bhLangTables; bhEntries.Parse(p + 4 + 8 * 2); bhStrings.Parse(p + 4 + 8 * 3); bhLangTables.Parse(p + 4 + 8 * 4); #ifdef NSIS_SCRIPT CBlockHeader bhFont; bhPages.Parse(p + 4 + 8 * 0); bhSections.Parse(p + 4 + 8 * 1); bhCtlColors.Parse(p + 4 + 8 * 5); bhFont.Parse(p + 4 + 8 * 6); bhData.Parse(p + 4 + 8 * 7); #endif _stringsPos = bhStrings.Offset; if (_stringsPos > _size) return S_FALSE; { if (bhLangTables.Offset < bhStrings.Offset) return S_FALSE; UInt32 stringTableSize = bhLangTables.Offset - bhStrings.Offset; if (stringTableSize < 2) return S_FALSE; const Byte *strData = _data + _stringsPos; if (strData[stringTableSize - 1] != 0) return S_FALSE; IsUnicode = (Get16(strData) == 0); NumStringChars = stringTableSize; if (IsUnicode) { if ((stringTableSize & 1) != 0) return S_FALSE; NumStringChars >>= 1; if (strData[stringTableSize - 2] != 0) return S_FALSE; } } if (bhEntries.Num > (1 << 25)) return S_FALSE; if (bhEntries.Offset > _size) return S_FALSE; if (bhEntries.Num * kCmdSize > _size - bhEntries.Offset) return S_FALSE; DetectNsisType(bhEntries, _data + bhEntries.Offset); #ifdef NSIS_SCRIPT { AddCommentAndString("NSIS script"); if (IsUnicode) Script += " (UTF-8)"; Space(); Script += GetFormatDescription(); AddLF(); } { AddCommentAndString(IsInstaller ? "Install" : "Uninstall"); AddLF(); } AddLF(); if (IsUnicode) AddStringLF("Unicode true"); if (Method != NMethodType::kCopy) { const char *m = NULL; switch (Method) { case NMethodType::kDeflate: m = "zlib"; break; case NMethodType::kBZip2: m = "bzip2"; break; case NMethodType::kLZMA: m = "lzma"; break; } Script += "SetCompressor"; if (IsSolid) Script += " /SOLID"; if (m) { Space(); Script += m; } AddLF(); } if (Method == NMethodType::kLZMA) { // if (DictionarySize != (8 << 20)) { Script += "SetCompressorDictSize"; AddParam_UInt(DictionarySize >> 20); AddLF(); } } Separator(); PrintNumComment("HEADER SIZE", FirstHeader.HeaderSize); // if (bhPages.Offset != 300 && bhPages.Offset != 288) if (bhPages.Offset != 0) { PrintNumComment("START HEADER SIZE", bhPages.Offset); } if (bhSections.Num > 0) { if (bhEntries.Offset < bhSections.Offset) return S_FALSE; SectionSize = (bhEntries.Offset - bhSections.Offset) / bhSections.Num; if (bhSections.Offset + bhSections.Num * SectionSize != bhEntries.Offset) return S_FALSE; if (SectionSize < kSectionSize_base) return S_FALSE; UInt32 maxStringLen = SectionSize - kSectionSize_base; if (IsUnicode) { if ((maxStringLen & 1) != 0) return S_FALSE; maxStringLen >>= 1; } // if (maxStringLen != 1024) { if (maxStringLen == 0) PrintNumComment("SECTION SIZE", SectionSize); else PrintNumComment("MAX STRING LENGTH", maxStringLen); } } PrintNumComment("STRING CHARS", NumStringChars); // PrintNumComment("LANG TABLE SIZE", bhCtlColors.Offset - bhLangTables.Offset); if (bhCtlColors.Offset > _size) AddErrorLF("Bad COLORS TABLE"); // PrintNumComment("COLORS TABLE SIZE", bhFont.Offset - bhCtlColors.Offset); if (bhCtlColors.Num != 0) PrintNumComment("COLORS Num", bhCtlColors.Num); // bhData uses offset in _afterHeader (not in _data) // PrintNumComment("FONT TABLE SIZE", bhData.Offset - bhFont.Offset); if (bhFont.Num != 0) PrintNumComment("FONTS Num", bhFont.Num); // PrintNumComment("DATA SIZE", FirstHeader.HeaderSize - bhData.Offset); if (bhData.Num != 0) PrintNumComment("DATA NUM", bhData.Num); AddLF(); AddStringLF("OutFile [NSIS].exe"); AddStringLF("!include WinMessages.nsh"); AddLF(); strUsed.Alloc(NumStringChars); memset(strUsed, 0, NumStringChars); { UInt32 ehFlags = Get32(p); UInt32 showDetails = ehFlags & 3;// CH_FLAGS_DETAILS_SHOWDETAILS & CH_FLAGS_DETAILS_NEVERSHOW; if (showDetails >= 1 && showDetails <= 2) { Script += IsInstaller ? "ShowInstDetails" : "ShowUninstDetails"; Script += (showDetails == 1) ? " show" : " nevershow"; AddLF(); } if (ehFlags & CH_FLAGS_PROGRESS_COLORED) AddStringLF("InstProgressFlags colored" ); if ((ehFlags & (CH_FLAGS_SILENT | CH_FLAGS_SILENT_LOG)) != 0) { Script += IsInstaller ? "SilentInstall " : "SilentUnInstall "; Script += (ehFlags & CH_FLAGS_SILENT_LOG) ? "silentlog" : "silent"; AddLF(); } if (ehFlags & CH_FLAGS_AUTO_CLOSE) AddStringLF("AutoCloseWindow true"); if ((ehFlags & CH_FLAGS_NO_ROOT_DIR) == 0) AddStringLF("AllowRootDirInstall true"); if (ehFlags & CH_FLAGS_NO_CUSTOM) AddStringLF("InstType /NOCUSTOM"); if (ehFlags & CH_FLAGS_COMP_ONLY_ON_CUSTOM) AddStringLF("InstType /COMPONENTSONLYONCUSTOM"); } // Separator(); // AddLF(); Int32 licenseLangIndex = -1; { const Byte *pp = _data + bhPages.Offset; for (UInt32 pageIndex = 0; pageIndex < bhPages.Num; pageIndex++, pp += kPageSize) { UInt32 wndProcID = Get32(pp + 4); UInt32 param1 = Get32(pp + 44 + 4 * 1); if (wndProcID != PWP_LICENSE || param1 == 0) continue; if ((Int32)param1 < 0) licenseLangIndex = - ((Int32)param1 + 1); else noParseStringIndexes.AddToUniqueSorted(param1); } } unsigned paramsOffset = 4 + 8 * 8; if (bhPages.Offset == 276) paramsOffset -= 8; const Byte *p2 = p + paramsOffset; { UInt32 rootKey = Get32(p2); // (rootKey = -1) in uninstaller by default (the bug in NSIS) UInt32 subKey = Get32(p2 + 4); UInt32 value = Get32(p2 + 8); if ((rootKey != 0 && rootKey != (UInt32)(Int32)-1) || subKey != 0 || value != 0) { Script += "InstallDirRegKey"; AddRegRoot(rootKey); AddParam(subKey); AddParam(value); AddLF(); } } { UInt32 bg_color1 = Get32(p2 + 12); UInt32 bg_color2 = Get32(p2 + 16); UInt32 bg_textcolor = Get32(p2 + 20); if (bg_color1 != (UInt32)(Int32)-1 || bg_color2 != (UInt32)(Int32)-1 || bg_textcolor != (UInt32)(Int32)-1) { Script += "BGGradient"; if (bg_color1 != 0 || bg_color2 != 0xFF0000 || bg_textcolor != (UInt32)(Int32)-1) { Add_ColorParam(bg_color1); Add_ColorParam(bg_color2); if (bg_textcolor != (UInt32)(Int32)-1) Add_ColorParam(bg_textcolor); } AddLF(); } } { UInt32 lb_bg = Get32(p2 + 24); UInt32 lb_fg = Get32(p2 + 28); if ((lb_bg != (UInt32)(Int32)-1 || lb_fg != (UInt32)(Int32)-1) && (lb_bg != 0 || lb_fg != 0xFF00)) { Script += "InstallColors"; Add_ColorParam(lb_fg); Add_ColorParam(lb_bg); AddLF(); } } UInt32 license_bg = Get32(p2 + 36); if (license_bg != (UInt32)(Int32)-1 && license_bg != -15) // COLOR_BTNFACE { Script += "LicenseBkColor"; if ((Int32)license_bg == -5) // COLOR_WINDOW Script += " /windows"; /* else if ((Int32)license_bg == -15) Script += " /grey"; */ else Add_ColorParam(license_bg); AddLF(); } UInt32 langtable_size = Get32(p2 + 32); if (bhLangTables.Num > 0) { if (langtable_size == (UInt32)(Int32)-1) return E_NOTIMPL; // maybe it's old NSIS archive() UInt32 numStrings = (langtable_size - 10) / 4; _numLangStrings = numStrings; AddLF(); Separator(); PrintNumComment("LANG TABLES", bhLangTables.Num); PrintNumComment("LANG STRINGS", numStrings); AddLF(); if (licenseLangIndex >= 0) { for (UInt32 i = 0; i < bhLangTables.Num; i++) { const Byte *p = _data + bhLangTables.Offset + langtable_size * i; LANGID langID = Get16(p); UInt32 val = Get32(p + 10 + (UInt32)licenseLangIndex * 4); if (val != 0) { Script += "LicenseLangString "; Add_LangStr_Simple(licenseLangIndex); AddParam_UInt(langID); AddLicense(val, langID); noParseStringIndexes.AddToUniqueSorted(val); NewLine(); } } AddLF(); } UInt32 brandingText = 0; UInt32 caption = 0; UInt32 name = 0; UInt32 i; for (i = 0; i < bhLangTables.Num; i++) { const Byte *p = _data + bhLangTables.Offset + langtable_size * i; LANGID langID = Get16(p); if (i == 0 || langID == 1033) _mainLang = p + 10; { UInt32 v = Get32(p + 10 + 0 * 4); if (v != 0 && (langID == 1033 || brandingText == 0)) brandingText = v; } { UInt32 v = Get32(p + 10 + 1 * 4); if (v != 0 && (langID == 1033 || caption == 0)) caption = v; } { UInt32 v = Get32(p + 10 + 2 * 4); if (v != 0 && (langID == 1033 || name == 0)) name = v; } } if (name != 0) { Script += "Name"; AddParam(name); NewLine(); ReadString2(Name, name); } /* if (caption != 0) { Script += "Caption"; AddParam(caption); NewLine(); } */ if (brandingText != 0) { Script += "BrandingText"; AddParam(brandingText); NewLine(); ReadString2(BrandingText, brandingText); } for (i = 0; i < bhLangTables.Num; i++) { const Byte *p = _data + bhLangTables.Offset + langtable_size * i; LANGID langID = Get16(p); AddLF(); AddCommentAndString("LANG:"); AddParam_UInt(langID); /* Script += " ("; LangId_To_String(Script, langID); Script += ')'; */ AddLF(); // UInt32 dlg_offset = Get32(p + 2); // UInt32 g_exec_flags_rtl = Get32(p + 6); for (UInt32 j = 0; j < numStrings; j++) { UInt32 val = Get32(p + 10 + j * 4); if (val != 0) { if ((Int32)j != licenseLangIndex) { Script += "LangString "; Add_LangStr_Simple(j); AddParam_UInt(langID); AddParam(val); AddLF(); } } } AddLF(); } ClearLangComment(); } { unsigned numInternalVars = GET_NUM_INTERNAL_VARS; UInt32 numUsedVars = GetNumUsedVars(); if (numUsedVars > numInternalVars) { Separator(); PrintNumComment("VARIABLES", numUsedVars - numInternalVars); AddLF(); AString temp; for (UInt32 i = numInternalVars; i < numUsedVars; i++) { Script += "Var "; temp.Empty(); GetVar2(temp, i); AddStringLF(temp); } AddLF(); } } onFuncOffset = paramsOffset + 40; numOnFunc = ARRAY_SIZE(kOnFunc); if (bhPages.Offset == 276) numOnFunc--; p2 += 40 + numOnFunc * 4; #define NSIS_MAX_INST_TYPES 32 AddLF(); UInt32 i; for (i = 0; i < NSIS_MAX_INST_TYPES + 1; i++, p2 += 4) { UInt32 instType = Get32(p2); if (instType != 0) { Script += "InstType"; AString s2; if (!IsInstaller) s2 += "un."; ReadString2(s2, instType); SpaceQuStr(s2); NewLine(); } } { UInt32 installDir = Get32(p2); p2 += 4; if (installDir != 0) { Script += "InstallDir"; AddParam(installDir); NewLine(); } } if (bhPages.Offset >= 288) for (i = 0; i < 4; i++) { if (i != 0 && bhPages.Offset < 300) break; UInt32 param = Get32(p2 + 4 * i); if (param == 0 || param == (UInt32)(Int32)-1) continue; /* uninstaller: UInt32 uninstChild = Get32(p2 + 8); // "$TEMP\\$1u_.exe" UInt32 uninstCmd = Get32(p2 + 12); // "\"$TEMP\\$1u_.exe\" $0 _?=$INSTDIR\\" int str_wininit = Get32(p2 + 16); // "$WINDIR\\wininit.ini" */ AddCommentAndString(k_PostStrings[i]); Script += " ="; AddParam(param); NewLine(); } AddLF(); #endif RINOK(ReadEntries(bhEntries)); #ifdef NSIS_SCRIPT Separator(); AddCommentAndString("UNREFERENCED STRINGS:"); AddLF(); AddLF(); CommentOpen(); for (i = 0; i < NumStringChars;) { if (!strUsed[i] && i != 0) // Script += "!!! "; { Add_UInt(i); AddParam(i); NewLine(); } if (IsUnicode) i += GetUi16Str_Len((const Byte *)_data + _stringsPos + i * 2); else i += (UInt32)strlen((const char *)(const Byte *)_data + _stringsPos + i); i++; } CommentClose(); #endif return SortItems(); } static bool IsLZMA(const Byte *p, UInt32 &dictionary) { dictionary = Get32(p + 1); return (p[0] == 0x5D && p[1] == 0x00 && p[2] == 0x00 && p[5] == 0x00 && (p[6] & 0x80) == 0x00); } static bool IsLZMA(const Byte *p, UInt32 &dictionary, bool &thereIsFlag) { if (IsLZMA(p, dictionary)) { thereIsFlag = false; return true; } if (p[0] <= 1 && IsLZMA(p + 1, dictionary)) { thereIsFlag = true; return true; } return false; } static bool IsBZip2(const Byte *p) { return (p[0] == 0x31 && p[1] < 14); } HRESULT CInArchive::Open2(const Byte *sig, size_t size) { const UInt32 kSigSize = 4 + 1 + 5 + 2; // size, flag, 5 - lzma props, 2 - lzma first bytes if (size < kSigSize) return S_FALSE; _headerIsCompressed = true; IsSolid = true; FilterFlag = false; UseFilter = false; DictionarySize = 1; #ifdef NSIS_SCRIPT AfterHeaderSize = 0; #endif UInt32 compressedHeaderSize = Get32(sig); /* XX XX XX XX XX XX XX XX == FirstHeader.HeaderSize, nonsolid, uncompressed 5D 00 00 dd dd 00 solid LZMA 00 5D 00 00 dd dd 00 solid LZMA, empty filter (there are no such archives) 01 5D 00 00 dd dd 00 solid LZMA, BCJ filter (only 7-Zip installer used that format) SS SS SS 80 00 5D 00 00 dd dd 00 non-solid LZMA, empty filter SS SS SS 80 01 5D 00 00 dd dd 00 non-solid LZMA, BCJ filte SS SS SS 80 01 tt non-solid BZip (tt < 14 SS SS SS 80 non-solid deflate 01 tt solid BZip (tt < 14 other solid Deflate */ if (compressedHeaderSize == FirstHeader.HeaderSize) { _headerIsCompressed = false; IsSolid = false; Method = NMethodType::kCopy; } else if (IsLZMA(sig, DictionarySize, FilterFlag)) Method = NMethodType::kLZMA; else if (sig[3] == 0x80) { IsSolid = false; if (IsLZMA(sig + 4, DictionarySize, FilterFlag) && sig[3] == 0x80) Method = NMethodType::kLZMA; else if (IsBZip2(sig + 4)) Method = NMethodType::kBZip2; else Method = NMethodType::kDeflate; } else if (IsBZip2(sig)) Method = NMethodType::kBZip2; else Method = NMethodType::kDeflate; if (IsSolid) { RINOK(_stream->Seek(DataStreamOffset, STREAM_SEEK_SET, NULL)); } else { _headerIsCompressed = ((compressedHeaderSize & kMask_IsCompressed) != 0); compressedHeaderSize &= ~kMask_IsCompressed; _nonSolidStartOffset = compressedHeaderSize; RINOK(_stream->Seek(DataStreamOffset + 4, STREAM_SEEK_SET, NULL)); } _data.Alloc(FirstHeader.HeaderSize); _size = (size_t)FirstHeader.HeaderSize; Decoder.Method = Method; Decoder.FilterFlag = FilterFlag; Decoder.Solid = IsSolid; Decoder.InputStream = _stream; Decoder.Buffer.Alloc(kInputBufSize); Decoder.StreamPos = 0; if (_headerIsCompressed) { RINOK(Decoder.Init(_stream, UseFilter)); if (IsSolid) { size_t processedSize = 4; Byte buf[4]; RINOK(Decoder.Read(buf, &processedSize)); if (processedSize != 4) return S_FALSE; if (Get32((const Byte *)buf) != FirstHeader.HeaderSize) return S_FALSE; } size_t processedSize = FirstHeader.HeaderSize; RINOK(Decoder.Read(_data, &processedSize)); if (processedSize != FirstHeader.HeaderSize) return S_FALSE; #ifdef NSIS_SCRIPT if (IsSolid) { /* we need additional bytes for data for WriteRegBin */ AfterHeaderSize = (1 << 12); _afterHeader.Alloc(AfterHeaderSize); size_t processedSize = AfterHeaderSize; RINOK(Decoder.Read(_afterHeader, &processedSize)); AfterHeaderSize = (UInt32)processedSize; } #endif } else { size_t processedSize = FirstHeader.HeaderSize; RINOK(ReadStream(_stream, (Byte *)_data, &processedSize)); if (processedSize < FirstHeader.HeaderSize) return S_FALSE; } #ifdef NUM_SPEED_TESTS for (unsigned i = 0; i < NUM_SPEED_TESTS; i++) { RINOK(Parse()); Clear2(); } #endif return Parse(); } /* NsisExe = { ExeStub Archive // must start from 512 * N #ifndef NSIS_CONFIG_CRC_ANAL { Some additional data } } Archive { FirstHeader Data #ifdef NSIS_CONFIG_CRC_SUPPORT && FirstHeader.ThereIsCrc() { CRC } } FirstHeader { UInt32 Flags; Byte Signature[16]; // points to the header+sections+entries+stringtable in the datablock UInt32 HeaderSize; UInt32 ArcSize; } */ // ---------- PE (EXE) parsing ---------- static const unsigned k_PE_StartSize = 0x40; static const unsigned k_PE_HeaderSize = 4 + 20; static const unsigned k_PE_OptHeader32_Size_MIN = 96; static inline bool CheckPeOffset(UInt32 pe) { return (pe >= 0x40 && pe <= 0x1000 && (pe & 7) == 0); } static bool IsArc_Pe(const Byte *p, size_t size) { if (size < 2) return false; if (p[0] != 'M' || p[1] != 'Z') return false; if (size < k_PE_StartSize) return false; // k_IsArc_Res_NEED_MORE; UInt32 pe = Get32(p + 0x3C); if (!CheckPeOffset(pe)) return false; if (pe + k_PE_HeaderSize > size) return false; // k_IsArc_Res_NEED_MORE; p += pe; if (Get32(p) != 0x00004550) return false; return Get16(p + 4 + 16) >= k_PE_OptHeader32_Size_MIN; } HRESULT CInArchive::Open(IInStream *inStream, const UInt64 *maxCheckStartPosition) { Clear(); RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &StartOffset)); const UInt32 kStartHeaderSize = 4 * 7; const unsigned kStep = 512; // nsis start is aligned for 512 Byte buf[kStep]; UInt64 pos = StartOffset; size_t bufSize = 0; UInt64 pePos = (UInt64)(Int64)-1; for (;;) { bufSize = kStep; RINOK(ReadStream(inStream, buf, &bufSize)); if (bufSize < kStartHeaderSize) return S_FALSE; if (memcmp(buf + 4, kSignature, kSignatureSize) == 0) break; if (IsArc_Pe(buf, bufSize)) pePos = pos; pos += kStep; UInt64 proc = pos - StartOffset; if (maxCheckStartPosition && proc > *maxCheckStartPosition) { if (pePos == 0) { if (proc > (1 << 20)) return S_FALSE; } else return S_FALSE; } } if (pePos == (UInt64)(Int64)-1) { UInt64 posCur = StartOffset; for (;;) { if (posCur < kStep) break; posCur -= kStep; if (pos - posCur > (1 << 20)) break; bufSize = kStep; RINOK(inStream->Seek(posCur, STREAM_SEEK_SET, NULL)); RINOK(ReadStream(inStream, buf, &bufSize)); if (bufSize < kStep) break; if (IsArc_Pe(buf, bufSize)) { pePos = posCur; break; } } // restore buf to nsis header bufSize = kStep; RINOK(inStream->Seek(pos, STREAM_SEEK_SET, NULL)); RINOK(ReadStream(inStream, buf, &bufSize)); if (bufSize < kStartHeaderSize) return S_FALSE; } StartOffset = pos; UInt32 peSize = 0; if (pePos != (UInt64)(Int64)-1) { UInt64 peSize64 = (pos - pePos); if (peSize64 < (1 << 20)) { peSize = (UInt32)peSize64; StartOffset = pePos; } } DataStreamOffset = pos + kStartHeaderSize; FirstHeader.Flags = Get32(buf); if ((FirstHeader.Flags & (~kFlagsMask)) != 0) return S_FALSE; IsInstaller = (FirstHeader.Flags & NFlags::kUninstall) == 0; FirstHeader.HeaderSize = Get32(buf + kSignatureSize + 4); FirstHeader.ArcSize = Get32(buf + kSignatureSize + 8); if (FirstHeader.ArcSize <= kStartHeaderSize) return S_FALSE; RINOK(inStream->Seek(0, STREAM_SEEK_END, &_fileSize)); IsArc = true; if (peSize != 0) { ExeStub.Alloc(peSize); RINOK(inStream->Seek(pePos, STREAM_SEEK_SET, NULL)); RINOK(ReadStream_FALSE(inStream, ExeStub, peSize)); } HRESULT res = S_FALSE; try { CLimitedInStream *_limitedStreamSpec = new CLimitedInStream; _stream = _limitedStreamSpec; _limitedStreamSpec->SetStream(inStream); _limitedStreamSpec->InitAndSeek(pos, FirstHeader.ArcSize); DataStreamOffset -= pos; res = Open2(buf + kStartHeaderSize, bufSize - kStartHeaderSize); } catch(...) { _stream.Release(); throw; // res = S_FALSE; } if (res != S_OK) { _stream.Release(); // Clear(); } return res; } UString CInArchive::ConvertToUnicode(const AString &s) const { if (IsUnicode) { UString res; if (ConvertUTF8ToUnicode(s, res)) return res; } return MultiByteToUnicodeString(s); } void CInArchive::Clear2() { IsUnicode = false; NsisType = k_NsisType_Nsis2; IsNsis225 = false; IsNsis200 = false; LogCmdIsEnabled = false; BadCmd = -1; #ifdef NSIS_SCRIPT Name.Empty(); BrandingText.Empty(); Script.Empty(); LicenseFiles.Clear(); _numRootLicenses = 0; _numLangStrings = 0; langStrIDs.Clear(); LangComment.Empty(); noParseStringIndexes.Clear(); #endif APrefixes.Clear(); UPrefixes.Clear(); Items.Clear(); IsUnicode = false; ExeStub.Free(); } void CInArchive::Clear() { Clear2(); IsArc = false; _stream.Release(); } }}