core: Move frame and goto methods from MovieClipData to MovieClip

This commit is contained in:
Mike Welsh 2020-06-17 17:12:22 -07:00
parent 8da0f43412
commit dd50071240
1 changed files with 341 additions and 332 deletions

View File

@ -416,12 +416,24 @@ impl<'gc> MovieClip<'gc> {
self,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
frame: FrameNumber,
mut frame: FrameNumber,
stop: bool,
) {
self.0
.write(context.gc_context)
.goto_frame(self.into(), avm, context, frame, stop)
// Stop first, in case we need to kill and restart the stream sound.
if stop {
self.stop(context);
} else {
self.play(context);
}
// Clamp frame number in bounds.
if frame < 1 {
frame = 1;
}
if frame != self.current_frame() {
self.run_goto(self.into(), avm, context, frame);
}
}
pub fn current_frame(self) -> FrameNumber {
@ -603,6 +615,295 @@ impl<'gc> MovieClip<'gc> {
.write(context.gc_context)
.run_clip_event(self.into(), context, event);
}
fn run_frame_internal(
self,
self_display_object: DisplayObject<'gc>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
run_display_actions: bool,
) {
// Advance frame number.
if self.current_frame() < self.total_frames() {
self.0.write(context.gc_context).current_frame += 1;
} else if self.total_frames() > 1 {
// Looping acts exactly like a gotoAndPlay(1).
// Specifically, object that existed on frame 1 should not be destroyed
// and recreated.
self.run_goto(self_display_object, avm, context, 1);
return;
} else {
// Single frame clips do not play.
self.stop(context);
}
let mc = self.0.read();
let _tag_pos = mc.tag_stream_pos;
let data = mc.static_data.swf.clone();
let mut reader = data.read_from(mc.tag_stream_pos);
let mut has_stream_block = false;
drop(mc);
use swf::TagCode;
let tag_callback = |reader: &mut _, tag_code, tag_len| match tag_code {
TagCode::DoAction => self.do_action(self_display_object, context, reader, tag_len),
TagCode::PlaceObject if run_display_actions => {
self.place_object(self_display_object, avm, context, reader, tag_len, 1)
}
TagCode::PlaceObject2 if run_display_actions => {
self.place_object(self_display_object, avm, context, reader, tag_len, 2)
}
TagCode::PlaceObject3 if run_display_actions => {
self.place_object(self_display_object, avm, context, reader, tag_len, 3)
}
TagCode::PlaceObject4 if run_display_actions => {
self.place_object(self_display_object, avm, context, reader, tag_len, 4)
}
TagCode::RemoveObject if run_display_actions => self.remove_object(context, reader, 1),
TagCode::RemoveObject2 if run_display_actions => self.remove_object(context, reader, 2),
TagCode::SetBackgroundColor => self.set_background_color(context, reader),
TagCode::StartSound => self.start_sound_1(context, reader),
TagCode::SoundStreamBlock => {
has_stream_block = true;
self.sound_stream_block(context, reader)
}
_ => Ok(()),
};
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame);
self.0.write(context.gc_context).tag_stream_pos = reader.get_ref().position();
// If we are playing a streaming sound, there should(?) be a `SoundStreamBlock` on each frame.
if !has_stream_block {
self.0.write(context.gc_context).stop_audio_stream(context);
}
}
#[allow(clippy::too_many_arguments)]
fn instantiate_child(
self,
self_display_object: DisplayObject<'gc>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
id: CharacterId,
depth: Depth,
place_object: &swf::PlaceObject,
copy_previous_properties: bool,
) -> Option<DisplayObject<'gc>> {
if let Ok(mut child) = context
.library
.library_for_movie_mut(self.movie().unwrap()) //TODO
.instantiate_by_id(id, context.gc_context)
{
// Remove previous child from children list,
// and add new childonto front of the list.
let prev_child = {
let mut mc = self.0.write(context.gc_context);
let prev_child = mc.children.insert(depth, child);
if let Some(prev_child) = prev_child {
mc.remove_child_from_exec_list(context, prev_child);
}
mc.add_child_to_exec_list(context.gc_context, child);
prev_child
};
{
// Set initial properties for child.
child.set_depth(context.gc_context, depth);
child.set_parent(context.gc_context, Some(self_display_object));
child.set_place_frame(context.gc_context, self.current_frame());
if copy_previous_properties {
if let Some(prev_child) = prev_child {
child.copy_display_properties_from(context.gc_context, prev_child);
}
}
// Run first frame.
child.apply_place_object(context.gc_context, place_object);
child.post_instantiation(avm, context, child, None, false);
child.run_frame(avm, context);
}
Some(child)
} else {
log::error!("Unable to instantiate display node id {}", id);
None
}
}
pub fn run_goto(
self,
self_display_object: DisplayObject<'gc>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
frame: FrameNumber,
) {
// Flash gotos are tricky:
// 1) Conceptually, a goto should act like the playhead is advancing forward or
// backward to a frame.
// 2) However, MovieClip timelines are stored as deltas from frame to frame,
// so for rewinds, we must restart to frame 1 and play forward.
// 3) Objects that would persist over the goto conceptually should not be
// destroyed and recreated; they should keep their properties.
// Particularly for rewinds, the object should persist if it was created
// *before* the frame we are going to. (DisplayObject::place_frame).
// 4) We want to avoid creating objects just to destroy them if they aren't on
// the goto frame, so we should instead aggregate the deltas into a final list
// of commands, and THEN modify the children as necessary.
// This map will maintain a map of depth -> placement commands.
// TODO: Move this to UpdateContext to avoid allocations.
let mut goto_commands = vec![];
self.0.write(context.gc_context).stop_audio_stream(context);
let is_rewind = if frame < self.current_frame() {
// Because we can only step forward, we have to start at frame 1
// when rewinding.
self.0.write(context.gc_context).tag_stream_pos = 0;
self.0.write(context.gc_context).current_frame = 0;
// Remove all display objects that were created after the desination frame.
// TODO: We want to do something like self.children.retain here,
// but BTreeMap::retain does not exist.
let children: SmallVec<[_; 16]> = self
.0
.read()
.children
.iter()
.filter_map(|(depth, clip)| {
if clip.place_frame() > frame {
Some((*depth, *clip))
} else {
None
}
})
.collect();
for (depth, child) in children {
let mut mc = self.0.write(context.gc_context);
mc.children.remove(&depth);
mc.remove_child_from_exec_list(context, child);
}
true
} else {
false
};
// Step through the intermediate frames, and aggregate the deltas of each frame.
let mc = self.0.read();
let mut frame_pos = mc.tag_stream_pos;
let data = mc.static_data.swf.clone();
let mut reader = data.read_from(mc.tag_stream_pos);
let mut index = 0;
let len = mc.tag_stream_len() as u64;
// Sanity; let's make sure we don't seek way too far.
// TODO: This should be self.frames_loaded() when we implement that.
let clamped_frame = if frame <= mc.total_frames() {
frame
} else {
mc.total_frames()
};
drop(mc);
while self.current_frame() < clamped_frame && frame_pos < len {
self.0.write(context.gc_context).current_frame += 1;
frame_pos = reader.get_inner().position();
let mut mc = self.0.write(context.gc_context);
use swf::TagCode;
let tag_callback = |reader: &mut _, tag_code, tag_len| match tag_code {
TagCode::PlaceObject => {
index += 1;
mc.goto_place_object(reader, tag_len, 1, &mut goto_commands, is_rewind, index)
}
TagCode::PlaceObject2 => {
index += 1;
mc.goto_place_object(reader, tag_len, 2, &mut goto_commands, is_rewind, index)
}
TagCode::PlaceObject3 => {
index += 1;
mc.goto_place_object(reader, tag_len, 3, &mut goto_commands, is_rewind, index)
}
TagCode::PlaceObject4 => {
index += 1;
mc.goto_place_object(reader, tag_len, 4, &mut goto_commands, is_rewind, index)
}
TagCode::RemoveObject => {
mc.goto_remove_object(reader, 1, context, &mut goto_commands, is_rewind)
}
TagCode::RemoveObject2 => {
mc.goto_remove_object(reader, 2, context, &mut goto_commands, is_rewind)
}
_ => Ok(()),
};
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame);
}
let hit_target_frame = self.0.read().current_frame == frame;
// Run the list of goto commands to actually create and update the display objects.
let run_goto_command = |clip: MovieClip<'gc>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
params: &GotoPlaceObject| {
let child_entry = clip.0.read().children.get(&params.depth()).copied();
match child_entry {
// Apply final delta to display pamareters.
// For rewinds, if an object was created before the final frame,
// it will exist on the final frame as well. Re-use this object
// instead of recreating.
// If the ID is 0, we are modifying a previous child. Otherwise, we're replacing it.
// If it's a rewind, we removed any dead children above, so we always
// modify the previous child.
Some(mut prev_child) if params.id() == 0 || is_rewind => {
prev_child.apply_place_object(context.gc_context, &params.place_object);
}
_ => {
if let Some(mut child) = clip.instantiate_child(
self_display_object,
avm,
context,
params.id(),
params.depth(),
&params.place_object,
params.modifies_original_item(),
) {
// Set the place frame to the frame where the object *would* have been placed.
child.set_place_frame(context.gc_context, params.frame);
}
}
}
};
// We have to be sure that queued actions are generated in the same order
// as if the playhead had reached this frame normally.
// First, sort the goto commands in the order of execution.
// (Maybe it'd be better to keeps this list sorted as we create it?
// Currently `swap_remove` calls futz with the order; but we could use `remove`).
goto_commands.sort_by_key(|params| params.index);
// Then, run frames for children that were created before this frame.
goto_commands
.iter()
.filter(|params| params.frame < frame)
.for_each(|goto| run_goto_command(self, avm, context, goto));
// Next, run the final frame for the parent clip.
// Re-run the final frame without display tags (DoAction, StartSound, etc.)
// Note that this only happens if the frame exists and is loaded;
// e.g. gotoAndStop(9999) displays the final frame, but actions don't run!
if hit_target_frame {
self.0.write(context.gc_context).current_frame -= 1;
self.0.write(context.gc_context).tag_stream_pos = frame_pos;
self.run_frame_internal(self_display_object, avm, context, false);
} else {
self.0.write(context.gc_context).current_frame = clamped_frame;
}
// Finally, run frames for children that are placed on this frame.
goto_commands
.iter()
.filter(|params| params.frame >= frame)
.for_each(|goto| run_goto_command(self, avm, context, goto));
}
}
impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
@ -631,14 +932,19 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
} else {
mc.run_clip_event((*self).into(), context, ClipEvent::EnterFrame);
}
drop(mc);
// Run my SWF tags.
if mc.playing() {
mc.run_frame_internal((*self).into(), avm, context, true);
if self.playing() {
self.run_frame_internal((*self).into(), avm, context, true);
}
if is_load_frame {
mc.run_clip_postevent((*self).into(), context, ClipEvent::Load);
self.0.write(context.gc_context).run_clip_postevent(
(*self).into(),
context,
ClipEvent::Load,
);
}
}
@ -912,139 +1218,6 @@ impl<'gc> MovieClipData<'gc> {
self.static_data.swf.end - self.static_data.swf.start
}
/// Queues up a goto to the specified frame.
/// `frame` should be 1-based.
pub fn goto_frame(
&mut self,
self_display_object: DisplayObject<'gc>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
mut frame: FrameNumber,
stop: bool,
) {
// Stop first, in case we need to kill and restart the stream sound.
if stop {
self.stop(context);
} else {
self.play();
}
// Clamp frame number in bounds.
if frame < 1 {
frame = 1;
}
if frame != self.current_frame() {
self.run_goto(self_display_object, avm, context, frame);
}
}
fn run_frame_internal(
&mut self,
self_display_object: DisplayObject<'gc>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
run_display_actions: bool,
) {
// Advance frame number.
if self.current_frame < self.total_frames() {
self.current_frame += 1;
} else if self.total_frames() > 1 {
// Looping acts exactly like a gotoAndPlay(1).
// Specifically, object that existed on frame 1 should not be destroyed
// and recreated.
self.run_goto(self_display_object, avm, context, 1);
return;
} else {
// Single frame clips do not play.
self.stop(context);
}
let _tag_pos = self.tag_stream_pos;
let data = self.static_data.swf.clone();
let mut reader = data.read_from(self.tag_stream_pos);
let mut has_stream_block = false;
use swf::TagCode;
let tag_callback = |reader: &mut _, tag_code, tag_len| match tag_code {
TagCode::DoAction => self.do_action(self_display_object, context, reader, tag_len),
TagCode::PlaceObject if run_display_actions => {
self.place_object(self_display_object, avm, context, reader, tag_len, 1)
}
TagCode::PlaceObject2 if run_display_actions => {
self.place_object(self_display_object, avm, context, reader, tag_len, 2)
}
TagCode::PlaceObject3 if run_display_actions => {
self.place_object(self_display_object, avm, context, reader, tag_len, 3)
}
TagCode::PlaceObject4 if run_display_actions => {
self.place_object(self_display_object, avm, context, reader, tag_len, 4)
}
TagCode::RemoveObject if run_display_actions => self.remove_object(context, reader, 1),
TagCode::RemoveObject2 if run_display_actions => self.remove_object(context, reader, 2),
TagCode::SetBackgroundColor => self.set_background_color(context, reader),
TagCode::StartSound => self.start_sound_1(context, reader),
TagCode::SoundStreamBlock => {
has_stream_block = true;
self.sound_stream_block(context, reader)
}
_ => Ok(()),
};
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame);
self.tag_stream_pos = reader.get_ref().position();
// If we are playing a streaming sound, there should(?) be a `SoundStreamBlock` on each frame.
if !has_stream_block {
self.stop_audio_stream(context);
}
}
#[allow(clippy::too_many_arguments)]
fn instantiate_child(
&mut self,
self_display_object: DisplayObject<'gc>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
id: CharacterId,
depth: Depth,
place_object: &swf::PlaceObject,
copy_previous_properties: bool,
) -> Option<DisplayObject<'gc>> {
if let Ok(mut child) = context
.library
.library_for_movie_mut(self.movie())
.instantiate_by_id(id, context.gc_context)
{
// Remove previous child from children list,
// and add new childonto front of the list.
let prev_child = self.children.insert(depth, child);
if let Some(prev_child) = prev_child {
self.remove_child_from_exec_list(context, prev_child);
}
self.add_child_to_exec_list(context.gc_context, child);
{
// Set initial properties for child.
child.set_depth(context.gc_context, depth);
child.set_parent(context.gc_context, Some(self_display_object));
child.set_place_frame(context.gc_context, self.current_frame());
if copy_previous_properties {
if let Some(prev_child) = prev_child {
child.copy_display_properties_from(context.gc_context, prev_child);
}
}
// Run first frame.
child.apply_place_object(context.gc_context, place_object);
child.post_instantiation(avm, context, child, None, false);
child.run_frame(avm, context);
}
Some(child)
} else {
log::error!("Unable to instantiate display node id {}", id);
None
}
}
/// Adds a child to the front of the execution list.
/// This does not affect the render list.
fn add_child_to_exec_list(
@ -1082,176 +1255,6 @@ impl<'gc> MovieClipData<'gc> {
// Flag child as removed.
child.unload(context);
}
pub fn run_goto(
&mut self,
self_display_object: DisplayObject<'gc>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
frame: FrameNumber,
) {
// Flash gotos are tricky:
// 1) Conceptually, a goto should act like the playhead is advancing forward or
// backward to a frame.
// 2) However, MovieClip timelines are stored as deltas from frame to frame,
// so for rewinds, we must restart to frame 1 and play forward.
// 3) Objects that would persist over the goto conceptually should not be
// destroyed and recreated; they should keep their properties.
// Particularly for rewinds, the object should persist if it was created
// *before* the frame we are going to. (DisplayObject::place_frame).
// 4) We want to avoid creating objects just to destroy them if they aren't on
// the goto frame, so we should instead aggregate the deltas into a final list
// of commands, and THEN modify the children as necessary.
// This map will maintain a map of depth -> placement commands.
// TODO: Move this to UpdateContext to avoid allocations.
let mut goto_commands = vec![];
self.stop_audio_stream(context);
let is_rewind = if frame < self.current_frame() {
// Because we can only step forward, we have to start at frame 1
// when rewinding.
self.tag_stream_pos = 0;
self.current_frame = 0;
// Remove all display objects that were created after the desination frame.
// TODO: We want to do something like self.children.retain here,
// but BTreeMap::retain does not exist.
let children: SmallVec<[_; 16]> = self
.children
.iter()
.filter_map(|(depth, clip)| {
if clip.place_frame() > frame {
Some((*depth, *clip))
} else {
None
}
})
.collect();
for (depth, child) in children {
self.children.remove(&depth);
self.remove_child_from_exec_list(context, child);
}
true
} else {
false
};
// Step through the intermediate frames, and aggregate the deltas of each frame.
let mut frame_pos = self.tag_stream_pos;
let data = self.static_data.swf.clone();
let mut reader = data.read_from(self.tag_stream_pos);
let mut index = 0;
let len = self.tag_stream_len() as u64;
// Sanity; let's make sure we don't seek way too far.
// TODO: This should be self.frames_loaded() when we implement that.
let clamped_frame = if frame <= self.total_frames() {
frame
} else {
self.total_frames()
};
while self.current_frame() < clamped_frame && frame_pos < len {
self.current_frame += 1;
frame_pos = reader.get_inner().position();
use swf::TagCode;
let tag_callback = |reader: &mut _, tag_code, tag_len| match tag_code {
TagCode::PlaceObject => {
index += 1;
self.goto_place_object(reader, tag_len, 1, &mut goto_commands, is_rewind, index)
}
TagCode::PlaceObject2 => {
index += 1;
self.goto_place_object(reader, tag_len, 2, &mut goto_commands, is_rewind, index)
}
TagCode::PlaceObject3 => {
index += 1;
self.goto_place_object(reader, tag_len, 3, &mut goto_commands, is_rewind, index)
}
TagCode::PlaceObject4 => {
index += 1;
self.goto_place_object(reader, tag_len, 4, &mut goto_commands, is_rewind, index)
}
TagCode::RemoveObject => {
self.goto_remove_object(reader, 1, context, &mut goto_commands, is_rewind)
}
TagCode::RemoveObject2 => {
self.goto_remove_object(reader, 2, context, &mut goto_commands, is_rewind)
}
_ => Ok(()),
};
let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame);
}
let hit_target_frame = self.current_frame == frame;
// Run the list of goto commands to actually create and update the display objects.
let run_goto_command = |clip: &mut MovieClipData<'gc>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
params: &GotoPlaceObject| {
let child_entry = clip.children.get_mut(&params.depth()).copied();
match child_entry {
// Apply final delta to display pamareters.
// For rewinds, if an object was created before the final frame,
// it will exist on the final frame as well. Re-use this object
// instead of recreating.
// If the ID is 0, we are modifying a previous child. Otherwise, we're replacing it.
// If it's a rewind, we removed any dead children above, so we always
// modify the previous child.
Some(mut prev_child) if params.id() == 0 || is_rewind => {
prev_child.apply_place_object(context.gc_context, &params.place_object);
}
_ => {
if let Some(mut child) = clip.instantiate_child(
self_display_object,
avm,
context,
params.id(),
params.depth(),
&params.place_object,
params.modifies_original_item(),
) {
// Set the place frame to the frame where the object *would* have been placed.
child.set_place_frame(context.gc_context, params.frame);
}
}
}
};
// We have to be sure that queued actions are generated in the same order
// as if the playhead had reached this frame normally.
// First, sort the goto commands in the order of execution.
// (Maybe it'd be better to keeps this list sorted as we create it?
// Currently `swap_remove` calls futz with the order; but we could use `remove`).
goto_commands.sort_by_key(|params| params.index);
// Then, run frames for children that were created before this frame.
goto_commands
.iter()
.filter(|params| params.frame < frame)
.for_each(|goto| run_goto_command(self, avm, context, goto));
// Next, run the final frame for the parent clip.
// Re-run the final frame without display tags (DoAction, StartSound, etc.)
// Note that this only happens if the frame exists and is loaded;
// e.g. gotoAndStop(9999) displays the final frame, but actions don't run!
if hit_target_frame {
self.current_frame -= 1;
self.tag_stream_pos = frame_pos;
self.run_frame_internal(self_display_object, avm, context, false);
} else {
self.current_frame = clamped_frame;
}
// Finally, run frames for children that are placed on this frame.
goto_commands
.iter()
.filter(|params| params.frame >= frame)
.for_each(|goto| run_goto_command(self, avm, context, goto));
}
/// Handles a PlaceObject tag when running a goto action.
#[inline]
@ -2065,10 +2068,10 @@ impl<'gc, 'a> MovieClipData<'gc> {
}
// Control tags
impl<'gc, 'a> MovieClipData<'gc> {
impl<'gc, 'a> MovieClip<'gc> {
#[inline]
fn do_action(
&mut self,
self,
self_display_object: DisplayObject<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
reader: &mut SwfStream<&'a [u8]>,
@ -2076,6 +2079,8 @@ impl<'gc, 'a> MovieClipData<'gc> {
) -> DecodeResult {
// Queue the actions.
let slice = self
.0
.read()
.static_data
.swf
.resize_to_reader(reader, tag_len)
@ -2094,7 +2099,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
}
fn place_object(
&mut self,
self,
self_display_object: DisplayObject<'gc>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
@ -2129,7 +2134,12 @@ impl<'gc, 'a> MovieClipData<'gc> {
}
}
PlaceObjectAction::Modify => {
if let Some(mut child) = self.children.get_mut(&place_object.depth.into()).copied()
if let Some(mut child) = self
.0
.read()
.children
.get(&place_object.depth.into())
.copied()
{
child.apply_place_object(context.gc_context, &place_object);
child
@ -2144,7 +2154,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
#[inline]
fn remove_object(
&mut self,
self,
context: &mut UpdateContext<'_, 'gc, '_>,
reader: &mut SwfStream<&'a [u8]>,
version: u8,
@ -2154,16 +2164,17 @@ impl<'gc, 'a> MovieClipData<'gc> {
} else {
reader.read_remove_object_2()
}?;
let child = self.children.remove(&remove_object.depth.into());
let mut mc = self.0.write(context.gc_context);
let child = mc.children.remove(&remove_object.depth.into());
if let Some(child) = child {
self.remove_child_from_exec_list(context, child);
mc.remove_child_from_exec_list(context, child);
}
Ok(())
}
#[inline]
fn set_background_color(
&mut self,
self,
context: &mut UpdateContext<'_, 'gc, '_>,
reader: &mut SwfStream<&'a [u8]>,
) -> DecodeResult {
@ -2173,29 +2184,27 @@ impl<'gc, 'a> MovieClipData<'gc> {
#[inline]
fn sound_stream_block(
&mut self,
self,
context: &mut UpdateContext<'_, 'gc, '_>,
_reader: &mut SwfStream<&'a [u8]>,
) -> DecodeResult {
if let (Some(stream_info), None) = (&self.static_data.audio_stream_info, self.audio_stream)
{
let slice = self
let mut mc = self.0.write(context.gc_context);
if let (Some(stream_info), None) = (&mc.static_data.audio_stream_info, mc.audio_stream) {
let slice = mc
.static_data
.swf
.to_start_and_end(self.tag_stream_pos as usize, self.tag_stream_len())
.to_start_and_end(mc.tag_stream_pos as usize, mc.tag_stream_len())
.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::Other,
"Invalid slice generated when constructing sound stream block",
)
})?;
let audio_stream = context.audio.start_stream(
self.id(),
self.current_frame() + 1,
slice,
&stream_info,
);
self.audio_stream = audio_stream.ok();
let audio_stream =
context
.audio
.start_stream(mc.id(), mc.current_frame() + 1, slice, &stream_info);
mc.audio_stream = audio_stream.ok();
}
Ok(())
@ -2203,14 +2212,14 @@ impl<'gc, 'a> MovieClipData<'gc> {
#[inline]
fn start_sound_1(
&mut self,
self,
context: &mut UpdateContext<'_, 'gc, '_>,
reader: &mut SwfStream<&'a [u8]>,
) -> DecodeResult {
let start_sound = reader.read_start_sound_1()?;
if let Some(handle) = context
.library
.library_for_movie_mut(self.movie())
.library_for_movie_mut(self.movie().unwrap()) // TODO
.get_sound(start_sound.id)
{
use swf::SoundEvent;