From 19034b76e440429a9132c2c7bffc18f634de8fb8 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Sun, 17 Jan 2021 22:46:18 -0800 Subject: [PATCH] swf: Return slices in swf::Reader Avoid copies by returning slices of the decompressed input. --- core/src/backend/audio/decoders.rs | 68 ++- core/src/display_object/button.rs | 5 +- core/src/display_object/edit_text.rs | 61 ++- core/src/display_object/movie_clip.rs | 757 ++++++++++++-------------- core/src/font.rs | 2 +- core/src/html/text_format.rs | 6 +- core/src/tag_utils.rs | 69 +-- scanner/src/main.rs | 5 +- swf/examples/reading.rs | 3 +- swf/src/avm2/read.rs | 11 +- swf/src/read.rs | 486 +++++++++-------- swf/src/test_data.rs | 125 ++--- swf/src/types.rs | 183 ++++--- swf/src/write.rs | 4 +- 14 files changed, 867 insertions(+), 918 deletions(-) diff --git a/core/src/backend/audio/decoders.rs b/core/src/backend/audio/decoders.rs index b39e9560f..f0609210e 100644 --- a/core/src/backend/audio/decoders.rs +++ b/core/src/backend/audio/decoders.rs @@ -209,11 +209,15 @@ pub trait SeekableDecoder: Decoder { } } +/// `StreamTagReader` reads through the SWF tag data of a `MovieClip`, extracting +/// audio data from the `SoundStreamBlock` tags. It can be used as an `Iterator` that +/// will return consecutive slices of the underlying audio data. /// `StreamTagReader` reads through the SWF tag data of a `MovieClip`, extracting /// audio data from the `SoundStreamBlock` tags. It can be used as an `Iterator` that /// will return consecutive slices of the underlying audio data. struct StreamTagReader { - reader: swf::read::Reader>, + swf_data: SwfSlice, + pos: usize, current_frame: u16, current_audio_data: SwfSlice, compression: AudioCompression, @@ -224,10 +228,10 @@ impl StreamTagReader { /// `swf_data` should be the tag data of a MovieClip. fn new(compression: AudioCompression, swf_data: SwfSlice) -> Self { let current_audio_data = SwfSlice::empty(swf_data.movie.clone()); - let version = swf_data.version(); Self { + swf_data, + pos: 0, compression, - reader: swf::read::Reader::new(Cursor::new(swf_data), version), current_frame: 1, current_audio_data, } @@ -250,40 +254,32 @@ impl Iterator for StreamTagReader { 0 }; - let tag_callback = - |reader: &mut swf::read::Reader>, tag_code, tag_len| match tag_code { - TagCode::ShowFrame => { - *current_frame += 1; - Ok(()) - } - TagCode::SoundStreamBlock => { - // TODO: Implement index ops on `SwfSlice`. - let pos = reader.get_ref().position() as usize; - let pos = reader.get_ref().get_ref().start + pos; - found = true; - if tag_len >= skip_len { - *audio_data = SwfSlice { - movie: std::sync::Arc::clone(&reader.get_ref().get_ref().movie), - start: pos + skip_len, - end: pos + tag_len, - }; - } else { - *audio_data = SwfSlice { - movie: std::sync::Arc::clone(&reader.get_ref().get_ref().movie), - start: pos, - end: pos + tag_len, - }; - }; - Ok(()) - } - _ => Ok(()), - }; + let swf_data = &self.swf_data; + let tag_callback = |reader: &mut swf::read::Reader<'_>, tag_code, tag_len| match tag_code { + TagCode::ShowFrame => { + *current_frame += 1; + Ok(()) + } + TagCode::SoundStreamBlock => { + // TODO: Implement index ops on `SwfSlice`. + //let pos = reader.get_ref().as_ptr() as usize - swf_data.as_ref().as_ptr() as usize; + found = true; + if tag_len >= skip_len { + *audio_data = swf_data + .to_subslice(&reader.get_ref()[skip_len..tag_len]) + .unwrap() + } else { + *audio_data = swf_data.to_subslice(&reader.get_ref()[..tag_len]).unwrap() + }; + Ok(()) + } + _ => Ok(()), + }; - let _ = crate::tag_utils::decode_tags( - &mut self.reader, - tag_callback, - TagCode::SoundStreamBlock, - ); + let version = swf_data.version(); + let mut reader = swf::read::Reader::new(&self.swf_data.as_ref()[self.pos..], version); + let _ = crate::tag_utils::decode_tags(&mut reader, tag_callback, TagCode::SoundStreamBlock); + self.pos = reader.get_ref().as_ptr() as usize - swf_data.as_ref().as_ptr() as usize; if found { Some(self.current_audio_data.clone()) diff --git a/core/src/display_object/button.rs b/core/src/display_object/button.rs index 710e62d46..bc43d6e7d 100644 --- a/core/src/display_object/button.rs +++ b/core/src/display_object/button.rs @@ -39,8 +39,9 @@ impl<'gc> Button<'gc> { ) -> Self { let mut actions = vec![]; for action in &button.actions { - let action_data = - source_movie.owned_subslice(action.action_data.clone(), &source_movie.movie); + let action_data = source_movie + .to_unbounded_subslice(action.action_data) + .unwrap(); for condition in &action.conditions { let button_action = ButtonAction { action_data: action_data.clone(), diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index aeb4a32dd..eb9d5d2c8 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -207,7 +207,28 @@ impl<'gc> EditText<'gc> { context.gc_context, EditTextStatic { swf: swf_movie, - text: swf_tag, + text: EditTextStaticData { + id: swf_tag.id, + bounds: swf_tag.bounds, + font_id: swf_tag.font_id, + font_class_name: swf_tag.font_class_name.map(str::to_string), + height: swf_tag.height, + color: swf_tag.color.clone(), + max_length: swf_tag.max_length, + layout: swf_tag.layout.clone(), + variable_name: swf_tag.variable_name.to_string(), + initial_text: swf_tag.initial_text.map(str::to_string), + is_word_wrap: swf_tag.is_word_wrap, + is_multiline: swf_tag.is_multiline, + is_password: swf_tag.is_password, + is_read_only: swf_tag.is_read_only, + is_auto_size: swf_tag.is_auto_size, + is_selectable: swf_tag.is_selectable, + has_border: swf_tag.has_border, + was_static: swf_tag.was_static, + is_html: swf_tag.is_html, + is_device_font: swf_tag.is_device_font, + }, }, ), is_multiline, @@ -225,7 +246,7 @@ impl<'gc> EditText<'gc> { intrinsic_bounds, bounds, autosize: AutoSizeMode::None, - variable, + variable: variable.map(str::to_string), bound_stage_object: None, firing_variable_binding: false, selection: None, @@ -272,7 +293,7 @@ impl<'gc> EditText<'gc> { indent: Twips::from_pixels(0.0), leading: Twips::from_pixels(0.0), }), - variable_name: "".to_string(), //TODO: should be null + variable_name: "", //TODO: should be null initial_text: None, is_word_wrap: false, is_multiline: false, @@ -598,7 +619,7 @@ impl<'gc> EditText<'gc> { .initial_text .clone() .unwrap_or_default(); - let _ = self.set_text(text, &mut activation.context); + let _ = self.set_text(text.to_string(), &mut activation.context); self.0.write(activation.context.gc_context).variable = variable; self.try_bind_text_field_variable(activation, true); @@ -1535,15 +1556,37 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> { } /// Static data shared between all instances of a text object. -#[allow(dead_code)] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Collect)] +#[collect(no_drop)] struct EditTextStatic { swf: Arc, - text: swf::EditText, + text: EditTextStaticData, +} +#[derive(Debug, Clone)] +struct EditTextStaticData { + id: CharacterId, + bounds: swf::Rectangle, + font_id: Option, // TODO(Herschel): Combine with height + font_class_name: Option, + height: Option, + color: Option, + max_length: Option, + layout: Option, + variable_name: String, + initial_text: Option, + is_word_wrap: bool, + is_multiline: bool, + is_password: bool, + is_read_only: bool, + is_auto_size: bool, + is_selectable: bool, + has_border: bool, + was_static: bool, + is_html: bool, + is_device_font: bool, } -unsafe impl<'gc> gc_arena::Collect for EditTextStatic { - #[inline] +unsafe impl<'gc> Collect for EditTextStaticData { fn needs_trace() -> bool { false } diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index 509f1629f..a0543c03e 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -170,230 +170,219 @@ impl<'gc> MovieClip<'gc> { // Should be able to hoist this up somewhere, or use MaybeUninit. let mut static_data = (&*self.0.read().static_data).clone(); let data = self.0.read().static_data.swf.clone(); - let mut reader = data.read_from(self.0.read().tag_stream_pos); + let mut reader = data.read_from(0); let mut cur_frame = 1; let mut ids = fnv::FnvHashMap::default(); - let version = reader.version(); - let tag_callback = |reader: &mut SwfStream<&[u8]>, tag_code, tag_len| { - let data = *reader.get_inner().get_ref(); - let tag_pos = reader.get_inner().position() as usize; - let tag_slice = data - .get(tag_pos..tag_pos + tag_len) - .ok_or("Unexpected end of tag")?; - let reader = &mut SwfStream::new(std::io::Cursor::new(tag_slice), version); - match tag_code { - TagCode::FileAttributes => { - let attributes = reader.read_file_attributes()?; - let avm_type = if attributes.is_action_script_3 { - log::warn!("This SWF contains ActionScript 3 which is not yet supported by Ruffle. The movie may not work as intended."); - AvmType::Avm2 - } else { - AvmType::Avm1 - }; + let tag_callback = |reader: &mut SwfStream<'_>, tag_code, tag_len| match tag_code { + TagCode::FileAttributes => { + let attributes = reader.read_file_attributes()?; + let avm_type = if attributes.is_action_script_3 { + log::warn!("This SWF contains ActionScript 3 which is not yet supported by Ruffle. The movie may not work as intended."); + AvmType::Avm2 + } else { + AvmType::Avm1 + }; - let movie = self.movie().unwrap(); - let library = context.library.library_for_movie_mut(movie); - if let Err(e) = library.check_avm_type(avm_type) { - log::warn!("{}", e); - } + let movie = self.movie().unwrap(); + let library = context.library.library_for_movie_mut(movie); + if let Err(e) = library.check_avm_type(avm_type) { + log::warn!("{}", e); + } - Ok(()) - } - TagCode::DefineBits => self - .0 - .write(context.gc_context) - .define_bits(context, reader, tag_len), - TagCode::DefineBitsJpeg2 => self - .0 - .write(context.gc_context) - .define_bits_jpeg_2(context, reader, tag_len), - TagCode::DefineBitsJpeg3 => self - .0 - .write(context.gc_context) - .define_bits_jpeg_3(context, reader, tag_len), - TagCode::DefineBitsJpeg4 => self - .0 - .write(context.gc_context) - .define_bits_jpeg_4(context, reader, tag_len), - TagCode::DefineBitsLossless => self - .0 - .write(context.gc_context) - .define_bits_lossless(context, reader, 1), - TagCode::DefineBitsLossless2 => self - .0 - .write(context.gc_context) - .define_bits_lossless(context, reader, 2), - TagCode::DefineButton => self - .0 - .write(context.gc_context) - .define_button_1(context, reader), - TagCode::DefineButton2 => self - .0 - .write(context.gc_context) - .define_button_2(context, reader), - TagCode::DefineButtonCxform => self - .0 - .write(context.gc_context) - .define_button_cxform(context, reader, tag_len), - TagCode::DefineButtonSound => self - .0 - .write(context.gc_context) - .define_button_sound(context, reader), - TagCode::DefineEditText => self - .0 - .write(context.gc_context) - .define_edit_text(context, reader), - TagCode::DefineFont => self - .0 - .write(context.gc_context) - .define_font_1(context, reader), - TagCode::DefineFont2 => self - .0 - .write(context.gc_context) - .define_font_2(context, reader), - TagCode::DefineFont3 => self - .0 - .write(context.gc_context) - .define_font_3(context, reader), - TagCode::DefineFont4 => self - .0 - .write(context.gc_context) - .define_font_4(context, reader), - TagCode::DefineMorphShape => self.0.write(context.gc_context).define_morph_shape( - context, - reader, - morph_shapes, - 1, - ), - TagCode::DefineMorphShape2 => self.0.write(context.gc_context).define_morph_shape( - context, - reader, - morph_shapes, - 2, - ), - TagCode::DefineShape => self - .0 - .write(context.gc_context) - .define_shape(context, reader, 1), - TagCode::DefineShape2 => self - .0 - .write(context.gc_context) - .define_shape(context, reader, 2), - TagCode::DefineShape3 => self - .0 - .write(context.gc_context) - .define_shape(context, reader, 3), - TagCode::DefineShape4 => self - .0 - .write(context.gc_context) - .define_shape(context, reader, 4), - TagCode::DefineSound => self - .0 - .write(context.gc_context) - .define_sound(context, reader), - TagCode::DefineSprite => self.0.write(context.gc_context).define_sprite( - context, - reader, - tag_len, - morph_shapes, - ), - TagCode::DefineText => self - .0 - .write(context.gc_context) - .define_text(context, reader, 1), - TagCode::DefineText2 => self - .0 - .write(context.gc_context) - .define_text(context, reader, 2), - TagCode::DoInitAction => self.do_init_action(context, reader, tag_len), - TagCode::DoAbc => self.do_abc(context, reader, tag_len), - TagCode::SymbolClass => self.symbol_class(context, reader), - TagCode::DefineSceneAndFrameLabelData => { - self.scene_and_frame_labels(reader, &mut static_data) - } - TagCode::ExportAssets => self - .0 - .write(context.gc_context) - .export_assets(context, reader), - TagCode::FrameLabel => self.0.write(context.gc_context).frame_label( - context, - reader, - tag_len, - cur_frame, - &mut static_data, - ), - TagCode::JpegTables => self - .0 - .write(context.gc_context) - .jpeg_tables(context, reader, tag_len), - TagCode::PlaceObject => self.0.write(context.gc_context).preload_place_object( - context, - reader, - tag_len, - &mut ids, - morph_shapes, - 1, - ), - TagCode::PlaceObject2 => self.0.write(context.gc_context).preload_place_object( - context, - reader, - tag_len, - &mut ids, - morph_shapes, - 2, - ), - TagCode::PlaceObject3 => self.0.write(context.gc_context).preload_place_object( - context, - reader, - tag_len, - &mut ids, - morph_shapes, - 3, - ), - TagCode::PlaceObject4 => self.0.write(context.gc_context).preload_place_object( - context, - reader, - tag_len, - &mut ids, - morph_shapes, - 4, - ), - TagCode::RemoveObject => self - .0 - .write(context.gc_context) - .preload_remove_object(context, reader, &mut ids, 1), - TagCode::RemoveObject2 => self - .0 - .write(context.gc_context) - .preload_remove_object(context, reader, &mut ids, 2), - TagCode::ShowFrame => self.0.write(context.gc_context).preload_show_frame( - context, - reader, - &mut cur_frame, - ), - TagCode::ScriptLimits => self - .0 - .write(context.gc_context) - .script_limits(reader, context.avm1), - TagCode::SoundStreamHead => self - .0 - .write(context.gc_context) - .preload_sound_stream_head(context, reader, cur_frame, &mut static_data, 1), - TagCode::SoundStreamHead2 => self - .0 - .write(context.gc_context) - .preload_sound_stream_head(context, reader, cur_frame, &mut static_data, 2), - TagCode::SoundStreamBlock => { - self.0.write(context.gc_context).preload_sound_stream_block( - context, - reader, - cur_frame, - &mut static_data, - tag_len, - ) - } - _ => Ok(()), + Ok(()) } + TagCode::DefineBits => self + .0 + .write(context.gc_context) + .define_bits(context, reader, tag_len), + TagCode::DefineBitsJpeg2 => self + .0 + .write(context.gc_context) + .define_bits_jpeg_2(context, reader, tag_len), + TagCode::DefineBitsJpeg3 => self + .0 + .write(context.gc_context) + .define_bits_jpeg_3(context, reader, tag_len), + TagCode::DefineBitsJpeg4 => self + .0 + .write(context.gc_context) + .define_bits_jpeg_4(context, reader, tag_len), + TagCode::DefineBitsLossless => self + .0 + .write(context.gc_context) + .define_bits_lossless(context, reader, 1), + TagCode::DefineBitsLossless2 => self + .0 + .write(context.gc_context) + .define_bits_lossless(context, reader, 2), + TagCode::DefineButton => self + .0 + .write(context.gc_context) + .define_button_1(context, reader), + TagCode::DefineButton2 => self + .0 + .write(context.gc_context) + .define_button_2(context, reader), + TagCode::DefineButtonCxform => self + .0 + .write(context.gc_context) + .define_button_cxform(context, reader, tag_len), + TagCode::DefineButtonSound => self + .0 + .write(context.gc_context) + .define_button_sound(context, reader), + TagCode::DefineEditText => self + .0 + .write(context.gc_context) + .define_edit_text(context, reader), + TagCode::DefineFont => self + .0 + .write(context.gc_context) + .define_font_1(context, reader), + TagCode::DefineFont2 => self + .0 + .write(context.gc_context) + .define_font_2(context, reader), + TagCode::DefineFont3 => self + .0 + .write(context.gc_context) + .define_font_3(context, reader), + TagCode::DefineFont4 => self + .0 + .write(context.gc_context) + .define_font_4(context, reader), + TagCode::DefineMorphShape => self.0.write(context.gc_context).define_morph_shape( + context, + reader, + morph_shapes, + 1, + ), + TagCode::DefineMorphShape2 => self.0.write(context.gc_context).define_morph_shape( + context, + reader, + morph_shapes, + 2, + ), + TagCode::DefineShape => self + .0 + .write(context.gc_context) + .define_shape(context, reader, 1), + TagCode::DefineShape2 => self + .0 + .write(context.gc_context) + .define_shape(context, reader, 2), + TagCode::DefineShape3 => self + .0 + .write(context.gc_context) + .define_shape(context, reader, 3), + TagCode::DefineShape4 => self + .0 + .write(context.gc_context) + .define_shape(context, reader, 4), + TagCode::DefineSound => self + .0 + .write(context.gc_context) + .define_sound(context, reader), + TagCode::DefineSprite => self.0.write(context.gc_context).define_sprite( + context, + reader, + tag_len, + morph_shapes, + ), + TagCode::DefineText => self + .0 + .write(context.gc_context) + .define_text(context, reader, 1), + TagCode::DefineText2 => self + .0 + .write(context.gc_context) + .define_text(context, reader, 2), + TagCode::DoInitAction => self.do_init_action(context, reader, tag_len), + TagCode::DoAbc => self.do_abc(context, reader, tag_len), + TagCode::SymbolClass => self.symbol_class(context, reader), + TagCode::DefineSceneAndFrameLabelData => { + self.scene_and_frame_labels(reader, &mut static_data) + } + TagCode::ExportAssets => self + .0 + .write(context.gc_context) + .export_assets(context, reader), + TagCode::FrameLabel => self.0.write(context.gc_context).frame_label( + context, + reader, + tag_len, + cur_frame, + &mut static_data, + ), + TagCode::JpegTables => self + .0 + .write(context.gc_context) + .jpeg_tables(context, reader, tag_len), + TagCode::PlaceObject => self.0.write(context.gc_context).preload_place_object( + context, + reader, + tag_len, + &mut ids, + morph_shapes, + 1, + ), + TagCode::PlaceObject2 => self.0.write(context.gc_context).preload_place_object( + context, + reader, + tag_len, + &mut ids, + morph_shapes, + 2, + ), + TagCode::PlaceObject3 => self.0.write(context.gc_context).preload_place_object( + context, + reader, + tag_len, + &mut ids, + morph_shapes, + 3, + ), + TagCode::PlaceObject4 => self.0.write(context.gc_context).preload_place_object( + context, + reader, + tag_len, + &mut ids, + morph_shapes, + 4, + ), + TagCode::RemoveObject => self + .0 + .write(context.gc_context) + .preload_remove_object(context, reader, &mut ids, 1), + TagCode::RemoveObject2 => self + .0 + .write(context.gc_context) + .preload_remove_object(context, reader, &mut ids, 2), + TagCode::ShowFrame => { + self.0 + .write(context.gc_context) + .preload_show_frame(context, reader, &mut cur_frame) + } + TagCode::ScriptLimits => self + .0 + .write(context.gc_context) + .script_limits(reader, context.avm1), + TagCode::SoundStreamHead => self.0.write(context.gc_context).preload_sound_stream_head( + context, + reader, + cur_frame, + &mut static_data, + 1, + ), + TagCode::SoundStreamHead2 => self + .0 + .write(context.gc_context) + .preload_sound_stream_head(context, reader, cur_frame, &mut static_data, 2), + TagCode::SoundStreamBlock => self + .0 + .write(context.gc_context) + .preload_sound_stream_block(context, reader, cur_frame, &mut static_data, tag_len), + _ => Ok(()), }; let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::End); self.0.write(context.gc_context).static_data = @@ -409,7 +398,7 @@ impl<'gc> MovieClip<'gc> { fn do_init_action( self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&[u8]>, + reader: &mut SwfStream<'_>, tag_len: usize, ) -> DecodeResult { let movie = self.movie().unwrap(); @@ -454,7 +443,7 @@ impl<'gc> MovieClip<'gc> { fn do_abc( self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&[u8]>, + reader: &mut SwfStream<'_>, tag_len: usize, ) -> DecodeResult { let movie = self.movie().unwrap(); @@ -499,7 +488,7 @@ impl<'gc> MovieClip<'gc> { fn symbol_class( self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&[u8]>, + reader: &mut SwfStream<'_>, ) -> DecodeResult { let movie = self .movie() @@ -560,7 +549,7 @@ impl<'gc> MovieClip<'gc> { #[inline] fn scene_and_frame_labels( self, - reader: &mut SwfStream<&[u8]>, + reader: &mut SwfStream<'_>, static_data: &mut MovieClipStatic, ) -> DecodeResult { let mut sfl_data = reader.read_define_scene_and_frame_label_data()?; @@ -587,7 +576,9 @@ impl<'gc> MovieClip<'gc> { } for FrameLabelData { frame_num, label } in sfl_data.frame_labels { - static_data.frame_labels.insert(label, frame_num as u16 + 1); + static_data + .frame_labels + .insert(label.to_string(), frame_num as u16 + 1); } Ok(()) @@ -918,25 +909,23 @@ impl<'gc> MovieClip<'gc> { if frame > 0 && frame <= self.total_frames() { let mut cur_frame = 1; let clip = self.0.read(); - let len = clip.tag_stream_len(); let mut reader = clip.static_data.swf.read_from(0); - while cur_frame <= frame && reader.get_ref().position() < len as u64 { - let tag_callback = - |reader: &mut Reader>, tag_code, tag_len| { - match tag_code { - TagCode::ShowFrame => cur_frame += 1, - TagCode::DoAction if cur_frame == frame => { - // On the target frame, add any DoAction tags to the array. - if let Some(code) = - clip.static_data.swf.resize_to_reader(reader, tag_len) - { - actions.push(code) - } + while cur_frame <= frame && !reader.get_ref().is_empty() { + let tag_callback = |reader: &mut Reader<'_>, tag_code, tag_len| { + match tag_code { + TagCode::ShowFrame => cur_frame += 1, + TagCode::DoAction if cur_frame == frame => { + // On the target frame, add any DoAction tags to the array. + if let Some(code) = + clip.static_data.swf.resize_to_reader(reader, tag_len) + { + actions.push(code) } - _ => (), } - Ok(()) - }; + _ => (), + } + Ok(()) + }; let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::ShowFrame); } @@ -1004,53 +993,41 @@ impl<'gc> MovieClip<'gc> { } let mc = self.0.read(); - let _tag_pos = mc.tag_stream_pos; + let tag_stream_start = mc.static_data.swf.as_ref().as_ptr() as u64; 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); - let version = reader.version(); use swf::TagCode; - let tag_callback = |reader: &mut SwfStream<&[u8]>, tag_code, tag_len| { - let data = *reader.get_inner().get_ref(); - let tag_pos = reader.get_inner().position() as usize; - let tag_slice = data - .get(tag_pos..tag_pos + tag_len) - .ok_or("Not enough data for tag")?; - let reader = &mut SwfStream::new(std::io::Cursor::new(tag_slice), version); - 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, context, reader, tag_len, 1) - } - TagCode::PlaceObject2 if run_display_actions => { - self.place_object(self_display_object, context, reader, tag_len, 2) - } - TagCode::PlaceObject3 if run_display_actions => { - self.place_object(self_display_object, context, reader, tag_len, 3) - } - TagCode::PlaceObject4 if run_display_actions => { - self.place_object(self_display_object, 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_callback = |reader: &mut SwfStream<'_>, 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, context, reader, tag_len, 1) } + TagCode::PlaceObject2 if run_display_actions => { + self.place_object(self_display_object, context, reader, tag_len, 2) + } + TagCode::PlaceObject3 if run_display_actions => { + self.place_object(self_display_object, context, reader, tag_len, 3) + } + TagCode::PlaceObject4 if run_display_actions => { + self.place_object(self_display_object, 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(); + self.0.write(context.gc_context).tag_stream_pos = + reader.get_ref().as_ptr() as u64 - tag_stream_start; // If we are playing a streaming sound, there should(?) be a `SoundStreamBlock` on each frame. if !has_stream_block { @@ -1150,7 +1127,7 @@ impl<'gc> MovieClip<'gc> { // This map will maintain a map of depth -> placement commands. // TODO: Move this to UpdateContext to avoid allocations. - let mut goto_commands = vec![]; + let mut goto_commands: Vec> = vec![]; self.0.write(context.gc_context).stop_audio_stream(context); @@ -1192,12 +1169,11 @@ impl<'gc> MovieClip<'gc> { // Step through the intermediate frames, and aggregate the deltas of each frame. let mc = self.0.read(); + let tag_stream_start = mc.static_data.swf.as_ref().as_ptr() as u64; 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() { @@ -1207,78 +1183,44 @@ impl<'gc> MovieClip<'gc> { }; drop(mc); - while self.current_frame() < clamped_frame && frame_pos < len { + let mut reader = data.read_from(frame_pos); + while self.current_frame() < clamped_frame && !reader.get_ref().is_empty() { self.0.write(context.gc_context).current_frame += 1; - frame_pos = reader.get_inner().position(); + frame_pos = reader.get_ref().as_ptr() as u64 - tag_stream_start; - let version = reader.version(); use swf::TagCode; - let tag_callback = |reader: &mut SwfStream<&[u8]>, tag_code, tag_len| { - let data = *reader.get_inner().get_ref(); - let tag_pos = reader.get_inner().position() as usize; - let tag_slice = &data[tag_pos..tag_pos + tag_len]; - let reader = &mut SwfStream::new(std::io::Cursor::new(tag_slice), version); - match tag_code { - TagCode::PlaceObject => { - index += 1; - let mut mc = self.0.write(context.gc_context); + let tag_callback = |reader: &mut SwfStream<'gc>, tag_code, tag_len| match tag_code { + TagCode::PlaceObject => { + index += 1; + let mut mc = self.0.write(context.gc_context); - mc.goto_place_object( - reader, - tag_len, - 1, - &mut goto_commands, - is_rewind, - index, - ) - } - TagCode::PlaceObject2 => { - index += 1; - let mut mc = self.0.write(context.gc_context); - - mc.goto_place_object( - reader, - tag_len, - 2, - &mut goto_commands, - is_rewind, - index, - ) - } - TagCode::PlaceObject3 => { - index += 1; - let mut mc = self.0.write(context.gc_context); - - mc.goto_place_object( - reader, - tag_len, - 3, - &mut goto_commands, - is_rewind, - index, - ) - } - TagCode::PlaceObject4 => { - index += 1; - let mut mc = self.0.write(context.gc_context); - - mc.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(()), + mc.goto_place_object(reader, tag_len, 1, &mut goto_commands, is_rewind, index) } + TagCode::PlaceObject2 => { + index += 1; + let mut mc = self.0.write(context.gc_context); + + mc.goto_place_object(reader, tag_len, 2, &mut goto_commands, is_rewind, index) + } + TagCode::PlaceObject3 => { + index += 1; + let mut mc = self.0.write(context.gc_context); + + mc.goto_place_object(reader, tag_len, 3, &mut goto_commands, is_rewind, index) + } + TagCode::PlaceObject4 => { + index += 1; + let mut mc = self.0.write(context.gc_context); + + mc.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); } @@ -1287,7 +1229,7 @@ impl<'gc> MovieClip<'gc> { // Run the list of goto commands to actually create and update the display objects. let run_goto_command = |clip: MovieClip<'gc>, context: &mut UpdateContext<'_, 'gc, '_>, - params: &GotoPlaceObject| { + params: &GotoPlaceObject<'_>| { let child_entry = clip.child_by_depth(params.depth()); match child_entry { // Apply final delta to display parameters. @@ -1564,10 +1506,10 @@ impl<'gc> MovieClip<'gc> { #[inline] fn goto_remove_object<'a>( mut self, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, version: u8, context: &mut UpdateContext<'_, 'gc, '_>, - goto_commands: &mut Vec, + goto_commands: &mut Vec>, is_rewind: bool, ) -> DecodeResult { let remove_object = if version == 1 { @@ -1940,10 +1882,10 @@ impl<'gc> MovieClipData<'gc> { #[inline] fn goto_place_object<'a>( &mut self, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, version: u8, - goto_commands: &mut Vec, + goto_commands: &mut Vec>, is_rewind: bool, index: usize, ) -> DecodeResult { @@ -2100,7 +2042,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_bits_lossless( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, version: u8, ) -> DecodeResult { let define_bits_lossless = reader.read_define_bits_lossless(version)?; @@ -2125,7 +2067,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_morph_shape( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, morph_shapes: &mut fnv::FnvHashMap, version: u8, ) -> DecodeResult { @@ -2140,7 +2082,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_shape( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, version: u8, ) -> DecodeResult { let swf_shape = reader.read_define_shape(version)?; @@ -2157,7 +2099,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn preload_place_object( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, ids: &mut fnv::FnvHashMap, morph_shapes: &mut fnv::FnvHashMap, @@ -2207,15 +2149,13 @@ impl<'gc, 'a> MovieClipData<'gc> { fn preload_sound_stream_block( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, cur_frame: FrameNumber, static_data: &mut MovieClipStatic, tag_len: usize, ) -> DecodeResult { if static_data.audio_stream_info.is_some() { - let pos = reader.get_ref().position() as usize; - let data = reader.get_ref().get_ref(); - let data = &data[pos..pos + tag_len]; + let data = &reader.get_ref()[..tag_len]; context .audio .preload_sound_stream_block(self.id(), cur_frame, data); @@ -2228,7 +2168,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn preload_sound_stream_head( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, cur_frame: FrameNumber, static_data: &mut MovieClipStatic, _version: u8, @@ -2245,7 +2185,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_bits( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, ) -> DecodeResult { use std::io::Read; @@ -2278,7 +2218,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_bits_jpeg_2( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, ) -> DecodeResult { use std::io::Read; @@ -2305,7 +2245,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_bits_jpeg_3( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, ) -> DecodeResult { use std::io::Read; @@ -2345,7 +2285,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_bits_jpeg_4( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, ) -> DecodeResult { use std::io::Read; @@ -2386,7 +2326,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_button_1( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ) -> DecodeResult { let swf_button = reader.read_define_button_1()?; let button = Button::from_swf_tag( @@ -2406,7 +2346,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_button_2( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ) -> DecodeResult { let swf_button = reader.read_define_button_2()?; let button = Button::from_swf_tag( @@ -2426,7 +2366,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_button_cxform( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, ) -> DecodeResult { let button_colors = reader.read_define_button_cxform(tag_len)?; @@ -2456,7 +2396,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_button_sound( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ) -> DecodeResult { let button_sounds = reader.read_define_button_sound()?; if let Some(button) = context @@ -2486,7 +2426,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_edit_text( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ) -> DecodeResult { let swf_edit_text = reader.read_define_edit_text()?; let edit_text = EditText::from_swf_tag(context, self.movie(), swf_edit_text); @@ -2501,7 +2441,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_font_1( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ) -> DecodeResult { let font = reader.read_define_font_1()?; let glyphs = font @@ -2518,7 +2458,7 @@ impl<'gc, 'a> MovieClipData<'gc> { let font = swf::Font { id: font.id, version: 0, - name: "".to_string(), + name: "", glyphs, language: swf::Language::Unknown, layout: None, @@ -2540,7 +2480,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_font_2( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ) -> DecodeResult { let font = reader.read_define_font_2(2)?; let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap(); @@ -2555,7 +2495,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_font_3( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ) -> DecodeResult { let font = reader.read_define_font_2(3)?; let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap(); @@ -2571,7 +2511,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_font_4( &mut self, _context: &mut UpdateContext<'_, 'gc, '_>, - _reader: &mut SwfStream<&'a [u8]>, + _reader: &mut SwfStream<'a>, ) -> DecodeResult { log::warn!("DefineFont4 tag (TLF text) is not implemented"); Ok(()) @@ -2581,7 +2521,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_sound( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ) -> DecodeResult { let sound = reader.read_define_sound()?; if let Ok(handle) = context.audio.register_sound(&sound) { @@ -2601,7 +2541,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_sprite( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, morph_shapes: &mut fnv::FnvHashMap, ) -> DecodeResult { @@ -2636,7 +2576,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn define_text( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, version: u8, ) -> DecodeResult { let text = reader.read_define_text(version)?; @@ -2649,11 +2589,7 @@ impl<'gc, 'a> MovieClipData<'gc> { } #[inline] - fn script_limits( - &mut self, - reader: &mut SwfStream<&'a [u8]>, - avm: &mut Avm1<'gc>, - ) -> DecodeResult { + fn script_limits(&mut self, reader: &mut SwfStream<'a>, avm: &mut Avm1<'gc>) -> DecodeResult { let max_recursion_depth = reader.read_u16()?; let _timeout_in_seconds = reader.read_u16()?; @@ -2666,7 +2602,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn export_assets( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ) -> DecodeResult { let exports = reader.read_export_assets()?; for export in exports { @@ -2677,7 +2613,8 @@ impl<'gc, 'a> MovieClipData<'gc> { // TODO: do other types of Character need to know their exported name? if let Some(Character::MovieClip(movie_clip)) = character { - *movie_clip.0.read().static_data.exported_name.borrow_mut() = Some(export.name); + *movie_clip.0.read().static_data.exported_name.borrow_mut() = + Some(export.name.to_string()); } } Ok(()) @@ -2687,16 +2624,15 @@ impl<'gc, 'a> MovieClipData<'gc> { fn frame_label( &mut self, _context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, cur_frame: FrameNumber, static_data: &mut MovieClipStatic, ) -> DecodeResult { - let mut frame_label = reader.read_frame_label(tag_len)?; + let frame_label = reader.read_frame_label(tag_len)?; // Frame labels are case insensitive (ASCII). - frame_label.label.make_ascii_lowercase(); - if let std::collections::hash_map::Entry::Vacant(v) = - static_data.frame_labels.entry(frame_label.label) + let label = frame_label.label.to_ascii_lowercase(); + if let std::collections::hash_map::Entry::Vacant(v) = static_data.frame_labels.entry(label) { v.insert(cur_frame); } else { @@ -2709,7 +2645,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn jpeg_tables( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, ) -> DecodeResult { use std::io::Read; @@ -2727,7 +2663,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn preload_remove_object( &mut self, _context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ids: &mut fnv::FnvHashMap, version: u8, ) -> DecodeResult { @@ -2744,7 +2680,7 @@ impl<'gc, 'a> MovieClipData<'gc> { fn preload_show_frame( &mut self, _context: &mut UpdateContext<'_, 'gc, '_>, - _reader: &mut SwfStream<&'a [u8]>, + _reader: &mut SwfStream<'a>, cur_frame: &mut FrameNumber, ) -> DecodeResult { *cur_frame += 1; @@ -2759,7 +2695,7 @@ impl<'gc, 'a> MovieClip<'gc> { self, self_display_object: DisplayObject<'gc>, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, ) -> DecodeResult { let movie = self.movie().unwrap(); @@ -2795,7 +2731,7 @@ impl<'gc, 'a> MovieClip<'gc> { self, self_display_object: DisplayObject<'gc>, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, tag_len: usize, version: u8, ) -> DecodeResult { @@ -2837,7 +2773,7 @@ impl<'gc, 'a> MovieClip<'gc> { fn remove_object( mut self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, version: u8, ) -> DecodeResult { let remove_object = if version == 1 { @@ -2861,7 +2797,7 @@ impl<'gc, 'a> MovieClip<'gc> { fn set_background_color( self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ) -> DecodeResult { // Set background color if none set // bgcolor attribute on the HTML embed would override this @@ -2878,7 +2814,7 @@ impl<'gc, 'a> MovieClip<'gc> { fn sound_stream_block( self, context: &mut UpdateContext<'_, 'gc, '_>, - _reader: &mut SwfStream<&'a [u8]>, + _reader: &mut SwfStream<'a>, ) -> DecodeResult { let mut mc = self.0.write(context.gc_context); if mc.playing() { @@ -2911,7 +2847,7 @@ impl<'gc, 'a> MovieClip<'gc> { fn start_sound_1( self, context: &mut UpdateContext<'_, 'gc, '_>, - reader: &mut SwfStream<&'a [u8]>, + reader: &mut SwfStream<'a>, ) -> DecodeResult { let start_sound = reader.read_start_sound_1()?; if let Some(handle) = context @@ -2996,19 +2932,19 @@ impl MovieClipStatic { /// Stores the placement settings for display objects during a /// goto command. #[derive(Debug)] -struct GotoPlaceObject { +struct GotoPlaceObject<'a> { /// The frame number that this character was first placed on. frame: FrameNumber, /// The display properties of the object. - place_object: swf::PlaceObject, + place_object: swf::PlaceObject<'a>, /// Increasing index of this place command, for sorting. index: usize, } -impl GotoPlaceObject { +impl<'a> GotoPlaceObject<'a> { fn new( frame: FrameNumber, - mut place_object: swf::PlaceObject, + mut place_object: swf::PlaceObject<'a>, is_rewind: bool, index: usize, ) -> Self { @@ -3063,7 +2999,7 @@ impl GotoPlaceObject { self.place_object.depth.into() } - fn merge(&mut self, next: &mut GotoPlaceObject) { + fn merge(&mut self, next: &mut GotoPlaceObject<'a>) { use swf::PlaceObjectAction; let cur_place = &mut self.place_object; let next_place = &mut next.place_object; @@ -3136,14 +3072,15 @@ impl ClipAction { /// Instead, we have to construct a fake `SwfMovie` just to hold one clip /// action. pub fn from_action_and_movie( - other: swf::ClipAction, + other: swf::ClipAction<'_>, movie: Arc, ) -> impl Iterator { use swf::ClipEventFlag; - let len = other.action_data.len(); let key_code = other.key_code; - let movie = Arc::new(movie.from_movie_and_subdata(other.action_data, &movie)); + let action_data = SwfSlice::from(movie) + .to_unbounded_subslice(other.action_data) + .unwrap(); other.events.into_iter().map(move |event| Self { event: match event { ClipEventFlag::Construct => ClipEvent::Construct, @@ -3170,11 +3107,7 @@ impl ClipAction { ClipEventFlag::ReleaseOutside => ClipEvent::ReleaseOutside, ClipEventFlag::Unload => ClipEvent::Unload, }, - action_data: SwfSlice { - movie: Arc::clone(&movie), - start: 0, - end: len, - }, + action_data: action_data.clone(), }) } } diff --git a/core/src/font.rs b/core/src/font.rs index b87213c27..4e480d04d 100644 --- a/core/src/font.rs +++ b/core/src/font.rs @@ -393,7 +393,7 @@ pub struct FontDescriptor { impl FontDescriptor { /// Obtain a font descriptor from a SWF font tag. pub fn from_swf_tag(val: &swf::Font) -> Self { - let mut name = val.name.clone(); + let mut name = val.name.to_string(); if let Some(first_null) = name.find('\0') { name.truncate(first_null); diff --git a/core/src/html/text_format.rs b/core/src/html/text_format.rs index a9355fd8b..a8551b8b2 100644 --- a/core/src/html/text_format.rs +++ b/core/src/html/text_format.rs @@ -189,7 +189,7 @@ impl TextFormat { /// This requires an `UpdateContext` as we will need to retrieve some font /// information from the actually-referenced font. pub fn from_swf_tag<'gc>( - et: swf::EditText, + et: swf::EditText<'_>, swf_movie: Arc, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Self { @@ -198,7 +198,7 @@ impl TextFormat { let font = et.font_id.and_then(|fid| movie_library.get_font(fid)); let font_class = et .font_class_name - .clone() + .map(str::to_string) .or_else(|| font.map(|font| font.descriptor().class().to_string())) .unwrap_or_else(|| "Times New Roman".to_string()); let align = et.layout.clone().map(|l| l.align); @@ -211,7 +211,7 @@ impl TextFormat { // Times New Roman non-bold, non-italic. This will need to be revised // when we start supporting device fonts. Self { - font: Some(font_class), + font: Some(font_class.to_string()), size: et.height.map(|h| h.to_pixels()), color: et.color, align, diff --git a/core/src/tag_utils.rs b/core/src/tag_utils.rs index 52a988fad..df875e78b 100644 --- a/core/src/tag_utils.rs +++ b/core/src/tag_utils.rs @@ -1,14 +1,13 @@ use crate::backend::navigator::url_from_relative_path; use crate::property_map::PropertyMap; use gc_arena::Collect; -use std::convert::TryInto; use std::path::Path; use std::sync::Arc; use swf::{Header, TagCode}; pub type Error = Box; pub type DecodeResult = Result<(), Error>; -pub type SwfStream = swf::read::Reader>; +pub type SwfStream<'a> = swf::read::Reader<'a>; /// An open, fully parsed SWF movie ready to play back, either in a Player or a /// MovieClip. @@ -75,21 +74,9 @@ impl SwfMovie { /// Construct a movie based on the contents of the SWF datastream. pub fn from_data(swf_data: &[u8], url: Option) -> Result { let swf_stream = swf::read::read_swf_header(&swf_data[..])?; - let header = swf_stream.header; - let mut reader = swf_stream.reader; - - // Decompress the entire SWF in memory. - // Sometimes SWFs will have an incorrectly compressed stream, - // but will otherwise decompress fine up to the End tag. - // So just warn on this case and try to continue gracefully. - let mut data = Vec::with_capacity(header.uncompressed_length.try_into().unwrap()); - if let Err(e) = reader.get_mut().read_to_end(&mut data) { - return Err(format!("Error decompressing SWF, may be corrupt: {}", e).into()); - } - Ok(Self { - header, - data, + header: swf_stream.header, + data: swf_stream.data, url, parameters: PropertyMap::new(), }) @@ -169,20 +156,6 @@ impl SwfSlice { } } - /// Construct a new slice with a given dataset only. - /// - /// This is used primarily for converting owned data back into a slice: we - /// reattach the SWF data to a fresh movie and return a new slice into it. - pub fn owned_subslice(&self, data: Vec, source: &SwfMovie) -> Self { - let len = data.len(); - - Self { - movie: Arc::new(self.movie.from_movie_and_subdata(data, source)), - start: 0, - end: len, - } - } - /// Construct a new SwfSlice from a regular slice. /// /// This function returns None if the given slice is not a subslice of the @@ -232,16 +205,15 @@ impl SwfSlice { /// If the resulting slice would be outside the bounds of the underlying /// movie, or the given reader refers to a different underlying movie, this /// function returns None. - pub fn resize_to_reader(&self, reader: &mut SwfStream<&[u8]>, size: usize) -> Option { - if self.movie.data().as_ptr() as usize <= reader.get_ref().get_ref().as_ptr() as usize - && (reader.get_ref().get_ref().as_ptr() as usize) + pub fn resize_to_reader(&self, reader: &mut SwfStream<'_>, size: usize) -> Option { + if self.movie.data().as_ptr() as usize <= reader.get_ref().as_ptr() as usize + && (reader.get_ref().as_ptr() as usize) < self.movie.data().as_ptr() as usize + self.movie.data().len() { let outer_offset = - reader.get_ref().get_ref().as_ptr() as usize - self.movie.data().as_ptr() as usize; - let inner_offset = reader.get_ref().position() as usize; - let new_start = outer_offset + inner_offset; - let new_end = outer_offset + inner_offset + size; + reader.get_ref().as_ptr() as usize - self.movie.data().as_ptr() as usize; + let new_start = outer_offset; + let new_end = outer_offset + size; let len = self.movie.data().len(); @@ -289,29 +261,26 @@ impl SwfSlice { /// Construct a reader for this slice. /// /// The `from` parameter is the offset to start reading the slice from. - pub fn read_from(&self, from: u64) -> swf::read::Reader> { - let mut cursor = std::io::Cursor::new(self.data()); - cursor.set_position(from); - swf::read::Reader::new(cursor, self.movie.version()) + pub fn read_from(&self, from: u64) -> swf::read::Reader<'_> { + swf::read::Reader::new(&self.data()[from as usize..], self.movie.version()) } } -pub fn decode_tags<'a, R, F>( - reader: &'a mut SwfStream, +pub fn decode_tags<'a, F>( + reader: &mut SwfStream<'a>, mut tag_callback: F, stop_tag: TagCode, ) -> Result<(), Box> where - R: 'a + AsRef<[u8]>, - F: FnMut(&mut SwfStream, TagCode, usize) -> DecodeResult, + F: for<'b> FnMut(&'b mut SwfStream<'a>, TagCode, usize) -> DecodeResult, { - use std::io::{Seek, SeekFrom}; loop { let (tag_code, tag_len) = reader.read_tag_code_and_length()?; - let end_pos = reader.get_ref().position() + tag_len as u64; - let tag = TagCode::from_u16(tag_code); + let tag_slice = &reader.get_ref()[..tag_len]; + let end_slice = &reader.get_ref()[tag_len..]; if let Some(tag) = tag { + *reader.get_mut() = tag_slice; let result = tag_callback(reader, tag, tag_len); if let Err(e) = result { @@ -319,14 +288,14 @@ where } if stop_tag == tag { - reader.get_mut().seek(SeekFrom::Start(end_pos))?; + *reader.get_mut() = end_slice; break; } } else { log::warn!("Unknown tag code: {:?}", tag_code); } - reader.get_mut().seek(SeekFrom::Start(end_pos))?; + *reader.get_mut() = end_slice; } Ok(()) diff --git a/scanner/src/main.rs b/scanner/src/main.rs index ee32dc8d7..c90cdb476 100644 --- a/scanner/src/main.rs +++ b/scanner/src/main.rs @@ -1,7 +1,7 @@ use clap::Clap; use indicatif::{ProgressBar, ProgressStyle}; use path_slash::PathExt; -use ruffle_core::swf::read_swf; +use ruffle_core::swf::{read_swf, read_swf_header}; use serde::Serialize; use std::path::{Path, PathBuf}; @@ -65,7 +65,8 @@ fn scan_file(file: DirEntry, name: String) -> FileResults { } }; - match catch_unwind(|| read_swf(&data[..])) { + let swf_stream = read_swf_header(&data[..]).unwrap(); + match catch_unwind(|| read_swf(&swf_stream)) { Ok(swf) => match swf { Ok(_swf) => FileResults { name, error: None }, Err(e) => FileResults { diff --git a/swf/examples/reading.rs b/swf/examples/reading.rs index 5d8a89aa3..771d76417 100644 --- a/swf/examples/reading.rs +++ b/swf/examples/reading.rs @@ -4,7 +4,8 @@ use std::io::BufReader; fn main() { let file = File::open("tests/swfs/SimpleRedBackground.swf").unwrap(); let reader = BufReader::new(file); - let swf = swf::read_swf(reader).unwrap(); + let swf_stream = swf::read_swf_header(reader).unwrap(); + let swf = swf::read_swf(&swf_stream).unwrap(); println!("The SWF has {} frame(s).", swf.header.num_frames); println!("The SWF has {} tag(s).", swf.tags.len()); } diff --git a/swf/src/avm2/read.rs b/swf/src/avm2/read.rs index 97e9a613f..b496b9a26 100644 --- a/swf/src/avm2/read.rs +++ b/swf/src/avm2/read.rs @@ -873,15 +873,12 @@ pub mod tests { pub fn read_abc_from_file(path: &str) -> Vec { use crate::types::Tag; - use std::fs::File; - - let mut file = File::open(path).unwrap(); - let mut data = Vec::new(); - file.read_to_end(&mut data).unwrap(); - let swf = crate::read_swf(&data[..]).unwrap(); + let data = std::fs::read(path).unwrap(); + let swf_stream = crate::read_swf_header(&data[..]).unwrap(); + let swf = crate::read_swf(&swf_stream).unwrap(); for tag in swf.tags { if let Tag::DoAbc(do_abc) = tag { - return do_abc.data; + return do_abc.data.to_vec(); } } panic!("ABC tag not found in {}", path); diff --git a/swf/src/read.rs b/swf/src/read.rs index 791709fe0..951073c7e 100644 --- a/swf/src/read.rs +++ b/swf/src/read.rs @@ -11,7 +11,6 @@ use crate::types::*; use byteorder::{LittleEndian, ReadBytesExt}; use enumset::EnumSet; use std::collections::HashSet; -use std::convert::TryInto; use std::io::{self, Read}; /// Convenience method to parse an SWF. @@ -24,47 +23,26 @@ use std::io::{self, Read}; /// ``` /// # std::env::set_current_dir(env!("CARGO_MANIFEST_DIR")); /// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap(); -/// let swf = swf::read_swf(&data[..]).unwrap(); +/// let stream = swf::read_swf_header(&data[..]).unwrap(); +/// let swf = swf::read_swf(&stream).unwrap(); /// println!("Number of frames: {}", swf.header.num_frames); /// ``` -pub fn read_swf(input: R) -> Result { - let swf_stream = read_swf_header(input)?; - let header = swf_stream.header; - let mut reader = swf_stream.reader; - - // Decompress all of SWF into memory at once. - let mut data = if header.compression == Compression::Lzma { - // TODO: The LZMA decoder is still funky. - // It always errors, and doesn't return all the data if you use read_to_end, - // but read_exact at least returns the data... why? - // Does the decoder need to be flushed somehow? - let mut data = vec![0u8; header.uncompressed_length.try_into().unwrap()]; - let _ = reader.get_mut().read_exact(&mut data); - data - } else { - let mut data = Vec::with_capacity(header.uncompressed_length.try_into().unwrap()); - if let Err(e) = reader.get_mut().read_to_end(&mut data) { - log::error!("Error decompressing SWF, may be corrupt: {}", e); - } - data - }; - let version = header.version; - - // Some SWF streams may not be compressed correctly, - // (e.g. incorrect data length in the stream), so decompressing - // may throw an error even though the data otherwise comes - // through the stream. - // We'll still try to parse what we get if the full decompression fails. - if let Err(e) = reader.get_mut().read_to_end(&mut data) { - log::warn!("Error decompressing SWF stream, may be corrupt: {}", e); - } - if data.len() != header.uncompressed_length.try_into().unwrap() { - log::warn!("SWF length doesn't match header, may be corrupt"); - } - let mut reader = Reader::new(&data[..], version); +pub fn read_swf<'a>(swf_stream: &'a SwfStream) -> Result> { + // // Some SWF streams may not be compressed correctly, + // // (e.g. incorrect data length in the stream), so decompressing + // // may throw an error even though the data otherwise comes + // // through the stream. + // // We'll still try to parse what we get if the full decompression fails. + // if let Err(e) = reader.get_mut().read_to_end(&mut data) { + // log::warn!("Error decompressing SWF stream, may be corrupt: {}", e); + // } + // if data.len() != header.uncompressed_length.try_into().unwrap() { + // log::warn!("SWF length doesn't match header, may be corrupt"); + // } + let mut reader = Reader::new(&swf_stream.data[..], swf_stream.header.version); Ok(Swf { - header, + header: swf_stream.header.clone(), tags: reader.read_tag_list()?, }) } @@ -81,15 +59,19 @@ pub fn read_swf(input: R) -> Result { /// let swf_stream = swf::read_swf_header(&data[..]).unwrap(); /// println!("FPS: {}", swf_stream.header.frame_rate); /// ``` -pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result> { +pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result { // Read SWF header. - let compression = Reader::read_compression_type(&mut input)?; + let compression = read_compression_type(&mut input)?; let version = input.read_u8()?; let uncompressed_length = input.read_u32::()?; // Now the SWF switches to a compressed stream. - let decompressed_input: Box = match compression { - Compression::None => Box::new(input), + let decompressed_input: Vec = match compression { + Compression::None => { + let mut data = Vec::with_capacity(uncompressed_length as usize); + input.read_to_end(&mut data)?; + data + } Compression::Zlib => { if version < 6 { log::warn!( @@ -97,7 +79,10 @@ pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result> version ); } - make_zlib_reader(input)? + let mut reader = make_zlib_reader(input)?; + let mut data = Vec::with_capacity(uncompressed_length as usize); + reader.read_to_end(&mut data)?; + data } Compression::Lzma => { if version < 13 { @@ -108,11 +93,14 @@ pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result> } // Uncompressed length includes the 4-byte header and 4-byte uncompressed length itself, // subtract it here. - make_lzma_reader(input, uncompressed_length - 8)? + let mut reader = make_lzma_reader(input, uncompressed_length - 8)?; + let mut data = Vec::with_capacity(uncompressed_length as usize); + reader.read_to_end(&mut data)?; + data } }; - let mut reader = Reader::new(decompressed_input, version); + let mut reader = Reader::new(&decompressed_input, version); let stage_size = reader.read_rectangle()?; let frame_rate = reader.read_fixed8()?; let num_frames = reader.read_u16()?; @@ -124,7 +112,8 @@ pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result> frame_rate, num_frames, }; - Ok(SwfStream { header, reader }) + let data = reader.get_ref().to_vec(); + Ok(SwfStream { header, data }) } #[cfg(feature = "flate2")] @@ -271,23 +260,15 @@ pub trait SwfRead { // TODO: Verify ANSI for SWF 5 and earlier. String::from_utf8(bytes).map_err(|_| Error::invalid_data("Invalid string data")) } - - fn bits(&mut self) -> BitReader<&mut R> { - BitReader { - input: self.get_inner(), - byte: 0, - bit_index: 0, - } - } } -pub struct BitReader { - input: R, +pub struct BitReader<'a, 'b> { + input: &'b mut &'a [u8], byte: u8, bit_index: u8, } -impl BitReader { +impl<'a, 'b> BitReader<'a, 'b> { #[inline] fn byte_align(&mut self) { self.bit_index = 0; @@ -335,18 +316,18 @@ impl BitReader { } #[inline] - fn reader(&mut self) -> &mut R { + fn reader(&mut self) -> &mut &'a [u8] { &mut self.input } } -pub struct Reader { - input: R, +pub struct Reader<'a> { + input: &'a [u8], version: u8, } -impl SwfRead for Reader { - fn get_inner(&mut self) -> &mut R { +impl<'a> SwfRead<&'a [u8]> for Reader<'a> { + fn get_inner(&mut self) -> &mut &'a [u8] { &mut self.input } @@ -383,8 +364,8 @@ impl SwfRead for Reader { } } -impl Reader { - pub fn new(input: R, version: u8) -> Reader { +impl<'a> Reader<'a> { + pub fn new(input: &'a [u8], version: u8) -> Reader<'a> { Reader { input, version } } @@ -393,14 +374,69 @@ impl Reader { } /// Returns a reference to the underlying `Reader`. - pub fn get_ref(&self) -> &R { - &self.input + pub fn get_ref(&self) -> &'a [u8] { + self.input + } + + fn bits<'b>(&'b mut self) -> BitReader<'a, 'b> { + BitReader { + input: self.get_inner(), + byte: 0, + bit_index: 0, + } + } + + fn read_slice(&mut self, len: usize) -> io::Result<&'a [u8]> { + if self.input.len() >= len { + let slice = &self.input[..len]; + self.input = &self.input[len..]; + Ok(slice) + } else { + Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "Not enough data for slice", + )) + } + } + + fn read_slice_to_end(&mut self) -> &'a [u8] { + let len = self.input.len(); + let slice = &self.input[..len]; + self.input = &self.input[len..]; + slice + } + + fn read_string(&mut self) -> io::Result> { + let mut pos = 0; + loop { + let byte = *self.input.get(pos).ok_or(io::Error::new( + io::ErrorKind::UnexpectedEof, + "Not enough data for slice", + ))?; + if byte == 0 { + break; + } + pos += 1; + } + // TODO: There is probably a better way to do this. + // TODO: Verify ANSI for SWF 5 and earlier. + let s = unsafe { std::str::from_utf8_unchecked(&self.input.get_unchecked(..pos)) }; + self.input = &self.input[pos + 1..]; + Ok(s) + } + + fn read_string_with_len(&mut self, len: usize) -> io::Result> { + // TODO: There is probably a better way to do this. + // TODO: Verify ANSI for SWF 5 and earlier. + let mut s = unsafe { std::str::from_utf8_unchecked(&self.read_slice(len)?) }; + s = s.trim_end_matches('\0'); + Ok(s) } /// Returns a mutable reference to the underlying `Reader`. /// /// Reading from this reference is not recommended. - pub fn get_mut(&mut self) -> &mut R { + pub fn get_mut(&mut self) -> &mut &'a [u8] { &mut self.input } @@ -410,11 +446,12 @@ impl Reader { /// # std::env::set_current_dir(env!("CARGO_MANIFEST_DIR")); /// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap(); /// let mut swf_stream = swf::read_swf_header(&data[..]).unwrap(); - /// while let Ok(tag) = swf_stream.reader.read_tag() { + /// let mut reader = swf::read::Reader::new(&swf_stream.data[..], swf_stream.header.version); + /// while let Ok(tag) = reader.read_tag() { /// println!("Tag: {:?}", tag); /// } /// ``` - pub fn read_tag(&mut self) -> Result { + pub fn read_tag(&mut self) -> Result> { let (tag_code, length) = self.read_tag_code_and_length()?; let tag = self.read_tag_with_code(tag_code, length); @@ -425,8 +462,8 @@ impl Reader { tag } - fn read_tag_with_code(&mut self, tag_code: u16, length: usize) -> Result { - let mut tag_reader = Reader::new(self.input.by_ref().take(length as u64), self.version); + fn read_tag_with_code(&mut self, tag_code: u16, length: usize) -> Result> { + let mut tag_reader = Reader::new(self.read_slice(length)?, self.version); use crate::tag_code::TagCode; let tag = match TagCode::from_u16(tag_code) { Some(TagCode::End) => Tag::End, @@ -435,20 +472,17 @@ impl Reader { Some(TagCode::DefineBinaryData) => { let id = tag_reader.read_u16()?; tag_reader.read_u32()?; // Reserved - let mut data = Vec::with_capacity(length - 6); - tag_reader.input.read_to_end(&mut data)?; + let data = tag_reader.read_slice_to_end(); Tag::DefineBinaryData { id, data } } Some(TagCode::DefineBits) => { let id = tag_reader.read_u16()?; - let mut jpeg_data = Vec::with_capacity(length - 2); - tag_reader.input.read_to_end(&mut jpeg_data)?; + let jpeg_data = tag_reader.read_slice_to_end(); Tag::DefineBits { id, jpeg_data } } Some(TagCode::DefineBitsJpeg2) => { let id = tag_reader.read_u16()?; - let mut jpeg_data = Vec::with_capacity(length - 2); - tag_reader.input.read_to_end(&mut jpeg_data)?; + let jpeg_data = tag_reader.read_slice_to_end(); Tag::DefineBitsJpeg2 { id, jpeg_data } } Some(TagCode::DefineBitsJpeg3) => tag_reader.read_define_bits_jpeg_3(3)?, @@ -503,28 +537,26 @@ impl Reader { Some(TagCode::EnableTelemetry) => { tag_reader.read_u16()?; // Reserved let password_hash = if length > 2 { - let mut data = vec![0; 32]; - tag_reader.input.read_exact(&mut data)?; - data + tag_reader.read_slice(32)? } else { - vec![] + &[] }; Tag::EnableTelemetry { password_hash } } Some(TagCode::ImportAssets) => { - let url = tag_reader.read_c_string()?; + let url = tag_reader.read_string()?; let num_imports = tag_reader.read_u16()?; let mut imports = Vec::with_capacity(num_imports as usize); for _ in 0..num_imports { imports.push(ExportedAsset { id: tag_reader.read_u16()?, - name: tag_reader.read_c_string()?, + name: tag_reader.read_string()?, }); } Tag::ImportAssets { url, imports } } Some(TagCode::ImportAssets2) => { - let url = tag_reader.read_c_string()?; + let url = tag_reader.read_string()?; tag_reader.read_u8()?; // Reserved; must be 1 tag_reader.read_u8()?; // Reserved; must be 0 let num_imports = tag_reader.read_u16()?; @@ -532,31 +564,23 @@ impl Reader { for _ in 0..num_imports { imports.push(ExportedAsset { id: tag_reader.read_u16()?, - name: tag_reader.read_c_string()?, + name: tag_reader.read_string()?, }); } Tag::ImportAssets { url, imports } } Some(TagCode::JpegTables) => { - let mut data = Vec::with_capacity(length); - tag_reader.input.read_to_end(&mut data)?; + let data = tag_reader.read_slice_to_end(); Tag::JpegTables(data) } - Some(TagCode::Metadata) => { - let mut s = String::with_capacity(length); - tag_reader.get_mut().read_to_string(&mut s)?; - // Remove trailing null bytes. There may or may not be a null byte. - s = s.trim_end_matches(char::from(0)).to_string(); - Tag::Metadata(s) - } + Some(TagCode::Metadata) => Tag::Metadata(tag_reader.read_string()?), Some(TagCode::SetBackgroundColor) => Tag::SetBackgroundColor(tag_reader.read_rgb()?), Some(TagCode::SoundStreamBlock) => { - let mut data = Vec::with_capacity(length); - tag_reader.input.read_to_end(&mut data)?; + let data = tag_reader.read_slice_to_end(); Tag::SoundStreamBlock(data) } @@ -572,7 +596,7 @@ impl Reader { Some(TagCode::StartSound) => Tag::StartSound(tag_reader.read_start_sound_1()?), Some(TagCode::StartSound2) => Tag::StartSound2 { - class_name: tag_reader.read_c_string()?, + class_name: tag_reader.read_string()?, sound_info: Box::new(tag_reader.read_sound_info()?), }, @@ -592,9 +616,8 @@ impl Reader { Some(TagCode::DoAbc) => { let flags = tag_reader.read_u32()?; - let name = tag_reader.read_c_string()?; - let mut abc_data = Vec::with_capacity(length - 4 - name.len()); - tag_reader.input.read_to_end(&mut abc_data)?; + let name = tag_reader.read_string()?; + let abc_data = tag_reader.read_slice_to_end(); Tag::DoAbc(DoAbc { name, is_lazy_initialize: flags & 1 != 0, @@ -603,22 +626,20 @@ impl Reader { } Some(TagCode::DoAction) => { - let mut action_data = Vec::with_capacity(length); - tag_reader.input.read_to_end(&mut action_data)?; + let action_data = tag_reader.read_slice_to_end(); Tag::DoAction(action_data) } Some(TagCode::DoInitAction) => { let id = tag_reader.read_u16()?; - let mut action_data = Vec::with_capacity(length); - tag_reader.input.read_to_end(&mut action_data)?; + let action_data = tag_reader.read_slice_to_end(); Tag::DoInitAction { id, action_data } } - Some(TagCode::EnableDebugger) => Tag::EnableDebugger(tag_reader.read_c_string()?), + Some(TagCode::EnableDebugger) => Tag::EnableDebugger(tag_reader.read_string()?), Some(TagCode::EnableDebugger2) => { tag_reader.read_u16()?; // Reserved - Tag::EnableDebugger(tag_reader.read_c_string()?) + Tag::EnableDebugger(tag_reader.read_string()?) } Some(TagCode::ScriptLimits) => Tag::ScriptLimits { @@ -637,7 +658,7 @@ impl Reader { for _ in 0..num_symbols { symbols.push(SymbolClassLink { id: tag_reader.read_u16()?, - class_name: tag_reader.read_c_string()?, + class_name: tag_reader.read_string()?, }); } Tag::SymbolClass(symbols) @@ -652,7 +673,7 @@ impl Reader { Some(TagCode::Protect) => { Tag::Protect(if length > 0 { tag_reader.read_u16()?; // TODO(Herschel): Two null bytes? Not specified in SWF19. - Some(tag_reader.read_c_string()?) + Some(tag_reader.read_string()?) } else { None }) @@ -664,14 +685,7 @@ impl Reader { Some(TagCode::FrameLabel) => Tag::FrameLabel(tag_reader.read_frame_label(length)?), - Some(TagCode::DefineSprite) => { - // TODO: There's probably a better way to prevent the infinite type recursion. - // Tags can only be nested one level deep, so perhaps I can implement - // read_tag_list for Reader> to enforce this. - let mut sprite_reader = - Reader::new(&mut tag_reader.input as &mut dyn Read, self.version); - sprite_reader.read_define_sprite()? - } + Some(TagCode::DefineSprite) => tag_reader.read_define_sprite()?, Some(TagCode::PlaceObject) => { Tag::PlaceObject(Box::new(tag_reader.read_place_object(length)?)) @@ -693,14 +707,12 @@ impl Reader { Some(TagCode::VideoFrame) => tag_reader.read_video_frame()?, Some(TagCode::ProductInfo) => Tag::ProductInfo(tag_reader.read_product_info()?), _ => { - let size = length as usize; - let mut data = vec![0; size]; - tag_reader.input.read_exact(&mut data[..])?; + let data = tag_reader.read_slice_to_end(); Tag::Unknown { tag_code, data } } }; - if tag_reader.read_u8().is_ok() { + if !tag_reader.input.is_empty() { // There should be no data remaining in the tag if we read it correctly. // If there is data remaining, the most likely scenario is we screwed up parsing. // But sometimes tools will export SWF tags that are larger than they should be. @@ -711,26 +723,11 @@ impl Reader { TagCode::name(tag_code), tag_code ); - - // Discard the rest of the data. - let _ = std::io::copy(&mut tag_reader.get_mut(), &mut io::sink()); } Ok(tag) } - pub fn read_compression_type(mut input: R) -> Result { - let mut signature = [0u8; 3]; - input.read_exact(&mut signature)?; - let compression = match &signature { - b"FWS" => Compression::None, - b"CWS" => Compression::Zlib, - b"ZWS" => Compression::Lzma, - _ => return Err(Error::invalid_data("Invalid SWF")), - }; - Ok(compression) - } - pub fn read_rectangle(&mut self) -> Result { let mut bits = self.bits(); let num_bits: u32 = bits.read_ubits(5)?; @@ -866,7 +863,7 @@ impl Reader { }) } - fn read_tag_list(&mut self) -> Result> { + fn read_tag_list(&mut self) -> Result>> { let mut tags = Vec::new(); loop { match self.read_tag() { @@ -892,14 +889,13 @@ impl Reader { Ok((tag_code, length)) } - pub fn read_define_button_1(&mut self) -> Result