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:
parent
1d9c11e145
commit
61628a74fc
|
@ -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;
|
||||||
|
|
|
@ -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(),
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
257
swf/src/read.rs
257
swf/src/read.rs
|
@ -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>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
117
swf/src/write.rs
117
swf/src/write.rs
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue