1extern crate byteorder;
16extern crate pcx;
17
18use std::{io, iter};
19use std::path::Path;
20use std::fs::File;
21use std::collections::BTreeMap;
22use byteorder::{LittleEndian, ReadBytesExt};
23
24#[derive(Debug, Copy, Clone, PartialEq, Eq)]
26pub enum PictureKind {
27 Picture,
29
30 Texture,
32
33 Mask,
35}
36
37#[derive(Debug, Copy, Clone, PartialEq, Eq)]
39pub enum ClippingMode {
40 Unclipped,
42
43 Ground,
45
46 Sky,
48}
49
50#[derive(Debug, Clone, Copy)]
52pub struct Picture {
53 pub kind : PictureKind,
55
56 pub distance : u32,
59
60 pub clipping : ClippingMode,
62}
63
64#[derive(Debug, Clone)]
66pub struct Image {
67 pub info : Option<Picture>,
69
70 pub width : u16,
72
73 pub height : u16,
75
76 pub pixels : Vec<u8>,
80
81 pub pcx : Vec<u8>,
85}
86
87#[derive(Debug, Clone)]
89pub struct Lgr {
90 pub images : BTreeMap <String, Image>,
92
93 pub palette : Vec<u8>,
95}
96
97impl Image {
98 #[inline]
102 pub fn get_pixel(&self, x : u16, y : u16) -> u8 {
103 self.pixels[(y as usize)*(self.width as usize) + (x as usize)]
104 }
105}
106
107impl Lgr {
108 #[inline]
112 pub fn get_palette_color(&self, i : u8) -> (u8, u8, u8) {
113 (
114 self.palette[(i as usize)*3],
115 self.palette[(i as usize)*3 + 1],
116 self.palette[(i as usize)*3 + 2]
117 )
118 }
119
120 pub fn load_from_file<P: AsRef<Path>>(path: P, load_pixels: bool, load_raw_pcx: bool) -> io::Result<Self> {
130 let file = File::open(path)?;
131 Self::load_from_stream(&mut io::BufReader::new(file), load_pixels, load_raw_pcx)
132 }
133
134 pub fn load_from_stream<R : io::Read>(stream : &mut R, load_pixels: bool, load_raw_pcx: bool) -> io::Result<Self> {
138 let mut magic = [0; 5];
139 stream.read_exact(&mut magic)?;
140
141 if &magic != b"LGR12" {
142 return error("Not an LGR");
143 }
144
145 let total_images = stream.read_u32::<LittleEndian>()? as usize;
146 let unknown = stream.read_u32::<LittleEndian>()?;
147 if unknown != 1002 { return error("LGR: invalid unknown value != 1002");
149 }
150
151 let listed_images = stream.read_u32::<LittleEndian>()? as usize;
152
153 let mut infos = read_pictures(stream, listed_images)?;
154
155 let mut images = BTreeMap::new();
156 let mut palette = Vec::new();
157 for _ in 0..total_images {
158 let name = read_string(stream, 12)?;
159 let _unknown_a = stream.read_i32::<LittleEndian>()?;
160 let _unknown_b = stream.read_i32::<LittleEndian>()?;
161 let length = stream.read_u32::<LittleEndian>()? as usize;
162
163 let mut pcx : Vec<u8> = std::iter::repeat(0).take(length).collect();
164 stream.read_exact(&mut pcx)?;
165
166 let info = infos.remove(name.trim_right_matches(".pcx"));
167
168 let (pixels, width, height) = {
169 let mut pcx_reader = pcx::Reader::new(&pcx[..])?;
170 let (width, height) = (pcx_reader.width() as usize, pcx_reader.height() as usize);
171
172 let pixels = if load_pixels {
173 let mut pixels: Vec<u8> = iter::repeat(0).take(width*height).collect();
174
175 for i in 0..height {
176 pcx_reader.next_row_paletted(&mut pixels[i*width..(i + 1)*width])?;
177 }
178
179 pixels
180 } else {
181 Vec::new()
182 };
183
184 let valid_palette = if let Some(info) = info {
186 info.kind != PictureKind::Mask
187 } else {
188 true
189 };
190
191 if valid_palette && palette.is_empty() {
192 palette = iter::repeat(0).take(256*3).collect();
193 pcx_reader.read_palette(&mut palette)?;
194 }
195
196 (pixels, width as u16, height as u16)
197 };
198
199 if !load_raw_pcx {
200 pcx.clear();
201 pcx.shrink_to_fit();
202 }
203
204 images.insert(name, Image {
205 info : info,
206 width : width,
207 height : height,
208 pixels : pixels,
209 pcx : pcx,
210 });
211 }
212
213 Ok(Lgr {
214 images : images,
215 palette : palette,
216 })
217 }
218}
219
220fn trim_at_zero(string : &mut Vec<u8>) {
221 for i in 0..string.len() {
222 if string[i] == 0 {
223 string.truncate(i);
224 break;
225 }
226 }
227}
228
229fn error<T>(msg : &str) -> io::Result<T> {
230 Err(io::Error::new(io::ErrorKind::InvalidData, msg))
231}
232
233fn read_string<R : io::Read>(stream : &mut R, len : usize) -> io::Result<String> {
234 let mut string : Vec<u8> = std::iter::repeat(0).take(len).collect();
235 stream.read_exact(&mut string)?;
236
237 trim_at_zero(&mut string);
238
239 match String::from_utf8(string) {
240 Ok(s) => Ok(s),
241 Err(_) => error("LGR: invalid ASCII"),
242 }
243}
244
245fn read_pictures<R : io::Read>(stream : &mut R, listed_images : usize) -> io::Result<BTreeMap<String, Picture>> {
246 let initial_info = Picture {
248 kind : PictureKind::Picture,
249 distance : 0,
250 clipping : ClippingMode::Unclipped,
251 };
252 let mut pictures : Vec<(String, Picture)> = std::iter::repeat(("".to_string(), initial_info)).take(listed_images).collect();
253
254 for i in 0..listed_images {
255 pictures[i].0 = read_string(stream, 10)?;
256 }
257
258 for i in 0..listed_images {
259 let kind = stream.read_u32::<LittleEndian>()?;
260 pictures[i].1.kind = match kind {
261 100 => PictureKind::Picture,
262 101 => PictureKind::Texture,
263 102 => PictureKind::Mask,
264 _ => return error("LGR: unknown picture kind"),
265 };
266 }
267
268 for i in 0..listed_images {
269 pictures[i].1.distance = stream.read_u32::<LittleEndian>()?;
270 }
271
272 for i in 0..listed_images {
273 let clipping = stream.read_u32::<LittleEndian>()?;
274 pictures[i].1.clipping = match clipping {
275 0 => ClippingMode::Unclipped,
276 1 => ClippingMode::Ground,
277 2 => ClippingMode::Sky,
278 _ => return error("LGR: unknown clipping mode"),
279 };
280 }
281
282 for _ in 0..listed_images {
283 let unknown = stream.read_u32::<LittleEndian>()?;
284 if unknown != 12 {
285 return error("LGR: invalid unknown value != 12");
286 }
287 }
288
289 Ok(pictures.into_iter().collect())
290}
291
292#[cfg(test)]
293mod tests {
294 use Lgr;
295
296 #[test]
297 fn load() {
298 let lgr = Lgr::load_from_file("lgr/example.lgr", false, false).unwrap();
299 assert_eq!(lgr.images.len(), 77);
300
301 let lgr = Lgr::load_from_file("lgr/example.lgr", false, true).unwrap();
302 assert_eq!(lgr.images.len(), 77);
303
304 let lgr = Lgr::load_from_file("lgr/example.lgr", true, false).unwrap();
305 assert_eq!(lgr.images.len(), 77);
306
307 let lgr = Lgr::load_from_file("lgr/example.lgr", true, true).unwrap();
308 assert_eq!(lgr.images.len(), 77);
309 }
310}