feat: ship validated HyperTwist Windows installer

This commit is contained in:
axiomlogicnexus 2026-07-26 09:41:16 +00:00
parent 02ad8fa781
commit d0e3f96364
23 changed files with 4777 additions and 16 deletions

View file

@ -18,11 +18,21 @@ Current operator delivery:
- ZIP size: `1,093,537,260` bytes
- ZIP SHA-256:
`630963f96c681fc49130cbc56c6c850008d4cddda7e83f2ee9c077c52d7709a9`
- Validated installer:
`C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19-Setup.exe`
- Installer size: `851049086` bytes
- Installer SHA-256:
`c31ed6b0b2ae1feb449c27e484d72dc2e07ba220e3cdafa4c3605429d638e0ea`
The delivered folder was compared against the validated archive source by
hashing all `173` files. The comparison found `0` mismatches. The Downloads ZIP
size and SHA-256 also match the validation-source ZIP.
The installer is separately grounded by
`docs/generated/alpha_test_20260726_installer/README.md`. It installs the same
Release 19 runtime payload, bundles the signed Microsoft Visual C++ runtime,
and has green install, launch, in-place upgrade, and uninstall lifecycle proof.
## Product Scope
This package contains the repaired first-run and player-facing shell, reliable
@ -63,6 +73,11 @@ where the visible global assistant could reject dictation on 4D, 5D, or
- Browser production build: `1,872` modules transformed.
- Exact solver table: `676,207,080` bytes, SHA-256
`dc6de3d909a37afe2ba7f1f978543a13eae215bd20d954d66813524a8ca20034`.
- Windows installer build: `passed`, `143` payload files,
`1,585,341,236` payload bytes, and `0` NSIS compiler warnings.
- Installer lifecycle: first install, complete hash parity, readiness, Shipping
launch, in-place upgrade, second parity, uninstall, user-data preservation,
and owned-state cleanup all `passed`.
The automation authority is
`player_experience_automation_index.json`. Package, staged-browser,
@ -130,6 +145,7 @@ conditions:
real audio hardware.
- Live XR headset-session and controller-input observation still requires a
connected headset and controllers.
- Installer/uninstaller, code signing, clean-machine prerequisites,
entitlement, and update-channel rollout remain release-engineering gates
above this portable Alpha package.
- Installer/uninstaller ownership and isolated-current-host prerequisite proof
are now closed. Trusted code signing, genuinely fresh-machine proof,
entitlement-backed public delivery, and update-channel rollout remain
release-engineering gates above this Alpha package.

View file

@ -0,0 +1,119 @@
# HyperTwist Alpha R19 Windows Installer Evidence
## Authority
This directory is the durable evidence index for the first validated
HyperTwist Windows installer above the Release 19 Shipping Alpha payload.
Current operator delivery:
- installer:
`C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19-Setup.exe`
- size: `851049086` bytes
- SHA-256:
`c31ed6b0b2ae1feb449c27e484d72dc2e07ba220e3cdafa4c3605429d638e0ea`
- Authenticode: `NotSigned`
The unsigned status is an explicit Alpha limitation. Production distribution
still requires a trusted Authenticode certificate and signed-artifact proof.
## Installer Ownership
The NSIS installer owns a per-machine, 64-bit installation with:
- Windows 10 22H2 / Windows 11 and x64 preflight
- a valid Microsoft-signed Visual C++ 2015-2022 x64 redistributable
- the complete `143`-file Release 19 runtime payload
- PDB and debug-manifest exclusion
- a complete SHA-256 payload manifest
- Add/Remove Programs registration
- Start Menu app, readiness, and uninstall shortcuts
- an optional all-users desktop shortcut
- marker-and-registry-gated upgrade and uninstall ownership
- a hard `HyperTwist` install-directory leaf contract
- refusal of unowned non-empty install directories
- user-visible, manifest-aware readiness reporting
- preservation of settings, saves, replays, and diagnostics during uninstall
The prior payload is not cleaned for upgrade until the bundled prerequisite
has succeeded. Failed builds remove incomplete installer output rather than
leaving a corrupt executable at the release destination.
## Validation
The final build passed with:
- NSIS `3.12`
- `0` compiler warnings
- payload files: `143`
- payload bytes: `1585341236`
- exact solver table: `676207080` bytes
- exact solver SHA-256:
`dc6de3d909a37afe2ba7f1f978543a13eae215bd20d954d66813524a8ca20034`
- source readiness: `ready-with-warnings`
- required readiness failures: `0`
- optional warnings: unsigned desktop code and no active OpenXR runtime
Bounded contract proof also passed:
- all `3` PowerShell scripts parsed on Linux PowerShell and Windows PowerShell
- disposable NSIS contract compilation completed with `0` warnings
- an unsafe install-directory leaf was refused with exit code `13`
- an unowned non-empty `HyperTwist` directory was refused with exit code `14`
- the unrelated sentinel survived and no product registration was created
- manifest traversal input was blocked as `invalid-path:../escape.txt`
- manifest file-count and total-size tampering were blocked
- the interactive readiness wait completed under redirected-input proof
The final lifecycle used a path containing spaces and passed:
- unowned-directory refusal
- first silent install, exit `0`
- first `143`-entry payload hash parity
- Add/Remove Programs and product-registration ownership
- resolved app, readiness, uninstall, and desktop shortcut contracts
- installed readiness with `0` required failures
- stock-Shipping launch diagnostics with runtime initialization
- `0` fatal lines and `0` package-owned listeners
- in-place upgrade, exit `0`
- second `143`-entry payload hash parity
- silent uninstall, exit `0`
- user-data sentinel preservation
- install-root, registration, and shortcut removal
- final owned-process count `0`
The validation class is `isolated-current-host`. No Windows Sandbox, clean
Hyper-V VM, or base VHD was available, so this evidence does not masquerade as
a fresh-machine proof.
## Failure Lineage
`pre-release-shortcut-failure-report.json` records the intentionally rejected
candidate whose readiness shortcut embedded the installing administrator's
temporary path. That report proved first-install payload parity and successful
failure cleanup. The final installer supersedes it with a per-user report path,
manifest-aware arguments, an interactive wait, and resolved shortcut-target
validation.
## Evidence Map
- `windows-installer-build-report.json`: final build and installer identity
- `windows-installer-payload-manifest.json`: complete payload hashes and sizes
- `windows-installer-source-readiness-report.json`: source-host readiness
- `windows-installer-lifecycle-report.json`: final install-to-uninstall proof
- `installed-readiness-report.json`: installed-host and manifest proof
- `installed-runtime.hyperdiagnostics.log`: Shipping runtime initialization
- `installed-first-run-launch.log`: first-run menu launch authority
- `manifest-hardening-positive.json`: full manifest reconciliation proof
- `makensis.log`: final compiler transcript
- `pre-release-shortcut-failure-report.json`: superseded failure lineage
- `post-task-hygiene-report.json`: doctrine-ordered artifact disposition
## Remaining External Gates
- trusted Authenticode code signing
- a genuinely fresh Windows VM install and first-launch proof
- entitlement-backed public release URL wiring
- update-channel, rollback, and staged-rollout proof
- real headset/controller observation
- configured-provider, microphone, and audible-TTS observation

View file

@ -0,0 +1,15 @@
session=20260726T092358Z
world=L_HyperTwist_ClassicTraining
runtime_log=C:/HyperTwist Installer Validation/20260726T091926Z/evidence/isolated-user/Saved/Logs/HyperTwistRuntime-latest.log
latest_diagnostics_log=C:/HyperTwist Installer Validation/20260726T091926Z/evidence/isolated-user/Saved/Logs/HyperTwistFirstRunLaunch-latest.log
[2026-07-26T09:23:58Z] [BeginPlay] First-run controller booted on world 'L_HyperTwist_ClassicTraining'.
[2026-07-26T09:23:58Z] [ApplyFirstRunInputMode] Applied game-and-UI input mode with unlocked cursor capture.
[2026-07-26T09:23:58Z] [ShowFirstRunLaunchMenu] Fell back to the native first-run widget class.
[2026-07-26T09:23:58Z] [ShowFirstRunLaunchMenu] Added the first-run launch widget to the viewport at z-order 100.
[2026-07-26T09:23:58Z] [ApplyFirstRunInputMode] Applied game-and-UI input mode with unlocked cursor capture.
[2026-07-26T09:24:00Z] [StartupRecovery] The first-run launch widget never produced a visible non-zero layout after 20 attempts (last size 0x0).
[2026-07-26T09:24:00Z] [StartupRecovery] Attempting fallback route 'classic-cube-training'.
[2026-07-26T09:24:00Z] [OpenFirstRunRoute] Opening route 'classic-cube-training' (dedicated-map).
[2026-07-26T09:24:00Z] [OpenMapRoute] Opening map '/Game/HyperTwistTraining/Maps/L_HyperTwist_ClassicTraining' with options 'game=/Script/UnrealHyperTwist.HyperTwistClassicCubeGameMode'.
result=startup-recovered
last_failure=The first-run launch widget never produced a visible non-zero layout after 20 attempts (last size 0x0).

View file

@ -0,0 +1,180 @@
{
"reportVersion": "ht-windows-readiness/v1",
"generatedAtUtc": "2026-07-26T09:23:56.9501545Z",
"result": "ready-with-warnings",
"payloadRoot": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist",
"targetInstallRoot": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist",
"requiredFailureCount": 0,
"optionalWarningCount": 2,
"checks": [
{
"id": "windows-x64",
"class": "required",
"passed": true,
"summary": "HyperTwist requires a 64-bit Windows operating system.",
"evidence": {
"architecture": "64-bit",
"processArchitecture": "AMD64"
}
},
{
"id": "windows-supported-build",
"class": "required",
"passed": true,
"summary": "HyperTwist requires Windows build 19045 or newer.",
"evidence": {
"caption": "Microsoft Windows 11 Home",
"version": "10.0.22631",
"build": 22631,
"minimumBuild": 19045
}
},
{
"id": "system-memory",
"class": "required",
"passed": true,
"summary": "At least 8 GiB of physical memory is required for the desktop Alpha.",
"evidence": {
"totalBytes": 16893206528,
"minimumBytes": 8589934592
}
},
{
"id": "install-volume-free-space",
"class": "required",
"passed": true,
"summary": "The target volume must have enough free space for installation and first-run caches.",
"evidence": {
"targetInstallRoot": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist",
"availableBytes": 167681560576,
"minimumBytes": 4294967296
}
},
{
"id": "vc-runtime-x64",
"class": "required",
"passed": true,
"summary": "Microsoft Visual C++ 2015-2022 x64 runtime 14.44.35211.0 or newer is required and is bundled by the installer.",
"evidence": {
"registryPath": "HKLM:\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64",
"installed": true,
"version": "14.44.35211.0"
}
},
{
"id": "windows-media-and-graphics-libraries",
"class": "required",
"passed": true,
"summary": "Windows DirectX, XInput, and Media Foundation runtime libraries must be available.",
"evidence": {
"systemDirectory": "C:\\Windows\\system32",
"requiredLibraries": [
"d3d11.dll",
"d3d12.dll",
"dxgi.dll",
"mf.dll",
"mfplat.dll",
"mfreadwrite.dll",
"xinput1_4.dll"
],
"missingLibraries": [
]
}
},
{
"id": "graphics-adapter",
"class": "required",
"passed": true,
"summary": "At least one Windows graphics adapter must be visible.",
"evidence": [
{
"name": "Intel(R) Iris(R) Xe Graphics",
"driverVersion": "31.0.101.3358",
"driverDate": "2022-08-04T02:00:00.0000000+02:00",
"adapterRamBytes": 1073741824,
"status": "OK"
}
]
},
{
"id": "audio-device",
"class": "optional",
"passed": true,
"summary": "An audio device is optional for puzzle play and recommended for narration and speech features.",
"evidence": [
{
"name": "Intel® Smart Sound Technology for Digital Microphones",
"status": "OK"
},
{
"name": "Intel® Smart Sound Technology for USB Audio",
"status": "OK"
},
{
"name": "Intel® Smart Sound Technology for Bluetooth® Audio",
"status": "OK"
},
{
"name": "Audio Device",
"status": "OK"
},
{
"name": "Virtual Audio Cable",
"status": "OK"
}
]
},
{
"id": "openxr-runtime",
"class": "optional",
"passed": false,
"summary": "An active OpenXR runtime is optional for keyboard/mouse play and required only for live headset sessions.",
"evidence": null
},
{
"id": "required-payload-files",
"class": "required",
"passed": true,
"summary": "The installed payload must contain every required executable, runtime, cooked-data, browser, solver, and OpenXR file.",
"evidence": {
"payloadRoot": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist",
"requiredFileCount": 15,
"cefRuntimeFiles": [
"Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/libcef.dll"
],
"failures": [
]
}
},
{
"id": "desktop-code-signature",
"class": "optional",
"passed": false,
"summary": "The Alpha payload is currently unsigned; production distribution requires Authenticode signing.",
"evidence": {
"status": "NotSigned",
"signer": null
}
},
{
"id": "payload-manifest-parity",
"class": "required",
"passed": true,
"summary": "The installed payload must match the release manifest without missing, truncated, or altered files.",
"evidence": {
"manifestPath": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist\\Installer\\payload-manifest.json",
"manifestVersion": "ht-windows-installer-payload/v1",
"manifestEntryCount": 143,
"declaredFileCount": 143,
"declaredTotalSizeBytes": 1585341236,
"computedTotalSizeBytes": 1585341236,
"hashesVerified": true,
"failures": [
]
}
}
]
}

View file

@ -0,0 +1,9 @@
format=hypertwist-runtime-diagnostics/v1
session=20260726T092358Z
process_id=14248
command_line=-notraceserver -traceautostart=0 -NullRHI -nosound -windowed -ResX=1280 -ResY=720 -log -FORCELOGFLUSH -abslog=C:\HyperTwist Installer Validation\20260726T091926Z\evidence\installed-runtime.log -HyperTwistDiagnosticsLog="C:\HyperTwist Installer Validation\20260726T091926Z\evidence\installed-runtime.hyperdiagnostics.log" -UserDir="C:\HyperTwist Installer Validation\20260726T091926Z\evidence\isolated-user"
diagnostics_path=C:/HyperTwist Installer Validation/20260726T091926Z/evidence/installed-runtime.hyperdiagnostics.log
[2026-07-26T09:23:58Z] [Process] HyperTwist runtime module initialized.
[2026-07-26T09:24:00Z] [MapReady] Runtime map ready: /Game/HyperTwistTraining/Maps/L_HyperTwist_ClassicTraining.L_HyperTwist_ClassicTraining.
[2026-07-26T09:24:00Z] [ClassicPresentation] Classic cube presentation initialized with 26 renderable pieces and orbit pawn ready.
[2026-07-26T09:24:07Z] [ClassicScrambleSettlement] Classic cube scramble settled with 11 completed quarter turns, 0 queued rotations, and 26 renderable pieces; renderable state valid.

View file

