XPStyle on

!include "Sections.nsh"
!include "FileFunc.nsh"
!include "nsDialogs.nsh"
!include "WordFunc.nsh"

;Installer pages
Page custom nsProgPathPage nsProgPathPageLeave "$(^DirSubCaption)"
Page components
Page instfiles

;Uninstaller pages

;Setup compression settings
SetCompressor lzma
SetCompressorDictSize 1
SetCompress auto


;Search for used plugins here
!addplugindir "."

; The file to write
OutFile "test01.exe"

; The default installation directory
InstallDir "$PROGRAMFILES\test01"

; Request application privileges for Windows Vista
;RequestExecutionLevel admin

;Show (un)installer's log window
ShowInstDetails show
ShowUninstDetails show

;Overrideable by script via SetAutoClose command
AutoCloseWindow false

!define PRODUCT_NAME "test01"
;Product GUID is same for all language versions
!define PRODUCT_GUID ""
!define PRODUCT_VERSION "1.00"
!define SMPROGGROUP "test\test01"
!define UNINSTREGKEY "test01"
!define MAINEXENAME "test.exe"
!define UNINSTEXENAME "UninstXX.exe"

;Set product name
Name "${PRODUCT_NAME}"

Var SF_UNSELECT
Var EXEFILEBASE
Var ForcedSetup
Var SourceOnly
Var ShortcutCallCnt

; The stuff to install
Section "Standalone program" MAINPROG
SectionEnd

Section /o "Extract setup script" SETUPSRC
  SetOutPath "$INSTDIR"
  File test01.nsi
SectionEnd


Function .onInit
  StrCpy $0 "InitProgPathPage"
  Call nsProgPathPage
  IntOp $SF_UNSELECT 0xFFFFFFFF ^ ${SF_SELECTED}
  IntOp $SourceOnly 0 - 1 ; valid values: -1=not_checked, 0=no, 1=yes
  IntOp $ForcedSetup 0 + 0 ; valid values: 0=no, 1=yes
  IntOp $ShortcutCallCnt 0 + 0 ; valid values: 0 or greater integer
  StrCpy $EXEFILEBASE $EXEFILE -4 0
  ${GetParameters} $0
  StrCpy $0 "$0 "
;SetSilent
  ClearErrors
  ${GetOptions} "$0" "/s " $1
  IfErrors +2 0
    SetSilent silent
;GetHelp
  ClearErrors
  ${GetOptions} "$0" "/h " $1
  IfErrors +2 0
    Goto DisplayHelp
  ClearErrors
  ${GetOptions} "$0" "-h " $1
  IfErrors +2 0
    Goto DisplayHelp
  ClearErrors
  ${GetOptions} "$0" "/? " $1
  IfErrors +2 0
    Goto DisplayHelp
  ClearErrors
  ${GetOptions} "$0" "/force " $1
  IfErrors +2 0
    IntOp $ForcedSetup 0 + 1
  ClearErrors
  ${GetOptionsS} "$0" "/d=" $INSTDIR
  IfErrors 0 0

  StrCpy $INSTDIR "$PROGRAMFILES\test01"
  Goto EndInit

  DisplayHelp:
    MessageBox MB_OK|MB_ICONINFORMATION "SUPPORTED COMMAND LINE PARAMETERS:$\r$\n$\r$\n\
      /h, -h, /? => Displays this help.$\r$\n\
      /s => Silent mode.$\r$\n\
      /force  => Allows installation if different version was already installed.$\r$\n\
      /D=install_path => It must be the last parameter used in the command line and must not contain any quotes, even if the path contains spaces. Only absolute paths are supported.$\r$\n\
      Note: all parameters are case insensitive.$\r$\n" /SD IDOK
    Abort
  EndInit:
FunctionEnd


