New patcher
This commit is contained in:
parent
984cfa0b2c
commit
fa15db7c01
13 changed files with 657 additions and 58 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -21,6 +21,7 @@
|
|||
!.gitignore
|
||||
!README.md
|
||||
!patch-config.txt
|
||||
!patch-config.example.txt
|
||||
!GameUpdate*
|
||||
!patch*
|
||||
!Game.dat
|
||||
|
|
@ -29,6 +30,8 @@
|
|||
!package.nw
|
||||
!SRPG_Unpacker.exe
|
||||
!(SRPG_Unpacker Patcher).bat
|
||||
!UberWolfCli.exe
|
||||
!UberWolfCli.LICENSE.txt
|
||||
!Data/BasicData/CommonEvent.dat
|
||||
!Data/BasicData/DataBase.dat
|
||||
!Data/BasicData/CDataBase.dat
|
||||
|
|
|
|||
65
README.md
65
README.md
|
|
@ -17,22 +17,67 @@
|
|||
2. Optional: set `GAMEUPDATE_PROMPT_PWSH=1` before running `GameUpdate.bat` if you want users to be prompted to install PowerShell 7 via winget.
|
||||
3. Optional: set `GAMEUPDATE_DL_ATTEMPTS` (default `2`) to control retries for API checks/downloads. Lower values fail faster; higher values tolerate flaky networks.
|
||||
|
||||
### patch-config.txt
|
||||
|
||||
Create `gameupdate/patch-config.txt` next to `patch.ps1` (see `patch-config.example.txt`).
|
||||
|
||||
In DazedMTLTool, set **Config → Game Update Defaults** (forge, host, org/username, branch) once. Step 1 **Copy gameupdate/** writes those into each game's `patch-config.txt`. You still set `repo=` per game.
|
||||
|
||||
```txt
|
||||
forge=gitlab
|
||||
host=gitgud.io
|
||||
username=YOUR_ORG_OR_USER
|
||||
repo=YOUR_PATCH_REPO
|
||||
branch=main
|
||||
```
|
||||
|
||||
| Key | Meaning |
|
||||
|-----|---------|
|
||||
| `forge` | `gitlab` (default), `github`, or `forgejo` (`gitea` also accepted) |
|
||||
| `host` | Hostname only. Defaults: `gitgud.io` / `github.com` / `codeberg.org` |
|
||||
| `username` | Owner / org / group (`owner=` / `org=` aliases work) |
|
||||
| `repo` | Repository name |
|
||||
| `branch` | Branch to track |
|
||||
|
||||
Examples:
|
||||
|
||||
```txt
|
||||
# GitLab / gitgud
|
||||
forge=gitlab
|
||||
host=gitgud.io
|
||||
username=myorg
|
||||
repo=cool-game-en
|
||||
branch=main
|
||||
|
||||
# GitHub
|
||||
forge=github
|
||||
host=github.com
|
||||
username=myorg
|
||||
repo=cool-game-en
|
||||
branch=main
|
||||
|
||||
# Forgejo / Codeberg
|
||||
forge=forgejo
|
||||
host=codeberg.org
|
||||
username=myorg
|
||||
repo=cool-game-en
|
||||
branch=main
|
||||
```
|
||||
|
||||
Older configs with only `username` / `repo` / `branch` still work (assumes GitLab on `gitgud.io`).
|
||||
|
||||
# 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.
|
||||
3. Auto-update calls the forge's public HTTP API (GitLab `/api/v4`, GitHub `/repos/...`, Forgejo `/api/v1`), not the web “Download ZIP” button - no account is required for public patch repos.
|
||||
|
||||
# Wolf Games
|
||||
1) Download the latest UberWolf.exe release from the following link:
|
||||
https://github.com/Sinflower/UberWolf/releases
|
||||
2) Drag Data.wolf onto UberWolf.exe. This will create a new folder called data.wolf~
|
||||
3) Rename the new data.wolf~ folder to Data
|
||||
4) Delete the Data.wolf file
|
||||
5) Delete previous_patch_sha.txt (this will exist if you ran GameUpdate.bat previously)
|
||||
6) Run GameUpdate.bat
|
||||
WOLF RPG installs from DLSite ship a master `Data.wolf` archive that takes priority over loose English patch files. `GameUpdate.bat` detects that and unpacks it automatically with the bundled `UberWolfCli.exe` (MIT, [Sinflower/UberWolf](https://github.com/Sinflower/UberWolf)), then renames `Data.wolf` to `Data.wolf.bak` so the patched loose `Data/` files load.
|
||||
|
||||
Just run **`GameUpdate.bat`**. No manual UberWolf download is required.
|
||||
|
||||
If unpack fails (rare Pro / protected builds), unpack once with [UberWolf](https://github.com/Sinflower/UberWolf/releases), ensure a loose `Data/` folder exists, rename or remove `Data.wolf`, delete `gameupdate/previous_patch_sha.txt`, and run `GameUpdate.bat` again.
|
||||
|
||||
# Edit/Contribute
|
||||
TLDR 3 steps.
|
||||
|
|
|
|||
21
UberWolfCli.LICENSE.txt
Normal file
21
UberWolfCli.LICENSE.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Sinflower
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
BIN
UberWolfCli.exe
Normal file
BIN
UberWolfCli.exe
Normal file
Binary file not shown.
28
gameupdate/patch-config.example.txt
Normal file
28
gameupdate/patch-config.example.txt
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# GameUpdate patch source (copy to patch-config.txt and edit).
|
||||
# Lines starting with # are comments. Keys are case-insensitive.
|
||||
#
|
||||
# Tip: set forge / host / username / branch once in DazedMTLTool
|
||||
# Config → Game Update Defaults. Step 1 "Copy gameupdate/" writes
|
||||
# those into this file automatically (you still set repo= per game).
|
||||
|
||||
# Where the patch repo lives:
|
||||
# gitlab - GitLab / gitgud.io / self-hosted GitLab (default)
|
||||
# github - GitHub.com or GitHub Enterprise
|
||||
# forgejo - Forgejo (also accepts gitea)
|
||||
forge=gitlab
|
||||
|
||||
# Host only (no https://). Defaults:
|
||||
# gitlab -> gitgud.io
|
||||
# github -> github.com
|
||||
# forgejo -> codeberg.org
|
||||
# Examples: gitgud.io gitlab.com github.com codeberg.org git.example.com
|
||||
host=gitgud.io
|
||||
|
||||
# Owner / org / group that owns the repo (set in Config; rarely changes)
|
||||
username=YOUR_ORG_OR_USER
|
||||
|
||||
# Repository name (per game)
|
||||
repo=YOUR_PATCH_REPO
|
||||
|
||||
# Branch to track
|
||||
branch=main
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
username=dazed-translations
|
||||
repo=magical-girls-runa-and-nanami
|
||||
branch=main
|
||||
# Generated by DazedMTLTool from Config → Game Update defaults.
|
||||
# Edit repo= per game. forge/host/username usually stay the same.
|
||||
forge=forgejo
|
||||
host=git.dazedtl.dev
|
||||
username=dazed-translations
|
||||
repo=YOUR_PATCH_REPO
|
||||
branch=main
|
||||
|
|
|
|||
|
|
@ -194,7 +194,13 @@ function Test-WrongWorkingDirectory {
|
|||
|
||||
function Read-PatchConfig {
|
||||
param([string]$ConfigPath)
|
||||
$cfg = @{ username = ''; repo = ''; branch = '' }
|
||||
$cfg = @{
|
||||
forge = 'gitlab'
|
||||
host = ''
|
||||
username = ''
|
||||
repo = ''
|
||||
branch = ''
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $ConfigPath)) {
|
||||
return $cfg
|
||||
}
|
||||
|
|
@ -206,7 +212,12 @@ function Read-PatchConfig {
|
|||
$k = $line.Substring(0, $eq).Trim().ToLowerInvariant()
|
||||
$v = if ($eq -lt $line.Length - 1) { $line.Substring($eq + 1).Trim() } else { '' }
|
||||
switch ($k) {
|
||||
'forge' { $cfg.forge = $v }
|
||||
'provider' { $cfg.forge = $v }
|
||||
'host' { $cfg.host = $v }
|
||||
'username' { $cfg.username = $v }
|
||||
'owner' { $cfg.username = $v }
|
||||
'org' { $cfg.username = $v }
|
||||
'repo' { $cfg.repo = $v }
|
||||
'branch' { $cfg.branch = $v }
|
||||
}
|
||||
|
|
@ -214,6 +225,115 @@ function Read-PatchConfig {
|
|||
return $cfg
|
||||
}
|
||||
|
||||
function Resolve-PatchForge {
|
||||
param([Parameter(Mandatory = $true)][hashtable]$Cfg)
|
||||
|
||||
$raw = ([string]$Cfg.forge).Trim().ToLowerInvariant()
|
||||
if ([string]::IsNullOrWhiteSpace($raw)) { $raw = 'gitlab' }
|
||||
|
||||
$kind = switch -Regex ($raw) {
|
||||
'^(gitlab|gl|gitgud)$' { 'gitlab' }
|
||||
'^(github|gh)$' { 'github' }
|
||||
'^(forgejo|gitea|fj|codeberg)$' { 'forgejo' }
|
||||
default { $null }
|
||||
}
|
||||
if (-not $kind) {
|
||||
throw ("PATCH_ERR:CONFIG:Unknown forge='{0}'. Use gitlab, github, or forgejo." -f $Cfg.forge)
|
||||
}
|
||||
|
||||
$hostName = ([string]$Cfg.host).Trim()
|
||||
$hostName = $hostName -replace '^https?://', ''
|
||||
$hostName = $hostName.TrimEnd('/')
|
||||
if ($hostName.Contains('/')) {
|
||||
$hostName = $hostName.Split('/')[0]
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($hostName)) {
|
||||
$hostName = switch ($kind) {
|
||||
'gitlab' { 'gitgud.io' }
|
||||
'github' { 'github.com' }
|
||||
'forgejo' { 'codeberg.org' }
|
||||
}
|
||||
}
|
||||
|
||||
$webBase = "https://$hostName"
|
||||
$apiBase = switch ($kind) {
|
||||
'gitlab' { "$webBase/api/v4" }
|
||||
'github' {
|
||||
if ($hostName -eq 'github.com') { 'https://api.github.com' }
|
||||
else { "$webBase/api/v3" }
|
||||
}
|
||||
'forgejo' { "$webBase/api/v1" }
|
||||
}
|
||||
|
||||
return @{
|
||||
kind = $kind
|
||||
host = $hostName
|
||||
webBase = $webBase
|
||||
apiBase = $apiBase
|
||||
username = ([string]$Cfg.username).Trim()
|
||||
repo = ([string]$Cfg.repo).Trim()
|
||||
branch = ([string]$Cfg.branch).Trim()
|
||||
}
|
||||
}
|
||||
|
||||
function New-PatchApiHeaders {
|
||||
param([Parameter(Mandatory = $true)][hashtable]$Resolved)
|
||||
$headers = @{ 'User-Agent' = 'DazedMTL-Patcher-1.0' }
|
||||
if ($Resolved.kind -eq 'github') {
|
||||
$headers['Accept'] = 'application/vnd.github+json'
|
||||
}
|
||||
return $headers
|
||||
}
|
||||
|
||||
function Get-PatchLatestSha {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][hashtable]$Resolved,
|
||||
[Parameter(Mandatory = $true)][hashtable]$Headers
|
||||
)
|
||||
$ownerEnc = [uri]::EscapeDataString($Resolved.username)
|
||||
$repoEnc = [uri]::EscapeDataString($Resolved.repo)
|
||||
$branchEnc = [uri]::EscapeDataString($Resolved.branch)
|
||||
|
||||
switch ($Resolved.kind) {
|
||||
'gitlab' {
|
||||
$id = [uri]::EscapeDataString($Resolved.username + '/' + $Resolved.repo)
|
||||
$uri = "$($Resolved.apiBase)/projects/$id/repository/branches/$branchEnc"
|
||||
return [string](Invoke-RestMethod -Uri $uri -Headers $Headers).commit.id
|
||||
}
|
||||
'github' {
|
||||
$uri = "$($Resolved.apiBase)/repos/$ownerEnc/$repoEnc/commits/$branchEnc"
|
||||
return [string](Invoke-RestMethod -Uri $uri -Headers $Headers).sha
|
||||
}
|
||||
'forgejo' {
|
||||
$uri = "$($Resolved.apiBase)/repos/$ownerEnc/$repoEnc/branches/$branchEnc"
|
||||
return [string](Invoke-RestMethod -Uri $uri -Headers $Headers).commit.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Get-PatchArchiveUrl {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][hashtable]$Resolved,
|
||||
[Parameter(Mandatory = $true)][string]$Sha
|
||||
)
|
||||
$ownerEnc = [uri]::EscapeDataString($Resolved.username)
|
||||
$repoEnc = [uri]::EscapeDataString($Resolved.repo)
|
||||
$shaEnc = [uri]::EscapeDataString($Sha)
|
||||
|
||||
switch ($Resolved.kind) {
|
||||
'gitlab' {
|
||||
$id = [uri]::EscapeDataString($Resolved.username + '/' + $Resolved.repo)
|
||||
return "$($Resolved.apiBase)/projects/$id/repository/archive.zip?sha=$shaEnc"
|
||||
}
|
||||
'github' {
|
||||
return "$($Resolved.apiBase)/repos/$ownerEnc/$repoEnc/zipball/$shaEnc"
|
||||
}
|
||||
'forgejo' {
|
||||
return "$($Resolved.apiBase)/repos/$ownerEnc/$repoEnc/archive/$shaEnc.zip"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-SrpgUnpacker {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$ExePath,
|
||||
|
|
@ -230,6 +350,140 @@ function Invoke-SrpgUnpacker {
|
|||
}
|
||||
}
|
||||
|
||||
function Test-WolfLooseDataReady {
|
||||
param([Parameter(Mandatory = $true)][string]$DataDir)
|
||||
if (-not (Test-Path -LiteralPath $DataDir -PathType Container)) {
|
||||
return $false
|
||||
}
|
||||
$markers = @(
|
||||
(Join-Path $DataDir 'BasicData\CommonEvent.dat'),
|
||||
(Join-Path $DataDir 'CommonEvent.dat')
|
||||
)
|
||||
foreach ($marker in $markers) {
|
||||
if (Test-Path -LiteralPath $marker -PathType Leaf) {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
if (@(Get-ChildItem -LiteralPath $DataDir -Filter '*.mps' -File -ErrorAction SilentlyContinue).Count -gt 0) {
|
||||
return $true
|
||||
}
|
||||
$basic = Join-Path $DataDir 'BasicData'
|
||||
if (Test-Path -LiteralPath $basic -PathType Container) {
|
||||
if (@(Get-ChildItem -LiteralPath $basic -Filter '*.dat' -File -ErrorAction SilentlyContinue).Count -gt 0) {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
function Find-UberWolfCli {
|
||||
param([Parameter(Mandatory = $true)][string]$Root)
|
||||
$candidates = @(
|
||||
(Join-Path $Root 'UberWolfCli.exe'),
|
||||
(Join-Path $Root 'gameupdate\UberWolfCli.exe')
|
||||
)
|
||||
foreach ($path in $candidates) {
|
||||
if (Test-Path -LiteralPath $path -PathType Leaf) {
|
||||
return $path
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
function Invoke-WolfPreSetup {
|
||||
<#
|
||||
WOLF installs often ship Data.wolf, which overrides loose patched Data/ files.
|
||||
Unpack once with UberWolfCli (bundled in the patch repo), then retire Data.wolf
|
||||
so GameUpdate can copy injected .mps/.dat on top.
|
||||
#>
|
||||
param([Parameter(Mandatory = $true)][string]$Root)
|
||||
|
||||
$dataWolf = $null
|
||||
foreach ($name in @('Data.wolf', 'data.wolf')) {
|
||||
$candidate = Join-Path $Root $name
|
||||
if (Test-Path -LiteralPath $candidate -PathType Leaf) {
|
||||
$dataWolf = $candidate
|
||||
break
|
||||
}
|
||||
}
|
||||
if (-not $dataWolf) {
|
||||
return
|
||||
}
|
||||
|
||||
$dataDir = Join-Path $Root 'Data'
|
||||
if (-not (Test-Path -LiteralPath $dataDir -PathType Container)) {
|
||||
$dataDirAlt = Join-Path $Root 'data'
|
||||
if (Test-Path -LiteralPath $dataDirAlt -PathType Container) {
|
||||
$dataDir = $dataDirAlt
|
||||
}
|
||||
}
|
||||
|
||||
if (Test-WolfLooseDataReady -DataDir $dataDir) {
|
||||
$bak = "$dataWolf.bak"
|
||||
if (-not (Test-Path -LiteralPath $bak)) {
|
||||
Write-Host '[Pre-Setup] Loose Data/ already present; retiring Data.wolf -> Data.wolf.bak'
|
||||
Rename-Item -LiteralPath $dataWolf -NewName ([IO.Path]::GetFileName($bak)) -Force
|
||||
}
|
||||
else {
|
||||
Write-Host '[Pre-Setup] Loose Data/ already present; removing leftover Data.wolf'
|
||||
Remove-Item -LiteralPath $dataWolf -Force
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
$cli = Find-UberWolfCli -Root $Root
|
||||
if (-not $cli) {
|
||||
Write-Host '[Pre-Setup] Data.wolf found but UberWolfCli.exe is missing.'
|
||||
Write-Host ' Copy UberWolfCli.exe into the game root (Step 1 gameupdate/) and re-run.'
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host '[Pre-Setup] Unpacking Data.wolf with UberWolfCli (one-time)...'
|
||||
$code = Invoke-SrpgUnpacker -ExePath $cli -WorkingDirectory $Root -Arguments @('-o', $dataWolf)
|
||||
if ($code -ne 0) {
|
||||
Write-Host ('[Pre-Setup] ERROR: UberWolfCli exited with code {0}.' -f $code)
|
||||
return
|
||||
}
|
||||
|
||||
# UberWolf writes next to the archive as <stem>/ (Data/). Also accept data.wolf~ leftovers.
|
||||
$unpacked = Join-Path $Root 'Data'
|
||||
if (-not (Test-Path -LiteralPath $unpacked -PathType Container)) {
|
||||
$unpacked = Join-Path $Root 'data'
|
||||
}
|
||||
if (-not (Test-WolfLooseDataReady -DataDir $unpacked)) {
|
||||
foreach ($alt in @('data.wolf~', 'Data.wolf~', 'Data~', 'data~')) {
|
||||
$altPath = Join-Path $Root $alt
|
||||
if (Test-Path -LiteralPath $altPath -PathType Container) {
|
||||
Write-Host ("[Pre-Setup] Promoting {0} -> Data\" -f $alt)
|
||||
if (Test-Path -LiteralPath (Join-Path $Root 'Data')) {
|
||||
Remove-FileOrDirectoryViaCmd -LiteralPath (Join-Path $Root 'Data')
|
||||
}
|
||||
Rename-Item -LiteralPath $altPath -NewName 'Data' -Force
|
||||
$unpacked = Join-Path $Root 'Data'
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-WolfLooseDataReady -DataDir $unpacked)) {
|
||||
Write-Host '[Pre-Setup] ERROR: Unpack finished but loose Data/ still looks empty.'
|
||||
return
|
||||
}
|
||||
|
||||
$bak = "$dataWolf.bak"
|
||||
if (Test-Path -LiteralPath $dataWolf -PathType Leaf) {
|
||||
if (-not (Test-Path -LiteralPath $bak)) {
|
||||
Write-Host '[Pre-Setup] Retiring Data.wolf -> Data.wolf.bak'
|
||||
Rename-Item -LiteralPath $dataWolf -NewName ([IO.Path]::GetFileName($bak)) -Force
|
||||
}
|
||||
else {
|
||||
Write-Host '[Pre-Setup] Removing Data.wolf so loose Data/ takes priority'
|
||||
Remove-Item -LiteralPath $dataWolf -Force
|
||||
}
|
||||
}
|
||||
Write-Host '[Pre-Setup] Wolf Data/ is ready for patching.'
|
||||
}
|
||||
|
||||
function Invoke-SrpgPreSetup {
|
||||
param([string]$Root)
|
||||
$unpacker = Join-Path $Root 'SRPG_Unpacker.exe'
|
||||
|
|
@ -512,9 +766,7 @@ 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
|
||||
[Parameter(Mandatory = $true)][hashtable]$Resolved
|
||||
)
|
||||
|
||||
if ($PSVersionTable.PSEdition -ne 'Core') {
|
||||
|
|
@ -526,13 +778,11 @@ function Invoke-PatchDownloadExtract {
|
|||
|
||||
Set-Location -LiteralPath $Root
|
||||
|
||||
$id = [uri]::EscapeDataString($Username + '/' + $Repo)
|
||||
$branchEnc = [uri]::EscapeDataString($Branch)
|
||||
$headers = @{ 'User-Agent' = 'DazedMTL-Patcher-1.0' }
|
||||
$headers = New-PatchApiHeaders -Resolved $Resolved
|
||||
$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
|
||||
Get-PatchLatestSha -Resolved $Resolved -Headers $headers
|
||||
}
|
||||
if (-not $latestSha) { throw 'PATCH_ERR:API:Latest commit SHA response was empty.' }
|
||||
$latestSha = ([string]$latestSha).Trim()
|
||||
|
|
@ -551,8 +801,7 @@ function Invoke-PatchDownloadExtract {
|
|||
$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"
|
||||
$archiveUrl = Get-PatchArchiveUrl -Resolved $Resolved -Sha $latestSha
|
||||
|
||||
$dlSw = [System.Diagnostics.Stopwatch]::StartNew()
|
||||
Invoke-WithRetry -Name 'Download archive with Invoke-WebRequest' -Attempts $dlAttempts -Action {
|
||||
|
|
@ -599,7 +848,21 @@ function Invoke-PatchDownloadExtract {
|
|||
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
|
||||
$stagedRoot = $dirs[0].FullName
|
||||
|
||||
# Stage UberWolfCli into the game root before unpack so first-time
|
||||
# players can convert Data.wolf -> Data/ before English files land.
|
||||
foreach ($cliName in @('UberWolfCli.exe', 'UberWolfCli.LICENSE.txt')) {
|
||||
$stagedCli = Join-Path $stagedRoot $cliName
|
||||
$destCli = Join-Path $Root $cliName
|
||||
if ((Test-Path -LiteralPath $stagedCli -PathType Leaf) -and
|
||||
-not (Test-Path -LiteralPath $destCli -PathType Leaf)) {
|
||||
Copy-Item -LiteralPath $stagedCli -Destination $destCli -Force
|
||||
}
|
||||
}
|
||||
Invoke-WolfPreSetup -Root $Root
|
||||
|
||||
Copy-Item -Path (Join-Path $stagedRoot '*') -Destination $PWD.Path -Recurse -Force
|
||||
}
|
||||
finally {
|
||||
if (Test-Path -LiteralPath $zipPath) {
|
||||
|
|
@ -717,8 +980,19 @@ try {
|
|||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ('Pulling patch from https://gitgud.io/{0}/{1} (branch: {2})' -f $cfg.username, $cfg.repo, $cfg.branch)
|
||||
try {
|
||||
$resolved = Resolve-PatchForge -Cfg $cfg
|
||||
}
|
||||
catch {
|
||||
Write-Host $_.Exception.Message
|
||||
Wait-ConsolePause
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ('Pulling patch from {0}/{1}/{2} ({3}, branch: {4})' -f `
|
||||
$resolved.webBase, $resolved.username, $resolved.repo, $resolved.kind, $resolved.branch)
|
||||
|
||||
Invoke-WolfPreSetup -Root $GameRoot
|
||||
Invoke-SrpgPreSetup -Root $GameRoot
|
||||
|
||||
$stateFile = Join-Path $PatchBundleRoot 'previous_patch_sha.txt'
|
||||
|
|
@ -726,7 +1000,7 @@ try {
|
|||
$patchDlExit = 1
|
||||
try {
|
||||
$patchDlExit = Invoke-PatchDownloadExtract -Root $GameRoot -StateFilePath $stateFile `
|
||||
-Username $cfg.username -Repo $cfg.repo -Branch $cfg.branch
|
||||
-Resolved $resolved
|
||||
}
|
||||
catch {
|
||||
Write-Host ''
|
||||
|
|
@ -753,6 +1027,9 @@ try {
|
|||
Write-Host 'Applying patch...'
|
||||
|
||||
Invoke-SrpgPostApply -Root $GameRoot
|
||||
# Safety: if Data/ is ready (from unpack or patch files) but Data.wolf
|
||||
# remains, retire it so the archive does not override loose English files.
|
||||
Invoke-WolfPreSetup -Root $GameRoot
|
||||
|
||||
Write-Host 'Cleaning up...'
|
||||
Remove-PatchTempArtifacts -Root $GameRoot
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ 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() {
|
||||
|
|
@ -38,19 +37,113 @@ echo "Config file path: $CONFIG_FILE"
|
|||
# shellcheck disable=SC1090
|
||||
. "$CONFIG_FILE"
|
||||
|
||||
if [ -z "${username:-}" ]; then
|
||||
# Defaults for older configs that only set username/repo/branch.
|
||||
forge="${forge:-${provider:-gitlab}}"
|
||||
host="${host:-}"
|
||||
username="${username:-${owner:-${org:-}}}"
|
||||
repo="${repo:-}"
|
||||
branch="${branch:-}"
|
||||
|
||||
if [ -z "$username" ]; then
|
||||
echo "ERROR: 'username=' is missing in gameupdate/patch-config.txt"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${repo:-}" ]; then
|
||||
if [ -z "$repo" ]; then
|
||||
echo "ERROR: 'repo=' is missing in gameupdate/patch-config.txt"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${branch:-}" ]; then
|
||||
if [ -z "$branch" ]; then
|
||||
echo "ERROR: 'branch=' is missing in gameupdate/patch-config.txt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
normalize_forge() {
|
||||
local raw
|
||||
raw="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"
|
||||
case "$raw" in
|
||||
''|gitlab|gl|gitgud) echo gitlab ;;
|
||||
github|gh) echo github ;;
|
||||
forgejo|gitea|fj|codeberg) echo forgejo ;;
|
||||
*)
|
||||
echo "ERROR: Unknown forge='$1'. Use gitlab, github, or forgejo." >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
FORGE_KIND="$(normalize_forge "$forge")" || exit 1
|
||||
|
||||
# Strip scheme / path from host if a user pasted a full URL.
|
||||
HOST_NAME="$(printf '%s' "$host" | sed -E 's#^https?://##; s#/.*##; s#[[:space:]]##g')"
|
||||
if [ -z "$HOST_NAME" ]; then
|
||||
case "$FORGE_KIND" in
|
||||
gitlab) HOST_NAME="gitgud.io" ;;
|
||||
github) HOST_NAME="github.com" ;;
|
||||
forgejo) HOST_NAME="codeberg.org" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
WEB_BASE="https://${HOST_NAME}"
|
||||
case "$FORGE_KIND" in
|
||||
gitlab) API_BASE="${WEB_BASE}/api/v4" ;;
|
||||
github)
|
||||
if [ "$HOST_NAME" = "github.com" ]; then
|
||||
API_BASE="https://api.github.com"
|
||||
else
|
||||
API_BASE="${WEB_BASE}/api/v3"
|
||||
fi
|
||||
;;
|
||||
forgejo) API_BASE="${WEB_BASE}/api/v1" ;;
|
||||
esac
|
||||
|
||||
API_HEADERS=( -H "User-Agent: GameUpdate/1.0" )
|
||||
if [ "$FORGE_KIND" = "github" ]; then
|
||||
API_HEADERS+=( -H "Accept: application/vnd.github+json" )
|
||||
fi
|
||||
|
||||
owner_enc=$(jq -nr --arg s "$username" '$s | @uri')
|
||||
repo_enc=$(jq -nr --arg s "$repo" '$s | @uri')
|
||||
branch_enc=$(jq -nr --arg b "$branch" '$b | @uri')
|
||||
project_enc=$(jq -nr --arg ns "$username" --arg rp "$repo" '$ns + "/" + $rp | @uri')
|
||||
|
||||
resolve_latest_sha() {
|
||||
case "$FORGE_KIND" in
|
||||
gitlab)
|
||||
curl -fsSL "${API_HEADERS[@]}" \
|
||||
"${API_BASE}/projects/${project_enc}/repository/branches/${branch_enc}" \
|
||||
| jq -r '.commit.id'
|
||||
;;
|
||||
github)
|
||||
curl -fsSL "${API_HEADERS[@]}" \
|
||||
"${API_BASE}/repos/${owner_enc}/${repo_enc}/commits/${branch_enc}" \
|
||||
| jq -r '.sha'
|
||||
;;
|
||||
forgejo)
|
||||
curl -fsSL "${API_HEADERS[@]}" \
|
||||
"${API_BASE}/repos/${owner_enc}/${repo_enc}/branches/${branch_enc}" \
|
||||
| jq -r '.commit.id'
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
archive_url_for_sha() {
|
||||
local sha_enc
|
||||
sha_enc=$(jq -nr --arg s "$1" '$s | @uri')
|
||||
case "$FORGE_KIND" in
|
||||
gitlab)
|
||||
echo "${API_BASE}/projects/${project_enc}/repository/archive.zip?sha=${sha_enc}"
|
||||
;;
|
||||
github)
|
||||
echo "${API_BASE}/repos/${owner_enc}/${repo_enc}/zipball/${sha_enc}"
|
||||
;;
|
||||
forgejo)
|
||||
echo "${API_BASE}/repos/${owner_enc}/${repo_enc}/archive/${sha_enc}.zip"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
echo "Pulling patch from ${WEB_BASE}/${username}/${repo} (${FORGE_KIND}, branch: ${branch})"
|
||||
|
||||
RETRIES="${GAMEUPDATE_DL_ATTEMPTS:-2}"
|
||||
if ! [[ "$RETRIES" =~ ^[1-9][0-9]*$ ]]; then
|
||||
RETRIES=2
|
||||
|
|
@ -74,23 +167,139 @@ retry_cmd() {
|
|||
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="$(
|
||||
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=""
|
||||
attempt=1
|
||||
while true; do
|
||||
if latest_patch_sha="$(resolve_latest_sha)"; then
|
||||
break
|
||||
fi
|
||||
if [ "$attempt" -ge "$RETRIES" ]; then
|
||||
echo "Resolve latest patch SHA failed after $attempt attempt(s)."
|
||||
exit 1
|
||||
fi
|
||||
echo "Resolve latest patch SHA failed ($attempt/$RETRIES), retrying..."
|
||||
attempt=$((attempt + 1))
|
||||
sleep 2
|
||||
done
|
||||
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: Wolf Data.wolf -> loose Data/ (before patch copy)
|
||||
# --------------------------------------------------------
|
||||
wolf_loose_data_ready() {
|
||||
local data_dir="$1"
|
||||
[ -d "$data_dir" ] || return 1
|
||||
[ -f "$data_dir/BasicData/CommonEvent.dat" ] && return 0
|
||||
[ -f "$data_dir/CommonEvent.dat" ] && return 0
|
||||
if compgen -G "$data_dir/*.mps" > /dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
if compgen -G "$data_dir/BasicData/*.dat" > /dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
find_uberwolf_cli() {
|
||||
if [ -f "$ROOT_DIR/UberWolfCli.exe" ]; then
|
||||
echo "$ROOT_DIR/UberWolfCli.exe"
|
||||
return 0
|
||||
fi
|
||||
if [ -f "$ROOT_DIR/gameupdate/UberWolfCli.exe" ]; then
|
||||
echo "$ROOT_DIR/gameupdate/UberWolfCli.exe"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
retire_data_wolf() {
|
||||
local data_wolf="$1"
|
||||
local bak="${data_wolf}.bak"
|
||||
if [ ! -f "$data_wolf" ]; then
|
||||
return 0
|
||||
fi
|
||||
if [ ! -e "$bak" ]; then
|
||||
echo "[Pre-Setup] Retiring $(basename "$data_wolf") -> $(basename "$bak")"
|
||||
mv -f "$data_wolf" "$bak"
|
||||
else
|
||||
echo "[Pre-Setup] Removing leftover $(basename "$data_wolf") so loose Data/ takes priority"
|
||||
rm -f "$data_wolf"
|
||||
fi
|
||||
}
|
||||
|
||||
invoke_wolf_pre_setup() {
|
||||
local data_wolf=""
|
||||
local name
|
||||
for name in Data.wolf data.wolf; do
|
||||
if [ -f "$ROOT_DIR/$name" ]; then
|
||||
data_wolf="$ROOT_DIR/$name"
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -n "$data_wolf" ] || return 0
|
||||
|
||||
local data_dir="$ROOT_DIR/Data"
|
||||
if [ ! -d "$data_dir" ] && [ -d "$ROOT_DIR/data" ]; then
|
||||
data_dir="$ROOT_DIR/data"
|
||||
fi
|
||||
|
||||
if wolf_loose_data_ready "$data_dir"; then
|
||||
retire_data_wolf "$data_wolf"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local cli
|
||||
cli="$(find_uberwolf_cli || true)"
|
||||
if [ -z "$cli" ]; then
|
||||
echo "[Pre-Setup] Data.wolf found but UberWolfCli.exe is missing."
|
||||
echo " Copy UberWolfCli.exe into the game root (Step 1 gameupdate/) and re-run."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "[Pre-Setup] Unpacking Data.wolf with UberWolfCli (one-time)..."
|
||||
# Native .exe works under Windows / Git Bash; wine covers Linux hosts.
|
||||
if ( cd "$ROOT_DIR" && "$cli" -o "$data_wolf" ); then
|
||||
:
|
||||
elif command -v wine > /dev/null 2>&1 && ( cd "$ROOT_DIR" && wine "$cli" -o "$data_wolf" ); then
|
||||
:
|
||||
else
|
||||
echo "[Pre-Setup] ERROR: UberWolfCli unpack failed."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local unpacked="$ROOT_DIR/Data"
|
||||
if ! wolf_loose_data_ready "$unpacked"; then
|
||||
unpacked="$ROOT_DIR/data"
|
||||
fi
|
||||
if ! wolf_loose_data_ready "$unpacked"; then
|
||||
local alt
|
||||
for alt in data.wolf~ Data.wolf~ Data~ data~; do
|
||||
if [ -d "$ROOT_DIR/$alt" ]; then
|
||||
echo "[Pre-Setup] Promoting $alt -> Data/"
|
||||
rm -rf "$ROOT_DIR/Data"
|
||||
mv -f "$ROOT_DIR/$alt" "$ROOT_DIR/Data"
|
||||
unpacked="$ROOT_DIR/Data"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if wolf_loose_data_ready "$unpacked"; then
|
||||
retire_data_wolf "$data_wolf"
|
||||
echo "[Pre-Setup] Wolf Data/ is ready for patching."
|
||||
else
|
||||
echo "[Pre-Setup] ERROR: Unpack finished but loose Data/ still looks empty."
|
||||
fi
|
||||
}
|
||||
|
||||
invoke_wolf_pre_setup
|
||||
|
||||
# --------------------------------------------------------
|
||||
# PRE-SETUP: Ensure SRPG data and patch structure exists
|
||||
# Run Steps 1 and 2 BEFORE pulling repo patch to avoid overwriting updates
|
||||
|
|
@ -132,10 +341,10 @@ fi
|
|||
|
||||
download_extract() {
|
||||
echo "Downloading latest patch..."
|
||||
archive_sha_enc=$(jq -nr --arg s "$latest_patch_sha" '$s | @uri')
|
||||
archive_url="$(archive_url_for_sha "$latest_patch_sha")"
|
||||
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}" \
|
||||
"$archive_url" \
|
||||
-o "$ROOT_DIR/repo.zip"; then
|
||||
echo "Download failed!"
|
||||
rm -f "$ROOT_DIR/repo.zip"
|
||||
|
|
@ -166,6 +375,15 @@ download_extract() {
|
|||
return 1
|
||||
fi
|
||||
|
||||
# Stage UberWolfCli before unpack so Data.wolf -> Data/ happens before
|
||||
# English patch files are copied on top.
|
||||
for cli_name in UberWolfCli.exe UberWolfCli.LICENSE.txt; do
|
||||
if [ -f "$inner/$cli_name" ] && [ ! -f "$ROOT_DIR/$cli_name" ]; then
|
||||
cp -f "$inner/$cli_name" "$ROOT_DIR/$cli_name"
|
||||
fi
|
||||
done
|
||||
invoke_wolf_pre_setup
|
||||
|
||||
echo "Applying patch..."
|
||||
if ! cp -r "$inner"/* "$ROOT_DIR/"; then
|
||||
echo "Patch application failed!"
|
||||
|
|
@ -205,6 +423,9 @@ download_extract() {
|
|||
fi
|
||||
fi
|
||||
|
||||
# Retire Data.wolf if loose Data/ is ready after the patch copy.
|
||||
invoke_wolf_pre_setup
|
||||
|
||||
echo "$latest_patch_sha" > "$STATE_FILE"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2853,4 +2853,4 @@
|
|||
}
|
||||
],
|
||||
"kind": "db"
|
||||
}
|
||||
}
|
||||
|
|
@ -17573,4 +17573,4 @@
|
|||
}
|
||||
],
|
||||
"kind": "db"
|
||||
}
|
||||
}
|
||||
|
|
@ -33,4 +33,4 @@
|
|||
],
|
||||
"defer_groups": [],
|
||||
"notes": "Recommended order: Names → DB foundation (258 lines) → DB narrative (2,333 lines) → Maps/events."
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
"nameWraps": [
|
||||
{
|
||||
"note": "├■街の噂(MOB)",
|
||||
"maxLines": 3,
|
||||
"width": 30
|
||||
},
|
||||
{
|
||||
"note": "├■ウワサ・MOB進行度等",
|
||||
"width": 30,
|
||||
"maxLines": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
"nameWraps": [
|
||||
{
|
||||
"note": "├■街の噂(MOB)",
|
||||
"maxLines": 3,
|
||||
"width": 30
|
||||
},
|
||||
{
|
||||
"note": "├■ウワサ・MOB進行度等",
|
||||
"width": 30,
|
||||
"maxLines": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -28,4 +28,4 @@
|
|||
"font": 30
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue