From b0081fd81af0cc98ec2d262259bf49202bb1df8d Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Thu, 27 Aug 2026 00:40:53 +0000 Subject: [PATCH] re: refute the OB entity-flag on the FULL enumeration -- the stationary-entity hole is closed Every earlier refutation in this file carried the caveat that entities2.typed types entities by their position CHANGING, so a stationary objective is invisible to it. This session's definition-pointer enumeration does not have that limit, so the sweep was re-run against it (ob_flag_all.py, guarded route, stage asserted, HUD cropped beside each sample): A: HUD 004, 147 entities -> 152 candidates B: HUD 012, 133 entities -> 15 candidates intersection: 1 The lone survivor pos+0x0250 = 239d6732 is the same offset AND identical value this file already characterised as a per-group word. Membership test: all 12 holders are UN_e010_ADAN_Attacker_S, 12 of 16 live attackers. It is a squad parameter, and it survived only because that population equalled the counter at both samples. Also reconfirms "not a class head-count" on 147 entities including capital ships. Trap recorded: the first sweep reported 298 entities and a class with head-count exactly 4 -- a perfect-looking hit that was pure artifact. Deduping by ADDRESS leaves the measured exact 2x duplication (pairs 0x1000 apart, byte-identical positions) intact and doubles every population. Dedup on the position VALUE. --- docs/re/mission-freeze-and-ob-flag.md | 60 +++++++++++++++++++++++++++ tools/re-capture/ob_flag_all.py | 56 +++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 tools/re-capture/ob_flag_all.py diff --git a/docs/re/mission-freeze-and-ob-flag.md b/docs/re/mission-freeze-and-ob-flag.md index 7a62b981..dd36b0bf 100644 --- a/docs/re/mission-freeze-and-ob-flag.md +++ b/docs/re/mission-freeze-and-ob-flag.md @@ -309,3 +309,63 @@ filter used, but the confirming screenshot at that instant did not agree. **What is still not ruled out** is unchanged from the word-level pass: anything outside the window, and anything on entities `entities2.typed` cannot see (it types by position *changing*, so stationary objectives are invisible). + +--- + +## 🔴 2026-08-27 — the last hole is closed: refuted on the FULL entity enumeration too + +Every refutation above carried the same caveat: *"anything on entities +`entities2.typed` cannot see — it types by position **changing**, so a stationary +objective is invisible to it."* That hole is now closed, because this session +established an enumeration that does not have it — search for the **definition +pointer** and take `position = hit - 0x130` +([`two-entity-enumerations.md`](two-entity-enumerations.md)), which finds every +entity moving or not, capital ships and the `ACROPOLIS` included. + +Re-run with that enumeration ([`ob_flag_all.py`](../../tools/re-capture/ob_flag_all.py)), +guarded route, stage asserted, HUD cropped beside every sample: + +| pass | HUD | entities | candidates | +|---|---|---|---| +| A | `004` | **147** | 152 | +| B | `012` | 133 | 15 | +| **intersection** | | | **1** | + +The lone survivor is **`pos+0x0250 = 239d6732`** — and it is **the same offset and +the identical value** this file already characterised as a per-group word. Put to +the membership test rather than admired: + +``` +holders of pos+0x250 == 239d6732 : 12 + 12 UN_e010_ADAN_Attacker_S (of 16 live attackers) +``` + +**All twelve are attackers.** It is a squad parameter or pointer shared by part of +the `e010` population, and it survived only because that population happened to +equal the counter at both samples. Not an objective flag. + +### What this adds + +* 🔴 The **"objectives are marked in the entity object"** family is now refuted on + an enumeration that **includes stationary entities** — the limit every earlier + pass had to state is gone, not merely narrowed. +* 🔴 **"Not a class head-count" is reconfirmed on 147 entities including capital + ships**: no class had a population of 4 at A or 12 at B. +* ⚠️ **A trap worth having.** The first run of this sweep reported **298** + entities and a class with head-count exactly 4 (`UN_f105_TCAF_Cruiser`) — an + apparently perfect hit. It was an artifact: deduplicating by *address* leaves + this session's measured **exact 2× duplication** (pairs `0x1000` apart holding + byte-identical positions) intact, so every entity is counted twice and every + population doubles. Deduplicating by **position value** gives 147 and the + spurious class hit disappears. Any sweep of this shape must dedup on the + position triple, not the address. + +### Still open + +* ❔ Anything **outside** the `-0x400 … +0xC00` window — unchanged. +* ❔ Sample A's holder membership was not re-checked (the world had moved on), so + "4 attackers at A" is inferred from B's membership plus this file's earlier + identification of the same word, not measured. +* ❔ What the counter *does* count. Still unexplained: it read `004` with ~118 + hostiles live, rose `004 → 012` as waves spawned, held through a third of the + hostile population dying, and decremented once when the player killed. diff --git a/tools/re-capture/ob_flag_all.py b/tools/re-capture/ob_flag_all.py new file mode 100644 index 00000000..4d312d6e --- /dev/null +++ b/tools/re-capture/ob_flag_all.py @@ -0,0 +1,56 @@ +"""Flag sweep over the DEFINITION-POINTER enumeration (sees stationary entities).""" +import sys, os, struct, collections, json +sys.path.insert(0, '/tmp/rc') +import gmem, gworld, entities2 as E + +LO, HI = -0x400, 0xC00 + +def entities(w): + """Every entity via its definition pointer: position = hit - 0x130.""" + defs = E.definitions(w) + lo, hi = gmem.va_to_off(E.ENT_VA_LO), gmem.va_to_off(E.ENT_VA_HI) + out = [] + for pat, nm in defs.items(): + for a, b in gmem.extents(w.fd, w.size): + a, b = max(a, lo), min(b, hi) + if b <= a: continue + blob = os.pread(w.fd, b - a, a); i = 0 + while True: + j = blob.find(pat, i) + if j < 0: break + off = a + j + if off % 4 == 0: out.append((off - 0x130, nm)) + i = j + 4 + return out + +def sweep(w, ents, n): + """(offset, value) pairs shared by exactly n entities.""" + tally = collections.defaultdict(collections.Counter) + for pos, nm in ents: + buf = os.pread(w.fd, HI - LO, pos + LO) + if len(buf) < HI - LO: continue + for k in range(0, HI - LO, 4): + tally[LO + k][struct.unpack('>I', buf[k:k+4])[0]] += 1 + return {(o, v) for o, c in tally.items() for v, k in c.items() if k == n} + +if __name__ == '__main__': + n = int(sys.argv[1]); tag = sys.argv[2] + w = gworld.World() + ents = entities(w) + # Dedup by POSITION VALUE, not by address: this session measured an exact + # 2x duplication (pairs 0x1000 apart holding byte-identical positions), so + # deduping on the address leaves every entity counted twice and the + # "exactly N agree" test can never match. + ded = {} + for pos, nm in ents: + b = os.pread(w.fd, 12, pos) + if len(b) < 12: continue + key = (nm, struct.unpack('>fff', b)) + ded.setdefault(key, pos) + ents = [(pos, nm) for (nm, _), pos in ded.items()] + print('entities (definition-pointer enumeration):', len(ents)) + c = collections.Counter(nm for _, nm in ents) + print('classes whose head-count equals %d: %s' % (n, [k for k, v in c.items() if v == n] or 'NONE')) + cand = sweep(w, ents, n) + print('offsets where exactly %d entities agree: %d' % (n, len(cand))) + json.dump([[o, v] for o, v in sorted(cand)], open('/tmp/flag_%s.json' % tag, 'w'))