1use std::io::{Read, Seek, SeekFrom};
8
9const FILE_MAGIC: u32 = 0x7275_6358;
10const IMAGE_TYPE: u32 = 0xfffd_0002;
11const IMAGE_MAX_SIZE: u16 = 0x7fff;
12
13#[derive(Debug)]
15pub(crate) enum Error {
16 Io,
18
19 InvalidMagic,
21
22 TooManyEntries,
24
25 NoImages,
27
28 CorruptImage,
30
31 ImageTooLarge,
33}
34
35impl From<std::io::Error> for Error {
36 fn from(_: std::io::Error) -> Self {
37 Error::Io
38 }
39}
40
41fn read_u32<R: Read>(read: &mut R) -> Result<u32, Error> {
45 let mut buffer = [0; 4];
46 read.read_exact(&mut buffer)?;
47 Ok(u32::from_le_bytes(buffer))
48}
49
50#[derive(Debug)]
52pub(crate) struct Image {
53 pub(crate) width: u16,
54 pub(crate) height: u16,
55 pub(crate) x_hot: u16,
56 pub(crate) y_hot: u16,
57 pub(crate) delay: u32,
58 pub(crate) pixels: Vec<u32>,
59}
60
61impl Image {
62 fn read<R: Read>(read: &mut R, expected_kind: u32, expected_size: u32) -> Result<Self, Error> {
64 let (_header, kind, size, _version) = (
65 read_u32(read)?,
66 read_u32(read)?,
67 read_u32(read)?,
68 read_u32(read)?,
69 );
70 if (kind, size) != (expected_kind, expected_size) {
71 return Err(Error::CorruptImage);
72 }
73
74 fn convert_size(size: u32) -> Result<u16, Error> {
75 size.try_into()
76 .ok()
77 .filter(|&size| size <= IMAGE_MAX_SIZE)
78 .ok_or(Error::ImageTooLarge)
79 }
80
81 let (width, height) = (
82 convert_size(read_u32(read)?)?,
83 convert_size(read_u32(read)?)?,
84 );
85 let (x_hot, y_hot) = (read_u32(read)?, read_u32(read)?);
86 let delay = read_u32(read)?;
87 let x_hot = x_hot.try_into().or(Err(Error::ImageTooLarge))?;
88 let y_hot = y_hot.try_into().or(Err(Error::ImageTooLarge))?;
89
90 let num_pixels = u32::from(width) * u32::from(height);
91 let pixels = (0..num_pixels)
92 .map(|_| read_u32(read))
93 .collect::<Result<Vec<_>, _>>()?;
94
95 Ok(Image {
96 width,
97 height,
98 x_hot,
99 y_hot,
100 delay,
101 pixels,
102 })
103 }
104}
105
106#[derive(Debug)]
108struct TocEntry {
109 kind: u32,
110 size: u32,
111 position: u32,
112}
113
114impl TocEntry {
115 fn read<R: Read>(read: &mut R) -> Result<Self, Error> {
117 Ok(TocEntry {
118 kind: read_u32(read)?,
119 size: read_u32(read)?,
120 position: read_u32(read)?,
121 })
122 }
123}
124
125fn find_best_size(toc: &[TocEntry], desired_size: u32) -> Result<u32, Error> {
127 fn distance(a: u32, b: u32) -> u32 {
128 a.max(b) - a.min(b)
129 }
130
131 fn is_better(desired_size: u32, entry: &TocEntry, result: &Result<u32, Error>) -> bool {
132 match result {
133 Err(_) => true,
134 Ok(size) => distance(entry.size, desired_size) < distance(*size, desired_size),
135 }
136 }
137
138 let mut result = Err(Error::NoImages);
139 for entry in toc {
140 if entry.kind == IMAGE_TYPE && is_better(desired_size, entry, &result) {
142 result = Ok(entry.size)
143 }
144 }
145 result
146}
147
148pub(crate) fn parse_cursor<R: Read + Seek>(
150 input: &mut R,
151 desired_size: u32,
152) -> Result<Vec<Image>, Error> {
153 let (magic, header, _version, ntoc) = (
154 read_u32(input)?,
155 read_u32(input)?,
156 read_u32(input)?,
157 read_u32(input)?,
158 );
159
160 if magic != FILE_MAGIC {
161 return Err(Error::InvalidMagic);
162 }
163
164 if ntoc > 0x1_0000 {
165 return Err(Error::TooManyEntries);
166 }
167
168 let _ = input.seek(SeekFrom::Start(header.into()))?;
170 let toc = (0..ntoc)
171 .map(|_| TocEntry::read(input))
172 .collect::<Result<Vec<_>, _>>()?;
173
174 let size = find_best_size(&toc, desired_size)?;
176
177 let mut result = Vec::new();
179 for entry in toc {
180 if entry.kind != IMAGE_TYPE || entry.size != size {
181 continue;
182 }
183 let _ = input.seek(SeekFrom::Start(entry.position.into()))?;
184 result.push(Image::read(input, entry.kind, entry.size)?);
185 }
186
187 Ok(result)
188}
189
190#[cfg(test)]
191mod test {
192 use super::{find_best_size, parse_cursor, Error, Image, TocEntry, IMAGE_TYPE};
193 use std::io::Cursor;
194
195 #[test]
196 fn read_3x5_image() {
197 let data = [
198 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfd, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
209 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
210 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
211 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
212 0x39, 0x3a, 0x3b, 0x3c,
213 ];
214 let image = Image::read(&mut Cursor::new(&data[..]), IMAGE_TYPE, 4).unwrap();
215 assert_eq!(image.width, 3);
216 assert_eq!(image.height, 5);
217 assert_eq!(image.x_hot, 7);
218 assert_eq!(image.y_hot, 8);
219 assert_eq!(image.delay, 42);
220 assert_eq!(
221 image.pixels,
222 &[
223 0x0403_0201,
224 0x0807_0605,
225 0x0c0b_0a09,
226 0x100f_0e0d,
227 0x1413_1211,
228 0x1817_1615,
229 0x1c1b_1a19,
230 0x201f_1e1d,
231 0x2423_2221,
232 0x2827_2625,
233 0x2c2b_2a29,
234 0x302f_2e2d,
235 0x3433_3231,
236 0x3837_3635,
237 0x3c3b_3a39,
238 ]
239 );
240 }
241
242 #[test]
243 fn read_corrupt_image_wrong_kind() {
244 let data = [
245 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfd, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
255 match Image::read(&mut Cursor::new(&data[..]), IMAGE_TYPE, 4) {
256 Err(Error::CorruptImage) => {}
257 r => panic!("Unexpected result {:?}", r),
258 }
259 }
260
261 #[test]
262 fn read_corrupt_image_wrong_size() {
263 let data = [
264 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfd, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
274 match Image::read(&mut Cursor::new(&data[..]), IMAGE_TYPE, 42) {
275 Err(Error::CorruptImage) => {}
276 r => panic!("Unexpected result {:?}", r),
277 }
278 }
279
280 #[test]
281 fn read_image_too_large_width() {
282 let data = [
283 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfd, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
293 match Image::read(&mut Cursor::new(&data[..]), IMAGE_TYPE, 4) {
294 Err(Error::ImageTooLarge) => {}
295 r => panic!("Unexpected result {:?}", r),
296 }
297 }
298
299 #[test]
300 fn read_image_too_large_height() {
301 let data = [
302 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfd, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
312 match Image::read(&mut Cursor::new(&data[..]), IMAGE_TYPE, 4) {
313 Err(Error::ImageTooLarge) => {}
314 r => panic!("Unexpected result {:?}", r),
315 }
316 }
317
318 #[test]
319 fn read_image_too_short() {
320 let data = [];
321 match Image::read(&mut Cursor::new(&data[..]), IMAGE_TYPE, 4) {
322 Err(Error::Io) => {}
323 r => panic!("Unexpected result {:?}", r),
324 }
325 }
326
327 #[test]
328 fn find_best_size_empty_input() {
329 let res = find_best_size(&[], 42);
330 match res {
331 Err(Error::NoImages) => {}
332 r => panic!("Unexpected result {:?}", r),
333 }
334 }
335
336 #[test]
337 fn find_best_size_one_input() {
338 let input = [TocEntry {
339 kind: IMAGE_TYPE,
340 size: 42,
341 position: 42,
342 }];
343 assert_eq!(42, find_best_size(&input, 10).unwrap());
344 }
345
346 #[test]
347 fn find_best_size_selects_better() {
348 let input = [
349 TocEntry {
350 kind: IMAGE_TYPE,
351 size: 42,
352 position: 42,
353 },
354 TocEntry {
355 kind: IMAGE_TYPE,
356 size: 32,
357 position: 42,
358 },
359 TocEntry {
360 kind: IMAGE_TYPE,
361 size: 3,
362 position: 42,
363 },
364 TocEntry {
365 kind: IMAGE_TYPE,
366 size: 22,
367 position: 42,
368 },
369 TocEntry {
370 kind: 0,
371 size: 10,
372 position: 42,
373 },
374 ];
375 assert_eq!(3, find_best_size(&input, 10).unwrap());
376 }
377
378 #[test]
379 fn parse_cursor_too_short() {
380 let data = [];
381 match parse_cursor(&mut Cursor::new(&data[..]), 10) {
382 Err(Error::Io) => {}
383 r => panic!("Unexpected result {:?}", r),
384 }
385 }
386
387 #[test]
388 fn parse_cursor_incorrect_magic() {
389 let data = [
390 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
395 match parse_cursor(&mut Cursor::new(&data[..]), 10) {
396 Err(Error::InvalidMagic) => {}
397 r => panic!("Unexpected result {:?}", r),
398 }
399 }
400
401 #[test]
402 fn parse_cursor_too_many_entries() {
403 let data = [
404 b'X', b'c', b'u', b'r', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, ];
409 match parse_cursor(&mut Cursor::new(&data[..]), 10) {
410 Err(Error::TooManyEntries) => {}
411 r => panic!("Unexpected result {:?}", r),
412 }
413 }
414
415 #[test]
416 fn parse_cursor_empty_toc() {
417 let data = [
418 b'X', b'c', b'u', b'r', 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
423 match parse_cursor(&mut Cursor::new(&data[..]), 10) {
424 Err(Error::NoImages) => {}
425 r => panic!("Unexpected result {:?}", r),
426 }
427 }
428
429 #[test]
430 fn parse_cursor_one_image() {
431 let data = [
432 b'X', b'c', b'u', b'r', 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfd, 0xff, 0x04, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfd, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
451 let expected = [Image {
452 width: 0,
453 height: 0,
454 x_hot: 0,
455 y_hot: 0,
456 delay: 0,
457 pixels: vec![],
458 }];
459 let actual = parse_cursor(&mut Cursor::new(&data[..]), 10).unwrap();
460 assert_same_images(&expected, &actual);
461 }
462
463 #[test]
464 fn parse_cursor_two_images_plus_one_ignored() {
465 let data = [
466 b'X', b'c', b'u', b'r', 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfd, 0xff, 0x04, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfd, 0xff, 0x03, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfd, 0xff, 0x04, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfd, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
491 let expected = [
492 Image {
493 width: 0,
494 height: 0,
495 x_hot: 0,
496 y_hot: 0,
497 delay: 0,
498 pixels: vec![],
499 },
500 Image {
501 width: 0,
502 height: 0,
503 x_hot: 0,
504 y_hot: 0,
505 delay: 0,
506 pixels: vec![],
507 },
508 ];
509 let actual = parse_cursor(&mut Cursor::new(&data[..]), 10).unwrap();
510 assert_same_images(&expected, &actual);
511 }
512
513 fn assert_same_images(a: &[Image], b: &[Image]) {
514 assert_eq!(a.len(), b.len(), "{:?} == {:?}", a, b);
515 for (i, (im1, im2)) in a.iter().zip(b.iter()).enumerate() {
516 assert_eq!(
517 im1.width,
518 im2.width,
519 "Width image {}: {} == {}",
520 i + 1,
521 im1.width,
522 im2.width
523 );
524 assert_eq!(
525 im1.height,
526 im2.height,
527 "Height image {}: {} == {}",
528 i + 1,
529 im1.height,
530 im2.height
531 );
532 assert_eq!(
533 im1.x_hot,
534 im2.x_hot,
535 "X-hot image {}: {} == {}",
536 i + 1,
537 im1.x_hot,
538 im2.x_hot
539 );
540 assert_eq!(
541 im1.y_hot,
542 im2.y_hot,
543 "Y-hot image {}: {} == {}",
544 i + 1,
545 im1.y_hot,
546 im2.y_hot
547 );
548 assert_eq!(
549 im1.delay,
550 im2.delay,
551 "Delay image {}: {} == {}",
552 i + 1,
553 im1.delay,
554 im2.delay
555 );
556 assert_eq!(
557 im1.pixels,
558 im2.pixels,
559 "Pixels image {}: {:?} == {:?}",
560 i + 1,
561 im1.pixels,
562 im2.pixels
563 );
564 }
565 }
566}