diff --git a/core/src/avm2/globals/flash/utils/bytearray.rs b/core/src/avm2/globals/flash/utils/bytearray.rs index d974ba864..6d3f8d221 100644 --- a/core/src/avm2/globals/flash/utils/bytearray.rs +++ b/core/src/avm2/globals/flash/utils/bytearray.rs @@ -7,6 +7,7 @@ use crate::avm2::object::{bytearray_allocator, Object, TObject}; use crate::avm2::string::AvmString; use crate::avm2::value::Value; use crate::avm2::Error; +use crate::character::Character; use encoding_rs::Encoding; use encoding_rs::UTF_8; use gc_arena::{GcCell, MutationContext}; @@ -20,6 +21,27 @@ pub fn instance_init<'gc>( ) -> Result, Error> { if let Some(this) = this { activation.super_init(this, &[])?; + + let class_object = this + .as_class_object() + .ok_or("Attempted to construct non-instance ByteArray")?; + if let Some((movie, id)) = activation + .context + .library + .avm2_class_registry() + .class_symbol(class_object) + { + if let Some(lib) = activation.context.library.library_for_movie(movie) { + if let Some(Character::BinaryData(binary_data)) = lib.character_by_id(id) { + let mut byte_array = this + .as_bytearray_mut(activation.context.gc_context) + .ok_or_else(|| "Unable to get bytearray storage".to_string())?; + byte_array.clear(); + byte_array.write_bytes(binary_data.as_ref())?; + byte_array.set_position(0); + } + } + } } Ok(Value::Undefined) diff --git a/core/src/binary_data.rs b/core/src/binary_data.rs new file mode 100644 index 000000000..9f5560060 --- /dev/null +++ b/core/src/binary_data.rs @@ -0,0 +1,10 @@ +use crate::tag_utils::{SwfMovie, SwfSlice}; +use std::sync::Arc; + +pub type BinaryData = SwfSlice; + +impl BinaryData { + pub fn from_swf_tag(movie: Arc, tag: &swf::DefineBinaryData) -> Self { + SwfSlice::from(movie).to_subslice(tag.data).unwrap() + } +} diff --git a/core/src/character.rs b/core/src/character.rs index 064793808..ef5989217 100644 --- a/core/src/character.rs +++ b/core/src/character.rs @@ -1,4 +1,5 @@ use crate::backend::audio::SoundHandle; +use crate::binary_data::BinaryData; use crate::display_object::{ Avm1Button, Avm2Button, Bitmap, EditText, Graphic, MorphShape, MovieClip, Text, Video, }; @@ -19,4 +20,5 @@ pub enum Character<'gc> { Text(Text<'gc>), Sound(#[collect(require_static)] SoundHandle), Video(Video<'gc>), + BinaryData(BinaryData), } diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index c2e8d9f9b..1c13ffb5f 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -12,6 +12,7 @@ use crate::backend::ui::MouseCursor; use bitflags::bitflags; use crate::avm1::activation::{Activation as Avm1Activation, ActivationIdentifier}; +use crate::binary_data::BinaryData; use crate::character::Character; use crate::context::{ActionType, RenderContext, UpdateContext}; use crate::display_object::container::{ @@ -465,6 +466,10 @@ impl<'gc> MovieClip<'gc> { tag_len, ) } + TagCode::DefineBinaryData => self + .0 + .write(context.gc_context) + .define_binary_data(context, reader), _ => Ok(()), }; let _ = tag_utils::decode_tags(&mut reader, tag_callback, TagCode::End); @@ -602,14 +607,21 @@ impl<'gc> MovieClip<'gc> { if id == 0 { //TODO: This assumes only the root movie has `SymbolClass` tags. self.set_avm2_class(activation.context.gc_context, Some(class_object)); - } else if let Some(Character::MovieClip(mc)) = library.character_by_id(id) { - mc.set_avm2_class(activation.context.gc_context, Some(class_object)); } else { - log::warn!( - "Symbol class {} cannot be assigned to invalid character id {}", - class_name, - id - ); + match library.character_by_id(id) { + Some(Character::MovieClip(mc)) => mc.set_avm2_class( + activation.context.gc_context, + Some(class_object), + ), + Some(Character::BinaryData(_)) => {} + _ => { + log::warn!( + "Symbol class {} cannot be assigned to invalid character id {}", + class_name, + id + ); + } + } } } Err(e) => log::warn!( @@ -2986,6 +2998,21 @@ impl<'gc, 'a> MovieClipData<'gc> { Ok(()) } + #[inline] + fn define_binary_data( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + reader: &mut SwfStream<'a>, + ) -> DecodeResult { + let tag_data = reader.read_define_binary_data()?; + let binary_data = BinaryData::from_swf_tag(self.movie(), &tag_data); + context + .library + .library_for_movie_mut(self.movie()) + .register_character(tag_data.id, Character::BinaryData(binary_data)); + Ok(()) + } + #[inline] fn script_limits(&mut self, reader: &mut SwfStream<'a>, avm: &mut Avm1<'gc>) -> DecodeResult { let max_recursion_depth = reader.read_u16()?; diff --git a/core/src/lib.rs b/core/src/lib.rs index b691cc530..a900c28c6 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -24,6 +24,7 @@ extern crate num_derive; #[macro_use] mod avm1; mod avm2; +mod binary_data; pub mod bitmap; mod bounding_box; mod character; diff --git a/swf/src/read.rs b/swf/src/read.rs index 41c596a31..92270e96c 100644 --- a/swf/src/read.rs +++ b/swf/src/read.rs @@ -363,10 +363,7 @@ impl<'a> Reader<'a> { TagCode::ShowFrame => Tag::ShowFrame, TagCode::CsmTextSettings => Tag::CsmTextSettings(tag_reader.read_csm_text_settings()?), TagCode::DefineBinaryData => { - let id = tag_reader.read_u16()?; - tag_reader.read_u32()?; // Reserved - let data = tag_reader.read_slice_to_end(); - Tag::DefineBinaryData { id, data } + Tag::DefineBinaryData(tag_reader.read_define_binary_data()?) } TagCode::DefineBits => { let id = tag_reader.read_u16()?; @@ -2342,6 +2339,13 @@ impl<'a> Reader<'a> { }) } + pub fn read_define_binary_data(&mut self) -> Result> { + let id = self.read_u16()?; + self.read_u32()?; // Reserved + let data = self.read_slice_to_end(); + Ok(DefineBinaryData { id, data }) + } + pub fn read_define_text(&mut self, version: u8) -> Result { let id = self.read_character_id()?; let bounds = self.read_rectangle()?; diff --git a/swf/src/test_data.rs b/swf/src/test_data.rs index 9f67ba1e1..dda10ccdb 100644 --- a/swf/src/test_data.rs +++ b/swf/src/test_data.rs @@ -41,10 +41,10 @@ pub fn tag_tests() -> Vec { ), ( 9, // Minimum version not listed in SWF19. - Tag::DefineBinaryData { + Tag::DefineBinaryData(DefineBinaryData { id: 1, data: &[84, 101, 115, 116, 105, 110, 103, 33], - }, + }), read_tag_bytes_from_file("tests/swfs/DefineBinaryData.swf", TagCode::DefineBinaryData), ), ( diff --git a/swf/src/types.rs b/swf/src/types.rs index ce3f258aa..61904bd71 100644 --- a/swf/src/types.rs +++ b/swf/src/types.rs @@ -786,10 +786,7 @@ pub enum Tag<'a> { Protect(Option<&'a SwfStr>), CsmTextSettings(CsmTextSettings), DebugId(DebugId), - DefineBinaryData { - id: CharacterId, - data: &'a [u8], - }, + DefineBinaryData(DefineBinaryData<'a>), DefineBits { id: CharacterId, jpeg_data: &'a [u8], @@ -1301,6 +1298,12 @@ pub struct FontInfo<'a> { pub code_table: Vec, } +#[derive(Clone, Debug, PartialEq)] +pub struct DefineBinaryData<'a> { + pub id: CharacterId, + pub data: &'a [u8], +} + #[derive(Clone, Debug, PartialEq)] pub struct Text { pub id: CharacterId, diff --git a/swf/src/write.rs b/swf/src/write.rs index 0ca9e7366..a1e4b041b 100644 --- a/swf/src/write.rs +++ b/swf/src/write.rs @@ -499,12 +499,7 @@ impl Writer { self.write_u8(0)?; // Reserved (0). } - Tag::DefineBinaryData { id, data } => { - self.write_tag_header(TagCode::DefineBinaryData, data.len() as u32 + 6)?; - self.write_u16(id)?; - self.write_u32(0)?; // Reserved - self.output.write_all(data)?; - } + Tag::DefineBinaryData(ref binary_data) => self.write_define_binary_data(binary_data)?, Tag::DefineBits { id, jpeg_data } => { self.write_tag_header(TagCode::DefineBits, jpeg_data.len() as u32 + 2)?; @@ -2284,6 +2279,14 @@ impl Writer { Ok(()) } + fn write_define_binary_data(&mut self, binary_data: &DefineBinaryData) -> Result<()> { + self.write_tag_header(TagCode::DefineBinaryData, binary_data.data.len() as u32 + 6)?; + self.write_u16(binary_data.id)?; + self.write_u32(0)?; // Reserved + self.output.write_all(binary_data.data)?; + Ok(()) + } + fn write_define_text(&mut self, text: &Text) -> Result<()> { let mut buf = Vec::new(); { diff --git a/tests/tests/regression_tests.rs b/tests/tests/regression_tests.rs index 32343aff9..b4efe34c4 100644 --- a/tests/tests/regression_tests.rs +++ b/tests/tests/regression_tests.rs @@ -621,6 +621,7 @@ swf_tests! { (as3_istypelate_coerce, "avm2/istypelate_coerce", 1), (as3_class_cast_call, "avm2/class_cast_call", 1), (as3_class_supercalls_mismatched, "avm2/class_supercalls_mismatched", 1), + (as3_symbol_class_binary_data, "avm2/symbol_class_binary_data", 1), } // TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough. diff --git a/tests/tests/swfs/avm2/symbol_class_binary_data/Test.as b/tests/tests/swfs/avm2/symbol_class_binary_data/Test.as new file mode 100644 index 000000000..ba337b708 --- /dev/null +++ b/tests/tests/swfs/avm2/symbol_class_binary_data/Test.as @@ -0,0 +1,34 @@ +package { + import flash.display.Sprite; + import flash.utils.ByteArray; + + public class TestArray extends ByteArray { + public function TestArray() { + } + } + + public class TestArray2 extends ByteArray { + public function TestArray2() { + } + } + + public class Test extends Sprite { + public function Test() + { + super(); + var bytearr:* = new TestArray(); + trace("ByteArray = "); + trace(bytearr); + trace("ByteArray Position = "); + trace(bytearr.position); + + var bytearr2:* = new TestArray2(); + trace("ByteArray2 = "); + trace(bytearr2); + trace("ByteArray2 Position = "); + trace(bytearr.position); + } + } +} + + diff --git a/tests/tests/swfs/avm2/symbol_class_binary_data/output.txt b/tests/tests/swfs/avm2/symbol_class_binary_data/output.txt new file mode 100644 index 000000000..b2bf0c3bb --- /dev/null +++ b/tests/tests/swfs/avm2/symbol_class_binary_data/output.txt @@ -0,0 +1,8 @@ +ByteArray = +TestArrayContent +ByteArray Position = +0 +ByteArray2 = +TestArray2Content +ByteArray2 Position = +0 diff --git a/tests/tests/swfs/avm2/symbol_class_binary_data/test.swf b/tests/tests/swfs/avm2/symbol_class_binary_data/test.swf new file mode 100644 index 000000000..e06f5c78a Binary files /dev/null and b/tests/tests/swfs/avm2/symbol_class_binary_data/test.swf differ