diff --git a/Cargo.lock b/Cargo.lock index caf58730b..55707a901 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1842,6 +1842,7 @@ name = "swf" version = "0.1.2" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "enumset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", "libflate 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/swf/Cargo.toml b/swf/Cargo.toml index 2ff6074b7..100e5f5b7 100644 --- a/swf/Cargo.toml +++ b/swf/Cargo.toml @@ -11,6 +11,7 @@ description = "Read and write the Adobe Flash SWF file format." [dependencies] byteorder = "1.0" +enumset = "0.4.2" num-derive = "0.3" num-traits = "0.2" libflate = {version = "0.1", optional = true} diff --git a/swf/src/read.rs b/swf/src/read.rs index 49116ef1f..cecbd5bd8 100644 --- a/swf/src/read.rs +++ b/swf/src/read.rs @@ -7,6 +7,7 @@ use crate::error::{Error, Result}; use crate::types::*; use byteorder::{LittleEndian, ReadBytesExt}; +use enumset::EnumSet; use std::collections::HashSet; use std::convert::TryInto; use std::io::{self, Read}; @@ -2234,7 +2235,7 @@ impl Reader { Ok(None) } else { let mut length = self.read_u32()?; - let key_code = if events.contains(&ClipEvent::KeyPress) { + let key_code = if events.contains(ClipEventFlag::KeyPress) { // ActionData length includes the 1 byte key code. length -= 1; Some(self.read_u8()?) @@ -2253,32 +2254,32 @@ impl Reader { } } - fn read_clip_event_flags(&mut self) -> Result> { + fn read_clip_event_flags(&mut self) -> Result> { // TODO: Switch to a bitset. - let mut event_list = HashSet::with_capacity(32); + let mut event_list = EnumSet::new(); if self.read_bit()? { - event_list.insert(ClipEvent::KeyUp); + event_list.insert(ClipEventFlag::KeyUp); } if self.read_bit()? { - event_list.insert(ClipEvent::KeyDown); + event_list.insert(ClipEventFlag::KeyDown); } if self.read_bit()? { - event_list.insert(ClipEvent::MouseUp); + event_list.insert(ClipEventFlag::MouseUp); } if self.read_bit()? { - event_list.insert(ClipEvent::MouseDown); + event_list.insert(ClipEventFlag::MouseDown); } if self.read_bit()? { - event_list.insert(ClipEvent::MouseMove); + event_list.insert(ClipEventFlag::MouseMove); } if self.read_bit()? { - event_list.insert(ClipEvent::Unload); + event_list.insert(ClipEventFlag::Unload); } if self.read_bit()? { - event_list.insert(ClipEvent::EnterFrame); + event_list.insert(ClipEventFlag::EnterFrame); } if self.read_bit()? { - event_list.insert(ClipEvent::Load); + event_list.insert(ClipEventFlag::Load); } if self.version < 6 { // SWF19 pp. 48-50: For SWFv5, the ClipEventFlags only had 2 bytes of flags, @@ -2287,41 +2288,41 @@ impl Reader { self.read_u8()?; } else { if self.read_bit()? { - event_list.insert(ClipEvent::DragOver); + event_list.insert(ClipEventFlag::DragOver); } if self.read_bit()? { - event_list.insert(ClipEvent::RollOut); + event_list.insert(ClipEventFlag::RollOut); } if self.read_bit()? { - event_list.insert(ClipEvent::RollOver); + event_list.insert(ClipEventFlag::RollOver); } if self.read_bit()? { - event_list.insert(ClipEvent::ReleaseOutside); + event_list.insert(ClipEventFlag::ReleaseOutside); } if self.read_bit()? { - event_list.insert(ClipEvent::Release); + event_list.insert(ClipEventFlag::Release); } if self.read_bit()? { - event_list.insert(ClipEvent::Press); + event_list.insert(ClipEventFlag::Press); } if self.read_bit()? { - event_list.insert(ClipEvent::Initialize); + event_list.insert(ClipEventFlag::Initialize); } if self.read_bit()? { - event_list.insert(ClipEvent::Data); + event_list.insert(ClipEventFlag::Data); } if self.version < 6 { self.read_u16()?; } else { self.read_ubits(5)?; if self.read_bit()? && self.version >= 7 { - event_list.insert(ClipEvent::Construct); + event_list.insert(ClipEventFlag::Construct); } if self.read_bit()? { - event_list.insert(ClipEvent::KeyPress); + event_list.insert(ClipEventFlag::KeyPress); } if self.read_bit()? { - event_list.insert(ClipEvent::DragOut); + event_list.insert(ClipEventFlag::DragOut); } self.read_u8()?; } diff --git a/swf/src/test_data.rs b/swf/src/test_data.rs index 1234a5005..23d86eaf7 100644 --- a/swf/src/test_data.rs +++ b/swf/src/test_data.rs @@ -2123,7 +2123,7 @@ pub fn tag_tests() -> Vec { background_color: None, blend_mode: BlendMode::Normal, clip_actions: vec![ClipAction { - events: vec![ClipEvent::EnterFrame].into_iter().collect(), + events: ClipEventFlag::EnterFrame.into(), key_code: None, action_data: vec![150, 6, 0, 0, 99, 108, 105, 112, 0, 38, 0], }], @@ -2154,19 +2154,17 @@ pub fn tag_tests() -> Vec { blend_mode: BlendMode::Normal, clip_actions: vec![ ClipAction { - events: vec![ClipEvent::Press, ClipEvent::Release] - .into_iter() - .collect(), + events: ClipEventFlag::Press | ClipEventFlag::Release, key_code: None, action_data: vec![150, 3, 0, 0, 65, 0, 38, 0], }, ClipAction { - events: vec![ClipEvent::KeyPress].into_iter().collect(), + events: ClipEventFlag::KeyPress.into(), key_code: Some(99), action_data: vec![150, 3, 0, 0, 66, 0, 38, 0], }, ClipAction { - events: vec![ClipEvent::EnterFrame].into_iter().collect(), + events: ClipEventFlag::EnterFrame.into(), key_code: None, action_data: vec![150, 3, 0, 0, 67, 0, 38, 0], }, @@ -2326,14 +2324,12 @@ pub fn tag_tests() -> Vec { blend_mode: BlendMode::Difference, clip_actions: vec![ ClipAction { - events: vec![ClipEvent::ReleaseOutside, ClipEvent::RollOver] - .into_iter() - .collect(), + events: ClipEventFlag::ReleaseOutside | ClipEventFlag::RollOver, key_code: None, action_data: vec![0], }, ClipAction { - events: vec![ClipEvent::Data].into_iter().collect(), + events: ClipEventFlag::Data.into(), key_code: None, action_data: vec![150, 3, 0, 0, 66, 0, 38, 0], }, diff --git a/swf/src/types.rs b/swf/src/types.rs index 0394daa34..adbec5f77 100644 --- a/swf/src/types.rs +++ b/swf/src/types.rs @@ -3,6 +3,7 @@ //! These structures are documented in the Adobe SWF File Foramt Specification //! version 19 (henceforth SWF19): //! https://www.adobe.com/content/dam/acom/en/devnet/pdf/swf-file-format-spec.pdf +use enumset::{EnumSet, EnumSetType}; use std::collections::HashSet; /// A complete header and tags in the SWF file. @@ -412,37 +413,46 @@ pub enum BlendMode { HardLight, } -#[derive(Debug, PartialEq, Eq, Clone)] +/// An clip action (a.k.a. clip event) placed on a movieclip instance. +/// Created in the Flash IDE using `onClipEvent` or `on` blocks. +/// +/// [SWF19 pp.37-38 ClipActionRecord](https://www.adobe.com/content/dam/acom/en/devnet/pdf/swf-file-format-spec.pdf#page=37) +#[derive(Debug, Clone, PartialEq)] pub struct ClipAction { - pub events: HashSet, - pub key_code: Option, + pub events: EnumSet, + pub key_code: Option, pub action_data: Vec, } -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub enum ClipEvent { +/// An event that can be attached to a movieclip instance using +/// an `onClipEvent` or `on` block. +/// +/// [SWF19 pp.48-50 ClipEvent](https://www.adobe.com/content/dam/acom/en/devnet/pdf/swf-file-format-spec.pdf#page=38) +#[derive(Debug, EnumSetType)] +pub enum ClipEventFlag { + Construct, + Data, + DragOut, + DragOver, + EnterFrame, + Initialize, KeyUp, KeyDown, + KeyPress, + Load, MouseUp, MouseDown, MouseMove, - Unload, - EnterFrame, - Load, - DragOver, + Press, RollOut, RollOver, - ReleaseOutside, Release, - Press, - Initialize, - Data, - Construct, - KeyPress, - DragOut, + ReleaseOutside, + Unload, } -pub type ClipEventFlags = HashSet; +/// A key code used in `ButtonAction` and `ClipAction` key press events. +pub type KeyCode = u8; /// Represents a tag in an SWF file. /// diff --git a/swf/src/write.rs b/swf/src/write.rs index dfa1b9e87..342a847c9 100644 --- a/swf/src/write.rs +++ b/swf/src/write.rs @@ -9,8 +9,8 @@ use crate::error::{Error, Result}; use crate::tag_code::TagCode; use crate::types::*; use byteorder::{LittleEndian, WriteBytesExt}; +use enumset::EnumSet; use std::cmp::max; -use std::collections::HashSet; use std::io::{self, Write}; /// Writes an SWF file to an output stream. @@ -2195,14 +2195,14 @@ impl Writer { fn write_clip_actions(&mut self, clip_actions: &[ClipAction]) -> Result<()> { self.write_u16(0)?; // Reserved { - let mut all_events = HashSet::with_capacity(32); + let mut all_events = EnumSet::new(); for action in clip_actions { - all_events = &all_events | &action.events; + all_events |= action.events; } - self.write_clip_event_flags(&all_events)?; + self.write_clip_event_flags(all_events)?; } for action in clip_actions { - self.write_clip_event_flags(&action.events)?; + self.write_clip_event_flags(action.events)?; let action_length = action.action_data.len() as u32 + if action.key_code.is_some() { 1 } else { 0 }; self.write_u32(action_length)?; @@ -2219,30 +2219,30 @@ impl Writer { Ok(()) } - fn write_clip_event_flags(&mut self, clip_events: &HashSet) -> Result<()> { + fn write_clip_event_flags(&mut self, clip_events: EnumSet) -> Result<()> { // TODO: Assert proper version. - self.write_bit(clip_events.contains(&ClipEvent::KeyUp))?; - self.write_bit(clip_events.contains(&ClipEvent::KeyDown))?; - self.write_bit(clip_events.contains(&ClipEvent::MouseUp))?; - self.write_bit(clip_events.contains(&ClipEvent::MouseDown))?; - self.write_bit(clip_events.contains(&ClipEvent::MouseMove))?; - self.write_bit(clip_events.contains(&ClipEvent::Unload))?; - self.write_bit(clip_events.contains(&ClipEvent::EnterFrame))?; - self.write_bit(clip_events.contains(&ClipEvent::Load))?; - self.write_bit(clip_events.contains(&ClipEvent::DragOver))?; - self.write_bit(clip_events.contains(&ClipEvent::RollOut))?; - self.write_bit(clip_events.contains(&ClipEvent::RollOver))?; - self.write_bit(clip_events.contains(&ClipEvent::ReleaseOutside))?; - self.write_bit(clip_events.contains(&ClipEvent::Release))?; - self.write_bit(clip_events.contains(&ClipEvent::Press))?; - self.write_bit(clip_events.contains(&ClipEvent::Initialize))?; - self.write_bit(clip_events.contains(&ClipEvent::Data))?; + self.write_bit(clip_events.contains(ClipEventFlag::KeyUp))?; + self.write_bit(clip_events.contains(ClipEventFlag::KeyDown))?; + self.write_bit(clip_events.contains(ClipEventFlag::MouseUp))?; + self.write_bit(clip_events.contains(ClipEventFlag::MouseDown))?; + self.write_bit(clip_events.contains(ClipEventFlag::MouseMove))?; + self.write_bit(clip_events.contains(ClipEventFlag::Unload))?; + self.write_bit(clip_events.contains(ClipEventFlag::EnterFrame))?; + self.write_bit(clip_events.contains(ClipEventFlag::Load))?; + self.write_bit(clip_events.contains(ClipEventFlag::DragOver))?; + self.write_bit(clip_events.contains(ClipEventFlag::RollOut))?; + self.write_bit(clip_events.contains(ClipEventFlag::RollOver))?; + self.write_bit(clip_events.contains(ClipEventFlag::ReleaseOutside))?; + self.write_bit(clip_events.contains(ClipEventFlag::Release))?; + self.write_bit(clip_events.contains(ClipEventFlag::Press))?; + self.write_bit(clip_events.contains(ClipEventFlag::Initialize))?; + self.write_bit(clip_events.contains(ClipEventFlag::Data))?; if self.version >= 6 { self.write_ubits(5, 0)?; - let has_construct = self.version >= 7 && clip_events.contains(&ClipEvent::Construct); + let has_construct = self.version >= 7 && clip_events.contains(ClipEventFlag::Construct); self.write_bit(has_construct)?; - self.write_bit(clip_events.contains(&ClipEvent::KeyPress))?; - self.write_bit(clip_events.contains(&ClipEvent::DragOut))?; + self.write_bit(clip_events.contains(ClipEventFlag::KeyPress))?; + self.write_bit(clip_events.contains(ClipEventFlag::DragOut))?; self.write_u8(0)?; } self.flush_bits()?;