Function nsProgPathPage
;IMPORTANT: You must set $0 to "InitProgPathPage" and then call from .onInit function
  StrCmp $0 "InitProgPathPage" 0 SkipInit
  var /GLOBAL ProgPath_Dialog
  var /GLOBAL ProgPath_Label
  var /GLOBAL ProgPath_Icon
  var /GLOBAL ProgPath_Icon_hdl
  Var /GLOBAL ProgGroupFolder
  Var /GLOBAL ProgPath_GroupBox
  Var /GLOBAL ProgPath_hdl
  Var /GLOBAL ProgPath_BBtn_hdl
  Var /GLOBAL ProgPath_SM_GroupBox
  var /GLOBAL ProgPath_SM_hdl
  Var /GLOBAL ProgPath_SM_BBtn_hdl
  var /GLOBAL ProgPath_SpaceReq
  Var /GLOBAL ProgPath_SpaceAvl
  !define SMDirBrowseText "Select the folder for $(^NameDA) icons:"
  StrCpy $ProgGroupFolder "${SMPROGGROUP}"
  StrCpy $0 ""
  Return
  SkipInit:
  Push $0
  Push $1
;  GetDlgItem $0 $hwndparent 3 ;1=Next, 2=Cancel ,3=Back
;  EnableWindow $0 1 ;0=disable, 1=enable
  nsDialogs::Create 1018
    Pop $ProgPath_Dialog
    StrCmp $ProgPath_Dialog "error" 0 +3
      messageBox MB_ICONEXCLAMATION "ProgPathPage dialog creation error!"
      Abort
  GetFunctionAddress $0 "nsProgPathPageLeave"
  nsDialogs::OnBack $0
  ${NSD_CreateIcon} 0 0 241u 23u ""
    Pop $ProgPath_Icon
    ${NSD_SetIconFromInstaller} $ProgPath_Icon $ProgPath_Icon_hdl
;  ${NSD_CreateLabel} 25u 0 241u 25u "Change the Start menu's Programs group subfolder."
  ${NSD_CreateLabel} 25u 0 241u 35u "$(^DirText)"
    Pop $ProgPath_Label

  ${NSD_CreateGroupBox} 1u 38u 264u 30u "$(^DirSubText)"
    Pop $ProgPath_GroupBox
  ${NSD_CreateText} 8u 49u 187u 12u "$INSTDIR"
    Pop $ProgPath_hdl
  ${NSD_CreateBrowseButton} 202u 48u 55u 14u "$(^BrowseBtn)"
    Pop $ProgPath_BBtn_hdl
    GetFunctionAddress $0 "nsProgPathPageBrowse"
    nsDialogs::OnClick $ProgPath_BBtn_hdl $0
    GetFunctionAddress $0 "nsProgPathPagePathChange"
    nsDialogs::OnChange $ProgPath_hdl $0

  ${NSD_CreateGroupBox} 1u 74u 264u 30u "Folder name for shortcuts in the Start menu Programs group:"
    Pop $ProgPath_SM_GroupBox
  ${NSD_CreateText} 8u 85u 187u 12u "$ProgGroupFolder"
    Pop $ProgPath_SM_hdl
  ${NSD_CreateBrowseButton} 202u 84u 55u 14u "$(^BrowseBtn)"
    Pop $ProgPath_SM_BBtn_hdl
    GetFunctionAddress $0 "nsProgPathPageSMBrowse"
    nsDialogs::OnClick $ProgPath_SM_BBtn_hdl $0

  Call GetSpaceRequired
  Pop $0
  IntCmp $0 100 +3 0 +3
    IntCmp $0 0 +2 +2 0
    IntOp $0 103 + 0
  IntOp $0 $0 * 10
  IntOp $0 $0 / 1024
  StrCpy $1 "$0" "" -1
  IntCmp $0 9 +3 +3 0
    StrCpy $0 "$0" -1 ""
    Goto +2
    StrCpy $0 "0"
  ${NSD_CreateLabel} 0u 111u 264u 11u "$(^SpaceRequired)$0.$1 MiB"
    Pop $ProgPath_SpaceReq
  Push "$INSTDIR"
  Call DisplayFreeSpace
  Pop $0
  ${NSD_CreateLabel} 0u 122u 264u 11u "$(^SpaceAvailable)$0"
    Pop $ProgPath_SpaceAvl
  nsDialogs::Show
  Pop $1
  Pop $0
FunctionEnd
Function nsProgPathPageBrowse
  Push $0
  Push $1
  ${NSD_GetText} $ProgPath_hdl $0
  CleanPath:
    StrCpy $1 "$0" "" -1
    StrCmp $1 "\" StripOut 0
    StrCmp $1 " " StripOut 0
    Goto Check
  StripOut:
    StrCpy $0 "$0" -1 ""
    Goto CleanPath
  Check:
    IfFileExists "$0\*.*" BrowsePath 0
    Push $0
    Call GetParentDir
    Exch
    Pop $0
    Pop $0
    StrCmp $0 "" 0 Check
    StrCpy $0 "$PROGRAMFILES"
  BrowsePath:
    nsDialogs::SelectFolderDialog "$(^DirBrowseText)" "$0" ;The initial directory should exist!
    Pop $0
    StrCmp $0 "error" KeepPath 0
      StrCmp $0 "" KeepPath 0
        StrCpy $INSTDIR "$0"
        ${NSD_SetText} $ProgPath_hdl "$INSTDIR"
        Push "$INSTDIR"
        Call DisplayFreeSpace
        Pop $0
        SendMessage $ProgPath_SpaceAvl ${WM_SETTEXT} 0 "STR:$(^SpaceAvailable)$0"
  KeepPath:
  Pop $1
  Pop $0
FunctionEnd
Function nsProgPathPageSMBrowse
  Push $0
  Push $1
  Push $2
  Push $3
  Push $4
  ${NSD_GetText} $ProgPath_SM_hdl $0
  CleanPath:
    StrCpy $1 "$0" "" -1
    StrCmp $1 "\" StripOut 0
    StrCmp $1 " " StripOut 0
    Goto Check
  StripOut:
    StrCpy $0 "$0" -1 ""
    Goto CleanPath
  Check:
    StrCmp $0 "" SetSMProgPath 0
    IfFileExists "$SMPROGRAMS\$0\*.*" SetSMPath 0
    Push $0
    Call GetParentDir
    Exch
    Pop $0
    Pop $0
    StrCmp $0 "" 0 Check
  SetSMProgPath:
    StrCpy $0 "$SMPROGRAMS"
    Goto BrowsePath
  SetSMPath:
    StrCpy $0 "$SMPROGRAMS\$0"
  BrowsePath:
;    nsDialogs::SelectFolderDialog "${SMDirBrowseText}" "$0" ;The directory must exist!!!

Goto +7 ;test the BrowseFolderByRoot function
Push "${SMDirBrowseText}"
Push "$0"
Push 2
Push "$ProgPath_Dialog"
Call BrowseFolderByCSIDL
Goto +6 ;skip the BrowseFolderByRoot function

Push "${SMDirBrowseText}"
Push "$0"
Push "$SMPROGRAMS"
Push "$ProgPath_Dialog"
Call BrowseFolderByRoot

    Pop $0
    StrCmp $0 "error" KeepPath 0
      StrCmp $0 "" KeepPath 0
        StrLen $1 "$0"
        StrLen $2 "$SMPROGRAMS"
        IntCmp $1 $2 SMPathErr2 SMPathErr 0
          StrCpy $3 "$0" $2 ""
          IntOp $2 $2 + 1
          StrCpy $4 "$0" "" $2
          StrCmp $3 "$SMPROGRAMS" 0 SMPathErr
            StrCmp $4 "" SMPathErr2 0
            StrCpy $ProgGroupFolder "$4"
            ${NSD_SetText} $ProgPath_SM_hdl "$ProgGroupFolder"
            Goto KeepPath
  SMPathErr:
    messageBox MB_ICONEXCLAMATION "ERROR: Folder selection outside of the Start menu Programs folder is prohibited!"
    Goto KeepPath
  SMPathErr2:
    messageBox MB_ICONEXCLAMATION "ERROR: A subfolder in the Start menu Programs folder must be selected!"
  KeepPath:
  Pop $4
  Pop $3
  Pop $2
  Pop $1
  Pop $0
FunctionEnd

