; ----disable UAC prompt----
RequestExecutionLevel user

; ----Compressor----
SetCompressor /SOLID lzma

;---Definitions----
!define SNAME "yaP"
!define nircmd "$TEMP\nircmd.exe"

;-----Runtime switches----
CRCCheck off
AutoCloseWindow True
SilentInstall silent
WindowIcon off
XPSTYLE on 

;-----Includes----
!include "Registry.nsh"
!include "nsProcess.nsh"
!include "FileFunc.nsh"
!insertmacro "Getparameters"
!define StrContains '!insertmacro "_StrContainsConstructor"'
!define StrReplaceV4 `!insertmacro StrReplaceV4`

!define Var0 $R0
!define Var1 $R1
!define Var2 $R2
!define Var3 $R3
!define Var4 $R4
!define Var5 $R5
!define Var6 $R6
!define Var7 $R7
!define Var8 $R8

!macro StrReplaceV4 Var Replace With In
 Push ${Var1}
 Push ${Var2}
 Push `${Replace}`
 Push `${With}`
 Push `${In}`
 Call StrReplaceV4
 Pop `${Var}`
 Pop ${Var2}
 Pop ${Var1}
!macroend

!macro _StrContainsConstructor OUT NEEDLE HAYSTACK
  Push "${HAYSTACK}"
  Push "${NEEDLE}"
  Call StrContains
  Pop "${OUT}"
!macroend

;-----Set basic information-----
Name "${SNAME}"
Icon "${SNAME}.ico"
Caption "${SNAME}"
OutFile "..\${SNAME}.exe"

;-----Version Information------
LoadLanguageFile "${NSISDIR}\Contrib\Language files\English.nlf"

VIProductVersion "0.0.2.3"
VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "${SNAME}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "Štpr 2011"
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "${SNAME} - yet another Portablizer"
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "0.23"

Var commandline
Var params
Var APP
Var SILENT
Var NOWAIT
Var rootdir
Var ini
Var DeleteRegKeyIfEmpty

Section "Main"

${StrReplaceV4} $0 "$EXEDIR\" "" "$EXEPATH"
${StrReplaceV4} $0 ".exe" "" "$0"

  StrCpy $ini $0

  IfFileExists "$EXEDIR\$0.ini" +2
	StrCpy $ini ${SNAME}	

  IfFileExists "$EXEDIR\$ini.ini" +6 0
  MessageBox MB_YESNO "No ini file found! Do you want to create a blank one?" IDYES 0 IDNO End
  SetOutPath "$EXEDIR"
  File ${SNAME}.ini
  Rename ${SNAME}.ini $0.ini
  goto End
  
  ReadINIStr "$APP" "$EXEDIR\$ini.ini" GENERAL application
  StrCmp "$APP" "" 0 +3
  MessageBox MB_OK "Application not set!$\nPlease check $ini.ini."
  goto End
  
  ; check if another instance of the application is running
	
	Push "$APP"
	Push "\"
	Call GetAfterChar
	Pop $9
	
	ReadINIStr $0 "$EXEDIR\$ini.ini" GENERAL multipleinstance	
	StrCmp $0 1 notRunning 0
	${nsProcess::FindProcess} "$9" $R0
	StrCmp $R0 0 0 notRunning
        StrCmp $0 2 0 check_multipleinstance_minus_one	; launch main application and quit if multipleinstance = 2
            ReadINIStr "$commandline" "$EXEDIR\$ini.ini" GENERAL commandline
            ${StrContains} $2 ":\" "$APP"	; check if $APP has a relative path
            StrCmp $2 ":\" 0 +3
                Exec '"$APP" $commandline'
                Abort
                Exec '"$EXEDIR\$APP" $commandline'
                Abort
        check_multipleinstance_minus_one:
		StrCmp $0 -1 0 +2	; if multipleinstance = -1 then Abort
            Abort
		MessageBox MB_YESNO "The application is already running!$\n($9)$\n$\nAre you sure to launch $ini?" IDYES notRunning IDNO End
	
    notRunning:
  
  ; determine path if it's absolute or relative
	
	${StrContains} $2 ":\" "$APP"	; check if $APP has a relative path
	StrCmp $2 ":\" path_check_end

		StrCpy $APP "$EXEDIR\$APP"

	path_check_end:
	
	IfFileExists "$APP" continue
	MessageBox MB_OK "Application not found!$\nPlease check $ini.ini."
	goto End

continue:
  
  ReadINIStr "$rootdir" "$EXEDIR\$ini.ini" GENERAL rootdir
  
    StrCmp "$rootdir" "" check_commandline
  
  ; determine rootdir if it's absolute or relative
	
	${StrContains} $2 ":\" "$rootdir"	; check if $rootdir is a relative path
	StrCmp $2 ":\" rootdir_check_end

		StrCpy $rootdir "$EXEDIR\$rootdir"

	rootdir_check_end:
	
	; remove trailing "\"
		StrCpy $OutDir $rootdir
		StrCpy $0 $OutDir
	
	IfFileExists $0 +3
	MessageBox MB_OK "Root directory not found!$\nPlease check $ini.ini."
	goto End
	
	; make sure rootdir path has one trailing "\"
	StrCpy $rootdir "$rootdir\"
	${StrReplaceV4} $rootdir "\\" "\" "$rootdir"
  
  check_commandline:
  
  ReadINIStr "$commandline" "$EXEDIR\$ini.ini" GENERAL commandline

  ; prompt the user to launch the main application or quit
  
  ReadINIStr $5 "$EXEDIR\$ini.ini" BEFORE message
  StrCmp $5 "" +3
  MessageBox MB_YESNO "You are about to launch $APP.$\nContinue?" IDYES +2
  goto End

  ; check files
  
  ReadINIStr $0 "$EXEDIR\$ini.ini" BEFORE batch
  StrCmp $0 "" +4
  IfFileExists "$rootdir$0" +3 0
  MessageBox MB_OK "$rootdir$0 not found!"
  goto End

  ReadINIStr $0 "$EXEDIR\$ini.ini" BEFORE registry
  StrCmp $0 "" +4
  IfFileExists "$rootdir$0" +3 0
  MessageBox MB_OK "$rootdir$0 not found!"
  goto End
  
  ReadINIStr $0 "$EXEDIR\$ini.ini" AFTER batch
  StrCmp $0 "" +4
  IfFileExists "$rootdir$0" +3 0
  MessageBox MB_OK "$rootdir$0 not found!"
  goto End

  ReadINIStr $0 "$EXEDIR\$ini.ini" AFTER registry
  StrCmp $0 "" +4
  IfFileExists "$rootdir$0" +3 0
  MessageBox MB_OK "$rootdir$0 not found!"
  goto End
  
  ReadINIStr "$SILENT" "$EXEDIR\$ini.ini" GENERAL silentbatch
  
  ReadINIStr "$NOWAIT" "$EXEDIR\$ini.ini" GENERAL nowait
  
  ReadINIStr $0 "$EXEDIR\$ini.ini" BEFORE batch
  StrCmp $0 "" reg_before
  
  IfFileExists "$rootdir$0" +3 0
  MessageBox MB_OK "$rootdir$0 not found!"
  goto End
  
    StrCmp "$SILENT" "1" 0 +6
    IfFileExists "${nircmd}" +3
		SetOutPath $TEMP
		File nircmd.exe
    ExecWait '"${nircmd}" exec hide "$rootdir$0"'
    goto reg_before
	
	; check if cmd window should be closed
		StrCmp "$SILENT" "-1" 0 +3
			Exec '"$SYSDIR\cmd.exe" /k $rootdir$0'	; do not close cmd window
			Goto reg_before
			ExecShell "" "$rootdir$0"	; close cmd window on finish
  
  
  reg_before:
  ReadINIStr $0 "$EXEDIR\$ini.ini" BEFORE registry
  StrCmp $0 "" sys_before
  
  IfFileExists "$rootdir$0" +3 0
  MessageBox MB_OK "$rootdir$0 not found!"
  goto End
  
  ${registry::RestoreKey} "$rootdir$0" $R0
  Sleep 200
  
  ; delete registry key if empty (before part)
  Sleep 1000
  
  ReadINIStr $0 "$EXEDIR\$ini.ini" BEFORE wait
  Sleep $0
  
  StrCpy "$DeleteRegKeyIfEmpty" ""
  StrCpy $9 0
  ClearErrors
  
  regKeyEmpty_loop_before:
	Sleep 200
  	IntOp $9 $9 + 1
  	ReadINIStr "$DeleteRegKeyIfEmpty" "$EXEDIR\$ini.ini" BEFORE DeleteRegKeyIfEmpty$9
	ifErrors sys_before
  	${registry::DeleteKeyEmpty} "$DeleteRegKeyIfEmpty" $R0
  goto regKeyEmpty_loop_before

  sys_before:
  ClearErrors
  ReadINIStr $0 "$EXEDIR\$ini.ini" BEFORE systemrefresh
  StrCmp $0 "1" 0 +2
  System::Call 'shell32::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
  
  Sleep 300
  
  ReadINIStr $0 "$EXEDIR\$ini.ini" BEFORE wait
  Sleep $0
  
  ${Getparameters} $params
  
  StrCpy $0 "$APP"
  
  StrCmp $commandline "" +2 0
  StrCpy $0 '"$APP" $commandline'
  
  StrCmp $params "" +2 0
  StrCpy $0 "$0 $params"
  	
  StrCmp $NOWAIT 1 0 +3
	Exec $0
	goto End

