Quicklaunch icon for packpanda and more changes

for a project i have been working on, i had to make some changes to the packpanda scripts which may be useful for other ppls.

  • added the option to create a quicklaunch and desktop icon
  • changed name of the launchers from “play $applicationname” to “$applicationname”
  • installation will be done into c:\program files$name$version
    (respects os-language progam folder name)
  • egg are only converted to bams if --bam defined
  • py are only converted to pyc’s if --pyc defined

change C:\Panda3D-1.5.3\direct\src\packpanda.nsi to:

; Panda3D installation script for the Nullsoft Installation System (NSIS).
; Jon Parise <jparise@cmu.edu>
; with Ben Johnson <bkj@andrew.cmu.edu>
; with Jason Pratt <pratt@andrew.cmu.edu>
; mangled by Josh Yelon <jyelon@andrew.cmu.edu>

; Caller needs to define these variables:
;
;   COMPRESSOR    - either zlib or lzma
;   NAME          - name of what we're building                 (ie, "Panda3D" or "Airblade")
;   SMDIRECTORY   - where to put this in the start menu         (ie, "Panda3D 1.1.0" or "Airblade 1.1.0")
;   INSTALLFOLDER - where to install the program                (ie, "C:\Program Files\Panda3D-1.1.0")
;   OUTFILE       - where to put the output file                (ie, "..\nsis-output.exe")
;   LICENSE       - location of the license file                (ie, "C:\Airblade\LICENSE.TXT")
;   LANGUAGE      - name of the Language file to use            (ie, "English" or "Panda3DEnglish")
;   RUNTEXT       - text for run-box at end of installation     (ie, "Run the Panda Greeting Card")
;   IBITMAP       - name of installer bitmap                    (ie, "C:\Airblade\Airblade.bmp")
;   UBITMAP       - name of uninstaller bitmap                  (ie, "C:\Airblade\Airblade.bmp")
;
;   PANDA         - location of panda install tree.
;   PANDACONF     - name of panda config directory - usually $PANDA\etc 
;   PSOURCE       - location of the panda source-tree if available, OR location of panda install tree.
;   PYEXTRAS      - directory containing python extras, if any.
;
;   PPGAME      - directory containing prepagaged game, if any        (ie, "C:\My Games\Airblade")
;   PPMAIN      - python program containing prepackaged game, if any  (ie, "Airblade.py")
;   PPICON      - file containing icon of prepackaged game.
;

!include "MUI.nsh"
!include "WinMessages.nsh"
Name "${NAME}"
InstallDir "${INSTALLDIR}"
OutFile "${OUTFILE}"

SetCompress auto
SetCompressor ${COMPRESSOR}

!define MUI_WELCOMEFINISHPAGE_BITMAP "${IBITMAP}"
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "${UBITMAP}"

!define MUI_ABORTWARNING
!define MUI_FINISHPAGE_NOREBOOTSUPPORT
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION runFunction
!define MUI_FINISHPAGE_RUN_TEXT "${RUNTEXT}"

!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "${LICENSE}"
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_COMPONENTS  ; <- added for custom startup icons
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH

!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH

!insertmacro MUI_LANGUAGE "${LANGUAGE}"

ShowInstDetails nevershow
ShowUninstDetails nevershow

LicenseData "${LICENSE}"

InstType "Typical"

!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS

var READABLE
var MANPAGE
var TUTNAME

!ifdef PPGAME
    ; option to select quicklaunch and desktop icons
    Section "Desktop icon" SecDesktop
        SectionIn 1
        !define DESKTOP_ICON
    SectionEnd
    
    Section "Quick Launch icon" SecQuick
        !define QUICKLAUNCH_ICON
    SectionEnd
    
    !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
        !insertmacro MUI_DESCRIPTION_TEXT ${SecDesktop} "An icon of ${NAME} \
          on the desktop"
        !insertmacro MUI_DESCRIPTION_TEXT ${SecQuick} "An icon of ${NAME} \
          in the Quick Launch bar"
    !insertmacro MUI_FUNCTION_DESCRIPTION_END
!else
    ; option to add panda3d to the default python installation
    
    ; example for adding the functionality
    ;-------------
    ; mostly doing what we need from, already modified a bit
    ; http://adullact.net/plugins/scmcvs/cvsweb.php/devkit-python/py25pywin32.nsi?rev=1.6;cvsroot=atelier-dev
    ;-------------
    ; add a .path file to the python installation
    ;Function PythonPathAdd
    ;    ${AdReadVarFromPackage} $0 "python25" "InstallLocation"
    ;    StrCmp "$0" "" 0 we_have_the_folder
    ;       
    ;    ReadRegStr $0 HKLM "SOFTWARE\Python\PythonCore\2.5\InstallPath" ""
    ;    StrCmp "$0" "" 0 we_have_the_folder
    ;        ${Log} "   Python is not installed !"
    ;        Goto install_done
    ;   
    ;    we_have_the_folder:
    ;        ${Log} "   Python is in '$0'"
    ;        ; here we should write the file
    ;        ;create that file...
    ;        ;sample from: http://nsis.sourceforge.net/Docs/AppendixE.html#E.2.14
    ;        ;FileOpen $1 "$1\autoexec.bat" a
    ;        ;FileSeek $1 0 END
    ;        ;GetFullPathName /SHORT $0 $0
    ;        ;FileWrite $1 "$\r$\nSET PATH=%PATH%;$0$\r$\n"
    ;        ;FileClose $1
    ;        ;TODO
    ;    
    ;    install_done:
    ;    
    ;    SetOutPath "$INSTDIR"
    ;FunctionEnd
    
    Section "Default Python Path" SecPythonPath
        ; for blueprint
        ; https://blueprints.launchpad.net/panda3d/+spec/windows-installer-respect-existing-python
        SectionIn 1
        ; call that Function PythonPathAdd when this option is selected
    SectionEnd
    
    !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
        !insertmacro MUI_DESCRIPTION_TEXT ${SecPythonPath} "Panda3d will be added \
          to the default python path (not yet implemented)"
    !insertmacro MUI_FUNCTION_DESCRIPTION_END
