1use static_assertions::assert_eq_size;
2use zerocopy::{
3 FromBytes,
4 little_endian::{F32, I32, U16, U32},
5};
6use zerocopy_derive::*;
7
8use crate::{
9 error::{ParsingError, ParsingResult},
10 util::{mip_level_size, pixel_size},
11};
12
13pub const MIP_LEVELS: usize = 4;
15pub const FONT_GLYPHS: usize = 256;
17
18pub const SPRITE_MAGIC: [u8; 4] = *b"IDSP";
20pub const SPRITE_VERSION: u32 = 2;
22
23pub const SPRITE_TYPE_FWD_PARALLEL_UPRIGHT: u32 = 0;
25pub const SPRITE_TYPE_FACING_UPRIGHT: u32 = 1;
26pub const SPRITE_TYPE_FWD_PARALLEL: u32 = 2;
27pub const SPRITE_TYPE_ORIENTED: u32 = 3;
28pub const SPRITE_TYPE_FWD_PARALLEL_ORIENTED: u32 = 4;
29
30pub const SPRITE_TEX_FORMAT_NORMAL: u32 = 0;
32pub const SPRITE_TEX_FORMAT_ADDITIVE: u32 = 1;
33pub const SPRITE_TEX_FORMAT_INDEX_ALPHA: u32 = 2;
34pub const SPRITE_TEX_FORMAT_ALPH_TEST: u32 = 3;
35
36pub const SPRITE_SYNC_TYPE_SYNC: u32 = 0;
38pub const SPRITE_SYNC_TYPE_RAND: u32 = 1;
39
40pub const SPRITE_FRAME_TYPE_SINGLE: u32 = 0;
42pub const SPRITE_FRAME_TYPE_GROUP: u32 = 1;
43
44pub type Rgb = [u8; 3];
46pub type PaletteIndex = u8;
48
49pub struct MipTexture<'a> {
51 pub header: &'a MipTextureHeader,
53 pub data: Option<ColorData<'a, MIP_LEVELS>>,
55}
56
57pub struct Picture<'a> {
59 pub header: &'a PictureHeader,
61 pub data: ColorData<'a, 1>,
63}
64
65pub struct Font<'a> {
67 pub header: &'a FontHeader,
69 pub data: ColorData<'a, 1>,
71}
72
73pub struct ColorData<'a, const N: usize> {
75 pub indices: [&'a [PaletteIndex]; N],
77 pub palette: &'a [Rgb],
79}
80
81pub struct Sprite<'a> {
83 pub header: &'a SpriteHeader,
85 pub palette: &'a [Rgb],
87 pub frames: Vec<SpriteFrame<'a>>,
89}
90
91pub enum SpriteFrame<'a> {
92 Single(SpriteFrameSingle<'a>),
93 Group(Vec<SpriteFrameGroup<'a>>),
94}
95
96pub struct SpriteFrameGroup<'a> {
97 pub interval: F32,
98 pub subframe: SpriteFrameSingle<'a>,
99}
100
101pub struct SpriteFrameSingle<'a> {
102 pub header: &'a SpriteFrameHeader,
104 pub indices: &'a [PaletteIndex],
106}
107
108#[repr(C)]
110#[derive(Debug, Clone, FromBytes, IntoBytes, KnownLayout, Immutable)]
111pub struct MipTextureHeader {
112 pub name: [u8; 16],
114 pub width: U32,
116 pub height: U32,
118 pub offsets: [U32; MIP_LEVELS],
120}
121
122#[repr(C)]
124#[derive(Debug, Clone, FromBytes, IntoBytes, KnownLayout, Immutable)]
125pub struct PictureHeader {
126 pub width: U32,
128 pub height: U32,
130}
131
132#[repr(C)]
134#[derive(Debug, Clone, FromBytes, IntoBytes, KnownLayout, Immutable)]
135pub struct FontHeader {
136 pub width: U32,
138 pub height: U32,
140 pub row_count: U32,
142 pub row_height: U32,
144 pub chars: [CharInfo; FONT_GLYPHS],
146}
147
148#[repr(C)]
150#[derive(Debug, Clone, FromBytes, IntoBytes, KnownLayout, Immutable)]
151pub struct CharInfo {
152 pub offset: U16,
154 pub width: U16,
156}
157
158#[repr(C)]
160#[derive(Debug, Clone, FromBytes, IntoBytes, KnownLayout, Immutable)]
161pub struct SpriteHeader {
162 pub magic: [u8; 4],
164 pub version: U32,
166 pub ty: U32,
168 pub tex_format: U32,
170 pub bounding_radius: F32,
172 pub bounds: [U32; 2],
174 pub frames_num: U32,
176 pub beam_len: F32,
178 pub sync_type: U32,
180}
181
182#[repr(C)]
184#[derive(Debug, Clone, FromBytes, IntoBytes, KnownLayout, Immutable)]
185pub struct SpriteFrameHeader {
186 pub origin: [I32; 2],
188 pub width: U32,
190 pub height: U32,
192}
193
194pub fn mip_texture(bytes: &[u8]) -> ParsingResult<MipTexture<'_>> {
195 let (header, _) = MipTextureHeader::ref_from_prefix(bytes)
196 .map_err(|_| ParsingError::OutOfRange("miptex header"))?;
197 let width = header.width.get();
198 let height = header.height.get();
199
200 if header.offsets.iter().any(|offset| offset.get() == 0) {
201 return Ok(MipTexture { header, data: None });
202 }
203
204 let mut ptr = bytes;
205 let mut indices = [Default::default(); MIP_LEVELS];
206 for (level, slot) in indices.iter_mut().enumerate() {
207 let offset = usize::try_from(header.offsets[level].get())
208 .map_err(|_| ParsingError::NumberOverflow("miptex offset"))?;
209 let size = mip_level_size(width, height, level, "miptex")?;
210 let data = bytes
211 .get(offset..)
212 .ok_or(ParsingError::OutOfRange("miptex data"))?;
213
214 let (indices, bytes) = <[PaletteIndex]>::ref_from_prefix_with_elems(data, size)
215 .map_err(|_| ParsingError::Invalid("miptex palette indices"))?;
216 *slot = indices;
217 ptr = bytes;
218 }
219
220 let (palette, _) = palette_ref(ptr)?;
221
222 Ok(MipTexture {
223 header,
224 data: Some(ColorData { indices, palette }),
225 })
226}
227
228pub fn picture(bytes: &[u8]) -> ParsingResult<Picture<'_>> {
229 let (header, bytes) = PictureHeader::ref_from_prefix(bytes)
230 .map_err(|_| ParsingError::OutOfRange("pic header"))?;
231 let width = header.width.get();
232 let height = header.height.get();
233 let size = pixel_size(width, height, "picture")?;
234
235 let (indices, bytes) = <[PaletteIndex]>::ref_from_prefix_with_elems(bytes, size)
236 .map_err(|_| ParsingError::Invalid("pic indices"))?;
237 let (palette, _) = palette_ref(bytes)?;
238
239 Ok(Picture {
240 header,
241 data: ColorData {
242 indices: [indices],
243 palette,
244 },
245 })
246}
247
248pub fn font(bytes: &[u8]) -> ParsingResult<Font<'_>> {
249 let (header, bytes) =
250 FontHeader::ref_from_prefix(bytes).map_err(|_| ParsingError::OutOfRange("font header"))?;
251
252 let width = header.width.get();
253 let height = header.height.get();
254 let size = pixel_size(width, height, "font")?;
255
256 let (indices, bytes) = <[PaletteIndex]>::ref_from_prefix_with_elems(bytes, size)
257 .map_err(|_| ParsingError::Invalid("font indices"))?;
258 let (palette, _) = palette_ref(bytes)?;
259
260 Ok(Font {
261 header,
262 data: ColorData {
263 indices: [indices],
264 palette,
265 },
266 })
267}
268
269pub fn sprite(bytes: &[u8]) -> ParsingResult<Sprite<'_>> {
270 let (header, bytes) = SpriteHeader::ref_from_prefix(bytes)
271 .map_err(|_| ParsingError::OutOfRange("sprite header"))?;
272
273 if header.magic != SPRITE_MAGIC {
274 return Err(ParsingError::WrongFourCC {
275 got: header.magic,
276 expected: SPRITE_MAGIC,
277 });
278 }
279
280 let version = header.version.get();
281 if version != SPRITE_VERSION {
282 return Err(ParsingError::WrongVersion {
283 got: version,
284 expected: SPRITE_VERSION,
285 });
286 }
287
288 let (palette, bytes) = palette_ref(bytes)?;
289 let frames = frames_ref(bytes, header)?;
290
291 Ok(Sprite {
292 header,
293 palette,
294 frames,
295 })
296}
297
298fn palette_ref(bytes: &[u8]) -> ParsingResult<(&'_ [Rgb], &'_ [u8])> {
299 let (size, bytes) =
300 U16::ref_from_prefix(bytes).map_err(|_| ParsingError::OutOfRange("palette header"))?;
301
302 let count = usize::from(size.get()).min(256);
303 let (palette, bytes) = <[Rgb]>::ref_from_prefix_with_elems(bytes, count)
304 .map_err(|_| ParsingError::Invalid("palette"))?;
305
306 Ok((palette, bytes))
307}
308
309fn frames_ref<'a>(bytes: &'a [u8], header: &SpriteHeader) -> ParsingResult<Vec<SpriteFrame<'a>>> {
310 let count = usize::try_from(header.frames_num.get())
311 .map_err(|_| ParsingError::NumberOverflow("sprite frame count"))?;
312
313 let mut frames = Vec::with_capacity(count);
314 let mut ptr = bytes;
315 for _ in 0..count {
316 let (frame, bytes) = frame_ref(ptr)?;
317 frames.push(frame);
318 ptr = bytes;
319 }
320
321 Ok(frames)
322}
323
324fn frame_ref(bytes: &[u8]) -> ParsingResult<(SpriteFrame<'_>, &'_ [u8])> {
325 let (group, bytes) =
326 U32::ref_from_prefix(bytes).map_err(|_| ParsingError::OutOfRange("sprite frame header"))?;
327
328 match group.get() {
329 SPRITE_FRAME_TYPE_SINGLE => {
330 let (single, bytes) = frame_single_ref(bytes)?;
331 Ok((SpriteFrame::Single(single), bytes))
332 }
333 SPRITE_FRAME_TYPE_GROUP => {
334 let (count, bytes) = U32::ref_from_prefix(bytes)
335 .map_err(|_| ParsingError::OutOfRange("sprite group header"))?;
336 let count = usize::try_from(count.get())
337 .map_err(|_| ParsingError::NumberOverflow("sprite group subframes count"))?;
338 let (intervals, bytes) = <[F32]>::ref_from_prefix_with_elems(bytes, count)
339 .map_err(|_| ParsingError::OutOfRange("sprite group intervals"))?;
340
341 let mut subframes = Vec::with_capacity(count);
342 let mut ptr = bytes;
343 for interval in intervals.iter().copied() {
344 let (subframe, bytes) = frame_single_ref(ptr)?;
345 subframes.push(SpriteFrameGroup { interval, subframe });
346 ptr = bytes;
347 }
348
349 Ok((SpriteFrame::Group(subframes), ptr))
350 }
351 _ => Err(ParsingError::Invalid("sprite group type")),
352 }
353}
354
355fn frame_single_ref(bytes: &[u8]) -> ParsingResult<(SpriteFrameSingle<'_>, &'_ [u8])> {
356 let (header, bytes) = SpriteFrameHeader::ref_from_prefix(bytes)
357 .map_err(|_| ParsingError::OutOfRange("sprite single frame header"))?;
358 let width = header.width.get();
359 let height = header.height.get();
360 let size = pixel_size(width, height, "sprite single frame")?;
361 let (indices, bytes) = <[PaletteIndex]>::ref_from_prefix_with_elems(bytes, size)
362 .map_err(|_| ParsingError::Invalid("sprite single frame indices"))?;
363
364 Ok((SpriteFrameSingle { header, indices }, bytes))
365}
366
367assert_eq_size!(MipTextureHeader, [u8; 40]);
368assert_eq_size!(PictureHeader, [u8; 8]);
369assert_eq_size!(FontHeader, ([u8; 16], [u8; 4 * FONT_GLYPHS]));
370assert_eq_size!(CharInfo, [u8; 4]);
371assert_eq_size!(SpriteHeader, [u8; 40]);
372assert_eq_size!(SpriteFrameHeader, [u8; 16]);
373assert_eq_size!(Rgb, [u8; 3]);