@echo off chcp 65001 >nul setlocal title Mise a jour Blablaland set "BBL_UPDATE_BOOTSTRAP=%~f0" powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$file=$env:BBL_UPDATE_BOOTSTRAP; $text=[IO.File]::ReadAllText($file,[Text.Encoding]::UTF8); $marker=':'+ '__BBL_UPDATE_PAYLOAD__'; $index=$text.IndexOf($marker); if($index -lt 0){throw 'Programme de mise a jour incomplet.'}; $code=$text.Substring($index+$marker.Length); & ([ScriptBlock]::Create($code)) -BootstrapPath $file" set "BBL_UPDATE_EXIT=%ERRORLEVEL%" set "BBL_UPDATE_BOOTSTRAP=" if not "%BBL_UPDATE_EXIT%"=="0" ( echo. echo La mise a jour a echoue. Aucun fichier personnel n'a ete supprime. pause ) exit /b %BBL_UPDATE_EXIT% :__BBL_UPDATE_PAYLOAD__ param([string]$BootstrapPath) $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' $manifestUrl = 'https://wolfqc-bbl.duckdns.org/downloads/update/manifest.json' # L'applet de hachage récente n'existe pas sur certains PowerShell Windows # encore utilisés par les joueurs. Le calcul .NET fonctionne sur ces versions et garde # exactement le même contrôle SHA-256 avant toute écriture dans l'installation. function Get-Sha256Hex { param([Parameter(Mandatory=$true)][string]$Path) $algorithm = [Security.Cryptography.SHA256]::Create() $stream = [IO.File]::OpenRead($Path) try { return ([BitConverter]::ToString($algorithm.ComputeHash($stream))).Replace('-','') } finally { $stream.Dispose() $algorithm.Dispose() } } Write-Host '' Write-Host 'Mise a jour differentielle de Blablaland' -ForegroundColor Cyan Write-Host 'Recherche de l installation du launcher...' $candidates = New-Object System.Collections.Generic.List[string] $scriptDirectory = Split-Path -Parent $BootstrapPath $candidates.Add((Join-Path $scriptDirectory 'resources\app.asar')) $candidates.Add((Join-Path $env:LOCALAPPDATA 'Blablaland\Blablaland Desktop\resources\app.asar')) $candidates.Add((Join-Path $env:LOCALAPPDATA 'Programs\Blablaland\resources\app.asar')) $candidates.Add((Join-Path $env:LOCALAPPDATA 'Programs\blablaland-desktop\resources\app.asar')) $candidates.Add((Join-Path $env:LOCALAPPDATA 'Programs\Blablaland Desktop\resources\app.asar')) $uninstallRoots = @('HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*','HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*') foreach ($application in (Get-ItemProperty $uninstallRoots -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like 'Blablaland Desktop*' })) { if ($application.InstallLocation) { $candidates.Add((Join-Path ([string]$application.InstallLocation) 'resources\app.asar')) continue } $uninstallCommand = [string]$application.UninstallString if ($uninstallCommand) { $quote = [char]34 $uninstallFile = $uninstallCommand.Trim().TrimStart($quote).Split($quote)[0] $candidates.Add((Join-Path (Split-Path -Parent $uninstallFile) 'resources\app.asar')) } } $appAsar = $candidates | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1 if (-not $appAsar) { Add-Type -AssemblyName System.Windows.Forms $dialog = New-Object System.Windows.Forms.FolderBrowserDialog $dialog.Description = 'Choisis le dossier ou Blablaland Desktop est installe (pas le fichier portable EXE).' if ($dialog.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { throw 'Mise a jour annulee.' } $selected = Join-Path $dialog.SelectedPath 'resources\app.asar' if (-not (Test-Path -LiteralPath $selected -PathType Leaf)) { throw 'Ce dossier ne contient pas une installation modifiable de Blablaland Desktop.' } $appAsar = $selected } $resourcesDirectory = Split-Path -Parent $appAsar $installDirectory = Split-Path -Parent $resourcesDirectory $launcher = Get-ChildItem -LiteralPath $installDirectory -Filter '*.exe' -File | Where-Object { $_.Name -like 'Blablaland Desktop*.exe' } | Select-Object -First 1 if (-not $launcher) { throw 'Executable Blablaland Desktop introuvable.' } Get-Process -ErrorAction SilentlyContinue | ForEach-Object { try { if ($_.Path -and ([IO.Path]::GetFullPath($_.Path) -eq [IO.Path]::GetFullPath($launcher.FullName))) { Write-Host 'Fermeture du launcher en cours...' $_ | Stop-Process -Force $_ | Wait-Process -Timeout 20 -ErrorAction SilentlyContinue } } catch {} } Write-Host 'Lecture du manifeste securise HTTPS...' $manifest = Invoke-RestMethod -Uri $manifestUrl -TimeoutSec 30 if ([int]$manifest.schemaVersion -ne 2 -or -not $manifest.files -or $manifest.files.Count -gt 5000) { throw 'Le manifeste de mise a jour est invalide.' } try { $manifestVersion = [Version]([string]$manifest.version) } catch { throw 'La version de mise a jour est invalide.' } if ($manifestVersion -lt [Version]'1.6.5') { throw 'La mise a jour 1.6.5 est en cours de preparation. Telecharge a nouveau ce fichier depuis le site dans quelques instants.' } $manifestUri = [Uri]$manifestUrl $installRoot = [IO.Path]::GetFullPath($installDirectory) $installPrefix = $installRoot.TrimEnd('\') + '\' $pending = New-Object System.Collections.Generic.List[object] $totalBytes = [long]0 $index = 0 foreach ($entry in $manifest.files) { $index++ $relative = [string]$entry.path $leaf = (($relative -split '/')[-1]).ToLowerInvariant() $isProtectedStatePath = $leaf -eq 'database.json' -or $leaf -match '\.(?:db|sqlite|sqlite3)(?:[-.](?:wal|shm|journal))?$' if ($isProtectedStatePath) { throw 'Une entree du manifeste contient des donnees de compte ou un chemin interdit.' } if ($relative -notmatch '^resources/' -or $relative.Contains('\') -or $relative -match '(^|/)\.\.?(?:/|$)' -or [string]$entry.sha256 -notmatch '^[a-fA-F0-9]{64}$' -or [long]$entry.bytes -le 0 -or [long]$entry.bytes -gt 250MB) { throw 'Une entree du manifeste est invalide.' } $destination = [IO.Path]::GetFullPath((Join-Path $installDirectory ($relative -replace '/', '\'))) if (-not $destination.StartsWith($installPrefix,[StringComparison]::OrdinalIgnoreCase)) { throw 'Destination de mise a jour refusee.' } $isCurrent = $false if ((Test-Path -LiteralPath $destination -PathType Leaf) -and (Get-Item -LiteralPath $destination).Length -eq [long]$entry.bytes) { $isCurrent = (Get-Sha256Hex -Path $destination) -eq ([string]$entry.sha256).ToUpperInvariant() } if (-not $isCurrent) { $downloadUrl = [Uri]::new($manifestUri,[string]$entry.url) if ($downloadUrl.Scheme -ne 'https' -or $downloadUrl.Host -ne $manifestUri.Host) { throw 'Adresse de telechargement refusee.' } $totalBytes += [long]$entry.bytes $pending.Add([pscustomobject]@{ entry=$entry; destination=$destination; url=$downloadUrl }) } if ($index % 25 -eq 0) { Write-Host ("Verification : {0}/{1} fichiers" -f $index,$manifest.files.Count) } } if ($totalBytes -gt 1GB) { throw 'Mise a jour trop volumineuse.' } if ($pending.Count -eq 0) { Write-Host 'Le launcher et les fichiers du jeu sont deja a jour.' -ForegroundColor Green if ($env:BBL_UPDATE_SKIP_RESTART -ne '1') { Start-Process -FilePath $launcher.FullName } exit 0 } $temporaryDirectory = Join-Path $env:TEMP 'Blablaland-Update' New-Item -ItemType Directory -Path $temporaryDirectory -Force | Out-Null $downloaded = [long]0 $staged = New-Object System.Collections.Generic.List[object] foreach ($item in $pending) { $payload = Join-Path $temporaryDirectory (([string]$item.entry.sha256) + '.payload') $validPayload = (Test-Path -LiteralPath $payload -PathType Leaf) -and (Get-Item -LiteralPath $payload).Length -eq [long]$item.entry.bytes if ($validPayload) { $validPayload = (Get-Sha256Hex -Path $payload) -eq ([string]$item.entry.sha256).ToUpperInvariant() } if (-not $validPayload) { Remove-Item -LiteralPath $payload -Force -ErrorAction SilentlyContinue $percent = if ($totalBytes -gt 0) { [Math]::Floor(($downloaded / $totalBytes) * 100) } else { 0 } Write-Host ("[{0,3}%] Telechargement : {1}" -f $percent,[string]$item.entry.path) Invoke-WebRequest -UseBasicParsing -Uri $item.url.AbsoluteUri -OutFile $payload -TimeoutSec 240 if ((Get-Item -LiteralPath $payload).Length -ne [long]$item.entry.bytes) { throw 'Taille du fichier telecharge invalide.' } if ((Get-Sha256Hex -Path $payload) -ne ([string]$item.entry.sha256).ToUpperInvariant()) { throw 'Empreinte SHA-256 invalide.' } } $downloaded += [long]$item.entry.bytes $staged.Add([pscustomobject]@{ source=$payload; destination=$item.destination }) } Write-Host 'Installation atomique des fichiers verifies...' $completed = New-Object System.Collections.Generic.List[object] try { foreach ($item in $staged) { New-Item -ItemType Directory -Path (Split-Path -Parent $item.destination) -Force | Out-Null $backup = ([string]$item.destination) + '.previous' $hadDestination = Test-Path -LiteralPath $item.destination $item | Add-Member -NotePropertyName hadDestination -NotePropertyValue $hadDestination -Force if ($hadDestination) { Copy-Item -LiteralPath $item.destination -Destination $backup -Force } Copy-Item -LiteralPath $item.source -Destination $item.destination -Force $completed.Add($item) } foreach ($item in $completed) { Remove-Item -LiteralPath (([string]$item.destination) + '.previous') -Force -ErrorAction SilentlyContinue } } catch { for ($rollback = $completed.Count - 1; $rollback -ge 0; $rollback--) { $item = $completed[$rollback] $backup = ([string]$item.destination) + '.previous' if (Test-Path -LiteralPath $backup) { Copy-Item -LiteralPath $backup -Destination $item.destination -Force } elseif (-not [bool]$item.hadDestination) { Remove-Item -LiteralPath $item.destination -Force -ErrorAction SilentlyContinue } } throw } Write-Host 'Mise a jour installee et verifiee. Redemarrage du launcher...' -ForegroundColor Green if ($env:BBL_UPDATE_SKIP_RESTART -ne '1') { Start-Process -FilePath $launcher.FullName } Start-Sleep -Seconds 2