!endif

Function runFunction
        !ifdef PPGAME
            ExecShell "open" "$SMPROGRAMS\${SMDIRECTORY}\${NAME}.lnk"
        !else
            ExecShell "open" "$SMPROGRAMS\${SMDIRECTORY}\Panda Manual.lnk"
        !endif
FunctionEnd

Section "${SMDIRECTORY}" SecCore
        SectionIn 1 2 3 RO

        SetDetailsPrint none
        SetOutPath $INSTDIR
        SetOverwrite try

        SetOutPath $INSTDIR
        File "${PANDA}\LICENSE"
        SetOutPath $INSTDIR\bin
        File /r "${PANDA}\bin\*.dll"
        File /r "${PANDA}\bin\Microsoft.VC80.CRT.manifest"
        SetOutPath $INSTDIR\etc
        File /r "${PANDACONF}\*"
        SetOutPath $INSTDIR\direct\src\directscripts
        File /r /x CVS /x Opt?-Win32 "${PSOURCE}\direct\src\directscripts\*"
        SetOutPath $INSTDIR\direct\src\filter
        File /r /x CVS /x Opt?-Win32 "${PSOURCE}\direct\src\filter\*.sha"
        SetOutPath $INSTDIR\direct
        File /r /x CVS /x Opt?-Win32 "${PSOURCE}\direct\*.py"
        File "${PANDA}\direct\__init__.py"
        SetOutPath $INSTDIR\pandac
        File /r "${PANDA}\pandac\*.py"
        SetOutPath $INSTDIR\python
        File /r "${PANDA}\python\*"
        CreateDirectory "$INSTDIR\modelcache"
        RMDir /r "$SMPROGRAMS\${SMDIRECTORY}"
        CreateDirectory "$SMPROGRAMS\${SMDIRECTORY}"

        !ifdef PPGAME

            SetOutPath $INSTDIR\models\audio
            File /r /x CVS "${PANDA}\models\audio\*"
            SetOutPath $INSTDIR\bin
            File /r "${PANDA}\bin\eggcacher.exe"
            SetOutpath $INSTDIR\game
            File /r "${PPGAME}\*"
            ;create start menu shortcut
            CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\${NAME}.lnk" "$INSTDIR\python\pythonw.exe" "-E ${PPMAIN}" "$INSTDIR\${PPICON}" 0 SW_SHOWMINIMIZED "" "${NAME}"
            ;create desktop shortcut
            !ifdef DESKTOP_ICON
                CreateShortCut "$DESKTOP\${NAME}.lnk" "$INSTDIR\python\pythonw.exe" "-E ${PPMAIN}" "$INSTDIR\${PPICON}" 0 SW_SHOWMINIMIZED "" "${NAME}"
            !endif
            ;create quicklaunch shortcut
            !ifdef QUICKLAUNCH_ICON
                CreateShortCut "$QUICKLAUNCH\${NAME}.lnk" "$INSTDIR\python\pythonw.exe" "-E ${PPMAIN}" "$INSTDIR\${PPICON}" 0 SW_SHOWMINIMIZED "" "${NAME}"
            !endif
            # Preload all EGG files into the model-cache
            SetOutPath $INSTDIR
            SetDetailsPrint textonly
            SetDetailsView show
            nsExec::ExecToLog '"$INSTDIR\bin\eggcacher.exe" --concise game'
            SetDetailsPrint none
            SetDetailsView hide

        !else

            SetOutPath $INSTDIR\plugins
            File /nonfatal /r "${PANDA}\plugins\*.dle"
            File /nonfatal /r "${PANDA}\plugins\*.dlo"
            File /nonfatal /r "${PANDA}\plugins\*.mll"
            File /nonfatal /r "${PANDA}\plugins\*.mel"
            File ${PSOURCE}\doc\INSTALLING-PLUGINS.TXT
            SetOutPath $INSTDIR\pandac\input
            File /r "${PANDA}\pandac\input\*"
            SetOutPath $INSTDIR\bin
            File /r "${PANDA}\bin\*.exe"
            SetOutPath $INSTDIR\lib
            File /r /x *.exp "${PANDA}\lib\*"
            SetOutPath $INSTDIR\include
            File /r /x *.exp "${PANDA}\include\*"
            SetOutPath $INSTDIR\Pmw
            File /r /x CVS "${PANDA}\Pmw\*"
            SetOutPath $INSTDIR\NSIS
            File /r /x CVS "${NSISDIR}\*"
            SetOutPath $INSTDIR
            File /r /x CVS "${PANDA}\ReleaseNotes"
            !ifdef PYEXTRAS
            SetOutPath $INSTDIR\python\lib
            File /nonfatal /r "${PYEXTRAS}\*"
            !endif
            SetOutPath $INSTDIR\models
            File /r /x CVS "${PANDA}\models\*"
            SetOutPath $INSTDIR\samples
            File /r /x CVS "${PSOURCE}\samples\*"
            # Preload all EGG files into the model-cache
            SetOutPath $INSTDIR
            SetDetailsPrint both
            SetDetailsView show
            nsExec::ExecToLog '"$INSTDIR\bin\eggcacher.exe" --concise models samples'
            SetDetailsPrint none
            SetDetailsView hide

            SetOutPath $INSTDIR
            WriteINIStr $INSTDIR\Website.url "InternetShortcut" "URL" "http://panda3d.etc.cmu.edu/"
            WriteINIStr $INSTDIR\Manual.url "InternetShortcut" "URL" "http://panda3d.etc.cmu.edu/wiki/index.php"
            WriteINIStr $INSTDIR\Samples.url "InternetShortcut" "URL" "http://panda3d.etc.cmu.edu/wiki/index.php/Sample_Programs_in_the_Distribution"
            SetOutPath $INSTDIR
            CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Panda Manual.lnk" "$INSTDIR\Manual.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Panda Manual"
            CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Panda Website.lnk" "$INSTDIR\Website.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Panda Website"
            CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Sample Program Manual.lnk" "$INSTDIR\Samples.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Sample Program Manual"

            FindFirst $0 $1 $INSTDIR\samples\*
            loop:
                StrCmp $1 "" done
                StrCmp $1 "." next
                StrCmp $1 ".." next
		Push $1
	        Push "-"
                Push " "
                Call StrRep
                Pop $R0
                StrCpy $READABLE $R0
		Push $1
	        Push "-"
                Push "_"
                Call StrRep
                Pop $R0
                StrCpy $MANPAGE $R0
                CreateDirectory "$SMPROGRAMS\${SMDIRECTORY}\Sample Programs\$READABLE"
                SetOutPath $INSTDIR\samples\$1
                WriteINIStr $INSTDIR\samples\$1\ManualPage.url "InternetShortcut" "URL" "http://panda3d.etc.cmu.edu/wiki/index.php/Sample_Programs:_$MANPAGE"
                CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Sample Programs\$READABLE\Manual Page.lnk" "$INSTDIR\samples\$1\ManualPage.url" "" "$INSTDIR\bin\eggcacher.exe" 0 "" "" "Manual Entry on this Sample Program"
                CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Sample Programs\$READABLE\View Source Code.lnk" "$INSTDIR\samples\$1"
                FindFirst $2 $3 $INSTDIR\samples\$1\Tut-*.py
                iloop:
                    StrCmp $3 "" idone
                    StrCpy $TUTNAME $3 -3 4
                    Push $TUTNAME
                    Push "-"
                    Push " "
                    Call StrRep
                    Pop $R0
                    StrCpy $TUTNAME $R0
                    CreateShortCut "$SMPROGRAMS\${SMDIRECTORY}\Sample Programs\$READABLE\Run $TUTNAME.lnk" "$INSTDIR\python\python.exe" "-E $3" "$INSTDIR\bin\eggcacher.exe" 0 SW_SHOWMINIMIZED "" "Run $TUTNAME"
                    CreateShortCut "$INSTDIR\samples\$1\Run $TUTNAME.lnk" "$INSTDIR\python\python.exe" "-E $3" "$INSTDIR\bin\eggcacher.exe" 0 SW_SHOWMINIMIZED "" "Run $TUTNAME"
                    FindNext $2 $3
                    goto iloop
                idone:
            next:
                FindNext $0 $1
                Goto loop
            done:

        !endif

