swf_parser/streaming/
movie.rs

1use crate::streaming::basic_data_types::{parse_le_ufixed8_p8, parse_rect};
2use crate::streaming::tag::parse_tag;
3use crate::streaming::decompress;
4use nom::number::streaming::{le_u16 as parse_le_u16, le_u32 as parse_le_u32, le_u8 as parse_u8};
5use nom::{IResult as NomResult, Needed};
6use std::convert::TryFrom;
7use swf_types as ast;
8
9pub fn parse_swf(input: &[u8]) -> NomResult<&[u8], ast::Movie> {
10  let (input, signature) = parse_swf_signature(input)?;
11
12  let result = match signature.compression_method {
13    ast::CompressionMethod::None => decompress::decompress_none(input),
14    ast::CompressionMethod::Deflate => decompress::decompress_zlib(input),
15    ast::CompressionMethod::Lzma => decompress::decompress_lzma(input),
16  };
17  let (input, payload) = result.unwrap();
18
19  // TODO: check decompressed payload length against signature?
20
21  match parse_movie(&payload, signature.swf_version) {
22    // TODO: should we assert that the payload is fully consumed?
23    Ok((_payload, movie)) => Ok((input, movie)),
24    Err(nom::Err::Error(e)) => Err(nom::Err::Error(nom::error::Error::new(&[], e.code))),
25    Err(nom::Err::Failure(e)) => Err(nom::Err::Failure(nom::error::Error::new(&[], e.code))),
26    Err(nom::Err::Incomplete(n)) => Err(nom::Err::Incomplete(n)),
27  }
28}
29
30pub fn parse_swf_signature(input: &[u8]) -> NomResult<&[u8], ast::SwfSignature> {
31  use nom::combinator::map;
32
33  let (input, compression_method) = parse_compression_method(input)?;
34  let (input, swf_version) = parse_u8(input)?;
35  let (input, uncompressed_file_length) = map(parse_le_u32, |x| usize::try_from(x).unwrap())(input)?;
36
37  Ok((
38    input,
39    ast::SwfSignature {
40      compression_method,
41      swf_version,
42      uncompressed_file_length,
43    },
44  ))
45}
46
47pub fn parse_compression_method(input: &[u8]) -> NomResult<&[u8], ast::CompressionMethod> {
48  use nom::bytes::streaming::take;
49  let (input, tag) = take(3usize)(input)?;
50  match tag {
51    b"FWS" => Ok((input, ast::CompressionMethod::None)),
52    b"CWS" => Ok((input, ast::CompressionMethod::Deflate)),
53    b"ZWS" => Ok((input, ast::CompressionMethod::Lzma)),
54    _ => Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Switch))),
55  }
56}
57
58pub fn parse_header(input: &[u8], swf_version: u8) -> NomResult<&[u8], ast::Header> {
59  let (input, frame_size) = parse_rect(input)?;
60  let (input, frame_rate) = parse_le_ufixed8_p8(input)?;
61  let (input, frame_count) = parse_le_u16(input)?;
62
63  Ok((
64    input,
65    ast::Header {
66      swf_version,
67      frame_size,
68      frame_rate,
69      frame_count,
70    },
71  ))
72}
73
74pub(crate) fn parse_movie(input: &[u8], swf_version: u8) -> NomResult<&[u8], ast::Movie> {
75  let (input, header) = parse_header(input, swf_version)?;
76  let (input, tags) = parse_tag_block_string(input, swf_version)?;
77
78  Ok((input, ast::Movie { header, tags }))
79}
80
81pub(crate) fn parse_tag_block_string(mut input: &[u8], swf_version: u8) -> NomResult<&[u8], Vec<ast::Tag>> {
82  let mut result: Vec<ast::Tag> = Vec::new();
83  loop {
84    input = match parse_tag(input, swf_version) {
85      Ok((input, Some(tag))) => {
86        result.push(tag);
87        input
88      }
89      Ok((input, None)) => return Ok((input, result)),
90      Err(_) => return Err(::nom::Err::Incomplete(Needed::Unknown)),
91    }
92  }
93}
94
95#[cfg(test)]
96mod tests {
97  use super::*;
98
99  #[test]
100  fn test_parse_compression_method() {
101    assert_eq!(
102      parse_compression_method(&b"FWS"[..]),
103      Ok((&[][..], ast::CompressionMethod::None))
104    );
105    assert_eq!(
106      parse_compression_method(&b"CWS"[..]),
107      Ok((&[][..], ast::CompressionMethod::Deflate))
108    );
109    assert_eq!(
110      parse_compression_method(&b"ZWS"[..]),
111      Ok((&[][..], ast::CompressionMethod::Lzma))
112    );
113  }
114
115  #[test]
116  fn test_parse_swf_header_signature() {
117    assert_eq!(
118      parse_swf_signature(&b"FWS\x0f\x08\x00\x00\x00"[..]),
119      Ok((
120        &[][..],
121        ast::SwfSignature {
122          compression_method: ast::CompressionMethod::None,
123          swf_version: 15u8,
124          uncompressed_file_length: 8,
125        }
126      ))
127    );
128    assert_eq!(
129      parse_swf_signature(&b"CWS\x0f\x08\x00\x00\x00"[..]),
130      Ok((
131        &[][..],
132        ast::SwfSignature {
133          compression_method: ast::CompressionMethod::Deflate,
134          swf_version: 15u8,
135          uncompressed_file_length: 8,
136        }
137      ))
138    );
139
140    assert_eq!(
141      parse_swf_signature(&b"\x43\x57\x53\x08\xac\x05\x00\x00"[..]),
142      Ok((
143        &[][..],
144        ast::SwfSignature {
145          compression_method: ast::CompressionMethod::Deflate,
146          swf_version: 8u8,
147          uncompressed_file_length: 1452,
148        }
149      ))
150    );
151  }
152}