What a clone lacked: the gates went green with a thousand skipped tests and said nothing, every login script assumed a worktree-pool slot and its probe account, the client defaulted to the maintainer's account, a docker path and a home directory were in the code, the temp files broke on GNU coreutils, and two generators could only run against a sibling repo. Now the test gates print how many data-gated tests skipped and why; a scripted run logs in as the account its checkout declares in `.probe-identity` or as the three variables name, never a default, and the client refuses a scripted login on any other account; the event and message tables regenerate from vendored inputs, byte for byte; the docs state the real prerequisites. Out of the tree, into the maintainer's own tooling: the worktree pool, its hooks, the perf leg runner, the cross-platform compile and the Vulkan container leg — a way of working, and setups not every machine has. One hook stays: no writes into the install. Two tests that could never run are gone with their subject: the quest-timer geometry diff compared a copy of the window that no longer exists (and joined a chain path onto a folder, so it always skipped), and the FrameXML flag gate now reads the stock files off the player's own patch chain instead of an extracted folder only one machine had. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
97 lines
3.7 KiB
Python
Executable File
97 lines
3.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
r"""Generate `crates/benilla-ui/src/messages/catalog.rs` from `reference/1.12-message-catalog.tsv`.
|
|
|
|
The TSV is wow-5875-re's byte-exact sweep of build 5875's `0xb4b498` registry -- the table
|
|
`CGGameUI::DisplayError 0x496720` indexes by message id -- vendored under `reference/` like the
|
|
other tables (it is 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 and is never read out of a
|
|
sibling checkout.
|
|
|
|
python3 scripts/gen-message-catalog.py # the vendored table -> the .rs, in place
|
|
python3 scripts/gen-message-catalog.py <tsv> # another table
|
|
|
|
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 os
|
|
import sys
|
|
|
|
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
TABLE = os.path.join(REPO, "reference", "1.12-message-catalog.tsv")
|
|
OUT = os.path.join(REPO, "crates", "benilla-ui", "src", "messages", "catalog.rs")
|
|
|
|
KINDS = {0: "Chat", 1: "Info", 2: "Error"}
|
|
|
|
HEADER = """\
|
|
//! The 5875 message catalog, row for row.
|
|
//!
|
|
//! @generated by `scripts/gen-message-catalog.py` from `reference/1.12-message-catalog.tsv`
|
|
//! (wow-5875-re's `re/ui/message-catalog.tsv`, extracted out of `WoW.exe` there).
|
|
//! 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:
|
|
if not os.path.exists(path):
|
|
raise SystemExit(f"no table at {path}")
|
|
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")
|
|
with open(OUT, "w") as f:
|
|
f.write("".join(out))
|
|
print(f"wrote {os.path.relpath(OUT, REPO)} ({len(rows)} rows)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) > 2:
|
|
raise SystemExit(__doc__)
|
|
sys.exit(main(sys.argv[1] if len(sys.argv) == 2 else TABLE))
|