SectionEnd


Section -post

        !ifndef PPGAME

        # Add the "bin" directory to the PATH.
        Push "$INSTDIR\python"
        Call RemoveFromPath
        Push "$INSTDIR\bin"
        Call RemoveFromPath
        Push "$INSTDIR\python"
        Call AddToPath
        Push "$INSTDIR\bin"
        Call AddToPath

        ReadRegStr $0 HKLM "Software\Python\PythonCore\2.5\InstallPath" ""
        StrCmp $0 "$INSTDIR\python" RegPath 0
        StrCmp $0 "" RegPath 0

        MessageBox MB_YESNO|MB_ICONQUESTION \
                "Python add-on installers use a registry key to locate the Python directory.  The registry key is already pointing to a copy of Python.  Do you want to change the registry key to point to Panda's copy?" \
                IDNO SkipRegPath

        RegPath:
        DetailPrint "Adding registry keys for python..."
        WriteRegStr HKLM "Software\Python\PythonCore\2.5\InstallPath" "" "$INSTDIR\python"
        WriteRegStr HKLM "Software\Python\PythonCore\2.5\Help" "" ""
        WriteRegStr HKLM "Software\Python\PythonCore\2.5\Help\Main Python Documentation" "" "$INSTDIR\python\Doc\Python24.chm"
        WriteRegStr HKLM "Software\Python\PythonCore\2.5\Help\Pythonwin Reference" "" "$INSTDIR\python\Lib\site-packages\PyWin32.chm"
        SkipRegPath:
        !endif

        DetailPrint "Adding the uninstaller ..."
        Delete "$INSTDIR\uninst.exe"
        WriteUninstaller "$INSTDIR\uninst.exe"
        WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SMDIRECTORY}" "DisplayName" "${SMDIRECTORY}"
        WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SMDIRECTORY}" "UninstallString" '"$INSTDIR\uninst.exe"'
        CreateShortcut "$SMPROGRAMS\${SMDIRECTORY}\Uninstall ${NAME}.lnk" "$INSTDIR\uninst.exe" ""

SectionEnd

Section Uninstall

        !ifndef PPGAME
        Push "$INSTDIR\python"
        Call un.RemoveFromPath
        Push "$INSTDIR\python"
        Call un.RemoveFromPath
        Push "$INSTDIR\bin"
        Call un.RemoveFromPath
        Push "$INSTDIR\bin"
        Call un.RemoveFromPath

        ReadRegStr $0 HKLM "Software\Python\PythonCore\2.5\InstallPath" ""
        StrCmp $0 "$INSTDIR\python" 0 SkipUnReg
        DeleteRegKey HKLM "Software\Python\PythonCore\2.5"
        SkipUnReg:
        !endif

        !ifdef DESKTOP_ICON
            ;delete desktop shortcut
            Delete "$DESKTOP\${NAME}.lnk"
        !endif
        !ifdef QUICKLAUNCH_ICON
            ;delete quicklaunch shortcut
            Delete "$QUICKLAUNCH\${NAME}.lnk"
        !endif

        Delete "$INSTDIR\uninst.exe"
        ;delete startmenu entry
        RMDir /r "$SMPROGRAMS\${SMDIRECTORY}"
        ;delete game
        RMDir /r "$INSTDIR"
        DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SMDIRECTORY}"

SectionEnd

# --[ Utility Functions ]------------------------------------------------------

; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
Function IsNT
        Push $0
        ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
        StrCmp $0 "" 0 IsNT_yes
        ; we are not NT.
        Pop $0
        Push 0
        Return
        IsNT_yes:
                ; NT!!!
                Pop $0
                Push 1
FunctionEnd

; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
Function un.IsNT
        Push $0
        ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
        StrCmp $0 "" 0 unIsNT_yes
        ; we are not NT.
        Pop $0
        Push 0
        Return
        unIsNT_yes:
                ; NT!!!
                Pop $0
                Push 1
FunctionEnd

; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
Function StrStr
        Push $0
        Exch
        Pop $0 ; $0 now have the string to find
        Push $1
        Exch 2
        Pop $1 ; $1 now have the string to find in
        Exch
        Push $2
        Push $3
        Push $4
        Push $5
        StrCpy $2 -1
        StrLen $3 $0
        StrLen $4 $1
        IntOp $4 $4 - $3
        unStrStr_loop:
                IntOp $2 $2 + 1
                IntCmp $2 $4 0 0 unStrStrReturn_notFound
                StrCpy $5 $1 $3 $2
                StrCmp $5 $0 unStrStr_done unStrStr_loop
        unStrStrReturn_notFound:
                StrCpy $2 -1
        unStrStr_done:
                Pop $5
                Pop $4
                Pop $3
                Exch $2
                Exch 2
                Pop $0
                Pop $1
FunctionEnd

; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
Function un.StrStr
        Push $0
        Exch
        Pop $0 ; $0 now have the string to find
        Push $1
        Exch 2
        Pop $1 ; $1 now have the string to find in
        Exch
        Push $2
        Push $3
        Push $4
        Push $5
        StrCpy $2 -1
        StrLen $3 $0
        StrLen $4 $1
        IntOp $4 $4 - $3
        unStrStr_loop:
                IntOp $2 $2 + 1
                IntCmp $2 $4 0 0 unStrStrReturn_notFound
                StrCpy $5 $1 $3 $2
                StrCmp $5 $0 unStrStr_done unStrStr_loop
        unStrStrReturn_notFound:
                StrCpy $2 -1
        unStrStr_done:
                Pop $5
                Pop $4
                Pop $3
                Exch $2
                Exch 2
                Pop $0
                Pop $1
FunctionEnd

; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
; Commentary and smarter ';' checking by Jon Parise <jparise@cmu.edu>
Function AddToPath
        Exch $0
        Push $1
        Push $2
        Push $3
        Call IsNT
        Pop $1

        StrCmp $1 1 AddToPath_NT
                ; We're not on NT, so modify the AUTOEXEC.BAT file.
                StrCpy $1 $WINDIR 2
                FileOpen $1 "$1\autoexec.bat" a
                FileSeek $1 0 END
                GetFullPathName /SHORT $0 $0
                FileWrite $1 "$\r$\nSET PATH=%PATH%;$0$\r$\n"
                FileClose $1
                Goto AddToPath_done
        
        AddToPath_NT:
                ReadRegStr $1 HKCU "Environment" "PATH"
                Call IsUserAdmin
                Pop $3
                ; If this is an Admin user, use the System env. variable instead of the user's env. variable
                StrCmp $3 1 0 +2
                        ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH"

                ; If the PATH string is empty, jump over the mangling routines.
                StrCmp $1 "" AddToPath_NTdoIt

                ; Pull off the last character of the PATH string.  If it's a semicolon,
                ; we don't need to add another one, so jump to the section where we
                ; append the new PATH component(s).
                StrCpy $2 $1 1 -1
                StrCmp $2 ";" AddToPath_NTAddPath AddToPath_NTAddSemi

                AddToPath_NTAddSemi:
                        StrCpy $1 "$1;"
                        Goto AddToPath_NTAddPath
                AddToPath_NTAddPath:
                        StrCpy $0 "$1$0"
                        Goto AddToPath_NTdoIt
                AddToPath_NTdoIt:
                        Call IsUserAdmin
                        Pop $3
                        StrCmp $3 1 0 NotAdmin
                                WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $0
                                Goto AddToPath_done
                        
                        NotAdmin:
                                WriteRegExpandStr HKCU "Environment" "PATH" $0
        AddToPath_done:
                SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
                Pop $3
                Pop $2
                Pop $1
                Pop $0
FunctionEnd

; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
Function RemoveFromPath
        Exch $0
        Push $1
        Push $2
        Push $3
        Push $4
        Push $5
        Call IsNT
        Pop $1
        StrCmp $1 1 unRemoveFromPath_NT
                ; Not on NT
                StrCpy $1 $WINDIR 2
                FileOpen $1 "$1\autoexec.bat" r
                GetTempFileName $4
                FileOpen $2 $4 w
                GetFullPathName /SHORT $0 $0
                StrCpy $0 "SET PATH=%PATH%;$0"
                SetRebootFlag true
                Goto unRemoveFromPath_dosLoop

                unRemoveFromPath_dosLoop:
                        FileRead $1 $3
                        StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoop
                        StrCmp $3 "$0$\n" unRemoveFromPath_dosLoop
                        StrCmp $3 "$0" unRemoveFromPath_dosLoop
                        StrCmp $3 "" unRemoveFromPath_dosLoopEnd
                        FileWrite $2 $3
                        Goto unRemoveFromPath_dosLoop

                unRemoveFromPath_dosLoopEnd:
                        FileClose $2
                        FileClose $1
                        StrCpy $1 $WINDIR 2
                        Delete "$1\autoexec.bat"
                        CopyFiles /SILENT $4 "$1\autoexec.bat"
                        Delete $4
                        Goto unRemoveFromPath_done

                unRemoveFromPath_NT:
                        StrLen $2 $0
                        Call IsUserAdmin
                        Pop $5
                        StrCmp $5 1 0 NotAdmin
                                ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH"
                                Push $1
                                Push $0
                                Call StrStr ; Find $0 in $1
                                Pop $0 ; pos of our dir
                                IntCmp $0 -1 unRemoveFromPath_done
                                        ; else, it is in path
                                        StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir
                                        IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';')
                                        IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon.
                                        StrLen $0 $1
                                        StrCpy $1 $1 $0 $2
                                        StrCpy $3 "$3$1"
                                        WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $3
                                        SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
                                        Goto unRemoveFromPath_done
                        
                        
                        NotAdmin:               
                                ReadRegStr $1 HKCU "Environment" "PATH"
                                Push $1
                                Push $0
                                Call StrStr ; Find $0 in $1
                                Pop $0 ; pos of our dir
                                IntCmp $0 -1 unRemoveFromPath_done
                                        ; else, it is in path
                                        StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir
                                        IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';')
                                        IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon.
                                        StrLen $0 $1
                                        StrCpy $1 $1 $0 $2
                                        StrCpy $3 "$3$1"
                                        WriteRegExpandStr HKCU "Environment" "PATH" $3
                                        SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000

                unRemoveFromPath_done:
                        Pop $5
                        Pop $4
                        Pop $3
                        Pop $2
                        Pop $1
                        Pop $0
FunctionEnd

; From: http://nsis.sourceforge.net/archive/viewpage.php?pageid=91
Function un.RemoveFromPath
        Exch $0
        Push $1
        Push $2
        Push $3
        Push $4
        Push $5
        Call un.IsNT
        Pop $1
        StrCmp $1 1 unRemoveFromPath_NT
                ; Not on NT
                StrCpy $1 $WINDIR 2
                FileOpen $1 "$1\autoexec.bat" r
                GetTempFileName $4
                FileOpen $2 $4 w
                GetFullPathName /SHORT $0 $0
                StrCpy $0 "SET PATH=%PATH%;$0"
                SetRebootFlag true
                Goto unRemoveFromPath_dosLoop

                unRemoveFromPath_dosLoop:
                        FileRead $1 $3
                        StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoop
                        StrCmp $3 "$0$\n" unRemoveFromPath_dosLoop
                        StrCmp $3 "$0" unRemoveFromPath_dosLoop
                        StrCmp $3 "" unRemoveFromPath_dosLoopEnd
                        FileWrite $2 $3
                        Goto unRemoveFromPath_dosLoop

                unRemoveFromPath_dosLoopEnd:
                        FileClose $2
                        FileClose $1
                        StrCpy $1 $WINDIR 2
                        Delete "$1\autoexec.bat"
                        CopyFiles /SILENT $4 "$1\autoexec.bat"
                        Delete $4
                        Goto unRemoveFromPath_done

                unRemoveFromPath_NT:
                        StrLen $2 $0
                        Call un.IsUserAdmin
                        Pop $5
                        StrCmp $5 1 0 NotAdmin
                                ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH"
                                Push $1
                                Push $0
                                Call un.StrStr ; Find $0 in $1
                                Pop $0 ; pos of our dir
                                IntCmp $0 -1 unRemoveFromPath_done
                                        ; else, it is in path
                                        StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir
                                        IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';')
                                        IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon.
                                        StrLen $0 $1
                                        StrCpy $1 $1 $0 $2
                                        StrCpy $3 "$3$1"
                                        WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $3
                                        SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
                                        Goto unRemoveFromPath_done
                        
                        
                        NotAdmin:               
                                ReadRegStr $1 HKCU "Environment" "PATH"
                                Push $1
                                Push $0
                                Call un.StrStr ; Find $0 in $1
                                Pop $0 ; pos of our dir
                                IntCmp $0 -1 unRemoveFromPath_done
                                        ; else, it is in path
                                        StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir
                                        IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';')
                                        IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon.
                                        StrLen $0 $1
                                        StrCpy $1 $1 $0 $2
                                        StrCpy $3 "$3$1"
                                        WriteRegExpandStr HKCU "Environment" "PATH" $3
                                        SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000

                unRemoveFromPath_done:
                        Pop $5
                        Pop $4
                        Pop $3
                        Pop $2
                        Pop $1
                        Pop $0
FunctionEnd

