Merge branch 'ruffle'
This commit is contained in:
commit
e11a8d416e
|
@ -193,7 +193,7 @@ impl<R: Read> Reader<R> {
|
|||
OpCode::With => {
|
||||
let code_length = action_reader.read_u16()?;
|
||||
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,
|
||||
);
|
||||
Action::With {
|
||||
|
@ -258,7 +258,7 @@ impl<R: Read> Reader<R> {
|
|||
}
|
||||
let code_length = self.read_u16()?;
|
||||
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,
|
||||
);
|
||||
Ok(Action::DefineFunction {
|
||||
|
@ -283,7 +283,7 @@ impl<R: Read> Reader<R> {
|
|||
}
|
||||
let code_length = self.read_u16()?;
|
||||
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,
|
||||
);
|
||||
Ok(Action::DefineFunction2(Function {
|
||||
|
@ -314,21 +314,21 @@ impl<R: Read> Reader<R> {
|
|||
};
|
||||
let try_actions = {
|
||||
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,
|
||||
);
|
||||
fn_reader.read_action_list()?
|
||||
};
|
||||
let catch_actions = {
|
||||
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,
|
||||
);
|
||||
fn_reader.read_action_list()?
|
||||
};
|
||||
let finally_actions = {
|
||||
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,
|
||||
);
|
||||
fn_reader.read_action_list()?
|
||||
|
|
322
src/read.rs
322
src/read.rs
|
@ -47,14 +47,16 @@ pub fn read_swf<R: Read>(input: R) -> Result<Swf> {
|
|||
/// let (header, _reader) = swf::read_swf_header(&data[..]).unwrap();
|
||||
/// 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.
|
||||
let compression = Reader::read_compression_type(&mut input)?;
|
||||
let version = input.read_u8()?;
|
||||
let _uncompressed_length = input.read_u32::<LittleEndian>()?;
|
||||
|
||||
// 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::Zlib => make_zlib_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")]
|
||||
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;
|
||||
Ok(Box::new(ZlibDecoder::new(input)))
|
||||
}
|
||||
|
||||
#[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;
|
||||
let decoder = Decoder::new(input)?;
|
||||
Ok(Box::new(decoder))
|
||||
}
|
||||
|
||||
#[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(
|
||||
ErrorKind::InvalidData,
|
||||
"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")]
|
||||
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
|
||||
// format.
|
||||
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"))]
|
||||
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(
|
||||
ErrorKind::InvalidData,
|
||||
"Support for LZMA compressed SWFs is not enabled.",
|
||||
|
@ -136,6 +138,10 @@ pub trait SwfRead<R: Read> {
|
|||
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> {
|
||||
self.get_inner().read_i8()
|
||||
}
|
||||
|
@ -279,8 +285,6 @@ impl<R: Read> Reader<R> {
|
|||
/// }
|
||||
/// ```
|
||||
pub fn read_tag(&mut self) -> Result<Tag> {
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
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);
|
||||
|
@ -310,8 +314,12 @@ impl<R: Read> Reader<R> {
|
|||
}
|
||||
Some(TagCode::DefineBitsJpeg3) => tag_reader.read_define_bits_jpeg_3(3)?,
|
||||
Some(TagCode::DefineBitsJpeg4) => tag_reader.read_define_bits_jpeg_3(4)?,
|
||||
Some(TagCode::DefineButton) => tag_reader.read_define_button()?,
|
||||
Some(TagCode::DefineButton2) => tag_reader.read_define_button_2()?,
|
||||
Some(TagCode::DefineButton) => {
|
||||
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) => {
|
||||
let id = tag_reader.read_u16()?;
|
||||
// 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::DefineFont) => tag_reader.read_define_font()?,
|
||||
Some(TagCode::DefineFont2) => tag_reader.read_define_font_2(2)?,
|
||||
Some(TagCode::DefineFont3) => tag_reader.read_define_font_2(3)?,
|
||||
Some(TagCode::DefineFont4) => tag_reader.read_define_font_4()?,
|
||||
Some(TagCode::DefineFont) => {
|
||||
Tag::DefineFont(Box::new(tag_reader.read_define_font_1()?))
|
||||
}
|
||||
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::DefineFontInfo) => tag_reader.read_define_font_info(1)?,
|
||||
Some(TagCode::DefineFontInfo2) => tag_reader.read_define_font_info(2)?,
|
||||
Some(TagCode::DefineFontName) => tag_reader.read_define_font_name()?,
|
||||
Some(TagCode::DefineMorphShape) => tag_reader.read_define_morph_shape(1)?,
|
||||
Some(TagCode::DefineMorphShape2) => tag_reader.read_define_morph_shape(2)?,
|
||||
Some(TagCode::DefineShape) => tag_reader.read_define_shape(1)?,
|
||||
Some(TagCode::DefineShape2) => tag_reader.read_define_shape(2)?,
|
||||
Some(TagCode::DefineShape3) => tag_reader.read_define_shape(3)?,
|
||||
Some(TagCode::DefineShape4) => tag_reader.read_define_shape(4)?,
|
||||
Some(TagCode::DefineSound) => tag_reader.read_define_sound()?,
|
||||
Some(TagCode::DefineText) => tag_reader.read_define_text()?,
|
||||
Some(TagCode::DefineMorphShape) => {
|
||||
Tag::DefineMorphShape(Box::new(tag_reader.read_define_morph_shape(1)?))
|
||||
}
|
||||
Some(TagCode::DefineMorphShape2) => {
|
||||
Tag::DefineMorphShape(Box::new(tag_reader.read_define_morph_shape(2)?))
|
||||
}
|
||||
Some(TagCode::DefineShape) => Tag::DefineShape(tag_reader.read_define_shape(1)?),
|
||||
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::EnableTelemetry) => {
|
||||
tag_reader.read_u16()?; // Reserved
|
||||
|
@ -438,25 +458,28 @@ impl<R: Read> Reader<R> {
|
|||
|
||||
Some(TagCode::SoundStreamHead) => Tag::SoundStreamHead(
|
||||
// TODO: Disallow certain compressions.
|
||||
Box::new(tag_reader.read_sound_stream_info()?),
|
||||
Box::new(tag_reader.read_sound_stream_head()?),
|
||||
),
|
||||
|
||||
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 {
|
||||
id: tag_reader.read_u16()?,
|
||||
sound_info: Box::new(tag_reader.read_sound_info()?),
|
||||
},
|
||||
Some(TagCode::StartSound) => Tag::StartSound(tag_reader.read_start_sound_1()?),
|
||||
|
||||
Some(TagCode::StartSound2) => Tag::StartSound2 {
|
||||
class_name: tag_reader.read_c_string()?,
|
||||
sound_info: Box::new(tag_reader.read_sound_info()?),
|
||||
},
|
||||
|
||||
Some(TagCode::DefineBitsLossless) => tag_reader.read_define_bits_lossless(1)?,
|
||||
Some(TagCode::DefineBitsLossless2) => tag_reader.read_define_bits_lossless(2)?,
|
||||
Some(TagCode::DebugId) => Tag::DebugId(tag_reader.read_debug_id()?),
|
||||
|
||||
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 {
|
||||
id: tag_reader.read_u16()?,
|
||||
|
@ -548,46 +571,40 @@ impl<R: Read> Reader<R> {
|
|||
})
|
||||
}
|
||||
|
||||
Some(TagCode::DefineSceneAndFrameLabelData) => {
|
||||
tag_reader.read_define_scene_and_frame_label_data()?
|
||||
}
|
||||
Some(TagCode::DefineSceneAndFrameLabelData) => Tag::DefineSceneAndFrameLabelData(
|
||||
tag_reader.read_define_scene_and_frame_label_data()?,
|
||||
),
|
||||
|
||||
Some(TagCode::FrameLabel) => {
|
||||
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::FrameLabel) => Tag::FrameLabel(tag_reader.read_frame_label(length)?),
|
||||
|
||||
Some(TagCode::DefineSprite) => {
|
||||
// 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
|
||||
// read_tag_list for Reader<Take<R>> to enforce this.
|
||||
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()?
|
||||
}
|
||||
|
||||
Some(TagCode::PlaceObject) => tag_reader.read_place_object()?,
|
||||
Some(TagCode::PlaceObject2) => tag_reader.read_place_object_2_or_3(2)?,
|
||||
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::PlaceObject) => {
|
||||
Tag::PlaceObject(Box::new(tag_reader.read_place_object(length)?))
|
||||
}
|
||||
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 {
|
||||
character_id: Some(tag_reader.read_u16()?),
|
||||
depth: tag_reader.read_i16()?,
|
||||
},
|
||||
Some(TagCode::RemoveObject) => Tag::RemoveObject(tag_reader.read_remove_object_1()?),
|
||||
|
||||
Some(TagCode::RemoveObject2) => Tag::RemoveObject {
|
||||
depth: tag_reader.read_i16()?,
|
||||
character_id: None,
|
||||
},
|
||||
Some(TagCode::RemoveObject2) => Tag::RemoveObject(tag_reader.read_remove_object_2()?),
|
||||
|
||||
Some(TagCode::VideoFrame) => tag_reader.read_video_frame()?,
|
||||
|
||||
Some(TagCode::ProductInfo) => Tag::ProductInfo(tag_reader.read_product_info()?),
|
||||
_ => {
|
||||
let size = length as usize;
|
||||
let mut data = vec![0; size];
|
||||
|
@ -605,7 +622,7 @@ impl<R: Read> Reader<R> {
|
|||
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];
|
||||
input.read_exact(&mut signature)?;
|
||||
let compression = match &signature {
|
||||
|
@ -617,7 +634,7 @@ impl<R: Read> Reader<R> {
|
|||
Ok(compression)
|
||||
}
|
||||
|
||||
fn read_rectangle(&mut self) -> Result<Rectangle> {
|
||||
pub fn read_rectangle(&mut self) -> Result<Rectangle> {
|
||||
self.byte_align();
|
||||
let num_bits = self.read_ubits(5)? as usize;
|
||||
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 {
|
||||
self.byte = self.input.read_u8()?;
|
||||
self.bit_index = 8;
|
||||
|
@ -638,11 +655,11 @@ impl<R: Read> Reader<R> {
|
|||
Ok(val)
|
||||
}
|
||||
|
||||
fn byte_align(&mut self) {
|
||||
pub fn byte_align(&mut self) {
|
||||
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;
|
||||
for _ in 0..num_bits {
|
||||
val <<= 1;
|
||||
|
@ -651,7 +668,7 @@ impl<R: Read> Reader<R> {
|
|||
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 {
|
||||
self.read_ubits(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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn read_encoded_u32(&mut self) -> Result<u32> {
|
||||
pub fn read_encoded_u32(&mut self) -> Result<u32> {
|
||||
let mut val = 0u32;
|
||||
for i in 0..5 {
|
||||
let byte = self.read_u8()?;
|
||||
|
@ -680,18 +697,18 @@ impl<R: Read> Reader<R> {
|
|||
Ok(val)
|
||||
}
|
||||
|
||||
fn read_character_id(&mut self) -> Result<CharacterId> {
|
||||
pub fn read_character_id(&mut self) -> Result<CharacterId> {
|
||||
self.read_u16()
|
||||
}
|
||||
|
||||
fn read_rgb(&mut self) -> Result<Color> {
|
||||
pub fn read_rgb(&mut self) -> Result<Color> {
|
||||
let r = self.read_u8()?;
|
||||
let g = self.read_u8()?;
|
||||
let b = self.read_u8()?;
|
||||
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 g = self.read_u8()?;
|
||||
let b = self.read_u8()?;
|
||||
|
@ -699,7 +716,7 @@ impl<R: Read> Reader<R> {
|
|||
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();
|
||||
let has_add = self.read_bit()?;
|
||||
let has_mult = self.read_bit()?;
|
||||
|
@ -822,7 +839,7 @@ impl<R: Read> Reader<R> {
|
|||
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 mut records = Vec::new();
|
||||
while let Some(record) = self.read_button_record(1)? {
|
||||
|
@ -830,7 +847,7 @@ impl<R: Read> Reader<R> {
|
|||
}
|
||||
let mut action_data = Vec::new();
|
||||
self.input.read_to_end(&mut action_data)?;
|
||||
Ok(Tag::DefineButton(Box::new(Button {
|
||||
Ok(Button {
|
||||
id,
|
||||
is_track_as_menu: false,
|
||||
records,
|
||||
|
@ -841,10 +858,10 @@ impl<R: Read> Reader<R> {
|
|||
key_code: None,
|
||||
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 flags = self.read_u8()?;
|
||||
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,
|
||||
is_track_as_menu,
|
||||
records,
|
||||
actions,
|
||||
})))
|
||||
})
|
||||
}
|
||||
|
||||
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 mut scenes = Vec::with_capacity(num_scenes);
|
||||
for _ in 0..num_scenes {
|
||||
scenes.push(FrameLabel {
|
||||
scenes.push(FrameLabelData {
|
||||
frame_num: self.read_encoded_u32()?,
|
||||
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 mut frame_labels = Vec::with_capacity(num_frame_labels);
|
||||
for _ in 0..num_frame_labels {
|
||||
frame_labels.push(FrameLabel {
|
||||
frame_labels.push(FrameLabelData {
|
||||
frame_num: self.read_encoded_u32()?,
|
||||
label: self.read_c_string()?,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Tag::DefineSceneAndFrameLabelData {
|
||||
Ok(DefineSceneAndFrameLabelData {
|
||||
scenes,
|
||||
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 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 flags = self.read_u8()?;
|
||||
|
@ -1149,7 +1176,7 @@ impl<R: Read> Reader<R> {
|
|||
None
|
||||
};
|
||||
|
||||
Ok(Tag::DefineFont2(Box::new(Font {
|
||||
Ok(Font {
|
||||
version,
|
||||
id,
|
||||
name,
|
||||
|
@ -1161,10 +1188,10 @@ impl<R: Read> Reader<R> {
|
|||
is_ansi,
|
||||
is_bold,
|
||||
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 flags = self.read_u8()?;
|
||||
let name = self.read_c_string()?;
|
||||
|
@ -1176,13 +1203,13 @@ impl<R: Read> Reader<R> {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Tag::DefineFont4(Font4 {
|
||||
Ok(Font4 {
|
||||
id,
|
||||
is_italic: flags & 0b10 != 0,
|
||||
is_bold: flags & 0b1 != 0,
|
||||
name,
|
||||
data,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
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 start_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)? {
|
||||
end_shape.push(record);
|
||||
}
|
||||
Ok(Tag::DefineMorphShape(Box::new(DefineMorphShape {
|
||||
Ok(DefineMorphShape {
|
||||
id,
|
||||
version: shape_version,
|
||||
has_non_scaling_strokes,
|
||||
|
@ -1373,7 +1400,7 @@ impl<R: Read> Reader<R> {
|
|||
fill_styles: end_fill_styles,
|
||||
line_styles: end_line_styles,
|
||||
},
|
||||
})))
|
||||
})
|
||||
}
|
||||
|
||||
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()?;
|
||||
(
|
||||
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 shape_bounds = self.read_rectangle()?;
|
||||
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)? {
|
||||
records.push(record);
|
||||
}
|
||||
Ok(Tag::DefineShape(Shape {
|
||||
Ok(Shape {
|
||||
version,
|
||||
id,
|
||||
shape_bounds,
|
||||
|
@ -1604,24 +1631,24 @@ impl<R: Read> Reader<R> {
|
|||
has_scaling_strokes,
|
||||
styles,
|
||||
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 format = self.read_sound_format()?;
|
||||
let num_samples = self.read_u32()?;
|
||||
let mut data = Vec::new();
|
||||
self.input.read_to_end(&mut data)?;
|
||||
Ok(Tag::DefineSound(Box::new(Sound {
|
||||
Ok(Sound {
|
||||
id,
|
||||
format,
|
||||
num_samples,
|
||||
data,
|
||||
})))
|
||||
})
|
||||
}
|
||||
|
||||
fn read_sound_stream_info(&mut self) -> Result<SoundStreamInfo> {
|
||||
pub fn read_sound_stream_head(&mut self) -> Result<SoundStreamHead> {
|
||||
// TODO: Verify version requirements.
|
||||
let playback_format = self.read_sound_format()?;
|
||||
let stream_format = self.read_sound_format()?;
|
||||
|
@ -1632,7 +1659,7 @@ impl<R: Read> Reader<R> {
|
|||
} else {
|
||||
0
|
||||
};
|
||||
Ok(SoundStreamInfo {
|
||||
Ok(SoundStreamHead {
|
||||
stream_format,
|
||||
playback_format,
|
||||
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()?,
|
||||
matrix: self.read_matrix()?,
|
||||
is_smoothed: (fill_style_type & 0b10) == 0,
|
||||
|
@ -1895,7 +1922,7 @@ impl<R: Read> Reader<R> {
|
|||
Ok(shape_record)
|
||||
}
|
||||
|
||||
fn read_define_sprite(&mut self) -> Result<Tag> {
|
||||
pub fn read_define_sprite(&mut self) -> Result<Tag> {
|
||||
Ok(Tag::DefineSprite(Sprite {
|
||||
id: 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?
|
||||
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,
|
||||
action: PlaceObjectAction::Place(self.read_u16()?),
|
||||
depth: self.read_i16()?,
|
||||
matrix: Some(self.read_matrix()?),
|
||||
color_transform: self.read_color_transform_no_alpha().ok(),
|
||||
action: PlaceObjectAction::Place(reader.read_u16()?),
|
||||
depth: reader.read_i16()?,
|
||||
matrix: Some(reader.read_matrix()?),
|
||||
color_transform: if !reader.get_ref().is_empty() {
|
||||
Some(reader.read_color_transform_no_alpha()?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
ratio: None,
|
||||
name: None,
|
||||
clip_depth: None,
|
||||
|
@ -1923,10 +1959,10 @@ impl<R: Read> Reader<R> {
|
|||
is_bitmap_cached: false,
|
||||
is_visible: true,
|
||||
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 {
|
||||
self.read_u16()?
|
||||
} else {
|
||||
|
@ -2014,7 +2050,7 @@ impl<R: Read> Reader<R> {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Tag::PlaceObject(Box::new(PlaceObject {
|
||||
Ok(PlaceObject {
|
||||
version: place_object_version,
|
||||
action,
|
||||
depth,
|
||||
|
@ -2032,10 +2068,24 @@ impl<R: Read> Reader<R> {
|
|||
background_color,
|
||||
blend_mode,
|
||||
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()? {
|
||||
0 | 1 => BlendMode::Normal,
|
||||
2 => BlendMode::Layer,
|
||||
|
@ -2164,7 +2214,7 @@ impl<R: Read> Reader<R> {
|
|||
Ok(event_list)
|
||||
}
|
||||
|
||||
fn read_filter(&mut self) -> Result<Filter> {
|
||||
pub fn read_filter(&mut self) -> Result<Filter> {
|
||||
self.byte_align();
|
||||
let filter = match self.read_u8()? {
|
||||
0 => Filter::DropShadowFilter(Box::new(DropShadowFilter {
|
||||
|
@ -2293,7 +2343,7 @@ impl<R: Read> Reader<R> {
|
|||
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 compression = match flags >> 4 {
|
||||
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 event = match (flags >> 4) & 0b11 {
|
||||
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 bounds = self.read_rectangle()?;
|
||||
let matrix = self.read_matrix()?;
|
||||
|
@ -2381,12 +2438,12 @@ impl<R: Read> Reader<R> {
|
|||
records.push(record);
|
||||
}
|
||||
|
||||
Ok(Tag::DefineText(Box::new(Text {
|
||||
Ok(Text {
|
||||
id,
|
||||
bounds,
|
||||
matrix,
|
||||
records,
|
||||
})))
|
||||
})
|
||||
}
|
||||
|
||||
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 format = match self.read_u8()? {
|
||||
3 => BitmapFormat::ColorMap8,
|
||||
|
@ -2617,7 +2674,7 @@ impl<R: Read> Reader<R> {
|
|||
};
|
||||
let mut data = Vec::new();
|
||||
self.input.read_to_end(&mut data)?;
|
||||
Ok(Tag::DefineBitsLossless(DefineBitsLossless {
|
||||
Ok(DefineBitsLossless {
|
||||
version,
|
||||
id,
|
||||
format,
|
||||
|
@ -2625,7 +2682,28 @@ impl<R: Read> Reader<R> {
|
|||
height,
|
||||
num_colors,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ pub enum TagCode {
|
|||
|
||||
DefineSprite = 39,
|
||||
|
||||
ProductInfo = 41,
|
||||
|
||||
FrameLabel = 43,
|
||||
|
||||
SoundStreamHead2 = 45,
|
||||
|
@ -57,6 +59,7 @@ pub enum TagCode {
|
|||
VideoFrame = 61,
|
||||
DefineFontInfo2 = 62,
|
||||
|
||||
DebugId = 63,
|
||||
EnableDebugger2 = 64,
|
||||
ScriptLimits = 65,
|
||||
SetTabIndex = 66,
|
||||
|
@ -87,3 +90,9 @@ pub enum TagCode {
|
|||
EnableTelemetry = 93,
|
||||
PlaceObject4 = 94,
|
||||
}
|
||||
|
||||
impl TagCode {
|
||||
pub fn from_u16(n: u16) -> Option<Self> {
|
||||
num_traits::FromPrimitive::from_u16(n)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1190,40 +1190,40 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
),
|
||||
(
|
||||
1, // Minimum version not listed in SWF19.
|
||||
Tag::DefineSceneAndFrameLabelData {
|
||||
Tag::DefineSceneAndFrameLabelData(DefineSceneAndFrameLabelData {
|
||||
scenes: vec![
|
||||
FrameLabel {
|
||||
FrameLabelData {
|
||||
frame_num: 0,
|
||||
label: "Scene 1".to_string(),
|
||||
},
|
||||
FrameLabel {
|
||||
FrameLabelData {
|
||||
frame_num: 25,
|
||||
label: "Scene2Scene2Scene2Scene2Scene2".to_string(),
|
||||
},
|
||||
FrameLabel {
|
||||
FrameLabelData {
|
||||
frame_num: 26,
|
||||
label: "test日本語test".to_string(),
|
||||
},
|
||||
],
|
||||
frame_labels: vec![
|
||||
FrameLabel {
|
||||
FrameLabelData {
|
||||
frame_num: 0,
|
||||
label: "a".to_string(),
|
||||
},
|
||||
FrameLabel {
|
||||
FrameLabelData {
|
||||
frame_num: 9,
|
||||
label: "b".to_string(),
|
||||
},
|
||||
FrameLabel {
|
||||
FrameLabelData {
|
||||
frame_num: 17,
|
||||
label: "❤😁aaa".to_string(),
|
||||
},
|
||||
FrameLabel {
|
||||
FrameLabelData {
|
||||
frame_num: 25,
|
||||
label: "frameInScene2".to_string(),
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
read_tag_bytes_from_file(
|
||||
"tests/swfs/DefineSceneAndFrameLabelData.swf",
|
||||
TagCode::DefineSceneAndFrameLabelData,
|
||||
|
@ -1793,10 +1793,10 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
),
|
||||
(
|
||||
3,
|
||||
Tag::FrameLabel {
|
||||
Tag::FrameLabel(FrameLabel {
|
||||
label: "test".to_string(),
|
||||
is_anchor: false,
|
||||
},
|
||||
}),
|
||||
read_tag_bytes_from_file_with_index(
|
||||
"tests/swfs/FrameLabel-CS6.swf",
|
||||
TagCode::FrameLabel,
|
||||
|
@ -1805,10 +1805,10 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
),
|
||||
(
|
||||
6, // Anchor tags supported in SWF version 6 and later.
|
||||
Tag::FrameLabel {
|
||||
Tag::FrameLabel(FrameLabel {
|
||||
label: "anchor_tag".to_string(),
|
||||
is_anchor: true,
|
||||
},
|
||||
}),
|
||||
read_tag_bytes_from_file_with_index(
|
||||
"tests/swfs/FrameLabel-CS6.swf",
|
||||
TagCode::FrameLabel,
|
||||
|
@ -2156,7 +2156,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
(1, Tag::ShowFrame, vec![0b01_000000, 0]),
|
||||
(
|
||||
3,
|
||||
Tag::SoundStreamHead2(Box::new(SoundStreamInfo {
|
||||
Tag::SoundStreamHead2(Box::new(SoundStreamHead {
|
||||
stream_format: SoundFormat {
|
||||
compression: AudioCompression::Uncompressed,
|
||||
sample_rate: 5512,
|
||||
|
@ -2190,7 +2190,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
),
|
||||
(
|
||||
4,
|
||||
Tag::StartSound {
|
||||
Tag::StartSound(StartSound {
|
||||
id: 1,
|
||||
sound_info: Box::new(SoundInfo {
|
||||
event: SoundEvent::Start,
|
||||
|
@ -2199,7 +2199,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
num_loops: 3,
|
||||
envelope: None,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
read_tag_bytes_from_file("tests/swfs/DefineSound.swf", TagCode::StartSound),
|
||||
),
|
||||
(
|
||||
|
|
85
src/types.rs
85
src/types.rs
|
@ -236,6 +236,18 @@ pub struct FileAttributes {
|
|||
|
||||
#[derive(Debug, PartialEq)]
|
||||
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 label: String,
|
||||
}
|
||||
|
@ -442,6 +454,7 @@ pub enum Tag {
|
|||
|
||||
Protect(Option<String>),
|
||||
CsmTextSettings(CsmTextSettings),
|
||||
DebugId(DebugId),
|
||||
DefineBinaryData {
|
||||
id: CharacterId,
|
||||
data: Vec<u8>,
|
||||
|
@ -489,7 +502,7 @@ pub enum Tag {
|
|||
DefineText(Box<Text>),
|
||||
DefineVideoStream(DefineVideoStream),
|
||||
DoAbc(DoAbc),
|
||||
DoAction(Vec<u8>),
|
||||
DoAction(DoAction),
|
||||
DoInitAction {
|
||||
id: CharacterId,
|
||||
action_data: Vec<u8>,
|
||||
|
@ -504,40 +517,30 @@ pub enum Tag {
|
|||
url: String,
|
||||
imports: Vec<ExportedAsset>,
|
||||
},
|
||||
JpegTables(Vec<u8>),
|
||||
SetBackgroundColor(Color),
|
||||
JpegTables(JpegTables),
|
||||
SetBackgroundColor(SetBackgroundColor),
|
||||
SetTabIndex {
|
||||
depth: Depth,
|
||||
tab_index: u16,
|
||||
},
|
||||
SoundStreamBlock(Vec<u8>),
|
||||
SoundStreamHead(Box<SoundStreamInfo>),
|
||||
SoundStreamHead2(Box<SoundStreamInfo>),
|
||||
StartSound {
|
||||
id: CharacterId,
|
||||
sound_info: Box<SoundInfo>,
|
||||
},
|
||||
SoundStreamBlock(SoundStreamBlock),
|
||||
SoundStreamHead(Box<SoundStreamHead>),
|
||||
SoundStreamHead2(Box<SoundStreamHead>),
|
||||
StartSound(StartSound),
|
||||
StartSound2 {
|
||||
class_name: String,
|
||||
sound_info: Box<SoundInfo>,
|
||||
},
|
||||
SymbolClass(Vec<SymbolClassLink>),
|
||||
PlaceObject(Box<PlaceObject>),
|
||||
RemoveObject {
|
||||
depth: Depth,
|
||||
character_id: Option<CharacterId>,
|
||||
},
|
||||
RemoveObject(RemoveObject),
|
||||
VideoFrame(VideoFrame),
|
||||
FileAttributes(FileAttributes),
|
||||
|
||||
FrameLabel {
|
||||
label: String,
|
||||
is_anchor: bool,
|
||||
},
|
||||
DefineSceneAndFrameLabelData {
|
||||
scenes: Vec<FrameLabel>,
|
||||
frame_labels: Vec<FrameLabel>,
|
||||
},
|
||||
FrameLabel(FrameLabel),
|
||||
DefineSceneAndFrameLabelData(DefineSceneAndFrameLabelData),
|
||||
|
||||
ProductInfo(ProductInfo),
|
||||
|
||||
Unknown {
|
||||
tag_code: u16,
|
||||
|
@ -551,6 +554,14 @@ pub struct ExportedAsset {
|
|||
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)]
|
||||
pub struct SymbolClassLink {
|
||||
pub id: CharacterId,
|
||||
|
@ -603,6 +614,12 @@ pub struct SoundEnvelopePoint {
|
|||
pub right_volume: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct StartSound {
|
||||
pub id: CharacterId,
|
||||
pub sound_info: Box<SoundInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Sprite {
|
||||
pub id: CharacterId,
|
||||
|
@ -750,13 +767,15 @@ pub struct SoundFormat {
|
|||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct SoundStreamInfo {
|
||||
pub struct SoundStreamHead {
|
||||
pub stream_format: SoundFormat,
|
||||
pub playback_format: SoundFormat,
|
||||
pub num_samples_per_block: u16,
|
||||
pub latency_seek: i16,
|
||||
}
|
||||
|
||||
pub type SoundStreamBlock = Vec<u8>;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Button {
|
||||
pub id: CharacterId,
|
||||
|
@ -1068,3 +1087,23 @@ pub struct DoAbc {
|
|||
pub is_lazy_initialize: bool,
|
||||
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];
|
||||
|
|
81
src/write.rs
81
src/write.rs
|
@ -142,6 +142,10 @@ pub trait SwfWrite<W: Write> {
|
|||
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<()> {
|
||||
self.get_inner().write_i8(n)
|
||||
}
|
||||
|
@ -891,7 +895,7 @@ impl<W: Write> Writer<W> {
|
|||
// TODO: Allow clone of color.
|
||||
Tag::SetBackgroundColor(ref color) => {
|
||||
self.write_tag_header(TagCode::SetBackgroundColor, 3)?;
|
||||
self.write_rgb(color)?;
|
||||
self.write_rgb(&color)?;
|
||||
}
|
||||
|
||||
Tag::ScriptLimits {
|
||||
|
@ -922,17 +926,14 @@ impl<W: Write> Writer<W> {
|
|||
}
|
||||
},
|
||||
|
||||
Tag::RemoveObject {
|
||||
depth,
|
||||
character_id,
|
||||
} => {
|
||||
if let Some(id) = character_id {
|
||||
Tag::RemoveObject(ref remove_object) => {
|
||||
if let Some(id) = remove_object.character_id {
|
||||
self.write_tag_header(TagCode::RemoveObject, 4)?;
|
||||
self.write_u16(id)?;
|
||||
} else {
|
||||
self.write_tag_header(TagCode::RemoveObject2, 2)?;
|
||||
}
|
||||
self.write_i16(depth)?;
|
||||
self.write_i16(remove_object.depth)?;
|
||||
}
|
||||
|
||||
Tag::SoundStreamBlock(ref data) => {
|
||||
|
@ -940,15 +941,16 @@ impl<W: Write> Writer<W> {
|
|||
self.output.write_all(data)?;
|
||||
}
|
||||
|
||||
Tag::SoundStreamHead(ref sound_stream_info) => {
|
||||
self.write_sound_stream_head(sound_stream_info, 1)?;
|
||||
Tag::SoundStreamHead(ref sound_stream_head) => {
|
||||
self.write_sound_stream_head(sound_stream_head, 1)?;
|
||||
}
|
||||
|
||||
Tag::SoundStreamHead2(ref sound_stream_info) => {
|
||||
self.write_sound_stream_head(sound_stream_info, 2)?;
|
||||
Tag::SoundStreamHead2(ref sound_stream_head) => {
|
||||
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
|
||||
+ if sound_info.in_sample.is_some() { 4 } else { 0 }
|
||||
+ if sound_info.out_sample.is_some() {
|
||||
|
@ -963,7 +965,7 @@ impl<W: Write> Writer<W> {
|
|||
0
|
||||
};
|
||||
self.write_tag_header(TagCode::StartSound, length)?;
|
||||
self.write_u16(id)?;
|
||||
self.write_u16(start_sound.id)?;
|
||||
self.write_sound_info(sound_info)?;
|
||||
}
|
||||
|
||||
|
@ -1032,10 +1034,10 @@ impl<W: Write> Writer<W> {
|
|||
self.write_u32(flags)?;
|
||||
}
|
||||
|
||||
Tag::FrameLabel {
|
||||
Tag::FrameLabel(FrameLabel {
|
||||
ref label,
|
||||
is_anchor,
|
||||
} => {
|
||||
}) => {
|
||||
// TODO: Assert proper version
|
||||
let is_anchor = is_anchor && self.version >= 6;
|
||||
let length = label.len() as u32 + if is_anchor { 2 } else { 1 };
|
||||
|
@ -1046,10 +1048,9 @@ impl<W: Write> Writer<W> {
|
|||
}
|
||||
}
|
||||
|
||||
Tag::DefineSceneAndFrameLabelData {
|
||||
ref scenes,
|
||||
ref frame_labels,
|
||||
} => self.write_define_scene_and_frame_label_data(scenes, frame_labels)?,
|
||||
Tag::DefineSceneAndFrameLabelData(ref data) => self.write_define_scene_and_frame_label_data(data)?,
|
||||
Tag::ProductInfo(ref product_info) => self.write_product_info(product_info)?,
|
||||
Tag::DebugId(ref debug_id) => self.write_debug_id(debug_id)?,
|
||||
|
||||
Tag::Unknown { tag_code, ref data } => {
|
||||
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(
|
||||
&mut self,
|
||||
scenes: &[FrameLabel],
|
||||
frame_labels: &[FrameLabel],
|
||||
data: &DefineSceneAndFrameLabelData,
|
||||
) -> 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);
|
||||
writer.write_encoded_u32(scenes.len() as u32)?;
|
||||
for scene in scenes {
|
||||
writer.write_encoded_u32(data.scenes.len() as u32)?;
|
||||
for scene in &data.scenes {
|
||||
writer.write_encoded_u32(scene.frame_num)?;
|
||||
writer.write_c_string(&scene.label)?;
|
||||
}
|
||||
writer.write_encoded_u32(frame_labels.len() as u32)?;
|
||||
for frame_label in frame_labels {
|
||||
writer.write_encoded_u32(data.frame_labels.len() as u32)?;
|
||||
for frame_label in &data.frame_labels {
|
||||
writer.write_encoded_u32(frame_label.frame_num)?;
|
||||
writer.write_c_string(&frame_label.label)?;
|
||||
}
|
||||
|
@ -2261,7 +2261,7 @@ impl<W: Write> Writer<W> {
|
|||
|
||||
fn write_sound_stream_head(
|
||||
&mut self,
|
||||
stream_info: &SoundStreamInfo,
|
||||
stream_head: &SoundStreamHead,
|
||||
version: u8,
|
||||
) -> Result<()> {
|
||||
let tag_code = if version >= 2 {
|
||||
|
@ -2270,17 +2270,17 @@ impl<W: Write> Writer<W> {
|
|||
TagCode::SoundStreamHead
|
||||
};
|
||||
// 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
|
||||
} else {
|
||||
4
|
||||
};
|
||||
self.write_tag_header(tag_code, length)?;
|
||||
self.write_sound_format(&stream_info.playback_format)?;
|
||||
self.write_sound_format(&stream_info.stream_format)?;
|
||||
self.write_u16(stream_info.num_samples_per_block)?;
|
||||
if stream_info.stream_format.compression == AudioCompression::Mp3 {
|
||||
self.write_i16(stream_info.latency_seek)?;
|
||||
self.write_sound_format(&stream_head.playback_format)?;
|
||||
self.write_sound_format(&stream_head.stream_format)?;
|
||||
self.write_u16(stream_head.num_samples_per_block)?;
|
||||
if stream_head.stream_format.compression == AudioCompression::Mp3 {
|
||||
self.write_i16(stream_head.latency_seek)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2672,6 +2672,21 @@ impl<W: Write> Writer<W> {
|
|||
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<()> {
|
||||
self.write_tag_code_and_length(tag_code as u16, length)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue