- a player build ships beside the dev one — dev chords and probe surfaces go dark, and a gate enforces what a player can still reach, by medium not by module - the binary carries its own shaders and its shipped UI, finds the WoW folder by itself, and keeps its local state in one benilla-config folder - the glue screens get a capture shutter, and the engine stops depending on the protocol crate Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
65 lines
2.8 KiB
Rust
65 lines
2.8 KiB
Rust
//! Throwaway: dump `SkillRaceClassInfo.dbc` rows admitting a race/class, with the flags word in
|
|
//! hex — the table the client's `GetSkillLineInfo` reads for its 0x400 single-rank cap and 0x20
|
|
//! unlearnable bit. Usage: `cargo run -p benilla-formats --example srci_dump -- <race> <class>`.
|
|
fn main() -> anyhow::Result<()> {
|
|
let mut args = std::env::args().skip(1);
|
|
let race: u32 = args.next().unwrap_or_else(|| "4".into()).parse()?;
|
|
let class: u32 = args.next().unwrap_or_else(|| "3".into()).parse()?;
|
|
let data = benilla_formats::wow_data().expect("no WoW install found (set $WOW_DATA)");
|
|
let mut chain = benilla_formats::open_chain(&data)?;
|
|
|
|
// Skill line names, so the dump reads.
|
|
let lines = chain.read_file("DBFilesClient\\SkillLine.dbc")?;
|
|
let name_of = |id: u32| -> String {
|
|
let g = |b: &[u8], o: usize| u32::from_le_bytes(b[o..o + 4].try_into().unwrap());
|
|
let (rows, _fields, rec) = (g(&lines, 4), g(&lines, 8), g(&lines, 12) as usize);
|
|
let sb = 20 + rows as usize * rec;
|
|
for r in 0..rows as usize {
|
|
let base = 20 + r * rec;
|
|
if g(&lines, base) == id {
|
|
let off = sb + g(&lines, base + 3 * 4) as usize;
|
|
let end = lines[off..].iter().position(|&c| c == 0).unwrap_or(0) + off;
|
|
return format!(
|
|
"{} (cat {})",
|
|
String::from_utf8_lossy(&lines[off..end]),
|
|
g(&lines, base + 4)
|
|
);
|
|
}
|
|
}
|
|
"?".into()
|
|
};
|
|
|
|
let bytes = chain.read_file("DBFilesClient\\SkillRaceClassInfo.dbc")?;
|
|
let g = |o: usize| u32::from_le_bytes(bytes[o..o + 4].try_into().unwrap());
|
|
let (rows, _fields, rec) = (g(4), g(8), g(12) as usize);
|
|
let (rmask, cmask) = (
|
|
1u32.checked_shl(race.wrapping_sub(1)).unwrap_or(0),
|
|
1u32.checked_shl(class.wrapping_sub(1)).unwrap_or(0),
|
|
);
|
|
println!("race {race} (mask {rmask:#x}) class {class} (mask {cmask:#x})");
|
|
let mut seen: Vec<u32> = Vec::new();
|
|
for r in 0..rows as usize {
|
|
let base = 20 + r * rec;
|
|
let (skill, race_mask, class_mask, flags, req_level, tier) = (
|
|
g(base + 4),
|
|
g(base + 8),
|
|
g(base + 12),
|
|
g(base + 16),
|
|
g(base + 20),
|
|
g(base + 24),
|
|
);
|
|
// race/class 0 = dump EVERY row (masks unfiltered, duplicates kept).
|
|
let all = race == 0 || class == 0;
|
|
if !all && (race_mask & rmask == 0 || class_mask & cmask == 0 || seen.contains(&skill)) {
|
|
continue;
|
|
}
|
|
seen.push(skill);
|
|
println!(
|
|
"skill {skill:>4} flags {flags:#07x} (0x400={}) reqLevel {req_level} tier {tier} {}",
|
|
flags & 0x400 != 0,
|
|
name_of(skill)
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|