swf: Add custom error type for SWF/AVM1 parse errors
Improves the error handling for the swf crate: * Custom swf::error::Error type added to handle various errors in SWF parsing. * Invalid parsing of tags/AVM1 ops results in a Error::ParseError that can include info about the underlying failure. * Implement Display for these errors. Output descriptive names for the tag/opcode when it fails to parse. * Handle out of bounds reads in avm1::Reader::read_slice. Previously this would panic, now it returns an io::Error. Closes #85.
This commit is contained in:
parent
40722dcef0
commit
3058b88011
|
@ -1,4 +1,4 @@
|
|||
mod opcode;
|
||||
pub(crate) mod opcode;
|
||||
pub mod read;
|
||||
pub mod types;
|
||||
pub mod write;
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
use crate::avm1::opcode::OpCode;
|
||||
use crate::avm1::types::*;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::read::SwfRead;
|
||||
use std::io::{Cursor, Error, ErrorKind, Result};
|
||||
use std::io::Cursor;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Reader<'a> {
|
||||
|
@ -37,11 +38,13 @@ impl<'a> Reader<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn read_slice(&mut self, len: usize) -> &'a [u8] {
|
||||
fn read_slice(&mut self, len: usize) -> Result<&'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
|
||||
let slice = self.inner.get_ref().get(pos..pos + len).ok_or_else(|| {
|
||||
std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "Buffer underrun")
|
||||
})?;
|
||||
Ok(slice)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -60,15 +63,50 @@ impl<'a> Reader<'a> {
|
|||
// 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"))
|
||||
std::str::from_utf8(str_slice).map_err(|_| Error::invalid_data("Invalid string data"))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_action(&mut self) -> Result<Option<Action<'a>>> {
|
||||
use num_traits::FromPrimitive;
|
||||
let (opcode, mut length) = self.read_opcode_and_length()?;
|
||||
|
||||
let (opcode, length) = self.read_opcode_and_length()?;
|
||||
let start_pos = self.pos();
|
||||
let action = self.read_op(opcode, &mut length);
|
||||
if let Err(e) = action {
|
||||
return Err(Error::avm1_parse_error_with_source(opcode, e));
|
||||
}
|
||||
|
||||
// Verify that we parsed the correct amount of data.
|
||||
let end_pos = start_pos + length;
|
||||
let pos = self.pos();
|
||||
if pos != end_pos {
|
||||
// We incorrectly parsed this action.
|
||||
// Re-sync to the expected end of the action and throw an error.
|
||||
use std::convert::TryInto;
|
||||
self.inner.set_position(end_pos.try_into().unwrap());
|
||||
return Err(Error::avm1_parse_error(opcode));
|
||||
}
|
||||
action
|
||||
}
|
||||
|
||||
pub fn read_opcode_and_length(&mut self) -> Result<(u8, usize)> {
|
||||
let opcode = self.read_u8()?;
|
||||
let length = if opcode >= 0x80 {
|
||||
self.read_u16()? as usize
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Ok((opcode, length))
|
||||
}
|
||||
|
||||
/// Reads an action with the given opcode.
|
||||
/// `length` is an in-out parameter and will be modified in the case of instructions
|
||||
/// that contain sub-blocks of code, such as `DefineFunction`.
|
||||
/// The `length` passed in should be the length excluding any sub-blocks.
|
||||
/// The final `length` returned will be total length of the action, including sub-blocks.
|
||||
#[inline]
|
||||
fn read_op(&mut self, opcode: u8, length: &mut usize) -> Result<Option<Action<'a>>> {
|
||||
use num_traits::FromPrimitive;
|
||||
let action = if let Some(op) = OpCode::from_u8(opcode) {
|
||||
match op {
|
||||
OpCode::End => return Ok(None),
|
||||
|
@ -97,8 +135,8 @@ impl<'a> Reader<'a> {
|
|||
Action::ConstantPool(constants)
|
||||
}
|
||||
OpCode::Decrement => Action::Decrement,
|
||||
OpCode::DefineFunction => self.read_define_function()?,
|
||||
OpCode::DefineFunction2 => self.read_define_function_2()?,
|
||||
OpCode::DefineFunction => self.read_define_function(length)?,
|
||||
OpCode::DefineFunction2 => self.read_define_function_2(length)?,
|
||||
OpCode::DefineLocal => Action::DefineLocal,
|
||||
OpCode::DefineLocal2 => Action::DefineLocal2,
|
||||
OpCode::Delete => Action::Delete,
|
||||
|
@ -127,10 +165,9 @@ impl<'a> Reader<'a> {
|
|||
1 => SendVarsMethod::Get,
|
||||
2 => SendVarsMethod::Post,
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"Invalid HTTP method in ActionGetUrl2",
|
||||
))
|
||||
));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -181,7 +218,7 @@ 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 => self.read_push(*length)?,
|
||||
OpCode::PushDuplicate => Action::PushDuplicate,
|
||||
OpCode::RandomNumber => Action::RandomNumber,
|
||||
OpCode::RemoveSprite => Action::RemoveSprite,
|
||||
|
@ -211,16 +248,17 @@ impl<'a> Reader<'a> {
|
|||
OpCode::ToNumber => Action::ToNumber,
|
||||
OpCode::ToString => Action::ToString,
|
||||
OpCode::Trace => Action::Trace,
|
||||
OpCode::Try => self.read_try()?,
|
||||
OpCode::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 = self.read_u16()?;
|
||||
let code_length = usize::from(self.read_u16()?);
|
||||
*length += code_length;
|
||||
Action::With {
|
||||
actions: self.read_slice(code_length.into()),
|
||||
actions: self.read_slice(code_length)?,
|
||||
}
|
||||
}
|
||||
OpCode::WaitForFrame2 => Action::WaitForFrame2 {
|
||||
|
@ -228,26 +266,16 @@ impl<'a> Reader<'a> {
|
|||
},
|
||||
}
|
||||
} else {
|
||||
self.read_unknown_action(opcode, length)?
|
||||
self.read_unknown_action(opcode, *length)?
|
||||
};
|
||||
|
||||
Ok(Some(action))
|
||||
}
|
||||
|
||||
pub fn read_opcode_and_length(&mut self) -> Result<(u8, usize)> {
|
||||
let opcode = self.read_u8()?;
|
||||
let length = if opcode >= 0x80 {
|
||||
self.read_u16()? as usize
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Ok((opcode, length))
|
||||
}
|
||||
|
||||
fn read_unknown_action(&mut self, opcode: u8, length: usize) -> Result<Action<'a>> {
|
||||
Ok(Action::Unknown {
|
||||
opcode,
|
||||
data: self.read_slice(length),
|
||||
data: self.read_slice(length)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -272,17 +300,12 @@ impl<'a> Reader<'a> {
|
|||
7 => Value::Int(self.read_i32()?),
|
||||
8 => Value::ConstantPool(self.read_u8()?.into()),
|
||||
9 => Value::ConstantPool(self.read_u16()?),
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Invalid value type in ActionPush",
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::invalid_data("Invalid value type in ActionPush")),
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn read_define_function(&mut self) -> Result<Action<'a>> {
|
||||
fn read_define_function(&mut self, action_length: &mut usize) -> 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);
|
||||
|
@ -290,15 +313,16 @@ impl<'a> Reader<'a> {
|
|||
params.push(self.read_c_string()?);
|
||||
}
|
||||
// code_length isn't included in the DefineFunction's action length.
|
||||
let code_length = self.read_u16()?;
|
||||
let code_length = usize::from(self.read_u16()?);
|
||||
*action_length += code_length;
|
||||
Ok(Action::DefineFunction {
|
||||
name,
|
||||
params,
|
||||
actions: self.read_slice(code_length.into()),
|
||||
actions: self.read_slice(code_length)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_define_function_2(&mut self) -> Result<Action<'a>> {
|
||||
fn read_define_function_2(&mut self, action_length: &mut usize) -> Result<Action<'a>> {
|
||||
let name = self.read_c_string()?;
|
||||
let num_params = self.read_u16()?;
|
||||
let num_registers = self.read_u8()?; // Number of registers
|
||||
|
@ -312,7 +336,8 @@ impl<'a> Reader<'a> {
|
|||
});
|
||||
}
|
||||
// code_length isn't included in the DefineFunction's length.
|
||||
let code_length = self.read_u16()?;
|
||||
let code_length = usize::from(self.read_u16()?);
|
||||
*action_length += code_length;
|
||||
Ok(Action::DefineFunction2(Function {
|
||||
name,
|
||||
params,
|
||||
|
@ -325,23 +350,24 @@ impl<'a> Reader<'a> {
|
|||
preload_arguments: flags & 0b100 != 0,
|
||||
suppress_this: flags & 0b10 != 0,
|
||||
preload_this: flags & 0b1 != 0,
|
||||
actions: self.read_slice(code_length.into()),
|
||||
actions: self.read_slice(code_length)?,
|
||||
}))
|
||||
}
|
||||
|
||||
fn read_try(&mut self) -> Result<Action<'a>> {
|
||||
fn read_try(&mut self, length: &mut usize) -> Result<Action<'a>> {
|
||||
let flags = self.read_u8()?;
|
||||
let try_length = self.read_u16()?;
|
||||
let catch_length = self.read_u16()?;
|
||||
let finally_length = self.read_u16()?;
|
||||
let try_length = usize::from(self.read_u16()?);
|
||||
let catch_length = usize::from(self.read_u16()?);
|
||||
let finally_length = usize::from(self.read_u16()?);
|
||||
*length += try_length + catch_length + finally_length;
|
||||
let catch_var = if flags & 0b100 != 0 {
|
||||
CatchVar::Var(self.read_c_string()?)
|
||||
} else {
|
||||
CatchVar::Register(self.read_u8()?)
|
||||
};
|
||||
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());
|
||||
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 {
|
||||
try_actions,
|
||||
catch: if flags & 0b1 != 0 {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::avm2::types::*;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::read::SwfRead;
|
||||
use std::io::{Error, ErrorKind, Read, Result};
|
||||
use std::io::Read;
|
||||
|
||||
pub struct Reader<R: Read> {
|
||||
inner: R,
|
||||
|
@ -138,7 +139,7 @@ impl<R: Read> Reader<R> {
|
|||
0x18 => Namespace::Protected(name),
|
||||
0x19 => Namespace::Explicit(name),
|
||||
0x1a => Namespace::StaticProtected(name),
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid namespace kind")),
|
||||
_ => return Err(Error::invalid_data("Invalid namespace kind")),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -184,7 +185,7 @@ impl<R: Read> Reader<R> {
|
|||
0x1c => Multiname::MultinameLA {
|
||||
namespace_set: self.read_index()?,
|
||||
},
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid multiname kind")),
|
||||
_ => return Err(Error::invalid_data("Invalid multiname kind")),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -314,7 +315,7 @@ impl<R: Read> Reader<R> {
|
|||
0x18 => DefaultValue::Protected(Index::new(index)),
|
||||
0x19 => DefaultValue::Explicit(Index::new(index)),
|
||||
0x1a => DefaultValue::StaticProtected(Index::new(index)),
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid default value")),
|
||||
_ => return Err(Error::invalid_data("Invalid default value")),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -339,7 +340,7 @@ impl<R: Read> Reader<R> {
|
|||
0x18 => DefaultValue::Protected(Index::new(index)),
|
||||
0x19 => DefaultValue::Explicit(Index::new(index)),
|
||||
0x1a => DefaultValue::StaticProtected(Index::new(index)),
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid default value")),
|
||||
_ => return Err(Error::invalid_data("Invalid default value")),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -455,7 +456,7 @@ impl<R: Read> Reader<R> {
|
|||
type_name: self.read_index()?,
|
||||
value: self.read_optional_value()?,
|
||||
},
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid trait kind")),
|
||||
_ => return Err(Error::invalid_data("Invalid trait kind")),
|
||||
};
|
||||
|
||||
let mut metadata = vec![];
|
||||
|
@ -522,7 +523,7 @@ impl<R: Read> Reader<R> {
|
|||
|
||||
let opcode = match OpCode::from_u8(self.read_u8()?) {
|
||||
Some(o) => o,
|
||||
None => return Err(Error::new(ErrorKind::InvalidData, "Invalid opcode")),
|
||||
None => return Err(Error::invalid_data("Invalid opcode")),
|
||||
};
|
||||
|
||||
let op = match opcode {
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
use std::{borrow, error, fmt, io};
|
||||
|
||||
/// A `Result` from reading SWF data.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// An error occurred while parsing an AVM1 action.
|
||||
/// This can contain sub-errors with further information (`Error::source`)
|
||||
Avm1ParseError {
|
||||
opcode: u8,
|
||||
source: Option<Box<dyn error::Error + 'static>>,
|
||||
},
|
||||
|
||||
/// Invalid or unknown data was encountered.
|
||||
InvalidData(borrow::Cow<'static, str>),
|
||||
|
||||
/// An error occurred while parsing an SWF tag.
|
||||
/// This can contain sub-errors with further information (`Error::source`)
|
||||
SwfParseError {
|
||||
tag_code: u16,
|
||||
source: Option<Box<dyn error::Error + 'static>>,
|
||||
},
|
||||
/// An IO error occurred (probably unexpected EOF).
|
||||
IoError(io::Error),
|
||||
/// This SWF requires unsupported features.
|
||||
Unsupported(borrow::Cow<'static, str>),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Helper method to create `Error::Avm1ParseError`.
|
||||
#[inline]
|
||||
pub fn avm1_parse_error(opcode: u8) -> Self {
|
||||
Error::Avm1ParseError {
|
||||
opcode,
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
/// Helper method to create `Error::Avm1ParseError`.
|
||||
#[inline]
|
||||
pub fn avm1_parse_error_with_source(opcode: u8, source: impl error::Error + 'static) -> Self {
|
||||
Error::Avm1ParseError {
|
||||
opcode,
|
||||
source: Some(Box::new(source)),
|
||||
}
|
||||
}
|
||||
/// Helper method to create `Error::InvalidData`.
|
||||
#[inline]
|
||||
pub fn invalid_data(message: impl Into<borrow::Cow<'static, str>>) -> Self {
|
||||
Error::InvalidData(message.into())
|
||||
}
|
||||
/// Helper method to create `Error::SwfParseError`.
|
||||
#[inline]
|
||||
pub fn swf_parse_error(tag_code: u16) -> Self {
|
||||
Error::SwfParseError {
|
||||
tag_code,
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
/// Helper method to create `Error::SwfParseError`.
|
||||
#[inline]
|
||||
pub fn swf_parse_error_with_source(tag_code: u16, source: impl error::Error + 'static) -> Self {
|
||||
Error::SwfParseError {
|
||||
tag_code,
|
||||
source: Some(Box::new(source)),
|
||||
}
|
||||
}
|
||||
/// Helper method to create `Error::Unsupported`.
|
||||
#[inline]
|
||||
pub fn unsupported(message: impl Into<borrow::Cow<'static, str>>) -> Self {
|
||||
Error::Unsupported(message.into())
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
"Error parsing AVM1 action ".fmt(f)?;
|
||||
if let Some(op) = op {
|
||||
write!(f, "{:?}", op)?;
|
||||
} else {
|
||||
write!(f, "Unknown({})", opcode)?;
|
||||
};
|
||||
if let Some(source) = source {
|
||||
write!(f, ": {}", source)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Error::SwfParseError { tag_code, source } => {
|
||||
let tag = crate::tag_code::TagCode::from_u16(*tag_code);
|
||||
"Error parsing SWF tag ".fmt(f)?;
|
||||
if let Some(tag) = tag {
|
||||
write!(f, "{:?}", tag)?;
|
||||
} else {
|
||||
write!(f, "Unknown({})", tag_code)?;
|
||||
};
|
||||
if let Some(source) = source {
|
||||
write!(f, ": {}", source)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Error::IoError(e) => e.fmt(f),
|
||||
Error::InvalidData(message) => write!(f, "Invalid data: {}", message),
|
||||
Error::Unsupported(message) => write!(f, "Unsupported data: {}", message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
use std::ops::Deref;
|
||||
match self {
|
||||
Error::Avm1ParseError { source, .. } => source.as_ref().map(|s| s.deref()),
|
||||
Error::IoError(e) => e.source(),
|
||||
Error::InvalidData(_) => None,
|
||||
Error::SwfParseError { source, .. } => source.as_ref().map(|s| s.deref()),
|
||||
Error::Unsupported(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(error: io::Error) -> Error {
|
||||
Error::IoError(error)
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ extern crate xz2;
|
|||
|
||||
pub mod avm1;
|
||||
pub mod avm2;
|
||||
pub mod error;
|
||||
pub mod read;
|
||||
mod tag_code;
|
||||
mod types;
|
||||
|
|
159
swf/src/read.rs
159
swf/src/read.rs
|
@ -4,10 +4,11 @@
|
|||
clippy::unreadable_literal
|
||||
)]
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::types::*;
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use std::collections::HashSet;
|
||||
use std::io::{Error, ErrorKind, Read, Result};
|
||||
use std::io::{self, Read};
|
||||
|
||||
/// Convenience method to parse an SWF.
|
||||
///
|
||||
|
@ -91,8 +92,7 @@ fn make_zlib_reader<'a, R: Read + 'a>(input: R) -> Result<Box<dyn Read + 'a>> {
|
|||
|
||||
#[cfg(not(any(feature = "flate2", feature = "libflate")))]
|
||||
fn make_zlib_reader<'a, R: Read + 'a>(_input: R) -> Result<Box<dyn Read + 'a>> {
|
||||
Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
Err(Error::unsupported(
|
||||
"Support for Zlib compressed SWFs is not enabled.",
|
||||
))
|
||||
}
|
||||
|
@ -117,56 +117,55 @@ fn make_lzma_reader<'a, R: Read + 'a>(mut input: R) -> Result<Box<dyn Read + 'a>
|
|||
|
||||
#[cfg(not(feature = "lzma-support"))]
|
||||
fn make_lzma_reader<'a, R: Read + 'a>(_input: R) -> Result<Box<dyn Read + 'a>> {
|
||||
Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Support for LZMA compressed SWFs is not enabled.",
|
||||
Err(Error::unsupported(
|
||||
"Support for Zlib compressed SWFs is not enabled.",
|
||||
))
|
||||
}
|
||||
|
||||
pub trait SwfRead<R: Read> {
|
||||
fn get_inner(&mut self) -> &mut R;
|
||||
|
||||
fn read_u8(&mut self) -> Result<u8> {
|
||||
fn read_u8(&mut self) -> io::Result<u8> {
|
||||
self.get_inner().read_u8()
|
||||
}
|
||||
|
||||
fn read_u16(&mut self) -> Result<u16> {
|
||||
fn read_u16(&mut self) -> io::Result<u16> {
|
||||
self.get_inner().read_u16::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn read_u32(&mut self) -> Result<u32> {
|
||||
fn read_u32(&mut self) -> io::Result<u32> {
|
||||
self.get_inner().read_u32::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn read_u64(&mut self) -> Result<u64> {
|
||||
fn read_u64(&mut self) -> io::Result<u64> {
|
||||
self.get_inner().read_u64::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn read_i8(&mut self) -> Result<i8> {
|
||||
fn read_i8(&mut self) -> io::Result<i8> {
|
||||
self.get_inner().read_i8()
|
||||
}
|
||||
|
||||
fn read_i16(&mut self) -> Result<i16> {
|
||||
fn read_i16(&mut self) -> io::Result<i16> {
|
||||
self.get_inner().read_i16::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn read_i32(&mut self) -> Result<i32> {
|
||||
fn read_i32(&mut self) -> io::Result<i32> {
|
||||
self.get_inner().read_i32::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn read_fixed8(&mut self) -> Result<f32> {
|
||||
fn read_fixed8(&mut self) -> io::Result<f32> {
|
||||
self.read_i16().map(|n| f32::from(n) / 256f32)
|
||||
}
|
||||
|
||||
fn read_fixed16(&mut self) -> Result<f64> {
|
||||
fn read_fixed16(&mut self) -> io::Result<f64> {
|
||||
self.read_i32().map(|n| f64::from(n) / 65536f64)
|
||||
}
|
||||
|
||||
fn read_f32(&mut self) -> Result<f32> {
|
||||
fn read_f32(&mut self) -> io::Result<f32> {
|
||||
self.get_inner().read_f32::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn read_f64(&mut self) -> Result<f64> {
|
||||
fn read_f64(&mut self) -> io::Result<f64> {
|
||||
// Flash weirdly stores f64 as two LE 32-bit chunks.
|
||||
// First word is the hi-word, second word is the lo-word.
|
||||
let mut num = [0u8; 8];
|
||||
|
@ -189,8 +188,7 @@ pub trait SwfRead<R: Read> {
|
|||
}
|
||||
// TODO: There is probably a better way to do this.
|
||||
// TODO: Verify ANSI for SWF 5 and earlier.
|
||||
String::from_utf8(bytes)
|
||||
.map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid string data"))
|
||||
String::from_utf8(bytes).map_err(|_| Error::invalid_data("Invalid string data"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,42 +208,42 @@ impl<R: Read> SwfRead<R> for Reader<R> {
|
|||
&mut self.input
|
||||
}
|
||||
|
||||
fn read_u8(&mut self) -> Result<u8> {
|
||||
fn read_u8(&mut self) -> io::Result<u8> {
|
||||
self.byte_align();
|
||||
self.input.read_u8()
|
||||
}
|
||||
|
||||
fn read_u16(&mut self) -> Result<u16> {
|
||||
fn read_u16(&mut self) -> io::Result<u16> {
|
||||
self.byte_align();
|
||||
self.input.read_u16::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn read_u32(&mut self) -> Result<u32> {
|
||||
fn read_u32(&mut self) -> io::Result<u32> {
|
||||
self.byte_align();
|
||||
self.input.read_u32::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn read_i8(&mut self) -> Result<i8> {
|
||||
fn read_i8(&mut self) -> io::Result<i8> {
|
||||
self.byte_align();
|
||||
self.input.read_i8()
|
||||
}
|
||||
|
||||
fn read_i16(&mut self) -> Result<i16> {
|
||||
fn read_i16(&mut self) -> io::Result<i16> {
|
||||
self.byte_align();
|
||||
self.input.read_i16::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn read_i32(&mut self) -> Result<i32> {
|
||||
fn read_i32(&mut self) -> io::Result<i32> {
|
||||
self.byte_align();
|
||||
self.input.read_i32::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn read_f32(&mut self) -> Result<f32> {
|
||||
fn read_f32(&mut self) -> io::Result<f32> {
|
||||
self.byte_align();
|
||||
self.input.read_f32::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn read_f64(&mut self) -> Result<f64> {
|
||||
fn read_f64(&mut self) -> io::Result<f64> {
|
||||
self.byte_align();
|
||||
self.input.read_f64::<LittleEndian>()
|
||||
}
|
||||
|
@ -286,7 +284,16 @@ impl<R: Read> Reader<R> {
|
|||
/// ```
|
||||
pub fn read_tag(&mut self) -> Result<Tag> {
|
||||
let (tag_code, length) = self.read_tag_code_and_length()?;
|
||||
let tag = self.read_tag_with_code(tag_code, length);
|
||||
|
||||
if let Err(e) = tag {
|
||||
return Err(Error::swf_parse_error_with_source(tag_code, e));
|
||||
}
|
||||
|
||||
tag
|
||||
}
|
||||
|
||||
fn read_tag_with_code(&mut self, tag_code: u16, length: usize) -> Result<Tag> {
|
||||
let mut tag_reader = Reader::new(self.input.by_ref().take(length as u64), self.version);
|
||||
use crate::tag_code::TagCode;
|
||||
let tag = match TagCode::from_u16(tag_code) {
|
||||
|
@ -634,7 +641,7 @@ impl<R: Read> Reader<R> {
|
|||
b"FWS" => Compression::None,
|
||||
b"CWS" => Compression::Zlib,
|
||||
b"ZWS" => Compression::Lzma,
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid SWF")),
|
||||
_ => return Err(Error::invalid_data("Invalid SWF")),
|
||||
};
|
||||
Ok(compression)
|
||||
}
|
||||
|
@ -703,7 +710,8 @@ impl<R: Read> Reader<R> {
|
|||
}
|
||||
|
||||
pub fn read_character_id(&mut self) -> Result<CharacterId> {
|
||||
self.read_u16()
|
||||
let id = self.read_u16()?;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub fn read_rgb(&mut self) -> Result<Color> {
|
||||
|
@ -809,7 +817,7 @@ impl<R: Read> Reader<R> {
|
|||
3 => Language::Korean,
|
||||
4 => Language::SimplifiedChinese,
|
||||
5 => Language::TraditionalChinese,
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid language code.")),
|
||||
_ => return Err(Error::invalid_data("Invalid language code")),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -821,11 +829,6 @@ impl<R: Read> Reader<R> {
|
|||
Ok(tag) => tags.push(tag),
|
||||
Err(err) => {
|
||||
// We screwed up reading this tag in some way.
|
||||
// TODO: We could recover more gracefully in some way.
|
||||
// Simply throw away this tag, but keep going.
|
||||
if cfg!(debug_assertions) {
|
||||
panic!("Error reading tag");
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
@ -1011,12 +1014,7 @@ impl<R: Read> Reader<R> {
|
|||
0b00_000 => TextGridFit::None,
|
||||
0b01_000 => TextGridFit::Pixel,
|
||||
0b10_000 => TextGridFit::SubPixel,
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Invalid text grid fitting",
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::invalid_data("Invalid text grid fitting")),
|
||||
},
|
||||
thickness,
|
||||
sharpness,
|
||||
|
@ -1239,12 +1237,7 @@ impl<R: Read> Reader<R> {
|
|||
0b00_000000 => FontThickness::Thin,
|
||||
0b01_000000 => FontThickness::Medium,
|
||||
0b10_000000 => FontThickness::Thick,
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Invalid font thickness type.",
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::invalid_data("Invalid font thickness type.")),
|
||||
};
|
||||
let mut zones = vec![];
|
||||
while let Ok(zone) = self.read_font_align_zone() {
|
||||
|
@ -1427,7 +1420,7 @@ impl<R: Read> Reader<R> {
|
|||
0 => LineCapStyle::Round,
|
||||
1 => LineCapStyle::None,
|
||||
2 => LineCapStyle::Square,
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid line cap type.")),
|
||||
_ => return Err(Error::invalid_data("Invalid line cap type.")),
|
||||
};
|
||||
let join_style_id = self.read_ubits(2)?;
|
||||
let has_fill = self.read_bit()?;
|
||||
|
@ -1440,13 +1433,13 @@ impl<R: Read> Reader<R> {
|
|||
0 => LineCapStyle::Round,
|
||||
1 => LineCapStyle::None,
|
||||
2 => LineCapStyle::Square,
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid line cap type.")),
|
||||
_ => return Err(Error::invalid_data("Invalid line cap type.")),
|
||||
};
|
||||
let join_style = match join_style_id {
|
||||
0 => LineJoinStyle::Round,
|
||||
1 => LineJoinStyle::Bevel,
|
||||
2 => LineJoinStyle::Miter(self.read_fixed8()?),
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid line cap type.")),
|
||||
_ => return Err(Error::invalid_data("Invalid line cap type.")),
|
||||
};
|
||||
let (start_color, end_color) = if !has_fill {
|
||||
(self.read_rgba()?, self.read_rgba()?)
|
||||
|
@ -1528,8 +1521,7 @@ impl<R: Read> Reader<R> {
|
|||
|
||||
0x13 => {
|
||||
if self.version < 8 || shape_version < 2 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"Focal gradients are only supported in SWF version 8 \
|
||||
or higher.",
|
||||
));
|
||||
|
@ -1568,7 +1560,7 @@ impl<R: Read> Reader<R> {
|
|||
)
|
||||
}
|
||||
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid fill style.")),
|
||||
_ => return Err(Error::invalid_data("Invalid fill style.")),
|
||||
};
|
||||
Ok(fill_style)
|
||||
}
|
||||
|
@ -1718,8 +1710,7 @@ impl<R: Read> Reader<R> {
|
|||
|
||||
0x13 => {
|
||||
if self.version < 8 || shape_version < 4 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"Focal gradients are only supported in SWF version 8 \
|
||||
or higher.",
|
||||
));
|
||||
|
@ -1738,7 +1729,7 @@ impl<R: Read> Reader<R> {
|
|||
is_repeating: (fill_style_type & 0b01) == 0,
|
||||
},
|
||||
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid fill style.")),
|
||||
_ => return Err(Error::invalid_data("Invalid fill style.")),
|
||||
};
|
||||
Ok(fill_style)
|
||||
}
|
||||
|
@ -1761,7 +1752,7 @@ impl<R: Read> Reader<R> {
|
|||
0 => LineCapStyle::Round,
|
||||
1 => LineCapStyle::None,
|
||||
2 => LineCapStyle::Square,
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid line cap type.")),
|
||||
_ => return Err(Error::invalid_data("Invalid line cap type.")),
|
||||
};
|
||||
let join_style_id = self.read_ubits(2)?;
|
||||
let has_fill = self.read_bit()?;
|
||||
|
@ -1774,13 +1765,13 @@ impl<R: Read> Reader<R> {
|
|||
0 => LineCapStyle::Round,
|
||||
1 => LineCapStyle::None,
|
||||
2 => LineCapStyle::Square,
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid line cap type.")),
|
||||
_ => return Err(Error::invalid_data("Invalid line cap type.")),
|
||||
};
|
||||
let join_style = match join_style_id {
|
||||
0 => LineJoinStyle::Round,
|
||||
1 => LineJoinStyle::Bevel,
|
||||
2 => LineJoinStyle::Miter(self.read_fixed8()?),
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid line cap type.")),
|
||||
_ => return Err(Error::invalid_data("Invalid line cap type.")),
|
||||
};
|
||||
let color = if !has_fill {
|
||||
self.read_rgba()?
|
||||
|
@ -1819,22 +1810,12 @@ impl<R: Read> Reader<R> {
|
|||
0 => GradientSpread::Pad,
|
||||
1 => GradientSpread::Reflect,
|
||||
2 => GradientSpread::Repeat,
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Invalid gradient spread mode.",
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::invalid_data("Invalid gradient spread mode.")),
|
||||
};
|
||||
let interpolation = match self.read_ubits(2)? {
|
||||
0 => GradientInterpolation::RGB,
|
||||
1 => GradientInterpolation::LinearRGB,
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Invalid gradient interpolation mode.",
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::invalid_data("Invalid gradient interpolation mode.")),
|
||||
};
|
||||
let num_records = self.read_ubits(4)? as usize;
|
||||
let mut records = Vec::with_capacity(num_records);
|
||||
|
@ -1990,12 +1971,7 @@ impl<R: Read> Reader<R> {
|
|||
0b01 => PlaceObjectAction::Modify,
|
||||
0b10 => PlaceObjectAction::Place(self.read_u16()?),
|
||||
0b11 => PlaceObjectAction::Replace(self.read_u16()?),
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Invalid PlaceObject type",
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::invalid_data("Invalid PlaceObject type")),
|
||||
};
|
||||
let matrix = if (flags & 0b100) != 0 {
|
||||
Some(self.read_matrix()?)
|
||||
|
@ -2107,7 +2083,7 @@ impl<R: Read> Reader<R> {
|
|||
12 => BlendMode::Erase,
|
||||
13 => BlendMode::Overlay,
|
||||
14 => BlendMode::HardLight,
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid blend mode")),
|
||||
_ => return Err(Error::invalid_data("Invalid blend mode")),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -2345,7 +2321,7 @@ impl<R: Read> Reader<R> {
|
|||
num_passes: self.read_ubits(4)? as u8 & 0b011111,
|
||||
}))
|
||||
}
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid filter type")),
|
||||
_ => return Err(Error::invalid_data("Invalid filter type")),
|
||||
};
|
||||
self.byte_align();
|
||||
Ok(filter)
|
||||
|
@ -2362,7 +2338,7 @@ impl<R: Read> Reader<R> {
|
|||
5 => AudioCompression::Nellymoser8Khz,
|
||||
6 => AudioCompression::Nellymoser,
|
||||
11 => AudioCompression::Speex,
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid audio format.")),
|
||||
_ => return Err(Error::invalid_data("Invalid audio format.")),
|
||||
};
|
||||
let sample_rate = match (flags & 0b11_00) >> 2 {
|
||||
0 => 5512,
|
||||
|
@ -2553,12 +2529,7 @@ impl<R: Read> Reader<R> {
|
|||
1 => TextAlign::Right,
|
||||
2 => TextAlign::Center,
|
||||
3 => TextAlign::Justify,
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Invalid edit text alignment",
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::invalid_data("Invalid edit text alignment")),
|
||||
},
|
||||
left_margin: Twips::new(self.read_u16()?),
|
||||
right_margin: Twips::new(self.read_u16()?),
|
||||
|
@ -2610,7 +2581,7 @@ impl<R: Read> Reader<R> {
|
|||
3 => VideoCodec::ScreenVideo,
|
||||
4 => VideoCodec::VP6,
|
||||
5 => VideoCodec::VP6WithAlpha,
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid video codec.")),
|
||||
_ => return Err(Error::invalid_data("Invalid video codec.")),
|
||||
};
|
||||
Ok(Tag::DefineVideoStream(DefineVideoStream {
|
||||
id,
|
||||
|
@ -2626,12 +2597,7 @@ impl<R: Read> Reader<R> {
|
|||
0b011_0 => VideoDeblocking::Level2,
|
||||
0b100_0 => VideoDeblocking::Level3,
|
||||
0b101_0 => VideoDeblocking::Level4,
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Invalid video deblocking value.",
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::invalid_data("Invalid video deblocking value.")),
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
@ -2676,7 +2642,7 @@ impl<R: Read> Reader<R> {
|
|||
3 => BitmapFormat::ColorMap8,
|
||||
4 if version == 1 => BitmapFormat::Rgb15,
|
||||
5 => BitmapFormat::Rgb32,
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid bitmap format.")),
|
||||
_ => return Err(Error::invalid_data("Invalid bitmap format.")),
|
||||
};
|
||||
let width = self.read_u16()?;
|
||||
let height = self.read_u16()?;
|
||||
|
@ -3142,7 +3108,10 @@ pub mod tests {
|
|||
fn read_tags() {
|
||||
for (swf_version, expected_tag, tag_bytes) in test_data::tag_tests() {
|
||||
let mut reader = Reader::new(&tag_bytes[..], swf_version);
|
||||
let parsed_tag = reader.read_tag().unwrap();
|
||||
let parsed_tag = match reader.read_tag() {
|
||||
Ok(tag) => tag,
|
||||
Err(e) => panic!("Error parsing tag: {}", e),
|
||||
};
|
||||
if parsed_tag != expected_tag {
|
||||
// Failed, result doesn't match.
|
||||
panic!(
|
||||
|
|
141
swf/src/write.rs
141
swf/src/write.rs
|
@ -5,12 +5,13 @@
|
|||
clippy::unreadable_literal
|
||||
)]
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::tag_code::TagCode;
|
||||
use crate::types::*;
|
||||
use byteorder::{LittleEndian, WriteBytesExt};
|
||||
use std::cmp::max;
|
||||
use std::collections::HashSet;
|
||||
use std::io::{Error, ErrorKind, Result, Write};
|
||||
use std::io::{self, Write};
|
||||
|
||||
/// Writes an SWF file to an output stream.
|
||||
/// # Example
|
||||
|
@ -98,8 +99,7 @@ fn write_zlib_swf<W: Write>(mut output: W, swf_body: &[u8]) -> Result<()> {
|
|||
|
||||
#[cfg(not(any(feature = "flate2", feature = "libflate")))]
|
||||
fn write_zlib_swf<W: Write>(_output: W, _swf_body: &[u8]) -> Result<()> {
|
||||
Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
Err(Error::unsupported(
|
||||
"Support for Zlib compressed SWFs is not enabled.",
|
||||
))
|
||||
}
|
||||
|
@ -121,8 +121,7 @@ fn write_lzma_swf<W: Write>(mut output: W, swf_body: &[u8]) -> Result<()> {
|
|||
|
||||
#[cfg(not(feature = "lzma-support"))]
|
||||
fn write_lzma_swf<W: Write>(_output: W, _swf_body: &[u8]) -> Result<()> {
|
||||
Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
Err(Error::unsupported(
|
||||
"Support for LZMA compressed SWFs is not enabled.",
|
||||
))
|
||||
}
|
||||
|
@ -130,47 +129,47 @@ fn write_lzma_swf<W: Write>(_output: W, _swf_body: &[u8]) -> Result<()> {
|
|||
pub trait SwfWrite<W: Write> {
|
||||
fn get_inner(&mut self) -> &mut W;
|
||||
|
||||
fn write_u8(&mut self, n: u8) -> Result<()> {
|
||||
fn write_u8(&mut self, n: u8) -> io::Result<()> {
|
||||
self.get_inner().write_u8(n)
|
||||
}
|
||||
|
||||
fn write_u16(&mut self, n: u16) -> Result<()> {
|
||||
fn write_u16(&mut self, n: u16) -> io::Result<()> {
|
||||
self.get_inner().write_u16::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_u32(&mut self, n: u32) -> Result<()> {
|
||||
fn write_u32(&mut self, n: u32) -> io::Result<()> {
|
||||
self.get_inner().write_u32::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_u64(&mut self, n: u64) -> Result<()> {
|
||||
fn write_u64(&mut self, n: u64) -> io::Result<()> {
|
||||
self.get_inner().write_u64::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_i8(&mut self, n: i8) -> Result<()> {
|
||||
fn write_i8(&mut self, n: i8) -> io::Result<()> {
|
||||
self.get_inner().write_i8(n)
|
||||
}
|
||||
|
||||
fn write_i16(&mut self, n: i16) -> Result<()> {
|
||||
fn write_i16(&mut self, n: i16) -> io::Result<()> {
|
||||
self.get_inner().write_i16::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_i32(&mut self, n: i32) -> Result<()> {
|
||||
fn write_i32(&mut self, n: i32) -> io::Result<()> {
|
||||
self.get_inner().write_i32::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_fixed8(&mut self, n: f32) -> Result<()> {
|
||||
fn write_fixed8(&mut self, n: f32) -> io::Result<()> {
|
||||
self.write_i16((n * 256f32) as i16)
|
||||
}
|
||||
|
||||
fn write_fixed16(&mut self, n: f64) -> Result<()> {
|
||||
fn write_fixed16(&mut self, n: f64) -> io::Result<()> {
|
||||
self.write_i32((n * 65536f64) as i32)
|
||||
}
|
||||
|
||||
fn write_f32(&mut self, n: f32) -> Result<()> {
|
||||
fn write_f32(&mut self, n: f32) -> io::Result<()> {
|
||||
self.get_inner().write_f32::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_f64(&mut self, n: f64) -> Result<()> {
|
||||
fn write_f64(&mut self, n: f64) -> io::Result<()> {
|
||||
// Flash weirdly stores f64 as two LE 32-bit chunks.
|
||||
// First word is the hi-word, second word is the lo-word.
|
||||
let mut num = [0u8; 8];
|
||||
|
@ -182,7 +181,7 @@ pub trait SwfWrite<W: Write> {
|
|||
self.get_inner().write_all(&num)
|
||||
}
|
||||
|
||||
fn write_c_string(&mut self, s: &str) -> Result<()> {
|
||||
fn write_c_string(&mut self, s: &str) -> io::Result<()> {
|
||||
self.get_inner().write_all(s.as_bytes())?;
|
||||
self.write_u8(0)
|
||||
}
|
||||
|
@ -202,47 +201,47 @@ impl<W: Write> SwfWrite<W> for Writer<W> {
|
|||
&mut self.output
|
||||
}
|
||||
|
||||
fn write_u8(&mut self, n: u8) -> Result<()> {
|
||||
fn write_u8(&mut self, n: u8) -> io::Result<()> {
|
||||
self.flush_bits()?;
|
||||
self.output.write_u8(n)
|
||||
}
|
||||
|
||||
fn write_u16(&mut self, n: u16) -> Result<()> {
|
||||
fn write_u16(&mut self, n: u16) -> io::Result<()> {
|
||||
self.flush_bits()?;
|
||||
self.output.write_u16::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_u32(&mut self, n: u32) -> Result<()> {
|
||||
fn write_u32(&mut self, n: u32) -> io::Result<()> {
|
||||
self.flush_bits()?;
|
||||
self.output.write_u32::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_i8(&mut self, n: i8) -> Result<()> {
|
||||
fn write_i8(&mut self, n: i8) -> io::Result<()> {
|
||||
self.flush_bits()?;
|
||||
self.output.write_i8(n)
|
||||
}
|
||||
|
||||
fn write_i16(&mut self, n: i16) -> Result<()> {
|
||||
fn write_i16(&mut self, n: i16) -> io::Result<()> {
|
||||
self.flush_bits()?;
|
||||
self.output.write_i16::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_i32(&mut self, n: i32) -> Result<()> {
|
||||
fn write_i32(&mut self, n: i32) -> io::Result<()> {
|
||||
self.flush_bits()?;
|
||||
self.output.write_i32::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_f32(&mut self, n: f32) -> Result<()> {
|
||||
fn write_f32(&mut self, n: f32) -> io::Result<()> {
|
||||
self.flush_bits()?;
|
||||
self.output.write_f32::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_f64(&mut self, n: f64) -> Result<()> {
|
||||
fn write_f64(&mut self, n: f64) -> io::Result<()> {
|
||||
self.flush_bits()?;
|
||||
self.output.write_f64::<LittleEndian>(n)
|
||||
}
|
||||
|
||||
fn write_c_string(&mut self, s: &str) -> Result<()> {
|
||||
fn write_c_string(&mut self, s: &str) -> io::Result<()> {
|
||||
self.flush_bits()?;
|
||||
self.get_inner().write_all(s.as_bytes())?;
|
||||
self.write_u8(0)
|
||||
|
@ -277,7 +276,7 @@ impl<W: Write> Writer<W> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn flush_bits(&mut self) -> Result<()> {
|
||||
fn flush_bits(&mut self) -> io::Result<()> {
|
||||
if self.bit_index != 8 {
|
||||
self.output.write_u8(self.byte)?;
|
||||
self.bit_index = 8;
|
||||
|
@ -341,7 +340,8 @@ impl<W: Write> Writer<W> {
|
|||
}
|
||||
|
||||
fn write_character_id(&mut self, id: CharacterId) -> Result<()> {
|
||||
self.write_u16(id)
|
||||
self.write_u16(id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_rgb(&mut self, color: &Color) -> Result<()> {
|
||||
|
@ -509,7 +509,8 @@ impl<W: Write> Writer<W> {
|
|||
Language::Korean => 3,
|
||||
Language::SimplifiedChinese => 4,
|
||||
Language::TraditionalChinese => 5,
|
||||
})
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_tag(&mut self, tag: &Tag) -> Result<()> {
|
||||
|
@ -918,12 +919,7 @@ impl<W: Write> Writer<W> {
|
|||
2 => self.write_place_object_2_or_3(place_object, 2)?,
|
||||
3 => self.write_place_object_2_or_3(place_object, 3)?,
|
||||
4 => self.write_place_object_2_or_3(place_object, 4)?,
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Invalid PlaceObject version.",
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::invalid_data("Invalid PlaceObject version.")),
|
||||
},
|
||||
|
||||
Tag::RemoveObject(ref remove_object) => {
|
||||
|
@ -1191,8 +1187,7 @@ impl<W: Write> Writer<W> {
|
|||
if data.start.fill_styles.len() != data.end.fill_styles.len()
|
||||
|| data.start.line_styles.len() != data.end.line_styles.len()
|
||||
{
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"Start and end state of a morph shape must have the same number of styles.",
|
||||
));
|
||||
}
|
||||
|
@ -1337,8 +1332,7 @@ impl<W: Write> Writer<W> {
|
|||
},
|
||||
) => {
|
||||
if self.version < 8 || shape_version < 2 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"Focal gradients are only support in SWF version 8 \
|
||||
and higher.",
|
||||
));
|
||||
|
@ -1379,8 +1373,7 @@ impl<W: Write> Writer<W> {
|
|||
}
|
||||
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"Morph start and end fill styles must be the same variant.",
|
||||
))
|
||||
}
|
||||
|
@ -1392,8 +1385,7 @@ impl<W: Write> Writer<W> {
|
|||
self.write_matrix(&start.matrix)?;
|
||||
self.write_matrix(&end.matrix)?;
|
||||
if start.records.len() != end.records.len() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"Morph start and end gradient must have the same amount of records.",
|
||||
));
|
||||
}
|
||||
|
@ -1428,8 +1420,7 @@ impl<W: Write> Writer<W> {
|
|||
|| start.allow_close != end.allow_close
|
||||
|| start.end_cap != end.end_cap
|
||||
{
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"Morph start and end line styles must have the same join parameters.",
|
||||
));
|
||||
}
|
||||
|
@ -1483,8 +1474,7 @@ impl<W: Write> Writer<W> {
|
|||
}
|
||||
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"Morph start and end line styles must both have fill styles.",
|
||||
))
|
||||
}
|
||||
|
@ -1553,12 +1543,7 @@ impl<W: Write> Writer<W> {
|
|||
2 => TagCode::DefineShape2,
|
||||
3 => TagCode::DefineShape3,
|
||||
4 => TagCode::DefineShape4,
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Invalid DefineShape version.",
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::invalid_data("Invalid DefineShape version.")),
|
||||
};
|
||||
self.write_tag_header(tag_code, buf.len() as u32)?;
|
||||
self.output.write_all(&buf)?;
|
||||
|
@ -1649,7 +1634,8 @@ impl<W: Write> Writer<W> {
|
|||
BlendMode::Erase => 12,
|
||||
BlendMode::Overlay => 13,
|
||||
BlendMode::HardLight => 14,
|
||||
})
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_shape_styles(&mut self, styles: &ShapeStyles, shape_version: u8) -> Result<()> {
|
||||
|
@ -1752,8 +1738,7 @@ impl<W: Write> Writer<W> {
|
|||
}
|
||||
if let Some(ref new_styles) = style_change.new_styles {
|
||||
if shape_version < 2 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"Only DefineShape2 and higher may change styles.",
|
||||
));
|
||||
}
|
||||
|
@ -1790,8 +1775,7 @@ impl<W: Write> Writer<W> {
|
|||
focal_point,
|
||||
} => {
|
||||
if self.version < 8 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"Focal gradients are only support in SWF version 8 \
|
||||
and higher.",
|
||||
));
|
||||
|
@ -1911,8 +1895,7 @@ impl<W: Write> Writer<W> {
|
|||
if let PlaceObjectAction::Place(character_id) = place_object.action {
|
||||
writer.write_u16(character_id)?;
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
return Err(Error::invalid_data(
|
||||
"PlaceObject version 1 can only use a Place action.",
|
||||
));
|
||||
}
|
||||
|
@ -2074,12 +2057,7 @@ impl<W: Write> Writer<W> {
|
|||
2 => TagCode::PlaceObject2,
|
||||
3 => TagCode::PlaceObject3,
|
||||
4 => TagCode::PlaceObject4,
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Invalid PlaceObject version.",
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::invalid_data("Invalid PlaceObject version.")),
|
||||
};
|
||||
self.write_tag_header(tag_code, buf.len() as u32)?;
|
||||
self.output.write_all(&buf)?;
|
||||
|
@ -2310,7 +2288,7 @@ impl<W: Write> Writer<W> {
|
|||
11025 => 1,
|
||||
22050 => 2,
|
||||
44100 => 3,
|
||||
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid sample rate.")),
|
||||
_ => return Err(Error::invalid_data("Invalid sample rate.")),
|
||||
},
|
||||
)?;
|
||||
self.write_bit(sound_format.is_16_bit)?;
|
||||
|
@ -2447,14 +2425,19 @@ impl<W: Write> Writer<W> {
|
|||
writer.write_u16(layout.descent)?;
|
||||
writer.write_i16(layout.leading)?;
|
||||
for glyph in &font.glyphs {
|
||||
writer.write_i16(glyph.advance.ok_or_else(|| {
|
||||
Error::new(ErrorKind::InvalidData, "glyph.advance cannot be None")
|
||||
})?)?;
|
||||
writer.write_i16(
|
||||
glyph
|
||||
.advance
|
||||
.ok_or_else(|| Error::invalid_data("glyph.advance cannot be None"))?,
|
||||
)?;
|
||||
}
|
||||
for glyph in &font.glyphs {
|
||||
writer.write_rectangle(glyph.bounds.as_ref().ok_or_else(|| {
|
||||
Error::new(ErrorKind::InvalidData, "glyph.bounds cannot be None")
|
||||
})?)?;
|
||||
writer.write_rectangle(
|
||||
glyph
|
||||
.bounds
|
||||
.as_ref()
|
||||
.ok_or_else(|| Error::invalid_data("glyph.bounds cannot be None"))?,
|
||||
)?;
|
||||
}
|
||||
writer.write_u16(layout.kerning.len() as u16)?;
|
||||
for kerning_record in &layout.kerning {
|
||||
|
@ -2688,7 +2671,8 @@ impl<W: Write> Writer<W> {
|
|||
}
|
||||
|
||||
fn write_debug_id(&mut self, debug_id: &DebugId) -> Result<()> {
|
||||
self.get_inner().write_all(debug_id)
|
||||
self.get_inner().write_all(debug_id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_tag_header(&mut self, tag_code: TagCode, length: u32) -> Result<()> {
|
||||
|
@ -2700,12 +2684,13 @@ impl<W: Write> Writer<W> {
|
|||
let mut tag_code_and_length: u16 = tag_code << 6;
|
||||
if length < 0b111111 {
|
||||
tag_code_and_length |= length as u16;
|
||||
self.write_u16(tag_code_and_length)
|
||||
self.write_u16(tag_code_and_length)?;
|
||||
} else {
|
||||
tag_code_and_length |= 0b111111;
|
||||
self.write_u16(tag_code_and_length)?;
|
||||
self.write_u32(length)
|
||||
self.write_u32(length)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_tag_list(&mut self, tags: &[Tag]) -> Result<()> {
|
||||
|
@ -2762,7 +2747,6 @@ mod tests {
|
|||
use super::Writer;
|
||||
use super::*;
|
||||
use crate::test_data;
|
||||
use std::io::Result;
|
||||
|
||||
fn new_swf() -> Swf {
|
||||
Swf {
|
||||
|
@ -2788,7 +2772,8 @@ mod tests {
|
|||
let mut buf = Vec::new();
|
||||
let mut swf = new_swf();
|
||||
swf.header.compression = compression;
|
||||
write_swf(&swf, &mut buf)
|
||||
write_swf(&swf, &mut buf)?;
|
||||
Ok(())
|
||||
}
|
||||
assert!(
|
||||
write_dummy_swf(Compression::None).is_ok(),
|
||||
|
|
Loading…
Reference in New Issue