avm1: Reader reads from &[u8]
This commit is contained in:
parent
1f85a779ac
commit
f5db446fe1
226
src/avm1/read.rs
226
src/avm1/read.rs
|
@ -3,39 +3,72 @@
|
|||
use crate::avm1::opcode::OpCode;
|
||||
use crate::avm1::types::*;
|
||||
use crate::read::SwfRead;
|
||||
use std::io::{Error, ErrorKind, Read, Result};
|
||||
use std::io::{Cursor, Error, ErrorKind, Result};
|
||||
|
||||
pub struct Reader<R: Read> {
|
||||
inner: R,
|
||||
#[allow(dead_code)]
|
||||
pub struct Reader<'a> {
|
||||
inner: Cursor<&'a [u8]>,
|
||||
version: u8,
|
||||
}
|
||||
|
||||
impl<R: Read> SwfRead<R> for Reader<R> {
|
||||
fn get_inner(&mut self) -> &mut R {
|
||||
impl<'a> SwfRead<Cursor<&'a [u8]>> for Reader<'a> {
|
||||
fn get_inner(&mut self) -> &mut Cursor<&'a [u8]> {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Reader<R> {
|
||||
pub fn new(inner: R, version: u8) -> Reader<R> {
|
||||
Reader { inner, version }
|
||||
}
|
||||
|
||||
pub fn read_action_list(&mut self) -> Result<Vec<Action>> {
|
||||
let mut actions = Vec::new();
|
||||
while let Some(action) = self.read_action()? {
|
||||
actions.push(action);
|
||||
impl<'a> Reader<'a> {
|
||||
pub fn new(input: &'a [u8], version: u8) -> Self {
|
||||
Self {
|
||||
inner: Cursor::new(input),
|
||||
version,
|
||||
}
|
||||
Ok(actions)
|
||||
}
|
||||
|
||||
pub fn read_action(&mut self) -> Result<Option<Action>> {
|
||||
#[inline]
|
||||
pub fn pos(&self) -> usize {
|
||||
self.inner.position() as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn seek(&mut self, relative_offset: isize) {
|
||||
let new_pos = self.inner.position() as i64 + relative_offset as i64;
|
||||
self.inner.set_position(new_pos as u64);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_slice(&mut self, len: usize) -> &'a [u8] {
|
||||
let pos = self.pos();
|
||||
let slice = &self.inner.get_ref()[pos..pos + len];
|
||||
self.inner.set_position(pos as u64 + len as u64);
|
||||
slice
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_c_string(&mut self) -> Result<&'a str> {
|
||||
// Find zero terminator.
|
||||
let str_slice = {
|
||||
let start_pos = self.pos();
|
||||
loop {
|
||||
let byte = self.read_u8()?;
|
||||
if byte == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
&self.inner.get_ref()[start_pos..self.pos() - 1]
|
||||
};
|
||||
// TODO: What does Flash do on invalid UTF8?
|
||||
// Do we silently let it pass?
|
||||
// TODO: Verify ANSI for SWF 5 and earlier.
|
||||
std::str::from_utf8(str_slice)
|
||||
.map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid string data"))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_action(&mut self) -> Result<Option<Action<'a>>> {
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
let (opcode, length) = self.read_opcode_and_length()?;
|
||||
|
||||
let mut action_reader = Reader::new(self.inner.by_ref().take(length as u64), self.version);
|
||||
|
||||
let action = if let Some(op) = OpCode::from_u8(opcode) {
|
||||
match op {
|
||||
OpCode::End => return Ok(None),
|
||||
|
@ -58,14 +91,14 @@ impl<R: Read> Reader<R> {
|
|||
OpCode::CloneSprite => Action::CloneSprite,
|
||||
OpCode::ConstantPool => {
|
||||
let mut constants = vec![];
|
||||
for _ in 0..action_reader.read_u16()? {
|
||||
constants.push(action_reader.read_c_string()?);
|
||||
for _ in 0..self.read_u16()? {
|
||||
constants.push(self.read_c_string()?);
|
||||
}
|
||||
Action::ConstantPool(constants)
|
||||
}
|
||||
OpCode::Decrement => Action::Decrement,
|
||||
OpCode::DefineFunction => action_reader.read_define_function()?,
|
||||
OpCode::DefineFunction2 => action_reader.read_define_function_2()?,
|
||||
OpCode::DefineFunction => self.read_define_function()?,
|
||||
OpCode::DefineFunction2 => self.read_define_function_2()?,
|
||||
OpCode::DefineLocal => Action::DefineLocal,
|
||||
OpCode::DefineLocal2 => Action::DefineLocal2,
|
||||
OpCode::Delete => Action::Delete,
|
||||
|
@ -81,11 +114,11 @@ impl<R: Read> Reader<R> {
|
|||
OpCode::GetProperty => Action::GetProperty,
|
||||
OpCode::GetTime => Action::GetTime,
|
||||
OpCode::GetUrl => Action::GetUrl {
|
||||
url: action_reader.read_c_string()?,
|
||||
target: action_reader.read_c_string()?,
|
||||
url: self.read_c_string()?,
|
||||
target: self.read_c_string()?,
|
||||
},
|
||||
OpCode::GetUrl2 => {
|
||||
let flags = action_reader.read_u8()?;
|
||||
let flags = self.read_u8()?;
|
||||
Action::GetUrl2 {
|
||||
is_target_sprite: flags & 0b10 != 0,
|
||||
is_load_vars: flags & 0b1 != 0,
|
||||
|
@ -104,24 +137,24 @@ impl<R: Read> Reader<R> {
|
|||
}
|
||||
OpCode::GetVariable => Action::GetVariable,
|
||||
OpCode::GotoFrame => {
|
||||
let frame = action_reader.read_u16()?;
|
||||
let frame = self.read_u16()?;
|
||||
Action::GotoFrame(frame)
|
||||
}
|
||||
OpCode::GotoFrame2 => {
|
||||
let flags = action_reader.read_u8()?;
|
||||
let flags = self.read_u8()?;
|
||||
Action::GotoFrame2 {
|
||||
set_playing: flags & 0b1 != 0,
|
||||
scene_offset: if flags & 0b10 != 0 {
|
||||
action_reader.read_u16()?
|
||||
self.read_u16()?
|
||||
} else {
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
OpCode::GotoLabel => Action::GotoLabel(action_reader.read_c_string()?),
|
||||
OpCode::GotoLabel => Action::GotoLabel(self.read_c_string()?),
|
||||
OpCode::Greater => Action::Greater,
|
||||
OpCode::If => Action::If {
|
||||
offset: action_reader.read_i16()?,
|
||||
offset: self.read_i16()?,
|
||||
},
|
||||
OpCode::ImplementsOp => Action::ImplementsOp,
|
||||
OpCode::Increment => Action::Increment,
|
||||
|
@ -129,7 +162,7 @@ impl<R: Read> Reader<R> {
|
|||
OpCode::InitObject => Action::InitObject,
|
||||
OpCode::InstanceOf => Action::InstanceOf,
|
||||
OpCode::Jump => Action::Jump {
|
||||
offset: action_reader.read_i16()?,
|
||||
offset: self.read_i16()?,
|
||||
},
|
||||
OpCode::Less => Action::Less,
|
||||
OpCode::Less2 => Action::Less2,
|
||||
|
@ -148,27 +181,21 @@ impl<R: Read> Reader<R> {
|
|||
OpCode::Pop => Action::Pop,
|
||||
OpCode::PreviousFrame => Action::PreviousFrame,
|
||||
// TODO: Verify correct version for complex types.
|
||||
OpCode::Push => {
|
||||
let mut values = vec![];
|
||||
while let Ok(value) = action_reader.read_push_value() {
|
||||
values.push(value);
|
||||
}
|
||||
Action::Push(values)
|
||||
}
|
||||
OpCode::Push => self.read_push(length)?,
|
||||
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(action_reader.read_c_string()?),
|
||||
OpCode::SetTarget => Action::SetTarget(self.read_c_string()?),
|
||||
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(action_reader.read_u8()?),
|
||||
OpCode::StoreRegister => Action::StoreRegister(self.read_u8()?),
|
||||
OpCode::StrictEquals => Action::StrictEquals,
|
||||
OpCode::StringAdd => Action::StringAdd,
|
||||
OpCode::StringEquals => Action::StringEquals,
|
||||
|
@ -184,28 +211,24 @@ impl<R: Read> Reader<R> {
|
|||
OpCode::ToNumber => Action::ToNumber,
|
||||
OpCode::ToString => Action::ToString,
|
||||
OpCode::Trace => Action::Trace,
|
||||
OpCode::Try => action_reader.read_try()?,
|
||||
OpCode::Try => self.read_try()?,
|
||||
OpCode::TypeOf => Action::TypeOf,
|
||||
OpCode::WaitForFrame => Action::WaitForFrame {
|
||||
frame: action_reader.read_u16()?,
|
||||
num_actions_to_skip: action_reader.read_u8()?,
|
||||
frame: self.read_u16()?,
|
||||
num_actions_to_skip: self.read_u8()?,
|
||||
},
|
||||
OpCode::With => {
|
||||
let code_length = action_reader.read_u16()?;
|
||||
let mut with_reader = Reader::new(
|
||||
(&mut action_reader.inner as &mut dyn Read).take(code_length.into()),
|
||||
self.version,
|
||||
);
|
||||
let code_length = self.read_u16()?;
|
||||
Action::With {
|
||||
actions: with_reader.read_action_list()?,
|
||||
actions: self.read_slice(code_length.into()),
|
||||
}
|
||||
}
|
||||
OpCode::WaitForFrame2 => Action::WaitForFrame2 {
|
||||
num_actions_to_skip: action_reader.read_u8()?,
|
||||
num_actions_to_skip: self.read_u8()?,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
action_reader.read_unknown_action(opcode, length)?
|
||||
self.read_unknown_action(opcode, length)?
|
||||
};
|
||||
|
||||
Ok(Some(action))
|
||||
|
@ -221,13 +244,23 @@ impl<R: Read> Reader<R> {
|
|||
Ok((opcode, length))
|
||||
}
|
||||
|
||||
fn read_unknown_action(&mut self, opcode: u8, length: usize) -> Result<Action> {
|
||||
let mut data = vec![0u8; length];
|
||||
self.inner.read_exact(&mut data)?;
|
||||
Ok(Action::Unknown { opcode, data })
|
||||
fn read_unknown_action(&mut self, opcode: u8, length: usize) -> Result<Action<'a>> {
|
||||
Ok(Action::Unknown {
|
||||
opcode,
|
||||
data: self.read_slice(length),
|
||||
})
|
||||
}
|
||||
|
||||
fn read_push_value(&mut self) -> Result<Value> {
|
||||
fn read_push(&mut self, length: usize) -> Result<Action<'a>> {
|
||||
let end_pos = self.pos() + length;
|
||||
let mut values = Vec::with_capacity(end_pos);
|
||||
while self.pos() < end_pos {
|
||||
values.push(self.read_push_value()?);
|
||||
}
|
||||
Ok(Action::Push(values))
|
||||
}
|
||||
|
||||
fn read_push_value(&mut self) -> Result<Value<'a>> {
|
||||
let value = match self.read_u8()? {
|
||||
0 => Value::Str(self.read_c_string()?),
|
||||
1 => Value::Float(self.read_f32()?),
|
||||
|
@ -249,26 +282,23 @@ impl<R: Read> Reader<R> {
|
|||
Ok(value)
|
||||
}
|
||||
|
||||
fn read_define_function(&mut self) -> Result<Action> {
|
||||
fn read_define_function(&mut self) -> Result<Action<'a>> {
|
||||
let name = self.read_c_string()?;
|
||||
let num_params = self.read_u16()?;
|
||||
let mut params = Vec::with_capacity(num_params as usize);
|
||||
for _ in 0..num_params {
|
||||
params.push(self.read_c_string()?);
|
||||
}
|
||||
// code_length isn't included in the DefineFunction's action length.
|
||||
let code_length = self.read_u16()?;
|
||||
let mut fn_reader = Reader::new(
|
||||
(&mut self.inner as &mut dyn Read).take(code_length.into()),
|
||||
self.version,
|
||||
);
|
||||
Ok(Action::DefineFunction {
|
||||
name,
|
||||
params,
|
||||
actions: fn_reader.read_action_list()?,
|
||||
actions: self.read_slice(code_length.into()),
|
||||
})
|
||||
}
|
||||
|
||||
fn read_define_function_2(&mut self) -> Result<Action> {
|
||||
fn read_define_function_2(&mut self) -> Result<Action<'a>> {
|
||||
let name = self.read_c_string()?;
|
||||
let num_params = self.read_u16()?;
|
||||
let num_registers = self.read_u8()?; // Number of registers
|
||||
|
@ -281,11 +311,8 @@ impl<R: Read> Reader<R> {
|
|||
register_index: if register == 0 { None } else { Some(register) },
|
||||
});
|
||||
}
|
||||
// code_length isn't included in the DefineFunction's length.
|
||||
let code_length = self.read_u16()?;
|
||||
let mut fn_reader = Reader::new(
|
||||
(&mut self.inner as &mut dyn Read).take(code_length.into()),
|
||||
self.version,
|
||||
);
|
||||
Ok(Action::DefineFunction2(Function {
|
||||
name,
|
||||
params,
|
||||
|
@ -298,11 +325,11 @@ impl<R: Read> Reader<R> {
|
|||
preload_arguments: flags & 0b100 != 0,
|
||||
suppress_this: flags & 0b10 != 0,
|
||||
preload_this: flags & 0b1 != 0,
|
||||
actions: fn_reader.read_action_list()?,
|
||||
actions: self.read_slice(code_length.into()),
|
||||
}))
|
||||
}
|
||||
|
||||
fn read_try(&mut self) -> Result<Action> {
|
||||
fn read_try(&mut self) -> Result<Action<'a>> {
|
||||
let flags = self.read_u8()?;
|
||||
let try_length = self.read_u16()?;
|
||||
let catch_length = self.read_u16()?;
|
||||
|
@ -312,27 +339,9 @@ impl<R: Read> Reader<R> {
|
|||
} else {
|
||||
CatchVar::Register(self.read_u8()?)
|
||||
};
|
||||
let try_actions = {
|
||||
let mut fn_reader = Reader::new(
|
||||
(&mut self.inner as &mut dyn Read).take(try_length.into()),
|
||||
self.version,
|
||||
);
|
||||
fn_reader.read_action_list()?
|
||||
};
|
||||
let catch_actions = {
|
||||
let mut fn_reader = Reader::new(
|
||||
(&mut self.inner as &mut dyn Read).take(catch_length.into()),
|
||||
self.version,
|
||||
);
|
||||
fn_reader.read_action_list()?
|
||||
};
|
||||
let finally_actions = {
|
||||
let mut fn_reader = Reader::new(
|
||||
(&mut self.inner as &mut dyn Read).take(finally_length.into()),
|
||||
self.version,
|
||||
);
|
||||
fn_reader.read_action_list()?
|
||||
};
|
||||
let try_actions = self.read_slice(try_length.into());
|
||||
let catch_actions = self.read_slice(catch_length.into());
|
||||
let finally_actions = self.read_slice(finally_length.into());
|
||||
Ok(Action::Try(TryBlock {
|
||||
try_actions,
|
||||
catch: if flags & 0b1 != 0 {
|
||||
|
@ -368,4 +377,39 @@ pub mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_define_function() {
|
||||
// Ensure we read a function properly along with the function data.
|
||||
let action_bytes = vec![
|
||||
0x9b, 0x08, 0x00, 0x66, 0x6f, 0x6f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x96, 0x06, 0x00,
|
||||
0x00, 0x74, 0x65, 0x73, 0x74, 0x00, 0x26, 0x00,
|
||||
];
|
||||
let mut reader = Reader::new(&action_bytes[..], 5);
|
||||
let action = reader.read_action().unwrap().unwrap();
|
||||
assert_eq!(
|
||||
action,
|
||||
Action::DefineFunction {
|
||||
name: "foo",
|
||||
params: vec![],
|
||||
actions: &[0x96, 0x06, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x00, 0x26],
|
||||
}
|
||||
);
|
||||
|
||||
if let Action::DefineFunction { actions, .. } = action {
|
||||
let mut reader = Reader::new(actions, 5);
|
||||
let action = reader.read_action().unwrap().unwrap();
|
||||
assert_eq!(action, Action::Push(vec![Value::Str("test")]));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_push_to_end_of_action() {
|
||||
// ActionPush doesn't provide an explicit # of values, but instead reads values
|
||||
// until the end of the action. Ensure we don't read extra values.
|
||||
let action_bytes = [0x96, 2, 0, 2, 3, 3]; // Extra 3 at the end shouldn't be read.
|
||||
let mut reader = Reader::new(&action_bytes[..], 5);
|
||||
let action = reader.read_action().unwrap().unwrap();
|
||||
assert_eq!(action, Action::Push(vec![Value::Null, Value::Undefined]));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Action {
|
||||
pub enum Action<'a> {
|
||||
Add,
|
||||
Add2,
|
||||
And,
|
||||
|
@ -16,14 +16,14 @@ pub enum Action {
|
|||
CastOp,
|
||||
CharToAscii,
|
||||
CloneSprite,
|
||||
ConstantPool(Vec<String>),
|
||||
ConstantPool(Vec<&'a str>),
|
||||
Decrement,
|
||||
DefineFunction {
|
||||
name: String,
|
||||
params: Vec<String>,
|
||||
actions: Vec<Action>,
|
||||
name: &'a str,
|
||||
params: Vec<&'a str>,
|
||||
actions: &'a [u8],
|
||||
},
|
||||
DefineFunction2(Function),
|
||||
DefineFunction2(Function<'a>),
|
||||
DefineLocal,
|
||||
DefineLocal2,
|
||||
Delete,
|
||||
|
@ -39,8 +39,8 @@ pub enum Action {
|
|||
GetProperty,
|
||||
GetTime,
|
||||
GetUrl {
|
||||
url: String,
|
||||
target: String,
|
||||
url: &'a str,
|
||||
target: &'a str,
|
||||
},
|
||||
GetUrl2 {
|
||||
send_vars_method: SendVarsMethod,
|
||||
|
@ -53,7 +53,7 @@ pub enum Action {
|
|||
set_playing: bool,
|
||||
scene_offset: u16,
|
||||
},
|
||||
GotoLabel(String),
|
||||
GotoLabel(&'a str),
|
||||
Greater,
|
||||
If {
|
||||
offset: i16,
|
||||
|
@ -82,14 +82,14 @@ pub enum Action {
|
|||
Play,
|
||||
Pop,
|
||||
PreviousFrame,
|
||||
Push(Vec<Value>),
|
||||
Push(Vec<Value<'a>>),
|
||||
PushDuplicate,
|
||||
RandomNumber,
|
||||
RemoveSprite,
|
||||
Return,
|
||||
SetMember,
|
||||
SetProperty,
|
||||
SetTarget(String),
|
||||
SetTarget(&'a str),
|
||||
SetTarget2,
|
||||
SetVariable,
|
||||
StackSwap,
|
||||
|
@ -112,7 +112,7 @@ pub enum Action {
|
|||
ToString,
|
||||
ToggleQuality,
|
||||
Trace,
|
||||
Try(TryBlock),
|
||||
Try(TryBlock<'a>),
|
||||
TypeOf,
|
||||
WaitForFrame {
|
||||
frame: u16,
|
||||
|
@ -122,23 +122,23 @@ pub enum Action {
|
|||
num_actions_to_skip: u8,
|
||||
},
|
||||
With {
|
||||
actions: Vec<Action>,
|
||||
actions: &'a [u8],
|
||||
},
|
||||
Unknown {
|
||||
opcode: u8,
|
||||
data: Vec<u8>,
|
||||
data: &'a [u8],
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Value {
|
||||
pub enum Value<'a> {
|
||||
Undefined,
|
||||
Null,
|
||||
Bool(bool),
|
||||
Int(i32),
|
||||
Float(f32),
|
||||
Double(f64),
|
||||
Str(String),
|
||||
Str(&'a str),
|
||||
Register(u8),
|
||||
ConstantPool(u16),
|
||||
}
|
||||
|
@ -151,9 +151,9 @@ pub enum SendVarsMethod {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Function {
|
||||
pub name: String,
|
||||
pub params: Vec<FunctionParam>,
|
||||
pub struct Function<'a> {
|
||||
pub name: &'a str,
|
||||
pub params: Vec<FunctionParam<'a>>,
|
||||
pub preload_parent: bool,
|
||||
pub preload_root: bool,
|
||||
pub suppress_super: bool,
|
||||
|
@ -163,24 +163,24 @@ pub struct Function {
|
|||
pub suppress_this: bool,
|
||||
pub preload_this: bool,
|
||||
pub preload_global: bool,
|
||||
pub actions: Vec<Action>,
|
||||
pub actions: &'a [u8],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct FunctionParam {
|
||||
pub name: String,
|
||||
pub struct FunctionParam<'a> {
|
||||
pub name: &'a str,
|
||||
pub register_index: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TryBlock {
|
||||
pub try_actions: Vec<Action>,
|
||||
pub catch: Option<(CatchVar, Vec<Action>)>,
|
||||
pub finally: Option<Vec<Action>>,
|
||||
pub struct TryBlock<'a> {
|
||||
pub try_actions: &'a [u8],
|
||||
pub catch: Option<(CatchVar<'a>, &'a [u8])>,
|
||||
pub finally: Option<&'a [u8]>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum CatchVar {
|
||||
Var(String),
|
||||
pub enum CatchVar<'a> {
|
||||
Var(&'a str),
|
||||
Register(u8),
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::avm1::types::*;
|
|||
use crate::write::SwfWrite;
|
||||
use std::io::{Result, Write};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Writer<W: Write> {
|
||||
inner: W,
|
||||
version: u8,
|
||||
|
@ -21,14 +22,6 @@ impl<W: Write> Writer<W> {
|
|||
Writer { inner, version }
|
||||
}
|
||||
|
||||
pub fn write_action_list(&mut self, actions: &[Action]) -> Result<()> {
|
||||
for action in actions {
|
||||
self.write_action(action)?;
|
||||
}
|
||||
self.write_u8(0)?; // End
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_action(&mut self, action: &Action) -> Result<()> {
|
||||
match *action {
|
||||
Action::Add => self.write_action_header(OpCode::Add, 0)?,
|
||||
|
@ -61,32 +54,22 @@ impl<W: Write> Writer<W> {
|
|||
ref params,
|
||||
ref actions,
|
||||
} => {
|
||||
let mut action_buf = vec![];
|
||||
{
|
||||
let mut fn_writer = Writer::new(&mut action_buf, self.version);
|
||||
fn_writer.write_action_list(actions)?;
|
||||
}
|
||||
let len = name.len()
|
||||
+ 1
|
||||
+ 2
|
||||
+ params.iter().map(|p| p.len() + 1).sum::<usize>()
|
||||
+ 2
|
||||
+ action_buf.len();
|
||||
+ actions.len();
|
||||
self.write_action_header(OpCode::DefineFunction, len)?;
|
||||
self.write_c_string(name)?;
|
||||
self.write_u16(params.len() as u16)?;
|
||||
for param in params {
|
||||
self.write_c_string(param)?;
|
||||
}
|
||||
self.write_u16(action_buf.len() as u16)?;
|
||||
self.inner.write_all(&action_buf)?;
|
||||
self.write_u16(actions.len() as u16)?;
|
||||
self.inner.write_all(actions)?;
|
||||
}
|
||||
Action::DefineFunction2(ref function) => {
|
||||
let mut action_buf = vec![];
|
||||
{
|
||||
let mut fn_writer = Writer::new(&mut action_buf, self.version);
|
||||
fn_writer.write_action_list(&function.actions)?;
|
||||
}
|
||||
let len = function.name.len()
|
||||
+ 1
|
||||
+ 3
|
||||
|
@ -96,7 +79,7 @@ impl<W: Write> Writer<W> {
|
|||
.map(|p| p.name.len() + 2)
|
||||
.sum::<usize>()
|
||||
+ 4
|
||||
+ action_buf.len();
|
||||
+ function.actions.len();
|
||||
let num_registers = function
|
||||
.params
|
||||
.iter()
|
||||
|
@ -134,8 +117,8 @@ impl<W: Write> Writer<W> {
|
|||
})?;
|
||||
self.write_c_string(¶m.name)?;
|
||||
}
|
||||
self.write_u16(action_buf.len() as u16)?;
|
||||
self.inner.write_all(&action_buf)?;
|
||||
self.write_u16(function.actions.len() as u16)?;
|
||||
self.inner.write_all(&function.actions)?;
|
||||
}
|
||||
Action::DefineLocal => self.write_action_header(OpCode::DefineLocal, 0)?,
|
||||
Action::DefineLocal2 => self.write_action_header(OpCode::DefineLocal2, 0)?,
|
||||
|
@ -290,17 +273,20 @@ impl<W: Write> Writer<W> {
|
|||
let finally_length;
|
||||
let mut action_buf = vec![];
|
||||
{
|
||||
let mut fn_writer = Writer::new(&mut action_buf, self.version);
|
||||
fn_writer.write_action_list(&try_block.try_actions)?;
|
||||
try_length = fn_writer.inner.len();
|
||||
if let Some((_, ref catch)) = try_block.catch {
|
||||
fn_writer.write_action_list(catch)?;
|
||||
}
|
||||
catch_length = fn_writer.inner.len() - try_length;
|
||||
if let Some(ref finally) = try_block.finally {
|
||||
fn_writer.write_action_list(finally)?;
|
||||
}
|
||||
finally_length = fn_writer.inner.len() - (try_length + catch_length);
|
||||
action_buf.write_all(&try_block.try_actions)?;
|
||||
try_length = try_block.try_actions.len();
|
||||
catch_length = if let Some((_, ref catch)) = try_block.catch {
|
||||
action_buf.write_all(catch)?;
|
||||
catch.len()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
finally_length = if let Some(ref finally) = try_block.finally {
|
||||
action_buf.write_all(finally)?;
|
||||
finally.len()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
}
|
||||
let len = 7
|
||||
+ action_buf.len()
|
||||
|
@ -344,13 +330,8 @@ impl<W: Write> Writer<W> {
|
|||
self.write_u8(num_actions_to_skip)?;
|
||||
}
|
||||
Action::With { ref actions } => {
|
||||
let mut action_buf = vec![];
|
||||
{
|
||||
let mut fn_writer = Writer::new(&mut action_buf, self.version);
|
||||
fn_writer.write_action_list(actions)?;
|
||||
}
|
||||
self.write_action_header(OpCode::With, action_buf.len())?;
|
||||
self.inner.write_all(&action_buf)?;
|
||||
self.write_action_header(OpCode::With, actions.len())?;
|
||||
self.inner.write_all(&actions)?;
|
||||
}
|
||||
Action::Unknown { opcode, ref data } => {
|
||||
self.write_opcode_and_length(opcode, data.len())?;
|
||||
|
|
|
@ -21,7 +21,7 @@ pub fn echo_swf(filename: &str) {
|
|||
|
||||
pub type TestData<T> = (u8, T, Vec<u8>);
|
||||
pub type TagTestData = TestData<Tag>;
|
||||
pub type Avm1TestData = TestData<Action>;
|
||||
pub type Avm1TestData = TestData<Action<'static>>;
|
||||
pub type Avm2TestData = TestData<AbcFile>;
|
||||
|
||||
pub fn tag_tests() -> Vec<TagTestData> {
|
||||
|
@ -2339,8 +2339,8 @@ pub fn avm1_tests() -> Vec<Avm1TestData> {
|
|||
(
|
||||
3,
|
||||
Action::GetUrl {
|
||||
url: String::from("a"),
|
||||
target: String::from("b"),
|
||||
url: "a",
|
||||
target: "b",
|
||||
},
|
||||
vec![0x83, 4, 0, 97, 0, 98, 0],
|
||||
),
|
||||
|
@ -2373,7 +2373,7 @@ pub fn avm1_tests() -> Vec<Avm1TestData> {
|
|||
),
|
||||
(
|
||||
3,
|
||||
Action::GotoLabel("testb".to_string()),
|
||||
Action::GotoLabel("testb"),
|
||||
vec![0x8C, 6, 0, 116, 101, 115, 116, 98, 0],
|
||||
),
|
||||
(4, Action::If { offset: 1 }, vec![0x9D, 2, 0, 1, 0]),
|
||||
|
@ -2393,7 +2393,7 @@ pub fn avm1_tests() -> Vec<Avm1TestData> {
|
|||
(3, Action::PreviousFrame, vec![0x05]),
|
||||
(
|
||||
4,
|
||||
Action::Push(vec![Value::Str("test".to_string())]),
|
||||
Action::Push(vec![Value::Str("test")]),
|
||||
vec![0x96, 6, 0, 0, 116, 101, 115, 116, 0],
|
||||
),
|
||||
(
|
||||
|
@ -2456,7 +2456,7 @@ pub fn avm1_tests() -> Vec<Avm1TestData> {
|
|||
(4, Action::RandomNumber, vec![0x30]),
|
||||
(
|
||||
3,
|
||||
Action::SetTarget("test".to_string()),
|
||||
Action::SetTarget("test"),
|
||||
vec![0x8B, 5, 0, 116, 101, 115, 116, 0],
|
||||
),
|
||||
(4, Action::SetVariable, vec![0x1D]),
|
||||
|
@ -2490,7 +2490,7 @@ pub fn avm1_tests() -> Vec<Avm1TestData> {
|
|||
1,
|
||||
Action::Unknown {
|
||||
opcode: 0x79,
|
||||
data: vec![],
|
||||
data: &[],
|
||||
},
|
||||
vec![0x79],
|
||||
),
|
||||
|
@ -2498,7 +2498,7 @@ pub fn avm1_tests() -> Vec<Avm1TestData> {
|
|||
1,
|
||||
Action::Unknown {
|
||||
opcode: 0xA0,
|
||||
data: vec![2, 3],
|
||||
data: &[2, 3],
|
||||
},
|
||||
vec![0xA0, 2, 0, 2, 3],
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue