re: confirm the cmesh<->model link via GameResourceID; world unit is oracle-blocked

Two follow-ups on yesterday's^Wthis morning's CollisionSet write-up.

1. The _cmesh <-> render-model link, which I recorded as UNTESTED because
   matching stems against .xbg object names covered 4 of 158.  The disc keeps
   only one build manifest, so that corpus was never going to answer it.  The
   right corpus is the GameResourceID field of the DefTables / GP_MAIN_GAME
   records -- 480 distinct values.  Against those, with a control that shuffles
   the characters of each stem:

       ship/mob stems prefixed by a real resource id   108/112 = 96.4%
       same stems, characters shuffled  (control)        0/112 =  0.0%
       asteroid stems prefixed (expected none)           0/46

   So a CollisionSet entry is <GameResourceID>[_<part>]_cmesh.  The 0/46 on
   asteroids matters as much as the 108/112: a test that fired on everything
   would be the bound-check hazard again.

2. The world unit.  Sweeping every pak for a name carrying a kilometre figure
   returns mapmesh_box_500km.col/.rgn and nothing else -- 162 references, all to
   that one pair.  The reading rests on a single filename with no corroborating
   instance anywhere in the data, so no static test can settle it; marking it
   blocked on the oracle rather than leaving it as an open static question.

   My objection's premise did survive: rou_e010 is a real GameResourceID and
   e010_ADAN_Attacker_S is in the stage tables, so the 133-unit mesh does belong
   to a craft the game calls an attacker.  Whether the trailing _S means "small"
   is a further guess (there are _EX4 / _HF / _HF_Wayne variants), so it stays
   suggestive rather than evidence.
This commit is contained in:
Sylpheed RE agent
2026-08-26 09:41:46 +00:00
parent cc1f9e6734
commit 83d019515f
4 changed files with 130 additions and 10 deletions

View File

@@ -437,8 +437,15 @@ search cannot find a *schedule*.
proxies (`_bdy`/`_brg`/`_eng`/`_wep`/`_sld`) plus 46 stage asteroids.
⚠️ **This downgraded the "1 unit = 1 m" claim to 🟡** — it implies a craft the
tables call *small* is 133 m and the largest object 447 km (89 % of the arena).
🟡 The `_cmesh`↔render-model name link is **untested, not confirmed**: only one
`.xbg` build manifest survives, so the check covers 4/158.
**The `_cmesh`model link is now CONFIRMED** (upgraded from 🟡 the same day):
the right corpus was the **`GameResourceID`** field, not the `.xbg` manifests —
**108/112** ship/mob stems are prefixed by one of the 480 resource ids, against
a **0/112** shuffled-character control, and correctly **0/46** on asteroids. A
`CollisionSet` entry is `<GameResourceID>[_<part>]_cmesh`.
🔴 **The world unit is BLOCKED on the oracle.** Sweeping every pak for a
km-bearing name returns `mapmesh_box_500km` and **nothing else** (162 refs, all
that one pair) — the convention has no second instance, so no static test can
settle it. Needs a measured in-game traversal against the flight model.
`tools/re-capture/collisionset.py verify|list|obj`
([`data/collisionset.txt`](data/collisionset.txt)).
❔ Still open: no material/surface-type field exists in the record, and the

View File

@@ -169,3 +169,9 @@ PASS
158 meshes, 90836 triangles
PASS
distinct GameResourceID values : 480
ship/mob stems prefixed by a resource id: 108/112 = 96.4% (exact match 55)
same stems, characters shuffled (control): 0/112 = 0.0%
asteroid stems prefixed (expected none) : 0/46
PASS

View File

@@ -67,10 +67,30 @@ weapon and shield — the same sub-part vocabulary as the `XBG7` render models,
these are per-part collision proxies. The asteroid meshes carry exactly the
stage prefixes that have an `_AsteroidVolume_wp` `MCOL`.
🟡 The name correspondence to the render models is **not demonstrated**. Only one
`XBG2` build manifest survives on the disc (`machines\rou_e104\…`), so matching
`_cmesh` stems against `.xbg` object names covers 4 of 158 — no coverage, and
therefore no evidence either way. It is not a refutation; it is an untested claim.
### ✅ The names *are* the game's own resource ids (upgraded from 🟡 the same day)
The previous write-up recorded this link as **untested**, because matching
`_cmesh` stems against `.xbg` object names covered only 4 of 158 — the disc keeps
just one build manifest (`machines\rou_e104\…`), so that test had no power in
either direction.
The right corpus is not the manifests, it is the **`GameResourceID` field** of
the `DefTables` / `GP_MAIN_GAME` records — 480 distinct values, e.g.
`GameResourceID = 'rou_e010'`. Against those, with a control that shuffles the
characters of each stem:
| | |
|---|---|
| ship / mob stems prefixed by a real `GameResourceID` | **108 / 112 = 96.4 %** (55 exact) |
| same stems, characters shuffled (control) | **0 / 112 = 0.0 %** |
| asteroid stems prefixed (expected none — they are not resources) | **0 / 46** |
So a `CollisionSet` entry is `<GameResourceID>[_<part>]_cmesh`: the collision
proxy for one part of one game resource. The four unmatched stems are
`rou_e303_wep`, `rou_f301_body`, `rou_f302`, `rou_f303_body`.
The 0 / 46 on asteroids is worth keeping: a test that fires on everything would
be the bound-check hazard again. It separates the two families correctly.
## 🟡 The world unit — the metre reading does not survive this file
@@ -95,9 +115,22 @@ game's units really are metres and its ships are kilometre-scale, or `500km` in
that filename names something other than the box's span. The 500 000 arithmetic
stands; the *interpretation* does not have independent support.
An independent scale reference the flight model's speed values against a
measured traversal time in the running game — would settle it, and has not been
run.
**And there is no second instance of the convention to check against.** Sweeping
every `.pak` on the disc for a name carrying a kilometre figure returns
`mapmesh_box_500km.col` / `.rgn` and nothing else — 162 references, all to that
one pair. So the reading rests on a single filename, with no corroborating
example anywhere in the data. That is not a refutation; it is the absence of any
static way to settle it.
My objection's own premise did survive its check: `rou_e010` is a real
`GameResourceID`, and `e010_ADAN_Attacker_S` appears in the stage tables, so the
133-unit mesh does belong to a craft the game calls an attacker. (Whether the
trailing `_S` means "small" is a further guess — there are `_EX4`, `_HF` and
`_HF_Wayne` variants of the same unit — so it is suggestive, not evidence.)
🔴 **Blocked on the oracle.** An independent scale reference — the flight model's
speed values against a measured traversal time in the running game — is the only
route left, and it needs an emulator run rather than the disc.
Reproduce with `tools/re-capture/collisionset.py verify` / `list`
([`../data/collisionset.txt`](../data/collisionset.txt)); `collisionset.py obj`

