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.
|
/// Add a stack frame for any arbitrary code.
|
||||||
pub fn insert_stack_frame(
|
pub fn insert_stack_frame(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
|
@ -90,6 +90,9 @@ pub struct QueuedActions<'gc> {
|
||||||
|
|
||||||
/// The ActionScript bytecode.
|
/// The ActionScript bytecode.
|
||||||
pub actions: SwfSlice,
|
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.
|
/// 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.
|
/// Queues ActionScript to run for the given movie clip.
|
||||||
/// `actions` is the slice of ActionScript bytecode to run.
|
/// `actions` is the slice of ActionScript bytecode to run.
|
||||||
/// The actions will be skipped if the clip is removed before the actions 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) {
|
pub fn queue_actions(&mut self, clip: DisplayNode<'gc>, actions: SwfSlice, is_init: bool) {
|
||||||
self.queue.push_back(QueuedActions { clip, actions })
|
self.queue.push_back(QueuedActions {
|
||||||
|
clip,
|
||||||
|
actions,
|
||||||
|
is_init,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pops the next actions off of the queue.
|
/// 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.
|
// Note that AVM1 buttons run actions relative to their parent, not themselves.
|
||||||
context
|
context
|
||||||
.action_queue
|
.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::DefineSprite => self.define_sprite(context, reader, tag_len, morph_shapes),
|
||||||
TagCode::DefineText => self.define_text(context, reader, 1),
|
TagCode::DefineText => self.define_text(context, reader, 1),
|
||||||
TagCode::DefineText2 => self.define_text(context, reader, 2),
|
TagCode::DefineText2 => self.define_text(context, reader, 2),
|
||||||
|
TagCode::DoInitAction => self.do_init_action(context, reader, tag_len),
|
||||||
TagCode::FrameLabel => {
|
TagCode::FrameLabel => {
|
||||||
self.frame_label(context, reader, tag_len, cur_frame, &mut static_data)
|
self.frame_label(context, reader, tag_len, cur_frame, &mut static_data)
|
||||||
}
|
}
|
||||||
|
@ -1236,7 +1237,36 @@ impl<'gc, 'a> MovieClip<'gc> {
|
||||||
};
|
};
|
||||||
context
|
context
|
||||||
.action_queue
|
.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -622,7 +622,16 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
|
||||||
context.start_clip = actions.clip;
|
context.start_clip = actions.clip;
|
||||||
context.active_clip = actions.clip;
|
context.active_clip = actions.clip;
|
||||||
context.target_clip = Some(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);
|
let _ = avm.run_stack_till_empty(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue