param( [string]$Root = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path ) $ErrorActionPreference = 'Stop' function Pass($message) { Write-Host "[OK] $message" -ForegroundColor Green } function Fail($message) { throw "[FAIL] $message" } function Require-File($relative) { $path = Join-Path $Root $relative if (!(Test-Path -LiteralPath $path -PathType Leaf)) { Fail "Missing file: $relative" } Pass "Found $relative" } Push-Location $Root try { Require-File 'PokerGameSetup.exe' Require-File 'Assembler Casino.exe' Require-File 'Uninstall Assembler Casino.exe' Require-File 'bin\poker.exe' Require-File 'assets\icons\casino.ico' Require-File 'assets\animations\hakari_dance.ans' Require-File 'assets\animations\dbd_hook.ans' Require-File 'tools\preview_dbd_hook.bat' Require-File 'docs\README_RELEASE.txt' Require-File 'docs\RELEASE_CHECKLIST.txt' Require-File 'docs\KNOWN_LIMITATIONS.txt' Require-File 'docs\SECURITY_NOTES.txt' Require-File 'docs\ROADMAP.txt' Require-File 'docs\ONLINE_RADMIN.txt' foreach ($mambo in @( 'assets\animations\mambo1.ans', 'assets\animations\mambo2.ans', 'assets\animations\mambo3.ans', 'assets\animations\mambo4.ans' )) { if (Test-Path -LiteralPath (Join-Path $Root $mambo) -PathType Leaf) { Fail "Removed MAMBO animation still exists: $mambo" } } Pass 'MAMBO animation files are removed from assets' $dbdBat = Get-Content -LiteralPath (Join-Path $Root 'tools\preview_dbd_hook.bat') -Raw if ($dbdBat -match 'Clear-Host') { Fail 'DBD helper still uses Clear-Host' } Pass 'DBD helper does not use Clear-Host' $asmBytes = [IO.File]::ReadAllBytes((Join-Path $Root 'poker.asm')) $cp1251 = [Text.Encoding]::GetEncoding(1251) $asmText = $cp1251.GetString($asmBytes) foreach ($needle in @( 'settingsCinematicText db "Cinematic -', 'settingsAdaptiveText db "Adaptive -', 'settingsStableText db "Stable -', 'AnimShouldDrawFrame PROC', 'AnimFrameDelay PROC', 'WriteAnimProfileFile PROC', 'SAVE_SIGN_SIZE equ 276', 'SAVE_FILE_SIZE equ 280', 'CalcSaveSignature PROC', 'mov DWORD PTR [saveBuffer+SAVE_SIGN_SIZE], eax', 'cmp charsRead, SAVE_FILE_SIZE', 'includelib ws2_32.lib', 'GameOnlineCasino PROC', 'OnlineRRSessionHost PROC', 'OnlineRRSessionClient PROC' )) { if ($asmText.IndexOf($needle) -lt 0) { Fail "Missing source marker: $needle" } } Pass 'Animation profile and save signature markers exist in poker.asm' if ($asmText -match 'MAMBO|mambo|PlayMambo') { Fail 'MAMBO markers still exist in poker.asm' } Pass 'MAMBO markers are removed from poker.asm' $launcherSource = Join-Path $Root 'build\AssemblerCasinoLauncher.cs' if (!(Test-Path -LiteralPath $launcherSource -PathType Leaf)) { Fail 'Missing launcher source: build\AssemblerCasinoLauncher.cs' } $launcherText = Get-Content -LiteralPath $launcherSource -Raw -Encoding UTF8 $expectedPokerHash = $null if ($launcherText -match 'ExpectedPokerSha256 = "([A-Fa-f0-9]{64})"') { $expectedPokerHash = $matches[1].ToUpperInvariant() } if ([string]::IsNullOrWhiteSpace($expectedPokerHash)) { Fail 'Launcher source does not contain a fixed poker.exe SHA-256 hash' } $pokerSha = [Security.Cryptography.SHA256]::Create() try { $actualPokerHash = [BitConverter]::ToString($pokerSha.ComputeHash([IO.File]::ReadAllBytes((Join-Path $Root 'bin\poker.exe')))).Replace('-', '').ToUpperInvariant() } finally { $pokerSha.Dispose() } if ($actualPokerHash -ne $expectedPokerHash) { Fail "Launcher poker.exe SHA-256 mismatch. Expected $expectedPokerHash, actual $actualPokerHash" } Pass 'Launcher poker.exe SHA-256 matches current bin\poker.exe' $setupSource = Join-Path $Root 'build\PokerGameSetupGui.cs' if (!(Test-Path -LiteralPath $setupSource -PathType Leaf)) { Fail 'Missing installer source: build\PokerGameSetupGui.cs' } $setupSourceText = Get-Content -LiteralPath $setupSource -Raw -Encoding UTF8 $expectedHash = $null if ($setupSourceText -match 'ExpectedPayloadSha256 = "([A-Fa-f0-9]{64})"') { $expectedHash = $matches[1].ToUpperInvariant() } if ([string]::IsNullOrWhiteSpace($expectedHash)) { Fail 'Installer source does not contain a fixed SHA-256 payload hash' } Add-Type -AssemblyName System.IO.Compression Add-Type -AssemblyName System.IO.Compression.FileSystem $setupPath = Join-Path $Root 'PokerGameSetup.exe' $fs = [IO.File]::OpenRead($setupPath) try { $head = New-Object byte[] ([Math]::Min(2097152, $fs.Length)) [void]$fs.Read($head, 0, $head.Length) $marker = [Text.Encoding]::UTF8.GetBytes('<>') $idx = -1 for ($i = 0; $i -le $head.Length - $marker.Length; $i++) { if ($head[$i] -ne $marker[0]) { continue } $ok = $true for ($j = 1; $j -lt $marker.Length; $j++) { if ($head[$i + $j] -ne $marker[$j]) { $ok = $false; break } } if ($ok) { $idx = $i; break } } if ($idx -lt 0) { Fail 'Installer payload marker not found' } $fs.Position = $idx + $marker.Length $zipBytes = New-Object byte[] ($fs.Length - $fs.Position) [void]$fs.Read($zipBytes, 0, $zipBytes.Length) } finally { $fs.Dispose() } $ms = New-Object IO.MemoryStream(,$zipBytes) $zip = New-Object IO.Compression.ZipArchive($ms, [IO.Compression.ZipArchiveMode]::Read) try { $names = @($zip.Entries | ForEach-Object FullName) foreach ($entry in @( 'Assembler Casino.exe', 'Uninstall Assembler Casino.exe', 'bin/poker.exe', 'assets/icons/casino.ico', 'tools/preview_dbd_hook.bat', 'tools/release_smoke_check.ps1', 'docs/README_RELEASE.txt', 'docs/RELEASE_CHECKLIST.txt', 'docs/KNOWN_LIMITATIONS.txt', 'docs/SECURITY_NOTES.txt', 'docs/ROADMAP.txt', 'docs/ONLINE_RADMIN.txt' )) { if ($names -notcontains $entry) { Fail "Installer payload missing: $entry" } } if ($names -contains 'Assembler Casino.bat') { Fail 'Installer payload still contains old Assembler Casino.bat' } if ($names | Where-Object { $_ -like 'tools/generated_frames/*' }) { Fail 'Installer payload contains generated preview frames/tools' } foreach ($entry in @( 'assets/animations/mambo1.ans', 'assets/animations/mambo2.ans', 'assets/animations/mambo3.ans', 'assets/animations/mambo4.ans' )) { if ($names -contains $entry) { Fail "Installer payload still contains removed MAMBO animation: $entry" } } Pass "Installer payload OK ($($names.Count) files)" } finally { $zip.Dispose() $ms.Dispose() } $sha = [Security.Cryptography.SHA256]::Create() try { $actualHash = [BitConverter]::ToString($sha.ComputeHash($zipBytes)).Replace('-', '').ToUpperInvariant() } finally { $sha.Dispose() } if ($actualHash -ne $expectedHash) { Fail "Installer payload SHA-256 mismatch. Expected $expectedHash, actual $actualHash" } Pass 'Installer payload SHA-256 matches embedded hash' Write-Host '' Write-Host 'Release smoke check passed.' -ForegroundColor Cyan } finally { Pop-Location }