1use crate::color::Color16;
15#[cfg(feature = "image")]
16use crate::color::{BLACK_16, Color};
17#[cfg(feature = "image")]
18use crate::error::ToImageError;
19use crate::error::{MulReaderError, MulReaderResult};
20use crate::mul::MulReader;
21use byteorder::{LittleEndian, ReadBytesExt};
22#[cfg(feature = "image")]
23use image::error::{DecodingError, ImageFormatHint};
24#[cfg(feature = "image")]
25use image::{ImageError, Rgba, RgbaImage};
26use std::fs::File;
27use std::io::{Cursor, Read, Seek, SeekFrom};
28use std::path::Path;
29
30#[derive(Debug, PartialEq, Eq, Clone, Copy)]
32pub struct GumpPair {
33 pub color: Color16,
34 pub count: u16,
35}
36
37#[derive(Debug, PartialEq, Eq, Clone)]
39pub struct Gump {
40 pub width: u16,
41 pub height: u16,
42 pub data: Vec<Vec<GumpPair>>,
44}
45
46#[cfg(feature = "image")]
47impl Gump {
48 pub fn to_image(&self) -> Result<RgbaImage, ImageError> {
50 let mut buffer = RgbaImage::new(self.width as u32, self.height as u32);
51 for (y, row) in self.data.iter().enumerate() {
52 let mut x = 0;
53 for run_pair in row {
54 if x + run_pair.count > self.width {
56 return Err(ImageError::Decoding(DecodingError::new(
57 ImageFormatHint::Name("UO Gump".to_string()),
58 ToImageError::PixelOutOfBounds {
59 x: (x + run_pair.count) as i64,
60 y: y as i64,
61 },
62 )));
63 }
64
65 if run_pair.color != BLACK_16 {
67 let (r, g, b, a) = run_pair.color.to_rgba();
68 for i in 0..run_pair.count {
69 buffer.put_pixel(x as u32 + i as u32, y as u32, Rgba([r, g, b, a]));
70 }
71 }
72 x += run_pair.count;
73 }
74 }
75 Ok(buffer)
76 }
77}
78
79#[derive(Debug)]
81pub struct GumpReader<T: Read + Seek> {
82 mul_reader: MulReader<T>,
83}
84
85impl GumpReader<File> {
86 pub fn new(index_path: &Path, mul_path: &Path) -> MulReaderResult<GumpReader<File>> {
88 let mul_reader = MulReader::new(index_path, mul_path)?;
89 Ok(GumpReader { mul_reader })
90 }
91}
92
93impl<T: Read + Seek> GumpReader<T> {
94 pub fn from_mul(reader: MulReader<T>) -> GumpReader<T> {
96 GumpReader { mul_reader: reader }
97 }
98
99 pub fn read(&mut self, index: u32) -> MulReaderResult<Gump> {
101 let raw = self.mul_reader.read(index)?;
102 let mut output = vec![];
103 let len = raw.data.len();
104
105 if len % 4 != 0 {
106 return Err(MulReaderError::UnexpectedSize {
107 found: len as u32,
108 expected: (len + (len % 4)) as u32,
109 });
110 }
111
112 let mut reader = Cursor::new(raw.data);
113 let mut row_offsets = vec![];
114 for _i in 0..raw.opt1 {
116 row_offsets.push(reader.read_u32::<LittleEndian>()?);
117 }
118
119 for (row_idx, offset) in row_offsets.iter().enumerate() {
121 let row_length = if row_idx == row_offsets.len() - 1 {
122 (len / 4) as u32 - offset
123 } else {
124 let next_row = row_offsets[row_idx + 1];
125 next_row - offset
126 };
127 reader.seek(SeekFrom::Start((*offset as u64) * 4))?;
128 let mut row = vec![];
129 for _i in 0..row_length {
130 let color = reader.read_u16::<LittleEndian>()?;
131 let count = reader.read_u16::<LittleEndian>()?;
132 row.push(GumpPair { color, count });
133 }
134 output.push(row);
135 }
136 Ok(Gump {
137 height: raw.opt1,
138 width: raw.opt2,
139 data: output,
140 })
141 }
142}