Implement remaining AVM1 tags; Use Rust 1.13 ? operator

This commit is contained in:
Mike Welsh 2016-11-14 01:03:02 -08:00
parent 94bed0cfd0
commit 461ab7ad15
4 changed files with 460 additions and 79 deletions

View File

@ -81,8 +81,6 @@ pub enum OpCode {
InstanceOf = 0x54, InstanceOf = 0x54,
Enumerate2 = 0x55, Enumerate2 = 0x55,
DoInitAction = 0x59,
BitAnd = 0x60, BitAnd = 0x60,
BitOr = 0x61, BitOr = 0x61,
BitXor = 0x62, BitXor = 0x62,

View File

@ -1,4 +1,4 @@
use avm1::types::{Action, SendVarsMethod, Value}; use avm1::types::*;
use avm1::opcode::OpCode; use avm1::opcode::OpCode;
use read::SwfRead; use read::SwfRead;
use std::io::{Error, ErrorKind, Read, Result}; use std::io::{Error, ErrorKind, Read, Result};
@ -38,14 +38,43 @@ impl<R: Read> Reader<R> {
OpCode::End => return Ok(None), OpCode::End => return Ok(None),
OpCode::Add => Action::Add, OpCode::Add => Action::Add,
OpCode::Add2 => Action::Add2,
OpCode::And => Action::And, OpCode::And => Action::And,
OpCode::AsciiToChar => Action::AsciiToChar, OpCode::AsciiToChar => Action::AsciiToChar,
OpCode::BitAnd => Action::BitAnd,
OpCode::BitLShift => Action::BitLShift,
OpCode::BitOr => Action::BitOr,
OpCode::BitRShift => Action::BitRShift,
OpCode::BitURShift => Action::BitURShift,
OpCode::BitXor => Action::BitXor,
OpCode::Call => Action::Call, OpCode::Call => Action::Call,
OpCode::CallFunction => Action::CallFunction,
OpCode::CallMethod => Action::CallMethod,
OpCode::CastOp => Action::CastOp,
OpCode::CharToAscii => Action::CharToAscii, OpCode::CharToAscii => Action::CharToAscii,
OpCode::CloneSprite => Action::CloneSprite, OpCode::CloneSprite => Action::CloneSprite,
OpCode::ConstantPool => {
let mut constants = vec![];
for _ in 0..action_reader.read_u16()? {
constants.push(action_reader.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::DefineLocal => Action::DefineLocal,
OpCode::DefineLocal2 => Action::DefineLocal2,
OpCode::Delete => Action::Delete,
OpCode::Delete2 => Action::Delete2,
OpCode::Divide => Action::Divide, OpCode::Divide => Action::Divide,
OpCode::EndDrag => Action::EndDrag, OpCode::EndDrag => Action::EndDrag,
OpCode::Enumerate => Action::Enumerate,
OpCode::Enumerate2 => Action::Enumerate2,
OpCode::Equals => Action::Equals, OpCode::Equals => Action::Equals,
OpCode::Equals2 => Action::Equals2,
OpCode::Extends => Action::Extends,
OpCode::GetMember => Action::GetMember,
OpCode::GetProperty => Action::GetProperty, OpCode::GetProperty => Action::GetProperty,
OpCode::GetTime => Action::GetTime, OpCode::GetTime => Action::GetTime,
OpCode::GetUrl => Action::GetUrl { OpCode::GetUrl => Action::GetUrl {
@ -80,14 +109,24 @@ impl<R: Read> Reader<R> {
} }
}, },
OpCode::GotoLabel => Action::GotoLabel(try!(action_reader.read_c_string())), OpCode::GotoLabel => Action::GotoLabel(try!(action_reader.read_c_string())),
OpCode::Greater => Action::Greater,
OpCode::If => Action::If { offset: try!(action_reader.read_i16()) }, OpCode::If => Action::If { offset: try!(action_reader.read_i16()) },
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: try!(action_reader.read_i16()) }, OpCode::Jump => Action::Jump { offset: try!(action_reader.read_i16()) },
OpCode::Less => Action::Less, OpCode::Less => Action::Less,
OpCode::Less2 => Action::Less2,
OpCode::MBAsciiToChar => Action::MBAsciiToChar, OpCode::MBAsciiToChar => Action::MBAsciiToChar,
OpCode::MBCharToAscii => Action::MBCharToAscii, OpCode::MBCharToAscii => Action::MBCharToAscii,
OpCode::MBStringExtract => Action::MBStringExtract, OpCode::MBStringExtract => Action::MBStringExtract,
OpCode::MBStringLength => Action::MBStringLength, OpCode::MBStringLength => Action::MBStringLength,
OpCode::Modulo => Action::Modulo,
OpCode::Multiply => Action::Multiply, OpCode::Multiply => Action::Multiply,
OpCode::NewMethod => Action::NewMethod,
OpCode::NewObject => Action::NewObject,
OpCode::NextFrame => Action::NextFrame, OpCode::NextFrame => Action::NextFrame,
OpCode::Not => Action::Not, OpCode::Not => Action::Not,
OpCode::Or => Action::Or, OpCode::Or => Action::Or,
@ -102,32 +141,52 @@ impl<R: Read> Reader<R> {
}; };
Action::Push(values) Action::Push(values)
}, },
OpCode::PushDuplicate => Action::PushDuplicate,
OpCode::RandomNumber => Action::RandomNumber, OpCode::RandomNumber => Action::RandomNumber,
OpCode::RemoveSprite => Action::RemoveSprite, OpCode::RemoveSprite => Action::RemoveSprite,
OpCode::Return => Action::Return,
OpCode::SetMember => Action::SetMember,
OpCode::SetProperty => Action::SetProperty, OpCode::SetProperty => Action::SetProperty,
OpCode::SetTarget => Action::SetTarget(try!(action_reader.read_c_string())), OpCode::SetTarget => Action::SetTarget(action_reader.read_c_string()?),
OpCode::SetTarget2 => Action::SetTarget2, OpCode::SetTarget2 => Action::SetTarget2,
OpCode::SetVariable => Action::SetVariable, OpCode::SetVariable => Action::SetVariable,
OpCode::StackSwap => Action::StackSwap,
OpCode::StartDrag => Action::StartDrag, OpCode::StartDrag => Action::StartDrag,
OpCode::Stop => Action::Stop, OpCode::Stop => Action::Stop,
OpCode::StopSounds => Action::StopSounds, OpCode::StopSounds => Action::StopSounds,
OpCode::StoreRegister => Action::StoreRegister(action_reader.read_u8()?),
OpCode::StrictEquals => Action::StrictEquals,
OpCode::StringAdd => Action::StringAdd, OpCode::StringAdd => Action::StringAdd,
OpCode::StringEquals => Action::StringEquals, OpCode::StringEquals => Action::StringEquals,
OpCode::StringExtract => Action::StringExtract, OpCode::StringExtract => Action::StringExtract,
OpCode::StringGreater => Action::StringGreater,
OpCode::StringLength => Action::StringLength, OpCode::StringLength => Action::StringLength,
OpCode::StringLess => Action::StringLess, OpCode::StringLess => Action::StringLess,
OpCode::Subtract => Action::Subtract, OpCode::Subtract => Action::Subtract,
OpCode::ToInteger => Action::ToInteger, OpCode::TargetPath => Action::TargetPath,
OpCode::Throw => Action::Throw,
OpCode::ToggleQuality => Action::ToggleQuality, OpCode::ToggleQuality => Action::ToggleQuality,
OpCode::ToInteger => Action::ToInteger,
OpCode::ToNumber => Action::ToNumber,
OpCode::ToString => Action::ToString,
OpCode::Trace => Action::Trace, OpCode::Trace => Action::Trace,
OpCode::Try => action_reader.read_try()?,
OpCode::TypeOf => Action::TypeOf,
OpCode::WaitForFrame => Action::WaitForFrame { OpCode::WaitForFrame => Action::WaitForFrame {
frame: try!(action_reader.read_u16()), frame: try!(action_reader.read_u16()),
num_actions_to_skip: try!(action_reader.read_u8()), num_actions_to_skip: try!(action_reader.read_u8()),
}, },
OpCode::With => {
let code_length = action_reader.read_u16()?;
let mut with_reader = Reader::new(
(&mut action_reader.inner as &mut Read).take(code_length as u64),
self.version
);
Action::With { actions: with_reader.read_action_list()? }
},
OpCode::WaitForFrame2 => Action::WaitForFrame2 { OpCode::WaitForFrame2 => Action::WaitForFrame2 {
num_actions_to_skip: try!(action_reader.read_u8()), num_actions_to_skip: try!(action_reader.read_u8()),
}, },
_ => action_reader.read_unknown_action(opcode, length)?
} }
} else { } else {
action_reader.read_unknown_action(opcode, length)? action_reader.read_unknown_action(opcode, length)?
@ -166,6 +225,97 @@ impl<R: Read> Reader<R> {
}; };
Ok(value) Ok(value)
} }
fn read_define_function(&mut self) -> Result<Action> {
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()?);
}
let code_length = self.read_u16()?;
let mut fn_reader = Reader::new(
(&mut self.inner as &mut Read).take(code_length as u64),
self.version
);
Ok(Action::DefineFunction {
name: name,
params: params,
actions: fn_reader.read_action_list()?,
})
}
fn read_define_function_2(&mut self) -> Result<Action> {
let name = self.read_c_string()?;
let num_params = self.read_u16()?;
let num_registers = self.read_u8()?; // Number of registers
let flags = self.read_u16()?;
let mut params = Vec::with_capacity(num_params as usize + num_registers as usize);
for _ in 0..num_params {
let register = self.read_u8()?;
params.push(FunctionParam {
name: self.read_c_string()?,
register_index: if register == 0 { None } else { Some(register) },
});
}
let code_length = self.read_u16()?;
let mut fn_reader = Reader::new(
(&mut self.inner as &mut Read).take(code_length as u64),
self.version
);
Ok(Action::DefineFunction2(Function {
name: name,
params: params,
preload_global: flags & 0b1_00000000 != 0,
preload_parent: flags & 0b10000000 != 0,
preload_root: flags & 0b1000000 != 0,
suppress_super: flags & 0b100000 != 0,
preload_super: flags & 0b10000 != 0,
suppress_arguments: flags & 0b1000 != 0,
preload_arguments: flags & 0b100 != 0,
suppress_this: flags & 0b10 != 0,
preload_this: flags & 0b1 != 0,
actions: fn_reader.read_action_list()?,
}))
}
fn read_try(&mut self) -> Result<Action> {
let flags = self.read_u8()?;
let try_length = self.read_u16()?;
let catch_length = self.read_u16()?;
let finally_length = self.read_u16()?;
let catch_var = if flags & 0b100 != 0 {
CatchVar::Var(self.read_c_string()?)
} else {
CatchVar::Register(self.read_u8()?)
};
let try_actions = {
let mut fn_reader = Reader::new(
(&mut self.inner as &mut Read).take(try_length as u64),
self.version
);
fn_reader.read_action_list()?
};
let catch_actions = {
let mut fn_reader = Reader::new(
(&mut self.inner as &mut Read).take(catch_length as u64),
self.version
);
fn_reader.read_action_list()?
};
let finally_actions = {
let mut fn_reader = Reader::new(
(&mut self.inner as &mut Read).take(finally_length as u64),
self.version
);
fn_reader.read_action_list()?
};
Ok(Action::Try(TryBlock {
try: try_actions,
catch: if flags & 0b1 != 0 { Some((catch_var, catch_actions)) } else { None },
finally: if flags & 0b10 != 0 { Some(finally_actions) } else { None },
}))
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,14 +1,37 @@
#[derive(Debug,PartialEq)] #[derive(Clone,Debug,PartialEq)]
pub enum Action { pub enum Action {
Add, Add,
Add2,
And, And,
AsciiToChar, AsciiToChar,
BitAnd,
BitLShift,
BitOr,
BitRShift,
BitURShift,
BitXor,
Call, Call,
CallFunction,
CallMethod,
CastOp,
CharToAscii, CharToAscii,
CloneSprite, CloneSprite,
ConstantPool(Vec<String>),
Decrement,
DefineFunction { name: String, params: Vec<String>, actions: Vec<Action> },
DefineFunction2(Function),
DefineLocal,
DefineLocal2,
Delete,
Delete2,
Divide, Divide,
EndDrag, EndDrag,
Enumerate,
Enumerate2,
Equals, Equals,
Equals2,
Extends,
GetMember,
GetProperty, GetProperty,
GetTime, GetTime,
GetUrl { url: String, target: String }, GetUrl { url: String, target: String },
@ -17,14 +40,24 @@ pub enum Action {
GotoFrame(u16), GotoFrame(u16),
GotoFrame2 { set_playing: bool, scene_offset: u16 }, GotoFrame2 { set_playing: bool, scene_offset: u16 },
GotoLabel(String), GotoLabel(String),
Greater,
If { offset: i16 }, If { offset: i16 },
ImplementsOp,
Increment,
InitArray,
InitObject,
InstanceOf,
Jump { offset: i16 }, Jump { offset: i16 },
Less, Less,
Less2,
MBAsciiToChar, MBAsciiToChar,
MBCharToAscii, MBCharToAscii,
MBStringExtract, MBStringExtract,
MBStringLength, MBStringLength,
Modulo,
Multiply, Multiply,
NewMethod,
NewObject,
NextFrame, NextFrame,
Not, Not,
Or, Or,
@ -32,30 +65,44 @@ pub enum Action {
Pop, Pop,
PreviousFrame, PreviousFrame,
Push(Vec<Value>), Push(Vec<Value>),
PushDuplicate,
RandomNumber, RandomNumber,
RemoveSprite, RemoveSprite,
Return,
SetMember,
SetProperty, SetProperty,
SetTarget(String), SetTarget(String),
SetTarget2, SetTarget2,
SetVariable, SetVariable,
StackSwap,
StartDrag, StartDrag,
Stop, Stop,
StopSounds, StopSounds,
StoreRegister(u8),
StrictEquals,
StringAdd, StringAdd,
StringEquals, StringEquals,
StringExtract, StringExtract,
StringGreater,
StringLength, StringLength,
StringLess, StringLess,
Subtract, Subtract,
TargetPath,
Throw,
ToInteger, ToInteger,
ToNumber,
ToString,
ToggleQuality, ToggleQuality,
Trace, Trace,
Try(TryBlock),
TypeOf,
WaitForFrame { frame: u16, num_actions_to_skip: u8 }, WaitForFrame { frame: u16, num_actions_to_skip: u8 },
WaitForFrame2 { num_actions_to_skip: u8 }, WaitForFrame2 { num_actions_to_skip: u8 },
With { actions: Vec<Action> },
Unknown { opcode: u8, data: Vec<u8> }, Unknown { opcode: u8, data: Vec<u8> },
} }
#[derive(Debug,PartialEq)] #[derive(Clone,Debug,PartialEq)]
pub enum Value { pub enum Value {
Undefined, Undefined,
Null, Null,
@ -73,4 +120,39 @@ pub enum SendVarsMethod {
None, None,
Get, Get,
Post, Post,
}
#[derive(Clone,Debug,PartialEq)]
pub struct Function {
pub name: String,
pub params: Vec<FunctionParam>,
pub preload_parent: bool,
pub preload_root: bool,
pub suppress_super: bool,
pub preload_super: bool,
pub suppress_arguments: bool,
pub preload_arguments: bool,
pub suppress_this: bool,
pub preload_this: bool,
pub preload_global: bool,
pub actions: Vec<Action>,
}
#[derive(Clone,Debug,PartialEq,Eq)]
pub struct FunctionParam {
pub name: String,
pub register_index: Option<u8>,
}
#[derive(Clone,Debug,PartialEq)]
pub struct TryBlock {
pub try: Vec<Action>,
pub catch: Option<(CatchVar, Vec<Action>)>,
pub finally: Option<Vec<Action>>,
}
#[derive(Clone,Debug,PartialEq,Eq)]
pub enum CatchVar {
Var(String),
Register(u8),
} }

View File

@ -1,4 +1,4 @@
use avm1::types::{Action, SendVarsMethod, Value}; use avm1::types::*;
use avm1::opcode::OpCode; use avm1::opcode::OpCode;
use write::SwfWrite; use write::SwfWrite;
use std::io::{Result, Write}; use std::io::{Result, Write};
@ -29,24 +29,105 @@ impl<W: Write> Writer<W> {
pub fn write_action(&mut self, action: &Action) -> Result<()> { pub fn write_action(&mut self, action: &Action) -> Result<()> {
match action { match action {
&Action::Add => try!(self.write_action_header(OpCode::Add, 0)), &Action::Add => self.write_action_header(OpCode::Add, 0)?,
&Action::And => try!(self.write_action_header(OpCode::And, 0)), &Action::Add2 => self.write_action_header(OpCode::Add2, 0)?,
&Action::AsciiToChar => try!(self.write_action_header(OpCode::AsciiToChar, 0)), &Action::And => self.write_action_header(OpCode::And, 0)?,
&Action::Call => try!(self.write_action_header(OpCode::Call, 0)), &Action::AsciiToChar => self.write_action_header(OpCode::AsciiToChar, 0)?,
&Action::CharToAscii => try!(self.write_action_header(OpCode::CharToAscii, 0)), &Action::BitAnd => self.write_action_header(OpCode::BitAnd, 0)?,
&Action::CloneSprite => try!(self.write_action_header(OpCode::CloneSprite, 0)), &Action::BitLShift => self.write_action_header(OpCode::BitLShift, 0)?,
&Action::Divide => try!(self.write_action_header(OpCode::Divide, 0)), &Action::BitOr => self.write_action_header(OpCode::BitOr, 0)?,
&Action::EndDrag => try!(self.write_action_header(OpCode::EndDrag, 0)), &Action::BitRShift => self.write_action_header(OpCode::BitRShift, 0)?,
&Action::Equals => try!(self.write_action_header(OpCode::Equals, 0)), &Action::BitURShift => self.write_action_header(OpCode::BitURShift, 0)?,
&Action::GetProperty => try!(self.write_action_header(OpCode::GetProperty, 0)), &Action::BitXor => self.write_action_header(OpCode::BitXor, 0)?,
&Action::GetTime => try!(self.write_action_header(OpCode::GetTime, 0)), &Action::Call => self.write_action_header(OpCode::Call, 0)?,
&Action::CallFunction => self.write_action_header(OpCode::CallFunction, 0)?,
&Action::CallMethod => self.write_action_header(OpCode::CallMethod, 0)?,
&Action::CastOp => self.write_action_header(OpCode::CastOp, 0)?,
&Action::CharToAscii => self.write_action_header(OpCode::CharToAscii, 0)?,
&Action::CloneSprite => self.write_action_header(OpCode::CloneSprite, 0)?,
&Action::ConstantPool(ref constants) => {
let len = 2 + constants.iter().map(|c| c.len() + 1).sum::<usize>();
self.write_action_header(OpCode::ConstantPool, len)?;
self.write_u16(constants.len() as u16)?;
for constant in constants {
self.write_c_string(constant)?;
}
},
&Action::Decrement => self.write_action_header(OpCode::Decrement, 0)?,
&Action::DefineFunction { ref name, 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();
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)?;
},
&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 + function.params.iter().map(|p| p.name.len() + 2).sum::<usize>()
+ 4 + action_buf.len();
let num_registers = function.params.iter().map(
|p| if p.register_index.is_none() { 1 } else { 0 }).sum();
self.write_action_header(OpCode::DefineFunction2, len)?;
self.write_c_string(&function.name)?;
self.write_u16(function.params.len() as u16)?;
self.write_u8(num_registers)?;
let flags =
if function.preload_global { 0b1_00000000 } else { 0 } |
if function.preload_parent { 0b10000000 } else { 0 } |
if function.preload_root { 0b1000000 } else { 0 } |
if function.suppress_super { 0b100000 } else { 0 } |
if function.preload_super { 0b10000 } else { 0 } |
if function.suppress_arguments { 0b1000 } else { 0 } |
if function.preload_arguments { 0b100 } else { 0 } |
if function.suppress_this { 0b10 } else { 0 } |
if function.preload_this { 0b1 } else { 0 };
self.write_u16(flags)?;
for param in &function.params {
self.write_u8(
if let Some(n) = param.register_index { n } else { 0 }
)?;
self.write_c_string(&param.name)?;
}
self.write_u16(action_buf.len() as u16)?;
self.inner.write_all(&action_buf)?;
},
&Action::DefineLocal => self.write_action_header(OpCode::DefineLocal, 0)?,
&Action::DefineLocal2 => self.write_action_header(OpCode::DefineLocal2, 0)?,
&Action::Divide => self.write_action_header(OpCode::Divide, 0)?,
&Action::Delete => self.write_action_header(OpCode::Delete, 0)?,
&Action::Delete2 => self.write_action_header(OpCode::Delete2, 0)?,
&Action::EndDrag => self.write_action_header(OpCode::EndDrag, 0)?,
&Action::Enumerate => self.write_action_header(OpCode::Enumerate, 0)?,
&Action::Enumerate2 => self.write_action_header(OpCode::Enumerate2, 0)?,
&Action::Equals => self.write_action_header(OpCode::Equals, 0)?,
&Action::Equals2 => self.write_action_header(OpCode::Equals2, 0)?,
&Action::Extends => self.write_action_header(OpCode::Extends, 0)?,
&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 { ref url, ref target } => { &Action::GetUrl { ref url, ref target } => {
try!(self.write_action_header(OpCode::GetUrl, url.len() + target.len() + 2)); self.write_action_header(OpCode::GetUrl, url.len() + target.len() + 2)?;
try!(self.write_c_string(url)); self.write_c_string(url)?;
try!(self.write_c_string(target)); self.write_c_string(target)?;
}, },
&Action::GetUrl2 { send_vars_method, is_target_sprite, is_load_vars } => { &Action::GetUrl2 { send_vars_method, is_target_sprite, is_load_vars } => {
try!(self.write_action_header(OpCode::GetUrl2, 1)); self.write_action_header(OpCode::GetUrl2, 1)?;
let flags = let flags =
(match send_vars_method { (match send_vars_method {
SendVarsMethod::None => 0, SendVarsMethod::None => 0,
@ -55,47 +136,57 @@ impl<W: Write> Writer<W> {
} << 6) | } << 6) |
if is_target_sprite { 0b10 } else { 0 } | if is_target_sprite { 0b10 } else { 0 } |
if is_load_vars { 0b1 } else { 0 }; if is_load_vars { 0b1 } else { 0 };
try!(self.write_u8(flags)); self.write_u8(flags)?;
}, },
&Action::GetVariable => try!(self.write_action_header(OpCode::GetVariable, 0)), &Action::GetVariable => self.write_action_header(OpCode::GetVariable, 0)?,
&Action::GotoFrame(frame) => { &Action::GotoFrame(frame) => {
try!(self.write_action_header(OpCode::GotoFrame, 2)); self.write_action_header(OpCode::GotoFrame, 2)?;
try!(self.write_u16(frame)); self.write_u16(frame)?;
}, },
&Action::GotoFrame2 { set_playing, scene_offset } => { &Action::GotoFrame2 { set_playing, scene_offset } => {
if scene_offset != 0 { if scene_offset != 0 {
try!(self.write_action_header(OpCode::GotoFrame2, 3)); self.write_action_header(OpCode::GotoFrame2, 3)?;
try!(self.write_u8(if set_playing { 0b11 } else { 0b01 })); self.write_u8(if set_playing { 0b11 } else { 0b01 })?;
try!(self.write_u16(scene_offset)); self.write_u16(scene_offset)?;
} else { } else {
try!(self.write_action_header(OpCode::GotoFrame2, 1)); self.write_action_header(OpCode::GotoFrame2, 1)?;
try!(self.write_u8(if set_playing { 0b10 } else { 0b00 })); self.write_u8(if set_playing { 0b10 } else { 0b00 })?;
} }
}, },
&Action::GotoLabel(ref label) => { &Action::GotoLabel(ref label) => {
try!(self.write_action_header(OpCode::GotoLabel, label.len() + 1)); self.write_action_header(OpCode::GotoLabel, label.len() + 1)?;
try!(self.write_c_string(label)); self.write_c_string(label)?;
}, },
&Action::Greater => self.write_action_header(OpCode::Greater, 0)?,
&Action::If { offset } => { &Action::If { offset } => {
try!(self.write_action_header(OpCode::If, 2)); self.write_action_header(OpCode::If, 2)?;
try!(self.write_i16(offset)); self.write_i16(offset)?;
}, },
&Action::ImplementsOp => self.write_action_header(OpCode::ImplementsOp, 0)?,
&Action::Increment => self.write_action_header(OpCode::Increment, 0)?,
&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 } => {
try!(self.write_action_header(OpCode::Jump, 2)); self.write_action_header(OpCode::Jump, 2)?;
try!(self.write_i16(offset)); self.write_i16(offset)?;
}, },
&Action::Less => try!(self.write_action_header(OpCode::Less, 0)), &Action::Less => self.write_action_header(OpCode::Less, 0)?,
&Action::MBAsciiToChar => try!(self.write_action_header(OpCode::MBAsciiToChar, 0)), &Action::Less2 => self.write_action_header(OpCode::Less2, 0)?,
&Action::MBCharToAscii => try!(self.write_action_header(OpCode::MBCharToAscii, 0)), &Action::MBAsciiToChar => self.write_action_header(OpCode::MBAsciiToChar, 0)?,
&Action::MBStringExtract => try!(self.write_action_header(OpCode::MBStringExtract, 0)), &Action::MBCharToAscii => self.write_action_header(OpCode::MBCharToAscii, 0)?,
&Action::MBStringLength => try!(self.write_action_header(OpCode::MBStringLength, 0)), &Action::MBStringExtract => self.write_action_header(OpCode::MBStringExtract, 0)?,
&Action::Multiply => try!(self.write_action_header(OpCode::Multiply, 0)), &Action::MBStringLength => self.write_action_header(OpCode::MBStringLength, 0)?,
&Action::NextFrame => try!(self.write_action_header(OpCode::NextFrame, 0)), &Action::Modulo => self.write_action_header(OpCode::Modulo, 0)?,
&Action::Not => try!(self.write_action_header(OpCode::Not, 0)), &Action::Multiply => self.write_action_header(OpCode::Multiply, 0)?,
&Action::Or => try!(self.write_action_header(OpCode::Or, 0)), &Action::NewMethod => self.write_action_header(OpCode::NewMethod, 0)?,
&Action::Play => try!(self.write_action_header(OpCode::Play, 0)), &Action::NewObject => self.write_action_header(OpCode::NewObject, 0)?,
&Action::Pop => try!(self.write_action_header(OpCode::Pop, 0)), &Action::NextFrame => self.write_action_header(OpCode::NextFrame, 0)?,
&Action::PreviousFrame => try!(self.write_action_header(OpCode::PreviousFrame, 0)), &Action::Not => self.write_action_header(OpCode::Not, 0)?,
&Action::Or => self.write_action_header(OpCode::Or, 0)?,
&Action::Play => self.write_action_header(OpCode::Play, 0)?,
&Action::Pop => self.write_action_header(OpCode::Pop, 0)?,
&Action::PreviousFrame => self.write_action_header(OpCode::PreviousFrame, 0)?,
&Action::Push(ref values) => { &Action::Push(ref values) => {
let len = values.iter().map(|v| { let len = values.iter().map(|v| {
match v { match v {
@ -110,44 +201,104 @@ impl<W: Write> Writer<W> {
&Value::ConstantPool(v) => if v < 256 { 2 } else { 3 }, &Value::ConstantPool(v) => if v < 256 { 2 } else { 3 },
} }
}).sum(); }).sum();
try!(self.write_action_header(OpCode::Push, len)); self.write_action_header(OpCode::Push, len)?;
for value in values { for value in values {
try!(self.write_push_value(value)); self.write_push_value(value)?;
} }
}, },
&Action::RandomNumber => try!(self.write_action_header(OpCode::RandomNumber, 0)), &Action::PushDuplicate => self.write_action_header(OpCode::PushDuplicate, 0)?,
&Action::RemoveSprite => try!(self.write_action_header(OpCode::RemoveSprite, 0)), &Action::RandomNumber => self.write_action_header(OpCode::RandomNumber, 0)?,
&Action::SetProperty => try!(self.write_action_header(OpCode::SetProperty, 0)), &Action::RemoveSprite => self.write_action_header(OpCode::RemoveSprite, 0)?,
&Action::Return => self.write_action_header(OpCode::Return, 0)?,
&Action::SetMember => self.write_action_header(OpCode::SetMember, 0)?,
&Action::SetProperty => self.write_action_header(OpCode::SetProperty, 0)?,
&Action::SetTarget(ref target) => { &Action::SetTarget(ref target) => {
try!(self.write_action_header(OpCode::SetTarget, target.len() + 1)); self.write_action_header(OpCode::SetTarget, target.len() + 1)?;
try!(self.write_c_string(target)); self.write_c_string(target)?;
}, },
&Action::SetTarget2 => try!(self.write_action_header(OpCode::SetTarget2, 0)), &Action::SetTarget2 => self.write_action_header(OpCode::SetTarget2, 0)?,
&Action::SetVariable => try!(self.write_action_header(OpCode::SetVariable, 0)), &Action::SetVariable => self.write_action_header(OpCode::SetVariable, 0)?,
&Action::StartDrag => try!(self.write_action_header(OpCode::StartDrag, 0)), &Action::StackSwap => self.write_action_header(OpCode::StackSwap, 0)?,
&Action::Stop => try!(self.write_action_header(OpCode::Stop, 0)), &Action::StartDrag => self.write_action_header(OpCode::StartDrag, 0)?,
&Action::StopSounds => try!(self.write_action_header(OpCode::StopSounds, 0)), &Action::Stop => self.write_action_header(OpCode::Stop, 0)?,
&Action::StringAdd => try!(self.write_action_header(OpCode::StringAdd, 0)), &Action::StopSounds => self.write_action_header(OpCode::StopSounds, 0)?,
&Action::StringEquals => try!(self.write_action_header(OpCode::StringEquals, 0)), &Action::StoreRegister(register) => {
&Action::StringExtract => try!(self.write_action_header(OpCode::StringExtract, 0)), self.write_action_header(OpCode::StoreRegister, 1)?;
&Action::StringLength => try!(self.write_action_header(OpCode::StringLength, 0)), self.write_u8(register)?;
&Action::StringLess => try!(self.write_action_header(OpCode::StringLess, 0)), },
&Action::Subtract => try!(self.write_action_header(OpCode::Subtract, 0)), &Action::StrictEquals => self.write_action_header(OpCode::StrictEquals, 0)?,
&Action::ToInteger => try!(self.write_action_header(OpCode::ToInteger, 0)), &Action::StringAdd => self.write_action_header(OpCode::StringAdd, 0)?,
&Action::ToggleQuality => try!(self.write_action_header(OpCode::ToggleQuality, 0)), &Action::StringEquals => self.write_action_header(OpCode::StringEquals, 0)?,
&Action::Trace => try!(self.write_action_header(OpCode::Trace, 0)), &Action::StringExtract => self.write_action_header(OpCode::StringExtract, 0)?,
&Action::StringGreater => self.write_action_header(OpCode::StringGreater, 0)?,
&Action::StringLength => self.write_action_header(OpCode::StringLength, 0)?,
&Action::StringLess => self.write_action_header(OpCode::StringLess, 0)?,
&Action::Subtract => self.write_action_header(OpCode::Subtract, 0)?,
&Action::TargetPath => self.write_action_header(OpCode::TargetPath, 0)?,
&Action::Throw => self.write_action_header(OpCode::Throw, 0)?,
&Action::ToggleQuality => self.write_action_header(OpCode::ToggleQuality, 0)?,
&Action::ToInteger => self.write_action_header(OpCode::ToInteger, 0)?,
&Action::ToNumber => self.write_action_header(OpCode::ToNumber, 0)?,
&Action::ToString => self.write_action_header(OpCode::ToString, 0)?,
&Action::Trace => self.write_action_header(OpCode::Trace, 0)?,
&Action::Try(ref try_block) => {
let try_length;
let catch_length;
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)?;
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);
}
let len = 7 + action_buf.len() +
if let Some((CatchVar::Var(ref name), _)) = try_block.catch { name.len() + 1 } else { 1 };
self.write_action_header(OpCode::Try, len)?;
self.write_u8(
if let Some((CatchVar::Register(_), _)) = try_block.catch { 0b100 } else { 0 } |
if try_block.finally.is_some() { 0b10 } else { 0 } |
if try_block.catch.is_some() { 0b1 } else { 0 }
)?;
self.write_u16(try_length as u16)?;
self.write_u16(catch_length as u16)?;
self.write_u16(finally_length as u16)?;
match try_block.catch {
Some((CatchVar::Var(ref name), _)) => self.write_c_string(name)?,
Some((CatchVar::Register(i), _)) => self.write_u8(i)?,
_ => (),
}
self.inner.write_all(&action_buf)?;
},
&Action::TypeOf => self.write_action_header(OpCode::TypeOf, 0)?,
&Action::WaitForFrame { frame, num_actions_to_skip } => { &Action::WaitForFrame { frame, num_actions_to_skip } => {
try!(self.write_action_header(OpCode::WaitForFrame, 3)); self.write_action_header(OpCode::WaitForFrame, 3)?;
try!(self.write_u16(frame)); self.write_u16(frame)?;
try!(self.write_u8(num_actions_to_skip)); self.write_u8(num_actions_to_skip)?;
}, },
&Action::WaitForFrame2 { num_actions_to_skip } => { &Action::WaitForFrame2 { num_actions_to_skip } => {
try!(self.write_action_header(OpCode::WaitForFrame2, 1)); self.write_action_header(OpCode::WaitForFrame2, 1)?;
try!(self.write_u8(num_actions_to_skip)); 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)?;
}, },
&Action::Unknown { opcode, ref data } => { &Action::Unknown { opcode, ref data } => {
try!(self.write_opcode_and_length(opcode, data.len())); self.write_opcode_and_length(opcode, data.len())?;
try!(self.inner.write_all(&data)); self.inner.write_all(&data)?;
} }
} }