use assembly_data::fdb::{ common::Latin1Str, mem::{Database, Row, Table, Tables}, }; use color_eyre::eyre::{eyre, WrapErr}; use mapr::Mmap; use std::{ collections::{BTreeMap, BTreeSet}, fs::File, path::PathBuf, }; use structopt::StructOpt; #[derive(StructOpt)] struct Options { fdb: PathBuf, } fn get_table<'a>(tables: Tables<'a>, name: &str) -> color_eyre::Result> { let table = tables .by_name(name) .ok_or_else(|| eyre!("Missing table '{}'", name))??; Ok(table) } fn get_column_index(table: Table<'_>, name: &str) -> color_eyre::Result { let index = table .column_iter() .position(|col| col.name() == name) .ok_or_else(|| eyre!("Missing columns '{}'.'{}'", table.name(), name))?; Ok(index) } struct DestructibleComponent<'a> { inner: Row<'a>, } impl<'a> DestructibleComponent<'a> { fn faction_list(&self) -> Option<&'a Latin1Str> { self.inner.field_at(2).unwrap().into_opt_text() } fn faction(&self) -> Option { self.inner.field_at(1).unwrap().into_opt_integer() } fn id(&self) -> i32 { self.inner.field_at(0).unwrap().into_opt_integer().unwrap() } } fn main() -> color_eyre::Result<()> { color_eyre::install()?; let opts = Options::from_args(); // Load the database file let file = File::open(&opts.fdb) .wrap_err_with(|| format!("Failed to open input file '{}'", opts.fdb.display()))?; let mmap = unsafe { Mmap::map(&file)? }; let buffer: &[u8] = &mmap; // Start using the database let db = Database::new(buffer); // Find table let tables = db.tables()?; let destructible_component_table = get_table(tables, "DestructibleComponent")?; assert_eq!( 1, get_column_index(destructible_component_table, "faction")? ); assert_eq!( 2, get_column_index(destructible_component_table, "factionList")? ); let mut map: BTreeMap> = BTreeMap::new(); for inner in destructible_component_table.row_iter() { let component = DestructibleComponent { inner }; let id = component.id(); if let Some(faction) = component.faction() { if faction > -1 { map.entry(faction).or_default().insert(id); } } if let Some(faction_list) = component.faction_list() { for elem in faction_list.decode().split(',') { let faction: i32 = elem.parse()?; if faction > -1 { map.entry(faction).or_default().insert(id); } } } } let string = serde_json::to_string(&map)?; println!("{}", string); Ok(()) }