Merge branch 'ruffle'

This commit is contained in:
Mike Welsh 2019-08-18 16:30:07 -07:00
commit e11a8d416e
6 changed files with 341 additions and 200 deletions

View File

@ -193,7 +193,7 @@ impl<R: Read> Reader<R> {
OpCode::With => { OpCode::With => {
let code_length = action_reader.read_u16()?; let code_length = action_reader.read_u16()?;
let mut with_reader = Reader::new( let mut with_reader = Reader::new(
(&mut action_reader.inner as &mut Read).take(code_length.into()), (&mut action_reader.inner as &mut dyn Read).take(code_length.into()),
self.version, self.version,
); );
Action::With { Action::With {
@ -258,7 +258,7 @@ impl<R: Read> Reader<R> {
} }
let code_length = self.read_u16()?; let code_length = self.read_u16()?;
let mut fn_reader = Reader::new( let mut fn_reader = Reader::new(
(&mut self.inner as &mut Read).take(code_length.into()), (&mut self.inner as &mut dyn Read).take(code_length.into()),
self.version, self.version,
); );
Ok(Action::DefineFunction { Ok(Action::DefineFunction {
@ -283,7 +283,7 @@ impl<R: Read> Reader<R> {
} }
let code_length = self.read_u16()?; let code_length = self.read_u16()?;
let mut fn_reader = Reader::new( let mut fn_reader = Reader::new(
(&mut self.inner as &mut Read).take(code_length.into()), (&mut self.inner as &mut dyn Read).take(code_length.into()),
self.version, self.version,
); );
Ok(Action::DefineFunction2(Function { Ok(Action::DefineFunction2(Function {
@ -314,21 +314,21 @@ impl<R: Read> Reader<R> {
}; };
let try_actions = { let try_actions = {
let mut fn_reader = Reader::new( let mut fn_reader = Reader::new(
(&mut self.inner as &mut Read).take(try_length.into()), (&mut self.inner as &mut dyn Read).take(try_length.into()),
self.version, self.version,
); );
fn_reader.read_action_list()? fn_reader.read_action_list()?
}; };
let catch_actions = { let catch_actions = {
let mut fn_reader = Reader::new( let mut fn_reader = Reader::new(
(&mut self.inner as &mut Read).take(catch_length.into()), (&mut self.inner as &mut dyn Read).take(catch_length.into()),
self.version, self.version,
); );
fn_reader.read_action_list()? fn_reader.read_action_list()?
}; };
let finally_actions = { let finally_actions = {
let mut fn_reader = Reader::new( let mut fn_reader = Reader::new(
(&mut self.inner as &mut Read).take(finally_length.into()), (&mut self.inner as &mut dyn Read).take(finally_length.into()),
self.version, self.version,
); );
fn_reader.read_action_list()? fn_reader.read_action_list()?

View File

@ -47,14 +47,16 @@ pub fn read_swf<R: Read>(input: R) -> Result<Swf> {
/// let (header, _reader) = swf::read_swf_header(&data[..]).unwrap(); /// let (header, _reader) = swf::read_swf_header(&data[..]).unwrap();
/// println!("FPS: {}", header.frame_rate); /// println!("FPS: {}", header.frame_rate);
/// ``` /// ```
pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result<(Header, Reader<Box<Read + 'a>>)> { pub fn read_swf_header<'a, R: Read + 'a>(
mut input: R,
) -> Result<(Header, Reader<Box<dyn Read + 'a>>)> {
// Read SWF header. // Read SWF header.
let compression = Reader::read_compression_type(&mut input)?; let compression = Reader::read_compression_type(&mut input)?;
let version = input.read_u8()?; let version = input.read_u8()?;
let _uncompressed_length = input.read_u32::<LittleEndian>()?; let _uncompressed_length = input.read_u32::<LittleEndian>()?;
// Now the SWF switches to a compressed stream. // Now the SWF switches to a compressed stream.
let decompressed_input: Box<Read> = match compression { let decompressed_input: Box<dyn Read> = match compression {
Compression::None => Box::new(input), Compression::None => Box::new(input),
Compression::Zlib => make_zlib_reader(input)?, Compression::Zlib => make_zlib_reader(input)?,
Compression::Lzma => make_lzma_reader(input)?, Compression::Lzma => make_lzma_reader(input)?,
@ -75,20 +77,20 @@ pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result<(Header, Reader
} }
#[cfg(feature = "flate2")] #[cfg(feature = "flate2")]
fn make_zlib_reader<'a, R: Read + 'a>(input: R) -> Result<Box<Read + 'a>> { fn make_zlib_reader<'a, R: Read + 'a>(input: R) -> Result<Box<dyn Read + 'a>> {
use flate2::read::ZlibDecoder; use flate2::read::ZlibDecoder;
Ok(Box::new(ZlibDecoder::new(input))) Ok(Box::new(ZlibDecoder::new(input)))
} }
#[cfg(feature = "libflate")] #[cfg(feature = "libflate")]
fn make_zlib_reader<'a, R: Read + 'a>(input: R) -> Result<Box<Read + 'a>> { fn make_zlib_reader<'a, R: Read + 'a>(input: R) -> Result<Box<dyn Read + 'a>> {
use libflate::zlib::Decoder; use libflate::zlib::Decoder;
let decoder = Decoder::new(input)?; let decoder = Decoder::new(input)?;
Ok(Box::new(decoder)) Ok(Box::new(decoder))
} }
#[cfg(not(any(feature = "flate2", feature = "libflate")))] #[cfg(not(any(feature = "flate2", feature = "libflate")))]
fn make_zlib_reader<'a, R: Read + 'a>(_input: R) -> Result<Box<Read + 'a>> { fn make_zlib_reader<'a, R: Read + 'a>(_input: R) -> Result<Box<dyn Read + 'a>> {
Err(Error::new( Err(Error::new(
ErrorKind::InvalidData, ErrorKind::InvalidData,
"Support for Zlib compressed SWFs is not enabled.", "Support for Zlib compressed SWFs is not enabled.",
@ -96,7 +98,7 @@ fn make_zlib_reader<'a, R: Read + 'a>(_input: R) -> Result<Box<Read + 'a>> {
} }
#[cfg(feature = "lzma-support")] #[cfg(feature = "lzma-support")]
fn make_lzma_reader<'a, R: Read + 'a>(mut input: R) -> Result<Box<Read + 'a>> { fn make_lzma_reader<'a, R: Read + 'a>(mut input: R) -> Result<Box<dyn Read + 'a>> {
// Flash uses a mangled LZMA header, so we have to massage it into the normal // Flash uses a mangled LZMA header, so we have to massage it into the normal
// format. // format.
use byteorder::WriteBytesExt; use byteorder::WriteBytesExt;
@ -114,7 +116,7 @@ fn make_lzma_reader<'a, R: Read + 'a>(mut input: R) -> Result<Box<Read + 'a>> {
} }
#[cfg(not(feature = "lzma-support"))] #[cfg(not(feature = "lzma-support"))]
fn make_lzma_reader<'a, R: Read + 'a>(_input: R) -> Result<Box<Read + 'a>> { fn make_lzma_reader<'a, R: Read + 'a>(_input: R) -> Result<Box<dyn Read + 'a>> {
Err(Error::new( Err(Error::new(
ErrorKind::InvalidData, ErrorKind::InvalidData,
"Support for LZMA compressed SWFs is not enabled.", "Support for LZMA compressed SWFs is not enabled.",
@ -136,6 +138,10 @@ pub trait SwfRead<R: Read> {
self.get_inner().read_u32::<LittleEndian>() self.get_inner().read_u32::<LittleEndian>()
} }
fn read_u64(&mut self) -> Result<u64> {
self.get_inner().read_u64::<LittleEndian>()
}
fn read_i8(&mut self) -> Result<i8> { fn read_i8(&mut self) -> Result<i8> {
self.get_inner().read_i8() self.get_inner().read_i8()
} }
@ -279,8 +285,6 @@ impl<R: Read> Reader<R> {
/// } /// }
/// ``` /// ```
pub fn read_tag(&mut self) -> Result<Tag> { pub fn read_tag(&mut self) -> Result<Tag> {
use num_traits::FromPrimitive;
let (tag_code, length) = self.read_tag_code_and_length()?; let (tag_code, length) = self.read_tag_code_and_length()?;
let mut tag_reader = Reader::new(self.input.by_ref().take(length as u64), self.version); let mut tag_reader = Reader::new(self.input.by_ref().take(length as u64), self.version);
@ -310,8 +314,12 @@ impl<R: Read> Reader<R> {
} }
Some(TagCode::DefineBitsJpeg3) => tag_reader.read_define_bits_jpeg_3(3)?, Some(TagCode::DefineBitsJpeg3) => tag_reader.read_define_bits_jpeg_3(3)?,
Some(TagCode::DefineBitsJpeg4) => tag_reader.read_define_bits_jpeg_3(4)?, Some(TagCode::DefineBitsJpeg4) => tag_reader.read_define_bits_jpeg_3(4)?,
Some(TagCode::DefineButton) => tag_reader.read_define_button()?, Some(TagCode::DefineButton) => {
Some(TagCode::DefineButton2) => tag_reader.read_define_button_2()?, Tag::DefineButton(Box::new(tag_reader.read_define_button_1()?))
}
Some(TagCode::DefineButton2) => {
Tag::DefineButton2(Box::new(tag_reader.read_define_button_2()?))
}
Some(TagCode::DefineButtonCxform) => { Some(TagCode::DefineButtonCxform) => {
let id = tag_reader.read_u16()?; let id = tag_reader.read_u16()?;
// SWF19 is incorrect here. It seems you can have many color transforms in this // SWF19 is incorrect here. It seems you can have many color transforms in this
@ -365,22 +373,34 @@ impl<R: Read> Reader<R> {
})) }))
} }
Some(TagCode::DefineEditText) => tag_reader.read_define_edit_text()?, Some(TagCode::DefineEditText) => tag_reader.read_define_edit_text()?,
Some(TagCode::DefineFont) => tag_reader.read_define_font()?, Some(TagCode::DefineFont) => {
Some(TagCode::DefineFont2) => tag_reader.read_define_font_2(2)?, Tag::DefineFont(Box::new(tag_reader.read_define_font_1()?))
Some(TagCode::DefineFont3) => tag_reader.read_define_font_2(3)?, }
Some(TagCode::DefineFont4) => tag_reader.read_define_font_4()?, Some(TagCode::DefineFont2) => {
Tag::DefineFont2(Box::new(tag_reader.read_define_font_2(2)?))
}
Some(TagCode::DefineFont3) => {
Tag::DefineFont2(Box::new(tag_reader.read_define_font_2(3)?))
}
Some(TagCode::DefineFont4) => Tag::DefineFont4(tag_reader.read_define_font_4()?),
Some(TagCode::DefineFontAlignZones) => tag_reader.read_define_font_align_zones()?, Some(TagCode::DefineFontAlignZones) => tag_reader.read_define_font_align_zones()?,
Some(TagCode::DefineFontInfo) => tag_reader.read_define_font_info(1)?, Some(TagCode::DefineFontInfo) => tag_reader.read_define_font_info(1)?,
Some(TagCode::DefineFontInfo2) => tag_reader.read_define_font_info(2)?, Some(TagCode::DefineFontInfo2) => tag_reader.read_define_font_info(2)?,
Some(TagCode::DefineFontName) => tag_reader.read_define_font_name()?, Some(TagCode::DefineFontName) => tag_reader.read_define_font_name()?,
Some(TagCode::DefineMorphShape) => tag_reader.read_define_morph_shape(1)?, Some(TagCode::DefineMorphShape) => {
Some(TagCode::DefineMorphShape2) => tag_reader.read_define_morph_shape(2)?, Tag::DefineMorphShape(Box::new(tag_reader.read_define_morph_shape(1)?))
Some(TagCode::DefineShape) => tag_reader.read_define_shape(1)?, }
Some(TagCode::DefineShape2) => tag_reader.read_define_shape(2)?, Some(TagCode::DefineMorphShape2) => {
Some(TagCode::DefineShape3) => tag_reader.read_define_shape(3)?, Tag::DefineMorphShape(Box::new(tag_reader.read_define_morph_shape(2)?))
Some(TagCode::DefineShape4) => tag_reader.read_define_shape(4)?, }
Some(TagCode::DefineSound) => tag_reader.read_define_sound()?, Some(TagCode::DefineShape) => Tag::DefineShape(tag_reader.read_define_shape(1)?),
Some(TagCode::DefineText) => tag_reader.read_define_text()?, Some(TagCode::DefineShape2) => Tag::DefineShape(tag_reader.read_define_shape(2)?),
Some(TagCode::DefineShape3) => Tag::DefineShape(tag_reader.read_define_shape(3)?),
Some(TagCode::DefineShape4) => Tag::DefineShape(tag_reader.read_define_shape(4)?),
Some(TagCode::DefineSound) => {
Tag::DefineSound(Box::new(tag_reader.read_define_sound()?))
}
Some(TagCode::DefineText) => Tag::DefineText(Box::new(tag_reader.read_define_text()?)),
Some(TagCode::DefineVideoStream) => tag_reader.read_define_video_stream()?, Some(TagCode::DefineVideoStream) => tag_reader.read_define_video_stream()?,
Some(TagCode::EnableTelemetry) => { Some(TagCode::EnableTelemetry) => {
tag_reader.read_u16()?; // Reserved tag_reader.read_u16()?; // Reserved
@ -438,25 +458,28 @@ impl<R: Read> Reader<R> {
Some(TagCode::SoundStreamHead) => Tag::SoundStreamHead( Some(TagCode::SoundStreamHead) => Tag::SoundStreamHead(
// TODO: Disallow certain compressions. // TODO: Disallow certain compressions.
Box::new(tag_reader.read_sound_stream_info()?), Box::new(tag_reader.read_sound_stream_head()?),
), ),
Some(TagCode::SoundStreamHead2) => { Some(TagCode::SoundStreamHead2) => {
Tag::SoundStreamHead2(Box::new(tag_reader.read_sound_stream_info()?)) Tag::SoundStreamHead2(Box::new(tag_reader.read_sound_stream_head()?))
} }
Some(TagCode::StartSound) => Tag::StartSound { Some(TagCode::StartSound) => Tag::StartSound(tag_reader.read_start_sound_1()?),
id: tag_reader.read_u16()?,
sound_info: Box::new(tag_reader.read_sound_info()?),
},
Some(TagCode::StartSound2) => Tag::StartSound2 { Some(TagCode::StartSound2) => Tag::StartSound2 {
class_name: tag_reader.read_c_string()?, class_name: tag_reader.read_c_string()?,
sound_info: Box::new(tag_reader.read_sound_info()?), sound_info: Box::new(tag_reader.read_sound_info()?),
}, },
Some(TagCode::DefineBitsLossless) => tag_reader.read_define_bits_lossless(1)?, Some(TagCode::DebugId) => Tag::DebugId(tag_reader.read_debug_id()?),
Some(TagCode::DefineBitsLossless2) => tag_reader.read_define_bits_lossless(2)?,
Some(TagCode::DefineBitsLossless) => {
Tag::DefineBitsLossless(tag_reader.read_define_bits_lossless(1)?)
}
Some(TagCode::DefineBitsLossless2) => {
Tag::DefineBitsLossless(tag_reader.read_define_bits_lossless(2)?)
}
Some(TagCode::DefineScalingGrid) => Tag::DefineScalingGrid { Some(TagCode::DefineScalingGrid) => Tag::DefineScalingGrid {
id: tag_reader.read_u16()?, id: tag_reader.read_u16()?,
@ -548,46 +571,40 @@ impl<R: Read> Reader<R> {
}) })
} }
Some(TagCode::DefineSceneAndFrameLabelData) => { Some(TagCode::DefineSceneAndFrameLabelData) => Tag::DefineSceneAndFrameLabelData(
tag_reader.read_define_scene_and_frame_label_data()? tag_reader.read_define_scene_and_frame_label_data()?,
} ),
Some(TagCode::FrameLabel) => { Some(TagCode::FrameLabel) => Tag::FrameLabel(tag_reader.read_frame_label(length)?),
let label = tag_reader.read_c_string()?;
Tag::FrameLabel {
is_anchor: tag_reader.version >= 6
&& length > label.len() + 1
&& tag_reader.read_u8()? != 0,
label,
}
}
Some(TagCode::DefineSprite) => { Some(TagCode::DefineSprite) => {
// TODO: There's probably a better way to prevent the infinite type recursion. // TODO: There's probably a better way to prevent the infinite type recursion.
// Tags can only be nested one level deep, so perhaps I can implement // Tags can only be nested one level deep, so perhaps I can implement
// read_tag_list for Reader<Take<R>> to enforce this. // read_tag_list for Reader<Take<R>> to enforce this.
let mut sprite_reader = let mut sprite_reader =
Reader::new(&mut tag_reader.input as &mut Read, self.version); Reader::new(&mut tag_reader.input as &mut dyn Read, self.version);
sprite_reader.read_define_sprite()? sprite_reader.read_define_sprite()?
} }
Some(TagCode::PlaceObject) => tag_reader.read_place_object()?, Some(TagCode::PlaceObject) => {
Some(TagCode::PlaceObject2) => tag_reader.read_place_object_2_or_3(2)?, Tag::PlaceObject(Box::new(tag_reader.read_place_object(length)?))
Some(TagCode::PlaceObject3) => tag_reader.read_place_object_2_or_3(3)?, }
Some(TagCode::PlaceObject4) => tag_reader.read_place_object_2_or_3(4)?, Some(TagCode::PlaceObject2) => {
Tag::PlaceObject(Box::new(tag_reader.read_place_object_2_or_3(2)?))
}
Some(TagCode::PlaceObject3) => {
Tag::PlaceObject(Box::new(tag_reader.read_place_object_2_or_3(3)?))
}
Some(TagCode::PlaceObject4) => {
Tag::PlaceObject(Box::new(tag_reader.read_place_object_2_or_3(4)?))
}
Some(TagCode::RemoveObject) => Tag::RemoveObject { Some(TagCode::RemoveObject) => Tag::RemoveObject(tag_reader.read_remove_object_1()?),
character_id: Some(tag_reader.read_u16()?),
depth: tag_reader.read_i16()?,
},
Some(TagCode::RemoveObject2) => Tag::RemoveObject { Some(TagCode::RemoveObject2) => Tag::RemoveObject(tag_reader.read_remove_object_2()?),
depth: tag_reader.read_i16()?,
character_id: None,
},
Some(TagCode::VideoFrame) => tag_reader.read_video_frame()?, Some(TagCode::VideoFrame) => tag_reader.read_video_frame()?,
Some(TagCode::ProductInfo) => Tag::ProductInfo(tag_reader.read_product_info()?),
_ => { _ => {
let size = length as usize; let size = length as usize;
let mut data = vec![0; size]; let mut data = vec![0; size];
@ -605,7 +622,7 @@ impl<R: Read> Reader<R> {
Ok(tag) Ok(tag)
} }
fn read_compression_type(mut input: R) -> Result<Compression> { pub fn read_compression_type(mut input: R) -> Result<Compression> {
let mut signature = [0u8; 3]; let mut signature = [0u8; 3];
input.read_exact(&mut signature)?; input.read_exact(&mut signature)?;
let compression = match &signature { let compression = match &signature {
@ -617,7 +634,7 @@ impl<R: Read> Reader<R> {
Ok(compression) Ok(compression)
} }
fn read_rectangle(&mut self) -> Result<Rectangle> { pub fn read_rectangle(&mut self) -> Result<Rectangle> {
self.byte_align(); self.byte_align();
let num_bits = self.read_ubits(5)? as usize; let num_bits = self.read_ubits(5)? as usize;
Ok(Rectangle { Ok(Rectangle {
@ -628,7 +645,7 @@ impl<R: Read> Reader<R> {
}) })
} }
fn read_bit(&mut self) -> Result<bool> { pub fn read_bit(&mut self) -> Result<bool> {
if self.bit_index == 0 { if self.bit_index == 0 {
self.byte = self.input.read_u8()?; self.byte = self.input.read_u8()?;
self.bit_index = 8; self.bit_index = 8;
@ -638,11 +655,11 @@ impl<R: Read> Reader<R> {
Ok(val) Ok(val)
} }
fn byte_align(&mut self) { pub fn byte_align(&mut self) {
self.bit_index = 0; self.bit_index = 0;
} }
fn read_ubits(&mut self, num_bits: usize) -> Result<u32> { pub fn read_ubits(&mut self, num_bits: usize) -> Result<u32> {
let mut val = 0u32; let mut val = 0u32;
for _ in 0..num_bits { for _ in 0..num_bits {
val <<= 1; val <<= 1;
@ -651,7 +668,7 @@ impl<R: Read> Reader<R> {
Ok(val) Ok(val)
} }
fn read_sbits(&mut self, num_bits: usize) -> Result<i32> { pub fn read_sbits(&mut self, num_bits: usize) -> Result<i32> {
if num_bits > 0 { if num_bits > 0 {
self.read_ubits(num_bits) self.read_ubits(num_bits)
.map(|n| (n as i32) << (32 - num_bits) >> (32 - num_bits)) .map(|n| (n as i32) << (32 - num_bits) >> (32 - num_bits))
@ -660,15 +677,15 @@ impl<R: Read> Reader<R> {
} }
} }
fn read_sbits_twips(&mut self, num_bits: usize) -> Result<Twips> { pub fn read_sbits_twips(&mut self, num_bits: usize) -> Result<Twips> {
self.read_sbits(num_bits).map(Twips::new) self.read_sbits(num_bits).map(Twips::new)
} }
fn read_fbits(&mut self, num_bits: usize) -> Result<f32> { pub fn read_fbits(&mut self, num_bits: usize) -> Result<f32> {
self.read_sbits(num_bits).map(|n| (n as f32) / 65536f32) self.read_sbits(num_bits).map(|n| (n as f32) / 65536f32)
} }
fn read_encoded_u32(&mut self) -> Result<u32> { pub fn read_encoded_u32(&mut self) -> Result<u32> {
let mut val = 0u32; let mut val = 0u32;
for i in 0..5 { for i in 0..5 {
let byte = self.read_u8()?; let byte = self.read_u8()?;
@ -680,18 +697,18 @@ impl<R: Read> Reader<R> {
Ok(val) Ok(val)
} }
fn read_character_id(&mut self) -> Result<CharacterId> { pub fn read_character_id(&mut self) -> Result<CharacterId> {
self.read_u16() self.read_u16()
} }
fn read_rgb(&mut self) -> Result<Color> { pub fn read_rgb(&mut self) -> Result<Color> {
let r = self.read_u8()?; let r = self.read_u8()?;
let g = self.read_u8()?; let g = self.read_u8()?;
let b = self.read_u8()?; let b = self.read_u8()?;
Ok(Color { r, g, b, a: 255 }) Ok(Color { r, g, b, a: 255 })
} }
fn read_rgba(&mut self) -> Result<Color> { pub fn read_rgba(&mut self) -> Result<Color> {
let r = self.read_u8()?; let r = self.read_u8()?;
let g = self.read_u8()?; let g = self.read_u8()?;
let b = self.read_u8()?; let b = self.read_u8()?;
@ -699,7 +716,7 @@ impl<R: Read> Reader<R> {
Ok(Color { r, g, b, a }) Ok(Color { r, g, b, a })
} }
fn read_color_transform_no_alpha(&mut self) -> Result<ColorTransform> { pub fn read_color_transform_no_alpha(&mut self) -> Result<ColorTransform> {
self.byte_align(); self.byte_align();
let has_add = self.read_bit()?; let has_add = self.read_bit()?;
let has_mult = self.read_bit()?; let has_mult = self.read_bit()?;
@ -822,7 +839,7 @@ impl<R: Read> Reader<R> {
Ok((tag_code, length)) Ok((tag_code, length))
} }
fn read_define_button(&mut self) -> Result<Tag> { pub fn read_define_button_1(&mut self) -> Result<Button> {
let id = self.read_u16()?; let id = self.read_u16()?;
let mut records = Vec::new(); let mut records = Vec::new();
while let Some(record) = self.read_button_record(1)? { while let Some(record) = self.read_button_record(1)? {
@ -830,7 +847,7 @@ impl<R: Read> Reader<R> {
} }
let mut action_data = Vec::new(); let mut action_data = Vec::new();
self.input.read_to_end(&mut action_data)?; self.input.read_to_end(&mut action_data)?;
Ok(Tag::DefineButton(Box::new(Button { Ok(Button {
id, id,
is_track_as_menu: false, is_track_as_menu: false,
records, records,
@ -841,10 +858,10 @@ impl<R: Read> Reader<R> {
key_code: None, key_code: None,
action_data: action_data, action_data: action_data,
}], }],
}))) })
} }
fn read_define_button_2(&mut self) -> Result<Tag> { pub fn read_define_button_2(&mut self) -> Result<Button> {
let id = self.read_u16()?; let id = self.read_u16()?;
let flags = self.read_u8()?; let flags = self.read_u8()?;
let is_track_as_menu = (flags & 0b1) != 0; let is_track_as_menu = (flags & 0b1) != 0;
@ -866,12 +883,12 @@ impl<R: Read> Reader<R> {
} }
} }
Ok(Tag::DefineButton2(Box::new(Button { Ok(Button {
id, id,
is_track_as_menu, is_track_as_menu,
records, records,
actions, actions,
}))) })
} }
fn read_button_record(&mut self, version: u8) -> Result<Option<ButtonRecord>> { fn read_button_record(&mut self, version: u8) -> Result<Option<ButtonRecord>> {
@ -1001,11 +1018,21 @@ impl<R: Read> Reader<R> {
})) }))
} }
fn read_define_scene_and_frame_label_data(&mut self) -> Result<Tag> { pub fn read_frame_label(&mut self, length: usize) -> Result<FrameLabel> {
let label = self.read_c_string()?;
Ok(FrameLabel {
is_anchor: self.version >= 6 && length > label.len() + 1 && self.read_u8()? != 0,
label,
})
}
pub fn read_define_scene_and_frame_label_data(
&mut self,
) -> Result<DefineSceneAndFrameLabelData> {
let num_scenes = self.read_encoded_u32()? as usize; let num_scenes = self.read_encoded_u32()? as usize;
let mut scenes = Vec::with_capacity(num_scenes); let mut scenes = Vec::with_capacity(num_scenes);
for _ in 0..num_scenes { for _ in 0..num_scenes {
scenes.push(FrameLabel { scenes.push(FrameLabelData {
frame_num: self.read_encoded_u32()?, frame_num: self.read_encoded_u32()?,
label: self.read_c_string()?, label: self.read_c_string()?,
}); });
@ -1014,19 +1041,19 @@ impl<R: Read> Reader<R> {
let num_frame_labels = self.read_encoded_u32()? as usize; let num_frame_labels = self.read_encoded_u32()? as usize;
let mut frame_labels = Vec::with_capacity(num_frame_labels); let mut frame_labels = Vec::with_capacity(num_frame_labels);
for _ in 0..num_frame_labels { for _ in 0..num_frame_labels {
frame_labels.push(FrameLabel { frame_labels.push(FrameLabelData {
frame_num: self.read_encoded_u32()?, frame_num: self.read_encoded_u32()?,
label: self.read_c_string()?, label: self.read_c_string()?,
}); });
} }
Ok(Tag::DefineSceneAndFrameLabelData { Ok(DefineSceneAndFrameLabelData {
scenes, scenes,
frame_labels, frame_labels,
}) })
} }
fn read_define_font(&mut self) -> Result<Tag> { pub fn read_define_font_1(&mut self) -> Result<FontV1> {
let id = self.read_u16()?; let id = self.read_u16()?;
let num_glyphs = self.read_u16()? / 2; let num_glyphs = self.read_u16()? / 2;
@ -1048,10 +1075,10 @@ impl<R: Read> Reader<R> {
} }
} }
Ok(Tag::DefineFont(Box::new(FontV1 { id, glyphs }))) Ok(FontV1 { id, glyphs })
} }
fn read_define_font_2(&mut self, version: u8) -> Result<Tag> { pub fn read_define_font_2(&mut self, version: u8) -> Result<Font> {
let id = self.read_character_id()?; let id = self.read_character_id()?;
let flags = self.read_u8()?; let flags = self.read_u8()?;
@ -1149,7 +1176,7 @@ impl<R: Read> Reader<R> {
None None
}; };
Ok(Tag::DefineFont2(Box::new(Font { Ok(Font {
version, version,
id, id,
name, name,
@ -1161,10 +1188,10 @@ impl<R: Read> Reader<R> {
is_ansi, is_ansi,
is_bold, is_bold,
is_italic, is_italic,
}))) })
} }
fn read_define_font_4(&mut self) -> Result<Tag> { pub fn read_define_font_4(&mut self) -> Result<Font4> {
let id = self.read_character_id()?; let id = self.read_character_id()?;
let flags = self.read_u8()?; let flags = self.read_u8()?;
let name = self.read_c_string()?; let name = self.read_c_string()?;
@ -1176,13 +1203,13 @@ impl<R: Read> Reader<R> {
} else { } else {
None None
}; };
Ok(Tag::DefineFont4(Font4 { Ok(Font4 {
id, id,
is_italic: flags & 0b10 != 0, is_italic: flags & 0b10 != 0,
is_bold: flags & 0b1 != 0, is_bold: flags & 0b1 != 0,
name, name,
data, data,
})) })
} }
fn read_kerning_record(&mut self, has_wide_codes: bool) -> Result<KerningRecord> { fn read_kerning_record(&mut self, has_wide_codes: bool) -> Result<KerningRecord> {
@ -1290,7 +1317,7 @@ impl<R: Read> Reader<R> {
}) })
} }
fn read_define_morph_shape(&mut self, shape_version: u8) -> Result<Tag> { pub fn read_define_morph_shape(&mut self, shape_version: u8) -> Result<DefineMorphShape> {
let id = self.read_character_id()?; let id = self.read_character_id()?;
let start_shape_bounds = self.read_rectangle()?; let start_shape_bounds = self.read_rectangle()?;
let end_shape_bounds = self.read_rectangle()?; let end_shape_bounds = self.read_rectangle()?;
@ -1354,7 +1381,7 @@ impl<R: Read> Reader<R> {
while let Some(record) = self.read_shape_record(1)? { while let Some(record) = self.read_shape_record(1)? {
end_shape.push(record); end_shape.push(record);
} }
Ok(Tag::DefineMorphShape(Box::new(DefineMorphShape { Ok(DefineMorphShape {
id, id,
version: shape_version, version: shape_version,
has_non_scaling_strokes, has_non_scaling_strokes,
@ -1373,7 +1400,7 @@ impl<R: Read> Reader<R> {
fill_styles: end_fill_styles, fill_styles: end_fill_styles,
line_styles: end_line_styles, line_styles: end_line_styles,
}, },
}))) })
} }
fn read_morph_line_style(&mut self, shape_version: u8) -> Result<(LineStyle, LineStyle)> { fn read_morph_line_style(&mut self, shape_version: u8) -> Result<(LineStyle, LineStyle)> {
@ -1518,7 +1545,7 @@ impl<R: Read> Reader<R> {
) )
} }
0x40...0x43 => { 0x40..=0x43 => {
let id = self.read_character_id()?; let id = self.read_character_id()?;
( (
FillStyle::Bitmap { FillStyle::Bitmap {
@ -1573,7 +1600,7 @@ impl<R: Read> Reader<R> {
)) ))
} }
fn read_define_shape(&mut self, version: u8) -> Result<Tag> { pub fn read_define_shape(&mut self, version: u8) -> Result<Shape> {
let id = self.read_u16()?; let id = self.read_u16()?;
let shape_bounds = self.read_rectangle()?; let shape_bounds = self.read_rectangle()?;
let (edge_bounds, has_fill_winding_rule, has_non_scaling_strokes, has_scaling_strokes) = let (edge_bounds, has_fill_winding_rule, has_non_scaling_strokes, has_scaling_strokes) =
@ -1594,7 +1621,7 @@ impl<R: Read> Reader<R> {
while let Some(record) = self.read_shape_record(version)? { while let Some(record) = self.read_shape_record(version)? {
records.push(record); records.push(record);
} }
Ok(Tag::DefineShape(Shape { Ok(Shape {
version, version,
id, id,
shape_bounds, shape_bounds,
@ -1604,24 +1631,24 @@ impl<R: Read> Reader<R> {
has_scaling_strokes, has_scaling_strokes,
styles, styles,
shape: records, shape: records,
})) })
} }
fn read_define_sound(&mut self) -> Result<Tag> { pub fn read_define_sound(&mut self) -> Result<Sound> {
let id = self.read_u16()?; let id = self.read_u16()?;
let format = self.read_sound_format()?; let format = self.read_sound_format()?;
let num_samples = self.read_u32()?; let num_samples = self.read_u32()?;
let mut data = Vec::new(); let mut data = Vec::new();
self.input.read_to_end(&mut data)?; self.input.read_to_end(&mut data)?;
Ok(Tag::DefineSound(Box::new(Sound { Ok(Sound {
id, id,
format, format,
num_samples, num_samples,
data, data,
}))) })
} }
fn read_sound_stream_info(&mut self) -> Result<SoundStreamInfo> { pub fn read_sound_stream_head(&mut self) -> Result<SoundStreamHead> {
// TODO: Verify version requirements. // TODO: Verify version requirements.
let playback_format = self.read_sound_format()?; let playback_format = self.read_sound_format()?;
let stream_format = self.read_sound_format()?; let stream_format = self.read_sound_format()?;
@ -1632,7 +1659,7 @@ impl<R: Read> Reader<R> {
} else { } else {
0 0
}; };
Ok(SoundStreamInfo { Ok(SoundStreamHead {
stream_format, stream_format,
playback_format, playback_format,
num_samples_per_block, num_samples_per_block,
@ -1698,7 +1725,7 @@ impl<R: Read> Reader<R> {
} }
} }
0x40...0x43 => FillStyle::Bitmap { 0x40..=0x43 => FillStyle::Bitmap {
id: self.read_u16()?, id: self.read_u16()?,
matrix: self.read_matrix()?, matrix: self.read_matrix()?,
is_smoothed: (fill_style_type & 0b10) == 0, is_smoothed: (fill_style_type & 0b10) == 0,
@ -1895,7 +1922,7 @@ impl<R: Read> Reader<R> {
Ok(shape_record) Ok(shape_record)
} }
fn read_define_sprite(&mut self) -> Result<Tag> { pub fn read_define_sprite(&mut self) -> Result<Tag> {
Ok(Tag::DefineSprite(Sprite { Ok(Tag::DefineSprite(Sprite {
id: self.read_u16()?, id: self.read_u16()?,
num_frames: self.read_u16()?, num_frames: self.read_u16()?,
@ -1903,14 +1930,23 @@ impl<R: Read> Reader<R> {
})) }))
} }
fn read_place_object(&mut self) -> Result<Tag> { pub fn read_place_object(&mut self, tag_length: usize) -> Result<PlaceObject> {
// TODO: What's a best way to know if the tag has a color transform? // TODO: What's a best way to know if the tag has a color transform?
Ok(Tag::PlaceObject(Box::new(PlaceObject { // You only know if there is still data remaining after the matrix.
// This sucks.
let mut vector = [0; 128];
self.get_mut().read_exact(&mut vector[..tag_length])?;
let mut reader = Reader::new(&vector[..], self.version);
Ok(PlaceObject {
version: 1, version: 1,
action: PlaceObjectAction::Place(self.read_u16()?), action: PlaceObjectAction::Place(reader.read_u16()?),
depth: self.read_i16()?, depth: reader.read_i16()?,
matrix: Some(self.read_matrix()?), matrix: Some(reader.read_matrix()?),
color_transform: self.read_color_transform_no_alpha().ok(), color_transform: if !reader.get_ref().is_empty() {
Some(reader.read_color_transform_no_alpha()?)
} else {
None
},
ratio: None, ratio: None,
name: None, name: None,
clip_depth: None, clip_depth: None,
@ -1923,10 +1959,10 @@ impl<R: Read> Reader<R> {
is_bitmap_cached: false, is_bitmap_cached: false,
is_visible: true, is_visible: true,
amf_data: None, amf_data: None,
}))) })
} }
fn read_place_object_2_or_3(&mut self, place_object_version: u8) -> Result<Tag> { pub fn read_place_object_2_or_3(&mut self, place_object_version: u8) -> Result<PlaceObject> {
let flags = if place_object_version >= 3 { let flags = if place_object_version >= 3 {
self.read_u16()? self.read_u16()?
} else { } else {
@ -2014,7 +2050,7 @@ impl<R: Read> Reader<R> {
} else { } else {
None None
}; };
Ok(Tag::PlaceObject(Box::new(PlaceObject { Ok(PlaceObject {
version: place_object_version, version: place_object_version,
action, action,
depth, depth,
@ -2032,10 +2068,24 @@ impl<R: Read> Reader<R> {
background_color, background_color,
blend_mode, blend_mode,
amf_data, amf_data,
}))) })
} }
fn read_blend_mode(&mut self) -> Result<BlendMode> { pub fn read_remove_object_1(&mut self) -> Result<RemoveObject> {
Ok(RemoveObject {
character_id: Some(self.read_u16()?),
depth: self.read_i16()?,
})
}
pub fn read_remove_object_2(&mut self) -> Result<RemoveObject> {
Ok(RemoveObject {
depth: self.read_i16()?,
character_id: None,
})
}
pub fn read_blend_mode(&mut self) -> Result<BlendMode> {
Ok(match self.read_u8()? { Ok(match self.read_u8()? {
0 | 1 => BlendMode::Normal, 0 | 1 => BlendMode::Normal,
2 => BlendMode::Layer, 2 => BlendMode::Layer,
@ -2164,7 +2214,7 @@ impl<R: Read> Reader<R> {
Ok(event_list) Ok(event_list)
} }
fn read_filter(&mut self) -> Result<Filter> { pub fn read_filter(&mut self) -> Result<Filter> {
self.byte_align(); self.byte_align();
let filter = match self.read_u8()? { let filter = match self.read_u8()? {
0 => Filter::DropShadowFilter(Box::new(DropShadowFilter { 0 => Filter::DropShadowFilter(Box::new(DropShadowFilter {
@ -2293,7 +2343,7 @@ impl<R: Read> Reader<R> {
Ok(filter) Ok(filter)
} }
fn read_sound_format(&mut self) -> Result<SoundFormat> { pub fn read_sound_format(&mut self) -> Result<SoundFormat> {
let flags = self.read_u8()?; let flags = self.read_u8()?;
let compression = match flags >> 4 { let compression = match flags >> 4 {
0 => AudioCompression::UncompressedUnknownEndian, 0 => AudioCompression::UncompressedUnknownEndian,
@ -2323,7 +2373,7 @@ impl<R: Read> Reader<R> {
}) })
} }
fn read_sound_info(&mut self) -> Result<SoundInfo> { pub fn read_sound_info(&mut self) -> Result<SoundInfo> {
let flags = self.read_u8()?; let flags = self.read_u8()?;
let event = match (flags >> 4) & 0b11 { let event = match (flags >> 4) & 0b11 {
0b10 | 0b11 => SoundEvent::Stop, 0b10 | 0b11 => SoundEvent::Stop,
@ -2369,7 +2419,14 @@ impl<R: Read> Reader<R> {
}) })
} }
fn read_define_text(&mut self) -> Result<Tag> { pub fn read_start_sound_1(&mut self) -> Result<StartSound> {
Ok(StartSound {
id: self.read_u16()?,
sound_info: Box::new(self.read_sound_info()?),
})
}
pub fn read_define_text(&mut self) -> Result<Text> {
let id = self.read_character_id()?; let id = self.read_character_id()?;
let bounds = self.read_rectangle()?; let bounds = self.read_rectangle()?;
let matrix = self.read_matrix()?; let matrix = self.read_matrix()?;
@ -2381,12 +2438,12 @@ impl<R: Read> Reader<R> {
records.push(record); records.push(record);
} }
Ok(Tag::DefineText(Box::new(Text { Ok(Text {
id, id,
bounds, bounds,
matrix, matrix,
records, records,
}))) })
} }
fn read_text_record( fn read_text_record(
@ -2600,7 +2657,7 @@ impl<R: Read> Reader<R> {
})) }))
} }
fn read_define_bits_lossless(&mut self, version: u8) -> Result<Tag> { pub fn read_define_bits_lossless(&mut self, version: u8) -> Result<DefineBitsLossless> {
let id = self.read_character_id()?; let id = self.read_character_id()?;
let format = match self.read_u8()? { let format = match self.read_u8()? {
3 => BitmapFormat::ColorMap8, 3 => BitmapFormat::ColorMap8,
@ -2617,7 +2674,7 @@ impl<R: Read> Reader<R> {
}; };
let mut data = Vec::new(); let mut data = Vec::new();
self.input.read_to_end(&mut data)?; self.input.read_to_end(&mut data)?;
Ok(Tag::DefineBitsLossless(DefineBitsLossless { Ok(DefineBitsLossless {
version, version,
id, id,
format, format,
@ -2625,7 +2682,28 @@ impl<R: Read> Reader<R> {
height, height,
num_colors, num_colors,
data, data,
})) })
}
pub fn read_product_info(&mut self) -> Result<ProductInfo> {
// Not documented in SWF19 reference.
// See http://wahlers.com.br/claus/blog/undocumented-swf-tags-written-by-mxmlc/
Ok(ProductInfo {
product_id: self.read_u32()?,
edition: self.read_u32()?,
major_version: self.read_u8()?,
minor_version: self.read_u8()?,
build_number: self.get_mut().read_u64::<LittleEndian>()?,
compilation_date: self.get_mut().read_u64::<LittleEndian>()?,
})
}
pub fn read_debug_id(&mut self) -> Result<DebugId> {
// Not documented in SWF19 reference.
// See http://wahlers.com.br/claus/blog/undocumented-swf-tags-written-by-mxmlc/
let mut debug_id = [0u8; 16];
self.get_mut().read_exact(&mut debug_id)?;
Ok(debug_id)
} }
} }

View File

@ -42,6 +42,8 @@ pub enum TagCode {
DefineSprite = 39, DefineSprite = 39,
ProductInfo = 41,
FrameLabel = 43, FrameLabel = 43,
SoundStreamHead2 = 45, SoundStreamHead2 = 45,
@ -57,6 +59,7 @@ pub enum TagCode {
VideoFrame = 61, VideoFrame = 61,
DefineFontInfo2 = 62, DefineFontInfo2 = 62,
DebugId = 63,
EnableDebugger2 = 64, EnableDebugger2 = 64,
ScriptLimits = 65, ScriptLimits = 65,
SetTabIndex = 66, SetTabIndex = 66,
@ -87,3 +90,9 @@ pub enum TagCode {
EnableTelemetry = 93, EnableTelemetry = 93,
PlaceObject4 = 94, PlaceObject4 = 94,
} }
impl TagCode {
pub fn from_u16(n: u16) -> Option<Self> {
num_traits::FromPrimitive::from_u16(n)
}
}

View File

@ -1190,40 +1190,40 @@ pub fn tag_tests() -> Vec<TagTestData> {
), ),
( (
1, // Minimum version not listed in SWF19. 1, // Minimum version not listed in SWF19.
Tag::DefineSceneAndFrameLabelData { Tag::DefineSceneAndFrameLabelData(DefineSceneAndFrameLabelData {
scenes: vec![ scenes: vec![
FrameLabel { FrameLabelData {
frame_num: 0, frame_num: 0,
label: "Scene 1".to_string(), label: "Scene 1".to_string(),
}, },
FrameLabel { FrameLabelData {
frame_num: 25, frame_num: 25,
label: "Scene2Scene2Scene2Scene2Scene2".to_string(), label: "Scene2Scene2Scene2Scene2Scene2".to_string(),
}, },
FrameLabel { FrameLabelData {
frame_num: 26, frame_num: 26,
label: "test日本語test".to_string(), label: "test日本語test".to_string(),
}, },
], ],
frame_labels: vec![ frame_labels: vec![
FrameLabel { FrameLabelData {
frame_num: 0, frame_num: 0,
label: "a".to_string(), label: "a".to_string(),
}, },
FrameLabel { FrameLabelData {
frame_num: 9, frame_num: 9,
label: "b".to_string(), label: "b".to_string(),
}, },
FrameLabel { FrameLabelData {
frame_num: 17, frame_num: 17,
label: "❤😁aaa".to_string(), label: "❤😁aaa".to_string(),
}, },
FrameLabel { FrameLabelData {
frame_num: 25, frame_num: 25,
label: "frameInScene2".to_string(), label: "frameInScene2".to_string(),
}, },
], ],
}, }),
read_tag_bytes_from_file( read_tag_bytes_from_file(
"tests/swfs/DefineSceneAndFrameLabelData.swf", "tests/swfs/DefineSceneAndFrameLabelData.swf",
TagCode::DefineSceneAndFrameLabelData, TagCode::DefineSceneAndFrameLabelData,
@ -1793,10 +1793,10 @@ pub fn tag_tests() -> Vec<TagTestData> {
), ),
( (
3, 3,
Tag::FrameLabel { Tag::FrameLabel(FrameLabel {
label: "test".to_string(), label: "test".to_string(),
is_anchor: false, is_anchor: false,
}, }),
read_tag_bytes_from_file_with_index( read_tag_bytes_from_file_with_index(
"tests/swfs/FrameLabel-CS6.swf", "tests/swfs/FrameLabel-CS6.swf",
TagCode::FrameLabel, TagCode::FrameLabel,
@ -1805,10 +1805,10 @@ 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 { Tag::FrameLabel(FrameLabel {
label: "anchor_tag".to_string(), label: "anchor_tag".to_string(),
is_anchor: true, is_anchor: true,
}, }),
read_tag_bytes_from_file_with_index( read_tag_bytes_from_file_with_index(
"tests/swfs/FrameLabel-CS6.swf", "tests/swfs/FrameLabel-CS6.swf",
TagCode::FrameLabel, TagCode::FrameLabel,
@ -2156,7 +2156,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
(1, Tag::ShowFrame, vec![0b01_000000, 0]), (1, Tag::ShowFrame, vec![0b01_000000, 0]),
( (
3, 3,
Tag::SoundStreamHead2(Box::new(SoundStreamInfo { Tag::SoundStreamHead2(Box::new(SoundStreamHead {
stream_format: SoundFormat { stream_format: SoundFormat {
compression: AudioCompression::Uncompressed, compression: AudioCompression::Uncompressed,
sample_rate: 5512, sample_rate: 5512,
@ -2190,7 +2190,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
), ),
( (
4, 4,
Tag::StartSound { Tag::StartSound(StartSound {
id: 1, id: 1,
sound_info: Box::new(SoundInfo { sound_info: Box::new(SoundInfo {
event: SoundEvent::Start, event: SoundEvent::Start,
@ -2199,7 +2199,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
num_loops: 3, num_loops: 3,
envelope: None, envelope: None,
}), }),
}, }),
read_tag_bytes_from_file("tests/swfs/DefineSound.swf", TagCode::StartSound), read_tag_bytes_from_file("tests/swfs/DefineSound.swf", TagCode::StartSound),
), ),
( (

View File

@ -236,6 +236,18 @@ pub struct FileAttributes {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct FrameLabel { pub struct FrameLabel {
pub label: String,
pub is_anchor: bool,
}
#[derive(Debug, PartialEq)]
pub struct DefineSceneAndFrameLabelData {
pub scenes: Vec<FrameLabelData>,
pub frame_labels: Vec<FrameLabelData>,
}
#[derive(Debug, PartialEq)]
pub struct FrameLabelData {
pub frame_num: u32, pub frame_num: u32,
pub label: String, pub label: String,
} }
@ -442,6 +454,7 @@ pub enum Tag {
Protect(Option<String>), Protect(Option<String>),
CsmTextSettings(CsmTextSettings), CsmTextSettings(CsmTextSettings),
DebugId(DebugId),
DefineBinaryData { DefineBinaryData {
id: CharacterId, id: CharacterId,
data: Vec<u8>, data: Vec<u8>,
@ -489,7 +502,7 @@ pub enum Tag {
DefineText(Box<Text>), DefineText(Box<Text>),
DefineVideoStream(DefineVideoStream), DefineVideoStream(DefineVideoStream),
DoAbc(DoAbc), DoAbc(DoAbc),
DoAction(Vec<u8>), DoAction(DoAction),
DoInitAction { DoInitAction {
id: CharacterId, id: CharacterId,
action_data: Vec<u8>, action_data: Vec<u8>,
@ -504,40 +517,30 @@ pub enum Tag {
url: String, url: String,
imports: Vec<ExportedAsset>, imports: Vec<ExportedAsset>,
}, },
JpegTables(Vec<u8>), JpegTables(JpegTables),
SetBackgroundColor(Color), SetBackgroundColor(SetBackgroundColor),
SetTabIndex { SetTabIndex {
depth: Depth, depth: Depth,
tab_index: u16, tab_index: u16,
}, },
SoundStreamBlock(Vec<u8>), SoundStreamBlock(SoundStreamBlock),
SoundStreamHead(Box<SoundStreamInfo>), SoundStreamHead(Box<SoundStreamHead>),
SoundStreamHead2(Box<SoundStreamInfo>), SoundStreamHead2(Box<SoundStreamHead>),
StartSound { StartSound(StartSound),
id: CharacterId,
sound_info: Box<SoundInfo>,
},
StartSound2 { StartSound2 {
class_name: String, class_name: String,
sound_info: Box<SoundInfo>, sound_info: Box<SoundInfo>,
}, },
SymbolClass(Vec<SymbolClassLink>), SymbolClass(Vec<SymbolClassLink>),
PlaceObject(Box<PlaceObject>), PlaceObject(Box<PlaceObject>),
RemoveObject { RemoveObject(RemoveObject),
depth: Depth,
character_id: Option<CharacterId>,
},
VideoFrame(VideoFrame), VideoFrame(VideoFrame),
FileAttributes(FileAttributes), FileAttributes(FileAttributes),
FrameLabel { FrameLabel(FrameLabel),
label: String, DefineSceneAndFrameLabelData(DefineSceneAndFrameLabelData),
is_anchor: bool,
}, ProductInfo(ProductInfo),
DefineSceneAndFrameLabelData {
scenes: Vec<FrameLabel>,
frame_labels: Vec<FrameLabel>,
},
Unknown { Unknown {
tag_code: u16, tag_code: u16,
@ -551,6 +554,14 @@ pub struct ExportedAsset {
pub name: String, pub name: String,
} }
#[derive(Debug, PartialEq, Clone)]
pub struct RemoveObject {
pub depth: Depth,
pub character_id: Option<CharacterId>,
}
pub type SetBackgroundColor = Color;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct SymbolClassLink { pub struct SymbolClassLink {
pub id: CharacterId, pub id: CharacterId,
@ -603,6 +614,12 @@ pub struct SoundEnvelopePoint {
pub right_volume: f32, pub right_volume: f32,
} }
#[derive(Clone, Debug, PartialEq)]
pub struct StartSound {
pub id: CharacterId,
pub sound_info: Box<SoundInfo>,
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Sprite { pub struct Sprite {
pub id: CharacterId, pub id: CharacterId,
@ -750,13 +767,15 @@ pub struct SoundFormat {
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct SoundStreamInfo { pub struct SoundStreamHead {
pub stream_format: SoundFormat, pub stream_format: SoundFormat,
pub playback_format: SoundFormat, pub playback_format: SoundFormat,
pub num_samples_per_block: u16, pub num_samples_per_block: u16,
pub latency_seek: i16, pub latency_seek: i16,
} }
pub type SoundStreamBlock = Vec<u8>;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Button { pub struct Button {
pub id: CharacterId, pub id: CharacterId,
@ -1068,3 +1087,23 @@ pub struct DoAbc {
pub is_lazy_initialize: bool, pub is_lazy_initialize: bool,
pub data: Vec<u8>, pub data: Vec<u8>,
} }
pub type DoAction = Vec<u8>;
pub type JpegTables = Vec<u8>;
/// `ProductInfo` contains information about the software used to generate the SWF.
/// Not documented in the SWF19 reference. Emitted by mxmlc.
/// See http://wahlers.com.br/claus/blog/undocumented-swf-tags-written-by-mxmlc/
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProductInfo {
pub product_id: u32,
pub edition: u32,
pub major_version: u8,
pub minor_version: u8,
pub build_number: u64,
pub compilation_date: u64,
}
/// `DebugId` is a UUID written to debug SWFs and used by the Flash Debugger.
pub type DebugId = [u8; 16];

View File

@ -142,6 +142,10 @@ pub trait SwfWrite<W: Write> {
self.get_inner().write_u32::<LittleEndian>(n) self.get_inner().write_u32::<LittleEndian>(n)
} }
fn write_u64(&mut self, n: u64) -> Result<()> {
self.get_inner().write_u64::<LittleEndian>(n)
}
fn write_i8(&mut self, n: i8) -> Result<()> { fn write_i8(&mut self, n: i8) -> Result<()> {
self.get_inner().write_i8(n) self.get_inner().write_i8(n)
} }
@ -891,7 +895,7 @@ impl<W: Write> Writer<W> {
// TODO: Allow clone of color. // TODO: Allow clone of color.
Tag::SetBackgroundColor(ref color) => { Tag::SetBackgroundColor(ref color) => {
self.write_tag_header(TagCode::SetBackgroundColor, 3)?; self.write_tag_header(TagCode::SetBackgroundColor, 3)?;
self.write_rgb(color)?; self.write_rgb(&color)?;
} }
Tag::ScriptLimits { Tag::ScriptLimits {
@ -922,17 +926,14 @@ impl<W: Write> Writer<W> {
} }
}, },
Tag::RemoveObject { Tag::RemoveObject(ref remove_object) => {
depth, if let Some(id) = remove_object.character_id {
character_id,
} => {
if let Some(id) = character_id {
self.write_tag_header(TagCode::RemoveObject, 4)?; self.write_tag_header(TagCode::RemoveObject, 4)?;
self.write_u16(id)?; self.write_u16(id)?;
} else { } else {
self.write_tag_header(TagCode::RemoveObject2, 2)?; self.write_tag_header(TagCode::RemoveObject2, 2)?;
} }
self.write_i16(depth)?; self.write_i16(remove_object.depth)?;
} }
Tag::SoundStreamBlock(ref data) => { Tag::SoundStreamBlock(ref data) => {
@ -940,15 +941,16 @@ impl<W: Write> Writer<W> {
self.output.write_all(data)?; self.output.write_all(data)?;
} }
Tag::SoundStreamHead(ref sound_stream_info) => { Tag::SoundStreamHead(ref sound_stream_head) => {
self.write_sound_stream_head(sound_stream_info, 1)?; self.write_sound_stream_head(sound_stream_head, 1)?;
} }
Tag::SoundStreamHead2(ref sound_stream_info) => { Tag::SoundStreamHead2(ref sound_stream_head) => {
self.write_sound_stream_head(sound_stream_info, 2)?; self.write_sound_stream_head(sound_stream_head, 2)?;
} }
Tag::StartSound { id, ref sound_info } => { Tag::StartSound(ref start_sound) => {
let sound_info = &start_sound.sound_info;
let length = 3 let length = 3
+ if sound_info.in_sample.is_some() { 4 } else { 0 } + if sound_info.in_sample.is_some() { 4 } else { 0 }
+ if sound_info.out_sample.is_some() { + if sound_info.out_sample.is_some() {
@ -963,7 +965,7 @@ impl<W: Write> Writer<W> {
0 0
}; };
self.write_tag_header(TagCode::StartSound, length)?; self.write_tag_header(TagCode::StartSound, length)?;
self.write_u16(id)?; self.write_u16(start_sound.id)?;
self.write_sound_info(sound_info)?; self.write_sound_info(sound_info)?;
} }
@ -1032,10 +1034,10 @@ impl<W: Write> Writer<W> {
self.write_u32(flags)?; self.write_u32(flags)?;
} }
Tag::FrameLabel { Tag::FrameLabel(FrameLabel {
ref label, ref label,
is_anchor, is_anchor,
} => { }) => {
// TODO: Assert proper version // TODO: Assert proper version
let is_anchor = is_anchor && self.version >= 6; let is_anchor = is_anchor && self.version >= 6;
let length = label.len() as u32 + if is_anchor { 2 } else { 1 }; let length = label.len() as u32 + if is_anchor { 2 } else { 1 };
@ -1046,10 +1048,9 @@ impl<W: Write> Writer<W> {
} }
} }
Tag::DefineSceneAndFrameLabelData { Tag::DefineSceneAndFrameLabelData(ref data) => self.write_define_scene_and_frame_label_data(data)?,
ref scenes, Tag::ProductInfo(ref product_info) => self.write_product_info(product_info)?,
ref frame_labels, Tag::DebugId(ref debug_id) => self.write_debug_id(debug_id)?,
} => self.write_define_scene_and_frame_label_data(scenes, frame_labels)?,
Tag::Unknown { tag_code, ref data } => { Tag::Unknown { tag_code, ref data } => {
self.write_tag_code_and_length(tag_code, data.len() as u32)?; self.write_tag_code_and_length(tag_code, data.len() as u32)?;
@ -1492,19 +1493,18 @@ impl<W: Write> Writer<W> {
fn write_define_scene_and_frame_label_data( fn write_define_scene_and_frame_label_data(
&mut self, &mut self,
scenes: &[FrameLabel], data: &DefineSceneAndFrameLabelData,
frame_labels: &[FrameLabel],
) -> Result<()> { ) -> Result<()> {
let mut buf = Vec::with_capacity((scenes.len() + frame_labels.len()) * 4); let mut buf = Vec::with_capacity((data.scenes.len() + data.frame_labels.len()) * 4);
{ {
let mut writer = Writer::new(&mut buf, self.version); let mut writer = Writer::new(&mut buf, self.version);
writer.write_encoded_u32(scenes.len() as u32)?; writer.write_encoded_u32(data.scenes.len() as u32)?;
for scene in scenes { for scene in &data.scenes {
writer.write_encoded_u32(scene.frame_num)?; writer.write_encoded_u32(scene.frame_num)?;
writer.write_c_string(&scene.label)?; writer.write_c_string(&scene.label)?;
} }
writer.write_encoded_u32(frame_labels.len() as u32)?; writer.write_encoded_u32(data.frame_labels.len() as u32)?;
for frame_label in frame_labels { for frame_label in &data.frame_labels {
writer.write_encoded_u32(frame_label.frame_num)?; writer.write_encoded_u32(frame_label.frame_num)?;
writer.write_c_string(&frame_label.label)?; writer.write_c_string(&frame_label.label)?;
} }
@ -2261,7 +2261,7 @@ impl<W: Write> Writer<W> {
fn write_sound_stream_head( fn write_sound_stream_head(
&mut self, &mut self,
stream_info: &SoundStreamInfo, stream_head: &SoundStreamHead,
version: u8, version: u8,
) -> Result<()> { ) -> Result<()> {
let tag_code = if version >= 2 { let tag_code = if version >= 2 {
@ -2270,17 +2270,17 @@ impl<W: Write> Writer<W> {
TagCode::SoundStreamHead TagCode::SoundStreamHead
}; };
// MP3 compression has added latency seek field. // MP3 compression has added latency seek field.
let length = if stream_info.stream_format.compression == AudioCompression::Mp3 { let length = if stream_head.stream_format.compression == AudioCompression::Mp3 {
6 6
} else { } else {
4 4
}; };
self.write_tag_header(tag_code, length)?; self.write_tag_header(tag_code, length)?;
self.write_sound_format(&stream_info.playback_format)?; self.write_sound_format(&stream_head.playback_format)?;
self.write_sound_format(&stream_info.stream_format)?; self.write_sound_format(&stream_head.stream_format)?;
self.write_u16(stream_info.num_samples_per_block)?; self.write_u16(stream_head.num_samples_per_block)?;
if stream_info.stream_format.compression == AudioCompression::Mp3 { if stream_head.stream_format.compression == AudioCompression::Mp3 {
self.write_i16(stream_info.latency_seek)?; self.write_i16(stream_head.latency_seek)?;
} }
Ok(()) Ok(())
} }
@ -2672,6 +2672,21 @@ impl<W: Write> Writer<W> {
Ok(()) Ok(())
} }
fn write_product_info(&mut self, product_info: &ProductInfo) -> Result<()> {
self.write_tag_header(TagCode::ProductInfo, 26)?;
self.write_u32(product_info.product_id)?;
self.write_u32(product_info.edition)?;
self.write_u8(product_info.major_version)?;
self.write_u8(product_info.minor_version)?;
self.write_u64(product_info.build_number)?;
self.write_u64(product_info.compilation_date)?;
Ok(())
}
fn write_debug_id(&mut self, debug_id: &DebugId) -> Result<()> {
self.get_inner().write_all(debug_id)
}
fn write_tag_header(&mut self, tag_code: TagCode, length: u32) -> Result<()> { fn write_tag_header(&mut self, tag_code: TagCode, length: u32) -> Result<()> {
self.write_tag_code_and_length(tag_code as u16, length) self.write_tag_code_and_length(tag_code as u16, length)
} }