;----------------------------------------------------
; SynAppsPortable
; ----------------------------------------------------
; Par fat115
; Basé sur une idée originale de John T. Haller
; License : GPL
; Ce script permet de créer le lanceur de SynAppsPortable.
; Compiler: AutoHotkey_L (http://www.autohotkey.net/~Lexikos/AutoHotkey_L/).
; $id=SynAppsPortable.ahk $date=2011-10-31
; ----------------------------------------------------
;Copyright © 2005-2011 Framakey

;Website: http://www.framakey.org

;This software is OSI Certified Open Source Software.
;OSI Certified is a certification mark of the Open Source Initiative.

;This program is free software; you can redistribute it and/or
;modify it under the terms of the GNU General Public License
;as published by the Free Software Foundation; either version 2
;of the License, or (at your option) any later version.

;This program is distributed in the hope that it will be useful,
;but WITHOUT ANY WARRANTY; without even the implied warranty of
;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
;GNU General Public License for more details.

;You should have received a copy of the GNU General Public License
;along with this program; if not, write to the Free Software
;Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA.
; ----------------------------------------------------

; -----------------------------------------------------------------------------
; --- Valeurs spécifiques à l'application => à modifier lors de la création ---
; -----------------------------------------------------------------------------
_DEFAULTEXE := "main.py"
_DEFAULTAPPDIR := "App\SynApps"
EnvGet, _PythonPortablePath, FramaPython
If _PythonPortablePath
	_DEFAULTPYTHONEXE := _PythonPortablePath "\PythonWPortable.exe"
Else
	_DEFAULTPYTHONEXE := A_ScriptDir . "\..\PythonPortable\PythonWPortable.exe"
_DEFAULTDATADIR := "Data\settings"
_LANG := "fr-FR"
; préparation de l'object contenant d'éventuelles variables d'environnement
o_EnvVar := Object()

; ----------------------------------------------
; --- Options : décommentez pour application ---
; ----------------------------------------------
; Maintenir un historique du dernier emplacement
; Utile pour faire des modifs dans les paramètres ou le registre
; Modifs dans les paramètres :
; 		sous-routines UpdateSettings_AppPath & UpdateSettings_DataPath
; Modifs dans le registre :
;		sous-routines UpdateRegFile_AppPath & UpdateRegFile_DataPath
; Ces 4 sous-routines sont situées en fin de script pour faciliter leur modification
;_HISTORY := True

; copie et sauvegarde/restauration automatique du dossier %APPDATA%\Nom_de_l_appli\.
;_APPDATABACKUP := True
; vous pouvez personnaliser le sous-dossier d'Application Data utilisé
;_APPDATASUBDIR := "%_APPNAME%"

; pour les applications basée sur Mozilla, il est nécessaire de mettre en place
; une boucle de vérification à cause d'une relance automatique
;_MOZLOOP := True

; met en place un flag pour création d'un lanceur à partir d'un
; media en lecture seule
;_READONLYL := true

; met en place un flag pour création d'un lanceur à partir d'un
; lecteur réseau avec sauvegarde du profil
;_NETWORKL := True

; copie et sauvegarde/restauration automatique de la clé HKEY_CURRENT_USER\Software\%_APPNAME%
;_REGBACKUP := True
; Le chemin est configurable ci-dessous.
;_REGPATH := "HKEY_CURRENT_USER\Software\%_APPNAME%"

; teste si JAVA est installé sur la machine
;_TESTJAVA := True

; définition d'éventuelles variables d'environnement
o_EnvVar.Insert("HOME", "%p_DataDirUnified%")
; il est possible d'utiliser des variables définies ultérieurement, elles seront évaluées avant le lancement de l'application
; Exemple, la variable GIMP2_DIRECTORY contient le chemin vers le dossier unifié des données s'écrit comme suit =>
; o_EnvVar.Insert("GIMP2_DIRECTORY", "%p_DataDirUnified%")

/*
----------------------------------------------------------------
		Définition du format de la chaine de lancement
------------------------------------------------------------------
Le define suivant vous permet de configurer la chaine d'exécution et donc, entre autres, l'ordre des paramètres et d'éventuels paramètres "obligatoires".
Pour info :
p_AppDirectory => le dossier de l'exécutable (cf _DEFAULTAPPDIR)
f_AppExecutable => le nom de l'exécutable (cf _DEFAULTEXE)
p_DataDirUnified => le dossier réellement utilisé pour les données
	Il peut être différent de p_DataDir pour une appli en lecture
	seule par exemple (_PLUGINSDIR\_FULLNAME)
s_Parameters => les paramètres passés au lanceur
s_AdditionalParameters => les paramètres du fichier INI
Exemple : la chaine de lancement du FramaKiosk =>
"""%p_AppDirectory%\%f_AppExecutable%"" -profilesDir "%p_DataDirUnified%" -webapp %s_Parameters% %s_AdditionalParameters% "%A_ScriptDir%\Data\FramaKiosk\index.html"`
----------------------------------------------------------------
*/
_EXECSTRING := """%f_PythonExecutable%"" ""%p_AppDirectory%\%f_AppExecutable%"" %s_Parameters% %s_AdditionalParameters%"


;-----------------------------------------------------------------------------------------------
;	Vous pouvez passer à la définition des sous-routines précitées si l'application le nécessite
;-----------------------------------------------------------------------------------------------


; ------------ Bibliothèques et paramètres généraux -----------
_SCRIPTVER := "2.0.1.0" ;Version du script du lanceur
#NoTrayIcon
#SingleInstance OFF
#Include %A_ScriptDir%
#Include Functions.ahk
#Include ini.ahk
#Include RegFunctions.ahk
#Include Notify.ahk
FileEncoding, UTF-8
SetWorkingDir %A_ScriptDir%
OnExit, QuitPortableApp

; --------- Récupération des noms application/lanceur ---------
_FULLNAME := SubStr(A_ScriptName,1,-4)
FileGetVersion, _VER, %_FULLNAME% ".exe"
_MUTEXNAME := _FULLNAME . "_" . _VER

_APPNAME := StringReplace(_FULLNAME, "Portable")
If (SubStr(_APPNAME, -1) = "_N")
	_APPNAME := StringTrimRight(_APPNAME, 2)
If (SubStr(_APPNAME, -2) = "_RO")
	_APPNAME := StringTrimRight(_APPNAME, 3)

; -------------- simulation du PLUGINSDIR de NSIS -------------
Process, Exist
i_PID := ErrorLevel
p_PluginsDir := A_Temp . "\" . _FULLNAME . "." . i_PID
FileCreateDir, %p_PluginsDir%

; ---------- chargement des traductions des messages ----------
FileInstall, Lang.ini, %p_PluginsDir%\lang.ini
Gosub LoadTranslation

;----------------------------------------------------
;					Test JAVA
;----------------------------------------------------
If _TESTJAVA
	Gosub SubTestJava

;----------------------------------------------------
;          Définition/Récupération des valeurs
;----------------------------------------------------

EnvGet, b_nosplash, NOSPLASH
; Récupération des paramètres passés au lanceur (par exemple par CAFE)
s_Parameters := ""
Loop, %0%
{
	_param := %A_Index%
	IfInString, _param, %A_Space%
		_param := """" . _param . """"
	s_Parameters .= (!s_Parameters) ? _param : " " . _param
}

; S'il existe un fichier INI, on utilise les valeurs sinon on passe sur des valeurs par défaut
ini_load(s_AppIniFile, A_ScriptDir . "\" . _FULLNAME . ".ini", "UTF-8")
b_DisableSplashScreen := ini_getValuewDefault(s_AppIniFile, _FULLNAME, "DisableSplashScreen", False)
p_AppDirectory := A_ScriptDir . "\" . ini_getValuewDefault(s_AppIniFile, _FULLNAME, "ApplicationDirectory", _DEFAULTAPPDIR)
f_AppExecutable := ini_getValuewDefault(s_AppIniFile, _FULLNAME, "ApplicationExecutable", _DEFAULTEXE)
f_PythonExecutable := ini_getValuewDefault(s_AppIniFile, _FULLNAME, "PythonExecutable", _DEFAULTPYTHONEXE)
p_DataDir := A_ScriptDir . "\" . ini_getValuewDefault(s_AppIniFile, _FULLNAME, "SettingsDirectory",  _DEFAULTDATADIR)
If _NETWORKL
	p_NetDataDir := ini_getValuewDefault(s_AppIniFile, _FULLNAME, "NetworkSettingsDirectory", "P:\Profile\" . _APPNAME)
s_CopyCustom := ini_getValuewDefault(s_AppIniFile, _FULLNAME, "CopyCustomProfile", True)
s_AdditionalParameters := ini_getValue(s_AppIniFile, _FULLNAME, "AdditionalParameters")
s_AppIniFile := ""


;-------------------------------------------------------
;Traitement conditionnel : définition de DataDirUnified
;-------------------------------------------------------
If _APPDATABACKUP
	{
	If _APPDATASUBDIR
		p_DataDirUnified := A_AppData . "\" . Dereference(_APPDATASUBDIR)
	Else
		p_DataDirUnified := A_AppData . "\" . _APPNAME
	}
Else If _READONLYL
	p_DataDirUnified := p_PluginsDir . "\settings"
Else If _NETWORKL
	p_DataDirUnified := p_NetDataDir
Else
	p_DataDirUnified := p_DataDir
	
;----------------------------------------------------
;				Vérifications
;----------------------------------------------------

; Si l'exécutable est introuvable
IfNotExist, %p_AppDirectory%\%f_AppExecutable%
	ErrMsg("NOEXEMSG")
IfNotExist, %f_PythonExecutable%
	ErrMsg("NOPYTHON")

;----------------------------------------------------
;Mise en place des variables d'environnement si besoin
;----------------------------------------------------
For _envvar, _value in o_EnvVar
	EnvSet, % _envvar, % Dereference(_value)

; Si la version portable est déjà lancée : on lance directement
If DllCall("OpenMutex", UInt, "0x00020000", Int, "0", Str, _MUTEXNAME)
	{
	Run, % Dereference(_EXECSTRING)
	ExitApp
	}

; Si la version locale est déjà lancée
Process, Exist, %_DEFAULTEXE%
If ErrorLevel
	ErrMsg("FOUNDPROCESSMSG")

; on teste s'il faut afficher le splashscreen
If !(b_DisableSplashScreen = "True" OR b_nosplash = "True")
	{
	SetTimer, UnLoadSplash, 500
	_splashid := Notify(_APPNAME, "Lancement en cours ...", "-0", "GT=192 TS=14", A_ScriptDir "\App\AppInfo\appicon_48.png")
	}

;----------------------------------------------------
;	Traitement conditionnel : maintien historique
;----------------------------------------------------

If _HISTORY
	{
	IniRead, s_LastDataDir, %p_DataDir%\PortableHistory.ini, History, LastDataDir
	If ((s_LastDataDir != p_DataDirUnified) && (s_LastDataDir != "ERROR"))
		b_UpDataPath := True
	IniRead, s_LastAppDir, %p_DataDir%\PortableHistory.ini, History, LastAppDir
	If ((s_LastAppDir != A_ScriptDir) && (s_LastDataDir != "ERROR"))
		b_UpAppPath := True
	SplitPath, A_ScriptDir, , , , , s_AppDrive
	SplitPath, s_LastAppDir, , , , , s_LastAppDrive
	}

;----------------------------------------------------
;	Traitement conditionnel : recopie de p_DataDir
;----------------------------------------------------
If _APPDATABACKUP
; Sauvegarde des préférences de l'application locale
	IfExist, %p_DataDirUnified%\*.*
		{
		FileMoveDir, %p_DataDirUnified%, %p_DataDirUnified%-BackupBy%_FULLNAME%
		If ErrorLevel
			ErrMsg("APPDATABK_ERR_MSG")
		}

; en cas d'utilisation réseau, on vérifie s'il faut copier les préférences par défaut dans le profil réseau
If _NETWORKL
	{
	If (s_CopyCustom && !FileExist(p_NetDataDir))
		FileCopyDir, %p_DataDir%, %p_NetDataDir%
	p_DataDir := p_NetDataDir
	}

If (_APPDATABACKUP || _READONLYL || _NETWORKL)
; Copie des préférences de l'application portable
	{
	If (s_CopyCustom && !FileExist(p_DataDirUnified))
		FileCopyDir, %p_DataDir%, %p_DataDirUnified%
	If ErrorLevel
		ErrMsg("CPYPREF_ERR_MSG")
	If (_READONLYL || _NETWORKL)
		FileDelete, %p_DataDirUnified%\PortableHistory.ini
	}

; inutile d'essayer d'appliquer des modifs sur qqch qui n'existe pas
If FileExist(p_DataDirUnified)
{
	If b_UpDataPath
		Gosub UpdateSettings_DataPath
	If b_UpAppPath
		Gosub UpdateSettings_AppPath
}

;----------------------------------------------------
;	Traitement conditionnel : sauvegarde de REGPATH
;----------------------------------------------------
If _REGBACKUP
	{
	If !_REGPATH
		_REGPATH := "HKEY_CURRENT_USER\Software\" . _APPNAME
	Else
		_REGPATH := Dereference(_REGPATH)
	; Sauvegarde des préférences de l'application locale
	RegMove(_REGPATH, _REGPATH . "-BackupBy" . _FULLNAME)
	; Mise en place des préférences de l'application locale
	IfExist, %p_DataDirUnified%\%_APPNAME%.ahkreg
		{
		FileRead, _reglist, %p_DataDirUnified%\%_APPNAME%.ahkreg
		If b_UpDataPath
			Gosub UpdateRegFile_DataPath
		If b_UpAppPath
			Gosub UpdateRegFile_AppPath
		_errors := VarToReg(_reglist)
		; traitement des erreurs
		If _errors
			ErrMsg("REG_WRST_ERR_MSG", _errors)
		}
	}

;----------------------------------------------------
;					Exécution
;----------------------------------------------------
; Première instance portable : on crée un mutex
DllCall("CreateMutex", Int, 0, Int, 0, Str, _MUTEXNAME)
; Lancement de l'application
SetWorkingDir %p_AppDirectory%
RunWait, % Dereference(_EXECSTRING), , , _pid

;----------------------------------------------------
;	Traitement conditionnel : boucle MOZILLA
;----------------------------------------------------
If _MOZLOOP
	{
	; on donne 0.5s au processus pour se relancer
	Sleep, 500
	Process, Exist, %f_AppExecutable%
	If ErrorLevel
		Process, WaitClose, %f_AppExecutable%
	}

;----------------------------------------------------
;	Traitement conditionnel : maintien historique
;----------------------------------------------------
If (_HISTORY && !_READONLYL)
	{
	IniWrite, %p_DataDirUnified%, %p_DataDirUnified%\PortableHistory.ini, History, LastDataDir
	IniWrite, %A_ScriptDir%, %p_DataDirUnified%\PortableHistory.ini, History, LastAppDir
	}

;----------------------------------------------------
;	Traitement conditionnel : Permutation du registre
;----------------------------------------------------
If _REGBACKUP
	{
	; export de la branche REGPATH
	If !_READONLYL
		RegToFile(_REGPATH, p_DataDirUnified . "\" . _APPNAME . ".ahkreg")
	; effacement systématique de la clé
	RegDeleteStd(_REGPATH)
	; restauration de la branche fixe
	RegMove(_REGPATH . "-BackupBy" . _FULLNAME, _REGPATH)
	}

;----------------------------------------------------
;Traitement conditionnel : permutation dans APPDATA
;----------------------------------------------------
If _APPDATABACKUP
	{
	; enregistrement des préférences portables
	If !_READONLYL
		{
		FileRemoveDir, p_DataDir
		FileMoveDir, %p_DataDirUnified%, %p_DataDir%, 1
		If ErrorLevel
			ErrMsg("CPYPREFBK_ERR_MSG")
		}
	; restauration des préférences fixes
	IfExist %p_DataDirUnified%-BackupBy%_FULLNAME%
		{
		FileMoveDir, %p_DataDirUnified%-BackupBy%_FULLNAME%, %p_DataDirUnified%, 1
		If ErrorLevel
			ErrMsg("APPDATART_ERR_MSG")
		}
	}

ExitApp

;===============================================================================
; Function Name:	Dereference
;===============================================================================
Dereference(_var)
{
	Transform, _value, Deref, % _var
Return _value
}

;===============================================================================
; Function Name:	ErrMsg
;===============================================================================
ErrMsg(_errname, _errnb = "", _exit=1)
{
	Global o_MsgTrans
	Global _FULLNAME

	_message := Dereference(o_MsgTrans[_errname])
	If _errnb
		_message := "Err:" . _errnb . "`n" . _message
	MsgBox, 48, %_FULLNAME%, %_message%
	If _exit
		ExitApp
	Else
		Return
}

;===============================================================================
; Function Name:	WinChildExist
;===============================================================================
WinChildExist(_parent_pid)
{
	_result := False
	for process in ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
		{
		If (process.ParentProcessId = _parent_pid)
			_result += WinExist("ahk_pid " . process.ProcessId)
		}
Return _result
}

;===============================================================================
; Function Name:	Path2WSAPI_URI
;===============================================================================
Path2WSAPI_URI(_path)
{
	_string := StringReplace(_path, "\", "/", "All")
	f = %A_FormatInteger%
	SetFormat, Integer, H
	pos = 1
	Loop
		If pos := RegExMatch(_string, "i)[^\/\w\.~`:%&=-]", char, pos++)
			StringReplace, _string, _string, %char%, % "%" . Asc(char), All
		Else Break
	SetFormat, Integer, %f%
	_result := "file:///" . StringReplace(_string, "0x", "", "All")
Return _result
}

;===============================================================================
; Function Name:	UpdateObjectInFile
;===============================================================================
UpdateObjectInFile(_file, _object, _codepage="CP1252", _newline="CRLF")
{
	FileRead, _content, %_file%
	For key, value in _object
		_content := RegExReplace(_content, "i)\b\Q" key "\E", value)
	FileDelete, %_file%
	If (_newline = "LF")
		{
		_content := StringReplace(_content, "`r", "", "All")
		_binmode := "*"
		}
	Else
		_binmode := ""
	FileAppend, %_content%, %_binmode%%_file%, %_codepage%
}

;===============================================================================
; 							Subroutines
;===============================================================================

QuitPortableApp:
{
	; On efface le dossier temporaire
	FileRemoveDir, %p_PluginsDir%, 1
ExitApp
Return
}

UnLoadSplash:
{
	If _pid
		{
		If (WinExist("ahk_pid " . _pid) || WinChildExist(_pid))
			{
			SetTimer, UnLoadSplash, Off
			Notify("","",-1,"Wait", _splashid)
			}
		}
Return
}

LoadTranslation:
{
ini_load(s_LangFile, p_PluginsDir . "\lang.ini", "UTF-8")
_allkeys := ini_getAllKeyNames(s_LangFile, _LANG)
o_MsgTrans := Object()
Loop, parse, _allkeys, `,
	{
	o_MsgTrans[A_LoopField] := ini_getValue(s_LangFile, _LANG, A_LoopField)
	}
Return
}

SubTestJava:
{
	Loop, HKLM, SOFTWARE\JavaSoft\Java Runtime Environment, 1, 1
		{
		If (A_LoopRegName = "CurrentVersion")
			RegRead, _javaversion
		IfInString, A_LoopRegName, FamilyVersion
			RegRead, _javacompleteversion
		If (A_LoopRegName = "JavaHome")
			{
			RegRead, _javahome
			IfNotExist, %_javahome%\bin\javaw.exe
				_javahome := False
			}
		}
	If !_javahome
		ErrMsg("NOJAVAMSG")
Return
}

;===============================================================================
; 						Modifications à apporter
;===============================================================================
; modifications à apporter au fichier registre suivant l'emplacement du "profil"
UpdateRegFile_DataPath:
{
	
Return
}

; modifications à apporter au fichier registre suivant l'emplacement de l'application
UpdateRegFile_AppPath:
{
	
Return
}

; modifications à apporter au "profil" suivant son emplacement
UpdateSettings_DataPath:
{
	
Return
}

; modifications à apporter au "profil" suivant l'emplacement de l'application
UpdateSettings_AppPath:
{
	
Return
}



