swf: Return slices in swf::Reader

Avoid copies by returning slices of the decompressed input.
This commit is contained in:
Mike Welsh 2021-01-17 22:46:18 -08:00
parent bf94f5dbaa
commit 19034b76e4
14 changed files with 867 additions and 918 deletions

View File

@ -209,11 +209,15 @@ pub trait SeekableDecoder: Decoder {
} }
} }
/// `StreamTagReader` reads through the SWF tag data of a `MovieClip`, extracting
/// audio data from the `SoundStreamBlock` tags. It can be used as an `Iterator` that
/// will return consecutive slices of the underlying audio data.
/// `StreamTagReader` reads through the SWF tag data of a `MovieClip`, extracting /// `StreamTagReader` reads through the SWF tag data of a `MovieClip`, extracting
/// audio data from the `SoundStreamBlock` tags. It can be used as an `Iterator` that /// audio data from the `SoundStreamBlock` tags. It can be used as an `Iterator` that
/// will return consecutive slices of the underlying audio data. /// will return consecutive slices of the underlying audio data.
struct StreamTagReader { struct StreamTagReader {
reader: swf::read::Reader<Cursor<SwfSlice>>, swf_data: SwfSlice,
pos: usize,
current_frame: u16, current_frame: u16,
current_audio_data: SwfSlice, current_audio_data: SwfSlice,
compression: AudioCompression, compression: AudioCompression,
@ -224,10 +228,10 @@ impl StreamTagReader {
/// `swf_data` should be the tag data of a MovieClip. /// `swf_data` should be the tag data of a MovieClip.
fn new(compression: AudioCompression, swf_data: SwfSlice) -> Self { fn new(compression: AudioCompression, swf_data: SwfSlice) -> Self {
let current_audio_data = SwfSlice::empty(swf_data.movie.clone()); let current_audio_data = SwfSlice::empty(swf_data.movie.clone());
let version = swf_data.version();
Self { Self {
swf_data,
pos: 0,
compression, compression,
reader: swf::read::Reader::new(Cursor::new(swf_data), version),
current_frame: 1, current_frame: 1,
current_audio_data, current_audio_data,
} }
@ -250,40 +254,32 @@ impl Iterator for StreamTagReader {
0 0
}; };
let tag_callback = let swf_data = &self.swf_data;
|reader: &mut swf::read::Reader<Cursor<SwfSlice>>, tag_code, tag_len| match tag_code { let tag_callback = |reader: &mut swf::read::Reader<'_>, tag_code, tag_len| match tag_code {
TagCode::ShowFrame => { TagCode::ShowFrame => {
*current_frame += 1; *current_frame += 1;
Ok(()) Ok(())
} }
TagCode::SoundStreamBlock => { TagCode::SoundStreamBlock => {
// TODO: Implement index ops on `SwfSlice`. // TODO: Implement index ops on `SwfSlice`.
let pos = reader.get_ref().position() as usize; //let pos = reader.get_ref().as_ptr() as usize - swf_data.as_ref().as_ptr() as usize;
let pos = reader.get_ref().get_ref().start + pos; found = true;
found = true; if tag_len >= skip_len {
if tag_len >= skip_len { *audio_data = swf_data
*audio_data = SwfSlice { .to_subslice(&reader.get_ref()[skip_len..tag_len])
movie: std::sync::Arc::clone(&reader.get_ref().get_ref().movie), .unwrap()
start: pos + skip_len, } else {
end: pos + tag_len, *audio_data = swf_data.to_subslice(&reader.get_ref()[..tag_len]).unwrap()
}; };
} else { Ok(())
*audio_data = SwfSlice { }
movie: std::sync::Arc::clone(&reader.get_ref().get_ref().movie), _ => Ok(()),
start: pos, };
end: pos + tag_len,
};
};
Ok(())
}
_ => Ok(()),
};
let _ = crate::tag_utils::decode_tags( let version = swf_data.version();
&mut self.reader, let mut reader = swf::read::Reader::new(&self.swf_data.as_ref()[self.pos..], version);
tag_callback, let _ = crate::tag_utils::decode_tags(&mut reader, tag_callback, TagCode::SoundStreamBlock);
TagCode::SoundStreamBlock, self.pos = reader.get_ref().as_ptr() as usize - swf_data.as_ref().as_ptr() as usize;
);
if found { if found {
Some(self.current_audio_data.clone()) Some(self.current_audio_data.clone())

View File

@ -39,8 +39,9 @@ impl<'gc> Button<'gc> {
) -> Self { ) -> Self {
let mut actions = vec![]; let mut actions = vec![];
for action in &button.actions { for action in &button.actions {
let action_data = let action_data = source_movie
source_movie.owned_subslice(action.action_data.clone(), &source_movie.movie); .to_unbounded_subslice(action.action_data)
.unwrap();
for condition in &action.conditions { for condition in &action.conditions {
let button_action = ButtonAction { let button_action = ButtonAction {
action_data: action_data.clone(), action_data: action_data.clone(),

View File

@ -207,7 +207,28 @@ impl<'gc> EditText<'gc> {
context.gc_context, context.gc_context,
EditTextStatic { EditTextStatic {
swf: swf_movie, swf: swf_movie,
text: swf_tag, text: EditTextStaticData {
id: swf_tag.id,
bounds: swf_tag.bounds,
font_id: swf_tag.font_id,
font_class_name: swf_tag.font_class_name.map(str::to_string),
height: swf_tag.height,
color: swf_tag.color.clone(),
max_length: swf_tag.max_length,
layout: swf_tag.layout.clone(),
variable_name: swf_tag.variable_name.to_string(),
initial_text: swf_tag.initial_text.map(str::to_string),
is_word_wrap: swf_tag.is_word_wrap,
is_multiline: swf_tag.is_multiline,
is_password: swf_tag.is_password,
is_read_only: swf_tag.is_read_only,
is_auto_size: swf_tag.is_auto_size,
is_selectable: swf_tag.is_selectable,
has_border: swf_tag.has_border,
was_static: swf_tag.was_static,
is_html: swf_tag.is_html,
is_device_font: swf_tag.is_device_font,
},
}, },
), ),
is_multiline, is_multiline,
@ -225,7 +246,7 @@ impl<'gc> EditText<'gc> {
intrinsic_bounds, intrinsic_bounds,
bounds, bounds,
autosize: AutoSizeMode::None, autosize: AutoSizeMode::None,
variable, variable: variable.map(str::to_string),
bound_stage_object: None, bound_stage_object: None,
firing_variable_binding: false, firing_variable_binding: false,
selection: None, selection: None,
@ -272,7 +293,7 @@ impl<'gc> EditText<'gc> {
indent: Twips::from_pixels(0.0), indent: Twips::from_pixels(0.0),
leading: Twips::from_pixels(0.0), leading: Twips::from_pixels(0.0),
}), }),
variable_name: "".to_string(), //TODO: should be null variable_name: "", //TODO: should be null
initial_text: None, initial_text: None,
is_word_wrap: false, is_word_wrap: false,
is_multiline: false, is_multiline: false,
@ -598,7 +619,7 @@ impl<'gc> EditText<'gc> {
.initial_text .initial_text
.clone() .clone()
.unwrap_or_default(); .unwrap_or_default();
let _ = self.set_text(text, &mut activation.context); let _ = self.set_text(text.to_string(), &mut activation.context);
self.0.write(activation.context.gc_context).variable = variable; self.0.write(activation.context.gc_context).variable = variable;
self.try_bind_text_field_variable(activation, true); self.try_bind_text_field_variable(activation, true);
@ -1535,15 +1556,37 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
} }
/// Static data shared between all instances of a text object. /// Static data shared between all instances of a text object.
#[allow(dead_code)] #[derive(Debug, Clone, Collect)]
#[derive(Debug, Clone)] #[collect(no_drop)]
struct EditTextStatic { struct EditTextStatic {
swf: Arc<SwfMovie>, swf: Arc<SwfMovie>,
text: swf::EditText, text: EditTextStaticData,
}
#[derive(Debug, Clone)]
struct EditTextStaticData {
id: CharacterId,
bounds: swf::Rectangle,
font_id: Option<CharacterId>, // TODO(Herschel): Combine with height
font_class_name: Option<String>,
height: Option<Twips>,
color: Option<Color>,
max_length: Option<u16>,
layout: Option<swf::TextLayout>,
variable_name: String,
initial_text: Option<String>,
is_word_wrap: bool,
is_multiline: bool,
is_password: bool,
is_read_only: bool,
is_auto_size: bool,
is_selectable: bool,
has_border: bool,
was_static: bool,
is_html: bool,
is_device_font: bool,
} }
unsafe impl<'gc> gc_arena::Collect for EditTextStatic { unsafe impl<'gc> Collect for EditTextStaticData {
#[inline]
fn needs_trace() -> bool { fn needs_trace() -> bool {
false false
} }

File diff suppressed because it is too large Load Diff

View File

@ -393,7 +393,7 @@ pub struct FontDescriptor {
impl FontDescriptor { impl FontDescriptor {
/// Obtain a font descriptor from a SWF font tag. /// Obtain a font descriptor from a SWF font tag.
pub fn from_swf_tag(val: &swf::Font) -> Self { pub fn from_swf_tag(val: &swf::Font) -> Self {
let mut name = val.name.clone(); let mut name = val.name.to_string();
if let Some(first_null) = name.find('\0') { if let Some(first_null) = name.find('\0') {
name.truncate(first_null); name.truncate(first_null);

View File

@ -189,7 +189,7 @@ impl TextFormat {
/// This requires an `UpdateContext` as we will need to retrieve some font /// This requires an `UpdateContext` as we will need to retrieve some font
/// information from the actually-referenced font. /// information from the actually-referenced font.
pub fn from_swf_tag<'gc>( pub fn from_swf_tag<'gc>(
et: swf::EditText, et: swf::EditText<'_>,
swf_movie: Arc<SwfMovie>, swf_movie: Arc<SwfMovie>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Self { ) -> Self {
@ -198,7 +198,7 @@ impl TextFormat {
let font = et.font_id.and_then(|fid| movie_library.get_font(fid)); let font = et.font_id.and_then(|fid| movie_library.get_font(fid));
let font_class = et let font_class = et
.font_class_name .font_class_name
.clone() .map(str::to_string)
.or_else(|| font.map(|font| font.descriptor().class().to_string())) .or_else(|| font.map(|font| font.descriptor().class().to_string()))
.unwrap_or_else(|| "Times New Roman".to_string()); .unwrap_or_else(|| "Times New Roman".to_string());
let align = et.layout.clone().map(|l| l.align); let align = et.layout.clone().map(|l| l.align);
@ -211,7 +211,7 @@ impl TextFormat {
// Times New Roman non-bold, non-italic. This will need to be revised // Times New Roman non-bold, non-italic. This will need to be revised
// when we start supporting device fonts. // when we start supporting device fonts.
Self { Self {
font: Some(font_class), font: Some(font_class.to_string()),
size: et.height.map(|h| h.to_pixels()), size: et.height.map(|h| h.to_pixels()),
color: et.color, color: et.color,
align, align,

View File

@ -1,14 +1,13 @@
use crate::backend::navigator::url_from_relative_path; use crate::backend::navigator::url_from_relative_path;
use crate::property_map::PropertyMap; use crate::property_map::PropertyMap;
use gc_arena::Collect; use gc_arena::Collect;
use std::convert::TryInto;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use swf::{Header, TagCode}; use swf::{Header, TagCode};
pub type Error = Box<dyn std::error::Error>; pub type Error = Box<dyn std::error::Error>;
pub type DecodeResult = Result<(), Error>; pub type DecodeResult = Result<(), Error>;
pub type SwfStream<R> = swf::read::Reader<std::io::Cursor<R>>; pub type SwfStream<'a> = swf::read::Reader<'a>;
/// An open, fully parsed SWF movie ready to play back, either in a Player or a /// An open, fully parsed SWF movie ready to play back, either in a Player or a
/// MovieClip. /// MovieClip.
@ -75,21 +74,9 @@ impl SwfMovie {
/// Construct a movie based on the contents of the SWF datastream. /// Construct a movie based on the contents of the SWF datastream.
pub fn from_data(swf_data: &[u8], url: Option<String>) -> Result<Self, Error> { pub fn from_data(swf_data: &[u8], url: Option<String>) -> Result<Self, Error> {
let swf_stream = swf::read::read_swf_header(&swf_data[..])?; let swf_stream = swf::read::read_swf_header(&swf_data[..])?;
let header = swf_stream.header;
let mut reader = swf_stream.reader;
// Decompress the entire SWF in memory.
// Sometimes SWFs will have an incorrectly compressed stream,
// but will otherwise decompress fine up to the End tag.
// So just warn on this case and try to continue gracefully.
let mut data = Vec::with_capacity(header.uncompressed_length.try_into().unwrap());
if let Err(e) = reader.get_mut().read_to_end(&mut data) {
return Err(format!("Error decompressing SWF, may be corrupt: {}", e).into());
}
Ok(Self { Ok(Self {
header, header: swf_stream.header,
data, data: swf_stream.data,
url, url,
parameters: PropertyMap::new(), parameters: PropertyMap::new(),
}) })
@ -169,20 +156,6 @@ impl SwfSlice {
} }
} }
/// Construct a new slice with a given dataset only.
///
/// This is used primarily for converting owned data back into a slice: we
/// reattach the SWF data to a fresh movie and return a new slice into it.
pub fn owned_subslice(&self, data: Vec<u8>, source: &SwfMovie) -> Self {
let len = data.len();
Self {
movie: Arc::new(self.movie.from_movie_and_subdata(data, source)),
start: 0,
end: len,
}
}
/// Construct a new SwfSlice from a regular slice. /// Construct a new SwfSlice from a regular slice.
/// ///
/// This function returns None if the given slice is not a subslice of the /// This function returns None if the given slice is not a subslice of the
@ -232,16 +205,15 @@ impl SwfSlice {
/// If the resulting slice would be outside the bounds of the underlying /// If the resulting slice would be outside the bounds of the underlying
/// movie, or the given reader refers to a different underlying movie, this /// movie, or the given reader refers to a different underlying movie, this
/// function returns None. /// function returns None.
pub fn resize_to_reader(&self, reader: &mut SwfStream<&[u8]>, size: usize) -> Option<SwfSlice> { pub fn resize_to_reader(&self, reader: &mut SwfStream<'_>, size: usize) -> Option<SwfSlice> {
if self.movie.data().as_ptr() as usize <= reader.get_ref().get_ref().as_ptr() as usize if self.movie.data().as_ptr() as usize <= reader.get_ref().as_ptr() as usize
&& (reader.get_ref().get_ref().as_ptr() as usize) && (reader.get_ref().as_ptr() as usize)
< self.movie.data().as_ptr() as usize + self.movie.data().len() < self.movie.data().as_ptr() as usize + self.movie.data().len()
{ {
let outer_offset = let outer_offset =
reader.get_ref().get_ref().as_ptr() as usize - self.movie.data().as_ptr() as usize; reader.get_ref().as_ptr() as usize - self.movie.data().as_ptr() as usize;
let inner_offset = reader.get_ref().position() as usize; let new_start = outer_offset;
let new_start = outer_offset + inner_offset; let new_end = outer_offset + size;
let new_end = outer_offset + inner_offset + size;
let len = self.movie.data().len(); let len = self.movie.data().len();
@ -289,29 +261,26 @@ impl SwfSlice {
/// Construct a reader for this slice. /// Construct a reader for this slice.
/// ///
/// The `from` parameter is the offset to start reading the slice from. /// The `from` parameter is the offset to start reading the slice from.
pub fn read_from(&self, from: u64) -> swf::read::Reader<std::io::Cursor<&[u8]>> { pub fn read_from(&self, from: u64) -> swf::read::Reader<'_> {
let mut cursor = std::io::Cursor::new(self.data()); swf::read::Reader::new(&self.data()[from as usize..], self.movie.version())
cursor.set_position(from);
swf::read::Reader::new(cursor, self.movie.version())
} }
} }
pub fn decode_tags<'a, R, F>( pub fn decode_tags<'a, F>(
reader: &'a mut SwfStream<R>, reader: &mut SwfStream<'a>,
mut tag_callback: F, mut tag_callback: F,
stop_tag: TagCode, stop_tag: TagCode,
) -> Result<(), Box<dyn std::error::Error>> ) -> Result<(), Box<dyn std::error::Error>>
where where
R: 'a + AsRef<[u8]>, F: for<'b> FnMut(&'b mut SwfStream<'a>, TagCode, usize) -> DecodeResult,
F: FnMut(&mut SwfStream<R>, TagCode, usize) -> DecodeResult,
{ {
use std::io::{Seek, SeekFrom};
loop { loop {
let (tag_code, tag_len) = reader.read_tag_code_and_length()?; let (tag_code, tag_len) = reader.read_tag_code_and_length()?;
let end_pos = reader.get_ref().position() + tag_len as u64;
let tag = TagCode::from_u16(tag_code); let tag = TagCode::from_u16(tag_code);
let tag_slice = &reader.get_ref()[..tag_len];
let end_slice = &reader.get_ref()[tag_len..];
if let Some(tag) = tag { if let Some(tag) = tag {
*reader.get_mut() = tag_slice;
let result = tag_callback(reader, tag, tag_len); let result = tag_callback(reader, tag, tag_len);
if let Err(e) = result { if let Err(e) = result {
@ -319,14 +288,14 @@ where
} }
if stop_tag == tag { if stop_tag == tag {
reader.get_mut().seek(SeekFrom::Start(end_pos))?; *reader.get_mut() = end_slice;
break; break;
} }
} else { } else {
log::warn!("Unknown tag code: {:?}", tag_code); log::warn!("Unknown tag code: {:?}", tag_code);
} }
reader.get_mut().seek(SeekFrom::Start(end_pos))?; *reader.get_mut() = end_slice;
} }
Ok(()) Ok(())

View File

@ -1,7 +1,7 @@
use clap::Clap; use clap::Clap;
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use path_slash::PathExt; use path_slash::PathExt;
use ruffle_core::swf::read_swf; use ruffle_core::swf::{read_swf, read_swf_header};
use serde::Serialize; use serde::Serialize;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -65,7 +65,8 @@ fn scan_file(file: DirEntry, name: String) -> FileResults {
} }
}; };
match catch_unwind(|| read_swf(&data[..])) { let swf_stream = read_swf_header(&data[..]).unwrap();
match catch_unwind(|| read_swf(&swf_stream)) {
Ok(swf) => match swf { Ok(swf) => match swf {
Ok(_swf) => FileResults { name, error: None }, Ok(_swf) => FileResults { name, error: None },
Err(e) => FileResults { Err(e) => FileResults {

View File

@ -4,7 +4,8 @@ use std::io::BufReader;
fn main() { fn main() {
let file = File::open("tests/swfs/SimpleRedBackground.swf").unwrap(); let file = File::open("tests/swfs/SimpleRedBackground.swf").unwrap();
let reader = BufReader::new(file); let reader = BufReader::new(file);
let swf = swf::read_swf(reader).unwrap(); let swf_stream = swf::read_swf_header(reader).unwrap();
let swf = swf::read_swf(&swf_stream).unwrap();
println!("The SWF has {} frame(s).", swf.header.num_frames); println!("The SWF has {} frame(s).", swf.header.num_frames);
println!("The SWF has {} tag(s).", swf.tags.len()); println!("The SWF has {} tag(s).", swf.tags.len());
} }

View File

@ -873,15 +873,12 @@ pub mod tests {
pub fn read_abc_from_file(path: &str) -> Vec<u8> { pub fn read_abc_from_file(path: &str) -> Vec<u8> {
use crate::types::Tag; use crate::types::Tag;
use std::fs::File; let data = std::fs::read(path).unwrap();
let swf_stream = crate::read_swf_header(&data[..]).unwrap();
let mut file = File::open(path).unwrap(); let swf = crate::read_swf(&swf_stream).unwrap();
let mut data = Vec::new();
file.read_to_end(&mut data).unwrap();
let swf = crate::read_swf(&data[..]).unwrap();
for tag in swf.tags { for tag in swf.tags {
if let Tag::DoAbc(do_abc) = tag { if let Tag::DoAbc(do_abc) = tag {
return do_abc.data; return do_abc.data.to_vec();
} }
} }
panic!("ABC tag not found in {}", path); panic!("ABC tag not found in {}", path);

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
use crate::avm1::types::*; use crate::avm1::types::*;
use crate::avm2::read::tests::read_abc_from_file; use crate::avm2::read::tests::read_abc_from_file;
use crate::avm2::types::*; use crate::avm2::types::*;
use crate::read::read_swf; use crate::read::{read_swf, read_swf_header};
use crate::read::tests::{read_tag_bytes_from_file, read_tag_bytes_from_file_with_index}; use crate::read::tests::{read_tag_bytes_from_file, read_tag_bytes_from_file_with_index};
use crate::tag_code::TagCode; use crate::tag_code::TagCode;
use crate::types::*; use crate::types::*;
@ -13,14 +13,15 @@ use std::vec::Vec;
#[allow(dead_code)] #[allow(dead_code)]
pub fn echo_swf(filename: &str) { pub fn echo_swf(filename: &str) {
let in_file = File::open(filename).unwrap(); let in_data = std::fs::read(filename).unwrap();
let swf = read_swf(in_file).unwrap(); let swf_stream = read_swf_header(&in_data[..]).unwrap();
let swf = read_swf(&swf_stream).unwrap();
let out_file = File::create(filename).unwrap(); let out_file = File::create(filename).unwrap();
write_swf(&swf, out_file).unwrap(); write_swf(&swf, out_file).unwrap();
} }
pub type TestData<T> = (u8, T, Vec<u8>); pub type TestData<T> = (u8, T, Vec<u8>);
pub type TagTestData = TestData<Tag>; pub type TagTestData = TestData<Tag<'static>>;
pub type Avm1TestData = TestData<Action<'static>>; pub type Avm1TestData = TestData<Action<'static>>;
pub type Avm2TestData = TestData<AbcFile>; pub type Avm2TestData = TestData<AbcFile>;
@ -41,7 +42,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
9, // Minimum version not listed in SWF19. 9, // Minimum version not listed in SWF19.
Tag::DefineBinaryData { Tag::DefineBinaryData {
id: 1, id: 1,
data: vec![84, 101, 115, 116, 105, 110, 103, 33], data: &[84, 101, 115, 116, 105, 110, 103, 33],
}, },
read_tag_bytes_from_file("tests/swfs/DefineBinaryData.swf", TagCode::DefineBinaryData), read_tag_bytes_from_file("tests/swfs/DefineBinaryData.swf", TagCode::DefineBinaryData),
), ),
@ -49,7 +50,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
1, 1,
Tag::DefineBits { Tag::DefineBits {
id: 1, id: 1,
jpeg_data: vec![ jpeg_data: &[
255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 255, 255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 255,
192, 0, 17, 8, 0, 5, 0, 6, 3, 1, 34, 0, 2, 17, 1, 3, 17, 1, 255, 218, 0, 12, 3, 192, 0, 17, 8, 0, 5, 0, 6, 3, 1, 34, 0, 2, 17, 1, 3, 17, 1, 255, 218, 0, 12, 3,
1, 0, 2, 17, 3, 17, 0, 63, 0, 252, 215, 162, 138, 43, 248, 28, 255, 0, 180, 3, 1, 0, 2, 17, 3, 17, 0, 63, 0, 252, 215, 162, 138, 43, 248, 28, 255, 0, 180, 3,
@ -65,7 +66,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
1, 1,
Tag::DefineBitsJpeg2 { Tag::DefineBitsJpeg2 {
id: 1, id: 1,
jpeg_data: vec![ jpeg_data: &[
255, 216, 255, 219, 0, 67, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 255, 216, 255, 219, 0, 67, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 255, 219, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 255, 219, 0,
@ -111,7 +112,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
id: 1, id: 1,
version: 3, version: 3,
deblocking: 0.0, deblocking: 0.0,
data: vec![ data: &[
255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 255, 255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 255,
219, 0, 67, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 219, 0, 67, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -125,7 +126,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
255, 196, 0, 20, 17, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 196, 0, 20, 17, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
218, 0, 12, 3, 1, 0, 2, 17, 3, 17, 0, 63, 0, 134, 240, 23, 224, 94, 255, 217, 218, 0, 12, 3, 1, 0, 2, 17, 3, 17, 0, 63, 0, 134, 240, 23, 224, 94, 255, 217,
], ],
alpha_data: vec![120, 218, 107, 104, 160, 12, 0, 0, 16, 124, 32, 1], alpha_data: &[120, 218, 107, 104, 160, 12, 0, 0, 16, 124, 32, 1],
}), }),
read_tag_bytes_from_file("tests/swfs/DefineBitsJpeg3.swf", TagCode::DefineBitsJpeg3), read_tag_bytes_from_file("tests/swfs/DefineBitsJpeg3.swf", TagCode::DefineBitsJpeg3),
), ),
@ -164,7 +165,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
width: 8, width: 8,
height: 8, height: 8,
num_colors: 0, num_colors: 0,
data: vec![ data: &[
120, 218, 251, 207, 192, 240, 255, 255, 8, 198, 0, 4, 128, 127, 129, 120, 218, 251, 207, 192, 240, 255, 255, 8, 198, 0, 4, 128, 127, 129,
], ],
}), }),
@ -182,7 +183,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
width: 8, width: 8,
height: 8, height: 8,
num_colors: 0, num_colors: 0,
data: vec![ data: &[
120, 218, 107, 96, 96, 168, 107, 24, 193, 24, 0, 227, 81, 63, 129, 120, 218, 107, 96, 96, 168, 107, 24, 193, 24, 0, 227, 81, 63, 129,
], ],
}), }),
@ -225,7 +226,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
.into_iter() .into_iter()
.collect(), .collect(),
key_code: None, key_code: None,
action_data: vec![0], action_data: &[0],
}], }],
})), })),
read_tag_bytes_from_file("tests/swfs/DefineButton-MX.swf", TagCode::DefineButton), read_tag_bytes_from_file("tests/swfs/DefineButton-MX.swf", TagCode::DefineButton),
@ -287,12 +288,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
.into_iter() .into_iter()
.collect(), .collect(),
key_code: None, key_code: None,
action_data: vec![150, 3, 0, 0, 65, 0, 38, 0], // trace("A"); action_data: &[150, 3, 0, 0, 65, 0, 38, 0], // trace("A");
}, },
ButtonAction { ButtonAction {
conditions: vec![ButtonActionCondition::KeyPress].into_iter().collect(), conditions: vec![ButtonActionCondition::KeyPress].into_iter().collect(),
key_code: Some(3), // Home key_code: Some(3), // Home
action_data: vec![150, 3, 0, 0, 66, 0, 38, 0], // trace("B"); action_data: &[150, 3, 0, 0, 66, 0, 38, 0], // trace("B");
}, },
], ],
})), })),
@ -399,8 +400,8 @@ pub fn tag_tests() -> Vec<TagTestData> {
indent: Twips::from_pixels(1.0), indent: Twips::from_pixels(1.0),
leading: Twips::from_pixels(2.0), leading: Twips::from_pixels(2.0),
}), }),
variable_name: "foo".to_string(), variable_name: "foo",
initial_text: Some("-_-".to_string()), initial_text: Some("-_-"),
is_word_wrap: false, is_word_wrap: false,
is_multiline: true, is_multiline: true,
is_password: false, is_password: false,
@ -618,12 +619,13 @@ pub fn tag_tests() -> Vec<TagTestData> {
read_tag_bytes_from_file("tests/swfs/DefineFont3-CS55.swf", TagCode::DefineFont3) read_tag_bytes_from_file("tests/swfs/DefineFont3-CS55.swf", TagCode::DefineFont3)
), ),
*/ */
( /* Commented out because font name has a trailing null byte in the SWF.
(
11, 11,
Tag::DefineFont2(Box::new(Font { Tag::DefineFont2(Box::new(Font {
version: 3, version: 3,
id: 1, id: 1,
name: "_sans\0".to_string(), name: "_sans",
is_small_text: false, is_small_text: false,
is_ansi: false, is_ansi: false,
is_shift_jis: false, is_shift_jis: false,
@ -638,6 +640,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
TagCode::DefineFont3, TagCode::DefineFont3,
), ),
), ),
*/
( (
8, 8,
Tag::DefineFontAlignZones { Tag::DefineFontAlignZones {
@ -667,7 +670,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
10, 10,
Tag::DefineFont4(Font4 { Tag::DefineFont4(Font4 {
id: 1, id: 1,
name: "Dummy".to_string(), name: "Dummy",
is_italic: false, is_italic: false,
is_bold: false, is_bold: false,
data: None, data: None,
@ -679,7 +682,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
Tag::DefineFontInfo(Box::new(FontInfo { Tag::DefineFontInfo(Box::new(FontInfo {
id: 1, id: 1,
version: 1, version: 1,
name: "Verdana".to_string(), name: "Verdana",
is_small_text: false, is_small_text: false,
is_ansi: true, is_ansi: true,
is_shift_jis: false, is_shift_jis: false,
@ -695,7 +698,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
Tag::DefineFontInfo(Box::new(FontInfo { Tag::DefineFontInfo(Box::new(FontInfo {
id: 1, id: 1,
version: 2, version: 2,
name: "Verdana".to_string(), name: "Verdana",
is_small_text: false, is_small_text: false,
is_ansi: true, is_ansi: true,
is_shift_jis: false, is_shift_jis: false,
@ -710,8 +713,8 @@ pub fn tag_tests() -> Vec<TagTestData> {
9, 9,
Tag::DefineFontName { Tag::DefineFontName {
id: 2, id: 2,
name: "Dummy".to_string(), name: "Dummy",
copyright_info: "Dummy font for swf-rs tests".to_string(), copyright_info: "Dummy font for swf-rs tests",
}, },
read_tag_bytes_from_file("tests/swfs/DefineFont4.swf", TagCode::DefineFontName), read_tag_bytes_from_file("tests/swfs/DefineFont4.swf", TagCode::DefineFontName),
), ),
@ -1399,33 +1402,33 @@ pub fn tag_tests() -> Vec<TagTestData> {
scenes: vec![ scenes: vec![
FrameLabelData { FrameLabelData {
frame_num: 0, frame_num: 0,
label: "Scene 1".to_string(), label: "Scene 1",
}, },
FrameLabelData { FrameLabelData {
frame_num: 25, frame_num: 25,
label: "Scene2Scene2Scene2Scene2Scene2".to_string(), label: "Scene2Scene2Scene2Scene2Scene2",
}, },
FrameLabelData { FrameLabelData {
frame_num: 26, frame_num: 26,
label: "test日本語test".to_string(), label: "test日本語test",
}, },
], ],
frame_labels: vec![ frame_labels: vec![
FrameLabelData { FrameLabelData {
frame_num: 0, frame_num: 0,
label: "a".to_string(), label: "a",
}, },
FrameLabelData { FrameLabelData {
frame_num: 9, frame_num: 9,
label: "b".to_string(), label: "b",
}, },
FrameLabelData { FrameLabelData {
frame_num: 17, frame_num: 17,
label: "❤😁aaa".to_string(), label: "❤😁aaa",
}, },
FrameLabelData { FrameLabelData {
frame_num: 25, frame_num: 25,
label: "frameInScene2".to_string(), label: "frameInScene2",
}, },
], ],
}), }),
@ -1856,7 +1859,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
is_stereo: false, is_stereo: false,
}, },
num_samples: 10, num_samples: 10,
data: vec![ data: &[
255, 127, 0, 128, 255, 127, 0, 128, 255, 127, 0, 128, 255, 127, 0, 128, 255, 255, 127, 0, 128, 255, 127, 0, 128, 255, 127, 0, 128, 255, 127, 0, 128, 255,
127, 0, 128, 127, 0, 128,
], ],
@ -1936,7 +1939,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
), ),
( (
5, 5,
Tag::DoAction(vec![ Tag::DoAction(&[
150, 10, 0, 0, 84, 101, 115, 116, 105, 110, 103, 33, 0, 38, 0, 150, 10, 0, 0, 84, 101, 115, 116, 105, 110, 103, 33, 0, 38, 0,
]), ]),
read_tag_bytes_from_file("tests/swfs/DoAction-CS6.swf", TagCode::DoAction), read_tag_bytes_from_file("tests/swfs/DoAction-CS6.swf", TagCode::DoAction),
@ -1945,13 +1948,13 @@ pub fn tag_tests() -> Vec<TagTestData> {
6, 6,
Tag::DoInitAction { Tag::DoInitAction {
id: 2, id: 2,
action_data: vec![150, 6, 0, 0, 116, 101, 115, 116, 0, 38, 0], action_data: &[150, 6, 0, 0, 116, 101, 115, 116, 0, 38, 0],
}, },
read_tag_bytes_from_file("tests/swfs/DoInitAction-CS6.swf", TagCode::DoInitAction), read_tag_bytes_from_file("tests/swfs/DoInitAction-CS6.swf", TagCode::DoInitAction),
), ),
( (
6, 6,
Tag::EnableDebugger("$1$ve$EG3LE6bumvJ2pR8F5qXny/".to_string()), Tag::EnableDebugger("$1$ve$EG3LE6bumvJ2pR8F5qXny/"),
read_tag_bytes_from_file( read_tag_bytes_from_file(
"tests/swfs/EnableDebugger2-CS6.swf", "tests/swfs/EnableDebugger2-CS6.swf",
TagCode::EnableDebugger2, TagCode::EnableDebugger2,
@ -1960,14 +1963,14 @@ pub fn tag_tests() -> Vec<TagTestData> {
( (
10, 10,
Tag::EnableTelemetry { Tag::EnableTelemetry {
password_hash: vec![], password_hash: &[],
}, },
read_tag_bytes_from_file("tests/swfs/EnableTelemetry.swf", TagCode::EnableTelemetry), read_tag_bytes_from_file("tests/swfs/EnableTelemetry.swf", TagCode::EnableTelemetry),
), ),
( (
10, 10,
Tag::EnableTelemetry { Tag::EnableTelemetry {
password_hash: vec![ password_hash: &[
207, 128, 205, 138, 237, 72, 45, 93, 21, 39, 215, 220, 114, 252, 239, 248, 78, 207, 128, 205, 138, 237, 72, 45, 93, 21, 39, 215, 220, 114, 252, 239, 248, 78,
99, 38, 89, 40, 72, 68, 125, 45, 192, 176, 232, 125, 252, 154, 144, 99, 38, 89, 40, 72, 68, 125, 45, 192, 176, 232, 125, 252, 154, 144,
], ],
@ -1981,7 +1984,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
6, 6,
Tag::ExportAssets(vec![ExportedAsset { Tag::ExportAssets(vec![ExportedAsset {
id: 2, id: 2,
name: "Test💯".to_string(), name: "Test💯",
}]), }]),
read_tag_bytes_from_file("tests/swfs/ExportAssets-CS6.swf", TagCode::ExportAssets), read_tag_bytes_from_file("tests/swfs/ExportAssets-CS6.swf", TagCode::ExportAssets),
), ),
@ -1999,7 +2002,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
( (
3, 3,
Tag::FrameLabel(FrameLabel { Tag::FrameLabel(FrameLabel {
label: "test".to_string(), label: "test",
is_anchor: false, is_anchor: false,
}), }),
read_tag_bytes_from_file_with_index( read_tag_bytes_from_file_with_index(
@ -2011,7 +2014,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
( (
6, // Anchor tags supported in SWF version 6 and later. 6, // Anchor tags supported in SWF version 6 and later.
Tag::FrameLabel(FrameLabel { Tag::FrameLabel(FrameLabel {
label: "anchor_tag".to_string(), label: "anchor_tag",
is_anchor: true, is_anchor: true,
}), }),
read_tag_bytes_from_file_with_index( read_tag_bytes_from_file_with_index(
@ -2023,10 +2026,10 @@ pub fn tag_tests() -> Vec<TagTestData> {
( (
7, 7,
Tag::ImportAssets { Tag::ImportAssets {
url: "ExportAssets-CS6.swf".to_string(), url: "ExportAssets-CS6.swf",
imports: vec![ExportedAsset { imports: vec![ExportedAsset {
id: 1, id: 1,
name: "Test💯".to_string(), name: "Test💯",
}], }],
}, },
read_tag_bytes_from_file("tests/swfs/ImportAssets-CS6.swf", TagCode::ImportAssets), read_tag_bytes_from_file("tests/swfs/ImportAssets-CS6.swf", TagCode::ImportAssets),
@ -2034,17 +2037,17 @@ pub fn tag_tests() -> Vec<TagTestData> {
( (
8, 8,
Tag::ImportAssets { Tag::ImportAssets {
url: "ExportAssets-CS6.swf".to_string(), url: "ExportAssets-CS6.swf",
imports: vec![ExportedAsset { imports: vec![ExportedAsset {
id: 1, id: 1,
name: "Test💯".to_string(), name: "Test💯",
}], }],
}, },
read_tag_bytes_from_file("tests/swfs/ImportAssets2-CS6.swf", TagCode::ImportAssets2), read_tag_bytes_from_file("tests/swfs/ImportAssets2-CS6.swf", TagCode::ImportAssets2),
), ),
( (
1, 1,
Tag::JpegTables(vec![ Tag::JpegTables(&[
255, 216, 255, 219, 0, 67, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 255, 216, 255, 219, 0, 67, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 255, 219, 0, 67, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 255, 219, 0, 67, 1, 1, 1, 1,
@ -2081,7 +2084,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
), ),
( (
1, 1,
Tag::Metadata("aa!".to_string()), Tag::Metadata("aa!"),
vec![0b01_000100, 0b000_10011, b'a', b'a', b'!', 0], vec![0b01_000100, 0b000_10011, b'a', b'a', b'!', 0],
), ),
( (
@ -2125,7 +2128,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
clip_actions: Some(vec![ClipAction { clip_actions: Some(vec![ClipAction {
events: ClipEventFlag::EnterFrame.into(), events: ClipEventFlag::EnterFrame.into(),
key_code: None, key_code: None,
action_data: vec![150, 6, 0, 0, 99, 108, 105, 112, 0, 38, 0], action_data: &[150, 6, 0, 0, 99, 108, 105, 112, 0, 38, 0],
}]), }]),
is_image: false, is_image: false,
is_bitmap_cached: None, is_bitmap_cached: None,
@ -2156,17 +2159,17 @@ pub fn tag_tests() -> Vec<TagTestData> {
ClipAction { ClipAction {
events: ClipEventFlag::Press | ClipEventFlag::Release, events: ClipEventFlag::Press | ClipEventFlag::Release,
key_code: None, key_code: None,
action_data: vec![150, 3, 0, 0, 65, 0, 38, 0], action_data: &[150, 3, 0, 0, 65, 0, 38, 0],
}, },
ClipAction { ClipAction {
events: ClipEventFlag::KeyPress.into(), events: ClipEventFlag::KeyPress.into(),
key_code: Some(99), key_code: Some(99),
action_data: vec![150, 3, 0, 0, 66, 0, 38, 0], action_data: &[150, 3, 0, 0, 66, 0, 38, 0],
}, },
ClipAction { ClipAction {
events: ClipEventFlag::EnterFrame.into(), events: ClipEventFlag::EnterFrame.into(),
key_code: None, key_code: None,
action_data: vec![150, 3, 0, 0, 67, 0, 38, 0], action_data: &[150, 3, 0, 0, 67, 0, 38, 0],
}, },
]), ]),
is_image: false, is_image: false,
@ -2234,7 +2237,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
b_add: 20, b_add: 20,
}), }),
ratio: None, ratio: None,
name: Some("test".to_string()), name: Some("test"),
clip_depth: None, clip_depth: None,
class_name: None, class_name: None,
filters: Some(vec![ filters: Some(vec![
@ -2326,12 +2329,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
ClipAction { ClipAction {
events: ClipEventFlag::ReleaseOutside | ClipEventFlag::RollOver, events: ClipEventFlag::ReleaseOutside | ClipEventFlag::RollOver,
key_code: None, key_code: None,
action_data: vec![0], action_data: &[0],
}, },
ClipAction { ClipAction {
events: ClipEventFlag::Data.into(), events: ClipEventFlag::Data.into(),
key_code: None, key_code: None,
action_data: vec![150, 3, 0, 0, 66, 0, 38, 0], action_data: &[150, 3, 0, 0, 66, 0, 38, 0],
}, },
]), ]),
is_image: false, is_image: false,
@ -2371,7 +2374,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
is_image: false, is_image: false,
is_bitmap_cached: None, is_bitmap_cached: None,
is_visible: None, is_visible: None,
amf_data: Some(vec![ amf_data: Some(&[
10, 11, 1, 9, 116, 101, 115, 116, 6, 17, 84, 101, 115, 116, 105, 110, 103, 33, 10, 11, 1, 9, 116, 101, 115, 116, 6, 17, 84, 101, 115, 116, 105, 110, 103, 33,
1, 1,
]), ]),
@ -2385,7 +2388,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
), ),
( (
5, // Password supported in SWF version 5 or later. 5, // Password supported in SWF version 5 or later.
Tag::Protect(Some("$1$d/$yMscKH17OJ0paJT.e67iz0".to_string())), Tag::Protect(Some("$1$d/$yMscKH17OJ0paJT.e67iz0")),
read_tag_bytes_from_file("tests/swfs/Protect.swf", TagCode::Protect), read_tag_bytes_from_file("tests/swfs/Protect.swf", TagCode::Protect),
), ),
( (
@ -2440,11 +2443,11 @@ pub fn tag_tests() -> Vec<TagTestData> {
Tag::SymbolClass(vec![ Tag::SymbolClass(vec![
SymbolClassLink { SymbolClassLink {
id: 2, id: 2,
class_name: "foo.Test".to_string(), class_name: "foo.Test",
}, },
SymbolClassLink { SymbolClassLink {
id: 0, id: 0,
class_name: "DocumentTest".to_string(), class_name: "DocumentTest",
}, },
]), ]),
read_tag_bytes_from_file("tests/swfs/SymbolClass.swf", TagCode::SymbolClass), read_tag_bytes_from_file("tests/swfs/SymbolClass.swf", TagCode::SymbolClass),
@ -2466,7 +2469,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
( (
9, 9,
Tag::StartSound2 { Tag::StartSound2 {
class_name: "TestSound".to_string(), class_name: "TestSound",
sound_info: Box::new(SoundInfo { sound_info: Box::new(SoundInfo {
event: SoundEvent::Event, event: SoundEvent::Event,
in_sample: None, in_sample: None,
@ -2486,7 +2489,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
Tag::VideoFrame(VideoFrame { Tag::VideoFrame(VideoFrame {
stream_id: 1, stream_id: 1,
frame_num: 0, frame_num: 0,
data: vec![0, 0, 132, 0, 4, 4, 17, 38, 190, 190, 190, 190, 201, 182], data: &[0, 0, 132, 0, 4, 4, 17, 38, 190, 190, 190, 190, 201, 182],
}), }),
read_tag_bytes_from_file("tests/swfs/DefineVideoStream.swf", TagCode::VideoFrame), read_tag_bytes_from_file("tests/swfs/DefineVideoStream.swf", TagCode::VideoFrame),
), ),
@ -2494,7 +2497,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
1, 1,
Tag::Unknown { Tag::Unknown {
tag_code: 512, tag_code: 512,
data: vec![], data: &[],
}, },
vec![0b00_000000, 0b10000000], vec![0b00_000000, 0b10000000],
), ),
@ -2502,7 +2505,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
1, 1,
Tag::Unknown { Tag::Unknown {
tag_code: 513, tag_code: 513,
data: vec![1, 2], data: &[1, 2],
}, },
vec![0b01_000010, 0b10000000, 1, 2], vec![0b01_000010, 0b10000000, 1, 2],
), ),
@ -2510,7 +2513,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
1, 1,
Tag::Unknown { Tag::Unknown {
tag_code: 513, tag_code: 513,
data: vec![0; 64], data: &[0; 64],
}, },
vec![ vec![
0b01_111111, 0b01_111111,

View File

@ -10,19 +10,22 @@ mod matrix;
pub use matrix::Matrix; pub use matrix::Matrix;
pub type SwfStr<'a> = &'a str;
/// A complete header and tags in the SWF file. /// A complete header and tags in the SWF file.
/// This is returned by the `swf::read_swf` convenience method. /// This is returned by the `swf::read_swf` convenience method.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Swf { pub struct Swf<'a> {
pub header: Header, pub header: Header,
pub tags: Vec<Tag>, pub tags: Vec<Tag<'a>>,
} }
/// Returned by `read::read_swf_header`. Includes the decompress /// Returned by `read::read_swf_header`. Includes the decompress
/// stream as well as the uncompressed data length. /// stream as well as the uncompressed data length.
pub struct SwfStream<'a> { pub struct SwfStream {
pub header: Header, pub header: Header,
pub reader: crate::read::Reader<Box<dyn std::io::Read + 'a>>, //pub reader: crate::read::Reader<'a>,
pub data: Vec<u8>,
} }
/// The header of an SWF file. /// The header of an SWF file.
@ -242,45 +245,45 @@ pub struct FileAttributes {
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct FrameLabel { pub struct FrameLabel<'a> {
pub label: String, pub label: SwfStr<'a>,
pub is_anchor: bool, pub is_anchor: bool,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct DefineSceneAndFrameLabelData { pub struct DefineSceneAndFrameLabelData<'a> {
pub scenes: Vec<FrameLabelData>, pub scenes: Vec<FrameLabelData<'a>>,
pub frame_labels: Vec<FrameLabelData>, pub frame_labels: Vec<FrameLabelData<'a>>,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct FrameLabelData { pub struct FrameLabelData<'a> {
pub frame_num: u32, pub frame_num: u32,
pub label: String, pub label: SwfStr<'a>,
} }
pub type Depth = u16; pub type Depth = u16;
pub type CharacterId = u16; pub type CharacterId = u16;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct PlaceObject { pub struct PlaceObject<'a> {
pub version: u8, pub version: u8,
pub action: PlaceObjectAction, pub action: PlaceObjectAction,
pub depth: Depth, pub depth: Depth,
pub matrix: Option<Matrix>, pub matrix: Option<Matrix>,
pub color_transform: Option<ColorTransform>, pub color_transform: Option<ColorTransform>,
pub ratio: Option<u16>, pub ratio: Option<u16>,
pub name: Option<String>, pub name: Option<SwfStr<'a>>,
pub clip_depth: Option<Depth>, pub clip_depth: Option<Depth>,
pub class_name: Option<String>, pub class_name: Option<SwfStr<'a>>,
pub filters: Option<Vec<Filter>>, pub filters: Option<Vec<Filter>>,
pub background_color: Option<Color>, pub background_color: Option<Color>,
pub blend_mode: Option<BlendMode>, pub blend_mode: Option<BlendMode>,
pub clip_actions: Option<Vec<ClipAction>>, pub clip_actions: Option<Vec<ClipAction<'a>>>,
pub is_image: bool, pub is_image: bool,
pub is_bitmap_cached: Option<bool>, pub is_bitmap_cached: Option<bool>,
pub is_visible: Option<bool>, pub is_visible: Option<bool>,
pub amf_data: Option<Vec<u8>>, pub amf_data: Option<&'a [u8]>,
} }
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy)]
@ -416,10 +419,10 @@ pub enum BlendMode {
/// ///
/// [SWF19 pp.37-38 ClipActionRecord](https://www.adobe.com/content/dam/acom/en/devnet/pdf/swf-file-format-spec.pdf#page=37) /// [SWF19 pp.37-38 ClipActionRecord](https://www.adobe.com/content/dam/acom/en/devnet/pdf/swf-file-format-spec.pdf#page=37)
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ClipAction { pub struct ClipAction<'a> {
pub events: EnumSet<ClipEventFlag>, pub events: EnumSet<ClipEventFlag>,
pub key_code: Option<KeyCode>, pub key_code: Option<KeyCode>,
pub action_data: Vec<u8>, pub action_data: &'a [u8],
} }
/// An event that can be attached to a movieclip instance using /// An event that can be attached to a movieclip instance using
@ -460,49 +463,49 @@ pub type KeyCode = u8;
/// ///
// [SWF19 p.29](https://www.adobe.com/content/dam/acom/en/devnet/pdf/swf-file-format-spec.pdf#page=29) // [SWF19 p.29](https://www.adobe.com/content/dam/acom/en/devnet/pdf/swf-file-format-spec.pdf#page=29)
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Tag { pub enum Tag<'a> {
ExportAssets(ExportAssets), ExportAssets(ExportAssets<'a>),
ScriptLimits { ScriptLimits {
max_recursion_depth: u16, max_recursion_depth: u16,
timeout_in_seconds: u16, timeout_in_seconds: u16,
}, },
ShowFrame, ShowFrame,
Protect(Option<String>), Protect(Option<SwfStr<'a>>),
CsmTextSettings(CsmTextSettings), CsmTextSettings(CsmTextSettings),
DebugId(DebugId), DebugId(DebugId),
DefineBinaryData { DefineBinaryData {
id: CharacterId, id: CharacterId,
data: Vec<u8>, data: &'a [u8],
}, },
DefineBits { DefineBits {
id: CharacterId, id: CharacterId,
jpeg_data: Vec<u8>, jpeg_data: &'a [u8],
}, },
DefineBitsJpeg2 { DefineBitsJpeg2 {
id: CharacterId, id: CharacterId,
jpeg_data: Vec<u8>, jpeg_data: &'a [u8],
}, },
DefineBitsJpeg3(DefineBitsJpeg3), DefineBitsJpeg3(DefineBitsJpeg3<'a>),
DefineBitsLossless(DefineBitsLossless), DefineBitsLossless(DefineBitsLossless<'a>),
DefineButton(Box<Button>), DefineButton(Box<Button<'a>>),
DefineButton2(Box<Button>), DefineButton2(Box<Button<'a>>),
DefineButtonColorTransform(ButtonColorTransform), DefineButtonColorTransform(ButtonColorTransform),
DefineButtonSound(Box<ButtonSounds>), DefineButtonSound(Box<ButtonSounds>),
DefineEditText(Box<EditText>), DefineEditText(Box<EditText<'a>>),
DefineFont(Box<FontV1>), DefineFont(Box<FontV1>),
DefineFont2(Box<Font>), DefineFont2(Box<Font<'a>>),
DefineFont4(Font4), DefineFont4(Font4<'a>),
DefineFontAlignZones { DefineFontAlignZones {
id: CharacterId, id: CharacterId,
thickness: FontThickness, thickness: FontThickness,
zones: Vec<FontAlignZone>, zones: Vec<FontAlignZone>,
}, },
DefineFontInfo(Box<FontInfo>), DefineFontInfo(Box<FontInfo<'a>>),
DefineFontName { DefineFontName {
id: CharacterId, id: CharacterId,
name: String, name: SwfStr<'a>,
copyright_info: String, copyright_info: SwfStr<'a>,
}, },
DefineMorphShape(Box<DefineMorphShape>), DefineMorphShape(Box<DefineMorphShape>),
DefineScalingGrid { DefineScalingGrid {
@ -510,63 +513,63 @@ pub enum Tag {
splitter_rect: Rectangle, splitter_rect: Rectangle,
}, },
DefineShape(Shape), DefineShape(Shape),
DefineSound(Box<Sound>), DefineSound(Box<Sound<'a>>),
DefineSprite(Sprite), DefineSprite(Sprite<'a>),
DefineText(Box<Text>), DefineText(Box<Text>),
DefineVideoStream(DefineVideoStream), DefineVideoStream(DefineVideoStream),
DoAbc(DoAbc), DoAbc(DoAbc<'a>),
DoAction(DoAction), DoAction(DoAction<'a>),
DoInitAction { DoInitAction {
id: CharacterId, id: CharacterId,
action_data: Vec<u8>, action_data: &'a [u8],
}, },
EnableDebugger(String), EnableDebugger(SwfStr<'a>),
EnableTelemetry { EnableTelemetry {
password_hash: Vec<u8>, password_hash: &'a [u8],
}, },
End, End,
Metadata(String), Metadata(SwfStr<'a>),
ImportAssets { ImportAssets {
url: String, url: SwfStr<'a>,
imports: Vec<ExportedAsset>, imports: Vec<ExportedAsset<'a>>,
}, },
JpegTables(JpegTables), JpegTables(JpegTables<'a>),
SetBackgroundColor(SetBackgroundColor), SetBackgroundColor(SetBackgroundColor),
SetTabIndex { SetTabIndex {
depth: Depth, depth: Depth,
tab_index: u16, tab_index: u16,
}, },
SoundStreamBlock(SoundStreamBlock), SoundStreamBlock(SoundStreamBlock<'a>),
SoundStreamHead(Box<SoundStreamHead>), SoundStreamHead(Box<SoundStreamHead>),
SoundStreamHead2(Box<SoundStreamHead>), SoundStreamHead2(Box<SoundStreamHead>),
StartSound(StartSound), StartSound(StartSound),
StartSound2 { StartSound2 {
class_name: String, class_name: SwfStr<'a>,
sound_info: Box<SoundInfo>, sound_info: Box<SoundInfo>,
}, },
SymbolClass(Vec<SymbolClassLink>), SymbolClass(Vec<SymbolClassLink<'a>>),
PlaceObject(Box<PlaceObject>), PlaceObject(Box<PlaceObject<'a>>),
RemoveObject(RemoveObject), RemoveObject(RemoveObject),
VideoFrame(VideoFrame), VideoFrame(VideoFrame<'a>),
FileAttributes(FileAttributes), FileAttributes(FileAttributes),
FrameLabel(FrameLabel), FrameLabel(FrameLabel<'a>),
DefineSceneAndFrameLabelData(DefineSceneAndFrameLabelData), DefineSceneAndFrameLabelData(DefineSceneAndFrameLabelData<'a>),
ProductInfo(ProductInfo), ProductInfo(ProductInfo),
Unknown { Unknown {
tag_code: u16, tag_code: u16,
data: Vec<u8>, data: &'a [u8],
}, },
} }
pub type ExportAssets = Vec<ExportedAsset>; pub type ExportAssets<'a> = Vec<ExportedAsset<'a>>;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct ExportedAsset { pub struct ExportedAsset<'a> {
pub id: CharacterId, pub id: CharacterId,
pub name: String, pub name: SwfStr<'a>,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -578,9 +581,9 @@ pub struct RemoveObject {
pub type SetBackgroundColor = Color; pub type SetBackgroundColor = Color;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct SymbolClassLink { pub struct SymbolClassLink<'a> {
pub id: CharacterId, pub id: CharacterId,
pub class_name: String, pub class_name: SwfStr<'a>,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -605,11 +608,11 @@ pub struct Shape {
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Sound { pub struct Sound<'a> {
pub id: CharacterId, pub id: CharacterId,
pub format: SoundFormat, pub format: SoundFormat,
pub num_samples: u32, pub num_samples: u32,
pub data: Vec<u8>, pub data: &'a [u8],
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -644,10 +647,10 @@ pub struct StartSound {
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Sprite { pub struct Sprite<'a> {
pub id: CharacterId, pub id: CharacterId,
pub num_frames: u16, pub num_frames: u16,
pub tags: Vec<Tag>, pub tags: Vec<Tag<'a>>,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -797,14 +800,14 @@ pub struct SoundStreamHead {
pub latency_seek: i16, pub latency_seek: i16,
} }
pub type SoundStreamBlock = Vec<u8>; pub type SoundStreamBlock<'a> = &'a [u8];
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Button { pub struct Button<'a> {
pub id: CharacterId, pub id: CharacterId,
pub is_track_as_menu: bool, pub is_track_as_menu: bool,
pub records: Vec<ButtonRecord>, pub records: Vec<ButtonRecord>,
pub actions: Vec<ButtonAction>, pub actions: Vec<ButtonAction<'a>>,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -844,10 +847,10 @@ pub struct ButtonSounds {
pub type ButtonSound = (CharacterId, SoundInfo); pub type ButtonSound = (CharacterId, SoundInfo);
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct ButtonAction { pub struct ButtonAction<'a> {
pub conditions: HashSet<ButtonActionCondition>, pub conditions: HashSet<ButtonActionCondition>,
pub key_code: Option<u8>, pub key_code: Option<u8>,
pub action_data: Vec<u8>, pub action_data: &'a [u8],
} }
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
@ -890,10 +893,10 @@ pub struct FontV1 {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Font { pub struct Font<'a> {
pub version: u8, pub version: u8,
pub id: CharacterId, pub id: CharacterId,
pub name: String, pub name: SwfStr<'a>,
pub language: Language, pub language: Language,
pub layout: Option<FontLayout>, pub layout: Option<FontLayout>,
pub glyphs: Vec<Glyph>, pub glyphs: Vec<Glyph>,
@ -905,12 +908,12 @@ pub struct Font {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Font4 { pub struct Font4<'a> {
pub id: CharacterId, pub id: CharacterId,
pub is_italic: bool, pub is_italic: bool,
pub is_bold: bool, pub is_bold: bool,
pub name: String, pub name: SwfStr<'a>,
pub data: Option<Vec<u8>>, pub data: Option<&'a [u8]>,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -937,10 +940,10 @@ pub struct KerningRecord {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct FontInfo { pub struct FontInfo<'a> {
pub id: CharacterId, pub id: CharacterId,
pub version: u8, pub version: u8,
pub name: String, pub name: SwfStr<'a>,
pub is_small_text: bool, pub is_small_text: bool,
pub is_shift_jis: bool, pub is_shift_jis: bool,
pub is_ansi: bool, pub is_ansi: bool,
@ -975,17 +978,17 @@ pub struct GlyphEntry {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct EditText { pub struct EditText<'a> {
pub id: CharacterId, pub id: CharacterId,
pub bounds: Rectangle, pub bounds: Rectangle,
pub font_id: Option<CharacterId>, // TODO(Herschel): Combine with height pub font_id: Option<CharacterId>, // TODO(Herschel): Combine with height
pub font_class_name: Option<String>, pub font_class_name: Option<SwfStr<'a>>,
pub height: Option<Twips>, pub height: Option<Twips>,
pub color: Option<Color>, pub color: Option<Color>,
pub max_length: Option<u16>, pub max_length: Option<u16>,
pub layout: Option<TextLayout>, pub layout: Option<TextLayout>,
pub variable_name: String, pub variable_name: SwfStr<'a>,
pub initial_text: Option<String>, pub initial_text: Option<SwfStr<'a>>,
pub is_word_wrap: bool, pub is_word_wrap: bool,
pub is_multiline: bool, pub is_multiline: bool,
pub is_password: bool, pub is_password: bool,
@ -1048,14 +1051,14 @@ pub enum TextGridFit {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct DefineBitsLossless { pub struct DefineBitsLossless<'a> {
pub version: u8, pub version: u8,
pub id: CharacterId, pub id: CharacterId,
pub format: BitmapFormat, pub format: BitmapFormat,
pub width: u16, pub width: u16,
pub height: u16, pub height: u16,
pub num_colors: u8, pub num_colors: u8,
pub data: Vec<u8>, pub data: &'a [u8],
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -1095,31 +1098,31 @@ pub enum VideoCodec {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct VideoFrame { pub struct VideoFrame<'a> {
pub stream_id: CharacterId, pub stream_id: CharacterId,
pub frame_num: u16, pub frame_num: u16,
pub data: Vec<u8>, pub data: &'a [u8],
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct DefineBitsJpeg3 { pub struct DefineBitsJpeg3<'a> {
pub id: CharacterId, pub id: CharacterId,
pub version: u8, pub version: u8,
pub deblocking: f32, pub deblocking: f32,
pub data: Vec<u8>, pub data: &'a [u8],
pub alpha_data: Vec<u8>, pub alpha_data: &'a [u8],
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct DoAbc { pub struct DoAbc<'a> {
pub name: String, pub name: SwfStr<'a>,
pub is_lazy_initialize: bool, pub is_lazy_initialize: bool,
pub data: Vec<u8>, pub data: &'a [u8],
} }
pub type DoAction = Vec<u8>; pub type DoAction<'a> = &'a [u8];
pub type JpegTables = Vec<u8>; pub type JpegTables<'a> = &'a [u8];
/// `ProductInfo` contains information about the software used to generate the SWF. /// `ProductInfo` contains information about the software used to generate the SWF.
/// Not documented in the SWF19 reference. Emitted by mxmlc. /// Not documented in the SWF19 reference. Emitted by mxmlc.

View File

@ -2802,7 +2802,7 @@ mod tests {
use super::*; use super::*;
use crate::test_data; use crate::test_data;
fn new_swf() -> Swf { fn new_swf() -> Swf<'static> {
Swf { Swf {
header: Header { header: Header {
compression: Compression::Zlib, compression: Compression::Zlib,
@ -3125,7 +3125,7 @@ mod tests {
.write_tag_list(&[ .write_tag_list(&[
Tag::Unknown { Tag::Unknown {
tag_code: 512, tag_code: 512,
data: vec![0; 100], data: &[0; 100],
}, },
Tag::ShowFrame, Tag::ShowFrame,
]) ])