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 match parse_movie(&payload, signature.swf_version) {
22 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}