# This script updates (if this script is run from a folder where Echosync is already present) or downloads and extracts Echosync in the current folder
# Version 2.12 (2025-04-04)
# Copyright Sylvain Rougeaux, 2018-2025, www.luminescence-software.org

param([string] $scriptFolder = '?')
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'

$scriptBlock = 
{
   param([string] $echosyncFolder)

   $DownloadingEchosync = 'Downloading Echosync from the Luminescence Software website...'
   $ScriptRootNotIdentified = 'The folder in which Echosync should be installed or updated could not be identified.'
   $WaitingForEchosyncClosing = 'Waiting for Echosync closing...'
   $EchosyncClosed = 'Echosync has been closed.'
   $EchosyncDownloaded = 'Echosync has been successfully downloaded.'
   $InvalidZipFile = "The Echosync zip archive is not valid because it doesn't contain any root folder."
   $EchosyncUpdating = 'Echosync is ready to be updated.'
   $EchosyncExtracting = 'Extracting data from the Echosync zip archive...'
   $EchosyncUpdated = "Echosync has been successfully updated in '{0}'."
   $PreviousInstallationLocation = "Previous installation has been kept in '{0}'. You can delete this folder if you are satisfied with the latest version."
   $EchosyncInstalling = 'Echosync is ready to be installed.'
   $EchosyncFolderAlreadyExists = "The Echosync zip archive can not be extracted in '{0}' because a folder named '{1}' is already there."
   $EchosyncInstalled = "Echosync has been successfully installed in '{0}'."
   $FolderAccessDenied = 'Make sure you have closed all processes using this path, and have sufficient rights to write to this path, before trying again.'
   $AbortRetryQuestion = "Press 'A' to abort or any other key to retry"
   $FileHashMismatch = "The Echosync zip archive is not valid because its hash ({0}) doesn't match the expected one ({1})."

   $culture = (Get-UICulture).TwoLetterISOLanguageName
   if ($culture -eq 'fr')
   {
      $DownloadingEchosync = "Téléchargement d'Echosync depuis le site web Luminescence Software..."
      $ScriptRootNotIdentified = "Le répertoire dans lequel Echosync doit être installé ou mis à jour n'a pas pu être identifié."
      $WaitingForEchosyncClosing = "En attente de la fermeture d'Echosync..."
      $EchosyncClosed = 'Echosync a été fermé.'
      $EchosyncDownloaded = 'Echosync a été téléchargé avec succès.'
      $InvalidZipFile = "L'archive zip d'Echosync n'est pas valide car elle ne contient pas de répertoire racine."
      $EchosyncUpdating = 'Echosync est prêt à être mis à jour.'
      $EchosyncExtracting = "Décompression des données de l'archive zip d'Echosync..."
      $EchosyncUpdated = "Echosync a été mis à jour avec succès dans '{0}'."
      $PreviousInstallationLocation = "L'installation précédente a été conservée dans '{0}'. Vous pourrez supprimer ce répertoire si vous êtes satisfait de la dernière version."
      $EchosyncInstalling = 'Echosync est prêt à être installé.'
      $EchosyncFolderAlreadyExists = "L'archive zip d'Echosync ne peut être extraite dans '{0}' car un dossier nommé '{1}' y est déjà présent."
      $EchosyncInstalled = "Echosync a été installé avec succès dans '{0}'."
      $FolderAccessDenied = "Assurez-vous d'avoir fermé tous les processus utilisant ce chemin, et de disposer des droits suffisants pour écrire dans ce chemin, avant de réessayer."
      $AbortRetryQuestion = "Appuyez sur 'A' pour abandonner ou toute autre touche pour réessayer"
      $FileHashMismatch = "L'archive zip d'Echosync n'est pas valide car son hash ({0}) ne correspond pas à celui attendu ({1})."
   }
   elseif ($culture -eq 'it')
   {
      # Italian translations provided by Fabio Biocchetti
      $DownloadingEchosync = 'Download di Echosync dal sito di Luminescence Software in corso...'
      $ScriptRootNotIdentified = 'La cartella di installazione di Echosync non può essere trovata.'
      $WaitingForEchosyncClosing = 'Chiusura di Echosync in corso...'
      $EchosyncClosed = '	Echosync è stato chiuso.'
      $EchosyncDownloaded = 'Echosync è stato scaricato con successo.'
      $InvalidZipFile = "L'archivio zip di Echosync non è valido perché non contiene alcuna cartella di root."
      $EchosyncUpdating = 'Echosync è pronto per essere aggiornato.'
      $EchosyncExtracting = "Estrazione dei dati dall'archivio zip di Echosync..."
      $EchosyncUpdated = "Echosync è stato aggiornato con successo in '{0}'."
      $PreviousInstallationLocation = "Una precedente installazione è stata conservata in '{0}'. Puoi eliminare questa cartella se ti stai trovando bene con l'ultima versione."
      $EchosyncInstalling = 'Echosync è pronto per essere installato.'
      $EchosyncFolderAlreadyExists = "L'archivio zip di Echosync non può essere estratto in '{0}' perché una cartella chiamata '{1}' già esiste in quel percorso."
      $EchosyncInstalled = "Echosync è stato installato con successo in '{0}'."
      $FolderAccessDenied = 'Assicurati di aver chiuso tutti i processi che usano questo percorso, e di avere sufficienti diritti di scrittura, prima di riprovare.'
      $AbortRetryQuestion = "Premi 'A' per annullare o qualsiasi altro tasto per riprovare"
      $FileHashMismatch = "L'archivio zip Echosync non è valido perché il suo hash ({0}) non corrisponde a quello atteso ({1})."
   }
   elseif ($culture -eq 'es')
   {
      # Spanish translations provided by Javier Tapia Torres
      $DownloadingEchosync = 'Descargando Echosync desde el sitio web de Luminescence Software...'
      $ScriptRootNotIdentified = 'La carpeta en la cual Echosync debería ser instalado o actualizado puede no estar identificada.'
      $WaitingForEchosyncClosing = 'Esperando que Echosync se cierre...'
      $EchosyncClosed = 'Echosync se ha cerrado.'
      $EchosyncDownloaded = 'Echosync ha sido descargado correctamente.'
      $InvalidZipFile = 'El archivo zip de Echosync no es válido porque no contiene ninguna carpeta raíz.'
      $EchosyncUpdating = 'Echosync está listo para ser actualizado.'
      $EchosyncExtracting = 'Extrayendo datos desde el archivo zip de Echosync...'
      $EchosyncUpdated = "Echosync ha sido actualizado con éxito en '{0}'."
      $PreviousInstallationLocation = "La instalación anterior se ha mantenido en '{0}'. Puede eliminar esta carpeta si está satisfecho con la última versión."
      $EchosyncInstalling = 'Echosync está listo para ser instalado.'
      $EchosyncFolderAlreadyExists = "El archivo zip de Echosync no se puede extraer en '{0}' porque ya hay una carpeta llamada '{1}'."
      $EchosyncInstalled = "Echosync se ha instalado con éxito en '{0}'."
      $FolderAccessDenied = 'Asegúrese de haber cerrado todos los procesos que utilizan esta ruta y de tener derechos insuficientes para escribir en esta ruta, antes de intentarlo de nuevo.'
      $AbortRetryQuestion = "Pulse 'A' para anular o cualquier otra tecla para reintentar"
      $FileHashMismatch = 'El archivo zip de Echosync no es válido porque su hash ({0}) no coincide con el esperado ({1}).'
   }
   elseif ($culture -eq 'de')
   {
      # German translations provided by Roger Taouss-Schirmer
      $DownloadingEchosync = 'Echosync wird von Luminescence Software heruntergeladen...'
      $ScriptRootNotIdentified = 'Der Installationsordner für Echosync wurde nicht gefunden.'
      $WaitingForEchosyncClosing = 'Warten auf Beenden von Echosync...'
      $EchosyncClosed = 'Echosync wurde beendet.'
      $EchosyncDownloaded = 'Echosync wurde erfolgreich heruntergeladen.'
      $InvalidZipFile = 'Das Echosync zip Archiv ist ungültig, da es keinen Quellordner enthält.'
      $EchosyncUpdating = 'Echosync kann upgedated werden.'
      $EchosyncExtracting = 'Extraktion des Echosync zip Archivs...'
      $EchosyncUpdated = "Echosync wurde in '{0}' erfolgreich upgedated."
      $PreviousInstallationLocation = "Die vorherige Installation wurde in '{0}' belassen. Sie können diesen Ordner löschen, wenn Sie mit der neuesten Version zufrieden sind."
      $EchosyncInstalling = 'Echosync kann nun installiert werden.'
      $EchosyncFolderAlreadyExists = "Echosync kann nicht nach '{0}' extrahiert werden, da dort bereits ein Ordner '{1}' existiert."
      $EchosyncInstalled = "Echosync wurde erfolgreich in '{0}' installiert."
      $FolderAccessDenied = 'Verlassen Sie alle Prozesse, die diesen Weg benutzen und vergewissern Sie sich, dass Sie autorisiert sind diesen Weg zu benutzen bevor Sie es erneut versuchen.'
      $AbortRetryQuestion = "Geben Sie 'A' ein um abzubrechen oder drücken Sie irgendeine andere Taste um erneut zu versuchen"
      $FileHashMismatch = 'Das Echosync zip-Archiv ist ungültig da die digitale Signatur {0}  nicht mit der erwarteten Signatur {1} übereinstimmt.'
   }
   elseif ($culture -eq 'ru')
   {
      # Russian translations provided by Jyrkka Teterev
      $DownloadingEchosync = 'Скачивание Echosync с веб-сайта Luminescence Software...'
      $ScriptRootNotIdentified = 'Папка, в которой Echosync должен быть установлен или обновлён, не может быть идентифицирована.'
      $WaitingForEchosyncClosing = 'Ожидание закрытия Echosync...'
      $EchosyncClosed = 'Echosync был закрыт.'
      $EchosyncDownloaded = 'Echosync был успешно скачан.'
      $InvalidZipFile = 'ZIP-архив Echosync недействителен, поскольку в нём нет корневой папки.'
      $EchosyncUpdating = 'Echosync готов к обновлению.'
      $EchosyncExtracting = 'Извлечение данных из ZIP-архива Echosync...'
      $EchosyncUpdated = "Echosync был успешно обнавлён в '{0}'."
      $PreviousInstallationLocation = "Предыдущая установка была сохранена в '{0}'. Вы можете удалить эту папку, если вас устраивает последняя версия."
      $EchosyncInstalling = 'Echosync готов к установке.'
      $EchosyncFolderAlreadyExists = "Не удалось распаковать ZIP-архив Echosync в '{0}', поскольку папка с именем '{1}' уже существует."
      $EchosyncInstalled = "Echosync был успешно установлен в '{0}'."
      $FolderAccessDenied = 'Перед повторной попыткой убедитесь, что вы закрыли все процессы, использующие этот путь, и имеете достаточные права для записи по этому пути.'
      $AbortRetryQuestion = "Нажмите 'A' для отмены или любую другую клавишу для повторной попытки"
      $FileHashMismatch = 'Архив Echosync, вероятно, повреждён, поскольку его контрольная сумма({0}) не совпадает с ожидаемой({1}).'
   }
   elseif ($culture -eq 'zh')
   {
      # Chinese translations provided by 王晨旭
      $DownloadingEchosync = '从 Luminescence 网站下载 Echosync...'
      $ScriptRootNotIdentified = '无法确定应在其中安装或更新 Echosync 的文件夹。'
      $WaitingForEchosyncClosing = '等待 Echosync 关闭...'
      $EchosyncClosed = 'Echosync 已关闭。'
      $EchosyncDownloaded = 'Echosync 下载已成功。'
      $InvalidZipFile = 'Echosync zip 存档无效，因为它不包含任何根文件夹。'
      $EchosyncUpdating = 'Echosync 已准备好进行更新。'
      $EchosyncExtracting = '从 Echosync zip 存档中提取数据...'
      $EchosyncUpdated = "Echosync 已成功在 '{0}' 中更新。"
      $PreviousInstallationLocation = "以前的安装已保存在 '{0}' 中。 如果您对最新版本满意，可以删除此文件夹。"
      $EchosyncInstalling = 'Echosync 已准备好安装。'
      $EchosyncFolderAlreadyExists = "无法在 '{0}' 中提取 Echosync zip 存档，因为已经存在名为 '{1}' 的文件夹。"
      $EchosyncInstalled = "Echosync 已成功安装在 '{0}' 中。"
      $FolderAccessDenied = '在重试之前，请确保已关闭使用此路径的所有进程，并具有足够的权限来写入此路径。'
      $AbortRetryQuestion = "按 'A' 放弃或按其他任何键重试"
      $FileHashMismatch = 'Echosync zip 存档无效，因为其哈希 ({0}) 与预期的 ({1}) 不匹配。'
   }
   elseif ($culture -eq 'nl')
   {
      # Dutch translations provided by Jeroen Westera
      $DownloadingEchosync = 'Download Echosync van de Luminescence Software website...'
      $ScriptRootNotIdentified = 'De map waarin Echosync moet worden geïnstalleerd of moet worden bijgewerkt kan niet gevonden worden.'
      $WaitingForEchosyncClosing = 'Bezig met wachten op het sluiten van Echosync...'
      $EchosyncClosed = 'Echosync is afgesloten.'
      $EchosyncDownloaded = 'Echosync is succesvol gedownload.'
      $InvalidZipFile = 'Het zip-archief van Echosync is niet geldig omdat het geen hoofdmap bevat.'
      $EchosyncUpdating = 'Echosync is klaar om te worden bijgewerkt.'
      $EchosyncExtracting = 'Gegevens uit het Echosync zip-archief halen...'
      $EchosyncUpdated = "Echosync is succesvol bijgewerkt in '{0}'."
      $PreviousInstallationLocation = "De vorige installatie is in de map '{0}' bewaard. U kunt deze map verwijderen als u tevreden bent met de nieuwste versie."
      $EchosyncInstalling = 'Echosync is klaar om te worden geïnstalleerd.'
      $EchosyncFolderAlreadyExists = "Het zip-archief van Echosync kan niet worden uitgepakt in '{0}' omdat er al een map met de naam '{1}' is."
      $EchosyncInstalled = "Echosync is succesvol geïnstalleerd in '{0}'."
      $FolderAccessDenied = 'Zorg ervoor dat u alle processen die dit pad gebruiken, hebt gesloten en voldoende rechten hebt om naar dit pad te schrijven, voordat u het opnieuw probeert.'
      $AbortRetryQuestion = "Druk op 'A' om af te breken of een andere toets om het opnieuw te proberen"
      $FileHashMismatch = 'Het zip-archief van Echosync is niet geldig omdat de hash ({0}) niet overeenkomt met de verwachte ({1}).'
   }

   if (-not (Test-Path -Path $echosyncFolder -PathType Container))
   {
      Write-Host $ScriptRootNotIdentified -ForegroundColor Red
      exit
   }

   Add-Type -Assembly 'System.IO.Compression.FileSystem'
   Function GetUniqueRootFolder([string] $zipPath)
   {
      $zip = [IO.Compression.ZipFile]::OpenRead($zipPath)
      try
      {
         if ($zip.Entries.Count -eq 0) { return $null }

         $index = $zip.Entries[0].FullName.IndexOf('/')
         if ($index -eq -1) { return $null }

         $firstEntry = $zip.Entries[0].FullName.Substring(0, $index + 1)
         foreach ($entry in $zip.Entries)
         {
            if (-not $entry.FullName.StartsWith($firstEntry)) { return $null }
         }

         return $firstEntry.TrimEnd('/')
      }
      finally 
      {
         $zip.Dispose()
      }
   }

   Function WaitForEchosyncToExit()
   {
      $echosyncProcess = Get-Process -Name 'Echosync' -ErrorAction SilentlyContinue
      $echocliProcess = Get-Process -Name 'Echocli' -ErrorAction SilentlyContinue
      if ($null -ne $echosyncProcess -or $null -ne $echocliProcess)
      {
         Write-Host $WaitingForEchosyncClosing -ForegroundColor Yellow
         if ($null -ne $echosyncProcess)
         {
            $echosyncProcess | ForEach-Object { $_.CloseMainWindow() | Out-Null }
            Wait-Process -InputObject $echosyncProcess
         }
         if ($null -ne $echocliProcess)
         {
            Wait-Process -InputObject $echocliProcess
         }
         Write-Host $EchosyncClosed
      }
   }

   Function TryRenameFolder([string] $path, [string] $newPath)
   {
      while ($true)
      {
         try
         {
            Rename-Item -Path $path -NewName $newPath -Force
            return $true
         }
         catch
         {
            Write-Host $_.Exception.Message -ForegroundColor Red
            Write-Host $FolderAccessDenied -ForegroundColor Yellow

            $response = Read-Host -Prompt $AbortRetryQuestion
            if ($response -eq 'a') { return $false }
         }
      }
   }

   Function TryExtractZip([string] $zipPath, [string] $destinationPath)
   {
      try
      {
         [IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $destinationPath)
         return $true
      }
      catch
      {
         Write-Host $_.Exception.GetBaseException().Message -ForegroundColor Red
         return $false
      }
   }

   Function Quit()
   {
      Set-Location -Path $echosyncFolder
      exit
   }

   # -UseBasicParsing parameter of Invoke-WebRequest command is required if the script is run with PowerShell < 6.0
   Write-Host $DownloadingEchosync
   $api = 'https://api.luminescence-software.org/download.php?software=echosync&installer=portable'
   if ($PSVersionTable.PSVersion.Major -ge 7)
   {
      $url = (Invoke-WebRequest -Uri $api -MaximumRedirection 0 -Method Head -ErrorAction Ignore -SkipHttpErrorCheck).Headers.Location
   }
   else
   {
      $url = (Invoke-WebRequest -Uri $api -MaximumRedirection 0 -Method Head -ErrorAction Ignore -UseBasicParsing).Headers.Location
   }

   $installerZip = Join-Path -Path $env:TEMP -ChildPath 'Echosync-Portable.exe'
   Invoke-WebRequest -Uri $url -OutFile $installerZip -UseBasicParsing
   Write-Host $EchosyncDownloaded

   $algo = 'sha256'
   $hash = (Get-FileHash -Algorithm $algo -Path $installerZip).Hash
   $expected_hash = [System.Text.Encoding]::UTF8.GetString((Invoke-WebRequest -Uri "$url.$algo" -UseBasicParsing).Content)
   if ($hash -ne $expected_hash)
   {
      Write-Host ($FileHashMismatch -f $hash, $expected_hash) -ForegroundColor Red
      Quit
   }

   $rootFolder = GetUniqueRootFolder $installerZip
   if ($null -eq $rootFolder)
   {
      Write-Host $InvalidZipFile -ForegroundColor Red
      Quit
   }

   $echosyncExe = Join-Path -Path $echosyncFolder -ChildPath 'Echosync.exe'
   if (Test-Path -Path $echosyncExe -PathType Leaf)
   {
      Write-Host $EchosyncUpdating

      $oldProfilesPath = Join-Path -Path $echosyncFolder -ChildPath 'Profiles'
      $hasProfiles = Test-Path -Path $oldProfilesPath -PathType Container
      if ($hasProfiles) { WaitForEchosyncToExit }

      $echosyncParentFolder = Split-Path $echosyncFolder -Parent
      $newEchosyncFolder = Join-Path -Path $echosyncParentFolder -ChildPath $rootFolder
      $oldEchosyncFolder = $echosyncFolder

      if (Test-Path -Path $newEchosyncFolder -PathType Container)
      {
         $oldEchosyncFolder = "${newEchosyncFolder}_old_$((Get-Date).Ticks)"
         if ((TryRenameFolder $newEchosyncFolder $oldEchosyncFolder) -ne $true) { Quit }
         $oldProfilesPath = Join-Path -Path $oldEchosyncFolder -ChildPath 'Profiles'
      }

      Write-Host $EchosyncExtracting
      if ((TryExtractZip $installerZip $echosyncParentFolder) -ne $true) { Quit }

      if ($hasProfiles) { Copy-Item -Path $oldProfilesPath -Recurse -Destination $newEchosyncFolder -Container }

      Write-Host ($EchosyncUpdated -f $newEchosyncFolder) -ForegroundColor Green
      Write-Host ($PreviousInstallationLocation -f $oldEchosyncFolder)
   }
   else 
   {
      Write-Host $EchosyncInstalling

      $newEchosyncFolder = Join-Path -Path $echosyncFolder -ChildPath $rootFolder
      if (Test-Path -Path $newEchosyncFolder -PathType Container)
      {
         Write-Host ($EchosyncFolderAlreadyExists -f $echosyncFolder, $rootFolder) -ForegroundColor Red
         Quit
      }

      Write-Host $EchosyncExtracting
      if ((TryExtractZip $installerZip $echosyncFolder) -ne $true) { Quit }

      Write-Host ($EchosyncInstalled -f $newEchosyncFolder) -ForegroundColor Green
   }

   Remove-Item $installerZip -ErrorAction SilentlyContinue

   Set-Location -Path $newEchosyncFolder
   Start-Process -FilePath $newEchosyncFolder
}

