ruffle/src/avm1/read.rs

185 lines
7.7 KiB
Rust
Raw Normal View History

2016-11-10 02:49:15 +00:00
use avm1::types::{Action, SendVarsMethod, Value};
2016-09-25 20:30:09 +00:00
use avm1::opcode::OpCode;
use read::SwfRead;
2016-11-09 23:50:54 +00:00
use std::io::{Error, ErrorKind, Read, Result};
2016-09-25 20:30:09 +00:00
pub struct Reader<R: Read> {
2016-09-25 20:30:09 +00:00
inner: R,
version: u8,
}
impl<R: Read> SwfRead<R> for Reader<R> {
fn get_inner(&mut self) -> &mut R {
&mut self.inner
}
}
impl<R: Read> Reader<R> {
pub fn new(inner: R, version: u8) -> Reader<R> {
Reader { inner: inner, version: version }
2016-09-25 20:30:09 +00:00
}
2016-09-29 06:36:05 +00:00
pub fn read_action_list(&mut self) -> Result<Vec<Action>> {
let mut actions = Vec::new();
while let Some(action) = try!(self.read_action()) {
actions.push(action);
}
Ok(actions)
}
2016-09-25 20:30:09 +00:00
pub fn read_action(&mut self) -> Result<Option<Action>> {
2016-09-29 06:36:05 +00:00
let (opcode, length) = try!(self.read_opcode_and_length());
let mut action_reader = Reader::new(self.inner.by_ref().take(length as u64), self.version);
2016-09-29 06:36:05 +00:00
use num::FromPrimitive;
let action = match OpCode::from_u8(opcode) {
Some(OpCode::End) => return Ok(None),
2016-11-10 00:10:40 +00:00
Some(OpCode::Add) => Action::Add,
2016-11-10 02:49:15 +00:00
Some(OpCode::And) => Action::And,
Some(OpCode::AsciiToChar) => Action::AsciiToChar,
Some(OpCode::Call) => Action::Call,
Some(OpCode::CharToAscii) => Action::CharToAscii,
Some(OpCode::CloneSprite) => Action::CloneSprite,
2016-11-10 00:10:40 +00:00
Some(OpCode::Divide) => Action::Divide,
2016-11-10 02:49:15 +00:00
Some(OpCode::EndDrag) => Action::EndDrag,
Some(OpCode::Equals) => Action::Equals,
Some(OpCode::GetProperty) => Action::GetProperty,
Some(OpCode::GetTime) => Action::GetTime,
2016-11-06 02:44:49 +00:00
Some(OpCode::GetUrl) => Action::GetUrl {
url: try!(action_reader.read_c_string()),
target: try!(action_reader.read_c_string()),
},
2016-11-10 02:49:15 +00:00
Some(OpCode::GetUrl2) => {
let flags = try!(action_reader.read_u8());
Action::GetUrl2 {
is_target_sprite: flags & 0b10 != 0,
is_load_vars: flags & 0b1 != 0,
send_vars_method: match flags >> 6 {
0 => SendVarsMethod::None,
1 => SendVarsMethod::Get,
2 => SendVarsMethod::Post,
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid HTTP method in ActionGetUrl2")),
}
}
},
Some(OpCode::GetVariable) => Action::GetVariable,
2016-11-06 02:44:49 +00:00
Some(OpCode::GotoFrame) => {
let frame = try!(action_reader.read_u16());
2016-11-06 02:44:49 +00:00
Action::GotoFrame(frame)
},
2016-11-10 02:49:15 +00:00
Some(OpCode::GotoFrame2) => {
let flags = try!(action_reader.read_u8());
Action::GotoFrame2 {
set_playing: flags & 0b1 != 0,
scene_offset: if flags & 0b10 != 0 {
try!(action_reader.read_u16())
} else { 0 },
}
},
2016-11-09 00:53:36 +00:00
Some(OpCode::GotoLabel) => Action::GotoLabel(try!(action_reader.read_c_string())),
2016-11-10 02:49:15 +00:00
Some(OpCode::If) => Action::If { offset: try!(action_reader.read_i16()) },
Some(OpCode::Jump) => Action::Jump { offset: try!(action_reader.read_i16()) },
Some(OpCode::Less) => Action::Less,
Some(OpCode::MBAsciiToChar) => Action::MBAsciiToChar,
Some(OpCode::MBCharToAscii) => Action::MBCharToAscii,
Some(OpCode::MBStringExtract) => Action::MBStringExtract,
Some(OpCode::MBStringLength) => Action::MBStringLength,
2016-11-10 00:10:40 +00:00
Some(OpCode::Multiply) => Action::Multiply,
2016-09-29 06:36:05 +00:00
Some(OpCode::NextFrame) => Action::NextFrame,
2016-11-10 02:49:15 +00:00
Some(OpCode::Not) => Action::Not,
Some(OpCode::Or) => Action::Or,
2016-10-07 07:18:56 +00:00
Some(OpCode::Play) => Action::Play,
2016-11-09 01:05:53 +00:00
Some(OpCode::Pop) => Action::Pop,
2016-10-07 07:18:56 +00:00
Some(OpCode::PreviousFrame) => Action::PreviousFrame,
2016-11-09 23:50:54 +00:00
// TODO: Verify correct version for complex types.
Some(OpCode::Push) => {
let mut values = vec![];
while let Ok(value) = action_reader.read_push_value() {
values.push(value);
};
Action::Push(values)
},
2016-11-10 02:49:15 +00:00
Some(OpCode::RandomNumber) => Action::RandomNumber,
Some(OpCode::RemoveSprite) => Action::RemoveSprite,
Some(OpCode::SetProperty) => Action::SetProperty,
2016-11-08 23:35:31 +00:00
Some(OpCode::SetTarget) => Action::SetTarget(try!(action_reader.read_c_string())),
2016-11-10 02:49:15 +00:00
Some(OpCode::SetTarget2) => Action::SetTarget2,
Some(OpCode::SetVariable) => Action::SetVariable,
Some(OpCode::StartDrag) => Action::StartDrag,
2016-10-07 07:18:56 +00:00
Some(OpCode::Stop) => Action::Stop,
Some(OpCode::StopSounds) => Action::StopSounds,
2016-11-10 02:49:15 +00:00
Some(OpCode::StringAdd) => Action::StringAdd,
Some(OpCode::StringEquals) => Action::StringEquals,
Some(OpCode::StringExtract) => Action::StringExtract,
Some(OpCode::StringLength) => Action::StringLength,
Some(OpCode::StringLess) => Action::StringLess,
2016-11-10 00:10:40 +00:00
Some(OpCode::Subtract) => Action::Subtract,
2016-11-10 02:49:15 +00:00
Some(OpCode::ToInteger) => Action::ToInteger,
2016-10-07 07:18:56 +00:00
Some(OpCode::ToggleQuality) => Action::ToggleQuality,
2016-11-10 02:49:15 +00:00
Some(OpCode::Trace) => Action::Trace,
2016-11-06 02:44:49 +00:00
Some(OpCode::WaitForFrame) => Action::WaitForFrame {
frame: try!(action_reader.read_u16()),
num_actions_to_skip: try!(action_reader.read_u8()),
2016-11-10 02:49:15 +00:00
},
Some(OpCode::WaitForFrame2) => Action::WaitForFrame2 {
num_actions_to_skip: try!(action_reader.read_u8()),
2016-11-06 02:44:49 +00:00
},
2016-09-29 06:36:05 +00:00
_ => {
let mut data = Vec::with_capacity(length);
try!(action_reader.inner.read_to_end(&mut data));
Action::Unknown { opcode: opcode, data: data }
}
};
Ok(Some(action))
}
pub fn read_opcode_and_length(&mut self) -> Result<(u8, usize)> {
let opcode = try!(self.read_u8());
2016-09-29 06:36:05 +00:00
let length = if opcode >= 0x80 {
try!(self.read_u16()) as usize
2016-09-29 06:36:05 +00:00
} else { 0 };
Ok((opcode, length))
2016-09-25 20:30:09 +00:00
}
2016-11-09 23:50:54 +00:00
fn read_push_value(&mut self) -> Result<Value> {
let value = match try!(self.read_u8()) {
0 => Value::Str(try!(self.read_c_string())),
1 => Value::Float(try!(self.read_f32())),
2 => Value::Null,
3 => Value::Undefined,
4 => Value::Register(try!(self.read_u8())),
5 => Value::Bool(try!(self.read_u8()) != 0),
6 => Value::Double(try!(self.read_f64())),
7 => Value::Int(try!(self.read_u32())),
8 => Value::ConstantPool(try!(self.read_u8()) as u16),
9 => Value::ConstantPool(try!(self.read_u16())),
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid value type in ActionPush")),
};
Ok(value)
}
2016-10-06 06:49:34 +00:00
}
#[cfg(test)]
pub mod tests {
use super::*;
use test_data;
#[test]
fn read_action() {
for (swf_version, expected_action, action_bytes) in test_data::avm1_tests() {
let mut reader = Reader::new(&action_bytes[..], swf_version);
let parsed_action = reader.read_action().unwrap().unwrap();
if parsed_action != expected_action {
// Failed, result doesn't match.
panic!(
"Incorrectly parsed action.\nRead:\n{:?}\n\nExpected:\n{:?}",
parsed_action,
expected_action
);
}
}
}
2016-09-25 20:30:09 +00:00
}