mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 23:18:56 -07:00
432 lines
18 KiB
PowerShell
432 lines
18 KiB
PowerShell
<#
|
|
Read-only triage for a Radicade-patched August 2023 Rec Room install.
|
|
|
|
Usage:
|
|
powershell -ExecutionPolicy Bypass -File august-client-triage.ps1
|
|
powershell -ExecutionPolicy Bypass -File august-client-triage.ps1 -GameDir "C:\path\to\2023-08-29T06-53-33Z"
|
|
|
|
It only reads files and opens two TCP sockets to localhost. It writes
|
|
august-client-report.txt next to this script (override with -OutFile).
|
|
Send that file back verbatim; it names exactly which piece of the stack is missing.
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$GameDir,
|
|
[string]$OutFile
|
|
)
|
|
|
|
$ErrorActionPreference = 'Continue'
|
|
$script:Report = New-Object System.Collections.Generic.List[string]
|
|
$script:Failures = 0
|
|
$script:Warnings = 0
|
|
|
|
function Add-Line {
|
|
param([string]$Text = '')
|
|
$script:Report.Add($Text)
|
|
}
|
|
|
|
function Write-Both {
|
|
param([string]$Text = '')
|
|
Write-Host $Text
|
|
Add-Line $Text
|
|
}
|
|
|
|
function Add-Verdict {
|
|
param(
|
|
[ValidateSet('OK', 'WARN', 'MISSING')][string]$State,
|
|
[string]$Text
|
|
)
|
|
if ($State -eq 'MISSING') { $script:Failures++ }
|
|
if ($State -eq 'WARN') { $script:Warnings++ }
|
|
Add-Line ("[{0,-7}] {1}" -f $State, $Text)
|
|
if ($State -ne 'OK') { Write-Host ("[{0}] {1}" -f $State, $Text) }
|
|
}
|
|
|
|
function Get-Md5 {
|
|
param([string]$Path)
|
|
try { return (Get-FileHash -LiteralPath $Path -Algorithm MD5).Hash.ToLower() }
|
|
catch { return 'unreadable' }
|
|
}
|
|
|
|
function Get-ByteString {
|
|
param([string]$Path)
|
|
try {
|
|
$bytes = [System.IO.File]::ReadAllBytes($Path)
|
|
return [System.Text.Encoding]::GetEncoding(28591).GetString($bytes)
|
|
}
|
|
catch { return $null }
|
|
}
|
|
|
|
function Test-Utf16Literal {
|
|
param([string]$Path, [string]$Literal)
|
|
try {
|
|
$bytes = [System.IO.File]::ReadAllBytes($Path)
|
|
$needle = [System.Text.Encoding]::Unicode.GetBytes($Literal)
|
|
for ($i = 0; $i -le ($bytes.Length - $needle.Length); $i++) {
|
|
if ($bytes[$i] -ne $needle[0]) { continue }
|
|
$hit = $true
|
|
for ($j = 1; $j -lt $needle.Length; $j++) {
|
|
if ($bytes[$i + $j] -ne $needle[$j]) { $hit = $false; break }
|
|
}
|
|
if ($hit) { return $true }
|
|
}
|
|
}
|
|
catch { }
|
|
return $false
|
|
}
|
|
|
|
function Test-Tcp {
|
|
param([int]$Port)
|
|
$client = New-Object System.Net.Sockets.TcpClient
|
|
try {
|
|
$task = $client.ConnectAsync('127.0.0.1', $Port)
|
|
if ($task.Wait(1500) -and $client.Connected) { return $true }
|
|
return $false
|
|
}
|
|
catch { return $false }
|
|
finally { $client.Close() }
|
|
}
|
|
|
|
function Get-LogMarkers {
|
|
param([string]$Path, [string[]]$Markers)
|
|
$result = @{}
|
|
foreach ($m in $Markers) { $result[$m] = 0 }
|
|
if (-not (Test-Path -LiteralPath $Path)) { return $result }
|
|
try {
|
|
$text = Get-Content -LiteralPath $Path -Raw -ErrorAction Stop
|
|
foreach ($m in $Markers) {
|
|
$result[$m] = ([regex]::Matches($text, [regex]::Escape($m))).Count
|
|
}
|
|
}
|
|
catch { }
|
|
return $result
|
|
}
|
|
|
|
# --- locate the install -------------------------------------------------------
|
|
|
|
if (-not $GameDir) {
|
|
$candidates = @(
|
|
(Join-Path $env:USERPROFILE 'Downloads\2023-08-29T06-53-33Z'),
|
|
(Join-Path $env:USERPROFILE 'Desktop\2023-08-29T06-53-33Z'),
|
|
'C:\2023-08-29T06-53-33Z'
|
|
)
|
|
$downloads = Join-Path $env:USERPROFILE 'Downloads'
|
|
if (Test-Path -LiteralPath $downloads) {
|
|
$candidates += (Get-ChildItem -LiteralPath $downloads -Directory -ErrorAction SilentlyContinue |
|
|
Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName 'RecRoom.exe') } |
|
|
ForEach-Object { $_.FullName })
|
|
}
|
|
foreach ($c in $candidates) {
|
|
if ($c -and (Test-Path -LiteralPath (Join-Path $c 'RecRoom.exe'))) { $GameDir = $c; break }
|
|
}
|
|
}
|
|
|
|
if (-not $GameDir) { $GameDir = Read-Host 'Path to the Rec Room folder that contains RecRoom.exe' }
|
|
$GameDir = $GameDir.TrimEnd('\')
|
|
|
|
if (-not $OutFile) {
|
|
if ($PSScriptRoot) { $OutFile = Join-Path $PSScriptRoot 'august-client-report.txt' }
|
|
else { $OutFile = Join-Path (Get-Location) 'august-client-report.txt' }
|
|
}
|
|
|
|
$playerLog = Join-Path $env:USERPROFILE 'AppData\LocalLow\Against Gravity\Rec Room\Player.log'
|
|
$bepLog = Join-Path $GameDir 'BepInEx\LogOutput.log'
|
|
$metadata = Join-Path $GameDir 'Recroom_Data\il2cpp_data\Metadata\global-metadata.dat'
|
|
$interopDat = Join-Path $GameDir 'Recroom_Data\il2cpp_data\Metadata\global-metadata.interop.dat'
|
|
$bootConfig = Join-Path $GameDir 'Recroom_Data\boot.config'
|
|
$patchJson = Join-Path $GameDir 'Recroom_Data\radicade-patch.json'
|
|
$winhttp = Join-Path $GameDir 'winhttp.dll'
|
|
$doorstop = Join-Path $GameDir 'doorstop_config.ini'
|
|
$loader = Join-Path $GameDir 'BepInEx\core\BepInEx.Unity.IL2CPP.dll'
|
|
$plugin = Join-Path $GameDir 'BepInEx\plugins\RadicadeAugustClient.dll'
|
|
$assembly = Join-Path $GameDir 'GameAssembly.dll'
|
|
$interopDir = Join-Path $GameDir 'BepInEx\interop'
|
|
|
|
# Expected values taken from the reference install that logs in and loads rooms.
|
|
$expected = @{
|
|
MetadataSize = 38654794
|
|
MetadataMd5 = 'cc9c32014b5fc3dd1a2a8fb12376be15'
|
|
InteropSize = 38654794
|
|
InteropMd5 = '4a1a28078f957af470f3b31951b32648'
|
|
BootSize = 173
|
|
BootMd5 = 'dc2e5f7af7d5df736a4cc3477b4b1a1f'
|
|
WinhttpSize = 26112
|
|
WinhttpMd5 = 'b2a4d0cb04bf8f5a27ccab237ecc2586'
|
|
LoaderSize = 60928
|
|
LoaderMd5 = '9430bb84349d52b753a181788a86c5e2'
|
|
PluginSize = 62464
|
|
PluginMd5 = '8718a45dd42d7cb72b3e46e04b744d91'
|
|
InteropCount = 335
|
|
AssemblySize = 167390208
|
|
}
|
|
|
|
# --- header ------------------------------------------------------------------
|
|
|
|
Write-Both 'Radicade August 2023 client triage'
|
|
Write-Both ("generated : {0}" -f (Get-Date).ToString('yyyy-MM-dd HH:mm:ss'))
|
|
Write-Both ("game folder : {0}" -f $GameDir)
|
|
Write-Both ("powershell : {0}" -f $PSVersionTable.PSVersion)
|
|
Write-Both ''
|
|
Write-Both 'NOTE: "Failed to load Type RecRoom.Analytics.Statsig.StatsigDebugOverrides" in'
|
|
Write-Both 'Player.log is NORMAL. It appears in a completely unpatched launch of this'
|
|
Write-Both 'build and in every working install. It is not a symptom of anything.'
|
|
Write-Both ''
|
|
|
|
# --- install identity --------------------------------------------------------
|
|
|
|
Write-Both '== game build =='
|
|
if (Test-Path -LiteralPath $assembly) {
|
|
$info = Get-Item -LiteralPath $assembly
|
|
$md5 = Get-Md5 $assembly
|
|
Write-Both ("GameAssembly.dll : {0} b md5 {1}" -f $info.Length, $md5)
|
|
Write-Both (" file version : {0}" -f $info.VersionInfo.FileVersion)
|
|
if ($info.Length -eq $expected.AssemblySize) {
|
|
Add-Verdict 'OK' ('GameAssembly.dll size matches the reference build ({0} b)' -f $expected.AssemblySize)
|
|
} else {
|
|
Add-Verdict 'WARN' ("GameAssembly.dll is {0} b, the reference build is {1} b - a different build will not accept this patch stack" -f $info.Length, $expected.AssemblySize)
|
|
}
|
|
} else {
|
|
Add-Verdict 'MISSING' 'GameAssembly.dll - this is not a Rec Room install folder?'
|
|
}
|
|
if (Test-Path -LiteralPath $playerLog) {
|
|
$firstLine = (Get-Content -LiteralPath $playerLog -TotalCount 1 -ErrorAction SilentlyContinue)
|
|
Write-Both ("Player.log line 1 : {0}" -f $firstLine)
|
|
if ($firstLine -notmatch '2020\.3\.33f1') {
|
|
Add-Verdict 'WARN' 'Player.log does not report Unity 2020.3.33f1 - unexpected engine build'
|
|
}
|
|
} else {
|
|
Add-Verdict 'WARN' ('Player.log not found at {0}' -f $playerLog)
|
|
}
|
|
Write-Both ''
|
|
|
|
# --- the patched data files --------------------------------------------------
|
|
|
|
Write-Both '== patched data files =='
|
|
if (Test-Path -LiteralPath $metadata) {
|
|
$info = Get-Item -LiteralPath $metadata
|
|
$md5 = Get-Md5 $metadata
|
|
$text = Get-ByteString $metadata
|
|
$local = if ($text) { ([regex]::Matches($text, 'localhost:3030')).Count } else { -1 }
|
|
$recnet = if ($text) { ([regex]::Matches($text, 'rec\.net')).Count } else { -1 }
|
|
Write-Both ("global-metadata.dat : {0} b md5 {1}" -f $info.Length, $md5)
|
|
Write-Both (" localhost:3030 : {0} occurrence(s) (reference: 28)" -f $local)
|
|
Write-Both (" rec.net literals : {0} occurrence(s) (reference: 50)" -f $recnet)
|
|
if ($info.Length -eq $expected.MetadataSize -and $md5 -eq $expected.MetadataMd5) {
|
|
Add-Verdict 'OK' 'global-metadata.dat is the Radicade-patched file'
|
|
} elseif ($local -eq 0) {
|
|
Add-Verdict 'MISSING' 'global-metadata.dat is UNPATCHED - the client points at the official servers, the plugin cannot fix that'
|
|
} else {
|
|
Add-Verdict 'WARN' 'global-metadata.dat differs from the reference file (size or md5)'
|
|
}
|
|
} else {
|
|
Add-Verdict 'MISSING' 'Recroom_Data\il2cpp_data\Metadata\global-metadata.dat'
|
|
}
|
|
$metadataOrig = $metadata + '.orig'
|
|
if (Test-Path -LiteralPath $metadataOrig) {
|
|
$origInfo = Get-Item -LiteralPath $metadataOrig
|
|
Add-Line (" global-metadata.dat.orig: {0} b (a 32039456 b copy is the pristine vanilla file the patcher backed up)" -f $origInfo.Length)
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $interopDat) {
|
|
$info = Get-Item -LiteralPath $interopDat
|
|
$md5 = Get-Md5 $interopDat
|
|
if ($info.Length -eq $expected.InteropSize -and $md5 -eq $expected.InteropMd5) {
|
|
Add-Verdict 'OK' 'global-metadata.interop.dat matches the reference'
|
|
} else {
|
|
Add-Verdict 'WARN' ("global-metadata.interop.dat is {0} b md5 {1} (reference {2} b {3})" -f $info.Length, $md5, $expected.InteropSize, $expected.InteropMd5)
|
|
}
|
|
} else {
|
|
Add-Verdict 'MISSING' 'Recroom_Data\il2cpp_data\Metadata\global-metadata.interop.dat - the patched BepInEx loader needs it to build interop assemblies'
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $bootConfig) {
|
|
$info = Get-Item -LiteralPath $bootConfig
|
|
$md5 = Get-Md5 $bootConfig
|
|
Write-Both ("boot.config : {0} b md5 {1}" -f $info.Length, $md5)
|
|
Write-Both (' content : ' + ((Get-Content -LiteralPath $bootConfig) -join ' | '))
|
|
if ($info.Length -eq $expected.BootSize -and $md5 -eq $expected.BootMd5) {
|
|
Add-Verdict 'OK' 'boot.config is the Radicade variant'
|
|
} else {
|
|
Add-Verdict 'WARN' 'boot.config differs from the reference (vr-enabled=0 / player-connection-mode=Listen)'
|
|
}
|
|
} else {
|
|
Add-Verdict 'MISSING' 'Recroom_Data\boot.config'
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $patchJson) {
|
|
Add-Line 'radicade-patch.json :'
|
|
foreach ($line in (Get-Content -LiteralPath $patchJson)) { Add-Line (' ' + $line) }
|
|
Add-Verdict 'OK' 'Recroom_Data\radicade-patch.json present - the metadata was patched on this machine'
|
|
} else {
|
|
Add-Verdict 'MISSING' 'Recroom_Data\radicade-patch.json - no evidence this install was ever patched'
|
|
}
|
|
Write-Both ''
|
|
|
|
# --- loader stack ------------------------------------------------------------
|
|
|
|
Write-Both '== loader stack =='
|
|
if (Test-Path -LiteralPath $winhttp) {
|
|
$info = Get-Item -LiteralPath $winhttp
|
|
$md5 = Get-Md5 $winhttp
|
|
if ($info.Length -eq $expected.WinhttpSize -and $md5 -eq $expected.WinhttpMd5) {
|
|
Add-Verdict 'OK' 'winhttp.dll is the Doorstop 4.3.0 proxy'
|
|
} else {
|
|
Add-Verdict 'WARN' ("winhttp.dll is {0} b md5 {1} (reference {2} b {3}) - another proxy or a disabled/renamed copy?" -f $info.Length, $md5, $expected.WinhttpSize, $expected.WinhttpMd5)
|
|
}
|
|
} else {
|
|
Add-Verdict 'MISSING' 'winhttp.dll - without the Doorstop proxy nothing loads BepInEx'
|
|
if (Test-Path -LiteralPath (Join-Path $GameDir 'winhttp.dll.baseline-disabled')) {
|
|
Add-Verdict 'MISSING' 'winhttp.dll is renamed to winhttp.dll.baseline-disabled - rename it back'
|
|
}
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $doorstop) {
|
|
$cfg = Get-Content -LiteralPath $doorstop -Raw
|
|
if ($cfg -match 'enabled\s*=\s*true' -or $cfg -match '^\s*enabled\s*=\s*1') {
|
|
Add-Verdict 'OK' 'doorstop_config.ini has enabled = true'
|
|
} else {
|
|
Add-Verdict 'MISSING' 'doorstop_config.ini does not set enabled = true'
|
|
}
|
|
if ($cfg -match 'BepInEx\\core\\BepInEx\.Unity\.IL2CPP\.dll') {
|
|
Add-Verdict 'OK' 'doorstop_config.ini targets BepInEx\core\BepInEx.Unity.IL2CPP.dll'
|
|
} else {
|
|
Add-Verdict 'WARN' 'doorstop_config.ini target_assembly is not the IL2CPP preloader'
|
|
}
|
|
} else {
|
|
Add-Verdict 'MISSING' 'doorstop_config.ini'
|
|
}
|
|
if (Test-Path -LiteralPath (Join-Path $GameDir '.doorstop_version')) {
|
|
Add-Line (' .doorstop_version : ' + (Get-Content -LiteralPath (Join-Path $GameDir '.doorstop_version') -Raw).Trim())
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $loader) {
|
|
$info = Get-Item -LiteralPath $loader
|
|
$md5 = Get-Md5 $loader
|
|
$patched = Test-Utf16Literal -Path $loader -Literal 'global-metadata.interop.dat'
|
|
if ($info.Length -eq $expected.LoaderSize -and $md5 -eq $expected.LoaderMd5) {
|
|
Add-Verdict 'OK' 'BepInEx.Unity.IL2CPP.dll is the patched loader (reads global-metadata.interop.dat)'
|
|
} elseif (-not $patched) {
|
|
Add-Verdict 'MISSING' 'BepInEx.Unity.IL2CPP.dll is UNPATCHED - Cpp2IL will reject this build''s metadata header'
|
|
} else {
|
|
Add-Verdict 'WARN' ("BepInEx.Unity.IL2CPP.dll is {0} b md5 {1} (reference {2} b {3})" -f $info.Length, $md5, $expected.LoaderSize, $expected.LoaderMd5)
|
|
}
|
|
} else {
|
|
Add-Verdict 'MISSING' 'BepInEx\core\BepInEx.Unity.IL2CPP.dll'
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $interopDir) {
|
|
$count = (Get-ChildItem -LiteralPath $interopDir -File -ErrorAction SilentlyContinue).Count
|
|
if ($count -ge $expected.InteropCount) {
|
|
Add-Verdict 'OK' ("BepInEx\interop holds {0} assemblies" -f $count)
|
|
} else {
|
|
Add-Verdict 'WARN' ("BepInEx\interop holds {0} assemblies (reference {1}) - generation may have failed halfway" -f $count, $expected.InteropCount)
|
|
}
|
|
} else {
|
|
Add-Verdict 'MISSING' 'BepInEx\interop - the game''s managed proxy assemblies are absent'
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $plugin) {
|
|
$info = Get-Item -LiteralPath $plugin
|
|
$md5 = Get-Md5 $plugin
|
|
if ($info.Length -eq $expected.PluginSize -and $md5 -eq $expected.PluginMd5) {
|
|
Add-Verdict 'OK' 'RadicadeAugustClient.dll matches the reference plugin build'
|
|
} else {
|
|
Add-Verdict 'WARN' ("RadicadeAugustClient.dll is {0} b md5 {1} (reference {2} b {3}) - a different build" -f $info.Length, $md5, $expected.PluginSize, $expected.PluginMd5)
|
|
}
|
|
} else {
|
|
Add-Verdict 'MISSING' 'BepInEx\plugins\RadicadeAugustClient.dll'
|
|
}
|
|
$otherPlugins = Get-ChildItem -LiteralPath (Join-Path $GameDir 'BepInEx\plugins') -File -Filter *.dll -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.Name -ne 'RadicadeAugustClient.dll' }
|
|
foreach ($p in $otherPlugins) {
|
|
Add-Verdict 'WARN' ('extra plugin in BepInEx\plugins: {0} ({1} b) - it may not be August-compatible' -f $p.Name, $p.Length)
|
|
}
|
|
Write-Both ''
|
|
|
|
# --- server reachability -----------------------------------------------------
|
|
|
|
Write-Both '== local server =='
|
|
$https = Test-Tcp -Port 3030
|
|
$http = Test-Tcp -Port 3031
|
|
if ($https) { Add-Verdict 'OK' 'something is listening on https://127.0.0.1:3030' }
|
|
else { Add-Verdict 'MISSING' 'nothing is listening on port 3030 - the client is hardwired to https://localhost:3030, so the Radicade server must run on THIS machine' }
|
|
if ($http) { Add-Verdict 'OK' 'something is listening on http://127.0.0.1:3031' }
|
|
else { Add-Verdict 'WARN' 'nothing is listening on port 3031' }
|
|
Write-Both ''
|
|
|
|
# --- logs --------------------------------------------------------------------
|
|
|
|
Write-Both '== BepInEx log =='
|
|
if (Test-Path -LiteralPath $bepLog) {
|
|
$markers = Get-LogMarkers -Path $bepLog -Markers @(
|
|
'Chainloader startup complete',
|
|
'Radicade August Client',
|
|
'Bypassing native launch validator',
|
|
'global-metadata.interop.dat',
|
|
'Cpp2IL',
|
|
'[Error',
|
|
'[Warning'
|
|
)
|
|
foreach ($k in $markers.Keys) { Add-Line (" {0,-38}: {1}" -f $k, $markers[$k]) }
|
|
if ($markers['Chainloader startup complete'] -gt 0) { Add-Verdict 'OK' 'BepInEx chainloader came up' }
|
|
else { Add-Verdict 'MISSING' 'BepInEx chainloader never completed - read the log tail below' }
|
|
if ($markers['Radicade August Client'] -gt 0) { Add-Verdict 'OK' 'the Radicade plugin logged under its own name' }
|
|
else { Add-Verdict 'MISSING' 'the Radicade plugin never logged - it did not load' }
|
|
Add-Line ''
|
|
Add-Line ' last 25 lines:'
|
|
foreach ($line in (Get-Content -LiteralPath $bepLog -Tail 25 -ErrorAction SilentlyContinue)) { Add-Line (' ' + $line) }
|
|
} else {
|
|
Add-Verdict 'MISSING' ('BepInEx\LogOutput.log not found at {0} - BepInEx never started' -f $bepLog)
|
|
}
|
|
Write-Both ''
|
|
|
|
Write-Both '== Player.log =='
|
|
if (Test-Path -LiteralPath $playerLog) {
|
|
$markers = Get-LogMarkers -Path $playerLog -Markers @(
|
|
'Failed to load Type "RecRoom.Analytics.Statsig.StatsigDebugOverrides"',
|
|
'Could not resolve host',
|
|
"Could not resolve host 'ns.rec.net'",
|
|
'Launch validation failed',
|
|
'FailedToSpawnPlayer',
|
|
'Error downloading save data',
|
|
'ns.rec.net',
|
|
'localhost:3030'
|
|
)
|
|
foreach ($k in $markers.Keys) { Add-Line (" {0,-72}: {1}" -f $k, $markers[$k]) }
|
|
if ($markers["Could not resolve host 'ns.rec.net'"] -gt 0 -or $markers['ns.rec.net'] -gt 0) {
|
|
Add-Verdict 'MISSING' 'the client still talks to ns.rec.net - the metadata host literals are unpatched (or the plugin''s nameserver redirect never installed)'
|
|
} elseif ($markers['localhost:3030'] -gt 0) {
|
|
Add-Verdict 'OK' 'the client''s requests go to localhost:3030'
|
|
}
|
|
if ($markers['Launch validation failed'] -gt 0) {
|
|
Add-Verdict 'MISSING' 'the native launch validator rejected the install and no bypass ran'
|
|
}
|
|
Add-Line ''
|
|
Add-Line ' last 25 lines:'
|
|
foreach ($line in (Get-Content -LiteralPath $playerLog -Tail 25 -ErrorAction SilentlyContinue)) { Add-Line (' ' + $line) }
|
|
} else {
|
|
Add-Verdict 'MISSING' ('Player.log not found at {0}' -f $playerLog)
|
|
}
|
|
|
|
# --- verdict -----------------------------------------------------------------
|
|
|
|
Write-Both ''
|
|
Write-Both ('RESULT: {0} missing piece(s), {1} warning(s)' -f $script:Failures, $script:Warnings)
|
|
if ($script:Failures -eq 0) {
|
|
Write-Both 'Every required piece is present and the local server answers. If the game still'
|
|
Write-Both 'fails, the report above (BepInEx log tail + Player.log tail) names the next cause.'
|
|
} else {
|
|
Write-Both 'The [MISSING] lines above are the install steps that did not happen.'
|
|
}
|
|
Write-Both ''
|
|
Write-Both 'Reminder: the StatsigDebugOverrides line is expected noise in every launch.'
|
|
|
|
try {
|
|
Set-Content -LiteralPath $OutFile -Value ($script:Report -join "`r`n") -Encoding UTF8
|
|
Write-Host ''
|
|
Write-Host ("Report written to {0}" -f $OutFile)
|
|
}
|
|
catch {
|
|
Write-Host ("Could not write {0}: {1}" -f $OutFile, $_.Exception.Message)
|
|
}
|