From 55ffda9c972d726edd004eca6629f097a6034bd9 Mon Sep 17 00:00:00 2001 From: relrelb Date: Fri, 27 Aug 2021 14:10:53 +0300 Subject: [PATCH] swf: Take advantage of `ClipEventFlag` bit pattern Re-number the `ClipEventFlag` enum members to match how they actually appear in a SWF. This allows much simpler read/write operations. Also, gracefully handle malformed ClipActions that are only 1 or 0 bytes, as it seems that Flash accepts those too. --- swf/src/read.rs | 54 +++++++++++------------------------------------- swf/src/types.rs | 41 ++++++++++++++++++++---------------- swf/src/write.rs | 29 +++++--------------------- 3 files changed, 40 insertions(+), 84 deletions(-) diff --git a/swf/src/read.rs b/swf/src/read.rs index 92270e96c..329b7b636 100644 --- a/swf/src/read.rs +++ b/swf/src/read.rs @@ -2027,7 +2027,7 @@ impl<'a> Reader<'a> { fn read_clip_actions(&mut self) -> Result>> { self.read_u16()?; // Must be 0 - self.read_clip_event_flags()?; // All event flags + self.read_clip_event_flags(); // All event flags let mut clip_actions = vec![]; while let Some(clip_action) = self.read_clip_action()? { clip_actions.push(clip_action); @@ -2036,7 +2036,7 @@ impl<'a> Reader<'a> { } fn read_clip_action(&mut self) -> Result>> { - let events = self.read_clip_event_flags()?; + let events = self.read_clip_event_flags(); if events.is_empty() { Ok(None) } else { @@ -2058,51 +2058,21 @@ impl<'a> Reader<'a> { } } - fn read_clip_event_flags(&mut self) -> Result { - // TODO: Switch to a bitset. - let mut event_list = ClipEventFlag::empty(); - - let flags = self.read_u8()?; - event_list.set(ClipEventFlag::KEY_UP, flags & 0b1000_0000 != 0); - event_list.set(ClipEventFlag::KEY_DOWN, flags & 0b0100_0000 != 0); - event_list.set(ClipEventFlag::MOUSE_UP, flags & 0b0010_0000 != 0); - event_list.set(ClipEventFlag::MOUSE_DOWN, flags & 0b0001_0000 != 0); - event_list.set(ClipEventFlag::MOUSE_MOVE, flags & 0b0000_1000 != 0); - event_list.set(ClipEventFlag::UNLOAD, flags & 0b0000_0100 != 0); - event_list.set(ClipEventFlag::ENTER_FRAME, flags & 0b0000_0010 != 0); - event_list.set(ClipEventFlag::LOAD, flags & 0b0000_0001 != 0); - - if self.version > 5 { - // There are SWFs in the wild with malformed final ClipActions that is only two bytes - // instead of four bytes (see #2899). Handle this gracefully to allow the tag to run. - // TODO: We may need a more general way to handle truncated tags, since this has - // occurred in a few different places. - // Allow for only two bytes in the clip action tag. - let flags = self.read_u8().unwrap_or_default(); - let flags2 = self.read_u8().unwrap_or_default(); - let _ = self.read_u8(); - event_list.set(ClipEventFlag::DRAG_OVER, flags & 0b1000_0000 != 0); - event_list.set(ClipEventFlag::ROLL_OUT, flags & 0b0100_0000 != 0); - event_list.set(ClipEventFlag::ROLL_OVER, flags & 0b0010_0000 != 0); - event_list.set(ClipEventFlag::RELEASE_OUTSIDE, flags & 0b0001_0000 != 0); - event_list.set(ClipEventFlag::RELEASE, flags & 0b0000_1000 != 0); - event_list.set(ClipEventFlag::PRESS, flags & 0b0000_0100 != 0); - event_list.set(ClipEventFlag::INITIALIZE, flags & 0b0000_0010 != 0); - event_list.set(ClipEventFlag::DATA, flags & 0b0000_0001 != 0); - - // Construct was only added in SWF7, but it's not version-gated; - // Construct events will still fire in SWF6 in a v7+ player. (#1424) - event_list.set(ClipEventFlag::CONSTRUCT, flags2 & 0b0000_0100 != 0); - event_list.set(ClipEventFlag::KEY_PRESS, flags2 & 0b0000_0010 != 0); - event_list.set(ClipEventFlag::DRAG_OUT, flags2 & 0b0000_0001 != 0); + fn read_clip_event_flags(&mut self) -> ClipEventFlag { + // There are SWFs in the wild with malformed final ClipActions that is only 2 bytes + // instead of 4 bytes (#2899). Handle this gracefully to allow the tag to run. + // TODO: We may need a more general way to handle truncated tags, since this has + // occurred in a few different places. + let bits = if self.version >= 6 { + self.read_u32().unwrap_or_default() } else { // SWF19 pp. 48-50: For SWFv5, the ClipEventFlags only had 2 bytes of flags, // with the 2nd byte reserved (all 0). // This was expanded to 4 bytes in SWFv6. - self.read_u8()?; - } + (self.read_u16().unwrap_or_default() as u8).into() + }; - Ok(event_list) + ClipEventFlag::from_bits_truncate(bits) } pub fn read_filter(&mut self) -> Result { diff --git a/swf/src/types.rs b/swf/src/types.rs index 61904bd71..f49ed30a2 100644 --- a/swf/src/types.rs +++ b/swf/src/types.rs @@ -742,25 +742,30 @@ bitflags! { /// /// [SWF19 pp.48-50 ClipEvent](https://www.adobe.com/content/dam/acom/en/devnet/pdf/swf-file-format-spec.pdf#page=50) pub struct ClipEventFlag: u32 { - const CONSTRUCT = 1 << 0; - const DATA = 1 << 1; - const DRAG_OUT = 1 << 2; - const DRAG_OVER = 1 << 3; - const ENTER_FRAME = 1 << 4; - const INITIALIZE = 1 << 5; - const KEY_UP = 1 << 6; - const KEY_DOWN = 1 << 7; - const KEY_PRESS = 1 << 8; - const LOAD = 1 << 9; - const MOUSE_UP = 1 << 10; - const MOUSE_DOWN = 1 << 11; - const MOUSE_MOVE = 1 << 12; - const PRESS = 1 << 13; + const LOAD = 1 << 0; + const ENTER_FRAME = 1 << 1; + const UNLOAD = 1 << 2; + const MOUSE_MOVE = 1 << 3; + const MOUSE_DOWN = 1 << 4; + const MOUSE_UP = 1 << 5; + const KEY_DOWN = 1 << 6; + const KEY_UP = 1 << 7; + + // Added in SWF6. + const DATA = 1 << 8; + const INITIALIZE = 1 << 9; + const PRESS = 1 << 10; + const RELEASE = 1 << 11; + const RELEASE_OUTSIDE = 1 << 12; + const ROLL_OVER = 1 << 13; const ROLL_OUT = 1 << 14; - const ROLL_OVER = 1 << 15; - const RELEASE = 1 << 16; - const RELEASE_OUTSIDE = 1 << 17; - const UNLOAD = 1 << 18; + const DRAG_OVER = 1 << 15; + const DRAG_OUT = 1 << 16; + const KEY_PRESS = 1 << 17; + + // Construct was only added in SWF7, but it's not version-gated; + // Construct events will still fire in SWF6 in a v7+ player (#1424). + const CONSTRUCT = 1 << 18; } } diff --git a/swf/src/write.rs b/swf/src/write.rs index a1e4b041b..4227aaec2 100644 --- a/swf/src/write.rs +++ b/swf/src/write.rs @@ -2009,30 +2009,11 @@ impl Writer { fn write_clip_event_flags(&mut self, clip_events: ClipEventFlag) -> Result<()> { // TODO: Assert proper version. - let version = self.version; - let mut bits = self.bits(); - bits.write_bit(clip_events.contains(ClipEventFlag::KEY_UP))?; - bits.write_bit(clip_events.contains(ClipEventFlag::KEY_DOWN))?; - bits.write_bit(clip_events.contains(ClipEventFlag::MOUSE_UP))?; - bits.write_bit(clip_events.contains(ClipEventFlag::MOUSE_DOWN))?; - bits.write_bit(clip_events.contains(ClipEventFlag::MOUSE_MOVE))?; - bits.write_bit(clip_events.contains(ClipEventFlag::UNLOAD))?; - bits.write_bit(clip_events.contains(ClipEventFlag::ENTER_FRAME))?; - bits.write_bit(clip_events.contains(ClipEventFlag::LOAD))?; - bits.write_bit(clip_events.contains(ClipEventFlag::DRAG_OVER))?; - bits.write_bit(clip_events.contains(ClipEventFlag::ROLL_OUT))?; - bits.write_bit(clip_events.contains(ClipEventFlag::ROLL_OVER))?; - bits.write_bit(clip_events.contains(ClipEventFlag::RELEASE_OUTSIDE))?; - bits.write_bit(clip_events.contains(ClipEventFlag::RELEASE))?; - bits.write_bit(clip_events.contains(ClipEventFlag::PRESS))?; - bits.write_bit(clip_events.contains(ClipEventFlag::INITIALIZE))?; - bits.write_bit(clip_events.contains(ClipEventFlag::DATA))?; - if version >= 6 { - bits.write_ubits(5, 0)?; - bits.write_bit(clip_events.contains(ClipEventFlag::CONSTRUCT))?; - bits.write_bit(clip_events.contains(ClipEventFlag::KEY_PRESS))?; - bits.write_bit(clip_events.contains(ClipEventFlag::DRAG_OUT))?; - bits.write_ubits(8, 0)?; + let bits = clip_events.bits(); + if self.version >= 6 { + self.write_u32(bits)?; + } else { + self.write_u16((bits as u8).into())?; } Ok(()) }