use crate::parsers::{
blend::{Block, BlockData, Header as BlendHeader, RawBlend},
dna::{Dna, DnaStruct, DnaType},
field::{parse_field, FieldInfo},
primitive::*,
Endianness, PointerSize,
};
use linked_hash_map::LinkedHashMap;
use std::fmt;
use std::{io::Read, mem::size_of, num::NonZeroU64, path::Path};
#[derive(Clone)]
pub enum InstanceDataFormat<'a> {
Block(&'a Block),
Raw(&'a [u8]),
}
pub enum PointerInfo<'a> {
Block(&'a Block),
Null,
Invalid,
}
impl<'a> InstanceDataFormat<'a> {
pub fn get(&self, start: usize, len: usize) -> &'a [u8] {
&self.data()[start..start + len]
}
pub fn data(&self) -> &'a [u8] {
match self {
InstanceDataFormat::Block(block) => match block {
Block::Principal { data, .. }
| Block::Subsidiary { data, .. }
| Block::Global { data, .. } => &data.data[..],
_ => unimplemented!(),
},
InstanceDataFormat::Raw(data) => &data[..],
}
}
fn code(&self) -> Option<[u8; 4]> {
match self {
InstanceDataFormat::Block(block) => match block {
Block::Principal { code, .. } => Some([code[0], code[1], 0, 0]),
Block::Global { .. } => Some(*b"GLOB"),
Block::Rend { .. } => Some(*b"REND"),
Block::Test { .. } => Some(*b"TEST"),
Block::Dna { .. } => Some(*b"DNA1"),
Block::Subsidiary { .. } => None,
},
InstanceDataFormat::Raw(_) => None,
}
}
pub fn memory_address(&self) -> Option<NonZeroU64> {
match self {
InstanceDataFormat::Block(block) => match block {
Block::Principal { memory_address, .. }
| Block::Subsidiary { memory_address, .. }
| Block::Global { memory_address, .. } => Some(*memory_address),
_ => unimplemented!(),
},
InstanceDataFormat::Raw(_) => None,
}
}
}
#[derive(Debug, Clone)]
pub struct FieldTemplate {
pub info: FieldInfo,
pub type_index: usize,
pub type_name: String,
pub data_start: usize,
pub data_len: usize,
pub is_primitive: bool,
}
#[derive(Clone)]
pub struct Instance<'a> {
dna: &'a Dna,
blend: &'a RawBlend,
pub type_name: String,
pub data: InstanceDataFormat<'a>,
pub fields: LinkedHashMap<String, FieldTemplate>, }
impl<'a> std::fmt::Debug for Instance<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Instance")
.field("type_name", &self.type_name)
.field("fields", &self.fields)
.finish()
}
}
#[allow(clippy::cognitive_complexity)]
fn fmt_instance(
seen_addresses: &mut std::collections::HashSet<NonZeroU64>,
f: &mut fmt::Formatter,
inst: &Instance,
ident: usize,
) -> fmt::Result {
let ident_str: String = std::iter::repeat(" ").take(4 * ident).collect();
write!(f, "{}", inst.type_name)?;
match (inst.data.code(), inst.data.memory_address()) {
(Some(code), Some(memory_address)) => {
write!(
f,
" (code:{}|@{})",
String::from_utf8_lossy(&code[0..=1]),
memory_address
)?;
}
(Some(code), None) => {
write!(f, " (code:{})", String::from_utf8_lossy(&code[0..=1]))?;
}
(None, Some(memory_address)) => {
write!(f, " (@{})", memory_address)?;
}
(None, None) => {}
}
writeln!(f, " {{")?;
for (field_name, field) in inst.fields.iter().filter(|(n, _)| !n.starts_with("_pad")) {
match &field.info {
FieldInfo::Value => {
write!(f, "{} {}: ", ident_str, field_name)?;
match &field.type_name[..] {
"int" => writeln!(f, "{} = {};", field.type_name, inst.get_i32(field_name))?,
"char" => writeln!(f, "{} = {};", field.type_name, inst.get_u8(field_name))?,
"short" => writeln!(f, "{} = {};", field.type_name, inst.get_i16(field_name))?,
"float" => writeln!(f, "{} = {};", field.type_name, inst.get_f32(field_name))?,
"double" => writeln!(f, "{} = {};", field.type_name, inst.get_f64(field_name))?,
"int64_t" => {
writeln!(f, "{} = {};", field.type_name, inst.get_i64(field_name))?
}
"uint64_t" => {
writeln!(f, "{} = {};", field.type_name, inst.get_u64(field_name))?
}
_ if field.is_primitive => panic!("unknown primitive"),
_ => {
if field.type_name == "ListBase" {
if inst.is_valid(field_name) {
let list_base_instance = inst
.get_iter(field_name)
.next()
.expect("a valid ListBase always has at least one element");
writeln!(f, "ListBase<{}>[#?] = [", list_base_instance.type_name)?;
if list_base_instance.data.code().is_none() {
if !seen_addresses
.contains(&list_base_instance.memory_address())
{
seen_addresses.insert(list_base_instance.memory_address());
write!(f, "{} ", ident_str)?;
fmt_instance(
seen_addresses,
f,
&list_base_instance,
ident + 2,
)?;
} else {
writeln!(
f,
"{} @{},",
ident_str,
list_base_instance.memory_address()
)?;
}
} else {
unimplemented!()
}
writeln!(f, "{} ];", ident_str)?;
} else {
writeln!(f, "ListBase<?>[] = null;")?;
}
} else {
fmt_instance(seen_addresses, f, &inst.get(field_name), ident + 1)?;
}
}
}
}
FieldInfo::ValueArray { dimensions, .. } => {
write!(
f,
"{} {}: {}{:?} = ",
ident_str, field_name, field.type_name, dimensions
)?;
match &field.type_name[..] {
"char" => {
let data = inst.data.get(field.data_start, field.data_len);
if let Ok(string_data) = String::from_utf8(
data.iter().take_while(|&&b| b != 0).cloned().collect(),
) {
writeln!(f, "\"{}\";", string_data)?;
} else {
writeln!(f, "{:?};", inst.get_u8_vec(field_name))?;
}
}
"int" => writeln!(f, "{:?};", inst.get_i32_vec(field_name))?,
"short" => writeln!(f, "{:?};", inst.get_i16_vec(field_name))?,
"float" => writeln!(f, "{:?};", inst.get_f32_vec(field_name))?,
"double" => writeln!(f, "{:?};", inst.get_f64_vec(field_name))?,
"int64_t" => writeln!(f, "{:?};", inst.get_i64_vec(field_name))?,
"uint64_t" => writeln!(f, "{:?};", inst.get_u64_vec(field_name))?,
_ if field.is_primitive => panic!("unknown primitive"),
_ => {
writeln!(f, "[")?;
if let Some(i) = inst.get_iter(field_name).next() {
write!(f, "{} ", ident_str)?;
fmt_instance(seen_addresses, f, &i, ident + 2)?;
}
writeln!(f, "{} ];", ident_str)?;
}
}
}
FieldInfo::Pointer {
indirection_count: 1,
} => {
if ["next", "prev", "first", "last"]
.iter()
.any(|n| n == field_name)
{
if inst.is_valid(field_name) {
writeln!(
f,
"{} {}: {} = (@{});",
ident_str,
field_name,
inst.get(field_name).type_name,
inst.parse_ptr_address(
&inst.data.get(field.data_start, field.data_len)
)
.unwrap()
)?
} else {
writeln!(
f,
"{} {}: {} = null;",
ident_str, field_name, field.type_name
)?
}
} else if inst.is_valid(field_name) {
let ptr_inst = inst.get(field_name);
if ptr_inst.data.code().is_none()
&& !seen_addresses.contains(&inst.get(field_name).memory_address())
{
if ptr_inst.type_name == "Link" {
writeln!(
f,
"{} {}: {}* = (not enough type information);",
ident_str, field_name, field.type_name
)?
} else {
seen_addresses.insert(ptr_inst.memory_address());
match ptr_inst.data {
InstanceDataFormat::Block(block) => match block {
Block::Principal { data, .. }
| Block::Subsidiary { data, .. } => {
if data.count > 1 {
writeln!(
f,
"{} {}: {}[{}] = [",
ident_str, field_name, field.type_name, data.count
)?;
if let Some(p) = inst.get_iter(field_name).next() {
write!(f, "{} ", ident_str)?;
fmt_instance(seen_addresses, f, &p, ident + 2)?;
}
writeln!(f, "{} ];", ident_str)?;
} else {
write!(
f,
"{} {}: {} = ",
ident_str, field_name, field.type_name
)?;
fmt_instance(seen_addresses, f, &ptr_inst, ident + 1)?;
}
}
_ => unimplemented!(),
},
_ => unimplemented!(),
}
}
} else {
writeln!(
f,
"{} {}: {} = (@{});",
ident_str,
field_name,
field.type_name,
inst.parse_ptr_address(
&inst.data.get(field.data_start, field.data_len)
)
.unwrap()
)?
}
} else {
writeln!(
f,
"{} {}: {} = null;",
ident_str, field_name, field.type_name
)?;
}
}
FieldInfo::Pointer {
indirection_count: 2,
} => {
if inst.is_valid(field_name) {
let mut instances = inst.get_iter(field_name);
write!(
f,
"{} {}: {}[%?] = [",
ident_str,
field_name,
field.type_name,
)?;
if let Some(instance) = instances.next() {
write!(f, "{}", instance)?;
}
writeln!(f, "];")?;
} else {
writeln!(
f,
"{} {}: {}[] = null;",
ident_str, field_name, field.type_name
)?;
}
}
FieldInfo::FnPointer => writeln!(f, "{} {}: fn() = null", ident_str, field_name)?,
FieldInfo::PointerArray { dimensions, .. } => {
let mut instances = inst.get_iter(field_name);
writeln!(
f,
"{} {}: {}{:?}!? = $ [",
ident_str, field_name, field.type_name, dimensions,
)?;
if let Some(instance) = instances.next() {
if instance.data.code().is_none()
&& !seen_addresses.contains(&instance.memory_address())
{
seen_addresses.insert(instance.memory_address());
write!(f, "{} ", ident_str)?;
if instance.type_name == "Link" {
writeln!(f, "(not enough type information);")?
} else {
fmt_instance(seen_addresses, f, &instance, ident + 2)?;
}
} else {
write!(f, "{} @{}", ident_str, instance.memory_address())?;
}
}
writeln!(f, "{} ];", ident_str)?;
}
_ => unimplemented!("unknown type"),
}
}
writeln!(f, "{}}}", ident_str)
}
impl fmt::Display for Instance<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_instance(&mut std::collections::HashSet::new(), f, &self, 0)
}
}
fn parse_ptr_address(
data: &[u8],
pointer_size: PointerSize,
endianness: Endianness,
) -> Option<NonZeroU64> {
let address = match pointer_size {
PointerSize::Bits32 => u64::from(parse_u32(data, endianness)),
PointerSize::Bits64 => parse_u64(data, endianness),
};
NonZeroU64::new(address)
}
impl<'a> Instance<'a> {
pub fn code(&self) -> [u8; 4] {
self.data.code().expect("instance doesn't have a code")
}
pub fn memory_address(&self) -> NonZeroU64 {
self.data
.memory_address()
.expect("instance doesn't have memory address")
}
fn expect_field(&self, name: &str) -> &FieldTemplate {
match &self.fields.get(name) {
Some(field) => field,
None => panic!("invalid field '{}'", name),
}
}
fn parse_ptr_address(&self, data: &[u8]) -> Option<NonZeroU64> {
parse_ptr_address(
data,
self.blend.header.pointer_size,
self.blend.header.endianness,
)
}
fn get_ptr(&self, field: &FieldTemplate) -> PointerInfo<'a> {
match field.info {
FieldInfo::Pointer { .. } => {}
_ => panic!(
"get_ptr can only be called for pointer fields. ({:?})",
field
),
}
let address = self.parse_ptr_address(&self.data.get(field.data_start, field.data_len));
match address {
None => PointerInfo::Null,
Some(address) => {
match self.blend.blocks.iter().find(|b| match b {
Block::Principal { memory_address, .. }
| Block::Subsidiary { memory_address, .. } => *memory_address == address,
_ => false,
}) {
Some(block) => PointerInfo::Block(block),
None => PointerInfo::Invalid,
}
}
}
}
pub fn is_valid<T: AsRef<str>>(&self, name: T) -> bool {
let name = name.as_ref();
if !self.fields.contains_key(name) {
return false;
}
let field = self.expect_field(name);
match field.info {
FieldInfo::Pointer { indirection_count } if indirection_count == 1 => {
assert_eq!(
field.data_len,
size_of::<u64>(),
"field '{}' doesn't have enough data for a pointer address. ({:?})",
name,
field
);
let pointer = self.get_ptr(field);
match pointer {
PointerInfo::Null | PointerInfo::Invalid => false,
PointerInfo::Block(block) => match block {
Block::Principal { .. } | Block::Subsidiary { .. } => true,
_ => unimplemented!(),
},
}
}
FieldInfo::Pointer { indirection_count } if indirection_count == 2 => {
let pointer = self.get_ptr(&field);
let block = match pointer {
PointerInfo::Block(block) => block,
PointerInfo::Null | PointerInfo::Invalid => return false,
};
let pointer_size = self.blend.header.pointer_size.bytes_num();
let pointer_count = match block {
Block::Principal { data, .. } | Block::Subsidiary { data, .. } => {
data.data.len() / pointer_size
}
_ => unimplemented!(),
};
for i in 0..pointer_count {
match block {
Block::Principal { data, .. } | Block::Subsidiary { data, .. } => {
let address = self.parse_ptr_address(&data.data[i * pointer_size..]);
match address {
Some(address) => {
if !self.blend.blocks.iter().any(|b| match b {
Block::Principal { memory_address, .. }
| Block::Subsidiary { memory_address, .. } => {
*memory_address == address
}
_ => false,
}) {
return false;
} else {
continue;
}
}
None => return false,
}
}
_ => unimplemented!(),
}
}
true
}
FieldInfo::FnPointer => false,
FieldInfo::PointerArray { .. } => unimplemented!(), FieldInfo::Value => {
if field.type_name == "ListBase" {
let instance = self.get(name);
instance.is_valid("first") && instance.is_valid("last")
} else {
true
}
}
FieldInfo::ValueArray { .. } => true,
_ => panic!(
"is_valid called for unknown field '{}'. ({:?})",
name, field,
),
}
}
fn get_value<T: AsRef<str>, U: BlendPrimitive>(&self, name: T) -> U {
let name = name.as_ref();
let field = self.expect_field(name);
let blender_type_name = U::blender_name();
match field.info {
FieldInfo::Value if field.is_primitive && field.type_name == blender_type_name => {
assert_eq!(
field.data_len,
size_of::<U>(),
"field '{}' doesn't have enough data for a {}. ({:?})",
name,
blender_type_name,
field,
);
U::parse(
&self.data.get(field.data_start, field.data_len),
self.blend.header.endianness,
)
}
_ => panic!(
"field '{}' is not {}. ({:?})",
name, blender_type_name, field
),
}
}
pub fn get_u8<T: AsRef<str>>(&self, name: T) -> u8 {
self.get_value(name)
}
pub fn get_i8<T: AsRef<str>>(&self, name: T) -> i8 {
self.get_value(name)
}
pub fn get_char<T: AsRef<str>>(&self, name: T) -> char {
self.get_u8(name) as char
}
pub fn get_u16<T: AsRef<str>>(&self, name: T) -> u16 {
self.get_value(name)
}
pub fn get_i16<T: AsRef<str>>(&self, name: T) -> i16 {
self.get_value(name)
}
pub fn get_i32<T: AsRef<str>>(&self, name: T) -> i32 {
self.get_value(name)
}
pub fn get_f32<T: AsRef<str>>(&self, name: T) -> f32 {
self.get_value(name)
}
pub fn get_f64<T: AsRef<str>>(&self, name: T) -> f64 {
self.get_value(name)
}
pub fn get_u64<T: AsRef<str>>(&self, name: T) -> u64 {
self.get_value(name)
}
pub fn get_i64<T: AsRef<str>>(&self, name: T) -> i64 {
self.get_value(name)
}
fn get_value_vec<T: AsRef<str>, U: BlendPrimitive>(&self, name: T) -> Vec<U> {
let name = name.as_ref();
let field = self.expect_field(name);
let blender_type_name = U::blender_name();
let data = match field.info {
FieldInfo::ValueArray { len, .. } if field.is_primitive => {
assert_eq!(
field.data_len / len,
size_of::<U>(),
"field '{}' doesn't have enough data for a {} array. ({:?})",
name,
blender_type_name,
field,
);
self.data.get(field.data_start, field.data_len)
}
FieldInfo::Pointer { indirection_count } if indirection_count == 1 => {
let pointer = self.get_ptr(&field);
let block = match pointer {
PointerInfo::Block(block) => block,
PointerInfo::Null | PointerInfo::Invalid => panic!(
"field '{}' is a null or invalid pointer. ({:?})",
name, field
),
};
let size = size_of::<U>();
match block {
Block::Principal { data, .. } | Block::Subsidiary { data, .. } => {
assert!(data.data.len() % size == 0);
&data.data[..]
}
_ => unimplemented!(),
}
}
_ => panic!(
"field '{}' is not a {} array. ({:?})",
name, blender_type_name, field
),
};
data.chunks(size_of::<U>())
.map(|s| U::parse(s, self.blend.header.endianness))
.collect()
}
pub fn get_u8_vec<T: AsRef<str>>(&self, name: T) -> Vec<u8> {
self.get_value_vec(name)
}
pub fn get_i8_vec<T: AsRef<str>>(&self, name: T) -> Vec<i8> {
self.get_value_vec(name)
}
pub fn get_i32_vec<T: AsRef<str>>(&self, name: T) -> Vec<i32> {
self.get_value_vec(name)
}
pub fn get_i16_vec<T: AsRef<str>>(&self, name: T) -> Vec<i16> {
self.get_value_vec(name)
}
pub fn get_f32_vec<T: AsRef<str>>(&self, name: T) -> Vec<f32> {
self.get_value_vec(name)
}
pub fn get_f64_vec<T: AsRef<str>>(&self, name: T) -> Vec<f64> {
self.get_value_vec(name)
}
pub fn get_u64_vec<T: AsRef<str>>(&self, name: T) -> Vec<u64> {
self.get_value_vec(name)
}
pub fn get_i64_vec<T: AsRef<str>>(&self, name: T) -> Vec<i64> {
self.get_value_vec(name)
}
pub fn get_string<T: AsRef<str>>(&self, name: T) -> String {
let name = name.as_ref();
let field = self.expect_field(name);
match field.info {
FieldInfo::Value | FieldInfo::ValueArray { .. } => {
if !field.is_primitive || field.type_name != "char" {
panic!(
"field '{}' is not a primitive or has the wrong type. ({:?})",
name, field
)
}
let data = &self.data.get(field.data_start, field.data_len);
data.iter()
.take_while(|c| **c != 0)
.map(|c| *c as u8 as char)
.collect()
}
_ => panic!("field '{}' is not a string. ({:?})", name, field),
}
}
pub fn get<T: AsRef<str>>(&self, name: T) -> Instance<'a> {
let name = name.as_ref();
let field = self.expect_field(name);
match field.info {
FieldInfo::Value => {
if field.is_primitive {
panic!("cannot access field '{}' as a struct. ({:?})", name, field,)
}
let r#struct = &self
.dna
.structs
.iter()
.find(|s| s.type_index == field.type_index)
.unwrap_or_else(|| {
panic!(
"could not find type information for field '{}'. ({:?})",
name, field
)
});
let r#type = &self.dna.types[r#struct.type_index];
let fields = generate_fields(r#struct, r#type, self.dna, &self.blend.header);
Instance {
dna: self.dna,
blend: self.blend,
type_name: r#type.name.clone(),
data: InstanceDataFormat::Raw(self.data.get(field.data_start, field.data_len)),
fields,
}
}
FieldInfo::Pointer { indirection_count } if indirection_count == 1 => {
let pointer = self.get_ptr(&field);
let block = match pointer {
PointerInfo::Block(block) => block,
PointerInfo::Null | PointerInfo::Invalid => panic!(
"field '{}' is null or doesn't point to a valid block. ({:?})",
name, field
),
};
let (fields, r#type) = match block {
Block::Principal {
data, dna_index, ..
} => {
assert!(
data.count == 1,
"field '{}' is a list of structs, use get_instances to access. ({:?})",
name,
field
);
let r#struct = &self.dna.structs[*dna_index];
let r#type = &self.dna.types[r#struct.type_index];
(
generate_fields(r#struct, r#type, &self.dna, &self.blend.header),
r#type,
)
}
Block::Subsidiary { dna_index, .. } => {
if let Some(v) = generate_subsidiary_fields(
&self.dna,
&self.blend.header,
field,
*dna_index,
) {
v
} else {
println!("{:#?}", self);
println!("{}: {:?}\n{:?}", name, block, field);
panic!()
}
}
_ => unimplemented!(),
};
Instance {
dna: &self.dna,
blend: &self.blend,
type_name: r#type.name.clone(),
data: InstanceDataFormat::Block(block),
fields,
}
}
_ => panic!("field '{}' is not a valid struct ({:?})", name, field),
}
}
pub fn get_iter<T: AsRef<str>>(&self, name: T) -> impl Iterator<Item = Instance<'a>> {
let name = name.as_ref();
let field = self.expect_field(name);
enum InstanceIterator<'b> {
ListBase {
last_address: NonZeroU64,
cur: Instance<'b>,
ended: bool,
},
ValueArray {
dna: &'b Dna,
blend: &'b RawBlend,
fields: LinkedHashMap<String, FieldTemplate>,
data: &'b [u8],
len: usize,
type_name: String,
cur_index: usize,
},
Pointer1 {
dna: &'b Dna,
blend: &'b RawBlend,
fields: LinkedHashMap<String, FieldTemplate>,
data: &'b BlockData,
type_name: String,
cur_index: usize,
},
Pointer2 {
dna: &'b Dna,
blend: &'b RawBlend,
pointers: std::vec::IntoIter<NonZeroU64>,
field: FieldTemplate,
},
}
impl<'b> Iterator for InstanceIterator<'b> {
type Item = Instance<'b>;
fn next(&mut self) -> Option<Self::Item> {
match self {
InstanceIterator::ListBase {
ref last_address,
ref mut cur,
ref mut ended,
} => {
if *ended {
return None;
}
let ret = cur.clone();
if ret.memory_address() == *last_address {
*ended = true;
Some(ret)
} else {
while !cur.is_valid("next") {
*cur = cur.get(cur.fields.keys().next().expect(""));
}
*cur = cur.get("next");
Some(ret)
}
}
InstanceIterator::ValueArray {
ref dna,
ref blend,
ref fields,
ref data,
ref len,
ref type_name,
ref mut cur_index,
} => {
let data_len = (data.len() / len) as usize;
let data_start = *cur_index * data_len;
if data_start == data.len() {
return None;
}
*cur_index += 1;
Some(Instance {
dna,
blend,
type_name: type_name.clone(),
data: InstanceDataFormat::Raw(&data[data_start..data_start + data_len]),
fields: fields.clone(),
})
}
InstanceIterator::Pointer1 {
ref dna,
ref blend,
ref fields,
ref data,
ref type_name,
ref mut cur_index,
} => {
let data_len = (data.data.len() / data.count) as usize;
let data_start = *cur_index * data_len;
if data_start == data.data.len() {
return None;
}
*cur_index += 1;
Some(Instance {
dna,
blend,
type_name: type_name.clone(),
data: InstanceDataFormat::Raw(
&data.data[data_start..data_start + data_len],
),
fields: fields.clone(),
})
}
InstanceIterator::Pointer2 {
ref blend,
ref dna,
ref mut pointers,
ref field,
} => {
for address in pointers {
let block = blend.blocks.iter().find(|b| match b {
Block::Principal { memory_address, .. }
| Block::Subsidiary { memory_address, .. } => {
*memory_address == address
}
_ => false,
});
match block {
Some(Block::Principal { dna_index, .. }) => {
let r#struct = &dna.structs[*dna_index];
let r#type = &dna.types[r#struct.type_index];
let fields =
generate_fields(r#struct, r#type, &dna, &blend.header);
return Some(Instance {
dna: &dna,
blend: &blend,
type_name: r#type.name.clone(),
data: InstanceDataFormat::Block(
block.expect("we are sure block is some here"),
),
fields,
});
}
Some(Block::Subsidiary { dna_index, .. }) => {
if let Some((fields, r#type)) = generate_subsidiary_fields(
dna,
&blend.header,
&field,
*dna_index,
) {
return Some(Instance {
dna,
blend,
type_name: r#type.name.clone(),
data: InstanceDataFormat::Block(
block.expect("we are sure block is some here"),
),
fields,
});
} else {
continue;
}
}
Some(_) => unimplemented!(),
None => continue,
}
}
None
}
}
}
}
match field.info {
FieldInfo::Value => {
if field.type_name != "ListBase" {
panic!("field '{}' cannot be read as a list. ({:?})", name, field)
}
let list_instance = self.get(name);
let last_address = list_instance.get("last").memory_address();
let cur = list_instance.get("first");
InstanceIterator::ListBase {
last_address,
cur,
ended: false,
}
}
FieldInfo::ValueArray { len, .. } => {
if field.is_primitive {
panic!(
"field '{}' is a primitive array, call the appropriate method. ({:?})",
name, field
);
}
if let Some(r#struct) = &self
.dna
.structs
.iter()
.find(|s| s.type_index == field.type_index)
{
let r#type = &self.dna.types[r#struct.type_index as usize];
let fields = generate_fields(r#struct, r#type, self.dna, &self.blend.header);
let data = self.data.data();
InstanceIterator::ValueArray {
dna: self.dna,
blend: self.blend,
fields,
data,
len,
type_name: r#type.name.clone(),
cur_index: 0,
}
} else {
unreachable!("no type information found")
}
}
FieldInfo::Pointer { indirection_count } if indirection_count == 1 => {
let pointer = self.get_ptr(&field);
let block = match pointer {
PointerInfo::Block(block) => block,
PointerInfo::Null | PointerInfo::Invalid => panic!(
"field '{}' is null or doesn't point to a valid block. ({:?})",
name, field
),
};
match block {
Block::Principal {
data, dna_index, ..
} => {
let r#struct = &self.dna.structs[*dna_index];
let r#type = &self.dna.types[r#struct.type_index];
let fields =
generate_fields(r#struct, r#type, &self.dna, &self.blend.header);
InstanceIterator::Pointer1 {
dna: self.dna,
blend: self.blend,
fields,
data,
type_name: r#type.name.clone(),
cur_index: 0,
}
}
Block::Subsidiary {
data, dna_index, ..
} => {
let (fields, r#type) = generate_subsidiary_fields(
&self.dna,
&self.blend.header,
&field,
*dna_index,
)
.expect("");
InstanceIterator::Pointer1 {
dna: self.dna,
blend: self.blend,
fields,
data,
type_name: r#type.name.clone(),
cur_index: 0,
}
}
_ => unimplemented!(),
}
}
FieldInfo::Pointer { indirection_count } if indirection_count == 2 => {
let pointer = self.get_ptr(&field);
let block = match pointer {
PointerInfo::Block(block) => block,
PointerInfo::Null | PointerInfo::Invalid => panic!(
"field '{}' is null or doesn't point to a valid block. ({:?})",
name, field
),
};
let pointer_size = self.blend.header.pointer_size.bytes_num();
let pointer_count = match block {
Block::Principal { data, .. } | Block::Subsidiary { data, .. } => {
data.data.len() / pointer_size
}
_ => unimplemented!(),
};
let mut pointers = Vec::new();
for i in 0..pointer_count {
match block {
Block::Principal { data, .. } | Block::Subsidiary { data, .. } => {
match self.parse_ptr_address(&data.data[i * pointer_size..]) {
None => {
continue;
}
Some(address) => pointers.push(address),
}
}
_ => unimplemented!(),
}
}
InstanceIterator::Pointer2 {
blend: &self.blend,
dna: &self.dna,
field: field.clone(),
pointers: pointers.into_iter(),
}
}
FieldInfo::PointerArray {
indirection_count,
len,
..
} if indirection_count == 1 => {
let data = self.data.get(field.data_start, field.data_len);
let pointer_size = self.blend.header.pointer_size.bytes_num();
let pointer_count = data.len() / pointer_size;
assert_eq!(len, pointer_count);
let mut pointers = Vec::new();
for i in 0..pointer_count {
match self.parse_ptr_address(&data[i * pointer_size..]) {
None => {
continue;
}
Some(address) => pointers.push(address),
}
}
InstanceIterator::Pointer2 {
blend: &self.blend,
dna: &self.dna,
field: field.clone(),
pointers: pointers.into_iter(),
}
}
_ => unimplemented!(),
}
}
}
pub struct Blend {
pub blend: RawBlend,
}
impl Blend {
pub fn from_path<T: AsRef<Path>>(path: T) -> Blend {
use std::{fs::File, io::Cursor};
let mut file = File::open(path).expect("could not open .blend file");
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)
.expect("could not read .blend file");
Blend::new(Cursor::new(buffer))
}
pub fn new<T: Read>(data: T) -> Blend {
let blend = RawBlend::from_data(data).unwrap();
Self { blend }
}
pub fn get_all_root_blocks(&self) -> Vec<Instance> {
self.blend
.blocks
.iter()
.filter_map(|block| match block {
Block::Principal { dna_index, .. } => {
let dna_struct = &self.blend.dna.structs[*dna_index];
let dna_type = &self.blend.dna.types[dna_struct.type_index];
let fields =
generate_fields(dna_struct, dna_type, &self.blend.dna, &self.blend.header);
Some(Instance {
dna: &self.blend.dna,
blend: &self.blend,
type_name: dna_type.name.clone(),
data: InstanceDataFormat::Block(block),
fields,
})
}
_ => None,
})
.collect::<Vec<_>>()
}
pub fn get_by_code(&self, search_code: [u8; 2]) -> Vec<Instance> {
self.blend
.blocks
.iter()
.filter_map(|block| match block {
Block::Principal {
data,
dna_index,
code,
..
} if *code == search_code => {
assert!(
data.count == 1,
"blocks with a 2 letter code are assumed to not be lists"
);
let r#struct = &self.blend.dna.structs[*dna_index];
let r#type = &self.blend.dna.types[r#struct.type_index];
let fields =
generate_fields(r#struct, r#type, &self.blend.dna, &self.blend.header);
Some(Instance {
dna: &self.blend.dna,
blend: &self.blend,
type_name: r#type.name.clone(),
data: InstanceDataFormat::Block(block),
fields,
})
}
_ => None,
})
.collect::<Vec<_>>()
}
}
fn generate_fields(
dna_struct: &DnaStruct,
dna_type: &DnaType,
dna: &Dna,
header: &BlendHeader,
) -> LinkedHashMap<String, FieldTemplate> {
let mut fields = LinkedHashMap::new();
let mut data_start = 0;
for field in &dna_struct.fields {
let field_dna_type = &dna.types[field.type_index];
let field_full_name = &dna.names[field.name_index];
let is_primitive = field.type_index < 12;
let (_, (field_name, field_info)) =
parse_field(field_full_name).expect("field name could not be parsed");
let field_bytes_len = match field_info {
FieldInfo::Pointer { .. } | FieldInfo::FnPointer => header.pointer_size.bytes_num(),
FieldInfo::PointerArray { len, .. } => header.pointer_size.bytes_num() * len,
FieldInfo::ValueArray { len, .. } => field_dna_type.bytes_len * len,
FieldInfo::Value => field_dna_type.bytes_len,
};
fields.insert(
String::from(field_name),
FieldTemplate {
info: field_info,
type_index: field.type_index,
type_name: field_dna_type.name.clone(),
data_start,
data_len: field_bytes_len,
is_primitive,
},
);
data_start += field_bytes_len;
}
assert_eq!(dna_type.bytes_len, data_start);
fields
}
fn generate_subsidiary_fields<'a>(
dna: &'a Dna,
header: &BlendHeader,
field: &FieldTemplate,
dna_index: usize,
) -> Option<(LinkedHashMap<String, FieldTemplate>, &'a DnaType)> {
if field.type_index >= 12 {
if dna_index >= 12 {
let r#struct = &dna.structs[dna_index];
let r#type = &dna.types[r#struct.type_index];
Some((generate_fields(r#struct, r#type, dna, header), r#type))
} else if let Some(r#struct) = &dna
.structs
.iter()
.find(|s| s.type_index == field.type_index)
{
let r#type = &dna.types[r#struct.type_index as usize];
Some((generate_fields(r#struct, r#type, dna, header), r#type))
} else {
None
}
} else {
let r#struct = &dna.structs[dna_index];
if r#struct.type_index >= 12 {
let r#type = &dna.types[r#struct.type_index];
Some((generate_fields(r#struct, r#type, &dna, header), r#type))
} else {
None
}
}
}