Updates to patcher
This commit is contained in:
parent
3f4ac69d88
commit
bf344de85a
5 changed files with 361 additions and 178 deletions
|
|
@ -1,11 +1,14 @@
|
|||
@echo off
|
||||
setlocal
|
||||
pushd "%~dp0"
|
||||
if not exist "gameupdate\patch.bat" (
|
||||
echo ERROR: gameupdate\patch.bat not found!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
call "gameupdate\patch.bat"
|
||||
popd
|
||||
endlocal
|
||||
|
||||
REM Copy GAMEUPDATE.bat to a new file
|
||||
copy "gameupdate\patch.bat" "gameupdate\patch2.bat"
|
||||
|
||||
REM Run the new file
|
||||
call "gameupdate\patch2.bat"
|
||||
|
||||
REM Delete the new file
|
||||
del "gameupdate\patch2.bat"
|
||||
|
||||
endlocal
|
||||
@echo on
|
||||
|
|
@ -5,11 +5,15 @@
|
|||
|
||||
## Future Patching
|
||||
1. Run GAMEUPDATE.bat to auto patch.
|
||||
2. Keep the whole `gameupdate` folder together when you ship the game—`patch.bat` and `patch-download.ps1` must sit side by side.
|
||||
3. Optional: set `GAMEUPDATE_PROMPT_PWSH=1` before running `GameUpdate.bat` if you want users to be prompted to install PowerShell 7 via winget.
|
||||
4. Optional: set `GAMEUPDATE_DL_ATTEMPTS` (default `2`) to control retries for API checks/downloads. Lower values fail faster; higher values tolerate flaky networks.
|
||||
|
||||
# Troubleshooting
|
||||
**GAMEUPDATE.bat doesn't update and closes immediately**
|
||||
1. Make sure your path doesn't contain any Japanese characters or lots of whitespace.
|
||||
2. Make sure you actually have permissions in the folder
|
||||
3. Auto-update calls GitLab’s public HTTP API (`/api/v4/...`), not the web “Download ZIP” URL—no account or token is required for public patch repos.
|
||||
|
||||
For WOLF RPG games, if you downloaded the game off of DLSite, you will need to do some extra steps to patch it. This is because there is a "master" file called Data.wolf that will take priority over the english patch files. You will need this file to be a folder before patching will work.
|
||||
|
||||
|
|
|
|||
159
gameupdate/patch-download.ps1
Normal file
159
gameupdate/patch-download.ps1
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
#Requires -Version 5.1
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$GameRoot,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$StateFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$exitCode = 1
|
||||
|
||||
if ($PSVersionTable.PSEdition -ne 'Core') {
|
||||
try {
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
|
||||
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 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
|
||||
}
|
||||
|
||||
# Fast path: let Invoke-WebRequest handle transfer and progress internally.
|
||||
# Custom stream/polling progress gives richer stats but adds measurable overhead.
|
||||
$prevPref = $ProgressPreference
|
||||
try {
|
||||
$ProgressPreference = 'Continue'
|
||||
Invoke-WebRequest -Uri $Url -OutFile $OutFile -Headers $Headers -UseBasicParsing
|
||||
}
|
||||
finally {
|
||||
$ProgressPreference = $prevPref
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Set-Location -LiteralPath $GameRoot
|
||||
|
||||
if (-not $env:GU_USERNAME -or -not $env:GU_REPO -or -not $env:GU_BRANCH) {
|
||||
throw 'PATCH_ERR:CONFIG:patch.bat must set GU_USERNAME, GU_REPO, and GU_BRANCH.'
|
||||
}
|
||||
|
||||
$id = [uri]::EscapeDataString($env:GU_USERNAME + '/' + $env:GU_REPO)
|
||||
$branch = [uri]::EscapeDataString($env:GU_BRANCH)
|
||||
$ua = 'DazedMTL-Patcher-1.0'
|
||||
$headers = @{ 'User-Agent' = $ua }
|
||||
$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/$branch") -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 $StateFile) {
|
||||
$previousSha = (Get-Content -LiteralPath $StateFile -Raw).Trim()
|
||||
} else {
|
||||
Write-Host 'Previous SHA hash not found. Assuming first time patching...'
|
||||
}
|
||||
if ($previousSha -eq $latestSha) {
|
||||
$exitCode = 10
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host 'Update found! Patching...'
|
||||
Write-Host 'Downloading latest patch... (backend: iwr, GitLab API)'
|
||||
$zipPath = Join-Path $PWD.Path 'repo.zip'
|
||||
$stage = Join-Path $PWD.Path '_patch_extract_tmp'
|
||||
$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) {
|
||||
throw 'PATCH_ERR:ZIP:Download is not a valid ZIP (error page or empty file).'
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$fs.Dispose()
|
||||
}
|
||||
|
||||
if (Test-Path -LiteralPath $stage) { Remove-Item -LiteralPath $stage -Recurse -Force }
|
||||
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
|
||||
}
|
||||
finally {
|
||||
if (Test-Path -LiteralPath $stage) { Remove-Item -LiteralPath $stage -Recurse -Force }
|
||||
}
|
||||
|
||||
Set-Content -LiteralPath $StateFile -Value $latestSha -Encoding Ascii
|
||||
$exitCode = 0
|
||||
}
|
||||
catch {
|
||||
Write-Host ''
|
||||
Write-Host ($_.Exception.Message)
|
||||
if ($_.InvocationInfo.PositionMessage) { Write-Host $_.InvocationInfo.PositionMessage }
|
||||
$exitCode = 1
|
||||
}
|
||||
|
||||
exit $exitCode
|
||||
|
|
@ -29,41 +29,38 @@ echo Using root directory: "%ROOT_DIR%"
|
|||
echo Using config: "%CONFIG_FILE%"
|
||||
|
||||
echo Checking for pwsh...
|
||||
set _my_shell=pwsh
|
||||
|
||||
REM Check if pwsh.exe exists
|
||||
set "_my_shell=pwsh"
|
||||
where /q !_my_shell!
|
||||
if !errorlevel! neq 0 (
|
||||
echo pwsh not found.
|
||||
echo.
|
||||
echo PowerShell 7 ^(pwsh^) is faster and recommended.
|
||||
set /p "INSTALL_PWSH=Would you like to install it 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
|
||||
if !errorlevel! neq 0 (
|
||||
echo Install failed or winget not available. Falling back to powershell...
|
||||
set _my_shell=powershell
|
||||
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 PowerShell 7 installed successfully.
|
||||
echo Please re-run GameUpdate.bat to use pwsh.
|
||||
pause
|
||||
exit /b 0
|
||||
echo Skipping install; falling back to powershell.
|
||||
set "_my_shell=powershell"
|
||||
)
|
||||
) else (
|
||||
echo Skipping install. Falling back to powershell...
|
||||
set _my_shell=powershell
|
||||
echo Tip: Set GAMEUPDATE_PROMPT_PWSH=1 to offer PowerShell 7 install.
|
||||
echo Falling back to powershell...
|
||||
set "_my_shell=powershell"
|
||||
)
|
||||
|
||||
REM Check if powershell.exe exists
|
||||
echo Checking for powershell...
|
||||
where /q !_my_shell!
|
||||
if !errorlevel! neq 0 (
|
||||
echo.Error: Powershell not found!
|
||||
echo ERROR: Neither pwsh nor powershell was found.
|
||||
pause
|
||||
exit /B 1
|
||||
) else (
|
||||
echo powershell found.
|
||||
exit /b 1
|
||||
)
|
||||
) else (
|
||||
echo pwsh found.
|
||||
|
|
@ -85,6 +82,28 @@ for /f "usebackq tokens=1,2 delims==" %%a in ("%CONFIG_FILE%") do (
|
|||
if "%%a"=="branch" set "branch=%%b"
|
||||
)
|
||||
|
||||
REM Validate required config keys early for clearer errors
|
||||
if "%username%"=="" (
|
||||
echo ERROR: 'username=' is missing in gameupdate\patch-config.txt
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
if "%repo%"=="" (
|
||||
echo ERROR: 'repo=' is missing in gameupdate\patch-config.txt
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
if "%branch%"=="" (
|
||||
echo ERROR: 'branch=' is missing in gameupdate\patch-config.txt
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM For PowerShell (proper URL encoding; avoids cmd %% pitfalls)
|
||||
set "GU_USERNAME=%username%"
|
||||
set "GU_REPO=%repo%"
|
||||
set "GU_BRANCH=%branch%"
|
||||
|
||||
REM --------------------------------------------------------
|
||||
REM PRE-SETUP: Ensure SRPG data and patch structure exists
|
||||
REM Run Steps 1 and 2 BEFORE pulling repo patch to avoid overwriting updates
|
||||
|
|
@ -144,83 +163,31 @@ if exist "%ROOT_DIR%\data.dts" (
|
|||
echo [Pre-Setup] data.dts not found; skipping pre-setup SRPG steps.
|
||||
)
|
||||
|
||||
REM Get the latest hash
|
||||
echo "Getting latest commit SHA hash"
|
||||
!_my_shell! -Command "(Invoke-RestMethod -Uri 'https://gitgud.io/api/v4/projects/%username%%%2F%repo%/repository/branches/%branch%').commit.id" > "%ROOT_DIR%\latest_patch_sha.txt"
|
||||
|
||||
REM Read the latest SHA from the file
|
||||
set /p latest_patch_sha=<"%ROOT_DIR%\latest_patch_sha.txt"
|
||||
|
||||
REM Check if previous_patch_sha.txt exists in gameupdate
|
||||
if not exist "%SCRIPT_DIR%previous_patch_sha.txt" (
|
||||
echo "Previous SHA hash not found!"
|
||||
echo "Assuming first time patching..."
|
||||
goto download_extract
|
||||
)
|
||||
|
||||
REM Read the stored SHA from previous check
|
||||
set /p previous_patch_sha=<"%SCRIPT_DIR%previous_patch_sha.txt"
|
||||
|
||||
REM Trim whitespace from SHA strings
|
||||
set "previous_patch_sha=%previous_patch_sha: =%"
|
||||
set "latest_patch_sha=%latest_patch_sha: =%"
|
||||
|
||||
REM Compare trimmed SHAs
|
||||
if "%latest_patch_sha%" neq "%previous_patch_sha%" (
|
||||
echo "Update found! Patching..."
|
||||
goto download_extract
|
||||
) else (
|
||||
echo "Patch is up to date."
|
||||
)
|
||||
|
||||
REM Delete latest_patch_sha.txt
|
||||
del "%ROOT_DIR%\latest_patch_sha.txt"
|
||||
|
||||
endlocal
|
||||
pause
|
||||
exit /b
|
||||
|
||||
:download_extract
|
||||
|
||||
REM Escape single quotes in paths
|
||||
set "escaped_root=%ROOT_DIR:'=''%"
|
||||
|
||||
REM Download zip file to root via GitLab API (bypasses Cloudflare DDoS protection)
|
||||
echo "Downloading latest patch..."
|
||||
!_my_shell! -Command "Set-Location -LiteralPath '%escaped_root%'; $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -Uri 'https://gitgud.io/api/v4/projects/%username%%%2F%repo%/repository/archive.zip?sha=%branch%' -Headers @{'User-Agent'='git/2.0'} -OutFile 'repo.zip'"
|
||||
if !errorlevel! neq 0 (
|
||||
REM Download/extract via patch-download.ps1 (IWR-based); Bypass avoids ExecutionPolicy prompts
|
||||
if not exist "%SCRIPT_DIR%patch-download.ps1" (
|
||||
echo ERROR: patch-download.ps1 not found next to patch.bat in the gameupdate folder.
|
||||
pause
|
||||
exit /b
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM Extract contents, overwriting conflicts into root
|
||||
echo "Extracting..."
|
||||
!_my_shell! -Command "Set-Location -LiteralPath '%escaped_root%'; $ProgressPreference = 'SilentlyContinue'; Expand-Archive -Path '.\repo.zip' -DestinationPath '.' -Force"
|
||||
if !errorlevel! neq 0 (
|
||||
echo Extraction failed!
|
||||
del "%ROOT_DIR%\repo.zip"
|
||||
rmdir /s /q "%ROOT_DIR%\%repo%-%branch%"
|
||||
!_my_shell! -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%patch-download.ps1" -GameRoot "%ROOT_DIR%" -StateFile "%SCRIPT_DIR%previous_patch_sha.txt"
|
||||
set "PATCH_DL_EXIT=!errorlevel!"
|
||||
if "!PATCH_DL_EXIT!"=="10" (
|
||||
echo Patch is up to date.
|
||||
endlocal
|
||||
pause
|
||||
exit /b
|
||||
exit /b 0
|
||||
)
|
||||
if not "!PATCH_DL_EXIT!"=="0" (
|
||||
echo Download or extraction failed!
|
||||
if exist "%ROOT_DIR%\repo.zip" del "%ROOT_DIR%\repo.zip"
|
||||
if exist "%ROOT_DIR%\_patch_extract_tmp" rmdir /s /q "%ROOT_DIR%\_patch_extract_tmp"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo "Applying patch..."
|
||||
REM API zip uses a different folder name (repo-branch-sha), find it dynamically
|
||||
set "EXTRACTED_DIR="
|
||||
for /d %%D in ("%ROOT_DIR%\%repo%-*") do set "EXTRACTED_DIR=%%D"
|
||||
if not defined EXTRACTED_DIR (
|
||||
echo Patch application failed - extracted folder not found!
|
||||
del "%ROOT_DIR%\repo.zip"
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
robocopy "!EXTRACTED_DIR!" "%ROOT_DIR%" /s /e /xf "GameUpdate.bat" "patch.bat"
|
||||
if !errorlevel! geq 8 (
|
||||
echo Patch application failed!
|
||||
del "%ROOT_DIR%\repo.zip"
|
||||
rmdir /s /q "!EXTRACTED_DIR!"
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
REM Files merged by Copy-Item above
|
||||
REM --------------------------------------------------------
|
||||
REM POST-APPLY: Run Steps 3 and 4 after patch files are merged
|
||||
REM 3) Apply Patch to data\project.dat
|
||||
|
|
@ -265,10 +232,7 @@ if exist "%ROOT_DIR%\data.dts" (
|
|||
REM Clean up
|
||||
echo "Cleaning up..."
|
||||
del "%ROOT_DIR%\repo.zip"
|
||||
if defined EXTRACTED_DIR rmdir /s /q "!EXTRACTED_DIR!"
|
||||
del "%ROOT_DIR%\latest_patch_sha.txt"
|
||||
REM Store latest SHA for next check in gameupdate
|
||||
echo %latest_patch_sha% > "%SCRIPT_DIR%previous_patch_sha.txt"
|
||||
if exist "%ROOT_DIR%\_patch_extract_tmp" rmdir /s /q "%ROOT_DIR%\_patch_extract_tmp"
|
||||
endlocal
|
||||
pause
|
||||
exit /b
|
||||
|
|
|
|||
|
|
@ -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,93 @@ 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.bat: 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
|
||||
else
|
||||
echo "[Pre-Setup] data.dts not found; skipping pre-setup SRPG steps."
|
||||
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,31 +196,30 @@ 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
|
||||
else
|
||||
echo "data.dts not found; skipping SRPG patch steps."
|
||||
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
|
||||
if [ ! -f "$STATE_FILE" ]; then
|
||||
echo "Previous SHA hash not found!"
|
||||
echo "Assuming first time patching..."
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in a new issue