mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-10 09:11:52 -04:00
332 lines
20 KiB
PowerShell
332 lines
20 KiB
PowerShell
[CmdletBinding(PositionalBinding = $false)]
|
|
param(
|
|
[string]$OutputDirectory = 'Launcher/dist',
|
|
[string]$PortableToolsDirectory = 'Launcher/artifacts/portable-tools',
|
|
[string]$DependencySourceDirectory = 'Launcher/artifacts/dependencies',
|
|
[string]$VcRuntimeDirectory,
|
|
[string]$ToolkitReleaseTag = $env:GITHUB_REF_NAME
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
Set-StrictMode -Version 3.0
|
|
|
|
# The canonical configure flags, and the Assert-File/Assert-Directory/Get-MkwFileSha256
|
|
# helpers shared with LocalBuild.ps1 and Prepare-NativePrebuilt.ps1.
|
|
. (Join-Path $PSScriptRoot 'NativeBuildFlags.ps1')
|
|
|
|
$repoRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
|
$outputRoot = [IO.Path]::GetFullPath((Join-Path $repoRoot $OutputDirectory))
|
|
$portableTools = [IO.Path]::GetFullPath((Join-Path $repoRoot $PortableToolsDirectory))
|
|
$dependencySources = [IO.Path]::GetFullPath((Join-Path $repoRoot $DependencySourceDirectory))
|
|
$workRoot = Join-Path $PSScriptRoot 'artifacts\installer-build'
|
|
$publish = Join-Path $workRoot 'publish'
|
|
$payloadRoot = Join-Path $workRoot 'payload'
|
|
$setupProject = Join-Path $PSScriptRoot 'WiiCompiled.Setup.Windows\WiiCompiled.Setup.Windows.csproj'
|
|
$translatorProject = Join-Path $repoRoot 'translator\src\Translator.Cli\Translator.Cli.csproj'
|
|
$projectFile = Join-Path $repoRoot 'projects\mkwii\recomp.yml'
|
|
|
|
# Everything Mario-Kart-specific in the manifest below is read from the project file rather than
|
|
# restated here, and Test-PinnedFacts.ps1 checks the copies that cannot read it (the C++ entry
|
|
# address, the C# endpoint constant, the lists this script and the installed host both carry).
|
|
$pins = Get-MkwProjectPins $projectFile
|
|
# The audit reports by throwing, which aborts this run; nothing native has been invoked yet, so
|
|
# there is no exit code to inspect.
|
|
& (Join-Path $PSScriptRoot 'Test-PinnedFacts.ps1') -RepositoryRoot $repoRoot
|
|
|
|
function Reset-Directory([string]$Path) {
|
|
$full = [IO.Path]::GetFullPath($Path)
|
|
$allowed = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot 'artifacts')).TrimEnd('\') + '\'
|
|
if (-not $full.StartsWith($allowed, [StringComparison]::OrdinalIgnoreCase)) {
|
|
throw "Refusing to reset a directory outside Launcher/artifacts: $full"
|
|
}
|
|
if (Test-Path -LiteralPath $full) { Remove-Item -LiteralPath $full -Recurse -Force }
|
|
[IO.Directory]::CreateDirectory($full) | Out-Null
|
|
}
|
|
function Reset-OutputDirectory([string]$Path) {
|
|
$full = [IO.Path]::GetFullPath($Path)
|
|
$allowed = [IO.Path]::GetFullPath($PSScriptRoot).TrimEnd('\') + '\'
|
|
if (-not $full.StartsWith($allowed, [StringComparison]::OrdinalIgnoreCase)) {
|
|
throw "Refusing to reset an output directory outside Launcher: $full"
|
|
}
|
|
if (Test-Path -LiteralPath $full) { Remove-Item -LiteralPath $full -Recurse -Force }
|
|
[IO.Directory]::CreateDirectory($full) | Out-Null
|
|
}
|
|
function Resolve-VcRuntimeDirectory([string]$RequestedPath) {
|
|
if ($RequestedPath) {
|
|
$resolved = [IO.Path]::GetFullPath($RequestedPath)
|
|
Assert-File (Join-Path $resolved 'vcruntime140.dll') 'Visual C++ runtime'
|
|
Assert-File (Join-Path $resolved 'msvcp140.dll') 'Visual C++ standard library runtime'
|
|
return $resolved
|
|
}
|
|
$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'
|
|
Assert-File $vswhere 'Visual Studio locator (or pass -VcRuntimeDirectory)'
|
|
$installation = (& $vswhere -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath | Select-Object -First 1)
|
|
if (-not $installation) { throw 'Visual C++ redistributable files were not found; pass -VcRuntimeDirectory.' }
|
|
$redistRoot = Join-Path $installation 'VC\Redist\MSVC'
|
|
$candidate = Get-ChildItem -LiteralPath $redistRoot -Directory -Recurse | Where-Object {
|
|
$_.Parent.Name -eq 'x64' -and $_.Name -match '^Microsoft\.VC\d+\.CRT$' -and
|
|
(Test-Path (Join-Path $_.FullName 'vcruntime140.dll')) -and (Test-Path (Join-Path $_.FullName 'msvcp140.dll'))
|
|
} | Sort-Object FullName -Descending | Select-Object -First 1
|
|
if (-not $candidate) { throw "No redistributable x64 Visual C++ runtime was found below $redistRoot." }
|
|
return $candidate.FullName
|
|
}
|
|
function Copy-Directory([string]$Source, [string]$Destination) {
|
|
Assert-Directory $Source "Source directory"
|
|
[IO.Directory]::CreateDirectory($Destination) | Out-Null
|
|
Copy-Item -Path (Join-Path $Source '*') -Destination $Destination -Recurse -Force
|
|
}
|
|
function Compress-Zip([string]$Source, [string]$Destination, [string[]]$Entries) {
|
|
$tar = Join-Path $env:SystemRoot 'System32\tar.exe'
|
|
Assert-File $tar 'Windows archive tool'
|
|
if (Test-Path -LiteralPath $Destination) { Remove-Item -LiteralPath $Destination -Force }
|
|
& $tar -a -c -f $Destination -C $Source @Entries
|
|
if ($LASTEXITCODE -ne 0) { throw "ZIP creation failed with exit code $LASTEXITCODE." }
|
|
Assert-File $Destination 'ZIP output'
|
|
}
|
|
|
|
Assert-File $setupProject '.NET setup project'
|
|
Assert-File $translatorProject 'Translator CLI project'
|
|
|
|
if (-not (Test-Path -LiteralPath (Join-Path $portableTools 'llvm-mingw\bin\x86_64-w64-mingw32-clang++.exe'))) {
|
|
# A PowerShell script, not a native executable - it never touches $LASTEXITCODE, and its own
|
|
# $ErrorActionPreference = 'Stop' + throw already aborts this run on failure.
|
|
& (Join-Path $PSScriptRoot 'Prepare-PortableTools.ps1') -Destination $portableTools
|
|
}
|
|
Assert-File (Join-Path $portableTools 'CMake\bin\cmake.exe') 'Portable CMake'
|
|
Assert-File (Join-Path $portableTools 'Ninja\ninja.exe') 'Portable Ninja'
|
|
# native_prebuilt carries the aurora/third-party archives the user no longer has
|
|
# to compile (launcher/Prepare-NativePrebuilt.ps1).
|
|
# Kept in step with InstalledLayout.DependencyNames by Test-PinnedFacts.ps1: the installed host
|
|
# refuses to call a toolkit complete unless every one of these directories is present.
|
|
$requiredDependencies = @('abseil-cpp','cppwinrt','dawn_prebuilt','fmt','freetype','imgui','libusb','native_prebuilt','png','SDL','sqlite3','tracy','xxhash','zlib','zstd')
|
|
$missingSources = @($requiredDependencies | Where-Object { $_ -ne 'native_prebuilt' } |
|
|
Where-Object { -not (Test-Path -LiteralPath (Join-Path $dependencySources $_) -PathType Container) })
|
|
if ($missingSources.Count -gt 0) {
|
|
& (Join-Path $PSScriptRoot 'Prepare-Dependencies.ps1') -Destination $dependencySources
|
|
}
|
|
Assert-Directory $dependencySources 'Pinned offline dependency sources'
|
|
|
|
# The precompiled archives are only interchangeable with what the user's machine
|
|
# compiles if both came from this toolchain and this flag set, so a stale package
|
|
# is rebuilt here rather than shipped. Without it every installation would fall
|
|
# back to compiling aurora from source, which is what this package exists to
|
|
# avoid - and a mismatched one would be an ABI hazard.
|
|
$portableClangHash = Get-MkwFileSha256 (Join-Path $portableTools 'llvm-mingw\bin\clang-22.exe')
|
|
function Test-NativePrebuiltCurrent([string]$PackageDirectory) {
|
|
$provenancePath = Join-Path $PackageDirectory 'provenance.json'
|
|
if (-not (Test-Path -LiteralPath $provenancePath -PathType Leaf)) { return $false }
|
|
$provenance = Get-Content -LiteralPath $provenancePath -Raw | ConvertFrom-Json
|
|
# Refreshing the pinned Dawn without re-harvesting would ship archives compiled
|
|
# against headers that no longer describe the shipped DLL. A package predating
|
|
# this field cannot prove it matches, so it is treated as stale.
|
|
$recordedDawn = $provenance.PSObject.Properties['DawnRuntimeSha256']
|
|
# Same reasoning for aurora itself: this release ships aurora-main sources
|
|
# whose headers the user's runtime compiles against, so archives harvested
|
|
# before an aurora change would be linked into call sites that no longer
|
|
# match them.
|
|
$recordedAurora = $provenance.PSObject.Properties['AuroraSourceFingerprint']
|
|
# And for the vendored Crypto++ whose archive the package now carries: this
|
|
# release ships runtime/third_party sources that first-party TUs compile
|
|
# against, so archives harvested before a vendored change are stale.
|
|
$recordedThirdParty = $provenance.PSObject.Properties['ThirdPartySourceFingerprint']
|
|
return ($provenance.CompilerSha256 -eq $portableClangHash) -and
|
|
($provenance.FlagFingerprint -eq (Get-MkwNativeFlagFingerprint)) -and
|
|
($null -ne $recordedDawn) -and
|
|
($recordedDawn.Value -eq (Get-MkwDawnRuntimeSha256 $dependencySources)) -and
|
|
($null -ne $recordedAurora) -and
|
|
($recordedAurora.Value -eq (Get-MkwAuroraSourceFingerprint (Join-Path $repoRoot 'aurora-main'))) -and
|
|
($null -ne $recordedThirdParty) -and
|
|
($recordedThirdParty.Value -eq (Get-MkwThirdPartySourceFingerprint (Join-Path $repoRoot 'runtime\third_party')))
|
|
}
|
|
$nativePrebuiltPackage = Join-Path $dependencySources 'native_prebuilt'
|
|
if (-not (Test-NativePrebuiltCurrent $nativePrebuiltPackage)) {
|
|
Write-Host '[0/6] Building the precompiled aurora and third-party package...'
|
|
& (Join-Path $PSScriptRoot 'Prepare-NativePrebuilt.ps1') `
|
|
-PortableToolsDirectory $portableTools -DependencySourceDirectory $dependencySources
|
|
if ($LASTEXITCODE -ne 0) { throw 'Precompiled aurora package preparation failed.' }
|
|
if (-not (Test-NativePrebuiltCurrent $nativePrebuiltPackage)) {
|
|
throw 'The precompiled aurora package still does not match this toolchain and flag set.'
|
|
}
|
|
}
|
|
foreach ($name in $requiredDependencies) { Assert-Directory (Join-Path $dependencySources $name) "Pinned dependency $name" }
|
|
$vcRuntime = Resolve-VcRuntimeDirectory $VcRuntimeDirectory
|
|
|
|
Reset-Directory $workRoot
|
|
Reset-OutputDirectory $outputRoot
|
|
foreach ($path in @($publish,$payloadRoot)) { [IO.Directory]::CreateDirectory($path) | Out-Null }
|
|
|
|
Write-Host '[1/6] Publishing self-contained CLI host and translator...'
|
|
& dotnet publish $setupProject -c Release -r win-x64 --self-contained true `
|
|
-p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true `
|
|
-p:DebugType=None -o (Join-Path $publish 'setup')
|
|
if ($LASTEXITCODE -ne 0) { throw "Setup publish failed with exit code $LASTEXITCODE." }
|
|
& dotnet publish $translatorProject -c Release -r win-x64 --self-contained true `
|
|
-p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true `
|
|
-p:DebugType=None -o (Join-Path $publish 'translator')
|
|
if ($LASTEXITCODE -ne 0) { throw "Translator publish failed with exit code $LASTEXITCODE." }
|
|
$setupHost = Join-Path $publish 'setup\WiiCompiled.Setup.exe'
|
|
$translator = Join-Path $publish 'translator\Translator.Cli.exe'
|
|
Assert-File $setupHost 'Published setup host'
|
|
Assert-File $translator 'Self-contained translator'
|
|
|
|
# Resolved via the shared WiiCompiled.Setup.Common.Cli helper (also used by build-appimage.sh on
|
|
# Linux) rather than a separate download/version-pin copy here: it downloads and caches the same way
|
|
# NodToolProvider.cs always does (Launcher/artifacts/nodtool.exe)
|
|
$nodToolCliProject = Join-Path $PSScriptRoot 'WiiCompiled.Setup.Common.Cli'
|
|
$nodTool = (& dotnet run --project $nodToolCliProject -c Release -- --workspace $repoRoot | Select-Object -Last 1)
|
|
if ($LASTEXITCODE -ne 0) { throw "nodtool resolution failed with exit code $LASTEXITCODE." }
|
|
Assert-File $nodTool 'Resolved nodtool'
|
|
|
|
Write-Host '[2/6] Staging the explicit, game-code-free payload allowlist...'
|
|
# The staged layout mirrors the installed layout exactly (Toolkit, BuildWorkspace): payload
|
|
# identities hash relative paths, so the names here are part of the fingerprint contract.
|
|
$toolkit = Join-Path $payloadRoot 'Toolkit'
|
|
$workspace = Join-Path $payloadRoot 'BuildWorkspace'
|
|
[IO.Directory]::CreateDirectory($toolkit) | Out-Null
|
|
Copy-Directory (Join-Path $portableTools 'llvm-mingw') (Join-Path $toolkit 'llvm-mingw')
|
|
Copy-Directory (Join-Path $portableTools 'CMake') (Join-Path $toolkit 'CMake')
|
|
Copy-Directory (Join-Path $portableTools 'Ninja') (Join-Path $toolkit 'Ninja')
|
|
# Debuggers, Python bindings, and the CMake GUI are not part of the local build graph.
|
|
# Leaving them out reduces the attack/dependency surface without removing compiler support files.
|
|
foreach ($unused in @(
|
|
(Join-Path $toolkit 'llvm-mingw\python'),
|
|
(Join-Path $toolkit 'CMake\bin\cmake-gui.exe')
|
|
)) {
|
|
if (Test-Path -LiteralPath $unused) { Remove-Item -LiteralPath $unused -Recurse -Force }
|
|
}
|
|
Get-ChildItem -LiteralPath (Join-Path $toolkit 'llvm-mingw\bin') -File |
|
|
Where-Object { $_.Name -like 'lldb*' -or $_.Name -like 'liblldb*' } |
|
|
Remove-Item -Force
|
|
[IO.Directory]::CreateDirectory((Join-Path $toolkit 'Translator')) | Out-Null
|
|
Copy-Item -LiteralPath $translator -Destination (Join-Path $toolkit 'Translator\Translator.Cli.exe')
|
|
Copy-Item -LiteralPath $nodTool -Destination (Join-Path $toolkit 'nodtool.exe')
|
|
[IO.Directory]::CreateDirectory((Join-Path $toolkit 'Redist')) | Out-Null
|
|
Copy-Item -Path (Join-Path $vcRuntime '*.dll') -Destination (Join-Path $toolkit 'Redist')
|
|
Copy-Item -Path (Join-Path $vcRuntime '*.dll') -Destination (Join-Path $toolkit 'CMake\bin')
|
|
Copy-Item -Path (Join-Path $vcRuntime '*.dll') -Destination (Join-Path $toolkit 'Ninja')
|
|
Copy-Item -Path (Join-Path $vcRuntime '*.dll') -Destination $toolkit
|
|
|
|
Copy-Directory (Join-Path $repoRoot 'runtime') (Join-Path $workspace 'runtime')
|
|
Copy-Directory (Join-Path $repoRoot 'aurora-main') (Join-Path $workspace 'aurora-main')
|
|
# Source trees can contain ignored developer build directories. They are never release inputs.
|
|
$runtimeDeveloperBuild = Join-Path $workspace 'runtime\build'
|
|
if (Test-Path -LiteralPath $runtimeDeveloperBuild) {
|
|
Remove-Item -LiteralPath $runtimeDeveloperBuild -Recurse -Force
|
|
}
|
|
$auroraExtern = Join-Path $workspace 'aurora-main\extern'
|
|
Get-ChildItem -LiteralPath $auroraExtern -Directory -Force | Remove-Item -Recurse -Force
|
|
Copy-Directory (Join-Path $repoRoot 'projects\mkwii') (Join-Path $workspace 'projects\mkwii')
|
|
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'LocalBuild.ps1') -Destination (Join-Path $workspace 'LocalBuild.ps1')
|
|
# LocalBuild.ps1 dot-sources the canonical configure flags from this sibling.
|
|
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'NativeBuildFlags.ps1') -Destination (Join-Path $workspace 'NativeBuildFlags.ps1')
|
|
[IO.Directory]::CreateDirectory((Join-Path $workspace 'Dependencies')) | Out-Null
|
|
foreach ($name in $requiredDependencies) { Copy-Directory (Join-Path $dependencySources $name) (Join-Path $workspace "Dependencies\$name") }
|
|
|
|
[IO.Directory]::CreateDirectory((Join-Path $payloadRoot 'host')) | Out-Null
|
|
Copy-Item $setupHost (Join-Path $payloadRoot 'host\WiiCompiled-Setup.exe')
|
|
|
|
Write-Host '[3/6] Writing manifests and third-party license inventory...'
|
|
[IO.Directory]::CreateDirectory((Join-Path $payloadRoot 'licenses')) | Out-Null
|
|
Copy-Item (Join-Path $portableTools 'README-LICENSES.txt') (Join-Path $payloadRoot 'licenses\Portable-build-tools.txt')
|
|
Copy-Item (Join-Path $repoRoot 'aurora-main\LICENSE') (Join-Path $payloadRoot 'licenses\Aurora-LICENSE.txt')
|
|
Copy-Item (Join-Path $dependencySources 'cppwinrt\LICENSE.txt') (Join-Path $payloadRoot 'licenses\CppWinRT-LICENSE.txt')
|
|
# The precompiled aurora/third-party archives are built from the very sources
|
|
# already shipped under build-workspace\Dependencies and aurora-main, so they add
|
|
# no third-party component and therefore no new license obligation.
|
|
@"
|
|
nodtool (disc image extraction)
|
|
|
|
Project: https://github.com/encounter/nod
|
|
Dual-licensed under MIT OR Apache-2.0.
|
|
|
|
MIT License
|
|
|
|
Copyright 2021 Luke Street.
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
"@ | Set-Content (Join-Path $payloadRoot 'licenses\nodtool-LICENSE-MIT.txt') -Encoding UTF8
|
|
@"
|
|
Microsoft Visual C++ Runtime
|
|
|
|
Redistributable x64 runtime DLLs are included app-locally for nodtool and third-party renderer DLLs.
|
|
Microsoft license terms: https://visualstudio.microsoft.com/license-terms/
|
|
"@ | Set-Content (Join-Path $payloadRoot 'licenses\Microsoft-VC-Runtime.txt') -Encoding UTF8
|
|
# Compute the payload's content identities once, here, with the same code every installed host
|
|
# uses. Shipping them in the manifest is what keeps user machines from re-hashing the toolkit
|
|
# during installation; only --repair-products re-derives content identity locally.
|
|
Write-Host 'Computing the payload content identities (hashes the staged toolkit once)...'
|
|
$identityJson = & $setupHost --emit-payload-identities --payload-root $payloadRoot
|
|
if ($LASTEXITCODE -ne 0) { throw "Payload identity computation failed with exit code $LASTEXITCODE." }
|
|
$identities = ($identityJson -join '') | ConvertFrom-Json
|
|
foreach ($required in @('ToolkitFingerprint','TranslationFingerprint','NativeToolchainFingerprint',
|
|
'ToolkitPackageFingerprint','RuntimeAssetsFingerprint')) {
|
|
if ([string]::IsNullOrWhiteSpace($identities.$required)) { throw "Payload identity output is missing $required." }
|
|
}
|
|
|
|
$manifest = [ordered]@{
|
|
SchemaVersion = 2
|
|
ProductVersion = '0.2.31'
|
|
ExpectedGameId = $pins.GameId
|
|
ExpectedDolSha256 = $pins.DolSha256
|
|
ExpectedRelSha256 = $pins.RelSha256
|
|
ToolkitReleaseTag = if ($ToolkitReleaseTag) { $ToolkitReleaseTag } else { '' }
|
|
BuildModel = 'local-translation-and-compilation'
|
|
RetroWfcPayloadUri = $pins.RetroWfcPayloadUri
|
|
ToolkitFingerprint = $identities.ToolkitFingerprint
|
|
ToolkitPackageFingerprint = $identities.ToolkitPackageFingerprint
|
|
RuntimeAssetsFingerprint = $identities.RuntimeAssetsFingerprint
|
|
TranslationFingerprint = $identities.TranslationFingerprint
|
|
NativeToolchainFingerprint = $identities.NativeToolchainFingerprint
|
|
}
|
|
$manifest | ConvertTo-Json | Set-Content (Join-Path $payloadRoot 'payload-manifest.json') -Encoding UTF8
|
|
|
|
Write-Host '[4/6] Enforcing the copyright and generated-code boundary...'
|
|
# Both audits are PowerShell scripts that throw directly on failure (Test-PayloadBoundary.ps1
|
|
# never invokes a native command at all, so it never touches $LASTEXITCODE); there is no exit
|
|
# code to check here, and doing so risks reading a stale value from an unrelated earlier command.
|
|
& (Join-Path $PSScriptRoot 'Test-PayloadBoundary.ps1') -PayloadRoot $payloadRoot
|
|
& (Join-Path $PSScriptRoot 'Test-NativeDependencies.ps1') -PayloadRoot $payloadRoot -SetupHost $setupHost `
|
|
-LlvmReadobjPath (Join-Path $portableTools 'llvm-mingw\bin\llvm-readobj.exe')
|
|
|
|
Write-Host '[5/6] Creating the canonical installer payload...'
|
|
$payloadZip = Join-Path $workRoot 'payload.zip'
|
|
Compress-Zip $payloadRoot $payloadZip @(
|
|
'Toolkit','BuildWorkspace','host','licenses','payload-manifest.json')
|
|
|
|
Write-Host '[6/6] Producing the single-file setup executable...'
|
|
$outputSetup = Join-Path $outputRoot 'WiiCompiled-Setup.exe'
|
|
Copy-Item $setupHost $outputSetup -Force
|
|
$outputStream = [IO.File]::Open($outputSetup, [IO.FileMode]::Append, [IO.FileAccess]::Write, [IO.FileShare]::None)
|
|
try {
|
|
$offset = $outputStream.Position
|
|
$input = [IO.File]::OpenRead($payloadZip)
|
|
try { $input.CopyTo($outputStream) } finally { $input.Dispose() }
|
|
$length = $outputStream.Position - $offset
|
|
$writer = [IO.BinaryWriter]::new($outputStream, [Text.Encoding]::ASCII, $true)
|
|
try {
|
|
$writer.Write([Text.Encoding]::ASCII.GetBytes('MKWCPAY1'))
|
|
$writer.Write([Int64]$offset)
|
|
$writer.Write([Int64]$length)
|
|
} finally { $writer.Dispose() }
|
|
} finally { $outputStream.Dispose() }
|
|
|
|
$setupItem = Get-Item -LiteralPath $outputSetup
|
|
Write-Host ("{0}: {1} MiB; SHA-256 {2}" -f $setupItem.Name,
|
|
[math]::Round($setupItem.Length / 1MB, 1), (Get-MkwFileSha256 $outputSetup))
|