@ -0,0 +1,557 @@
MakeNSIS v3.12 - Copyright 1999-2026 Contributors
See the file COPYING for license details.
Credits can be found in the Users Manual.
Command line defined: "PRODUCT_VERSION=0.19.0.0"
Command line defined: "DISPLAY_VERSION=Alpha R19"
Command line defined: "RELEASE_ID=20260723-R19"
Command line defined: "OUTPUT_FILE=C:\HyperTwist_worktrees\installer-release19-20260726\HyperTwist-Alpha-Test-20260723-R19-Setup.exe"
Command line defined: "PAYLOAD_ROOT=C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows"
Command line defined: "PAYLOAD_MANIFEST=C:\HyperTwist_worktrees\installer-release19-20260726\validation\windows-installer-payload-manifest.json"
Command line defined: "READINESS_SCRIPT=C:\HyperTwist_worktrees\phase10validate\scripts\Test-HyperTwistWindowsReadiness.ps1"
Command line defined: "VC_REDIST_PATH=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Redist\MSVC\v143\vc_redist.x64.exe"
Command line defined: "INSTALLER_ICON=C:\HyperTwist_worktrees\installer-release19-20260726\build-context\HyperTwist.ico"
Command line defined: "PAYLOAD_SIZE_KB=1548185"
Processing config: C:\Program Files (x86)\NSIS\nsisconf.nsh
Processing default plugins: "C:\Program Files (x86)\NSIS\Plugins\x86-unicode\*.dll"
+ AdvSplash::show
+ Banner::destroy
+ Banner::getWindow
+ Banner::show
+ BgImage::AddImage
+ BgImage::AddText
+ BgImage::Clear
+ BgImage::Destroy
+ BgImage::Redraw
+ BgImage::SetBg
+ BgImage::SetReturn
+ BgImage::Sound
+ Dialer::AttemptConnect
+ Dialer::AutodialHangup
+ Dialer::AutodialOnline
+ Dialer::AutodialUnattended
+ Dialer::GetConnectedState
+ InstallOptions::dialog
+ InstallOptions::initDialog
+ InstallOptions::make_unicode
+ InstallOptions::show
+ LangDLL::LangDialog
+ Math::Script
+ NSISdl::download
+ NSISdl::download_quiet
+ Splash::show
+ StartMenu::Init
+ StartMenu::Select
+ StartMenu::Show
+ System::Alloc
+ System::Call
+ System::Copy
+ System::Free
+ System::Get
+ System::Int64Op
+ System::Store
+ System::StrAlloc
+ TypeLib::GetLibVersion
+ TypeLib::Register
+ TypeLib::UnRegister
+ UserInfo::GetAccountType
+ UserInfo::GetName
+ UserInfo::GetOriginalAccountType
+ VPatch::GetFileCRC32
+ VPatch::GetFileMD5
+ VPatch::vpatchfile
+ nsDialogs::Create
+ nsDialogs::CreateControl
+ nsDialogs::CreateItem
+ nsDialogs::CreateTimer
+ nsDialogs::GetUserData
+ nsDialogs::KillTimer
+ nsDialogs::OnBack
+ nsDialogs::OnChange
+ nsDialogs::OnClick
+ nsDialogs::OnNotify
+ nsDialogs::SelectFileDialog
+ nsDialogs::SelectFolderDialog
+ nsDialogs::SetRTL
+ nsDialogs::SetUserData
+ nsDialogs::Show
+ nsExec::Exec
+ nsExec::ExecToLog
+ nsExec::ExecToStack
!define: "MUI_INSERT_NSISCONF"=""
Changing directory to: "C:\HyperTwist_worktrees\phase10validate\packaging\windows"
Processing script file: "C:\HyperTwist_worktrees\phase10validate\packaging\windows\HyperTwistInstaller.nsi" (ACP)
Unicode: true
Processing default plugins: "C:\Program Files (x86)\NSIS\Plugins\x86-unicode\*.dll"
!include: "C:\Program Files (x86)\NSIS\Include\MUI2.nsh" (UTF8)
!include: "C:\Program Files (x86)\NSIS\Contrib\Modern UI 2\MUI2.nsh" (UTF8)
NSIS Modern User Interface version 2.1 - Copyright 2002-2026 Joost Verburg (C:\Program Files (x86)\NSIS\Contrib\Modern UI 2\MUI2.nsh:14)
!include: closed: "C:\Program Files (x86)\NSIS\Contrib\Modern UI 2\MUI2.nsh"
!include: closed: "C:\Program Files (x86)\NSIS\Include\MUI2.nsh"
!include: "C:\Program Files (x86)\NSIS\Include\LogicLib.nsh" (UTF8)
!include: closed: "C:\Program Files (x86)\NSIS\Include\LogicLib.nsh"
!include: "C:\Program Files (x86)\NSIS\Include\WinVer.nsh" (UTF8)
!include: closed: "C:\Program Files (x86)\NSIS\Include\WinVer.nsh"
!include: "C:\Program Files (x86)\NSIS\Include\x64.nsh" (UTF8)
!define: "___X64__NSH___"=""
!include: "C:\Program Files (x86)\NSIS\Include\LogicLib.nsh" (UTF8)
!include: closed: "C:\Program Files (x86)\NSIS\Include\LogicLib.nsh"
!define: "IsWow64"=""" IsWow64 """
!define: "RunningX64"=""" RunningX64 """
!define: "GetNativeMachineArchitecture"="!insertmacro GetNativeMachineArchitecture "
!define: "IsNativeMachineArchitecture"=""" IsNativeMachineArchitecture "
!define: "IsNativeIA32"=""" IsNativeMachineArchitecture 332"
!define: "IsNativeAMD64"=""" IsNativeMachineArchitecture 34404"
!define: "IsNativeARM64"=""" IsNativeMachineArchitecture 43620"
!define: "DisableX64FSRedirection"="!insertmacro DisableX64FSRedirection"
!define: "EnableX64FSRedirection"="!insertmacro EnableX64FSRedirection"
!include: closed: "C:\Program Files (x86)\NSIS\Include\x64.nsh"
!include: "C:\Program Files (x86)\NSIS\Include\FileFunc.nsh" (UTF8)
!include: closed: "C:\Program Files (x86)\NSIS\Include\FileFunc.nsh"
!define: "PRODUCT_NAME"="HyperTwist"
!define: "PRODUCT_CHANNEL"="Alpha"
!define: "PRODUCT_PUBLISHER"="HyperTwist"
!define: "PRODUCT_WEBSITE"="https://hypertwist.app"
!define: "PRODUCT_SUPPORT"="https://hypertwist.app/support"
!define: "PRODUCT_REG_KEY"="Software\HyperTwist\HyperTwist"
!define: "PRODUCT_UNINSTALL_KEY"="Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist"
!define: "INSTALL_MARKER"="HyperTwist.install-marker"
Name: "HyperTwist Alpha"
Caption: "HyperTwist Alpha R19 Setup"
BrandingText: "HyperTwist | Explore dimensions beyond the ordinary"
OutFile: "C:\HyperTwist_worktrees\installer-release19-20260726\HyperTwist-Alpha-Test-20260723-R19-Setup.exe"
InstallDir: "$PROGRAMFILES64\HyperTwist"
CRCCheck: force
SetDatablockOptimize: on
SetCompressor: /SOLID lzma
SetCompressorDictSize: 64 mb
ShowInstDetails: show
ShowUninstDetails: show
Icon: "C:\HyperTwist_worktrees\installer-release19-20260726\build-context\HyperTwist.ico"
UninstallIcon: "C:\HyperTwist_worktrees\installer-release19-20260726\build-context\HyperTwist.ico"
VIAddVersionKey: "ProductName" "HyperTwist"
VIAddVersionKey: "ProductVersion" "Alpha R19"
VIAddVersionKey: "CompanyName" "HyperTwist"
VIAddVersionKey: "FileDescription" "HyperTwist Alpha Installer"
VIAddVersionKey: "FileVersion" "0.19.0.0"
VIAddVersionKey: "LegalCopyright" "Copyright (c) HyperTwist"
VIAddVersionKey: "OriginalFilename" "HyperTwist-Setup.exe"
!define: "MUI_ABORTWARNING"=""
!define: "MUI_ICON"="C:\HyperTwist_worktrees\installer-release19-20260726\build-context\HyperTwist.ico"
!define: "MUI_UNICON"="C:\HyperTwist_worktrees\installer-release19-20260726\build-context\HyperTwist.ico"
!define: "MUI_FINISHPAGE_LINK"="Visit hypertwist.app"
!define: "MUI_FINISHPAGE_LINK_LOCATION"="https://hypertwist.app"
!insertmacro: MUI_PAGE_WELCOME
!insertmacro: end of MUI_PAGE_WELCOME
!insertmacro: MUI_PAGE_COMPONENTS
!insertmacro: end of MUI_PAGE_COMPONENTS
!insertmacro: MUI_PAGE_DIRECTORY
!insertmacro: end of MUI_PAGE_DIRECTORY
!insertmacro: MUI_PAGE_INSTFILES
!insertmacro: end of MUI_PAGE_INSTFILES
!insertmacro: MUI_PAGE_FINISH
!insertmacro: end of MUI_PAGE_FINISH
!insertmacro: MUI_UNPAGE_CONFIRM
!insertmacro: end of MUI_UNPAGE_CONFIRM
!insertmacro: MUI_UNPAGE_INSTFILES
!insertmacro: end of MUI_UNPAGE_INSTFILES
!insertmacro: MUI_UNPAGE_FINISH
!insertmacro: end of MUI_UNPAGE_FINISH
!insertmacro: MUI_LANGUAGE
!insertmacro: end of MUI_LANGUAGE
Function: ".onInit"
SetRegView: 64
!insertmacro: _If
!insertmacro: end of _If
MessageBox: 16: "HyperTwist requires 64-bit Windows."
SetErrorLevel: 10
Quit
!insertmacro: _EndIf
!insertmacro: end of _EndIf
!insertmacro: _If
!insertmacro: end of _If
MessageBox: 16: "HyperTwist requires Windows 10 version 22H2 or Windows 11."
SetErrorLevel: 11
Quit
!insertmacro: _EndIf
!insertmacro: end of _EndIf
ReadRegStr $1 HKLM\Software\HyperTwist\HyperTwist\InstallLocation
StrCmp "$1" "" equal=install_location_ready, nonequal=
StrCpy $INSTDIR "$1" () ()
ReadRegStr $0 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentBuildNumber
IntCmp $0:19045 equal=windows_build_ok, < windows_build_too_old, > windows_build_ok
MessageBox: 16: "This Windows build is too old for the supported HyperTwist Alpha baseline. Update to Windows 10 22H2 or Windows 11 and try again."
SetErrorLevel: 12
Quit
FunctionEnd
Function: "CleanOwnedPayloadForUpgrade"
ReadRegStr $0 HKLM\Software\HyperTwist\HyperTwist\InstallLocation
StrCmp "$0" "$INSTDIR" equal=registry_owner_confirmed, nonequal=cleanup_done
IfFileExists: "$INSTDIR\Installer\HyperTwist.install-marker" ? 0 : cleanup_done
DetailPrint: "Removing the previous HyperTwist payload before upgrade."
RMDir: /r "$INSTDIR\Content"
RMDir: /r "$INSTDIR\Engine"
RMDir: /r "$INSTDIR\UnrealHyperTwist"
RMDir: /r "$INSTDIR\Installer"
Delete: "$INSTDIR\Manifest_DebugFiles_Win64.txt"
Delete: "$INSTDIR\Manifest_NonUFSFiles_Win64.txt"
Delete: "$INSTDIR\Manifest_UFSFiles_Win64.txt"
Delete: "$INSTDIR\UnrealHyperTwist.exe"
Delete: "$INSTDIR\Uninstall HyperTwist.exe"
FunctionEnd
Function: "AssertSafeInstallLocation"
!insertmacro: GetFileNameCall
!insertmacro: end of GetFileNameCall
StrCmp "$0" "HyperTwist" equal=install_leaf_safe, nonequal=install_location_refused
ReadRegStr $0 HKLM\Software\HyperTwist\HyperTwist\InstallLocation
StrCmp "$0" "$INSTDIR" equal=install_registry_owner_candidate, nonequal=install_unowned_scan
IfFileExists: "$INSTDIR\Installer\HyperTwist.install-marker" ? install_location_safe : install_unowned_scan
FindFirst: spec="$INSTDIR\*" handle=$0 output=$1
StrCmp "$1" "" equal=install_unowned_empty, nonequal=
StrCmp "$1" "." equal=install_unowned_continue, nonequal=
StrCmp "$1" ".." equal=install_unowned_continue, nonequal=
FindClose: $0
MessageBox: 16: "The selected HyperTwist folder is not empty and is not owned by an existing registered HyperTwist installation. Choose an empty folder named HyperTwist."
SetErrorLevel: 14
Abort: ""
FindNext: handle=$0 output=$1
Goto: install_unowned_scan_next
FindClose: $0
Goto: install_location_safe
MessageBox: 16: "The installation folder itself must be named HyperTwist, for example C:\Program Files\HyperTwist or D:\Games\HyperTwist."
SetErrorLevel: 13
Abort: ""
FunctionEnd
Section: "-ValidateInstallLocation"
Call "AssertSafeInstallLocation"
SectionEnd
Section: "-MicrosoftRuntime"
SetOutPath: "$PLUGINSDIR"
File: "vc_redist.x64.exe"->"vc_redist.x64.exe" 25635768 bytes
DetailPrint: "Installing or repairing the Microsoft Visual C++ 2015-2022 x64 runtime."
ExecWait: ""$PLUGINSDIR\vc_redist.x64.exe" /install /quiet /norestart" (->$0)
!insertmacro: _If
!insertmacro: end of _If
DetailPrint: "The Microsoft runtime requested a restart."
SetRebootFlag: true
!insertmacro: _ElseIf
!insertmacro: end of _ElseIf
DetailPrint: "A compatible or newer Microsoft runtime is already installed."
!insertmacro: _ElseIf
!insertmacro: end of _ElseIf
MessageBox: 16: "Microsoft Visual C++ runtime setup failed with exit code $0. HyperTwist was not installed."
SetErrorLevel: 20
Abort: ""
!insertmacro: _EndIf
!insertmacro: end of _EndIf
SectionEnd
Section: "!HyperTwist application" ->(SEC_CORE)
SectionIn: [RO]
AddSize: 1548185 kb
Call "CleanOwnedPayloadForUpgrade"
SetOutPath: "$INSTDIR"
File: "Manifest_NonUFSFiles_Win64.txt" 10686 bytes
File: "Manifest_UFSFiles_Win64.txt" 255758 bytes
File: "UnrealHyperTwist.exe" 165376 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Content\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Content\Browser\"
File: "index.html" 7473 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Content\Browser\dist\"
File: "browser-spatial-runtime.js" 30651 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Content\Browser\dist\assets\"
File: "MathUtils.js" 185 bytes
File: "MeshoptDecoder.js" 595800 bytes
File: "Painter.js" 255062 bytes
File: "client.js" 875934 bytes
File: "decode.js" 26052 bytes
File: "easing-0f4db1c0.esm.js" 29269 bytes
File: "events-b389eeca.esm.js" 608404 bytes
File: "gltf-viewer.module.js" 371169 bytes
File: "index.js" 101 bytes
File: "index10.js" 463085 bytes
File: "index11.js" 1010478 bytes
File: "index12.js" 8792 bytes
File: "index13.js" 867555 bytes
File: "index2.js" 16990 bytes
File: "index3.js" 541053 bytes
File: "index4.js" 44323 bytes
File: "index5.js" 65696 bytes
File: "index6.js" 1608912 bytes
File: "index7.js" 200923 bytes
File: "index8.js" 2925000 bytes
File: "index9.js" 536 bytes
File: "install.js" 396204 bytes
File: "inter.js" 440193 bytes
File: "jsx-runtime.js" 22632 bytes
File: "leva.esm.js" 282310 bytes
File: "maath.esm.js" 13221 bytes
File: "model-viewer-effects.js" 28163 bytes
File: "model-viewer.js" 569271 bytes
File: "morphPath.js" 59852 bytes
File: "property.js" 20774 bytes
File: "react-spring_core.modern.js" 69814 bytes
File: "react-spring_three.modern.js" 1577 bytes
File: "react-spring_web.modern.js" 6888 bytes
File: "react-three-fiber.esm.js" 7829 bytes
File: "three.module.js" 1292285 bytes
File: "tslib.es6.js" 3434 bytes
File: "use-gesture-react.esm.js" 42626 bytes
File: "vanilla.js" 541 bytes
File: "vision_bundle.js" 169647 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Content\Browser\src\"
File: "browser-runtime-bootstrap.js" 3422 bytes
File: "browser-spatial-runtime-fallback.js" 30201 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\CEF3\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\CEF3\Win64\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\CEF3\Win64\128.4.13+ge76af7e+chromium-128.0.6613.138\"
File: "chrome_100_percent.pak" 695026 bytes
File: "chrome_200_percent.pak" 1094579 bytes
File: "chrome_elf.dll" 1370552 bytes
File: "d3dcompiler_47.dll" 4927552 bytes
File: "dxcompiler.dll" 20817848 bytes
File: "dxil.dll" 1519176 bytes
File: "icudtl.dat" 10468208 bytes
File: "libEGL.dll" 536504 bytes
File: "libGLESv2.dll" 8959416 bytes
File: "libcef.dll" 239992248 bytes
File: "resources.pak" 8683856 bytes
File: "snapshot_blob.bin" 313526 bytes
File: "v8_context_snapshot.bin" 665981 bytes
File: "vk_swiftshader.dll" 5572536 bytes
File: "vulkan-1.dll" 930744 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\CEF3\Win64\128.4.13+ge76af7e+chromium-128.0.6613.138\Resources\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\CEF3\Win64\128.4.13+ge76af7e+chromium-128.0.6613.138\Resources\locales\"
File: "af.pak" 538998 bytes
File: "am.pak" 866444 bytes
File: "ar.pak" 953918 bytes
File: "bg.pak" 987762 bytes
File: "bn.pak" 1269605 bytes
File: "ca.pak" 612377 bytes
File: "cs.pak" 622919 bytes
File: "da.pak" 567958 bytes
File: "de.pak" 609935 bytes
File: "el.pak" 1080416 bytes
File: "en-GB.pak" 492124 bytes
File: "en-US.pak" 497015 bytes
File: "es-419.pak" 598355 bytes
File: "es.pak" 596806 bytes
File: "et.pak" 544221 bytes
File: "fa.pak" 879421 bytes
File: "fi.pak" 552657 bytes
File: "fil.pak" 624599 bytes
File: "fr.pak" 646732 bytes
File: "gu.pak" 1255599 bytes
File: "he.pak" 777593 bytes
File: "hi.pak" 1330999 bytes
File: "hr.pak" 600581 bytes
File: "hu.pak" 646489 bytes
File: "id.pak" 535422 bytes
File: "it.pak" 594371 bytes
File: "ja.pak" 719063 bytes
File: "kn.pak" 1424332 bytes
File: "ko.pak" 606589 bytes
File: "lt.pak" 652194 bytes
File: "lv.pak" 651016 bytes
File: "ml.pak" 1479560 bytes
File: "mr.pak" 1223735 bytes
File: "ms.pak" 560048 bytes
File: "nb.pak" 543051 bytes
File: "nl.pak" 562204 bytes
File: "pl.pak" 627081 bytes
File: "pt-BR.pak" 587941 bytes
File: "pt-PT.pak" 592511 bytes
File: "ro.pak" 612678 bytes
File: "ru.pak" 1003077 bytes
File: "sk.pak" 632482 bytes
File: "sl.pak" 607050 bytes
File: "sr.pak" 929699 bytes
File: "sv.pak" 547471 bytes
File: "sw.pak" 576117 bytes
File: "ta.pak" 1475609 bytes
File: "te.pak" 1361935 bytes
File: "th.pak" 1144623 bytes
File: "tr.pak" 587129 bytes
File: "uk.pak" 1001189 bytes
File: "ur.pak" 872334 bytes
File: "vi.pak" 695768 bytes
File: "zh-CN.pak" 503346 bytes
File: "zh-TW.pak" 500142 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\DbgHelp\"
File: "dbghelp.dll" 2232688 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\MsQuic\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\MsQuic\v220\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\MsQuic\v220\win64\"
File: "msquic.dll" 2547080 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\NVIDIA\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\NVIDIA\NVaftermath\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\NVIDIA\NVaftermath\Win64\"
File: "GFSDK_Aftermath_Lib.x64.dll" 5442592 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\Ogg\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\Ogg\Win64\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\Ogg\Win64\VS2015\"
File: "libogg_64.dll" 59832 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\OpenXR\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\OpenXR\win64\"
File: "openxr_loader.dll" 1971640 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\Vorbis\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\Vorbis\Win64\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\Vorbis\Win64\VS2015\"
File: "libvorbis_64.dll" 1724856 bytes
File: "libvorbisfile_64.dll" 48568 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\Windows\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\Windows\XAudio2_9\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\ThirdParty\Windows\XAudio2_9\x64\"
File: "xaudio2_9redist.dll" 858144 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Binaries\Win64\"
File: "EpicWebHelper.exe" 4751288 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Config\"
File: "StagedBuild_UnrealHyperTwist.ini" 3 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Content\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Content\Renderer\"
File: "TessellationTable.bin" 259568 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Content\Slate\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Content\Slate\Cursor\"
File: "invisible.cur" 326 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Content\SlateDebug\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Content\SlateDebug\Fonts\"
File: "LastResort.tps" 930 bytes
File: "LastResort.ttf" 5395052 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Plugins\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Plugins\NNE\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Plugins\NNE\NNERuntimeORT\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Plugins\NNE\NNERuntimeORT\Binaries\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Plugins\NNE\NNERuntimeORT\Binaries\ThirdParty\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Plugins\NNE\NNERuntimeORT\Binaries\ThirdParty\Onnxruntime\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\Engine\Plugins\NNE\NNERuntimeORT\Binaries\ThirdParty\Onnxruntime\Win64\"
File: "onnxruntime.dll" 14692872 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\UnrealHyperTwist\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\UnrealHyperTwist\Binaries\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\UnrealHyperTwist\Binaries\Win64\"
File: "UnrealHyperTwist-Win64-Shipping.exe" 181121024 bytes
File: "tbb12.dll" 331704 bytes
File: "tbbmalloc.dll" 117688 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\UnrealHyperTwist\Binaries\Win64\D3D12\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\UnrealHyperTwist\Binaries\Win64\D3D12\x64\"
File: "D3D12Core.dll" 4544000 bytes
File: "d3d12SDKLayers.dll" 5104632 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\UnrealHyperTwist\Binaries\Win64\DML\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\UnrealHyperTwist\Binaries\Win64\DML\x64\"
File: "DirectML.dll" 18537968 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\UnrealHyperTwist\Content\"
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\UnrealHyperTwist\Content\Paks\"
File: "UnrealHyperTwist-Windows.pak" 11069853 bytes
File: "UnrealHyperTwist-Windows.ucas" 281222144 bytes
File: "UnrealHyperTwist-Windows.utoc" 216013 bytes
File: "global.ucas" 3325696 bytes
File: "global.utoc" 806 bytes
File: Descending to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows\UnrealHyperTwist\Saved\"
File: "twophase-ht.tbl" 676207080 bytes
File: Returning to: "C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723\Windows"
SetOutPath: "$INSTDIR\Installer"
File: "windows-installer-payload-manifest.json"->"payload-manifest.json" 44356 bytes
File: "Test-HyperTwistWindowsReadiness.ps1"->"Test-HyperTwistWindowsReadiness.ps1" 21840 bytes
FileOpen: $INSTDIR\Installer\HyperTwist.install-marker as w -> $0
FileWrite: product=HyperTwist
->$0
FileWrite: release=20260723-R19
->$0
FileWrite: version=Alpha R19
->$0
FileClose: $0
WriteUninstaller: "$INSTDIR\Uninstall HyperTwist.exe"
WriteRegStr: HKLM\Software\HyperTwist\HyperTwist\InstallLocation=$INSTDIR
WriteRegStr: HKLM\Software\HyperTwist\HyperTwist\ReleaseId=20260723-R19
WriteRegStr: HKLM\Software\HyperTwist\HyperTwist\DisplayVersion=Alpha R19
WriteRegStr: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\DisplayName=HyperTwist Alpha
WriteRegStr: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\DisplayVersion=Alpha R19
WriteRegStr: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\Publisher=HyperTwist
WriteRegStr: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\InstallLocation=$INSTDIR
WriteRegStr: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\DisplayIcon=$INSTDIR\UnrealHyperTwist.exe,0
WriteRegStr: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\URLInfoAbout=https://hypertwist.app
WriteRegStr: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\HelpLink=https://hypertwist.app/support
WriteRegStr: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\UninstallString="$INSTDIR\Uninstall HyperTwist.exe"
WriteRegStr: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\QuietUninstallString="$INSTDIR\Uninstall HyperTwist.exe" /S
WriteRegDWORD: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\EstimatedSize=1548185
WriteRegDWORD: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\NoModify=1
WriteRegDWORD: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist\NoRepair=1
SetShellVarContext: all
CreateDirectory: "$SMPROGRAMS\HyperTwist"
CreateShortcut: "$SMPROGRAMS\HyperTwist\HyperTwist.lnk"->"$INSTDIR\UnrealHyperTwist.exe" icon:$INSTDIR\UnrealHyperTwist.exe,0, nwd=0, showmode=0x0, hotkey=0x0, comment=
CreateShortcut: "$SMPROGRAMS\HyperTwist\System Readiness Report.lnk"->"$SYSDIR\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\Installer\Test-HyperTwistWindowsReadiness.ps1" -PayloadRoot "$INSTDIR" -TargetInstallRoot "$INSTDIR" -ExpectedPayloadManifestPath "$INSTDIR\Installer\payload-manifest.json" -WaitForUser icon:$INSTDIR\UnrealHyperTwist.exe,0, nwd=0, showmode=0x0, hotkey=0x0, comment=
CreateShortcut: "$SMPROGRAMS\HyperTwist\Uninstall HyperTwist.lnk"->"$INSTDIR\Uninstall HyperTwist.exe" icon:,0, nwd=0, showmode=0x0, hotkey=0x0, comment=
SectionEnd
Section: "Desktop shortcut" ->(SEC_DESKTOP)
SetShellVarContext: all
CreateShortcut: "$DESKTOP\HyperTwist.lnk"->"$INSTDIR\UnrealHyperTwist.exe" icon:$INSTDIR\UnrealHyperTwist.exe,0, nwd=0, showmode=0x0, hotkey=0x0, comment=
SectionEnd
LangString: "DESC_SEC_CORE" 1033 "The complete HyperTwist desktop Alpha, puzzle runtimes, learning studio, browser shell, and local solver data."
LangString: "DESC_SEC_DESKTOP" 1033 "Create an all-users HyperTwist shortcut on the desktop."
!insertmacro: MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro: end of MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro: MUI_DESCRIPTION_TEXT
!insertmacro: end of MUI_DESCRIPTION_TEXT
!insertmacro: MUI_DESCRIPTION_TEXT
!insertmacro: end of MUI_DESCRIPTION_TEXT
!insertmacro: MUI_FUNCTION_DESCRIPTION_END
!insertmacro: end of MUI_FUNCTION_DESCRIPTION_END
Section: "Uninstall"
SetRegView: 64
!insertmacro: GetFileNameCall
!insertmacro: end of GetFileNameCall
StrCmp "$1" "HyperTwist" equal=uninstall_leaf_safe, nonequal=uninstall_refused
ReadRegStr $0 HKLM\Software\HyperTwist\HyperTwist\InstallLocation
StrCmp "$0" "$INSTDIR" equal=uninstall_registry_owned, nonequal=uninstall_refused
IfFileExists: "$INSTDIR\Installer\HyperTwist.install-marker" ? uninstall_owned : uninstall_refused
MessageBox: 16: "HyperTwist registry ownership or the install marker is missing. The uninstaller will not delete an unverified directory."
SetErrorLevel: 30
Abort: ""
SetShellVarContext: all
Delete: "$DESKTOP\HyperTwist.lnk"
RMDir: /r "$SMPROGRAMS\HyperTwist"
DeleteRegKey: HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist
DeleteRegKey: HKLM\Software\HyperTwist\HyperTwist
DeleteRegKey: HKLM\Software\HyperTwist
RMDir: /r "$INSTDIR\Content"
RMDir: /r "$INSTDIR\Engine"
RMDir: /r "$INSTDIR\UnrealHyperTwist"
Delete: "$INSTDIR\Manifest_DebugFiles_Win64.txt"
Delete: "$INSTDIR\Manifest_NonUFSFiles_Win64.txt"
Delete: "$INSTDIR\Manifest_UFSFiles_Win64.txt"
Delete: "$INSTDIR\UnrealHyperTwist.exe"
Delete: "$INSTDIR\Installer\payload-manifest.json"
Delete: "$INSTDIR\Installer\Test-HyperTwistWindowsReadiness.ps1"
Delete: "$INSTDIR\Installer\HyperTwist.install-marker"
RMDir: "$INSTDIR\Installer"
Delete: "$INSTDIR\Uninstall HyperTwist.exe"
RMDir: "$INSTDIR"
SectionEnd
Processed 1 file, writing output (x86-unicode):
Adding plug-ins initializing function... Done!
Processing pages... Done!
Removing unused resources... Done!
Generating language tables... Done!
Generating uninstaller... Done!
Output: "C:\HyperTwist_worktrees\installer-release19-20260726\HyperTwist-Alpha-Test-20260723-R19-Setup.exe"
Install: 5 pages (320 bytes), 4 sections (3 required) (8288 bytes), 796 instructions (22288 bytes), 489 strings (19966 bytes), 1 language table (342 bytes).
Uninstall: 3 pages (256 bytes), 1 section (2072 bytes), 326 instructions (9128 bytes), 183 strings (5968 bytes), 1 language table (282 bytes).
Datablock optimizer saved 26498 bytes (~0.0%).
Using lzma (compress whole) compression.
EXE header size: 41472 / 38912 bytes
Install code: (51692 bytes)
Install data: (1611092306 bytes)
Uninstall code+data: (67420 bytes)
Compressed data: 851007610 / 1611211418 bytes
CRC (0x2B3F13D2): 4 / 4 bytes
Total size: 851049086 / 1611250334 bytes (52.8%)

View file

@ -0,0 +1,180 @@
{
"reportVersion": "ht-windows-readiness/v1",
"generatedAtUtc": "2026-07-26T08:30:49.1663635Z",
"result": "ready-with-warnings",
"payloadRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_alpha_release19_shipping_20260723\\Windows",
"targetInstallRoot": "C:\\Program Files\\HyperTwist",
"requiredFailureCount": 0,
"optionalWarningCount": 2,
"checks": [
{
"id": "windows-x64",
"class": "required",
"passed": true,
"summary": "HyperTwist requires a 64-bit Windows operating system.",
"evidence": {
"architecture": "64-bit",
"processArchitecture": "AMD64"
}
},
{
"id": "windows-supported-build",
"class": "required",
"passed": true,
"summary": "HyperTwist requires Windows build 19045 or newer.",
"evidence": {
"caption": "Microsoft Windows 11 Home",
"version": "10.0.22631",
"build": 22631,
"minimumBuild": 19045
}
},
{
"id": "system-memory",
"class": "required",
"passed": true,
"summary": "At least 8 GiB of physical memory is required for the desktop Alpha.",
"evidence": {
"totalBytes": 16893206528,
"minimumBytes": 8589934592
}
},
{
"id": "install-volume-free-space",
"class": "required",
"passed": true,
"summary": "The target volume must have enough free space for installation and first-run caches.",
"evidence": {
"targetInstallRoot": "C:\\Program Files\\HyperTwist",
"availableBytes": 171947429888,
"minimumBytes": 4294967296
}
},
{
"id": "vc-runtime-x64",
"class": "required",
"passed": true,
"summary": "Microsoft Visual C++ 2015-2022 x64 runtime 14.44.35211.0 or newer is required and is bundled by the installer.",
"evidence": {
"registryPath": "HKLM:\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64",
"installed": true,
"version": "14.44.35211.0"
}
},
{
"id": "windows-media-and-graphics-libraries",
"class": "required",
"passed": true,
"summary": "Windows DirectX, XInput, and Media Foundation runtime libraries must be available.",
"evidence": {
"systemDirectory": "C:\\Windows\\system32",
"requiredLibraries": [
"d3d11.dll",
"d3d12.dll",
"dxgi.dll",
"mf.dll",
"mfplat.dll",
"mfreadwrite.dll",
"xinput1_4.dll"
],
"missingLibraries": [
]
}
},
{
"id": "graphics-adapter",
"class": "required",
"passed": true,
"summary": "At least one Windows graphics adapter must be visible.",
"evidence": [
{
"name": "Intel(R) Iris(R) Xe Graphics",
"driverVersion": "31.0.101.3358",
"driverDate": "2022-08-04T02:00:00.0000000+02:00",
"adapterRamBytes": 1073741824,
"status": "OK"
}
]
},
{
"id": "audio-device",
"class": "optional",
"passed": true,
"summary": "An audio device is optional for puzzle play and recommended for narration and speech features.",
"evidence": [
{
"name": "Intel® Smart Sound Technology for Digital Microphones",
"status": "OK"
},
{
"name": "Intel® Smart Sound Technology for USB Audio",
"status": "OK"
},
{
"name": "Intel® Smart Sound Technology for Bluetooth® Audio",
"status": "OK"
},
{
"name": "Audio Device",
"status": "OK"
},
{
"name": "Virtual Audio Cable",
"status": "OK"
}
]
},
{
"id": "openxr-runtime",
"class": "optional",
"passed": false,
"summary": "An active OpenXR runtime is optional for keyboard/mouse play and required only for live headset sessions.",
"evidence": null
},
{
"id": "required-payload-files",
"class": "required",
"passed": true,
"summary": "The installed payload must contain every required executable, runtime, cooked-data, browser, solver, and OpenXR file.",
"evidence": {
"payloadRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_alpha_release19_shipping_20260723\\Windows",
"requiredFileCount": 15,
"cefRuntimeFiles": [
"Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/libcef.dll"
],
"failures": [
]
}
},
{
"id": "desktop-code-signature",
"class": "optional",
"passed": false,
"summary": "The Alpha payload is currently unsigned; production distribution requires Authenticode signing.",
"evidence": {
"status": "NotSigned",
"signer": null
}
},
{
"id": "payload-manifest-parity",
"class": "required",
"passed": true,
"summary": "The installed payload must match the release manifest without missing, truncated, or altered files.",
"evidence": {
"manifestPath": "C:\\HyperTwist_worktrees\\installer-release19-20260726\\validation\\windows-installer-payload-manifest.json",
"manifestVersion": "ht-windows-installer-payload/v1",
"manifestEntryCount": 143,
"declaredFileCount": 143,
"declaredTotalSizeBytes": 1585341236,
"computedTotalSizeBytes": 1585341236,
"hashesVerified": true,
"failures": [
]
}
}
]
}

