1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use crate::{parse::read_string, AsepriteParseError, Result};
use byteorder::{LittleEndian, ReadBytesExt};
use std::io::Cursor;

/// A tag is a grouping of one or more frames.
///
/// Tag ranges may overlap each other. Tag names are _not_ guaranteed to be
/// unique.
#[derive(Debug, Clone)]
pub struct Tag {
    name: String,
    from_frame: u16,
    to_frame: u16,
    animation_direction: AnimationDirection,
}

impl Tag {
    /// Tag name. May not be unique among all tags.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// First frame included in the tag.
    pub fn from_frame(&self) -> u32 {
        self.from_frame as u32
    }

    /// Last frame included in the tag.
    pub fn to_frame(&self) -> u32 {
        self.to_frame as u32
    }

    /// See [AnimationDirection] for details.
    pub fn animation_direction(&self) -> AnimationDirection {
        self.animation_direction
    }
}

/// Describes how the tag's frames should be animated.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AnimationDirection {
    /// Start at `from_frame` and count up to `to_frame`.
    Forward,
    /// Start at `to_frame` and count down to `from_frame`.
    Reverse,
    /// Start at `from_frame`, count up to `to_frame`, then back down to `from_frame`.
    PingPong,
}

pub(crate) fn parse_tags_chunk(data: &[u8]) -> Result<Vec<Tag>> {
    let mut input = Cursor::new(data);

    let num_tags = input.read_u16::<LittleEndian>()?;
    let _reserved = input.read_u64::<LittleEndian>()?;

    let mut result = Vec::with_capacity(num_tags as usize);

    for _tag in 0..num_tags {
        let from_frame = input.read_u16::<LittleEndian>()?;
        let to_frame = input.read_u16::<LittleEndian>()?;
        let anim_dir = input.read_u8()?;
        let _reserved = input.read_u64::<LittleEndian>()?;
        let _color = input.read_u32::<LittleEndian>()?;
        let name = read_string(&mut input)?;
        let animation_direction = parse_animation_direction(anim_dir)?;
        result.push(Tag {
            name,
            from_frame,
            to_frame,
            animation_direction,
        });
    }

    Ok(result)
}

fn parse_animation_direction(id: u8) -> Result<AnimationDirection> {
    match id {
        0 => Ok(AnimationDirection::Forward),
        1 => Ok(AnimationDirection::Reverse),
        2 => Ok(AnimationDirection::PingPong),
        _ => Err(AsepriteParseError::InvalidInput(format!(
            "Unknown animation direction: {}",
            id
        ))),
    }
}