swf_emitter/
lib.rs

1pub mod basic_data_types;
2pub mod bit_count;
3pub mod button;
4pub mod display;
5mod error;
6pub mod gradient;
7pub mod io_bits;
8pub mod morph_shape;
9pub mod movie;
10pub mod primitives;
11pub mod shape;
12pub mod sound;
13pub mod tags;
14pub mod text;
15
16use crate::movie::emit_swf as write_swf;
17use crate::tags::emit_tag as write_tag;
18use swf_types::{CompressionMethod, Movie, Tag};
19
20pub use error::SwfEmitError;
21
22pub fn emit_swf(value: &Movie, compression_method: CompressionMethod) -> Result<Vec<u8>, SwfEmitError> {
23  let mut swf_writer: Vec<u8> = Vec::new();
24  match compression_method {
25    CompressionMethod::None => crate::movie::emit_uncompressed_swf_to_buf(&mut swf_writer, value)?,
26    _ => write_swf(&mut swf_writer, value, compression_method)?,
27  }
28  Ok(swf_writer)
29}
30
31pub fn emit_tag(value: &Tag, swf_version: u8) -> std::io::Result<Vec<u8>> {
32  let mut tag_writer: Vec<u8> = Vec::new();
33  write_tag(&mut tag_writer, value, swf_version)?;
34  Ok(tag_writer)
35}
36
37#[cfg(test)]
38mod tests {
39  use std::path::Path;
40
41  use ::test_generator::test_expand_paths;
42  use swf_types::Matrix;
43  use swf_types::Rect;
44  use swf_types::{ColorTransformWithAlpha, CompressionMethod, Header, Movie, SwfSignature, Tag};
45
46  use crate::basic_data_types::{emit_color_transform_with_alpha, emit_leb128_u32, emit_matrix, emit_rect};
47  use crate::movie::{emit_header, emit_swf_signature};
48  use crate::primitives::emit_le_f16;
49  use crate::{emit_swf, emit_tag};
50
51  test_expand_paths! { test_emit_movie; "../tests/movies/*/" }
52  fn test_emit_movie(path: &str) {
53    let path: &Path = Path::new(path);
54    let _name = path
55      .components()
56      .last()
57      .unwrap()
58      .as_os_str()
59      .to_str()
60      .expect("Failed to retrieve sample name");
61
62    let ast_path = path.join("ast.json");
63    let ast_file = ::std::fs::File::open(ast_path).expect("Failed to open AST file");
64    let ast_reader = ::std::io::BufReader::new(ast_file);
65    let value: Movie = serde_json::from_reader(ast_reader).expect("Failed to read value");
66
67    const COMPRESSION_METHODS: &[(CompressionMethod, &str)] = &[
68      (CompressionMethod::None, "local-main.rs.swf"),
69      #[cfg(feature="deflate")]
70      (CompressionMethod::Deflate, "local-main-deflate.rs.swf"),
71      #[cfg(feature="lzma")]
72      (CompressionMethod::Lzma, "local-main-lzma.rs.swf"),
73    ];
74
75    for (method, filename) in COMPRESSION_METHODS {
76      let actual_bytes = emit_swf(&value, *method).unwrap();
77
78      let actual_movie_path = path.join(*filename);
79      ::std::fs::write(actual_movie_path, &actual_bytes).expect("Failed to write actual SWF");
80  
81      let actual_movie = swf_parser::parse_swf(&actual_bytes).expect("Failed to parse movie");
82
83      assert_eq!(actual_movie, value);
84    }
85  }
86
87  test_expand_paths! { test_emit_tag; "../tests/tags/*/*/" }
88  fn test_emit_tag(path: &str) {
89    let path: &Path = Path::new(path);
90    let name = path
91      .components()
92      .last()
93      .unwrap()
94      .as_os_str()
95      .to_str()
96      .expect("Failed to retrieve sample name");
97
98    let value_path = path.join("value.json");
99    let value_file = ::std::fs::File::open(value_path).expect("Failed to open value file");
100    let value_reader = ::std::io::BufReader::new(value_file);
101    let value = serde_json::from_reader::<_, Tag>(value_reader).expect("Failed to read value");
102
103    let swf_version: u8 = match name {
104      "po2-swf5" => 5,
105      _ => 10,
106    };
107
108    let actual_bytes = emit_tag(&value, swf_version).unwrap();
109
110    let expected_bytes: Vec<u8> = {
111      match ::std::fs::read(path.join("output.bytes")) {
112        Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => {
113          ::std::fs::read(path.join("input.bytes")).expect("Failed to read expected output (input.bytes)")
114        }
115        r => r.expect("Failed to read expected output (output.bytes)"),
116      }
117    };
118
119    assert_eq!(expected_bytes, actual_bytes);
120  }
121
122  macro_rules! test_various_ref_emitter_impl {
123    ($name:ident, $glob:expr, $emitter:ident, $type:ty) => {
124      test_expand_paths! { $name; $glob }
125      fn $name(path: &str) {
126        let path: &Path = Path::new(path);
127        let _name = path
128          .components()
129          .last()
130          .unwrap()
131          .as_os_str()
132          .to_str()
133          .expect("Failed to retrieve sample name");
134
135        let value_path = path.join("value.json");
136        let value_file = ::std::fs::File::open(value_path).expect("Failed to open value file");
137        let value_reader = ::std::io::BufReader::new(value_file);
138        let value = serde_json::from_reader::<_, $type>(value_reader).expect("Failed to read value");
139
140        let mut actual_bytes = Vec::new();
141        $emitter(&mut actual_bytes, &value).expect("Failed to emit");
142
143        let expected_bytes: Vec<u8> = {
144          match ::std::fs::read(path.join("output.bytes")) {
145            Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => {
146              ::std::fs::read(path.join("input.bytes")).expect("Failed to read expected output (input.bytes)")
147            }
148            r => r.expect("Failed to read expected output (output.bytes)"),
149          }
150        };
151
152        assert_eq!(expected_bytes, actual_bytes);
153      }
154    };
155  }
156
157  // TODO: Avoid duplication with `test_various_ref_emitter_impl`. The only difference is that the value is passed by
158  //       copy instead of reference.
159  macro_rules! test_various_copy_emitter_impl {
160    ($name:ident, $glob:expr, $emitter:ident, $type:ty) => {
161      test_expand_paths! { $name; $glob }
162      fn $name(path: &str) {
163        let path: &Path = Path::new(path);
164        let _name = path
165          .components()
166          .last()
167          .unwrap()
168          .as_os_str()
169          .to_str()
170          .expect("Failed to retrieve sample name");
171
172        let value_path = path.join("value.json");
173        let value_file = ::std::fs::File::open(value_path).expect("Failed to open value file");
174        let value_reader = ::std::io::BufReader::new(value_file);
175        let value = serde_json::from_reader::<_, $type>(value_reader).expect("Failed to read value");
176
177        let mut actual_bytes = Vec::new();
178        $emitter(&mut actual_bytes, value).expect("Failed to emit");
179
180        let expected_bytes: Vec<u8> = {
181          match ::std::fs::read(path.join("output.bytes")) {
182            Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => {
183              ::std::fs::read(path.join("input.bytes")).expect("Failed to read expected output (input.bytes)")
184            }
185            r => r.expect("Failed to read expected output (output.bytes)"),
186          }
187        };
188
189        assert_eq!(expected_bytes, actual_bytes);
190      }
191    };
192  }
193
194  test_various_ref_emitter_impl!(
195    test_emit_color_transform_with_alpha,
196    "../tests/various/color-transform-with-alpha/*/",
197    emit_color_transform_with_alpha,
198    ColorTransformWithAlpha
199  );
200
201  test_various_copy_emitter_impl!(test_emit_le_f16, "../tests/various/float16-le/*/", emit_le_f16, f32);
202
203  test_various_ref_emitter_impl!(test_emit_header, "../tests/various/header/*/", emit_header, Header);
204
205  test_various_ref_emitter_impl!(test_emit_matrix, "../tests/various/matrix/*/", emit_matrix, Matrix);
206
207  test_various_ref_emitter_impl!(test_emit_rect, "../tests/various/rect/*/", emit_rect, Rect);
208
209  test_various_ref_emitter_impl!(
210    test_emit_swf_signature,
211    "../tests/various/swf-signature/*/",
212    emit_swf_signature,
213    SwfSignature
214  );
215
216  test_various_copy_emitter_impl!(
217    test_emit_leb128_u32,
218    "../tests/various/uint32-leb128/*/",
219    emit_leb128_u32,
220    u32
221  );
222}