# Installs Animated Interactive Objects and Neural Software TriEdit on Windows. In PowerShell: # # irm https://triedit.aions.click/install.ps1 | iex # # It downloads the newest TriEdit setup (a per-user MSI) from the TriEdit releases on GitHub, checks that it is signed # by AIONS, installs it for your user account (no administrator rights needed) and starts TriEdit. Running it again # updates TriEdit to the newest version. # # Options, set before running the line above: # $env:TRIEDIT_QUIET = "1" no progress window, and TriEdit is not started afterwards # $env:TRIEDIT_NO_START = "1" show the progress window but do not start TriEdit # $env:TRIEDIT_REINSTALL = "1" install again even if this version is already installed # $env:TRIEDIT_MSI = "" install this setup instead of the newest one # # By installing you accept the licence (https://triedit.aions.click/EULA.txt) and the privacy policy # (https://triedit.aions.click/privacy.html). & { $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" # downloads are far slower in Windows PowerShell with the progress bar $appName = "Animated Interactive Objects and Neural Software TriEdit" $upgradeCode = "{B1B32FA2-A477-4B75-A3C6-11A268279997}" $releases = "https://api.github.com/repos/aionstech/homebrew-tap/releases?per_page=20" $latestMsi = "https://github.com/aionstech/homebrew-tap/releases/latest/download/TriEdit-Setup.msi" # The AIONS code-signing certificate (until a certificate trusted by Windows replaces it). $knownThumbprints = @("F097C6AE7C2858B4EEB5A0601FEF44C76B5AEC5C") $quiet = $env:TRIEDIT_QUIET -eq "1" $startAfter = -not $quiet -and $env:TRIEDIT_NO_START -ne "1" function Say([string]$Text, [string]$Colour = "Gray") { Write-Host $Text -ForegroundColor $Colour } function Invoke([object]$Target, [string]$Member, [string]$Kind, [object[]]$Arguments) { $Target.GetType().InvokeMember($Member, $Kind, $null, $Target, $Arguments) } function MsiProperty([string]$Path, [string]$Name) { $installer = New-Object -ComObject WindowsInstaller.Installer $database = Invoke $installer "OpenDatabase" "InvokeMethod" @($Path, 0) $view = Invoke $database "OpenView" "InvokeMethod" @("SELECT Value FROM Property WHERE Property='$Name'") [void](Invoke $view "Execute" "InvokeMethod" $null) $record = Invoke $view "Fetch" "InvokeMethod" $null $value = if ($record) { [string](Invoke $record "StringData" "GetProperty" @(1)) } else { "" } [void](Invoke $view "Close" "InvokeMethod" $null) [void][Runtime.InteropServices.Marshal]::ReleaseComObject($database) return $value } # The TriEdit already installed for this user, found by the setup's upgrade code: "" when there is none. function InstalledVersion { $installer = New-Object -ComObject WindowsInstaller.Installer foreach ($code in (Invoke $installer "RelatedProducts" "GetProperty" @($upgradeCode))) { return [string](Invoke $installer "ProductInfo" "GetProperty" @($code, "VersionString")) } return "" } # ----- Can TriEdit run here? ----- if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) { Say "This installer is for Windows. On macOS and Linux, install TriEdit with Homebrew:" Yellow Say " brew tap aionstech/tap" Say " brew install triedit" return } if (-not [Environment]::Is64BitOperatingSystem -or [Environment]::OSVersion.Version.Major -lt 10) { Say "TriEdit needs 64-bit Windows 10 or 11." Red return } [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 $running = Get-Process -Name $appName -ErrorAction SilentlyContinue if ($running) { Say "TriEdit is open. Save your work, close TriEdit, then run this again." Yellow return } # ----- Download ----- $source = $env:TRIEDIT_MSI $digest = $null $remoteVersion = $null if (-not $source) { # The newest release that has a Windows setup (a Homebrew-only release may come first for a short while). try { $list = Invoke-RestMethod $releases -Headers @{ "User-Agent" = "TriEdit-installer" } -UseBasicParsing foreach ($release in $list) { if ($release.draft -or $release.prerelease) { continue } $asset = $release.assets | Where-Object { $_.name -eq "TriEdit-Setup.msi" } | Select-Object -First 1 if ($asset) { $source = $asset.browser_download_url $remoteVersion = $release.tag_name -replace "^triedit-v", "" if ($asset.digest -like "sha256:*") { $digest = $asset.digest.Substring(7).ToUpperInvariant() } break } } } catch { } if (-not $source) { $source = $latestMsi } # Nothing to download when the newest version is already installed. $installed = InstalledVersion $current = $false try { $current = $installed -and $remoteVersion -and [version]$installed -ge [version]$remoteVersion } catch { } if ($current -and $env:TRIEDIT_REINSTALL -ne "1") { Say "TriEdit $installed is already installed and up to date." Green Say "(To install it again, set `$env:TRIEDIT_REINSTALL = `"1`" and run this again.)" return } } $msi = Join-Path ([IO.Path]::GetTempPath()) ("TriEdit-Setup-" + [Guid]::NewGuid().ToString("N").Substring(0, 8) + ".msi") if ($source -match "^https?://") { Say "Downloading TriEdit..." try { Invoke-WebRequest $source -OutFile $msi -UseBasicParsing -Headers @{ "User-Agent" = "TriEdit-installer" } } catch { Say "The download failed: $($_.Exception.Message)" Red Say "Download TriEdit from https://triedit.aions.click/download.html instead." return } } elseif (Test-Path $source) { Copy-Item $source $msi } else { Say "No setup found at $source" Red return } try { # ----- Check it before running it ----- if ($digest -and (Get-FileHash $msi -Algorithm SHA256).Hash -ne $digest) { Say "The download is damaged (its checksum does not match). Nothing was installed. Please try again." Red return } $signature = Get-AuthenticodeSignature $msi $signer = $signature.SignerCertificate $trusted = $signer -and $signature.Status -eq "Valid" -and $signer.Subject -match "AIONS|Animated Interactive Objects and Neural Software" $known = $signer -and $knownThumbprints -contains $signer.Thumbprint if (-not ($trusted -or $known)) { Say "The setup is not signed by AIONS, so it was not run. Nothing was installed." Red return } try { $version = MsiProperty $msi "ProductVersion" $product = MsiProperty $msi "ProductName" } catch { $version = ""; $product = "" } if ($product -ne $appName -or -not $version) { Say "The download is not a TriEdit setup. Nothing was installed." Red return } $before = InstalledVersion if ($before) { if ([version]$before -gt [version]$version) { Say "TriEdit $before is already installed, which is newer than $version. Nothing was changed." Green return } if ([version]$before -eq [version]$version -and $env:TRIEDIT_REINSTALL -ne "1") { Say "TriEdit $version is already installed and up to date." Green Say "(To install it again, set `$env:TRIEDIT_REINSTALL = `"1`" and run this again.)" return } Say "Updating TriEdit $before to $version..." } else { Say "Installing TriEdit $version for $env:USERNAME..." } # ----- Install ----- $log = Join-Path ([IO.Path]::GetTempPath()) "TriEdit-install.log" $ui = if ($quiet) { "/qn" } else { "/passive" } $process = Start-Process msiexec.exe -ArgumentList @("/i", "`"$msi`"", $ui, "/norestart", "/l*v", "`"$log`"") -Wait -PassThru switch ($process.ExitCode) { 0 { } 3010 { Say "Windows asks for a restart to finish." Yellow } 1602 { Say "The installation was cancelled. Nothing was installed." Yellow; return } 1618 { Say "Another installation is running. Wait for it to finish, then run this again." Yellow; return } default { Say "The installation failed (Windows Installer error $($process.ExitCode)). Details: $log" Red return } } Remove-Item $log -ErrorAction SilentlyContinue $exe = Join-Path $env:LOCALAPPDATA "Programs\$appName\$appName.exe" if (-not (Test-Path $exe)) { Say "Windows Installer finished, but TriEdit was not found in $(Split-Path $exe). Please try the setup from https://triedit.aions.click/download.html." Red return } Say "TriEdit $version is installed. Open it from the Start menu or the desktop." Green Say "Run this line again any time to update it. To uninstall, use Settings > Apps." if ($startAfter) { Start-Process $exe } } finally { Remove-Item $msi -ErrorAction SilentlyContinue } }