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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user