newer GameUpdate files (dazed)
This commit is contained in:
parent
22f94d51fb
commit
1e7ab4d65d
4 changed files with 962 additions and 89 deletions
|
|
@ -1,14 +1,69 @@
|
|||
@echo off
|
||||
setlocal
|
||||
setlocal EnableExtensions EnableDelayedExpansion
|
||||
|
||||
REM Copy GAMEUPDATE.bat to a new file
|
||||
copy "gameupdate\patch.bat" "gameupdate\patch2.bat"
|
||||
REM When deployed on a game: this file sits in the game root; patch scripts live in .\gameupdate\
|
||||
REM In this repo the same layout is kept for neatness: this bat is under DazedMTLTool\gameupdate\ next to a nested gameupdate\ folder with patch.ps1.
|
||||
set "GU_ROOT=%~dp0"
|
||||
REM Game root is this batch file's folder (not %%CD%%, so full-path launches still work).
|
||||
set "GAME_ROOT=!GU_ROOT!"
|
||||
if "!GAME_ROOT:~-1!"=="\" set "GAME_ROOT=!GAME_ROOT:~0,-1!"
|
||||
set "PATCH_SCRIPT_DIR=!GU_ROOT!gameupdate"
|
||||
|
||||
REM Run the new file
|
||||
call "gameupdate\patch2.bat"
|
||||
if not exist "!PATCH_SCRIPT_DIR!\patch.ps1" (
|
||||
echo ERROR: patch.ps1 not found at:
|
||||
echo !PATCH_SCRIPT_DIR!\patch.ps1
|
||||
echo Expected layout: GameUpdate.bat and a gameupdate folder next to each other ^(same parent^).
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM Delete the new file
|
||||
del "gameupdate\patch2.bat"
|
||||
REM Copy patch.ps1 so the live script can be overwritten during updates
|
||||
copy /Y "!PATCH_SCRIPT_DIR!\patch.ps1" "!PATCH_SCRIPT_DIR!\patch2.ps1" >nul
|
||||
if errorlevel 1 (
|
||||
echo ERROR: Could not copy patch.ps1 to patch2.ps1 in:
|
||||
echo !PATCH_SCRIPT_DIR!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
endlocal
|
||||
@echo on
|
||||
set "_my_shell=pwsh"
|
||||
where /q !_my_shell!
|
||||
if !errorlevel! neq 0 (
|
||||
echo PowerShell 7 ^(pwsh^) not found.
|
||||
if /I "%GAMEUPDATE_PROMPT_PWSH%"=="1" (
|
||||
echo.
|
||||
set /p "INSTALL_PWSH=PowerShell 7 is faster. Install now via winget? (Y/N): "
|
||||
if /I "!INSTALL_PWSH!"=="Y" (
|
||||
echo Installing PowerShell 7 via winget...
|
||||
winget install --id Microsoft.PowerShell --source winget --accept-package-agreements --accept-source-agreements
|
||||
where /q pwsh
|
||||
if !errorlevel! equ 0 (
|
||||
set "_my_shell=pwsh"
|
||||
echo PowerShell 7 installed; using pwsh.
|
||||
) else (
|
||||
echo Install failed or unavailable; falling back to powershell.
|
||||
set "_my_shell=powershell"
|
||||
)
|
||||
) else (
|
||||
echo Skipping install; falling back to powershell.
|
||||
set "_my_shell=powershell"
|
||||
)
|
||||
) else (
|
||||
echo Tip: Set GAMEUPDATE_PROMPT_PWSH=1 to offer PowerShell 7 install.
|
||||
echo Falling back to powershell...
|
||||
set "_my_shell=powershell"
|
||||
)
|
||||
where /q !_my_shell!
|
||||
if !errorlevel! neq 0 (
|
||||
echo ERROR: Neither pwsh nor powershell was found.
|
||||
del /Q "!PATCH_SCRIPT_DIR!\patch2.ps1" >nul 2>&1
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
!_my_shell! -NoProfile -ExecutionPolicy Bypass -File "!PATCH_SCRIPT_DIR!\patch2.ps1" -GameRoot "!GAME_ROOT!"
|
||||
set "GU_PATCH_EXIT=!errorlevel!"
|
||||
del /Q "!PATCH_SCRIPT_DIR!\patch2.ps1" >nul 2>&1
|
||||
|
||||
exit /b !GU_PATCH_EXIT!
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@
|
|||
# Enable error handling
|
||||
set -e
|
||||
|
||||
# Copy patch.sh to a new file in gameupdate
|
||||
cp "gameupdate/patch.sh" "gameupdate/patch2.sh"
|
||||
# Same layout as GameUpdate.bat: patch assets live under ./gameupdate/ next to this file.
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
GAME_ROOT="$SCRIPT_DIR"
|
||||
PATCH_DIR="$SCRIPT_DIR/gameupdate"
|
||||
|
||||
# Run the new file
|
||||
bash "gameupdate/patch2.sh"
|
||||
|
||||
# Delete the new file
|
||||
rm "gameupdate/patch2.sh"
|
||||
cd "$GAME_ROOT"
|
||||
cp "$PATCH_DIR/patch.sh" "$PATCH_DIR/patch2.sh"
|
||||
bash "$PATCH_DIR/patch2.sh"
|
||||
rm "$PATCH_DIR/patch2.sh"
|
||||
|
|
|
|||
769
gameupdate/patch.ps1
Normal file
769
gameupdate/patch.ps1
Normal file
|
|
@ -0,0 +1,769 @@
|
|||
#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 {
|
||||
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
|
||||
)
|
||||
$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(
|
||||
[Parameter(Mandatory = $true, Position = 0)][object]$Rows,
|
||||
[Parameter(Mandatory = $true, Position = 1)][int]$TargetHeight
|
||||
)
|
||||
[string[]]$lines = ConvertTo-LineStringArray -Value $Rows
|
||||
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 {
|
||||
# Refresh via generate_icon_ascii.ps1 when assets/icon.png changes.
|
||||
$raw = @'
|
||||
# %#
|
||||
%:.-@ #..=
|
||||
#:.-*:.:# *..=*..-%
|
||||
*=.:.+# %% @*-:::+#
|
||||
*-# +...=# =-%
|
||||
% #..==..:# %@
|
||||
%..* -.:%%#=..=##*##%% %*=:...+
|
||||
*:.=+.:= ..=%##%*:..::.....:=+:..-=*:.:
|
||||
*=:--.=* ..=%###%########*+=:.-+#%%%:.-
|
||||
-:% #..=%###%%%%%###%%%%%#%%%#%*..+
|
||||
%..=%##-.=*=-*%#####%%%%%#%#:.:
|
||||
+.:###%+....:*%###:.=*=-*%%-..%
|
||||
-.:%#%+..:..=%####=....-#%%+..%
|
||||
*..*%##**%*-=###%=..:..=%#%*..#
|
||||
=..*%%%%#%%%#####*#%*-+%%%-..
|
||||
+..-*#%%%%######%%#%%%%*:..#
|
||||
%+...-+*##%%%%%%%%#*+-..=%
|
||||
%..:.....::---::....:*%
|
||||
:.:%%#*+=--::::-=+:.:*
|
||||
%..+%#%%%%%%%%%%%%%%+..-@ #-:::+@
|
||||
#..+%###############%#:.:% #..=*:..#
|
||||
..=%################%#:..% %..+%%-..@
|
||||
%..:%###%#%##%%#%#####%#:.- *..*%*..+
|
||||
#....*%##-.+%%#:.+%##%#%%*..* :.-%%:.-
|
||||
%..=..-%#%:..#%+..+%#%+::#%:.- :.-%%:.-
|
||||
-.:%=..*%%*..+%:.:%#%#..:#%=.. #..+%*..+
|
||||
..=%#..:%%#..-#..=%#%-..*%%=..*:.-%%-..@
|
||||
-.-%%+..+%%-.:+..+%%+..+%#%-....=%%-..#
|
||||
+..*%*..:%%=..=..*%%:..*%%#...=#%+:.:#
|
||||
..-**..:+**=..:..+**+:..**+..=*=:..+
|
||||
+..............................:=#
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%@
|
||||
'@
|
||||
return [string[]]@($raw -split '\r?\n')
|
||||
}
|
||||
|
||||
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 -Rows $titleRows -TargetHeight $targetH
|
||||
$catPadded = Pad-LineBlockToHeight -Rows $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('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 ' ')
|
||||
}
|
||||
|
||||
$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 ' ')
|
||||
}
|
||||
|
||||
$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(
|
||||
[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
|
||||
}
|
||||
|
||||
$outFull = [System.IO.Path]::GetFullPath($OutFile)
|
||||
Initialize-GameUpdateConsoleCloseHelper
|
||||
|
||||
$prevPref = $ProgressPreference
|
||||
$completed = $false
|
||||
$registeredCloseHandler = $false
|
||||
try {
|
||||
if ($script:GameUpdateConsoleCloseHelperWorks) {
|
||||
$env:GAMEUPDATE_REPO_ZIP_PATH = $outFull
|
||||
[GameUpdate.ConsoleCloseHelper]::Register()
|
||||
$registeredCloseHandler = $true
|
||||
}
|
||||
|
||||
$ProgressPreference = 'Continue'
|
||||
Invoke-WebRequest -Uri $Url -OutFile $OutFile -Headers $Headers -UseBasicParsing
|
||||
$completed = $true
|
||||
}
|
||||
finally {
|
||||
$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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
$zipPath = Join-Path $Root ('dazedmtl_patch_' + [Guid]::NewGuid().ToString('N') + '.zip')
|
||||
Write-Host ('Downloading patch... ' + $zipPath)
|
||||
$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 {
|
||||
Invoke-PatchArchiveDownload -Url $archiveUrl -OutFile $zipPath -Headers $headers
|
||||
} | Out-Null
|
||||
$dlSw.Stop()
|
||||
$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)
|
||||
$mbps = ($bytes / 1MB) / $secs
|
||||
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)
|
||||
try {
|
||||
$hdr = New-Object byte[] 4
|
||||
if ($fs.Read($hdr, 0, 4) -lt 4 -or $hdr[0] -ne 0x50 -or $hdr[1] -ne 0x4B) {
|
||||
$zipHeaderBad = $true
|
||||
$zipHeaderHint = Get-InvalidZipDownloadHint -LiteralPath $zipPath
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$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) {
|
||||
Remove-FileOrDirectoryViaCmd -LiteralPath $stage
|
||||
}
|
||||
New-Item -ItemType Directory -Path $stage | Out-Null
|
||||
try {
|
||||
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
|
||||
}
|
||||
finally {
|
||||
if (Test-Path -LiteralPath $zipPath) {
|
||||
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-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 {
|
||||
param([string]$Root)
|
||||
$zipPath = Join-Path $Root 'repo.zip'
|
||||
$legacyTmp = Join-Path $Root '_patch_extract_tmp'
|
||||
|
||||
Remove-StalePatchZipArtifactsInGameRoot -Root $Root
|
||||
|
||||
if (Test-Path -LiteralPath $zipPath) {
|
||||
Write-Host ' Removing repo.zip...'
|
||||
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
|
||||
|
||||
Remove-StalePatchZipArtifactsInGameRoot -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)
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(pwd)"
|
||||
CONFIG_FILE="$SCRIPT_DIR/patch-config.txt"
|
||||
STATE_FILE="$SCRIPT_DIR/previous_patch_sha.txt"
|
||||
GITGUD_API="https://gitgud.io/api/v4"
|
||||
API_HEADERS=( -H "User-Agent: GameUpdate/1.0" )
|
||||
|
||||
check_dependency() {
|
||||
if ! command -v "$1" > /dev/null 2>&1; then
|
||||
|
|
@ -32,11 +35,61 @@ echo "Root directory: $ROOT_DIR"
|
|||
echo "Config file path: $CONFIG_FILE"
|
||||
|
||||
# Read configuration from file
|
||||
# shellcheck disable=SC1090
|
||||
. "$CONFIG_FILE"
|
||||
|
||||
if [ -z "${username:-}" ]; then
|
||||
echo "ERROR: 'username=' is missing in gameupdate/patch-config.txt"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${repo:-}" ]; then
|
||||
echo "ERROR: 'repo=' is missing in gameupdate/patch-config.txt"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${branch:-}" ]; then
|
||||
echo "ERROR: 'branch=' is missing in gameupdate/patch-config.txt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RETRIES="${GAMEUPDATE_DL_ATTEMPTS:-2}"
|
||||
if ! [[ "$RETRIES" =~ ^[1-9][0-9]*$ ]]; then
|
||||
RETRIES=2
|
||||
fi
|
||||
|
||||
retry_cmd() {
|
||||
local name="$1"
|
||||
shift
|
||||
local attempt=1
|
||||
while true; do
|
||||
if "$@"; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$attempt" -ge "$RETRIES" ]; then
|
||||
echo "$name failed after $attempt attempt(s)."
|
||||
return 1
|
||||
fi
|
||||
echo "$name failed ($attempt/$RETRIES), retrying..."
|
||||
attempt=$((attempt + 1))
|
||||
sleep 2
|
||||
done
|
||||
}
|
||||
|
||||
project_enc=$(jq -nr --arg ns "$username" --arg rp "$repo" '$ns + "/" + $rp | @uri')
|
||||
branch_enc=$(jq -nr --arg b "$branch" '$b | @uri')
|
||||
|
||||
# Get the latest hash
|
||||
echo "Getting latest commit SHA hash"
|
||||
latest_patch_sha=$(curl -s "https://gitgud.io/api/v4/projects/$username%2F$repo/repository/branches/$branch" | jq -r '.commit.id')
|
||||
latest_patch_sha="$(
|
||||
retry_cmd "Resolve latest patch SHA" \
|
||||
curl -fsSL "${API_HEADERS[@]}" \
|
||||
"${GITGUD_API}/projects/${project_enc}/repository/branches/${branch_enc}" \
|
||||
| jq -r '.commit.id'
|
||||
)"
|
||||
latest_patch_sha="$(printf '%s' "$latest_patch_sha" | tr -d '[:space:]')"
|
||||
if [ -z "$latest_patch_sha" ] || [ "$latest_patch_sha" = "null" ]; then
|
||||
echo "PATCH_ERR:API:Latest commit SHA response was empty."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --------------------------------------------------------
|
||||
# PRE-SETUP: Ensure SRPG data and patch structure exists
|
||||
|
|
@ -49,92 +102,91 @@ if [ -f "$ROOT_DIR/data.dts" ]; then
|
|||
if [ -f "$UNPACKER" ]; then
|
||||
echo "[Pre-Setup] Running SRPG_Unpacker preparation steps..."
|
||||
|
||||
# Step 1: Unpack (once)
|
||||
if [ ! -d "$ROOT_DIR/data" ]; then
|
||||
if [ -f "$ROOT_DIR/data.dts" ]; then
|
||||
echo "[Pre-Setup] Step 1: Unpacking data.dts -> data"
|
||||
( cd "$ROOT_DIR" && "$UNPACKER" -o data data.dts ) || echo "[Pre-Setup] ERROR: Unpack failed."
|
||||
# Step 1: Unpack (once) — mirror patch.ps1: unpack if no data/ or no data/project.dat
|
||||
if [ ! -d "$ROOT_DIR/data" ] || [ ! -f "$ROOT_DIR/data/project.dat" ]; then
|
||||
if [ -f "$ROOT_DIR/data.dts" ]; then
|
||||
echo "[Pre-Setup] Step 1: Unpacking data.dts -> data"
|
||||
( cd "$ROOT_DIR" && "$UNPACKER" -o data data.dts ) || echo "[Pre-Setup] ERROR: Unpack failed."
|
||||
else
|
||||
echo "[Pre-Setup] Step 1: Skipping unpack (no data folder and no data.dts found)."
|
||||
fi
|
||||
else
|
||||
echo "[Pre-Setup] Step 1: Skipping unpack (no data folder and no data.dts found)."
|
||||
echo "[Pre-Setup] Step 1: data folder exists; skipping unpack."
|
||||
fi
|
||||
else
|
||||
echo "[Pre-Setup] Step 1: data folder exists; skipping unpack."
|
||||
fi
|
||||
|
||||
# Step 2: Create Patch (once)
|
||||
if [ ! -d "$ROOT_DIR/patch" ]; then
|
||||
if [ -f "$ROOT_DIR/data/project.dat" ]; then
|
||||
echo "[Pre-Setup] Step 2: Creating patch from data/project.dat"
|
||||
( cd "$ROOT_DIR" && "$UNPACKER" ./data/project.dat -c ) || echo "[Pre-Setup] ERROR: Create Patch failed."
|
||||
else
|
||||
echo "[Pre-Setup] Step 2: Skipping create patch (data/project.dat not found)."
|
||||
fi
|
||||
# Step 2: Create Patch (once)
|
||||
if [ ! -d "$ROOT_DIR/patch" ]; then
|
||||
if [ -f "$ROOT_DIR/data/project.dat" ]; then
|
||||
echo "[Pre-Setup] Step 2: Creating patch from data/project.dat"
|
||||
( cd "$ROOT_DIR" && "$UNPACKER" ./data/project.dat -c ) || echo "[Pre-Setup] ERROR: Create Patch failed."
|
||||
else
|
||||
echo "[Pre-Setup] Step 2: Skipping create patch (data/project.dat not found)."
|
||||
fi
|
||||
else
|
||||
echo "[Pre-Setup] Step 2: patch folder exists; skipping create."
|
||||
fi
|
||||
else
|
||||
echo "[Pre-Setup] SRPG_Unpacker.exe not found in root; skipping pre-setup steps."
|
||||
fi
|
||||
else
|
||||
echo "[Pre-Setup] data.dts not found; skipping pre-setup SRPG steps."
|
||||
echo "[Pre-Setup] SRPG_Unpacker.exe not found in root; skipping pre-setup steps."
|
||||
fi
|
||||
fi
|
||||
|
||||
download_extract() {
|
||||
# Download zip file into root via GitLab API (bypasses Cloudflare DDoS protection)
|
||||
echo "Downloading latest patch..."
|
||||
curl -sL -A "git/2.0" "https://gitgud.io/api/v4/projects/$username%2F$repo/repository/archive.zip?sha=$branch" -o "$ROOT_DIR/repo.zip"
|
||||
if [ $? -ne 0 ]; then
|
||||
archive_sha_enc=$(jq -nr --arg s "$latest_patch_sha" '$s | @uri')
|
||||
if ! retry_cmd "Download archive with curl" \
|
||||
curl -fSL --progress-bar "${API_HEADERS[@]}" \
|
||||
"${GITGUD_API}/projects/${project_enc}/repository/archive.zip?sha=${archive_sha_enc}" \
|
||||
-o "$ROOT_DIR/repo.zip"; then
|
||||
echo "Download failed!"
|
||||
rm -f "$ROOT_DIR/repo.zip"
|
||||
rm -rf "$ROOT_DIR/$repo-$branch"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract contents, overwriting conflicts into root
|
||||
TMP_EX=$(mktemp -d)
|
||||
|
||||
echo "Extracting..."
|
||||
unzip -qo "$ROOT_DIR/repo.zip" -d "$ROOT_DIR"
|
||||
if [ $? -ne 0 ]; then
|
||||
if ! unzip -qo "$ROOT_DIR/repo.zip" -d "$TMP_EX"; then
|
||||
echo "Extraction failed!"
|
||||
rm -rf "$TMP_EX"
|
||||
rm -f "$ROOT_DIR/repo.zip"
|
||||
return 1
|
||||
fi
|
||||
|
||||
inner=""
|
||||
for d in "$TMP_EX"/*; do
|
||||
if [ -d "$d" ]; then
|
||||
inner="$d"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$inner" ]; then
|
||||
echo "Archive had no root folder!"
|
||||
rm -rf "$TMP_EX"
|
||||
rm -f "$ROOT_DIR/repo.zip"
|
||||
rm -rf "$ROOT_DIR/$repo-$branch"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Applying patch..."
|
||||
# API zip uses a different folder name (repo-branch-sha), find it dynamically
|
||||
EXTRACTED_DIR=$(find "$ROOT_DIR" -maxdepth 1 -type d -name "${repo}-*" | head -1)
|
||||
if [ -z "$EXTRACTED_DIR" ]; then
|
||||
echo "Patch application failed - extracted folder not found!"
|
||||
rm -f "$ROOT_DIR/repo.zip"
|
||||
return 1
|
||||
fi
|
||||
cp -r "$EXTRACTED_DIR/"* "$ROOT_DIR/"
|
||||
if [ $? -ne 0 ]; then
|
||||
if ! cp -r "$inner"/* "$ROOT_DIR/"; then
|
||||
echo "Patch application failed!"
|
||||
rm -rf "$TMP_EX"
|
||||
rm -f "$ROOT_DIR/repo.zip"
|
||||
rm -rf "$ROOT_DIR/$repo-$branch"
|
||||
return 1
|
||||
fi
|
||||
|
||||
rm -rf "$TMP_EX"
|
||||
|
||||
echo "Cleaning up..."
|
||||
rm -f "$ROOT_DIR/repo.zip"
|
||||
[ -n "$EXTRACTED_DIR" ] && rm -rf "$EXTRACTED_DIR"
|
||||
rm -f "$ROOT_DIR/latest_patch_sha.txt"
|
||||
|
||||
# Store latest SHA for next check in gameupdate
|
||||
echo "$latest_patch_sha" > "$SCRIPT_DIR/previous_patch_sha.txt"
|
||||
# --------------------------------------------------------
|
||||
# POST-APPLY: Run Steps 3 and 4 after patch files are merged
|
||||
# --------------------------------------------------------
|
||||
UNPACKER="$ROOT_DIR/SRPG_Unpacker.exe"
|
||||
if [ -f "$ROOT_DIR/data.dts" ]; then
|
||||
if [ -f "$UNPACKER" ]; then
|
||||
echo "Running SRPG_Unpacker apply/pack steps..."
|
||||
|
||||
# --------------------------------------------------------
|
||||
# POST-APPLY: Run Steps 3 and 4 after patch files are merged
|
||||
# 3) Apply Patch to data/project.dat
|
||||
# 4) Pack data back into data.dts
|
||||
# --------------------------------------------------------
|
||||
UNPACKER="$ROOT_DIR/SRPG_Unpacker.exe"
|
||||
if [ -f "$ROOT_DIR/data.dts" ]; then
|
||||
if [ -f "$UNPACKER" ]; then
|
||||
echo "Running SRPG_Unpacker apply/pack steps..."
|
||||
|
||||
# Step 3: Apply Patch
|
||||
if [ -f "$ROOT_DIR/data/project.dat" ]; then
|
||||
echo "Step 3: Applying patch to data/project.dat"
|
||||
( cd "$ROOT_DIR" && "$UNPACKER" ./data/project.dat -a ) || echo "ERROR: Apply Patch failed."
|
||||
|
|
@ -142,35 +194,31 @@ download_extract() {
|
|||
echo "ERROR: data/project.dat not found; cannot apply patch."
|
||||
fi
|
||||
|
||||
# Step 4: Pack
|
||||
if [ -d "$ROOT_DIR/data" ]; then
|
||||
echo "Step 4: Packing data -> data.dts"
|
||||
( cd "$ROOT_DIR" && "$UNPACKER" -o data.dts data ) || echo "WARNING: Pack failed."
|
||||
else
|
||||
echo "Step 4: Skipping pack (data folder not found)."
|
||||
fi
|
||||
else
|
||||
echo "SRPG_Unpacker.exe not found in root; skipping SRPG patch steps."
|
||||
fi
|
||||
else
|
||||
echo "data.dts not found; skipping SRPG patch steps."
|
||||
fi
|
||||
else
|
||||
echo "Step 4: Skipping pack (data folder not found)."
|
||||
fi
|
||||
else
|
||||
echo "SRPG_Unpacker.exe not found in root; skipping SRPG patch steps."
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$latest_patch_sha" > "$STATE_FILE"
|
||||
}
|
||||
|
||||
# Check if previous_patch_sha.txt exists in gameupdate
|
||||
if [ ! -f "$SCRIPT_DIR/previous_patch_sha.txt" ]; then
|
||||
echo "Previous SHA hash not found!"
|
||||
echo "Assuming first time patching..."
|
||||
if [ ! -f "$STATE_FILE" ]; then
|
||||
echo "No saved patch version yet (first run); comparing with remote..."
|
||||
download_extract
|
||||
else
|
||||
# Read the stored SHA from previous check
|
||||
previous_patch_sha=$(cat "$SCRIPT_DIR/previous_patch_sha.txt")
|
||||
previous_patch_sha=$(tr -d '[:space:]' < "$STATE_FILE")
|
||||
|
||||
# Compare trimmed SHAs
|
||||
if [ "$latest_patch_sha" != "$previous_patch_sha" ]; then
|
||||
echo "Update found! Patching..."
|
||||
download_extract
|
||||
else
|
||||
echo "Patch is up to date."
|
||||
echo "Already up to date (matches latest patch commit)."
|
||||
fi
|
||||
fi
|
||||
|
|
|
|||
Loading…
Reference in a new issue