Function nsProgPathPagePathChange
  Exch $0
  Push $1
  Push $2
  StrCpy $1 "$INSTDIR" 3 0
  ${NSD_GetText} $ProgPath_hdl $0
  StrCpy $2 "$0" 2 1
  StrCmp $2 ":\" 0 EndFunction
    StrCpy $2 "$0" 3 0
    StrCmp $1 $2 EndFunction 0
      StrCpy $INSTDIR "$0"
      Push "$2" ;The correct space is shown only for existing path, otherwise is shown for a SystemRoot
      Call DisplayFreeSpace
      Pop $0
      SendMessage $ProgPath_SpaceAvl ${WM_SETTEXT} 0 "STR:$(^SpaceAvailable)$0"
  EndFunction:
  Pop $2
  Pop $1
  Pop $0
FunctionEnd

Function nsProgPathPageLeave
  Push $0
;NOTE: INSTDIR variable is always validated automatically if changed.
;      We will use it to validate the Start menu Programs group folder.
;Validating ProgGroupFolder
  ${NSD_GetText} $ProgPath_SM_hdl $0
  ${NSD_GetText} $ProgPath_SM_hdl $INSTDIR
  StrCmp "$0" "" 0 +3
    messageBox MB_ICONEXCLAMATION "Start menu Folder name is empty!"
    Abort
  Push "$INSTDIR"
  Call ValidatePathPlus
  Pop $INSTDIR
  StrCmp "$INSTDIR" "$0" +4 0
    messageBox MB_ICONEXCLAMATION "WARNING! The Start menu Programs group folder was changed by setup to prevent incompatibilities with Windows. Please check it again and then click Next to continue."
    ${NSD_SetText} $ProgPath_SM_hdl "$INSTDIR"
    Abort
  StrCpy $ProgGroupFolder "$0"
;Validating INSTDIR
  ${NSD_GetText} $ProgPath_hdl $0
  ${NSD_GetText} $ProgPath_hdl $INSTDIR
  Push "$INSTDIR"
  Call ValidatePathPlus
  Pop $INSTDIR
  StrCmp "$INSTDIR" "$0" +4 0
    messageBox MB_ICONEXCLAMATION "WARNING! Destination folder was changed by setup to prevent incompatibilities with Windows. Please check it again and then click Next to continue."
    ${NSD_SetText} $ProgPath_hdl "$INSTDIR"
    Abort
  Pop $0
FunctionEnd


Function ValidatePathPlus
;This filtering makes an additional safety+compatibility for Windows
;Input: Full or relative path on top of the stacks
;Output: Filtered path on top of the stacks
  Exch $0
  Push $1
  Loop1:
    StrCpy $1 "$0"
;Replace relative paths with "*"
    ${WordReplace} $0 '\.\' '\*\' '+*' $0
    ${WordReplace} $0 '\..\' '\**\' '+*' $0
    ${WordReplace} $0 ':.\' ':*\' '+' $0
    ${WordReplace} $0 ':..\' ':**\' '+' $0
    StrCmp "$0" "$1" 0 Loop1
;Replace incompatible constellations
  Loop2:
    StrCpy $1 "$0"
; A dot at the end of a folder name is invalid
    ${WordReplace} $0 '.\' '\' '+*' $0
; A space at the begening of a folder name is invalid
    ${WordReplace} $0 ' \' '\' '+*' $0
; A space at the end of a folder name is invalid
    ${WordReplace} $0 '\ ' '\' '+*' $0
    StrCmp "$0" "$1" 0 Loop2
;Restore relative paths
  Loop3:
    StrCpy $1 "$0"
;Replace "*" with "."
    ${WordReplace} $0 '\*\' '\.\' '+*' $0
    ${WordReplace} $0 '\**\' '\..\' '+*' $0
    ${WordReplace} $0 ':*\' ':.\' '+' $0
    ${WordReplace} $0 ':**\' ':..\' '+' $0
    StrCmp "$0" "$1" 0 Loop3
  Pop $1
  Exch $0
FunctionEnd