; From: http://nsis.sourceforge.net/archive/nsisweb.php?page=329&instances=0,11
; Localized by Ben Johnson (bkj@andrew.cmu.edu)
Function IsUserAdmin
        Push $0
        Push $1
        Push $2
        Push $3
        Call IsNT
        Pop $1

        ClearErrors
        UserInfo::GetName
        ;IfErrors Win9x
        Pop $2
        UserInfo::GetAccountType
        Pop $3

        ; Compare results of IsNT with "1"
        StrCmp $1 1 0 NotNT
                ;This is NT
                

                StrCmp $3 "Admin" 0 NotAdmin
                        ; Observation: I get here when running Win98SE. (Lilla)
                        ; The functions UserInfo.dll looks for are there on Win98 too, 
                        ; but just don't work. So UserInfo.dll, knowing that admin isn't required
                        ; on Win98, returns admin anyway. (per kichik)
                        ; MessageBox MB_OK 'User "$R1" is in the Administrators group'
                        Pop $3
                        Pop $2
                        Pop $1
                        Pop $0

                        Push 1
                        Return

                NOtAdmin:
                        ; You should still check for an empty string because the functions
                        ; UserInfo.dll looks for may not be present on Windows 95. (per kichik)
                
                        #StrCmp $2 "" Win9x
                        #StrCpy $0 0
                        ;MessageBox MB_OK 'User "$2" is in the "$3" group'
                        Pop $3
                        Pop $2
                        Pop $1
                        Pop $0

                        Push 0
                        Return

        ;Because we use IsNT, this is redundant.
        #Win9x:
        #       ; comment/message below is by UserInfo.nsi author:
        #       ; This one means you don't need to care about admin or
        #       ; not admin because Windows 9x doesn't either
        #       ;MessageBox MB_OK "Error! This DLL can't run under Windows 9x!"
        #       StrCpy $0 0

        NotNT:
                ;We are not NT
                ;Win9x doesn't have "admin" users.
                ;Let the user do whatever.
                Pop $3
                Pop $2
                Pop $1
                Pop $0

                Push 1

FunctionEnd

Function un.IsUserAdmin
        Push $0
        Push $1
        Push $2
        Push $3
        Call un.IsNT
        Pop $1

        ClearErrors
        UserInfo::GetName
        ;IfErrors Win9x
        Pop $2
        UserInfo::GetAccountType
        Pop $3

        ; Compare results of IsNT with "1"
        StrCmp $1 1 0 NotNT
                ;This is NT
                

                StrCmp $3 "Admin" 0 NotAdmin
                        ; Observation: I get here when running Win98SE. (Lilla)
                        ; The functions UserInfo.dll looks for are there on Win98 too, 
                        ; but just don't work. So UserInfo.dll, knowing that admin isn't required
                        ; on Win98, returns admin anyway. (per kichik)
                        ; MessageBox MB_OK 'User "$R1" is in the Administrators group'
                        Pop $3
                        Pop $2
                        Pop $1
                        Pop $0

                        Push 1
                        Return

                NOtAdmin:
                        ; You should still check for an empty string because the functions
                        ; UserInfo.dll looks for may not be present on Windows 95. (per kichik)
                
                        #StrCmp $2 "" Win9x
                        #StrCpy $0 0
                        ;MessageBox MB_OK 'User "$2" is in the "$3" group'
                        Pop $3
                        Pop $2
                        Pop $1
                        Pop $0

                        Push 0
                        Return

        ;Because we use IsNT, this is redundant.
        #Win9x:
        #       ; comment/message below is by UserInfo.nsi author:
        #       ; This one means you don't need to care about admin or
        #       ; not admin because Windows 9x doesn't either
        #       ;MessageBox MB_OK "Error! This DLL can't run under Windows 9x!"
        #       StrCpy $0 0

        NotNT:
                ;We are not NT
                ;Win9x doesn't have "admin" users.
                ;Let the user do whatever.
                Pop $3
                Pop $2
                Pop $1
                Pop $0

                Push 1

FunctionEnd

Function StrRep

  ;Written by dirtydingus 2003-02-20 04:30:09 
  ; USAGE
  ;Push String to do replacement in (haystack)
  ;Push String to replace (needle)
  ;Push Replacement
  ;Call StrRep
  ;Pop $R0 result
  ;StrCpy $Result STR $R0

  Exch $R4 ; $R4 = Replacement String
  Exch
  Exch $R3 ; $R3 = String to replace (needle)
  Exch 2
  Exch $R1 ; $R1 = String to do replacement in (haystack)
  Push $R2 ; Replaced haystack
  Push $R5 ; Len (needle)
  Push $R6 ; len (haystack)
  Push $R7 ; Scratch reg
  StrCpy $R2 ""
  StrLen $R5 $R3
  StrLen $R6 $R1
loop:
  StrCpy $R7 $R1 $R5
  StrCmp $R7 $R3 found
  StrCpy $R7 $R1 1 ; - optimization can be removed if U know len needle=1
  StrCpy $R2 "$R2$R7"
  StrCpy $R1 $R1 $R6 1
  StrCmp $R1 "" done loop
found:
  StrCpy $R2 "$R2$R4"
  StrCpy $R1 $R1 $R6 $R5
  StrCmp $R1 "" done loop
done:
  StrCpy $R3 $R2
  Pop $R7
  Pop $R6
  Pop $R5
  Pop $R2
  Pop $R1
  Pop $R4
  Exch $R3
	
FunctionEnd

change C:\Panda3D-1.5.3\direct\src\packpanda.py to:

#############################################################################
#
# packpanda - this is a tool that packages up a panda game into a
# convenient, easily-downloaded windows executable.  Packpanda relies on
# NSIS, the netscape scriptable install system, to do the hard work.
#
# This is intentionally a very simplistic game-packer with very
# limited options.  The goal is simplicity, not feature richness.
# There are dozens of complex, powerful packaging tools already out
# there.  This one is for people who just want to do it quick and
# easy.
#
##############################################################################

# changelog:
# - installation will be done into c:\program files\$name$version
#   (respects os-language progam folder name)
# - egg are only converted to bams if --bam defined
# - py are only converted to pyc's if --pyc defined

import sys, os, getopt, string, shutil, py_compile

OPTIONLIST = [
("dir",       1, "Name of directory containing game"),
("name",      1, "Human-readable name of the game"),
("version",   1, "Version number to add to game name"),
("rmdir",     2, "Delete all directories with given name"),
("rmext",     2, "Delete all files with given extension"),
("fast",      0, "Use fast compression instead of good compression"),
("bam",       0, "Generate BAM files, change default-model-extension to BAM"),
("pyc",       0, "Generate PYC files"),
]

