swf_parser/streaming/parser/
mod.rs

1use std::fmt;
2
3use crate::stream_buffer::{FlatBuffer, StreamBuffer};
4use crate::streaming::movie::parse_swf_signature;
5use swf_types::CompressionMethod;
6use swf_types::{Header as SwfHeader, Tag};
7
8mod simple;
9#[cfg(feature="deflate")]
10mod deflate;
11#[cfg(feature="lzma")]
12mod lzma;
13
14use simple::SimpleStream;
15#[cfg(feature="deflate")]
16use deflate::DeflateStream;
17#[cfg(feature="lzma")]
18use lzma::LzmaStream;
19
20/// Streaming parser currently parsing the SWF header
21///
22/// This struct holds the internal state of the parser, including an internal
23/// buffer with the unparsed input provided so far.
24///
25/// This struct is logically an enum where each variant represents the state
26/// of the parser. See `InnerHeaderParser` for details on these states.
27pub struct HeaderParser(InnerHeaderParser);
28
29/// Enum holding the state of `HeaderParser`
30enum InnerHeaderParser {
31  /// Still parsing the SWF signature (8 first bytes)
32  Signature(Vec<u8>),
33  /// Finished parsing the signature, started parsing the uncompressed payload
34  Simple(SimpleStream<FlatBuffer>),
35  /// Finished parsing the signature, started parsing the `Deflate`-compressed
36  /// payload
37  #[cfg(feature="deflate")]
38  Deflate(DeflateStream<FlatBuffer>),
39  /// Finished parsing the signature, started parsing the `LZMA`-compressed
40  /// payload
41  #[cfg(feature="lzma")]
42  Lzma(LzmaStream<FlatBuffer>),
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
46pub enum HeaderParserError {
47  /// Failed to parse the header due to a disabled feature in `swf-parser`
48  MissingFeature(&'static str),
49  /// Other error (todo: replace this variant to provide more details)
50  Other,
51}
52
53impl std::error::Error for HeaderParserError {}
54
55impl fmt::Display for HeaderParserError {
56  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57    match self {
58        HeaderParserError::MissingFeature(feat) => write!(
59          f,
60          "unsupported compression type in SWF header: compile `swf-parser` with the `{}` feature",
61          feat,
62        ),
63        HeaderParserError::Other => f.write_str("couldn't parse SWF header"),
64    }
65  }
66}
67
68impl HeaderParser {
69  /// Creates a new empty streaming parser.
70  pub fn new() -> Self {
71    Self(InnerHeaderParser::Signature(Vec::new()))
72  }
73
74  /// Appends `bytes` to the internal buffer and tries to parse the SWF header.
75  ///
76  /// If there is not enough data to parse the SWF header, it returns an error containing a
77  /// `HeaderParser` to continue parsing when more data is available.
78  /// If the data is unreadable (e.g. due to an invalid compression) it returns a failure (TODO).
79  /// If there is enough data to parse the header, it returns an `Ok` result with the parsed header
80  /// and a `TagParser` to start parsing the SWF tags.
81  ///
82  /// Note: this method consumes `self` to prevent from trying to parse the SWF
83  /// header multiple times.
84  pub fn header(self, bytes: &[u8]) -> Result<(SwfHeader, TagParser), (Self, HeaderParserError)> {
85    match self.0 {
86      InnerHeaderParser::Signature(mut buffer) => {
87        let (input, parser) = Self::parser_from_signature(&mut buffer, bytes)?;
88        parser.header(input)
89      }
90      InnerHeaderParser::Simple(stream) => HeaderParser::simple_header(stream, bytes).map_err(|this| (this, HeaderParserError::Other)),
91      #[cfg(feature="lzma")]
92      InnerHeaderParser::Lzma(stream) => HeaderParser::lzma_header(stream, bytes).map_err(|this| (this, HeaderParserError::Other)),
93      #[cfg(feature="deflate")]
94      InnerHeaderParser::Deflate(stream) => HeaderParser::deflate_header(stream, bytes).map_err(|this| (this, HeaderParserError::Other)),
95    }
96  }
97
98  fn parser_from_signature<'a>(buffer: &'a mut Vec<u8>, bytes: &[u8]) -> Result<(&'a [u8], Self), (Self, HeaderParserError)> {
99    buffer.extend_from_slice(bytes);
100
101    // Weird dance to avoid borrowck issues.
102    let consumed_and_sig = parse_swf_signature(&*buffer).map(|(remaining, signature)| {
103      (buffer.len() - remaining.len(), signature)
104    });
105    let (input, signature) = match consumed_and_sig {
106      Ok((off, signature)) => (&buffer[off..], signature),
107      Err(_) => return Err((Self(InnerHeaderParser::Signature(std::mem::take(buffer))), HeaderParserError::Other)),
108    };
109
110    let buffer: FlatBuffer = FlatBuffer::new();
111    let parser = match signature.compression_method {
112      CompressionMethod::None => InnerHeaderParser::Simple(SimpleStream::new(buffer, signature)),
113      #[cfg(feature="lzma")]
114      CompressionMethod::Lzma => InnerHeaderParser::Lzma(LzmaStream::new(buffer, signature)),
115      #[cfg(not(feature="lzma"))]
116      CompressionMethod::Lzma => return Err((Self(InnerHeaderParser::Signature(std::mem::take(buffer))), HeaderParserError::MissingFeature("lzma"))),
117      #[cfg(feature="deflate")]
118      CompressionMethod::Deflate => InnerHeaderParser::Deflate(DeflateStream::new(buffer, signature)),
119      #[cfg(not(feature="deflate"))]
120      CompressionMethod::Deflate => return Err((Self(InnerHeaderParser::Signature(std::mem::take(buffer))), HeaderParserError::MissingFeature("deflate"))),
121    };
122    Ok((input, Self(parser)))
123  }
124
125  /// Finish parsing the header from an uncompressed payload.
126  fn simple_header(mut stream: SimpleStream<FlatBuffer>, bytes: &[u8]) -> Result<(SwfHeader, TagParser), Self> {
127    stream.write(bytes);
128    match stream.header() {
129      Ok((header, stream)) => Ok((header, TagParser(InnerTagParser::Simple(stream)))),
130      Err(stream) => Err(Self(InnerHeaderParser::Simple(stream))),
131    }
132  }
133
134  /// Finish parsing the header from a LZMA-compressed payload.
135  #[cfg(feature="lzma")]
136  fn lzma_header(mut stream: LzmaStream<FlatBuffer>, bytes: &[u8]) -> Result<(SwfHeader, TagParser), Self> {
137    stream.write(bytes);
138    match stream.header() {
139      Ok((header, stream)) => Ok((header, TagParser(InnerTagParser::Lzma(stream)))),
140      Err(stream) => Err(Self(InnerHeaderParser::Lzma(stream))),
141    }
142  }
143
144  /// Finish parsing the header from a deflate-compressed payload.
145  #[cfg(feature="deflate")]
146  fn deflate_header(mut stream: DeflateStream<FlatBuffer>, bytes: &[u8]) -> Result<(SwfHeader, TagParser), Self> {
147    stream.write(bytes);
148    match stream.header() {
149      Ok((header, stream)) => Ok((header, TagParser(InnerTagParser::Deflate(stream)))),
150      Err(stream) => Err(Self(InnerHeaderParser::Deflate(stream))),
151    }
152  }
153}
154
155impl Default for HeaderParser {
156  fn default() -> Self {
157    Self::new()
158  }
159}
160
161/// Streaming parser currently parsing the SWF tags.
162///
163/// The recommended way to get a `TagParser` instance is to first parse a header using
164/// an `SwfHeaderParser`.
165///
166/// This struct holds the internal state of the parser, including an internal
167/// buffer with the unparsed input provided so far.
168///
169/// This struct is logically an enum where each variant represents the state
170/// of the parser. See `InnerTagParser` for details on these states.
171pub struct TagParser(InnerTagParser);
172
173enum InnerTagParser {
174  /// Parse tags from an uncompressed stream
175  Simple(SimpleStream<FlatBuffer>),
176  /// Parse tags from a deflate-compressed stream
177  #[cfg(feature="deflate")]
178  Deflate(DeflateStream<FlatBuffer>),
179  /// Parse tags from a LZMA-compressed stream
180#[cfg(feature="lzma")]
181  Lzma(LzmaStream<FlatBuffer>),
182}
183
184// TODO: Implement proper error type
185#[derive(Debug)]
186pub struct ParseTagsError;
187
188impl std::error::Error for ParseTagsError {}
189
190impl fmt::Display for ParseTagsError {
191  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192    f.write_str("failed to parse SWF tag")
193  }
194}
195
196impl TagParser {
197  /// Appends the provided bytes to the internal buffer and tries to parse most of the tags.
198  /// Return `None` if it has finished parsing the movie.
199  ///
200  /// TODO: `impl Iterator<Item=Tag>` instead of `Vec<Tag>`
201  pub fn tags(&mut self, bytes: &[u8]) -> Result<Option<Vec<Tag>>, ParseTagsError> {
202    match &mut self.0 {
203      InnerTagParser::Simple(ref mut stream) => {
204        stream.write(bytes);
205        stream.tags()
206      }
207      #[cfg(feature="deflate")]
208      InnerTagParser::Deflate(ref mut stream) => {
209        stream.write(bytes);
210        stream.tags()
211      }
212      #[cfg(feature="lzma")]
213      InnerTagParser::Lzma(ref mut stream) => {
214        stream.write(bytes);
215        stream.tags()
216      }
217    }
218  }
219}
220
221#[cfg(test)]
222mod tests {
223  use super::*;
224  use swf_types::Movie;
225
226  #[test]
227  fn test_stream_parse_blank() {
228    let movie_ast_bytes: &[u8] = include_bytes!("../../../../tests/movies/blank/ast.json");
229    let expected: Movie = serde_json_v8::from_slice::<Movie>(movie_ast_bytes).expect("Failed to read AST");
230
231    let movie_bytes: &[u8] = include_bytes!("../../../../tests/movies/blank/main.swf");
232    let mut movie_bytes = movie_bytes.iter().copied().enumerate();
233
234    let mut parser = HeaderParser::new();
235    let mut header_output: Option<(SwfHeader, TagParser)> = None;
236    for (index, byte) in movie_bytes.by_ref() {
237      match parser.header(&[byte]) {
238        Ok((header, tag_parser)) => {
239          assert_eq!(index, 20);
240          header_output = Some((header, tag_parser));
241          break;
242        }
243        Err((next_parser, HeaderParserError::Other)) => parser = next_parser,
244        Err((_, e)) => panic!("{e:?}"),
245      }
246    }
247    assert!(header_output.is_some());
248    let (header, mut parser) = header_output.unwrap();
249    let mut tags: Vec<Tag> = Vec::new();
250    for (index, byte) in movie_bytes {
251      match parser.tags(&[byte]) {
252        Ok(Some(new_tags)) => tags.extend_from_slice(&new_tags),
253        Ok(None) => {
254          assert_eq!(index, 52);
255          break;
256        }
257        Err(_) => {}
258      }
259    }
260    let actual: Movie = Movie { header, tags };
261    assert_eq!(actual, expected);
262  }
263}