Implement `DoInitAction`.
This pushes an extra `undefined` onto the stack to fix underflow in AS2 interface declarations. It is currently unknown if this is a miscompilation or if some other value is supposed to be there. # Conflicts: # core/src/avm1.rs # core/src/avm1/object.rs
This commit is contained in:
parent
33c0bbd0ce
commit
6a81b5327d
|
@ -137,6 +137,35 @@ impl<'gc> Avm1<'gc> {
|
|||
));
|
||||
}
|
||||
|
||||
/// Add a stack frame that executes code in initializer scope
|
||||
pub fn insert_stack_frame_for_init_action(
|
||||
&mut self,
|
||||
swf_version: u8,
|
||||
code: SwfSlice,
|
||||
action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) {
|
||||
let global_scope = GcCell::allocate(
|
||||
action_context.gc_context,
|
||||
Scope::from_global_object(self.globals),
|
||||
);
|
||||
let clip_obj = action_context
|
||||
.active_clip
|
||||
.read()
|
||||
.object()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
let child_scope = GcCell::allocate(
|
||||
action_context.gc_context,
|
||||
Scope::new(global_scope, scope::ScopeClass::Target, clip_obj),
|
||||
);
|
||||
self.push(Value::Undefined);
|
||||
self.stack_frames.push(GcCell::allocate(
|
||||
action_context.gc_context,
|
||||
Activation::from_action(swf_version, code, child_scope, clip_obj, None),
|
||||
));
|
||||
}
|
||||
|
||||
/// Add a stack frame for any arbitrary code.
|
||||
pub fn insert_stack_frame(
|
||||
&mut self,
|
||||
|
|
|
@ -90,6 +90,9 @@ pub struct QueuedActions<'gc> {
|
|||
|
||||
/// The ActionScript bytecode.
|
||||
pub actions: SwfSlice,
|
||||
|
||||
/// If this queued action is an init action
|
||||
pub is_init: bool,
|
||||
}
|
||||
|
||||
/// Action and gotos need to be queued up to execute at the end of the frame.
|
||||
|
@ -110,8 +113,12 @@ impl<'gc> ActionQueue<'gc> {
|
|||
/// Queues ActionScript to run for the given movie clip.
|
||||
/// `actions` is the slice of ActionScript bytecode to run.
|
||||
/// The actions will be skipped if the clip is removed before the actions run.
|
||||
pub fn queue_actions(&mut self, clip: DisplayNode<'gc>, actions: SwfSlice) {
|
||||
self.queue.push_back(QueuedActions { clip, actions })
|
||||
pub fn queue_actions(&mut self, clip: DisplayNode<'gc>, actions: SwfSlice, is_init: bool) {
|
||||
self.queue.push_back(QueuedActions {
|
||||
clip,
|
||||
actions,
|
||||
is_init,
|
||||
})
|
||||
}
|
||||
|
||||
/// Pops the next actions off of the queue.
|
||||
|
|
|
@ -139,7 +139,7 @@ impl<'gc> Button<'gc> {
|
|||
// Note that AVM1 buttons run actions relative to their parent, not themselves.
|
||||
context
|
||||
.action_queue
|
||||
.queue_actions(parent, action.action_data.clone());
|
||||
.queue_actions(parent, action.action_data.clone(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -665,6 +665,7 @@ impl<'gc, 'a> MovieClip<'gc> {
|
|||
TagCode::DefineSprite => self.define_sprite(context, reader, tag_len, morph_shapes),
|
||||
TagCode::DefineText => self.define_text(context, reader, 1),
|
||||
TagCode::DefineText2 => self.define_text(context, reader, 2),
|
||||
TagCode::DoInitAction => self.do_init_action(context, reader, tag_len),
|
||||
TagCode::FrameLabel => {
|
||||
self.frame_label(context, reader, tag_len, cur_frame, &mut static_data)
|
||||
}
|
||||
|
@ -1236,7 +1237,36 @@ impl<'gc, 'a> MovieClip<'gc> {
|
|||
};
|
||||
context
|
||||
.action_queue
|
||||
.queue_actions(context.active_clip, slice);
|
||||
.queue_actions(context.active_clip, slice, false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn do_init_action(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
reader: &mut SwfStream<&'a [u8]>,
|
||||
tag_len: usize,
|
||||
) -> DecodeResult {
|
||||
// Queue the init actions.
|
||||
|
||||
// TODO: Init actions are supposed to be executed once, and it gives a
|
||||
// sprite ID... how does that work?
|
||||
let sprite_id = reader.read_u16()?;
|
||||
log::info!("Init Action sprite ID {}", sprite_id);
|
||||
|
||||
// TODO: The reader is actually reading the tag slice at this point (tag_stream.take()),
|
||||
// so make sure to get the proper offsets. This feels kind of bad.
|
||||
let start = (self.tag_stream_start() + reader.get_ref().position()) as usize;
|
||||
let end = start + tag_len;
|
||||
let slice = crate::tag_utils::SwfSlice {
|
||||
data: std::sync::Arc::clone(context.swf_data),
|
||||
start,
|
||||
end,
|
||||
};
|
||||
context
|
||||
.action_queue
|
||||
.queue_actions(context.active_clip, slice, true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -622,7 +622,16 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
|
|||
context.start_clip = actions.clip;
|
||||
context.active_clip = actions.clip;
|
||||
context.target_clip = Some(actions.clip);
|
||||
avm.insert_stack_frame_for_action(context.swf_version, actions.actions, context);
|
||||
|
||||
if actions.is_init {
|
||||
avm.insert_stack_frame_for_init_action(
|
||||
context.swf_version,
|
||||
actions.actions,
|
||||
context,
|
||||
);
|
||||
} else {
|
||||
avm.insert_stack_frame_for_action(context.swf_version, actions.actions, context);
|
||||
}
|
||||
let _ = avm.run_stack_till_empty(context);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue