Until now this repo was an export of a private tree, pushed as squashed snapshots. From here on it is the working tree itself, and the private repo is archived. The root holds AGENTS.md and the Rust and GitHub files only. Everything an agent needs to orient is in docs/: METHOD.md (the rules and how the project is built, 123 lines, project method only), MAP.md (generated from the code at every land) and CONTRIBUTING.md (what gets in and how a change is judged). One skill, `probe`, holds the procedure for running the client unattended against the local server. Brought over because the project needs it: reference/ (the 1.12 name catalogues 34 source files read; the export failed its own tests without them), the gate chain and the pool (scripts/), the hooks that enforce the install rule and the worktree rule, the full .gitignore, the `cargo play` alias, and the original wording of eleven source files the export had reworded. Left out: the decision records (archived with the private repo), the export tooling, the operator's Discord and triage loop, the lab-laptop scripts and the instruments nothing references, which live outside the repo now. What read the records is gone from the tooling: no corpus lint, no records index, no `reserve` verb, no number-collision check. Three hooks are gone: fmt-at-commit and the file-size nudge were redundant with the gates, and the assets/ui guard is now a test (`assets_ui_does_not_grow`) that fails for everyone, not only for a Claude Code session. The remaining pool hooks stand down on a machine without a pool. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
86 lines
3.2 KiB
Python
Executable File
86 lines
3.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
r"""Generate `crates/benilla-ui/src/messages/catalog.rs` from wow-5875-re's message-catalog.tsv.
|
|
|
|
The TSV is the sibling repo's byte-exact sweep of build 5875's `0xb4b498` registry -- the table
|
|
`CGGameUI::DisplayError 0x496720` indexes by message id. It is *their* artifact (regenerated there
|
|
with `re/ui/extract-message-catalog.py`); this script only re-shapes it into a Rust const, so the
|
|
table ships with benilla instead of being re-read out of a sibling checkout at build time.
|
|
|
|
python3 scripts/gen-message-catalog.py ../wow-5875-re/re/ui/message-catalog.tsv
|
|
|
|
Refuses rather than emits on anything it does not understand: a non-dense id range, a duplicate key,
|
|
an unknown kind. A generated table that quietly dropped a row would be worse than no table.
|
|
"""
|
|
import sys
|
|
|
|
KINDS = {0: "Chat", 1: "Info", 2: "Error"}
|
|
|
|
HEADER = """\
|
|
//! The 5875 message catalog, row for row.
|
|
//!
|
|
//! @generated by `scripts/gen-message-catalog.py` from wow-5875-re `re/ui/message-catalog.tsv`
|
|
//! (itself generated by that repo's `re/ui/extract-message-catalog.py` out of `WoW.exe`).
|
|
//! Do not edit by hand — regenerate.
|
|
|
|
use super::{MessageRecord, MsgKind};
|
|
|
|
/// Every row of `0xb4b498`, indexed by message id (the ids are dense `0..465`, so
|
|
/// `CATALOG[id]` *is* the reference's `0xb4b498 + 20*id`).
|
|
///
|
|
/// `rustfmt` is held off so that a regeneration is a no-op diff: one row per line is the shape
|
|
/// this script emits, and a formatter that rewrapped it would make the file drift from its own
|
|
/// generator.
|
|
#[rustfmt::skip]
|
|
pub static CATALOG: [MessageRecord; {count}] = [
|
|
"""
|
|
|
|
|
|
def main(path: str) -> int:
|
|
rows = []
|
|
for line in open(path):
|
|
if line.startswith("#") or line.startswith("id_hex"):
|
|
continue
|
|
f = line.rstrip("\n").split("\t")
|
|
if len(f) != 8:
|
|
raise SystemExit(f"malformed row (expected 8 fields, got {len(f)}): {line!r}")
|
|
rows.append(
|
|
{
|
|
"id": int(f[1]),
|
|
"key": f[3],
|
|
"kind": int(f[4]),
|
|
"sound": f[5],
|
|
"type_tag": int(f[6], 16),
|
|
"chat_type": int(f[7]),
|
|
}
|
|
)
|
|
|
|
for want, r in enumerate(rows):
|
|
if r["id"] != want:
|
|
raise SystemExit(f"ids are not dense: expected {want}, got {r['id']}")
|
|
keys = [r["key"] for r in rows]
|
|
if len(set(keys)) != len(keys):
|
|
raise SystemExit("duplicate key in the catalog — by_key would be ambiguous")
|
|
for r in rows:
|
|
if r["kind"] not in KINDS:
|
|
raise SystemExit(
|
|
f"row {r['id']} ({r['key']}) has kind {r['kind']}, which MsgKind does not model"
|
|
)
|
|
|
|
out = [HEADER.replace("{count}", str(len(rows)))]
|
|
for r in rows:
|
|
sound = "None" if r["sound"] == "NONE" else f'Some("{r["sound"]}")'
|
|
out.append(
|
|
f' MessageRecord {{ id: {r["id"]}, key: "{r["key"]}", '
|
|
f'kind: MsgKind::{KINDS[r["kind"]]}, sound: {sound}, '
|
|
f'type_tag: {r["type_tag"]:#04x}, chat_type: {r["chat_type"]} }},\n'
|
|
)
|
|
out.append("];\n")
|
|
sys.stdout.write("".join(out))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) != 2:
|
|
raise SystemExit(__doc__)
|
|
sys.exit(main(sys.argv[1]))
|