def ParseFailure():
  print ""
  print "packpanda usage:"
  print ""
  for (opt, hasval, explanation) in OPTIONLIST:
    if (hasval):
      print "  --%-10s    %s"%(opt+" x", explanation)
    else:
      print "  --%-10s    %s"%(opt+"  ", explanation)
  sys.exit(1)

def ParseOptions(args):
  try:
    options = {}
    longopts = []
    for (opt, hasval, explanation) in OPTIONLIST:
      if (hasval==2):
        longopts.append(opt+"=")
        options[opt] = []
      elif (hasval==1):
        longopts.append(opt+"=")
        options[opt] = ""
      else:
        longopts.append(opt)
        options[opt] = 0
    opts, extras = getopt.getopt(args, "", longopts)
    for option, value in opts:
      for (opt, hasval, explanation) in OPTIONLIST:
        if (option == "--"+opt):
          if (hasval==2): options[opt].append(value)
          elif (hasval==1): options[opt] = value
          else: options[opt] = 1
    return options
  except: ParseFailure();

OPTIONS = ParseOptions(sys.argv[1:])

##############################################################################
#
# Locate the relevant trees.
#
##############################################################################

PANDA=None
for dir in sys.path:
    if (dir != "") and os.path.exists(os.path.join(dir,"direct")) and os.path.exists(os.path.join(dir,"pandac")):
        PANDA=os.path.abspath(dir)
if (PANDA is None):
  sys.exit("Cannot locate the panda root directory in the python path (cannot locate directory containing direct and pandac).")
print "PANDA located at "+PANDA

if (os.path.exists(os.path.join(PANDA,"..","makepanda","makepanda.py"))) and (os.path.exists(os.path.join(PANDA,"..","thirdparty","win-nsis","makensis.exe"))):
  PSOURCE=os.path.abspath(os.path.join(PANDA,".."))
  NSIS=os.path.abspath(os.path.join(PANDA,"..","thirdparty","win-nsis"))
else:
  PSOURCE=PANDA
  NSIS=os.path.join(PANDA,"nsis")

##############################################################################
#
# Identify the main parts of the game: DIR, NAME, MAIN, ICON, BITMAP, etc
#
##############################################################################

VER=OPTIONS["version"]
DIR=OPTIONS["dir"]
if (DIR==""):
  print "You must specify the --dir option."
  ParseFailure()
DIR=os.path.abspath(DIR)
NAME=os.path.basename(DIR)
if (OPTIONS["name"] != ""):
  NAME=OPTIONS["name"]
SMDIRECTORY=NAME
if (VER!=""): SMDIRECTORY=SMDIRECTORY+" "+VER
ICON=os.path.join(DIR, "icon.ico")
BITMAP=os.path.join(DIR, "installer.bmp")
LICENSE=os.path.join(DIR, "license.txt")
OUTFILE=os.path.basename(DIR)
if (VER!=""): OUTFILE=OUTFILE+"-"+VER
OUTFILE=os.path.abspath(OUTFILE+".exe")
INSTALLDIR = "$PROGRAMFILES\\"+os.path.basename(DIR)
if (VER!=""): INSTALLDIR=INSTALLDIR+"-"+VER
COMPRESS="lzma"
if (OPTIONS["fast"]): COMPRESS="zlib"
if (OPTIONS["pyc"]): MAIN="main.pyc"
else: MAIN="main.py"

def PrintFileStatus(label, file):
  if (os.path.exists(file)):
    print "%-15s: %s"%(label, file)
  else:
    print "%-15s: %s (MISSING)"%(label, file)

PrintFileStatus("Dir", DIR)
print "%-15s: %s"%("Name", NAME)
print "%-15s: %s"%("Start Menu", SMDIRECTORY)
PrintFileStatus("Main", os.path.join(DIR, MAIN))
PrintFileStatus("Icon", ICON)
PrintFileStatus("Bitmap", BITMAP)
PrintFileStatus("License", LICENSE)
print "%-15s: %s"%("Output", OUTFILE)
print "%-15s: %s"%("Install Dir", INSTALLDIR)

if (os.path.isdir(DIR)==0):
  sys.exit("Difficulty reading "+DIR+". Cannot continue.")

if (os.path.isfile(os.path.join(DIR, "main.py"))==0):
  sys.exit("Difficulty reading main.py. Cannot continue.")

if (os.path.isfile(LICENSE)==0):
  LICENSE=os.path.join(PANDA,"LICENSE")

if (os.path.isfile(BITMAP)==0):
  BITMAP=os.path.join(NSIS,"Contrib","Graphics","Wizard","nsis.bmp")

if (os.path.isfile(ICON)==0):
  PPICON="bin\\ppython.exe"
else:
  PPICON="game\\icon.ico"

##############################################################################
#
# Copy the game to a temporary directory, so we can modify it safely.
#
##############################################################################

def limitedCopyTree(src, dst, rmdir):
    if (os.path.isdir(src)):
        if (rmdir.has_key(os.path.basename(src))):
            return
        os.mkdir(dst)
        for x in os.listdir(src):
            limitedCopyTree(os.path.join(src,x), os.path.join(dst,x), rmdir)
    else:
        shutil.copyfile(src, dst)


TMPDIR=os.path.abspath("packpanda-TMP")
TMPGAME=os.path.join(TMPDIR,"game")
TMPETC=os.path.join(TMPDIR,"etc")
print ""
print "Copying the game to "+TMPDIR+"..."
if (os.path.exists(TMPDIR)):
    try: shutil.rmtree(TMPDIR)
    except: sys.exit("Cannot delete "+TMPDIR)
