1use crate::color::Color16;
10use crate::error::MEMWRITER_ERROR;
11use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
12use std::fs::File;
13use std::io::{Cursor, Read, Result, Seek, SeekFrom, Write};
14use std::path::Path;
15use std::str::from_utf8;
16
17#[derive(Debug, PartialEq, Eq, Clone)]
21pub struct Hue {
22 pub color_table: [Color16; 32],
24 pub table_start: Color16,
26 pub table_end: Color16,
28 pub name: String,
30}
31
32impl Hue {
33 pub fn new(
34 color_table: [Color16; 32],
35 table_start: Color16,
36 table_end: Color16,
37 name: String,
38 ) -> Hue {
39 Hue {
40 color_table,
41 table_start,
42 table_end,
43 name,
44 }
45 }
46
47 pub fn serialize(&self) -> Vec<u8> {
49 let mut writer = vec![];
50 for color in self.color_table.iter() {
51 writer
52 .write_u16::<LittleEndian>(*color)
53 .expect(MEMWRITER_ERROR);
54 }
55 writer
56 .write_u16::<LittleEndian>(self.table_start)
57 .expect(MEMWRITER_ERROR);
58 writer
59 .write_u16::<LittleEndian>(self.table_end)
60 .expect(MEMWRITER_ERROR);
61
62 writer
63 .write_all(self.name.as_bytes())
64 .expect(MEMWRITER_ERROR);
65 writer
66 .write_all(vec![0; 20 - self.name.len()].as_slice())
67 .expect(MEMWRITER_ERROR);
68
69 assert_eq!(writer.len(), ENTRY_SIZE as usize);
70
71 writer
72 }
73}
74
75#[derive(Debug, PartialEq, Eq, Clone)]
77pub struct HueGroup {
78 pub header: u32,
80 pub entries: [Hue; 8],
81}
82
83impl HueGroup {
84 pub fn new(header: u32, entries: [Hue; 8]) -> HueGroup {
85 HueGroup { header, entries }
86 }
87
88 pub fn serialize(&self) -> Vec<u8> {
90 let mut writer = Cursor::new(vec![]);
91 writer
92 .write_u32::<LittleEndian>(self.header)
93 .expect(MEMWRITER_ERROR);
94 for hue in self.entries.iter() {
95 writer
96 .write_all(hue.serialize().as_slice())
97 .expect(MEMWRITER_ERROR);
98 }
99 writer.into_inner()
100 }
101}
102
103const ENTRY_SIZE: u32 = 88;
105const GROUP_SIZE: u32 = (ENTRY_SIZE * 8) + 4;
107
108#[derive(Debug)]
109pub struct HueReader<T: Read + Seek> {
111 data_reader: T,
112}
113
114impl HueReader<File> {
115 pub fn new(hues_path: &Path) -> Result<HueReader<File>> {
116 let data_reader = File::open(hues_path)?;
117
118 Ok(HueReader { data_reader })
119 }
120}
121
122impl<T: Read + Seek> HueReader<T> {
123 pub fn from_readable(data_reader: T) -> HueReader<T> {
125 HueReader { data_reader }
126 }
127
128 pub fn read_hue_group(&mut self, id: u32) -> Result<HueGroup> {
130 self.data_reader
131 .seek(SeekFrom::Start((id * GROUP_SIZE) as u64))?;
132
133 let header = self.data_reader.read_u32::<LittleEndian>()?;
134
135 let entries: [Hue; 8] = [
136 self.read_hue()?,
137 self.read_hue()?,
138 self.read_hue()?,
139 self.read_hue()?,
140 self.read_hue()?,
141 self.read_hue()?,
142 self.read_hue()?,
143 self.read_hue()?,
144 ];
145
146 Ok(HueGroup { header, entries })
147 }
148
149 fn read_hue(&mut self) -> Result<Hue> {
150 let mut color_table = [0u16; 32];
151 for cell in &mut color_table {
152 *cell = self.data_reader.read_u16::<LittleEndian>()?;
153 }
154
155 let table_start = self.data_reader.read_u16::<LittleEndian>()?;
156 let table_end = self.data_reader.read_u16::<LittleEndian>()?;
157
158 let mut raw_name = [0; 20];
159 self.data_reader.read_exact(&mut raw_name)?;
160
161 let trimmed_name: Vec<u8> = raw_name
163 .iter()
164 .take_while(|&element| *element != 0)
165 .cloned()
166 .collect();
167
168 let name = match from_utf8(trimmed_name.as_slice()) {
169 Ok(s) => {
170 if s.is_ascii() {
171 s.to_string()
172 } else {
173 "Error".to_string()
174 }
175 }
176 Err(_) => "Error".to_string(),
177 };
178
179 Ok(Hue::new(color_table, table_start, table_end, name))
180 }
181}