use assembly_data::fdb::{ common::Latin1Str, mem::{Database, Row, Table, Tables}, }; use color_eyre::eyre::{eyre, WrapErr}; use mapr::Mmap; use serde::Serialize; use std::{fs::File, path::PathBuf}; use structopt::StructOpt; #[derive(StructOpt)] struct Options { fdb: PathBuf, #[structopt(long)] out: Option, } 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) } #[allow(non_snake_case)] #[derive(Serialize, Default, Clone, Debug)] pub struct Precondition { pub id: i32, pub r#type: Option, pub targetLOT: Option, pub targetGroup: Option, pub targetCount: Option, pub iconID: Option, pub localize: bool, pub validContexts: i64, pub locStatus: i32, pub gate_version: Option, } #[allow(non_snake_case)] #[derive(Copy, Clone)] pub struct PreconditionLoader { ci_id: usize, ci_type: usize, ci_targetLOT: usize, ci_targetGroup: usize, ci_targetCount: usize, ci_iconID: usize, ci_localize: usize, ci_validContexts: usize, ci_locStatus: usize, ci_gate_version: usize, } impl PreconditionLoader { pub fn from_table(table: Table<'_>) -> Self { let mut res = Self::default(); for (ci, col) in table.column_iter().enumerate() { let name = col.name(); match name.as_ref() { "id" => res.ci_id = ci, "type" => res.ci_type = ci, "targetLOT" => res.ci_targetLOT = ci, "targetGroup" => res.ci_targetGroup = ci, "targetCount" => res.ci_targetCount = ci, "iconID" => res.ci_iconID = ci, "localize" => res.ci_localize = ci, "validContexts" => res.ci_validContexts = ci, "locStatus" => res.ci_locStatus = ci, "gate_version" => res.ci_gate_version = ci, _ => {} } } res } #[rustfmt::skip] pub fn load(&self, row: Row) -> Precondition { Precondition { id: row.field_at(self.ci_id).unwrap().into_opt_integer().unwrap(), r#type: row.field_at(self.ci_type).unwrap().into_opt_integer(), targetLOT: row.field_at(self.ci_targetLOT).unwrap().into_opt_text().map(decode_to_owned), targetGroup: row.field_at(self.ci_targetGroup).unwrap().into_opt_text().map(decode_to_owned), targetCount: row.field_at(self.ci_targetCount).unwrap().into_opt_integer(), iconID: row.field_at(self.ci_iconID).unwrap().into_opt_integer(), localize: row.field_at(self.ci_localize).unwrap().into_opt_boolean().unwrap(), validContexts: row.field_at(self.ci_validContexts).unwrap().into_opt_big_int().unwrap(), locStatus: row.field_at(self.ci_locStatus).unwrap().into_opt_integer().unwrap(), gate_version: row.field_at(self.ci_gate_version).unwrap().into_opt_text().map(decode_to_owned), } } } fn decode_to_owned(input: &Latin1Str) -> String { input.decode().into_owned() } impl Default for PreconditionLoader { fn default() -> Self { Self { ci_id: 0, ci_type: 1, ci_targetLOT: 2, ci_targetGroup: 3, ci_targetCount: 4, ci_iconID: 5, ci_localize: 6, ci_validContexts: 7, ci_locStatus: 8, ci_gate_version: 9, } } } 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 preconditions = get_table(tables, "Preconditions")?; let prec_loader = PreconditionLoader::from_table(preconditions); if let Some(out) = &opts.out { std::fs::create_dir(out)?; } for row in preconditions.row_iter() { let prec = prec_loader.load(row); let string = serde_json::to_string(&prec)?; if let Some(out) = &opts.out { let path = out.join(&format!("{}.json", prec.id)); std::fs::write(path, string)?; } else { println!("{}", string); } } Ok(()) }