swf: Rename and organize some util methods

* SwfRead -> SwfReadExt
 * SwfWrite -> SwfWriteExt
 * read_swf_header -> decompress_swf
 * read_swf -> parse_swf
This commit is contained in:
Mike Welsh 2021-01-18 16:46:20 -08:00
parent 1d9c11e145
commit 61628a74fc
13 changed files with 419 additions and 291 deletions

View File

@ -31,7 +31,7 @@ use std::cell::{Ref, RefCell};
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::sync::Arc; use std::sync::Arc;
use swf::read::SwfRead; use swf::read::SwfReadExt;
use swf::{FillStyle, FrameLabelData, LineStyle}; use swf::{FillStyle, FrameLabelData, LineStyle};
type FrameNumber = u16; type FrameNumber = u16;

View File

@ -73,10 +73,10 @@ impl SwfMovie {
/// Construct a movie based on the contents of the SWF datastream. /// Construct a movie based on the contents of the SWF datastream.
pub fn from_data(swf_data: &[u8], url: Option<String>) -> Result<Self, Error> { pub fn from_data(swf_data: &[u8], url: Option<String>) -> Result<Self, Error> {
let swf_stream = swf::read::read_swf_header(&swf_data[..])?; let swf_buf = swf::read::decompress_swf(&swf_data[..])?;
Ok(Self { Ok(Self {
header: swf_stream.header, header: swf_buf.header,
data: swf_stream.data, data: swf_buf.data,
url, url,
parameters: PropertyMap::new(), parameters: PropertyMap::new(),
}) })

View File

@ -1,7 +1,7 @@
use clap::Clap; use clap::Clap;
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use path_slash::PathExt; use path_slash::PathExt;
use ruffle_core::swf::{read_swf, read_swf_header}; use ruffle_core::swf::{decompress_swf, parse_swf};
use serde::Serialize; use serde::Serialize;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -65,8 +65,8 @@ fn scan_file(file: DirEntry, name: String) -> FileResults {
} }
}; };
let swf_stream = read_swf_header(&data[..]).unwrap(); let swf_buf = decompress_swf(&data[..]).unwrap();
match catch_unwind(|| read_swf(&swf_stream)) { match catch_unwind(|| parse_swf(&swf_buf)) {
Ok(swf) => match swf { Ok(swf) => match swf {
Ok(_swf) => FileResults { name, error: None }, Ok(_swf) => FileResults { name, error: None },
Err(e) => FileResults { Err(e) => FileResults {

View File

@ -4,8 +4,8 @@ use std::io::BufReader;
fn main() { fn main() {
let file = File::open("tests/swfs/SimpleRedBackground.swf").unwrap(); let file = File::open("tests/swfs/SimpleRedBackground.swf").unwrap();
let reader = BufReader::new(file); let reader = BufReader::new(file);
let swf_stream = swf::read_swf_header(reader).unwrap(); let swf_buf = swf::decompress_swf(reader).unwrap();
let swf = swf::read_swf(&swf_stream).unwrap(); let swf = swf::parse_swf(&swf_buf).unwrap();
println!("The SWF has {} frame(s).", swf.header.num_frames); println!("The SWF has {} frame(s).", swf.header.num_frames);
println!("The SWF has {} tag(s).", swf.tags.len()); println!("The SWF has {} tag(s).", swf.tags.len());
} }

View File

@ -2,9 +2,10 @@
use crate::avm1::{opcode::OpCode, types::*}; use crate::avm1::{opcode::OpCode, types::*};
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::read::SwfRead; use crate::read::SwfReadExt;
use crate::string::SwfStr; use crate::string::SwfStr;
use std::io; use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{self, Read};
#[allow(dead_code)] #[allow(dead_code)]
pub struct Reader<'a> { pub struct Reader<'a> {
@ -13,12 +14,6 @@ pub struct Reader<'a> {
encoding: &'static encoding_rs::Encoding, encoding: &'static encoding_rs::Encoding,
} }
impl<'a> SwfRead<&'a [u8]> for Reader<'a> {
fn get_inner(&mut self) -> &mut &'a [u8] {
&mut self.input
}
}
impl<'a> Reader<'a> { impl<'a> Reader<'a> {
pub fn new(input: &'a [u8], version: u8) -> Self { pub fn new(input: &'a [u8], version: u8) -> Self {
Self { Self {
@ -75,6 +70,19 @@ impl<'a> Reader<'a> {
Ok(s) Ok(s)
} }
#[inline]
fn read_f64_me(&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];
self.input.read_exact(&mut num)?;
num.swap(0, 4);
num.swap(1, 5);
num.swap(2, 6);
num.swap(3, 7);
(&num[..]).read_f64::<LittleEndian>()
}
#[inline] #[inline]
pub fn read_action(&mut self) -> Result<Option<Action<'a>>> { pub fn read_action(&mut self) -> Result<Option<Action<'a>>> {
let (opcode, mut length) = self.read_opcode_and_length()?; let (opcode, mut length) = self.read_opcode_and_length()?;
@ -394,6 +402,53 @@ impl<'a> Reader<'a> {
} }
} }
impl<'a> SwfReadExt for Reader<'a> {
#[inline]
fn read_u8(&mut self) -> io::Result<u8> {
self.input.read_u8()
}
#[inline]
fn read_u16(&mut self) -> io::Result<u16> {
self.input.read_u16::<LittleEndian>()
}
#[inline]
fn read_u32(&mut self) -> io::Result<u32> {
self.input.read_u32::<LittleEndian>()
}
#[inline]
fn read_u64(&mut self) -> io::Result<u64> {
self.input.read_u64::<LittleEndian>()
}
#[inline]
fn read_i8(&mut self) -> io::Result<i8> {
self.input.read_i8()
}
#[inline]
fn read_i16(&mut self) -> io::Result<i16> {
self.input.read_i16::<LittleEndian>()
}
#[inline]
fn read_i32(&mut self) -> io::Result<i32> {
self.input.read_i32::<LittleEndian>()
}
#[inline]
fn read_f32(&mut self) -> io::Result<f32> {
self.input.read_f32::<LittleEndian>()
}
#[inline]
fn read_f64(&mut self) -> io::Result<f64> {
self.input.read_f64::<LittleEndian>()
}
}
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;

View File

@ -2,24 +2,86 @@
use crate::avm1::opcode::OpCode; use crate::avm1::opcode::OpCode;
use crate::avm1::types::*; use crate::avm1::types::*;
use crate::write::SwfWrite; use crate::string::SwfStr;
use std::io::{Result, Write}; use crate::write::SwfWriteExt;
use byteorder::{LittleEndian, WriteBytesExt};
use std::io::{self, Result, Write};
#[allow(dead_code)] #[allow(dead_code)]
pub struct Writer<W: Write> { pub struct Writer<W: Write> {
inner: W, output: W,
version: u8, version: u8,
} }
impl<W: Write> SwfWrite<W> for Writer<W> { impl<W: Write> SwfWriteExt for Writer<W> {
fn get_inner(&mut self) -> &mut W { #[inline]
&mut self.inner fn write_u8(&mut self, n: u8) -> io::Result<()> {
self.output.write_u8(n)
}
#[inline]
fn write_u16(&mut self, n: u16) -> io::Result<()> {
self.output.write_u16::<LittleEndian>(n)
}
#[inline]
fn write_u32(&mut self, n: u32) -> io::Result<()> {
self.output.write_u32::<LittleEndian>(n)
}
#[inline]
fn write_u64(&mut self, n: u64) -> io::Result<()> {
self.output.write_u64::<LittleEndian>(n)
}
#[inline]
fn write_i8(&mut self, n: i8) -> io::Result<()> {
self.output.write_i8(n)
}
#[inline]
fn write_i16(&mut self, n: i16) -> io::Result<()> {
self.output.write_i16::<LittleEndian>(n)
}
#[inline]
fn write_i32(&mut self, n: i32) -> io::Result<()> {
self.output.write_i32::<LittleEndian>(n)
}
#[inline]
fn write_f32(&mut self, n: f32) -> io::Result<()> {
self.output.write_f32::<LittleEndian>(n)
}
#[inline]
fn write_f64(&mut self, n: f64) -> io::Result<()> {
self.output.write_f64::<LittleEndian>(n)
}
#[inline]
fn write_string(&mut self, s: SwfStr<'_>) -> io::Result<()> {
self.output.write_all(s.as_bytes())?;
self.write_u8(0)
} }
} }
impl<W: Write> Writer<W> { impl<W: Write> Writer<W> {
pub fn new(inner: W, version: u8) -> Writer<W> { pub fn new(output: W, version: u8) -> Writer<W> {
Writer { inner, version } Writer { output, version }
}
#[inline]
fn write_f64_me(&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];
(&mut num[..]).write_f64::<LittleEndian>(n)?;
num.swap(0, 4);
num.swap(1, 5);
num.swap(2, 6);
num.swap(3, 7);
self.output.write_all(&num)
} }
#[allow(clippy::inconsistent_digit_grouping)] #[allow(clippy::inconsistent_digit_grouping)]
@ -66,7 +128,7 @@ impl<W: Write> Writer<W> {
self.write_string(*param)?; self.write_string(*param)?;
} }
self.write_u16(actions.len() as u16)?; self.write_u16(actions.len() as u16)?;
self.inner.write_all(actions)?; self.output.write_all(actions)?;
} }
Action::DefineFunction2(ref function) => { Action::DefineFunction2(ref function) => {
let len = function.name.len() let len = function.name.len()
@ -111,7 +173,7 @@ impl<W: Write> Writer<W> {
self.write_string(param.name)?; self.write_string(param.name)?;
} }
self.write_u16(function.actions.len() as u16)?; self.write_u16(function.actions.len() as u16)?;
self.inner.write_all(&function.actions)?; self.output.write_all(&function.actions)?;
} }
Action::DefineLocal => self.write_action_header(OpCode::DefineLocal, 0)?, Action::DefineLocal => self.write_action_header(OpCode::DefineLocal, 0)?,
Action::DefineLocal2 => self.write_action_header(OpCode::DefineLocal2, 0)?, Action::DefineLocal2 => self.write_action_header(OpCode::DefineLocal2, 0)?,
@ -304,7 +366,7 @@ impl<W: Write> Writer<W> {
Some((CatchVar::Register(i), _)) => self.write_u8(i)?, Some((CatchVar::Register(i), _)) => self.write_u8(i)?,
_ => (), _ => (),
} }
self.inner.write_all(&action_buf)?; self.output.write_all(&action_buf)?;
} }
Action::TypeOf => self.write_action_header(OpCode::TypeOf, 0)?, Action::TypeOf => self.write_action_header(OpCode::TypeOf, 0)?,
Action::WaitForFrame { Action::WaitForFrame {
@ -323,11 +385,11 @@ impl<W: Write> Writer<W> {
} }
Action::With { ref actions } => { Action::With { ref actions } => {
self.write_action_header(OpCode::With, actions.len())?; self.write_action_header(OpCode::With, actions.len())?;
self.inner.write_all(&actions)?; self.output.write_all(&actions)?;
} }
Action::Unknown { opcode, ref data } => { Action::Unknown { opcode, ref data } => {
self.write_opcode_and_length(opcode, data.len())?; self.write_opcode_and_length(opcode, data.len())?;
self.inner.write_all(data)?; self.output.write_all(data)?;
} }
} }
@ -376,7 +438,7 @@ impl<W: Write> Writer<W> {
} }
Value::Double(v) => { Value::Double(v) => {
self.write_u8(6)?; self.write_u8(6)?;
self.write_f64(v)?; self.write_f64_me(v)?;
} }
Value::Int(v) => { Value::Int(v) => {
self.write_u8(7)?; self.write_u8(7)?;

View File

@ -1,16 +1,11 @@
use crate::avm2::types::*; use crate::avm2::types::*;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::read::SwfRead; use crate::read::SwfReadExt;
use std::io::{Read, Seek, SeekFrom}; use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{self, Read, Seek, SeekFrom};
pub struct Reader<R: Read> { pub struct Reader<R: Read> {
inner: R, input: R,
}
impl<R: Read> SwfRead<R> for Reader<R> {
fn get_inner(&mut self) -> &mut R {
&mut self.inner
}
} }
impl<R> Reader<R> impl<R> Reader<R>
@ -19,13 +14,13 @@ where
{ {
#[inline] #[inline]
pub fn seek(&mut self, relative_offset: i64) -> std::io::Result<u64> { pub fn seek(&mut self, relative_offset: i64) -> std::io::Result<u64> {
self.inner.seek(SeekFrom::Current(relative_offset as i64)) self.input.seek(SeekFrom::Current(relative_offset as i64))
} }
} }
impl<R: Read> Reader<R> { impl<R: Read> Reader<R> {
pub fn new(inner: R) -> Reader<R> { pub fn new(input: R) -> Reader<R> {
Reader { inner } Reader { input }
} }
pub fn read(&mut self) -> Result<AbcFile> { pub fn read(&mut self) -> Result<AbcFile> {
@ -128,7 +123,7 @@ impl<R: Read> Reader<R> {
fn read_string(&mut self) -> Result<String> { fn read_string(&mut self) -> Result<String> {
let len = self.read_u30()? as usize; let len = self.read_u30()? as usize;
let mut s = String::with_capacity(len); let mut s = String::with_capacity(len);
self.inner self.input
.by_ref() .by_ref()
.take(len as u64) .take(len as u64)
.read_to_string(&mut s)?; .read_to_string(&mut s)?;
@ -501,7 +496,7 @@ impl<R: Read> Reader<R> {
// Read the code data. // Read the code data.
let code_len = self.read_u30()?; let code_len = self.read_u30()?;
let mut code = Vec::with_capacity(code_len as usize); let mut code = Vec::with_capacity(code_len as usize);
self.inner self.input
.by_ref() .by_ref()
.take(code_len.into()) .take(code_len.into())
.read_to_end(&mut code)?; .read_to_end(&mut code)?;
@ -866,6 +861,53 @@ impl<R: Read> Reader<R> {
} }
} }
impl<'a, R: 'a + Read> SwfReadExt for Reader<R> {
#[inline]
fn read_u8(&mut self) -> io::Result<u8> {
self.input.read_u8()
}
#[inline]
fn read_u16(&mut self) -> io::Result<u16> {
self.input.read_u16::<LittleEndian>()
}
#[inline]
fn read_u32(&mut self) -> io::Result<u32> {
self.input.read_u32::<LittleEndian>()
}
#[inline]
fn read_u64(&mut self) -> io::Result<u64> {
self.input.read_u64::<LittleEndian>()
}
#[inline]
fn read_i8(&mut self) -> io::Result<i8> {
self.input.read_i8()
}
#[inline]
fn read_i16(&mut self) -> io::Result<i16> {
self.input.read_i16::<LittleEndian>()
}
#[inline]
fn read_i32(&mut self) -> io::Result<i32> {
self.input.read_i32::<LittleEndian>()
}
#[inline]
fn read_f32(&mut self) -> io::Result<f32> {
self.input.read_f32::<LittleEndian>()
}
#[inline]
fn read_f64(&mut self) -> io::Result<f64> {
self.input.read_f64::<LittleEndian>()
}
}
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
@ -874,8 +916,8 @@ pub mod tests {
pub fn read_abc_from_file(path: &str) -> Vec<u8> { pub fn read_abc_from_file(path: &str) -> Vec<u8> {
use crate::types::Tag; use crate::types::Tag;
let data = std::fs::read(path).unwrap(); let data = std::fs::read(path).unwrap();
let swf_stream = crate::read_swf_header(&data[..]).unwrap(); let swf_buf = crate::decompress_swf(&data[..]).unwrap();
let swf = crate::read_swf(&swf_stream).unwrap(); let swf = crate::parse_swf(&swf_buf).unwrap();
for tag in swf.tags { for tag in swf.tags {
if let Tag::DoAbc(do_abc) = tag { if let Tag::DoAbc(do_abc) = tag {
return do_abc.data.to_vec(); return do_abc.data.to_vec();

View File

@ -1,21 +1,70 @@
use crate::avm2::opcode::OpCode; use crate::avm2::opcode::OpCode;
use crate::avm2::types::*; use crate::avm2::types::*;
use crate::write::SwfWrite; use crate::string::SwfStr;
use std::io::{Result, Write}; use crate::write::SwfWriteExt;
use byteorder::{LittleEndian, WriteBytesExt};
use std::io::{self, Result, Write};
pub struct Writer<W: Write> { pub struct Writer<W: Write> {
inner: W, output: W,
} }
impl<W: Write> SwfWrite<W> for Writer<W> { impl<W: Write> SwfWriteExt for Writer<W> {
fn get_inner(&mut self) -> &mut W { #[inline]
&mut self.inner fn write_u8(&mut self, n: u8) -> io::Result<()> {
self.output.write_u8(n)
}
#[inline]
fn write_u16(&mut self, n: u16) -> io::Result<()> {
self.output.write_u16::<LittleEndian>(n)
}
#[inline]
fn write_u32(&mut self, n: u32) -> io::Result<()> {
self.output.write_u32::<LittleEndian>(n)
}
#[inline]
fn write_u64(&mut self, n: u64) -> io::Result<()> {
self.output.write_u64::<LittleEndian>(n)
}
#[inline]
fn write_i8(&mut self, n: i8) -> io::Result<()> {
self.output.write_i8(n)
}
#[inline]
fn write_i16(&mut self, n: i16) -> io::Result<()> {
self.output.write_i16::<LittleEndian>(n)
}
#[inline]
fn write_i32(&mut self, n: i32) -> io::Result<()> {
self.output.write_i32::<LittleEndian>(n)
}
#[inline]
fn write_f32(&mut self, n: f32) -> io::Result<()> {
self.output.write_f32::<LittleEndian>(n)
}
#[inline]
fn write_f64(&mut self, n: f64) -> io::Result<()> {
self.output.write_f64::<LittleEndian>(n)
}
#[inline]
fn write_string(&mut self, s: SwfStr<'_>) -> io::Result<()> {
self.output.write_all(s.as_bytes())?;
self.write_u8(0)
} }
} }
impl<W: Write> Writer<W> { impl<W: Write> Writer<W> {
pub fn new(inner: W) -> Writer<W> { pub fn new(output: W) -> Writer<W> {
Writer { inner } Writer { output }
} }
pub fn write(&mut self, abc_file: AbcFile) -> Result<()> { pub fn write(&mut self, abc_file: AbcFile) -> Result<()> {
@ -105,7 +154,7 @@ impl<W: Write> Writer<W> {
fn write_string(&mut self, s: &str) -> Result<()> { fn write_string(&mut self, s: &str) -> Result<()> {
self.write_u30(s.len() as u32)?; self.write_u30(s.len() as u32)?;
self.inner.write_all(s.as_bytes())?; self.output.write_all(s.as_bytes())?;
Ok(()) Ok(())
} }
@ -525,7 +574,7 @@ impl<W: Write> Writer<W> {
self.write_u30(method_body.max_scope_depth)?; self.write_u30(method_body.max_scope_depth)?;
self.write_u30(method_body.code.len() as u32)?; self.write_u30(method_body.code.len() as u32)?;
self.inner.write_all(&method_body.code)?; self.output.write_all(&method_body.code)?;
self.write_u30(method_body.exceptions.len() as u32)?; self.write_u30(method_body.exceptions.len() as u32)?;
for exception in &method_body.exceptions { for exception in &method_body.exceptions {

View File

@ -30,7 +30,7 @@ pub mod write;
mod test_data; mod test_data;
/// Reexports /// Reexports
pub use read::{read_swf, read_swf_header}; pub use read::{decompress_swf, parse_swf};
pub use string::*; pub use string::*;
pub use tag_code::TagCode; pub use tag_code::TagCode;
pub use types::*; pub use types::*;

View File

@ -16,36 +16,21 @@ use enumset::EnumSet;
use std::collections::HashSet; use std::collections::HashSet;
use std::io::{self, Read}; use std::io::{self, Read};
/// Convenience method to parse an SWF. /// Parse a decompressed SWF and return a `Vec` of tags.
///
/// Decompresses the SWF in memory and returns a `Vec` of tags.
/// If you would like to stream the SWF instead, use `read_swf_header` and
/// `read_tag`.
/// ///
/// # Example /// # Example
/// ``` /// ```
/// # std::env::set_current_dir(env!("CARGO_MANIFEST_DIR")); /// # std::env::set_current_dir(env!("CARGO_MANIFEST_DIR"));
/// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap(); /// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap();
/// let stream = swf::read_swf_header(&data[..]).unwrap(); /// let stream = swf::decompress_swf(&data[..]).unwrap();
/// let swf = swf::read_swf(&stream).unwrap(); /// let swf = swf::parse_swf(&stream).unwrap();
/// println!("Number of frames: {}", swf.header.num_frames); /// println!("Number of frames: {}", swf.header.num_frames);
/// ``` /// ```
pub fn read_swf<'a>(swf_stream: &'a SwfStream) -> Result<Swf<'a>> { pub fn parse_swf<'a>(swf_buf: &'a SwfBuf) -> Result<Swf<'a>> {
// // Some SWF streams may not be compressed correctly, let mut reader = Reader::new(&swf_buf.data[..], swf_buf.header.version);
// // (e.g. incorrect data length in the stream), so decompressing
// // may throw an error even though the data otherwise comes
// // through the stream.
// // We'll still try to parse what we get if the full decompression fails.
// if let Err(e) = reader.get_mut().read_to_end(&mut data) {
// log::warn!("Error decompressing SWF stream, may be corrupt: {}", e);
// }
// if data.len() != header.uncompressed_length.try_into().unwrap() {
// log::warn!("SWF length doesn't match header, may be corrupt");
// }
let mut reader = Reader::new(&swf_stream.data[..], swf_stream.header.version);
Ok(Swf { Ok(Swf {
header: swf_stream.header.clone(), header: swf_buf.header.clone(),
tags: reader.read_tag_list()?, tags: reader.read_tag_list()?,
}) })
} }
@ -59,22 +44,18 @@ pub fn read_swf<'a>(swf_stream: &'a SwfStream) -> Result<Swf<'a>> {
/// ``` /// ```
/// # std::env::set_current_dir(env!("CARGO_MANIFEST_DIR")); /// # std::env::set_current_dir(env!("CARGO_MANIFEST_DIR"));
/// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap(); /// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap();
/// let swf_stream = swf::read_swf_header(&data[..]).unwrap(); /// let swf_stream = swf::decompress_swf(&data[..]).unwrap();
/// println!("FPS: {}", swf_stream.header.frame_rate); /// println!("FPS: {}", swf_stream.header.frame_rate);
/// ``` /// ```
pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result<SwfStream> { pub fn decompress_swf<'a, R: Read + 'a>(mut input: R) -> Result<SwfBuf> {
// Read SWF header. // Read SWF header.
let compression = read_compression_type(&mut input)?; let compression = read_compression_type(&mut input)?;
let version = input.read_u8()?; let version = input.read_u8()?;
let uncompressed_length = input.read_u32::<LittleEndian>()?; let uncompressed_length = input.read_u32::<LittleEndian>()?;
// Now the SWF switches to a compressed stream. // Now the SWF switches to a compressed stream.
let decompressed_input: Vec<u8> = match compression { let mut decompress_stream: Box<dyn Read> = match compression {
Compression::None => { Compression::None => Box::new(input),
let mut data = Vec::with_capacity(uncompressed_length as usize);
input.read_to_end(&mut data)?;
data
}
Compression::Zlib => { Compression::Zlib => {
if version < 6 { if version < 6 {
log::warn!( log::warn!(
@ -82,10 +63,7 @@ pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result<SwfStream> {
version version
); );
} }
let mut reader = make_zlib_reader(input)?; make_zlib_reader(input)?
let mut data = Vec::with_capacity(uncompressed_length as usize);
reader.read_to_end(&mut data)?;
data
} }
Compression::Lzma => { Compression::Lzma => {
if version < 13 { if version < 13 {
@ -96,13 +74,24 @@ pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result<SwfStream> {
} }
// Uncompressed length includes the 4-byte header and 4-byte uncompressed length itself, // Uncompressed length includes the 4-byte header and 4-byte uncompressed length itself,
// subtract it here. // subtract it here.
let mut reader = make_lzma_reader(input, uncompressed_length - 8)?; make_lzma_reader(input, uncompressed_length - 8)?
let mut data = Vec::with_capacity(uncompressed_length as usize);
reader.read_to_end(&mut data)?;
data
} }
}; };
// Some SWF streams may not be compressed correctly,
// (e.g. incorrect data length in the stream), so decompressing
// may throw an error even though the data otherwise comes
// through the stream.
// We'll still try to parse what we get if the full decompression fails.
let mut decompressed_input = Vec::with_capacity(uncompressed_length as usize);
if let Err(e) = decompress_stream.read_to_end(&mut decompressed_input) {
log::error!("Error decompressing SWF: {}", e);
}
if decompressed_input.len() as u64 != uncompressed_length as u64 {
log::warn!("SWF length doesn't match header, may be corrupt");
}
let mut reader = Reader::new(&decompressed_input, version); let mut reader = Reader::new(&decompressed_input, version);
let stage_size = reader.read_rectangle()?; let stage_size = reader.read_rectangle()?;
let frame_rate = reader.read_fixed8()?; let frame_rate = reader.read_fixed8()?;
@ -116,7 +105,7 @@ pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result<SwfStream> {
num_frames, num_frames,
}; };
let data = reader.get_ref().to_vec(); let data = reader.get_ref().to_vec();
Ok(SwfStream { header, data }) Ok(SwfBuf { header, data })
} }
#[cfg(feature = "flate2")] #[cfg(feature = "flate2")]
@ -191,64 +180,16 @@ fn make_lzma_reader<'a, R: Read + 'a>(
)) ))
} }
pub trait SwfRead<R: Read> { pub trait SwfReadExt {
fn get_inner(&mut self) -> &mut R; fn read_u8(&mut self) -> io::Result<u8>;
fn read_u16(&mut self) -> io::Result<u16>;
fn read_u8(&mut self) -> io::Result<u8> { fn read_u32(&mut self) -> io::Result<u32>;
self.get_inner().read_u8() fn read_u64(&mut self) -> io::Result<u64>;
} fn read_i8(&mut self) -> io::Result<i8>;
fn read_i16(&mut self) -> io::Result<i16>;
fn read_u16(&mut self) -> io::Result<u16> { fn read_i32(&mut self) -> io::Result<i32>;
self.get_inner().read_u16::<LittleEndian>() fn read_f32(&mut self) -> io::Result<f32>;
} fn read_f64(&mut self) -> io::Result<f64>;
fn read_u32(&mut self) -> io::Result<u32> {
self.get_inner().read_u32::<LittleEndian>()
}
fn read_u64(&mut self) -> io::Result<u64> {
self.get_inner().read_u64::<LittleEndian>()
}
fn read_i8(&mut self) -> io::Result<i8> {
self.get_inner().read_i8()
}
fn read_i16(&mut self) -> io::Result<i16> {
self.get_inner().read_i16::<LittleEndian>()
}
fn read_i32(&mut self) -> io::Result<i32> {
self.get_inner().read_i32::<LittleEndian>()
}
fn read_fixed8(&mut self) -> io::Result<f32> {
self.read_i16().map(|n| f32::from(n) / 256f32)
}
fn read_fixed16(&mut self) -> io::Result<f64> {
self.read_i32().map(|n| f64::from(n) / 65536f64)
}
fn read_f32(&mut self) -> io::Result<f32> {
self.get_inner().read_f32::<LittleEndian>()
}
fn read_f64(&mut self) -> io::Result<f64> {
self.get_inner().read_f64::<LittleEndian>()
}
fn read_f64_me(&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];
self.get_inner().read_exact(&mut num)?;
num.swap(0, 4);
num.swap(1, 5);
num.swap(2, 6);
num.swap(3, 7);
(&num[..]).read_f64::<LittleEndian>()
}
} }
pub struct BitReader<'a, 'b> { pub struct BitReader<'a, 'b> {
@ -316,44 +257,6 @@ pub struct Reader<'a> {
encoding: &'static encoding_rs::Encoding, encoding: &'static encoding_rs::Encoding,
} }
impl<'a> SwfRead<&'a [u8]> for Reader<'a> {
fn get_inner(&mut self) -> &mut &'a [u8] {
&mut self.input
}
fn read_u8(&mut self) -> io::Result<u8> {
self.input.read_u8()
}
fn read_u16(&mut self) -> io::Result<u16> {
self.input.read_u16::<LittleEndian>()
}
fn read_u32(&mut self) -> io::Result<u32> {
self.input.read_u32::<LittleEndian>()
}
fn read_i8(&mut self) -> io::Result<i8> {
self.input.read_i8()
}
fn read_i16(&mut self) -> io::Result<i16> {
self.input.read_i16::<LittleEndian>()
}
fn read_i32(&mut self) -> io::Result<i32> {
self.input.read_i32::<LittleEndian>()
}
fn read_f32(&mut self) -> io::Result<f32> {
self.input.read_f32::<LittleEndian>()
}
fn read_f64(&mut self) -> io::Result<f64> {
self.input.read_f64::<LittleEndian>()
}
}
impl<'a> Reader<'a> { impl<'a> Reader<'a> {
pub fn new(input: &'a [u8], version: u8) -> Reader<'a> { pub fn new(input: &'a [u8], version: u8) -> Reader<'a> {
Reader { Reader {
@ -386,12 +289,22 @@ impl<'a> Reader<'a> {
fn bits<'b>(&'b mut self) -> BitReader<'a, 'b> { fn bits<'b>(&'b mut self) -> BitReader<'a, 'b> {
BitReader { BitReader {
input: self.get_inner(), input: &mut self.input,
byte: 0, byte: 0,
bit_index: 0, bit_index: 0,
} }
} }
#[inline]
fn read_fixed8(&mut self) -> io::Result<f32> {
self.read_i16().map(|n| f32::from(n) / 256f32)
}
#[inline]
fn read_fixed16(&mut self) -> io::Result<f64> {
self.read_i32().map(|n| f64::from(n) / 65536f64)
}
fn read_slice(&mut self, len: usize) -> io::Result<&'a [u8]> { fn read_slice(&mut self, len: usize) -> io::Result<&'a [u8]> {
if self.input.len() >= len { if self.input.len() >= len {
let slice = &self.input[..len]; let slice = &self.input[..len];
@ -442,8 +355,8 @@ impl<'a> Reader<'a> {
/// ``` /// ```
/// # std::env::set_current_dir(env!("CARGO_MANIFEST_DIR")); /// # std::env::set_current_dir(env!("CARGO_MANIFEST_DIR"));
/// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap(); /// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap();
/// let mut swf_stream = swf::read_swf_header(&data[..]).unwrap(); /// let mut swf_buf = swf::decompress_swf(&data[..]).unwrap();
/// let mut reader = swf::read::Reader::new(&swf_stream.data[..], swf_stream.header.version); /// let mut reader = swf::read::Reader::new(&swf_buf.data[..], swf_buf.header.version);
/// while let Ok(tag) = reader.read_tag() { /// while let Ok(tag) = reader.read_tag() {
/// println!("Tag: {:?}", tag); /// println!("Tag: {:?}", tag);
/// } /// }
@ -2932,17 +2845,6 @@ pub fn read_compression_type<R: Read>(mut input: R) -> Result<Compression> {
Ok(compression) Ok(compression)
} }
// pub fn read_rectangle(mut input: R) -> Result<Rectangle> {
// let mut bits = self.bits();
// let num_bits: u32 = bits.read_ubits(5)?;
// Ok(Rectangle {
// x_min: bits.read_sbits_twips(num_bits)?,
// x_max: bits.read_sbits_twips(num_bits)?,
// y_min: bits.read_sbits_twips(num_bits)?,
// y_max: bits.read_sbits_twips(num_bits)?,
// })
// }
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
@ -2971,15 +2873,15 @@ pub mod tests {
file.read_to_end(&mut data).unwrap(); file.read_to_end(&mut data).unwrap();
// Halfway parse the SWF file until we find the tag we're searching for. // Halfway parse the SWF file until we find the tag we're searching for.
let swf_stream = super::read_swf_header(&data[..]).unwrap(); let swf_buf = super::decompress_swf(&data[..]).unwrap();
let data = swf_stream.data; let data = swf_buf.data;
let mut pos = 0; let mut pos = 0;
let mut tag_header_length; let mut tag_header_length;
dbg!(tag_code); dbg!(tag_code);
loop { loop {
let (swf_tag_code, length) = { let (swf_tag_code, length) = {
let mut tag_reader = Reader::new(&data[pos..], swf_stream.header.version); let mut tag_reader = Reader::new(&data[pos..], swf_buf.header.version);
let ret = tag_reader.read_tag_code_and_length().unwrap(); let ret = tag_reader.read_tag_code_and_length().unwrap();
tag_header_length = tag_header_length =
tag_reader.get_ref().as_ptr() as usize - (pos + data.as_ptr() as usize); tag_reader.get_ref().as_ptr() as usize - (pos + data.as_ptr() as usize);
@ -3023,9 +2925,9 @@ pub mod tests {
#[test] #[test]
fn read_swfs() { fn read_swfs() {
fn read_from_file(path: &str) -> SwfStream { fn read_from_file(path: &str) -> SwfBuf {
let data = std::fs::read(path).unwrap(); let data = std::fs::read(path).unwrap();
read_swf_header(&data[..]).unwrap() decompress_swf(&data[..]).unwrap()
} }
assert_eq!( assert_eq!(
@ -3049,7 +2951,7 @@ pub mod tests {
#[test] #[test]
fn read_invalid_swf() { fn read_invalid_swf() {
let junk = [0u8; 128]; let junk = [0u8; 128];
let result = read_swf_header(&junk[..]); let result = decompress_swf(&junk[..]);
// TODO: Verify correct error. // TODO: Verify correct error.
assert!(result.is_err()); assert!(result.is_err());
} }
@ -3427,3 +3329,50 @@ pub mod tests {
} }
} }
} }
impl<'a> SwfReadExt for Reader<'a> {
#[inline]
fn read_u8(&mut self) -> io::Result<u8> {
self.input.read_u8()
}
#[inline]
fn read_u16(&mut self) -> io::Result<u16> {
self.input.read_u16::<LittleEndian>()
}
#[inline]
fn read_u32(&mut self) -> io::Result<u32> {
self.input.read_u32::<LittleEndian>()
}
#[inline]
fn read_u64(&mut self) -> io::Result<u64> {
self.input.read_u64::<LittleEndian>()
}
#[inline]
fn read_i8(&mut self) -> io::Result<i8> {
self.input.read_i8()
}
#[inline]
fn read_i16(&mut self) -> io::Result<i16> {
self.input.read_i16::<LittleEndian>()
}
#[inline]
fn read_i32(&mut self) -> io::Result<i32> {
self.input.read_i32::<LittleEndian>()
}
#[inline]
fn read_f32(&mut self) -> io::Result<f32> {
self.input.read_f32::<LittleEndian>()
}
#[inline]
fn read_f64(&mut self) -> io::Result<f64> {
self.input.read_f64::<LittleEndian>()
}
}

View File

@ -4,7 +4,7 @@ use crate::avm1::types::*;
use crate::avm2::read::tests::read_abc_from_file; use crate::avm2::read::tests::read_abc_from_file;
use crate::avm2::types::*; use crate::avm2::types::*;
use crate::read::tests::{read_tag_bytes_from_file, read_tag_bytes_from_file_with_index}; use crate::read::tests::{read_tag_bytes_from_file, read_tag_bytes_from_file_with_index};
use crate::read::{read_swf, read_swf_header}; use crate::read::{decompress_swf, parse_swf};
use crate::string::SwfStr; use crate::string::SwfStr;
use crate::tag_code::TagCode; use crate::tag_code::TagCode;
use crate::types::*; use crate::types::*;
@ -16,8 +16,8 @@ use std::vec::Vec;
#[allow(dead_code)] #[allow(dead_code)]
pub fn echo_swf(filename: &str) { pub fn echo_swf(filename: &str) {
let in_data = std::fs::read(filename).unwrap(); let in_data = std::fs::read(filename).unwrap();
let swf_stream = read_swf_header(&in_data[..]).unwrap(); let swf_buf = decompress_swf(&in_data[..]).unwrap();
let swf = read_swf(&swf_stream).unwrap(); let swf = parse_swf(&swf_buf).unwrap();
let out_file = File::create(filename).unwrap(); let out_file = File::create(filename).unwrap();
write_swf(&swf, out_file).unwrap(); write_swf(&swf, out_file).unwrap();
} }

View File

@ -19,9 +19,9 @@ pub struct Swf<'a> {
pub tags: Vec<Tag<'a>>, pub tags: Vec<Tag<'a>>,
} }
/// Returned by `read::read_swf_header`. Includes the decompress /// Returned by `read::decompress_swf`.
/// stream as well as the uncompressed data length. /// Owns the decompressed SWF data, which will be referenced when parsed by `parse_swf`.
pub struct SwfStream { pub struct SwfBuf {
pub header: Header, pub header: Header,
//pub reader: crate::read::Reader<'a>, //pub reader: crate::read::Reader<'a>,
pub data: Vec<u8>, pub data: Vec<u8>,

View File

@ -133,73 +133,17 @@ fn write_lzma_swf<W: Write>(_output: W, _swf_body: &[u8]) -> Result<()> {
)) ))
} }
pub trait SwfWrite<W: Write> { pub trait SwfWriteExt {
fn get_inner(&mut self) -> &mut W; fn write_u8(&mut self, n: u8) -> io::Result<()>;
fn write_u16(&mut self, n: u16) -> io::Result<()>;
fn write_u8(&mut self, n: u8) -> io::Result<()> { fn write_u32(&mut self, n: u32) -> io::Result<()>;
self.get_inner().write_u8(n) fn write_u64(&mut self, n: u64) -> io::Result<()>;
} fn write_i8(&mut self, n: i8) -> io::Result<()>;
fn write_i16(&mut self, n: i16) -> io::Result<()>;
fn write_u16(&mut self, n: u16) -> io::Result<()> { fn write_i32(&mut self, n: i32) -> io::Result<()>;
self.get_inner().write_u16::<LittleEndian>(n) fn write_f32(&mut self, n: f32) -> io::Result<()>;
} fn write_f64(&mut self, n: f64) -> io::Result<()>;
fn write_string(&mut self, s: SwfStr<'_>) -> io::Result<()>;
fn write_u32(&mut self, n: u32) -> io::Result<()> {
self.get_inner().write_u32::<LittleEndian>(n)
}
fn write_u64(&mut self, n: u64) -> io::Result<()> {
self.get_inner().write_u64::<LittleEndian>(n)
}
fn write_i8(&mut self, n: i8) -> io::Result<()> {
self.get_inner().write_i8(n)
}
fn write_i16(&mut self, n: i16) -> io::Result<()> {
self.get_inner().write_i16::<LittleEndian>(n)
}
fn write_i32(&mut self, n: i32) -> io::Result<()> {
self.get_inner().write_i32::<LittleEndian>(n)
}
fn write_fixed8(&mut self, n: f32) -> io::Result<()> {
self.write_i16((n * 256f32) as i16)
}
fn write_fixed16(&mut self, n: f64) -> io::Result<()> {
self.write_i32((n * 65536f64) as i32)
}
fn write_f32(&mut self, n: f32) -> io::Result<()> {
self.get_inner().write_f32::<LittleEndian>(n)
}
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];
(&mut num[..]).write_f64::<LittleEndian>(n)?;
num.swap(0, 4);
num.swap(1, 5);
num.swap(2, 6);
num.swap(3, 7);
self.get_inner().write_all(&num)
}
fn write_string(&mut self, s: SwfStr<'_>) -> io::Result<()> {
self.get_inner().write_all(s.as_bytes())?;
self.write_u8(0)
}
fn bits(&mut self) -> BitWriter<&mut W> {
BitWriter {
output: self.get_inner(),
byte: 0,
bit_index: 8,
}
}
} }
pub struct BitWriter<W: Write> { pub struct BitWriter<W: Write> {
@ -272,43 +216,53 @@ struct Writer<W: Write> {
pub version: u8, pub version: u8,
} }
impl<W: Write> SwfWrite<W> for Writer<W> { impl<W: Write> SwfWriteExt for Writer<W> {
fn get_inner(&mut self) -> &mut W { #[inline]
&mut self.output
}
fn write_u8(&mut self, n: u8) -> io::Result<()> { fn write_u8(&mut self, n: u8) -> io::Result<()> {
self.output.write_u8(n) self.output.write_u8(n)
} }
#[inline]
fn write_u16(&mut self, n: u16) -> io::Result<()> { fn write_u16(&mut self, n: u16) -> io::Result<()> {
self.output.write_u16::<LittleEndian>(n) self.output.write_u16::<LittleEndian>(n)
} }
#[inline]
fn write_u32(&mut self, n: u32) -> io::Result<()> { fn write_u32(&mut self, n: u32) -> io::Result<()> {
self.output.write_u32::<LittleEndian>(n) self.output.write_u32::<LittleEndian>(n)
} }
#[inline]
fn write_u64(&mut self, n: u64) -> io::Result<()> {
self.output.write_u64::<LittleEndian>(n)
}
#[inline]
fn write_i8(&mut self, n: i8) -> io::Result<()> { fn write_i8(&mut self, n: i8) -> io::Result<()> {
self.output.write_i8(n) self.output.write_i8(n)
} }
#[inline]
fn write_i16(&mut self, n: i16) -> io::Result<()> { fn write_i16(&mut self, n: i16) -> io::Result<()> {
self.output.write_i16::<LittleEndian>(n) self.output.write_i16::<LittleEndian>(n)
} }
#[inline]
fn write_i32(&mut self, n: i32) -> io::Result<()> { fn write_i32(&mut self, n: i32) -> io::Result<()> {
self.output.write_i32::<LittleEndian>(n) self.output.write_i32::<LittleEndian>(n)
} }
#[inline]
fn write_f32(&mut self, n: f32) -> io::Result<()> { fn write_f32(&mut self, n: f32) -> io::Result<()> {
self.output.write_f32::<LittleEndian>(n) self.output.write_f32::<LittleEndian>(n)
} }
#[inline]
fn write_f64(&mut self, n: f64) -> io::Result<()> { fn write_f64(&mut self, n: f64) -> io::Result<()> {
self.output.write_f64::<LittleEndian>(n) self.output.write_f64::<LittleEndian>(n)
} }
#[inline]
fn write_string(&mut self, s: SwfStr<'_>) -> io::Result<()> { fn write_string(&mut self, s: SwfStr<'_>) -> io::Result<()> {
self.output.write_all(s.as_bytes())?; self.output.write_all(s.as_bytes())?;
self.write_u8(0) self.write_u8(0)
@ -325,6 +279,23 @@ impl<W: Write> Writer<W> {
self.output self.output
} }
#[inline]
fn bits(&mut self) -> BitWriter<&mut W> {
BitWriter {
output: &mut self.output,
byte: 0,
bit_index: 8,
}
}
fn write_fixed8(&mut self, n: f32) -> io::Result<()> {
self.write_i16((n * 256f32) as i16)
}
fn write_fixed16(&mut self, n: f64) -> io::Result<()> {
self.write_i32((n * 65536f64) as i32)
}
fn write_encoded_u32(&mut self, mut n: u32) -> Result<()> { fn write_encoded_u32(&mut self, mut n: u32) -> Result<()> {
loop { loop {
let mut byte = (n & 0b01111111) as u8; let mut byte = (n & 0b01111111) as u8;
@ -2722,7 +2693,7 @@ impl<W: Write> Writer<W> {
} }
fn write_debug_id(&mut self, debug_id: &DebugId) -> Result<()> { fn write_debug_id(&mut self, debug_id: &DebugId) -> Result<()> {
self.get_inner().write_all(debug_id)?; self.output.write_all(debug_id)?;
Ok(()) Ok(())
} }