This commit is contained in:
DazedAnon 2026-05-11 16:36:31 -05:00
parent 7b7424eea1
commit 570c704427

View file

@ -27,7 +27,6 @@ function Wait-ConsolePause {
} }
function Get-DazedPatcherBannerGlyphs { function Get-DazedPatcherBannerGlyphs {
# Fixed 7 rows × 8 cols per glyph (monospace). Letters only; words built separately.
return @{ return @{
'A' = @(' ', ' AAA ', ' A A ', ' AAAAA ', ' A A ', ' A A ', ' ') 'A' = @(' ', ' AAA ', ' A A ', ' AAAAA ', ' A A ', ' A A ', ' ')
'C' = @(' ', ' CCCC ', ' C ', ' C ', ' C ', ' CCCC ', ' ') 'C' = @(' ', ' CCCC ', ' C ', ' C ', ' C ', ' CCCC ', ' ')
@ -46,7 +45,6 @@ function Build-AsciiWordLines {
[Parameter(Mandatory = $true)][string]$Word, [Parameter(Mandatory = $true)][string]$Word,
[hashtable]$Glyphs [hashtable]$Glyphs
) )
# Force array: pipeline assignment can otherwise collapse to a scalar and break glyph lookup.
$letters = @( $letters = @(
$Word.ToUpperInvariant().ToCharArray() | ForEach-Object { [string]$_ } $Word.ToUpperInvariant().ToCharArray() | ForEach-Object { [string]$_ }
) )
@ -78,11 +76,10 @@ function ConvertTo-LineStringArray {
function Pad-LineBlockToHeight { function Pad-LineBlockToHeight {
param( param(
# Untyped: PS may wrongly coerce to '' before binding [string[]]; normalize ourselves. [Parameter(Mandatory = $true, Position = 0)][object]$Rows,
[Parameter(Mandatory = $true, Position = 0)][object]$PatchVerticalRows,
[Parameter(Mandatory = $true, Position = 1)][int]$TargetHeight [Parameter(Mandatory = $true, Position = 1)][int]$TargetHeight
) )
[string[]]$lines = ConvertTo-LineStringArray -Value $PatchVerticalRows [string[]]$lines = ConvertTo-LineStringArray -Value $Rows
if ($lines.Count -eq 0 -and $TargetHeight -gt 0) { if ($lines.Count -eq 0 -and $TargetHeight -gt 0) {
$blank = New-Object string[] $TargetHeight $blank = New-Object string[] $TargetHeight
for ($i = 0; $i -lt $TargetHeight; $i++) { $blank[$i] = '' } for ($i = 0; $i -lt $TargetHeight; $i++) { $blank[$i] = '' }
@ -102,7 +99,7 @@ function Pad-LineBlockToHeight {
} }
function Get-EmbeddedIconAsciiCatLines { function Get-EmbeddedIconAsciiCatLines {
# Generated from assets/icon.png; rerun generate_icon_ascii.ps1 and paste here if the icon changes. # Refresh via generate_icon_ascii.ps1 when assets/icon.png changes.
$raw = @' $raw = @'
# %# # %#
%:.-@ #..= %:.-@ #..=
@ -137,8 +134,7 @@ function Get-EmbeddedIconAsciiCatLines {
+..............................:=# +..............................:=#
%%%%%%%%%%%%%%%%%%%%%%%%%%%%@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%@
'@ '@
[string[]]$split = @($raw -split '\r?\n') return [string[]]@($raw -split '\r?\n')
return [string[]]$split
} }
function Write-AsciiHeader { function Write-AsciiHeader {
@ -156,8 +152,8 @@ function Write-AsciiHeader {
$catLines = @(Get-EmbeddedIconAsciiCatLines) $catLines = @(Get-EmbeddedIconAsciiCatLines)
$targetH = [Math]::Max($titleRows.Count, $catLines.Count) $targetH = [Math]::Max($titleRows.Count, $catLines.Count)
$titlePadded = Pad-LineBlockToHeight -PatchVerticalRows $titleRows -TargetHeight $targetH $titlePadded = Pad-LineBlockToHeight -Rows $titleRows -TargetHeight $targetH
$catPadded = Pad-LineBlockToHeight -PatchVerticalRows $catLines -TargetHeight $targetH $catPadded = Pad-LineBlockToHeight -Rows $catLines -TargetHeight $targetH
$titleWidth = 0 $titleWidth = 0
foreach ($ln in $titlePadded) { foreach ($ln in $titlePadded) {
@ -344,7 +340,7 @@ function Get-InvalidZipDownloadHint {
$parts = [System.Collections.Generic.List[string]]::new() $parts = [System.Collections.Generic.List[string]]::new()
$parts.Add("File size: $len bytes.") $parts.Add("File size: $len bytes.")
if ($len -eq 0) { if ($len -eq 0) {
$parts.Add('Download was empty.') $parts.Add('Empty file often means the window was closed during download, the connection dropped, or security software blocked the file. Run again and let it finish.')
return ($parts -join ' ') return ($parts -join ' ')
} }
@ -387,7 +383,67 @@ function Get-InvalidZipDownloadHint {
return ($parts -join ' ') return ($parts -join ' ')
} }
function Download-WithIwrSpeedProgress { $script:GameUpdateConsoleCloseHelperLoaded = $false
function Initialize-GameUpdateConsoleCloseHelper {
if ($script:GameUpdateConsoleCloseHelperLoaded) { return }
$script:GameUpdateConsoleCloseHelperLoaded = $true
$script:GameUpdateConsoleCloseHelperWorks = $false
if ($env:OS -ne 'Windows_NT') { return }
try {
Add-Type -TypeDefinition @'
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace GameUpdate {
public static class ConsoleCloseHelper {
private static HandlerRoutine _handler;
public static void Register() {
_handler = Handle;
SetConsoleCtrlHandler(_handler, true);
}
public static void Unregister() {
if (_handler != null) {
SetConsoleCtrlHandler(_handler, false);
_handler = null;
}
}
private static bool Handle(uint ctrlType) {
const uint CTRL_CLOSE_EVENT = 2;
const uint CTRL_LOGOFF_EVENT = 5;
const uint CTRL_SHUTDOWN_EVENT = 6;
if (ctrlType != CTRL_CLOSE_EVENT && ctrlType != CTRL_LOGOFF_EVENT && ctrlType != CTRL_SHUTDOWN_EVENT) {
return false;
}
try {
string path = Environment.GetEnvironmentVariable("GAMEUPDATE_REPO_ZIP_PATH");
if (!string.IsNullOrEmpty(path) && File.Exists(path)) {
File.Delete(path);
}
}
catch { }
return false;
}
private delegate bool HandlerRoutine(uint ctrlType);
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(HandlerRoutine handler, bool add);
}
}
'@ -ErrorAction Stop
$script:GameUpdateConsoleCloseHelperWorks = $true
}
catch {
$script:GameUpdateConsoleCloseHelperWorks = $false
}
}
function Invoke-PatchArchiveDownload {
param( param(
[Parameter(Mandatory = $true)][string]$Url, [Parameter(Mandatory = $true)][string]$Url,
[Parameter(Mandatory = $true)][string]$OutFile, [Parameter(Mandatory = $true)][string]$OutFile,
@ -398,13 +454,37 @@ function Download-WithIwrSpeedProgress {
Remove-Item -LiteralPath $OutFile -Force Remove-Item -LiteralPath $OutFile -Force
} }
$outFull = [System.IO.Path]::GetFullPath($OutFile)
Initialize-GameUpdateConsoleCloseHelper
$prevPref = $ProgressPreference $prevPref = $ProgressPreference
$completed = $false
$registeredCloseHandler = $false
try { try {
if ($script:GameUpdateConsoleCloseHelperWorks) {
$env:GAMEUPDATE_REPO_ZIP_PATH = $outFull
[GameUpdate.ConsoleCloseHelper]::Register()
$registeredCloseHandler = $true
}
$ProgressPreference = 'Continue' $ProgressPreference = 'Continue'
Invoke-WebRequest -Uri $Url -OutFile $OutFile -Headers $Headers -UseBasicParsing Invoke-WebRequest -Uri $Url -OutFile $OutFile -Headers $Headers -UseBasicParsing
$completed = $true
} }
finally { finally {
$ProgressPreference = $prevPref $ProgressPreference = $prevPref
if ($registeredCloseHandler) {
try {
[GameUpdate.ConsoleCloseHelper]::Unregister()
}
catch { }
Remove-Item env:GAMEUPDATE_REPO_ZIP_PATH -ErrorAction SilentlyContinue
}
if (-not $completed -and (Test-Path -LiteralPath $OutFile)) {
Remove-FileOrDirectoryViaCmd -LiteralPath $OutFile
}
} }
} }
@ -468,47 +548,64 @@ function Invoke-PatchDownloadExtract {
return 10 return 10
} }
Write-Host 'Downloading patch...' $zipPath = Join-Path $Root ('dazedmtl_patch_' + [Guid]::NewGuid().ToString('N') + '.zip')
$zipPath = Join-Path $PWD.Path 'repo.zip' Write-Host ('Downloading patch... ' + $zipPath)
$stage = Join-Path ([IO.Path]::GetTempPath()) ('gu_' + [Guid]::NewGuid().ToString('N')) $stage = Join-Path ([IO.Path]::GetTempPath()) ('gu_' + [Guid]::NewGuid().ToString('N'))
$archiveSha = [uri]::EscapeDataString($latestSha) $archiveSha = [uri]::EscapeDataString($latestSha)
$archiveUrl = "https://gitgud.io/api/v4/projects/$id/repository/archive.zip?sha=$archiveSha" $archiveUrl = "https://gitgud.io/api/v4/projects/$id/repository/archive.zip?sha=$archiveSha"
$dlSw = [System.Diagnostics.Stopwatch]::StartNew() $dlSw = [System.Diagnostics.Stopwatch]::StartNew()
Invoke-WithRetry -Name 'Download archive with Invoke-WebRequest' -Attempts $dlAttempts -Action { Invoke-WithRetry -Name 'Download archive with Invoke-WebRequest' -Attempts $dlAttempts -Action {
Download-WithIwrSpeedProgress -Url $archiveUrl -OutFile $zipPath -Headers $headers Invoke-PatchArchiveDownload -Url $archiveUrl -OutFile $zipPath -Headers $headers
} | Out-Null } | Out-Null
$dlSw.Stop() $dlSw.Stop()
$bytes = (Get-Item -LiteralPath $zipPath).Length $bytes = (Get-Item -LiteralPath $zipPath).Length
if ($bytes -eq 0) {
$hint = Get-InvalidZipDownloadHint -LiteralPath $zipPath
Remove-DownloadedRepoZip -LiteralPath $zipPath
throw "PATCH_ERR:ZIP:Download is empty (0 bytes). $hint"
}
$secs = [Math]::Max($dlSw.Elapsed.TotalSeconds, 0.001) $secs = [Math]::Max($dlSw.Elapsed.TotalSeconds, 0.001)
$mbps = ($bytes / 1MB) / $secs $mbps = ($bytes / 1MB) / $secs
Write-Host ("Download complete via iwr: {0:N1} MB in {1:N1}s ({2:N1} MB/s)" -f ($bytes / 1MB), $secs, $mbps) Write-Host ("Download complete: {0:N1} MB in {1:N1}s ({2:N1} MB/s)" -f ($bytes / 1MB), $secs, $mbps)
$zipHeaderBad = $false
$zipHeaderHint = ''
$fs = [System.IO.File]::OpenRead($zipPath) $fs = [System.IO.File]::OpenRead($zipPath)
try { try {
$hdr = New-Object byte[] 4 $hdr = New-Object byte[] 4
if ($fs.Read($hdr, 0, 4) -lt 4 -or $hdr[0] -ne 0x50 -or $hdr[1] -ne 0x4B) { if ($fs.Read($hdr, 0, 4) -lt 4 -or $hdr[0] -ne 0x50 -or $hdr[1] -ne 0x4B) {
$hint = Get-InvalidZipDownloadHint -LiteralPath $zipPath $zipHeaderBad = $true
throw "PATCH_ERR:ZIP:Download is not a valid ZIP (missing PK header). $hint" $zipHeaderHint = Get-InvalidZipDownloadHint -LiteralPath $zipPath
} }
} }
finally { finally {
$fs.Dispose() $fs.Dispose()
} }
if ($zipHeaderBad) {
Remove-DownloadedRepoZip -LiteralPath $zipPath
throw "PATCH_ERR:ZIP:Download is not a valid ZIP (missing PK header). $zipHeaderHint"
}
if (Test-Path -LiteralPath $stage) { if (Test-Path -LiteralPath $stage) {
Remove-FileOrDirectoryViaCmd -LiteralPath $stage Remove-FileOrDirectoryViaCmd -LiteralPath $stage
} }
New-Item -ItemType Directory -Path $stage | Out-Null New-Item -ItemType Directory -Path $stage | Out-Null
try { try {
Expand-Archive -LiteralPath $zipPath -DestinationPath $stage -Force try {
$dirs = @(Get-ChildItem -LiteralPath $stage -Directory) Expand-Archive -LiteralPath $zipPath -DestinationPath $stage -Force
if ($dirs.Count -ne 1) { $dirs = @(Get-ChildItem -LiteralPath $stage -Directory)
throw ('PATCH_ERR:ZIP:Expected one root folder in archive, found {0}.' -f $dirs.Count) if ($dirs.Count -ne 1) {
throw ('PATCH_ERR:ZIP:Expected one root folder in archive, found {0}.' -f $dirs.Count)
}
Copy-Item -Path (Join-Path $dirs[0].FullName '*') -Destination $PWD.Path -Recurse -Force
}
finally {
if (Test-Path -LiteralPath $zipPath) {
Remove-DownloadedRepoZip -LiteralPath $zipPath
}
} }
Copy-Item -Path (Join-Path $dirs[0].FullName '*') -Destination $PWD.Path -Recurse -Force
Write-Host 'Removing downloaded archive (repo.zip)...'
Remove-DownloadedRepoZip -LiteralPath $zipPath
} }
finally { finally {
if (Test-Path -LiteralPath $stage) { if (Test-Path -LiteralPath $stage) {
@ -558,13 +655,24 @@ function Invoke-SrpgPostApply {
} }
} }
function Remove-StalePatchZipArtifactsInGameRoot {
param([Parameter(Mandatory = $true)][string]$Root)
if (-not (Test-Path -LiteralPath $Root)) { return }
Get-ChildItem -LiteralPath $Root -Filter 'dazedmtl_patch_*.zip' -File -ErrorAction SilentlyContinue |
ForEach-Object {
Remove-FileOrDirectoryViaCmd -LiteralPath $_.FullName
}
}
function Remove-PatchTempArtifacts { function Remove-PatchTempArtifacts {
param([string]$Root) param([string]$Root)
$zipPath = Join-Path $Root 'repo.zip' $zipPath = Join-Path $Root 'repo.zip'
$legacyTmp = Join-Path $Root '_patch_extract_tmp' $legacyTmp = Join-Path $Root '_patch_extract_tmp'
Remove-StalePatchZipArtifactsInGameRoot -Root $Root
if (Test-Path -LiteralPath $zipPath) { if (Test-Path -LiteralPath $zipPath) {
Write-Host ' Removing repo.zip - large files can sit here for minutes if antivirus is scanning.' Write-Host ' Removing repo.zip...'
Remove-FileOrDirectoryViaCmd -LiteralPath $zipPath Remove-FileOrDirectoryViaCmd -LiteralPath $zipPath
} }
if (Test-Path -LiteralPath $legacyTmp) { if (Test-Path -LiteralPath $legacyTmp) {
@ -582,7 +690,7 @@ try {
Write-AsciiHeader Write-AsciiHeader
Write-Host "Root: $GameRoot" Remove-StalePatchZipArtifactsInGameRoot -Root $GameRoot
$configPath = Join-Path $PatchBundleRoot 'patch-config.txt' $configPath = Join-Path $PatchBundleRoot 'patch-config.txt'
if (-not (Test-Path -LiteralPath $configPath)) { if (-not (Test-Path -LiteralPath $configPath)) {
@ -610,7 +718,6 @@ try {
} }
Write-Host ('Pulling patch from https://gitgud.io/{0}/{1} (branch: {2})' -f $cfg.username, $cfg.repo, $cfg.branch) Write-Host ('Pulling patch from https://gitgud.io/{0}/{1} (branch: {2})' -f $cfg.username, $cfg.repo, $cfg.branch)
Write-Host ''
Invoke-SrpgPreSetup -Root $GameRoot Invoke-SrpgPreSetup -Root $GameRoot