use crate::color::Color16;
use crate::error::MEMWRITER_ERROR;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fs::File;
use std::io::{Cursor, Read, Result, Seek, SeekFrom, Write};
use std::path::Path;
use std::str::from_utf8;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Hue {
pub color_table: [Color16; 32],
pub table_start: Color16,
pub table_end: Color16,
pub name: String,
}
impl Hue {
pub fn new(
color_table: [Color16; 32],
table_start: Color16,
table_end: Color16,
name: String,
) -> Hue {
Hue {
color_table,
table_start,
table_end,
name,
}
}
pub fn serialize(&self) -> Vec<u8> {
let mut writer = vec![];
for color in self.color_table.iter() {
writer
.write_u16::<LittleEndian>(*color)
.expect(MEMWRITER_ERROR);
}
writer
.write_u16::<LittleEndian>(self.table_start)
.expect(MEMWRITER_ERROR);
writer
.write_u16::<LittleEndian>(self.table_end)
.expect(MEMWRITER_ERROR);
writer
.write_all(self.name.as_bytes())
.expect(MEMWRITER_ERROR);
writer
.write_all(vec![0; 20 - self.name.len()].as_slice())
.expect(MEMWRITER_ERROR);
assert_eq!(writer.len(), ENTRY_SIZE as usize);
writer
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct HueGroup {
pub header: u32,
pub entries: [Hue; 8],
}
impl HueGroup {
pub fn new(header: u32, entries: [Hue; 8]) -> HueGroup {
HueGroup { header, entries }
}
pub fn serialize(&self) -> Vec<u8> {
let mut writer = Cursor::new(vec![]);
writer
.write_u32::<LittleEndian>(self.header)
.expect(MEMWRITER_ERROR);
for hue in self.entries.iter() {
writer
.write_all(hue.serialize().as_slice())
.expect(MEMWRITER_ERROR);
}
writer.into_inner()
}
}
const ENTRY_SIZE: u32 = 88;
const GROUP_SIZE: u32 = (ENTRY_SIZE * 8) + 4;
#[derive(Debug)]
pub struct HueReader<T: Read + Seek> {
data_reader: T,
}
impl HueReader<File> {
pub fn new(hues_path: &Path) -> Result<HueReader<File>> {
let data_reader = File::open(hues_path)?;
Ok(HueReader { data_reader })
}
}
impl<T: Read + Seek> HueReader<T> {
pub fn from_readable(data_reader: T) -> HueReader<T> {
HueReader { data_reader }
}
pub fn read_hue_group(&mut self, id: u32) -> Result<HueGroup> {
self.data_reader
.seek(SeekFrom::Start((id * GROUP_SIZE) as u64))?;
let header = self.data_reader.read_u32::<LittleEndian>()?;
let entries: [Hue; 8] = [
self.read_hue()?,
self.read_hue()?,
self.read_hue()?,
self.read_hue()?,
self.read_hue()?,
self.read_hue()?,
self.read_hue()?,
self.read_hue()?,
];
Ok(HueGroup { header, entries })
}
fn read_hue(&mut self) -> Result<Hue> {
let mut color_table = [0u16; 32];
for cell in &mut color_table {
*cell = self.data_reader.read_u16::<LittleEndian>()?;
}
let table_start = self.data_reader.read_u16::<LittleEndian>()?;
let table_end = self.data_reader.read_u16::<LittleEndian>()?;
let mut raw_name = [0; 20];
self.data_reader.read_exact(&mut raw_name)?;
let trimmed_name: Vec<u8> = raw_name
.iter()
.take_while(|&element| *element != 0)
.cloned()
.collect();
let name = match from_utf8(trimmed_name.as_slice()) {
Ok(s) => {
if s.is_ascii() {
s.to_string()
} else {
"Error".to_string()
}
}
Err(_) => "Error".to_string(),
};
Ok(Hue::new(color_table, table_start, table_end, name))
}
}