Files
2026-06-17 14:00:51 +02:00

1360 lines
46 KiB
NSIS
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
; -------------------------------------------------------------------------
; - JMRI Launcher
; -------------------------------------------------------------------------
; - This is used to launch a JMRI application on Microsoft Windows.
; - It performs the following:
; - find the Java installation
; - determines appropriate native libraries based on JRE architecture
; - build a dynamic ClassPath
; - find the installed memory
; - launch Java with the specified class
; -------------------------------------------------------------------------
; -------------------------------------------------------------------------
; - Compilation instructions
; -------------------------------------------------------------------------
; - To build the resulting .exe file use one of:
; - makensis.exe (command-line)
; - makensisw.exe (Windows GUI)
; - These are part of the NSIS installation suite available from:
; - http://nsis.sourceforge.net/
; - This MUST be built using the 'Large strings' build available from:
; - http://nsis.sourceforge.net/Special_Builds
; -------------------------------------------------------------------------
; -------------------------------------------------------------------------
; - Version History
; -------------------------------------------------------------------------
; - Version 0.1.31.0
; - Make /noisy do what its supposed to do and display the console
; - NB version 0.1.30.0 seems to have been skipped
; -------------------------------------------------------------------------
; - Version 0.1.28.1
; - Do not set the jinput.plugins property
; -------------------------------------------------------------------------
; - Version 0.1.29.0
; - Prepend the "Settings/lib" jars before the appended "Cp:a=..." entry, if used,
; where "Settings" is generally the JMRI directory immediately below the user's
; "home" directory.
; -------------------------------------------------------------------------
; - Version 0.1.28.1
; - Do not set the jmri.log.path System property.
; -------------------------------------------------------------------------
; - Version 0.1.28.0
; - Check default version of installed java before looking anywhere else
; - If environment variable JMRI_JAVA_HOME is present use that
; -------------------------------------------------------------------------
; - Version 0.1.27.0
; - Add support for 200 return code to shutdown the host machine
; - Add support for 210 return code to reboot the host machine
; -------------------------------------------------------------------------
; - Version 0.1.26.0
; - Add support for Open JDK 8 Registry Keys
; -------------------------------------------------------------------------
; - Version 0.1.25.1
; - Fix bug introduced by disabling alternate launcher with JDK 11
; -------------------------------------------------------------------------
; - Version 0.1.25.0
; - Add option to use standard launcher '/noalt'
; - Disable use of alternate launcher when JDK 11 in use.
; -------------------------------------------------------------------------
; - Version 0.1.24.0
; - Add support for Java 11 Registry Keys
; -------------------------------------------------------------------------
; - Version 0.1.23.0
; - Add JVM option 'Djogamp.gluegen.UseTempJarCache=false'
; -------------------------------------------------------------------------
; - Version 0.1.22.0
; - Support Java 9
; -------------------------------------------------------------------------
; - Version 0.1.21.0
; - Alter max and initial heap size calculations to be more in-line with
; - POSIX platforms
; - Allow options to be defaulted from %userprofile%\JMRI\jmri.conf
; - Set default L&F to WindowsLookAndFeel
; -------------------------------------------------------------------------
; - Version 0.1.20.0
; - Allow options to be specified either as '/' or '-'
; - Add option '/J' to pass JVM option
; -------------------------------------------------------------------------
; - Version 0.1.19.0
; - Bring heap size calculation into line with Linux and OS X launchers
; - Increase minimum memory requirement to 96 MB
; - Change maximum memory requirement to be between 192 and 768 and to use
; - maximum of half physical physical available RAM when above 192
; - Allow use of '/profile' parameter to specify specific profile
; -------------------------------------------------------------------------
; - Version 0.1.18.0
; - Check if application is already running with option to continue
; - or abort
; -------------------------------------------------------------------------
; - Version 0.1.17.0
; - Modification to pass JMRI process return code back to caller
; -------------------------------------------------------------------------
; - Version 0.1.16.0
; - Modification to pass flag for correct usage with UTF-8 encoded files
; -------------------------------------------------------------------------
; - Version 0.1.15.0
; - Add flag to allow 64-bit Windows to force the use of a 32-bit JRE
; -------------------------------------------------------------------------
; - Version 0.1.14.0
; - Modification to monitor launched Java process for a return code that
; - signals a re-launch of JMRI
; - Tidied up source code
; -------------------------------------------------------------------------
; - Version 0.1.13.0
; - Modification to enable the process name in Windows Task Manager to
; - match the JMRI application being launched
; - Improve command line option processing
; - Restore environment variable usage (see notes below for 0.1.9.0)
; - Ensure user.home is correctly set - see Sun Java bug:
; - http://bugs.sun.com/view_bug.do?bug_id=4787931
; -------------------------------------------------------------------------
; - Version 0.1.12.0
; - Change to use javaw.exe when not 'noisy' and remove window minimising
; - Re-introduced jinput.plugins option
; - Due to increasing length of classpath and option string, must be built
; - using large-string version of makensis (8192 byte length as opposed to
; - default 1024 byte length) otherwise launch failures will occur in many
; - configurations
; -------------------------------------------------------------------------
; - Version 0.1.11.0
; - Reverse out jinput.plugins option for now as it is causing problems
; - with some configurations
; -------------------------------------------------------------------------
; - Version 0.1.10.0
; - Reversed out environment variable modifications for now
; -------------------------------------------------------------------------
; - Version 0.1.9.0
; - Add jinput.plugins option to Java command line.
; - Add the possibility to modify launcher behaviour using environment
; - variables (as per Mac OS X and Linux platforms):
; - JMRI_HOME - determines the program location
; - JMRI_OPTIONS - specifies additional JVM options
; - JMRI_PREFSDIR - specifies an alternative preferences directory
; - JMRI_USERHOME - specifies an alternative user home directory
; - If both JMRI_PREFSDIR and JMRI_USERHOME are defined, JMRI_PREFSDIR will
; - take precedence for preference file location.
; -------------------------------------------------------------------------
; - Version 0.1.8.0
; - Correction of the sort-order for native libraries - is now architecture
; - specific first, followed by generic
; - Improved initial memory checks
; -------------------------------------------------------------------------
; - Version 0.1.7.0
; - Update to correctly identify JRE architecture on x64 systems and set
; - path to appropriate native libraries
; -------------------------------------------------------------------------
; - Version 0.1.6.0
; - correct bug that caused crash when launching with single quote in path
; -------------------------------------------------------------------------
; - Version 0.1.5.0
; - modified delay in window minimising routine to help with problems seen
; - on slower machines (change from 10ms to 60ms delay)
; - fixed problem in minimising loop (was infinite)
; - added window class to look for under Win98
; -------------------------------------------------------------------------
; - Version 0.1.4.0
; - modified free memory calculation to correctly work on x64 based systems
; -------------------------------------------------------------------------
; - Version 0.1.3.0
; - added path options for Jython and messages.log to locate these in the
; - user's profile folder, rather than the JMRI program directory when run
; - on Windows 2000 or later.
; -------------------------------------------------------------------------
; - Version 0.1.2.0
; - modified to minimise java console by default
; - added '/noisy' command-line option to display the java console
; -------------------------------------------------------------------------
; - Version 0.1.1.0
; - modified command-line parameter capture behaviour
; - added '/debug' command-line option
; - for Vista, ensured that launcher runs as a user level process
; -------------------------------------------------------------------------
; - Version 0.1.0.0
; - initial release
; -------------------------------------------------------------------------
; -------------------------------------------------------------------------
; - Basic information
; - These should be edited to suit the application
; -------------------------------------------------------------------------
!define AUTHOR "Matt Harris for JMRI" ; Author name
!define APP "LaunchJMRI" ; Application name
!define COPYRIGHT "(C) 1997-2026 JMRI Community" ; Copyright string
!define VER "0.1.31.0" ; Launcher version
!define PNAME "${APP}" ; Name of launcher
; -- Comment out next line to use {app}.ico
!define ICON "decpro5.ico" ; Launcher icon
!define INITHEAP 96 ; Initial heap size in Mbyte
!define MINMEM 192 ; Minimum memory in Mbyte
!define X86MAX 1024 ; Maximum heap size for x86 in Mbyte
!ifndef JRE_VER
; -- usually, this will be determined by the build.xml ant script
!define JRE_VER "11" ; Required JRE version
!endif
; -------------------------------------------------------------------------
; - End of basic information
; - Lines below here should not normally require editing
; -------------------------------------------------------------------------
; -------------------------------------------------------------------------
; - Variable declarations
; -------------------------------------------------------------------------
Var JAVAPATH ; holds the path to the location where JAVA files can be found
Var JAVAEXE ; holds the name of the JAVA exe to use
Var JEXEPATH ; holds the path to the temporary JAVA exe used
Var CLASSPATH ; holds the class path for JMRI .jars
Var P_CLASSPATH ; holds additional classpath to prepend to standard
Var CLASSPATH_A ; holds additional classpath to append to standard
Var CLASS ; holds the class to launch
Var APPNAME ; holds the application name
Var OPTIONS ; holds the JRE options
Var JVMOPTIONS ; holds the additonal JRE options passed via '/J'
Var JMRIOPTIONS ; holds the JMRI-specific options (read from JMRI_OPTIONS)
Var JMRIPREFS ; holds the path to user preferences (read from JMRI_PREFSDIR)
Var JMRIHOME ; holds the path to JMRI program files (read from JMRI_HOME)
Var JMRIUSERHOME ; holds the path to user files (read from JMRI_USERHOME)
Var JMRIPROFILE ; holds the file to use for profile
Var CALCMAXMEM ; holds the calculated maximum memory
Var PARAMETERS ; holds the commandline parameters (class and config file)
Var NOISY ; used to determine if console should be visible or not
Var x64 ; used to determine OS architecture
Var x64JRE ; used to determine JRE architecture
Var FORCE32BIT ; used to determine if 32-bit JRE should always be used
Var DEFOPTIONS ; used to hold any default options
Var ALTLAUNCH ; used to determine use of the alternate launcher
Var USEDEFJAVA ; use default java
; -------------------------------------------------------------------------
; - Various constants
; -------------------------------------------------------------------------
!define SW_NORMAL 1 ; from WinAPI for Normal window
!define SW_MINIMIZE 6 ; from WinAPI for Minimized window
!define ARCH_32BIT 0 ; represents 32-bit architecture
!define ARCH_64BIT 1 ; represents 64-bit architecture
!define FLAG_NO 0 ; represents a NO return value from subroutines
!define FLAG_YES 1 ; represents a YES return value from subroutines
; -------------------------------------------------------------------------
; - includes
; -------------------------------------------------------------------------
; include "MultiUser.nsh" ; MultiUser installation
!include "WordFunc.nsh" ; add header for word manipulation
!insertmacro VersionCompare ; add function to compare versions
;-- from https://nsis.sourceforge.io/GetInQuotes:_Get_string_from_between_quotes
Function GetInQuotes
Exch $R0
Push $R1
Push $R2
Push $R3
StrCpy $R2 -1
IntOp $R2 $R2 + 1
StrCpy $R3 $R0 1 $R2
StrCmp $R3 "" 0 +3
StrCpy $R0 ""
Goto Done
StrCmp $R3 '"' 0 -5
IntOp $R2 $R2 + 1
StrCpy $R0 $R0 "" $R2
StrCpy $R2 0
IntOp $R2 $R2 + 1
StrCpy $R3 $R0 1 $R2
StrCmp $R3 "" 0 +3
StrCpy $R0 ""
Goto Done
StrCmp $R3 '"' 0 -5
StrCpy $R0 $R0 $R2
Done:
Pop $R3
Pop $R2
Pop $R1
Exch $R0
FunctionEnd
; -------------------------------------------------------------------------
; - Compiler Flags (to reduce executable size, saves some bytes)
; -------------------------------------------------------------------------
SetDatablockOptimize on
SetCompress force
SetCompressor /SOLID /FINAL lzma
; -------------------------------------------------------------------------
; - Runtime Switches
; -------------------------------------------------------------------------
CRCCheck On ; do CRC check on launcher before start ("Off" for later EXE compression)
WindowIcon Off ; show no icon of the launcher
ShowInstDetails Show ; show the installation details
;SilentInstall Silent ; start as launcher, not as installer
;AutoCloseWindow True ; do not automatically close when finished
RequestExecutionLevel user ; set execution level on Windows Vista to user
; -------------------------------------------------------------------------
; - Set basic information
; -------------------------------------------------------------------------
Name "${APP}"
Caption "${APP} - ${VER}"
!ifdef ICON
Icon "${ICON}"
!else
Icon "${APP}.ico"
!endif
OutFile "${PNAME}.exe"
; -------------------------------------------------------------------------
; - Set version information
; -------------------------------------------------------------------------
LoadLanguageFile "${NSISDIR}\Contrib\Language files\English.nlf"
VIProductVersion "${VER}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "${APP}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "Comments" "Used to launch a JMRI application."
VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "${COPYRIGHT}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "by ${AUTHOR}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "${APP}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "${VER}"
VIAddVersionKey /LANG=${LANG_ENGLISH} "OriginalFilename" "${PNAME}.exe"
; -------------------------------------------------------------------------
; - Main section
; -------------------------------------------------------------------------
Section "Main"
DetailPrint "CommandLine: $CMDLINE"
DetailPrint "Default options: $DEFOPTIONS"
DetailPrint "AppName: $APPNAME"
DetailPrint "Class: $CLASS"
DetailPrint "Parameters: $PARAMETERS"
DetailPrint "Noisy: $NOISY"
DetailPrint "Force32bit: $FORCE32BIT"
DetailPrint "Profile: $JMRIPROFILE"
DetailPrint "Alternate launcher: $ALTLAUNCH"
; -- First determine if we're running on x64
DetailPrint "Testing for x64..."
System::Call kernel32::GetCurrentProcess()i.s
System::Call kernel32::IsWow64Process(is,*i.s)
Pop $x64
DetailPrint "Result: $x64"
; -- Find the JAVA install
; -- Initialise JRE architecture variable
StrCpy $x64JRE ${ARCH_32BIT}
; -- Determine which JAVA exe to use
StrCpy $R0 "java"
StrCmp $NOISY ${SW_NORMAL} IsNoisy
StrCpy $R0 "$R0w"
IsNoisy:
StrCpy $JAVAEXE "$R0.exe"
; -- If we're running x64, first check for 64-bit JRE
StrCmp ${ARCH_32BIT} $x64 JRESearch
; -- Now check if we should force 32-bit JRE usage on x64
StrCmp ${FLAG_YES} $FORCE32BIT JRESearch
; -- No need to force
DetailPrint "Setting x64 registry view..."
SetRegView 64
StrCpy $x64JRE ${ARCH_64BIT}
; --check directory for jre
JRESearch:
ClearErrors
strCpy $R0 "$EXEDIR\jre"
DetailPrint "Checking local path [$R0\bin\$JAVAEXE]"
IfFileExists "$R0\bin\$JAVAEXE" 0 JRESearchNoLocal
StrCpy $UseDefJava "0"
DetailPrint "Using Local $R0"
Goto DisableAltLauncher
JRESearchNoLocal:
DetailPrint "Checking default java as Local not found"
; -- Check default java
ClearErrors
nsExec::ExecToStack "java -fullversion"
IfErrors JRESearchC ; continue to use registry
Pop $0
Pop $0 ;-- response code
DetailPrint "program response [$0]"
push $0
call GetInQuotes
Pop $R1
IfErrors JRESearchC ; continue to use registry
DetailPrint "Default java is [$R1]"
${VersionCompare} "$R1" "${JRE_VER}" $0
StrCpy $UseDefJava "1"
IntCmp $0 2 JRESearchC 0 JRESearchC
DetailPrint "Useing default java"
goto DisableAltLauncher
JRESearchC:
; -- loop over javasoft keys looking for our version or greater
StrCpy $0 0
loopKeys:
EnumRegKey $1 HKLM "SOFTWARE\JavaSoft" $0
StrCmp $1 "" done ;-- end of keys
StrCpy $2 0
loopSubKeys:
EnumRegKey $3 HKLM "SOFTWARE\JavaSoft\$1" $2
DetailPrint "Checked [SOFTWARE\JavaSoft\$1] item [$2] got [$3]"
StrCmp $3 "" doneSubKey ;--end of subkeys
ReadRegStr $6 HKLM "SOFTWARE\JavaSoft\$1\$3" "JavaHome"
DetailPrint "JavaHome [$6]"
IfErrors nextSubKey
${VersionCompare} "$3" "${JRE_VER}" $5
StrCpy $UseDefJava "0"
DetailPrint "Found key for java [$3]"
IntCmp $5 2 nextSubKey 0 nextSubKey
;--have good one
StrCpy $R0 $6 ;-- stuff home
StrCpy $R1 $3 ;-- stuff version
goto DisableAltLauncher
nextSubKey:
IntOp $2 $2 + 1
DetailPrint "$1 $3 Checked"
goto loopSubKeys
doneSubKey:
IntOp $0 $0 + 1 ;-- move to next top level key
DetailPrint "$1 checked - movin on"
goto loopKeys
done:
;-- still no good java
SetErrors
Goto FoundJavaInstallPoint ;-- counter intuitive I know, but going with errors
DisableAltLauncher: ;-- always use this an stop the ssd wear.
DetailPrint "Switching off alternate launcher..."
StrCpy $ALTLAUNCH ${FLAG_NO}
FoundJavaInstallPoint:
StrCpy $R0 "$R0\bin\$JAVAEXE"
StrCmp $UseDefJava "0" +2
StrCpy $R0 "$JAVAEXE" ;-- use just the exec name if using default
IfErrors 0 JreFound
; -- If we've got an error here on x64, switch to the 32-bit registry
; -- and retry (not needed if we've forced 32-bit above)
StrCmp ${ARCH_32BIT} $x64JRE JRENotFound
SetRegView 32
DetailPrint "Setting x86 registry view..."
StrCpy $x64JRE ${ARCH_32BIT}
Goto JRESearch
JreNotFound:
DetailPrint "Java not found!"
MessageBox MB_OK|MB_ICONSTOP "Java not found in jre, default or registry$\n$\rTry installing or using mklink jre {path to java version ${JRE_VER}"
Goto Exit
JreFound:
StrCpy $JAVAPATH $R0
DetailPrint "JavaPath: $JAVAPATH"
; -- Now we've determined Java is basically OK, copy the file to a
; -- temporary location and rename it if configured to do so
StrCmp ${FLAG_NO} $ALTLAUNCH UseStandardLauncher
; -- First try to remove any old temporary launchers
RMDir /r $TEMP\LaunchJMRI
; -- Now create temporary directory and copy JAVA launcher across
CreateDirectory `$TEMP\LaunchJMRI`
StrCpy $JEXEPATH `$TEMP\LaunchJMRI\$APPNAME.exe`
DetailPrint `CopyFiles: $JAVAPATH $JEXEPATH`
System::Call "kernel32::CopyFile(t `$JAVAPATH`, t `$JEXEPATH`, b `0`) ?e"
Pop $0
DetailPrint "Result: $0"
; -- Check that the temporary launcher file exists
ClearErrors
FindFirst $0 $1 $JEXEPATH
; -- Wasn't found so use regular launcher
IfErrors UseStandardLauncher ExeRenameDone
UseStandardLauncher:
StrCpy $JEXEPATH $JAVAPATH
ExeRenameDone:
DetailPrint "JExePath: $JEXEPATH"
; -- Get the memory status
StrCmp ${ARCH_32BIT} $x64 Notx64
Call GetSystemMemoryStatus64
Goto CalcMem
Notx64:
Call GetSystemMemoryStatus
CalcMem:
System::Int64Op $4 / 1048576
Pop $4
DetailPrint "PhysicalMemory: $4m"
; -- Default Java heap size is 1/4 total memory size
; -- Now it would be good to read this info from the JVM, but
; -- it's not as simple to do that compared to the methods available
; -- on POSIX systems so, for the time being, we will just assume
; -- that the default calculation is performed by the JVM
; -- Our required memory calculations will be as follows:
; -- - 1/4 total memory size on systems with more than 4GB RAM
; -- - 1/2 total memory size on systems with 1-4GB RAM
; -- - 3/4 total memory size on systems with less than 1GB RAM
; -- - with an absolute minimum of 192MB (MINMEM)
; -- If running on an x86 JVM, we peg the maximum to ${X86MAX}
; -- as, it seems, the x86 JVM cannot always allocate a larger
; -- amount of RAM even if there is sufficient on the machine
; -- Check that physical memory is >= MINMEM
IntCmp $4 ${MINMEM} cmp_mem_gt_4 cmp_min_lt cmp_mem_gt_4
cmp_min_lt:
; -- If insufficient physical memory, stop with an error
MessageBox MB_OK|MB_ICONSTOP "Not enough available memory to start. JMRI requires at least ${MINMEM} MBytes memory."
Goto Exit
cmp_mem_gt_4:
; -- Check if physical memory is greater than 4GB
DetailPrint "Check if more than 4GB memory"
IntCmp $4 4096 cmp_mem_bt_1_and_4 cmp_mem_bt_1_and_4
; -- More than 4GB so use 1/4 memory size
IntOp $CALCMAXMEM $4 / 4
DetailPrint "More than 4GB"
Goto cmp_done
cmp_mem_bt_1_and_4:
; -- Check if physical memory is greater than 1GB
DetailPrint "Less than 4GB"
DetailPrint "Check if more than 1GB memory"
IntCmp $4 1024 cmp_mem_lt_1 cmp_mem_lt_1
; -- More than 1GB so use 1/2 memory size
IntOp $CALCMAXMEM $4 / 2
DetailPrint "More than 1GB"
Goto cmp_done
cmp_mem_lt_1:
DetailPrint "Less than 1GB"
; -- Less than 1GB so use 3/4 memory size
IntOp $CALCMAXMEM $4 * 3
IntOp $CALCMAXMEM $CALCMAXMEM / 4
cmp_done:
DetailPrint "InitHeap: ${INITHEAP}m"
DetailPrint "MinMemory: ${MINMEM}m"
DetailPrint "MaxMemory: $CALCMAXMEMm"
; -- Check if we're on a 32-bit JVM and adjust max heap down if necessary
DetailPrint "Checking maximum heap size..."
StrCmp $x64JRE ${ARCH_64BIT} check_heap_done
; -- we're on a 32-bit JRE, so check the calculated heap size
DetailPrint "Running x86 JVM"
IntCmp $CALCMAXMEM ${X86MAX} check_heap_done check_heap_done
StrCpy $CALCMAXMEM ${X86MAX}
DetailPrint "Adjusted MaxMemory: $CALCMAXMEMm"
check_heap_done:
DetailPrint "...finished"
; -- Build options string
; -- JVM and RMI options
; -- Read environment variable
; -- JMRI_OPTIONS - additional JMRI options
; -- If not defined, it returns an empty value
ReadEnvStr $JMRIOPTIONS "JMRI_OPTIONS"
; -- Add profile (if specified)
StrCmp $JMRIPROFILE "" contOptions
StrCpy $JMRIOPTIONS '$JMRIOPTIONS -Dorg.jmri.profile="$JMRIPROFILE"'
contOptions:
StrCpy $OPTIONS "$JMRIOPTIONS $JVMOPTIONS -noverify"
StrCpy $OPTIONS "$OPTIONS -Dsun.java2d.d3d=false"
StrCpy $OPTIONS "$OPTIONS -Djava.security.policy=security.policy"
StrCpy $OPTIONS "$OPTIONS -Djogamp.gluegen.UseTempJarCache=false"
StrCpy $OPTIONS "$OPTIONS -Dswing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsLookAndFeel"
; following line is for DarkLAF
StrCpy $OPTIONS "$OPTIONS --add-exports=java.desktop/sun.awt=ALL-UNNAMED"
StrCmp ${ARCH_64BIT} $x64JRE x64Libs x86Libs
x86Libs:
; -- 32-bit libraries
StrCpy $OPTIONS "$OPTIONS -Djava.library.path=.;lib\x86;lib"
Goto LibsDone
x64Libs:
; -- 64-bit libraries
StrCpy $OPTIONS "$OPTIONS -Djava.library.path=.;lib\x64;lib"
LibsDone:
StrCpy $OPTIONS "$OPTIONS -Djava.rmi.server.codebase=file:java/classes/"
; -- ddraw is disabled to get around Swing performance problems in Java 1.5.0
StrCpy $OPTIONS "$OPTIONS -Dsun.java2d.noddraw"
; -- memory start and max limits
StrCpy $OPTIONS "$OPTIONS -Xms${INITHEAP}m"
StrCpy $OPTIONS "$OPTIONS -Xmx$CALCMAXMEMm"
; -- default file coding
StrCpy $OPTIONS "$OPTIONS -Dfile.encoding=UTF-8"
; -- Read environment variable
; -- JMRI_USERHOME - user files location
ClearErrors
ReadEnvStr $JMRIUSERHOME "JMRI_USERHOME"
; -- If defined, set user.home property
IfErrors CheckUserHome
DetailPrint "Set user.home to JMRI_USERHOME: $JMRIUSERHOME"
StrCpy $OPTIONS `$OPTIONS -Duser.home="$JMRIUSERHOME"`
Goto ReadPrefsDir
CheckUserHome:
; -- If not defined, check user home is consistent
Call CheckUserHome
Pop $0
StrCmp $0 ${FLAG_YES} ReadPrefsDir
; -- Not consistent - set to Profile
DetailPrint "Set user.home to %USERPROFILE%: $PROFILE"
StrCpy $OPTIONS `$OPTIONS -Duser.home="$PROFILE"`
ReadPrefsDir:
; -- Read environment variable
; -- JMRI_PREFSDIR - user preferences location
ClearErrors
ReadEnvStr $JMRIPREFS "JMRI_PREFSDIR"
; -- If defined, set jmri.prefsdir property
IfErrors 0 SetPrefsDir
StrCmp $PROFILE "" Prefs98
StrCpy $JMRIPREFS "$PROFILE\JMRI"
Goto PathOptions
Prefs98:
StrCpy $JMRIPREFS "$WINDIR\JMRI"
Goto PathOptions
SetPrefsDir:
StrCpy $OPTIONS `$OPTIONS -Djmri.prefsdir="$JMRIPREFS"`
PathOptions:
; -- set path for Jython
; -- Creates the necessary directory if not existing
; -- User Profile is only valid for Win2K and later
; -- so skip on earlier versions
StrCmp $PROFILE "" OptionsDone
IfFileExists "$JMRIPREFS\systemfiles\*.*" SetPaths
CreateDirectory "$JMRIPREFS\systemfiles"
SetPaths:
StrCpy $OPTIONS '$OPTIONS -Dpython.home="$JMRIPREFS\systemfiles"'
OptionsDone:
DetailPrint "Options: $OPTIONS"
; -- Read environment variable
; -- JMRI_HOME - location of JMRI program files
ClearErrors
ReadEnvStr $JMRIHOME "JMRI_HOME"
IfErrors 0 EnvJmriHomeDone
; -- If not defined, use the launcher location
StrCpy $JMRIHOME $EXEDIR
EnvJmriHomeDone:
; -- Build the ClassPath
;
; The CLASSPATH shall be made up as, first to last:
; a) Any path elements specified with the -cp:p prepend argument
; b) JMRIs code itself via the jmri.jar file
; c) contents of the program lib/ directory
; d) contents of the settings:lib/ directory
; e) Any path elements specified with the --cp:a append argument
StrCpy $CLASSPATH ".;classes"
StrCpy $0 "$JMRIHOME" ; normally 'C:\Program Files\JMRI'
StrCpy $3 "jmri.jar" ; set to jmri.jar to skip jmri.jar
StrCpy $4 "" ; no prefix required
Call GetClassPath
StrCmp $9 "" +2 0
StrCpy $CLASSPATH "$CLASSPATH;$9"
StrCpy $CLASSPATH "$CLASSPATH;jmri.jar"
StrCpy $3 "" ; set to blank to include all .jar files
StrCpy $4 "lib\" ; lib prefix
StrCpy $0 "$JMRIHOME\lib" ; normally 'C:\Program Files\JMRI\lib'
Call GetClassPath
StrCmp $9 "" +2 0
StrCpy $CLASSPATH "$CLASSPATH;$9"
DetailPrint "ClassPath: $CLASSPATH"
; -- Now prepend and/or append when required
DetailPrint "Check for any prepended/appended classpath entries"
StrCmp $P_CLASSPATH "" ClassPathAppend
StrCpy $CLASSPATH "$P_CLASSPATH;$CLASSPATH"
DetailPrint "Prepended $P_CLASSPATH"
ClassPathAppend:
; add something here to "append" any/all "settings:"\lib\.jar filenames,
; separated by a ";".
StrCpy $0 "$PROFILE\JMRI\lib"
DetailPrint "Check $0 for any appended classpath entries"
Call GetSettingsClassPath
DetailPrint "Checked GetSettingsClassPath from $0 and found $9"
StrCmp $9 "" ContinueClasspathAppend
StrCpy $CLASSPATH "$CLASSPATH;$9"
DetailPrint " Adding $9 to Classpath."
ContinueClasspathAppend:
StrCmp $CLASSPATH_A "" ContinueClassAppend
StrCpy $CLASSPATH "$CLASSPATH;$CLASSPATH_A"
DetailPrint "Appended $CLASSPATH_A"
ContinueClassAppend:
DetailPrint "Final ClassPath: $CLASSPATH"
DetailPrint "MaxLen: ${NSIS_MAX_STRLEN}"
DetailPrint `ExeString: "$JEXEPATH" $OPTIONS -Djava.class.path="$CLASSPATH" $CLASS $PARAMETERS`
; -- Create a preferences:\lib directory for this user
IfFileExists "$PROFILE\JMRI\lib\*.*" +2
CreateDirectory "$PROFILE\JMRI\lib"
; -- Create the directory and README.txt file
IfFileExists "$PROFILE\JMRI\lib\*.*" +2
CreateDirectory "$PROFILE\JMRI\lib"
IfFileExists "$PROFILE\JMRI\lib\README.txt" ReadMeIsCreated
; Create a "$JMRIPREFS\lib\README.txt" file
Push $0
FileOpen $0 "$JMRIPREFS\lib\README.txt" w
FileWrite $0 '(Since JMRI version 5.7.1.)$\r$\n'
FileWrite $0 '$\r$\n'
FileWrite $0 'This directory can be used to hold "Plugin" files for JMRI.$\r$\n'
FileWrite $0 '$\r$\n'
FileWrite $0 'Third-party developers, and, on occasion, JMRI developers, may wish to provide$\r$\n'
FileWrite $0 'a "Plugin" to enhance JMRI functionality. One way to install such a plugin into$\r$\n'
FileWrite $0 'JMRI is to:$\r$\n'
FileWrite $0 ' 1. Quit all JMRI programs.$\r$\n'
FileWrite $0 ' 2. Acquire the plugin.$\r$\n'
FileWrite $0 ' 3. Copy the acquired plugin to this directory.$\r$\n'
FileWrite $0 ' 4. Re-start the JMRI programs that you use.$\r$\n'
FileWrite $0 'If it has been installed correctly, JMRI will now use the plugin. Any plugins $\r$\n'
FileWrite $0 'installed here will be available to all JMRI instances >for this user<, except $\r$\n'
FileWrite $0 'when JMRI has a similarly-implemented function.$\r$\n'
FileWrite $0 '$\r$\n'
FileWrite $0 'Any of these .jar files shall be added as the last .jar files in the CLASSPATH.$\r$\n'
FileWrite $0 '$\r$\n'
FileWrite $0 'Note: Plugin files installed in this directory are only available on a "user-"$\r$\n'
FileWrite $0 'specific basis. If a plugin is to be available to multiple Windows users using$\r$\n'
FileWrite $0 'this type of install, each Windows "user" who needs access to the plugin will$\r$\n'
FileWrite $0 'need to install the plugin in their own user-specific JMRI directory$\'s "lib"$\r$\n'
FileWrite $0 'directory.$\r$\n'
FileWrite $0 '$\r$\n'
FileClose $0
Pop $0
ReadMeIsCreated:
; -- Finally get ready to run the application
SetOutPath $JMRIHOME
; -- Launch the Java class.
LaunchJMRI:
DetailPrint "Launching JMRI"
; -- use $7 to hold return value
StrCmp $NOISY ${SW_NORMAL} IsNoisyExec
ExecWait `"$JEXEPATH" $OPTIONS -Djava.class.path="$CLASSPATH" $CLASS $PARAMETERS` $7
Goto IsNotNoisy
IsNoisyExec:
; -- use $7 to hold return value
nsExec::ExecToLog `"$JEXEPATH" $OPTIONS -Djava.class.path="$CLASSPATH" $CLASS $PARAMETERS`
Pop $0
Pop $7 ;-- response code
IsNotNoisy:
; -- We're no longer active
DetailPrint "Return code from process: $7"
; -- Check the return code is 100 - if so, re-launch
StrCmp $7 100 LaunchJMRI
; -- Check the return code is 200 - if so, shutdown
StrCmp $7 200 Shutdown
; -- Check the return code is 210 - if so, reboot
StrCmp $7 210 Reboot PreExit
Shutdown:
Exec "shutdown /s"
Goto PreExit
Reboot:
Exec "shutdown /r"
PreExit:
; -- Set ErrorLevel to return code
SetErrorLevel $7
Exit:
DetailPrint "To copy this text to the clipboard, right click then choose"
DetailPrint " 'Copy Details To Clipboard'"
SectionEnd
Function .onInit
; -- Setup the default environment
; This can be overriden by us of the "/debug" or "[/noisy]" parameter.
SetSilent silent
StrCpy $NOISY ${SW_MINIMIZE}
StrCpy $FORCE32BIT ${FLAG_NO}
StrCpy $ALTLAUNCH ${FLAG_YES}
; -- Read any default_options
Call ReadConfFile
Pop $DEFOPTIONS
; -- Check if we've got some
StrCmp $DEFOPTIONS "" cmdlineProcess
; -- If so, process them
StrCpy $0 $DEFOPTIONS
Call ProcessParameters
; -- Now process commandline
cmdlineProcess:
; -- Get commandline parameters
StrCpy $0 $CMDLINE
; -- Start reading commandline parameters
cmdLoop:
Push $0
Call GetParameters
Pop $0
StrCmp $0 "" 0 cmdlineOk
MessageBox MB_OK|MB_ICONSTOP "No command line parameter. Usage 'LaunchJMRI.exe [/debug] [/noisy] [/32bit] [/profile <profileID>] [/JOPTION] [--cp:a=CLASSPATH] [--cp:p=CLASSPATH] class [config]'"
Abort
cmdlineOk:
Call ProcessParameters
IfErrors 0 cmdLoop
; -- Read the class name
Push $0
Call GetWord
Pop $CLASS
; -- Determine the application name (last part of class name)
StrLen $1 $CLASS
appNameLoop:
IntOp $1 $1 - 1
StrCpy $3 $CLASS 1 $1
StrCmp $3 "." appNameGot
StrCmp $1 "0" appNameDone appNameLoop
appNameGot:
IntOp $1 $1 + 1
appNameDone:
StrCpy $APPNAME $CLASS "" $1
; -- Now check if we've already got an instance of this application running
System::Call 'kernel32::CreateMutex(i 0, i 0, t "JMRI.$CLASS") ?e'
Pop $R0
StrCmp $R0 0 okToLaunch
MessageBox MB_YESNO|MB_ICONSTOP|MB_DEFBUTTON2 "JMRI $APPNAME is already running. Do you want to continue?" IDYES okToLaunch
Abort
okToLaunch:
; -- Copy any remaining commandline parameters to $PARAMETERS
Push $0
Call GetParameters
Pop $PARAMETERS
FunctionEnd
; -------------------------------------------------------------------------
; - JMRI Launcher Functions
; -------------------------------------------------------------------------
Function ProcessParameters
; -------------------------------------------------------------------------
; - Processes parameters
; - input: parameter string in $0
; - output: none
; - modifies $0, $1, $2
; -------------------------------------------------------------------------
; -- Check if the first parameter is an option
Push $0
Call GetWord
Pop $1
StrCpy $2 $1 1
StrCmp $2 "/" optsGet
StrCmp $2 "-" optsGet optsDone
optsGet:
; -- Process the possible commandline options
; -- Strip first character
StrCpy $2 $1 "" 1
StrCmp $2 "debug" optsDebug
StrCmp $2 "noisy" optsNoisy
StrCmp $2 "32bit" opts32bit
StrCmp $2 "profile" optsProfile
StrCmp $2 "noalt" optsNoAlt
; -- Now check if we've got a '/J | -J' option
StrCpy $2 $2 1
StrCmp $2 "J" optsJVMOpts
; -- Now check if we've got a '--cp:a= | --cp:p=' option
; -- Start from complete option in $1
StrCpy $2 $1 7
StrCmp $2 "--cp:a=" optsCPA
StrCmp $2 "--cp:p=" optsPCP
; -- If we've got here, the commandline option is not known so give an error.
MessageBox MB_OK|MB_ICONSTOP "Command line option '$1' not known."
Abort
; -- Processing block for each option
optsDebug:
SetSilent normal
Return
optsNoisy:
StrCpy $NOISY ${SW_NORMAL}
Return
opts32bit:
StrCpy $FORCE32BIT ${FLAG_YES}
Return
optsProfile:
Push $0
Call GetParameters
Pop $0
Push $0
Call GetWord
Pop $JMRIPROFILE
StrCpy $2 $JMRIPROFILE 1
StrCmp $2 '"' 0 optsProfile_Done
StrCpy $JMRIPROFILE $JMRIPROFILE "" 1
optsProfile_Done:
Return
optsJVMOpts:
; -- Format is '-J-Dsun.java2d.hdiaware=true'
; -- to pass '-Dsun.java2d.hdiaware=true'
; -- $1 already contains complete option with '-J' prefix
StrCpy $2 $1 "" 2 ; strip first 2 chars
StrCmp $JVMOPTIONS "" optsJVMcont
; -- add space if more than one option
StrCpy $JVMOPTIONS `$JVMOPTIONS `
optsJVMcont:
StrCpy $JVMOPTIONS `$JVMOPTIONS$2`
Return
optsCPA:
; -- Format is '--cp:a=CLASSPATH'
; -- to append 'CLASSPATH' to classpath
; -- $1 already contains complete option with '--cp:a=' prefix
StrCpy $CLASSPATH_A $1 "" 7 ; strip first 7 chars
DetailPrint "Classpath_a is $CLASSPATH_A"
Return
optsPCP:
; -- Format is '--cp:p=CLASSPATH'
; -- to prepend 'CLASSPATH' to classpath
; -- $1 already contains complete option with '--cp:p=' prefix
StrCpy $P_CLASSPATH $1 "" 7 ; strip first 7 chars
Return
optsNoAlt:
StrCpy $ALTLAUNCH ${FLAG_NO}
Return
optsDone:
; -- No more parameters to process
; -- so set error flag to signify
SetErrors
FunctionEnd
Function ReadConfFile
; -------------------------------------------------------------------------
; - Gets default_options from '%userprofile%\JMRI\jmri.conf'
; - input: none
; - output: top of stack
; - modifies no other variables
; -------------------------------------------------------------------------
; -- Save variables to the stack
Push $0
Push $1
Push $2
ClearErrors
FileOpen $2 "$PROFILE\JMRI\jmri.conf" r
IfErrors ReadConfFile_Exit
ReadConfFile_ReadLine:
ClearErrors
FileRead $2 $1
IfErrors ReadConfFile_done
StrCpy $0 $1 1
; -- Skip any comments (line begins `#`)
StrCmp $0 "#" ReadConfFile_ReadLine
; -- Remove any trailing whitespace
ReadConfFile_WhiteSpaceLoop:
StrCpy $0 $1 1 -1
StrCmp $0 ` ` ReadConfFile_TrimRight
StrCmp $0 `$\t` ReadConfFile_TrimRight
StrCmp $0 `$\r` ReadConfFile_TrimRight
StrCmp $0 `$\n` ReadConfFile_TrimRight
Goto ReadConfFile_WhiteSpaceDone
ReadConfFile_TrimRight:
StrCpy $1 $1 -1
Goto ReadConfFile_WhiteSpaceLoop
ReadConfFile_WhiteSpaceDone:
; -- now parse
StrCpy $0 $1 16
; -- check if line is `default_options=`, otherwise skip
StrCmp $0 "default_options=" 0 ReadConfFile_ReadLine
; -- check first character is a quote
StrCpy $0 $1 1 16
; -- if so, continue, otherwise skip
StrCmp $0 `"` 0 ReadConfFile_ReadLine
; -- check last character is a quote
StrCpy $0 $1 1 -1
; -- if so, continue, otherwise skip
StrCmp $0 `"` 0 ReadConfFile_ReadLine
; -- grab options string
StrCpy $0 $1 -1 17
ReadConfFile_done:
FileClose $2
ReadConfFile_exit:
ClearErrors
; -- Restore variables from the stack
Pop $2
Pop $1
Exch $0
FunctionEnd
Function GetSystemMemoryStatus
; -------------------------------------------------------------------------
; - Get system memory status
; - input: none
; - output: $2 - Structure size (bytes)
; - $3 - Memory load (%)
; - $4 - Total physical memory (bytes)
; - $5 - Free physical memory (bytes)
; - $6 - Total page file (bytes)
; - $7 - Free page file (bytes)
; - $8 - Total virtual (bytes)
; - $9 - Free virtual (bytes)
; -------------------------------------------------------------------------
; - Notes:
; - If more than 2Gb, this will return 2Gb
; - GlobalMemoryStatutsEx should be used for
; - > 2Gb but this is not supported until Win2K
; -
; - To convert to MBytes, use:
; - System::Int64Op $VAR / 1048576
; - Pop $VAR
; -------------------------------------------------------------------------
; -- Allocate memory
System::Alloc /NOUNLOAD 32
Pop $1
; -- Initialise
System::Call /NOUNLOAD "*$1(i64)"
; -- Make system call
System::Call /NOUNLOAD "Kernel32::GlobalMemoryStatus(i r1)"
; -- Move returned info into NSIS variables
System::Call /NOUNLOAD "*$1(i.r2, i.r3, i.r4, i.r5, i.r6, i.r7, i.r8, i.r9, i.r10)"
; -- Free allocated memory
System::Free $1
FunctionEnd
Function GetSystemMemoryStatus64
; -------------------------------------------------------------------------
; - Get system memory status
; - input: none
; - output: $2 - Structure size (bytes)
; - $3 - Memory load (%)
; - $4 - Total physical memory (bytes)
; - $5 - Free physical memory (bytes)
; - $6 - Total page file (bytes)
; - $7 - Free page file (bytes)
; - $8 - Total virtual (bytes)
; - $9 - Free virtual (bytes)
; -------------------------------------------------------------------------
; - Notes:
; - If more than 2Gb, this will return 2Gb
; - GlobalMemoryStatutsEx should be used for
; - > 2Gb but this is not supported until Win2K
; -
; - To convert to MBytes, use:
; - System::Int64Op $VAR / 1048576
; - Pop $VAR
; -------------------------------------------------------------------------
; -- Allocate memory
System::Alloc /NOUNLOAD 64
Pop $1
; -- Initialise
System::Call /NOUNLOAD "*$1(i64)"
; -- Make system call
System::Call /NOUNLOAD "Kernel32::GlobalMemoryStatusEx(i r1)"
; -- Move returned info into NSIS variables
System::Call /NOUNLOAD "*$1(i.r2, i.r3, l.r4, l.r5, l.r6, l.r7, l.r8, l.r9, l.r10)"
; -- Free allocated memory
System::Free $1
FunctionEnd
Function GetClassPath
; -------------------------------------------------------------------------
; - Get the class path by searching for .jar files in specified path
; - input: $0 path to search
; - $3 single file to exclude (i.e. jmri.jar)
; - $4 prefix to add (i.e. lib/)
; - output: $9 ClassPath
; -------------------------------------------------------------------------
FindFirst $2 $1 $0\*.jar
StrCpy $9 ""
StrCmp $2 "" error
again:
StrCmp $1 "" done
StrCmp $1 $3 readnext
StrCmp $9 "" 0 append
StrCpy $9 $4$1
Goto readnext
append:
StrCpy $9 "$9;$4$1"
readnext:
FindNext $2 $1
Goto again
done:
FindClose $2
Goto finished
error:
; Oops!
finished:
FunctionEnd
Function GetSettingsClassPath
; -------------------------------------------------------------------------
; - Get any additional class path items from preferences:lib for "prepending" to
; the CLASSPATH
; - input: $0 path to search (i.e. the equivalent of "preferences:lib")
; - output: $9 AddToClassPath
; -------------------------------------------------------------------------
;DetailPrint ""
;DetailPrint "Checking $0 for jar files"
; find the first "*.jar" file at $0.
StrCpy $9 ""
FindFirst $2 $1 "$0\*.jar"
loop:
; DetailPrint "Checking: $1"
; quit if now done
StrCmp $1 "" Done
; NOTE: the "find" operation WILL find filenames that have other characters
; than the ones specified in FindFirst operation. So look for the last 4
; characters of the filename to match ".jar"...
; check if found object ends in ".jar"
StrCpy $4 $1 4 -4
; DetailPrint "end of text is $4"
StrCmp $4 ".jar" 0 GoingToNext
; DetailPrint "answer $0\$1"
StrCmp $9 "" SkipSemicolon
StrCpy $9 "$9;"
SkipSemicolon:
; add the latest jar file to the end of the list of
; preferences:lib files
StrCpy $9 "$9$0\$1"
GoingToNext:
; Find the next one, if available
FindNext $2 $1
Goto loop
Done:
FindClose $0
;DetailPrint ""
FunctionEnd
Function GetParameters
; -------------------------------------------------------------------------
; - Gets command line parameters
; - input: top of stack
; - output: top of stack
; - modifies no other variables
; -------------------------------------------------------------------------
Exch $R0
Push $R1
Push $R2
Push $R3
Push $R4
StrCpy $R4 $R0
StrCpy $R2 1
StrLen $R3 $R4
; -- Check for quote or space
StrCpy $R0 $R4 $R2
StrCmp $R0 '"' 0 +3
StrCpy $R1 '"'
Goto loop
StrCpy $R1 " "
loop:
IntOp $R2 $R2 + 1
StrCpy $R0 $R4 1 $R2
StrCmp $R0 $R1 get
StrCmp $R2 $R3 get
Goto loop
get:
IntOp $R2 $R2 + 1
StrCpy $R0 $R4 1 $R2
StrCmp $R0 " " get
StrCpy $R0 $R4 "" $R2
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Exch $R0
FunctionEnd
Function GetWord
; -------------------------------------------------------------------------
; - Gets first word from a string
; - input: top of stack
; - output: top of stack
; - modifies no other variables
; -------------------------------------------------------------------------
Exch $R0
Push $R1
Push $R2
Push $R3
Push $R4
StrCpy $R4 $R0
StrCpy $R2 1
StrLen $R3 $R4
; -- Check for quote or space
StrCpy $R0 $R4 $R2
StrCmp $R0 '"' 0 +3
StrCpy $R1 '"'
Goto loop
StrCpy $R1 " "
loop:
IntOp $R2 $R2 + 1
StrCpy $R0 $R4 1 $R2
StrCmp $R0 $R1 get
StrCmp $R2 $R3 get
Goto loop
get:
StrCpy $R0 $R4 $R2
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Exch $R0
FunctionEnd
Function CheckUserHome
; -------------------------------------------------------------------------
; - Check if the value of the registry key that Java uses to determine
; - user.home points to the user profile directory.
; - For non NT-based systems, always return FLAG_YES
; - input: none
; - output: result on top of stack (FLAG_YES if OK; FLAG_NO if not)
; -------------------------------------------------------------------------
; -- Save variables to the stack
Push $0
DetailPrint "Checking user.home..."
; -- Check if we're on Win2K or later
; -- If not, return OK
StrCmp $PROFILE "" CheckUserHomeOK
; -- Read the registry key Java uses to determine user.home
DetailPrint "Reading Desktop Shell Folder registry key..."
ReadRegStr $0 HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" "Desktop"
DetailPrint "...read: $0"
; -- Check if path is equal to user profile
DetailPrint "Checking if equal to %USERPROFILE%..."
; -- Retrieve parent directory
Push $0
Call GetParent
Pop $0
DetailPrint "Comparing: $0"
DetailPrint "to: $PROFILE"
StrCmp $0 $PROFILE CheckUserHomeOK
; -- Not equal
DetailPrint "user.home not OK"
StrCpy $0 ${FLAG_NO}
Goto CheckUserHomeDone
CheckUserHomeOK:
DetailPrint "user.home OK"
StrCpy $0 ${FLAG_YES}
CheckUserHomeDone:
; -- Restore variables from the stack
Exch $0
FunctionEnd
Function GetParent
; -------------------------------------------------------------------------
; - Return the parent directory of specified file or folder
; - input: complete filename on top of stack
; - output: parent directory on top of stack
; -------------------------------------------------------------------------
; -- Save variables to the stack
Exch $0
Push $1
Push $2
Push $3
; -- Initialise character counter and string length
StrCpy $1 0
StrLen $2 $0
; -- Loop through until right-most '\' found
; -- or counter >= string length
GetParentLoop:
; -- Increase character counter
IntOp $1 $1 + 1
; -- Check if we're at the end of the string
IntCmp $1 $2 GetParentDir 0 GetParentDir
; -- Grab the character at current counter position
; -- working from right-hand end
StrCpy $3 $0 1 -$1
; -- Check if the character is a path seperator
StrCmp $3 "\" GetParentDir
; -- If not, back round again
Goto GetParentLoop
; -- Strip characters from right-hand end of string
GetParentDir:
StrCpy $0 $0 -$1
; -- Restore variables from the stack
Pop $3
Pop $2
Pop $1
Exch $0
FunctionEnd