Function BrowseFolderByCSIDL
;Input1: A handle to the owner window for the dialog box on top of the stacks.
;Input2: The CSIDL of the root folder from which to start browsing 
;        as 2nd element of stacks. Only the specified folder and its 
;        subfolders in the namespace hierarchy appear in the dialog box. 
;        This member can be NULL; in that case, a default location is used.
;Input3: Initially selected folder as 3rd element of stacks
;Input4: Text above the tree view control in the dialog box as 4th element of stacks.
;Output: Selected path on top of the stacks.
;
;CSIDL_PROGRAMS=0x02 (2) ;StartMenu\Programs
;CSIDL_STARTMENU=0x0B (11) ;StartMenu
;MAX_PATH=260 ;Windows shell path lenght limitaion including terminating null character
;
  Exch $0
  Exch
  Exch $1
  Exch 2
  Exch $2
  Exch 1
  Exch 3
  Exch $3
  Push $4
  Push $5
  Push $6
  IntOp $4 $1 + 0
;Getting PIDL from CSIDL of root folder
  System::Call /NoUnload "shell32::SHGetSpecialFolderLocation(i r0, i $4, *i.r1) i.r6"
  StrCmp $6 "error" +2 +4 ;Error code 0 is returned on success
  IntCmp $6 0 0 0 +3 ;Error code 0 is returned on success
    messageBox MB_ICONEXCLAMATION "DEBUG: SHGetSpecialFolderLocation error!"
    Goto Error
  IntOp $4 0 + 0x41 ;0x01=BIF_RETURNONLYFSDIRS; 0x08=BIF_RETURNFSANCESTORS; 0x10=BIF_EDITBOX; 0x40=BIF_NEWDIALOGSTYLE; 0x4000=BIF_BROWSEINCLUDEFILES
;BROWSEINFO structure that contains information used to display the dialog box.
  System::Call /NoUnload "*(i $0, i r1, t r2, t r3, i $4, i n, i n, i n) i.r5"
;Display the dialog window
  System::Call /NoUnload "shell32::SHBrowseForFolder(i r5) i.r4"
  IntCmp $4 0 0 +2 +2 ;0 means user cancelled, other values means success
    Goto Canceled
;Read out the selected path
  System::Call /NoUnload "shell32::SHGetPathFromIDList(i r4, t.r0) i.r6"
  System::Call /NoUnload "ole32::CoTaskMemFree(i r4)" ;Free up the memory assigned for the output PIDL
  IntCmp $6 0 0 +3 +3 ;0 means error, other values means success
    messageBox MB_ICONEXCLAMATION "DEBUG: SHGetPathFromIDList error!"
    Goto Error
  Error:
    StrCpy $0 "error"
    Goto Finish
  Canceled:
    StrCpy $0 ""
  Finish:
  System::Free /NoUnload $5
  System::Free /NoUnload $2
  System::Call "ole32::CoTaskMemFree(i r1)" ;Free up the memory assigned for the BROWSEINFO.pidlRoot
  Pop $6
  Pop $5
  Pop $4
  Pop $3
  Pop $2
  Pop $1
  Exch $0
FunctionEnd

Function BrowseFolderByRoot
;Input1: A handle to the owner window for the dialog box on top of the stacks.
;Input2: The location of the root folder from which to start browsing 
;        as 2nd element of stacks. Only the specified folder and its 
;        subfolders in the namespace hierarchy appear in the dialog box. 
;        This member can be NULL; in that case, a default location is used.
;Input3: Initially selected folder as 3rd element of stacks
;Input4: Text above the tree view control in the dialog box as 4th element of stacks.
;Output: Selected path on top of the stacks.
;

