use std::collections::HashMap;
use crate::decoders::basics::*;
use crate::decoders::Buffer;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum CiffTag {
ColorInfo1 = 0x0032,
MakeModel = 0x080a,
ColorInfo2 = 0x102c,
WhiteBalance = 0x10a9,
SensorInfo = 0x1031,
DecoderTable = 0x1835,
}
fn ct (tag: CiffTag) -> u16 {
tag as u16
}
#[derive(Debug, Copy, Clone)]
pub struct CiffEntry<'a> {
pub tag: u16,
pub typ: u16,
pub count: usize,
pub bytesize: usize,
pub data_offset: usize,
pub data: &'a [u8],
}
#[derive(Debug, Clone)]
pub struct CiffIFD<'a> {
entries: HashMap<u16,CiffEntry<'a>>,
subifds: Vec<CiffIFD<'a>>,
}
pub fn is_ciff(buf: &[u8]) -> bool {
buf[6..14] == b"HEAPCCDR"[..]
}
impl<'a> CiffIFD<'a> {
pub fn new_file(buf: &'a Buffer) -> Result<CiffIFD<'a>,String> {
let data = &buf.buf;
CiffIFD::new(data, LEu32(data,2) as usize, buf.size, 1)
}
pub fn new(buf: &'a[u8], start: usize, end: usize, depth: u32) -> Result<CiffIFD<'a>, String> {
let mut entries = HashMap::new();
let mut subifds = Vec::new();
let valuedata_size = LEu32(buf, end-4) as usize;
let dircount = LEu16(buf, start+valuedata_size) as usize;
for i in 0..dircount {
let entry_offset: usize = start+valuedata_size+2+i*10;
let e = CiffEntry::new(buf, start, entry_offset)?;
if e.typ == 0x2800 || e.typ == 0x3000 { if depth < 10 { let ifd = CiffIFD::new(buf, e.data_offset, e.data_offset+e.bytesize, depth+1);
match ifd {
Ok(val) => {subifds.push(val);},
Err(_) => {entries.insert(e.tag, e);}, }
}
} else {
entries.insert(e.tag, e);
}
}
Ok(CiffIFD {
entries: entries,
subifds: subifds,
})
}
pub fn find_entry(&self, tag: CiffTag) -> Option<&CiffEntry> {
if self.entries.contains_key(&ct(tag)) {
self.entries.get(&ct(tag))
} else {
for ifd in &self.subifds {
match ifd.find_entry(tag) {
Some(x) => return Some(x),
None => {},
}
}
None
}
}
}
impl<'a> CiffEntry<'a> {
pub fn new(buf: &'a[u8], value_data: usize, offset: usize) -> Result<CiffEntry<'a>, String> {
let p = LEu16(buf, offset);
let tag = p & 0x3fff;
let datalocation = (p & 0xc000) as usize;
let typ = p & 0x3800;
let (bytesize, data_offset) = match datalocation {
0x0000 => (LEu32(buf, offset+2) as usize, LEu32(buf, offset+6) as usize + value_data),
0x4000 => (8, offset+2),
val => return Err(format!("CIFF: Don't know about data location {:x}", val).to_string()),
};
let data = &buf[data_offset..data_offset+bytesize];
let count = bytesize >> CiffEntry::element_shift(typ);
Ok(CiffEntry {
tag: tag,
typ: typ,
count: count,
bytesize: bytesize,
data_offset: data_offset,
data: data,
})
}
pub fn element_shift(typ: u16) -> usize {
match typ {
0x0000 | 0x8000 => 0,
0x1000 => 1,
0x1800 | 0x2000 | 0x2800 | 0x3000 => 2,
_ => 0,
}
}
pub fn get_strings(&self) -> Vec<String> {
String::from_utf8_lossy(self.data).split_terminator("\0").map(|x| x.to_string()).collect()
}
pub fn get_u32(&self, idx: usize) -> u32 {
match self.typ {
0x0000 | 0x8000 => self.data[idx] as u32,
0x1000 => LEu16(self.data, idx*2) as u32,
0x1800 | 0x2000 | 0x2800 | 0x3000 => LEu32(self.data, idx*4),
_ => panic!("Trying to read typ {} for a u32", self.typ),
}
}
pub fn get_usize(&self, idx: usize) -> usize { self.get_u32(idx) as usize }
pub fn get_f32(&self, idx: usize) -> f32 { self.get_u32(idx) as f32 }
pub fn get_force_u16(&self, idx: usize) -> u16 { LEu16(self.data, idx*2) }
}