Skip to content

Commit 5f65328

Browse files
committed
feat(rc3.1): add installer hardening and compatibility validation
1 parent e452bd9 commit 5f65328

15 files changed

Lines changed: 621 additions & 280 deletions

‎.github/workflows/aurora-tools-test.yml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ jobs:
2121
shell: powershell
2222
run: |
2323
& .\tests\Aurora_Diagnostics_Tests.ps1
24+
- name: Test RC3 redundant installer and precise recovery
25+
shell: powershell
26+
run: |
27+
& .\tests\Aurora_Installer_Tests.ps1

‎.github/workflows/just_build_no_signature.yml‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,15 @@ jobs:
116116
- name: Add Runtime Sync / 添加运行库同步工具
117117
shell: powershell
118118
run: |
119-
Copy-Item "${{ github.workspace }}\dist\runtime_sync\*.ps1" "${{ github.workspace }}\x64\Release\a\"
120-
Copy-Item "${{ github.workspace }}\dist\runtime_sync\*.bat" "${{ github.workspace }}\x64\Release\a\"
119+
& .\scripts\stage_aurora_package.ps1 -Destination (Join-Path $env:GITHUB_WORKSPACE 'x64\Release\a')
121120
122121
- name: Package Aurora / 打包 Aurora
123122
shell: powershell
124123
run: |
125124
$zipName = "${{ steps.get_version.outputs.filename }}.7z"
125+
& .\scripts\stage_aurora_package.ps1 -Destination (Join-Path $env:GITHUB_WORKSPACE 'x64\Release\a') -VerifyOnly
126126
7z a -r ${{ github.workspace }}\$zipName ${{ github.workspace }}\x64\Release\a\*.*
127+
if ($LASTEXITCODE -ne 0) { throw 'Aurora archive failed.' }
127128
128129
continue-on-error: false
129130

‎Aurora_Common.ps1‎

Lines changed: 168 additions & 19 deletions
Large diffs are not rendered by default.

