lewd-hypnosis-city-hypnosm/gameupdate/patch.ps1
2026-05-10 15:40:23 -05:00

662 lines
22 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#Requires -Version 5.1
param(
[Parameter(Mandatory = $false)]
[string]$GameRoot
)
$ErrorActionPreference = 'Stop'
if (-not $PSBoundParameters.ContainsKey('GameRoot') -or [string]::IsNullOrWhiteSpace($GameRoot)) {
$GameRoot = (Get-Location).ProviderPath
}
else {
$GameRoot = (Resolve-Path -LiteralPath $GameRoot).ProviderPath
}
$PatchBundleRoot = $PSScriptRoot
function Wait-ConsolePause {
Write-Host ''
Write-Host 'Press any key to close this window...'
try {
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
}
catch {
Read-Host 'Press Enter to close'
}
}
function Get-DazedPatcherBannerGlyphs {
# Fixed 7 rows × 8 cols per glyph (monospace). Letters only; words built separately.
return @{
'A' = @(' ', ' AAA ', ' A A ', ' AAAAA ', ' A A ', ' A A ', ' ')
'C' = @(' ', ' CCCC ', ' C ', ' C ', ' C ', ' CCCC ', ' ')
'D' = @(' ', ' DDDDD ', ' D D ', ' D D ', ' D D ', ' DDDDD ', ' ')
'E' = @(' ', ' EEEEE ', ' E ', ' EEE ', ' E ', ' EEEEE ', ' ')
'H' = @(' ', ' H H ', ' H H ', ' HHHHH ', ' H H ', ' H H ', ' ')
'P' = @(' ', ' PPPPP ', ' P P ', ' PPPP ', ' P ', ' P ', ' ')
'R' = @(' ', ' RRRRR ', ' R R ', ' RRRR ', ' R R ', ' R R ', ' ')
'T' = @(' ', ' TTTTT ', ' T ', ' T ', ' T ', ' T ', ' ')
'Z' = @(' ', ' ZZZZZ ', ' Z ', ' Z ', ' Z ', ' ZZZZZ ', ' ')
}
}
function Build-AsciiWordLines {
param(
[Parameter(Mandatory = $true)][string]$Word,
[hashtable]$Glyphs
)
# Force array: pipeline assignment can otherwise collapse to a scalar and break glyph lookup.
$letters = @(
$Word.ToUpperInvariant().ToCharArray() | ForEach-Object { [string]$_ }
)
$rows = New-Object string[] 7
for ($r = 0; $r -lt 7; $r++) {
$parts = New-Object System.Collections.Generic.List[string]
foreach ($letter in $letters) {
if (-not $Glyphs.ContainsKey($letter)) {
throw "Banner glyph missing for '$letter'."
}
$parts.Add($Glyphs[$letter][$r])
}
$rows[$r] = ($parts -join ' ')
}
return [string[]]$rows
}
function ConvertTo-LineStringArray {
param([AllowNull()][object]$Value)
if ($null -eq $Value) { return @() }
if ($Value -is [string[]]) { return $Value }
if ($Value -is [System.Collections.Generic.List[string]]) { return $Value.ToArray() }
if ($Value -is [string]) {
if ([string]::IsNullOrWhiteSpace([string]$Value)) { return @() }
return ,[string]$Value
}
return [string[]](@($Value) | ForEach-Object { if ($null -eq $_) { '' } else { "$_" } })
}
function Pad-LineBlockToHeight {
param(
# Untyped: PS may wrongly coerce to '' before binding [string[]]; normalize ourselves.
[Parameter(Mandatory = $true, Position = 0)][object]$PatchVerticalRows,
[Parameter(Mandatory = $true, Position = 1)][int]$TargetHeight
)
[string[]]$lines = ConvertTo-LineStringArray -Value $PatchVerticalRows
if ($lines.Count -eq 0 -and $TargetHeight -gt 0) {
$blank = New-Object string[] $TargetHeight
for ($i = 0; $i -lt $TargetHeight; $i++) { $blank[$i] = '' }
return $blank
}
if ($lines.Count -ge $TargetHeight) {
return [string[]]$lines
}
$padTotal = $TargetHeight - $lines.Count
$padTop = [int][Math]::Floor($padTotal / 2)
$padBot = $padTotal - $padTop
$top = @()
for ($i = 0; $i -lt $padTop; $i++) { $top += '' }
$bot = @()
for ($i = 0; $i -lt $padBot; $i++) { $bot += '' }
return [string[]]($top + $lines + $bot)
}
function Get-EmbeddedIconAsciiCatLines {
# Generated from assets/icon.png; rerun generate_icon_ascii.ps1 and paste here if the icon changes.
$raw = @'
# %#
%:.-@ #..=
#:.-*:.:# *..=*..-%
*=.:.+# %% @*-:::+#
*-# +...=# =-%
% #..==..:# %@
%..* -.:%%#=..=##*##%% %*=:...+
*:.=+.:= ..=%##%*:..::.....:=+:..-=*:.:
*=:--.=* ..=%###%########*+=:.-+#%%%:.-
-:% #..=%###%%%%%###%%%%%#%%%#%*..+
%..=%##-.=*=-*%#####%%%%%#%#:.:
+.:###%+....:*%###:.=*=-*%%-..%
-.:%#%+..:..=%####=....-#%%+..%
*..*%##**%*-=###%=..:..=%#%*..#
=..*%%%%#%%%#####*#%*-+%%%-..
+..-*#%%%%######%%#%%%%*:..#
%+...-+*##%%%%%%%%#*+-..=%
%..:.....::---::....:*%
:.:%%#*+=--::::-=+:.:*
%..+%#%%%%%%%%%%%%%%+..-@ #-:::+@
#..+%###############%#:.:% #..=*:..#
..=%################%#:..% %..+%%-..@
%..:%###%#%##%%#%#####%#:.- *..*%*..+
#....*%##-.+%%#:.+%##%#%%*..* :.-%%:.-
%..=..-%#%:..#%+..+%#%+::#%:.- :.-%%:.-
-.:%=..*%%*..+%:.:%#%#..:#%=.. #..+%*..+
..=%#..:%%#..-#..=%#%-..*%%=..*:.-%%-..@
-.-%%+..+%%-.:+..+%%+..+%#%-....=%%-..#
+..*%*..:%%=..=..*%%:..*%%#...=#%+:.:#
..-**..:+**=..:..+**+:..**+..=*=:..+
+..............................:=#
%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
'@
[string[]]$split = @($raw -split '\r?\n')
return [string[]]$split
}
function Write-AsciiHeader {
try {
Write-Host ''
$glyphs = Get-DazedPatcherBannerGlyphs
$wordDazed = Build-AsciiWordLines -Word 'Dazed' -Glyphs $glyphs
$wordPatcher = Build-AsciiWordLines -Word 'Patcher' -Glyphs $glyphs
$titleCore = [System.Collections.Generic.List[string]]::new()
foreach ($ln in $wordDazed) { $titleCore.Add($ln) }
[void]$titleCore.Add('')
foreach ($ln in $wordPatcher) { $titleCore.Add($ln) }
$titleRows = $titleCore.ToArray()
$catLines = @(Get-EmbeddedIconAsciiCatLines)
$targetH = [Math]::Max($titleRows.Count, $catLines.Count)
$titlePadded = Pad-LineBlockToHeight -PatchVerticalRows $titleRows -TargetHeight $targetH
$catPadded = Pad-LineBlockToHeight -PatchVerticalRows $catLines -TargetHeight $targetH
$titleWidth = 0
foreach ($ln in $titlePadded) {
$safe = if ($null -eq $ln) { '' } else { $ln }
if ($safe.Length -gt $titleWidth) { $titleWidth = $safe.Length }
}
$gap = ' '
for ($i = 0; $i -lt $targetH; $i++) {
$ti = if ($null -eq $titlePadded[$i]) { '' } else { $titlePadded[$i] }
$ci = if ($catPadded.Length -le $i -or $null -eq $catPadded[$i]) { '' } else { $catPadded[$i] }
$left = if ($titleWidth -gt 0) { $ti.PadRight($titleWidth) } else { '' }
Write-Host ($left + $gap + $ci)
}
Write-Host ''
}
catch {
Write-Host ('[Banner skipped] ' + $_.Exception.Message)
}
}
function Write-BannerWrongFolder {
Write-Host ''
Write-Host '========================================'
Write-Host 'ERROR: Wrong game root folder!'
Write-Host '========================================'
Write-Host ''
Write-Host 'Game root cannot be the gameupdate folder itself.'
Write-Host 'Run GameUpdate.bat from the game root (same folder as GameUpdate.bat).'
Write-Host '========================================'
Write-Host ''
}
function Test-WrongWorkingDirectory {
param([string]$Root)
return ($Root -and ((Split-Path -Leaf $Root) -ieq 'gameupdate'))
}
function Read-PatchConfig {
param([string]$ConfigPath)
$cfg = @{ username = ''; repo = ''; branch = '' }
if (-not (Test-Path -LiteralPath $ConfigPath)) {
return $cfg
}
foreach ($rawLine in Get-Content -LiteralPath $ConfigPath) {
$line = $rawLine.Trim()
if (-not $line -or $line.StartsWith('#')) { continue }
$eq = $line.IndexOf('=')
if ($eq -lt 1) { continue }
$k = $line.Substring(0, $eq).Trim().ToLowerInvariant()
$v = if ($eq -lt $line.Length - 1) { $line.Substring($eq + 1).Trim() } else { '' }
switch ($k) {
'username' { $cfg.username = $v }
'repo' { $cfg.repo = $v }
'branch' { $cfg.branch = $v }
}
}
return $cfg
}
function Invoke-SrpgUnpacker {
param(
[Parameter(Mandatory = $true)][string]$ExePath,
[Parameter(Mandatory = $true)][string]$WorkingDirectory,
[Parameter(Mandatory = $true)][string[]]$Arguments
)
Push-Location -LiteralPath $WorkingDirectory
try {
& $ExePath @Arguments
return $LASTEXITCODE
}
finally {
Pop-Location
}
}
function Invoke-SrpgPreSetup {
param([string]$Root)
$unpacker = Join-Path $Root 'SRPG_Unpacker.exe'
$dts = Join-Path $Root 'data.dts'
$dataDir = Join-Path $Root 'data'
$projectDat = Join-Path $dataDir 'project.dat'
$patchDir = Join-Path $Root 'patch'
if (-not (Test-Path -LiteralPath $dts)) {
return
}
if (-not (Test-Path -LiteralPath $unpacker)) {
Write-Host '[Pre-Setup] SRPG_Unpacker.exe not found; skipping data setup.'
return
}
$shouldUnpack =
-not (Test-Path -LiteralPath $dataDir) -or
-not (Test-Path -LiteralPath $projectDat)
$runUnpackBlock = {
if (Test-Path -LiteralPath $dts) {
Write-Host '[Pre-Setup] Unpacking data.dts to data\'
$code = Invoke-SrpgUnpacker -ExePath $unpacker -WorkingDirectory $Root -Arguments @('-o', 'data', 'data.dts')
if ($code -ne 0) {
Write-Host '[Pre-Setup] ERROR: Unpack failed. Continuing.'
}
}
else {
Write-Host '[Pre-Setup] Skipping unpack: data.dts missing.'
}
if (-not (Test-Path -LiteralPath $patchDir)) {
if (Test-Path -LiteralPath $projectDat) {
Write-Host '[Pre-Setup] Creating patch folder from data\project.dat'
$code = Invoke-SrpgUnpacker -ExePath $unpacker -WorkingDirectory $Root -Arguments @('.\data\project.dat', '-c')
if ($code -ne 0) {
Write-Host '[Pre-Setup] ERROR: Create Patch failed. Continuing.'
}
}
else {
Write-Host '[Pre-Setup] Skipping create patch: data\project.dat not found.'
}
}
}
if ($shouldUnpack) {
& $runUnpackBlock
}
else {
if (-not (Test-Path -LiteralPath $patchDir)) {
if (Test-Path -LiteralPath $projectDat) {
Write-Host '[Pre-Setup] Creating patch folder from data\project.dat'
$code = Invoke-SrpgUnpacker -ExePath $unpacker -WorkingDirectory $Root -Arguments @('.\data\project.dat', '-c')
if ($code -ne 0) {
Write-Host '[Pre-Setup] ERROR: Create Patch failed. Continuing.'
}
}
else {
Write-Host '[Pre-Setup] Skipping create patch: data\project.dat not found.'
}
}
}
}
function Get-EnvInt {
param(
[Parameter(Mandatory = $true)][string]$Name,
[Parameter(Mandatory = $true)][int]$Default
)
$raw = [Environment]::GetEnvironmentVariable($Name)
if ([string]::IsNullOrWhiteSpace($raw)) { return $Default }
$parsed = 0
if ([int]::TryParse([string]$raw, [ref]$parsed) -and $parsed -gt 0) { return $parsed }
return $Default
}
function Invoke-WithRetry {
param(
[Parameter(Mandatory = $true)][scriptblock]$Action,
[Parameter(Mandatory = $true)][string]$Name,
[int]$Attempts = 2,
[int]$DelaySeconds = 2
)
$lastErr = $null
for ($i = 1; $i -le $Attempts; $i++) {
try {
return & $Action
}
catch {
$lastErr = $_
if ($i -lt $Attempts) {
Write-Host ("{0} failed ({1}/{2}), retrying..." -f $Name, $i, $Attempts)
Start-Sleep -Seconds $DelaySeconds
}
}
}
throw $lastErr
}
function Get-InvalidZipDownloadHint {
param([Parameter(Mandatory = $true)][string]$LiteralPath)
$item = Get-Item -LiteralPath $LiteralPath -ErrorAction SilentlyContinue
if (-not $item) {
return '(downloaded file missing)'
}
$len = $item.Length
$parts = [System.Collections.Generic.List[string]]::new()
$parts.Add("File size: $len bytes.")
if ($len -eq 0) {
$parts.Add('Download was empty.')
return ($parts -join ' ')
}
$peekSize = [Math]::Min([int64]$len, 6144)
$buf = New-Object byte[] $peekSize
$fs = [System.IO.File]::OpenRead($LiteralPath)
try {
[void]$fs.Read($buf, 0, $peekSize)
}
finally {
$fs.Dispose()
}
$text = [System.Text.Encoding]::UTF8.GetString($buf)
if ($text -match '(?i)<!DOCTYPE\s+html|<html[\s>]') {
$parts.Add('Body looks like HTML (often an error page or block). Check username/repo/branch in patch-config.txt and network.')
}
elseif ($text.TrimStart().StartsWith('{')) {
try {
$j = ($text.TrimStart() | ConvertFrom-Json)
if ($j.message) {
$parts.Add("GitLab JSON: $($j.message)")
}
else {
$parts.Add('Body starts with JSON (likely an API error, not a ZIP).')
}
}
catch {
$parts.Add('Body starts with `{` but was not parseable JSON (truncated or wrong encoding).')
}
}
else {
$flat = ($text -replace '[\r\n]+', ' ')
$take = [Math]::Min(200, $flat.Length)
if ($take -gt 0) {
$parts.Add(('UTF-8 preview: ' + $flat.Substring(0, $take)))
}
}
return ($parts -join ' ')
}
function Download-WithIwrSpeedProgress {
param(
[Parameter(Mandatory = $true)][string]$Url,
[Parameter(Mandatory = $true)][string]$OutFile,
[Parameter(Mandatory = $true)][hashtable]$Headers
)
if (Test-Path -LiteralPath $OutFile) {
Remove-Item -LiteralPath $OutFile -Force
}
$prevPref = $ProgressPreference
try {
$ProgressPreference = 'Continue'
Invoke-WebRequest -Uri $Url -OutFile $OutFile -Headers $Headers -UseBasicParsing
}
finally {
$ProgressPreference = $prevPref
}
}
function Remove-FileOrDirectoryViaCmd {
param([Parameter(Mandatory = $true)][string]$LiteralPath)
if (-not (Test-Path -LiteralPath $LiteralPath)) {
return
}
$full = (Resolve-Path -LiteralPath $LiteralPath).ProviderPath
$item = Get-Item -LiteralPath $LiteralPath
if ($item.PSIsContainer) {
cmd.exe /c "rd /s /q `"$full`"" | Out-Null
}
else {
cmd.exe /c "del /f /q `"$full`"" | Out-Null
}
}
function Remove-DownloadedRepoZip {
param([Parameter(Mandatory = $true)][string]$LiteralPath)
Remove-FileOrDirectoryViaCmd -LiteralPath $LiteralPath
}
function Invoke-PatchDownloadExtract {
param(
[Parameter(Mandatory = $true)][string]$Root,
[Parameter(Mandatory = $true)][string]$StateFilePath,
[Parameter(Mandatory = $true)][string]$Username,
[Parameter(Mandatory = $true)][string]$Repo,
[Parameter(Mandatory = $true)][string]$Branch
)
if ($PSVersionTable.PSEdition -ne 'Core') {
try {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
}
catch {}
}
Set-Location -LiteralPath $Root
$id = [uri]::EscapeDataString($Username + '/' + $Repo)
$branchEnc = [uri]::EscapeDataString($Branch)
$headers = @{ 'User-Agent' = 'DazedMTL-Patcher-1.0' }
$dlAttempts = Get-EnvInt -Name 'GAMEUPDATE_DL_ATTEMPTS' -Default 2
$latestSha = Invoke-WithRetry -Name 'Resolve latest patch SHA' -Attempts $dlAttempts -Action {
(Invoke-RestMethod -Uri ("https://gitgud.io/api/v4/projects/$id/repository/branches/$branchEnc") -Headers $headers).commit.id
}
if (-not $latestSha) { throw 'PATCH_ERR:API:Latest commit SHA response was empty.' }
$latestSha = ([string]$latestSha).Trim()
$previousSha = ''
if (Test-Path -LiteralPath $StateFilePath) {
$previousSha = (Get-Content -LiteralPath $StateFilePath -Raw).Trim()
}
else {
Write-Host 'First run: comparing with remote...'
}
if ($previousSha -eq $latestSha) {
return 10
}
Write-Host 'Downloading patch...'
$zipPath = Join-Path $PWD.Path 'repo.zip'
$stage = Join-Path ([IO.Path]::GetTempPath()) ('gu_' + [Guid]::NewGuid().ToString('N'))
$archiveSha = [uri]::EscapeDataString($latestSha)
$archiveUrl = "https://gitgud.io/api/v4/projects/$id/repository/archive.zip?sha=$archiveSha"
$dlSw = [System.Diagnostics.Stopwatch]::StartNew()
Invoke-WithRetry -Name 'Download archive with Invoke-WebRequest' -Attempts $dlAttempts -Action {
Download-WithIwrSpeedProgress -Url $archiveUrl -OutFile $zipPath -Headers $headers
} | Out-Null
$dlSw.Stop()
$bytes = (Get-Item -LiteralPath $zipPath).Length
$secs = [Math]::Max($dlSw.Elapsed.TotalSeconds, 0.001)
$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)
$fs = [System.IO.File]::OpenRead($zipPath)
try {
$hdr = New-Object byte[] 4
if ($fs.Read($hdr, 0, 4) -lt 4 -or $hdr[0] -ne 0x50 -or $hdr[1] -ne 0x4B) {
$hint = Get-InvalidZipDownloadHint -LiteralPath $zipPath
throw "PATCH_ERR:ZIP:Download is not a valid ZIP (missing PK header). $hint"
}
}
finally {
$fs.Dispose()
}
if (Test-Path -LiteralPath $stage) {
Remove-FileOrDirectoryViaCmd -LiteralPath $stage
}
New-Item -ItemType Directory -Path $stage | Out-Null
try {
Expand-Archive -LiteralPath $zipPath -DestinationPath $stage -Force
$dirs = @(Get-ChildItem -LiteralPath $stage -Directory)
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
Write-Host 'Removing downloaded archive (repo.zip)...'
Remove-DownloadedRepoZip -LiteralPath $zipPath
}
finally {
if (Test-Path -LiteralPath $stage) {
Remove-FileOrDirectoryViaCmd -LiteralPath $stage
}
}
Set-Content -LiteralPath $StateFilePath -Value $latestSha -Encoding Ascii
return 0
}
function Invoke-SrpgPostApply {
param([string]$Root)
$unpacker = Join-Path $Root 'SRPG_Unpacker.exe'
$dts = Join-Path $Root 'data.dts'
$projectDat = Join-Path $Root 'data\project.dat'
$dataDir = Join-Path $Root 'data'
if (-not (Test-Path -LiteralPath $dts)) {
return
}
if (-not (Test-Path -LiteralPath $unpacker)) {
Write-Host 'SRPG_Unpacker.exe not found in root; skipping SRPG patch steps.'
return
}
if (Test-Path -LiteralPath $projectDat) {
Write-Host 'Applying patch to data\project.dat...'
$code = Invoke-SrpgUnpacker -ExePath $unpacker -WorkingDirectory $Root -Arguments @('.\data\project.dat', '-a')
if ($code -ne 0) {
Write-Host 'ERROR: Apply Patch failed.'
}
}
else {
Write-Host 'ERROR: data\project.dat not found; cannot apply patch.'
}
if (Test-Path -LiteralPath $dataDir) {
Write-Host 'Packing data folder to data.dts...'
$code = Invoke-SrpgUnpacker -ExePath $unpacker -WorkingDirectory $Root -Arguments @('-o', 'data.dts', 'data')
if ($code -ne 0) {
Write-Host 'WARNING: Pack failed.'
}
}
else {
Write-Host 'Step 4: Skipping pack - data folder not found.'
}
}
function Remove-PatchTempArtifacts {
param([string]$Root)
$zipPath = Join-Path $Root 'repo.zip'
$legacyTmp = Join-Path $Root '_patch_extract_tmp'
if (Test-Path -LiteralPath $zipPath) {
Write-Host ' Removing repo.zip - large files can sit here for minutes if antivirus is scanning.'
Remove-FileOrDirectoryViaCmd -LiteralPath $zipPath
}
if (Test-Path -LiteralPath $legacyTmp) {
Write-Host ' Removing _patch_extract_tmp - using rd /s /q (avoids PowerShell hangs on huge folders).'
Remove-FileOrDirectoryViaCmd -LiteralPath $legacyTmp
}
}
try {
if (Test-WrongWorkingDirectory -Root $GameRoot) {
Write-BannerWrongFolder
Wait-ConsolePause
exit 1
}
Write-AsciiHeader
Write-Host "Root: $GameRoot"
$configPath = Join-Path $PatchBundleRoot 'patch-config.txt'
if (-not (Test-Path -LiteralPath $configPath)) {
Write-Host 'patch-config.txt not found next to patch.ps1 - skipping patch.'
Wait-ConsolePause
exit 0
}
$cfg = Read-PatchConfig -ConfigPath $configPath
if ([string]::IsNullOrWhiteSpace($cfg.username)) {
Write-Host "ERROR: 'username=' is missing in patch-config.txt"
Wait-ConsolePause
exit 1
}
if ([string]::IsNullOrWhiteSpace($cfg.repo)) {
Write-Host "ERROR: 'repo=' is missing in patch-config.txt"
Wait-ConsolePause
exit 1
}
if ([string]::IsNullOrWhiteSpace($cfg.branch)) {
Write-Host "ERROR: 'branch=' is missing in patch-config.txt"
Wait-ConsolePause
exit 1
}
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
$stateFile = Join-Path $PatchBundleRoot 'previous_patch_sha.txt'
$patchDlExit = 1
try {
$patchDlExit = Invoke-PatchDownloadExtract -Root $GameRoot -StateFilePath $stateFile `
-Username $cfg.username -Repo $cfg.repo -Branch $cfg.branch
}
catch {
Write-Host ''
Write-Host $_.Exception.Message
if ($_.InvocationInfo.PositionMessage) { Write-Host $_.InvocationInfo.PositionMessage }
Remove-PatchTempArtifacts -Root $GameRoot
Wait-ConsolePause
exit 1
}
if ($patchDlExit -eq 10) {
Write-Host 'Already up to date.'
Wait-ConsolePause
exit 0
}
if ($patchDlExit -ne 0) {
Write-Host 'Download or extraction failed!'
Remove-PatchTempArtifacts -Root $GameRoot
Wait-ConsolePause
exit 1
}
Write-Host 'Applying patch...'
Invoke-SrpgPostApply -Root $GameRoot
Write-Host 'Cleaning up...'
Remove-PatchTempArtifacts -Root $GameRoot
Write-Host 'Done.'
Wait-ConsolePause
exit 0
}
catch {
Write-Host ''
Write-Host $_.Exception.Message
Wait-ConsolePause
exit 1
}