The previous commit announced message\MissionDialogMessage.tbl as a find. structures/ixud-localised-text.md OWNS it and already says S02_P1_OBJECTIVE and friends are record names in an IDXD map, message\MissionDialogMessage.tbl, whose positional fields list the lowercase per-line IXUD names -- and that *_GRAPH is the odd one, a single named field holding a texture. I grepped the manifest doc and not the text doc. Third overclaim in four days; the rule is now to grep the doc that owns the DATA, not only the doc that owns the FILE. Worse, the headline negative was wrong. MissionDialog_local_string.tbl DOES resolve -- as language\MissionDialog_local_string.tbl, IXUD, in all six GP_MAIN_GAME_* paks. My 33 prefixes omitted language\, which is precisely the convention ixud-localised-text.md records for language paks. A prefix sweep is only as good as its prefix list, and the list should come from the corpus. 'LOSE has exactly one entry' was wrong too. Per kind: HINT_PAUSE 4, HINT 3, LOSE 4, OBJECTIVE 4 positional fields, and GRAPH 1 NAMED field holding the .t32. The single-field bucket was GRAPH, not LOSE. What survives as new: the 11 non-MISSION field values read as a block; the GP_TEST and TEXTS sibling records; the per-stage phase census; and the five pgmsg_*.prt resolving nowhere under 34 prefixes x 41 archives, now with two working controls in the same sweep. Artefact 8 insertions / 3 deletions, every deleted line replaced by its corrected form; the other five regenerate byte-identical.
95 lines
4.4 KiB
Python
95 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
"""`Stage\\script.tbl` -- the mission-script manifest, and what its values point at.
|
|
|
|
Regenerates docs/re/data/script-manifest.txt.
|
|
"""
|
|
import sys, os, glob, re, collections
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, HERE)
|
|
import unitgroup as U
|
|
from unit_substructures import pak_entries
|
|
|
|
def main():
|
|
mg = glob.glob('/work/sylph_extract/**/GP_MAIN_GAME_E.pak', recursive=True)[0]
|
|
E = dict(pak_entries(mg))
|
|
recs = U.parse(E[U.name_hash('Stage\\script.tbl')])
|
|
|
|
print("# Stage\\script.tbl -- the mission-script manifest")
|
|
print("# Regenerate: python3 tools/re-capture/script_manifest.py")
|
|
print("# See docs/re/structures/mission-script-manifest.md")
|
|
|
|
print("\n## the three records")
|
|
for r in recs:
|
|
n = U.named(r)
|
|
print(" %-10s %2d fields" % (r['squadron'], len(r['fields'])))
|
|
if r['squadron'] == 'SCRIPTS': continue
|
|
for k, v in sorted(n.items()):
|
|
print(" %-22s %r" % (k, v))
|
|
|
|
sc = [r for r in recs if r['squadron'] == 'SCRIPTS'][0]
|
|
print("\n## SCRIPTS -- the 11 non-MISSION fields, read for the first time")
|
|
for t, n, v in sorted(sc['fields'], key=lambda x: (x[1] or '')):
|
|
if n and n.startswith('MISSION') and v.endswith('.ssb'): continue
|
|
print(" %-22s %r" % (n, v))
|
|
|
|
idx = {}
|
|
for pk in sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)):
|
|
idx[os.path.basename(pk)] = {h for h, b in pak_entries(pk)}
|
|
targets = ['MissionDialogMessage.tbl', 'MissionDialog_local_string.tbl',
|
|
'pgmsg_start.prt', 'pgmsg_end.prt', 'pgmsg_update.prt',
|
|
'pgmsg_failed.prt', 'pgmsg_restart.prt']
|
|
pre = ['', 'Stage\\', 'stage\\', 'message\\', 'language\\', 'msg\\', 'dialog\\', 'script\\',
|
|
'SCRIPTS\\', 'GP_SCRIPT\\', '2d\\', 'ui\\', 'prt\\', 'view\\', 'dat\\', 'hidden\\']
|
|
for l in ['eng', 'jpn', 'fra', 'deu', 'ita', 'esp']:
|
|
pre += [l + '\\', 'Stage\\' + l + '\\', 'message\\' + l + '\\']
|
|
print("\n## do those values resolve as pak entries? (%d prefixes x %d archives)"
|
|
% (len(pre), len(idx)))
|
|
for t in targets:
|
|
hits = sorted({p + t for p in pre if any(U.name_hash(p + t) in s for s in idx.values())})
|
|
if hits:
|
|
for hname in hits[:1]:
|
|
where = sorted(pk for pk, s in idx.items() if U.name_hash(hname) in s)
|
|
print(" %-32s FOUND as %-40s in %d archives" % (t, hname, len(where)))
|
|
else:
|
|
print(" %-32s not found" % t)
|
|
c = U.name_hash('Stage\\script.tbl')
|
|
print(" CONTROL Stage\\script.tbl resolves in %d archives"
|
|
% sum(1 for s in idx.values() if c in s))
|
|
print(" NOTE name_hash is CASE-INSENSITIVE (message\\ == Message\\); tag_hash is not.")
|
|
|
|
b = E[U.name_hash('message\\MissionDialogMessage.tbl')]
|
|
dr = U.parse(b)
|
|
pat = re.compile(r'^S(\d+)_P(\d+)_(.+)$')
|
|
fam = collections.Counter(); st = collections.defaultdict(set); nf = collections.Counter()
|
|
for r in dr:
|
|
m = pat.match(r['squadron'])
|
|
if m:
|
|
fam[m.group(3)] += 1
|
|
st[int(m.group(1))].add(int(m.group(2)))
|
|
nf[len(r['fields'])] += 1
|
|
print("\n## message\\MissionDialogMessage.tbl -- %d records, %d bytes" % (len(dr), len(b)))
|
|
print(" name families S<NN>_P<n>_<KIND>: %s" % dict(fam))
|
|
shape = collections.defaultdict(collections.Counter)
|
|
for r in dr:
|
|
m = pat.match(r['squadron'])
|
|
if m:
|
|
shape[m.group(3)][(len(r['fields']),
|
|
sum(1 for t, n, v in r['fields'] if n is not None))] += 1
|
|
print(" per kind (total fields, NAMED fields) -> count:")
|
|
for k in sorted(shape):
|
|
print(" %-12s %s" % (k, dict(shape[k])))
|
|
print(" stages present (%d): %s" % (len(st), sorted(st)))
|
|
print(" phases per stage: %s" % {k: len(v) for k, v in sorted(st.items())})
|
|
H = {h for h, _ in pak_entries(mg)}
|
|
ship = {i for i in range(0, 40) if U.name_hash('stage\\UnitGroup_S%02d.tbl' % i) in H}
|
|
print(" CONTROL subset of the %d shipped stages: %s ; shipped with NO hints: %s"
|
|
% (len(ship), set(st) <= ship, sorted(ship - set(st))))
|
|
r0 = [r for r in dr if r['squadron'].endswith('_HINT_PAUSE')][0]
|
|
print("\n sample %s:" % r0['squadron'])
|
|
for t, n, v in r0['fields']:
|
|
print(" tag %-3s %-8s %r" % (t, n or '<none>', v))
|
|
|
|
if __name__ == "__main__":
|
|
main()
|