;StrCpy $1 "par1"
;StrCpy $2 "par2"
;StrCpy $3 "par3"
;StrCpy $4 "par4"
;StrCpy $5 "par5"
  Exch $0
  Exch
  Exch $1
  Exch 2
  Exch $2
  Exch 1
  Exch 3
  Exch $3
  Push $4
  Push $5
  Push $6
  Push $7
  Push $8
  Push $9
  IntOp $4 $1 + 0
  StrCpy $7 "$2"
  !define MAX_PATH 260 ;Windows shell is limited to 260 characters(with null terminating char.)


  System::Call /NoUnload "ole32::CoInitialize(*i.r8) i.r5"
    StrCmp $5 "error" +3 +4 ;error means error
    IntCmp $5 0 +3 0 0 ; 0 means success, other values means error
    messageBox MB_ICONEXCLAMATION "DEBUG: CoInitialize error!"
    Goto Error

  System::Call /NoUnload "shell32::SHGetMalloc(*i.r9) i.r5"
    StrCmp $5 "error" +3 +4 ;error means error
    IntCmp $5 0 +3 0 0 ; 0 means success, other values means error
    messageBox MB_ICONEXCLAMATION "DEBUG: SHGetMalloc error!"
    Goto Error

  System::Call /NoUnload "shell32::SHGetDesktopFolder(*i.r6) i.r5"
    StrCmp $5 "error" +3 +4 ;error means error
    IntCmp $5 0 +3 0 0 ; 0 means success, other values means error
    messageBox MB_ICONEXCLAMATION "DEBUG: SHGetDesktopFolder error!"
    Goto Error

  System::Call /NoUnload "*(&w${MAX_PATH}) i.r2" ;Buffer allocation for unicode path
;Path string must be converted to Unicode 
;CodePage=0 (CP_ACP), dwFlags=0, string-to-convert, length=-1(null terminated),
  System::Call /NoUnload "kernel32::MultiByteToWideChar(i 0, i 0, t r1, i -1, i r2, i ${MAX_PATH}) i.r5"
    IntCmp $5 0 0 0 +3 ; Larger value than 0 indicates success
    messageBox MB_ICONEXCLAMATION "DEBUG: MultiByteToWideChar error!"
    Goto Error

;Convert Unicode path string to PIDL
  System::Call /NoUnload "shell32::ParseDisplayName(i n, i n, i r2, i.n, *i.r1, i n) i.r5"
    messageBox MB_ICONEXCLAMATION "DEBUG: 1=$1; 2=$2; 5=$5; 7=$7"
    IntCmp $1 0 +3 +6 +6 ;0 means error
    StrCmp $5 "error" +2 +5 ;error means error
    IntCmp $5 0 +4 0 0 ;0 means success, other values means error
    messageBox MB_ICONEXCLAMATION "DEBUG: ParseDisplayName error!"
    StrCpy $1 ""
;    Goto Error
    Nop

;Struct to handle the select folder dialog.
  IntOp $4 0 + 1 ;0x01=BIF_RETURNONLYFSDIRS; 0x04=BIF_STATUSTEXT
  System::Call /NoUnload "*(i $0, i r1, t r7, t '$3', i $4, i n, i n, i n) i.r5"

;Display the dialog window
  System::Call /NoUnload "shell32::SHBrowseForFolder(i r5) i.r4"
  IntCmp $4 0 0 +2 +2 ;0 means user cancelled, other values means success
    Goto Canceled
;Read out the selected path
  System::Call /NoUnload "shell32::SHGetPathFromIDList(i r4, t.r0) i.r6"
  System::Call /NoUnload "ole32::CoTaskMemFree(i r4)" ;Free up the memory assigned for the output PIDL
  IntCmp $6 0 0 +3 +3 ;0 means error, other values means success
    messageBox MB_ICONEXCLAMATION "DEBUG: SHGetPathFromIDList error!"
    Goto Error
  Error:
    StrCpy $0 "error"
    Goto Finish
  Canceled:
    StrCpy $0 ""
  Finish:
System::Free $6
System::Free $9
System::Call /NoUnload "ole32::CoUninitialize()"
  System::Free $1
  System::Free $2
  System::Free $5
  System::Free $6
  System::Free $7
  Pop $9
  Pop $8
  Pop $7
  Pop $6
  Pop $5
  Pop $4
  Pop $3
  Pop $2
  Pop $1
  Exch $0
;messageBox MB_ICONEXCLAMATION "DEBUG: par0=$0; par1=$1; par2=$2; par3=$3; par4=$4; par5=$5"
FunctionEnd

