re: bounded scan fixes the freeze; mission-over branch confirmed on the oracle

Bounding the pointer scan to 0xBC000000-0xBD000000 (with a full-sweep fallback)
drops find_mission from a ~371MB walk to 0.7s. The run then went 694s with the
probe attached and NO freeze, against 3-of-3 frozen inside ~4 minutes with the
unbounded version. n=1, but the first probe-attached run to survive.

State encoding pinned to three points: 1 = not yet deployed, 2 = active,
4 = destroyed. ADN111 caught going 2 -> 4 at 433s while the active count fell
36 -> 27.

The phase ended at 694.9s WITHOUT the ordinal advancing, and every field matches
the branch read statically from sub_82260710: [phase+300]=2 (last-phase flag),
[mission+20]=0 (mission-over state), [phase+196]=1 (finished), [mission+40]=1
(unchanged). The static state machine is confirmed on the live oracle for the
mission-over half.

But this was a LOSS, not a clear: GAME OVER on screen, escort at 35.7%, pilot
DEAD at 676s, and two of the three objective squadrons still at state 2. So the
'destroy all three clears phase 1' prediction remains untested. What is
established is that the else-branch is the only route to phase 2 and needs
[phase+300] != 2 when the phase ends.

Five attempts, still no phase advance observed -- the obstacle is now keeping the
escort alive, not the freeze or the instrument.
This commit is contained in:
Sylpheed RE agent
2026-08-25 16:07:26 +00:00
parent 22b6541cd9
commit 47af78d668
3 changed files with 115 additions and 1 deletions

View File

@@ -34,6 +34,11 @@ sys.path.insert(0, __file__.rsplit('/', 1)[0])
import gmem
import isl
# Bound for the pointer scan; observed ScriptMission addresses: 0xBC7A2A20 (x3),
# 0xBC79C960. Widened generously either side -- a miss falls back to the full
# sweep rather than failing.
PTR_WINDOW = (0xBC000000, 0xBD000000)
HDR_LEN = 20 # version, +4, code offset, symtab1, symtab2 -- distinctive
@@ -42,9 +47,25 @@ def _fd_extents(f, size):
return gmem.extents(f.fileno(), size)
def _find(f, size, needle):
def _find(f, size, needle, window=None):
"""Search allocated extents for `needle`.
`window` is an optional (lo_va, hi_va) guest-address bound. The full sweep
covers ~371 MB and is the leading suspect for the in-mission freeze
(mission-freeze-resume-spin.md); every ScriptMission observed so far has sat
in 0xBC79xxxx-0xBC7Axxxx, so bounding the POINTER scan cuts it by ~10x while
still finding the object. The header scan stays unbounded -- the .ssb has
been loaded at two different addresses across runs, so it cannot be bounded
on this evidence.
"""
out = []
for start, end in _fd_extents(f, size):
if window is not None:
vs = gmem.va_to_off(window[0]), gmem.va_to_off(window[1])
if vs[0] is not None and vs[1] is not None:
if end <= vs[0] or start >= vs[1]:
continue
start = max(start, vs[0]); end = min(end, vs[1])
f.seek(start)
remaining, base, prev = end - start, start, b''
while remaining > 0:
@@ -84,6 +105,19 @@ def find_mission(f, size, ssb):
hdr = ssb[:HDR_LEN]
code_off = struct.unpack_from('>I', ssb, 0x08)[0]
sym1_off = struct.unpack_from('>I', ssb, 0x0C)[0]
for off in _find(f, size, hdr):
for filebase in gmem.off_to_vas(off):
code_base = filebase + code_off
want_44 = filebase + sym1_off + 4
for poff in _find(f, size, struct.pack('>I', code_base & 0xFFFFFFFF),
window=PTR_WINDOW):
if poff % 4:
continue
for pva in gmem.off_to_vas(poff):
m = pva - 24
if u32(f, m + 44) == want_44:
return m, filebase
# nothing in the window -- fall back to the full sweep rather than fail
for off in _find(f, size, hdr):
for filebase in gmem.off_to_vas(off):
code_base = filebase + code_off