gen_addresses: per-lane fragment dir; duplicate names are a hard error

This commit is contained in:
alex 2026-09-08 05:55:03 -04:00
parent 0d84eb2d4c
commit 9834b3d175
2 changed files with 33 additions and 1 deletions

View file

@ -0,0 +1,18 @@
# Per-lane address fragments
`addresses.json` is a single shared file. When several lanes run concurrently they edit the
same lines, and three times on 2026-09-08 one lane's `git add` swept another's in-flight
entries into the wrong commit. Nothing was lost, but authorship and atomicity were.
A lane may instead drop its own file here:
```json
{ "entries": [ { "name": "...", "addr": "0x...", "convention": "...",
"prototype": "...", "status": "verified", "source": "findings/..." } ] }
```
Name it after the lane (`lane-d.json`). `tools/gen_addresses.py` merges every fragment in
sorted order after `addresses.json`. A **duplicate name across files is a hard error**, not
last-wins — two lanes disagreeing about an address is exactly the thing we must not paper over.
The integrator folds fragments back into `addresses.json` once the lane's work is merged.

View file

@ -1,10 +1,24 @@
#!/usr/bin/env python3
"""Emit include/generated/sots_addresses.h for sots-engine from ghidra/addresses.json.
Only facts (name, RVA, convention, prototype, status) — never decompiler text."""
import json, os, subprocess, sys, datetime
import glob, json, os, subprocess, sys, datetime
root = os.path.join(os.path.dirname(__file__), "..")
j = json.load(open(os.path.join(root, "ghidra", "addresses.json")))
base = int(j["image_base"], 16)
# Per-lane fragments. addresses.json is a single shared file, so concurrent lanes kept
# sweeping each other's in-flight entries into the wrong commit. A lane may instead write
# ghidra/addresses.d/<lane>.json ({"entries": [...]}) — its own file, no shared-line edits.
# Fragments are merged here in sorted order; a duplicate name is an error, not a silent
# last-wins, because two lanes disagreeing about an address is exactly what we must not paper over.
seen = {e["name"]: "addresses.json" for e in j["entries"]}
for frag_path in sorted(glob.glob(os.path.join(root, "ghidra", "addresses.d", "*.json"))):
frag_name = os.path.basename(frag_path)
for e in json.load(open(frag_path))["entries"]:
if e["name"] in seen:
sys.exit(f"duplicate address entry {e['name']!r}: in {seen[e['name']]} and {frag_name}")
seen[e["name"]] = frag_name
j["entries"].append(e)
try: rev = subprocess.check_output(["git","-C",root,"rev-parse","--short","HEAD"]).decode().strip()
except Exception: rev = "unknown"
out = [ "// GENERATED — do not edit. Facts about Sword of the Stars.exe (GOG 1.8.1).",