diff --git a/core/src/avm1/activation.rs b/core/src/avm1/activation.rs index 8b532f580cd6..f2304c65888d 100644 --- a/core/src/avm1/activation.rs +++ b/core/src/avm1/activation.rs @@ -22,9 +22,13 @@ use std::borrow::Cow; use std::cell::{Ref, RefMut}; use std::convert::TryFrom; use std::fmt; +use swf::avm1::opcode::OpCode; use swf::avm1::read::Reader; -use swf::avm1::types::{Action, CatchVar, Function, TryBlock}; -use swf::SwfStr; +use swf::avm1::types::{ + CatchVar, DefineFunction, DefineFunction2, GetUrl, GetUrl2, GotoFrame2, RegisterIndex, + SendVarsMethod, TryBlock, Value as SwfValue, WaitForFrame, WaitForFrame2, +}; +use swf::{FrameNumber, SwfStr}; use url::form_urlencoded; macro_rules! avm_debug { @@ -54,22 +58,22 @@ impl<'gc> RegisterSet<'gc> { /// Create a new register set with a given number of specified registers. /// /// The given registers will be set to `undefined`. - pub fn new(num: u8) -> Self { + pub fn new(num: RegisterIndex) -> Self { Self(smallvec![Value::Undefined; num as usize]) } /// Return a reference to a given register, if it exists. - pub fn get(&self, num: u8) -> Option<&Value<'gc>> { + pub fn get(&self, num: RegisterIndex) -> Option<&Value<'gc>> { self.0.get(num as usize) } /// Return a mutable reference to a given register, if it exists. - pub fn get_mut(&mut self, num: u8) -> Option<&mut Value<'gc>> { + pub fn get_mut(&mut self, num: RegisterIndex) -> Option<&mut Value<'gc>> { self.0.get_mut(num as usize) } - pub fn len(&self) -> u8 { - self.0.len() as u8 + pub fn len(&self) -> RegisterIndex { + self.0.len() as RegisterIndex } } @@ -459,153 +463,212 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { if reader.get_ref().as_ptr() as usize >= data.as_ref().as_ptr_range().end as usize { //Executing beyond the end of a function constitutes an implicit return. Ok(FrameControl::Return(ReturnType::Implicit)) - } else if let Some(action) = reader.read_action()? { - avm_debug!( - self.context.avm1, - "({}) Action: {:?}", - self.id.depth(), - action - ); + } else { + // TODO(madsmtm): Duplicates a lot of stuff in reader.read_action - clean this up! + let (opcode, mut length) = reader.read_opcode_and_length()?; + let start = reader.get_ref(); + + // TODO(madsmtm): Clean this up and remove this parameter + let mut should_check_length = true; + + let framecontrol = if let Some(opcode) = OpCode::from_u8(opcode) { + // We parse opcodes directly to avoid the performance hit that creating an + // Action enum causes. Additionally, this makes it easier to avoid allocations + self.dispatch_from_opcode(data, reader, &mut length, &mut should_check_length, opcode) + } else { + reader + .read_unknown_action(length) + .map_err(|e| e.into()) + .and_then(|data| self.unknown_op(opcode, data)) + }; - match action { - Action::Add => self.action_add(), - Action::Add2 => self.action_add_2(), - Action::And => self.action_and(), - Action::AsciiToChar => self.action_ascii_to_char(), - Action::BitAnd => self.action_bit_and(), - Action::BitLShift => self.action_bit_lshift(), - Action::BitOr => self.action_bit_or(), - Action::BitRShift => self.action_bit_rshift(), - Action::BitURShift => self.action_bit_urshift(), - Action::BitXor => self.action_bit_xor(), - Action::Call => self.action_call(), - Action::CallFunction => self.action_call_function(), - Action::CallMethod => self.action_call_method(), - Action::CastOp => self.action_cast_op(), - Action::CharToAscii => self.action_char_to_ascii(), - Action::CloneSprite => self.action_clone_sprite(), - Action::ConstantPool(constant_pool) => { - self.action_constant_pool(&constant_pool[..]) + let framecontrol = match framecontrol { + Err(Error::InvalidSwf(e)) => { + Err(swf::error::Error::avm1_parse_error_with_source(opcode, e).into()) } - Action::Decrement => self.action_decrement(), - Action::DefineFunction { + _ => framecontrol, + }; + + // Verify that we parsed the correct amount of data. + let end_pos = (start.as_ptr() as usize + length) as *const u8; + if should_check_length && reader.get_ref().as_ptr() != end_pos { + // We incorrectly parsed this action. + // Re-sync to the expected end of the action and throw an error. + *reader.get_mut() = &start[length.min(start.len())..]; + return Err(swf::error::Error::avm1_parse_error(opcode).into()); + } + framecontrol + } + } + + #[inline] + fn dispatch_from_opcode<'b>( + &mut self, + data: &'b SwfSlice, + reader: &mut Reader<'b>, + length: &mut usize, + should_check_length: &mut bool, + opcode: OpCode, + ) -> Result, Error<'gc>> { + match opcode { + OpCode::Add => self.action_add(), + OpCode::Add2 => self.action_add_2(), + OpCode::And => self.action_and(), + OpCode::AsciiToChar => self.action_ascii_to_char(), + OpCode::BitAnd => self.action_bit_and(), + OpCode::BitLShift => self.action_bit_lshift(), + OpCode::BitOr => self.action_bit_or(), + OpCode::BitRShift => self.action_bit_rshift(), + OpCode::BitURShift => self.action_bit_urshift(), + OpCode::BitXor => self.action_bit_xor(), + OpCode::Call => self.action_call(), + OpCode::CallFunction => self.action_call_function(), + OpCode::CallMethod => self.action_call_method(), + OpCode::CastOp => self.action_cast_op(), + OpCode::CharToAscii => self.action_char_to_ascii(), + OpCode::CloneSprite => self.action_clone_sprite(), + OpCode::ConstantPool => self.action_constant_pool(reader), + OpCode::Decrement => self.action_decrement(), + OpCode::DefineFunction => { + let DefineFunction { name, params, actions, - } => self.action_define_function( + } = reader.read_define_function(length)?; + self.action_define_function( name, ¶ms[..], data.to_unbounded_subslice(actions).unwrap(), - ), - Action::DefineFunction2(func) => self.action_define_function_2(&func, &data), - Action::DefineLocal => self.action_define_local(), - Action::DefineLocal2 => self.action_define_local_2(), - Action::Delete => self.action_delete(), - Action::Delete2 => self.action_delete_2(), - Action::Divide => self.action_divide(), - Action::EndDrag => self.action_end_drag(), - Action::Enumerate => self.action_enumerate(), - Action::Enumerate2 => self.action_enumerate_2(), - Action::Equals => self.action_equals(), - Action::Equals2 => self.action_equals_2(), - Action::Extends => self.action_extends(), - Action::GetMember => self.action_get_member(), - Action::GetProperty => self.action_get_property(), - Action::GetTime => self.action_get_time(), - Action::GetVariable => self.action_get_variable(), - Action::GetUrl { url, target } => self.action_get_url(url, target), - Action::GetUrl2 { + ) + } + OpCode::DefineFunction2 => { + self.action_define_function_2(&reader.read_define_function_2(length)?, &data) + } + OpCode::DefineLocal => self.action_define_local(), + OpCode::DefineLocal2 => self.action_define_local_2(), + OpCode::Delete => self.action_delete(), + OpCode::Delete2 => self.action_delete_2(), + OpCode::Divide => self.action_divide(), + OpCode::End => Ok(FrameControl::Return(ReturnType::Implicit)), + OpCode::EndDrag => self.action_end_drag(), + OpCode::Enumerate => self.action_enumerate(), + OpCode::Enumerate2 => self.action_enumerate_2(), + OpCode::Equals => self.action_equals(), + OpCode::Equals2 => self.action_equals_2(), + OpCode::Extends => self.action_extends(), + OpCode::GetMember => self.action_get_member(), + OpCode::GetProperty => self.action_get_property(), + OpCode::GetTime => self.action_get_time(), + OpCode::GetVariable => self.action_get_variable(), + OpCode::GetUrl => { + let GetUrl { url, target } = reader.read_get_url()?; + self.action_get_url(url, target) + } + OpCode::GetUrl2 => { + let GetUrl2 { send_vars_method, is_target_sprite, is_load_vars, - } => self.action_get_url_2(send_vars_method, is_target_sprite, is_load_vars), - Action::GotoFrame(frame) => self.action_goto_frame(frame), - Action::GotoFrame2 { + } = reader.read_get_url_2()?; + self.action_get_url_2(send_vars_method, is_target_sprite, is_load_vars) + } + OpCode::GotoFrame => self.action_goto_frame(reader.read_goto_frame()?), + OpCode::GotoFrame2 => { + let GotoFrame2 { set_playing, scene_offset, - } => self.action_goto_frame_2(set_playing, scene_offset), - Action::Greater => self.action_greater(), - Action::GotoLabel(label) => self.action_goto_label(label), - Action::If { offset } => self.action_if(offset, reader, data), - Action::Increment => self.action_increment(), - Action::InitArray => self.action_init_array(), - Action::InitObject => self.action_init_object(), - Action::ImplementsOp => self.action_implements_op(), - Action::InstanceOf => self.action_instance_of(), - Action::Jump { offset } => self.action_jump(offset, reader, data), - Action::Less => self.action_less(), - Action::Less2 => self.action_less_2(), - Action::MBAsciiToChar => self.action_mb_ascii_to_char(), - Action::MBCharToAscii => self.action_mb_char_to_ascii(), - Action::MBStringLength => self.action_mb_string_length(), - Action::MBStringExtract => self.action_mb_string_extract(), - Action::Modulo => self.action_modulo(), - Action::Multiply => self.action_multiply(), - Action::NextFrame => self.action_next_frame(), - Action::NewMethod => self.action_new_method(), - Action::NewObject => self.action_new_object(), - Action::Not => self.action_not(), - Action::Or => self.action_or(), - Action::Play => self.action_play(), - Action::Pop => self.action_pop(), - Action::PreviousFrame => self.action_prev_frame(), - Action::Push(values) => self.action_push(&values[..]), - Action::PushDuplicate => self.action_push_duplicate(), - Action::RandomNumber => self.action_random_number(), - Action::RemoveSprite => self.action_remove_sprite(), - Action::Return => self.action_return(), - Action::SetMember => self.action_set_member(), - Action::SetProperty => self.action_set_property(), - Action::SetTarget(target) => { - self.action_set_target(&target.to_str_lossy(self.encoding())) - } - Action::SetTarget2 => self.action_set_target2(), - Action::SetVariable => self.action_set_variable(), - Action::StackSwap => self.action_stack_swap(), - Action::StartDrag => self.action_start_drag(), - Action::Stop => self.action_stop(), - Action::StopSounds => self.action_stop_sounds(), - Action::StoreRegister(register) => self.action_store_register(register), - Action::StrictEquals => self.action_strict_equals(), - Action::StringAdd => self.action_string_add(), - Action::StringEquals => self.action_string_equals(), - Action::StringExtract => self.action_string_extract(), - Action::StringGreater => self.action_string_greater(), - Action::StringLength => self.action_string_length(), - Action::StringLess => self.action_string_less(), - Action::Subtract => self.action_subtract(), - Action::TargetPath => self.action_target_path(), - Action::ToggleQuality => self.toggle_quality(), - Action::ToInteger => self.action_to_integer(), - Action::ToNumber => self.action_to_number(), - Action::ToString => self.action_to_string(), - Action::Trace => self.action_trace(), - Action::TypeOf => self.action_type_of(), - Action::WaitForFrame { + } = reader.read_goto_frame_2()?; + self.action_goto_frame_2(set_playing, scene_offset) + } + OpCode::Greater => self.action_greater(), + OpCode::GotoLabel => self.action_goto_label(reader.read_goto_label()?), + OpCode::If => { + *should_check_length = false; + self.action_if(reader.read_instruction_offset()?, reader, data) + }, + OpCode::Increment => self.action_increment(), + OpCode::InitArray => self.action_init_array(), + OpCode::InitObject => self.action_init_object(), + OpCode::ImplementsOp => self.action_implements_op(), + OpCode::InstanceOf => self.action_instance_of(), + OpCode::Jump => { + *should_check_length = false; + self.action_jump(reader.read_instruction_offset()?, reader, data) + }, + OpCode::Less => self.action_less(), + OpCode::Less2 => self.action_less_2(), + OpCode::MBAsciiToChar => self.action_mb_ascii_to_char(), + OpCode::MBCharToAscii => self.action_mb_char_to_ascii(), + OpCode::MBStringLength => self.action_mb_string_length(), + OpCode::MBStringExtract => self.action_mb_string_extract(), + OpCode::Modulo => self.action_modulo(), + OpCode::Multiply => self.action_multiply(), + OpCode::NextFrame => self.action_next_frame(), + OpCode::NewMethod => self.action_new_method(), + OpCode::NewObject => self.action_new_object(), + OpCode::Not => self.action_not(), + OpCode::Or => self.action_or(), + OpCode::Play => self.action_play(), + OpCode::Pop => self.action_pop(), + OpCode::PreviousFrame => self.action_prev_frame(), + OpCode::Push => self.action_push(reader, *length), + OpCode::PushDuplicate => self.action_push_duplicate(), + OpCode::RandomNumber => self.action_random_number(), + OpCode::RemoveSprite => self.action_remove_sprite(), + OpCode::Return => self.action_return(), + OpCode::SetMember => self.action_set_member(), + OpCode::SetProperty => self.action_set_property(), + OpCode::SetTarget => { + self.action_set_target(&reader.read_set_target()?.to_str_lossy(self.encoding())) + } + OpCode::SetTarget2 => self.action_set_target2(), + OpCode::SetVariable => self.action_set_variable(), + OpCode::StackSwap => self.action_stack_swap(), + OpCode::StartDrag => self.action_start_drag(), + OpCode::Stop => self.action_stop(), + OpCode::StopSounds => self.action_stop_sounds(), + OpCode::StoreRegister => self.action_store_register(reader.read_store_register()?), + OpCode::StrictEquals => self.action_strict_equals(), + OpCode::StringAdd => self.action_string_add(), + OpCode::StringEquals => self.action_string_equals(), + OpCode::StringExtract => self.action_string_extract(), + OpCode::StringGreater => self.action_string_greater(), + OpCode::StringLength => self.action_string_length(), + OpCode::StringLess => self.action_string_less(), + OpCode::Subtract => self.action_subtract(), + OpCode::TargetPath => self.action_target_path(), + OpCode::ToggleQuality => self.toggle_quality(), + OpCode::ToInteger => self.action_to_integer(), + OpCode::ToNumber => self.action_to_number(), + OpCode::ToString => self.action_to_string(), + OpCode::Trace => self.action_trace(), + OpCode::TypeOf => self.action_type_of(), + OpCode::WaitForFrame => { + *should_check_length = false; + let WaitForFrame { frame, num_actions_to_skip, - } => self.action_wait_for_frame(frame, num_actions_to_skip, reader), - Action::WaitForFrame2 { + } = reader.read_wait_for_frame()?; + self.action_wait_for_frame(frame, num_actions_to_skip, reader) + } + OpCode::WaitForFrame2 => { + *should_check_length = false; + let WaitForFrame2 { num_actions_to_skip, - } => self.action_wait_for_frame_2(num_actions_to_skip, reader), - Action::With { actions } => { - self.action_with(data.to_unbounded_subslice(actions).unwrap()) - } - Action::Throw => self.action_throw(), - Action::Try(try_block) => self.action_try(&try_block, &data), - _ => self.unknown_op(action), + } = reader.read_wait_for_frame_2()?; + self.action_wait_for_frame_2(num_actions_to_skip, reader) } - } else { - //The explicit end opcode was encountered so return here - Ok(FrameControl::Return(ReturnType::Implicit)) + OpCode::With => self.action_with( + data.to_unbounded_subslice(reader.read_with(length)?) + .unwrap(), + ), + OpCode::Throw => self.action_throw(), + OpCode::Try => self.action_try(&reader.read_try(length)?, &data), } } - fn unknown_op( - &mut self, - action: swf::avm1::types::Action, - ) -> Result, Error<'gc>> { - avm_error!(self, "Unknown AVM1 opcode: {:?}", action); + fn unknown_op(&mut self, opcode: u8, _data: &[u8]) -> Result, Error<'gc>> { + avm_error!(self, "Unknown AVM1 opcode: {:?}", opcode); Ok(FrameControl::Continue) } @@ -786,7 +849,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { if let Some((clip, frame)) = call_frame { if frame <= u32::from(u16::MAX) { - for action in clip.actions_on_frame(&mut self.context, frame as u16) { + for action in clip.actions_on_frame(&mut self.context, frame as FrameNumber) { let _ = self.run_child_frame_for_action( "[Frame Call]", clip.into(), @@ -888,18 +951,18 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { fn action_constant_pool( &mut self, - constant_pool: &[&'_ SwfStr], + reader: &mut Reader, ) -> Result, Error<'gc>> { - self.context.avm1.constant_pool = GcCell::allocate( - self.context.gc_context, - constant_pool - .iter() - .map(|s| { + let constant_pool = reader + .read_constant_pool()? + .map(|r| { + r.map(|s| { AvmString::new(self.context.gc_context, s.to_string_lossy(self.encoding())) .into() }) - .collect(), - ); + }) + .collect::>()?; + self.context.avm1.constant_pool = GcCell::allocate(self.context.gc_context, constant_pool); self.set_constant_pool(self.context.avm1.constant_pool); Ok(FrameControl::Continue) @@ -953,7 +1016,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { fn action_define_function_2( &mut self, - action_func: &Function, + action_func: &DefineFunction2, parent_data: &SwfSlice, ) -> Result, Error<'gc>> { let swf_version = self.swf_version(); @@ -1278,7 +1341,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { fn action_get_url_2( &mut self, - swf_method: swf::avm1::types::SendVarsMethod, + swf_method: SendVarsMethod, is_target_sprite: bool, is_load_vars: bool, ) -> Result, Error<'gc>> { @@ -1393,7 +1456,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { Ok(FrameControl::Continue) } - fn action_goto_frame(&mut self, frame: u16) -> Result, Error<'gc>> { + fn action_goto_frame(&mut self, frame: FrameNumber) -> Result, Error<'gc>> { if let Some(clip) = self.target_clip() { if let Some(clip) = clip.as_movie_clip() { // The frame on the stack is 0-based, not 1-based. @@ -1774,24 +1837,25 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { fn action_push( &mut self, - values: &[swf::avm1::types::Value], + reader: &mut Reader, + length: usize, ) -> Result, Error<'gc>> { - for value in values { - use swf::avm1::types::Value as SwfValue; + for value in reader.read_push(length)? { + let value = value?; let value = match value { SwfValue::Undefined => Value::Undefined, SwfValue::Null => Value::Null, - SwfValue::Bool(v) => Value::Bool(*v), - SwfValue::Int(v) => f64::from(*v).into(), - SwfValue::Float(v) => f64::from(*v).into(), - SwfValue::Double(v) => (*v).into(), + SwfValue::Bool(v) => Value::Bool(v), + SwfValue::Int(v) => f64::from(v).into(), + SwfValue::Float(v) => f64::from(v).into(), + SwfValue::Double(v) => v.into(), SwfValue::Str(v) => { AvmString::new(self.context.gc_context, v.to_string_lossy(self.encoding())) .into() } - SwfValue::Register(v) => self.current_register(*v), + SwfValue::Register(v) => self.current_register(v), SwfValue::ConstantPool(i) => { - if let Some(value) = self.constant_pool().read().get(*i as usize) { + if let Some(value) = self.constant_pool().read().get(i as usize) { *value } else { avm_warn!( @@ -2025,7 +2089,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { Ok(FrameControl::Continue) } - fn action_store_register(&mut self, register: u8) -> Result, Error<'gc>> { + fn action_store_register( + &mut self, + register: RegisterIndex, + ) -> Result, Error<'gc>> { // The value must remain on the stack. let val = self.context.avm1.pop(); self.context.avm1.push(val); @@ -2200,7 +2267,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { fn action_wait_for_frame( &mut self, - _frame: u16, + _frame: FrameNumber, num_actions_to_skip: u8, r: &mut Reader<'_>, ) -> Result, Error<'gc>> { @@ -2220,7 +2287,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { r: &mut Reader<'_>, ) -> Result, Error<'gc>> { // TODO(Herschel): Always true for now. - let _frame_num = self.context.avm1.pop().coerce_to_f64(self)? as u16; + let _frame_num = self.context.avm1.pop().coerce_to_f64(self)? as FrameNumber; let loaded = true; if !loaded { // Note that the offset is given in # of actions, NOT in bytes. @@ -2328,7 +2395,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { /// /// If a given register does not exist, this function yields /// Value::Undefined, which is also a valid register value. - pub fn current_register(&self, id: u8) -> Value<'gc> { + pub fn current_register(&self, id: RegisterIndex) -> Value<'gc> { if self.has_local_register(id) { self.local_register(id).unwrap_or(Value::Undefined) } else { @@ -2344,7 +2411,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { /// Set a register to a given value. /// /// If a given register does not exist, this function does nothing. - pub fn set_current_register(&mut self, id: u8, value: Value<'gc>) { + pub fn set_current_register(&mut self, id: RegisterIndex, value: Value<'gc>) { if self.has_local_register(id) { self.set_local_register(id, value); } else if let Some(v) = self.context.avm1.registers.get_mut(id as usize) { @@ -2897,7 +2964,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { /// For SWF version 5 and lower, this is locale-dependent, /// and we default to WINDOWS-1252. pub fn encoding(&self) -> &'static swf::Encoding { - swf::SwfStr::encoding_for_version(self.swf_version) + SwfStr::encoding_for_version(self.swf_version) } /// Returns the SWF version of the action or function being executed. @@ -2955,13 +3022,13 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { } /// Returns true if this activation has a given local register ID. - pub fn has_local_register(&self, id: u8) -> bool { + pub fn has_local_register(&self, id: RegisterIndex) -> bool { self.local_registers .map(|rs| id < rs.read().len()) .unwrap_or(false) } - pub fn allocate_local_registers(&mut self, num: u8, mc: MutationContext<'gc, '_>) { + pub fn allocate_local_registers(&mut self, num: RegisterIndex, mc: MutationContext<'gc, '_>) { self.local_registers = match num { 0 => None, num => Some(GcCell::allocate(mc, RegisterSet::new(num))), @@ -2969,7 +3036,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { } /// Retrieve a local register. - pub fn local_register(&self, id: u8) -> Option> { + pub fn local_register(&self, id: RegisterIndex) -> Option> { if let Some(local_registers) = self.local_registers { local_registers.read().get(id).cloned() } else { @@ -2978,7 +3045,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { } /// Set a local register. - pub fn set_local_register(&mut self, id: u8, value: impl Into>) { + pub fn set_local_register(&mut self, id: RegisterIndex, value: impl Into>) { if let Some(ref mut local_registers) = self.local_registers { if let Some(r) = local_registers.write(self.context.gc_context).get_mut(id) { *r = value.into(); diff --git a/core/src/avm1/function.rs b/core/src/avm1/function.rs index 0b7500727b57..e4ce71d24fc3 100644 --- a/core/src/avm1/function.rs +++ b/core/src/avm1/function.rs @@ -12,7 +12,10 @@ use crate::tag_utils::SwfSlice; use gc_arena::{Collect, CollectionContext, Gc, GcCell, MutationContext}; use std::borrow::Cow; use std::fmt; -use swf::{avm1::types::FunctionParam, SwfStr}; +use swf::{ + avm1::types::{FunctionParam, RegisterIndex}, + SwfStr, +}; /// Represents a function defined in Ruffle's code. /// @@ -56,7 +59,7 @@ pub struct Avm1Function<'gc> { /// The number of registers to allocate for this function's private register /// set. Any register beyond this ID will be served from the global one. - register_count: u8, + register_count: RegisterIndex, preload_parent: bool, preload_root: bool, @@ -137,7 +140,7 @@ impl<'gc> Avm1Function<'gc> { pub fn from_df2( swf_version: u8, actions: SwfSlice, - swf_function: &swf::avm1::types::Function, + swf_function: &swf::avm1::types::DefineFunction2, scope: GcCell<'gc, Scope<'gc>>, constant_pool: GcCell<'gc, Vec>>, base_clip: DisplayObject<'gc>, @@ -197,7 +200,7 @@ impl<'gc> Avm1Function<'gc> { self.scope } - pub fn register_count(&self) -> u8 { + pub fn register_count(&self) -> RegisterIndex { self.register_count } } diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index b0de1defbf54..f2e395fde227 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -34,9 +34,7 @@ use std::collections::HashMap; use std::convert::TryFrom; use std::sync::Arc; use swf::extensions::ReadSwfExt; -use swf::{FillStyle, FrameLabelData, LineStyle, Tag}; - -type FrameNumber = u16; +use swf::{FillStyle, FrameLabelData, FrameNumber, LineStyle, Tag}; /// A movie clip is a display object with its own timeline that runs independently of the root timeline. /// The SWF19 spec calls this "Sprite" and the SWF tag defines it is "DefineSprite". diff --git a/swf/src/avm1.rs b/swf/src/avm1.rs index 40209f4a63be..934ca04c38c8 100644 --- a/swf/src/avm1.rs +++ b/swf/src/avm1.rs @@ -1,4 +1,4 @@ -pub(crate) mod opcode; +pub mod opcode; pub mod read; pub mod types; pub mod write; diff --git a/swf/src/avm1/opcode.rs b/swf/src/avm1/opcode.rs index 1cfc84838123..cb47ec478e02 100644 --- a/swf/src/avm1/opcode.rs +++ b/swf/src/avm1/opcode.rs @@ -116,3 +116,9 @@ pub enum OpCode { Call = 0x9E, GotoFrame2 = 0x9F, } + +impl OpCode { + pub fn from_u8(n: u8) -> Option { + num_traits::FromPrimitive::from_u8(n) + } +} diff --git a/swf/src/avm1/read.rs b/swf/src/avm1/read.rs index aebcef49429b..9ada537054ae 100644 --- a/swf/src/avm1/read.rs +++ b/swf/src/avm1/read.rs @@ -3,6 +3,8 @@ use crate::avm1::{opcode::OpCode, types::*}; use crate::error::{Error, Result}; use crate::extensions::ReadSwfExt; +use crate::{FrameNumber, SwfStr}; +use std::iter::from_fn; #[allow(dead_code)] pub struct Reader<'a> { @@ -78,7 +80,6 @@ impl<'a> Reader<'a> { #[inline] #[allow(clippy::inconsistent_digit_grouping)] fn read_op(&mut self, opcode: u8, length: &mut usize) -> Result>> { - use num_traits::FromPrimitive; let action = if let Some(op) = OpCode::from_u8(opcode) { match op { OpCode::End => return Ok(None), @@ -100,15 +101,15 @@ impl<'a> Reader<'a> { OpCode::CharToAscii => Action::CharToAscii, OpCode::CloneSprite => Action::CloneSprite, OpCode::ConstantPool => { - let mut constants = vec![]; - for _ in 0..self.read_u16()? { - constants.push(self.read_str()?); - } - Action::ConstantPool(constants) + Action::ConstantPool(self.read_constant_pool()?.collect::>()?) } OpCode::Decrement => Action::Decrement, - OpCode::DefineFunction => self.read_define_function(length)?, - OpCode::DefineFunction2 => self.read_define_function_2(length)?, + OpCode::DefineFunction => { + Action::DefineFunction(self.read_define_function(length)?) + } + OpCode::DefineFunction2 => { + Action::DefineFunction2(self.read_define_function_2(length)?) + } OpCode::DefineLocal => Action::DefineLocal, OpCode::DefineLocal2 => Action::DefineLocal2, OpCode::Delete => Action::Delete, @@ -123,56 +124,20 @@ impl<'a> Reader<'a> { OpCode::GetMember => Action::GetMember, OpCode::GetProperty => Action::GetProperty, OpCode::GetTime => Action::GetTime, - OpCode::GetUrl => Action::GetUrl { - url: self.read_str()?, - target: self.read_str()?, - }, - OpCode::GetUrl2 => { - let flags = self.read_u8()?; - Action::GetUrl2 { - is_load_vars: flags & 0b10_0000_00 != 0, - is_target_sprite: flags & 0b01_0000_00 != 0, - send_vars_method: match flags & 0b11 { - 0 => SendVarsMethod::None, - 1 => SendVarsMethod::Get, - 2 => SendVarsMethod::Post, - _ => { - return Err(Error::invalid_data( - "Invalid HTTP method in ActionGetUrl2", - )); - } - }, - } - } + OpCode::GetUrl => Action::GetUrl(self.read_get_url()?), + OpCode::GetUrl2 => Action::GetUrl2(self.read_get_url_2()?), OpCode::GetVariable => Action::GetVariable, - OpCode::GotoFrame => { - let frame = self.read_u16()?; - Action::GotoFrame(frame) - } - OpCode::GotoFrame2 => { - let flags = self.read_u8()?; - Action::GotoFrame2 { - set_playing: flags & 0b1 != 0, - scene_offset: if flags & 0b10 != 0 { - self.read_u16()? - } else { - 0 - }, - } - } - OpCode::GotoLabel => Action::GotoLabel(self.read_str()?), + OpCode::GotoFrame => Action::GotoFrame(self.read_goto_frame()?), + OpCode::GotoFrame2 => Action::GotoFrame2(self.read_goto_frame_2()?), + OpCode::GotoLabel => Action::GotoLabel(self.read_goto_label()?), OpCode::Greater => Action::Greater, - OpCode::If => Action::If { - offset: self.read_i16()?, - }, + OpCode::If => Action::If(self.read_instruction_offset()?), OpCode::ImplementsOp => Action::ImplementsOp, OpCode::Increment => Action::Increment, OpCode::InitArray => Action::InitArray, OpCode::InitObject => Action::InitObject, OpCode::InstanceOf => Action::InstanceOf, - OpCode::Jump => Action::Jump { - offset: self.read_i16()?, - }, + OpCode::Jump => Action::Jump(self.read_instruction_offset()?), OpCode::Less => Action::Less, OpCode::Less2 => Action::Less2, OpCode::MBAsciiToChar => Action::MBAsciiToChar, @@ -190,21 +155,21 @@ impl<'a> Reader<'a> { OpCode::Pop => Action::Pop, OpCode::PreviousFrame => Action::PreviousFrame, // TODO: Verify correct version for complex types. - OpCode::Push => self.read_push(*length)?, + OpCode::Push => Action::Push(self.read_push(*length)?.collect::>()?), OpCode::PushDuplicate => Action::PushDuplicate, OpCode::RandomNumber => Action::RandomNumber, OpCode::RemoveSprite => Action::RemoveSprite, OpCode::Return => Action::Return, OpCode::SetMember => Action::SetMember, OpCode::SetProperty => Action::SetProperty, - OpCode::SetTarget => Action::SetTarget(self.read_str()?), + OpCode::SetTarget => Action::SetTarget(self.read_set_target()?), OpCode::SetTarget2 => Action::SetTarget2, OpCode::SetVariable => Action::SetVariable, OpCode::StackSwap => Action::StackSwap, OpCode::StartDrag => Action::StartDrag, OpCode::Stop => Action::Stop, OpCode::StopSounds => Action::StopSounds, - OpCode::StoreRegister => Action::StoreRegister(self.read_u8()?), + OpCode::StoreRegister => Action::StoreRegister(self.read_store_register()?), OpCode::StrictEquals => Action::StrictEquals, OpCode::StringAdd => Action::StringAdd, OpCode::StringEquals => Action::StringEquals, @@ -220,64 +185,135 @@ impl<'a> Reader<'a> { OpCode::ToNumber => Action::ToNumber, OpCode::ToString => Action::ToString, OpCode::Trace => Action::Trace, - OpCode::Try => self.read_try(length)?, + OpCode::Try => Action::Try(self.read_try(length)?), OpCode::TypeOf => Action::TypeOf, - OpCode::WaitForFrame => Action::WaitForFrame { - frame: self.read_u16()?, - num_actions_to_skip: self.read_u8()?, - }, - OpCode::With => { - let code_length = usize::from(self.read_u16()?); - *length += code_length; - Action::With { - actions: self.read_slice(code_length)?, - } - } - OpCode::WaitForFrame2 => Action::WaitForFrame2 { - num_actions_to_skip: self.read_u8()?, - }, + OpCode::WaitForFrame => Action::WaitForFrame(self.read_wait_for_frame()?), + OpCode::WaitForFrame2 => Action::WaitForFrame2(self.read_wait_for_frame_2()?), + OpCode::With => Action::With(self.read_with(length)?), } } else { - self.read_unknown_action(opcode, *length)? + let data = self.read_unknown_action(*length)?; + Action::Unknown { opcode, data } }; Ok(Some(action)) } - fn read_unknown_action(&mut self, opcode: u8, length: usize) -> Result> { - Ok(Action::Unknown { - opcode, - data: self.read_slice(length)?, + #[inline] + pub fn read_unknown_action(&mut self, length: usize) -> Result> { + self.read_slice(length) + } + + #[inline] + pub fn read_constant_pool<'b>( + &'b mut self, + ) -> Result> + 'b> { + let len = self.read_u16()?; + Ok((0..len).map(move |_| self.read_str())) + } + + #[inline] + pub fn read_get_url(&mut self) -> Result> { + Ok(GetUrl { + url: self.read_str()?, + target: self.read_str()?, }) } - fn read_push(&mut self, length: usize) -> Result> { - let end_pos = (self.input.as_ptr() as usize + length) as *const u8; - let mut values = Vec::with_capacity(4); - while self.input.as_ptr() < end_pos { - values.push(self.read_push_value()?); - } - Ok(Action::Push(values)) + #[inline] + pub fn read_get_url_2(&mut self) -> Result { + let flags = self.read_u8()?; + let send_vars_method = match flags & 0b11 { + 0 => SendVarsMethod::None, + 1 => SendVarsMethod::Get, + 2 => SendVarsMethod::Post, + _ => { + return Err(Error::invalid_data("Invalid HTTP method in ActionGetUrl2")); + } + }; + Ok(GetUrl2 { + is_load_vars: flags & 0b10_0000_00 != 0, + is_target_sprite: flags & 0b01_0000_00 != 0, + send_vars_method, + }) + } + + #[inline] + pub fn read_goto_frame(&mut self) -> Result { + Ok(self.read_u16()?) } - fn read_push_value(&mut self) -> Result> { - let value = match self.read_u8()? { - 0 => Value::Str(self.read_str()?), - 1 => Value::Float(self.read_f32()?), + #[inline] + pub fn read_goto_frame_2(&mut self) -> Result { + let flags = self.read_u8()?; + let scene_offset = if flags & 0b10 != 0 { + self.read_u16()? + } else { + 0 + }; + Ok(GotoFrame2 { + set_playing: flags & 0b1 != 0, + scene_offset, + }) + } + + #[inline] + pub fn read_goto_label(&mut self) -> Result<&'a SwfStr> { + Ok(self.read_str()?) + } + + #[inline] + pub fn read_instruction_offset(&mut self) -> Result { + self.read_i16() + } + + #[inline] + pub fn read_push<'b>( + &'b mut self, + length: usize, + ) -> Result>> + 'b> { + let mut slice = self.read_slice(length)?; + Ok(from_fn(move || { + if let Ok(value_type) = slice.read_u8() { + Some(Reader::read_push_value(&mut slice, value_type)) + } else { + None + } + })) + } + + #[inline] + fn read_push_value(input: &mut &'a [u8], value_type: u8) -> Result> { + let value = match value_type { + 0 => Value::Str(input.read_str()?), + 1 => Value::Float(input.read_f32()?), 2 => Value::Null, 3 => Value::Undefined, - 4 => Value::Register(self.read_u8()?), - 5 => Value::Bool(self.read_u8()? != 0), - 6 => Value::Double(self.read_f64_me()?), - 7 => Value::Int(self.read_i32()?), - 8 => Value::ConstantPool(self.read_u8()?.into()), - 9 => Value::ConstantPool(self.read_u16()?), + 4 => Value::Register(input.read_u8()?), + 5 => Value::Bool(input.read_u8()? != 0), + 6 => Value::Double(input.read_f64_me()?), + 7 => Value::Int(input.read_i32()?), + 8 => Value::ConstantPool(input.read_u8()?.into()), + 9 => Value::ConstantPool(input.read_u16()?), _ => return Err(Error::invalid_data("Invalid value type in ActionPush")), }; Ok(value) } - fn read_define_function(&mut self, action_length: &mut usize) -> Result> { + #[inline] + pub fn read_set_target(&mut self) -> Result<&'a SwfStr> { + self.read_str() + } + + #[inline] + pub fn read_store_register(&mut self) -> Result { + self.read_u8() + } + + pub fn read_define_function( + &mut self, + action_length: &mut usize, + ) -> Result> { let name = self.read_str()?; let num_params = self.read_u16()?; let mut params = Vec::with_capacity(num_params as usize); @@ -287,14 +323,17 @@ impl<'a> Reader<'a> { // code_length isn't included in the DefineFunction's action length. let code_length = usize::from(self.read_u16()?); *action_length += code_length; - Ok(Action::DefineFunction { + Ok(DefineFunction { name, params, actions: self.read_slice(code_length)?, }) } - fn read_define_function_2(&mut self, action_length: &mut usize) -> Result> { + pub fn read_define_function_2( + &mut self, + action_length: &mut usize, + ) -> Result> { let name = self.read_str()?; let num_params = self.read_u16()?; let register_count = self.read_u8()?; // Number of registers @@ -310,7 +349,7 @@ impl<'a> Reader<'a> { // code_length isn't included in the DefineFunction's length. let code_length = usize::from(self.read_u16()?); *action_length += code_length; - Ok(Action::DefineFunction2(Function { + Ok(DefineFunction2 { name, params, register_count, @@ -324,10 +363,10 @@ impl<'a> Reader<'a> { suppress_this: flags & 0b10 != 0, preload_this: flags & 0b1 != 0, actions: self.read_slice(code_length)?, - })) + }) } - fn read_try(&mut self, length: &mut usize) -> Result> { + pub fn read_try(&mut self, length: &mut usize) -> Result> { let flags = self.read_u8()?; let try_length = usize::from(self.read_u16()?); let catch_length = usize::from(self.read_u16()?); @@ -341,7 +380,7 @@ impl<'a> Reader<'a> { let try_actions = self.read_slice(try_length)?; let catch_actions = self.read_slice(catch_length)?; let finally_actions = self.read_slice(finally_length)?; - Ok(Action::Try(TryBlock { + Ok(TryBlock { try_actions, catch: if flags & 0b1 != 0 { Some((catch_var, catch_actions)) @@ -353,14 +392,36 @@ impl<'a> Reader<'a> { } else { None }, - })) + }) + } + + #[inline] + pub fn read_wait_for_frame(&mut self) -> Result { + Ok(WaitForFrame { + frame: self.read_u16()?, + num_actions_to_skip: self.read_u8()?, + }) + } + + #[inline] + pub fn read_wait_for_frame_2(&mut self) -> Result { + Ok(WaitForFrame2 { + num_actions_to_skip: self.read_u8()?, + }) + } + + #[inline] + pub fn read_with(&mut self, length: &mut usize) -> Result> { + let code_length = usize::from(self.read_u16()?); + *length += code_length; + Ok(self.read_slice(code_length)?) } } #[cfg(test)] pub mod tests { use super::*; - use crate::string::{SwfStr, WINDOWS_1252}; + use crate::string::WINDOWS_1252; use crate::test_data; #[test] @@ -402,14 +463,14 @@ pub mod tests { let action = reader.read_action().unwrap().unwrap(); assert_eq!( action, - Action::DefineFunction { + Action::DefineFunction(DefineFunction { name: SwfStr::from_str_with_encoding("foo", WINDOWS_1252).unwrap(), params: vec![], actions: &[0x96, 0x06, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x00, 0x26], - } + }) ); - if let Action::DefineFunction { actions, .. } = action { + if let Action::DefineFunction(DefineFunction { actions, .. }) = action { let mut reader = Reader::new(actions, 5); let action = reader.read_action().unwrap().unwrap(); assert_eq!( diff --git a/swf/src/avm1/types.rs b/swf/src/avm1/types.rs index 709a1b966ed1..bc132846c13a 100644 --- a/swf/src/avm1/types.rs +++ b/swf/src/avm1/types.rs @@ -1,4 +1,9 @@ use crate::string::SwfStr; +use crate::types::FrameNumber; + +pub type ActionsData<'a> = &'a [u8]; + +pub type RegisterIndex = u8; #[derive(Clone, Debug, PartialEq)] pub enum Action<'a> { @@ -20,12 +25,8 @@ pub enum Action<'a> { CloneSprite, ConstantPool(Vec<&'a SwfStr>), Decrement, - DefineFunction { - name: &'a SwfStr, - params: Vec<&'a SwfStr>, - actions: &'a [u8], - }, - DefineFunction2(Function<'a>), + DefineFunction(DefineFunction<'a>), + DefineFunction2(DefineFunction2<'a>), DefineLocal, DefineLocal2, Delete, @@ -40,34 +41,20 @@ pub enum Action<'a> { GetMember, GetProperty, GetTime, - GetUrl { - url: &'a SwfStr, - target: &'a SwfStr, - }, - GetUrl2 { - send_vars_method: SendVarsMethod, - is_target_sprite: bool, - is_load_vars: bool, - }, + GetUrl(GetUrl<'a>), + GetUrl2(GetUrl2), GetVariable, - GotoFrame(u16), - GotoFrame2 { - set_playing: bool, - scene_offset: u16, - }, + GotoFrame(FrameNumber), + GotoFrame2(GotoFrame2), GotoLabel(&'a SwfStr), Greater, - If { - offset: i16, - }, + If(InstructionOffset), ImplementsOp, Increment, InitArray, InitObject, InstanceOf, - Jump { - offset: i16, - }, + Jump(InstructionOffset), Less, Less2, MBAsciiToChar, @@ -98,7 +85,7 @@ pub enum Action<'a> { StartDrag, Stop, StopSounds, - StoreRegister(u8), + StoreRegister(RegisterIndex), StrictEquals, StringAdd, StringEquals, @@ -116,20 +103,10 @@ pub enum Action<'a> { Trace, Try(TryBlock<'a>), TypeOf, - WaitForFrame { - frame: u16, - num_actions_to_skip: u8, - }, - WaitForFrame2 { - num_actions_to_skip: u8, - }, - With { - actions: &'a [u8], - }, - Unknown { - opcode: u8, - data: &'a [u8], - }, + WaitForFrame(WaitForFrame), + WaitForFrame2(WaitForFrame2), + With(ActionsData<'a>), + Unknown { opcode: u8, data: ActionsData<'a> }, } #[derive(Clone, Debug, PartialEq)] @@ -141,7 +118,7 @@ pub enum Value<'a> { Float(f32), Double(f64), Str(&'a SwfStr), - Register(u8), + Register(RegisterIndex), ConstantPool(u16), } @@ -153,9 +130,16 @@ pub enum SendVarsMethod { } #[derive(Clone, Debug, PartialEq)] -pub struct Function<'a> { +pub struct DefineFunction<'a> { + pub name: &'a SwfStr, + pub params: Vec<&'a SwfStr>, + pub actions: ActionsData<'a>, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct DefineFunction2<'a> { pub name: &'a SwfStr, - pub register_count: u8, + pub register_count: RegisterIndex, pub params: Vec>, pub preload_parent: bool, pub preload_root: bool, @@ -166,24 +150,56 @@ pub struct Function<'a> { pub suppress_this: bool, pub preload_this: bool, pub preload_global: bool, - pub actions: &'a [u8], + pub actions: ActionsData<'a>, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct FunctionParam<'a> { pub name: &'a SwfStr, - pub register_index: Option, + pub register_index: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct GetUrl<'a> { + pub url: &'a SwfStr, + pub target: &'a SwfStr, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct GetUrl2 { + pub send_vars_method: SendVarsMethod, + pub is_target_sprite: bool, + pub is_load_vars: bool, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct GotoFrame2 { + pub set_playing: bool, + pub scene_offset: u16, } +pub type InstructionOffset = i16; + #[derive(Clone, Debug, PartialEq)] pub struct TryBlock<'a> { - pub try_actions: &'a [u8], - pub catch: Option<(CatchVar<'a>, &'a [u8])>, - pub finally: Option<&'a [u8]>, + pub try_actions: ActionsData<'a>, + pub catch: Option<(CatchVar<'a>, ActionsData<'a>)>, + pub finally: Option>, } #[derive(Clone, Debug, PartialEq, Eq)] pub enum CatchVar<'a> { Var(&'a SwfStr), - Register(u8), + Register(RegisterIndex), +} + +#[derive(Clone, Debug, PartialEq)] +pub struct WaitForFrame { + pub frame: FrameNumber, + pub num_actions_to_skip: u8, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct WaitForFrame2 { + pub num_actions_to_skip: u8, } diff --git a/swf/src/avm1/write.rs b/swf/src/avm1/write.rs index 38dc7acebd56..597acf0cbfa6 100644 --- a/swf/src/avm1/write.rs +++ b/swf/src/avm1/write.rs @@ -112,11 +112,11 @@ impl Writer { } } Action::Decrement => self.write_action_header(OpCode::Decrement, 0)?, - Action::DefineFunction { + Action::DefineFunction(DefineFunction { ref name, ref params, ref actions, - } => { + }) => { // 1 zero byte for string name, 1 zero byte per param, 2 bytes for # of params, // 2 bytes for code length let len = @@ -189,19 +189,19 @@ impl Writer { Action::GetMember => self.write_action_header(OpCode::GetMember, 0)?, Action::GetProperty => self.write_action_header(OpCode::GetProperty, 0)?, Action::GetTime => self.write_action_header(OpCode::GetTime, 0)?, - Action::GetUrl { + Action::GetUrl(GetUrl { ref url, ref target, - } => { + }) => { self.write_action_header(OpCode::GetUrl, url.len() + target.len() + 2)?; self.write_string(*url)?; self.write_string(*target)?; } - Action::GetUrl2 { + Action::GetUrl2(GetUrl2 { send_vars_method, is_target_sprite, is_load_vars, - } => { + }) => { self.write_action_header(OpCode::GetUrl2, 1)?; let flags = (match send_vars_method { SendVarsMethod::None => 0, @@ -216,10 +216,10 @@ impl Writer { self.write_action_header(OpCode::GotoFrame, 2)?; self.write_u16(frame)?; } - Action::GotoFrame2 { + Action::GotoFrame2(GotoFrame2 { set_playing, scene_offset, - } => { + }) => { if scene_offset != 0 { self.write_action_header(OpCode::GotoFrame2, 3)?; self.write_u8(if set_playing { 0b11 } else { 0b01 })?; @@ -234,7 +234,7 @@ impl Writer { self.write_string(*label)?; } Action::Greater => self.write_action_header(OpCode::Greater, 0)?, - Action::If { offset } => { + Action::If(offset) => { self.write_action_header(OpCode::If, 2)?; self.write_i16(offset)?; } @@ -243,7 +243,7 @@ impl Writer { Action::InitArray => self.write_action_header(OpCode::InitArray, 0)?, Action::InitObject => self.write_action_header(OpCode::InitObject, 0)?, Action::InstanceOf => self.write_action_header(OpCode::InstanceOf, 0)?, - Action::Jump { offset } => { + Action::Jump(offset) => { self.write_action_header(OpCode::Jump, 2)?; self.write_i16(offset)?; } @@ -369,21 +369,21 @@ impl Writer { self.output.write_all(&action_buf)?; } Action::TypeOf => self.write_action_header(OpCode::TypeOf, 0)?, - Action::WaitForFrame { + Action::WaitForFrame(WaitForFrame { frame, num_actions_to_skip, - } => { + }) => { self.write_action_header(OpCode::WaitForFrame, 3)?; self.write_u16(frame)?; self.write_u8(num_actions_to_skip)?; } - Action::WaitForFrame2 { + Action::WaitForFrame2(WaitForFrame2 { num_actions_to_skip, - } => { + }) => { self.write_action_header(OpCode::WaitForFrame2, 1)?; self.write_u8(num_actions_to_skip)?; } - Action::With { ref actions } => { + Action::With(ref actions) => { self.write_action_header(OpCode::With, actions.len())?; self.output.write_all(&actions)?; } diff --git a/swf/src/error.rs b/swf/src/error.rs index aa2a4df45fcb..00c209b70a7a 100644 --- a/swf/src/error.rs +++ b/swf/src/error.rs @@ -67,7 +67,6 @@ impl Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use crate::num_traits::FromPrimitive; match self { Error::Avm1ParseError { opcode, source } => { let op = crate::avm1::opcode::OpCode::from_u8(*opcode); diff --git a/swf/src/extensions.rs b/swf/src/extensions.rs index 84219afa6c47..4cbe466b97b7 100644 --- a/swf/src/extensions.rs +++ b/swf/src/extensions.rs @@ -130,3 +130,9 @@ pub trait ReadSwfExt<'a> { Ok(SwfStr::from_bytes_null_terminated(bytes).unwrap_or_else(|| SwfStr::from_bytes(bytes))) } } + +impl<'a> ReadSwfExt<'a> for &'a [u8] { + fn as_mut_slice(&mut self) -> &mut &'a [u8] { + self + } +} diff --git a/swf/src/test_data.rs b/swf/src/test_data.rs index 038da9954376..7e2fcaa10aaf 100644 --- a/swf/src/test_data.rs +++ b/swf/src/test_data.rs @@ -2659,46 +2659,46 @@ pub fn avm1_tests() -> Vec { (4, Action::GetTime, vec![0x34]), ( 3, - Action::GetUrl { + Action::GetUrl(GetUrl { url: SwfStr::from_str_with_encoding("a", WINDOWS_1252).unwrap(), target: SwfStr::from_str_with_encoding("b", WINDOWS_1252).unwrap(), - }, + }), vec![0x83, 4, 0, 97, 0, 98, 0], ), ( 4, - Action::GetUrl2 { + Action::GetUrl2(GetUrl2 { send_vars_method: SendVarsMethod::Post, is_target_sprite: true, is_load_vars: false, - }, + }), vec![0x9A, 1, 0, 0b01_0000_10], ), ( 4, - Action::GetUrl2 { + Action::GetUrl2(GetUrl2 { send_vars_method: SendVarsMethod::None, is_target_sprite: true, is_load_vars: false, - }, + }), vec![0x9A, 1, 0, 0b01_0000_00], ), (4, Action::GetVariable, vec![0x1C]), (3, Action::GotoFrame(11), vec![0x81, 2, 0, 11, 0]), ( 4, - Action::GotoFrame2 { + Action::GotoFrame2(GotoFrame2 { set_playing: false, scene_offset: 0, - }, + }), vec![0x9F, 1, 0, 0], ), ( 4, - Action::GotoFrame2 { + Action::GotoFrame2(GotoFrame2 { set_playing: true, scene_offset: 259, - }, + }), vec![0x9F, 3, 0, 0b11, 3, 1], ), ( @@ -2706,8 +2706,8 @@ pub fn avm1_tests() -> Vec { Action::GotoLabel(SwfStr::from_str_with_encoding("testb", WINDOWS_1252).unwrap()), vec![0x8C, 6, 0, 116, 101, 115, 116, 98, 0], ), - (4, Action::If { offset: 1 }, vec![0x9D, 2, 0, 1, 0]), - (4, Action::Jump { offset: 1 }, vec![0x99, 2, 0, 1, 0]), + (4, Action::If(1), vec![0x9D, 2, 0, 1, 0]), + (4, Action::Jump(1), vec![0x99, 2, 0, 1, 0]), (4, Action::Less, vec![0x0F]), (4, Action::MBAsciiToChar, vec![0x37]), (4, Action::MBCharToAscii, vec![0x36]), @@ -2805,17 +2805,17 @@ pub fn avm1_tests() -> Vec { (4, Action::Trace, vec![0x26]), ( 3, - Action::WaitForFrame { + Action::WaitForFrame(WaitForFrame { frame: 4, num_actions_to_skip: 10, - }, + }), vec![0x8A, 3, 0, 4, 0, 10], ), ( 4, - Action::WaitForFrame2 { + Action::WaitForFrame2(WaitForFrame2 { num_actions_to_skip: 34, - }, + }), vec![0x8D, 1, 0, 34], ), ( @@ -2836,7 +2836,7 @@ pub fn avm1_tests() -> Vec { ), ( 5, - Action::DefineFunction { + Action::DefineFunction(DefineFunction { name: SwfStr::from_str_with_encoding("cliche", WINDOWS_1252).unwrap(), params: vec![ SwfStr::from_str_with_encoding("greeting", WINDOWS_1252).unwrap(), @@ -2847,7 +2847,7 @@ pub fn avm1_tests() -> Vec { 0x1c, 0x96, 0x03, 0x00, 0x00, 0x20, 0x00, 0x47, 0x96, 0x06, 0x00, 0x00, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x1c, 0x47, 0x3e, ], - }, + }), vec![ 0x9b, 0x19, 0x00, 0x63, 0x6c, 0x69, 0x63, 0x68, 0x65, 0x00, 0x02, 0x00, 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x00, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x21, 0x00, diff --git a/swf/src/types.rs b/swf/src/types.rs index e6177d97cf76..5ed25989aac6 100644 --- a/swf/src/types.rs +++ b/swf/src/types.rs @@ -40,7 +40,7 @@ pub struct Header { pub uncompressed_length: u32, pub stage_size: Rectangle, pub frame_rate: f32, - pub num_frames: u16, + pub num_frames: FrameNumber, } /// The compression format used internally by the SWF file. @@ -395,6 +395,7 @@ pub struct FrameLabelData<'a> { pub type Depth = u16; pub type CharacterId = u16; +pub type FrameNumber = u16; #[derive(Debug, PartialEq)] pub struct PlaceObject<'a> { @@ -781,7 +782,7 @@ pub struct StartSound { #[derive(Debug, PartialEq)] pub struct Sprite<'a> { pub id: CharacterId, - pub num_frames: u16, + pub num_frames: FrameNumber, pub tags: Vec>, } @@ -1205,7 +1206,7 @@ pub enum BitmapFormat { #[derive(Clone, Debug, PartialEq)] pub struct DefineVideoStream { pub id: CharacterId, - pub num_frames: u16, + pub num_frames: FrameNumber, pub width: u16, pub height: u16, pub is_smoothed: bool, @@ -1235,7 +1236,7 @@ pub enum VideoCodec { #[derive(Clone, Debug, PartialEq)] pub struct VideoFrame<'a> { pub stream_id: CharacterId, - pub frame_num: u16, + pub frame_num: FrameNumber, pub data: &'a [u8], }