View file

@ -0,0 +1,132 @@
{
"reportVersion": "ht-post-task-hygiene/v1",
"generatedAtUtc": "2026-07-26T09:38:34Z",
"doctrine": "docs/ops/HYPERTWIST_PACKET_CLEANUP_AND_REBUILDABLE_ARTIFACT_HYGIENE_2026-05-20.md",
"result": "passed-with-protected-retention",
"inventory": {
"initialPhase10ValidateFileCount": 53308,
"initialPhase10ValidateSizeBytes": 29264296595,
"postCleanupPhase10ValidateFileCount": 19793,
"postCleanupPhase10ValidateSizeBytes": 2514767231,
"finalWindowsFreeBytes": 167725654016
},
"safeDeletion": {
"measuredTargetCount": 52,
"measuredFileCount": 37793,
"measuredSizeBytes": 54474200710,
"passes": [
{
"class": "superseded-top-level-package-and-probe-roots",
"targetCount": 23,
"fileCount": 4264,
"sizeBytes": 27475757341
},
{
"class": "generated-subtrees-inside-phase10validate",
"targetCount": 24,
"fileCount": 33519,
"sizeBytes": 26749605009
},
{
"class": "intentionally-interrupted-installer-partials",
"targetCount": 2,
"fileCount": 2,
"sizeBytes": 248464450
},
{
"class": "incomplete-source-comparison-manifest",
"targetCount": 1,
"fileCount": 1,
"sizeBytes": 334999
},
{
"class": "evidence-harvested-installer-lifecycle-roots",
"targetCount": 2,
"fileCount": 7,
"sizeBytes": 38911
}
],
"additionalUnmeasuredDisposableArtifactsRemoved": [
"tiny-payload NSIS contract roots",
"readiness interactive-contract files",
"malicious-manifest contract files",
"hash-staged Downloads incoming file"
]
},
"protectedRetention": [
{
"path": "C:\\HyperTwist_worktrees\\phase10validate",
"fileCount": 19793,
"sizeBytes": 2514767231,
"reason": "Source/custody envelope rather than rebuildable-only output; complete duplicate proof was blocked by four relative source-tree reparse points."
},
{
"path": "C:\\HyperTwist_worktrees\\phase10validate_packaged_alpha_release17_20260723",
"fileCount": 30,
"sizeBytes": 2138251,
"reason": "Release 17 rollback provenance."
},
{
"path": "C:\\HyperTwist_worktrees\\phase10validate_packaged_alpha_release18_20260723",
"fileCount": 172,
"sizeBytes": 2288143046,
"reason": "Release 18 historical package evidence."
},
{
"path": "C:\\HyperTwist_worktrees\\phase10validate_packaged_alpha_release18_shipping_20260723",
"fileCount": 175,
"sizeBytes": 1920603184,
"reason": "Release 18 Shipping rollback evidence."
},
{
"path": "C:\\HyperTwist_worktrees\\phase10validate_packaged_alpha_release19_shipping_20260723",
"fileCount": 173,
"sizeBytes": 1920589115,
"reason": "Current Release 19 installer source authority."
},
{
"path": "C:\\HyperTwist_worktrees\\installer-release19-20260726",
"fileCount": 7,
"sizeBytes": 851156084,
"reason": "Current validated installer and machine-generated build evidence."
},
{
"path": "C:\\Users\\Anthracite Ace\\Downloads\\HyperTwist-Alpha-Test-20260723-R19",
"fileCount": 178,
"sizeBytes": 1920681761,
"reason": "Current portable operator delivery."
},
{
"path": "C:\\Users\\Anthracite Ace\\Downloads\\HyperTwist-Alpha-Test-20260723-R19.zip",
"fileCount": 1,
"sizeBytes": 1093537260,
"reason": "Current verified portable archive delivery."
},
{
"path": "C:\\Users\\Anthracite Ace\\Downloads\\HyperTwist-Alpha-Test-20260723-R19-Setup.exe",
"fileCount": 1,
"sizeBytes": 851049086,
"reason": "Current validated installer delivery."
},
{
"path": "C:\\Users\\Anthracite Ace\\AppData\\Local\\UnrealHyperTwist\\Saved",
"fileCount": 275,
"sizeBytes": 736756671,
"reason": "Protected operator settings, saves, replays, logs, and diagnostics."
},
{
"path": "/home/dev/src/HyperTwist",
"reason": "VPS main-worktree dependencies, mirrors, custody, source, and pre-existing ambiguous generated roots were not packet-created and were retained."
}
],
"finalVerification": {
"temporaryLifecycleRootsAbsent": true,
"partialInstallerOutputsAbsent": true,
"disposableContractRootsAbsent": true,
"downloadsStagingAbsent": true,
"hyperTwistInstallerRegistrationAbsent": true,
"makensisProcessCount": 0,
"todoJsonPolicy": "ignored",
"protectedPathsPresent": true
}
}

View file

@ -0,0 +1,67 @@
{
"reportVersion": "ht-windows-installer-lifecycle/v1",
"generatedAtUtc": "2026-07-26T07:56:13.8198132Z",
"result": "failed",
"validationClass": "isolated-current-host",
"cleanMachineAvailable": false,
"cleanMachineBoundary": "No Windows Sandbox, clean Hyper-V VM, or base VHD was available on the validation host.",
"installerPath": "C:\\HyperTwist_worktrees\\installer-release19-20260726\\HyperTwist-Alpha-Test-20260723-R19-Setup.exe",
"installerSizeBytes": 851127022,
"installerSha256": "42b9c7ce17b40cbce9adcef15cbc904e239bac673615447909d7a21419ab5ac0",
"installerSignatureStatus": "NotSigned",
"installRoot": "C:\\HyperTwistInstallerValidation\\20260726T075613Z\\HyperTwist",
"firstInstallExitCode": 0,
"upgradeInstallExitCode": null,
"payloadParityAfterFirstInstall": {
"passed": true,
"manifestEntryCount": 143,
"failureCount": 0,
"failures": [
],
"unexpectedFiles": [
],
"unexpectedPdbFiles": [
]
},
"payloadParityAfterUpgrade": null,
"readiness": null,
"launchSmoke": null,
"uninstallExitCode": null,
"registrationVerified": true,
"shortcutsVerified": false,
"shortcutDetails": {
"application": {
"path": "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\HyperTwist\\HyperTwist.lnk",
"exists": true,
"targetPath": "C:\\HyperTwistInstallerValidation\\20260726T075613Z\\HyperTwist\\UnrealHyperTwist.exe",
"arguments": ""
},
"readiness": {
"path": "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\HyperTwist\\System Readiness Report.lnk",
"exists": true,
"targetPath": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"arguments": "-NoProfile -ExecutionPolicy Bypass -File \"C:\\HyperTwistInstallerValidation\\20260726T075613Z\\HyperTwist\\Installer\\Test-HyperTwistWindowsReadiness.ps1\" -PayloadRoot \"C:\\HyperTwistInstallerValidation\\20260726T075613Z\\HyperTwist\" -ReportPath \"C:\\Users\\Anthracite Ace\\AppData\\Local\\Temp\\HyperTwist-readiness.json\""
},
"uninstall": {
"path": "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\HyperTwist\\Uninstall HyperTwist.lnk",
"exists": true,
"targetPath": "C:\\HyperTwistInstallerValidation\\20260726T075613Z\\HyperTwist\\Uninstall HyperTwist.exe",
"arguments": ""
},
"desktop": {
"path": "C:\\Users\\Public\\Desktop\\HyperTwist.lnk",
"exists": true,
"targetPath": "C:\\HyperTwistInstallerValidation\\20260726T075613Z\\HyperTwist\\UnrealHyperTwist.exe",
"arguments": ""
}
},
"userDataPreserved": false,
"installRootRemoved": false,
"registrationRemoved": false,
"shortcutsRemoved": false,
"cleanupCompleted": false,
"error": "One or more installer-managed shortcuts are missing."
}

View file

@ -0,0 +1,34 @@
{
"reportVersion": "ht-windows-installer-build/v1",
"generatedAtUtc": "2026-07-26T08:31:34.7586689Z",
"result": "passed",
"releaseId": "20260723-R19",
"displayVersion": "Alpha R19",
"productVersion": "0.19.0.0",
"packageRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_alpha_release19_shipping_20260723",
"payloadRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_alpha_release19_shipping_20260723\\Windows",
"installerPath": "C:\\HyperTwist_worktrees\\installer-release19-20260726\\HyperTwist-Alpha-Test-20260723-R19-Setup.exe",
"payloadManifestPath": "C:\\HyperTwist_worktrees\\installer-release19-20260726\\validation\\windows-installer-payload-manifest.json",
"sourceReadinessReportPath": "C:\\HyperTwist_worktrees\\installer-release19-20260726\\validation\\windows-installer-source-readiness-report.json",
"makensisPath": "C:\\Program Files (x86)\\NSIS\\makensis.exe",
"makensisLogPath": "C:\\HyperTwist_worktrees\\installer-release19-20260726\\validation\\makensis.log",
"vcRedist": {
"path": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Redist\\MSVC\\v143\\vc_redist.x64.exe",
"version": "14.44.35211.0",
"sizeBytes": 25635768,
"sha256": "cc0ff0eb1dc3f5188ae6300faef32bf5beeba4bdd6e8e445a9184072096b713b",
"signatureStatus": "Valid",
"signer": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
},
"excludedPatterns": [
"*.pdb",
"Manifest_DebugFiles_Win64.txt"
],
"payloadFileCount": 143,
"payloadSizeBytes": 1585341236,
"installerSizeBytes": 851049086,
"installerSha256": "c31ed6b0b2ae1feb449c27e484d72dc2e07ba220e3cdafa4c3605429d638e0ea",
"installerSignatureStatus": "NotSigned",
"incompleteInstallerRemoved": false,
"error": null
}

View file

@ -0,0 +1,287 @@
{
"reportVersion": "ht-windows-installer-lifecycle/v1",
"generatedAtUtc": "2026-07-26T09:19:26.8973913Z",
"result": "passed",
"validationClass": "isolated-current-host",
"cleanMachineAvailable": false,
"cleanMachineBoundary": "No Windows Sandbox, clean Hyper-V VM, or base VHD was available on the validation host.",
"installerPath": "C:\\HyperTwist_worktrees\\installer-release19-20260726\\HyperTwist-Alpha-Test-20260723-R19-Setup.exe",
"installerSizeBytes": 851049086,
"installerSha256": "c31ed6b0b2ae1feb449c27e484d72dc2e07ba220e3cdafa4c3605429d638e0ea",
"installerSignatureStatus": "NotSigned",
"installRoot": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist",
"unownedNonEmptyRefusal": {
"passed": true,
"exitCode": 14,
"expectedExitCode": 14,
"sentinelPreserved": true,
"registrationAbsent": true
},
"firstInstallExitCode": 0,
"upgradeInstallExitCode": 0,
"payloadParityAfterFirstInstall": {
"passed": true,
"manifestEntryCount": 143,
"failureCount": 0,
"failures": [
],
"unexpectedFiles": [
],
"unexpectedPdbFiles": [
]
},
"payloadParityAfterUpgrade": {
"passed": true,
"manifestEntryCount": 143,
"failureCount": 0,
"failures": [
],
"unexpectedFiles": [
],
"unexpectedPdbFiles": [
]
},
"readiness": {
"reportVersion": "ht-windows-readiness/v1",
"generatedAtUtc": "2026-07-26T09:23:56.9501545Z",
"result": "ready-with-warnings",
"payloadRoot": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist",
"targetInstallRoot": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist",
"requiredFailureCount": 0,
"optionalWarningCount": 2,
"checks": [
{
"id": "windows-x64",
"class": "required",
"passed": true,
"summary": "HyperTwist requires a 64-bit Windows operating system.",
"evidence": {
"architecture": "64-bit",
"processArchitecture": "AMD64"
}
},
{
"id": "windows-supported-build",
"class": "required",
"passed": true,
"summary": "HyperTwist requires Windows build 19045 or newer.",
"evidence": {
"caption": "Microsoft Windows 11 Home",
"version": "10.0.22631",
"build": 22631,
"minimumBuild": 19045
}
},
{
"id": "system-memory",
"class": "required",
"passed": true,
"summary": "At least 8 GiB of physical memory is required for the desktop Alpha.",
"evidence": {
"totalBytes": 16893206528,
"minimumBytes": 8589934592
}
},
{
"id": "install-volume-free-space",
"class": "required",
"passed": true,
"summary": "The target volume must have enough free space for installation and first-run caches.",
"evidence": {
"targetInstallRoot": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist",
"availableBytes": 167681560576,
"minimumBytes": 4294967296
}
},
{
"id": "vc-runtime-x64",
"class": "required",
"passed": true,
"summary": "Microsoft Visual C++ 2015-2022 x64 runtime 14.44.35211.0 or newer is required and is bundled by the installer.",
"evidence": {
"registryPath": "HKLM:\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64",
"installed": true,
"version": "14.44.35211.0"
}
},
{
"id": "windows-media-and-graphics-libraries",
"class": "required",
"passed": true,
"summary": "Windows DirectX, XInput, and Media Foundation runtime libraries must be available.",
"evidence": {
"systemDirectory": "C:\\Windows\\system32",
"requiredLibraries": [
"d3d11.dll",
"d3d12.dll",
"dxgi.dll",
"mf.dll",
"mfplat.dll",
"mfreadwrite.dll",
"xinput1_4.dll"
],
"missingLibraries": [
]
}
},
{
"id": "graphics-adapter",
"class": "required",
"passed": true,
"summary": "At least one Windows graphics adapter must be visible.",
"evidence": [
{
"name": "Intel(R) Iris(R) Xe Graphics",
"driverVersion": "31.0.101.3358",
"driverDate": "2022-08-04T02:00:00.0000000+02:00",
"adapterRamBytes": 1073741824,
"status": "OK"
}
]
},
{
"id": "audio-device",
"class": "optional",
"passed": true,
"summary": "An audio device is optional for puzzle play and recommended for narration and speech features.",
"evidence": [
{
"name": "Intel® Smart Sound Technology for Digital Microphones",
"status": "OK"
},
{
"name": "Intel® Smart Sound Technology for USB Audio",
"status": "OK"
},
{
"name": "Intel® Smart Sound Technology for Bluetooth® Audio",
"status": "OK"
},
{
"name": "Audio Device",
"status": "OK"
},
{
"name": "Virtual Audio Cable",
"status": "OK"
}
]
},
{
"id": "openxr-runtime",
"class": "optional",
"passed": false,
"summary": "An active OpenXR runtime is optional for keyboard/mouse play and required only for live headset sessions.",
"evidence": null
},
{
"id": "required-payload-files",
"class": "required",
"passed": true,
"summary": "The installed payload must contain every required executable, runtime, cooked-data, browser, solver, and OpenXR file.",
"evidence": {
"payloadRoot": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist",
"requiredFileCount": 15,
"cefRuntimeFiles": [
"Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/libcef.dll"
],
"failures": [
]
}
},
{
"id": "desktop-code-signature",
"class": "optional",
"passed": false,
"summary": "The Alpha payload is currently unsigned; production distribution requires Authenticode signing.",
"evidence": {
"status": "NotSigned",
"signer": null
}
},
{
"id": "payload-manifest-parity",
"class": "required",
"passed": true,
"summary": "The installed payload must match the release manifest without missing, truncated, or altered files.",
"evidence": {
"manifestPath": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist\\Installer\\payload-manifest.json",
"manifestVersion": "ht-windows-installer-payload/v1",
"manifestEntryCount": 143,
"declaredFileCount": 143,
"declaredTotalSizeBytes": 1585341236,
"computedTotalSizeBytes": 1585341236,
"hashesVerified": true,
"failures": [
]
}
}
]
},
"launchSmoke": {
"passed": true,
"startedAtUtc": "2026-07-26T09:23:57.0508749Z",
"launcherProcessId": 3756,
"ownedProcessIds": [
3756,
14248
],
"stoppedProcessIds": [
3756,
14248
],
"runtimeLogPath": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\evidence\\installed-runtime.log",
"diagnosticsPath": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\evidence\\installed-runtime.hyperdiagnostics.log",
"diagnosticsInitialized": true,
"fatalLines": [
],
"listeningEndpoints": [
]
},
"uninstallExitCode": 0,
"registrationVerified": true,
"shortcutsVerified": true,
"shortcutDetails": {
"application": {
"path": "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\HyperTwist\\HyperTwist.lnk",
"exists": true,
"targetPath": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist\\UnrealHyperTwist.exe",
"arguments": ""
},
"readiness": {
"path": "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\HyperTwist\\System Readiness Report.lnk",
"exists": true,
"targetPath": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"arguments": "-NoProfile -ExecutionPolicy Bypass -File \"C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist\\Installer\\Test-HyperTwistWindowsReadiness.ps1\" -PayloadRoot \"C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist\" -TargetInstallRoot \"C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist\" -ExpectedPayloadManifestPath \"C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist\\Installer\\payload-manifest.json\" -WaitForUser"
},
"uninstall": {
"path": "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\HyperTwist\\Uninstall HyperTwist.lnk",
"exists": true,
"targetPath": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist\\Uninstall HyperTwist.exe",
"arguments": ""
},
"desktop": {
"path": "C:\\Users\\Public\\Desktop\\HyperTwist.lnk",
"exists": true,
"targetPath": "C:\\HyperTwist Installer Validation\\20260726T091926Z\\HyperTwist\\UnrealHyperTwist.exe",
"arguments": ""
}
},
"userDataPreserved": true,
"installRootRemoved": true,
"registrationRemoved": true,
"shortcutsRemoved": true,
"cleanupCompleted": true,
"error": null
}

View file

@ -0,0 +1,729 @@
{
"manifestVersion": "ht-windows-installer-payload/v1",
"generatedAtUtc": "2026-07-26T08:31:40.6337955Z",
"releaseId": "20260723-R19",
"sourcePayloadRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_alpha_release19_shipping_20260723\\Windows",
"excludedPatterns": [
"*.pdb",
"Manifest_DebugFiles_Win64.txt"
],
"fileCount": 143,
"totalSizeBytes": 1585341236,
"files": [
{
"relativePath": "Content/Browser/dist/assets/client.js",
"sizeBytes": 875934,
"sha256": "524383f29f700b5907b999ec80fe1d4f472ac14af8ea5580a906696eba0955d9"
},
{
"relativePath": "Content/Browser/dist/assets/decode.js",
"sizeBytes": 26052,
"sha256": "f6b754561563af8aeda1e6aa9f503496a45f90cf079ce74119cdba7087058c9b"
},
{
"relativePath": "Content/Browser/dist/assets/easing-0f4db1c0.esm.js",
"sizeBytes": 29269,
"sha256": "38ddf7add1b0274ecb8e422332452e1cc2661758d576324c0267f1893cc96704"
},
{
"relativePath": "Content/Browser/dist/assets/events-b389eeca.esm.js",
"sizeBytes": 608404,
"sha256": "415f094c85d96883fe17136b2ccaf7c0599c74c1fec1d50fbfe77c5aa49e8a78"
},
{
"relativePath": "Content/Browser/dist/assets/gltf-viewer.module.js",
"sizeBytes": 371169,
"sha256": "803cf04b8bb52f4540459a6b755215ace8e3f9ca06ed6e269c2de58b1791084d"
},
{
"relativePath": "Content/Browser/dist/assets/index.js",
"sizeBytes": 101,
"sha256": "c17a644a35874aec14eb885b7570e76742265aab419e26bf5ec3aef3d0c33004"
},
{
"relativePath": "Content/Browser/dist/assets/index10.js",
"sizeBytes": 463085,
"sha256": "5eb1d53e1f4ad93014c9db5e7053a284ee75d12f67922a5342d5babaaf62f88f"
},
{
"relativePath": "Content/Browser/dist/assets/index11.js",
"sizeBytes": 1010478,
"sha256": "943fa33589d184eb7c2580122f44baf69426d720fc7459aad7b3b452666cdcbd"
},
{
"relativePath": "Content/Browser/dist/assets/index12.js",
"sizeBytes": 8792,
"sha256": "4ff02630aa51698e0e571bdd1615df1e47908ccb94c27fcf30abf886677c2dbd"
},
{
"relativePath": "Content/Browser/dist/assets/index13.js",
"sizeBytes": 867555,
"sha256": "dd8d1a29141fa69a55d38f196aee7ea64b02354bbeadfd5578f98533cdd8959f"
},
{
"relativePath": "Content/Browser/dist/assets/index2.js",
"sizeBytes": 16990,
"sha256": "00f5a4f3a3d11486be6feebae2511963dd06903eed45e1a1dc44c1b3be610d18"
},
{
"relativePath": "Content/Browser/dist/assets/index3.js",
"sizeBytes": 541053,
"sha256": "0fcda6a7676782f695f3e713f721b7dd998cb12740ab81814af7ca263547c69b"
},
{
"relativePath": "Content/Browser/dist/assets/index4.js",
"sizeBytes": 44323,
"sha256": "cfec865d65f73d1ef3102352554f5fb10a0d6c7b3572241de9077fbd89f11d26"
},
{
"relativePath": "Content/Browser/dist/assets/index5.js",
"sizeBytes": 65696,
"sha256": "e83b58f0daa45e909992294461784bc49ca4fd4d857227767719b2eb43d390d9"
},
{
"relativePath": "Content/Browser/dist/assets/index6.js",
"sizeBytes": 1608912,
"sha256": "bac438344a5a247cfbeb8b4025903b7c0fec959a04c80e2375f1bd53a89b27d0"
},
{
"relativePath": "Content/Browser/dist/assets/index7.js",
"sizeBytes": 200923,
"sha256": "493590d87a9126e4e74489acd71981ccae2cfef540956fbc41b1220ff4e6a117"
},
{
"relativePath": "Content/Browser/dist/assets/index8.js",
"sizeBytes": 2925000,
"sha256": "b94712cac9e7b384811054142511b411e9e50ea84e4244be57296d934a2b3ab8"
},
{
"relativePath": "Content/Browser/dist/assets/index9.js",
"sizeBytes": 536,
"sha256": "1a22f6a2f3f1694a068d86a10cdbb0612007a82609dad95d661de48924f47395"
},
{
"relativePath": "Content/Browser/dist/assets/install.js",
"sizeBytes": 396204,
"sha256": "43d1b725ff895424f5d6067851ff0b23a6822f4ab00e7e40877ae90925c985d2"
},
{
"relativePath": "Content/Browser/dist/assets/inter.js",
"sizeBytes": 440193,
"sha256": "24b0d2388654b0e94d12a73a0ab262c18155cd90b4ddefc048ac8be673f5a9ce"
},
{
"relativePath": "Content/Browser/dist/assets/jsx-runtime.js",
"sizeBytes": 22632,
"sha256": "a994feec9287fe3f3f3afd143dbf386e6ce68921d2573df3778fb1594dfe0954"
},
{
"relativePath": "Content/Browser/dist/assets/leva.esm.js",
"sizeBytes": 282310,
"sha256": "8b8e8048af8e722e886919ef3fc42e17201aa4fdc972979f40980bb324a24783"
},
{
"relativePath": "Content/Browser/dist/assets/maath.esm.js",
"sizeBytes": 13221,
"sha256": "9e0bb39344397d519cc0601ac2d9409e891b4ad627a509ca16a9d1a7c047959b"
},
{
"relativePath": "Content/Browser/dist/assets/MathUtils.js",
"sizeBytes": 185,
"sha256": "04ead5c1ece921b0dbb5c3a8c7b1b9f59698f43fe5f2c2ba7390307d35fe275d"
},
{
"relativePath": "Content/Browser/dist/assets/MeshoptDecoder.js",
"sizeBytes": 595800,
"sha256": "902a1fbb9e9dadb8aaac060e2f38f51bb9afc0f3f526c66d8f78339079af527d"
},
{
"relativePath": "Content/Browser/dist/assets/model-viewer.js",
"sizeBytes": 569271,
"sha256": "78b871924c1fe17e3907e221a0488ccb74a237622363917400c08e81300ddff2"
},
{
"relativePath": "Content/Browser/dist/assets/model-viewer-effects.js",
"sizeBytes": 28163,
"sha256": "1e8577509a823663aae78e29dd8f7a499a24ac30eb0df5874fac12d5a8d2ed45"
},
{
"relativePath": "Content/Browser/dist/assets/morphPath.js",
"sizeBytes": 59852,
"sha256": "47274c7da895da1dd209982343f7e0b96e69c8849785c370ab23b4bb909e9cb6"
},
{
"relativePath": "Content/Browser/dist/assets/Painter.js",
"sizeBytes": 255062,
"sha256": "482c8b4179b89e56847e02b9175d4d1b554a269d09b9f7c3b89a191c55f9ac87"
},
{
"relativePath": "Content/Browser/dist/assets/property.js",
"sizeBytes": 20774,
"sha256": "7d5b7c2bdf95125984b778c7b1ad8db105d85cec36d3f0a74205c6cc6f3fa267"
},
{
"relativePath": "Content/Browser/dist/assets/react-spring_core.modern.js",
"sizeBytes": 69814,
"sha256": "cbf81e7fec6557b7baef30cd7f180ac9cc3ade3ddb5bada3e4382ab7a63e9511"
},
{
"relativePath": "Content/Browser/dist/assets/react-spring_three.modern.js",
"sizeBytes": 1577,
"sha256": "ee4a91412c95a90241efff06bb7c80475e252250b8795eb2abd1265a18c0254b"
},
{
"relativePath": "Content/Browser/dist/assets/react-spring_web.modern.js",
"sizeBytes": 6888,
"sha256": "ca824b8b650778235149947119e4e37e5e9c997870cd9bc248d9bcd1efe9d911"
},
{
"relativePath": "Content/Browser/dist/assets/react-three-fiber.esm.js",
"sizeBytes": 7829,
"sha256": "3ca075d4805914a117e2496a0b3c5d49aedb99ed0fc5df9cfb19aa59700d3b25"
},
{
"relativePath": "Content/Browser/dist/assets/three.module.js",
"sizeBytes": 1292285,
"sha256": "31e68a7aa67d4d54c90d64ed6b1a86040965e1d619c2229154dc5ff63be67a1b"
},
{
"relativePath": "Content/Browser/dist/assets/tslib.es6.js",
"sizeBytes": 3434,
"sha256": "e525bc57e4875c563602b26f5ff4ce5c8cabc69f8ca5107cef68ce182bf6b81f"
},
{
"relativePath": "Content/Browser/dist/assets/use-gesture-react.esm.js",
"sizeBytes": 42626,
"sha256": "f3ff4be03a822f20e3275013068617c96ef4e2f147988806f7620bfbad4b8b11"
},
{
"relativePath": "Content/Browser/dist/assets/vanilla.js",
"sizeBytes": 541,
"sha256": "264fdf8ce2d8c33fef425b00082363420a4c1c3d5b737a44b3f64ad6ea62292a"
},
{
"relativePath": "Content/Browser/dist/assets/vision_bundle.js",
"sizeBytes": 169647,
"sha256": "268be65875c0724747ceae21789108e4f40f6402bc18baf469362a90003f9af5"
},
{
"relativePath": "Content/Browser/dist/browser-spatial-runtime.js",
"sizeBytes": 30651,
"sha256": "0feb7fc24108b4804e4b801518ce91a0a5a9eb8f806edaedb7506d8a82bb283e"
},
{
"relativePath": "Content/Browser/index.html",
"sizeBytes": 7473,
"sha256": "00a730f4d8ee099d0d0d63a171b94194f9fe9c9c92de810a411050c0b260a562"
},
{
"relativePath": "Content/Browser/src/browser-runtime-bootstrap.js",
"sizeBytes": 3422,
"sha256": "e35963e1888deac61578141579c64e686c15cb964b2b4d8d8c080ed35b4ed00e"
},
{
"relativePath": "Content/Browser/src/browser-spatial-runtime-fallback.js",
"sizeBytes": 30201,
"sha256": "0089004d3aa86de1edefcc7c95ea0fc80c9352e49778bc384f6d676e8a9ad1a4"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/chrome_100_percent.pak",
"sizeBytes": 695026,
"sha256": "3f665b02bbe58d6423c2e86211bd8f074d8a36726438e639541ad695f8430b9b"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/chrome_200_percent.pak",
"sizeBytes": 1094579,
"sha256": "8152796c92c3481a546564be8f0b2ca90bf50d1bf4a141db166c90377239794d"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/chrome_elf.dll",
"sizeBytes": 1370552,
"sha256": "6cb248829b22689691fcfeb93258e12d8b19204d62dafc75bfa84bd21b142679"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/d3dcompiler_47.dll",
"sizeBytes": 4927552,
"sha256": "7b52ad25b00b6c67e721ec0105601dcb462fefe233a55e5a93927ed69650829f"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/dxcompiler.dll",
"sizeBytes": 20817848,
"sha256": "7e8d14f9c13cf9d7344a865503cbe91e8cbe8e3b023525e5f208c155a1a28075"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/dxil.dll",
"sizeBytes": 1519176,
"sha256": "3b8648bd722480535753c28d34ee9de98ddb69f7e01bef96068303ffbd80ccc4"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/icudtl.dat",
"sizeBytes": 10468208,
"sha256": "9ae98c06cbb0ea43c5cd6b5725310c008c65e46072421a1118cb88e1de9a8b92"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/libcef.dll",
"sizeBytes": 239992248,
"sha256": "e89ec24b1abac5c77260c49c7bd33d010ddd67b752d22d87dc142d37d2e8d966"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/libEGL.dll",
"sizeBytes": 536504,
"sha256": "f3f527ac94b3e81e36c123d1a0b04c7e5cc6e079ae1fbd689540ab85108797d6"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/libGLESv2.dll",
"sizeBytes": 8959416,
"sha256": "251bbd3d72cbe381857401ad62e42b5627d2c4c05fbcdd4f68e774720ba01c44"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/resources.pak",
"sizeBytes": 8683856,
"sha256": "aa8d203d593ed170696eec09137e2421882f46273337a00d16c6b23e91de675e"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/af.pak",
"sizeBytes": 538998,
"sha256": "c514fe813e56c4767e9b190acca7a44d6f0f6d7c03dec6c4afb6586f0c89ebb7"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/am.pak",
"sizeBytes": 866444,
"sha256": "fd641fb8513ea2b93a5f34f45a9a8af3596e73dd738207bcc07ecfbd398f4285"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/ar.pak",
"sizeBytes": 953918,
"sha256": "1d11e3b7cae4762d0c35dc83c331813f49a7f9bc492eb63cada01954af8a7d2a"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/bg.pak",
"sizeBytes": 987762,
"sha256": "d2fa3f3e66113fbd7efe891eef6f66915e04dfa8aedbb162cebe27ee7db4d248"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/bn.pak",
"sizeBytes": 1269605,
"sha256": "a3f81cf087eb69c505243db32d36fce0f4266d074f22dd6e1742a1311cc8d5c5"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/ca.pak",
"sizeBytes": 612377,
"sha256": "bc32492cbaa82d9de625548c79f03173ec4db4d3e25d2e5d3099a7d7231f8ff1"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/cs.pak",
"sizeBytes": 622919,
"sha256": "3223737e45b3f06acd90d2ea6b923c99ef3a988f072234328f5cd337d428a219"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/da.pak",
"sizeBytes": 567958,
"sha256": "a2960a7b635fc8a100e325b5ebe0bd696cccaa0af5d77ec1ea2ef3a7ad77471c"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/de.pak",
"sizeBytes": 609935,
"sha256": "d7a8385e3e1bedd61d102be6b19d5e8cf09c814528cfd782c1d3bf62812690b8"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/el.pak",
"sizeBytes": 1080416,
"sha256": "889501de74431bc33ca326df8711c46e3def70cd82b1124a44524ee2829167c2"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/en-GB.pak",
"sizeBytes": 492124,
"sha256": "86c1766ba2e7b86f111445a1f57c5120e1be78a4b6e7b2e124dcd31116c7ccdb"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/en-US.pak",
"sizeBytes": 497015,
"sha256": "fc44badc0f1ee3daa57d905a9e39ae3ee6dbd38049cbd944905630409038ff2b"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/es.pak",
"sizeBytes": 596806,
"sha256": "270147cc81547477f181b5c6b75dd8cf46eb124de802de8eb2bc6855d2ef8fe8"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/es-419.pak",
"sizeBytes": 598355,
"sha256": "d7088cd07d220af16275befc09c56c2524d4b63a424876ce2f7f5442f56dcaa3"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/et.pak",
"sizeBytes": 544221,
"sha256": "21046ded9f9aab82bbc7ff1579fbc80b94bd6da09a3e4817188391d4af1c24be"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/fa.pak",
"sizeBytes": 879421,
"sha256": "af1dd21b0aa302cbebc56d39abd8bcfda981734e04c7ab8040e905cbbbaa193c"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/fi.pak",
"sizeBytes": 552657,
"sha256": "48a5e49251723e3e191c8502ff1b6dedf39e168ac252fcf535b36ad52e9ea8fd"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/fil.pak",
"sizeBytes": 624599,
"sha256": "7117986303e4954ac39bb144ac76bacb450f7be68ae1fde970ee639ad39bd9d8"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/fr.pak",
"sizeBytes": 646732,
"sha256": "4dee926b878d106a3599c1c884bb84dcdd7785836032384420f5fd0c76cea640"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/gu.pak",
"sizeBytes": 1255599,
"sha256": "9af5364f7cbe7ef972c0fb9b62bf2c13cb1a9ff4dde1d6fd2ffcacd088adb987"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/he.pak",
"sizeBytes": 777593,
"sha256": "ad956cf0f663b25c19907bb8737b2342704223ac79f8a85b7a3038d52e9afa7a"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/hi.pak",
"sizeBytes": 1330999,
"sha256": "6b2629ccdc38a3c14ee5de4eba558527bbc4c590c87cc9393f9b7e337b8e5efa"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/hr.pak",
"sizeBytes": 600581,
"sha256": "d2d55568886ef5bb2a073e619f8dbf98a3ff341ce5ba6f3f362224081cc0cdc8"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/hu.pak",
"sizeBytes": 646489,
"sha256": "d3f6a1560e893042b92f40a56b5761790e94a78efda73a26a640dd21997443ae"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/id.pak",
"sizeBytes": 535422,
"sha256": "8f84ac460c88e44518e2c5165d0758e8d0f2c6223cfb5ca6ad5507aae79f67bd"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/it.pak",
"sizeBytes": 594371,
"sha256": "fd236c934edfae7fe10176f9d7a2293838d2cd59f54beb7af689cd61014a51c9"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/ja.pak",
"sizeBytes": 719063,
"sha256": "539e2195c7e7bbf7b2976e9b3b4950bc4a6616a590cf85678bd264387382de1c"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/kn.pak",
"sizeBytes": 1424332,
"sha256": "70d9698ad1cb9d78e1eab70e7e1161ca4216399969991922abd0edeae0ecbb01"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/ko.pak",
"sizeBytes": 606589,
"sha256": "a21b8cb55ed43abd51fb2f97d40208be637b3cb2dae2f55b3abd210722e67951"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/lt.pak",
"sizeBytes": 652194,
"sha256": "160b2da4626a0ce7570460ed48326352f25624753dcc6a6aa90c6906eca15eb5"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/lv.pak",
"sizeBytes": 651016,
"sha256": "70e1736c8d1406513ccf47ad85eec64f46f2ada1a7aab1ddddeb049170152f14"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/ml.pak",
"sizeBytes": 1479560,
"sha256": "7f42ec438ff13b97b775146033fce3ee33a73d0806e89b61f6f284a80e1c350b"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/mr.pak",
"sizeBytes": 1223735,
"sha256": "7277f688e1773d68088ce48efabf2dde4386c7cc5cfc579a8e16806eb0e37989"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/ms.pak",
"sizeBytes": 560048,
"sha256": "95d27fc7023e763c546bf19d9d214b1d1eaef1ea01dab23391fe5fba7361a674"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/nb.pak",
"sizeBytes": 543051,
"sha256": "c135a7f5f35a3533a2da5666768cb78402580ecb41e1309eee580816a8a0ac28"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/nl.pak",
"sizeBytes": 562204,
"sha256": "39bbe7e945c5cedf6a53f88fbc5c298a65e48192fe46c4e6a66c289b0869de1c"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/pl.pak",
"sizeBytes": 627081,
"sha256": "9ad6f0c5b4c3f335b155251b07261f5241a29892667fa607817e48abeae19462"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/pt-BR.pak",
"sizeBytes": 587941,
"sha256": "8e5466e2acbe6e350dc3b0752495dfa44eaf1da8e6054ffba9c320c4277c96d1"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/pt-PT.pak",
"sizeBytes": 592511,
"sha256": "f193c222cfd9cd723dbd7839cf45c14b7749cde5527b552160df819c72b442c1"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/ro.pak",
"sizeBytes": 612678,
"sha256": "31c2568ba713ce7d9b2a784b8bce09b116408a5943171b2baf84b83a2cdd2801"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/ru.pak",
"sizeBytes": 1003077,
"sha256": "8badbaff9761b5cfa26a409524c2c85f252ec2166449edb32246a2bee5f914b0"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/sk.pak",
"sizeBytes": 632482,
"sha256": "6491c4f1c9e8ad0a498cf2a5fe11467fd419fa105622977105915639341f1c27"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/sl.pak",
"sizeBytes": 607050,
"sha256": "97b5ab48cc42512a693fa7887acc7d4f3ea5cdd9e6bbf334fa2940c922e121af"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/sr.pak",
"sizeBytes": 929699,
"sha256": "d53039029b8b60ffca583896abe66e860016bf8b250df689bece5e17c2d86abc"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/sv.pak",
"sizeBytes": 547471,
"sha256": "669fbcc98ccb78259f497e965efbf2ba38e5c0e5ad6d50c588f013c018e03d7a"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/sw.pak",
"sizeBytes": 576117,
"sha256": "c8a7fe1af281b73985da58b66570150c4df27b0425f81068df6949799ada2769"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/ta.pak",
"sizeBytes": 1475609,
"sha256": "4019bcfd216e224924e34cb879b8cec6d91fde49d4a362aede9120bc9355543e"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/te.pak",
"sizeBytes": 1361935,
"sha256": "cd3a666d5f0091ac4c6f065b45ab21a30a9fe2a1073f8c6bf6192f123ad8156c"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/th.pak",
"sizeBytes": 1144623,
"sha256": "114d7a817a0c73f2d0bff438eb6a076ad76a8bfb2c561e542aeb699cbf1298ef"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/tr.pak",
"sizeBytes": 587129,
"sha256": "982728b531abdb1c2e33fc65203bb20e3abac226674b4611d8d609a82a483cb2"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/uk.pak",
"sizeBytes": 1001189,
"sha256": "54e5c7d4cb654c880c54051b35ec108dff28621afcc896a7d227d3b01a98c60b"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/ur.pak",
"sizeBytes": 872334,
"sha256": "04a50bcbedd0760bb9c6e0628d8e520d58e8f9e9655c76349bceea5a891783d5"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/vi.pak",
"sizeBytes": 695768,
"sha256": "f87b6a8530ffa5760367c62260b6bceaeb9148f58ad4041222dbd9592488299e"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/zh-CN.pak",
"sizeBytes": 503346,
"sha256": "dc5703f8267b4343a9d8e6acf5b82a4a52559f494c4555aded02f29cf20ed631"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/Resources/locales/zh-TW.pak",
"sizeBytes": 500142,
"sha256": "ccaf09ef99e6feda260aee9cd073bd7513223f770422d799d7490a6f16f44fc5"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/snapshot_blob.bin",
"sizeBytes": 313526,
"sha256": "e8a4351c04b18aef318612989c147e2878082306094e39349fb0fea8711a19a0"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/v8_context_snapshot.bin",
"sizeBytes": 665981,
"sha256": "b443db3a84c0c020e1ceeeb355018d6589eb8ef5b48e110ef6e02fd723781d78"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/vk_swiftshader.dll",
"sizeBytes": 5572536,
"sha256": "06b32c4dae03afe26c5acb821fdf19b7bdb9e3ffe8f29af93bba4e8e49722287"
},
{
"relativePath": "Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/vulkan-1.dll",
"sizeBytes": 930744,
"sha256": "ede9bf15fc0a3dc6f60d76eda8a7a765b57866edc5410c91709c82c4b7fe7e5f"
},
{
"relativePath": "Engine/Binaries/ThirdParty/DbgHelp/dbghelp.dll",
"sizeBytes": 2232688,
"sha256": "d65dff2ebe90bf79fba62f3bb1f105a7ce8efaefaa8349379a886bb893a4ef45"
},
{
"relativePath": "Engine/Binaries/ThirdParty/MsQuic/v220/win64/msquic.dll",
"sizeBytes": 2547080,
"sha256": "832f4ae560b1623aa4d8f87b982e9c9414312723fbc905ddab93fd80d7f1cea2"
},
{
"relativePath": "Engine/Binaries/ThirdParty/NVIDIA/NVaftermath/Win64/GFSDK_Aftermath_Lib.x64.dll",
"sizeBytes": 5442592,
"sha256": "f2f1a88185345b28d05673e11111ee21845f3ebe65919cf6b3085bee7d09e30a"
},
{
"relativePath": "Engine/Binaries/ThirdParty/Ogg/Win64/VS2015/libogg_64.dll",
"sizeBytes": 59832,
"sha256": "1e2fe8a3dfe9616edcb3cd3a5a958c791f690d5b604eb1658b0f844cef156eee"
},
{
"relativePath": "Engine/Binaries/ThirdParty/OpenXR/win64/openxr_loader.dll",
"sizeBytes": 1971640,
"sha256": "36020b5170c5a653132468e7b297c5e06eaff5ba4d26d5478d723f88f699f8b5"
},
{
"relativePath": "Engine/Binaries/ThirdParty/Vorbis/Win64/VS2015/libvorbis_64.dll",
"sizeBytes": 1724856,
"sha256": "676bbdc5838755d7a881c847f84ca79997b525882594b66160ab800676cab9c0"
},
{
"relativePath": "Engine/Binaries/ThirdParty/Vorbis/Win64/VS2015/libvorbisfile_64.dll",
"sizeBytes": 48568,
"sha256": "ceb067771c03ae1a7afc125c15c85dbab027aaec72575dc101c618eba2bafea0"
},
{
"relativePath": "Engine/Binaries/ThirdParty/Windows/XAudio2_9/x64/xaudio2_9redist.dll",
"sizeBytes": 858144,
"sha256": "013984e33e884bd0b823734ab86a599a8dceffed675039ff293d15ffcf7f2be9"
},
{
"relativePath": "Engine/Binaries/Win64/EpicWebHelper.exe",
"sizeBytes": 4751288,
"sha256": "f727e281c69b7cbffc4343310342ff78dfccf40f94fc8ba09e9ce6b1bf842a17"
},
{
"relativePath": "Engine/Config/StagedBuild_UnrealHyperTwist.ini",
"sizeBytes": 3,
"sha256": "f1945cd6c19e56b3c1c78943ef5ec18116907a4ca1efc40a57d48ab1db7adfc5"
},
{
"relativePath": "Engine/Content/Renderer/TessellationTable.bin",
"sizeBytes": 259568,
"sha256": "edc4f3b628a139c5e185bbaeca661c95c1f427368d3b92892c5cff36c56dd941"
},
{
"relativePath": "Engine/Content/Slate/Cursor/invisible.cur",
"sizeBytes": 326,
"sha256": "fef3980fa567e9c45d5d8dcaa432a2c1a3f3bb17fe67d7ab198164f56c142aa1"
},
{
"relativePath": "Engine/Content/SlateDebug/Fonts/LastResort.tps",
"sizeBytes": 930,
"sha256": "0f775fa99d05e21d43d2d06d55982b23dcdccdb512a9a5a07aff674f212c41b8"
},
{
"relativePath": "Engine/Content/SlateDebug/Fonts/LastResort.ttf",
"sizeBytes": 5395052,
"sha256": "fff0f8de0b868b964950fe97c9b60f893e9bc85a4f90b3629c5693db058b97b9"
},
{
"relativePath": "Engine/Plugins/NNE/NNERuntimeORT/Binaries/ThirdParty/Onnxruntime/Win64/onnxruntime.dll",
"sizeBytes": 14692872,
"sha256": "381497f383b457180aba6e5bf56a2eb18cefd6735cfd47257539f6d87fbc8584"
},
{
"relativePath": "Manifest_NonUFSFiles_Win64.txt",
"sizeBytes": 10686,
"sha256": "b8abaae2ce29e4fc950008d19930e2dccfbe6b6b667baa789c4b67dce60f5710"
},
{
"relativePath": "Manifest_UFSFiles_Win64.txt",
"sizeBytes": 255758,
"sha256": "cb6e852604f98c5a63ad4c9eecaf32826522f660362b0eceb7d2a36b6531f869"
},
{
"relativePath": "UnrealHyperTwist.exe",
"sizeBytes": 165376,
"sha256": "886f69c6a823770b3ed68db4fe3790a488124c99c98dbd41944a6bed5d634dc6"
},
{
"relativePath": "UnrealHyperTwist/Binaries/Win64/D3D12/x64/D3D12Core.dll",
"sizeBytes": 4544000,
"sha256": "fb197903fea91aaf352d2322f342b1cec8db36ff0292a49f24173577e08f26cf"
},
{
"relativePath": "UnrealHyperTwist/Binaries/Win64/D3D12/x64/d3d12SDKLayers.dll",
"sizeBytes": 5104632,
"sha256": "a817223446f53b3da894edb1ee7a92b94573690e689a9470b87bf42866115f91"
},
{
"relativePath": "UnrealHyperTwist/Binaries/Win64/DML/x64/DirectML.dll",
"sizeBytes": 18537968,
"sha256": "664255a3d9a6b976e2d0e2440403537d9d0dd91b1fad866768638b4e73a2c33a"
},
{
"relativePath": "UnrealHyperTwist/Binaries/Win64/tbb12.dll",
"sizeBytes": 331704,
"sha256": "645b681e3581c04d57f468bd85c17648172decf873653ab00382211b96cc98fb"
},
{
"relativePath": "UnrealHyperTwist/Binaries/Win64/tbbmalloc.dll",
"sizeBytes": 117688,
"sha256": "f81a11f2e6e93036bab4e51a1daab9940fb8a9a5ec0f508c4cb76d398e27c100"
},
{
"relativePath": "UnrealHyperTwist/Binaries/Win64/UnrealHyperTwist-Win64-Shipping.exe",
"sizeBytes": 181121024,
"sha256": "e583531cbb21d22f6c223eb12bf0cf81f82798faff9e2fbec4ebeb953bf98bf3"
},
{
"relativePath": "UnrealHyperTwist/Content/Paks/global.ucas",
"sizeBytes": 3325696,
"sha256": "c7e20318d6b887c26a131d33bed944410469b1f331e76bbfe0e5f0168d808188"
},
{
"relativePath": "UnrealHyperTwist/Content/Paks/global.utoc",
"sizeBytes": 806,
"sha256": "846861acb8db76d1a68a9614d89d327fb7ae4ade5a1c9aa63be79c713118d424"
},
{
"relativePath": "UnrealHyperTwist/Content/Paks/UnrealHyperTwist-Windows.pak",
"sizeBytes": 11069853,
"sha256": "605e0b2ff7dd284762f4d4281945535f2a3ff86065904a2ce4f1c2837c72c900"
},
{
"relativePath": "UnrealHyperTwist/Content/Paks/UnrealHyperTwist-Windows.ucas",
"sizeBytes": 281222144,
"sha256": "d52a40ad8633351078b351f4bc1321d12ee12b397ad585f7a8a43418f54cfe62"
},
{
"relativePath": "UnrealHyperTwist/Content/Paks/UnrealHyperTwist-Windows.utoc",
"sizeBytes": 216013,
"sha256": "f317d5ac0c82871400697dc29b17925a49d55c7aa2608ddc4f91cfea896ec07e"
},
{
"relativePath": "UnrealHyperTwist/Saved/twophase-ht.tbl",
"sizeBytes": 676207080,
"sha256": "dc6de3d909a37afe2ba7f1f978543a13eae215bd20d954d66813524a8ca20034"
}
]
}

View file

