swf_parser/complete/
movie.rs

1use std::fmt;
2
3use crate::complete::parse_tag;
4use crate::streaming::movie::parse_swf_signature;
5use crate::streaming::decompress;
6use ast::CompressionMethod;
7use nom::IResult as NomResult;
8use swf_types as ast;
9
10/// Represents the possible parse errors when parsing an SWF file.
11///
12/// Fatal errors can only occur at the beginning of the parsing. Once the header
13/// is parsed, the tags are always parsed successfully. Invalid tags produce
14/// `Raw` tags but don't prevent the parser from completing: the parser is
15/// resilient to invalid (or unknown) tags.
16#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub enum SwfParseError {
18  /// Indicates an invalid SWF signature.
19  ///
20  /// The SWF signature corresponds to the first 8 bytes of the movie.
21  /// This error occurs either if there is not enough data to even parse
22  /// the signature or if the compression method is invalid.
23  InvalidSignature,
24
25  /// Indicates that the compression method used by the payload isn't supported.
26  ///
27  /// This can only happen when the corresponding Cargo feature is disabled.
28  UnsupportedCompression(CompressionMethod),
29
30  /// Indicates a failure to decompress the payload.
31  ///
32  /// The payload represents all the data following the SWF signature.
33  /// If the SWF file uses a compressed payload (`Deflate` or `Lzma`), this
34  /// error is emitted when the decompression fails for any reason.
35  InvalidPayload,
36
37  /// Indicates an invalid movie header.
38  ///
39  /// The movie header corresponds to the first few bytes of the payload.
40  /// This error occurs if there is not enough data to parse the header.
41  InvalidHeader,
42}
43
44impl std::error::Error for SwfParseError {}
45
46impl fmt::Display for SwfParseError {
47  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48    match self {
49      SwfParseError::InvalidSignature => f.write_str("invalid SWF signature"),
50      SwfParseError::UnsupportedCompression(comp) => {
51        f.write_str("unsupported SWF compression: ")?;
52        fmt::Debug::fmt(comp, f)
53      }
54      SwfParseError::InvalidPayload => f.write_str("invalid SWF payload"),
55      SwfParseError::InvalidHeader => f.write_str("invalid SWF header"),
56    }
57  }
58}
59
60/// Parses a completely loaded SWF file.
61///
62/// See [[SwfParseError]] for details on the possible errors.
63///
64/// This function never panics.
65pub fn parse_swf(input: &[u8]) -> Result<ast::Movie, SwfParseError> {
66  let (input, signature) = match parse_swf_signature(input) {
67    Ok(ok) => ok,
68    Err(_) => return Err(SwfParseError::InvalidSignature),
69  };
70
71  let result = match signature.compression_method {
72    CompressionMethod::None => decompress::decompress_none(input),
73    #[cfg(feature="deflate")]
74    CompressionMethod::Deflate => decompress::decompress_zlib(input),
75    #[cfg(feature="lzma")]
76    CompressionMethod::Lzma => decompress::decompress_lzma(input),
77    #[allow(unreachable_patterns)]
78    method => return Err(SwfParseError::UnsupportedCompression(method)),
79  };
80
81  let (_input, payload) = result.map_err(|_| SwfParseError::InvalidPayload)?;
82
83  // TODO: should we check that the input was fully consumed?
84  // TODO: check decompressed payload length against signature?
85
86  match parse_movie(&payload, signature.swf_version) {
87    Ok((_, movie)) => Ok(movie),
88    Err(_) => Err(SwfParseError::InvalidHeader),
89  }
90}
91
92/// Parses a completely loaded movie.
93///
94/// The movie is the uncompressed payload of the SWF.
95fn parse_movie(input: &[u8], swf_version: u8) -> NomResult<&[u8], ast::Movie> {
96  let (input, header) = parse_header(input, swf_version)?;
97  let tags = parse_tag_block_string(input, swf_version);
98
99  Ok((&[][..], ast::Movie { header, tags }))
100}
101
102/// Parses the movie header from a completely loaded input.
103fn parse_header(input: &[u8], swf_version: u8) -> NomResult<&[u8], ast::Header> {
104  match crate::streaming::movie::parse_header(input, swf_version) {
105    Ok(ok) => Ok(ok),
106    Err(nom::Err::Incomplete(_)) => Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Complete))),
107    Err(e) => Err(e),
108  }
109}
110
111/// Parses the string of tags from a completely loaded input.
112pub(crate) fn parse_tag_block_string(mut input: &[u8], swf_version: u8) -> Vec<ast::Tag> {
113  let mut tags: Vec<ast::Tag> = Vec::new();
114  loop {
115    input = match parse_tag(input, swf_version) {
116      (input, Some(tag)) => {
117        tags.push(tag);
118        input
119      }
120      (_, None) => return tags,
121    }
122  }
123}