$ErrorActionPreference = 'Stop' $BaseUrl = 'https://pois.zyrntopo.com/downloads' $VersionUrl = 'https://zyrntopo.com/version.json' try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls11 } catch { } $PrevProgress = $ProgressPreference $ProgressPreference = 'SilentlyContinue' function Write-Info { param($m) Write-Host "==> $m" -ForegroundColor Green } function Write-Warn { param($m) Write-Host " warning: $m" -ForegroundColor Yellow } function Die { param($m) Write-Host " error: $m" -ForegroundColor Red; throw $m } function Show-Usage { Write-Host @' ZYRNTOPO installer First install: irm https://zyrntopo.com/install.ps1 | iex After that, manage it with the zyrntopo command: zyrntopo launch the app zyrntopo update upgrade to the current release zyrntopo version show installed and published versions zyrntopo remove uninstall (keeps your saved maps) Architectures: x64, ARM64 and 32-bit x86, detected automatically. Environment: $env:ZYRN_VERSION='1.16.10' pin a version (only the current release is hosted) $env:ZYRN_METHOD='msi' use the .msi instead of the .exe The installers are not code-signed yet, so SmartScreen may warn. This script checks the SHA-256 against the published checksum before running anything, which is the same guarantee the download page's manual verify step gives you. '@ } $Action = $null if ($args -and $args.Count -gt 0) { $Action = [string]$args[0] } if (-not $Action) { $Action = $env:ZYRN_ACTION } if (-not $Action) { $Action = 'install' } $Action = $Action.ToLowerInvariant() switch ($Action) { 'update' { $Action = 'install' } 'upgrade' { $Action = 'install' } 'remove' { $Action = 'uninstall' } '-h' { $Action = 'help' } '--help' { $Action = 'help' } } if ($Action -eq 'help') { Show-Usage; return } $RawArch = $env:PROCESSOR_ARCHITEW6432 if (-not $RawArch) { $RawArch = $env:PROCESSOR_ARCHITECTURE } switch ($RawArch.ToUpperInvariant()) { 'AMD64' { $Arch = 'x64' } 'ARM64' { $Arch = 'arm64' } 'X86' { $Arch = 'ia32' } default { Die "unsupported processor architecture '$RawArch'. The web app runs in any browser: https://app.zyrntopo.com" } } function Get-InstalledEntry { $roots = @( 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' ) foreach ($root in $roots) { try { $items = Get-ItemProperty $root -ErrorAction Stop } catch { continue } foreach ($i in $items) { if ($i.DisplayName -and $i.DisplayName -match 'ZYRNTOPO') { return $i } } } return $null } if ($Action -eq 'uninstall') { $entry = Get-InstalledEntry if (-not $entry) { Write-Warn 'ZYRNTOPO does not appear to be installed.' $ProgressPreference = $PrevProgress return } Write-Info "Removing $($entry.DisplayName) $($entry.DisplayVersion)" if ($entry.QuietUninstallString) { cmd.exe /c $entry.QuietUninstallString | Out-Null } elseif ($entry.UninstallString -match 'msiexec') { $code = $entry.PSChildName Start-Process msiexec.exe -ArgumentList "/x $code /qn /norestart" -Wait } else { Start-Process $entry.UninstallString -ArgumentList '/S' -Wait } $shimPath = Join-Path $env:LOCALAPPDATA 'ZYRNTOPO\bin\zyrntopo.cmd' if (Test-Path $shimPath) { Remove-Item $shimPath -Force -ErrorAction SilentlyContinue } Write-Host '' Write-Info 'ZYRNTOPO removed.' Write-Host " Your saved maps and offline tiles were kept, in $env:APPDATA\ZYRNTOPO" -ForegroundColor DarkGray Write-Host " Delete them too with: Remove-Item -Recurse -Force '$env:APPDATA\ZYRNTOPO'" -ForegroundColor DarkGray $ProgressPreference = $PrevProgress return } $Version = $env:ZYRN_VERSION if (-not $Version) { Write-Info 'Resolving the current release...' try { $meta = Invoke-RestMethod -Uri $VersionUrl -UseBasicParsing -TimeoutSec 30 } catch { Die "could not read the current version from $VersionUrl. Pin one explicitly with `$env:ZYRN_VERSION." } if ($meta.platforms -and $meta.platforms.win) { $Version = $meta.platforms.win } else { $Version = $meta.version } if (-not $Version) { Die "could not read the current version from $VersionUrl." } } $entry = Get-InstalledEntry if ($Action -eq 'version') { if ($entry) { Write-Host "installed: $($entry.DisplayVersion)" } else { Write-Host 'installed: not installed' } Write-Host "published: $Version" $ProgressPreference = $PrevProgress return } Write-Info "ZYRNTOPO $Version ($Arch)" $Method = $env:ZYRN_METHOD if (-not $Method) { $Method = 'exe' } $Method = $Method.ToLowerInvariant() switch ($Method) { 'exe' { $File = "zyrntopo-$Version-win-$Arch-setup.exe" } 'msi' { $File = "zyrntopo-$Version-win-$Arch.msi" } default { Die "unknown ZYRN_METHOD '$Method' (expected exe or msi)." } } $Tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("zyrntopo-" + [guid]::NewGuid().ToString('N').Substring(0, 8)) New-Item -ItemType Directory -Path $Tmp -Force | Out-Null $Pkg = Join-Path $Tmp $File try { Write-Info "Downloading $File" Write-Host " $BaseUrl/$File" -ForegroundColor DarkGray Write-Host ' This is a large download - the offline map archive ships inside it.' -ForegroundColor DarkGray try { Invoke-WebRequest -Uri "$BaseUrl/$File" -OutFile $Pkg -UseBasicParsing } catch { Die "download failed. Is $Version a published release? Browse what is available: https://zyrntopo.com/downloads" } $SumsFile = Join-Path $Tmp 'SHA256SUMS.txt' $HaveSums = $true try { Invoke-WebRequest -Uri "$BaseUrl/SHA256SUMS-$Version.txt" -OutFile $SumsFile -UseBasicParsing } catch { $HaveSums = $false } if ($HaveSums) { $Pattern = '^([0-9a-fA-F]{64})\s+\*?' + [regex]::Escape($File) + '\s*$' $Want = $null foreach ($line in Get-Content $SumsFile) { if ($line -match $Pattern) { $Want = $Matches[1]; break } } if ($Want) { $Got = (Get-FileHash -Path $Pkg -Algorithm SHA256).Hash if ($Got.ToLowerInvariant() -ne $Want.ToLowerInvariant()) { Die "CHECKSUM MISMATCH for $File.`n expected $Want`n got $Got`n The download was corrupted or tampered with. Nothing was installed." } Write-Info 'Checksum verified.' } else { Write-Warn "no checksum listed for $File - continuing unverified." } } else { Write-Warn 'checksum file unavailable - continuing unverified.' } Write-Info 'Installing' if ($Method -eq 'msi') { $proc = Start-Process msiexec.exe -ArgumentList "/i `"$Pkg`" /qn /norestart" -Wait -PassThru if ($proc.ExitCode -ne 0) { Die "msiexec failed with exit code $($proc.ExitCode)." } } else { $proc = Start-Process $Pkg -ArgumentList '/S' -Wait -PassThru if ($proc.ExitCode -ne 0) { Die "the installer failed with exit code $($proc.ExitCode)." } } $BinDir = Join-Path $env:LOCALAPPDATA 'ZYRNTOPO\bin' New-Item -ItemType Directory -Path $BinDir -Force | Out-Null $Fresh = Get-InstalledEntry $ExePath = $null if ($Fresh -and $Fresh.DisplayIcon) { $ExePath = ($Fresh.DisplayIcon -split ',')[0] } if (-not $ExePath) { $ExePath = Join-Path $env:LOCALAPPDATA 'Programs\ZYRNTOPO\ZYRNTOPO.exe' } $Shim = @" @echo off if /I "%~1"=="update" goto manage if /I "%~1"=="upgrade" goto manage if /I "%~1"=="remove" goto manage if /I "%~1"=="uninstall" goto manage if /I "%~1"=="version" goto manage if /I "%~1"=="help" goto manage start "" "$ExePath" %* exit /b 0 :manage set "ZYRN_ACTION=%~1" powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://zyrntopo.com/install.ps1 | iex" exit /b %ERRORLEVEL% "@ [System.IO.File]::WriteAllText((Join-Path $BinDir 'zyrntopo.cmd'), $Shim, (New-Object System.Text.UTF8Encoding $false)) $UserPath = [Environment]::GetEnvironmentVariable('Path', 'User') if (-not $UserPath) { $UserPath = '' } if (($UserPath -split ';') -notcontains $BinDir) { $NewPath = $UserPath.TrimEnd(';') if ($NewPath) { $NewPath = "$NewPath;$BinDir" } else { $NewPath = $BinDir } [Environment]::SetEnvironmentVariable('Path', $NewPath, 'User') Write-Host " added $BinDir to your PATH (new terminals only)" -ForegroundColor DarkGray } Write-Host '' Write-Info "ZYRNTOPO $Version installed." Write-Host ' zyrntopo update / version / remove - manage it from the terminal' -ForegroundColor DarkGray Write-Host ' Launch it from the Start menu, or run: zyrntopo' -ForegroundColor DarkGray Write-Host ' Docs: https://zyrntopo.com/documentation' -ForegroundColor DarkGray } finally { Remove-Item -Recurse -Force $Tmp -ErrorAction SilentlyContinue $ProgressPreference = $PrevProgress }