@ -0,0 +1,180 @@
{
"reportVersion": "ht-windows-readiness/v1",
"generatedAtUtc": "2026-07-26T08:31:51.1163747Z",
"result": "ready-with-warnings",
"payloadRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_alpha_release19_shipping_20260723\\Windows",
"targetInstallRoot": "C:\\Program Files\\HyperTwist",
"requiredFailureCount": 0,
"optionalWarningCount": 2,
"checks": [
{
"id": "windows-x64",
"class": "required",
"passed": true,
"summary": "HyperTwist requires a 64-bit Windows operating system.",
"evidence": {
"architecture": "64-bit",
"processArchitecture": "AMD64"
}
},
{
"id": "windows-supported-build",
"class": "required",
"passed": true,
"summary": "HyperTwist requires Windows build 19045 or newer.",
"evidence": {
"caption": "Microsoft Windows 11 Home",
"version": "10.0.22631",
"build": 22631,
"minimumBuild": 19045
}
},
{
"id": "system-memory",
"class": "required",
"passed": true,
"summary": "At least 8 GiB of physical memory is required for the desktop Alpha.",
"evidence": {
"totalBytes": 16893206528,
"minimumBytes": 8589934592
}
},
{
"id": "install-volume-free-space",
"class": "required",
"passed": true,
"summary": "The target volume must have enough free space for installation and first-run caches.",
"evidence": {
"targetInstallRoot": "C:\\Program Files\\HyperTwist",
"availableBytes": 171945615360,
"minimumBytes": 4294967296
}
},
{
"id": "vc-runtime-x64",
"class": "required",
"passed": true,
"summary": "Microsoft Visual C++ 2015-2022 x64 runtime 14.44.35211.0 or newer is required and is bundled by the installer.",
"evidence": {
"registryPath": "HKLM:\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64",
"installed": true,
"version": "14.44.35211.0"
}
},
{
"id": "windows-media-and-graphics-libraries",
"class": "required",
"passed": true,
"summary": "Windows DirectX, XInput, and Media Foundation runtime libraries must be available.",
"evidence": {
"systemDirectory": "C:\\Windows\\system32",
"requiredLibraries": [
"d3d11.dll",
"d3d12.dll",
"dxgi.dll",
"mf.dll",
"mfplat.dll",
"mfreadwrite.dll",
"xinput1_4.dll"
],
"missingLibraries": [
]
}
},
{
"id": "graphics-adapter",
"class": "required",
"passed": true,
"summary": "At least one Windows graphics adapter must be visible.",
"evidence": [
{
"name": "Intel(R) Iris(R) Xe Graphics",
"driverVersion": "31.0.101.3358",
"driverDate": "2022-08-04T02:00:00.0000000+02:00",
"adapterRamBytes": 1073741824,
"status": "OK"
}
]
},
{
"id": "audio-device",
"class": "optional",
"passed": true,
"summary": "An audio device is optional for puzzle play and recommended for narration and speech features.",
"evidence": [
{
"name": "Intel® Smart Sound Technology for Digital Microphones",
"status": "OK"
},
{
"name": "Intel® Smart Sound Technology for USB Audio",
"status": "OK"
},
{
"name": "Intel® Smart Sound Technology for Bluetooth® Audio",
"status": "OK"
},
{
"name": "Audio Device",
"status": "OK"
},
{
"name": "Virtual Audio Cable",
"status": "OK"
}
]
},
{
"id": "openxr-runtime",
"class": "optional",
"passed": false,
"summary": "An active OpenXR runtime is optional for keyboard/mouse play and required only for live headset sessions.",
"evidence": null
},
{
"id": "required-payload-files",
"class": "required",
"passed": true,
"summary": "The installed payload must contain every required executable, runtime, cooked-data, browser, solver, and OpenXR file.",
"evidence": {
"payloadRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_alpha_release19_shipping_20260723\\Windows",
"requiredFileCount": 15,
"cefRuntimeFiles": [
"Engine/Binaries/ThirdParty/CEF3/Win64/128.4.13+ge76af7e+chromium-128.0.6613.138/libcef.dll"
],
"failures": [
]
}
},
{
"id": "desktop-code-signature",
"class": "optional",
"passed": false,
"summary": "The Alpha payload is currently unsigned; production distribution requires Authenticode signing.",
"evidence": {
"status": "NotSigned",
"signer": null
}
},
{
"id": "payload-manifest-parity",
"class": "required",
"passed": true,
"summary": "The installed payload must match the release manifest without missing, truncated, or altered files.",
"evidence": {
"manifestPath": "C:\\HyperTwist_worktrees\\installer-release19-20260726\\validation\\windows-installer-payload-manifest.json",
"manifestVersion": "ht-windows-installer-payload/v1",
"manifestEntryCount": 143,
"declaredFileCount": 143,
"declaredTotalSizeBytes": 1585341236,
"computedTotalSizeBytes": 1585341236,
"hashesVerified": true,
"failures": [
]
}
}
]
}

View file

@ -357,6 +357,7 @@ Current operator delivery:
- `C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19`
- `C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19.zip`
- `C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19-Setup.exe`
Durable pulled evidence:
@ -392,6 +393,12 @@ Final acceptance facts:
`caa45f3d3104a8e5406f493a60c8a136d852bd35e0bd1c8dfbf79738c1343892`
- no fatal signature, package-owned listener, trace-control listener, or
lingering owned packaged process remained
- the later `2026-07-26` installer authority passed with size `851049086`
bytes, SHA-256
`c31ed6b0b2ae1feb449c27e484d72dc2e07ba220e3cdafa4c3605429d638e0ea`,
`143` manifest-owned payload files, and green install, readiness, Shipping
launch, in-place upgrade, second parity, uninstall, user-data preservation,
and cleanup proof
The global-assistant regression fix is part of this exact package. A standalone
speech session no longer requires an active Classic training run, so the fixed
@ -400,6 +407,34 @@ provider-reachability, microphone, and audible-TTS test still requires
intentionally configured services and hardware and is not inferred from
structural automation.
### Release 19 Windows installer authority
Durable evidence:
`docs/generated/alpha_test_20260726_installer`
The installer is now the preferred first-time Alpha delivery while the
validated folder and ZIP remain portable and rollback-friendly alternatives.
It owns:
- x64 Windows 10 22H2 / Windows 11 preflight
- a valid Microsoft-signed Visual C++ 2015-2022 x64 prerequisite
- a complete `143`-file payload manifest with SHA-256 parity
- safe install-directory leaf and non-empty ownership refusal
- Add/Remove Programs plus resolved app/readiness/uninstall shortcuts
- marker-and-registry-gated upgrade and uninstall
- preservation of settings, saves, replays, and diagnostics
The lifecycle harness deliberately used an install path containing spaces. It
proved an unowned non-empty target was refused before prerequisite or payload
mutation, then passed first install, full parity, installed readiness,
stock-Shipping initialization, in-place upgrade, second parity, and uninstall.
No install root, registration, shortcut, listener, or owned process remained.
This is `isolated-current-host` evidence because no clean VM or Windows Sandbox
was available. The executable is `NotSigned`; trusted Authenticode remains a
production gate rather than an inferred Alpha capability.
## Operator diagnostics
The delivered Alpha uses stock Shipping configuration and first-party direct
@ -427,14 +462,15 @@ Never distribute a loose `UnrealHyperTwist.exe`. The executable depends on the
complete staged `Windows` directory, including engine binaries, project
content containers, and runtime DLLs such as `tbbmalloc.dll`.
The approved Alpha distribution shape is:
The approved Alpha distribution shapes are:
- the validated installer for normal first-time operator testing
- an extracted folder for direct operator testing
- a separately verified ZIP of that complete folder
- matching release metadata with SHA-256, byte count, file/entry count, and
validation references
Approved current delivery:
Historical Release 17 rollback delivery:
- folder:
`C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723`
@ -444,6 +480,19 @@ Approved current delivery:
- ZIP facts: `103` entries, `1082134734` bytes, SHA-256
`1ad8e1c0fc967b227ca09fb68c515ed644eca3e4bda07fdf8c378b438000fc42`
Approved current Release 19 delivery:
- folder:
`C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19`
- ZIP:
`C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19.zip`
- ZIP facts: `167` entries, `1093537260` bytes, SHA-256
`630963f96c681fc49130cbc56c6c850008d4cddda7e83f2ee9c077c52d7709a9`
- installer:
`C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19-Setup.exe`
- installer facts: `851049086` bytes, SHA-256
`c31ed6b0b2ae1feb449c27e484d72dc2e07ba220e3cdafa4c3605429d638e0ea`
The superseded July 9 folder and matching ZIP were deleted only after the
replacement folder, archive, critical-entry hashes, and final Downloads launch
were verified. The retirement report is retained at

View file

@ -576,6 +576,15 @@ Routing correction:
succeeded in `60.93` seconds and all `5`
`HyperTwist.PlayerExperience` tests passed, including
`GlobalAssistantSpeechSession`.
- [x] Release 19 Windows installer and isolated-host lifecycle gate on
`2026-07-26`: a first-party NSIS installer now owns x64/Windows preflight,
the signed Microsoft Visual C++ runtime, a complete `143`-file SHA-256
manifest, safe install-root and marker/registry ownership, resolved
shortcuts, readiness reporting, in-place upgrade, and uninstall that
preserves user data. The final `851049086`-byte installer passed first
install, full parity, stock-Shipping launch, upgrade, second parity,
uninstall, and cleanup from a path containing spaces. Durable proof lives in
`docs/generated/alpha_test_20260726_installer`.
**Estimated actions:** 2030
**Estimated time:** 12 days
@ -620,15 +629,18 @@ Routing correction:
## Immediate Next Step
**Current Alpha next move (`2026-07-23`):** Release 19 is now the current
portable-Alpha authority at
`C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19`, while
Release 17 remains rollback authority until operator acceptance. Prioritize
installer/uninstaller, code-signing, clean-machine prerequisite, entitlement,
and update-channel rollout proof next. Separately retain the real-hardware
XR/controller observation and configured-provider AI/STT/TTS audible smoke as
external-condition gates. Do not reopen MagicTile native-renderer widening or
unrelated simulator topology to perform this release engineering.
**Current Alpha next move (`2026-07-26`):** Release 19 is now the current
portable and installed-Alpha authority at
`C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19` and
`C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19-Setup.exe`,
while Release 17 remains rollback authority until operator acceptance.
Installer/uninstaller and isolated-current-host prerequisite proof are closed.
Prioritize trusted Authenticode signing, a genuinely fresh Windows VM install,
entitlement-backed public release URLs, and update-channel/rollback rollout
proof next. Separately retain the real-hardware XR/controller observation and
configured-provider AI/STT/TTS audible smoke as external-condition gates. Do
not reopen MagicTile native-renderer widening or unrelated simulator topology
to perform this release engineering.
**Phase 1 through Phase 5 are now closed on the intended simulator lane:** repo wiring, the
classic-cube renderer/runtime loop, dedicated training maps and materials, solver-guided training,

View file

@ -3880,6 +3880,63 @@ does not stand in for a real microphone, reachable provider, audible TTS
device, or explicit cloud consent; perform that external-service smoke only
when those inputs are intentionally configured.
## Release 19 Windows installer and readiness lane (`2026-07-26`)
The first-party Windows distribution lane is:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\Build-HyperTwistWindowsInstaller.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\Test-HyperTwistWindowsReadiness.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\Test-HyperTwistWindowsInstaller.ps1 -InstallerPath <setup-exe>
```
`packaging\windows\HyperTwistInstaller.nsi` is the installer definition.
`Build-HyperTwistWindowsInstaller.ps1` validates required runtime, cooked,
browser, OpenXR, and exact solver files; rejects payload reparse points;
requires a valid Microsoft signature on the bundled Visual C++ runtime;
excludes PDB/debug material; writes a complete SHA-256 manifest; checks source
readiness; and deletes incomplete output on failure.
The installer:
- requires x64 Windows 10 22H2 or Windows 11
- requires the selected directory leaf to be `HyperTwist`
- refuses a non-empty directory unless the 64-bit product registry and install
marker both prove existing HyperTwist ownership
- installs or repairs the bundled prerequisite before cleaning an owned prior
payload
- registers Add/Remove Programs and all-users shortcuts
- gives the readiness shortcut a per-user report path, complete manifest
validation, and an interactive close prompt
- never launches the desktop process elevated from the finish page
- removes only marker/registry-owned install payload on uninstall
- preserves `%LOCALAPPDATA%\UnrealHyperTwist\Saved`
The lifecycle harness must run from an elevated Windows token. It refuses to
replace a pre-existing registered install, uses a unique validation run,
proves rejection of an unowned non-empty root, performs first install and full
hash parity, validates resolved shortcut targets/arguments, runs installed
readiness, launches stock Shipping with isolated user data and direct
diagnostics, performs in-place upgrade and second parity, then uninstalls and
proves user-data preservation plus install/registry/shortcut cleanup. Stop
only executables whose resolved path is inside that run's install root.
The final Release 19 installer is:
- delivery:
`C:\Users\Anthracite Ace\Downloads\HyperTwist-Alpha-Test-20260723-R19-Setup.exe`
- size: `851049086` bytes
- SHA-256:
`c31ed6b0b2ae1feb449c27e484d72dc2e07ba220e3cdafa4c3605429d638e0ea`
- payload: `143` files and `1585341236` bytes
- lifecycle: `passed`
- evidence: `docs/generated/alpha_test_20260726_installer`
This is `isolated-current-host` proof, not fresh-machine proof. The host had no
Windows Sandbox, clean Hyper-V VM, or base VHD. The Alpha is also unsigned.
Trusted Authenticode signing and a genuinely fresh Windows install remain
release gates.
### Remote-probe hygiene
A locally timed-out or interrupted SSH wrapper can leave its encoded

File diff suppressed because one or more lines are too long

View file

@ -224,6 +224,14 @@ public pages, manuals, and operator closeouts:
the `167`-entry ZIP reopened cleanly, and both the validation-archive and
exact Downloads executables produced fresh semantic
`launch-menu-renderable` frames
- the `2026-07-26` installer continuation then closed per-machine Windows
installation, readiness, in-place upgrade, and uninstall ownership above
that exact Release 19 payload: the final installer carries a complete
`143`-file SHA-256 manifest, bundles a valid Microsoft-signed Visual C++
runtime, refuses unsafe or unowned non-empty targets, preserves user data,
and passed install-to-uninstall lifecycle validation from a path containing
spaces; this remains isolated-current-host evidence rather than a fabricated
fresh-machine or trusted-code-signing claim
- Paddle checkout URLs, public Windows release URLs, production auth/runtime
configuration, and corresponding-source/notices URLs remain deployment
configuration gates, not hardcoded product facts

View file

@ -579,14 +579,24 @@ Current consolidated milestone snapshot:
`-DisablePlugins=MovieRenderPipeline` plus `-SkipCookingEditorContent`,
aggregate validation JSON `result: passed`, both packaged smoke maps green,
and `BuildCookRun time: 1381.37 s`
- Release 19 Windows installer/uninstaller and isolated-host prerequisite
validation are now closed through first-party NSIS, PowerShell build,
readiness, and lifecycle ownership. The final `851049086`-byte installer
carries `143` manifest-owned payload files, bundles a valid Microsoft-signed
Visual C++ runtime, refuses unsafe or unowned non-empty install roots,
preserves user data on uninstall, and passed install, full hash parity,
stock-Shipping launch, in-place upgrade, second parity, uninstall, and
cleanup from a path containing spaces. Durable evidence lives under
`docs/generated/alpha_test_20260726_installer`
- `Phase 10A` structural-only validation deprecation is now closed through
`221` retained `Phase J` files carrying `443` deprecated validation
identifiers and `443` disabled flags with `0` remaining live
`HyperTwist.Validation.*` or `HyperTwist.FirstParty.Validation.*` names
- no implementation roadmap gate remains inside the current `Phase 1` through
`Phase 10` packet set; the remaining Alpha/release-engineering checks are the
operator-connected dashboard visual plus installer/uninstaller, code-signing,
clean-machine prerequisites, entitlement, and update-channel rollout proof,
operator-connected dashboard visual plus trusted code signing, genuinely
fresh-machine proof, entitlement-backed public release URLs, and
update-channel/rollback rollout proof,
while the remaining optional product-widening branches are the
alternative full-browser client path and any later dedicated native
`MagicTile` interaction/runtime replacement work rather than new

View file

@ -0,0 +1,331 @@
Unicode true
!include "MUI2.nsh"
!include "LogicLib.nsh"
!include "WinVer.nsh"
!include "x64.nsh"
!include "FileFunc.nsh"
!ifndef PRODUCT_VERSION
!error "PRODUCT_VERSION is required."
!endif
!ifndef DISPLAY_VERSION
!error "DISPLAY_VERSION is required."
!endif
!ifndef RELEASE_ID
!error "RELEASE_ID is required."
!endif
!ifndef OUTPUT_FILE
!error "OUTPUT_FILE is required."
!endif
!ifndef PAYLOAD_ROOT
!error "PAYLOAD_ROOT is required."
!endif
!ifndef PAYLOAD_MANIFEST
!error "PAYLOAD_MANIFEST is required."
!endif
!ifndef READINESS_SCRIPT
!error "READINESS_SCRIPT is required."
!endif
!ifndef VC_REDIST_PATH
!error "VC_REDIST_PATH is required."
!endif
!ifndef INSTALLER_ICON
!error "INSTALLER_ICON is required."
!endif
!ifndef PAYLOAD_SIZE_KB
!error "PAYLOAD_SIZE_KB is required."
!endif
!define PRODUCT_NAME "HyperTwist"
!define PRODUCT_CHANNEL "Alpha"
!define PRODUCT_PUBLISHER "HyperTwist"
!define PRODUCT_WEBSITE "https://hypertwist.app"
!define PRODUCT_SUPPORT "https://hypertwist.app/support"
!define PRODUCT_REG_KEY "Software\HyperTwist\HyperTwist"
!define PRODUCT_UNINSTALL_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist"
!define INSTALL_MARKER "HyperTwist.install-marker"
Name "${PRODUCT_NAME} ${PRODUCT_CHANNEL}"
Caption "${PRODUCT_NAME} ${DISPLAY_VERSION} Setup"
BrandingText "${PRODUCT_NAME} | Explore dimensions beyond the ordinary"
OutFile "${OUTPUT_FILE}"
InstallDir "$PROGRAMFILES64\HyperTwist"
RequestExecutionLevel admin
CRCCheck force
SetDatablockOptimize on
SetCompressor /SOLID lzma
SetCompressorDictSize 64
ShowInstDetails show
ShowUninstDetails show
ManifestDPIAware true
ManifestSupportedOS all
Icon "${INSTALLER_ICON}"
UninstallIcon "${INSTALLER_ICON}"
VIProductVersion "${PRODUCT_VERSION}"
VIAddVersionKey /LANG=1033 "ProductName" "${PRODUCT_NAME}"
VIAddVersionKey /LANG=1033 "ProductVersion" "${DISPLAY_VERSION}"
VIAddVersionKey /LANG=1033 "CompanyName" "${PRODUCT_PUBLISHER}"
VIAddVersionKey /LANG=1033 "FileDescription" "${PRODUCT_NAME} ${PRODUCT_CHANNEL} Installer"
VIAddVersionKey /LANG=1033 "FileVersion" "${PRODUCT_VERSION}"
VIAddVersionKey /LANG=1033 "LegalCopyright" "Copyright (c) HyperTwist"
VIAddVersionKey /LANG=1033 "OriginalFilename" "HyperTwist-Setup.exe"
!define MUI_ABORTWARNING
!define MUI_ICON "${INSTALLER_ICON}"
!define MUI_UNICON "${INSTALLER_ICON}"
!define MUI_FINISHPAGE_LINK "Visit hypertwist.app"
!define MUI_FINISHPAGE_LINK_LOCATION "${PRODUCT_WEBSITE}"
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
!insertmacro MUI_LANGUAGE "English"
Function .onInit
SetRegView 64
${IfNot} ${RunningX64}
MessageBox MB_OK|MB_ICONSTOP \
"HyperTwist requires 64-bit Windows." \
/SD IDOK
SetErrorLevel 10
Quit
${EndIf}
${IfNot} ${AtLeastWin10}
MessageBox MB_OK|MB_ICONSTOP \
"HyperTwist requires Windows 10 version 22H2 or Windows 11." \
/SD IDOK
SetErrorLevel 11
Quit
${EndIf}
ReadRegStr $1 HKLM "${PRODUCT_REG_KEY}" "InstallLocation"
StrCmp $1 "" install_location_ready
StrCpy $INSTDIR $1
install_location_ready:
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "CurrentBuildNumber"
IntCmp $0 19045 windows_build_ok windows_build_too_old windows_build_ok
windows_build_too_old:
MessageBox MB_OK|MB_ICONSTOP \
"This Windows build is too old for the supported HyperTwist Alpha baseline. Update to Windows 10 22H2 or Windows 11 and try again." \
/SD IDOK
SetErrorLevel 12
Quit
windows_build_ok:
FunctionEnd
Function CleanOwnedPayloadForUpgrade
ReadRegStr $0 HKLM "${PRODUCT_REG_KEY}" "InstallLocation"
StrCmp $0 $INSTDIR registry_owner_confirmed cleanup_done
registry_owner_confirmed:
IfFileExists "$INSTDIR\Installer\${INSTALL_MARKER}" 0 cleanup_done
DetailPrint "Removing the previous HyperTwist payload before upgrade."
RMDir /r "$INSTDIR\Content"
RMDir /r "$INSTDIR\Engine"
RMDir /r "$INSTDIR\UnrealHyperTwist"
RMDir /r "$INSTDIR\Installer"
Delete "$INSTDIR\Manifest_DebugFiles_Win64.txt"
Delete "$INSTDIR\Manifest_NonUFSFiles_Win64.txt"
Delete "$INSTDIR\Manifest_UFSFiles_Win64.txt"
Delete "$INSTDIR\UnrealHyperTwist.exe"
Delete "$INSTDIR\Uninstall HyperTwist.exe"
cleanup_done:
FunctionEnd
Function AssertSafeInstallLocation
${GetFileName} "$INSTDIR" $0
StrCmp $0 "HyperTwist" install_leaf_safe install_location_refused
install_leaf_safe:
ReadRegStr $0 HKLM "${PRODUCT_REG_KEY}" "InstallLocation"
StrCmp $0 $INSTDIR install_registry_owner_candidate install_unowned_scan
install_registry_owner_candidate:
IfFileExists "$INSTDIR\Installer\${INSTALL_MARKER}" install_location_safe install_unowned_scan
install_unowned_scan:
FindFirst $0 $1 "$INSTDIR\*"
install_unowned_scan_next:
StrCmp $1 "" install_unowned_empty
StrCmp $1 "." install_unowned_continue
StrCmp $1 ".." install_unowned_continue
FindClose $0
MessageBox MB_OK|MB_ICONSTOP \
"The selected HyperTwist folder is not empty and is not owned by an existing registered HyperTwist installation. Choose an empty folder named HyperTwist." \
/SD IDOK
SetErrorLevel 14
Abort
install_unowned_continue:
FindNext $0 $1
Goto install_unowned_scan_next
install_unowned_empty:
FindClose $0
Goto install_location_safe
install_location_refused:
MessageBox MB_OK|MB_ICONSTOP \
"The installation folder itself must be named HyperTwist, for example C:\Program Files\HyperTwist or D:\Games\HyperTwist." \
/SD IDOK
SetErrorLevel 13
Abort
install_location_safe:
FunctionEnd
Section -ValidateInstallLocation
Call AssertSafeInstallLocation
SectionEnd
Section -MicrosoftRuntime
SetOutPath "$PLUGINSDIR"
File /oname=vc_redist.x64.exe "${VC_REDIST_PATH}"
DetailPrint "Installing or repairing the Microsoft Visual C++ 2015-2022 x64 runtime."
ExecWait '"$PLUGINSDIR\vc_redist.x64.exe" /install /quiet /norestart' $0
${If} $0 == 3010
DetailPrint "The Microsoft runtime requested a restart."
SetRebootFlag true
${ElseIf} $0 == 1638
DetailPrint "A compatible or newer Microsoft runtime is already installed."
${ElseIf} $0 != 0
MessageBox MB_OK|MB_ICONSTOP \
"Microsoft Visual C++ runtime setup failed with exit code $0. HyperTwist was not installed." \
/SD IDOK
SetErrorLevel 20
Abort
${EndIf}
SectionEnd
Section "!HyperTwist application" SEC_CORE
SectionIn RO
AddSize ${PAYLOAD_SIZE_KB}
; Do not disturb a working prior install until every bundled prerequisite succeeds.
Call CleanOwnedPayloadForUpgrade
SetOutPath "$INSTDIR"
File /r \
/x "*.pdb" \
/x "Manifest_DebugFiles_Win64.txt" \
"${PAYLOAD_ROOT}\*"
SetOutPath "$INSTDIR\Installer"
File /oname=payload-manifest.json "${PAYLOAD_MANIFEST}"
File /oname=Test-HyperTwistWindowsReadiness.ps1 "${READINESS_SCRIPT}"
FileOpen $0 "$INSTDIR\Installer\${INSTALL_MARKER}" w
FileWrite $0 "product=HyperTwist$\r$\n"
FileWrite $0 "release=${RELEASE_ID}$\r$\n"
FileWrite $0 "version=${DISPLAY_VERSION}$\r$\n"
FileClose $0
WriteUninstaller "$INSTDIR\Uninstall HyperTwist.exe"
WriteRegStr HKLM "${PRODUCT_REG_KEY}" "InstallLocation" "$INSTDIR"
WriteRegStr HKLM "${PRODUCT_REG_KEY}" "ReleaseId" "${RELEASE_ID}"
WriteRegStr HKLM "${PRODUCT_REG_KEY}" "DisplayVersion" "${DISPLAY_VERSION}"
WriteRegStr HKLM "${PRODUCT_UNINSTALL_KEY}" "DisplayName" "${PRODUCT_NAME} ${PRODUCT_CHANNEL}"
WriteRegStr HKLM "${PRODUCT_UNINSTALL_KEY}" "DisplayVersion" "${DISPLAY_VERSION}"
WriteRegStr HKLM "${PRODUCT_UNINSTALL_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
WriteRegStr HKLM "${PRODUCT_UNINSTALL_KEY}" "InstallLocation" "$INSTDIR"
WriteRegStr HKLM "${PRODUCT_UNINSTALL_KEY}" "DisplayIcon" "$INSTDIR\UnrealHyperTwist.exe,0"
WriteRegStr HKLM "${PRODUCT_UNINSTALL_KEY}" "URLInfoAbout" "${PRODUCT_WEBSITE}"
WriteRegStr HKLM "${PRODUCT_UNINSTALL_KEY}" "HelpLink" "${PRODUCT_SUPPORT}"
WriteRegStr HKLM "${PRODUCT_UNINSTALL_KEY}" "UninstallString" "$\"$INSTDIR\Uninstall HyperTwist.exe$\""
WriteRegStr HKLM "${PRODUCT_UNINSTALL_KEY}" "QuietUninstallString" "$\"$INSTDIR\Uninstall HyperTwist.exe$\" /S"
WriteRegDWORD HKLM "${PRODUCT_UNINSTALL_KEY}" "EstimatedSize" ${PAYLOAD_SIZE_KB}
WriteRegDWORD HKLM "${PRODUCT_UNINSTALL_KEY}" "NoModify" 1
WriteRegDWORD HKLM "${PRODUCT_UNINSTALL_KEY}" "NoRepair" 1
SetShellVarContext all
CreateDirectory "$SMPROGRAMS\HyperTwist"
CreateShortCut "$SMPROGRAMS\HyperTwist\HyperTwist.lnk" \
"$INSTDIR\UnrealHyperTwist.exe" "" "$INSTDIR\UnrealHyperTwist.exe" 0
CreateShortCut "$SMPROGRAMS\HyperTwist\System Readiness Report.lnk" \
"$SYSDIR\WindowsPowerShell\v1.0\powershell.exe" \
"-NoProfile -ExecutionPolicy Bypass -File $\"$INSTDIR\Installer\Test-HyperTwistWindowsReadiness.ps1$\" -PayloadRoot $\"$INSTDIR$\" -TargetInstallRoot $\"$INSTDIR$\" -ExpectedPayloadManifestPath $\"$INSTDIR\Installer\payload-manifest.json$\" -WaitForUser" \
"$INSTDIR\UnrealHyperTwist.exe" 0
CreateShortCut "$SMPROGRAMS\HyperTwist\Uninstall HyperTwist.lnk" \
"$INSTDIR\Uninstall HyperTwist.exe"
SectionEnd
Section "Desktop shortcut" SEC_DESKTOP
SetShellVarContext all
CreateShortCut "$DESKTOP\HyperTwist.lnk" \
"$INSTDIR\UnrealHyperTwist.exe" "" "$INSTDIR\UnrealHyperTwist.exe" 0
SectionEnd
LangString DESC_SEC_CORE ${LANG_ENGLISH} \
"The complete HyperTwist desktop Alpha, puzzle runtimes, learning studio, browser shell, and local solver data."
LangString DESC_SEC_DESKTOP ${LANG_ENGLISH} \
"Create an all-users HyperTwist shortcut on the desktop."
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_CORE} $(DESC_SEC_CORE)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_DESKTOP} $(DESC_SEC_DESKTOP)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
Section "Uninstall"
SetRegView 64
${GetFileName} "$INSTDIR" $1
StrCmp $1 "HyperTwist" uninstall_leaf_safe uninstall_refused
uninstall_leaf_safe:
ReadRegStr $0 HKLM "${PRODUCT_REG_KEY}" "InstallLocation"
StrCmp $0 $INSTDIR uninstall_registry_owned uninstall_refused
uninstall_registry_owned:
IfFileExists "$INSTDIR\Installer\${INSTALL_MARKER}" uninstall_owned uninstall_refused
uninstall_refused:
MessageBox MB_OK|MB_ICONSTOP \
"HyperTwist registry ownership or the install marker is missing. The uninstaller will not delete an unverified directory." \
/SD IDOK
SetErrorLevel 30
Abort
uninstall_owned:
SetShellVarContext all
Delete "$DESKTOP\HyperTwist.lnk"
RMDir /r "$SMPROGRAMS\HyperTwist"
DeleteRegKey HKLM "${PRODUCT_UNINSTALL_KEY}"
DeleteRegKey HKLM "${PRODUCT_REG_KEY}"
DeleteRegKey /ifempty HKLM "Software\HyperTwist"
RMDir /r "$INSTDIR\Content"
RMDir /r "$INSTDIR\Engine"
RMDir /r "$INSTDIR\UnrealHyperTwist"
Delete "$INSTDIR\Manifest_DebugFiles_Win64.txt"
Delete "$INSTDIR\Manifest_NonUFSFiles_Win64.txt"
Delete "$INSTDIR\Manifest_UFSFiles_Win64.txt"
Delete "$INSTDIR\UnrealHyperTwist.exe"
Delete "$INSTDIR\Installer\payload-manifest.json"
Delete "$INSTDIR\Installer\Test-HyperTwistWindowsReadiness.ps1"
Delete "$INSTDIR\Installer\${INSTALL_MARKER}"
RMDir "$INSTDIR\Installer"
Delete "$INSTDIR\Uninstall HyperTwist.exe"
RMDir "$INSTDIR"
SectionEnd

View file

@ -0,0 +1,464 @@
param(
[string]$PackageRoot = 'C:\HyperTwist_worktrees\phase10validate_packaged_alpha_release19_shipping_20260723',
[string]$OutputDirectory = 'C:\HyperTwist_worktrees\installer-release19-20260726',
[string]$InstallerFileName = 'HyperTwist-Alpha-Test-20260723-R19-Setup.exe',
[string]$ProductVersion = '0.19.0.0',
[string]$DisplayVersion = 'Alpha R19',
[string]$ReleaseId = '20260723-R19',
[string]$MakensisPath = 'C:\Program Files (x86)\NSIS\makensis.exe',
[string]$VcRedistPath = '',
[string]$ReportPath = '',
[switch]$KeepBuildContext
)
$ErrorActionPreference = 'Stop'
$ExpectedSolverTableSizeBytes = 676207080
$ExpectedSolverTableSha256 = 'dc6de3d909a37afe2ba7f1f978543a13eae215bd20d954d66813524a8ca20034'
function Write-Utf8JsonFile {
param(
[Parameter(Mandatory = $true)]
[string]$Path,
[Parameter(Mandatory = $true)]
[object]$Value
)
$ParentPath = Split-Path -Parent $Path
if (-not [string]::IsNullOrWhiteSpace($ParentPath))
{
New-Item -ItemType Directory -Force -Path $ParentPath | Out-Null
}
$Json = $Value | ConvertTo-Json -Depth 12
$Utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText($Path, $Json, $Utf8NoBom)
}
function Resolve-WindowsPayloadRoot {
param(
[Parameter(Mandatory = $true)]
[string]$RootPath
)
$ResolvedRootPath = (Resolve-Path -LiteralPath $RootPath).Path
$WindowsCandidate = Join-Path $ResolvedRootPath 'Windows'
if (Test-Path -LiteralPath (Join-Path $WindowsCandidate 'UnrealHyperTwist.exe') -PathType Leaf)
{
return $WindowsCandidate
}
if (Test-Path -LiteralPath (Join-Path $ResolvedRootPath 'UnrealHyperTwist.exe') -PathType Leaf)
{
return $ResolvedRootPath
}
throw "No packaged Windows payload was found beneath '$RootPath'."
}
function Get-NormalizedRelativePath {
param(
[Parameter(Mandatory = $true)]
[string]$RootPath,
[Parameter(Mandatory = $true)]
[string]$FilePath
)
$RootPrefix = $RootPath.TrimEnd('\', '/') + [System.IO.Path]::DirectorySeparatorChar
$ResolvedFilePath = [System.IO.Path]::GetFullPath($FilePath)
if (-not $ResolvedFilePath.StartsWith(
$RootPrefix,
[System.StringComparison]::OrdinalIgnoreCase))
{
throw "File '$ResolvedFilePath' is outside payload root '$RootPath'."
}
return $ResolvedFilePath.Substring($RootPrefix.Length).Replace('\', '/')
}
function Assert-RequiredPayloadFile {
param(
[Parameter(Mandatory = $true)]
[string]$PayloadRoot,
[Parameter(Mandatory = $true)]
[string]$RelativePath,
[Int64]$ExpectedSizeBytes = -1,
[string]$ExpectedSha256 = ''
)
$RequiredPath = Join-Path $PayloadRoot $RelativePath
if (-not (Test-Path -LiteralPath $RequiredPath -PathType Leaf))
{
throw "Required payload file '$RelativePath' was not found."
}
$RequiredItem = Get-Item -LiteralPath $RequiredPath
if ($RequiredItem.Length -le 0)
{
throw "Required payload file '$RelativePath' is empty."
}
if ($ExpectedSizeBytes -ge 0 -and $RequiredItem.Length -ne $ExpectedSizeBytes)
{
throw "Required payload file '$RelativePath' has an unexpected size."
}
$ActualSha256 = (Get-FileHash -LiteralPath $RequiredPath -Algorithm SHA256).Hash.ToLowerInvariant()
if (-not [string]::IsNullOrWhiteSpace($ExpectedSha256) `
-and $ActualSha256 -ne $ExpectedSha256.ToLowerInvariant())
{
throw "Required payload file '$RelativePath' has an unexpected SHA-256."
}
}
function Resolve-VcRedistPath {
param(
[string]$RequestedPath
)
if (-not [string]::IsNullOrWhiteSpace($RequestedPath))
{
if (-not (Test-Path -LiteralPath $RequestedPath -PathType Leaf))
{
throw "Requested VC++ redistributable '$RequestedPath' was not found."
}
return (Resolve-Path -LiteralPath $RequestedPath).Path
}
$CandidateRoots = @(
'C:\Program Files\Epic Games\UE_5.7\Engine\Extras\Redist',
'C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Redist',
'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Redist'
)
$Candidates = @(
foreach ($CandidateRoot in $CandidateRoots)
{
if (Test-Path -LiteralPath $CandidateRoot)
{
Get-ChildItem -LiteralPath $CandidateRoot `
-Filter 'vc_redist.x64.exe' `
-File `
-Recurse `
-ErrorAction SilentlyContinue
}
}
)
$Candidate = $Candidates |
Sort-Object { [version]$_.VersionInfo.FileVersion } -Descending |
Select-Object -First 1
if ($null -eq $Candidate)
{
throw 'No Microsoft Visual C++ 2015-2022 x64 redistributable was found.'
}
return $Candidate.FullName
}
if (-not (Test-Path -LiteralPath $PackageRoot -PathType Container))
{
throw "Package root '$PackageRoot' was not found."
}
if (-not (Test-Path -LiteralPath $MakensisPath -PathType Leaf))
{
throw "NSIS compiler '$MakensisPath' was not found."
}
try
{
$ParsedProductVersion = [version]$ProductVersion
}
catch
{
throw "ProductVersion '$ProductVersion' is not a valid numeric Windows file version."
}
if ($ParsedProductVersion.Build -lt 0 -or $ParsedProductVersion.Revision -lt 0)
{
throw 'ProductVersion must contain exactly four numeric components.'
}
if ($InstallerFileName -ne [System.IO.Path]::GetFileName($InstallerFileName) `
-or [System.IO.Path]::GetExtension($InstallerFileName) -ne '.exe')
{
throw "InstallerFileName '$InstallerFileName' must be a leaf .exe filename."
}
foreach ($MetadataValue in @($DisplayVersion, $ReleaseId))
{
if ([string]::IsNullOrWhiteSpace($MetadataValue) `
-or $MetadataValue -notmatch '^[A-Za-z0-9][A-Za-z0-9 ._-]{0,63}$')
{
throw "Installer metadata value '$MetadataValue' contains unsupported characters or length."
}
}
$ResolvedPackageRoot = (Resolve-Path -LiteralPath $PackageRoot).Path
$ResolvedPayloadRoot = Resolve-WindowsPayloadRoot -RootPath $ResolvedPackageRoot
$ResolvedOutputDirectory = [System.IO.Path]::GetFullPath($OutputDirectory)
$ResolvedInstallerPath = Join-Path $ResolvedOutputDirectory $InstallerFileName
$PayloadRootPrefix = $ResolvedPayloadRoot.TrimEnd('\') + '\'
if ($ResolvedInstallerPath.StartsWith(
$PayloadRootPrefix,
[System.StringComparison]::OrdinalIgnoreCase))
{
throw 'Installer output must not be written inside its source payload.'
}
$RepositoryRoot = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
$NsiScriptPath = Join-Path $RepositoryRoot 'packaging\windows\HyperTwistInstaller.nsi'
$ReadinessScriptPath = Join-Path $RepositoryRoot 'scripts\Test-HyperTwistWindowsReadiness.ps1'
if (-not (Test-Path -LiteralPath $NsiScriptPath -PathType Leaf))
{
throw "Installer definition '$NsiScriptPath' was not found."
}
if (-not (Test-Path -LiteralPath $ReadinessScriptPath -PathType Leaf))
{
throw "Readiness script '$ReadinessScriptPath' was not found."
}
$ResolvedVcRedistPath = Resolve-VcRedistPath -RequestedPath $VcRedistPath
$VcRedistSignature = Get-AuthenticodeSignature -LiteralPath $ResolvedVcRedistPath
if ($VcRedistSignature.Status -ne [System.Management.Automation.SignatureStatus]::Valid)
{
throw "VC++ redistributable '$ResolvedVcRedistPath' does not have a valid Authenticode signature."
}
$RequiredRelativePaths = @(
'UnrealHyperTwist.exe',
'UnrealHyperTwist\Binaries\Win64\UnrealHyperTwist-Win64-Shipping.exe',
'UnrealHyperTwist\Binaries\Win64\tbb12.dll',
'UnrealHyperTwist\Binaries\Win64\tbbmalloc.dll',
'UnrealHyperTwist\Content\Paks\UnrealHyperTwist-Windows.pak',
'UnrealHyperTwist\Content\Paks\UnrealHyperTwist-Windows.utoc',
'UnrealHyperTwist\Content\Paks\UnrealHyperTwist-Windows.ucas',
'Content\Browser\index.html',
'Content\Browser\src\browser-runtime-bootstrap.js',
'Content\Browser\src\browser-spatial-runtime-fallback.js',
'Content\Browser\dist\browser-spatial-runtime.js'
)
foreach ($RequiredRelativePath in $RequiredRelativePaths)
{
Assert-RequiredPayloadFile `
-PayloadRoot $ResolvedPayloadRoot `
-RelativePath $RequiredRelativePath
}
Assert-RequiredPayloadFile `
-PayloadRoot $ResolvedPayloadRoot `
-RelativePath 'UnrealHyperTwist\Saved\twophase-ht.tbl' `
-ExpectedSizeBytes $ExpectedSolverTableSizeBytes `
-ExpectedSha256 $ExpectedSolverTableSha256
$ReparsePoints = @(
Get-ChildItem -LiteralPath $ResolvedPayloadRoot -Force -Recurse |
Where-Object {
$_.Attributes -band [System.IO.FileAttributes]::ReparsePoint
}
)
if ($ReparsePoints.Count -gt 0)
{
throw (
"The payload contains reparse points, which are not permitted in an installer source: " +
(($ReparsePoints | Select-Object -ExpandProperty FullName) -join ', ')
)
}
New-Item -ItemType Directory -Force -Path $ResolvedOutputDirectory | Out-Null
$ValidationDirectory = Join-Path $ResolvedOutputDirectory 'validation'
$BuildContextDirectory = Join-Path $ResolvedOutputDirectory 'build-context'
New-Item -ItemType Directory -Force -Path $ValidationDirectory | Out-Null
if (Test-Path -LiteralPath $BuildContextDirectory)
{
Remove-Item -LiteralPath $BuildContextDirectory -Recurse -Force
}
New-Item -ItemType Directory -Force -Path $BuildContextDirectory | Out-Null
if ([string]::IsNullOrWhiteSpace($ReportPath))
{
$ReportPath = Join-Path $ValidationDirectory 'windows-installer-build-report.json'
}
$PayloadManifestPath = Join-Path $ValidationDirectory 'windows-installer-payload-manifest.json'
$SourceReadinessReportPath = Join-Path $ValidationDirectory 'windows-installer-source-readiness-report.json'
$MakensisLogPath = Join-Path $ValidationDirectory 'makensis.log'
$InstallerIconPath = Join-Path $BuildContextDirectory 'HyperTwist.ico'
$BuildReport = [ordered]@{
reportVersion = 'ht-windows-installer-build/v1'
generatedAtUtc = [DateTime]::UtcNow.ToString('o')
result = 'failed'
releaseId = $ReleaseId
displayVersion = $DisplayVersion
productVersion = $ProductVersion
packageRoot = $ResolvedPackageRoot
payloadRoot = $ResolvedPayloadRoot
installerPath = $ResolvedInstallerPath
payloadManifestPath = $PayloadManifestPath
sourceReadinessReportPath = $SourceReadinessReportPath
makensisPath = (Resolve-Path -LiteralPath $MakensisPath).Path
makensisLogPath = $MakensisLogPath
vcRedist = $null
excludedPatterns = @('*.pdb', 'Manifest_DebugFiles_Win64.txt')
payloadFileCount = 0
payloadSizeBytes = 0
installerSizeBytes = 0
installerSha256 = $null
installerSignatureStatus = $null
incompleteInstallerRemoved = $false
error = $null
}
try
{
$VcRedistItem = Get-Item -LiteralPath $ResolvedVcRedistPath
$BuildReport.vcRedist = [pscustomobject][ordered]@{
path = $ResolvedVcRedistPath
version = [string]$VcRedistItem.VersionInfo.FileVersion
sizeBytes = [Int64]$VcRedistItem.Length
sha256 = (Get-FileHash -LiteralPath $ResolvedVcRedistPath -Algorithm SHA256).Hash.ToLowerInvariant()
signatureStatus = $VcRedistSignature.Status.ToString()
signer = [string]$VcRedistSignature.SignerCertificate.Subject
}
$PayloadFiles = @(
Get-ChildItem -LiteralPath $ResolvedPayloadRoot -Force -File -Recurse |
Where-Object {
$_.Extension -ne '.pdb' `
-and $_.Name -ne 'Manifest_DebugFiles_Win64.txt'
} |
Sort-Object FullName
)
$PayloadManifestEntries = @(
foreach ($PayloadFile in $PayloadFiles)
{
[pscustomobject][ordered]@{
relativePath = Get-NormalizedRelativePath `
-RootPath $ResolvedPayloadRoot `
-FilePath $PayloadFile.FullName
sizeBytes = [Int64]$PayloadFile.Length
sha256 = (Get-FileHash -LiteralPath $PayloadFile.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
}
}
)
$PayloadSizeBytes = [Int64](($PayloadFiles | Measure-Object Length -Sum).Sum)
$PayloadManifest = [pscustomobject][ordered]@{
manifestVersion = 'ht-windows-installer-payload/v1'
generatedAtUtc = [DateTime]::UtcNow.ToString('o')
releaseId = $ReleaseId
sourcePayloadRoot = $ResolvedPayloadRoot
excludedPatterns = @('*.pdb', 'Manifest_DebugFiles_Win64.txt')
fileCount = $PayloadManifestEntries.Count
totalSizeBytes = $PayloadSizeBytes
files = $PayloadManifestEntries
}
Write-Utf8JsonFile -Path $PayloadManifestPath -Value $PayloadManifest
$BuildReport.payloadFileCount = $PayloadManifestEntries.Count
$BuildReport.payloadSizeBytes = $PayloadSizeBytes
$ReadinessReport = & $ReadinessScriptPath `
-PayloadRoot $ResolvedPayloadRoot `
-TargetInstallRoot (Join-Path ([Environment]::GetFolderPath('ProgramFiles')) 'HyperTwist') `
-ExpectedPayloadManifestPath $PayloadManifestPath `
-ReportPath $SourceReadinessReportPath `
-VerifyPayloadHashes `
-PassThru
if ($ReadinessReport.result -eq 'blocked')
{
throw 'Source payload or Windows host readiness is blocked.'
}
Add-Type -AssemblyName System.Drawing
$PayloadExecutablePath = Join-Path $ResolvedPayloadRoot 'UnrealHyperTwist.exe'
$InstallerIcon = [System.Drawing.Icon]::ExtractAssociatedIcon($PayloadExecutablePath)
if ($null -eq $InstallerIcon)
{
throw "No application icon could be extracted from '$PayloadExecutablePath'."
}
$IconStream = [System.IO.File]::Open(
$InstallerIconPath,
[System.IO.FileMode]::Create,
[System.IO.FileAccess]::Write,
[System.IO.FileShare]::None
)
try
{
$InstallerIcon.Save($IconStream)
}
finally
{
$IconStream.Dispose()
$InstallerIcon.Dispose()
}
if (Test-Path -LiteralPath $ResolvedInstallerPath)
{
Remove-Item -LiteralPath $ResolvedInstallerPath -Force
}
$PayloadSizeKilobytes = [Math]::Ceiling($PayloadSizeBytes / 1KB)
$MakensisArguments = @(
'/V4',
"/DPRODUCT_VERSION=$ProductVersion",
"/DDISPLAY_VERSION=$DisplayVersion",
"/DRELEASE_ID=$ReleaseId",
"/DOUTPUT_FILE=$ResolvedInstallerPath",
"/DPAYLOAD_ROOT=$ResolvedPayloadRoot",
"/DPAYLOAD_MANIFEST=$PayloadManifestPath",
"/DREADINESS_SCRIPT=$ReadinessScriptPath",
"/DVC_REDIST_PATH=$ResolvedVcRedistPath",
"/DINSTALLER_ICON=$InstallerIconPath",
"/DPAYLOAD_SIZE_KB=$PayloadSizeKilobytes",
$NsiScriptPath
)
$MakensisOutput = @(
& $MakensisPath @MakensisArguments 2>&1 |
ForEach-Object {
$Line = $_.ToString()
[Console]::Out.WriteLine($Line)
$Line
}
)
$MakensisExitCode = $LASTEXITCODE
[System.IO.File]::WriteAllLines(
$MakensisLogPath,
$MakensisOutput,
(New-Object System.Text.UTF8Encoding($false))
)
if ($MakensisExitCode -ne 0)
{
throw "NSIS compilation failed with exit code $MakensisExitCode."
}
if (-not (Test-Path -LiteralPath $ResolvedInstallerPath -PathType Leaf))
{
throw "NSIS did not create '$ResolvedInstallerPath'."
}
$InstallerItem = Get-Item -LiteralPath $ResolvedInstallerPath
if ($InstallerItem.Length -le 0)
{
throw 'The generated installer is empty.'
}
$InstallerSignature = Get-AuthenticodeSignature -LiteralPath $ResolvedInstallerPath
$BuildReport.installerSizeBytes = [Int64]$InstallerItem.Length
$BuildReport.installerSha256 = (Get-FileHash -LiteralPath $ResolvedInstallerPath -Algorithm SHA256).Hash.ToLowerInvariant()
$BuildReport.installerSignatureStatus = $InstallerSignature.Status.ToString()
$BuildReport.result = 'passed'
}
catch
{
$BuildReport.error = $_.Exception.Message
throw
}
finally
{
if ($BuildReport.result -ne 'passed' `
-and (Test-Path -LiteralPath $ResolvedInstallerPath -PathType Leaf))
{
Remove-Item -LiteralPath $ResolvedInstallerPath -Force
$BuildReport.incompleteInstallerRemoved = $true
}
Write-Utf8JsonFile -Path $ReportPath -Value $BuildReport
if (-not $KeepBuildContext -and (Test-Path -LiteralPath $BuildContextDirectory))
{
Remove-Item -LiteralPath $BuildContextDirectory -Recurse -Force
}
}
[Console]::Out.WriteLine('HyperTwist Windows installer built successfully.')
[Console]::Out.WriteLine("Installer: $ResolvedInstallerPath")
[Console]::Out.WriteLine("SHA-256: $($BuildReport.installerSha256)")
[Console]::Out.WriteLine("Report: $([System.IO.Path]::GetFullPath($ReportPath))")

View file

@ -0,0 +1,707 @@
param(
[Parameter(Mandatory = $true)]
[string]$InstallerPath,
[string]$ValidationRoot = 'C:\HyperTwistInstallerValidation',
[string]$ReportPath = '',
[int]$LaunchSmokeSeconds = 20,
[switch]$KeepInstalled
)
$ErrorActionPreference = 'Stop'
function Write-Utf8JsonFile {
param(
[Parameter(Mandatory = $true)]
[string]$Path,
[Parameter(Mandatory = $true)]
[object]$Value
)
$ParentPath = Split-Path -Parent $Path
if (-not [string]::IsNullOrWhiteSpace($ParentPath))
{
New-Item -ItemType Directory -Force -Path $ParentPath | Out-Null
}
$Json = $Value | ConvertTo-Json -Depth 12
$Utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText($Path, $Json, $Utf8NoBom)
}
function Invoke-InstallerProcess {
param(
[Parameter(Mandatory = $true)]
[string]$ExecutablePath,
[Parameter(Mandatory = $true)]
[string[]]$Arguments,
[Parameter(Mandatory = $true)]
[string]$Label
)
$Process = Start-Process `
-FilePath $ExecutablePath `
-ArgumentList $Arguments `
-PassThru `
-Wait
if ($Process.ExitCode -notin @(0, 3010))
{
throw "$Label failed with exit code $($Process.ExitCode)."
}
return [int]$Process.ExitCode
}
function Invoke-InstallerProcessForExitCode {
param(
[Parameter(Mandatory = $true)]
[string]$ExecutablePath,
[Parameter(Mandatory = $true)]
[string[]]$Arguments
)
$Process = Start-Process `
-FilePath $ExecutablePath `
-ArgumentList $Arguments `
-PassThru `
-Wait
return [int]$Process.ExitCode
}
function Resolve-PayloadRelativeFilePath {
param(
[Parameter(Mandatory = $true)]
[string]$RootPath,
[Parameter(Mandatory = $true)]
[string]$RelativePath
)
if ([string]::IsNullOrWhiteSpace($RelativePath))
{
throw 'A payload-relative path cannot be empty.'
}
$WindowsRelativePath = $RelativePath.Replace('/', '\')
if ([System.IO.Path]::IsPathRooted($WindowsRelativePath))
{
throw "Payload-relative path '$RelativePath' must not be rooted."
}
$ResolvedRootPath = [System.IO.Path]::GetFullPath($RootPath)
$RootPrefix = $ResolvedRootPath.TrimEnd('\', '/') + [System.IO.Path]::DirectorySeparatorChar
$ResolvedFilePath = [System.IO.Path]::GetFullPath(
(Join-Path $ResolvedRootPath $WindowsRelativePath)
)
if (-not $ResolvedFilePath.StartsWith(
$RootPrefix,
[System.StringComparison]::OrdinalIgnoreCase))
{
throw "Payload-relative path '$RelativePath' escapes root '$RootPath'."
}
return $ResolvedFilePath
}
function Test-PayloadManifest {
param(
[Parameter(Mandatory = $true)]
[string]$InstallRoot,
[Parameter(Mandatory = $true)]
[string]$ManifestPath
)
$Manifest = Get-Content -LiteralPath $ManifestPath -Raw | ConvertFrom-Json
$Failures = New-Object System.Collections.ArrayList
$ExpectedPaths = @{}
foreach ($Entry in @($Manifest.files))
{
$EntryRelativePath = [string]$Entry.relativePath
$EntryPathKey = $EntryRelativePath.Replace('\', '/').ToLowerInvariant()
if ($ExpectedPaths.ContainsKey($EntryPathKey))
{
[void]$Failures.Add("duplicate:$EntryRelativePath")
continue
}
$ExpectedPaths[$EntryPathKey] = $true
try
{
$InstalledPath = Resolve-PayloadRelativeFilePath `
-RootPath $InstallRoot `
-RelativePath $EntryRelativePath
}
catch
{
[void]$Failures.Add("invalid-path:$EntryRelativePath")
continue
}
if (-not (Test-Path -LiteralPath $InstalledPath -PathType Leaf))
{
[void]$Failures.Add("missing:$EntryRelativePath")
continue
}
$InstalledItem = Get-Item -LiteralPath $InstalledPath
if ($InstalledItem.Length -ne [Int64]$Entry.sizeBytes)
{
[void]$Failures.Add("size:$EntryRelativePath")
continue
}
$InstalledHash = (Get-FileHash -LiteralPath $InstalledPath -Algorithm SHA256).Hash.ToLowerInvariant()
if ($InstalledHash -ne ([string]$Entry.sha256).ToLowerInvariant())
{
[void]$Failures.Add("sha256:$EntryRelativePath")
}
}
$AllowedInstallerPaths = @{}
@(
'Installer/payload-manifest.json',
'Installer/Test-HyperTwistWindowsReadiness.ps1',
'Installer/HyperTwist.install-marker',
'Uninstall HyperTwist.exe'
) | ForEach-Object { $AllowedInstallerPaths[$_.ToLowerInvariant()] = $true }
$InstallRootPrefix = [System.IO.Path]::GetFullPath($InstallRoot).TrimEnd('\') + '\'
$UnexpectedFiles = @(
Get-ChildItem -LiteralPath $InstallRoot -File -Force -Recurse |
ForEach-Object {
$RelativePath = $_.FullName.Substring($InstallRootPrefix.Length).Replace('\', '/')
$RelativePathKey = $RelativePath.ToLowerInvariant()
if (-not $ExpectedPaths.ContainsKey($RelativePathKey) `
-and -not $AllowedInstallerPaths.ContainsKey($RelativePathKey))
{
$RelativePath
}
}
)
$UnexpectedPdbFiles = @(
Get-ChildItem -LiteralPath $InstallRoot -Filter '*.pdb' -File -Recurse -ErrorAction SilentlyContinue
)
return [pscustomobject][ordered]@{
passed = $Failures.Count -eq 0 `
-and $UnexpectedPdbFiles.Count -eq 0 `
-and $UnexpectedFiles.Count -eq 0
manifestEntryCount = @($Manifest.files).Count
failureCount = $Failures.Count
failures = @($Failures)
unexpectedFiles = $UnexpectedFiles
unexpectedPdbFiles = @($UnexpectedPdbFiles | Select-Object -ExpandProperty FullName)
}
}
function Get-OwnedProcessRecords {
param(
[Parameter(Mandatory = $true)]
[string]$InstallRoot
)
$InstallPrefix = $InstallRoot.TrimEnd('\') + '\'
return @(
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Where-Object {
-not [string]::IsNullOrWhiteSpace([string]$_.ExecutablePath) `
-and ([string]$_.ExecutablePath).StartsWith(
$InstallPrefix,
[System.StringComparison]::OrdinalIgnoreCase)
}
)
}
function Stop-OwnedProcesses {
param(
[Parameter(Mandatory = $true)]
[string]$InstallRoot
)
$StoppedProcessIds = New-Object 'System.Collections.Generic.HashSet[int]'
for ($Attempt = 0; $Attempt -lt 8; $Attempt += 1)
{
$OwnedProcesses = @(Get-OwnedProcessRecords -InstallRoot $InstallRoot)
if ($OwnedProcesses.Count -eq 0)
{
break
}
foreach ($OwnedProcess in $OwnedProcesses)
{
Stop-Process -Id ([int]$OwnedProcess.ProcessId) -Force -ErrorAction SilentlyContinue
[void]$StoppedProcessIds.Add([int]$OwnedProcess.ProcessId)
}
Start-Sleep -Milliseconds 250
}
return @($StoppedProcessIds)
}
function Invoke-InstalledLaunchSmoke {
param(
[Parameter(Mandatory = $true)]
[string]$InstallRoot,
[Parameter(Mandatory = $true)]
[string]$EvidenceDirectory,
[int]$SmokeSeconds
)
$ExecutablePath = Join-Path $InstallRoot 'UnrealHyperTwist.exe'
$RuntimeLogPath = Join-Path $EvidenceDirectory 'installed-runtime.log'
$DiagnosticsPath = Join-Path $EvidenceDirectory 'installed-runtime.hyperdiagnostics.log'
$UserDirectory = Join-Path $EvidenceDirectory 'isolated-user'
New-Item -ItemType Directory -Force -Path $UserDirectory | Out-Null
$Arguments = @(
'-NullRHI',
'-nosound',
'-windowed',
'-ResX=1280',
'-ResY=720',
'-log',
'-FORCELOGFLUSH',
"-abslog=$RuntimeLogPath",
"-HyperTwistDiagnosticsLog=`"$DiagnosticsPath`"",
"-UserDir=`"$UserDirectory`""
)
$StartedAtUtc = [DateTime]::UtcNow
$LaunchProcess = Start-Process `
-FilePath $ExecutablePath `
-ArgumentList $Arguments `
-PassThru
Start-Sleep -Seconds $SmokeSeconds
$OwnedProcesses = @(Get-OwnedProcessRecords -InstallRoot $InstallRoot)
$OwnedProcessIds = @($OwnedProcesses | Select-Object -ExpandProperty ProcessId)
$ListeningEndpoints = @(
if ($OwnedProcessIds.Count -gt 0)
{
Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue |
Where-Object { $OwnedProcessIds -contains [int]$_.OwningProcess } |
ForEach-Object {
[pscustomobject][ordered]@{
processId = [int]$_.OwningProcess
localAddress = [string]$_.LocalAddress
localPort = [int]$_.LocalPort
}
}
}
)
$FatalPatterns = @(
'Fatal error:',
'LowLevelFatalError',
'Assertion failed:',
'Critical error:',
'Unhandled Exception:',
'StaticShutdownAfterError'
)
$RuntimeLines = @(
if (Test-Path -LiteralPath $RuntimeLogPath)
{
Get-Content -LiteralPath $RuntimeLogPath
}
)
$DiagnosticsLines = @(
if (Test-Path -LiteralPath $DiagnosticsPath)
{
Get-Content -LiteralPath $DiagnosticsPath
}
)
$FatalLines = @(
@($RuntimeLines + $DiagnosticsLines) |
Where-Object {
$Line = $_.ToString()
@($FatalPatterns | Where-Object { $Line -like "*$_*" }).Count -gt 0
}
)
$Initialized = @(
$DiagnosticsLines |
Where-Object {
$_.ToString().IndexOf(
'[Process] HyperTwist runtime module initialized.',
[System.StringComparison]::OrdinalIgnoreCase
) -ge 0
}
).Count -gt 0
$StoppedProcessIds = @(Stop-OwnedProcesses -InstallRoot $InstallRoot)
$Passed = $OwnedProcessIds.Count -gt 0 `
-and (Test-Path -LiteralPath $DiagnosticsPath -PathType Leaf) `
-and $Initialized `
-and $FatalLines.Count -eq 0 `
-and @($ListeningEndpoints | Where-Object { $_.localPort -eq 1985 }).Count -eq 0
return [pscustomobject][ordered]@{
passed = $Passed
startedAtUtc = $StartedAtUtc.ToString('o')
launcherProcessId = [int]$LaunchProcess.Id
ownedProcessIds = $OwnedProcessIds
stoppedProcessIds = $StoppedProcessIds
runtimeLogPath = $RuntimeLogPath
diagnosticsPath = $DiagnosticsPath
diagnosticsInitialized = $Initialized
fatalLines = $FatalLines
listeningEndpoints = $ListeningEndpoints
}
}
function Get-ShortcutEvidence {
param(
[Parameter(Mandatory = $true)]
[string]$Path
)
if (-not (Test-Path -LiteralPath $Path -PathType Leaf))
{
return [pscustomobject][ordered]@{
path = $Path
exists = $false
targetPath = $null
arguments = $null
}
}
$Shell = New-Object -ComObject WScript.Shell
$Shortcut = $null
try
{
$Shortcut = $Shell.CreateShortcut($Path)
return [pscustomobject][ordered]@{
path = $Path
exists = $true
targetPath = [string]$Shortcut.TargetPath
arguments = [string]$Shortcut.Arguments
}
}
finally
{
if ($null -ne $Shortcut)
{
[void][System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($Shortcut)
}
[void][System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($Shell)
}
}
$CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$CurrentPrincipal = New-Object Security.Principal.WindowsPrincipal($CurrentIdentity)
if (-not $CurrentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
{
throw 'Installer lifecycle validation requires an elevated Windows session.'
}
if (-not (Test-Path -LiteralPath $InstallerPath -PathType Leaf))
{
throw "Installer '$InstallerPath' was not found."
}
$ResolvedInstallerPath = (Resolve-Path -LiteralPath $InstallerPath).Path
$ResolvedValidationRoot = [System.IO.Path]::GetFullPath($ValidationRoot)
$RunId = [DateTime]::UtcNow.ToString('yyyyMMddTHHmmssZ')
$RunDirectory = Join-Path $ResolvedValidationRoot $RunId
$InstallRoot = Join-Path $RunDirectory 'HyperTwist'
$EvidenceDirectory = Join-Path $RunDirectory 'evidence'
New-Item -ItemType Directory -Force -Path $EvidenceDirectory | Out-Null
if ([string]::IsNullOrWhiteSpace($ReportPath))
{
$ReportPath = Join-Path $EvidenceDirectory 'windows-installer-lifecycle-report.json'
}
$RepositoryRoot = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
$ReadinessScriptPath = Join-Path $RepositoryRoot 'scripts\Test-HyperTwistWindowsReadiness.ps1'
$UninstallRegistryPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\HyperTwist'
$ProductRegistryPath = 'HKLM:\SOFTWARE\HyperTwist\HyperTwist'
$CommonProgramsPath = [Environment]::GetFolderPath('CommonPrograms')
$CommonDesktopPath = [Environment]::GetFolderPath('CommonDesktopDirectory')
$StartMenuDirectory = Join-Path $CommonProgramsPath 'HyperTwist'
$DesktopShortcutPath = Join-Path $CommonDesktopPath 'HyperTwist.lnk'
if (Test-Path -LiteralPath $UninstallRegistryPath)
{
throw 'A registered HyperTwist installation already exists; validation will not replace it.'
}
$InstallerItem = Get-Item -LiteralPath $ResolvedInstallerPath
$Report = [ordered]@{
reportVersion = 'ht-windows-installer-lifecycle/v1'
generatedAtUtc = [DateTime]::UtcNow.ToString('o')
result = 'failed'
validationClass = 'isolated-current-host'
cleanMachineAvailable = $false
cleanMachineBoundary = 'No Windows Sandbox, clean Hyper-V VM, or base VHD was available on the validation host.'
installerPath = $ResolvedInstallerPath
installerSizeBytes = [Int64]$InstallerItem.Length
installerSha256 = (Get-FileHash -LiteralPath $ResolvedInstallerPath -Algorithm SHA256).Hash.ToLowerInvariant()
installerSignatureStatus = (Get-AuthenticodeSignature -LiteralPath $ResolvedInstallerPath).Status.ToString()
installRoot = $InstallRoot
unownedNonEmptyRefusal = $null
firstInstallExitCode = $null
upgradeInstallExitCode = $null
payloadParityAfterFirstInstall = $null
payloadParityAfterUpgrade = $null
readiness = $null
launchSmoke = $null
uninstallExitCode = $null
registrationVerified = $false
shortcutsVerified = $false
shortcutDetails = $null
userDataPreserved = $false
installRootRemoved = $false
registrationRemoved = $false
shortcutsRemoved = $false
cleanupCompleted = $false
error = $null
}
$UninstallerPath = Join-Path $InstallRoot 'Uninstall HyperTwist.exe'
$SentinelDirectory = Join-Path $env:LOCALAPPDATA 'UnrealHyperTwist\Saved\InstallerValidation'
$SentinelPath = Join-Path $SentinelDirectory "$RunId.txt"
$Installed = $false
try
{
New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null
$UnownedSentinelPath = Join-Path $InstallRoot 'unrelated-owner-sentinel.txt'
[System.IO.File]::WriteAllText(
$UnownedSentinelPath,
"Unowned install-root refusal sentinel $RunId",
(New-Object System.Text.UTF8Encoding($false))
)
$UnownedRefusalExitCode = Invoke-InstallerProcessForExitCode `
-ExecutablePath $ResolvedInstallerPath `
-Arguments @('/S', "/D=$InstallRoot")
if ($UnownedRefusalExitCode -eq 0)
{
$Installed = Test-Path -LiteralPath $UninstallerPath -PathType Leaf
}
$Report.unownedNonEmptyRefusal = [pscustomobject][ordered]@{
passed = $UnownedRefusalExitCode -eq 14 `
-and (Test-Path -LiteralPath $UnownedSentinelPath -PathType Leaf) `
-and -not (Test-Path -LiteralPath $UninstallRegistryPath) `
-and -not (Test-Path -LiteralPath $ProductRegistryPath)
exitCode = $UnownedRefusalExitCode
expectedExitCode = 14
sentinelPreserved = Test-Path -LiteralPath $UnownedSentinelPath -PathType Leaf
registrationAbsent = `
-not (Test-Path -LiteralPath $UninstallRegistryPath) `
-and -not (Test-Path -LiteralPath $ProductRegistryPath)
}
if (-not $Report.unownedNonEmptyRefusal.passed)
{
throw 'The installer did not safely refuse an unowned non-empty installation root.'
}
Remove-Item -LiteralPath $InstallRoot -Recurse -Force
$Report.firstInstallExitCode = Invoke-InstallerProcess `
-ExecutablePath $ResolvedInstallerPath `
-Arguments @('/S', "/D=$InstallRoot") `
-Label 'Silent installer'
$Installed = $true
$ManifestPath = Join-Path $InstallRoot 'Installer\payload-manifest.json'
if (-not (Test-Path -LiteralPath $ManifestPath -PathType Leaf))
{
throw 'The installed payload manifest is missing.'
}
$Report.payloadParityAfterFirstInstall = Test-PayloadManifest `
-InstallRoot $InstallRoot `
-ManifestPath $ManifestPath
if (-not $Report.payloadParityAfterFirstInstall.passed)
{
throw 'Installed payload parity failed after first install.'
}
$RegisteredProduct = Get-ItemProperty -LiteralPath $UninstallRegistryPath
$RegisteredOwner = Get-ItemProperty -LiteralPath $ProductRegistryPath
$Report.registrationVerified = `
[System.IO.Path]::GetFullPath([string]$RegisteredProduct.InstallLocation) -eq $InstallRoot `
-and [System.IO.Path]::GetFullPath([string]$RegisteredOwner.InstallLocation) -eq $InstallRoot
if (-not $Report.registrationVerified)
{
throw 'Add/Remove Programs or product registration does not match the isolated install root.'
}
$ExpectedApplicationPath = Join-Path $InstallRoot 'UnrealHyperTwist.exe'
$ExpectedReadinessScriptPath = Join-Path $InstallRoot 'Installer\Test-HyperTwistWindowsReadiness.ps1'
$ExpectedManifestPath = Join-Path $InstallRoot 'Installer\payload-manifest.json'
$ExpectedUninstallerPath = Join-Path $InstallRoot 'Uninstall HyperTwist.exe'
$ApplicationShortcut = Get-ShortcutEvidence `
-Path (Join-Path $StartMenuDirectory 'HyperTwist.lnk')
$ReadinessShortcut = Get-ShortcutEvidence `
-Path (Join-Path $StartMenuDirectory 'System Readiness Report.lnk')
$UninstallShortcut = Get-ShortcutEvidence `
-Path (Join-Path $StartMenuDirectory 'Uninstall HyperTwist.lnk')
$DesktopShortcut = Get-ShortcutEvidence -Path $DesktopShortcutPath
$Report.shortcutDetails = [pscustomobject][ordered]@{
application = $ApplicationShortcut
readiness = $ReadinessShortcut
uninstall = $UninstallShortcut
desktop = $DesktopShortcut
}
$Report.shortcutsVerified = `
$ApplicationShortcut.exists `
-and $ApplicationShortcut.targetPath -eq $ExpectedApplicationPath `
-and $ReadinessShortcut.exists `
-and $ReadinessShortcut.targetPath.EndsWith(
'\WindowsPowerShell\v1.0\powershell.exe',
[System.StringComparison]::OrdinalIgnoreCase
) `
-and $ReadinessShortcut.arguments.Contains($ExpectedReadinessScriptPath) `
-and $ReadinessShortcut.arguments.Contains($ExpectedManifestPath) `
-and $ReadinessShortcut.arguments.Contains('-WaitForUser') `
-and $UninstallShortcut.exists `
-and $UninstallShortcut.targetPath -eq $ExpectedUninstallerPath `
-and $DesktopShortcut.exists `
-and $DesktopShortcut.targetPath -eq $ExpectedApplicationPath
if (-not $Report.shortcutsVerified)
{
throw 'One or more installer-managed shortcuts are missing or have incorrect targets or arguments.'
}
$ReadinessReportPath = Join-Path $EvidenceDirectory 'installed-readiness-report.json'
$Report.readiness = & $ReadinessScriptPath `
-PayloadRoot $InstallRoot `
-TargetInstallRoot $InstallRoot `
-ExpectedPayloadManifestPath $ManifestPath `
-ReportPath $ReadinessReportPath `
-VerifyPayloadHashes `
-PassThru
if ($Report.readiness.result -eq 'blocked')
{
throw 'Installed-host readiness validation is blocked.'
}
New-Item -ItemType Directory -Force -Path $SentinelDirectory | Out-Null
[System.IO.File]::WriteAllText(
$SentinelPath,
"HyperTwist installer validation sentinel $RunId",
(New-Object System.Text.UTF8Encoding($false))
)
$Report.launchSmoke = Invoke-InstalledLaunchSmoke `
-InstallRoot $InstallRoot `
-EvidenceDirectory $EvidenceDirectory `
-SmokeSeconds $LaunchSmokeSeconds
if (-not $Report.launchSmoke.passed)
{
throw 'Installed executable launch smoke failed.'
}
$Report.upgradeInstallExitCode = Invoke-InstallerProcess `
-ExecutablePath $ResolvedInstallerPath `
-Arguments @('/S', "/D=$InstallRoot") `
-Label 'Silent in-place upgrade'
$ManifestPath = Join-Path $InstallRoot 'Installer\payload-manifest.json'
$Report.payloadParityAfterUpgrade = Test-PayloadManifest `
-InstallRoot $InstallRoot `
-ManifestPath $ManifestPath
if (-not $Report.payloadParityAfterUpgrade.passed)
{
throw 'Installed payload parity failed after in-place upgrade.'
}
if (-not $KeepInstalled)
{
$Report.uninstallExitCode = Invoke-InstallerProcess `
-ExecutablePath $UninstallerPath `
-Arguments @('/S') `
-Label 'Silent uninstaller'
$Installed = $false
Start-Sleep -Seconds 3
$Report.userDataPreserved = Test-Path -LiteralPath $SentinelPath -PathType Leaf
$Report.installRootRemoved = -not (Test-Path -LiteralPath $InstallRoot)
$Report.registrationRemoved = `
-not (Test-Path -LiteralPath $UninstallRegistryPath) `
-and -not (Test-Path -LiteralPath $ProductRegistryPath)
$Report.shortcutsRemoved = `
-not (Test-Path -LiteralPath $StartMenuDirectory) `
-and -not (Test-Path -LiteralPath $DesktopShortcutPath)
if (-not $Report.userDataPreserved)
{
throw 'Default uninstall removed current-user data unexpectedly.'
}
if (-not $Report.installRootRemoved)
{
throw 'The isolated install root remains after uninstall.'
}
if (-not $Report.registrationRemoved)
{
throw 'Installer registry state remains after uninstall.'
}
if (-not $Report.shortcutsRemoved)
{
throw 'Installer-managed shortcuts remain after uninstall.'
}
}
$Report.cleanupCompleted = -not $KeepInstalled
$Report.result = 'passed'
}
catch
{
$Report.error = $_.Exception.Message
throw
}
finally
{
[void](Stop-OwnedProcesses -InstallRoot $InstallRoot)
if ($Installed -and -not $KeepInstalled -and (Test-Path -LiteralPath $UninstallerPath -PathType Leaf))
{
try
{
[void](Invoke-InstallerProcess `
-ExecutablePath $UninstallerPath `
-Arguments @('/S') `
-Label 'Failure-cleanup uninstaller')
Start-Sleep -Seconds 3
$Installed = $false
}
catch
{
if ([string]::IsNullOrWhiteSpace([string]$Report.error))
{
$Report.error = $_.Exception.Message
}
else
{
$Report.error = "$($Report.error) | Cleanup: $($_.Exception.Message)"
}
}
}
if (-not $KeepInstalled `
-and (Test-Path -LiteralPath $InstallRoot) `
-and -not (Test-Path -LiteralPath $UninstallRegistryPath) `
-and -not (Test-Path -LiteralPath $ProductRegistryPath))
{
Remove-Item -LiteralPath $InstallRoot -Recurse -Force
}
if (Test-Path -LiteralPath $SentinelPath)
{
Remove-Item -LiteralPath $SentinelPath -Force
}
if ((Test-Path -LiteralPath $SentinelDirectory) `
-and @(Get-ChildItem -LiteralPath $SentinelDirectory -Force).Count -eq 0)
{
Remove-Item -LiteralPath $SentinelDirectory -Force
}
if (-not $KeepInstalled)
{
$Report.installRootRemoved = -not (Test-Path -LiteralPath $InstallRoot)
$Report.registrationRemoved = `
-not (Test-Path -LiteralPath $UninstallRegistryPath) `
-and -not (Test-Path -LiteralPath $ProductRegistryPath)
$Report.shortcutsRemoved = `
-not (Test-Path -LiteralPath $StartMenuDirectory) `
-and -not (Test-Path -LiteralPath $DesktopShortcutPath)
$Report.cleanupCompleted = `
-not $Installed `
-and $Report.installRootRemoved `
-and $Report.registrationRemoved `
-and $Report.shortcutsRemoved
}
Write-Utf8JsonFile -Path $ReportPath -Value $Report
}
[Console]::Out.WriteLine('HyperTwist Windows installer lifecycle validation passed.')
[Console]::Out.WriteLine("Report: $([System.IO.Path]::GetFullPath($ReportPath))")

View file

@ -0,0 +1,617 @@
param(
[string]$PayloadRoot = '',
[string]$TargetInstallRoot = '',
[string]$ExpectedPayloadManifestPath = '',
[string]$ReportPath = '',
[Int64]$MinimumMemoryBytes = 8589934592,
[Int64]$MinimumFreeSpaceBytes = 4294967296,
[int]$MinimumWindowsBuild = 19045,
[switch]$VerifyPayloadHashes,
[switch]$WaitForUser,
[switch]$PassThru
)
$ErrorActionPreference = 'Stop'
$ExpectedSolverTableSizeBytes = 676207080
$ExpectedSolverTableSha256 = 'dc6de3d909a37afe2ba7f1f978543a13eae215bd20d954d66813524a8ca20034'
$RequiredVcRuntimeVersion = [version]'14.44.35211.0'
function Write-Utf8JsonFile {
param(
[Parameter(Mandatory = $true)]
[string]$Path,
[Parameter(Mandatory = $true)]
[object]$Value
)
$ParentPath = Split-Path -Parent $Path
if (-not [string]::IsNullOrWhiteSpace($ParentPath))
{
New-Item -ItemType Directory -Force -Path $ParentPath | Out-Null
}
$Json = $Value | ConvertTo-Json -Depth 12
$Utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText($Path, $Json, $Utf8NoBom)
}
function Get-NormalizedRelativePath {
param(
[Parameter(Mandatory = $true)]
[string]$RootPath,
[Parameter(Mandatory = $true)]
[string]$FilePath
)
$RootPrefix = $RootPath.TrimEnd('\', '/') + [System.IO.Path]::DirectorySeparatorChar
$ResolvedFilePath = [System.IO.Path]::GetFullPath($FilePath)
if (-not $ResolvedFilePath.StartsWith(
$RootPrefix,
[System.StringComparison]::OrdinalIgnoreCase))
{
throw "File '$ResolvedFilePath' is outside payload root '$RootPath'."
}
return $ResolvedFilePath.Substring($RootPrefix.Length).Replace('\', '/')
}
function Resolve-PayloadRelativeFilePath {
param(
[Parameter(Mandatory = $true)]
[string]$RootPath,
[Parameter(Mandatory = $true)]
[string]$RelativePath
)
if ([string]::IsNullOrWhiteSpace($RelativePath))
{
throw 'A payload-relative path cannot be empty.'
}
$WindowsRelativePath = $RelativePath.Replace('/', '\')
if ([System.IO.Path]::IsPathRooted($WindowsRelativePath))
{
throw "Payload-relative path '$RelativePath' must not be rooted."
}
$ResolvedRootPath = [System.IO.Path]::GetFullPath($RootPath)
$RootPrefix = $ResolvedRootPath.TrimEnd('\', '/') + [System.IO.Path]::DirectorySeparatorChar
$ResolvedFilePath = [System.IO.Path]::GetFullPath(
(Join-Path $ResolvedRootPath $WindowsRelativePath)
)
if (-not $ResolvedFilePath.StartsWith(
$RootPrefix,
[System.StringComparison]::OrdinalIgnoreCase))
{
throw "Payload-relative path '$RelativePath' escapes root '$RootPath'."
}
return $ResolvedFilePath
}
function Get-ExistingPathRoot {
param(
[Parameter(Mandatory = $true)]
[string]$Path
)
$CandidatePath = [System.IO.Path]::GetFullPath($Path)
while (-not (Test-Path -LiteralPath $CandidatePath))
{
$ParentPath = Split-Path -Parent $CandidatePath
if ([string]::IsNullOrWhiteSpace($ParentPath) -or $ParentPath -eq $CandidatePath)
{
throw "No existing parent could be resolved for '$Path'."
}
$CandidatePath = $ParentPath
}
return $CandidatePath
}
function Get-VcRuntimeEvidence {
$RegistryPaths = @(
'HKLM:\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x64'
)
foreach ($RegistryPath in $RegistryPaths)
{
if (-not (Test-Path -LiteralPath $RegistryPath))
{
continue
}
$Runtime = Get-ItemProperty -LiteralPath $RegistryPath
$VersionText = ([string]$Runtime.Version).TrimStart('v')
try
{
$ParsedVersion = [version]$VersionText
}
catch
{
continue
}
return [pscustomobject][ordered]@{
registryPath = $RegistryPath
installed = [int]$Runtime.Installed -eq 1
version = $ParsedVersion.ToString()
}
}
return $null
}
if ([string]::IsNullOrWhiteSpace($TargetInstallRoot))
{
$TargetInstallRoot = Join-Path ([Environment]::GetFolderPath('ProgramFiles')) 'HyperTwist'
}
if ([string]::IsNullOrWhiteSpace($ReportPath))
{
$ReportPath = Join-Path $env:TEMP 'HyperTwist-readiness.json'
}
$Checks = New-Object System.Collections.ArrayList
function Add-ReadinessCheck {
param(
[Parameter(Mandatory = $true)]
[string]$Id,
[Parameter(Mandatory = $true)]
[ValidateSet('required', 'optional', 'informational')]
[string]$Class,
[Parameter(Mandatory = $true)]
[bool]$Passed,
[Parameter(Mandatory = $true)]
[string]$Summary,
[object]$Evidence = $null
)
[void]$Checks.Add([pscustomobject][ordered]@{
id = $Id
class = $Class
passed = $Passed
summary = $Summary
evidence = $Evidence
})
}
$OperatingSystem = Get-CimInstance Win32_OperatingSystem
$ComputerSystem = Get-CimInstance Win32_ComputerSystem
$WindowsBuild = [int]$OperatingSystem.BuildNumber
$Is64BitOperatingSystem = [Environment]::Is64BitOperatingSystem
Add-ReadinessCheck `
-Id 'windows-x64' `
-Class 'required' `
-Passed $Is64BitOperatingSystem `
-Summary 'HyperTwist requires a 64-bit Windows operating system.' `
-Evidence ([pscustomobject][ordered]@{
architecture = [string]$OperatingSystem.OSArchitecture
processArchitecture = [string]$env:PROCESSOR_ARCHITECTURE
})
Add-ReadinessCheck `
-Id 'windows-supported-build' `
-Class 'required' `
-Passed ($WindowsBuild -ge $MinimumWindowsBuild) `
-Summary "HyperTwist requires Windows build $MinimumWindowsBuild or newer." `
-Evidence ([pscustomobject][ordered]@{
caption = [string]$OperatingSystem.Caption
version = [string]$OperatingSystem.Version
build = $WindowsBuild
minimumBuild = $MinimumWindowsBuild
})
$TotalMemoryBytes = [Int64]$ComputerSystem.TotalPhysicalMemory
Add-ReadinessCheck `
-Id 'system-memory' `
-Class 'required' `
-Passed ($TotalMemoryBytes -ge $MinimumMemoryBytes) `
-Summary 'At least 8 GiB of physical memory is required for the desktop Alpha.' `
-Evidence ([pscustomobject][ordered]@{
totalBytes = $TotalMemoryBytes
minimumBytes = $MinimumMemoryBytes
})
$ExistingInstallPathRoot = Get-ExistingPathRoot -Path $TargetInstallRoot
$TargetDriveName = [System.IO.Path]::GetPathRoot($ExistingInstallPathRoot).TrimEnd('\').TrimEnd(':')
$TargetDrive = Get-PSDrive -Name $TargetDriveName
$AvailableFreeSpaceBytes = [Int64]$TargetDrive.Free
Add-ReadinessCheck `
-Id 'install-volume-free-space' `
-Class 'required' `
-Passed ($AvailableFreeSpaceBytes -ge $MinimumFreeSpaceBytes) `
-Summary 'The target volume must have enough free space for installation and first-run caches.' `
-Evidence ([pscustomobject][ordered]@{
targetInstallRoot = [System.IO.Path]::GetFullPath($TargetInstallRoot)
availableBytes = $AvailableFreeSpaceBytes
minimumBytes = $MinimumFreeSpaceBytes
})
$VcRuntime = Get-VcRuntimeEvidence
$VcRuntimeReady = $null -ne $VcRuntime `
-and $VcRuntime.installed `
-and [version]$VcRuntime.version -ge $RequiredVcRuntimeVersion
Add-ReadinessCheck `
-Id 'vc-runtime-x64' `
-Class 'required' `
-Passed $VcRuntimeReady `
-Summary "Microsoft Visual C++ 2015-2022 x64 runtime $RequiredVcRuntimeVersion or newer is required and is bundled by the installer." `
-Evidence $VcRuntime
$SystemDirectory = [Environment]::SystemDirectory
$RequiredSystemLibraries = @(
'd3d11.dll',
'd3d12.dll',
'dxgi.dll',
'mf.dll',
'mfplat.dll',
'mfreadwrite.dll',
'xinput1_4.dll'
)
$MissingSystemLibraries = @(
$RequiredSystemLibraries |
Where-Object { -not (Test-Path -LiteralPath (Join-Path $SystemDirectory $_)) }
)
Add-ReadinessCheck `
-Id 'windows-media-and-graphics-libraries' `
-Class 'required' `
-Passed ($MissingSystemLibraries.Count -eq 0) `
-Summary 'Windows DirectX, XInput, and Media Foundation runtime libraries must be available.' `
-Evidence ([pscustomobject][ordered]@{
systemDirectory = $SystemDirectory
requiredLibraries = $RequiredSystemLibraries
missingLibraries = $MissingSystemLibraries
})
$VideoControllers = @(
Get-CimInstance Win32_VideoController -ErrorAction SilentlyContinue |
ForEach-Object {
[pscustomobject][ordered]@{
name = [string]$_.Name
driverVersion = [string]$_.DriverVersion
driverDate = if ($null -eq $_.DriverDate) { $null } else { $_.DriverDate.ToString('o') }
adapterRamBytes = [Int64]$_.AdapterRAM
status = [string]$_.Status
}
}
)
Add-ReadinessCheck `
-Id 'graphics-adapter' `
-Class 'required' `
-Passed ($VideoControllers.Count -gt 0) `
-Summary 'At least one Windows graphics adapter must be visible.' `
-Evidence $VideoControllers
$SoundDevices = @(
Get-CimInstance Win32_SoundDevice -ErrorAction SilentlyContinue |
ForEach-Object {
[pscustomobject][ordered]@{
name = [string]$_.Name
status = [string]$_.Status
}
}
)
Add-ReadinessCheck `
-Id 'audio-device' `
-Class 'optional' `
-Passed ($SoundDevices.Count -gt 0) `
-Summary 'An audio device is optional for puzzle play and recommended for narration and speech features.' `
-Evidence $SoundDevices
$OpenXrRegistryPaths = @(
'HKLM:\SOFTWARE\Khronos\OpenXR\1',
'HKLM:\SOFTWARE\WOW6432Node\Khronos\OpenXR\1'
)
$OpenXrRuntime = $null
foreach ($OpenXrRegistryPath in $OpenXrRegistryPaths)
{
if (Test-Path -LiteralPath $OpenXrRegistryPath)
{
$OpenXrRuntime = Get-ItemProperty -LiteralPath $OpenXrRegistryPath
break
}
}
Add-ReadinessCheck `
-Id 'openxr-runtime' `
-Class 'optional' `
-Passed ($null -ne $OpenXrRuntime -and -not [string]::IsNullOrWhiteSpace([string]$OpenXrRuntime.ActiveRuntime)) `
-Summary 'An active OpenXR runtime is optional for keyboard/mouse play and required only for live headset sessions.' `
-Evidence $(if ($null -eq $OpenXrRuntime) {
$null
} else {
[pscustomobject][ordered]@{ activeRuntime = [string]$OpenXrRuntime.ActiveRuntime }
})
$ResolvedPayloadRoot = $null
if (-not [string]::IsNullOrWhiteSpace($PayloadRoot))
{
if (-not (Test-Path -LiteralPath $PayloadRoot -PathType Container))
{
throw "Payload root '$PayloadRoot' was not found."
}
$ResolvedPayloadRoot = (Resolve-Path -LiteralPath $PayloadRoot).Path
$RequiredPayloadFiles = @(
[pscustomobject]@{ path = 'UnrealHyperTwist.exe'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'UnrealHyperTwist\Binaries\Win64\UnrealHyperTwist-Win64-Shipping.exe'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'UnrealHyperTwist\Binaries\Win64\tbb12.dll'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'UnrealHyperTwist\Binaries\Win64\tbbmalloc.dll'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'UnrealHyperTwist\Content\Paks\UnrealHyperTwist-Windows.pak'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'UnrealHyperTwist\Content\Paks\UnrealHyperTwist-Windows.utoc'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'UnrealHyperTwist\Content\Paks\UnrealHyperTwist-Windows.ucas'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'UnrealHyperTwist\Saved\twophase-ht.tbl'; size = $ExpectedSolverTableSizeBytes; sha256 = $ExpectedSolverTableSha256 },
[pscustomobject]@{ path = 'Content\Browser\index.html'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'Content\Browser\src\browser-runtime-bootstrap.js'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'Content\Browser\src\browser-spatial-runtime-fallback.js'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'Content\Browser\dist\browser-spatial-runtime.js'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'Engine\Binaries\ThirdParty\Windows\XAudio2_9\x64\XAudio2_9Redist.dll'; size = -1; sha256 = '' },
[pscustomobject]@{ path = 'Engine\Binaries\ThirdParty\OpenXR\win64\openxr_loader.dll'; size = -1; sha256 = '' }
)
$PayloadFailures = New-Object System.Collections.ArrayList
foreach ($RequiredPayloadFile in $RequiredPayloadFiles)
{
$RequiredPath = Join-Path $ResolvedPayloadRoot $RequiredPayloadFile.path
if (-not (Test-Path -LiteralPath $RequiredPath -PathType Leaf))
{
[void]$PayloadFailures.Add("missing:$($RequiredPayloadFile.path)")
continue
}
$RequiredItem = Get-Item -LiteralPath $RequiredPath
if ($RequiredItem.Length -le 0)
{
[void]$PayloadFailures.Add("empty:$($RequiredPayloadFile.path)")
continue
}
if ([Int64]$RequiredPayloadFile.size -ge 0 `
-and $RequiredItem.Length -ne [Int64]$RequiredPayloadFile.size)
{
[void]$PayloadFailures.Add("size:$($RequiredPayloadFile.path)")
continue
}
if (-not [string]::IsNullOrWhiteSpace([string]$RequiredPayloadFile.sha256))
{
$ActualHash = (Get-FileHash -LiteralPath $RequiredPath -Algorithm SHA256).Hash.ToLowerInvariant()
if ($ActualHash -ne ([string]$RequiredPayloadFile.sha256).ToLowerInvariant())
{
[void]$PayloadFailures.Add("sha256:$($RequiredPayloadFile.path)")
}
}
}
$CefRuntimeFiles = @(
Get-ChildItem `
-LiteralPath (Join-Path $ResolvedPayloadRoot 'Engine\Binaries\ThirdParty\CEF3\Win64') `
-Filter 'libcef.dll' `
-File `
-Recurse `
-ErrorAction SilentlyContinue
)
if ($CefRuntimeFiles.Count -eq 0)
{
[void]$PayloadFailures.Add('missing:Engine/Binaries/ThirdParty/CEF3/Win64/*/libcef.dll')
}
Add-ReadinessCheck `
-Id 'required-payload-files' `
-Class 'required' `
-Passed ($PayloadFailures.Count -eq 0) `
-Summary 'The installed payload must contain every required executable, runtime, cooked-data, browser, solver, and OpenXR file.' `
-Evidence ([pscustomobject][ordered]@{
payloadRoot = $ResolvedPayloadRoot
requiredFileCount = $RequiredPayloadFiles.Count + 1
cefRuntimeFiles = @(
$CefRuntimeFiles |
ForEach-Object {
Get-NormalizedRelativePath `
-RootPath $ResolvedPayloadRoot `
-FilePath $_.FullName
}
)
failures = @($PayloadFailures)
})
$OuterExecutablePath = Join-Path $ResolvedPayloadRoot 'UnrealHyperTwist.exe'
if (Test-Path -LiteralPath $OuterExecutablePath -PathType Leaf)
{
$Signature = Get-AuthenticodeSignature -LiteralPath $OuterExecutablePath
Add-ReadinessCheck `
-Id 'desktop-code-signature' `
-Class 'optional' `
-Passed ($Signature.Status -eq [System.Management.Automation.SignatureStatus]::Valid) `
-Summary 'The Alpha payload is currently unsigned; production distribution requires Authenticode signing.' `
-Evidence ([pscustomobject][ordered]@{
status = $Signature.Status.ToString()
signer = if ($null -eq $Signature.SignerCertificate) {
$null
} else {
[string]$Signature.SignerCertificate.Subject
}
})
}
}
if (-not [string]::IsNullOrWhiteSpace($ExpectedPayloadManifestPath))
{
if ($null -eq $ResolvedPayloadRoot)
{
throw 'ExpectedPayloadManifestPath requires PayloadRoot.'
}
if (-not (Test-Path -LiteralPath $ExpectedPayloadManifestPath -PathType Leaf))
{
throw "Payload manifest '$ExpectedPayloadManifestPath' was not found."
}
$Manifest = Get-Content -LiteralPath $ExpectedPayloadManifestPath -Raw | ConvertFrom-Json
$ManifestFailures = New-Object System.Collections.ArrayList
$SeenManifestPaths = @{}
$ManifestEntries = @($Manifest.files)
if ([string]$Manifest.manifestVersion -ne 'ht-windows-installer-payload/v1')
{
[void]$ManifestFailures.Add("manifest-version:$($Manifest.manifestVersion)")
}
$DeclaredFileCount = 0
if (-not [int]::TryParse([string]$Manifest.fileCount, [ref]$DeclaredFileCount) `
-or $DeclaredFileCount -ne $ManifestEntries.Count)
{
[void]$ManifestFailures.Add("manifest-file-count:$($Manifest.fileCount)")
}
$DeclaredTotalSizeBytes = 0L
if (-not [Int64]::TryParse(
[string]$Manifest.totalSizeBytes,
[ref]$DeclaredTotalSizeBytes
) -or $DeclaredTotalSizeBytes -lt 0)
{
[void]$ManifestFailures.Add("manifest-total-size:$($Manifest.totalSizeBytes)")
}
$ComputedTotalSizeBytes = 0L
foreach ($ManifestEntry in $ManifestEntries)
{
$ManifestRelativePath = [string]$ManifestEntry.relativePath
$ManifestPathKey = $ManifestRelativePath.Replace('\', '/').ToLowerInvariant()
if ($SeenManifestPaths.ContainsKey($ManifestPathKey))
{
[void]$ManifestFailures.Add("duplicate:$ManifestRelativePath")
continue
}
$SeenManifestPaths[$ManifestPathKey] = $true
$ManifestEntrySizeBytes = 0L
if (-not [Int64]::TryParse(
[string]$ManifestEntry.sizeBytes,
[ref]$ManifestEntrySizeBytes
) -or $ManifestEntrySizeBytes -lt 0)
{
[void]$ManifestFailures.Add("invalid-size:$ManifestRelativePath")
continue
}
$ComputedTotalSizeBytes += $ManifestEntrySizeBytes
$ExpectedManifestHash = ([string]$ManifestEntry.sha256).ToLowerInvariant()
if ($ExpectedManifestHash -notmatch '^[0-9a-f]{64}$')
{
[void]$ManifestFailures.Add("invalid-sha256:$ManifestRelativePath")
continue
}
try
{
$ManifestFilePath = Resolve-PayloadRelativeFilePath `
-RootPath $ResolvedPayloadRoot `
-RelativePath $ManifestRelativePath
}
catch
{
[void]$ManifestFailures.Add("invalid-path:$ManifestRelativePath")
continue
}
if (-not (Test-Path -LiteralPath $ManifestFilePath -PathType Leaf))
{
[void]$ManifestFailures.Add("missing:$ManifestRelativePath")
continue
}
$ManifestItem = Get-Item -LiteralPath $ManifestFilePath
if ($ManifestItem.Length -ne $ManifestEntrySizeBytes)
{
[void]$ManifestFailures.Add("size:$ManifestRelativePath")
continue
}
if ($VerifyPayloadHashes)
{
$ManifestHash = (Get-FileHash -LiteralPath $ManifestFilePath -Algorithm SHA256).Hash.ToLowerInvariant()
if ($ManifestHash -ne $ExpectedManifestHash)
{
[void]$ManifestFailures.Add("sha256:$ManifestRelativePath")
}
}
}
if ($DeclaredTotalSizeBytes -ne $ComputedTotalSizeBytes)
{
[void]$ManifestFailures.Add(
"manifest-total-size-parity:$DeclaredTotalSizeBytes/$ComputedTotalSizeBytes"
)
}
Add-ReadinessCheck `
-Id 'payload-manifest-parity' `
-Class 'required' `
-Passed ($ManifestFailures.Count -eq 0) `
-Summary 'The installed payload must match the release manifest without missing, truncated, or altered files.' `
-Evidence ([pscustomobject][ordered]@{
manifestPath = (Resolve-Path -LiteralPath $ExpectedPayloadManifestPath).Path
manifestVersion = [string]$Manifest.manifestVersion
manifestEntryCount = $ManifestEntries.Count
declaredFileCount = $DeclaredFileCount
declaredTotalSizeBytes = $DeclaredTotalSizeBytes
computedTotalSizeBytes = $ComputedTotalSizeBytes
hashesVerified = [bool]$VerifyPayloadHashes
failures = @($ManifestFailures)
})
}
$RequiredFailures = @(
$Checks |
Where-Object { $_.class -eq 'required' -and -not $_.passed }
)
$OptionalWarnings = @(
$Checks |
Where-Object { $_.class -eq 'optional' -and -not $_.passed }
)
$Result = if ($RequiredFailures.Count -gt 0) {
'blocked'
} elseif ($OptionalWarnings.Count -gt 0) {
'ready-with-warnings'
} else {
'ready'
}
$Report = [pscustomobject][ordered]@{
reportVersion = 'ht-windows-readiness/v1'
generatedAtUtc = [DateTime]::UtcNow.ToString('o')
result = $Result
payloadRoot = $ResolvedPayloadRoot
targetInstallRoot = [System.IO.Path]::GetFullPath($TargetInstallRoot)
requiredFailureCount = $RequiredFailures.Count
optionalWarningCount = $OptionalWarnings.Count
checks = @($Checks)
}
Write-Utf8JsonFile -Path $ReportPath -Value $Report
[Console]::Out.WriteLine((
"HyperTwist Windows readiness: {0} ({1} required failure(s), {2} optional warning(s))." -f
$Result,
$RequiredFailures.Count,
$OptionalWarnings.Count
))
[Console]::Out.WriteLine("Report: $([System.IO.Path]::GetFullPath($ReportPath))")
if ($PassThru)
{
return $Report
}
if ($WaitForUser)
{
[void](Read-Host 'Press Enter to close the HyperTwist readiness report')
}
if ($Result -eq 'blocked')
{
exit 2
}
exit 0