200 lines
No EOL
5.3 KiB
PowerShell
200 lines
No EOL
5.3 KiB
PowerShell
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function Invoke-SteamCmd {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)] [string] $SteamCmdPath,
|
|
[Parameter(Mandatory)] [string[]] $Arguments,
|
|
[Parameter()] [string] $LogPath = ""
|
|
)
|
|
|
|
$psi = [System.Diagnostics.ProcessStartInfo]::new()
|
|
$psi.FileName = $SteamCmdPath
|
|
$psi.UseShellExecute = $false
|
|
$psi.RedirectStandardOutput = $true
|
|
$psi.RedirectStandardError = $true
|
|
|
|
foreach ($a in $Arguments) { [void]$psi.ArgumentList.Add($a) }
|
|
|
|
$proc = [System.Diagnostics.Process]::new()
|
|
$proc.StartInfo = $psi
|
|
|
|
[void]$proc.Start()
|
|
|
|
$stdout = $proc.StandardOutput.ReadToEnd()
|
|
$stderr = $proc.StandardError.ReadToEnd()
|
|
|
|
$proc.WaitForExit()
|
|
|
|
$combined = @(
|
|
"===== SteamCMD STDOUT ====="
|
|
$stdout
|
|
"===== SteamCMD STDERR ====="
|
|
$stderr
|
|
) -join "`n"
|
|
|
|
if ($LogPath) {
|
|
$dir = Split-Path -Parent $LogPath
|
|
if ($dir) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
|
|
Set-Content -LiteralPath $LogPath -Value $combined -Encoding UTF8
|
|
}
|
|
|
|
[PSCustomObject]@{
|
|
ExitCode = $proc.ExitCode
|
|
StdOut = $stdout
|
|
StdErr = $stderr
|
|
Combined = $combined
|
|
}
|
|
}
|
|
|
|
function Install-SteamGame {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[int] $AppId,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string] $GameName,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string] $InstallRoot,
|
|
|
|
[Parameter()]
|
|
[string] $SteamCmdPath = "steamcmd",
|
|
|
|
[Parameter()]
|
|
[string] $Username = "anonymous",
|
|
|
|
[Parameter()]
|
|
[string] $Password = "",
|
|
|
|
[Parameter()]
|
|
[string] $Branch = "",
|
|
|
|
[Parameter()]
|
|
[string] $BranchPassword = "",
|
|
|
|
[Parameter()]
|
|
[switch] $Validate,
|
|
|
|
[Parameter()]
|
|
[switch] $IncludeSteamNews = $true
|
|
)
|
|
|
|
function Join-PathPortable([string] $A, [string] $B) {
|
|
if ($A.EndsWith("/") -or $A.EndsWith("\")) { return "$A$B" }
|
|
if ($A -match "^[a-zA-Z]:\\") { return "$A\$B" }
|
|
return "$A/$B"
|
|
}
|
|
|
|
function Find-AppManifest([string] $InstallDir, [int] $AppIdValue) {
|
|
$expected = Join-PathPortable $InstallDir ("steamapps/appmanifest_{0}.acf" -f $AppIdValue)
|
|
if (Test-Path -LiteralPath $expected) { return $expected }
|
|
|
|
Get-ChildItem -LiteralPath $InstallDir -Recurse -File `
|
|
-Filter ("appmanifest_{0}.acf" -f $AppIdValue) `
|
|
-ErrorAction SilentlyContinue |
|
|
Select-Object -First 1 |
|
|
ForEach-Object FullName
|
|
}
|
|
|
|
function Parse-AppManifestBuildInfo([string] $ManifestPath) {
|
|
$content = Get-Content -LiteralPath $ManifestPath -Raw
|
|
|
|
$buildId = [regex]::Match($content, '"buildid"\s+"(\d+)"').Groups[1].Value
|
|
$lastUpdated = [regex]::Match($content, '"LastUpdated"\s+"(\d+)"').Groups[1].Value
|
|
|
|
[PSCustomObject]@{
|
|
BuildId = $buildId
|
|
LastUpdatedUtc = if ($lastUpdated) {
|
|
[DateTimeOffset]::FromUnixTimeSeconds([int64]$lastUpdated).UtcDateTime
|
|
}
|
|
}
|
|
}
|
|
|
|
function Get-SteamNewsChangelog([int] $AppIdValue) {
|
|
try {
|
|
$uri = "https://api.steampowered.com/ISteamNews/GetNewsForApp/v2/?appid=$AppIdValue&count=3&maxlength=800&format=json"
|
|
$resp = Invoke-RestMethod -Uri $uri -TimeoutSec 15
|
|
$resp.appnews.newsitems |
|
|
ForEach-Object {
|
|
"[{0}] {1}`n{2}" -f `
|
|
([DateTimeOffset]::FromUnixTimeSeconds($_.date).ToString("u")),
|
|
$_.title,
|
|
$_.contents
|
|
} -join "`n`n"
|
|
}
|
|
catch {
|
|
""
|
|
}
|
|
}
|
|
|
|
# ---- Install ----
|
|
|
|
$installDir = Join-PathPortable $InstallRoot $GameName
|
|
New-Item -ItemType Directory -Path $installDir -Force | Out-Null
|
|
|
|
$args = @(
|
|
"+force_install_dir", $installDir,
|
|
"+login", $Username
|
|
)
|
|
|
|
if ($Username -ne "anonymous" -and $Password) {
|
|
$args += $Password
|
|
}
|
|
|
|
$args += "+app_update", $AppId
|
|
|
|
if ($Branch) {
|
|
$args += "-beta", $Branch
|
|
if ($BranchPassword) {
|
|
$args += "-betapassword", $BranchPassword
|
|
}
|
|
}
|
|
|
|
if ($Validate) {
|
|
$args += "validate"
|
|
}
|
|
|
|
$args += "+quit"
|
|
|
|
$logPath = Join-Path -Path $installDir -ChildPath "steamcmd.log"
|
|
|
|
$result = Invoke-SteamCmd -SteamCmdPath $SteamCmdPath -Arguments $args -LogPath $logPath
|
|
|
|
if ($result.ExitCode -ne 0) {
|
|
$tail = ($result.Combined -split "`r?`n" | Select-Object -Last 80) -join "`n"
|
|
throw @(
|
|
"SteamCMD failed with exit code $($result.ExitCode)."
|
|
"Log: $logPath"
|
|
""
|
|
"Last 80 lines:"
|
|
$tail
|
|
) -join "`n"
|
|
}
|
|
|
|
|
|
# ---- Version / Changelog ----
|
|
|
|
$manifest = Find-AppManifest -InstallDir $installDir -AppIdValue $AppId
|
|
$build = if ($manifest) { Parse-AppManifestBuildInfo $manifest }
|
|
|
|
$version = $build?.BuildId ?? "unknown"
|
|
|
|
$changelog = if ($IncludeSteamNews) {
|
|
Get-SteamNewsChangelog $AppId
|
|
}
|
|
|
|
if (-not $changelog) {
|
|
$changelog = "BuildID: $version"
|
|
}
|
|
|
|
return [PSCustomObject]@{
|
|
Path = $installDir
|
|
Version = $version
|
|
Changelog = $changelog
|
|
}
|
|
}
|
|
|
|
Export-ModuleMember -Function Install-SteamGame |