swf_parser/complete/
button.rs1use 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 current_input = ¤t_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 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}