mod macros;
mod parser;
mod serializer;
mod text_tree;
mod json;
pub mod visitor;
pub mod data;
use std::io;
use std::fs;
use std::path::Path;
use thiserror::Error;
use cdragon_hashes::{HashKind, HashMapper, HashError};
use cdragon_utils::parsing::ParseError;
pub use cdragon_hashes::bin::{BinHashKind, BinHashMapper};
pub use serializer::{BinSerializer, BinEntriesSerializer};
pub use data::*;
pub use parser::{BinEntryScanner, BinEntryScannerItem};
pub use text_tree::TextTreeSerializer;
pub use json::JsonSerializer;
pub use visitor::{BinVisitor, BinTraversal};
type Result<T, E = PropError> = std::result::Result<T, E>;
#[allow(missing_docs)]
#[derive(Debug)]
pub struct BinHashKindMapping<T, U> {
pub entry_path: T,
pub class_name: T,
pub field_name: T,
pub hash_value: T,
pub path_value: U,
}
impl<T, U> BinHashKindMapping<T, U> {
#[inline]
pub fn get(&self, kind: BinHashKind) -> &T {
match kind {
BinHashKind::EntryPath => &self.entry_path,
BinHashKind::ClassName => &self.class_name,
BinHashKind::FieldName => &self.field_name,
BinHashKind::HashValue => &self.hash_value,
}
}
#[inline]
pub fn get_mut(&mut self, kind: BinHashKind) -> &mut T {
match kind {
BinHashKind::EntryPath => &mut self.entry_path,
BinHashKind::ClassName => &mut self.class_name,
BinHashKind::FieldName => &mut self.field_name,
BinHashKind::HashValue => &mut self.hash_value,
}
}
}
impl<T: Default, U: Default> Default for BinHashKindMapping<T, U> {
fn default() -> Self {
Self {
entry_path: T::default(),
class_name: T::default(),
field_name: T::default(),
hash_value: T::default(),
path_value: U::default(),
}
}
}
pub type BinHashMappers = BinHashKindMapping<BinHashMapper, HashMapper<u64, 64>>;
impl BinHashMappers {
pub fn from_dirpath(path: &Path) -> Result<Self, HashError> {
let mut this = Self::default();
this.load_dirpath(path)?;
Ok(this)
}
pub fn load_dirpath(&mut self, path: &Path) -> Result<(), HashError> {
self.entry_path.load_path(path.join(HashKind::BinEntryPath.mapping_path()))?;
self.class_name.load_path(path.join(HashKind::BinClassName.mapping_path()))?;
self.field_name.load_path(path.join(HashKind::BinFieldName.mapping_path()))?;
self.hash_value.load_path(path.join(HashKind::BinHashValue.mapping_path()))?;
self.path_value.load_path(path.join(HashKind::WadGame.mapping_path()))?;
Ok(())
}
pub fn write_dirpath(&self, path: &Path) -> Result<(), HashError> {
self.entry_path.write_path(path.join(HashKind::BinEntryPath.mapping_path()))?;
self.class_name.write_path(path.join(HashKind::BinClassName.mapping_path()))?;
self.field_name.write_path(path.join(HashKind::BinFieldName.mapping_path()))?;
self.hash_value.write_path(path.join(HashKind::BinHashValue.mapping_path()))?;
self.path_value.write_path(path.join(HashKind::WadGame.mapping_path()))?;
Ok(())
}
}
#[derive(Debug)]
pub struct PropFile {
pub version: u32,
pub is_patch: bool,
pub linked_files: Vec<String>,
pub entries: Vec<BinEntry>,
}
impl PropFile {
pub fn from_slice(data: &[u8]) -> Result<PropFile> {
Ok(parser::binparse(data)?)
}
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<PropFile> {
Self::from_slice(&fs::read(path.as_ref())?)
}
pub fn scan_entries_from_reader<R: io::Read>(reader: R) -> Result<BinEntryScanner<R>> {
let scanner = BinEntryScanner::new(reader)?;
Ok(scanner)
}
pub fn scan_entries_from_path<P: AsRef<Path>>(path: P) -> Result<BinEntryScanner<io::BufReader<fs::File>>> {
let file = fs::File::open(path)?;
let reader = io::BufReader::new(file);
let scanner = BinEntryScanner::new(reader)?;
Ok(scanner)
}
}
pub type BinEntryHeader = (BinEntryPath, BinClassName);
#[derive(Debug)]
pub struct BinEntry {
pub path: BinEntryPath,
pub ctype: BinClassName,
pub fields: Vec<BinField>,
}
impl BinEntry {
pub fn get(&self, name: BinFieldName) -> Option<&BinField> {
self.fields.iter().find(|f| f.name == name)
}
pub fn getv<T: BinValue + 'static>(&self, name: BinFieldName) -> Option<&T> {
self.get(name).and_then(|field| field.downcast::<T>())
}
}
pub const NON_PROP_BASENAMES: &[&str] = &[
"atlas_info.bin",
"tftoutofgamecharacterdata.bin",
"tftmapcharacterlists.bin",
"tftactivesets.bin",
"tftitemlist.bin",
];
pub fn is_binfile_path(path: &Path) -> bool {
if let Some(true) = path.extension().map(|s| s == "bin") {
if let Some(name) = path.file_name() {
return name.to_str()
.map(|s| !NON_PROP_BASENAMES.contains(&s))
.unwrap_or(false)
}
}
false
}
#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum PropError {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("parsing error")]
Parsing(#[from] ParseError),
}