swf_parser/complete/
button.rs

1use crate::complete::base::skip;
2use crate::complete::display::{parse_blend_mode, parse_filter_list};
3use crate::complete::sound::parse_sound_info;
4use crate::streaming::basic_data_types::{parse_color_transform_with_alpha, parse_matrix};
5use nom::number::complete::{le_u16 as parse_le_u16, le_u8 as parse_u8};
6use nom::IResult as NomResult;
7use swf_types as swf;
8
9#[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)]
10pub enum ButtonVersion {
11  Button1,
12  Button2,
13}
14
15pub fn parse_button_record_string(input: &[u8], version: ButtonVersion) -> NomResult<&[u8], Vec<swf::ButtonRecord>> {
16  let mut result: Vec<swf::ButtonRecord> = Vec::new();
17  let mut current_input: &[u8] = input;
18  loop {
19    if current_input.is_empty() {
20      return Err(::nom::Err::Incomplete(::nom::Needed::Unknown));
21    }
22    if current_input[0] == 0 {
23      // End of string
24      current_input = &current_input[1..];
25      break;
26    }
27    match parse_button_record(current_input, version) {
28      Ok((next_input, button_record)) => {
29        current_input = next_input;
30        result.push(button_record);
31      }
32      Err(::nom::Err::Incomplete(_)) => return Err(::nom::Err::Incomplete(::nom::Needed::Unknown)),
33      Err(e) => return Err(e),
34    };
35  }
36  Ok((current_input, result))
37}
38
39pub fn parse_button_record(input: &[u8], version: ButtonVersion) -> NomResult<&[u8], swf::ButtonRecord> {
40  let (input, flags) = parse_u8(input)?;
41  #[allow(clippy::identity_op)]
42  let state_up = (flags & (1 << 0)) != 0;
43  let state_over = (flags & (1 << 1)) != 0;
44  let state_down = (flags & (1 << 2)) != 0;
45  let state_hit_test = (flags & (1 << 3)) != 0;
46  let has_filter_list = (flags & (1 << 4)) != 0;
47  let has_blend_mode = (flags & (1 << 5)) != 0;
48  // (Skip bits [6, 7])
49  let (input, character_id) = parse_le_u16(input)?;
50  let (input, depth) = parse_le_u16(input)?;
51  let (input, matrix) = parse_matrix(input)?;
52  let (input, color_transform) = if version >= ButtonVersion::Button2 {
53    parse_color_transform_with_alpha(input)?
54  } else {
55    (input, swf::ColorTransformWithAlpha::default())
56  };
57  let (input, filters) = if version >= ButtonVersion::Button2 && has_filter_list {
58    parse_filter_list(input)?
59  } else {
60    (input, Vec::new())
61  };
62  let (input, blend_mode) = if version >= ButtonVersion::Button2 && has_blend_mode {
63    parse_blend_mode(input)?
64  } else {
65    (input, swf::BlendMode::Normal)
66  };
67
68  Ok((
69    input,
70    swf::ButtonRecord {
71      state_up,
72      state_over,
73      state_down,
74      state_hit_test,
75      character_id,
76      depth,
77      matrix,
78      color_transform,
79      filters,
80      blend_mode,
81    },
82  ))
83}
84
85pub fn parse_button2_cond_action_string(mut input: &[u8]) -> NomResult<&[u8], Vec<swf::ButtonCondAction>> {
86  let mut actions: Vec<swf::ButtonCondAction> = Vec::new();
87  loop {
88    let (_, (next_action_offset, cond_action)) = parse_button2_cond_action(input)?;
89    actions.push(cond_action);
90    if next_action_offset == 0 {
91      break;
92    }
93    let (next_input, ()) = skip(next_action_offset)(input)?;
94    input = next_input;
95  }
96  Ok((input, actions))
97}
98
99pub fn parse_button2_cond_action(input: &[u8]) -> NomResult<&[u8], (usize, swf::ButtonCondAction)> {
100  use nom::combinator::map;
101  let (input, next_action_offset) = map(parse_le_u16, usize::from)(input)?;
102  let (input, conditions) = parse_button_cond(input)?;
103  let value = swf::ButtonCondAction {
104    conditions: Some(conditions),
105    actions: input.to_vec(),
106  };
107  Ok((input, (next_action_offset, value)))
108}
109
110pub fn parse_button_cond(input: &[u8]) -> NomResult<&[u8], swf::ButtonCond> {
111  fn key_press_from_code(key_press_id: u16) -> Result<Option<u32>, ()> {
112    match key_press_id {
113      0 => Ok(None),
114      k @ 1..=6 | k @ 8 | k @ 13..=19 | k @ 32..=126 => Ok(Some(u32::from(k))),
115      _ => Err(()),
116    }
117  }
118
119  let (input, flags) = parse_le_u16(input)?;
120  #[allow(clippy::identity_op)]
121  let idle_to_over_up = (flags & (1 << 0)) != 0;
122  let over_up_to_idle = (flags & (1 << 1)) != 0;
123  let over_up_to_over_down = (flags & (1 << 2)) != 0;
124  let over_down_to_over_up = (flags & (1 << 3)) != 0;
125  let over_down_to_out_down = (flags & (1 << 4)) != 0;
126  let out_down_to_over_down = (flags & (1 << 5)) != 0;
127  let out_down_to_idle = (flags & (1 << 6)) != 0;
128  let idle_to_over_down = (flags & (1 << 7)) != 0;
129  let over_down_to_idle = (flags & (1 << 8)) != 0;
130  let key_press =
131    key_press_from_code((flags >> 9) & 0x7f).map_err(|_| nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Switch)))?;
132
133  Ok((
134    input,
135    swf::ButtonCond {
136      idle_to_over_up,
137      over_up_to_idle,
138      over_up_to_over_down,
139      over_down_to_over_up,
140      over_down_to_out_down,
141      out_down_to_over_down,
142      out_down_to_idle,
143      idle_to_over_down,
144      over_down_to_idle,
145      key_press,
146    },
147  ))
148}
149
150pub fn parse_button_sound(input: &[u8]) -> NomResult<&[u8], Option<swf::ButtonSound>> {
151  let (input, sound_id) = parse_le_u16(input)?;
152  if sound_id == 0 {
153    Ok((input, None))
154  } else {
155    let (input, sound_info) = parse_sound_info(input)?;
156    Ok((input, Some(swf::ButtonSound { sound_id, sound_info })))
157  }
158}