chore: simplify project to engine docs and tests

Remove planning and acceptance scaffolding while retaining the native Vulkan mission preview, format readers, runtime algorithms, and ordinary Rust tests. Keep the book aligned with the runnable project and validate checked-in shaders without generated tool metadata.
This commit is contained in:
2026-09-06 05:22:12 +04:00
parent da03e77f53
commit aa51f3574d
167 changed files with 667 additions and 23705 deletions
-111
View File
@@ -1,111 +0,0 @@
[CmdletBinding()]
param([Parameter(Mandatory = $true)][int]$ProcessId)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
# Read-only observer for ai.dll's `GetSuperAI` singleton array. It never sends
# input, writes memory, suspends, injects, or calls into the original process.
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
public static class FparkanAiInitCapture {
public const uint TH32CS_SNAPMODULE = 0x00000008;
public const uint TH32CS_SNAPMODULE32 = 0x00000010;
public const uint PROCESS_QUERY_INFORMATION = 0x00000400;
public const uint PROCESS_VM_READ = 0x00000010;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MODULEENTRY32 {
public uint dwSize, th32ModuleID, th32ProcessID, GlblcntUsage, ProccntUsage;
public IntPtr modBaseAddr;
public uint modBaseSize;
public IntPtr hModule;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string szModule;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szExePath;
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateToolhelp32Snapshot(uint flags, uint processId);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool Module32First(IntPtr snapshot, ref MODULEENTRY32 entry);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool Module32Next(IntPtr snapshot, ref MODULEENTRY32 entry);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(uint access, bool inheritHandle, uint processId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadProcessMemory(IntPtr process, IntPtr address,
[Out] byte[] buffer, IntPtr size, out IntPtr bytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr handle);
}
'@
function Read-Bytes([IntPtr]$Process, [Int64]$Address, [int]$Length) {
$bytes = [byte[]]::new($Length); $read = [IntPtr]::Zero
if (-not [FparkanAiInitCapture]::ReadProcessMemory($Process, [IntPtr]$Address,
$bytes, [IntPtr]$Length, [ref]$read) -or $read.ToInt64() -ne $Length) {
throw "ReadProcessMemory failed at 0x$('{0:X8}' -f $Address)"
}
$bytes
}
$snapshot = [FparkanAiInitCapture]::CreateToolhelp32Snapshot(
[FparkanAiInitCapture]::TH32CS_SNAPMODULE -bor [FparkanAiInitCapture]::TH32CS_SNAPMODULE32,
[uint32]$ProcessId)
$aiBase = $null
$modules = @()
try {
$entry = [FparkanAiInitCapture+MODULEENTRY32]::new()
$entry.dwSize = [Runtime.InteropServices.Marshal]::SizeOf([type][FparkanAiInitCapture+MODULEENTRY32])
if ([FparkanAiInitCapture]::Module32First($snapshot, [ref]$entry)) {
do {
$modules += [ordered]@{
name = $entry.szModule
base = $entry.modBaseAddr.ToInt64()
size = [int64]$entry.modBaseSize
}
if ($entry.szModule -ieq 'ai.dll') { $aiBase = $entry.modBaseAddr.ToInt64() }
$entry = [FparkanAiInitCapture+MODULEENTRY32]::new()
$entry.dwSize = [Runtime.InteropServices.Marshal]::SizeOf([type][FparkanAiInitCapture+MODULEENTRY32])
} while ([FparkanAiInitCapture]::Module32Next($snapshot, [ref]$entry))
}
} finally { [void][FparkanAiInitCapture]::CloseHandle($snapshot) }
if ($null -eq $aiBase) { throw "ai.dll is not loaded by process $ProcessId" }
$process = [FparkanAiInitCapture]::OpenProcess(
[FparkanAiInitCapture]::PROCESS_QUERY_INFORMATION -bor [FparkanAiInitCapture]::PROCESS_VM_READ,
$false, [uint32]$ProcessId)
if ($process -eq [IntPtr]::Zero) { throw "OpenProcess read-only failed" }
try {
# CreateSuperAI stores its tenth host-callback argument at DAT_100555e4.
$callbackBytes = Read-Bytes $process ($aiBase + 0x555e4) 4
$callback = [BitConverter]::ToUInt32($callbackBytes, 0)
$callbackModule = $modules | Where-Object {
$callback -ge $_.base -and [int64]$callback -lt ($_.base + $_.size)
} | Select-Object -First 1
# GetSuperAI(i) returns (&DAT_10055398)[i], with ai.dll preferred base 0x10000000.
$entries = Read-Bytes $process ($aiBase + 0x55398) (64 * 4)
$samples = for ($index = 0; $index -lt 64; $index++) {
$pointer = [BitConverter]::ToUInt32($entries, $index * 4)
if ($pointer -le 0x10000) { continue }
try {
$fields = Read-Bytes $process ([int64]$pointer) 0x88
[ordered]@{
index = $index
super_ai = ('0x{0:X8}' -f $pointer)
word_7c = [BitConverter]::ToUInt32($fields, 0x7c)
float_80 = [BitConverter]::ToSingle($fields, 0x80)
float_84 = [BitConverter]::ToSingle($fields, 0x84)
}
} catch { }
}
[ordered]@{
schema = 'fparkan-ai-init-v1'
process_id = $ProcessId
ai_module_base = ('0x{0:X8}' -f $aiBase)
handler30_callback = ('0x{0:X8}' -f $callback)
handler30_callback_module = if ($null -eq $callbackModule) { $null } else { $callbackModule.name }
handler30_callback_rva = if ($null -eq $callbackModule) { $null } else { ('0x{0:X}' -f ([int64]$callback - $callbackModule.base)) }
entries = @($samples)
} |
ConvertTo-Json -Depth 4 -Compress
} finally { [void][FparkanAiInitCapture]::CloseHandle($process) }
-213
View File
@@ -1,213 +0,0 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[int]$ProcessId,
[ValidateRange(1, 16384)]
[int]$ViewportWidth = 1024,
[ValidateRange(1, 16384)]
[int]$ViewportHeight = 768,
[ValidateRange(0.001, 1000.0)]
[double]$NearPlane = 0.5,
[ValidateRange(0.01, 100000.0)]
[double]$FarPlane = 700.0,
[ValidateRange(0.01, 3.13)]
[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
$ErrorActionPreference = 'Stop'
# This observer deliberately opens only PROCESS_QUERY_INFORMATION | PROCESS_VM_READ.
# It neither sends input nor writes, suspends, injects into, or calls the original game.
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
public static class FparkanOriginalCamera {
public const uint TH32CS_SNAPMODULE = 0x00000008;
public const uint TH32CS_SNAPMODULE32 = 0x00000010;
public const uint PROCESS_QUERY_INFORMATION = 0x00000400;
public const uint PROCESS_VM_READ = 0x00000010;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MODULEENTRY32 {
public uint dwSize;
public uint th32ModuleID;
public uint th32ProcessID;
public uint GlblcntUsage;
public uint ProccntUsage;
public IntPtr modBaseAddr;
public uint modBaseSize;
public IntPtr hModule;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string szModule;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szExePath;
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateToolhelp32Snapshot(uint flags, uint processId);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool Module32First(IntPtr snapshot, ref MODULEENTRY32 entry);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool Module32Next(IntPtr snapshot, ref MODULEENTRY32 entry);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(uint access, bool inheritHandle, uint processId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadProcessMemory(
IntPtr process,
IntPtr address,
[Out] byte[] buffer,
IntPtr size,
out IntPtr bytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr handle);
}
'@
function Get-LastWin32ErrorText {
$code = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
"$code ($([ComponentModel.Win32Exception]::new($code).Message))"
}
function Read-OriginalBytes {
param(
[IntPtr]$Process,
[Int64]$Address,
[int]$Length
)
$buffer = [byte[]]::new($Length)
$read = [IntPtr]::Zero
if (-not [FparkanOriginalCamera]::ReadProcessMemory(
$Process,
[IntPtr]$Address,
$buffer,
[IntPtr]$Length,
[ref]$read)) {
throw "ReadProcessMemory at 0x$('{0:X8}' -f $Address) failed: $(Get-LastWin32ErrorText)"
}
if ($read.ToInt64() -ne $Length) {
throw "ReadProcessMemory at 0x$('{0:X8}' -f $Address) returned $($read.ToInt64()) of $Length bytes"
}
$buffer
}
$snapshot = [FparkanOriginalCamera]::CreateToolhelp32Snapshot(
[FparkanOriginalCamera]::TH32CS_SNAPMODULE -bor [FparkanOriginalCamera]::TH32CS_SNAPMODULE32,
[uint32]$ProcessId
)
if ($snapshot -eq [IntPtr]::Zero -or $snapshot.ToInt64() -eq -1) {
throw "CreateToolhelp32Snapshot failed: $(Get-LastWin32ErrorText)"
}
$terrainBase = $null
try {
$module = [FparkanOriginalCamera+MODULEENTRY32]::new()
$module.dwSize = [Runtime.InteropServices.Marshal]::SizeOf([type][FparkanOriginalCamera+MODULEENTRY32])
if (-not [FparkanOriginalCamera]::Module32First($snapshot, [ref]$module)) {
throw "Module32First failed: $(Get-LastWin32ErrorText)"
}
do {
if ($module.szModule -ieq 'Terrain.dll') {
$terrainBase = $module.modBaseAddr.ToInt64()
break
}
$module = [FparkanOriginalCamera+MODULEENTRY32]::new()
$module.dwSize = [Runtime.InteropServices.Marshal]::SizeOf([type][FparkanOriginalCamera+MODULEENTRY32])
} while ([FparkanOriginalCamera]::Module32Next($snapshot, [ref]$module))
} finally {
[void][FparkanOriginalCamera]::CloseHandle($snapshot)
}
if ($null -eq $terrainBase) {
throw "Terrain.dll is not loaded by process $ProcessId"
}
$process = [FparkanOriginalCamera]::OpenProcess(
[FparkanOriginalCamera]::PROCESS_QUERY_INFORMATION -bor [FparkanOriginalCamera]::PROCESS_VM_READ,
$false,
[uint32]$ProcessId
)
if ($process -eq [IntPtr]::Zero) {
throw "OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ) failed: $(Get-LastWin32ErrorText)"
}
try {
# 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)"
}
$words = for ($index = 0; $index -lt 16; $index++) {
[BitConverter]::ToUInt32($matrixBytes, $index * 4)
}
[ordered]@{
schema = 'fparkan-legacy-camera-v1'
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
far_plane = $FarPlane
field_of_view_radians = $FieldOfViewRadians
} | ConvertTo-Json -Depth 4 -Compress
} finally {
[void][FparkanOriginalCamera]::CloseHandle($process)
}
-272
View File
@@ -1,272 +0,0 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[int]$ProcessId,
[ValidateRange(1, 60)]
[int]$CaptureAttempts = 6,
[ValidateRange(0, 1000)]
[int]$RetryIntervalMilliseconds = 100,
[ValidateRange(0x00010000, 0x7fff0000)]
[UInt32]$SearchStart = 0x01000000,
[ValidateRange(0x00010000, 0x7fff0000)]
[UInt32]$SearchEnd = 0x20000000
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
# This observer deliberately opens only PROCESS_QUERY_INFORMATION | PROCESS_VM_READ.
# It neither sends input nor writes, suspends, injects into, or calls the original game.
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
public static class FparkanTerrainShadeProbe {
public const uint TH32CS_SNAPMODULE = 0x00000008;
public const uint TH32CS_SNAPMODULE32 = 0x00000010;
public const uint PROCESS_QUERY_INFORMATION = 0x00000400;
public const uint PROCESS_VM_READ = 0x00000010;
public const uint MEM_COMMIT = 0x1000;
public const uint PAGE_NOACCESS = 0x01;
public const uint PAGE_GUARD = 0x100;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MODULEENTRY32 {
public uint dwSize;
public uint th32ModuleID;
public uint th32ProcessID;
public uint GlblcntUsage;
public uint ProccntUsage;
public IntPtr modBaseAddr;
public uint modBaseSize;
public IntPtr hModule;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string szModule;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szExePath;
}
[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_BASIC_INFORMATION {
public IntPtr BaseAddress;
public IntPtr AllocationBase;
public uint AllocationProtect;
public IntPtr RegionSize;
public uint State;
public uint Protect;
public uint Type;
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateToolhelp32Snapshot(uint flags, uint processId);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool Module32First(IntPtr snapshot, ref MODULEENTRY32 entry);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool Module32Next(IntPtr snapshot, ref MODULEENTRY32 entry);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(uint access, bool inheritHandle, uint processId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadProcessMemory(
IntPtr process, IntPtr address, [Out] byte[] buffer, IntPtr size, out IntPtr bytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualQueryEx(
IntPtr process, IntPtr address, out MEMORY_BASIC_INFORMATION buffer, IntPtr length);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr handle);
public static int FindU32(byte[] bytes, uint expected, int start) {
for (int index = start; index <= bytes.Length - 4; index += 4) {
if (BitConverter.ToUInt32(bytes, index) == expected) return index;
}
return -1;
}
public static ulong Fnv1a64(byte[] bytes, int offset, int length) {
ulong hash = 14695981039346656037UL;
unchecked {
for (int index = offset; index < offset + length; index++) {
hash ^= bytes[index];
hash *= 1099511628211UL;
}
}
return hash;
}
}
'@
function Get-LastWin32ErrorText {
$code = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
"$code ($([ComponentModel.Win32Exception]::new($code).Message))"
}
function Get-TerrainModuleBase {
param([int]$ProbeProcessId)
$snapshot = [FparkanTerrainShadeProbe]::CreateToolhelp32Snapshot(
[FparkanTerrainShadeProbe]::TH32CS_SNAPMODULE -bor [FparkanTerrainShadeProbe]::TH32CS_SNAPMODULE32,
[uint32]$ProbeProcessId
)
if ($snapshot -eq [IntPtr]::Zero -or $snapshot.ToInt64() -eq -1) {
throw "CreateToolhelp32Snapshot failed: $(Get-LastWin32ErrorText)"
}
try {
$module = [FparkanTerrainShadeProbe+MODULEENTRY32]::new()
$module.dwSize = [Runtime.InteropServices.Marshal]::SizeOf([type][FparkanTerrainShadeProbe+MODULEENTRY32])
if (-not [FparkanTerrainShadeProbe]::Module32First($snapshot, [ref]$module)) {
throw "Module32First failed: $(Get-LastWin32ErrorText)"
}
do {
if ($module.szModule -ieq 'Terrain.dll') {
return $module.modBaseAddr.ToInt64()
}
$module = [FparkanTerrainShadeProbe+MODULEENTRY32]::new()
$module.dwSize = [Runtime.InteropServices.Marshal]::SizeOf([type][FparkanTerrainShadeProbe+MODULEENTRY32])
} while ([FparkanTerrainShadeProbe]::Module32Next($snapshot, [ref]$module))
} finally {
[void][FparkanTerrainShadeProbe]::CloseHandle($snapshot)
}
throw "Terrain.dll is not loaded by process $ProbeProcessId"
}
function Read-Bytes {
param([IntPtr]$Process, [Int64]$Address, [int]$Length)
$buffer = [byte[]]::new($Length)
$read = [IntPtr]::Zero
if (-not [FparkanTerrainShadeProbe]::ReadProcessMemory(
$Process, [IntPtr]$Address, $buffer, [IntPtr]$Length, [ref]$read)) {
return $null
}
if ($read.ToInt64() -ne $Length) {
return $null
}
return $buffer
}
function Find-ShadeCache {
param(
[IntPtr]$Process,
[UInt32]$ExpectedVtable,
[UInt32]$Start,
[UInt32]$End,
[Int64]$ExcludedImageStart,
[Int64]$ExcludedImageEnd
)
$mbiSize = [Runtime.InteropServices.Marshal]::SizeOf([type][FparkanTerrainShadeProbe+MEMORY_BASIC_INFORMATION])
[Int64]$cursor = $Start
while ($cursor -lt $End) {
$mbi = [FparkanTerrainShadeProbe+MEMORY_BASIC_INFORMATION]::new()
$queried = [FparkanTerrainShadeProbe]::VirtualQueryEx(
$Process, [IntPtr]$cursor, [ref]$mbi, [IntPtr]$mbiSize
)
if ($queried -eq [IntPtr]::Zero) { break }
$base = $mbi.BaseAddress.ToInt64()
$size = $mbi.RegionSize.ToInt64()
if ($size -le 0) { break }
$next = $base + $size
if ($mbi.State -eq [FparkanTerrainShadeProbe]::MEM_COMMIT -and
($mbi.Protect -band [FparkanTerrainShadeProbe]::PAGE_NOACCESS) -eq 0 -and
($mbi.Protect -band [FparkanTerrainShadeProbe]::PAGE_GUARD) -eq 0) {
$regionStart = [Math]::Max($base, [Int64]$Start)
$regionEnd = [Math]::Min($next, [Int64]$End)
for ([Int64]$offset = $regionStart; $offset -lt $regionEnd; $offset += 65536) {
$length = [int][Math]::Min(65536, $regionEnd - $offset)
$bytes = Read-Bytes $Process $offset $length
if ($null -eq $bytes) { continue }
$searchIndex = 0
while ($searchIndex -le $bytes.Length - 4) {
$index = [FparkanTerrainShadeProbe]::FindU32($bytes, $ExpectedVtable, $searchIndex)
if ($index -lt 0) { break }
$candidate = $offset + $index
if ($candidate -lt $ExcludedImageStart -or $candidate -ge $ExcludedImageEnd) {
return $candidate
}
$searchIndex = $index + 4
}
}
}
$cursor = [Math]::Max($next, $cursor + 4096)
}
return $null
}
function Get-ShadeCacheSummary {
param([IntPtr]$Process, [Int64]$Cache, [byte[]]$Header)
$entryTable = [BitConverter]::ToUInt32($Header, 316)
$entryCount = [BitConverter]::ToUInt32($Header, 320)
$summary = [ordered]@{
materialized_entries = $null
profile_banks = @()
}
# The count field is runtime data. Keep a hard observer bound so a corrupt
# or stale candidate cannot turn a passive sample into an excessive read.
if ($entryTable -lt 0x1000 -or $entryCount -gt 100000) { return $summary }
$entryBytes = Read-Bytes $Process $entryTable ([int]($entryCount * 8))
if ($null -eq $entryBytes) { return $summary }
$materialized = 0
$banks = [System.Collections.Generic.SortedSet[int]]::new()
for ($index = 0; $index -lt $entryCount; $index++) {
$offset = $index * 8
if ([BitConverter]::ToUInt32($entryBytes, $offset) -ge 0x1000) {
$materialized++
[void]$banks.Add([int]$entryBytes[$offset + 4])
}
}
$summary.materialized_entries = $materialized
if ($banks.Count -eq 0) { return $summary }
$maxBank = $banks.Max
if ($maxBank -ge 100) { return $summary }
$bankBytes = Read-Bytes $Process ($Cache + 332) (($maxBank + 1) * 212)
if ($null -eq $bankBytes) { return $summary }
$profiles = [System.Collections.Generic.List[object]]::new()
foreach ($bank in $banks) {
$profiles.Add([ordered]@{
bank = $bank
fnv1a64 = [FparkanTerrainShadeProbe]::Fnv1a64($bankBytes, $bank * 212, 212).ToString()
})
}
$summary.profile_banks = @($profiles)
return $summary
}
if ($SearchEnd -le $SearchStart) { throw 'SearchEnd must exceed SearchStart' }
$terrainBase = Get-TerrainModuleBase $ProcessId
$process = [FparkanTerrainShadeProbe]::OpenProcess(
[FparkanTerrainShadeProbe]::PROCESS_QUERY_INFORMATION -bor [FparkanTerrainShadeProbe]::PROCESS_VM_READ,
$false,
[uint32]$ProcessId
)
if ($process -eq [IntPtr]::Zero) { throw "OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ) failed: $(Get-LastWin32ErrorText)" }
try {
$expectedVtable = [uint32]($terrainBase + 0x643d0)
$last = 'cache vtable was not present in readable scan range'
for ($attempt = 1; $attempt -le $CaptureAttempts; $attempt++) {
# The vtable value itself naturally appears in the Terrain image as
# relocation data; only a heap-resident object is a cache candidate.
$cache = Find-ShadeCache $process $expectedVtable $SearchStart $SearchEnd $terrainBase ($terrainBase + 0x100000)
if ($null -ne $cache) {
$header = Read-Bytes $process $cache 324
if ($null -ne $header) {
$summary = Get-ShadeCacheSummary $process $cache $header
[ordered]@{
schema = 'fparkan-terrain-shade-cache-v1'
process_id = $ProcessId
terrain_module_base = ('0x{0:X8}' -f $terrainBase)
cache_vtable_rva = '0x643d0'
cache_object = ('0x{0:X8}' -f $cache)
result_view = ('0x{0:X8}' -f [BitConverter]::ToUInt32($header, 24))
entry_table = ('0x{0:X8}' -f [BitConverter]::ToUInt32($header, 316))
entry_count = [BitConverter]::ToUInt32($header, 320)
materialized_entries = $summary.materialized_entries
profile_banks = $summary.profile_banks
scan_attempt = $attempt
} | ConvertTo-Json -Compress
exit 0
}
$last = "cache candidate 0x$('{0:X8}' -f $cache) became unreadable"
}
if ($attempt -lt $CaptureAttempts -and $RetryIntervalMilliseconds -gt 0) {
Start-Sleep -Milliseconds $RetryIntervalMilliseconds
}
}
throw "No live GetShade cache after $CaptureAttempts scans ($last)"
} finally {
[void][FparkanTerrainShadeProbe]::CloseHandle($process)
}
-23
View File
@@ -1,23 +0,0 @@
// Emits the GOG CreateSuperAI export to recover the mission-to-SuperAI
// initialization boundary. Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiCreateSuperAi extends GhidraScript {
private static final long ADDRESS = 0x1000f710L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI CreateSuperAI =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 120, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,25 +0,0 @@
// Emits the decompiled AI expression evaluator containing the recovered
// tag 1..5 dispatch. Run through Ghidra headless analysis only; it never
// mutates the original PE image.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiExpressionDispatcher extends GhidraScript {
private static final long ADDRESS = 0x10005180L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionContaining(address);
println("===== AI expression dispatcher =====");
if (function == null) { println("missing"); return; }
println("entry=" + function.getEntryPoint());
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits the x87-to-integer helper called by Handler(19). Run headless; the
// original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiFtol extends GhidraScript {
private static final long ADDRESS = 0x1001df70L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI x87 __ftol helper =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits the GOG ai.dll GetSuperAI export to recover the live singleton
// boundary for read-only handler-input capture.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiGetSuperAi extends GhidraScript {
private static final long ADDRESS = 0x1000f780L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI GetSuperAI =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits the GOG AI script-bundle loader, discovered from references to
// "MISSIONS\\SCRIPTS\\" and ".scr". Run headless; original input stays read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiScriptLoader extends GhidraScript {
private static final long ADDRESS = 0x10001000L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI script loader =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,27 +0,0 @@
// Emits path literals used by the GOG AI script loader at 0x10001000.
// Run headless; the original PE remains read only.
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
public class ExportAiScriptLoaderStrings extends GhidraScript {
private static final long[] ADDRESSES = {
0x10038a88L, 0x10038a9cL, 0x10038aa4L, 0x10038aacL,
0x10038ab4L, 0x10038abcL, 0x10038ad0L, 0x10038ad8L,
0x10038ae0L
};
@Override
public void run() throws Exception {
for (long value : ADDRESSES) {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(value);
byte[] bytes = new byte[256];
int count = currentProgram.getMemory().getBytes(address, bytes);
StringBuilder text = new StringBuilder();
for (int index = 0; index < count && bytes[index] != 0; index++) {
text.append((char) (bytes[index] & 0xff));
}
println(address + " = \"" + text + "\"");
}
}
}
@@ -1,23 +0,0 @@
// Emits the immediate .scr package reader called by the AI script loader.
// Run through Ghidra headless analysis; the original PE is never modified.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiScriptPackageReader extends GhidraScript {
private static final long ADDRESS = 0x10011B20L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI .scr package reader =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,23 +0,0 @@
// Emits the SuperAI constructor called by CreateSuperAI. Run headless; the
// original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiSuperAiConstructor extends GhidraScript {
private static final long ADDRESS = 0x10001000L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI SuperAI constructor =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 120, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits the text `.var` loader called after the AI script loader selects a
// bundle-local or shared varset. Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVarSetLoader extends GhidraScript {
private static final long ADDRESS = 0x10011ea0L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI varset loader =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits the actual text-to-varset parser called by the AI varset loader.
// Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVarSetParser extends GhidraScript {
private static final long ADDRESS = 0x100174a0L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI varset parser =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,23 +0,0 @@
// Emits the u32 resolver used by corpus-reachable Handler(30) for indexed
// varset records. Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVarSetU32Resolver extends GhidraScript {
private static final long ADDRESS = 0x10013570L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI varset u32 resolver =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits the first function in the AI DLL's verified 73-entry VM handler table.
// Run through Ghidra headless analysis; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler0 extends GhidraScript {
private static final long ADDRESS = 0x10008034L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM handler 0 =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits Handler(1), the second function in the AI DLL's verified 73-entry VM table.
// Run through Ghidra headless analysis; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler1 extends GhidraScript {
private static final long ADDRESS = 0x10007fd0L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM Handler(1) =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits Handler(15), a frequent non-sentinel selector in the GOG compiled
// script corpus. Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler15 extends GhidraScript {
private static final long ADDRESS = 0x10008054L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM Handler(15) =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits Handler(19), the first instruction in both AutoDemo default-script
// Init events. Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler19 extends GhidraScript {
private static final long ADDRESS = 0x1000aa38L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM Handler(19) =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,23 +0,0 @@
// Emits Handler(19)'s common x87 varset setter. Run headless; the original PE
// remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler19Setter extends GhidraScript {
private static final long ADDRESS = 0x10013770L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM Handler(19) setter =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,23 +0,0 @@
// Emits the common varset storage helper reached by Handler(19)'s setter.
// Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler19SetterCallee extends GhidraScript {
private static final long ADDRESS = 0x10012fe0L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM Handler(19) setter storage helper =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,25 +0,0 @@
// Emits the two direct callees recovered from AI VM Handler(1).
// Run through Ghidra headless analysis; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler1Callees extends GhidraScript {
private static final long[] ADDRESSES = { 0x10002d30L, 0x10013190L };
@Override
public void run() throws Exception {
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
for (long value : ADDRESSES) {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(value);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM Handler(1) callee " + address + " =====");
if (function == null) { println("missing"); continue; }
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
}
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits Handler(2), the third function in the AI DLL's verified 73-entry VM table.
// Run through Ghidra headless analysis; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler2 extends GhidraScript {
private static final long ADDRESS = 0x10009610L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM Handler(2) =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,23 +0,0 @@
// Emits the opaque direct callee reached by corpus-reachable AI VM Handler(2).
// Run through Ghidra headless analysis; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler2Callee extends GhidraScript {
private static final long ADDRESS = 0x100059f0L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM Handler(2) direct callee =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,27 +0,0 @@
// Emits the direct post-insertion dispatcher reached by the corpus-reachable
// AI VM Handler(2) scheduler boundary. Run headless; the original PE is read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler2Dispatch extends GhidraScript {
private static final long[] ADDRESSES = {
0x1000f920L, 0x10004be0L, 0x10004d00L, 0x10004db0L
};
@Override
public void run() throws Exception {
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
for (long value : ADDRESSES) {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(value);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI Handler(2) post-insertion helper " + address + " =====");
if (function == null) { println("missing"); continue; }
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
}
decompiler.dispose();
}
}
@@ -1,28 +0,0 @@
// Emits record construction, equality, refresh and insertion helpers called by
// the corpus-reachable AI VM Handler(2) scheduler boundary.
// Run through Ghidra headless analysis; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler2SchedulerHelpers extends GhidraScript {
private static final long[] ADDRESSES = {
0x10004e50L, 0x10004c50L, 0x10005070L, 0x100073e0L
};
@Override
public void run() throws Exception {
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
for (long value : ADDRESSES) {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(value);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI Handler(2) scheduler helper " + address + " =====");
if (function == null) { println("missing"); continue; }
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
}
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits Handler(30), the most frequent non-sentinel selector in the GOG
// compiled-script corpus. Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler30 extends GhidraScript {
private static final long ADDRESS = 0x1000c266L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM Handler(30) =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits Handler(8), a frequent non-sentinel selector in the GOG compiled
// script corpus. Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler8 extends GhidraScript {
private static final long ADDRESS = 0x10009b0dL;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM Handler(8) =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,25 +0,0 @@
// Emits the two non-trivial local callees reached by Handler(8). Run headless;
// the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler8Callees extends GhidraScript {
private static final long[] ADDRESSES = {0x10002e90L, 0x10005710L};
@Override
public void run() throws Exception {
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
for (long value : ADDRESSES) {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(value);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI Handler(8) callee " + address + " =====");
if (function == null) { println("missing"); continue; }
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
}
decompiler.dispose();
}
}
@@ -1,25 +0,0 @@
// Emits the state-transition helpers selected by Handler(8). Run headless;
// the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmHandler8Transitions extends GhidraScript {
private static final long[] ADDRESSES = {0x10005010L, 0x10005040L};
@Override
public void run() throws Exception {
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
for (long value : ADDRESSES) {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(value);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI Handler(8) transition " + address + " =====");
if (function == null) { println("missing"); continue; }
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
}
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits the routine that receives the AI VM's verified 73-entry handler table.
// Run through Ghidra headless analysis; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAiVmTableInstall extends GhidraScript {
private static final long ADDRESS = 0x10011E70L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== AI VM handler table install =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,21 +0,0 @@
// Emits the function containing the observed AniMesh Control-loader sequence.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportAniMeshControlCaller extends GhidraScript {
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(0x100032e7L);
Function function = currentProgram.getFunctionManager().getFunctionContaining(address);
println("===== AniMesh Control caller =====");
if (function == null) { println("missing"); return; }
println("entry=" + function.getEntryPoint());
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
-35
View File
@@ -1,35 +0,0 @@
// Emits decompiled C for the stable public Control.dll exports.
// Run only through Ghidra headless analysis; it does not modify the input PE.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportControlFunctions extends GhidraScript {
private static final String[] NAMES = {
"InitializeSettings", "LoadControlSystem", "LoadPhysicalModel",
"CreateCollManager", "CreateCollObject"
};
private static final long[] ADDRESSES = {
0x10032260L, 0x10032280L, 0x10032580L, 0x100325d0L, 0x10032600L
};
@Override
public void run() throws Exception {
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
for (int index = 0; index < NAMES.length; index++) {
Address address = currentProgram.getAddressFactory()
.getDefaultAddressSpace().getAddress(ADDRESSES[index]);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("\n===== " + NAMES[index] + " =====");
if (function == null) {
println("missing");
continue;
}
println(decompiler.decompileFunction(function, 60, monitor)
.getDecompiledFunction().getC());
}
decompiler.dispose();
}
}
-23
View File
@@ -1,23 +0,0 @@
// Emits the live CreateSuperAI host callback selected by the GOG AutoDemo.
// Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportIron3dAiCallback extends GhidraScript {
private static final long ADDRESS = 0x100611d0L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== Iron3D CreateSuperAI callback =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 120, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,23 +0,0 @@
// Emits the command-one consumer reached from the recovered CreateSuperAI
// host callback. Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportIron3dAiCallbackCommand1 extends GhidraScript {
private static final long ADDRESS = 0x10095160L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== Iron3D CreateSuperAI callback command 1 =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 120, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,23 +0,0 @@
// Emits command one's selected-node dispatch callee. Run headless; the
// original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class ExportIron3dAiCallbackCommand1Dispatch extends GhidraScript {
private static final long ADDRESS = 0x10095600L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
println("===== Iron3D callback command 1 node dispatch =====");
if (function == null) { println("missing"); return; }
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
println(decompiler.decompileFunction(function, 120, monitor).getDecompiledFunction().getC());
decompiler.dispose();
}
}
@@ -1,29 +0,0 @@
// Locates callers that reference the stable AI script-loader literals.
// Run through Ghidra headless analysis; the original PE is read only.
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceManager;
public class FindAiScriptLoaderReferences extends GhidraScript {
private static final String[] NEEDLES = {".scr", "MISSIONS\\SCRIPTS\\"};
@Override
public void run() throws Exception {
Memory memory = currentProgram.getMemory();
ReferenceManager references = currentProgram.getReferenceManager();
for (String needle : NEEDLES) {
byte[] bytes = (needle + "\0").getBytes("US-ASCII");
Address address = memory.findBytes(memory.getMinAddress(), memory.getMaxAddress(), bytes, null, true, monitor);
println("===== " + needle + " =====");
if (address == null) { println("missing"); continue; }
println("literal=" + address);
for (Reference reference : references.getReferencesTo(address)) {
Function caller = currentProgram.getFunctionManager().getFunctionContaining(reference.getFromAddress());
println("reference=" + reference.getFromAddress() + " caller=" + (caller == null ? "missing" : caller.getEntryPoint()));
}
}
}
}
@@ -1,27 +0,0 @@
// Finds writers and readers of Handler(30)'s callback pointer, then decompiles
// their containing functions. Run headless; the original PE remains read only.
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.symbol.Reference;
public class FindAiVmHandler30Callback extends GhidraScript {
private static final long ADDRESS = 0x100555e4L;
@Override
public void run() throws Exception {
Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(ADDRESS);
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(currentProgram);
for (Reference reference : currentProgram.getReferenceManager().getReferencesTo(address)) {
Function function = currentProgram.getFunctionManager()
.getFunctionContaining(reference.getFromAddress());
println("===== callback reference " + reference.getFromAddress() + " =====");
if (function == null) { println("no containing function"); continue; }
println(decompiler.decompileFunction(function, 60, monitor).getDecompiledFunction().getC());
}
decompiler.dispose();
}
}