try:
    os.mkdir(TMPDIR)
    rmdir = {}
    for x in OPTIONS["rmdir"]:
        rmdir[x] = 1
    limitedCopyTree(DIR, TMPGAME, rmdir)
    if not os.path.isdir( TMPGAME ):
        os.mkdir(TMPGAME)
    limitedCopyTree(os.path.join(PANDA, "etc"), TMPETC, {})
    if not os.path.isdir( TMPETC ):
        os.mkdir(TMPETC)
except: sys.exit("Cannot copy game to "+TMPDIR)

##############################################################################
#
# If --bam requested, change default-model-extension .egg to bam.
#
##############################################################################

def ReadFile(wfile):
    try:
        srchandle = open(wfile, "rb")
        data = srchandle.read()
        srchandle.close()
        return data
    except: exit("Cannot read "+wfile)

def WriteFile(wfile,data):
    try:
        dsthandle = open(wfile, "wb")
        dsthandle.write(data)
        dsthandle.close()
    except: exit("Cannot write "+wfile)

if OPTIONS["bam"]:
    CONF=ReadFile(os.path.join(TMPETC,"Confauto.prc"))
    CONF=CONF.replace("default-model-extension .egg","default-model-extension .bam")
    WriteFile(os.path.join(TMPETC,"Confauto.prc"), CONF)

##############################################################################
#
# Compile all py files, convert all egg files.
#
# We do this as a sanity check, even if the user
# hasn't requested that his files be compiled.
#
##############################################################################

EGG2BAM=os.path.join(PANDA,"bin","egg2bam.exe")

def egg2bam(file,bam):
    present = os.path.exists(bam)
    if (present): bam = "packpanda-TMP.bam";
    cmd = 'egg2bam -noabs -ps rel -pd . "'+file+'" -o "'+bam+'"'
    print "Executing: "+cmd
    res = os.spawnl(os.P_WAIT, EGG2BAM, cmd)
    if (res != 0): sys.exit("Problem in egg file: "+file)
    if (present) or (OPTIONS["bam"]==0):
        os.unlink(bam)

def py2pyc(file):
    print "Compiling python "+file
    pyc = file[:-3]+'.pyc'
    pyo = file[:-3]+'.pyo'
    if (os.path.exists(pyc)): os.unlink(pyc)
    if (os.path.exists(pyo)): os.unlink(pyo)
    try: py_compile.compile(file)
    except: sys.exit("Cannot compile "+file)
    if (OPTIONS["pyc"]==0):
        if (os.path.exists(pyc)):
            os.unlink(pyc)
        if (os.path.exists(pyo)):
            os.unlink(pyo)


# separated compileFiles to compileBam and compilePy
# made the compilation optional, only if --bam or --py defined
def CompileBamFiles(file):
    if (os.path.isfile(file)):
        if (file.endswith(".egg")):
            egg2bam(file, file[:-4]+'.bam')
        elif (file.endswith(".egg.pz")):
            egg2bam(file, file[:-7]+'.bam')
        else: pass
    elif (os.path.isdir(file)):
        for x in os.listdir(file):
            CompileBamFiles(os.path.join(file, x))

# separated compileFiles to compileBam and compilePy
# made the compilation optional, only if --bam or --py defined
def CompilePyFiles(file):
    if (os.path.isfile(file)):
        if (file.endswith(".py")):
            py2pyc(file)
        else: pass
    elif (os.path.isdir(file)):
        for x in os.listdir(file):
            CompilePyFiles(os.path.join(file, x))

def DeleteFiles(file):
    base = string.lower(os.path.basename(file))
    if (os.path.isdir(file)):
        for pattern in OPTIONS["rmdir"]:
            if (string.lower(pattern) == base):
                print "Deleting "+file
                shutil.rmtree(file)
                return
        for x in os.listdir(file):
            DeleteFiles(os.path.join(file, x))
    else:
        for ext in OPTIONS["rmext"]:
            if (base[-(len(ext)+1):] == string.lower("."+ext)):
                print "Deleting "+file
                os.unlink(file)
                return

print ""
print "Compiling BAM and PYC files..."
os.chdir(TMPGAME)
# separated compileFiles to compileBam and compilePy
# made the compilation optional, only if --bam or --py defined
if OPTIONS["pyc"]:
  CompilePyFiles(".")
if OPTIONS["bam"]:
  CompileBamFiles(".")
DeleteFiles(".")

##############################################################################
#
# Run NSIS. Yay!
#
##############################################################################

CMD=NSIS+"\\makensis.exe /V2 "
CMD=CMD+'/DCOMPRESSOR="'+COMPRESS+'" '
CMD=CMD+'/DNAME="'+NAME+'" '
CMD=CMD+'/DSMDIRECTORY="'+SMDIRECTORY+'" '
CMD=CMD+'/DINSTALLDIR="'+INSTALLDIR+'" '
CMD=CMD+'/DOUTFILE="'+OUTFILE+'" '
CMD=CMD+'/DLICENSE="'+LICENSE+'" '
CMD=CMD+'/DLANGUAGE="English" '
CMD=CMD+'/DRUNTEXT="Play '+NAME+'" '
CMD=CMD+'/DIBITMAP="'+BITMAP+'" '
CMD=CMD+'/DUBITMAP="'+BITMAP+'" '
CMD=CMD+'/DPANDA="'+PANDA+'" '
CMD=CMD+'/DPANDACONF="'+TMPETC+'" '
CMD=CMD+'/DPSOURCE="'+PSOURCE+'" '
CMD=CMD+'/DPPGAME="'+TMPGAME+'" '
CMD=CMD+'/DPPMAIN="'+MAIN+'" '
CMD=CMD+'/DPPICON="'+PPICON+'" '
CMD=CMD+'"'+PSOURCE+'\\direct\\src\\directscripts\\packpanda.nsi"'

print ""
print CMD
print "packing..."
os.system(CMD)

is the changes done? how far along is this? has this been merged into the tree?