param( [string]$LauncherPath = (Join-Path (Split-Path $PSScriptRoot -Parent) '..\Launcher'), [string]$BuildDirectory = (Join-Path (Split-Path $PSScriptRoot -Parent) 'build-nmake') ) $ErrorActionPreference = 'Stop' $root = Split-Path $PSScriptRoot -Parent $launcherPath = [System.IO.Path]::GetFullPath($LauncherPath) $buildDirectory = [System.IO.Path]::GetFullPath($BuildDirectory) function Assert-Condition { param([bool]$Condition, [string]$Message) if (-not $Condition) { throw $Message } } function Read-FeatureKeys { param([string]$IniPath) $section = '' $keys = [System.Collections.Generic.List[string]]::new() foreach ($rawLine in Get-Content $IniPath) { $line = $rawLine.Trim() if ($line -match '^\[(.+)\]$') { $section = $Matches[1] continue } if ($section -ieq 'rem-essentials' -and $line -match '^([^;][^=]+?)\s*=') { $keys.Add($Matches[1].Trim()) } } return $keys } function Normalize-IniText { param([string]$Text) return (($Text -replace "`r`n", "`n").TrimEnd()) + "`n" } $mainSource = Get-Content (Join-Path $root 'src\main.cpp') -Raw $registeredKeys = [regex]::Matches($mainSource, 'manager\.Register\("([^"]+)"') | ForEach-Object { $_.Groups[1].Value } $iniKeys = Read-FeatureKeys (Join-Path $root 'rem-essentials.ini') $missingInIni = @($registeredKeys | Where-Object { $_ -notin $iniKeys }) $missingInCode = @($iniKeys | Where-Object { $_ -notin $registeredKeys }) Assert-Condition ($missingInIni.Count -eq 0) "Registered features missing from INI: $($missingInIni -join ', ')" Assert-Condition ($missingInCode.Count -eq 0) "INI features missing from code: $($missingInCode -join ', ')" Assert-Condition (($registeredKeys | Select-Object -Unique).Count -eq $registeredKeys.Count) 'Duplicate feature registrations found.' $cmake = Get-Content (Join-Path $root 'CMakeLists.txt') -Raw Assert-Condition ($cmake -notmatch '(?i)direct_?ips') 'direct_ips must not be compiled.' $launcherServicePath = Join-Path $launcherPath 'RemLauncher.Core\Services\GameSettingsService.cs' $launcherMainPath = Join-Path $launcherPath 'RemLauncher\MainWindow.xaml.cs' $launcherHtmlPath = Join-Path $launcherPath 'web\templates\gamesettings.html' Assert-Condition (Test-Path $launcherServicePath) "Launcher service not found at $launcherServicePath" $launcherService = Get-Content $launcherServicePath -Raw $rawIniMatch = [regex]::Match( $launcherService, 'private const string DefaultRemEssentialsIni = """\r?\n(?.*?)\r?\n\s*""";', [System.Text.RegularExpressions.RegexOptions]::Singleline) Assert-Condition $rawIniMatch.Success 'Could not extract the embedded launcher INI.' $embeddedLines = $rawIniMatch.Groups['ini'].Value -split '\r?\n' $embeddedIni = ($embeddedLines | ForEach-Object { if ($_.Length -ge 8) { $_.Substring(8) } else { '' } }) -join "`n" $sourceIni = Get-Content (Join-Path $root 'rem-essentials.ini') -Raw Assert-Condition ((Normalize-IniText $embeddedIni) -ceq (Normalize-IniText $sourceIni)) 'Launcher and plugin default INIs differ.' $launcherMain = Get-Content $launcherMainPath -Raw $launcherHtml = Get-Content $launcherHtmlPath -Raw Assert-Condition ($launcherMain -match 'PostMessage\(String\(message\)\);\s*return true;') 'CEF bridge does not report successful messages.' Assert-Condition ($launcherHtml -match "postMessage\('getGameSettings'\) === false") 'Settings page does not handle bridge failure explicitly.' Assert-Condition ($launcherMain -match 'ApplyRemEssentialsUserSettings\(\);\s*await _log\.AddLogAsync\("REM Essentials user settings applied after patching') 'Overrides are not applied after patching.' $builtDll = Join-Path $buildDirectory 'bin\rem-essentials.dll' $releaseDll = Join-Path $root 'release\rem-essentials.dll' Assert-Condition (Test-Path $builtDll) "Built DLL not found at $builtDll" Assert-Condition (Test-Path $releaseDll) "Release DLL not found at $releaseDll" $bytes = [System.IO.File]::ReadAllBytes($builtDll) Assert-Condition ($bytes.Length -gt 0x40 -and $bytes[0] -eq 0x4D -and $bytes[1] -eq 0x5A) 'Built DLL is not a PE file.' $peOffset = [BitConverter]::ToInt32($bytes, 0x3C) $machine = [BitConverter]::ToUInt16($bytes, $peOffset + 4) Assert-Condition ($machine -eq 0x014C) ('Built DLL is not x86 (machine 0x{0:X4}).' -f $machine) $builtHash = (Get-FileHash $builtDll -Algorithm SHA256).Hash $releaseHash = (Get-FileHash $releaseDll -Algorithm SHA256).Hash Assert-Condition ($builtHash -eq $releaseHash) 'Release DLL does not match the current build.' Write-Host "Release validation passed: $($registeredKeys.Count) features, x86 DLL $builtHash"