From de186ed5f3b8921f0d9fdc1b613948dca9db7b19 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Thu, 17 Sep 2020 20:58:36 -0400 Subject: [PATCH] avm2: Implement `MovieClip.currentLabels` --- core/src/avm2/globals.rs | 10 +++- .../avm2/globals/flash/display/movieclip.rs | 48 +++++++++++++++++- core/src/display_object/movie_clip.rs | 30 +++++++++++ core/tests/regression_tests.rs | 1 + .../avm2/movieclip_currentlabels/output.txt | 8 +++ .../avm2/movieclip_currentlabels/test.fla | Bin 0 -> 4243 bytes .../avm2/movieclip_currentlabels/test.swf | Bin 0 -> 1048 bytes 7 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 core/tests/swfs/avm2/movieclip_currentlabels/output.txt create mode 100644 core/tests/swfs/avm2/movieclip_currentlabels/test.fla create mode 100644 core/tests/swfs/avm2/movieclip_currentlabels/test.swf diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 4e3b38ec7..53f87ee0c 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -54,6 +54,7 @@ pub struct SystemPrototypes<'gc> { pub uint: Object<'gc>, pub namespace: Object<'gc>, pub array: Object<'gc>, + pub framelabel: Object<'gc>, } impl<'gc> SystemPrototypes<'gc> { @@ -81,6 +82,7 @@ impl<'gc> SystemPrototypes<'gc> { uint: empty, namespace: empty, array: empty, + framelabel: empty, } } } @@ -377,7 +379,13 @@ pub fn load_player_globals<'gc>(activation: &mut Activation<'_, 'gc, '_>) -> Res flash::display::movieclip::create_class(activation.context.gc_context), implicit_deriver, )?; - class( + activation + .context + .avm2 + .system_prototypes + .as_mut() + .unwrap() + .framelabel = class( activation, gs, flash::display::framelabel::create_class(activation.context.gc_context), diff --git a/core/src/avm2/globals/flash/display/movieclip.rs b/core/src/avm2/globals/flash/display/movieclip.rs index 34a64ed51..e24aef181 100644 --- a/core/src/avm2/globals/flash/display/movieclip.rs +++ b/core/src/avm2/globals/flash/display/movieclip.rs @@ -1,10 +1,12 @@ //! `flash.display.MovieClip` builtin/prototype use crate::avm2::activation::Activation; +use crate::avm2::array::ArrayStorage; use crate::avm2::class::Class; +use crate::avm2::globals::flash::display::framelabel; use crate::avm2::method::Method; use crate::avm2::names::{Namespace, QName}; -use crate::avm2::object::{Object, TObject}; +use crate::avm2::object::{ArrayObject, Object, TObject}; use crate::avm2::string::AvmString; use crate::avm2::traits::Trait; use crate::avm2::value::Value; @@ -120,6 +122,45 @@ pub fn current_label<'gc>( Ok(Value::Undefined) } +/// Implements `currentLabels`. +pub fn current_labels<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mc) = this + .and_then(|o| o.as_display_object()) + .and_then(|dobj| dobj.as_movie_clip()) + { + let mut frame_labels = Vec::new(); + let frame_label_proto = activation.context.avm2.prototypes().framelabel; + let current_scene_start = mc + .current_scene() + .map(|(_, frame)| frame.saturating_sub(1)) + .unwrap_or(0); + + for (name, frame) in mc.current_labels() { + let name: Value<'gc> = AvmString::new(activation.context.gc_context, name).into(); + let local_frame = frame - current_scene_start; + let frame_label = + frame_label_proto.construct(activation, &[name.clone(), local_frame.into()])?; + + framelabel::instance_init(activation, Some(frame_label), &[name, local_frame.into()])?; + + frame_labels.push(Some(frame_label.into())); + } + + return Ok(ArrayObject::from_array( + ArrayStorage::from_storage(frame_labels), + activation.context.avm2.prototypes().array, + activation.context.gc_context, + ) + .into()); + } + + Ok(Value::Undefined) +} + /// Implements `framesLoaded`. pub fn frames_loaded<'gc>( _activation: &mut Activation<'_, 'gc, '_>, @@ -379,6 +420,11 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc> Method::from_builtin(current_label), )); + write.define_instance_trait(Trait::from_getter( + QName::new(Namespace::package(""), "currentLabels"), + Method::from_builtin(current_labels), + )); + write.define_instance_trait(Trait::from_getter( QName::new(Namespace::package(""), "framesLoaded"), Method::from_builtin(frames_loaded), diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index 92949e64e..8c7c92f03 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -681,6 +681,36 @@ impl<'gc> MovieClip<'gc> { best.map(|(s, fnum)| (s.to_string(), fnum)) } + /// Yield a list of labels and frame-nubmers in the current scene. + /// + /// Labels are returned sorted by frame number. + pub fn current_labels(self) -> Vec<(String, FrameNumber)> { + let read = self.0.read(); + let current_scene = self.current_scene(); + let next_scene = self.next_scene(); + + let mut values: Vec<(String, FrameNumber)> = read + .static_data + .frame_labels + .iter() + .filter(|(_label, frame)| { + current_scene + .as_ref() + .map(|(_, scene_start)| **frame >= *scene_start) + .unwrap_or(false) + && next_scene + .as_ref() + .map(|(_, scene_start)| **frame < *scene_start) + .unwrap_or(true) + }) + .map(|(label, frame)| (label.clone(), *frame)) + .collect(); + + values.sort_unstable_by(|(_, framea), (_, frameb)| framea.cmp(frameb)); + + values + } + pub fn total_frames(self) -> FrameNumber { self.0.read().static_data.total_frames } diff --git a/core/tests/regression_tests.rs b/core/tests/regression_tests.rs index e8a8946e5..bb4ad6eb3 100644 --- a/core/tests/regression_tests.rs +++ b/core/tests/regression_tests.rs @@ -394,6 +394,7 @@ swf_tests! { (as3_movieclip_prev_scene, "avm2/movieclip_prev_scene", 5), (as3_movieclip_next_scene, "avm2/movieclip_next_scene", 5), (as3_framelabel_constr, "avm2/framelabel_constr", 5), + (as3_movieclip_currentlabels, "avm2/movieclip_currentlabels", 5), } // TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough. diff --git a/core/tests/swfs/avm2/movieclip_currentlabels/output.txt b/core/tests/swfs/avm2/movieclip_currentlabels/output.txt new file mode 100644 index 000000000..b18c049cd --- /dev/null +++ b/core/tests/swfs/avm2/movieclip_currentlabels/output.txt @@ -0,0 +1,8 @@ +//(contents of this.currentLabels) +1 +frame1 +3 +frame3 +//(contents of this.currentLabels in scene 2) +1 +frame4 diff --git a/core/tests/swfs/avm2/movieclip_currentlabels/test.fla b/core/tests/swfs/avm2/movieclip_currentlabels/test.fla new file mode 100644 index 0000000000000000000000000000000000000000..c94ab6257ee15eaf554f27d5fce9d6f99ebcb4d9 GIT binary patch literal 4243 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 literal 0 HcmV?d00001 diff --git a/core/tests/swfs/avm2/movieclip_currentlabels/test.swf b/core/tests/swfs/avm2/movieclip_currentlabels/test.swf new file mode 100644 index 0000000000000000000000000000000000000000..96246ceaff6855db166979a40c13021f87848824 GIT binary patch literal 1048 zcmV+z1n2uhS5qrm2LJ$goQ+l6QX4lImiFi~HUa@o6xSM zU&IgHGlKUl(w)9_jr=Y7C}F^%sV-jol}G(*QC7*&<-{VCK_W9xH?UCB?sgdK)+!af z?Y3~)aP7*|qn*mOrai2*dc#J+Ma;Av(pb9qn;yxh>ryf9Q<3PBP?d+$MTF96Ok$Z${;| zXGiOIJ>u}FPj8^hx4>ScrMlf6LD-=m!=x9KVLOV1@i;Rdhh<*P+z75(YD zVU42r&TVUEtiiRL22YLiM?7SPOXK9T(AHbOV}tFv4&yDw-qN_}z~Qyi4MxszD4hSy zA`7FyQY@Yv>nv9)_YJ;ppf0**k#z_y8$HkC#IM7nw4NS= z55DM7Bu|9fD=Ic0JBi?r4o4kKDz=y z+?S7)cbBGW#_9_6l?cQGAYGQ=o7e9z-fQ(Pbhf~CI3Cm&vY