Skip to main content

uorustlibs/
hue.rs

1//! Methods for reading hue data out of hues.mul
2//!
3//! Hues are represented in a continuous, unindexed file as groups -
4//! `|header: u32|hues: [HueEntry..8]|`
5//!
6//! Individual HueEntries are defined as
7//! `|color_table:[u16..32]|table_start:u16|table_end:u16|name:[u8..20]|`
8//!
9use 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///An individual Hue.
18///
19///Hues act as a ramp of colors that can be used for palette swapping other art assets
20#[derive(Debug, PartialEq, Eq, Clone)]
21pub struct Hue {
22    ///32 color values
23    pub color_table: [Color16; 32],
24    ///The first hue value in the table
25    pub table_start: Color16,
26    ///The last hue value in the table
27    pub table_end: Color16,
28    ///A label for the hue
29    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    ///Convert a hue back into its canonical mul representation
48    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///A collection of 8 hues
76#[derive(Debug, PartialEq, Eq, Clone)]
77pub struct HueGroup {
78    ///Unknown usage
79    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    ///Convert a hue group back into its canonical mul representation
89    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
103//A hue_entry is (32 * 2) + 2 + 2 + 20 bytes = 88 bytes
104const ENTRY_SIZE: u32 = 88;
105//8 entries to a group, plus a 4 byte header. 708 bytes.
106const GROUP_SIZE: u32 = (ENTRY_SIZE * 8) + 4;
107
108#[derive(Debug)]
109/// A struct to help read out Hue data
110pub 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    /// Create a HueReader from a file-like object
124    pub fn from_readable(data_reader: T) -> HueReader<T> {
125        HueReader { data_reader }
126    }
127
128    /// Read the given indexed group
129    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        //Slice it down into a normal string size
162        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}