Files
skate-3-rust-engine/tools/recomp-trace/first_pass.py
T
Hails 418dcecd41 Audio: interim cue tables retired, native water splash, published tools
- The interim cue tables and their opt-outs are gone; the native runtime is the only audio path. Old
  settings/audio.json files still load ("interim" / "native" ignored). Missing native data logs an error
  instead of falling back.
- Water splash from retail's mechanism (SFXObj_OffBoard start / update): Skate_Collisions 1187 / 1197 once
  per water stay, 1198 when the board is in water.
- The random interim riding bed is removed; riding collision and cloth sounds compared with the recomp
  (doc 11). Native output otherwise byte-identical (13 scenarios + 5 sessions).
- Published tools under tools/ (one folder per tool with a README; the recomp ones labelled for use with the
  research hooks), doc 14 and PR plan bucket L.
- Docs: doc 11 (retirement, bail density measured against the recomp, collision posts), doc 13 (usage guide).
2026-10-03 10:20:08 -06:00

54 lines
2.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Summarise first-pass raw-register hook lines (hooks_npc.cpp format) per kind.
usage: py -3.13 tools/recomp-trace/first_pass.py <trace.tsv> [KIND ...]
Line format: KIND ms [tag] skipped=N | r3 r4 r5 r6 r7 r8 | f1 f2 | ret r3 | ret f1 | callers.
For each kind (and tag), prints the call count (logged + skipped), then per field the number of distinct
values and the most common ones: constant fields are object pointers or flags, varying ones are data.
"""
import sys
from collections import Counter, defaultdict
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from trace import Trace # noqa: E402
FIELDS = ['r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'f1', 'f2', 'ret', 'retf', 'callers']
def rows(trace: Trace, kind: str):
for ms, f in trace.lines.get(kind, []):
tag = ''
if f and not f[0].startswith('skipped='):
tag, f = f[0], f[1:]
if len(f) < 6 or not f[0].startswith('skipped='):
continue # interleaved / truncated line
regs = f[1].split()
floats = f[2].split()
if len(regs) != 6 or len(floats) != 2:
continue
yield tag, ms, int(f[0][8:]), dict(zip(FIELDS, regs + floats + [f[3], f[4], f[5]]))
def main() -> None:
if len(sys.argv) < 2 or sys.argv[1] in ('-h', '--help'):
sys.exit(__doc__)
trace = Trace(sys.argv[1])
kinds = sys.argv[2:] or sorted(k for k in trace.lines if trace.lines[k] and
trace.lines[k][0][1][-1:] and '<' in trace.lines[k][0][1][-1])
for kind in kinds:
groups = defaultdict(list)
for tag, ms, skipped, values in rows(trace, kind):
groups[tag].append((ms, skipped, values))
for tag, items in groups.items():
total = len(items) + sum(s for _, s, _ in items)
span = (items[-1][0] - items[0][0]) / 1000 if len(items) > 1 else 0
print(f'== {kind} {tag} logged={len(items)} calls~{total} over {span:.0f}s')
for field in FIELDS:
counts = Counter(v[field] for _, _, v in items)
top = ', '.join(f'{k}×{n}' for k, n in counts.most_common(4))
print(f' {field:7s} distinct={len(counts):5d} {top}')
if __name__ == '__main__':
main()