use structure::Structure;
use file_block::FileBlock;
use file::FileDb;
use std::str;
use types::{Error, Result, Group};
use std::mem;
use std::slice;
use std::ffi::CStr;
use std::cmp::min;
use std::ops::Range;
use std::fmt::{self, Debug, Formatter};
use std::error::Error as StdError;
macro_rules! from_c_str{
($c_string: expr) => (
unsafe{str::from_utf8(CStr::from_ptr(mem::transmute($c_string.as_ptr())).to_bytes())}
);
}
#[derive(Clone)]
pub struct Object<'a>{
file_db: FileDb<'a>,
structure: &'a Structure,
block: &'a FileBlock,
offset: usize,
}
impl<'a> Object<'a>{
pub fn new(file_db: FileDb<'a>, block: &'a FileBlock) -> Object<'a>{
let len = file_db.dna.structures().len();
let idx = if (block.sdna_index as usize) < len { block.sdna_index as usize } else { 0 };
let structure = &file_db.dna.structures()[idx];
Object{
file_db: file_db,
structure: structure,
block: block,
offset: 0,
}
}
pub fn new_with_index(file_db: FileDb<'a>, block: &'a FileBlock, index: usize) -> Object<'a>{
let len = file_db.dna.structures().len();
let idx = if (block.sdna_index as usize) < len { block.sdna_index as usize } else { 0 };
let structure = &file_db.dna.structures()[idx];
let offset = structure.size() * index;
Object{
file_db: file_db,
structure: structure,
block: block,
offset: offset,
}
}
fn new_from_structure(file_db: FileDb<'a>, structure: &'a Structure, block: &'a FileBlock, offset: usize) -> Object<'a>{
Object{
file_db: file_db,
structure: structure,
block: block,
offset: offset,
}
}
pub(crate) fn object_by_addr(&self, addr: u64) -> Option<Object<'a>>{
match self.file_db.file_blocks_addr_table.get(&addr){
Some(idx) => Some(self.file_db.file_blocks[*idx].object(self.file_db.clone())),
None => None
}
}
pub fn cast_to(self, structure: &'a str) -> Result<Object>{
match self.file_db.dna.structure(structure){
Ok(structure) => Ok(Object::new_from_structure(self.file_db, structure, self.block, self.offset)),
Err(err) => Err(err)
}
}
pub fn addr(&self) -> u64{
self.block.address + self.offset as u64
}
pub fn data(&self) -> &'a [u8]{
&self.block.data[self.offset..min(self.offset + self.structure.size(), self.block.data.len())]
}
pub fn get<T>(&self, field_name: &str) -> Result<&'a T>{
let field = self.structure.field(field_name);
let field = field.ok_or(Error(format!("Field {} not found", field_name)))?;
if field.is_ptr(){
let mut addr = self.get_addr(field_name)?;
if field.indirection_level() > 1{
for _i in 1..field.indirection_level(){
match self.object_by_addr(addr){
Some(next) => {
addr = self.file_db.dna.parse_ptr(next.block.data()).unwrap_or(0)
}
None => addr = 0
}
}
}
if addr != 0{
match self.file_db.file_blocks_addr_table.get(&addr){
Some(idx) => self.file_db.file_blocks[*idx].cast::<T>(),
None => Err(Error(format!("Couldn't find {} in address table", field_name)))
}
}else{
Err(Error(format!("Found nullptr while dereferencing field {}", field_name)))
}
}else{
let data = self.read_field(field_name)?;
if data.len() != mem::size_of::<T>(){
Err(Error(format!("Trying to get wrong type size, asked for {} but {} has size {}", mem::size_of::<T>(), field_name, data.len())))
}else{
Ok(unsafe{ mem::transmute(data.as_ptr()) })
}
}
}
pub unsafe fn get_contiguous<T>(&self, field_name: &str) -> Result<&'a T>{
let data = self.read_size(field_name, mem::size_of::<T>())?;
if data.len() != mem::size_of::<T>(){
Err(Error(format!("Trying to get wrong type size, asked for {} but {} has size {}", mem::size_of::<T>(), field_name, data.len())))
}else{
Ok(mem::transmute(data.as_ptr()))
}
}
pub fn read_field(&self, field_name: &str) -> Result<&'a [u8]>{
let field = self.structure.field(field_name);
let field = field.ok_or(Error(format!("Field {} not found", field_name)))?;
let offset = self.structure.field_offset(field_name);
let offset = offset.ok_or(Error(format!("Field {} not found for offset", field_name)))?;
let field_size = field.size();
if self.data().len() >= offset + field_size{
Ok(&self.data()[offset..offset + field_size])
}else{
Err(Error(format!("Object {} has size {} but tried to access {} for field {}", self.structure.name(), self.data().len(), offset + field_size, field_name)))
}
}
pub unsafe fn read_size(&self, field_name: &str, field_size: usize) -> Result<&'a [u8]>{
let field = self.structure.field(field_name);
let field = field.ok_or(Error(format!("Field {} not found", field_name)))?;
let offset = self.structure.field_offset(field_name);
let offset = offset.ok_or(Error(format!("Field {} not found for offset", field_name)))?;
if self.data().len() >= offset + field_size{
Ok(&self.data()[offset..offset + field_size])
}else{
Err(Error(format!("Object {} has size {} but tried to access {} for field {}", self.structure.name(), self.data().len(), offset + field_size, field_name)))
}
}
pub fn get_object(&self, field_name: &str) -> Result<Object<'a>>{
let field = self.structure.field(field_name);
let field = field.ok_or(Error(format!("Field {} not found", field_name)))?;
if field.is_ptr(){
let mut addr = self.get_addr(field_name)?;
if field.indirection_level() > 1{
for _i in 1..field.indirection_level(){
match self.object_by_addr(addr){
Some(next) => {
addr = self.file_db.dna.parse_ptr(next.block.data()).unwrap_or(0)
}
None => addr = 0
}
}
}
if addr != 0{
self.object_by_addr(addr)
.ok_or(Error(format!("Couldn't find object {} by address", field_name)))
}else{
Err(Error(format!("Found nullptr while dereferencing field {}", field_name)))
}
}else{
let offset = self.structure.field_offset(field_name);
let offset = offset.ok_or(Error(format!("Field {} not found for offset", field_name)))?;
let structure = self.file_db.dna.field_structure(field)?;
Ok(Object::new_from_structure(self.file_db.clone(), structure, self.block, offset))
}
}
pub fn get_str(&self, field_name: &str) -> Result<&'a str>{
let field = self.structure.field(field_name);
let field = field.ok_or(Error(format!("Field {} not found", field_name)))?;
if field.type_name() == "char" && field.is_array(){
let data = self.read_field(field_name)?;
from_c_str!(data).map_err(|err| Error(format!("Error parsing string: {}", err)))
}else if field.type_name() == "char" && field.is_ptr(){
let addr = self.get_addr(field_name)?;
match self.file_db.file_blocks_addr_table.get(&addr){
Some(idx) => {
let data = self.file_db.file_blocks[*idx].data();
from_c_str!(data).map_err(|err| Error(format!("Error parsing string: {}", err)))
}
None => Err(Error(format!("Retrieving data block for {}", field_name)))
}
}else{
Err(Error("Field type is not char*".to_string()))
}
}
pub fn get_addr(&self, field_name: &str) -> Result<u64>{
let data = self.read_field(field_name)?;
let field = self.structure.field(field_name);
let field = field.ok_or(Error(format!("Field {} not found", field_name)))?;
if field.is_ptr(){
self.file_db.dna.parse_ptr(data)
}else{
Err(Error("Field is not a pointer".to_string()))
}
}
pub fn get_slice<T>(&self, field_name: &str) -> Result<&'a [T]>{
let data = self.read_field(field_name)?;
let field = self.structure.field(field_name);
let field = field.ok_or(Error(format!("Field {} not found", field_name)))?;
let dim = field.total_array_size();
let field_size = field.size();
if dim > 0 && field_size / dim == mem::size_of::<T>(){
Ok(unsafe{slice::from_raw_parts(mem::transmute(data.as_ptr()), dim)})
}else{
Err(Error(format!("Wrong array dimension, field size is {} and total array size {} but type size is {} which is not a multiple", field_size, dim, mem::size_of::<T>())))
}
}
pub fn get_data_slice<T>(&self, field_name: &str, len: usize) -> Result<&'a [T]>{
let addr = self.get_addr(field_name)?;
match self.file_db.file_blocks_addr_table.get(&addr){
Some(idx) => self.file_db.file_blocks[*idx].data_slice::<T>(len),
None => Err(Error(format!("Couldn't find {} in address table", field_name)))
}
}
pub fn get_vec<T: Clone>(&self, field_name: &str, len: usize) -> Result<Vec<T>>{
let addr = self.get_addr(field_name)?;
match self.file_db.file_blocks_addr_table.get(&addr){
Some(idx) => self.file_db.file_blocks[*idx].data_slice::<T>(len).map(|slice| slice.to_vec()),
None => Err(Error(format!("Couldn't find {} in address table", field_name)))
}
}
pub fn get_object_slice(&self, field_name: &str) -> Result<Vec<Object<'a>>>{
let field = self.structure.field(field_name);
let field = field.ok_or(Error(format!("Field {} not found", field_name)))?;
if field.is_ptr(){
let ptrs = if self.file_db.dna.pointer_size() == 32{
let addresses = self.get_slice::<u32>(field_name)?;
addresses.iter().map(|ptr| *ptr as u64).collect()
}else{
let addresses = self.get_slice::<u64>(field_name)?;
addresses.to_vec()
};
Ok(ptrs.iter().filter_map(|ptr| self.object_by_addr(*ptr)).collect())
}else{
let dim = field.total_array_size();
let field_size = field.size();
let structure = self.file_db.dna.field_structure(field)?;
let offset = structure.field_offset(field_name)
.ok_or(Error::from("Can't find field offset (probably not an object slice)".to_owned()))?;
let size = structure.size();
if dim > 0 && field_size / dim == size{
Ok(Range{start: 0, end: dim}.map(|i| {
Object::new_from_structure(self.file_db.clone(), structure, self.block, offset + size*i )
}).collect())
}else{
Err(Error(format!("Wrong array dimension, field size is {} and total array size {} but type size is {} which is not a multiple", field_size, dim, size)))
}
}
}
pub fn get_ptr_slice(&self, field_name: &str, len: usize) -> Result<Vec<Object<'a>>>{
let mut addr = self.get_addr(field_name).map_err(|err| Error(format!("{} for {}", err.msg(), field_name)))?;
let field = self.structure.field(field_name);
let field = field.ok_or(Error(format!("Field {} not found", field_name)))?;
if field.indirection_level() > 1{
for _i in 1..field.indirection_level()-1{
match self.object_by_addr(addr){
Some(next) => {
addr = self.file_db.dna.parse_ptr(next.block.data()).unwrap();
}
None => addr = 0
}
}
let addrs = match self.object_by_addr(addr){
Some(next) => {
let data_size = next.block.data().len();
let ptr_bytes = self.file_db.dna.pointer_bytes();
let num_addr = data_size/ptr_bytes;
if num_addr < len{
return Err(Error(format!("Error retrieving ptr_slice, asked for {} elements but last level of indirection only has {}", len, num_addr)));
}
Range{start: 0, end: min(num_addr, len)}.filter_map(|i| {
let begin = i*ptr_bytes;
let end = begin + ptr_bytes;
let ptr_data = &next.block.data()[begin..end];
self.file_db.dna.parse_ptr(ptr_data).ok()
}).collect()
}
None => Vec::new()
};
let objects = addrs.iter().filter_map(|addr| {
self.object_by_addr(*addr)
}).collect();
Ok(objects)
}else{
match self.file_db.file_blocks_addr_table.get(&addr){
Some(idx) => Ok(self.file_db.file_blocks[*idx].slice(len, self.file_db.clone())),
None => Err(Error(format!("Couldn't find {} in address table", field_name)))
}
}
}
pub fn get_object_at(&self, field_name: &str, at: usize) -> Result<Object<'a>>{
let addr = self.get_addr(field_name)?;
match self.file_db.file_blocks_addr_table.get(&addr){
Some(idx) => Ok(Object::new_with_index(self.file_db.clone(), &self.file_db.file_blocks[*idx], at)),
None => Err(Error(format!("Couldn't find {} in address table", field_name)))
}
}
pub fn get_list(&self, field_name: &str) -> Result<List<'a>>{
let field = self.structure.field(field_name);
let field = field.ok_or(Error(format!("Field {} not found", field_name)))?;
if field.type_name() == "ListBase"{
self.get_object(field_name).map(|obj| List{object: obj})
}else{
Err(Error(format!("Trying to retrieve object {} which is of type {} not ListBase", field_name, field.type_name())))
}
}
fn fmt_field(&self, field_name: &str, mut recursive_check: Vec<u64>, indent: &str, fmt: &mut Formatter) -> Result<()>{
recursive_check.push(self.addr());
let field = self.structure().field(field_name);
if field.is_none(){
fmt.write_str(&format!("{}: Field not found,\n", field_name));
return Err(Error("Field not found".to_string()));
}
let field = field.unwrap();
if field.is_array() || field.is_ptr(){
let member_addr = match self.get_addr(field.name()){
Ok(addr) => addr,
Err(err) => if "nullptr" == err.description(){
0
}else{
return Err(err)
}
};
match field.type_name(){
"char" => fmt.write_str(&format!("{}{}: {},\n", indent, field_name, self.get_str(field.name())?)),
"short" => fmt.write_str(&format!("{}{}: {:?},\n", indent, field_name, self.get_slice::<i16>(field.name())?)),
"int" => fmt.write_str(&format!("{}{}: {:?},\n", indent, field_name, self.get_slice::<i32>(field.name())?)),
"uint64_t" => fmt.write_str(&format!("{}{}: {:?},\n", indent, field_name, self.get_slice::<i64>(field.name())?)),
"float" => fmt.write_str(&format!("{}{}: {:?},\n", indent, field_name, self.get_slice::<f32>(field.name())?)),
"double" => fmt.write_str(&format!("{}{}: {:?},\n", indent, field_name, self.get_slice::<f64>(field.name())?)),
_ty if field_name == "next" || field_name == "prev" => fmt.write_str(&format!("{}{}: 0x{:x},\n", indent, field_name, member_addr)),
ty => if field.indirection_level() == 1 {
match self.get_object(field.name()) {
Ok(member) => if !recursive_check.contains(&member.addr()) {
fmt.write_str(&format!("{}{}: {} {{\n", indent, field_name, member.structure().name()));
for field in member.structure().fields(){
member.fmt_field(field.name(), recursive_check.clone(), &(indent.to_owned() + " "), fmt);
}
fmt.write_str(&format!("{}}}\n", indent));
Ok(())
}else{
fmt.write_str(&format!("{}{}: {}* = 0x{:x},\n", indent, field.name(), ty, member_addr))
}
Err(ref err) if err.description() == "nullptr" => {
fmt.write_str(&format!("{}{}: {}* = None,\n", indent, field.name(), ty))
}
Err(err) =>
fmt.write_str(&format!("{}{}: {}, // error retrieving field value: {}\n", indent, field.name(), ty, err)),
}
}else{
fmt.write_str(&format!("{}{}: {},\n", indent, field_name, "ptr slice of unkwown size"))
}
}.map_err(|_| Error("formatting error".to_string()))
}else{
match field.type_name(){
"char" => fmt.write_str(&format!("{}{}: {}\n", indent, field_name, self.get::<i8>(field.name())?)),
"short" => fmt.write_str(&format!("{}{}: {}\n", indent, field_name, self.get::<i16>(field.name())?)),
"int" => fmt.write_str(&format!("{}{}: {}\n", indent, field_name, self.get::<i32>(field.name())?)),
"uint64_t" => fmt.write_str(&format!("{}{}: {}\n", indent, field_name, self.get::<i64>(field.name())?)),
"float" => fmt.write_str(&format!("{}{}: {}\n", indent, field_name, self.get::<f32>(field.name())?)),
"double" => fmt.write_str(&format!("{}{}: {}\n", indent, field_name, self.get::<f64>(field.name())?)),
"ListBase" =>
fmt.write_str(&format!("{}{}: {:?},\n", indent, field_name, self.get_list(field.name())?.iter().map(|f| format!("{:?}@0x{:x}", f.name(), f.addr())).collect::<Vec<_>>())),
ty => {
if let Ok(member) = self.get_object(field.name()) {
fmt.write_str(&format!("{}{}: {} {{\n", indent, field_name, member.structure().name()));
for field in member.structure().fields(){
member.fmt_field(field.name(), recursive_check.clone(), &(indent.to_owned() + " "), fmt);
}
fmt.write_str(&format!("{}}}\n", indent));
Ok(())
}else{
fmt.write_str(&format!("{}{}: {} // error retrieving value\n", indent, field.name(), ty))
}
}
}.map_err(|_| Error("formatting error".to_string()))
}
}
pub fn structure(&self) -> &Structure{
self.structure
}
pub fn name(&self) -> Result<&'a str>{
match self.get_str("name"){
Ok(name) => Ok(name),
Err(_) => self.get_object("id").and_then(|id| id.get_str("name"))
}
}
pub fn file_block(&self) -> &FileBlock{
&self.block
}
pub fn custom_properties(&self) -> Option<Group<'a>>{
self.get_object("id").and_then(|i| i.get_object("properties")).ok()
.and_then(|props|{
let len = *props.get::<i32>("len").unwrap() as usize;
let address = props.addr();
let pos = self.file_db.file_blocks_addr_table[&address];
self.file_db.file_blocks[pos+1 .. pos+len+1].iter()
.map(|block| block.object(self.file_db.clone()))
.find(|p| if let Ok("_RNA_UI") = p.name(){
true
}else{
false
})
.and_then(|group_prop| group_prop.get_object("data").and_then(|o| o.get_list("group")).ok())
.map(|list| Group{
list
})
})
}
pub fn children(&self) -> impl Iterator<Item=Object>{
self.file_db.file_blocks.iter()
.map(move |block| block.object(self.file_db.clone()))
.filter(move |obj| obj.get_object("parent").map(|obj_parent| obj_parent.addr() == self.addr()).unwrap_or(false))
}
}
impl<'a> Debug for Object<'a>{
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result{
fmt.write_str(&format!("{}:{{\n",self.structure().name()))?;
for field in self.structure().fields(){
self.fmt_field(field.name(), vec![], " ", fmt);
}
fmt.write_str(&format!("}}"))?;
Ok(())
}
}
#[derive(Clone)]
pub struct List<'a>{
pub(crate) object: Object<'a>,
}
impl<'a> List<'a>{
pub fn iter(&self) -> ListIter<'a>{
ListIter{ next: self.object.get_object("first").ok() }
}
}
pub struct ListIter<'a>{
next: Option<Object<'a>>,
}
impl<'a> Iterator for ListIter<'a>{
type Item = Object<'a>;
fn next(&mut self) -> Option<Object<'a>>{
let ret = match self.next{
Some(ref next) => {
Some(next.clone())
}
None => None
};
self.next = match ret{
Some(ref ret) => ret.get_object("next")
.or_else(|_err|
match ret.structure().fields().first(){
Some(field) => ret.get_object(field.name()).and_then(|id| id.get_object("next")),
None => panic!("Couldn't find any next field while trying to dereference list iterator")
}
)
.ok(),
None => None
};
ret
}
}
impl<'a> IntoIterator for List<'a>{
type Item = Object<'a>;
type IntoIter = ListIter<'a>;
fn into_iter(self) -> ListIter<'a>{
ListIter{ next: self.object.get_object("first").ok() }
}
}