1use crate::{ShpFrame, ShpHeader};
2use apng::{Encoder, Frame, PNGImage, load_dynamic_image};
3use byteorder::{LittleEndian, ReadBytesExt};
4use ra2_pal::Palette;
5use ra2_types::{DynamicImage, Ra2Error};
6use std::{
7 ffi::OsStr,
8 fs::File,
9 io::{BufReader, BufWriter, Read, Seek, SeekFrom},
10 path::Path,
11};
12
13#[derive(Debug)]
14pub struct ShpReader<R> {
15 header: ShpHeader,
16 reader: BufReader<R>,
17}
18
19impl<R: Read> ShpReader<R> {
20 pub fn new(buffer: R) -> Result<Self, Ra2Error> {
37 let mut reader = BufReader::new(buffer);
38 let file_header = read_file_header(&mut reader)?;
39 Ok(Self { header: file_header, reader })
40 }
41 pub fn animation_frames(&self) -> u32 {
53 self.header.number_of_frames as u32
54 }
55 pub fn animation_width(&self) -> u32 {
67 self.header.width as u32
68 }
69 pub fn animation_height(&self) -> u32 {
81 self.header.height as u32
82 }
83 pub fn get_frame(&mut self, index: u64) -> Result<ShpFrame, Ra2Error>
101 where
102 R: Seek,
103 {
104 self.reader.seek(SeekFrom::Start(8 + index * 24))?;
105 let mut buffer = ShpFrame::default();
106 buffer.read_frame_header(&mut self.reader)?;
107 buffer.read_frame_data(&mut self.reader)?;
108 Ok(buffer)
109 }
110}
111
112pub fn read_file_header<R: Read>(reader: &mut R) -> Result<ShpHeader, Ra2Error> {
114 let reserved = reader.read_u16::<LittleEndian>()?;
115 let width = reader.read_u16::<LittleEndian>()?;
116 let height = reader.read_u16::<LittleEndian>()?;
117 let number_of_frames = reader.read_u16::<LittleEndian>()?;
118 Ok(ShpHeader { reserved, width, height, number_of_frames })
119}
120
121impl ShpFrame {
122 fn read_frame_header<R: Read>(&mut self, reader: &mut R) -> Result<(), Ra2Error> {
123 self.x = reader.read_u16::<LittleEndian>()?;
124 self.y = reader.read_u16::<LittleEndian>()?;
125 self.width = reader.read_u16::<LittleEndian>()?;
126 self.height = reader.read_u16::<LittleEndian>()?;
127 self.flags = reader.read_u8()?;
128 reader.read_exact(&mut self.reserved1)?;
129 self.color = reader.read_u32::<LittleEndian>()?;
130 reader.read_exact(&mut self.reserved2)?;
131 self.offset = reader.read_u32::<LittleEndian>()?;
132 Ok(())
133 }
134 fn read_frame_data<R: Read + Seek>(&mut self, reader: &mut R) -> Result<(), Ra2Error> {
136 if self.offset == 0 {
138 return Ok(());
139 }
140 reader.seek(SeekFrom::Start(self.offset as u64))?;
142 if self.flags & 0x02 == 0 {
144 let frame_size = self.width as u32 * self.height as u32;
146 self.buffer = vec![0u8; frame_size as usize];
147 reader.read_exact(&mut self.buffer)?;
148 }
149 else {
150 self.buffer = decompress_rle_data(reader, self.width, self.height)?;
152 }
153 debug_assert_eq!(self.buffer.len(), self.width as usize * self.height as usize);
154 Ok(())
155 }
156}
157
158pub fn decompress_rle_data<R: Read>(reader: &mut R, frame_width: u16, frame_height: u16) -> Result<Vec<u8>, Ra2Error> {
160 let mut decompressed_data = Vec::with_capacity(frame_width as usize * frame_height as usize);
161 for _ in 0..frame_height {
162 let mut line_buffer = Vec::with_capacity(frame_width as usize);
163 let row_length = reader.read_u16::<LittleEndian>()?;
165 let mut current_byte_index = 2;
167 while current_byte_index < row_length {
168 let control_byte = reader.read_u8()?;
169 current_byte_index += 1;
170 if control_byte == 0x00 {
172 let transparent_count = reader.read_u8()?;
174 current_byte_index += 1;
175 line_buffer.extend(vec![0x00; transparent_count as usize]);
176 }
177 else {
178 line_buffer.push(control_byte);
179 }
180 }
181 for index in 0..frame_width {
183 let byte = line_buffer.get(index as usize).unwrap_or(&0);
184 decompressed_data.push(*byte);
185 }
186 }
187 Ok(decompressed_data)
188}
189
190pub fn shp2png(file: &Path, palette: &Palette) -> Result<(), Ra2Error> {
204 match file.extension() {
205 Some(s) if s.eq("shp") => {
206 let mut shp = ShpReader::new(File::open(&file)?)?;
207 let frame = shp.get_frame(0)?;
208 let image = frame.render(palette, shp.animation_width(), shp.animation_height())?;
209 image.save(&file.with_extension("png"))?;
210 }
211 _ => {}
212 }
213 Ok(())
214}
215pub fn shp2apng(file: &Path, palette: &Palette) -> Result<(), Ra2Error> {
229 let shp_path = Path::new(file);
230 let mut shp = ShpReader::new(File::open(shp_path)?)?;
231 let mut png_images: Vec<PNGImage> = Vec::new();
232 for index in 0..shp.animation_frames() {
233 match shp.get_frame(index as u64) {
234 Ok(frame) => {
235 let dy = DynamicImage::ImageRgba8(frame.render(&palette, shp.animation_width(), shp.animation_height())?);
236 let png = load_dynamic_image(dy).unwrap();
237 png_images.push(png)
238 }
239 Err(e) => {
240 tracing::error!("{}", e);
241 }
242 }
243 }
244 let path = shp_path.with_extension("apng");
245 let mut out = BufWriter::new(File::create(path)?);
246 let config = apng::create_config(&png_images, None)
247 .map_err(|e| Ra2Error::DecodeError { format: "apng".to_string(), message: e.to_string() })?;
248 let mut encoder = Encoder::new(&mut out, config).unwrap();
249 let frame = Frame { delay_num: Some(1), delay_den: Some(24), ..Default::default() };
250 encoder
251 .encode_all(png_images, Some(&frame))
252 .map_err(|e| Ra2Error::DecodeError { format: "apng".to_string(), message: e.to_string() })?;
253 Ok(())
254}