From bf344de85a925009289833dbdb16d2e51ac93ff2 Mon Sep 17 00:00:00 2001 From: DazedAnon Date: Sat, 9 May 2026 16:19:09 -0500 Subject: [PATCH] Updates to patcher --- GameUpdate.bat | 21 ++-- README.md | 4 + gameupdate/patch-download.ps1 | 159 ++++++++++++++++++++++++++++ gameupdate/patch.bat | 162 +++++++++++----------------- gameupdate/patch.sh | 193 ++++++++++++++++++++++------------ 5 files changed, 361 insertions(+), 178 deletions(-) create mode 100644 gameupdate/patch-download.ps1 diff --git a/GameUpdate.bat b/GameUpdate.bat index 7610834..ab6b767 100644 --- a/GameUpdate.bat +++ b/GameUpdate.bat @@ -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 \ No newline at end of file + +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 \ No newline at end of file diff --git a/README.md b/README.md index a924ae1..b638701 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/gameupdate/patch-download.ps1 b/gameupdate/patch-download.ps1 new file mode 100644 index 0000000..8c6cf7e --- /dev/null +++ b/gameupdate/patch-download.ps1 @@ -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 diff --git a/gameupdate/patch.bat b/gameupdate/patch.bat index 021a442..85137ed 100644 --- a/gameupdate/patch.bat +++ b/gameupdate/patch.bat @@ -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 diff --git a/gameupdate/patch.sh b/gameupdate/patch.sh index 0902451..346bb0e 100644 --- a/gameupdate/patch.sh +++ b/gameupdate/patch.sh @@ -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