Function DisplayFreeSpace
  Exch $0
  Push $1
  Push $2
  Push $0
  Call GetSpaceAvailable
  Pop $0
  Pop $1
  StrCmp $1 "KiB" in_KiB 0
    StrCmp $1 "MiB" in_MiB 0
      StrCmp $1 "GiB" in_GiB 0
        Goto Error
  in_KiB:
    IntCmp $0 8096 0 KiB_to_KiBI 0
    IntCmp $0 1048576 0 KiB_to_MiBF 0 ;Floating point
    IntCmp $0 8388608 0 KiB_to_MiBI 0 ;Integer
    IntCmp $0 1073741824 0 KiB_to_GiBF 0
    Goto KiB_to_GiBI
    KiB_to_KiBI:
      StrCpy $0 "$0 KiB"
      Goto EndFunction
    KiB_to_MiBF:
      IntOp $1 $0 * 10
      IntOp $1 $1 / 1024
      StrCpy $2 "$1" "" "-1"
      StrCpy $1 "$1" "-1" ""
      StrCpy $0 "$1.$2 MiB"
      Goto EndFunction
    KiB_to_MiBI:
      IntOp $1 $0 / 1024
      StrCpy $0 "$1 MiB"
      Goto EndFunction
    KiB_to_GiBF:
      IntOp $1 $0 / 1048576
      IntOp $2 $1 * 1048576
      IntOp $2 $0 - $2
      IntOp $2 $2 * 10
      IntOp $2 $2 / 1048576
      IntCmp $2 10 0 +2 0
        IntOp $2 9 + 0
      StrCpy $0 "$1.$2 GiB"
      Goto EndFunction
    KiB_to_GiBI:
      IntOp $1 $0 / 1048576
      StrCpy $0 "$1 GiB"
      Goto EndFunction
  in_MiB:
    IntCmp $0 8096 0 MiB_to_MiBI 0
    IntCmp $0 1048576 0 MiB_to_GiBF 0
    IntCmp $0 8388608 0 MiB_to_GiBI 0
    IntCmp $0 1073741824 0 MiB_to_TiBF 0
    Goto MiB_to_TiBI
    MiB_to_MiBI:
      StrCpy $0 "$0 MiB"
      Goto EndFunction
    MiB_to_GiBF:
      IntOp $1 $0 * 10
      IntOp $1 $1 / 1024
      StrCpy $2 "$1" "" "-1"
      StrCpy $1 "$1" "-1" ""
      StrCpy $0 "$1.$2 GiB"
      Goto EndFunction
    MiB_to_GiBI:
      IntOp $1 $0 / 1024
      StrCpy $0 "$1 GiB"
      Goto EndFunction
    MiB_to_TiBF:
      IntOp $1 $0 / 1048576
      IntOp $2 $1 * 1048576
      IntOp $2 $0 - $2
      IntOp $2 $2 * 10
      IntOp $2 $2 / 1048576
      IntCmp $2 10 0 +2 0
        IntOp $2 9 + 0
      StrCpy $0 "$1.$2 TiB"
      Goto EndFunction
    MiB_to_TiBI:
      IntOp $1 $0 / 1048576
      StrCpy $0 "$1 TiB"
      Goto EndFunction
  in_GiB:
    IntCmp $0 8096 0 GiB_to_GiBI 0
    IntCmp $0 1048576 0 GiB_to_TiBF 0 
    Goto GiB_to_TiBI
    GiB_to_GiBI:
      StrCpy $0 "$0 GiB"
      Goto EndFunction
    GiB_to_TiBF:
      IntOp $1 $0 * 10
      IntOp $1 $1 / 1024
      StrCpy $2 "$1" "" "-1"
      StrCpy $1 "$1" "-1" ""
      StrCpy $0 "$1.$2 TiB"
      Goto EndFunction
    GiB_to_TiBI:
      IntOp $1 $0 / 1024
      StrCpy $0 "$1 TiB"
      Goto EndFunction
  Error:
    messageBox MB_ICONEXCLAMATION "GetSpaceAvailable function returned an invalid unit prefix!"
    StrCpy $0 "error"
  EndFunction:
  Pop $2
  Pop $1
  Exch $0
FunctionEnd


Function GetParentDir
; Uses stacks top item as input, ending backslash is automatically removed
; Returns modified path as stacks top element
; Returns splitted-out file/directory name as stacks 2nd element
; No error flag is set on error, but output string will be empty
;
  Exch $0
  Push $1
  Push $2
  StrCpy $1 $0 1 -1
  StrCmp $1 '\' 0 +3
    StrCpy $0 $0 -1 ""
    Goto -3
    IntOp $2 0 + 0
    IntOp $2 $2 - 1
    StrCpy $1 $0 1 $2
    StrCmp $1 '\' +2 0
      StrCmp $1 '' 0 -3
      IntOp $2 $2 + 1
      StrCpy $1 $0 "" $2
      IntOp $2 $2 - 1
      StrCpy $0 $0 $2 ""
  Pop $2
  Exch $1
  Exch
  Exch $0
FunctionEnd


Function GetSpaceAvailable
;Based on script by Anders, http://stackoverflow.com/questions/13006071/how-to-check-freespace-available-on-installdir-in-nsis
;Input: Full path to the directory on top of the stack
;Output1: Available free space for the directory on top of the stacks (32-bit integer)
;Output2: KiB, MiB or GiB suffix for returned value as 2nd element of stacks
  Exch $0
  Push $1
  System::Call 'kernel32::GetDriveType(t) i(r0) .r1'
  IntCmp $1 5 0 +3 +3 ;Type 5 is the CD-ROM
    IntOp $0 0 + 0
    Goto CnvToKiB
  System::Call 'kernel32::GetDiskFreeSpaceEx(tr0,*l0s,*l,*l)i.r1'
  IntCmp $1 0 0 PostProcess PostProcess
    Exch $2 ;Throw away result from GetDiskFreeSpaceEx
    System::Call 'kernel32::SetCurrentDirectory(tr0)'
    System::Call 'kernel32::GetDiskFreeSpace(i0,*i0r1,*i0r0,*i0r2,*i)'
    System::Call 'kernel32::SetCurrentDirectory(to)'
    IntOp $1 $1 * $0
    System::Int64Op $1 * $2
    Exch
    Pop $2
  PostProcess:
    Pop $0
    System::Int64Op $0 < 0x000001FFFFFFFC00
    Pop $1
    IntCmp $1 0 0 0 CnvToKiB
      System::Int64Op $0 < 0x0007FFFFFFF00000
      Pop $1
      IntCmp $1 0 0 0 CnvToMiB
        Goto CnvToGiB
  CnvToKiB:
    Push "KiB"
    System::Int64Op $0 / 1024 ;to KiB
    Goto CnvEnd
  CnvToMiB:
    Push "MiB"
    System::Int64Op $0 / 1048576 ;to MiB
    Goto CnvEnd
  CnvToGiB:
    Push "GiB"
    System::Int64Op $0 / 1073741824 ;to GiB
    Goto CnvEnd
  CnvEnd:
    Exch 2
    Exch
    Exch 3
    Pop $0
    Pop $1
FunctionEnd

Function GetSpaceRequired
  Push $0
  Push $1
  IntOp $1 0 + 0
  SectionGetFlags ${MAINPROG} $0
  IntOp $0 $0 & ${SF_SELECTED}
  IntCmp $0 1 0 +3 0
    SectionGetSize ${MAINPROG} $0
    IntOp $1 $1 + $0
  SectionGetFlags ${SETUPSRC} $0
  IntOp $0 $0 & ${SF_SELECTED}
  IntCmp $0 1 0 +3 0
    SectionGetSize ${SETUPSRC} $0
    IntOp $1 $1 + $0
  IntOp $0 $1 + 60 ;60 KiB additional space
  Pop $1
  Exch $0
FunctionEnd


Function .onGUIInit
FunctionEnd

Function .onGUIEnd
FunctionEnd