ra2_shp/reader/
mod.rs

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    /// Create a new shp reader from file or buffer
21    ///
22    /// # Arguments
23    ///
24    /// * `buffer`:
25    ///
26    /// returns: Result<ShpReader<R>, Ra2Error>
27    ///
28    /// # Examples
29    ///
30    /// ```no_run
31    /// # use std::fs::File;
32    /// # use ra2_shp::ShpReader;
33    /// let file = File::open("ra2/conquer/engineer.shp")?;
34    /// let shp = ShpReader::new(file)?;
35    /// ```
36    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    /// Count the frames in `sha` animation
42    ///
43    /// # Examples
44    ///
45    /// ```no_run
46    /// # use std::fs::File;
47    /// # use ra2_shp::ShpReader;
48    /// let file = File::open("ra2/conquer/engineer.shp")?;
49    /// let mut shp = ShpReader::new(file)?;
50    /// let frames = shp.animation_frames();
51    /// ```
52    pub fn animation_frames(&self) -> u32 {
53        self.header.number_of_frames as u32
54    }
55    /// Get the max animation width in `sha` frames
56    ///
57    /// # Examples
58    ///
59    /// ```no_run
60    /// # use std::fs::File;
61    /// # use ra2_shp::ShpReader;
62    /// let file = File::open("ra2/conquer/engineer.shp")?;
63    /// let mut shp = ShpReader::new(file)?;
64    /// let width = shp.animation_width();
65    /// ```
66    pub fn animation_width(&self) -> u32 {
67        self.header.width as u32
68    }
69    /// Get the max animation height in `sha` frames
70    ///
71    /// # Examples
72    ///
73    /// ```no_run
74    /// # use std::fs::File;
75    /// # use ra2_shp::ShpReader;
76    /// let file = File::open("ra2/conquer/engineer.shp")?;
77    /// let mut shp = ShpReader::new(file)?;
78    /// let width = shp.animation_height();
79    /// ```
80    pub fn animation_height(&self) -> u32 {
81        self.header.height as u32
82    }
83    /// Get the raw `sha` frame buffer in `O(1)` time
84    ///
85    /// # Arguments
86    ///
87    /// * `index`:
88    ///
89    /// returns: Result<ShpFrame, Ra2Error>
90    ///
91    /// # Examples
92    ///
93    /// ```no_run
94    /// # use std::fs::File;
95    /// # use ra2_shp::ShpReader;
96    /// let file = File::open("ra2/conquer/engineer.shp")?;
97    /// let mut shp = ShpReader::new(file)?;
98    /// let idle = shp.get_frame(0);
99    /// ```
100    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
112// 读取文件头
113pub 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    // 读取帧数据
135    fn read_frame_data<R: Read + Seek>(&mut self, reader: &mut R) -> Result<(), Ra2Error> {
136        // 如果偏移量为 0,则表示空帧
137        if self.offset == 0 {
138            return Ok(());
139        }
140        // 跳转到帧数据的偏移位置
141        reader.seek(SeekFrom::Start(self.offset as u64))?;
142        // 检查是否使用压缩
143        if self.flags & 0x02 == 0 {
144            // 未压缩
145            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            // 使用 RLE 压缩
151            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
158// 解压缩 RLE 数据
159pub 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        // 获取该行长度
164        let row_length = reader.read_u16::<LittleEndian>()?;
165        // 已经读取了两个字节的行长度
166        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            // 0x00 代表透明
171            if control_byte == 0x00 {
172                // 透明像素个数
173                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        // 不明原因导致 line_buffer 有可能比 frame_width 长, 此时截掉多余部分即可
182        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
190/// Convert shp file to png format
191///
192/// # Arguments
193///
194/// * `file`:
195/// * `palette`:
196///
197/// returns: Result<(), Ra2Error>
198///
199/// # Examples
200///
201/// ```
202/// ```
203pub 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}
215/// Convert shp file to apng format
216///
217/// # Arguments
218///
219/// * `file`:
220/// * `palette`:
221///
222/// returns: Result<(), Ra2Error>
223///
224/// # Examples
225///
226/// ```
227/// ```
228pub 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}