pmd_wan/
fragment.rs

1use crate::get_bit_u16;
2use crate::FragmentFlip;
3use crate::OamShape;
4use crate::WanError;
5use anyhow::bail;
6use byteorder::WriteBytesExt;
7use byteorder::{ReadBytesExt, LE};
8use std::io::{Read, Write};
9
10/// A [`Fragment`] may reference an [`crate::FragmentBytes`], that will form a single (or all if small enought) part of an [`crate::Frame`]
11#[derive(Debug, PartialEq, Eq, Clone)]
12pub struct Fragment {
13    pub unk1: u16,
14    /// Two value with unknown property in the offset y data.
15    /// most of the time, the unk3 is equal to offset_y < 0, and unk4 the inverse (will be automatically computed if set to None)
16    /// otherwise the two boolean in the tuple will be used
17    pub unk3_4: Option<(bool, bool)>,
18    pub unk5: bool, // maybe is "invert palette color"
19    pub fragment_bytes_index: usize,
20    pub offset_y: i8,
21    pub offset_x: i16,
22    pub flip: FragmentFlip,
23    pub is_mosaic: bool,
24    pub pal_idx: u16,
25    pub resolution: OamShape,
26}
27
28impl Fragment {
29    /// parse a metaframe from the file.
30    /// The second value is whether the "is_last" bit has been set to true, meaning it's the last Fragment from the Frame
31    pub fn new_from_bytes<F: Read>(
32        file: &mut F,
33        previous_fragment_bytes: Option<usize>,
34    ) -> Result<(Fragment, bool), WanError> {
35        trace!("parsing a fragment");
36        let fragment_bytes_index = match file.read_i16::<LE>()? {
37            -1 => match previous_fragment_bytes {
38                None => return Err(WanError::FragmentBytesIDPointBackButFirstFragment),
39                Some(value) => value,
40            },
41            x => {
42                if x >= 0 {
43                    x as usize
44                } else {
45                    return Err(WanError::FragmentLessThanLessOne(x));
46                }
47            }
48        };
49
50        let unk1 = file.read_u16::<LE>()?;
51
52        // they are quite strangely encoded (the fact I should read as little-endian the 2 byte correctly reading them)
53
54        // bit in ppmdu tool are right to left !!!
55        let offset_y_data = file.read_u16::<LE>()?;
56        let size_indice_y = ((0xC000 & offset_y_data) >> (8 + 6)) as u8;
57        let is_mosaic = get_bit_u16(offset_y_data, 3).unwrap(); //no panic: always return if indice less than 16
58        let unk3 = get_bit_u16(offset_y_data, 7).unwrap();
59        let unk4 = get_bit_u16(offset_y_data, 6).unwrap();
60        let offset_y = (offset_y_data & 0x00FF) as i8; //range: 0-255 (integer) //TODO: shouldn’t that be u8?
61
62        #[allow(clippy::collapsible_else_if)]
63        let unk3_4 = if offset_y < 0 {
64            if unk3 && !unk4 {
65                None
66            } else {
67                Some((unk3, unk4))
68            }
69        } else {
70            if !unk3 && unk4 {
71                None
72            } else {
73                Some((unk3, unk4))
74            }
75        };
76
77        let offset_x_data = file.read_u16::<LE>()?;
78        let size_indice_x = ((0xC000 & offset_x_data) >> (8 + 6)) as u8;
79        let v_flip = get_bit_u16(offset_x_data, 2).unwrap(); //as no panic as before
80        let h_flip = get_bit_u16(offset_x_data, 3).unwrap();
81        let flip = FragmentFlip::from_bools(v_flip, h_flip);
82        let is_last = get_bit_u16(offset_x_data, 4).unwrap();
83        let unk5 = get_bit_u16(offset_x_data, 5).unwrap();
84        let offset_x = (offset_x_data & 0x01FF) as i16 - 256; //range: 0-511
85
86        let alloc_and_palette = file.read_u16::<LE>()?;
87        let pal_idx = (0xF000 & alloc_and_palette) >> 12;
88
89        Ok((
90            Fragment {
91                unk1,
92                unk3_4,
93                unk5,
94                fragment_bytes_index,
95                offset_x,
96                offset_y,
97                flip,
98                is_mosaic,
99                pal_idx,
100                resolution: match OamShape::new(size_indice_y, size_indice_x) {
101                    Some(r) => r,
102                    None => {
103                        return Err(WanError::InvalidResolutionIndice(
104                            size_indice_x,
105                            size_indice_y,
106                        ))
107                    }
108                },
109            },
110            is_last,
111        ))
112    }
113
114    pub fn write<F: Write>(
115        &self,
116        file: &mut F,
117        previous_fragment_bytes: Option<usize>,
118        is_last: bool,
119        fragment_alloc_counter: u16,
120    ) -> anyhow::Result<()> {
121        //TODO: use try_into, or maybe even directly i16
122        let fragment_bytes_index: i16 = match previous_fragment_bytes {
123            None => self.fragment_bytes_index as i16,
124            Some(value) => {
125                if self.fragment_bytes_index == value {
126                    -1
127                } else {
128                    self.fragment_bytes_index as i16
129                }
130            }
131        };
132
133        file.write_i16::<LE>(fragment_bytes_index)?;
134        file.write_u16::<LE>(self.unk1)?;
135
136        let (unk3, unk4) = match self.unk3_4 {
137            Some((unk3, unk4)) => (unk3, unk4),
138            None => {
139                let unk3 = self.offset_y < 0;
140                (unk3, !unk3)
141            }
142        };
143
144        let offset_y_data: u16 = ((self.resolution.shape_indice() as u16) << (8 + 6))
145            + if self.is_mosaic { 1 << (8 + 4) } else { 0 }
146            + ((unk4 as u16) << (8 + 1))
147            + ((unk3 as u16) << 8)
148            + ((self.offset_y as u16) & 0x00FF);
149
150        let written_offset_x = self.offset_x + 256;
151        if written_offset_x >= 0x200 {
152            bail!(
153                "The x coordinate of this metaframe is more than 255 (it is {})",
154                self.offset_x
155            );
156        }
157        if written_offset_x < 0 {
158            bail!(
159                "The x coordinate of this metaframe is less than 256 (it is {})",
160                self.offset_x
161            );
162        }
163
164        let (v_flip, h_flip) = self.flip.to_bools();
165
166        let offset_x_data: u16 = ((self.resolution.size_indice() as u16) << (8 + 6))
167            + ((v_flip as u16) << (8 + 5))
168            + ((h_flip as u16) << (8 + 4))
169            + ((is_last as u16) << (8 + 3))
170            + ((self.unk5 as u16) << (8 + 2))
171            + (((written_offset_x) as u16) & 0x01FF);
172
173        file.write_u16::<LE>(offset_y_data)?;
174        file.write_u16::<LE>(offset_x_data)?;
175        file.write_u16::<LE>(
176            ((self.pal_idx & 0xF) << 12) + 0x0C00 + (fragment_alloc_counter & 0x3FF),
177        )?;
178
179        Ok(())
180    }
181}