View File

@@ -7,6 +7,7 @@ asteroids), separate from the per-map `MCOL` hull.
./collisionset.py list <MiscBin.pak> # the 158 meshes
./collisionset.py verify <MiscBin.pak> # the checks in the doc
./collisionset.py link <extract root> # cmesh names <-> GameResourceID
./collisionset.py obj <MiscBin.pak> <name> <out.obj>
A record is
@@ -111,11 +112,84 @@ def cmd_obj(pak, want, out):
raise SystemExit(f"no mesh named {want!r}")
def cmd_link(root):
"""Are the mesh names the game's own resource ids? With a control."""
import random
import re
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from unitgroup import tag_hash
grid = tag_hash("GameResourceID")
ids = set()
for pak in (os.path.join(root, "hidden", "DefTables.pak"),
os.path.join(root, "dat", "GP_MAIN_GAME_E.pak")):
for _, blob in pak_entries(pak):
p = _idxd(blob)
if not p:
continue
for key, _, voff in p["fields"]:
if key == grid:
v = _pool(p, voff)
if v:
ids.add(v)
stems = [re.sub(r"_(cmesh|c)$", "", n)
for n, _, _, _ in meshes(os.path.join(root, "hidden", "MiscBin.pak"))[0]]
ship = [s for s in stems if s[:4] in ("rou_", "rob_", "mob_")]
rock = [s for s in stems if s not in ship]
pre = sum(any(s.startswith(i) for i in ids) for s in ship)
exact = sum(s in ids for s in ship)
random.seed(3)
ctl = 0
for s in ship:
t = list(s)
random.shuffle(t)
ctl += any("".join(t).startswith(i) for i in ids)
print(f"distinct GameResourceID values : {len(ids)}")
print(f"ship/mob stems prefixed by a resource id: {pre}/{len(ship)} "
f"= {100*pre/len(ship):.1f}% (exact match {exact})")
print(f"same stems, characters shuffled (control): {ctl}/{len(ship)} "
f"= {100*ctl/len(ship):.1f}%")
print(f"asteroid stems prefixed (expected none) : "
f"{sum(any(s.startswith(i) for i in ids) for s in rock)}/{len(rock)}")
return pre / len(ship) > 0.9 and ctl == 0
def _idxd(b):
if b[:4] != b"IDXD" or len(b) < 0x10:
return None
n = struct.unpack_from(">I", b, 4)[0]
if not 0 < n <= 200_000:
return None
recs, o = [(None,) + struct.unpack_from(">III", b, 0x0C)], 0x18
if o + 16 * (n - 1) + 4 > len(b):
return None
for _ in range(n - 1):
recs.append(struct.unpack_from(">IIII", b, o))
o += 16
m = struct.unpack_from(">I", b, o)[0]
o += 4
if o + 12 * m + 4 > len(b):
return None
return dict(recs=recs,
fields=[struct.unpack_from(">III", b, o + 12 * i) for i in range(m)],
pool=o + 12 * m + 4, raw=b)
def _pool(p, off):
b, base = p["raw"], p["pool"]
if base + off >= len(b):
return None
return b[base + off:b.index(b"\0", base + off)].decode("latin-1")
if __name__ == "__main__":
if len(sys.argv) < 3:
raise SystemExit(__doc__)
cmd, pak = sys.argv[1], sys.argv[2]
fn = {"list": cmd_list, "verify": cmd_verify}.get(cmd)
fn = {"list": cmd_list, "verify": cmd_verify, "link": cmd_link}.get(cmd)
ok = cmd_obj(pak, sys.argv[3], sys.argv[4]) if cmd == "obj" else fn(pak)
print("\nPASS" if ok else "\nFAIL")
sys.exit(0 if ok else 1)