swf/
write.rs

1use crate::{
2    error::{Error, Result},
3    string::SwfStr,
4    tag_code::TagCode,
5    types::*,
6};
7use bitstream_io::BitWrite;
8use byteorder::{LittleEndian, WriteBytesExt};
9use std::cmp::max;
10use std::io::{self, Write};
11
12/// Writes an SWF file to an output stream.
13/// # Example
14/// ```
15/// use swf::*;
16///
17/// let header = Header {
18///     compression: Compression::Zlib,
19///     version: 6,
20///     stage_size: Rectangle {
21///         x_min: Twips::ZERO,
22///         x_max: Twips::from_pixels(400.0),
23///         y_min: Twips::ZERO,
24///         y_max: Twips::from_pixels(400.0),
25///     },
26///     frame_rate: Fixed8::from_f32(60.0),
27///     num_frames: 1,
28/// };
29/// let tags = [
30///     Tag::SetBackgroundColor(Color { r: 255, g: 0, b: 0, a: 255 }),
31///     Tag::ShowFrame,
32/// ];
33/// let output = Vec::new();
34/// swf::write_swf(&header, &tags, output).unwrap();
35/// ```
36pub fn write_swf<W: Write>(header: &Header, tags: &[Tag<'_>], output: W) -> Result<()> {
37    // Write SWF body.
38    let mut swf_body = Vec::new();
39    {
40        let mut writer = Writer::new(&mut swf_body, header.version);
41        // Write main timeline tag list.
42        writer.write_tag_list(tags)?;
43    }
44    write_swf_raw_tags(header, &swf_body, output)
45}
46
47/// Writes a SWF to the output stream, where the tag list has already been serialized to bytes.
48/// This still appends other header information such as stage size.
49pub fn write_swf_raw_tags<W: Write>(header: &Header, tags: &[u8], mut output: W) -> Result<()> {
50    let signature = match header.compression {
51        Compression::None => b"FWS",
52        Compression::Zlib => b"CWS",
53        Compression::Lzma => b"ZWS",
54    };
55    output.write_all(&signature[..])?;
56    output.write_u8(header.version)?;
57
58    // Write SWF body.
59    let mut swf_body = Vec::new();
60    {
61        let mut writer = Writer::new(&mut swf_body, header.version);
62
63        writer.write_rectangle(&header.stage_size)?;
64        writer.write_fixed8(header.frame_rate)?;
65        writer.write_u16(header.num_frames)?;
66    }
67    swf_body.extend_from_slice(tags);
68
69    // Write SWF header.
70    // Uncompressed SWF length.
71    output.write_u32::<LittleEndian>(swf_body.len() as u32 + 8)?;
72
73    // Compress SWF body.
74    match header.compression {
75        Compression::None => output.write_all(&swf_body)?,
76
77        Compression::Zlib => write_zlib_swf(&mut output, &swf_body)?,
78
79        Compression::Lzma => {
80            write_lzma_swf(&mut output, &swf_body)?;
81            // 5 bytes of garbage data?
82            //output.write_all(&[0xFF, 0xB5, 0xE6, 0xF8, 0xCB])?;
83        }
84    }
85
86    Ok(())
87}
88
89#[cfg(feature = "flate2")]
90fn write_zlib_swf<W: Write>(mut output: W, swf_body: &[u8]) -> Result<()> {
91    use flate2::write::ZlibEncoder;
92    use flate2::Compression;
93    let mut encoder = ZlibEncoder::new(&mut output, Compression::best());
94    encoder.write_all(swf_body)?;
95    encoder.finish()?;
96    Ok(())
97}
98
99#[cfg(not(feature = "flate2"))]
100fn write_zlib_swf<W: Write>(_output: W, _swf_body: &[u8]) -> Result<()> {
101    Err(Error::unsupported(
102        "Support for Zlib compressed SWFs is not enabled.",
103    ))
104}
105
106#[cfg(feature = "lzma")]
107fn write_lzma_swf<W: Write>(mut output: W, mut swf_body: &[u8]) -> Result<()> {
108    // Compress data using LZMA.
109    let mut data = vec![];
110    lzma_rs::lzma_compress(&mut swf_body, &mut data)?;
111
112    // Flash uses a mangled LZMA header, so we have to massage it into the SWF format.
113    // https://helpx.adobe.com/flash-player/kb/exception-thrown-you-decompress-lzma-compressed.html
114    output.write_u32::<LittleEndian>(data.len() as u32 - 13)?; // Compressed length (- 13 to not include lzma header)
115    output.write_all(&data[0..5])?; // LZMA properties
116    output.write_all(&data[13..])?; // Data
117    Ok(())
118}
119
120#[cfg(not(feature = "lzma"))]
121fn write_lzma_swf<W: Write>(_output: W, _swf_body: &[u8]) -> Result<()> {
122    Err(Error::unsupported(
123        "Support for LZMA compressed SWFs is not enabled.",
124    ))
125}
126
127pub trait SwfWriteExt {
128    fn write_u8(&mut self, n: u8) -> io::Result<()>;
129    fn write_u16(&mut self, n: u16) -> io::Result<()>;
130    fn write_u32(&mut self, n: u32) -> io::Result<()>;
131    fn write_u64(&mut self, n: u64) -> io::Result<()>;
132    fn write_i8(&mut self, n: i8) -> io::Result<()>;
133    fn write_i16(&mut self, n: i16) -> io::Result<()>;
134    fn write_i32(&mut self, n: i32) -> io::Result<()>;
135    fn write_f32(&mut self, n: f32) -> io::Result<()>;
136    fn write_f64(&mut self, n: f64) -> io::Result<()>;
137    fn write_string(&mut self, s: &'_ SwfStr) -> io::Result<()>;
138}
139
140pub struct BitWriter<W: Write> {
141    bits: bitstream_io::BitWriter<W, bitstream_io::BigEndian>,
142}
143
144impl<W: Write> BitWriter<W> {
145    #[inline]
146    fn write_bit(&mut self, bit: bool) -> io::Result<()> {
147        self.bits.write_bit(bit)
148    }
149
150    #[inline]
151    fn write_ubits(&mut self, num_bits: u32, n: u32) -> io::Result<()> {
152        if num_bits > 0 {
153            self.bits.write(num_bits, n)
154        } else {
155            Ok(())
156        }
157    }
158
159    #[inline]
160    fn write_sbits(&mut self, num_bits: u32, n: i32) -> io::Result<()> {
161        if num_bits > 0 {
162            self.bits.write_signed(num_bits, n)
163        } else {
164            Ok(())
165        }
166    }
167
168    #[inline]
169    fn write_sbits_fixed8(&mut self, num_bits: u32, n: Fixed8) -> io::Result<()> {
170        self.write_sbits(num_bits, n.get().into())
171    }
172
173    #[inline]
174    fn write_sbits_twips(&mut self, num_bits: u32, twips: Twips) -> io::Result<()> {
175        self.write_sbits(num_bits, twips.get())
176    }
177
178    #[inline]
179    fn write_fbits(&mut self, num_bits: u32, n: Fixed16) -> io::Result<()> {
180        self.write_sbits(num_bits, n.get())
181    }
182
183    #[inline]
184    fn flush(&mut self) -> io::Result<()> {
185        self.bits.byte_align()
186    }
187
188    #[inline]
189    fn writer(&mut self) -> &mut W {
190        let _ = self.bits.flush();
191        self.bits.writer().unwrap()
192    }
193}
194
195impl<W: Write> Drop for BitWriter<W> {
196    #[inline]
197    fn drop(&mut self) {
198        let _ = self.flush();
199        let _ = self.bits.flush();
200    }
201}
202
203struct Writer<W: Write> {
204    pub output: W,
205    pub version: u8,
206}
207
208impl<W: Write> SwfWriteExt for Writer<W> {
209    #[inline]
210    fn write_u8(&mut self, n: u8) -> io::Result<()> {
211        self.output.write_u8(n)
212    }
213
214    #[inline]
215    fn write_u16(&mut self, n: u16) -> io::Result<()> {
216        self.output.write_u16::<LittleEndian>(n)
217    }
218
219    #[inline]
220    fn write_u32(&mut self, n: u32) -> io::Result<()> {
221        self.output.write_u32::<LittleEndian>(n)
222    }
223
224    #[inline]
225    fn write_u64(&mut self, n: u64) -> io::Result<()> {
226        self.output.write_u64::<LittleEndian>(n)
227    }
228
229    #[inline]
230    fn write_i8(&mut self, n: i8) -> io::Result<()> {
231        self.output.write_i8(n)
232    }
233
234    #[inline]
235    fn write_i16(&mut self, n: i16) -> io::Result<()> {
236        self.output.write_i16::<LittleEndian>(n)
237    }
238
239    #[inline]
240    fn write_i32(&mut self, n: i32) -> io::Result<()> {
241        self.output.write_i32::<LittleEndian>(n)
242    }
243
244    #[inline]
245    fn write_f32(&mut self, n: f32) -> io::Result<()> {
246        self.output.write_f32::<LittleEndian>(n)
247    }
248
249    #[inline]
250    fn write_f64(&mut self, n: f64) -> io::Result<()> {
251        self.output.write_f64::<LittleEndian>(n)
252    }
253
254    #[inline]
255    fn write_string(&mut self, s: &'_ SwfStr) -> io::Result<()> {
256        self.output.write_all(s.as_bytes())?;
257        self.write_u8(0)
258    }
259}
260
261impl<W: Write> Writer<W> {
262    fn new(output: W, version: u8) -> Self {
263        Self { output, version }
264    }
265
266    #[inline]
267    fn bits(&mut self) -> BitWriter<&mut W> {
268        BitWriter {
269            bits: bitstream_io::BitWriter::new(&mut self.output),
270        }
271    }
272
273    #[inline]
274    fn write_fixed8(&mut self, n: Fixed8) -> io::Result<()> {
275        self.write_i16(n.get())
276    }
277
278    #[inline]
279    fn write_fixed16(&mut self, n: Fixed16) -> io::Result<()> {
280        self.write_i32(n.get())
281    }
282
283    fn write_encoded_u32(&mut self, mut n: u32) -> Result<()> {
284        loop {
285            let mut byte = (n & 0b01111111) as u8;
286            n >>= 7;
287            if n != 0 {
288                byte |= 0b10000000;
289            }
290            self.write_u8(byte)?;
291            if n == 0 {
292                break;
293            }
294        }
295        Ok(())
296    }
297
298    fn write_rectangle(&mut self, rectangle: &Rectangle<Twips>) -> Result<()> {
299        let num_bits = [
300            rectangle.x_min,
301            rectangle.x_max,
302            rectangle.y_min,
303            rectangle.y_max,
304        ]
305        .iter()
306        .map(|x| count_sbits_twips(*x))
307        .max()
308        .unwrap();
309        let mut bits = self.bits();
310        bits.write_ubits(5, num_bits)?;
311        bits.write_sbits_twips(num_bits, rectangle.x_min)?;
312        bits.write_sbits_twips(num_bits, rectangle.x_max)?;
313        bits.write_sbits_twips(num_bits, rectangle.y_min)?;
314        bits.write_sbits_twips(num_bits, rectangle.y_max)?;
315        Ok(())
316    }
317
318    fn write_character_id(&mut self, id: CharacterId) -> Result<()> {
319        self.write_u16(id)?;
320        Ok(())
321    }
322
323    fn write_rgb(&mut self, color: &Color) -> Result<()> {
324        self.write_u8(color.r)?;
325        self.write_u8(color.g)?;
326        self.write_u8(color.b)?;
327        Ok(())
328    }
329
330    fn write_rgba(&mut self, color: &Color) -> Result<()> {
331        self.write_u8(color.r)?;
332        self.write_u8(color.g)?;
333        self.write_u8(color.b)?;
334        self.write_u8(color.a)?;
335        Ok(())
336    }
337
338    fn write_color_transform_no_alpha(&mut self, color_transform: &ColorTransform) -> Result<()> {
339        // TODO: Assert that alpha is 1.0?
340        let has_mult = !color_transform.r_multiply.is_one()
341            || !color_transform.g_multiply.is_one()
342            || !color_transform.b_multiply.is_one();
343        let has_add =
344            color_transform.r_add != 0 || color_transform.g_add != 0 || color_transform.b_add != 0;
345        let multiply = [
346            color_transform.r_multiply,
347            color_transform.g_multiply,
348            color_transform.b_multiply,
349        ];
350        let add = [
351            color_transform.a_add,
352            color_transform.g_add,
353            color_transform.b_add,
354            color_transform.a_add,
355        ];
356        let mut bits = self.bits();
357        bits.write_bit(has_mult)?;
358        bits.write_bit(has_add)?;
359        let mut num_bits = if has_mult {
360            multiply
361                .iter()
362                .map(|n| count_sbits(n.get().into()))
363                .max()
364                .unwrap()
365        } else {
366            0
367        };
368        if has_add {
369            num_bits = max(
370                num_bits,
371                add.iter().map(|n| count_sbits((*n).into())).max().unwrap(),
372            );
373        }
374        bits.write_ubits(4, num_bits)?;
375        if has_mult {
376            bits.write_sbits_fixed8(num_bits, color_transform.r_multiply)?;
377            bits.write_sbits_fixed8(num_bits, color_transform.g_multiply)?;
378            bits.write_sbits_fixed8(num_bits, color_transform.b_multiply)?;
379        }
380        if has_add {
381            bits.write_sbits(num_bits, color_transform.r_add.into())?;
382            bits.write_sbits(num_bits, color_transform.g_add.into())?;
383            bits.write_sbits(num_bits, color_transform.b_add.into())?;
384        }
385        Ok(())
386    }
387
388    fn write_color_transform(&mut self, color_transform: &ColorTransform) -> Result<()> {
389        let has_mult = !color_transform.r_multiply.is_one()
390            || !color_transform.g_multiply.is_one()
391            || !color_transform.b_multiply.is_one()
392            || !color_transform.a_multiply.is_one();
393        let has_add = color_transform.r_add != 0
394            || color_transform.g_add != 0
395            || color_transform.b_add != 0
396            || color_transform.a_add != 0;
397        let multiply = [
398            color_transform.r_multiply,
399            color_transform.g_multiply,
400            color_transform.b_multiply,
401            color_transform.a_multiply,
402        ];
403        let add = [
404            color_transform.r_add,
405            color_transform.g_add,
406            color_transform.b_add,
407            color_transform.a_add,
408        ];
409        let mut bits = self.bits();
410        bits.write_bit(has_add)?;
411        bits.write_bit(has_mult)?;
412        let mut num_bits = if has_mult {
413            multiply
414                .iter()
415                .map(|n| count_sbits(n.get().into()))
416                .max()
417                .unwrap()
418        } else {
419            0
420        };
421        if has_add {
422            num_bits = max(
423                num_bits,
424                add.iter().map(|n| count_sbits((*n).into())).max().unwrap(),
425            );
426        }
427        bits.write_ubits(4, num_bits)?;
428        if has_mult {
429            bits.write_sbits_fixed8(num_bits, color_transform.r_multiply)?;
430            bits.write_sbits_fixed8(num_bits, color_transform.g_multiply)?;
431            bits.write_sbits_fixed8(num_bits, color_transform.b_multiply)?;
432            bits.write_sbits_fixed8(num_bits, color_transform.a_multiply)?;
433        }
434        if has_add {
435            bits.write_sbits(num_bits, color_transform.r_add.into())?;
436            bits.write_sbits(num_bits, color_transform.g_add.into())?;
437            bits.write_sbits(num_bits, color_transform.b_add.into())?;
438            bits.write_sbits(num_bits, color_transform.a_add.into())?;
439        }
440        Ok(())
441    }
442
443    fn write_matrix(&mut self, m: &Matrix) -> Result<()> {
444        let mut bits = self.bits();
445        // Scale
446        let has_scale = m.a != Fixed16::ONE || m.d != Fixed16::ONE;
447        bits.write_bit(has_scale)?;
448        if has_scale {
449            let num_bits = max(count_fbits(m.a), count_fbits(m.d));
450            bits.write_ubits(5, num_bits)?;
451            bits.write_fbits(num_bits, m.a)?;
452            bits.write_fbits(num_bits, m.d)?;
453        }
454        // Rotate/Skew
455        let has_rotate_skew = m.b != Fixed16::ZERO || m.c != Fixed16::ZERO;
456        bits.write_bit(has_rotate_skew)?;
457        if has_rotate_skew {
458            let num_bits = max(count_fbits(m.b), count_fbits(m.c));
459            bits.write_ubits(5, num_bits)?;
460            bits.write_fbits(num_bits, m.b)?;
461            bits.write_fbits(num_bits, m.c)?;
462        }
463        // Translate (always written)
464        let num_bits = max(count_sbits_twips(m.tx), count_sbits_twips(m.ty));
465        bits.write_ubits(5, num_bits)?;
466        bits.write_sbits_twips(num_bits, m.tx)?;
467        bits.write_sbits_twips(num_bits, m.ty)?;
468        Ok(())
469    }
470
471    fn write_language(&mut self, language: Language) -> Result<()> {
472        self.write_u8(language as u8)?;
473        Ok(())
474    }
475
476    fn write_tag(&mut self, tag: &Tag) -> Result<()> {
477        match *tag {
478            Tag::ShowFrame => self.write_tag_header(TagCode::ShowFrame, 0)?,
479
480            Tag::ExportAssets(ref exports) => self.write_export_assets(&exports[..])?,
481
482            Tag::Protect(password) => {
483                if let Some(password_md5) = password {
484                    self.write_tag_header(TagCode::Protect, password_md5.len() as u32 + 3)?;
485                    self.write_u16(0)?; // Two null bytes? Not specified in SWF19.
486                    self.write_string(password_md5)?;
487                } else {
488                    self.write_tag_header(TagCode::Protect, 0)?;
489                }
490            }
491
492            #[allow(clippy::unusual_byte_groupings)]
493            Tag::CsmTextSettings(ref settings) => {
494                self.write_tag_header(TagCode::CsmTextSettings, 12)?;
495                self.write_character_id(settings.id)?;
496                self.write_u8(
497                    if settings.use_advanced_rendering {
498                        0b01_000000
499                    } else {
500                        0
501                    } | ((settings.grid_fit as u8) << 3),
502                )?;
503                self.write_f32(settings.thickness)?;
504                self.write_f32(settings.sharpness)?;
505                self.write_u8(0)?; // Reserved (0).
506            }
507
508            Tag::DefineBinaryData(ref binary_data) => self.write_define_binary_data(binary_data)?,
509
510            Tag::DefineBits { id, jpeg_data } => {
511                self.write_tag_header(TagCode::DefineBits, jpeg_data.len() as u32 + 2)?;
512                self.write_u16(id)?;
513                self.output.write_all(jpeg_data)?;
514            }
515
516            Tag::DefineBitsJpeg2 { id, jpeg_data } => {
517                self.write_tag_header(TagCode::DefineBitsJpeg2, jpeg_data.len() as u32 + 2)?;
518                self.write_u16(id)?;
519                self.output.write_all(jpeg_data)?;
520            }
521
522            Tag::DefineBitsJpeg3(ref jpeg) => {
523                self.write_tag_header(
524                    TagCode::DefineBitsJpeg3,
525                    (jpeg.data.len() + jpeg.alpha_data.len() + 6) as u32,
526                )?;
527                self.write_u16(jpeg.id)?;
528                if jpeg.version >= 4 {
529                    self.write_fixed8(jpeg.deblocking)?;
530                }
531                // TODO(Herschel): Verify deblocking parameter is zero in version 3.
532                self.write_u32(jpeg.data.len() as u32)?;
533                self.output.write_all(jpeg.data)?;
534                self.output.write_all(jpeg.alpha_data)?;
535            }
536
537            Tag::DefineBitsLossless(ref tag) => {
538                let mut length = 7 + tag.data.len();
539                if let BitmapFormat::ColorMap8 { .. } = tag.format {
540                    length += 1;
541                }
542                // TODO(Herschel): Throw error if RGB15 in tag version 2.
543                let tag_code = if tag.version == 1 {
544                    TagCode::DefineBitsLossless
545                } else {
546                    TagCode::DefineBitsLossless2
547                };
548                self.write_tag_header(tag_code, length as u32)?;
549                self.write_character_id(tag.id)?;
550                let format_id = match tag.format {
551                    BitmapFormat::ColorMap8 { .. } => 3,
552                    BitmapFormat::Rgb15 => 4,
553                    BitmapFormat::Rgb32 => 5,
554                };
555                self.write_u8(format_id)?;
556                self.write_u16(tag.width)?;
557                self.write_u16(tag.height)?;
558                if let BitmapFormat::ColorMap8 { num_colors } = tag.format {
559                    self.write_u8(num_colors)?;
560                }
561                self.output.write_all(&tag.data)?;
562            }
563
564            Tag::DefineButton(ref button) => self.write_define_button(button)?,
565
566            Tag::DefineButton2(ref button) => self.write_define_button_2(button)?,
567
568            Tag::DefineButtonColorTransform(ref button_color) => {
569                let mut buf = Vec::new();
570                {
571                    let mut writer = Writer::new(&mut buf, self.version);
572                    writer.write_character_id(button_color.id)?;
573                    for color_transform in &button_color.color_transforms {
574                        writer.write_color_transform_no_alpha(color_transform)?;
575                    }
576                }
577                self.write_tag_header(TagCode::DefineButtonCxform, buf.len() as u32)?;
578                self.output.write_all(&buf)?;
579            }
580
581            Tag::DefineButtonSound(ref button_sounds) => {
582                let mut buf = Vec::new();
583                {
584                    let mut writer = Writer::new(&mut buf, self.version);
585                    writer.write_u16(button_sounds.id)?;
586                    if let Some(ref sound) = button_sounds.over_to_up_sound {
587                        writer.write_u16(sound.0)?;
588                        writer.write_sound_info(&sound.1)?;
589                    } else {
590                        writer.write_u16(0)?
591                    };
592                    if let Some(ref sound) = button_sounds.up_to_over_sound {
593                        writer.write_u16(sound.0)?;
594                        writer.write_sound_info(&sound.1)?;
595                    } else {
596                        writer.write_u16(0)?
597                    };
598                    if let Some(ref sound) = button_sounds.over_to_down_sound {
599                        writer.write_u16(sound.0)?;
600                        writer.write_sound_info(&sound.1)?;
601                    } else {
602                        writer.write_u16(0)?
603                    };
604                    if let Some(ref sound) = button_sounds.down_to_over_sound {
605                        writer.write_u16(sound.0)?;
606                        writer.write_sound_info(&sound.1)?;
607                    } else {
608                        writer.write_u16(0)?
609                    };
610                }
611                self.write_tag_header(TagCode::DefineButtonSound, buf.len() as u32)?;
612                self.output.write_all(&buf)?;
613            }
614
615            Tag::DefineEditText(ref edit_text) => self.write_define_edit_text(edit_text)?,
616
617            Tag::DefineFont(ref font) => {
618                let num_glyphs = font.glyphs.len();
619                let mut offsets = Vec::with_capacity(num_glyphs);
620                let mut buf = vec![];
621                {
622                    let mut writer = Writer::new(&mut buf, self.version);
623                    for glyph in &font.glyphs {
624                        let offset = num_glyphs * 2 + writer.output.len();
625                        offsets.push(offset as u16);
626
627                        // Bit length for fill and line indices.
628                        // TODO: This theoretically could be >1?
629                        let mut shape_context = ShapeContext {
630                            swf_version: self.version,
631                            shape_version: 1,
632                            num_fill_bits: 1,
633                            num_line_bits: 0,
634                        };
635                        writer.write_u8(0b0001_0000)?;
636                        let mut bits = writer.bits();
637
638                        for shape_record in glyph {
639                            Self::write_shape_record(shape_record, &mut bits, &mut shape_context)?;
640                        }
641                        // End shape record.
642                        bits.write_ubits(6, 0)?;
643                    }
644                }
645
646                let tag_len = (2 + 2 * font.glyphs.len() + buf.len()) as u32;
647                self.write_tag_header(TagCode::DefineFont, tag_len)?;
648                self.write_u16(font.id)?;
649                for offset in offsets {
650                    self.write_u16(offset)?;
651                }
652                self.output.write_all(&buf)?;
653            }
654
655            Tag::DefineFont2(ref font) => self.write_define_font_2(font)?,
656            Tag::DefineFont4(ref font) => self.write_define_font_4(font)?,
657
658            #[allow(clippy::unusual_byte_groupings)]
659            Tag::DefineFontAlignZones {
660                id,
661                thickness,
662                ref zones,
663            } => {
664                self.write_tag_header(TagCode::DefineFontAlignZones, 3 + 10 * zones.len() as u32)?;
665                self.write_character_id(id)?;
666                self.write_u8((thickness as u8) << 6)?;
667                for zone in zones {
668                    self.write_u8(2)?; // Always 2 dimensions.
669                    self.write_i16(zone.left)?;
670                    self.write_i16(zone.width)?;
671                    self.write_i16(zone.bottom)?;
672                    self.write_i16(zone.height)?;
673                    self.write_u8(0b000000_11)?; // Always 2 dimensions.
674                }
675            }
676
677            Tag::DefineFontInfo(ref font_info) => self.write_define_font_info(font_info)?,
678
679            Tag::DefineFontName {
680                id,
681                name,
682                copyright_info,
683            } => {
684                let len = name.len() + copyright_info.len() + 4;
685                self.write_tag_header(TagCode::DefineFontName, len as u32)?;
686                self.write_character_id(id)?;
687                self.write_string(name)?;
688                self.write_string(copyright_info)?;
689            }
690
691            Tag::DefineMorphShape(ref define_morph_shape) => {
692                self.write_define_morph_shape(define_morph_shape)?
693            }
694
695            Tag::DefineScalingGrid {
696                id,
697                ref splitter_rect,
698            } => {
699                let mut buf = Vec::new();
700                {
701                    let mut writer = Writer::new(&mut buf, self.version);
702                    writer.write_u16(id)?;
703                    writer.write_rectangle(splitter_rect)?;
704                }
705                self.write_tag_header(TagCode::DefineScalingGrid, buf.len() as u32)?;
706                self.output.write_all(&buf)?;
707            }
708
709            Tag::DefineShape(ref shape) => self.write_define_shape(shape)?,
710            Tag::DefineSound(ref sound) => self.write_define_sound(sound)?,
711            Tag::DefineSprite(ref sprite) => self.write_define_sprite(sprite)?,
712            Tag::DefineText(ref text) => self.write_define_text(text, 1)?,
713            Tag::DefineText2(ref text) => self.write_define_text(text, 2)?,
714            Tag::DefineVideoStream(ref video) => self.write_define_video_stream(video)?,
715            Tag::DoAbc(data) => {
716                self.write_tag_header(TagCode::DoAbc, data.len() as u32)?;
717                self.output.write_all(data)?;
718            }
719            Tag::DoAbc2(ref do_abc) => {
720                let len = do_abc.data.len() + do_abc.name.len() + 5;
721                self.write_tag_header(TagCode::DoAbc2, len as u32)?;
722                self.write_u32(do_abc.flags.bits())?;
723                self.write_string(do_abc.name)?;
724                self.output.write_all(do_abc.data)?;
725            }
726            Tag::DoAction(action_data) => {
727                self.write_tag_header(TagCode::DoAction, action_data.len() as u32)?;
728                self.output.write_all(action_data)?;
729            }
730            Tag::DoInitAction { id, action_data } => {
731                self.write_tag_header(TagCode::DoInitAction, action_data.len() as u32 + 2)?;
732                self.write_u16(id)?;
733                self.output.write_all(action_data)?;
734            }
735
736            Tag::EnableDebugger(password_md5) => {
737                let len = password_md5.len() as u32 + 1;
738                if self.version >= 6 {
739                    // SWF v6+ uses EnableDebugger2 tag.
740                    self.write_tag_header(TagCode::EnableDebugger2, len + 2)?;
741                    self.write_u16(0)?; // Reserved
742                } else {
743                    self.write_tag_header(TagCode::EnableDebugger, len)?;
744                }
745
746                self.write_string(password_md5)?;
747            }
748
749            Tag::EnableTelemetry { password_hash } => {
750                if !password_hash.is_empty() {
751                    self.write_tag_header(TagCode::EnableTelemetry, 34)?;
752                    self.write_u16(0)?;
753                    self.output.write_all(&password_hash[0..32])?;
754                } else {
755                    self.write_tag_header(TagCode::EnableTelemetry, 2)?;
756                    self.write_u16(0)?;
757                }
758            }
759
760            Tag::End => self.write_tag_header(TagCode::End, 0)?,
761
762            Tag::ImportAssets { url, ref imports } => {
763                let len = imports.iter().map(|e| e.name.len() as u32 + 3).sum::<u32>()
764                    + url.len() as u32
765                    + 1
766                    + 2;
767                // SWF v8 and later use ImportAssets2 tag.
768                if self.version >= 8 {
769                    self.write_tag_header(TagCode::ImportAssets2, len + 2)?;
770                    self.write_string(url)?;
771                    self.write_u8(1)?;
772                    self.write_u8(0)?;
773                } else {
774                    self.write_tag_header(TagCode::ImportAssets, len)?;
775                    self.write_string(url)?;
776                }
777                self.write_u16(imports.len() as u16)?;
778                for &ExportedAsset { id, name } in imports {
779                    self.write_u16(id)?;
780                    self.write_string(name)?;
781                }
782            }
783
784            Tag::JpegTables(data) => {
785                self.write_tag_header(TagCode::JpegTables, data.len() as u32)?;
786                self.output.write_all(data)?;
787            }
788
789            Tag::Metadata(metadata) => {
790                self.write_tag_header(TagCode::Metadata, metadata.len() as u32 + 1)?;
791                self.write_string(metadata)?;
792            }
793
794            // TODO: Allow clone of color.
795            Tag::SetBackgroundColor(ref color) => {
796                self.write_tag_header(TagCode::SetBackgroundColor, 3)?;
797                self.write_rgb(color)?;
798            }
799
800            Tag::ScriptLimits {
801                max_recursion_depth,
802                timeout_in_seconds,
803            } => {
804                self.write_tag_header(TagCode::ScriptLimits, 4)?;
805                self.write_u16(max_recursion_depth)?;
806                self.write_u16(timeout_in_seconds)?;
807            }
808
809            Tag::SetTabIndex { depth, tab_index } => {
810                self.write_tag_header(TagCode::SetTabIndex, 4)?;
811                self.write_u16(depth)?;
812                self.write_u16(tab_index)?;
813            }
814
815            Tag::PlaceObject(ref place_object) => match place_object.version {
816                1 => self.write_place_object(place_object)?,
817                2 => self.write_place_object_2_or_3(place_object, 2)?,
818                3 => self.write_place_object_2_or_3(place_object, 3)?,
819                4 => self.write_place_object_2_or_3(place_object, 4)?,
820                _ => return Err(Error::invalid_data("Invalid PlaceObject version.")),
821            },
822
823            Tag::RemoveObject(ref remove_object) => {
824                if let Some(id) = remove_object.character_id {
825                    self.write_tag_header(TagCode::RemoveObject, 4)?;
826                    self.write_u16(id)?;
827                } else {
828                    self.write_tag_header(TagCode::RemoveObject2, 2)?;
829                }
830                self.write_u16(remove_object.depth)?;
831            }
832
833            Tag::SoundStreamBlock(data) => {
834                self.write_tag_header(TagCode::SoundStreamBlock, data.len() as u32)?;
835                self.output.write_all(data)?;
836            }
837
838            Tag::SoundStreamHead(ref sound_stream_head) => {
839                self.write_sound_stream_head(sound_stream_head, 1)?;
840            }
841
842            Tag::SoundStreamHead2(ref sound_stream_head) => {
843                self.write_sound_stream_head(sound_stream_head, 2)?;
844            }
845
846            Tag::StartSound(ref start_sound) => {
847                let sound_info = &start_sound.sound_info;
848                let length = 3
849                    + if sound_info.in_sample.is_some() { 4 } else { 0 }
850                    + if sound_info.out_sample.is_some() {
851                        4
852                    } else {
853                        0
854                    }
855                    + if sound_info.num_loops > 1 { 2 } else { 0 }
856                    + if let Some(ref e) = sound_info.envelope {
857                        e.len() as u32 * 8 + 1
858                    } else {
859                        0
860                    };
861                self.write_tag_header(TagCode::StartSound, length)?;
862                self.write_u16(start_sound.id)?;
863                self.write_sound_info(sound_info)?;
864            }
865
866            Tag::StartSound2 {
867                class_name,
868                ref sound_info,
869            } => {
870                let length = class_name.len() as u32
871                    + 2
872                    + if sound_info.in_sample.is_some() { 4 } else { 0 }
873                    + if sound_info.out_sample.is_some() {
874                        4
875                    } else {
876                        0
877                    }
878                    + if sound_info.num_loops > 1 { 2 } else { 0 }
879                    + if let Some(ref e) = sound_info.envelope {
880                        e.len() as u32 * 8 + 1
881                    } else {
882                        0
883                    };
884                self.write_tag_header(TagCode::StartSound2, length)?;
885                self.write_string(class_name)?;
886                self.write_sound_info(sound_info)?;
887            }
888
889            Tag::SymbolClass(ref symbols) => {
890                let len = symbols
891                    .iter()
892                    .map(|e| e.class_name.len() as u32 + 3)
893                    .sum::<u32>()
894                    + 2;
895                self.write_tag_header(TagCode::SymbolClass, len)?;
896                self.write_u16(symbols.len() as u16)?;
897                for &SymbolClassLink { id, class_name } in symbols {
898                    self.write_u16(id)?;
899                    self.write_string(class_name)?;
900                }
901            }
902
903            Tag::VideoFrame(ref frame) => {
904                self.write_tag_header(TagCode::VideoFrame, 4 + frame.data.len() as u32)?;
905                self.write_character_id(frame.stream_id)?;
906                self.write_u16(frame.frame_num)?;
907                self.output.write_all(frame.data)?;
908            }
909
910            Tag::FileAttributes(attributes) => {
911                self.write_tag_header(TagCode::FileAttributes, 4)?;
912                self.write_u32(attributes.bits() as u32)?;
913            }
914
915            Tag::FrameLabel(FrameLabel { label, is_anchor }) => {
916                // TODO: Assert proper version
917                let is_anchor = is_anchor && self.version >= 6;
918                let length = label.len() as u32 + if is_anchor { 2 } else { 1 };
919                self.write_tag_header(TagCode::FrameLabel, length)?;
920                self.write_string(label)?;
921                if is_anchor {
922                    self.write_u8(1)?;
923                }
924            }
925
926            Tag::DefineSceneAndFrameLabelData(ref data) => {
927                self.write_define_scene_and_frame_label_data(data)?
928            }
929            Tag::ProductInfo(ref product_info) => self.write_product_info(product_info)?,
930            Tag::DebugId(ref debug_id) => self.write_debug_id(debug_id)?,
931            Tag::NameCharacter(ref name_character) => self.write_name_character(name_character)?,
932            Tag::Unknown { tag_code, data } => {
933                self.write_tag_code_and_length(tag_code, data.len() as u32)?;
934                self.output.write_all(data)?;
935            }
936        }
937        Ok(())
938    }
939
940    fn write_define_button(&mut self, button: &Button) -> Result<()> {
941        let mut buf = Vec::new();
942        {
943            let mut writer = Writer::new(&mut buf, self.version);
944            writer.write_u16(button.id)?;
945            for record in &button.records {
946                writer.write_button_record(record, 1)?;
947            }
948            writer.write_u8(0)?; // End button records
949                                 // TODO: Assert we have some action.
950            writer.output.write_all(button.actions[0].action_data)?;
951        }
952        self.write_tag_header(TagCode::DefineButton, buf.len() as u32)?;
953        self.output.write_all(&buf)?;
954        Ok(())
955    }
956
957    fn write_define_button_2(&mut self, button: &Button) -> Result<()> {
958        let mut buf = Vec::new();
959        {
960            let mut writer = Writer::new(&mut buf, self.version);
961            writer.write_u16(button.id)?;
962            let flags = if button.is_track_as_menu { 1 } else { 0 };
963            writer.write_u8(flags)?;
964
965            let mut record_data = Vec::new();
966            {
967                let mut writer_2 = Writer::new(&mut record_data, self.version);
968                for record in &button.records {
969                    writer_2.write_button_record(record, 2)?;
970                }
971                writer_2.write_u8(0)?; // End button records
972            }
973
974            // Write action offset, this is explicitly 0 when there are no actions.
975            if button.actions.is_empty() {
976                writer.write_u16(0)?;
977            } else {
978                writer.write_u16(record_data.len() as u16 + 2)?;
979            }
980
981            writer.output.write_all(&record_data)?;
982
983            let mut iter = button.actions.iter().peekable();
984            while let Some(action) = iter.next() {
985                if iter.peek().is_some() {
986                    let length = action.action_data.len() as u16 + 4;
987                    writer.write_u16(length)?;
988                } else {
989                    writer.write_u16(0)?;
990                }
991                let flags = action.conditions.bits();
992                writer.write_u16(flags)?;
993                writer.output.write_all(action.action_data)?;
994            }
995        }
996        self.write_tag_header(TagCode::DefineButton2, buf.len() as u32)?;
997        self.output.write_all(&buf)?;
998        Ok(())
999    }
1000
1001    fn write_define_morph_shape(&mut self, data: &DefineMorphShape) -> Result<()> {
1002        if data.start.fill_styles.len() != data.end.fill_styles.len()
1003            || data.start.line_styles.len() != data.end.line_styles.len()
1004        {
1005            return Err(Error::invalid_data(
1006                "Start and end state of a morph shape must have the same number of styles.",
1007            ));
1008        }
1009
1010        let num_fill_styles = data.start.fill_styles.len();
1011        let num_line_styles = data.start.line_styles.len();
1012        let num_fill_bits = count_ubits(num_fill_styles as u32) as u8;
1013        let num_line_bits = count_ubits(num_line_styles as u32) as u8;
1014
1015        // Need to write styles first, to calculate offset to EndEdges.
1016        let mut start_buf = Vec::new();
1017        {
1018            let mut writer = Writer::new(&mut start_buf, self.version);
1019
1020            // Styles
1021            // TODO(Herschel): Make fn write_style_len. Check version.
1022            if num_fill_styles >= 0xff {
1023                writer.write_u8(0xff)?;
1024                writer.write_u16(num_fill_styles as u16)?;
1025            } else {
1026                writer.write_u8(num_fill_styles as u8)?;
1027            }
1028            for (start, end) in data
1029                .start
1030                .fill_styles
1031                .iter()
1032                .zip(data.end.fill_styles.iter())
1033            {
1034                writer.write_morph_fill_style(start, end)?;
1035            }
1036
1037            if num_line_styles >= 0xff {
1038                writer.write_u8(0xff)?;
1039                writer.write_u16(num_line_styles as u16)?;
1040            } else {
1041                writer.write_u8(num_line_styles as u8)?;
1042            }
1043            for (start, end) in data
1044                .start
1045                .line_styles
1046                .iter()
1047                .zip(data.end.line_styles.iter())
1048            {
1049                writer.write_morph_line_style(start, end, data.version)?;
1050            }
1051
1052            // TODO(Herschel): Make fn write_shape.
1053            writer.write_u8((num_fill_bits << 4) | (num_line_bits & 0b1111))?;
1054
1055            let mut shape_context = ShapeContext {
1056                swf_version: self.version,
1057                shape_version: 1,
1058                num_fill_bits,
1059                num_line_bits,
1060            };
1061            let mut bits = writer.bits();
1062            for shape_record in &data.start.shape {
1063                Self::write_shape_record(shape_record, &mut bits, &mut shape_context)?;
1064            }
1065            // End shape record.
1066            bits.write_ubits(6, 0)?;
1067        }
1068
1069        let mut buf = Vec::new();
1070        {
1071            let mut writer = Writer::new(&mut buf, self.version);
1072            writer.write_character_id(data.id)?;
1073            writer.write_rectangle(&data.start.shape_bounds)?;
1074            writer.write_rectangle(&data.end.shape_bounds)?;
1075            if data.version >= 2 {
1076                writer.write_rectangle(&data.start.edge_bounds)?;
1077                writer.write_rectangle(&data.end.edge_bounds)?;
1078                writer.write_u8(data.flags.bits())?;
1079            }
1080
1081            // Offset to EndEdges.
1082            writer.write_u32(start_buf.len() as u32)?;
1083
1084            writer.output.write_all(&start_buf)?;
1085
1086            // EndEdges.
1087            writer.write_u8(0)?; // NumFillBits and NumLineBits are written as 0 for the end shape.
1088            let mut shape_context = ShapeContext {
1089                swf_version: self.version,
1090                shape_version: 1,
1091                num_fill_bits,
1092                num_line_bits,
1093            };
1094            let mut bits = writer.bits();
1095            for shape_record in &data.end.shape {
1096                Self::write_shape_record(shape_record, &mut bits, &mut shape_context)?;
1097            }
1098            // End shape record.
1099            bits.write_ubits(6, 0)?;
1100        }
1101
1102        let tag_code = if data.version == 1 {
1103            TagCode::DefineMorphShape
1104        } else {
1105            TagCode::DefineMorphShape2
1106        };
1107        self.write_tag_header(tag_code, buf.len() as u32)?;
1108        self.output.write_all(&buf)?;
1109        Ok(())
1110    }
1111
1112    fn write_morph_fill_style(&mut self, start: &FillStyle, end: &FillStyle) -> Result<()> {
1113        match (start, end) {
1114            (FillStyle::Color(start_color), FillStyle::Color(end_color)) => {
1115                self.write_u8(0x00)?; // Solid color.
1116                self.write_rgba(start_color)?;
1117                self.write_rgba(end_color)?;
1118            }
1119
1120            (
1121                FillStyle::LinearGradient(start_gradient),
1122                FillStyle::LinearGradient(end_gradient),
1123            ) => {
1124                self.write_u8(0x10)?; // Linear gradient.
1125                self.write_morph_gradient(start_gradient, end_gradient)?;
1126            }
1127
1128            (
1129                FillStyle::RadialGradient(start_gradient),
1130                FillStyle::RadialGradient(end_gradient),
1131            ) => {
1132                self.write_u8(0x12)?; // Linear gradient.
1133                self.write_morph_gradient(start_gradient, end_gradient)?;
1134            }
1135
1136            (
1137                FillStyle::FocalGradient {
1138                    gradient: start_gradient,
1139                    focal_point: start_focal_point,
1140                },
1141                FillStyle::FocalGradient {
1142                    gradient: end_gradient,
1143                    focal_point: end_focal_point,
1144                },
1145            ) => {
1146                self.write_u8(0x13)?; // Focal gradient.
1147                self.write_morph_gradient(start_gradient, end_gradient)?;
1148                self.write_fixed8(*start_focal_point)?;
1149                self.write_fixed8(*end_focal_point)?;
1150            }
1151
1152            (
1153                &FillStyle::Bitmap {
1154                    id,
1155                    matrix: ref start_matrix,
1156                    is_smoothed,
1157                    is_repeating,
1158                },
1159                &FillStyle::Bitmap {
1160                    id: end_id,
1161                    matrix: ref end_matrix,
1162                    is_smoothed: end_is_smoothed,
1163                    is_repeating: end_is_repeating,
1164                },
1165            ) if id == end_id && is_smoothed == end_is_smoothed
1166                || is_repeating == end_is_repeating =>
1167            {
1168                let fill_style_type = match (is_smoothed, is_repeating) {
1169                    (true, true) => 0x40,
1170                    (true, false) => 0x41,
1171                    (false, true) => 0x42,
1172                    (false, false) => 0x43,
1173                };
1174                self.write_u8(fill_style_type)?;
1175                self.write_u16(id)?;
1176                self.write_matrix(start_matrix)?;
1177                self.write_matrix(end_matrix)?;
1178            }
1179
1180            _ => {
1181                return Err(Error::invalid_data(
1182                    "Morph start and end fill styles must be the same variant.",
1183                ))
1184            }
1185        }
1186        Ok(())
1187    }
1188
1189    fn write_morph_gradient(&mut self, start: &Gradient, end: &Gradient) -> Result<()> {
1190        self.write_matrix(&start.matrix)?;
1191        self.write_matrix(&end.matrix)?;
1192        if start.records.len() != end.records.len() {
1193            return Err(Error::invalid_data(
1194                "Morph start and end gradient must have the same amount of records.",
1195            ));
1196        }
1197        self.write_gradient_flags(start)?;
1198        for (start_record, end_record) in start.records.iter().zip(end.records.iter()) {
1199            self.write_u8(start_record.ratio)?;
1200            self.write_rgba(&start_record.color)?;
1201            self.write_u8(end_record.ratio)?;
1202            self.write_rgba(&end_record.color)?;
1203        }
1204        Ok(())
1205    }
1206
1207    fn write_morph_line_style(
1208        &mut self,
1209        start: &LineStyle,
1210        end: &LineStyle,
1211        shape_version: u8,
1212    ) -> Result<()> {
1213        if shape_version < 2 {
1214            // TODO(Herschel): Handle overflow.
1215            self.write_u16(start.width.get() as u16)?;
1216            self.write_u16(end.width.get() as u16)?;
1217            match (&start.fill_style, &end.fill_style) {
1218                (FillStyle::Color(start), FillStyle::Color(end)) => {
1219                    self.write_rgba(start)?;
1220                    self.write_rgba(end)?;
1221                }
1222                _ => {
1223                    return Err(Error::invalid_data(
1224                        "Complex line styles can only be used in DefineMorphShape2 tags",
1225                    ));
1226                }
1227            }
1228        } else {
1229            if start.flags != end.flags {
1230                return Err(Error::invalid_data(
1231                    "Morph start and end line styles must have the same join parameters.",
1232                ));
1233            }
1234
1235            // TODO(Herschel): Handle overflow.
1236            self.write_u16(start.width.get() as u16)?;
1237            self.write_u16(end.width.get() as u16)?;
1238
1239            // MorphLineStyle2
1240            self.write_u16(start.flags.bits())?;
1241            if let LineJoinStyle::Miter(miter_factor) = start.join_style() {
1242                self.write_fixed8(miter_factor)?;
1243            }
1244            if start.flags.contains(LineStyleFlag::HAS_FILL) {
1245                self.write_morph_fill_style(&start.fill_style, &end.fill_style)?;
1246            } else {
1247                match (&start.fill_style, &end.fill_style) {
1248                    (FillStyle::Color(start), FillStyle::Color(end)) => {
1249                        self.write_rgba(start)?;
1250                        self.write_rgba(end)?;
1251                    }
1252                    _ => {
1253                        return Err(Error::invalid_data("Unexpected line fill style fill type"));
1254                    }
1255                }
1256            }
1257        }
1258        Ok(())
1259    }
1260
1261    fn write_define_scene_and_frame_label_data(
1262        &mut self,
1263        data: &DefineSceneAndFrameLabelData,
1264    ) -> Result<()> {
1265        let mut buf = Vec::with_capacity((data.scenes.len() + data.frame_labels.len()) * 4);
1266        {
1267            let mut writer = Writer::new(&mut buf, self.version);
1268            writer.write_encoded_u32(data.scenes.len() as u32)?;
1269            for scene in &data.scenes {
1270                writer.write_encoded_u32(scene.frame_num)?;
1271                writer.write_string(scene.label)?;
1272            }
1273            writer.write_encoded_u32(data.frame_labels.len() as u32)?;
1274            for frame_label in &data.frame_labels {
1275                writer.write_encoded_u32(frame_label.frame_num)?;
1276                writer.write_string(frame_label.label)?;
1277            }
1278        }
1279        self.write_tag_header(TagCode::DefineSceneAndFrameLabelData, buf.len() as u32)?;
1280        self.output.write_all(&buf)?;
1281        Ok(())
1282    }
1283
1284    fn write_define_shape(&mut self, shape: &Shape) -> Result<()> {
1285        let mut buf = Vec::new();
1286        {
1287            let mut writer = Writer::new(&mut buf, self.version);
1288            writer.write_u16(shape.id)?;
1289            writer.write_rectangle(&shape.shape_bounds)?;
1290            if shape.version >= 4 {
1291                writer.write_rectangle(&shape.edge_bounds)?;
1292                writer.write_u8(shape.flags.bits())?;
1293            }
1294
1295            let (num_fill_bits, num_line_bits) =
1296                writer.write_shape_styles(&shape.styles, shape.version)?;
1297            let mut shape_context = ShapeContext {
1298                swf_version: self.version,
1299                shape_version: shape.version,
1300                num_fill_bits,
1301                num_line_bits,
1302            };
1303            let mut bits = writer.bits();
1304            for shape_record in &shape.shape {
1305                Self::write_shape_record(shape_record, &mut bits, &mut shape_context)?;
1306            }
1307            // End shape record.
1308            bits.write_ubits(6, 0)?;
1309        }
1310
1311        let tag_code = match shape.version {
1312            1 => TagCode::DefineShape,
1313            2 => TagCode::DefineShape2,
1314            3 => TagCode::DefineShape3,
1315            4 => TagCode::DefineShape4,
1316            _ => return Err(Error::invalid_data("Invalid DefineShape version.")),
1317        };
1318        self.write_tag_header(tag_code, buf.len() as u32)?;
1319        self.output.write_all(&buf)?;
1320        Ok(())
1321    }
1322
1323    fn write_define_sound(&mut self, sound: &Sound) -> Result<()> {
1324        self.write_tag_header(TagCode::DefineSound, 7 + sound.data.len() as u32)?;
1325        self.write_u16(sound.id)?;
1326        self.write_sound_format(&sound.format)?;
1327        self.write_u32(sound.num_samples)?;
1328        self.output.write_all(sound.data)?;
1329        Ok(())
1330    }
1331
1332    fn write_define_sprite(&mut self, sprite: &Sprite) -> Result<()> {
1333        let mut buf = Vec::new();
1334        {
1335            let mut writer = Writer::new(&mut buf, self.version);
1336            writer.write_u16(sprite.id)?;
1337            writer.write_u16(sprite.num_frames)?;
1338            writer.write_tag_list(&sprite.tags)?;
1339        };
1340        self.write_tag_header(TagCode::DefineSprite, buf.len() as u32)?;
1341        self.output.write_all(&buf)?;
1342        Ok(())
1343    }
1344
1345    fn write_export_assets(&mut self, exports: &[ExportedAsset]) -> Result<()> {
1346        let len = exports.iter().map(|e| e.name.len() as u32 + 1).sum::<u32>()
1347            + exports.len() as u32 * 2
1348            + 2;
1349        self.write_tag_header(TagCode::ExportAssets, len)?;
1350        self.write_u16(exports.len() as u16)?;
1351        for &ExportedAsset { id, name } in exports {
1352            self.write_u16(id)?;
1353            self.write_string(name)?;
1354        }
1355        Ok(())
1356    }
1357
1358    fn write_button_record(&mut self, record: &ButtonRecord, tag_version: u8) -> Result<()> {
1359        // TODO: Validate version
1360        let flags = if record.blend_mode != BlendMode::Normal {
1361            0b10_0000
1362        } else {
1363            0
1364        } | if !record.filters.is_empty() {
1365            0b1_0000
1366        } else {
1367            0
1368        } | record.states.bits();
1369        self.write_u8(flags)?;
1370        self.write_u16(record.id)?;
1371        self.write_u16(record.depth)?;
1372        self.write_matrix(&record.matrix)?;
1373        if tag_version >= 2 {
1374            self.write_color_transform(&record.color_transform)?;
1375            if !record.filters.is_empty() {
1376                self.write_u8(record.filters.len() as u8)?;
1377                for filter in &record.filters {
1378                    self.write_filter(filter)?;
1379                }
1380            }
1381            if record.blend_mode != BlendMode::Normal {
1382                self.write_blend_mode(record.blend_mode)?;
1383            }
1384        }
1385        Ok(())
1386    }
1387
1388    fn write_blend_mode(&mut self, blend_mode: BlendMode) -> Result<()> {
1389        self.write_u8(blend_mode as u8)?;
1390        Ok(())
1391    }
1392
1393    fn write_shape_styles(&mut self, styles: &ShapeStyles, shape_version: u8) -> Result<(u8, u8)> {
1394        // TODO: Check shape_version.
1395        if styles.fill_styles.len() >= 0xff {
1396            self.write_u8(0xff)?;
1397            self.write_u16(styles.fill_styles.len() as u16)?;
1398        } else {
1399            self.write_u8(styles.fill_styles.len() as u8)?;
1400        }
1401        for fill_style in &styles.fill_styles {
1402            self.write_fill_style(fill_style, shape_version)?;
1403        }
1404
1405        if styles.line_styles.len() >= 0xff {
1406            self.write_u8(0xff)?;
1407            self.write_u16(styles.line_styles.len() as u16)?;
1408        } else {
1409            self.write_u8(styles.line_styles.len() as u8)?;
1410        }
1411        for line_style in &styles.line_styles {
1412            self.write_line_style(line_style, shape_version)?;
1413        }
1414
1415        let num_fill_bits = count_ubits(styles.fill_styles.len() as u32) as u8;
1416        let num_line_bits = count_ubits(styles.line_styles.len() as u32) as u8;
1417        self.write_u8((num_fill_bits << 4) | (num_line_bits & 0b1111))?;
1418
1419        Ok((num_fill_bits, num_line_bits))
1420    }
1421
1422    fn write_shape_record<T: Write>(
1423        record: &ShapeRecord,
1424        bits: &mut BitWriter<T>,
1425        context: &mut ShapeContext,
1426    ) -> Result<()> {
1427        match record {
1428            ShapeRecord::StraightEdge { delta } => {
1429                bits.write_ubits(2, 0b11)?; // Straight edge
1430                                            // TODO: Check underflow?
1431                let num_bits = count_sbits_twips(delta.dx)
1432                    .max(count_sbits_twips(delta.dy))
1433                    .max(2);
1434                let is_axis_aligned = delta.dx == Twips::ZERO || delta.dy == Twips::ZERO;
1435                bits.write_ubits(4, num_bits - 2)?;
1436                bits.write_bit(!is_axis_aligned)?;
1437                let is_vertical = is_axis_aligned && delta.dx == Twips::ZERO;
1438                if is_axis_aligned {
1439                    bits.write_bit(is_vertical)?;
1440                }
1441                if !is_axis_aligned || !is_vertical {
1442                    bits.write_sbits_twips(num_bits, delta.dx)?;
1443                }
1444                if !is_axis_aligned || is_vertical {
1445                    bits.write_sbits_twips(num_bits, delta.dy)?;
1446                }
1447            }
1448            ShapeRecord::CurvedEdge {
1449                control_delta,
1450                anchor_delta,
1451            } => {
1452                bits.write_ubits(2, 0b10)?; // Curved edge
1453                let num_bits = count_sbits_twips(control_delta.dx)
1454                    .max(count_sbits_twips(control_delta.dy))
1455                    .max(count_sbits_twips(anchor_delta.dx))
1456                    .max(count_sbits_twips(anchor_delta.dy))
1457                    .max(2);
1458                bits.write_ubits(4, num_bits - 2)?;
1459                bits.write_sbits_twips(num_bits, control_delta.dx)?;
1460                bits.write_sbits_twips(num_bits, control_delta.dy)?;
1461                bits.write_sbits_twips(num_bits, anchor_delta.dx)?;
1462                bits.write_sbits_twips(num_bits, anchor_delta.dy)?;
1463            }
1464            ShapeRecord::StyleChange(style_change) => {
1465                bits.write_bit(false)?; // Style change
1466                let num_fill_bits = context.num_fill_bits.into();
1467                let num_line_bits = context.num_line_bits.into();
1468                let mut flags = ShapeRecordFlag::empty();
1469                flags.set(ShapeRecordFlag::MOVE_TO, style_change.move_to.is_some());
1470                flags.set(
1471                    ShapeRecordFlag::FILL_STYLE_0,
1472                    style_change.fill_style_0.is_some(),
1473                );
1474                flags.set(
1475                    ShapeRecordFlag::FILL_STYLE_1,
1476                    style_change.fill_style_1.is_some(),
1477                );
1478                flags.set(
1479                    ShapeRecordFlag::LINE_STYLE,
1480                    style_change.line_style.is_some(),
1481                );
1482                flags.set(
1483                    ShapeRecordFlag::NEW_STYLES,
1484                    style_change.new_styles.is_some(),
1485                );
1486                bits.write_ubits(5, flags.bits().into())?;
1487                if let Some(move_to) = &style_change.move_to {
1488                    let num_bits = count_sbits_twips(move_to.x).max(count_sbits_twips(move_to.y));
1489                    bits.write_ubits(5, num_bits)?;
1490                    bits.write_sbits_twips(num_bits, move_to.x)?;
1491                    bits.write_sbits_twips(num_bits, move_to.y)?;
1492                }
1493                if let Some(fill_style_index) = style_change.fill_style_0 {
1494                    bits.write_ubits(num_fill_bits, fill_style_index)?;
1495                }
1496                if let Some(fill_style_index) = style_change.fill_style_1 {
1497                    bits.write_ubits(num_fill_bits, fill_style_index)?;
1498                }
1499                if let Some(line_style_index) = style_change.line_style {
1500                    bits.write_ubits(num_line_bits, line_style_index)?;
1501                }
1502                if let Some(ref new_styles) = style_change.new_styles {
1503                    if context.shape_version < 2 {
1504                        return Err(Error::invalid_data(
1505                            "Only DefineShape2 and higher may change styles.",
1506                        ));
1507                    }
1508                    bits.flush()?;
1509                    let mut writer = Writer::new(bits.writer(), context.swf_version);
1510                    let (num_fill_bits, num_line_bits) =
1511                        writer.write_shape_styles(new_styles, context.shape_version)?;
1512                    context.num_fill_bits = num_fill_bits;
1513                    context.num_line_bits = num_line_bits;
1514                }
1515            }
1516        }
1517        Ok(())
1518    }
1519
1520    fn write_fill_style(&mut self, fill_style: &FillStyle, shape_version: u8) -> Result<()> {
1521        match *fill_style {
1522            FillStyle::Color(ref color) => {
1523                self.write_u8(0x00)?; // Solid color.
1524                if shape_version >= 3 {
1525                    self.write_rgba(color)?
1526                } else {
1527                    self.write_rgb(color)?;
1528                }
1529            }
1530
1531            FillStyle::LinearGradient(ref gradient) => {
1532                self.write_u8(0x10)?; // Linear gradient.
1533                self.write_gradient(gradient, shape_version)?;
1534            }
1535
1536            FillStyle::RadialGradient(ref gradient) => {
1537                self.write_u8(0x12)?; // Linear gradient.
1538                self.write_gradient(gradient, shape_version)?;
1539            }
1540
1541            FillStyle::FocalGradient {
1542                ref gradient,
1543                focal_point,
1544            } => {
1545                self.write_u8(0x13)?; // Focal gradient.
1546                self.write_gradient(gradient, shape_version)?;
1547                self.write_fixed8(focal_point)?;
1548            }
1549
1550            FillStyle::Bitmap {
1551                id,
1552                ref matrix,
1553                is_smoothed,
1554                is_repeating,
1555            } => {
1556                // Bitmap smoothing only an option in SWF version 8+.
1557                // Lower versions use 0x40 and 0x41 type even when unsmoothed.
1558                let fill_style_type = match (is_smoothed || self.version < 8, is_repeating) {
1559                    (true, true) => 0x40,
1560                    (true, false) => 0x41,
1561                    (false, true) => 0x42,
1562                    (false, false) => 0x43,
1563                };
1564                self.write_u8(fill_style_type)?;
1565                self.write_u16(id)?;
1566                self.write_matrix(matrix)?;
1567            }
1568        }
1569        Ok(())
1570    }
1571
1572    fn write_line_style(&mut self, line_style: &LineStyle, shape_version: u8) -> Result<()> {
1573        // TODO(Herschel): Handle overflow.
1574        self.write_u16(line_style.width.get() as u16)?;
1575        if shape_version >= 4 {
1576            // LineStyle2
1577            self.write_u16(line_style.flags.bits())?;
1578            if let LineJoinStyle::Miter(miter_factor) = line_style.join_style() {
1579                self.write_fixed8(miter_factor)?;
1580            }
1581            if line_style.flags.contains(LineStyleFlag::HAS_FILL) {
1582                self.write_fill_style(&line_style.fill_style, shape_version)?;
1583            } else if let FillStyle::Color(color) = &line_style.fill_style {
1584                self.write_rgba(color)?;
1585            } else {
1586                return Err(Error::invalid_data("Unexpected line style fill type"));
1587            }
1588        } else {
1589            // LineStyle1
1590            let color = if let FillStyle::Color(color) = &line_style.fill_style {
1591                color
1592            } else {
1593                return Err(Error::invalid_data(
1594                    "Complex line styles can only be used in DefineShape4 tags",
1595                ));
1596            };
1597            if shape_version >= 3 {
1598                self.write_rgba(color)?
1599            } else {
1600                self.write_rgb(color)?
1601            }
1602        }
1603        Ok(())
1604    }
1605
1606    fn write_gradient(&mut self, gradient: &Gradient, shape_version: u8) -> Result<()> {
1607        self.write_matrix(&gradient.matrix)?;
1608        self.write_gradient_flags(gradient)?;
1609        for record in &gradient.records {
1610            self.write_u8(record.ratio)?;
1611            if shape_version >= 3 {
1612                self.write_rgba(&record.color)?;
1613            } else {
1614                self.write_rgb(&record.color)?;
1615            }
1616        }
1617        Ok(())
1618    }
1619
1620    fn write_gradient_flags(&mut self, gradient: &Gradient) -> Result<()> {
1621        let flags = ((gradient.spread as u8) << 6)
1622            | ((gradient.interpolation as u8) << 4)
1623            | ((gradient.records.len() as u8) & 0b1111);
1624        self.write_u8(flags)?;
1625        Ok(())
1626    }
1627
1628    fn write_place_object(&mut self, place_object: &PlaceObject) -> Result<()> {
1629        // TODO: Assert that the extraneous fields are the defaults.
1630        let mut buf = Vec::new();
1631        {
1632            let mut writer = Writer::new(&mut buf, self.version);
1633            if let PlaceObjectAction::Place(character_id) = place_object.action {
1634                writer.write_u16(character_id)?;
1635            } else {
1636                return Err(Error::invalid_data(
1637                    "PlaceObject version 1 can only use a Place action.",
1638                ));
1639            }
1640            writer.write_u16(place_object.depth)?;
1641            if let Some(ref matrix) = place_object.matrix {
1642                writer.write_matrix(matrix)?;
1643            } else {
1644                writer.write_matrix(&Matrix::IDENTITY)?;
1645            }
1646            if let Some(ref color_transform) = place_object.color_transform {
1647                writer.write_color_transform_no_alpha(color_transform)?;
1648            }
1649        }
1650        self.write_tag_header(TagCode::PlaceObject, buf.len() as u32)?;
1651        self.output.write_all(&buf)?;
1652        Ok(())
1653    }
1654
1655    fn write_place_object_2_or_3(
1656        &mut self,
1657        place_object: &PlaceObject,
1658        place_object_version: u8,
1659    ) -> Result<()> {
1660        let mut buf = Vec::new();
1661        {
1662            // TODO: Assert version.
1663            let mut writer = Writer::new(&mut buf, self.version);
1664
1665            let mut flags = PlaceFlag::empty();
1666            flags.set(
1667                PlaceFlag::MOVE,
1668                matches!(
1669                    place_object.action,
1670                    PlaceObjectAction::Modify | PlaceObjectAction::Replace(_)
1671                ),
1672            );
1673            flags.set(
1674                PlaceFlag::HAS_CHARACTER,
1675                matches!(
1676                    place_object.action,
1677                    PlaceObjectAction::Place(_) | PlaceObjectAction::Replace(_)
1678                ),
1679            );
1680            flags.set(PlaceFlag::HAS_MATRIX, place_object.matrix.is_some());
1681            flags.set(
1682                PlaceFlag::HAS_COLOR_TRANSFORM,
1683                place_object.color_transform.is_some(),
1684            );
1685            flags.set(PlaceFlag::HAS_RATIO, place_object.ratio.is_some());
1686            flags.set(PlaceFlag::HAS_NAME, place_object.name.is_some());
1687            flags.set(PlaceFlag::HAS_CLIP_DEPTH, place_object.clip_depth.is_some());
1688            flags.set(
1689                PlaceFlag::HAS_CLIP_ACTIONS,
1690                place_object.clip_actions.is_some(),
1691            );
1692
1693            if place_object_version >= 3 {
1694                flags.set(PlaceFlag::HAS_FILTER_LIST, place_object.filters.is_some());
1695                flags.set(PlaceFlag::HAS_BLEND_MODE, place_object.blend_mode.is_some());
1696                flags.set(
1697                    PlaceFlag::HAS_CACHE_AS_BITMAP,
1698                    place_object.is_bitmap_cached.is_some(),
1699                );
1700                flags.set(PlaceFlag::HAS_CLASS_NAME, place_object.class_name.is_some());
1701                flags.set(PlaceFlag::HAS_IMAGE, place_object.has_image);
1702                flags.set(PlaceFlag::HAS_VISIBLE, place_object.is_visible.is_some());
1703                flags.set(
1704                    PlaceFlag::OPAQUE_BACKGROUND,
1705                    place_object.background_color.is_some(),
1706                );
1707                writer.write_u16(flags.bits())?;
1708            } else {
1709                writer.write_u8(flags.bits() as u8)?;
1710            }
1711
1712            writer.write_u16(place_object.depth)?;
1713
1714            if place_object_version >= 3 {
1715                if let Some(class_name) = place_object.class_name {
1716                    writer.write_string(class_name)?;
1717                }
1718            }
1719
1720            match place_object.action {
1721                PlaceObjectAction::Place(character_id)
1722                | PlaceObjectAction::Replace(character_id) => writer.write_u16(character_id)?,
1723                PlaceObjectAction::Modify => (),
1724            }
1725            if let Some(ref matrix) = place_object.matrix {
1726                writer.write_matrix(matrix)?;
1727            };
1728            if let Some(ref color_transform) = place_object.color_transform {
1729                writer.write_color_transform(color_transform)?;
1730            };
1731            if let Some(ratio) = place_object.ratio {
1732                writer.write_u16(ratio)?;
1733            }
1734            if let Some(name) = place_object.name {
1735                writer.write_string(name)?;
1736            };
1737            if let Some(clip_depth) = place_object.clip_depth {
1738                writer.write_u16(clip_depth)?;
1739            }
1740
1741            if place_object_version >= 3 {
1742                if let Some(filters) = &place_object.filters {
1743                    writer.write_u8(filters.len() as u8)?;
1744                    for filter in filters {
1745                        writer.write_filter(filter)?;
1746                    }
1747                }
1748
1749                if let Some(blend_mode) = place_object.blend_mode {
1750                    writer.write_blend_mode(blend_mode)?;
1751                }
1752
1753                if let Some(is_bitmap_cached) = place_object.is_bitmap_cached {
1754                    writer.write_u8(if is_bitmap_cached { 1 } else { 0 })?;
1755                }
1756
1757                if let Some(is_visible) = place_object.is_visible {
1758                    writer.write_u8(if is_visible { 1 } else { 0 })?;
1759                }
1760
1761                if let Some(ref background_color) = place_object.background_color {
1762                    writer.write_rgba(background_color)?;
1763                }
1764            }
1765
1766            if let Some(clip_actions) = &place_object.clip_actions {
1767                writer.write_clip_actions(clip_actions)?;
1768            }
1769
1770            // PlaceObject4 adds some embedded AMF data per instance.
1771            if place_object_version >= 4 {
1772                if let Some(data) = place_object.amf_data {
1773                    writer.output.write_all(data)?;
1774                }
1775            }
1776        }
1777        let tag_code = match place_object_version {
1778            2 => TagCode::PlaceObject2,
1779            3 => TagCode::PlaceObject3,
1780            4 => TagCode::PlaceObject4,
1781            _ => return Err(Error::invalid_data("Invalid PlaceObject version.")),
1782        };
1783        self.write_tag_header(tag_code, buf.len() as u32)?;
1784        self.output.write_all(&buf)?;
1785        Ok(())
1786    }
1787
1788    fn write_drop_shadow_filter(&mut self, filter: &DropShadowFilter) -> Result<()> {
1789        self.write_rgba(&filter.color)?;
1790        self.write_fixed16(filter.blur_x)?;
1791        self.write_fixed16(filter.blur_y)?;
1792        self.write_fixed16(filter.angle)?;
1793        self.write_fixed16(filter.distance)?;
1794        self.write_fixed8(filter.strength)?;
1795        self.write_u8(filter.flags.bits())?;
1796        Ok(())
1797    }
1798
1799    fn write_blur_filter(&mut self, filter: &BlurFilter) -> Result<()> {
1800        self.write_fixed16(filter.blur_x)?;
1801        self.write_fixed16(filter.blur_y)?;
1802        self.write_u8(filter.flags.bits())?;
1803        Ok(())
1804    }
1805
1806    fn write_glow_filter(&mut self, filter: &GlowFilter) -> Result<()> {
1807        self.write_rgba(&filter.color)?;
1808        self.write_fixed16(filter.blur_x)?;
1809        self.write_fixed16(filter.blur_y)?;
1810        self.write_fixed8(filter.strength)?;
1811        self.write_u8(filter.flags.bits())?;
1812        Ok(())
1813    }
1814
1815    fn write_bevel_filter(&mut self, filter: &BevelFilter) -> Result<()> {
1816        // Note that the color order is wrong in the spec, it's highlight then shadow.
1817        self.write_rgba(&filter.highlight_color)?;
1818        self.write_rgba(&filter.shadow_color)?;
1819        self.write_fixed16(filter.blur_x)?;
1820        self.write_fixed16(filter.blur_y)?;
1821        self.write_fixed16(filter.angle)?;
1822        self.write_fixed16(filter.distance)?;
1823        self.write_fixed8(filter.strength)?;
1824        self.write_u8(filter.flags.bits())?;
1825        Ok(())
1826    }
1827
1828    fn write_gradient_filter(&mut self, filter: &GradientFilter) -> Result<()> {
1829        self.write_u8(filter.colors.len() as u8)?;
1830        for gradient_record in &filter.colors {
1831            self.write_rgba(&gradient_record.color)?;
1832        }
1833        for gradient_record in &filter.colors {
1834            self.write_u8(gradient_record.ratio)?;
1835        }
1836        self.write_fixed16(filter.blur_x)?;
1837        self.write_fixed16(filter.blur_y)?;
1838        self.write_fixed16(filter.angle)?;
1839        self.write_fixed16(filter.distance)?;
1840        self.write_fixed8(filter.strength)?;
1841        self.write_u8(filter.flags.bits())?;
1842        Ok(())
1843    }
1844
1845    fn write_convolution_filter(&mut self, filter: &ConvolutionFilter) -> Result<()> {
1846        self.write_u8(filter.num_matrix_cols)?;
1847        self.write_u8(filter.num_matrix_rows)?;
1848        self.write_f32(filter.divisor)?;
1849        self.write_f32(filter.bias)?;
1850        for val in &filter.matrix {
1851            self.write_f32(*val)?;
1852        }
1853        self.write_rgba(&filter.default_color)?;
1854        self.write_u8(filter.flags.bits())?;
1855        Ok(())
1856    }
1857
1858    fn write_color_matrix_filter(&mut self, filter: &ColorMatrixFilter) -> Result<()> {
1859        for m in filter.matrix {
1860            self.write_f32(m)?;
1861        }
1862        Ok(())
1863    }
1864
1865    fn write_filter(&mut self, filter: &Filter) -> Result<()> {
1866        match filter {
1867            Filter::DropShadowFilter(filter) => {
1868                self.write_u8(0)?;
1869                self.write_drop_shadow_filter(filter)
1870            }
1871            Filter::BlurFilter(filter) => {
1872                self.write_u8(1)?;
1873                self.write_blur_filter(filter)
1874            }
1875            Filter::GlowFilter(filter) => {
1876                self.write_u8(2)?;
1877                self.write_glow_filter(filter)
1878            }
1879            Filter::BevelFilter(filter) => {
1880                self.write_u8(3)?;
1881                self.write_bevel_filter(filter)
1882            }
1883            Filter::GradientGlowFilter(filter) => {
1884                self.write_u8(4)?;
1885                self.write_gradient_filter(filter)
1886            }
1887            Filter::ConvolutionFilter(filter) => {
1888                self.write_u8(5)?;
1889                self.write_convolution_filter(filter)
1890            }
1891            Filter::ColorMatrixFilter(filter) => {
1892                self.write_u8(6)?;
1893                self.write_color_matrix_filter(filter)
1894            }
1895            Filter::GradientBevelFilter(filter) => {
1896                self.write_u8(7)?;
1897                self.write_gradient_filter(filter)
1898            }
1899        }
1900    }
1901
1902    fn write_clip_actions(&mut self, clip_actions: &[ClipAction]) -> Result<()> {
1903        self.write_u16(0)?; // Reserved
1904        {
1905            let mut all_events = ClipEventFlag::empty();
1906            for action in clip_actions {
1907                all_events |= action.events;
1908            }
1909            self.write_clip_event_flags(all_events)?;
1910        }
1911        for action in clip_actions {
1912            self.write_clip_event_flags(action.events)?;
1913            let action_length =
1914                action.action_data.len() as u32 + if action.key_code.is_some() { 1 } else { 0 };
1915            self.write_u32(action_length)?;
1916            if let Some(k) = action.key_code {
1917                self.write_u8(k)?;
1918            }
1919            self.output.write_all(action.action_data)?;
1920        }
1921        if self.version <= 5 {
1922            self.write_u16(0)?;
1923        } else {
1924            self.write_u32(0)?;
1925        }
1926        Ok(())
1927    }
1928
1929    fn write_clip_event_flags(&mut self, clip_events: ClipEventFlag) -> Result<()> {
1930        // TODO: Assert proper version.
1931        let bits = clip_events.bits();
1932        if self.version >= 6 {
1933            self.write_u32(bits)?;
1934        } else {
1935            self.write_u16((bits as u8).into())?;
1936        }
1937        Ok(())
1938    }
1939
1940    fn write_sound_stream_head(
1941        &mut self,
1942        stream_head: &SoundStreamHead,
1943        version: u8,
1944    ) -> Result<()> {
1945        let tag_code = if version >= 2 {
1946            TagCode::SoundStreamHead2
1947        } else {
1948            TagCode::SoundStreamHead
1949        };
1950        // MP3 compression has added latency seek field.
1951        let length = if stream_head.stream_format.compression == AudioCompression::Mp3 {
1952            6
1953        } else {
1954            4
1955        };
1956        self.write_tag_header(tag_code, length)?;
1957        self.write_sound_format(&stream_head.playback_format)?;
1958        self.write_sound_format(&stream_head.stream_format)?;
1959        self.write_u16(stream_head.num_samples_per_block)?;
1960        if stream_head.stream_format.compression == AudioCompression::Mp3 {
1961            self.write_i16(stream_head.latency_seek)?;
1962        }
1963        Ok(())
1964    }
1965
1966    fn write_sound_format(&mut self, sound_format: &SoundFormat) -> Result<()> {
1967        let mut bits = self.bits();
1968        bits.write_ubits(4, sound_format.compression as u32)?;
1969        bits.write_ubits(
1970            2,
1971            match sound_format.sample_rate {
1972                5512 => 0,
1973                11025 => 1,
1974                22050 => 2,
1975                44100 => 3,
1976                _ => return Err(Error::invalid_data("Invalid sample rate.")),
1977            },
1978        )?;
1979        bits.write_bit(sound_format.is_16_bit)?;
1980        bits.write_bit(sound_format.is_stereo)?;
1981        Ok(())
1982    }
1983
1984    fn write_sound_info(&mut self, sound_info: &SoundInfo) -> Result<()> {
1985        let flags = ((sound_info.event as u8) << 4)
1986            | if sound_info.in_sample.is_some() {
1987                0b1
1988            } else {
1989                0
1990            }
1991            | if sound_info.out_sample.is_some() {
1992                0b10
1993            } else {
1994                0
1995            }
1996            | if sound_info.num_loops > 1 { 0b100 } else { 0 }
1997            | if sound_info.envelope.is_some() {
1998                0b1000
1999            } else {
2000                0
2001            };
2002        self.write_u8(flags)?;
2003        if let Some(n) = sound_info.in_sample {
2004            self.write_u32(n)?;
2005        }
2006        if let Some(n) = sound_info.out_sample {
2007            self.write_u32(n)?;
2008        }
2009        if sound_info.num_loops > 1 {
2010            self.write_u16(sound_info.num_loops)?;
2011        }
2012        if let Some(ref envelope) = sound_info.envelope {
2013            self.write_u8(envelope.len() as u8)?;
2014            for point in envelope {
2015                self.write_u32(point.sample)?;
2016                self.write_u16((point.left_volume * 32768f32) as u16)?;
2017                self.write_u16((point.right_volume * 32768f32) as u16)?;
2018            }
2019        }
2020        Ok(())
2021    }
2022
2023    fn write_define_font_2(&mut self, font: &Font) -> Result<()> {
2024        let mut buf = Vec::new();
2025        {
2026            let num_glyphs = font.glyphs.len();
2027
2028            // We must write the glyph shapes into a temporary buffer
2029            // so that we can calculate their offsets.
2030            // Note: these offsets are still wrong,
2031            // as there's a variable size CodeTableOffset field in between.
2032            // We correct for it with a +4/+2 addition later.
2033            let mut offsets = Vec::with_capacity(num_glyphs);
2034            let mut has_wide_offsets = false;
2035            let has_wide_codes = !font.flags.contains(FontFlag::IS_ANSI);
2036            let mut shape_buf = Vec::new();
2037            {
2038                let mut shape_writer = Writer::new(&mut shape_buf, self.version);
2039
2040                // ShapeTable
2041                let mut shape_context = ShapeContext {
2042                    swf_version: self.version,
2043                    shape_version: 1,
2044                    num_fill_bits: 1,
2045                    num_line_bits: 0,
2046                };
2047                for glyph in &font.glyphs {
2048                    // Store offset for later.
2049                    let offset = shape_writer.output.len();
2050                    offsets.push(offset);
2051                    if offset > 0xFFFF {
2052                        has_wide_offsets = true;
2053                    }
2054
2055                    shape_writer.write_u8(0b0001_0000)?;
2056                    let mut bits = shape_writer.bits();
2057                    for shape_record in &glyph.shape_records {
2058                        Self::write_shape_record(shape_record, &mut bits, &mut shape_context)?;
2059                    }
2060                    // End shape record.
2061                    bits.write_ubits(6, 0)?;
2062                }
2063            }
2064
2065            let mut writer = Writer::new(&mut buf, self.version);
2066            writer.write_character_id(font.id)?;
2067            writer.write_u8(font.flags.bits())?;
2068            writer.write_language(font.language)?;
2069            writer.write_u8(font.name.len() as u8)?;
2070            writer.output.write_all(font.name.as_bytes())?;
2071            writer.write_u16(num_glyphs as u16)?;
2072
2073            // If there are no glyphs, then the following tables are omitted.
2074            if num_glyphs > 0 {
2075                // OffsetTable size, plus CodeTableOffset size
2076                let init_offset = if has_wide_offsets {
2077                    num_glyphs * 4 + 4
2078                } else {
2079                    num_glyphs * 2 + 2
2080                };
2081
2082                // OffsetTable
2083                for offset in offsets {
2084                    if has_wide_offsets {
2085                        writer.write_u32((offset + init_offset) as u32)?;
2086                    } else {
2087                        writer.write_u16((offset + init_offset) as u16)?;
2088                    }
2089                }
2090
2091                // CodeTableOffset
2092                let code_table_offset =
2093                    (num_glyphs + 1) * if has_wide_offsets { 4 } else { 2 } + shape_buf.len();
2094                if has_wide_offsets {
2095                    writer.write_u32(code_table_offset as u32)?;
2096                } else {
2097                    writer.write_u16(code_table_offset as u16)?;
2098                }
2099
2100                writer.output.write_all(&shape_buf)?;
2101
2102                // CodeTable
2103                for glyph in &font.glyphs {
2104                    if has_wide_codes {
2105                        writer.write_u16(glyph.code)?;
2106                    } else {
2107                        writer.write_u8(glyph.code as u8)?;
2108                    }
2109                }
2110            }
2111
2112            if let Some(ref layout) = font.layout {
2113                writer.write_u16(layout.ascent)?;
2114                writer.write_u16(layout.descent)?;
2115                writer.write_i16(layout.leading)?;
2116                for glyph in &font.glyphs {
2117                    writer.write_i16(glyph.advance)?;
2118                }
2119                for glyph in &font.glyphs {
2120                    writer.write_rectangle(
2121                        glyph
2122                            .bounds
2123                            .as_ref()
2124                            .ok_or_else(|| Error::invalid_data("glyph.bounds cannot be None"))?,
2125                    )?;
2126                }
2127                writer.write_u16(layout.kerning.len() as u16)?;
2128                for kerning_record in &layout.kerning {
2129                    writer.write_kerning_record(kerning_record, has_wide_codes)?;
2130                }
2131            }
2132        }
2133
2134        let tag_code = if font.version == 2 {
2135            TagCode::DefineFont2
2136        } else {
2137            TagCode::DefineFont3
2138        };
2139        self.write_tag_header(tag_code, buf.len() as u32)?;
2140        self.output.write_all(&buf)?;
2141        Ok(())
2142    }
2143
2144    fn write_define_font_4(&mut self, font: &Font4) -> Result<()> {
2145        let mut tag_len = 4 + font.name.len();
2146        if let Some(data) = font.data {
2147            tag_len += data.len()
2148        };
2149        self.write_tag_header(TagCode::DefineFont4, tag_len as u32)?;
2150        self.write_character_id(font.id)?;
2151        self.write_u8(
2152            if font.data.is_some() { 0b100 } else { 0 }
2153                | if font.is_italic { 0b10 } else { 0 }
2154                | if font.is_bold { 0b1 } else { 0 },
2155        )?;
2156        self.write_string(font.name)?;
2157        if let Some(data) = font.data {
2158            self.output.write_all(data)?;
2159        }
2160        Ok(())
2161    }
2162
2163    fn write_define_font_info(&mut self, font_info: &FontInfo) -> Result<()> {
2164        let use_wide_codes = self.version >= 6
2165            || font_info.version >= 2
2166            || font_info.flags.contains(FontInfoFlag::HAS_WIDE_CODES);
2167
2168        let len = font_info.name.len()
2169            + if use_wide_codes { 2 } else { 1 } * font_info.code_table.len()
2170            + if font_info.version >= 2 { 1 } else { 0 }
2171            + 4;
2172
2173        let tag_id = if font_info.version == 1 {
2174            TagCode::DefineFontInfo
2175        } else {
2176            TagCode::DefineFontInfo2
2177        };
2178        self.write_tag_header(tag_id, len as u32)?;
2179        self.write_u16(font_info.id)?;
2180
2181        // SWF19 has ANSI and Shift-JIS backwards?
2182        self.write_u8(font_info.name.len() as u8)?;
2183        self.output.write_all(font_info.name.as_bytes())?;
2184
2185        let mut flags = font_info.flags;
2186        flags.set(FontInfoFlag::HAS_WIDE_CODES, use_wide_codes);
2187        self.write_u8(flags.bits())?;
2188
2189        // TODO(Herschel): Assert language is unknown for v1.
2190        if font_info.version >= 2 {
2191            self.write_language(font_info.language)?;
2192        }
2193        for &code in &font_info.code_table {
2194            if use_wide_codes {
2195                self.write_u16(code)?;
2196            } else {
2197                self.write_u8(code as u8)?;
2198            }
2199        }
2200        Ok(())
2201    }
2202
2203    fn write_kerning_record(
2204        &mut self,
2205        kerning: &KerningRecord,
2206        has_wide_codes: bool,
2207    ) -> Result<()> {
2208        if has_wide_codes {
2209            self.write_u16(kerning.left_code)?;
2210            self.write_u16(kerning.right_code)?;
2211        } else {
2212            self.write_u8(kerning.left_code as u8)?;
2213            self.write_u8(kerning.right_code as u8)?;
2214        }
2215        self.write_i16(kerning.adjustment.get() as i16)?; // TODO(Herschel): Handle overflow
2216        Ok(())
2217    }
2218
2219    fn write_define_binary_data(&mut self, binary_data: &DefineBinaryData) -> Result<()> {
2220        self.write_tag_header(TagCode::DefineBinaryData, binary_data.data.len() as u32 + 6)?;
2221        self.write_u16(binary_data.id)?;
2222        self.write_u32(0)?; // Reserved
2223        self.output.write_all(binary_data.data)?;
2224        Ok(())
2225    }
2226
2227    fn write_define_text(&mut self, text: &Text, version: u8) -> Result<()> {
2228        let mut buf = Vec::new();
2229        {
2230            let mut writer = Writer::new(&mut buf, self.version);
2231            writer.write_character_id(text.id)?;
2232            writer.write_rectangle(&text.bounds)?;
2233            writer.write_matrix(&text.matrix)?;
2234            let num_glyph_bits = text
2235                .records
2236                .iter()
2237                .flat_map(|r| r.glyphs.iter().map(|g| count_ubits(g.index)))
2238                .max()
2239                .unwrap_or(0);
2240            let num_advance_bits = text
2241                .records
2242                .iter()
2243                .flat_map(|r| r.glyphs.iter().map(|g| count_sbits(g.advance)))
2244                .max()
2245                .unwrap_or(0);
2246            writer.write_u8(num_glyph_bits as u8)?;
2247            writer.write_u8(num_advance_bits as u8)?;
2248
2249            for record in &text.records {
2250                let flags = 0b10000000
2251                    | if record.font_id.is_some() { 0b1000 } else { 0 }
2252                    | if record.color.is_some() { 0b100 } else { 0 }
2253                    | if record.y_offset.is_some() { 0b10 } else { 0 }
2254                    | if record.x_offset.is_some() { 0b1 } else { 0 };
2255                writer.write_u8(flags)?;
2256                if let Some(id) = record.font_id {
2257                    writer.write_character_id(id)?;
2258                }
2259                if let Some(ref color) = record.color {
2260                    if version == 1 {
2261                        writer.write_rgb(color)?;
2262                    } else {
2263                        writer.write_rgba(color)?;
2264                    }
2265                }
2266                if let Some(x) = record.x_offset {
2267                    writer.write_i16(x.get() as i16)?; // TODO(Herschel): Handle overflow.
2268                }
2269                if let Some(y) = record.y_offset {
2270                    writer.write_i16(y.get() as i16)?;
2271                }
2272                if let Some(height) = record.height {
2273                    writer.write_u16(height.get() as u16)?;
2274                }
2275                writer.write_u8(record.glyphs.len() as u8)?;
2276                let mut bits = writer.bits();
2277                for glyph in &record.glyphs {
2278                    bits.write_ubits(num_glyph_bits, glyph.index)?;
2279                    bits.write_sbits(num_advance_bits, glyph.advance)?;
2280                }
2281            }
2282            writer.write_u8(0)?; // End of text records.
2283        }
2284        if version == 1 {
2285            self.write_tag_header(TagCode::DefineText, buf.len() as u32)?;
2286        } else {
2287            self.write_tag_header(TagCode::DefineText2, buf.len() as u32)?;
2288        }
2289        self.output.write_all(&buf)?;
2290        Ok(())
2291    }
2292
2293    fn write_define_edit_text(&mut self, edit_text: &EditText) -> Result<()> {
2294        let mut buf = Vec::new();
2295        {
2296            let mut writer = Writer::new(&mut buf, self.version);
2297            writer.write_character_id(edit_text.id)?;
2298            writer.write_rectangle(&edit_text.bounds)?;
2299            writer.write_u16(edit_text.flags.bits())?;
2300
2301            if let Some(font_id) = edit_text.font_id() {
2302                writer.write_character_id(font_id)?;
2303            }
2304
2305            // TODO(Herschel): Check SWF version.
2306            if let Some(class) = edit_text.font_class() {
2307                writer.write_string(class)?;
2308            }
2309
2310            if let Some(height) = edit_text.height() {
2311                writer.write_u16(height.get() as u16)?
2312            }
2313
2314            if let Some(color) = edit_text.color() {
2315                writer.write_rgba(color)?
2316            }
2317
2318            if let Some(len) = edit_text.max_length() {
2319                writer.write_u16(len)?;
2320            }
2321
2322            if let Some(layout) = edit_text.layout() {
2323                writer.write_u8(layout.align as u8)?;
2324                writer.write_u16(layout.left_margin.get() as u16)?; // TODO: Handle overflow
2325                writer.write_u16(layout.right_margin.get() as u16)?;
2326                writer.write_u16(layout.indent.get() as u16)?;
2327                writer.write_i16(layout.leading.get() as i16)?;
2328            }
2329
2330            writer.write_string(edit_text.variable_name)?;
2331
2332            if let Some(text) = edit_text.initial_text() {
2333                writer.write_string(text)?;
2334            }
2335        }
2336
2337        self.write_tag_header(TagCode::DefineEditText, buf.len() as u32)?;
2338        self.output.write_all(&buf)?;
2339        Ok(())
2340    }
2341
2342    fn write_define_video_stream(&mut self, video: &DefineVideoStream) -> Result<()> {
2343        self.write_tag_header(TagCode::DefineVideoStream, 10)?;
2344        self.write_character_id(video.id)?;
2345        self.write_u16(video.num_frames)?;
2346        self.write_u16(video.width)?;
2347        self.write_u16(video.height)?;
2348        self.write_u8(((video.deblocking as u8) << 1) | if video.is_smoothed { 0b1 } else { 0 })?;
2349        self.write_u8(video.codec as u8)?;
2350        Ok(())
2351    }
2352
2353    fn write_product_info(&mut self, product_info: &ProductInfo) -> Result<()> {
2354        self.write_tag_header(TagCode::ProductInfo, 26)?;
2355        self.write_u32(product_info.product_id)?;
2356        self.write_u32(product_info.edition)?;
2357        self.write_u8(product_info.major_version)?;
2358        self.write_u8(product_info.minor_version)?;
2359        self.write_u64(product_info.build_number)?;
2360        self.write_u64(product_info.compilation_date)?;
2361        Ok(())
2362    }
2363
2364    fn write_debug_id(&mut self, debug_id: &DebugId) -> Result<()> {
2365        self.write_tag_header(TagCode::DebugId, debug_id.len() as u32)?;
2366        self.output.write_all(debug_id)?;
2367        Ok(())
2368    }
2369
2370    fn write_name_character(&mut self, name_character: &NameCharacter) -> Result<()> {
2371        self.write_tag_header(TagCode::NameCharacter, 3 + name_character.name.len() as u32)?;
2372        self.write_character_id(name_character.id)?;
2373        self.write_string(name_character.name)?;
2374        Ok(())
2375    }
2376
2377    fn write_tag_header(&mut self, tag_code: TagCode, length: u32) -> Result<()> {
2378        self.write_tag_code_and_length(tag_code as u16, length)
2379    }
2380
2381    fn write_tag_code_and_length(&mut self, tag_code: u16, length: u32) -> Result<()> {
2382        // TODO: Test for tag code/length overflow.
2383        let mut tag_code_and_length = tag_code << 6;
2384        if length < 0b111111 {
2385            tag_code_and_length |= length as u16;
2386            self.write_u16(tag_code_and_length)?;
2387        } else {
2388            tag_code_and_length |= 0b111111;
2389            self.write_u16(tag_code_and_length)?;
2390            self.write_u32(length)?;
2391        }
2392        Ok(())
2393    }
2394
2395    fn write_tag_list(&mut self, tags: &[Tag]) -> Result<()> {
2396        // TODO: Better error handling. Can skip errored tags, unless EOF.
2397        for tag in tags {
2398            self.write_tag(tag)?;
2399        }
2400        // Implicit end tag.
2401        self.write_tag(&Tag::End)?;
2402        Ok(())
2403    }
2404}
2405
2406fn count_ubits(n: u32) -> u32 {
2407    32 - n.leading_zeros()
2408}
2409
2410fn count_sbits(n: i32) -> u32 {
2411    if n == 0 {
2412        0
2413    } else if n == -1 {
2414        1
2415    } else if n < 0 {
2416        count_ubits((!n) as u32) + 1
2417    } else {
2418        count_ubits(n as u32) + 1
2419    }
2420}
2421
2422fn count_sbits_twips(n: Twips) -> u32 {
2423    count_sbits(n.get())
2424}
2425
2426fn count_fbits(n: Fixed16) -> u32 {
2427    count_sbits(n.get())
2428}
2429
2430#[cfg(test)]
2431#[allow(clippy::unusual_byte_groupings)]
2432mod tests {
2433    use super::*;
2434    use crate::test_data;
2435
2436    #[test]
2437    fn write_swfs() {
2438        fn write_dummy_swf(compression: Compression) -> Result<()> {
2439            let mut buf = Vec::new();
2440            let header = Header {
2441                compression,
2442                version: 13,
2443                stage_size: Rectangle {
2444                    x_min: Twips::ZERO,
2445                    x_max: Twips::from_pixels(640.0),
2446                    y_min: Twips::ZERO,
2447                    y_max: Twips::from_pixels(480.0),
2448                },
2449                frame_rate: Fixed8::from_f32(60.0),
2450                num_frames: 1,
2451            };
2452            write_swf(&header, &[], &mut buf)?;
2453            Ok(())
2454        }
2455        assert!(
2456            write_dummy_swf(Compression::None).is_ok(),
2457            "Failed to write uncompressed SWF."
2458        );
2459        assert!(
2460            write_dummy_swf(Compression::Zlib).is_ok(),
2461            "Failed to write zlib SWF."
2462        );
2463        if cfg!(feature = "lzma") {
2464            assert!(
2465                write_dummy_swf(Compression::Lzma).is_ok(),
2466                "Failed to write LZMA SWF."
2467            );
2468        }
2469    }
2470
2471    #[test]
2472    fn write_fixed8() {
2473        let mut buf = Vec::new();
2474        {
2475            let mut writer = Writer::new(&mut buf, 1);
2476            writer.write_fixed8(Fixed8::ZERO).unwrap();
2477            writer.write_fixed8(Fixed8::ONE).unwrap();
2478            writer.write_fixed8(Fixed8::from_f32(6.5)).unwrap();
2479            writer.write_fixed8(Fixed8::from_f32(-20.75)).unwrap();
2480        }
2481        assert_eq!(
2482            buf,
2483            [
2484                0b00000000, 0b00000000, 0b00000000, 0b00000001, 0b10000000, 0b00000110, 0b01000000,
2485                0b11101011
2486            ]
2487        );
2488    }
2489
2490    #[test]
2491    fn write_encoded_u32() {
2492        fn write_to_buf(n: u32) -> Vec<u8> {
2493            let mut buf = Vec::new();
2494            {
2495                let mut writer = Writer::new(&mut buf, 1);
2496                writer.write_encoded_u32(n).unwrap();
2497            }
2498            buf
2499        }
2500
2501        assert_eq!(write_to_buf(0), [0]);
2502        assert_eq!(write_to_buf(2), [2]);
2503        assert_eq!(write_to_buf(129), [0b1_0000001, 0b0_0000001]);
2504        assert_eq!(
2505            write_to_buf(0b1100111_0000001_0000001),
2506            [0b1_0000001, 0b1_0000001, 0b0_1100111]
2507        );
2508        assert_eq!(
2509            write_to_buf(0b1111_0000000_0000000_0000000_0000000u32),
2510            [
2511                0b1_0000000,
2512                0b1_0000000,
2513                0b1_0000000,
2514                0b1_0000000,
2515                0b0000_1111
2516            ]
2517        );
2518    }
2519
2520    #[test]
2521    fn write_bit() {
2522        let out_bits = [
2523            false, true, false, true, false, true, false, true, false, false, true, false, false,
2524            true, false, true,
2525        ];
2526        let mut buf = Vec::new();
2527        {
2528            let mut writer = Writer::new(&mut buf, 1);
2529            let mut bits = writer.bits();
2530            for b in &out_bits {
2531                bits.write_bit(*b).unwrap();
2532            }
2533        }
2534        assert_eq!(buf, [0b01010101, 0b00100101]);
2535    }
2536
2537    #[test]
2538    fn write_ubits() {
2539        let num_bits = 2;
2540        let nums = [1, 1, 1, 1, 0, 2, 1, 1];
2541        let mut buf = Vec::new();
2542        {
2543            let mut writer = Writer::new(&mut buf, 1);
2544            let mut bits = writer.bits();
2545            for n in &nums {
2546                bits.write_ubits(num_bits, *n).unwrap();
2547            }
2548        }
2549        assert_eq!(buf, [0b01010101, 0b00100101]);
2550    }
2551
2552    #[test]
2553    fn write_sbits() {
2554        let num_bits = 2;
2555        let nums = [1, 1, 1, 1, 0, -2, 1, 1];
2556        let mut buf = Vec::new();
2557        {
2558            let mut writer = Writer::new(&mut buf, 1);
2559            let mut bits = writer.bits();
2560            for n in &nums {
2561                bits.write_sbits(num_bits, *n).unwrap();
2562            }
2563        }
2564        assert_eq!(buf, [0b01010101, 0b00100101]);
2565    }
2566
2567    #[test]
2568    fn write_fbits() {
2569        let num_bits = 18;
2570        let nums = [1.0, -1.0];
2571        let mut buf = Vec::new();
2572        {
2573            let mut writer = Writer::new(&mut buf, 1);
2574            let mut bits = writer.bits();
2575            for n in &nums {
2576                bits.write_fbits(num_bits, Fixed16::from_f32(*n)).unwrap();
2577            }
2578        }
2579        assert_eq!(
2580            buf,
2581            [
2582                0b01_000000,
2583                0b00000000,
2584                0b00_11_0000,
2585                0b00000000,
2586                0b0000_0000
2587            ]
2588        );
2589    }
2590
2591    #[test]
2592    fn count_ubits() {
2593        assert_eq!(super::count_ubits(0), 0);
2594        assert_eq!(super::count_ubits(1u32), 1);
2595        assert_eq!(super::count_ubits(2u32), 2);
2596        assert_eq!(super::count_ubits(0b_00111101_00000000u32), 14);
2597    }
2598
2599    #[test]
2600    fn count_sbits() {
2601        assert_eq!(super::count_sbits(0), 0);
2602        assert_eq!(super::count_sbits(1), 2);
2603        assert_eq!(super::count_sbits(2), 3);
2604        assert_eq!(super::count_sbits(0b_00111101_00000000), 15);
2605
2606        assert_eq!(super::count_sbits(-1), 1);
2607        assert_eq!(super::count_sbits(-2), 2);
2608        assert_eq!(super::count_sbits(-0b_00110101_01010101), 15);
2609    }
2610
2611    #[test]
2612    fn write_c_string() {
2613        {
2614            let mut buf = Vec::new();
2615            {
2616                // TODO: What if I use a cursor instead of buf ?
2617                let mut writer = Writer::new(&mut buf, 1);
2618                writer.write_string("Hello!".into()).unwrap();
2619            }
2620            assert_eq!(buf, "Hello!\0".bytes().collect::<Vec<_>>());
2621        }
2622
2623        {
2624            let mut buf = Vec::new();
2625            {
2626                // TODO: What if I use a cursor instead of buf ?
2627                let mut writer = Writer::new(&mut buf, 1);
2628                writer.write_string("😀😂!🐼".into()).unwrap();
2629            }
2630            assert_eq!(buf, "😀😂!🐼\0".bytes().collect::<Vec<_>>());
2631        }
2632    }
2633
2634    #[test]
2635    fn write_rectangle_zero() {
2636        let rectangle = Rectangle {
2637            x_min: Twips::ZERO,
2638            y_min: Twips::ZERO,
2639            x_max: Twips::ZERO,
2640            y_max: Twips::ZERO,
2641        };
2642        let mut buf = Vec::new();
2643        {
2644            let mut writer = Writer::new(&mut buf, 1);
2645            writer.write_rectangle(&rectangle).unwrap();
2646        }
2647        assert_eq!(buf, [0]);
2648    }
2649
2650    #[test]
2651    fn write_rectangle_signed() {
2652        let rectangle = Rectangle {
2653            x_min: -Twips::ONE_PX,
2654            x_max: Twips::ONE_PX,
2655            y_min: -Twips::ONE_PX,
2656            y_max: Twips::ONE_PX,
2657        };
2658        let mut buf = Vec::new();
2659        {
2660            let mut writer = Writer::new(&mut buf, 1);
2661            writer.write_rectangle(&rectangle).unwrap();
2662        }
2663        assert_eq!(buf, [0b_00110_101, 0b100_01010, 0b0_101100_0, 0b_10100_000]);
2664    }
2665
2666    #[test]
2667    fn write_color() {
2668        {
2669            let color = Color {
2670                r: 1,
2671                g: 128,
2672                b: 255,
2673                a: 255,
2674            };
2675            let mut buf = Vec::new();
2676            {
2677                let mut writer = Writer::new(&mut buf, 1);
2678                writer.write_rgb(&color).unwrap();
2679            }
2680            assert_eq!(buf, [1, 128, 255]);
2681        }
2682        {
2683            let color = Color {
2684                r: 1,
2685                g: 2,
2686                b: 3,
2687                a: 11,
2688            };
2689            let mut buf = Vec::new();
2690            {
2691                let mut writer = Writer::new(&mut buf, 1);
2692                writer.write_rgba(&color).unwrap();
2693            }
2694            assert_eq!(buf, [1, 2, 3, 11]);
2695        }
2696    }
2697
2698    #[test]
2699    fn write_matrix() {
2700        fn write_to_buf(m: &Matrix) -> Vec<u8> {
2701            let mut buf = Vec::new();
2702            {
2703                let mut writer = Writer::new(&mut buf, 1);
2704                writer.write_matrix(m).unwrap();
2705            }
2706            buf
2707        }
2708
2709        let m = Matrix::IDENTITY;
2710        assert_eq!(write_to_buf(&m), [0]);
2711    }
2712
2713    #[test]
2714    fn write_tags() {
2715        for (swf_version, tag, expected_tag_bytes) in test_data::tag_tests() {
2716            let mut written_tag_bytes = Vec::new();
2717            Writer::new(&mut written_tag_bytes, swf_version)
2718                .write_tag(&tag)
2719                .unwrap();
2720            assert_eq!(
2721                written_tag_bytes, expected_tag_bytes,
2722                "Error reading tag.\nTag:\n{tag:?}\n\nWrote:\n{written_tag_bytes:?}\n\nExpected:\n{expected_tag_bytes:?}",
2723            );
2724        }
2725    }
2726
2727    #[test]
2728    fn write_tag_to_buf_list() {
2729        {
2730            let mut buf = Vec::new();
2731            {
2732                let mut writer = Writer::new(&mut buf, 1);
2733                writer.write_tag_list(&[]).unwrap();
2734            }
2735            assert_eq!(buf, [0, 0]);
2736        }
2737        {
2738            let mut buf = Vec::new();
2739            {
2740                let mut writer = Writer::new(&mut buf, 1);
2741                writer.write_tag_list(&[Tag::ShowFrame]).unwrap();
2742            }
2743            assert_eq!(buf, [0b01_000000, 0b00000000, 0, 0]);
2744        }
2745        {
2746            let mut buf = Vec::new();
2747            {
2748                let mut writer = Writer::new(&mut buf, 1);
2749                writer
2750                    .write_tag_list(&[
2751                        Tag::Unknown {
2752                            tag_code: 512,
2753                            data: &[0; 100],
2754                        },
2755                        Tag::ShowFrame,
2756                    ])
2757                    .unwrap();
2758            }
2759            let mut expected = vec![0b00_111111, 0b10000000, 100, 0, 0, 0];
2760            expected.extend_from_slice(&[0; 100]);
2761            expected.extend_from_slice(&[0b01_000000, 0b00000000, 0, 0]);
2762            assert_eq!(buf, expected);
2763        }
2764    }
2765
2766    #[test]
2767    fn write_font_3() {
2768        use crate::read::Reader;
2769
2770        let font = Font {
2771            version: 3,
2772            id: 1,
2773            name: SwfStr::from_bytes(b"font"),
2774            language: Language::Unknown,
2775            layout: None,
2776            glyphs: vec![Glyph {
2777                shape_records: vec![
2778                    ShapeRecord::StraightEdge {
2779                        delta: PointDelta::new(Twips::ONE_PX, -Twips::ONE_PX),
2780                    },
2781                    ShapeRecord::CurvedEdge {
2782                        control_delta: PointDelta::new(Twips::ONE_PX, Twips::ONE_PX),
2783                        anchor_delta: PointDelta::new(Twips::ONE_PX, -Twips::ONE_PX),
2784                    },
2785                    ShapeRecord::StraightEdge {
2786                        delta: PointDelta::new(Twips::ZERO, Twips::ZERO),
2787                    },
2788                    ShapeRecord::CurvedEdge {
2789                        control_delta: PointDelta::new(Twips::ZERO, Twips::ZERO),
2790                        anchor_delta: PointDelta::new(Twips::ZERO, Twips::ZERO),
2791                    },
2792                ],
2793                code: 1,
2794                advance: 0,
2795                bounds: None,
2796            }],
2797            flags: FontFlag::empty(),
2798        };
2799
2800        let mut buf = Vec::new();
2801        let mut writer = Writer::new(&mut buf, 13);
2802        writer.write_define_font_2(&font).unwrap();
2803
2804        let mut reader = Reader::new(&buf, 13);
2805        let tag = reader.read_tag().unwrap();
2806
2807        assert_eq!(tag, Tag::DefineFont2(Box::new(font)));
2808    }
2809
2810    #[test]
2811    fn write_define_button_2() {
2812        use crate::read::Reader;
2813
2814        let button = Button {
2815            id: 3,
2816            is_track_as_menu: false,
2817            records: vec![ButtonRecord {
2818                states: ButtonState::UP
2819                    | ButtonState::OVER
2820                    | ButtonState::DOWN
2821                    | ButtonState::HIT_TEST,
2822                id: 2,
2823                depth: 1,
2824                matrix: Matrix::translate(Twips::from_pixels(2.0), Twips::from_pixels(3.0)),
2825                color_transform: ColorTransform::default(),
2826                filters: vec![],
2827                blend_mode: BlendMode::Normal,
2828            }],
2829            actions: vec![],
2830        };
2831
2832        let mut buf = Vec::new();
2833        let mut writer = Writer::new(&mut buf, 15);
2834        writer.write_define_button_2(&button).unwrap();
2835
2836        let mut reader = Reader::new(&buf, 15);
2837        let tag = reader.read_tag().unwrap();
2838
2839        assert_eq!(tag, Tag::DefineButton2(Box::new(button)));
2840    }
2841
2842    #[test]
2843    fn write_define_button_2_with_keycode() {
2844        use crate::read::Reader;
2845
2846        let button = Button {
2847            id: 3,
2848            is_track_as_menu: false,
2849            records: vec![ButtonRecord {
2850                states: ButtonState::UP
2851                    | ButtonState::OVER
2852                    | ButtonState::DOWN
2853                    | ButtonState::HIT_TEST,
2854                id: 2,
2855                depth: 1,
2856                matrix: Matrix::translate(Twips::from_pixels(2.0), Twips::from_pixels(3.0)),
2857                color_transform: ColorTransform::default(),
2858                filters: vec![],
2859                blend_mode: BlendMode::Normal,
2860            }],
2861            actions: vec![ButtonAction {
2862                conditions: ButtonActionCondition::from_key_code(18),
2863                action_data: &[
2864                    150, 15, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 104, 105, 115, 0, 28, 150, 12,
2865                    0, 0, 116, 97, 98, 72, 97, 110, 100, 108, 101, 114, 0, 82, 23, 0,
2866                ],
2867            }],
2868        };
2869
2870        let mut buf = Vec::new();
2871        let mut writer = Writer::new(&mut buf, 15);
2872        writer.write_define_button_2(&button).unwrap();
2873
2874        let mut reader = Reader::new(&buf, 15);
2875        let tag = reader.read_tag().unwrap();
2876
2877        assert_eq!(tag, Tag::DefineButton2(Box::new(button)));
2878    }
2879
2880    #[test]
2881    fn write_bevel_filter() {
2882        use crate::read::Reader;
2883
2884        let filter = Filter::BevelFilter(Box::new(BevelFilter {
2885            shadow_color: Color {
2886                r: 111,
2887                g: 222,
2888                b: 33,
2889                a: 4,
2890            },
2891            highlight_color: Color {
2892                r: 10,
2893                g: 20,
2894                b: 30,
2895                a: 40,
2896            },
2897            blur_x: Fixed16::from_f32(3.1),
2898            blur_y: Fixed16::from_f32(5.5),
2899            angle: Fixed16::from_f32(180.0),
2900            distance: Fixed16::from_f32(10.8),
2901            strength: Fixed8::from_f32(3.1),
2902            flags: BevelFilterFlags::COMPOSITE_SOURCE
2903                | BevelFilterFlags::ON_TOP
2904                | BevelFilterFlags::from_passes(2),
2905        }));
2906
2907        let mut buf = Vec::new();
2908        let mut writer = Writer::new(&mut buf, 15);
2909        writer.write_filter(&filter).unwrap();
2910
2911        let mut reader = Reader::new(&buf, 15);
2912        let reread = reader.read_filter().unwrap();
2913
2914        assert_eq!(reread, filter);
2915    }
2916
2917    #[test]
2918    fn write_define_text_2() {
2919        use crate::read::Reader;
2920
2921        let text = Text {
2922            id: 60,
2923            bounds: Rectangle {
2924                x_min: Twips::new(2),
2925                x_max: Twips::new(559),
2926                y_min: Twips::new(-4),
2927                y_max: Twips::new(76),
2928            },
2929            matrix: Matrix {
2930                a: Fixed16::ONE,
2931                b: Fixed16::ZERO,
2932                c: Fixed16::ZERO,
2933                d: Fixed16::ONE,
2934                tx: Twips::ZERO,
2935                ty: Twips::ZERO,
2936            },
2937            records: vec![TextRecord {
2938                font_id: Some(57),
2939                color: Some(Color {
2940                    r: 112,
2941                    g: 103,
2942                    b: 5,
2943                    a: 69,
2944                }),
2945                x_offset: None,
2946                y_offset: Some(Twips::new(76)),
2947                height: Some(Twips::new(100)),
2948                glyphs: vec![
2949                    GlyphEntry {
2950                        index: 13,
2951                        advance: 32,
2952                    },
2953                    GlyphEntry {
2954                        index: 2,
2955                        advance: 38,
2956                    },
2957                    GlyphEntry {
2958                        index: 8,
2959                        advance: 41,
2960                    },
2961                    GlyphEntry {
2962                        index: 6,
2963                        advance: 18,
2964                    },
2965                    GlyphEntry {
2966                        index: 7,
2967                        advance: 33,
2968                    },
2969                    GlyphEntry {
2970                        index: 7,
2971                        advance: 33,
2972                    },
2973                    GlyphEntry {
2974                        index: 2,
2975                        advance: 38,
2976                    },
2977                    GlyphEntry {
2978                        index: 0,
2979                        advance: 19,
2980                    },
2981                    GlyphEntry {
2982                        index: 6,
2983                        advance: 18,
2984                    },
2985                    GlyphEntry {
2986                        index: 3,
2987                        advance: 35,
2988                    },
2989                    GlyphEntry {
2990                        index: 4,
2991                        advance: 37,
2992                    },
2993                    GlyphEntry {
2994                        index: 0,
2995                        advance: 19,
2996                    },
2997                    GlyphEntry {
2998                        index: 1,
2999                        advance: 23,
3000                    },
3001                    GlyphEntry {
3002                        index: 0,
3003                        advance: 19,
3004                    },
3005                    GlyphEntry {
3006                        index: 7,
3007                        advance: 33,
3008                    },
3009                    GlyphEntry {
3010                        index: 5,
3011                        advance: 39,
3012                    },
3013                    GlyphEntry {
3014                        index: 10,
3015                        advance: 40,
3016                    },
3017                ],
3018            }],
3019        };
3020
3021        let mut buf = Vec::new();
3022        let mut writer = Writer::new(&mut buf, 4);
3023        writer.write_define_text(&text, 2).unwrap();
3024
3025        let mut reader = Reader::new(&buf, 4);
3026        let reread = reader.read_tag().unwrap();
3027
3028        assert_eq!(reread, Tag::DefineText2(Box::new(text)));
3029    }
3030
3031    #[test]
3032    fn write_define_font_info() {
3033        use crate::read::Reader;
3034
3035        let font_info = FontInfo {
3036            id: 1,
3037            version: 1,
3038            name: SwfStr::from_bytes(b"Schauer"),
3039            flags: FontInfoFlag::HAS_WIDE_CODES | FontInfoFlag::IS_BOLD,
3040            language: Language::Unknown,
3041            code_table: vec![
3042                80, 108, 97, 121, 32, 109, 111, 118, 105, 101, 53, 48, 54, 75, 103, 115, 116, 74,
3043                65, 67, 83, 86, 71, 84, 89, 76, 69, 42, 104, 46, 73, 100, 87, 107, 110, 102, 117,
3044                99, 114, 63, 79, 39, 119, 44, 112, 33, 120, 72, 122, 58, 77, 45, 98, 40, 41, 70,
3045            ],
3046        };
3047
3048        let mut buf = Vec::new();
3049        let mut writer = Writer::new(&mut buf, 4);
3050        writer.write_define_font_info(&font_info).unwrap();
3051
3052        let mut reader = Reader::new(&buf, 4);
3053        let reread = reader.read_tag().unwrap();
3054
3055        assert_eq!(reread, Tag::DefineFontInfo(Box::new(font_info)));
3056    }
3057}