‎Aurora_Installer.ps1‎

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# RC3 orchestration; file writes and recovery use the RC2 journal primitives.
22
function Get-AuroraExecutableEvidence([string]$Path) {
3-
$stream=$null; $valid=$false; $imports=@()
3+
$stream=$null; $guard=$null; $valid=$false; $imports=@()
44
try {
55
Assert-AuroraPlainPath $Path
6-
$stream=[IO.File]::Open($Path,'Open','Read','ReadWrite'); $reader=New-Object IO.BinaryReader($stream)
6+
$guard=Enter-AuroraPathGuard $Path
7+
$stream=[AuroraDirectoryGuard]::Read((Get-AuroraPath $Path)); $reader=New-Object IO.BinaryReader($stream)
78
if ($stream.Length -lt 64 -or $reader.ReadUInt16() -ne 0x5A4D) { throw 'MZ' }
89
$stream.Position=60; $offset=$reader.ReadInt32()
910
if ($offset -lt 64 -or $offset -gt $stream.Length-264) { throw 'PE offset' }
@@ -41,7 +42,7 @@ function Get-AuroraExecutableEvidence([string]$Path) {
4142
if ($name -match '^(?i)(d3d(?:9|10|11|12)|dxgi|vulkan-1|opengl32|UnityPlayer)\.dll$') { $imports+=$name }
4243
}
4344
}
44-
} catch { } finally { if ($stream) { $stream.Dispose() } }
45+
} catch { } finally { if ($stream) { $stream.Dispose() }; if ($guard) { $guard.Dispose() } }
4546
[pscustomobject]@{Valid=$valid;GraphicsImports=$imports}
4647
}
4748
function Test-AuroraUtilityPath([string]$RelativePath, [switch]$Runtime) {
@@ -264,22 +265,34 @@ function New-AuroraPayload([string]$PackageDir,[string]$InstallDir,[string]$Prox
264265

265266
function Get-AuroraIndexPath([string]$Root) { Join-Path $Root 'OptiScaler\AuroraSetup\AuroraInstallManifest.json' }
266267
function Open-AuroraIndex([string]$Root) {
268+
$Root=Get-AuroraMetadataPath $Root
267269
$path=Get-AuroraIndexPath $Root; Assert-AuroraPlainPath $path
268270
if (-not (Test-Path -LiteralPath $path)) { return $null }
269-
$index=Get-Content -LiteralPath $path -Raw -Encoding UTF8 | ConvertFrom-Json
270-
if ($index.SchemaVersion -ne 3 -or (Get-AuroraPath $index.GameRoot) -ine $Root -or $index.Status -notin @('Pending','Applied','Removed','NeedsAttention')) { throw 'RC3 总清单版本、目录或状态不匹配。' }
271+
$index=Read-AuroraJson $path
272+
Assert-AuroraFields $index @('SchemaVersion','GameRoot','Status','Targets','RuntimeOwners','RuntimeGroups')
273+
if ($index.SchemaVersion -isnot [int] -or $index.Targets -isnot [array] -or $index.Targets.Count -lt 1 -or $index.Targets.Count -gt 256 -or $index.RuntimeOwners -isnot [array] -or $index.RuntimeGroups -isnot [array]) { throw 'RC3 总清单字段类型或目标数量无效。' }
274+
if ($index.PSObject.Properties['RuntimeJournalExpected'] -and $index.RuntimeJournalExpected -isnot [bool]) { throw 'Runtime 日志登记状态无效。' }
275+
if ($index.SchemaVersion -ne 3 -or (Get-AuroraMetadataPath $index.GameRoot) -ine $Root -or $index.Status -notin @('Pending','Applied','Removed','NeedsAttention')) { throw 'RC3 总清单版本、目录或状态不匹配。' }
276+
$index.GameRoot=$Root
271277
$seen=@{}
272278
foreach ($target in @($index.Targets)) {
273-
$dir=Get-AuroraPath $target.Directory
279+
Assert-AuroraFields $target @('Directory','Proxy','JournalPath','Executables')
280+
if ($target.Executables -isnot [array] -or -not $target.Executables.Count) { throw '入口列表必须是非空数组。' }
281+
$dir=Get-AuroraMetadataPath $target.Directory; $target.Directory=$dir
274282
if (-not (Test-AuroraWithin $dir $Root) -or (Test-AuroraWithin $dir (Join-Path $Root 'OptiScaler')) -or $seen.ContainsKey($dir)) { throw 'RC3 清单入口重复或越界。' }
275283
$seen[$dir]=$true; Assert-AuroraPlainPath $dir
276284
if ($target.Proxy -notin @('dxgi.dll','winmm.dll','version.dll','dbghelp.dll','d3d12.dll','wininet.dll','winhttp.dll','OptiScaler.asi')) { throw 'RC3 清单 Proxy 无效。' }
285+
$target.JournalPath=Get-AuroraMetadataPath $target.JournalPath
277286
if ($target.JournalPath -ine (Join-Path $dir 'OptiScaler\AuroraSetup\manifest.json')) { throw 'RC3 文件日志路径不匹配。' }
278-
if ($index.Status -eq 'Applied' -and -not (Test-Path -LiteralPath $target.JournalPath -PathType Leaf)) { throw '部署文件日志缺失,停止操作,不能猜测文件归属。' }
279-
foreach ($exe in @($target.Executables)) { if ([IO.Path]::GetDirectoryName((Get-AuroraPath $exe)) -ine $dir) { throw 'RC3 入口路径与目录不匹配。' } }
287+
if ($target.PSObject.Properties['JournalExpected'] -and $target.JournalExpected -isnot [bool]) { throw '核心日志登记状态无效。' }
288+
if (($index.Status -eq 'Applied' -or ($target.PSObject.Properties['JournalExpected'] -and $target.JournalExpected)) -and -not (Test-Path -LiteralPath $target.JournalPath -PathType Leaf)) { throw '部署文件日志缺失,停止操作,不能猜测文件归属。' }
289+
foreach ($exe in @($target.Executables)) { if ([IO.Path]::GetDirectoryName((Get-AuroraMetadataPath $exe)) -ine $dir) { throw 'RC3 入口路径与目录不匹配。' } }
280290
}
291+
$owners=@{}
281292
foreach ($dir in @($index.RuntimeOwners)) {
282-
if (-not $seen.ContainsKey((Get-AuroraPath $dir))) { throw 'RC3 Runtime 日志未关联部署入口。' }
293+
if (-not $seen.ContainsKey((Get-AuroraMetadataPath $dir))) { throw 'RC3 Runtime 日志未关联部署入口。' }
294+
$owner=Get-AuroraMetadataPath $dir
295+
if ($owners.ContainsKey($owner)) { throw 'Runtime 日志所有者重复。' }; $owners[$owner]=$true
283296
}
284297
return $index
285298
}
@@ -342,7 +355,7 @@ function Install-AuroraDeployment([string]$Root,[string]$PackageDir,[string]$Pro
342355
foreach ($candidate in $candidates) { Assert-AuroraGameClosed $Root $candidate.Path }
343356
$lock=Enter-AuroraLock $Root
344357
$index=Open-AuroraIndex $Root
345-
if (-not $index) { $index=[pscustomobject]@{SchemaVersion=3;GameRoot=$Root;Status='Pending';Targets=@();RuntimeOwners=@();RuntimeGroups=@()} }
358+
if (-not $index) { $index=[pscustomobject]@{SchemaVersion=3;GameRoot=$Root;Status='Pending';Targets=@();RuntimeOwners=@();RuntimeGroups=@();RuntimeJournalExpected=$false} }
346359
foreach ($group in @($candidates | Group-Object Directory)) {
347360
$dir=$group.Name
348361
$chosen=Get-AuroraRecommendedProxy @($group.Group) $Proxy
@@ -365,6 +378,7 @@ function Install-AuroraDeployment([string]$Root,[string]$PackageDir,[string]$Pro
365378
$index.Targets+=@($target)
366379
}
367380
$journal=Open-AuroraJournal $target.JournalPath $Root $dir
381+
$target | Add-Member NoteProperty JournalExpected (Test-Path -LiteralPath $target.JournalPath -PathType Leaf) -Force
368382
$payload=@(New-AuroraPayload $PackageDir $dir $chosen)
369383
$prepared+=@([pscustomobject]@{Target=$target;Journal=$journal;Payload=$payload})
370384
Assert-AuroraPayloadReady $target $journal $payload
@@ -391,6 +405,7 @@ function Install-AuroraDeployment([string]$Root,[string]$PackageDir,[string]$Pro
391405
# Create every payload before activating any new Proxy; each copy is journaled.
392406
foreach ($p in $prepared) {
393407
Write-AuroraJson $p.Target.JournalPath $p.Journal
408+
$p.Target.JournalExpected=$true; Write-AuroraJson (Get-AuroraIndexPath $Root) $index
394409
Write-AuroraJson (Join-Path $p.Target.Directory 'OptiScaler\AuroraSetup\installation.json') ([pscustomobject]@{GameRoot=$Root;GameExe=$p.Target.Executables[0];Proxy=$p.Target.Proxy;RC3Manifest=(Get-AuroraIndexPath $Root)})
395410
foreach ($file in @($p.Payload | Select-Object -Skip 1)) {
396411
Assert-AuroraPlannedFile $file
@@ -400,6 +415,11 @@ function Install-AuroraDeployment([string]$Root,[string]$PackageDir,[string]$Pro
400415
# One full-tree synchronization prevents different entries fighting over shared DLLs.
401416
# Recover pre-RC3 journals first only when explicitly requested by Remove/Restore;
402417
# multiple existing owners are kept read-only until that recovery is done.
418+
$runtimeJournalPath=Join-Path $index.RuntimeOwners[0] 'OptiScaler\RuntimeSync\manifest.json'
419+
$runtimeJournal=Open-AuroraJournal $runtimeJournalPath $Root $index.RuntimeOwners[0]
420+
Write-AuroraJson $runtimeJournalPath $runtimeJournal
421+
$index | Add-Member NoteProperty RuntimeJournalExpected $true -Force
422+
Write-AuroraJson (Get-AuroraIndexPath $Root) $index
403423
Invoke-AuroraRuntimeTask Install $Root $index.RuntimeOwners[0] ([IO.Path]::ChangeExtension($ReportPath,'.log.txt')) -CallerHasLock
404424
foreach ($p in $prepared) {
405425
$file=$p.Payload[0]; Assert-AuroraPlannedFile $file
@@ -409,8 +429,13 @@ function Install-AuroraDeployment([string]$Root,[string]$PackageDir,[string]$Pro
409429
Save-AuroraInstallReport $index $ReportPath
410430
return $index
411431
} catch {
412-
if ($started) { $index.Status='NeedsAttention'; Write-AuroraJson (Get-AuroraIndexPath $Root) $index; Save-AuroraInstallReport $index $ReportPath }
413-
throw
432+
$originalFailure=$_
433+
if ($started) {
434+
$index.Status='NeedsAttention'
435+
try { Write-AuroraJson (Get-AuroraIndexPath $Root) $index; Save-AuroraInstallReport $index $ReportPath }
436+
catch { Write-Warning '状态/报告写入也失败;保留原有 Pending 日志,不能视为安装成功。' }
437+
}
438+
throw $originalFailure
414439
} finally {
415440
if ($lock) { $lock.Dispose() }
416441
foreach ($p in $prepared) { foreach ($f in $p.Payload) {
@@ -422,16 +447,37 @@ function Assert-AuroraPlannedFile($File) {
422447
$current=''; if (Test-Path -LiteralPath $File.Target) { $current=Get-AuroraHash $File.Target }
423448
if ($current -ne $File.BeforeHash) { throw "文件在预检后变化,已停止:$($File.Target)" }
424449
}
450+
function Assert-AuroraCoreJournalScope($Journal) {
451+
$names=@('OptiScaler.dll','OptiScaler.ini','dxgi.dll','winmm.dll','version.dll','dbghelp.dll','d3d12.dll','wininet.dll','winhttp.dll','OptiScaler.asi','nvngx.dll_dlssnr.dll','LICENSE','READ ME - DLSS Neural Rendering.txt','Aurora_Common.ps1','Aurora_Installer.ps1','Aurora_RuntimeCatalog.ps1','Aurora_Diagnostics.ps1','Aurora_Setup.ps1','Aurora_Setup_Legacy.ps1','runtime_sync.ps1','Check_DLSS_Runtime.bat','Aurora_Setup.bat','Remove_Aurora.bat')
452+
foreach ($e in @($Journal.Entries)) {
453+
if (-not (Test-AuroraWithin $e.TargetPath $Journal.InstallDir)) { throw '核心日志目标不属于该入口目录。' }
454+
$rel=$e.TargetPath.Substring($Journal.InstallDir.Length).TrimStart('\')
455+
if ($rel -match '(?i)(^|\\)(AuroraSetup|RuntimeSync|_storage[^\\]*|\.git)(\\|$)') { throw '核心日志不得操作其他恢复记录。' }
456+
if ($rel -notin $names -and $rel -notmatch '(?i)^OptiScaler\\.+\.(dll|json|ini|txt|md|bin)$|^OptiScaler\\plugins\\OptiPatcher\.asi$|^Licenses\\[^\\]+$') { throw '核心日志指向非 Aurora payload 文件,保留现状。' }
457+
}
458+
}
425459
function Remove-AuroraDeployment($Index,[string]$ReportPath,[switch]$RuntimeOnly) {
426-
$root=$Index.GameRoot; $lock=$null; $preserved=@()
460+
$root=$Index.GameRoot; $lock=$null; $preserved=@(); $started=$false
427461
try {
428462
foreach ($t in @($Index.Targets)) { foreach ($exe in @($t.Executables)) { Assert-AuroraGameClosed $root $exe } }
429463
$lock=Enter-AuroraLock $root
430464
$Index=Open-AuroraIndex $root
431465
if (-not $Index) { throw 'RC3 总清单已变化,停止恢复。' }
432466
$journals=@()
433-
foreach ($dir in @($Index.RuntimeOwners)) { $path=Join-Path $dir 'OptiScaler\RuntimeSync\manifest.json'; $journals+=@([pscustomobject]@{Path=$path;Journal=(Open-AuroraJournal $path $root $dir);Runtime=$true}) }
467+
foreach ($dir in @($Index.RuntimeOwners)) {
468+
$path=Join-Path $dir 'OptiScaler\RuntimeSync\manifest.json'
469+
if (-not (Test-Path -LiteralPath $path) -and (-not $Index.PSObject.Properties['RuntimeJournalExpected'] -or $Index.RuntimeJournalExpected)) { throw 'Runtime 日志缺失,不能证明原版恢复状态,保留文件。' }
470+
$journals+=@([pscustomobject]@{Path=$path;Journal=(Open-AuroraJournal $path $root $dir);Runtime=$true})
471+
}
434472
if (-not $RuntimeOnly) { foreach ($t in @($Index.Targets)) { $journals+=@([pscustomobject]@{Path=$t.JournalPath;Journal=(Open-AuroraJournal $t.JournalPath $root $t.Directory);Runtime=$false}) } }
473+
$ownership=@{}
474+
foreach ($pair in $journals) {
475+
if (-not $pair.Runtime) { Assert-AuroraCoreJournalScope $pair.Journal }
476+
foreach ($e in @($pair.Journal.Entries | Where-Object { $_.Status -ne 'Restored' })) {
477+
if ($ownership.ContainsKey($e.TargetPath)) { throw '不同日志对同一路径声明冲突 ownership,未恢复或删除任何文件。' }
478+
$ownership[$e.TargetPath]=$pair.Path
479+
}
480+
}
435481
# Validate every backup before removing any tool. Changed targets are individually retained.
436482
foreach ($pair in $journals) {
437483
foreach ($e in @($pair.Journal.Entries)) {
@@ -443,19 +489,28 @@ function Remove-AuroraDeployment($Index,[string]$ReportPath,[switch]$RuntimeOnly
443489
if ($current -ne $e.OriginalHash -and -not $e.Created -and (-not $e.BackupPath -or (Get-AuroraHash $e.BackupPath) -ne $e.OriginalHash)) { throw "备份校验失败,尚未删除文件:$($e.TargetPath)" }
444490
}
445491
}
492+
$previousStatus=$Index.Status
493+
$Index.Status='Pending'; Write-AuroraJson (Get-AuroraIndexPath $root) $Index; $started=$true
446494
foreach ($pair in $journals) {
447495
Write-AuroraJson $pair.Path $pair.Journal
448496
$events=@(Restore-AuroraJournal $pair.Journal $pair.Path 6>&1)
449497
$failures=@($events | Where-Object { $_ -is [int] })[-1]
450498
$events | Where-Object { $_ -isnot [int] } | Out-File -LiteralPath ([IO.Path]::ChangeExtension($ReportPath,'.log.txt')) -Encoding utf8 -Append
451499
if ($failures) { throw '恢复过程中发现文件变化;保留剩余文件和恢复工具,请查看详细报告。' }
452500
}
453-
if (-not $RuntimeOnly) { $Index.Status='Removed' }
501+
if (-not $RuntimeOnly) { $Index.Status='Removed' } else { $Index.Status=$previousStatus }
454502
if ($preserved.Count) { $Index.Status='NeedsAttention' }
455503
# After explicit restoration, use a single owner on the next install.
456504
if ($RuntimeOnly -and -not $preserved.Count) { $Index.RuntimeOwners=@($Index.Targets[0].Directory) }
457505
Write-AuroraJson (Get-AuroraIndexPath $root) $Index
458506
Save-AuroraInstallReport $Index $ReportPath
459507
return @($preserved)
508+
} catch {
509+
$failure=$_
510+
if ($started) {
511+
$Index.Status='NeedsAttention'
512+
try { Write-AuroraJson (Get-AuroraIndexPath $root) $Index } catch { Write-Warning '恢复未完成,无法更新状态;请保留 Pending 清单与备份。' }
513+
}
514+
throw $failure
460515
} finally { if ($lock) { $lock.Dispose() } }
461516
}

‎Aurora_RC31_使用说明.txt‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Aurora RC3.1 累计安装工具包
2+
3+
包含全部 11 个当前安装/诊断/恢复工具,不包含新的 OptiScaler.dll 或 Runtime 二进制。
4+
先完整解压你已构建好的 RC2/RC3 发布包,再将本 ZIP 的工具覆盖到发布包根目录。
5+
从发布包运行 setup_windows.bat,核对游戏摘要后按 Enter 安装/更新。
6+
不要手工覆盖游戏中已由 manifest 管理的工具;否则会被当作用户修改而保留。
7+
卸载从部署入口运行 Remove_Aurora.bat;若工具已缺失,可从完整发布包运行恢复操作。
8+
不要删除 AuroraSetup/RuntimeSync 清单和备份;保留的用户文件与恢复记录是有意设计。
9+
BAT 已使用 CRLF。不要用 git archive 中未经打包规范化的 LF BAT 替代。
10+
11+
299 项本地新增测试通过不等于真实 GPU/游戏兼容性验证。
12+
仍需实测:巫师3 DX11/DX12、SL1.5.6、现有 6X、DLSS/XeSS input 闪烁对照、燕云/异环。
13+
本轮核心渲染 C++ 未修改;没有 Push、Merge、Release。
14+
源提交:07a080bbc8f3cf9d100eb58f158e708e37b66b25

‎Aurora_Setup.bat‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
@echo off
22
setlocal DisableDelayedExpansion
33
chcp 65001 >nul
4-
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Aurora_Setup.ps1"
5-
set "AURORA_EXIT=%ERRORLEVEL%"
6-
if not "%AURORA_EXIT%"=="0" pause
7-
exit /b %AURORA_EXIT%
4+
REM The menu can uninstall this BAT; leave its context before launching.
5+
REM PowerShell owns the interactive completion prompt and returns its exit code.
6+
(goto) 2>nul & powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Aurora_Setup.ps1"

‎Aurora_Setup.ps1‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ try {
3030
}
3131
$GameRoot=Get-AuroraPath $GameRoot; Assert-AuroraGameRoot $GameRoot
3232
$index=Open-AuroraIndex $GameRoot
33+
$localMeta=Join-Path $InstallDir 'OptiScaler\AuroraSetup\installation.json'
34+
if (-not $index -and (Test-Path -LiteralPath $localMeta)) {
35+
$savedMeta=Read-AuroraJson $localMeta
36+
if ($savedMeta.PSObject.Properties['RC3Manifest']) { throw 'RC3 总清单缺失,不能回退为单入口卸载;请保留文件与备份。' }
37+
}
3338
if ($Action -in @('Menu','Install','Repair')) {
3439
$scan=Get-AuroraScan $GameRoot
3540
if (-not $scan.Complete -and $Action -ne 'Menu') { throw '游戏目录未能完整读取,已停止安装。请确认目录权限,并避免目录联接。' }

‎Remove_Aurora.bat‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
@echo off
22
setlocal DisableDelayedExpansion
33
chcp 65001 >nul
4-
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Aurora_Setup.ps1" -Action Remove -InstallDir "%~dp0."
5-
set "AURORA_EXIT=%ERRORLEVEL%"
6-
if not "%AURORA_EXIT%"=="0" pause
7-
exit /b %AURORA_EXIT%
4+
REM Leave the BAT context before removal; no attempt to reread a deleted BAT.
5+
REM PowerShell owns the interactive completion prompt and returns its exit code.
6+
(goto) 2>nul & powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Aurora_Setup.ps1" -Action Remove -InstallDir "%~dp0."

0 commit comments

Comments
 (0)