# Builds IdleMafiaBot.exe with the C# compiler that comes with Windows (.NET Framework 4.x). # Needs the Windows 10/11 SDK for Windows.winmd (the built-in Windows OCR). # Usage: powershell -ExecutionPolicy Bypass -File build.ps1 [-Out file.exe] param([string]$Out = 'IdleMafiaBot.exe') $ErrorActionPreference = 'Stop' Set-Location $PSScriptRoot $fx = "$env:WINDIR\Microsoft.NET\Framework64\v4.0.30319" $csc = "$fx\csc.exe" $gac = "$env:WINDIR\Microsoft.NET\assembly\GAC_MSIL" $winmd = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\UnionMetadata\*\Windows.winmd" -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notmatch '\\Facade\\' } | Sort-Object FullName -Descending | Select-Object -First 1 if (-not $winmd) { throw 'Windows SDK metadata (Windows.winmd) not found - install the Windows 10/11 SDK.' } $refs = @( 'System.dll', 'System.Core.dll', 'System.Drawing.dll', 'System.Xaml.dll', "$fx\WPF\PresentationFramework.dll", "$fx\WPF\PresentationCore.dll", "$fx\WPF\WindowsBase.dll", "$fx\System.IO.Compression.dll", "$fx\System.IO.Compression.FileSystem.dll", 'System.Security.dll', $winmd.FullName, "$gac\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll", "$gac\System.Runtime.WindowsRuntime\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.WindowsRuntime.dll", "$gac\System.Runtime.InteropServices.WindowsRuntime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.InteropServices.WindowsRuntime.dll", "$gac\System.Threading.Tasks\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Threading.Tasks.dll" ) | ForEach-Object { "/r:$_" } # the window (WPF): src\ui\*.xaml are embedded in the exe and loaded at startup $resources = @(Get-ChildItem src\ui\*.xaml, src\ui\*.ttf | ForEach-Object { "/resource:$($_.FullName),IdleMafiaBot.ui.$($_.Name)" }) $icon = @('/win32icon:src\ui\app.ico', "/resource:$((Resolve-Path src\ui\app.ico).Path),IdleMafiaBot.ui.app.ico") $sources = @(Get-ChildItem src\*.cs | ForEach-Object { $_.FullName }) & $csc /nologo /optimize+ /codepage:65001 /target:winexe "/out:$Out" @icon @refs @resources @sources if ($LASTEXITCODE -ne 0) { throw "compile failed ($LASTEXITCODE)" } # Same code, same exe byte for byte, so a build can be checked against a release's SHA-256: the compiler writes three # random things - the build time, the module's ID (MVID) and a type name {guid}. # The time and the name are blanked, the ID becomes a hash of the rest. $b = [IO.File]::ReadAllBytes((Resolve-Path $Out)) $pe = [BitConverter]::ToInt32($b, 0x3C) [Array]::Clear($b, $pe + 8, 4) $opt = $pe + 24 $dirs = $opt + $(if ([BitConverter]::ToUInt16($b, $opt) -eq 0x20B) { 112 } else { 96 }) $secs = $opt + [BitConverter]::ToUInt16($b, $pe + 20) function Off([int]$rva) { for ($i = 0; $i -lt [BitConverter]::ToUInt16($b, $pe + 6); $i++) { $s = $secs + 40 * $i; $va = [BitConverter]::ToInt32($b, $s + 12) if ($rva -ge $va -and $rva -lt $va + [BitConverter]::ToInt32($b, $s + 16)) { return $rva - $va + [BitConverter]::ToInt32($b, $s + 20) } } throw "rva $rva not in a section" } $md = Off ([BitConverter]::ToInt32($b, (Off ([BitConverter]::ToInt32($b, $dirs + 14 * 8))) + 8)) $p = $md + 16 + [BitConverter]::ToInt32($b, $md + 12); $streams = [BitConverter]::ToUInt16($b, $p + 2); $p += 4 $mvid = -1 for ($i = 0; $i -lt $streams; $i++) { $at = [BitConverter]::ToInt32($b, $p); $e = $p + 8; while ($b[$e] -ne 0) { $e++ } if ([Text.Encoding]::ASCII.GetString($b, $p + 8, $e - $p - 8) -eq '#GUID') { $mvid = $md + $at } $p = ($e + 4) -band -4 } if ($mvid -lt 0) { throw 'no #GUID stream' } [Array]::Clear($b, $mvid, 16) $text = [Text.Encoding]::GetEncoding(28591).GetString($b) $tag = '{' for ($i = $text.IndexOf($tag, [StringComparison]::Ordinal); $i -ge 0; $i = $text.IndexOf($tag, $i + 1, [StringComparison]::Ordinal)) { [Text.Encoding]::ASCII.GetBytes('00000000-0000-0000-0000-000000000000').CopyTo($b, $i + $tag.Length) } [Array]::Copy([Security.Cryptography.SHA256]::Create().ComputeHash($b), 0, $b, $mvid, 16) [IO.File]::WriteAllBytes((Resolve-Path $Out), $b) "built $Out" (Get-FileHash $Out -Algorithm SHA256).Hash