1use brdb::{AsBrdbValue, Brdb, IntoReader};
2use brdb::schema::{BrdbSchema, BrdbSchemaStructProperty, WireVariant};
3use std::collections::{BTreeMap, BTreeSet};
4use std::path::PathBuf;
5
6fn format_value(
7 schema: &BrdbSchema,
8 ty_str: &str,
9 val: &dyn AsBrdbValue,
10) -> Option<String> {
11 let b = |s: String| format!("Box::new({s}) as Box<dyn AsBrdbValue>");
12 match ty_str {
13 "bool" => Some(b(format!("{}", val.as_brdb_bool().ok()?))),
14 "u8" => Some(b(format!("{}u8", val.as_brdb_u8().ok()?))),
15 "u16" => Some(b(format!("{}u16", val.as_brdb_u16().ok()?))),
16 "u32" => Some(b(format!("{}u32", val.as_brdb_u32().ok()?))),
17 "u64" => Some(b(format!("{}u64", val.as_brdb_u64().ok()?))),
18 "i8" => Some(b(format!("{}i8", val.as_brdb_i8().ok()?))),
19 "i16" => Some(b(format!("{}i16", val.as_brdb_i16().ok()?))),
20 "i32" => Some(b(format!("{}i32", val.as_brdb_i32().ok()?))),
21 "i64" => Some(b(format!("{}i64", val.as_brdb_i64().ok()?))),
22 "f32" => Some(b(format!("{:?}f32", val.as_brdb_f32().ok()?))),
23 "f64" => Some(b(format!("{:?}f64", val.as_brdb_f64().ok()?))),
24 "str" => Some(b(format!("String::from({:?})", val.as_brdb_str().ok()?))),
25 "bundle_path_ref" => Some(b(format!("String::from({:?})", val.as_brdb_str().unwrap_or("")))),
26 "wire_graph_variant" | "wire_graph_prim_math_variant" => {
27 let wv = match val.as_brdb_wire_variant().ok()? {
28 WireVariant::Number(n) => format!("WireVariant::Number({n:?})"),
29 WireVariant::Int(n) => format!("WireVariant::Int({n})"),
30 WireVariant::Bool(b) => format!("WireVariant::Bool({b})"),
31 WireVariant::Object(o) => format!("WireVariant::Object({o:?})"),
32 WireVariant::Exec => "WireVariant::Exec".into(),
33 WireVariant::Vector(v) => format!(
34 "WireVariant::Vector(Vector3f {{ x: {:?}, y: {:?}, z: {:?} }})",
35 v.x, v.y, v.z
36 ),
37 WireVariant::Str(s) => format!("WireVariant::Str({s:?}.into())"),
38 WireVariant::Rotator { pitch, yaw, roll } => format!(
39 "WireVariant::Rotator {{ pitch: {pitch:?}, yaw: {yaw:?}, roll: {roll:?} }}"
40 ),
41 WireVariant::Quat { x, y, z, w } => format!(
42 "WireVariant::Quat {{ x: {x:?}, y: {y:?}, z: {z:?}, w: {w:?} }}"
43 ),
44 WireVariant::LinearColor { r, g, b, a } => format!(
45 "WireVariant::LinearColor {{ r: {r:?}f32, g: {g:?}f32, b: {b:?}f32, a: {a:?}f32 }}"
46 ),
47 };
48 Some(b(wv))
49 }
50 "class" | "object" => None, other => {
52 if schema.get_enum(other).is_some() {
53 Some(b(format!("{}u8", val.as_brdb_u8().unwrap_or(0))))
54 } else if other == "Color" {
55 let s_id = schema.intern.get(other)?;
56 let get_u8 = |field: &str| -> u8 {
57 let fid = schema.intern.get(field).unwrap();
58 val.as_brdb_struct_prop_value(schema, s_id, fid)
59 .ok()
60 .and_then(|v| v.as_brdb_u8().ok())
61 .unwrap_or(0)
62 };
63 let r = get_u8("R"); let g = get_u8("G"); let b_ = get_u8("B"); let a = get_u8("A");
64 Some(b(format!("SavedBrickColor {{ r: {r}, g: {g}, b: {b_}, a: {a} }}")))
65 } else if let Some(s_ty) = schema.get_struct(other) {
66 let s_id = schema.intern.get(other)?;
70 let mut parts = Vec::new();
71 for (field_id, prop_ty) in s_ty {
72 let field_name = field_id.get(schema)?;
73 let inner_ty = match prop_ty {
74 BrdbSchemaStructProperty::Type(t) => schema.intern.lookup_ref(*t)?,
75 _ => continue,
76 };
77 let prop_val = val.as_brdb_struct_prop_value(schema, s_id, *field_id).ok()?;
78 let Some(formatted) = format_value(schema, &inner_ty, prop_val) else {
81 continue;
82 };
83 if parts.is_empty() {
86 parts.push(format!("(\"{field_name}\", {formatted})"));
87 } else {
88 let short = formatted.trim_end_matches(" as Box<dyn AsBrdbValue>");
89 parts.push(format!("(\"{field_name}\", {short})"));
90 }
91 }
92 if parts.is_empty() {
93 None
94 } else {
95 Some(b(format!("NestedStructDefault(vec![{}])", parts.join(", "))))
96 }
97 } else {
98 None
99 }
100 }
101 }
102}
103
104fn main() -> Result<(), Box<dyn std::error::Error>> {
105 let args: Vec<String> = std::env::args().collect();
106 let path = PathBuf::from(args.get(1).expect("usage: extract_defaults <dump.brdb> [output.rs]"));
107 let output_path = args.get(2).map(PathBuf::from);
108 let db = Brdb::open(path)?.into_reader();
109 let global_data = db.global_data()?;
110 let schema = db.components_schema()?;
111
112 let mut type_to_struct: BTreeMap<String, String> = BTreeMap::new();
113 let mut wire_ports: BTreeSet<String> = BTreeSet::new();
114 let mut struct_defaults: BTreeMap<String, Vec<(String, String, String)>> = BTreeMap::new();
115
116 for (i, type_name) in global_data.component_type_names.iter().enumerate() {
117 if let Some(struct_name) = global_data.component_data_struct_names.get(i) {
118 if struct_name != "None" {
119 type_to_struct.insert(type_name.clone(), struct_name.clone());
120 }
121 }
122 }
123
124 for port in &global_data.component_wire_port_names {
125 wire_ports.insert(port.clone());
126 }
127
128 for chunk in db.brick_chunk_index(1)? {
129 if chunk.num_components == 0 {
130 continue;
131 }
132 let Ok((_soa, components)) = db.component_chunk(1, *chunk) else {
133 continue;
134 };
135 for s in components {
136 let name = s.get_name().to_owned();
137 if struct_defaults.contains_key(&name) {
138 continue;
139 }
140
141 let Some(struct_def) = schema.get_struct(&name) else {
142 continue;
143 };
144 let s_id = match schema.intern.get(&name) {
145 Some(id) => id,
146 None => continue,
147 };
148
149 let mut fields = Vec::new();
150 for (field_id, prop_ty) in struct_def {
151 let field_name = match field_id.get(&schema) {
152 Some(n) => n.to_owned(),
153 None => continue,
154 };
155 let ty_str = match prop_ty {
156 BrdbSchemaStructProperty::Type(t) => {
157 match schema.intern.lookup_ref(*t) {
158 Some(s) => s.to_owned(),
159 None => continue,
160 }
161 }
162 _ => continue,
163 };
164 let val = match s.as_brdb_struct_prop_value(&schema, s_id, *field_id) {
165 Ok(v) => v,
166 Err(_) => continue,
167 };
168 let formatted = match format_value(&schema, &ty_str, val) {
169 Some(f) => f,
170 None => continue,
171 };
172 fields.push((field_name, ty_str, formatted));
173 }
174 struct_defaults.insert(name, fields);
175 }
176 }
177
178 use std::fmt::Write;
179 let mut out = String::new();
180 macro_rules! w { ($($t:tt)*) => { writeln!(out, $($t)*).unwrap() } }
181
182 w!("// Autogenerated from: cargo run --example extract_defaults -- path/to/dump.brdb");
183 w!();
184
185 w!("pub static COMPONENT_TYPE_STRUCT_PAIRS: &[(&str, &str)] = &[");
186 for (type_name, struct_name) in &type_to_struct {
187 w!(" (\"{type_name}\", \"{struct_name}\"),");
188 }
189 w!("];");
190 w!();
191
192 w!("pub static WIRE_PORT_NAMES: &[&str] = &[");
193 for port in &wire_ports {
194 w!(" \"{port}\",");
195 }
196 w!("];");
197 w!();
198
199 w!("pub static ENTITY_TYPE_STRUCT_PAIRS: &[(&str, &str)] = &[");
200 for (i, type_name) in global_data.entity_type_names.iter().enumerate() {
201 if let Some(class_name) = global_data.entity_data_class_names.get_index(i) {
202 w!(" (\"{type_name}\", \"{class_name}\"),");
203 }
204 }
205 w!("];");
206 w!();
207
208 w!("use std::sync::LazyLock;");
209 w!("#[allow(unused_imports)]");
212 w!("use crate::schema::WireVariant;");
213 w!("use crate::schema::as_brdb::AsBrdbValue;");
214 w!("#[allow(unused_imports)]");
217 w!("use crate::schema::as_brdb::NestedStructDefault;");
218 w!("use crate::SavedBrickColor;");
219 w!();
220 w!("/// Default field values for every component data struct.");
221 w!("pub static STRUCT_DEFAULTS: LazyLock<Vec<(&'static str, Vec<(&'static str, Box<dyn AsBrdbValue>)>)>> =");
222 w!(" LazyLock::new(|| vec![");
223 let mut first_entry = true;
224 for (name, fields) in &struct_defaults {
225 if fields.is_empty() {
226 continue;
227 }
228 w!(" (\"{name}\", vec![");
229 for (field_name, _ty, val) in fields {
230 if first_entry {
231 w!(" (\"{field_name}\", {val}),");
232 first_entry = false;
233 } else {
234 let short = val.trim_end_matches(" as Box<dyn AsBrdbValue>");
235 w!(" (\"{field_name}\", {short}),");
236 }
237 }
238 w!(" ]),");
239 }
240 w!(" ]);");
241
242 if let Some(ref p) = output_path {
243 std::fs::write(p, &out)?;
244 eprintln!("Wrote {}", p.display());
245 } else {
246 print!("{out}");
247 }
248
249 eprintln!(
250 "Extracted: {} type mappings, {} wire ports, {} struct defaults, {} entity types",
251 type_to_struct.len(),
252 wire_ports.len(),
253 struct_defaults.len(),
254 global_data.entity_type_names.len(),
255 );
256
257 Ok(())
258}