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::convert::TryFrom;
use std::sync::Arc;
use swf::read::SwfRead;
use swf::read::SwfReadExt;
use swf::{FillStyle, FrameLabelData, LineStyle};
type FrameNumber = u16;

View File

@ -73,10 +73,10 @@ impl SwfMovie {
/// Construct a movie based on the contents of the SWF datastream.
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 {
header: swf_stream.header,
data: swf_stream.data,
header: swf_buf.header,
data: swf_buf.data,
url,
parameters: PropertyMap::new(),
})

View File

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

View File

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

View File

@ -2,9 +2,10 @@
use crate::avm1::{opcode::OpCode, types::*};
use crate::error::{Error, Result};
use crate::read::SwfRead;
use crate::read::SwfReadExt;
use crate::string::SwfStr;
use std::io;
use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{self, Read};
#[allow(dead_code)]
pub struct Reader<'a> {
@ -13,12 +14,6 @@ pub struct Reader<'a> {
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> {
pub fn new(input: &'a [u8], version: u8) -> Self {
Self {
@ -75,6 +70,19 @@ impl<'a> Reader<'a> {
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]
pub fn read_action(&mut self) -> Result<Option<Action<'a>>> {
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)]
pub mod tests {
use super::*;

View File

@ -2,24 +2,86 @@
use crate::avm1::opcode::OpCode;
use crate::avm1::types::*;
use crate::write::SwfWrite;
use std::io::{Result, Write};
use crate::string::SwfStr;
use crate::write::SwfWriteExt;
use byteorder::{LittleEndian, WriteBytesExt};
use std::io::{self, Result, Write};
#[allow(dead_code)]
pub struct Writer<W: Write> {
inner: W,
output: W,
version: u8,
}
impl<W: Write> SwfWrite<W> for Writer<W> {
fn get_inner(&mut self) -> &mut W {
&mut self.inner
impl<W: Write> SwfWriteExt for Writer<W> {
#[inline]
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> {
pub fn new(inner: W, version: u8) -> Writer<W> {
Writer { inner, version }
pub fn new(output: W, version: u8) -> Writer<W> {
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)]
@ -66,7 +128,7 @@ impl<W: Write> Writer<W> {
self.write_string(*param)?;
}
self.write_u16(actions.len() as u16)?;
self.inner.write_all(actions)?;
self.output.write_all(actions)?;
}
Action::DefineFunction2(ref function) => {
let len = function.name.len()
@ -111,7 +173,7 @@ impl<W: Write> Writer<W> {
self.write_string(param.name)?;
}
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::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)?,
_ => (),
}
self.inner.write_all(&action_buf)?;
self.output.write_all(&action_buf)?;
}
Action::TypeOf => self.write_action_header(OpCode::TypeOf, 0)?,
Action::WaitForFrame {
@ -323,11 +385,11 @@ impl<W: Write> Writer<W> {
}
Action::With { ref actions } => {
self.write_action_header(OpCode::With, actions.len())?;
self.inner.write_all(&actions)?;
self.output.write_all(&actions)?;
}
Action::Unknown { opcode, ref data } => {
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) => {
self.write_u8(6)?;
self.write_f64(v)?;
self.write_f64_me(v)?;
}
Value::Int(v) => {
self.write_u8(7)?;

View File

@ -1,16 +1,11 @@
use crate::avm2::types::*;
use crate::error::{Error, Result};
use crate::read::SwfRead;
use std::io::{Read, Seek, SeekFrom};
use crate::read::SwfReadExt;
use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{self, Read, Seek, SeekFrom};
pub struct Reader<R: Read> {
inner: R,
}
impl<R: Read> SwfRead<R> for Reader<R> {
fn get_inner(&mut self) -> &mut R {
&mut self.inner
}
input: R,
}
impl<R> Reader<R>
@ -19,13 +14,13 @@ where
{
#[inline]
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> {
pub fn new(inner: R) -> Reader<R> {
Reader { inner }
pub fn new(input: R) -> Reader<R> {
Reader { input }
}
pub fn read(&mut self) -> Result<AbcFile> {
@ -128,7 +123,7 @@ impl<R: Read> Reader<R> {
fn read_string(&mut self) -> Result<String> {
let len = self.read_u30()? as usize;
let mut s = String::with_capacity(len);
self.inner
self.input
.by_ref()
.take(len as u64)
.read_to_string(&mut s)?;
@ -501,7 +496,7 @@ impl<R: Read> Reader<R> {
// Read the code data.
let code_len = self.read_u30()?;
let mut code = Vec::with_capacity(code_len as usize);
self.inner
self.input
.by_ref()
.take(code_len.into())
.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)]
pub mod tests {
use super::*;
@ -874,8 +916,8 @@ pub mod tests {
pub fn read_abc_from_file(path: &str) -> Vec<u8> {
use crate::types::Tag;
let data = std::fs::read(path).unwrap();
let swf_stream = crate::read_swf_header(&data[..]).unwrap();
let swf = crate::read_swf(&swf_stream).unwrap();
let swf_buf = crate::decompress_swf(&data[..]).unwrap();
let swf = crate::parse_swf(&swf_buf).unwrap();
for tag in swf.tags {
if let Tag::DoAbc(do_abc) = tag {
return do_abc.data.to_vec();

View File

@ -1,21 +1,70 @@
use crate::avm2::opcode::OpCode;
use crate::avm2::types::*;
use crate::write::SwfWrite;
use std::io::{Result, Write};
use crate::string::SwfStr;
use crate::write::SwfWriteExt;
use byteorder::{LittleEndian, WriteBytesExt};
use std::io::{self, Result, Write};
pub struct Writer<W: Write> {
inner: W,
output: W,
}
impl<W: Write> SwfWrite<W> for Writer<W> {
fn get_inner(&mut self) -> &mut W {
&mut self.inner
impl<W: Write> SwfWriteExt for Writer<W> {
#[inline]
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> {
pub fn new(inner: W) -> Writer<W> {
Writer { inner }
pub fn new(output: W) -> Writer<W> {
Writer { output }
}
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<()> {
self.write_u30(s.len() as u32)?;
self.inner.write_all(s.as_bytes())?;
self.output.write_all(s.as_bytes())?;
Ok(())
}
@ -525,7 +574,7 @@ impl<W: Write> Writer<W> {
self.write_u30(method_body.max_scope_depth)?;
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)?;
for exception in &method_body.exceptions {

View File

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

View File

@ -16,36 +16,21 @@ use enumset::EnumSet;
use std::collections::HashSet;
use std::io::{self, Read};
/// Convenience method to parse an SWF.
///
/// 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`.
/// Parse a decompressed SWF and return a `Vec` of tags.
///
/// # Example
/// ```
/// # std::env::set_current_dir(env!("CARGO_MANIFEST_DIR"));
/// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap();
/// let stream = swf::read_swf_header(&data[..]).unwrap();
/// let swf = swf::read_swf(&stream).unwrap();
/// let stream = swf::decompress_swf(&data[..]).unwrap();
/// let swf = swf::parse_swf(&stream).unwrap();
/// println!("Number of frames: {}", swf.header.num_frames);
/// ```
pub fn read_swf<'a>(swf_stream: &'a SwfStream) -> Result<Swf<'a>> {
// // 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.
// 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);
pub fn parse_swf<'a>(swf_buf: &'a SwfBuf) -> Result<Swf<'a>> {
let mut reader = Reader::new(&swf_buf.data[..], swf_buf.header.version);
Ok(Swf {
header: swf_stream.header.clone(),
header: swf_buf.header.clone(),
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"));
/// 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);
/// ```
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.
let compression = read_compression_type(&mut input)?;
let version = input.read_u8()?;
let uncompressed_length = input.read_u32::<LittleEndian>()?;
// Now the SWF switches to a compressed stream.
let decompressed_input: Vec<u8> = match compression {
Compression::None => {
let mut data = Vec::with_capacity(uncompressed_length as usize);
input.read_to_end(&mut data)?;
data
}
let mut decompress_stream: Box<dyn Read> = match compression {
Compression::None => Box::new(input),
Compression::Zlib => {
if version < 6 {
log::warn!(
@ -82,10 +63,7 @@ pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result<SwfStream> {
version
);
}
let mut reader = make_zlib_reader(input)?;
let mut data = Vec::with_capacity(uncompressed_length as usize);
reader.read_to_end(&mut data)?;
data
make_zlib_reader(input)?
}
Compression::Lzma => {
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,
// subtract it here.
let mut reader = make_lzma_reader(input, uncompressed_length - 8)?;
let mut data = Vec::with_capacity(uncompressed_length as usize);
reader.read_to_end(&mut data)?;
data
make_lzma_reader(input, uncompressed_length - 8)?
}
};
// 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 stage_size = reader.read_rectangle()?;
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,
};
let data = reader.get_ref().to_vec();
Ok(SwfStream { header, data })
Ok(SwfBuf { header, data })
}
#[cfg(feature = "flate2")]
@ -191,64 +180,16 @@ fn make_lzma_reader<'a, R: Read + 'a>(
))
}
pub trait SwfRead<R: Read> {
fn get_inner(&mut self) -> &mut R;
fn read_u8(&mut self) -> io::Result<u8> {
self.get_inner().read_u8()
}
fn read_u16(&mut self) -> io::Result<u16> {
self.get_inner().read_u16::<LittleEndian>()
}
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 trait SwfReadExt {
fn read_u8(&mut self) -> io::Result<u8>;
fn read_u16(&mut self) -> io::Result<u16>;
fn read_u32(&mut self) -> io::Result<u32>;
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_i32(&mut self) -> io::Result<i32>;
fn read_f32(&mut self) -> io::Result<f32>;
fn read_f64(&mut self) -> io::Result<f64>;
}
pub struct BitReader<'a, 'b> {
@ -316,44 +257,6 @@ pub struct Reader<'a> {
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> {
pub fn new(input: &'a [u8], version: u8) -> Reader<'a> {
Reader {
@ -386,12 +289,22 @@ impl<'a> Reader<'a> {
fn bits<'b>(&'b mut self) -> BitReader<'a, 'b> {
BitReader {
input: self.get_inner(),
input: &mut self.input,
byte: 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]> {
if self.input.len() >= len {
let slice = &self.input[..len];
@ -442,8 +355,8 @@ impl<'a> Reader<'a> {
/// ```
/// # std::env::set_current_dir(env!("CARGO_MANIFEST_DIR"));
/// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap();
/// let mut swf_stream = swf::read_swf_header(&data[..]).unwrap();
/// let mut reader = swf::read::Reader::new(&swf_stream.data[..], swf_stream.header.version);
/// let mut swf_buf = swf::decompress_swf(&data[..]).unwrap();
/// let mut reader = swf::read::Reader::new(&swf_buf.data[..], swf_buf.header.version);
/// while let Ok(tag) = reader.read_tag() {
/// println!("Tag: {:?}", tag);
/// }
@ -2932,17 +2845,6 @@ pub fn read_compression_type<R: Read>(mut input: R) -> Result<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)]
pub mod tests {
use super::*;
@ -2971,15 +2873,15 @@ pub mod tests {
file.read_to_end(&mut data).unwrap();
// Halfway parse the SWF file until we find the tag we're searching for.
let swf_stream = super::read_swf_header(&data[..]).unwrap();
let data = swf_stream.data;
let swf_buf = super::decompress_swf(&data[..]).unwrap();
let data = swf_buf.data;
let mut pos = 0;
let mut tag_header_length;
dbg!(tag_code);
loop {
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();
tag_header_length =
tag_reader.get_ref().as_ptr() as usize - (pos + data.as_ptr() as usize);
@ -3023,9 +2925,9 @@ pub mod tests {
#[test]
fn read_swfs() {
fn read_from_file(path: &str) -> SwfStream {
fn read_from_file(path: &str) -> SwfBuf {
let data = std::fs::read(path).unwrap();
read_swf_header(&data[..]).unwrap()
decompress_swf(&data[..]).unwrap()
}
assert_eq!(
@ -3049,7 +2951,7 @@ pub mod tests {
#[test]
fn read_invalid_swf() {
let junk = [0u8; 128];
let result = read_swf_header(&junk[..]);
let result = decompress_swf(&junk[..]);
// TODO: Verify correct error.
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::types::*;
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::tag_code::TagCode;
use crate::types::*;
@ -16,8 +16,8 @@ use std::vec::Vec;
#[allow(dead_code)]
pub fn echo_swf(filename: &str) {
let in_data = std::fs::read(filename).unwrap();
let swf_stream = read_swf_header(&in_data[..]).unwrap();
let swf = read_swf(&swf_stream).unwrap();
let swf_buf = decompress_swf(&in_data[..]).unwrap();
let swf = parse_swf(&swf_buf).unwrap();
let out_file = File::create(filename).unwrap();
write_swf(&swf, out_file).unwrap();
}

View File

@ -19,9 +19,9 @@ pub struct Swf<'a> {
pub tags: Vec<Tag<'a>>,
}
/// Returned by `read::read_swf_header`. Includes the decompress
/// stream as well as the uncompressed data length.
pub struct SwfStream {
/// Returned by `read::decompress_swf`.
/// Owns the decompressed SWF data, which will be referenced when parsed by `parse_swf`.
pub struct SwfBuf {
pub header: Header,
//pub reader: crate::read::Reader<'a>,
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> {
fn get_inner(&mut self) -> &mut W;
fn write_u8(&mut self, n: u8) -> io::Result<()> {
self.get_inner().write_u8(n)
}
fn write_u16(&mut self, n: u16) -> io::Result<()> {
self.get_inner().write_u16::<LittleEndian>(n)
}
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 trait SwfWriteExt {
fn write_u8(&mut self, n: u8) -> io::Result<()>;
fn write_u16(&mut self, n: u16) -> io::Result<()>;
fn write_u32(&mut self, n: u32) -> io::Result<()>;
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_i32(&mut self, n: i32) -> io::Result<()>;
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<()>;
}
pub struct BitWriter<W: Write> {
@ -272,43 +216,53 @@ struct Writer<W: Write> {
pub version: u8,
}
impl<W: Write> SwfWrite<W> for Writer<W> {
fn get_inner(&mut self) -> &mut W {
&mut self.output
}
impl<W: Write> SwfWriteExt for Writer<W> {
#[inline]
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)
@ -325,6 +279,23 @@ impl<W: Write> Writer<W> {
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<()> {
loop {
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<()> {
self.get_inner().write_all(debug_id)?;
self.output.write_all(debug_id)?;
Ok(())
}