# PowerShell v4 is required for Get-FileHash command
if (!(Test-Path variable:PSVersionTable) -or $PSVersionTable.PSVersion.Major -lt 4)
{
   Write-Host 'This script requires PowerShell v4 or later.' -ForegroundColor Red
   Read-Host -Prompt 'Press Enter to continue...' # PowerShell v3 is required for Pause command
   exit
}

$programFilesPath = [Environment]::GetFolderPath([Environment+SpecialFolder]::ProgramFiles)
if ($PSScriptRoot.StartsWith($programFilesPath) -eq $true)
{
   Write-Host 'This script can only install or update portable version of Echosync. It cannot be run from the Program Files folder.' -ForegroundColor Red
   Pause
   exit
}

$tempPath = [IO.Path]::GetTempPath().TrimEnd('\')
if ($PSScriptRoot.StartsWith($tempPath) -eq $false)
{
   Set-Location -Path 'C:\'
   Copy-Item -Path $PSCommandPath -Destination $tempPath -Force
   $scriptPath = Join-Path -Path $tempPath -ChildPath (Split-Path $PSCommandPath -Leaf)
   $powershell = 'pwsh.exe'
   if ($PSVersionTable.PSVersion.Major -lt 6) { $powershell = 'powershell.exe' }
   Start-Process -FilePath $powershell -ArgumentList "-NoExit -NoLogo -ExecutionPolicy Bypass -File `"$scriptPath`" `"$PSScriptRoot`""
   [Environment]::Exit(0)
}

Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $scriptFolder
