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.
This commit is contained in:
parent
945bce4a85
commit
55ffda9c97
|
@ -2027,7 +2027,7 @@ impl<'a> Reader<'a> {
|
||||||
|
|
||||||
fn read_clip_actions(&mut self) -> Result<Vec<ClipAction<'a>>> {
|
fn read_clip_actions(&mut self) -> Result<Vec<ClipAction<'a>>> {
|
||||||
self.read_u16()?; // Must be 0
|
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![];
|
let mut clip_actions = vec![];
|
||||||
while let Some(clip_action) = self.read_clip_action()? {
|
while let Some(clip_action) = self.read_clip_action()? {
|
||||||
clip_actions.push(clip_action);
|
clip_actions.push(clip_action);
|
||||||
|
@ -2036,7 +2036,7 @@ impl<'a> Reader<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_clip_action(&mut self) -> Result<Option<ClipAction<'a>>> {
|
fn read_clip_action(&mut self) -> Result<Option<ClipAction<'a>>> {
|
||||||
let events = self.read_clip_event_flags()?;
|
let events = self.read_clip_event_flags();
|
||||||
if events.is_empty() {
|
if events.is_empty() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
|
@ -2058,51 +2058,21 @@ impl<'a> Reader<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_clip_event_flags(&mut self) -> Result<ClipEventFlag> {
|
fn read_clip_event_flags(&mut self) -> ClipEventFlag {
|
||||||
// TODO: Switch to a bitset.
|
// There are SWFs in the wild with malformed final ClipActions that is only 2 bytes
|
||||||
let mut event_list = ClipEventFlag::empty();
|
// instead of 4 bytes (#2899). Handle this gracefully to allow the tag to run.
|
||||||
|
|
||||||
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
|
// TODO: We may need a more general way to handle truncated tags, since this has
|
||||||
// occurred in a few different places.
|
// occurred in a few different places.
|
||||||
// Allow for only two bytes in the clip action tag.
|
let bits = if self.version >= 6 {
|
||||||
let flags = self.read_u8().unwrap_or_default();
|
self.read_u32().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);
|
|
||||||
} else {
|
} else {
|
||||||
// SWF19 pp. 48-50: For SWFv5, the ClipEventFlags only had 2 bytes of flags,
|
// SWF19 pp. 48-50: For SWFv5, the ClipEventFlags only had 2 bytes of flags,
|
||||||
// with the 2nd byte reserved (all 0).
|
// with the 2nd byte reserved (all 0).
|
||||||
// This was expanded to 4 bytes in SWFv6.
|
// 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<Filter> {
|
pub fn read_filter(&mut self) -> Result<Filter> {
|
||||||
|
|
|
@ -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)
|
/// [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 {
|
pub struct ClipEventFlag: u32 {
|
||||||
const CONSTRUCT = 1 << 0;
|
const LOAD = 1 << 0;
|
||||||
const DATA = 1 << 1;
|
const ENTER_FRAME = 1 << 1;
|
||||||
const DRAG_OUT = 1 << 2;
|
const UNLOAD = 1 << 2;
|
||||||
const DRAG_OVER = 1 << 3;
|
const MOUSE_MOVE = 1 << 3;
|
||||||
const ENTER_FRAME = 1 << 4;
|
const MOUSE_DOWN = 1 << 4;
|
||||||
const INITIALIZE = 1 << 5;
|
const MOUSE_UP = 1 << 5;
|
||||||
const KEY_UP = 1 << 6;
|
const KEY_DOWN = 1 << 6;
|
||||||
const KEY_DOWN = 1 << 7;
|
const KEY_UP = 1 << 7;
|
||||||
const KEY_PRESS = 1 << 8;
|
|
||||||
const LOAD = 1 << 9;
|
// Added in SWF6.
|
||||||
const MOUSE_UP = 1 << 10;
|
const DATA = 1 << 8;
|
||||||
const MOUSE_DOWN = 1 << 11;
|
const INITIALIZE = 1 << 9;
|
||||||
const MOUSE_MOVE = 1 << 12;
|
const PRESS = 1 << 10;
|
||||||
const PRESS = 1 << 13;
|
const RELEASE = 1 << 11;
|
||||||
|
const RELEASE_OUTSIDE = 1 << 12;
|
||||||
|
const ROLL_OVER = 1 << 13;
|
||||||
const ROLL_OUT = 1 << 14;
|
const ROLL_OUT = 1 << 14;
|
||||||
const ROLL_OVER = 1 << 15;
|
const DRAG_OVER = 1 << 15;
|
||||||
const RELEASE = 1 << 16;
|
const DRAG_OUT = 1 << 16;
|
||||||
const RELEASE_OUTSIDE = 1 << 17;
|
const KEY_PRESS = 1 << 17;
|
||||||
const UNLOAD = 1 << 18;
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2009,30 +2009,11 @@ impl<W: Write> Writer<W> {
|
||||||
|
|
||||||
fn write_clip_event_flags(&mut self, clip_events: ClipEventFlag) -> Result<()> {
|
fn write_clip_event_flags(&mut self, clip_events: ClipEventFlag) -> Result<()> {
|
||||||
// TODO: Assert proper version.
|
// TODO: Assert proper version.
|
||||||
let version = self.version;
|
let bits = clip_events.bits();
|
||||||
let mut bits = self.bits();
|
if self.version >= 6 {
|
||||||
bits.write_bit(clip_events.contains(ClipEventFlag::KEY_UP))?;
|
self.write_u32(bits)?;
|
||||||
bits.write_bit(clip_events.contains(ClipEventFlag::KEY_DOWN))?;
|
} else {
|
||||||
bits.write_bit(clip_events.contains(ClipEventFlag::MOUSE_UP))?;
|
self.write_u16((bits as u8).into())?;
|
||||||
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)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue