Oxide.Rust/Steam.ps1

332 lines
13 KiB
PowerShell

param (
[Parameter(Mandatory=$true)][string]$game_name,
[Parameter(Mandatory=$true)][string]$dotnet,
[Parameter(Mandatory=$true)][string]$target_dir,
[Parameter(Mandatory=$true)][string]$managed_dir,
[string]$platform = "windows",
[string]$deobfuscator = "",
[string]$steam_appid = "0",
[string]$steam_branch = "public",
[string]$steam_depot = "",
[string]$steam_access = "anonymous",
[string]$references_override = ""
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# Check PowerShell version
$ps_version = $PSVersionTable.PSVersion.Major
if ($ps_version -le 5)
{
Write-Host "Error: PowerShell version 6 or higher required to continue, $ps_version currently installed"
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
}
# Format project name and set depot ID if provided
$project = "Oxide." + $game_name
if ($steam_depot) { $steam_depot = "-depot $steam_depot" }
# Set directory/file variables and create directories
$root_dir = $PSScriptRoot
$tools_dir = Join-Path $root_dir "tools"
$project_dir = Join-Path $root_dir "src"
$resources_dir = Join-Path $root_dir "resources"
$deps_dir = Join-Path $project_dir "Dependencies"
$platform_dir = Join-Path $deps_dir $platform
$managed_dir = Join-Path $platform_dir $managed_dir # TODO: Make sure passed path is Linux-compatible
$docs_dir = Join-Path $root_dir "docs"
$patcher_exe = Join-Path $tools_dir "OxidePatcher.exe"
$references_file = Join-Path $tools_dir ".references"
New-Item "$tools_dir", "$managed_dir", "$docs_dir" -ItemType Directory -Force | Out-Null
# Set URLs of dependencies and tools to download
$steam_depotdl_url = "https://img.mrblue.io/bf641959245341c381cffc95f38a2bc6.zip"
$de4dot_url = "https://github.com/0xd4d/de4dot/suites/507020524/artifacts/2658127"
$patcher_url = "https://github.com/OxideMod/Oxide.Patcher/releases/download/latest/OxidePatcher.exe"
# Set file path for patcher file (.opj)
$patcher_file = Join-Path $resources_dir "$game_name.opj"
# Set project file and get contents
$csproj = Get-Item "$project.csproj"
$xml = [xml](Get-Content $csproj)
# Remove patched file(s) and replace with _Original file(s)
Get-ChildItem (Join-Path $managed_dir "*_Original.*") -Recurse | ForEach-Object {
Remove-Item $_.FullName.Replace("_Original", "")
Rename-Item $_ $_.Name.Replace("_Original", "")
}
# TODO: Add support for GitHub API tokens for higher rate limit
function Find-Dependencies {
# Check if project file exists for game
if (!(Test-Path "$project.csproj")) {
Write-Host "Error: Could not find a .csproj file for $game_name"
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
}
# Copy any local dependencies
$deps_pattern = Join-Path $deps_dir "Original" "*.dll"
if (Test-Path $deps_pattern) {
Copy-Item $deps_pattern $managed_dir -Force
}
# Check if Steam is used for game dependencies
if ($steam_access.ToLower() -ne "nosteam") {
# Get references from .csproj file
Write-Host "Getting references for $steam_branch branch of $steam_appid"
try {
# TODO: Exclude dependencies included in repository
if ($references_override) {
$references_override | Out-File $references_file
Write-Host "References:" ((Get-Content $references_file) -Join ', ')
} else {
($xml.selectNodes("//Reference") | Select-Object Include -ExpandProperty Include) -Replace "\S+$", "regex:$&.dll" | Out-File $references_file
Write-Host "References:" ((Get-Content $references_file).Replace('regex:', '') -Join ', ')
}
} catch {
Write-Host "Error: Could not get references or none found in $project.csproj"
Write-Host $_.Exception | Format-List -Force
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
}
Get-Downloader
} else {
Get-Dependencies
}
}
function Get-Downloader {
# Check if DepotDownloader is already downloaded
$steam_depotdl_dll = Join-Path $tools_dir "DepotDownloader.exe"
$steam_depotdl_zip = Join-Path $tools_dir "DepotDownloader.zip"
if (!(Test-Path $steam_depotdl_dll) -or (Get-Item $steam_depotdl_dll).LastWriteTime -lt (Get-Date).AddDays(-7)) {
# Download and extract DepotDownloader
Write-Host "Downloading latest version of DepotDownloader"
try {
Invoke-WebRequest $steam_depotdl_url -OutFile $steam_depotdl_zip -UseBasicParsing
} catch {
Write-Host "Error: Could not download DepotDownloader"
Write-Host $_.Exception | Format-List -Force
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
}
# TODO: Compare size and hash of .zip vs. what GitHub has via API
Write-Host "Extracting DepotDownloader release files"
Expand-Archive $steam_depotdl_zip -DestinationPath $tools_dir -Force
if (!(Test-Path $steam_depotdl_zip)) {
Get-Downloader # TODO: Add infinite loop prevention
return
}
# Cleanup downloaded .zip file
Remove-Item $steam_depotdl_zip
} else {
Write-Host "Recent version of DepotDownloader already downloaded"
}
Get-Dependencies
}
function Get-Dependencies {
if ($steam_access.ToLower() -ne "nosteam") {
# TODO: Add handling for SteamGuard code entry
# Check if Steam login information is required or not
$steam_file = Join-Path $root_dir ".steamlogin"
if ($steam_access.ToLower() -ne "anonymous") {
if (Test-Path $steam_file) {
$steam_login = Get-Content $steam_file
if ($steam_login.Length -ne 2) {
Write-Host "Steam username and password not set in .steamlogin file"
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
} else {
$steam_access = "-username $($steam_login[0]) -password $($steam_login[1])"
}
} elseif ($env:STEAM_USERNAME -and $env:STEAM_PASSWORD) {
$steam_access = "-username $env:STEAM_USERNAME -password $env:STEAM_PASSWORD"
} else {
Write-Host "Error: No Steam credentials found, skipping build for $game_name"
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
}
}
# Cleanup existing game files, else they are not always the latest
#Remove-Item $managed_dir -Recurse -Force
# TODO: Check for and compare Steam buildid before downloading again
# Attempt to run DepotDownloader to get game DLLs
try {
Write-Host "$steam_access -app $steam_appid -branch $steam_branch $steam_depot -os $platform -dir $deps_dir"
Start-Process $steam_depotdl_dll -WorkingDirectory $tools_dir -ArgumentList "$steam_access -app $steam_appid -branch $steam_branch $steam_depot -os $platform -dir $platform_dir -filelist $references_file" -NoNewWindow -Wait
} catch {
Write-Host "Error: Could not start or complete getting dependencies"
Write-Host $_.Exception | Format-List -Force
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
}
# TODO: Store Steam buildid somewhere for comparison during next check
# TODO: Confirm all dependencies were downloaded (no 0kb files), else stop or retry and error with details
}
# Get package references from .csproj file
Write-Host "Getting package references for $game_name"
$packages = $null
try {
$packages = $xml.selectNodes("//PackageReference") | Select-Object Include,Version
Write-Host "Packages:" (($packages | Select-Object -ExpandProperty Include) -Join ', ')
} catch {
Write-Host "Error: Could not get package references or none found in $project.csproj"
Write-Host $_.Exception | Format-List -Force
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
}
# Copy latest package references for patching
Write-Host "Copying latest package references for $game_name"
try {
# Copy package references specified in .csproj file
ForEach ($package in $packages) {
Write-Host "Copying package reference $($package.Include) $($package.Version)..."
$lib = Join-Path $root_dir "packages" $package.Include.ToLower() $package.Version.ToLower() "lib" $dotnet "$($package.Include).dll"
Copy-Item $lib $managed_dir -Force
Copy-Item $lib $tools_dir -Force
}
} catch {
Write-Host "Error: Could not copy one or more dependencies to $deps_dir or $tools_dir"
Write-Host $_.Exception | Format-List -Force
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
}
if ($deobfuscator) {
Get-Deobfuscators
} else {
Get-Patcher
}
}
function Get-Deobfuscators {
# Check for which deobfuscator to get and use
if ($deobfuscator.ToLower() -eq "de4dot") {
$de4dot_dir = Join-Path $tools_dir ".de4dot"
$de4dot_dll = Join-Path $de4dot_dir "de4dot.dll"
$de4dot_zip = Join-Path $de4dot_dir "de4dot.zip"
New-Item $de4dot_dir -ItemType Directory -Force | Out-Null
# Check if de4dot is already downloaded
if (!(Test-Path $de4dot_dll) -or (Get-Item $de4dot_dll).LastWriteTime -lt (Get-Date).AddDays(-7)) {
# Download and extract de4dot
Write-Host "Downloading latest version of de4dot" # TODO: Get and show version
try {
Invoke-WebRequest $de4dot_url -OutFile $de4dot_zip -UseBasicParsing
#Invoke-WebRequest "https://github.com/0xd4d/de4dot/suites/266206734/artifacts/128547" -Out "$de4dot_dir\de4dot.zip"
} catch {
Write-Host "Error: Could not download de4dot"
Write-Host $_.Exception | Format-List -Force
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
}
# TODO: Compare size and hash of .zip vs. what GitHub has via API
Write-Host "Extracting de4dot release files"
Expand-Archive "$de4dot_dir\de4dot.zip" -DestinationPath "$de4dot_dir" -Force
Move-Item "$de4dot_dir\de4dot-net35\*" $de4dot_dir
Remove-Item "$de4dot_dir\de4dot-net35"
if (!(Test-Path $de4dot_dll)) {
Get-Deobfuscators # TODO: Add infinite loop prevention
return
}
# Cleanup downloaded .zip file
Remove-Item $de4dot_zip
} else {
Write-Host "Recent version of de4dot already downloaded"
}
Start-Deobfuscator
}
}
function Start-Deobfuscator {
if ($deobfuscator.ToLower() -eq "de4dot") {
# Attempt to deobfuscate game file(s)
try {
Start-Process dotnet -WorkingDirectory $managed_dir -ArgumentList "$de4dot_dll -r $managed_dir -ru" -NoNewWindow -Wait
} catch {
Write-Host "Error: Could not start or complete deobufcation process"
Write-Host $_.Exception | Format-List -Force
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1;
}
# Remove obfuscated file(s) and replace with cleaned file(s)
Get-ChildItem (Join-Path $managed_dir "*-cleaned.*") -Recurse | ForEach-Object {
Remove-Item $_.FullName.Replace("-cleaned", "")
Rename-Item $_ $_.Name.Replace("-cleaned", "")
Write-Host "Deobfuscated and cleaned $_ file to patch"
}
}
Get-Patcher
}
function Get-Patcher {
# TODO: MD5 comparison of local patcher file and remote header
# Check if patcher is already downloaded
if (!(Test-Path $patcher_exe) -or (Get-Item $patcher_exe).LastWriteTime -lt (Get-Date).AddDays(-7)) {
# Download latest patcher build
Write-Host "Downloading latest version of Oxide patcher"
try {
Invoke-WebRequest $patcher_url -OutFile $patcher_exe -UseBasicParsing
} catch {
Write-Host "Error: Could not download Oxide patcher"
Write-Host $_.Exception | Format-List -Force
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
}
} else {
Write-Host "Recent build of patcher already downloaded"
}
Start-Patcher
}
function Start-Patcher {
# Check if we need to get the patcher
if (!(Test-Path $patcher_exe)) {
Get-Patcher # TODO: Add infinite loop prevention
return
}
# TODO: Make sure dependencies exist before trying to patch
# Attempt to patch game using the patcher
try {
if ($IsLinux) {
Start-Process mono -WorkingDirectory $managed_dir -ArgumentList "$patcher_exe -c -p `"$managed_dir`" $patcher_file" -NoNewWindow -Wait
} elseif ($IsWindows) {
Start-Process $patcher_exe -WorkingDirectory $managed_dir -ArgumentList "-c -p `"$managed_dir`" -docs $docs_dir/docs.json $patcher_file" -NoNewWindow -Wait
}
} catch {
Write-Host "Error: Could not start or complete patching process"
Write-Host $_.Exception | Format-List -Force
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
exit 1
}
}
Find-Dependencies