!include "FileFunc.nsh" !insertmacro GetParameters !insertmacro GetOptions Var ProgressLogFile Var IsInAppUpdate Var RestartAfterInstall !macro initProgressLog StrCpy $ProgressLogFile "" StrCpy $IsInAppUpdate "0" StrCpy $RestartAfterInstall "0" ${GetParameters} $R9 ClearErrors ${GetOptions} $R9 "/UPDATE" $R8 ${IfNot} ${Errors} StrCpy $IsInAppUpdate "1" ${EndIf} ClearErrors ${GetOptions} $R9 "/RESTARTFC" $R8 ${IfNot} ${Errors} StrCpy $RestartAfterInstall "1" ${EndIf} ; Prefer env var so paths with spaces are reliable; /LOG= remains supported. ReadEnvStr $ProgressLogFile "FARMCONTROL_INSTALL_LOG" ${If} $ProgressLogFile == "" ClearErrors ${GetOptions} $R9 "/LOG=" $ProgressLogFile ${If} ${Errors} StrCpy $ProgressLogFile "" ${EndIf} ${EndIf} ${If} $ProgressLogFile != "" ; Strip surrounding quotes from /LOG="C:\path with spaces\log.log" StrCpy $R8 $ProgressLogFile 1 ${If} $R8 == '"' StrCpy $ProgressLogFile $ProgressLogFile "" 1 StrLen $R8 $ProgressLogFile IntOp $R8 $R8 - 1 ${If} $R8 > 0 StrCpy $ProgressLogFile $ProgressLogFile $R8 ${EndIf} ${EndIf} ; Truncate any previous log so progress starts clean. Push $0 FileOpen $0 "$ProgressLogFile" w ${If} $0 != "" FileClose $0 ${EndIf} Pop $0 ${EndIf} !macroend ; Open/append/close each line so the updater can read the log concurrently ; (avoids an exclusive lock for the whole install; LogEx SHARE_READ is an alternative). !macro progressLog line DetailPrint "${line}" ${If} $ProgressLogFile != "" Push $0 FileOpen $0 "$ProgressLogFile" a ${If} $0 != "" FileSeek $0 0 END FileWrite $0 "${line}$\r$\n" FileClose $0 ${EndIf} Pop $0 ${EndIf} !macroend !macro progressPercent percent !insertmacro progressLog "installer:%${percent}" !macroend !macro progressPhase phase !insertmacro progressLog "installer:PHASE:${phase}" !macroend !macro progressStatus status !insertmacro progressLog "installer:STATUS:${status}" !macroend !macro progressSuccess !insertmacro progressPercent 100 !insertmacro progressStatus "Installation complete. Restarting Farm Control..." !insertmacro progressLog "installer: The install was successful." !macroend !macro progressFailure message !insertmacro progressLog "installer:STATUS:${message}" !insertmacro progressLog "installer: The install failed." !macroend !macro quitFarmControl !insertmacro progressPhase "Stopping Farm Control" !insertmacro progressStatus "Stopping running Farm Control processes..." DetailPrint "Stopping running Farm Control processes..." ExecWait 'taskkill /F /IM launcher.exe /T' $R0 !macroend ; Windows allows renaming open executables; move locked binaries aside so File can replace them. !macro moveAsideIfPresent filePath ${If} ${FileExists} "${filePath}" Delete "${filePath}.old" ClearErrors Rename "${filePath}" "${filePath}.old" ClearErrors ${EndIf} !macroend ; Prefer renaming the whole bin dir (works even with open files inside). !macro prepareInPlaceUpdate !insertmacro progressPhase "Preparing update" !insertmacro progressStatus "Preparing files for update..." ${If} ${FileExists} "$INSTDIR\bin.old" RMDir /r "$INSTDIR\bin.old" ${EndIf} ${If} ${FileExists} "$INSTDIR\bin" ClearErrors Rename "$INSTDIR\bin" "$INSTDIR\bin.old" ${If} ${Errors} !insertmacro moveAsideIfPresent "$INSTDIR\bin\launcher.exe" !insertmacro moveAsideIfPresent "$INSTDIR\bin\bun.exe" !insertmacro moveAsideIfPresent "$INSTDIR\bin\bspatch.exe" !insertmacro moveAsideIfPresent "$INSTDIR\bin\zig-zstd.exe" ${EndIf} ${EndIf} !macroend !macro uninstallPreviousFarmControl ClearErrors ReadRegStr $R0 HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Farm Control" "UninstallString" StrCmp $R0 "" check_install_dir run_uninstall check_install_dir: ReadRegStr $R0 HKCU "Software\Tom Butcher\Farm Control" "InstallDir" StrCmp $R0 "" check_legacy_uninstall StrCpy $R0 "$R0\Uninstall.exe" Goto run_uninstall check_legacy_uninstall: ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Farm Control" "UninstallString" StrCmp $R0 "" check_legacy_install_dir run_uninstall check_legacy_install_dir: ReadRegStr $R0 HKLM "Software\Tom Butcher\Farm Control" "InstallDir" StrCmp $R0 "" check_default_dir StrCpy $R0 "$R0\Uninstall.exe" Goto run_uninstall check_default_dir: StrCpy $R0 "$LOCALAPPDATA\Programs\Farm Control\Uninstall.exe" IfFileExists $R0 run_uninstall check_legacy_default_dir check_legacy_default_dir: StrCpy $R0 "$PROGRAMFILES64\Farm Control\Uninstall.exe" IfFileExists $R0 run_uninstall done_uninstall run_uninstall: IfFileExists $R0 0 done_uninstall !insertmacro progressPhase "Removing previous version" !insertmacro progressStatus "Removing previous Farm Control installation..." DetailPrint "Removing previous Farm Control installation..." !insertmacro quitFarmControl ExecWait '"$R0" /S' $R1 DetailPrint "Previous installation removed (exit code: $R1)" !insertmacro progressLog "installer:STATUS:Previous installation removed" done_uninstall: !macroend !macro createDesktopShortcut SetShellVarContext current SetOutPath "$INSTDIR\bin" CreateShortCut "$DESKTOP\Farm Control.lnk" "$INSTDIR\bin\launcher.exe" "" "$INSTDIR\bin\launcher.exe" 0 SW_SHOWNORMAL "" "Farm Control" !macroend !macro createStartMenuShortcut SetShellVarContext current CreateDirectory "$SMPROGRAMS\Farm Control" SetOutPath "$INSTDIR\bin" CreateShortCut "$SMPROGRAMS\Farm Control\Farm Control.lnk" "$INSTDIR\bin\launcher.exe" "" "$INSTDIR\bin\launcher.exe" 0 SW_SHOWNORMAL "" "Farm Control" !macroend !macro removeDesktopShortcut SetShellVarContext current Delete "$DESKTOP\Farm Control.lnk" !macroend !macro removeStartMenuShortcut SetShellVarContext current Delete "$SMPROGRAMS\Farm Control\Farm Control.lnk" RMDir "$SMPROGRAMS\Farm Control" !macroend !macro cleanupUpdateBackups ; Best-effort; may fail while the previous process still holds handles. RMDir /r "$INSTDIR\bin.old" Delete "$INSTDIR\bin\launcher.exe.old" Delete "$INSTDIR\bin\bun.exe.old" Delete "$INSTDIR\bin\bspatch.exe.old" Delete "$INSTDIR\bin\zig-zstd.exe.old" !macroend !macro customInstall !insertmacro progressPhase "Configuring Farm Control" !insertmacro progressStatus "Registering farmcontrol URI handler..." DetailPrint "Register farmcontrol URI Handler" DeleteRegKey HKCU "Software\Classes\farmcontrol" WriteRegStr HKCU "Software\Classes\farmcontrol" "" "URL:farmcontrol" WriteRegStr HKCU "Software\Classes\farmcontrol" "URL Protocol" "" WriteRegStr HKCU "Software\Classes\farmcontrol\DefaultIcon" "" "$INSTDIR\bin\launcher.exe" WriteRegStr HKCU "Software\Classes\farmcontrol\shell" "" "" WriteRegStr HKCU "Software\Classes\farmcontrol\shell\Open" "" "" WriteRegStr HKCU "Software\Classes\farmcontrol\shell\Open\command" "" '"$INSTDIR\bin\bun.exe" "$INSTDIR\bin\deeplink.js" "%1"' !insertmacro progressStatus "Creating shortcuts..." DetailPrint "Creating shortcuts" !insertmacro createDesktopShortcut !insertmacro createStartMenuShortcut ; Don't remove bin.old during an in-app update — the running process still uses it. ${If} $IsInAppUpdate != "1" !insertmacro cleanupUpdateBackups ${EndIf} !macroend !macro customUnInstall !insertmacro removeDesktopShortcut !insertmacro removeStartMenuShortcut DeleteRegKey HKCU "Software\Classes\farmcontrol" !macroend ; Wait for the running app to exit, then relaunch. Only used when /RESTARTFC is passed. Function restartFarmControlAfterUpdate ${If} $RestartAfterInstall != "1" Return ${EndIf} !insertmacro progressStatus "Waiting for Farm Control to close..." Sleep 1000 restart_wait_loop: ExecWait 'cmd.exe /c tasklist /FI "IMAGENAME eq launcher.exe" 2>nul | find /I "launcher.exe"' $R0 ${If} $R0 == 0 Sleep 1000 Goto restart_wait_loop ${EndIf} !insertmacro progressStatus "Starting Farm Control..." SetOutPath "$INSTDIR\bin" Exec "$INSTDIR\bin\launcher.exe" FunctionEnd