use std::fmt;
use crate::stream_buffer::{FlatBuffer, StreamBuffer};
use crate::streaming::movie::parse_swf_signature;
use swf_types::CompressionMethod;
use swf_types::{Header as SwfHeader, Tag};
mod simple;
#[cfg(feature="deflate")]
mod deflate;
#[cfg(feature="lzma")]
mod lzma;
use simple::SimpleStream;
#[cfg(feature="deflate")]
use deflate::DeflateStream;
#[cfg(feature="lzma")]
use lzma::LzmaStream;
pub struct HeaderParser(InnerHeaderParser);
enum InnerHeaderParser {
Signature(Vec<u8>),
Simple(SimpleStream<FlatBuffer>),
#[cfg(feature="deflate")]
Deflate(DeflateStream<FlatBuffer>),
#[cfg(feature="lzma")]
Lzma(LzmaStream<FlatBuffer>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum HeaderParserError {
MissingFeature(&'static str),
Other,
}
impl std::error::Error for HeaderParserError {}
impl fmt::Display for HeaderParserError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HeaderParserError::MissingFeature(feat) => write!(
f,
"unsupported compression type in SWF header: compile `swf-parser` with the `{}` feature",
feat,
),
HeaderParserError::Other => f.write_str("couldn't parse SWF header"),
}
}
}
impl HeaderParser {
pub fn new() -> Self {
Self(InnerHeaderParser::Signature(Vec::new()))
}
pub fn header(self, bytes: &[u8]) -> Result<(SwfHeader, TagParser), (Self, HeaderParserError)> {
match self.0 {
InnerHeaderParser::Signature(mut buffer) => {
let (input, parser) = Self::parser_from_signature(&mut buffer, bytes)?;
parser.header(input)
}
InnerHeaderParser::Simple(stream) => HeaderParser::simple_header(stream, bytes).map_err(|this| (this, HeaderParserError::Other)),
#[cfg(feature="lzma")]
InnerHeaderParser::Lzma(stream) => HeaderParser::lzma_header(stream, bytes).map_err(|this| (this, HeaderParserError::Other)),
#[cfg(feature="deflate")]
InnerHeaderParser::Deflate(stream) => HeaderParser::deflate_header(stream, bytes).map_err(|this| (this, HeaderParserError::Other)),
}
}
fn parser_from_signature<'a>(buffer: &'a mut Vec<u8>, bytes: &[u8]) -> Result<(&'a [u8], Self), (Self, HeaderParserError)> {
buffer.extend_from_slice(bytes);
let consumed_and_sig = parse_swf_signature(&*buffer).map(|(remaining, signature)| {
(buffer.len() - remaining.len(), signature)
});
let (input, signature) = match consumed_and_sig {
Ok((off, signature)) => (&buffer[off..], signature),
Err(_) => return Err((Self(InnerHeaderParser::Signature(std::mem::take(buffer))), HeaderParserError::Other)),
};
let buffer: FlatBuffer = FlatBuffer::new();
let parser = match signature.compression_method {
CompressionMethod::None => InnerHeaderParser::Simple(SimpleStream::new(buffer, signature)),
#[cfg(feature="lzma")]
CompressionMethod::Lzma => InnerHeaderParser::Lzma(LzmaStream::new(buffer, signature)),
#[cfg(not(feature="lzma"))]
CompressionMethod::Lzma => return Err((Self(InnerHeaderParser::Signature(std::mem::take(buffer))), HeaderParserError::MissingFeature("lzma"))),
#[cfg(feature="deflate")]
CompressionMethod::Deflate => InnerHeaderParser::Deflate(DeflateStream::new(buffer, signature)),
#[cfg(not(feature="deflate"))]
CompressionMethod::Deflate => return Err((Self(InnerHeaderParser::Signature(std::mem::take(buffer))), HeaderParserError::MissingFeature("deflate"))),
};
Ok((input, Self(parser)))
}
fn simple_header(mut stream: SimpleStream<FlatBuffer>, bytes: &[u8]) -> Result<(SwfHeader, TagParser), Self> {
stream.write(bytes);
match stream.header() {
Ok((header, stream)) => Ok((header, TagParser(InnerTagParser::Simple(stream)))),
Err(stream) => Err(Self(InnerHeaderParser::Simple(stream))),
}
}
#[cfg(feature="lzma")]
fn lzma_header(mut stream: LzmaStream<FlatBuffer>, bytes: &[u8]) -> Result<(SwfHeader, TagParser), Self> {
stream.write(bytes);
match stream.header() {
Ok((header, stream)) => Ok((header, TagParser(InnerTagParser::Lzma(stream)))),
Err(stream) => Err(Self(InnerHeaderParser::Lzma(stream))),
}
}
#[cfg(feature="deflate")]
fn deflate_header(mut stream: DeflateStream<FlatBuffer>, bytes: &[u8]) -> Result<(SwfHeader, TagParser), Self> {
stream.write(bytes);
match stream.header() {
Ok((header, stream)) => Ok((header, TagParser(InnerTagParser::Deflate(stream)))),
Err(stream) => Err(Self(InnerHeaderParser::Deflate(stream))),
}
}
}
impl Default for HeaderParser {
fn default() -> Self {
Self::new()
}
}
pub struct TagParser(InnerTagParser);
enum InnerTagParser {
Simple(SimpleStream<FlatBuffer>),
#[cfg(feature="deflate")]
Deflate(DeflateStream<FlatBuffer>),
#[cfg(feature="lzma")]
Lzma(LzmaStream<FlatBuffer>),
}
#[derive(Debug)]
pub struct ParseTagsError;
impl std::error::Error for ParseTagsError {}
impl fmt::Display for ParseTagsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("failed to parse SWF tag")
}
}
impl TagParser {
pub fn tags(&mut self, bytes: &[u8]) -> Result<Option<Vec<Tag>>, ParseTagsError> {
match &mut self.0 {
InnerTagParser::Simple(ref mut stream) => {
stream.write(bytes);
stream.tags()
}
#[cfg(feature="deflate")]
InnerTagParser::Deflate(ref mut stream) => {
stream.write(bytes);
stream.tags()
}
#[cfg(feature="lzma")]
InnerTagParser::Lzma(ref mut stream) => {
stream.write(bytes);
stream.tags()
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use swf_types::Movie;
#[test]
fn test_stream_parse_blank() {
let movie_ast_bytes: &[u8] = include_bytes!("../../../../tests/movies/blank/ast.json");
let expected: Movie = serde_json_v8::from_slice::<Movie>(movie_ast_bytes).expect("Failed to read AST");
let movie_bytes: &[u8] = include_bytes!("../../../../tests/movies/blank/main.swf");
let mut movie_bytes = movie_bytes.iter().copied().enumerate();
let mut parser = HeaderParser::new();
let mut header_output: Option<(SwfHeader, TagParser)> = None;
for (index, byte) in movie_bytes.by_ref() {
match parser.header(&[byte]) {
Ok((header, tag_parser)) => {
assert_eq!(index, 20);
header_output = Some((header, tag_parser));
break;
}
Err((next_parser, HeaderParserError::Other)) => parser = next_parser,
Err((_, e)) => panic!("{e:?}"),
}
}
assert!(header_output.is_some());
let (header, mut parser) = header_output.unwrap();
let mut tags: Vec<Tag> = Vec::new();
for (index, byte) in movie_bytes {
match parser.tags(&[byte]) {
Ok(Some(new_tags)) => tags.extend_from_slice(&new_tags),
Ok(None) => {
assert_eq!(index, 52);
break;
}
Err(_) => {}
}
}
let actual: Movie = Movie { header, tags };
assert_eq!(actual, expected);
}
}