First batch of the benilla-formats goldens, all of them the realm's tables being bigger or retuned rather than anything misread. The safety check came first and is worth recording: **every `*_FIELDS` constant in the crate still matches its live DBC header** — Spell 173, AreaTable 25, CreatureFamily 18, Talent 21, Lock 33 and the rest — so no decoder is reading a moved layout, which is the only way one of these counts could have been a defect rather than data. Added `examples/dbc_shapes` to read that off the chain in one go. Counts: `AnimationData` 208 → 227, `AreaPOI` 339 → 483, `AreaTrigger` 432 → 530 (172/144 across the two continents, from 133/121), `AreaTable` 1081 → 1481, `GameTips` 74 → 95, cinematics 10 → 13 sequences and cameras both. Three that are more than a count: - **Rested experience is ×1.5 here, not ×2.** `Exhaustion.dbc` row 1 is where the client reads the multiplier it shows, and the realm retuned it. Every vanilla guide quotes 2.0. - **The help window offers two ticket categories, not ten** — "report someone's behavior" and "an issue with my character" replace 1.12's Stuck/Guild/Item/… list. - **Warlock minion pet icons are blank.** `CreatureFamily` rows 15-19, 23, 28, 29 carry no icon where 1.12 gave each one a path, so `GetPetIcon()` answers nil for a minion and an addon that concatenates it unguarded gets nothing. Not a column slip: every hunter family in the same column answers a real path. The realm also backported the TBC families (Dragonhawk, Ravager, Warp Stalker, Sporebat, Nether Ray, Serpent), added four of its own (Fox, Plagued, Horse, Moth) and filled two minion ids, Incubus 18 and Felguard 29 — 35 families where 5875 had 23. And `Emotes.dbc`'s gesture scan, which was pinned on a premise that has stopped holding: 1.12 had one row per gesture bit, so the walk order could not matter. The realm's file has three rows carrying `0x8` and a row (424) carrying `0x8|0x10|0x20`, so it matters now — and **our scan is already right**, which the disassembly settles rather than luck. `0x603a60` walks the table backwards (`imul $0x1c` to the end, `sub $0x1c` per step) and overwrites each slot, so the earliest record survives; and its bit test is an if/else-if chain, so one row fills at most the slot of its first set bit. Ours walks forward keeping the first, with `position` for the chain — the same table. The test now asserts that rule, re-derived from the file, instead of counting carriers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
45 lines
1.6 KiB
Rust
45 lines
1.6 KiB
Rust
//! Every `DBFilesClient/*.dbc` the player's chain holds, with its header shape.
|
|
//!
|
|
//! A DBC header is `WDBC`, then record count, field count, record size and string-block size
|
|
//! (`0x4c7c20` reads it). The **field count is the layout**: a decoder keyed to a column index
|
|
//! reads the wrong column the moment it moves, and that is a defect. The record count is just how
|
|
//! much data the realm ships, and a golden that pins one is a canary, not a contract.
|
|
//!
|
|
//! `cargo run -q -p benilla-formats --example dbc_shapes`
|
|
fn main() -> anyhow::Result<()> {
|
|
let data = benilla_formats::wow_data()
|
|
.ok_or_else(|| anyhow::anyhow!("no client data (set WOW_DATA)"))?;
|
|
let mut chain = benilla_formats::open_chain(&data)?;
|
|
let mut names: Vec<String> = chain
|
|
.list_under("DBFilesClient")?
|
|
.into_iter()
|
|
.map(|e| e.name)
|
|
.filter(|f| f.to_ascii_lowercase().ends_with(".dbc"))
|
|
.collect();
|
|
names.sort();
|
|
names.dedup();
|
|
println!(
|
|
"{:<44} {:>8} {:>7} {:>8} {:>9}",
|
|
"dbc", "records", "fields", "recsize", "strings"
|
|
);
|
|
for name in names {
|
|
let Ok(bytes) = chain.read_file(&name) else {
|
|
continue;
|
|
};
|
|
if bytes.len() < 20 || &bytes[..4] != b"WDBC" {
|
|
continue;
|
|
}
|
|
let u = |i: usize| u32::from_le_bytes(bytes[i..i + 4].try_into().unwrap());
|
|
let leaf = name.rsplit(['\\', '/']).next().unwrap_or(&name);
|
|
println!(
|
|
"{:<44} {:>8} {:>7} {:>8} {:>9}",
|
|
leaf,
|
|
u(4),
|
|
u(8),
|
|
u(12),
|
|
u(16)
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|