1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
pub mod basic_data_types;
pub mod bit_count;
pub mod button;
pub mod display;
pub mod gradient;
pub mod io_bits;
pub mod morph_shape;
pub mod movie;
pub mod primitives;
pub mod shape;
pub mod sound;
pub mod tags;
pub mod text;

use crate::movie::emit_swf as write_swf;
use crate::tags::emit_tag as write_tag;
use swf_types::{CompressionMethod, Movie, Tag};

pub fn emit_swf(value: &Movie, compression_method: CompressionMethod) -> std::io::Result<Vec<u8>> {
  let mut swf_writer: Vec<u8> = Vec::new();
  write_swf(&mut swf_writer, value, compression_method)?;
  Ok(swf_writer)
}

pub fn emit_tag(value: &Tag, swf_version: u8) -> std::io::Result<Vec<u8>> {
  let mut tag_writer: Vec<u8> = Vec::new();
  write_tag(&mut tag_writer, value, swf_version)?;
  Ok(tag_writer)
}

#[cfg(test)]
mod tests {
  use std::path::Path;

  use ::test_generator::test_expand_paths;
  use swf_parser;
  use swf_types::Matrix;
  use swf_types::Rect;
  use swf_types::{ColorTransformWithAlpha, CompressionMethod, Header, Movie, SwfSignature, Tag};

  use crate::basic_data_types::{emit_color_transform_with_alpha, emit_leb128_u32, emit_matrix, emit_rect};
  use crate::movie::{emit_header, emit_swf_signature};
  use crate::primitives::emit_le_f16;
  use crate::{emit_swf, emit_tag};

  test_expand_paths! { test_emit_movie; "../tests/movies/*/" }
  fn test_emit_movie(path: &str) {
    let path: &Path = Path::new(path);
    let _name = path
      .components()
      .last()
      .unwrap()
      .as_os_str()
      .to_str()
      .expect("Failed to retrieve sample name");

    let ast_path = path.join("ast.json");
    let ast_file = ::std::fs::File::open(ast_path).expect("Failed to open AST file");
    let ast_reader = ::std::io::BufReader::new(ast_file);
    let value: Movie = serde_json::from_reader(ast_reader).expect("Failed to read value");

    let actual_bytes = emit_swf(&value, CompressionMethod::None).unwrap();

    let actual_movie_path = path.join("local-main.rs.swf");
    ::std::fs::write(actual_movie_path, &actual_bytes).expect("Failed to write actual SWF");

    let actual_movie = swf_parser::parse_swf(&actual_bytes).expect("Failed to parse movie");

    assert_eq!(actual_movie, value);
  }

  test_expand_paths! { test_emit_tag; "../tests/tags/*/*/" }
  fn test_emit_tag(path: &str) {
    let path: &Path = Path::new(path);
    let name = path
      .components()
      .last()
      .unwrap()
      .as_os_str()
      .to_str()
      .expect("Failed to retrieve sample name");

    let value_path = path.join("value.json");
    let value_file = ::std::fs::File::open(value_path).expect("Failed to open value file");
    let value_reader = ::std::io::BufReader::new(value_file);
    let value = serde_json::from_reader::<_, Tag>(value_reader).expect("Failed to read value");

    let swf_version: u8 = match name {
      "po2-swf5" => 5,
      _ => 10,
    };

    let actual_bytes = emit_tag(&value, swf_version).unwrap();

    let expected_bytes: Vec<u8> = {
      match ::std::fs::read(path.join("output.bytes")) {
        Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => {
          ::std::fs::read(path.join("input.bytes")).expect("Failed to read expected output (input.bytes)")
        }
        r => r.expect("Failed to read expected output (output.bytes)"),
      }
    };

    assert_eq!(expected_bytes, actual_bytes);
  }

  macro_rules! test_various_ref_emitter_impl {
    ($name:ident, $glob:expr, $emitter:ident, $type:ty) => {
      test_expand_paths! { $name; $glob }
      fn $name(path: &str) {
        let path: &Path = Path::new(path);
        let _name = path
          .components()
          .last()
          .unwrap()
          .as_os_str()
          .to_str()
          .expect("Failed to retrieve sample name");

        let value_path = path.join("value.json");
        let value_file = ::std::fs::File::open(value_path).expect("Failed to open value file");
        let value_reader = ::std::io::BufReader::new(value_file);
        let value = serde_json::from_reader::<_, $type>(value_reader).expect("Failed to read value");

        let mut actual_bytes = Vec::new();
        $emitter(&mut actual_bytes, &value).expect("Failed to emit");

        let expected_bytes: Vec<u8> = {
          match ::std::fs::read(path.join("output.bytes")) {
            Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => {
              ::std::fs::read(path.join("input.bytes")).expect("Failed to read expected output (input.bytes)")
            }
            r => r.expect("Failed to read expected output (output.bytes)"),
          }
        };

        assert_eq!(expected_bytes, actual_bytes);
      }
    };
  }

  // TODO: Avoid duplication with `test_various_ref_emitter_impl`. The only difference is that the value is passed by
  //       copy instead of reference.
  macro_rules! test_various_copy_emitter_impl {
    ($name:ident, $glob:expr, $emitter:ident, $type:ty) => {
      test_expand_paths! { $name; $glob }
      fn $name(path: &str) {
        let path: &Path = Path::new(path);
        let _name = path
          .components()
          .last()
          .unwrap()
          .as_os_str()
          .to_str()
          .expect("Failed to retrieve sample name");

        let value_path = path.join("value.json");
        let value_file = ::std::fs::File::open(value_path).expect("Failed to open value file");
        let value_reader = ::std::io::BufReader::new(value_file);
        let value = serde_json::from_reader::<_, $type>(value_reader).expect("Failed to read value");

        let mut actual_bytes = Vec::new();
        $emitter(&mut actual_bytes, value).expect("Failed to emit");

        let expected_bytes: Vec<u8> = {
          match ::std::fs::read(path.join("output.bytes")) {
            Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => {
              ::std::fs::read(path.join("input.bytes")).expect("Failed to read expected output (input.bytes)")
            }
            r => r.expect("Failed to read expected output (output.bytes)"),
          }
        };

        assert_eq!(expected_bytes, actual_bytes);
      }
    };
  }

  test_various_ref_emitter_impl!(
    test_emit_color_transform_with_alpha,
    "../tests/various/color-transform-with-alpha/*/",
    emit_color_transform_with_alpha,
    ColorTransformWithAlpha
  );

  test_various_copy_emitter_impl!(test_emit_le_f16, "../tests/various/float16-le/*/", emit_le_f16, f32);

  test_various_ref_emitter_impl!(test_emit_header, "../tests/various/header/*/", emit_header, Header);

  test_various_ref_emitter_impl!(test_emit_matrix, "../tests/various/matrix/*/", emit_matrix, Matrix);

  test_various_ref_emitter_impl!(test_emit_rect, "../tests/various/rect/*/", emit_rect, Rect);

  test_various_ref_emitter_impl!(
    test_emit_swf_signature,
    "../tests/various/swf-signature/*/",
    emit_swf_signature,
    SwfSignature
  );

  test_various_copy_emitter_impl!(
    test_emit_leb128_u32,
    "../tests/various/uint32-leb128/*/",
    emit_leb128_u32,
    u32
  );
}