diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 92cb773d3..a3d736cd4 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -8,12 +8,14 @@ use gc_arena::{GcCell, MutationContext}; use rand::Rng; use std::collections::HashMap; use std::convert::TryInto; +use std::sync::Arc; use url::form_urlencoded; use swf::avm1::read::Reader; use swf::avm1::types::{Action, Function}; -use crate::tag_utils::SwfSlice; +use crate::display_object::DisplayObject; +use crate::tag_utils::{SwfMovie, SwfSlice}; #[cfg(test)] #[macro_use] @@ -1665,14 +1667,14 @@ impl<'gc> Avm1<'gc> { return fscommand::handle(fscommand, self, context); } - if is_load_vars { - let clip_target: Option> = if is_target_sprite { - let start = self.target_clip_or_root(context); - self.resolve_target_display_object(context, start, target)? - } else { - Some(self.target_clip_or_root(context)) - }; + let clip_target: Option> = if is_target_sprite { + let start = self.target_clip_or_root(context); + self.resolve_target_display_object(context, start, target.clone())? + } else { + Some(self.target_clip_or_root(context)) + }; + if is_load_vars { if let Some(clip_target) = clip_target { let target_obj = clip_target .as_movie_clip() @@ -1699,8 +1701,27 @@ impl<'gc> Avm1<'gc> { return Ok(()); } else if is_target_sprite { - log::warn!("GetURL into target sprite is not yet implemented"); - return Ok(()); //maybe error? + if let Some(clip_target) = clip_target { + let player = context.player.clone().unwrap().upgrade().unwrap(); + let fetch = context.navigator.fetch(url); + let slot = self.forcibly_root_object(clip_target.object().as_object()?); + + context.navigator.spawn_future(Box::pin(async move { + let data = fetch.await.unwrap(); + let movie = Arc::new(SwfMovie::from_data(&data)); + + player.lock().unwrap().update(|avm, uc| { + let that = avm.unroot_object(slot); + that.as_display_object() + .unwrap() + .as_movie_clip() + .unwrap() + .replace_with_movie(uc.gc_context, movie); + }) + })) + } + + return Ok(()); } else { let vars = match NavigationMethod::from_send_vars_method(swf_method) { Some(method) => Some((method, self.locals_into_form_values(context))), diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index 35097f5e7..4d5e72b74 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -103,6 +103,21 @@ impl<'gc> MovieClip<'gc> { ) } + /// Replace the current MovieClip with a completely new SwfMovie. + /// + /// Playback will start at position zero, any existing streamed audio will + /// be terminated, and so on. Children and AVM data will be kept across the + /// load boundary. + pub fn replace_with_movie( + &mut self, + gc_context: MutationContext<'gc, '_>, + movie: Arc, + ) { + self.0 + .write(gc_context) + .replace_with_movie(gc_context, movie) + } + pub fn preload( self, context: &mut UpdateContext<'_, 'gc, '_>, @@ -406,6 +421,34 @@ unsafe impl<'gc> Collect for MovieClipData<'gc> { } impl<'gc> MovieClipData<'gc> { + /// Replace the current MovieClipData with a completely new SwfMovie. + /// + /// Playback will start at position zero, any existing streamed audio will + /// be terminated, and so on. Children and AVM data will be kept across the + /// load boundary. + pub fn replace_with_movie( + &mut self, + gc_context: MutationContext<'gc, '_>, + movie: Arc, + ) { + let total_frames = movie.header().num_frames; + + self.static_data = Gc::allocate( + gc_context, + MovieClipStatic { + id: self.static_data.id, //TODO: This is WRONG; This is VERRRRY WRONG! + swf: movie.into(), + total_frames, + audio_stream_info: None, + frame_labels: HashMap::new(), + }, + ); + self.tag_stream_pos = 0; + self.flags = EnumSet::empty(); + self.current_frame = 0; + self.audio_stream = None; + } + fn id(&self) -> CharacterId { self.static_data.id }