aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-07-18 17:55:00 +0300
committerValentin Popov <valentin@popov.link>2026-07-18 17:55:00 +0300
commite96444459b9bcaa8eb055c89c82897494769307c (patch)
tree3efc12e8d84dbd0335b9205198df20f568bff547 /tools
parentfd9c04cade712dd41011c0da3ba10086799d61cf (diff)
downloadfparkan-e96444459b9bcaa8eb055c89c82897494769307c.tar.xz
fparkan-e96444459b9bcaa8eb055c89c82897494769307c.zip
fix(tools): reject non-world camera captures
Diffstat (limited to 'tools')
-rw-r--r--tools/capture-original-camera.ps171
1 files changed, 62 insertions, 9 deletions
diff --git a/tools/capture-original-camera.ps1 b/tools/capture-original-camera.ps1
index 62b5680..9e95d91 100644
--- a/tools/capture-original-camera.ps1
+++ b/tools/capture-original-camera.ps1
@@ -11,7 +11,13 @@ param(
[ValidateRange(0.01, 100000.0)]
[double]$FarPlane = 700.0,
[ValidateRange(0.01, 3.13)]
- [double]$FieldOfViewRadians = 1.3
+ [double]$FieldOfViewRadians = 1.3,
+ [ValidateRange(1, 600)]
+ [int]$CaptureAttempts = 60,
+ [ValidateRange(0, 1000000.0)]
+ [double]$MinimumWorldTranslation = 100.0,
+ [ValidateRange(0, 1000)]
+ [int]$RetryIntervalMilliseconds = 100
)
Set-StrictMode -Version Latest
@@ -131,15 +137,60 @@ if ($process -eq [IntPtr]::Zero) {
}
try {
- # Terrain.dll image RVA 0x7355c -> active camera interface pointer.
- # Selector 0's matrix is prefixed by a four-byte internal tag, so the
- # 16 IEEE-754 row-major words begin at interface + 32, not + 28.
- $cameraPointerBytes = Read-OriginalBytes $process ($terrainBase + 0x7355c) 4
- $cameraInterface = [BitConverter]::ToUInt32($cameraPointerBytes, 0)
- if ($cameraInterface -lt 0x10000) {
- throw "Terrain camera pointer is unavailable or invalid: 0x$('{0:X8}' -f $cameraInterface)"
+ # Terrain.dll image RVA 0x7355c -> active 0x1a4-byte outer camera object.
+ # Its selector-0 affine block begins at outer + 0x20. Words 3/7/11 are
+ # the proven world-space translation components. AutoDemo also briefly
+ # selects normalized/reflection-like camera objects; a finite matrix alone
+ # is not sufficient evidence that it can project the mission world.
+ $expectedOuterVtable = [uint32]($terrainBase + 0x665b4)
+ $matrixBytes = $null
+ $translation = $null
+ $cameraOuter = $null
+ $lastObservation = 'camera pointer unavailable'
+ for ($attempt = 1; $attempt -le $CaptureAttempts; $attempt++) {
+ $candidateOuter = [BitConverter]::ToUInt32(
+ (Read-OriginalBytes $process ($terrainBase + 0x7355c) 4),
+ 0
+ )
+ if ($candidateOuter -lt 0x10000) {
+ $lastObservation = "camera pointer 0x$('{0:X8}' -f $candidateOuter)"
+ continue
+ }
+ $outerVtable = [BitConverter]::ToUInt32(
+ (Read-OriginalBytes $process ([int64]$candidateOuter) 4),
+ 0
+ )
+ if ($outerVtable -ne $expectedOuterVtable) {
+ $lastObservation = "outer vtable 0x$('{0:X8}' -f $outerVtable)"
+ continue
+ }
+ $matrixBytes = Read-OriginalBytes $process ([int64]$candidateOuter + 0x20) 64
+ $candidateTranslation = @(
+ [BitConverter]::ToSingle($matrixBytes, 12),
+ [BitConverter]::ToSingle($matrixBytes, 28),
+ [BitConverter]::ToSingle($matrixBytes, 44)
+ )
+ $nonFinite = @($candidateTranslation | Where-Object {
+ [Single]::IsNaN($_) -or [Single]::IsInfinity($_)
+ })
+ $length = [Math]::Sqrt(
+ [double]$candidateTranslation[0] * $candidateTranslation[0] +
+ [double]$candidateTranslation[1] * $candidateTranslation[1] +
+ [double]$candidateTranslation[2] * $candidateTranslation[2]
+ )
+ if ($nonFinite.Count -eq 0 -and $length -ge $MinimumWorldTranslation) {
+ $cameraOuter = $candidateOuter
+ $translation = $candidateTranslation
+ break
+ }
+ $lastObservation = "translation length $length at 0x$('{0:X8}' -f $candidateOuter)"
+ if ($attempt -lt $CaptureAttempts -and $RetryIntervalMilliseconds -gt 0) {
+ Start-Sleep -Milliseconds $RetryIntervalMilliseconds
+ }
+ }
+ if ($null -eq $translation) {
+ throw "No world-space selector-0 transform after $CaptureAttempts samples (minimum translation length $MinimumWorldTranslation; last observation: $lastObservation)"
}
- $matrixBytes = Read-OriginalBytes $process ([int64]$cameraInterface + 32) 64
$words = for ($index = 0; $index -lt 16; $index++) {
[BitConverter]::ToUInt32($matrixBytes, $index * 4)
}
@@ -148,7 +199,9 @@ try {
process_id = $ProcessId
terrain_module_base = ('0x{0:X8}' -f $terrainBase)
terrain_camera_global_rva = '0x7355c'
+ terrain_camera_outer = ('0x{0:X8}' -f $cameraOuter)
selector0_words = @($words)
+ selector0_translation = @($translation)
# LegacyD3d7Projection carries a D3D7 RECT: left, top, right, bottom.
viewport = @(0, 0, $ViewportWidth, $ViewportHeight)
near_plane = $NearPlane