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