avm1: Accept path strings in ActionCall (fix #832)

This commit is contained in:
Mike Welsh 2020-07-14 19:46:47 -07:00
parent b43fdca4ad
commit 7c52a1bcc4
2 changed files with 54 additions and 39 deletions

View File

@ -734,38 +734,48 @@ impl<'a, 'gc: 'a> Activation<'a, 'gc> {
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<FrameControl<'gc>, Error<'gc>> {
// Runs any actions on the given frame.
let frame = self.avm.pop();
let clip = self.target_clip_or_root();
if let Some(clip) = clip.as_movie_clip() {
// Use frame # if parameter is a number, otherwise cast to string and check for frame labels.
let frame = if let Value::Number(frame) = frame {
let frame = f64_to_wrapping_u32(frame);
if frame >= 1 && frame <= u32::from(clip.total_frames()) {
Some(frame as u16)
} else {
None
}
} else {
let frame_label = frame.coerce_to_string(self, context)?;
clip.frame_label_to_number(&frame_label)
};
let arg = self.avm.pop();
let target = self.target_clip_or_root();
if let Some(frame) = frame {
for action in clip.actions_on_frame(context, frame) {
// The parameter can be a frame # or a path to a movie clip with a frame number.
let mut call_frame = None;
if let Value::Number(frame) = arg {
// Frame # on the current clip.
if let Some(target) = target.as_movie_clip() {
call_frame = Some((target, f64_to_wrapping_u32(frame)));
}
} else {
// An optional path to a movieclip and a frame #/label, such as "/clip:framelabel".
let frame_path = arg.coerce_to_string(self, context)?;
if let Some((clip, frame)) = self.resolve_variable_path(context, target, &frame_path)? {
if let Some(clip) = clip.as_display_object().and_then(|o| o.as_movie_clip()) {
if let Ok(frame) = frame.parse().map(f64_to_wrapping_u32) {
// First try to parse as a frame number.
call_frame = Some((clip, frame));
} else if let Some(frame) = clip.frame_label_to_number(&frame) {
// Otherwise, it's a frame label.
call_frame = Some((clip, frame.into()));
}
}
}
};
if let Some((clip, frame)) = call_frame {
if frame <= u32::from(std::u16::MAX) {
for action in clip.actions_on_frame(context, frame as u16) {
let _ = self.run_child_frame_for_action(
"[Frame Call]",
self.target_clip_or_root(),
clip.into(),
self.current_swf_version(),
action,
context,
)?;
}
} else {
log::warn!("Call: Invalid frame {:?}", frame);
}
} else {
log::warn!("Call: Expected MovieClip");
log::warn!("Call: Invalid call");
}
Ok(FrameControl::Continue)
}

View File

@ -562,28 +562,33 @@ impl<'gc> MovieClip<'gc> {
use swf::{read::Reader, TagCode};
let mut actions: SmallVec<[SwfSlice; 2]> = SmallVec::new();
let mut cur_frame = 1;
let clip = self.0.read();
let len = clip.tag_stream_len();
let mut reader = clip.static_data.swf.read_from(0);
// Iterate through this clip's tags, counting frames until we reach the target frame.
while cur_frame <= frame && reader.get_ref().position() < len as u64 {
let tag_callback = |reader: &mut Reader<std::io::Cursor<&[u8]>>, tag_code, tag_len| {
match tag_code {
TagCode::ShowFrame => cur_frame += 1,
TagCode::DoAction if cur_frame == frame => {
// On the target frame, add any DoAction tags to the array.
if let Some(code) = clip.static_data.swf.resize_to_reader(reader, tag_len) {
actions.push(code)
if frame > 0 && frame <= self.total_frames() {
let mut cur_frame = 1;
let clip = self.0.read();
let len = clip.tag_stream_len();
let mut reader = clip.static_data.swf.read_from(0);
while cur_frame <= frame && reader.get_ref().position() < len as u64 {
let tag_callback =
|reader: &mut Reader<std::io::Cursor<&[u8]>>, tag_code, tag_len| {
match tag_code {
TagCode::ShowFrame => cur_frame += 1,
TagCode::DoAction if cur_frame == frame => {
// On the target frame, add any DoAction tags to the array.
if let Some(code) =
clip.static_data.swf.resize_to_reader(reader, tag_len)
{
actions.push(code)
}
}
_ => (),
}
}
_ => (),
}
Ok(())
};
Ok(())
};
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame);
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame);
}
}
actions.into_iter()