; set working directory

	SetOutPath "$APP\..\"

  ReadINIStr $1 "$EXEDIR\$ini.ini" GENERAL workdir
  StrCmp $1 "" launch
	
	${StrContains} $2 ":\" "$1"	; check if workdir is a relative path
	StrCmp $2 ":\" workdir_check_end

		StrCpy $1 "$EXEDIR\$1"

	workdir_check_end:	
	SetOutPath $1

launch:

ExecWait $0
Sleep 300

	; restore working directory
	SetOutPath "$EXEDIR"
 
  ; AFTER PART
  
  StrCpy $5 ""
  ReadINIStr $5 "$EXEDIR\$ini.ini" AFTER message
  StrCmp $5 "" +3
  MessageBox MB_YESNO "You are about to execute AFTER items.$\nContinue?" IDYES +2
  goto End
  
  ReadINIStr $0 "$EXEDIR\$ini.ini" AFTER wait
  Sleep $0
  
  ReadINIStr "$SILENT" "$EXEDIR\$ini.ini" GENERAL silentbatch
  
  ReadINIStr $0 "$EXEDIR\$ini.ini" AFTER batch
  StrCmp $0 "" reg_after
  
  IfFileExists "$rootdir$0" +3 0
  MessageBox MB_OK "$rootdir$0 not found!"
  goto End
  
    StrCmp "$SILENT" "1" 0 +6
        IfFileExists "${nircmd}" +3
		SetOutPath $TEMP
		File nircmd.exe
    ExecWait '"${nircmd}" exec hide "$rootdir$0"'
    goto reg_after
   	
	; check if cmd window should be closed
		StrCmp "$SILENT" "-1" 0 +3
			Exec '"$SYSDIR\cmd.exe" /k $rootdir$0'	; do not close cmd window
			Goto reg_after
			ExecShell "" "$rootdir$0"	; close cmd window on finish
  
  reg_after:
  ReadINIStr $0 "$EXEDIR\$ini.ini" AFTER registry
  StrCmp $0 "" DeleteRegKey_after
  
  IfFileExists "$rootdir$0" +3 0
  MessageBox MB_OK "$rootdir$0 not found!"
  goto End
  
  ${registry::RestoreKey} "$rootdir$0" $R0
  Sleep 500
  
  DeleteRegKey_after:
  ; delete registry key if empty (after part)
  Sleep 1000
  
  ReadINIStr $0 "$EXEDIR\$ini.ini" AFTER wait
  Sleep $0
  
  StrCpy "$DeleteRegKeyIfEmpty" ""
  StrCpy $9 0
  ClearErrors
  
  regKeyEmpty_loop_after:
  	Sleep 200
  	IntOp $9 $9 + 1
  	ReadINIStr "$DeleteRegKeyIfEmpty" "$EXEDIR\$ini.ini" AFTER DeleteRegKeyIfEmpty$9
	ifErrors sys_after
  	${registry::DeleteKeyEmpty} "$DeleteRegKeyIfEmpty" $R0
  goto regKeyEmpty_loop_after
  
  sys_after:
  ClearErrors
  ReadINIStr $0 "$EXEDIR\$ini.ini" AFTER systemrefresh
  StrCmp $0 "1" 0 +2
  System::Call 'shell32::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'

End:

	Delete "$TEMP\nircmd.exe"
	${registry::unload}
	${nsProcess::Unload}
	
SectionEnd

; StrContains
; This function does a case sensitive searches for an occurrence of a substring in a string. 
; It returns the substring if it is found. 
; Otherwise it returns null(""). 
; Written by kenglish_hi
; Adapted from StrReplace written by dandaman32
 
Var STR_HAYSTACK
Var STR_NEEDLE
Var STR_CONTAINS_VAR_1
Var STR_CONTAINS_VAR_2
Var STR_CONTAINS_VAR_3
Var STR_CONTAINS_VAR_4
Var STR_RETURN_VAR
 
Function StrContains
  Exch $STR_NEEDLE
  Exch 1
  Exch $STR_HAYSTACK
  ; Uncomment to debug
  ;MessageBox MB_OK 'STR_NEEDLE = $STR_NEEDLE STR_HAYSTACK = $STR_HAYSTACK '
    StrCpy $STR_RETURN_VAR ""
    StrCpy $STR_CONTAINS_VAR_1 -1
    StrLen $STR_CONTAINS_VAR_2 $STR_NEEDLE
    StrLen $STR_CONTAINS_VAR_4 $STR_HAYSTACK
    loop:
      IntOp $STR_CONTAINS_VAR_1 $STR_CONTAINS_VAR_1 + 1
      StrCpy $STR_CONTAINS_VAR_3 $STR_HAYSTACK $STR_CONTAINS_VAR_2 $STR_CONTAINS_VAR_1
      StrCmp $STR_CONTAINS_VAR_3 $STR_NEEDLE found
      StrCmp $STR_CONTAINS_VAR_1 $STR_CONTAINS_VAR_4 done
      Goto loop
    found:
      StrCpy $STR_RETURN_VAR $STR_NEEDLE
      Goto done
    done:
   Pop $STR_NEEDLE ;Prevent "invalid opcode" errors and keep the
   Exch $STR_RETURN_VAR  
FunctionEnd

Function StrReplaceV4
Exch ${Var0} #in
Exch 1
Exch ${Var1} #with
Exch 2
Exch ${Var2} #replace
Push ${Var3}
Push ${Var4}
Push ${Var5}
Push ${Var6}
Push ${Var7}
Push ${Var8}
 
 StrCpy ${Var3} -1
 StrLen ${Var5} ${Var0}
 StrLen ${Var6} ${Var1}
 StrLen ${Var7} ${Var2}
 Loop:
  IntOp ${Var3} ${Var3} + 1
  StrCpy ${Var4} ${Var0} ${Var7} ${Var3}
  StrCmp ${Var3} ${Var5} End
  StrCmp ${Var4} ${Var2} 0 Loop
 
   StrCpy ${Var4} ${Var0} ${Var3}
   IntOp ${Var8} ${Var3} + ${Var7}
   StrCpy ${Var8} ${Var0} "" ${Var8}
   StrCpy ${Var0} ${Var4}${Var1}${Var8}
   IntOp ${Var3} ${Var3} + ${Var6}
   IntOp ${Var3} ${Var3} - 1
   IntOp ${Var5} ${Var5} - ${Var7}
   IntOp ${Var5} ${Var5} + ${Var6}
 
 Goto Loop
 End:
 
Pop ${Var8}
Pop ${Var7}
Pop ${Var6}
Pop ${Var5}
Pop ${Var4}
Pop ${Var3}
Pop ${Var2}
Pop ${Var1}
Exch ${Var0} #out
FunctionEnd

Function GetAfterChar
  Exch $0 ; chop char
  Exch
  Exch $1 ; input string
  Push $2
  Push $3
  StrCpy $2 0
  loop:
    IntOp $2 $2 - 1
    StrCpy $3 $1 1 $2
    StrCmp $3 "" 0 +3
      StrCpy $0 ""
      Goto exit2
    StrCmp $3 $0 exit1
    Goto loop
  exit1:
    IntOp $2 $2 + 1
    StrCpy $0 $1 "" $2
  exit2:
    Pop $3
    Pop $2
    Pop $1
    Exch $0 ; output
FunctionEnd