#1398 NSIS (Nullsoftinstaller) FileNames are cribbles

Daniello Alto

When you open some setup.exe that was created with the Nullsoftinstaller - the file names are not show correctly. You mostly see something like just 1-2 garbage glyphs.


Well Nullsoftstrings are somehow special. Since they mask stuff like $PROGRAMFILE, $INSTDIR, $SMPROGRAMS with special chars to save space. The NS_* const below are these special marker
The name of the function that handle that is call GetNSISString()...


// special escape characters used in strings: (we use control codes in order to minimize conflicts with normal characters)
#define NS_LANG_CODE  _T('\x01')    // for a langstring
#define NS_SHELL_CODE _T('\x02')    // for a shell folder path
#define NS_VAR_CODE   _T('\x03')    // for a variable
#define NS_SKIP_CODE  _T('\x04')    // to consider next character as a normal character
#define NS_IS_CODE(x) ((x) <= NS_SKIP_CODE) // NS_SKIP_CODE must always be the higher code


// $0..$9, $INSTDIR, etc are encoded as ASCII bytes starting from this value.
// Added by ramon 3 jun 2003
#define NS_SKIP_CODE 252
#define NS_VAR_CODE 253
#define NS_SHELL_CODE 254
#define NS_LANG_CODE 255

Well as you see these const were changed from Ver 2 to 3.

To detect a new version 3 I use that little crappy piece of code:
isVerNS3 = bool( GetNSISString( 0x0011 ) == "CommonFilesDir")

Okay implementing this + changing the compare for 'NS_SKIP_CODE' would take about 30 minutes to implement and does a great improvement.

    if (nVarIdx < NS_SKIP_CODE)
      if (nVarIdx == NS_SHELL_CODE)
        if (fldrs[1] & 0x80)
              GetNSISStringNP( fldrs[1] & 0x3F), 
              fldrs[1] & 0x40

Note fldrs[1] is just the first of the two data bytes that follow after some NS_* char.
However there's another 'great' enhancement in the 'NS_SHELL_CODE' branch. As you see NS_SHELL_CODE.fldrs[1] masked with 0x80 get's a special treatment. So just remove the marker bit and then next(the is intended for markingX64 via X86) via '& 0x3F' and pass that value(/offset) again into GetNSISString(). ... and append the OUTput to the new string.

You'll need to implement this to successful decode PROGRAMFILE and CommonFilesDir!

As you see this implementation limits string encode with NS_SHELL_CODE.0x80 to just access string in the NB_String table from 00..3F. For that NB_String is at first populate with 'Common string'. ...and detecting Ver3 via GetNSISString( 0x0011 ) is not really safe but may work well im most cases. :)

P.s. I wonder why NSIS dev take so much care to 'safe' some bytes and implement create at the end some own string substitution compression. I mean at the end the whole thing gets compressed anyway and the first thing what for ex zLib does on bytelevel is to do some string substitutions.
Na anyway normally you don't see this or need to take care of that. :D


  • Daniello Alto
    Daniello Alto

    About 4.) I forgot to mention that you may need to turn that registry key values back into real NSIS varnames like this:

       # so there's just 00..3F space left for indexing
       retval = GetNSISString(nsCur & 0x3F)
       #print "retval => %s" % retval
       if   "ProgramFilesDir" == retval:
           expanded = CSIDLs [csidl.CSIDL_PROGRAM_FILES]
       elif "CommonFilesDir" == retval:
           expanded = CSIDLs [csidl.CSIDL_PROGRAM_FILES_COMMON]

    However on the
    '''ProgramFilesDir''' instead of '''$PROGRAMFILES'''
    '''CommonFilesDir''' instead of '''$COMMONFILES'''

    ... as dir names looks more nice to me than the real NSIS ones. :D

  • Igor Pavlov
    Igor Pavlov

    Try latest alpha version of 7-Zip.
    Most of things must work.
    And next version of 7-Zip also will have new improved NSIS support.

  • Daniello Alto
    Daniello Alto

    Cool I just tried 7z932
    nice idea to also track 0x3E_EW_WRITEUNINSTALLER and so add that file to the list.
    (...and to take care for 0A_EW_SETFILEATTRIBUTES)
    Well maybe use parm3 instead of param1 as name - however regard to the source it doesn't make such a big difference ;)

      tstring full = tstring(_T("$INSTDIR\\")) + tstring(line.gettoken_str(1));

    Hmm how you get the file size of uninstaller? The value seem somehow too small.

    ... beside the 'standard' that's tracking da entries
    14_ EW_EXTRACTFILE and SetOutPath (0B_ EW_CREATEDIR with p1=1)

    Hmm could you also include the script itself in the files list?
    Maybe as script.bin. And since these files don't have any stored attributes set maybe the 'hidden'-flag for script.bin and 'uninstaller.exe' to label them somehow as special

    • Igor Pavlov
      Igor Pavlov

      Uninstaller's data in NSIS file contains only "patch" data to installer's exe stub. It provides some gain in size,, but also it creates some problems for extracting software like 7-Zip.

      Next alpha version will contain improved NSIS support. So just wait it.