#![allow( clippy::float_cmp, clippy::inconsistent_digit_grouping, clippy::unreadable_literal )] use crate::error::{Error, Result}; use crate::types::*; use byteorder::{LittleEndian, ReadBytesExt}; use enumset::EnumSet; use std::collections::HashSet; use std::convert::TryInto; use std::io::{self, Read}; /// Convenience method to parse an SWF. /// /// Decompresses the SWF in memory and returns a `Vec` of tags. /// If you would like to stream the SWF instead, use `read_swf_header` and /// `read_tag`. /// /// # Example /// ``` /// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap(); /// let swf = swf::read_swf(&data[..]).unwrap(); /// println!("Number of frames: {}", swf.header.num_frames); /// ``` pub fn read_swf(input: R) -> Result { let swf_stream = read_swf_header(input)?; let header = swf_stream.header; let mut reader = swf_stream.reader; // Decompress all of SWF into memory at once. let mut data = if header.compression == Compression::Lzma { // TODO: The LZMA decoder is still funky. // It always errors, and doesn't return all the data if you use read_to_end, // but read_exact at least returns the data... why? // Does the decoder need to be flushed somehow? let mut data = vec![0u8; swf_stream.uncompressed_length]; let _ = reader.get_mut().read_exact(&mut data); data } else { let mut data = Vec::with_capacity(swf_stream.uncompressed_length); if let Err(e) = reader.get_mut().read_to_end(&mut data) { log::error!("Error decompressing SWF, may be corrupt: {}", e); } data }; let version = header.version; // Some SWF streams may not be compressed correctly, // (e.g. incorrect data length in the stream), so decompressing // may throw an error even though the data otherwise comes // through the stream. // We'll still try to parse what we get if the full decompression fails. if let Err(e) = reader.get_mut().read_to_end(&mut data) { log::warn!("Error decompressing SWF stream, may be corrupt: {}", e); } if data.len() != swf_stream.uncompressed_length { log::warn!("SWF length doesn't match header, may be corrupt"); } let mut reader = Reader::new(&data[..], version); Ok(Swf { header, tags: reader.read_tag_list()?, }) } /// Parses an SWF header and returns a `Reader` that can be used /// to read the SWF tags inside the SWF file. /// /// Returns an `Error` if this is not a valid SWF file. /// /// # Example /// ``` /// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap(); /// let swf_stream = swf::read_swf_header(&data[..]).unwrap(); /// println!("FPS: {}", swf_stream.header.frame_rate); /// ``` pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result> { // Read SWF header. let compression = Reader::read_compression_type(&mut input)?; let version = input.read_u8()?; // Uncompressed length includes the 4-byte header and 4-byte uncompressed length itself, // subtract it here. let uncompressed_length = input.read_u32::()? - 8; // Now the SWF switches to a compressed stream. let decompressed_input: Box = match compression { Compression::None => Box::new(input), Compression::Zlib => { if version < 6 { log::warn!( "zlib compressed SWF is version {} but minimum version is 6", version ); } make_zlib_reader(input)? } Compression::Lzma => { if version < 13 { log::warn!( "LZMA compressed SWF is version {} but minimum version is 13", version ); } make_lzma_reader(input, uncompressed_length)? } }; let mut reader = Reader::new(decompressed_input, version); let stage_size = reader.read_rectangle()?; let frame_rate = reader.read_fixed8()?; let num_frames = reader.read_u16()?; let header = Header { version, compression, stage_size, frame_rate, num_frames, }; Ok(SwfStream { header, uncompressed_length: uncompressed_length.try_into().unwrap(), reader, }) } #[cfg(feature = "flate2")] fn make_zlib_reader<'a, R: Read + 'a>(input: R) -> Result> { use flate2::read::ZlibDecoder; Ok(Box::new(ZlibDecoder::new(input))) } #[cfg(feature = "libflate")] fn make_zlib_reader<'a, R: Read + 'a>(input: R) -> Result> { 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> { Err(Error::unsupported( "Support for Zlib compressed SWFs is not enabled.", )) } #[cfg(feature = "lzma")] fn make_lzma_reader<'a, R: Read + 'a>( mut input: R, uncompressed_length: u32, ) -> Result> { use byteorder::WriteBytesExt; use std::io::{Cursor, Write}; use xz2::{ read::XzDecoder, stream::{Action, Stream}, }; // Flash uses a mangled LZMA header, so we have to massage it into the normal format. // https://helpx.adobe.com/flash-player/kb/exception-thrown-you-decompress-lzma-compressed.html // LZMA SWF header: // Bytes 0..3: ZWS header // Byte 3: SWF version // Bytes 4..8: Uncompressed length // Bytes 8..12: Compressed length // Bytes 12..17: LZMA properties // // LZMA standard header // Bytes 0..5: LZMA properties // Bytes 5..13: Uncompressed length // Read compressed length let _ = input.read_u32::()?; // Read LZMA propreties to decoder let mut lzma_properties = [0u8; 5]; input.read_exact(&mut lzma_properties)?; // Rearrange above into LZMA format let mut lzma_header = Cursor::new(Vec::with_capacity(13)); lzma_header.write_all(&lzma_properties)?; lzma_header.write_u64::(uncompressed_length.into())?; // Create LZMA decoder stream and write header let mut lzma_stream = Stream::new_lzma_decoder(u64::max_value()).unwrap(); lzma_stream .process(&lzma_header.into_inner(), &mut [0u8; 1], Action::Run) .unwrap(); // Decoder is ready Ok(Box::new(XzDecoder::new_stream(input, lzma_stream))) } #[cfg(not(feature = "lzma"))] fn make_lzma_reader<'a, R: Read + 'a>( _input: R, _uncompressed_length: u32, ) -> Result> { Err(Error::unsupported( "Support for LZMA compressed SWFs is not enabled.", )) } pub trait SwfRead { fn get_inner(&mut self) -> &mut R; fn read_u8(&mut self) -> io::Result { self.get_inner().read_u8() } fn read_u16(&mut self) -> io::Result { self.get_inner().read_u16::() } fn read_u32(&mut self) -> io::Result { self.get_inner().read_u32::() } fn read_u64(&mut self) -> io::Result { self.get_inner().read_u64::() } fn read_i8(&mut self) -> io::Result { self.get_inner().read_i8() } fn read_i16(&mut self) -> io::Result { self.get_inner().read_i16::() } fn read_i32(&mut self) -> io::Result { self.get_inner().read_i32::() } fn read_fixed8(&mut self) -> io::Result { self.read_i16().map(|n| f32::from(n) / 256f32) } fn read_fixed16(&mut self) -> io::Result { self.read_i32().map(|n| f64::from(n) / 65536f64) } fn read_f32(&mut self) -> io::Result { self.get_inner().read_f32::() } fn read_f64(&mut self) -> io::Result { // Flash weirdly stores f64 as two LE 32-bit chunks. // First word is the hi-word, second word is the lo-word. let mut num = [0u8; 8]; self.get_inner().read_exact(&mut num)?; num.swap(0, 4); num.swap(1, 5); num.swap(2, 6); num.swap(3, 7); (&num[..]).read_f64::() } fn read_c_string(&mut self) -> Result { let mut bytes = Vec::new(); loop { let byte = self.read_u8()?; if byte == 0 { break; } bytes.push(byte) } // TODO: There is probably a better way to do this. // TODO: Verify ANSI for SWF 5 and earlier. String::from_utf8(bytes).map_err(|_| Error::invalid_data("Invalid string data")) } } pub struct Reader { input: R, version: u8, byte: u8, bit_index: u8, num_fill_bits: u8, num_line_bits: u8, } impl SwfRead for Reader { fn get_inner(&mut self) -> &mut R { &mut self.input } fn read_u8(&mut self) -> io::Result { self.byte_align(); self.input.read_u8() } fn read_u16(&mut self) -> io::Result { self.byte_align(); self.input.read_u16::() } fn read_u32(&mut self) -> io::Result { self.byte_align(); self.input.read_u32::() } fn read_i8(&mut self) -> io::Result { self.byte_align(); self.input.read_i8() } fn read_i16(&mut self) -> io::Result { self.byte_align(); self.input.read_i16::() } fn read_i32(&mut self) -> io::Result { self.byte_align(); self.input.read_i32::() } fn read_f32(&mut self) -> io::Result { self.byte_align(); self.input.read_f32::() } fn read_f64(&mut self) -> io::Result { self.byte_align(); self.input.read_f64::() } } impl Reader { pub fn new(input: R, version: u8) -> Reader { Reader { input, version, byte: 0, bit_index: 0, num_fill_bits: 0, num_line_bits: 0, } } /// Returns a reference to the underlying `Reader`. pub fn get_ref(&self) -> &R { &self.input } /// Returns a mutable reference to the underlying `Reader`. /// /// Reading from this reference is not recommended. pub fn get_mut(&mut self) -> &mut R { &mut self.input } /// Reads the next SWF tag from the stream. /// # Example /// ``` /// let data = std::fs::read("tests/swfs/DefineSprite.swf").unwrap(); /// let mut swf_stream = swf::read_swf_header(&data[..]).unwrap(); /// while let Ok(tag) = swf_stream.reader.read_tag() { /// println!("Tag: {:?}", tag); /// } /// ``` pub fn read_tag(&mut self) -> Result { let (tag_code, length) = self.read_tag_code_and_length()?; let tag = self.read_tag_with_code(tag_code, length); if let Err(e) = tag { return Err(Error::swf_parse_error_with_source(tag_code, e)); } tag } fn read_tag_with_code(&mut self, tag_code: u16, length: usize) -> Result { let mut tag_reader = Reader::new(self.input.by_ref().take(length as u64), self.version); use crate::tag_code::TagCode; let tag = match TagCode::from_u16(tag_code) { Some(TagCode::End) => Tag::End, Some(TagCode::ShowFrame) => Tag::ShowFrame, Some(TagCode::CsmTextSettings) => tag_reader.read_csm_text_settings()?, Some(TagCode::DefineBinaryData) => { let id = tag_reader.read_u16()?; tag_reader.read_u32()?; // Reserved let mut data = Vec::with_capacity(length - 6); tag_reader.input.read_to_end(&mut data)?; Tag::DefineBinaryData { id, data } } Some(TagCode::DefineBits) => { let id = tag_reader.read_u16()?; let mut jpeg_data = Vec::with_capacity(length - 2); tag_reader.input.read_to_end(&mut jpeg_data)?; Tag::DefineBits { id, jpeg_data } } Some(TagCode::DefineBitsJpeg2) => { let id = tag_reader.read_u16()?; let mut jpeg_data = Vec::with_capacity(length - 2); tag_reader.input.read_to_end(&mut jpeg_data)?; Tag::DefineBitsJpeg2 { id, jpeg_data } } 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::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 // tag, one for each character inside the button? In order of state/depth? let mut color_transforms = Vec::new(); while let Ok(color_transform) = tag_reader.read_color_transform_no_alpha() { color_transforms.push(color_transform); } Tag::DefineButtonColorTransform { id, color_transforms, } } Some(TagCode::DefineButtonSound) => { Tag::DefineButtonSound(Box::new(tag_reader.read_define_button_sound()?)) } Some(TagCode::DefineEditText) => { Tag::DefineEditText(Box::new(tag_reader.read_define_edit_text()?)) } 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::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(1)?)), Some(TagCode::DefineText2) => { Tag::DefineText(Box::new(tag_reader.read_define_text(2)?)) } Some(TagCode::DefineVideoStream) => tag_reader.read_define_video_stream()?, Some(TagCode::EnableTelemetry) => { tag_reader.read_u16()?; // Reserved let password_hash = if length > 2 { let mut data = vec![0; 32]; tag_reader.input.read_exact(&mut data)?; data } else { vec![] }; Tag::EnableTelemetry { password_hash } } Some(TagCode::ImportAssets) => { let url = tag_reader.read_c_string()?; let num_imports = tag_reader.read_u16()?; let mut imports = Vec::with_capacity(num_imports as usize); for _ in 0..num_imports { imports.push(ExportedAsset { id: tag_reader.read_u16()?, name: tag_reader.read_c_string()?, }); } Tag::ImportAssets { url, imports } } Some(TagCode::ImportAssets2) => { let url = tag_reader.read_c_string()?; tag_reader.read_u8()?; // Reserved; must be 1 tag_reader.read_u8()?; // Reserved; must be 0 let num_imports = tag_reader.read_u16()?; let mut imports = Vec::with_capacity(num_imports as usize); for _ in 0..num_imports { imports.push(ExportedAsset { id: tag_reader.read_u16()?, name: tag_reader.read_c_string()?, }); } Tag::ImportAssets { url, imports } } Some(TagCode::JpegTables) => { let mut data = Vec::with_capacity(length); tag_reader.input.read_to_end(&mut data)?; Tag::JpegTables(data) } Some(TagCode::Metadata) => { let mut s = String::with_capacity(length); tag_reader.get_mut().read_to_string(&mut s)?; // Remove trailing null bytes. There may or may not be a null byte. s = s.trim_end_matches(char::from(0)).to_string(); Tag::Metadata(s) } Some(TagCode::SetBackgroundColor) => Tag::SetBackgroundColor(tag_reader.read_rgb()?), Some(TagCode::SoundStreamBlock) => { let mut data = Vec::with_capacity(length); tag_reader.input.read_to_end(&mut data)?; Tag::SoundStreamBlock(data) } Some(TagCode::SoundStreamHead) => Tag::SoundStreamHead( // TODO: Disallow certain compressions. Box::new(tag_reader.read_sound_stream_head()?), ), Some(TagCode::SoundStreamHead2) => { Tag::SoundStreamHead2(Box::new(tag_reader.read_sound_stream_head()?)) } 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::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()?, splitter_rect: tag_reader.read_rectangle()?, }, Some(TagCode::DoAbc) => { let flags = tag_reader.read_u32()?; let name = tag_reader.read_c_string()?; let mut abc_data = Vec::with_capacity(length - 4 - name.len()); tag_reader.input.read_to_end(&mut abc_data)?; Tag::DoAbc(DoAbc { name, is_lazy_initialize: flags & 1 != 0, data: abc_data, }) } Some(TagCode::DoAction) => { let mut action_data = Vec::with_capacity(length); tag_reader.input.read_to_end(&mut action_data)?; Tag::DoAction(action_data) } Some(TagCode::DoInitAction) => { let id = tag_reader.read_u16()?; let mut action_data = Vec::with_capacity(length); tag_reader.input.read_to_end(&mut action_data)?; Tag::DoInitAction { id, action_data } } Some(TagCode::EnableDebugger) => Tag::EnableDebugger(tag_reader.read_c_string()?), Some(TagCode::EnableDebugger2) => { tag_reader.read_u16()?; // Reserved Tag::EnableDebugger(tag_reader.read_c_string()?) } Some(TagCode::ScriptLimits) => Tag::ScriptLimits { max_recursion_depth: tag_reader.read_u16()?, timeout_in_seconds: tag_reader.read_u16()?, }, Some(TagCode::SetTabIndex) => Tag::SetTabIndex { depth: tag_reader.read_u16()?, tab_index: tag_reader.read_u16()?, }, Some(TagCode::SymbolClass) => { let num_symbols = tag_reader.read_u16()?; let mut symbols = Vec::with_capacity(num_symbols as usize); for _ in 0..num_symbols { symbols.push(SymbolClassLink { id: tag_reader.read_u16()?, class_name: tag_reader.read_c_string()?, }); } Tag::SymbolClass(symbols) } Some(TagCode::ExportAssets) => Tag::ExportAssets(tag_reader.read_export_assets()?), Some(TagCode::FileAttributes) => { let flags = tag_reader.read_u32()?; Tag::FileAttributes(FileAttributes { use_direct_blit: (flags & 0b01000000) != 0, use_gpu: (flags & 0b00100000) != 0, has_metadata: (flags & 0b00010000) != 0, is_action_script_3: (flags & 0b00001000) != 0, use_network_sandbox: (flags & 0b00000001) != 0, }) } Some(TagCode::Protect) => { Tag::Protect(if length > 0 { tag_reader.read_u16()?; // TODO(Herschel): Two null bytes? Not specified in SWF19. Some(tag_reader.read_c_string()?) } else { None }) } Some(TagCode::DefineSceneAndFrameLabelData) => Tag::DefineSceneAndFrameLabelData( tag_reader.read_define_scene_and_frame_label_data()?, ), 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> to enforce this. let mut sprite_reader = Reader::new(&mut tag_reader.input as &mut dyn Read, self.version); sprite_reader.read_define_sprite()? } 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(tag_reader.read_remove_object_1()?), 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]; tag_reader.input.read_exact(&mut data[..])?; Tag::Unknown { tag_code, data } } }; if tag_reader.read_u8().is_ok() { // There should be no data remaining in the tag if we read it correctly. // If there is data remaining, the most likely scenario is we screwed up parsing. // But sometimes tools will export SWF tags that are larger than they should be. // TODO: It might be worthwhile to have a "strict mode" to determine // whether this should error or not. log::warn!( "Data remaining in buffer when parsing {} ({})", TagCode::name(tag_code), tag_code ); // Discard the rest of the data. let _ = std::io::copy(&mut tag_reader.get_mut(), &mut io::sink()); } Ok(tag) } pub fn read_compression_type(mut input: R) -> Result { let mut signature = [0u8; 3]; input.read_exact(&mut signature)?; let compression = match &signature { b"FWS" => Compression::None, b"CWS" => Compression::Zlib, b"ZWS" => Compression::Lzma, _ => return Err(Error::invalid_data("Invalid SWF")), }; Ok(compression) } pub fn read_rectangle(&mut self) -> Result { self.byte_align(); let num_bits = self.read_ubits(5)? as usize; Ok(Rectangle { x_min: self.read_sbits_twips(num_bits)?, x_max: self.read_sbits_twips(num_bits)?, y_min: self.read_sbits_twips(num_bits)?, y_max: self.read_sbits_twips(num_bits)?, }) } pub fn read_bit(&mut self) -> Result { if self.bit_index == 0 { self.byte = self.input.read_u8()?; self.bit_index = 8; } self.bit_index -= 1; let val = self.byte & (1 << self.bit_index) != 0; Ok(val) } pub fn byte_align(&mut self) { self.bit_index = 0; } pub fn read_ubits(&mut self, num_bits: usize) -> Result { let mut val = 0u32; for _ in 0..num_bits { val <<= 1; val |= if self.read_bit()? { 1 } else { 0 }; } Ok(val) } pub fn read_sbits(&mut self, num_bits: usize) -> Result { if num_bits > 0 { self.read_ubits(num_bits) .map(|n| (n as i32) << (32 - num_bits) >> (32 - num_bits)) } else { Ok(0) } } pub fn read_sbits_twips(&mut self, num_bits: usize) -> Result { self.read_sbits(num_bits).map(Twips::new) } pub fn read_fbits(&mut self, num_bits: usize) -> Result { self.read_sbits(num_bits).map(|n| (n as f32) / 65536f32) } pub fn read_encoded_u32(&mut self) -> Result { let mut val = 0u32; for i in 0..5 { let byte = self.read_u8()?; val |= u32::from(byte & 0b01111111) << (i * 7); if byte & 0b10000000 == 0 { break; } } Ok(val) } pub fn read_character_id(&mut self) -> Result { let id = self.read_u16()?; Ok(id) } pub fn read_rgb(&mut self) -> Result { let r = self.read_u8()?; let g = self.read_u8()?; let b = self.read_u8()?; Ok(Color { r, g, b, a: 255 }) } pub fn read_rgba(&mut self) -> Result { let r = self.read_u8()?; let g = self.read_u8()?; let b = self.read_u8()?; let a = self.read_u8()?; Ok(Color { r, g, b, a }) } pub fn read_color_transform_no_alpha(&mut self) -> Result { self.byte_align(); let has_add = self.read_bit()?; let has_mult = self.read_bit()?; let num_bits = self.read_ubits(4)? as usize; let mut color_transform = ColorTransform { r_multiply: 1f32, g_multiply: 1f32, b_multiply: 1f32, a_multiply: 1f32, r_add: 0i16, g_add: 0i16, b_add: 0i16, a_add: 0i16, }; if has_mult { color_transform.r_multiply = self.read_sbits(num_bits)? as f32 / 256f32; color_transform.g_multiply = self.read_sbits(num_bits)? as f32 / 256f32; color_transform.b_multiply = self.read_sbits(num_bits)? as f32 / 256f32; } if has_add { color_transform.r_add = self.read_sbits(num_bits)? as i16; color_transform.g_add = self.read_sbits(num_bits)? as i16; color_transform.b_add = self.read_sbits(num_bits)? as i16; } Ok(color_transform) } fn read_color_transform(&mut self) -> Result { self.byte_align(); let has_add = self.read_bit()?; let has_mult = self.read_bit()?; let num_bits = self.read_ubits(4)? as usize; let mut color_transform = ColorTransform { r_multiply: 1f32, g_multiply: 1f32, b_multiply: 1f32, a_multiply: 1f32, r_add: 0i16, g_add: 0i16, b_add: 0i16, a_add: 0i16, }; if has_mult { color_transform.r_multiply = self.read_sbits(num_bits)? as f32 / 256f32; color_transform.g_multiply = self.read_sbits(num_bits)? as f32 / 256f32; color_transform.b_multiply = self.read_sbits(num_bits)? as f32 / 256f32; color_transform.a_multiply = self.read_sbits(num_bits)? as f32 / 256f32; } if has_add { color_transform.r_add = self.read_sbits(num_bits)? as i16; color_transform.g_add = self.read_sbits(num_bits)? as i16; color_transform.b_add = self.read_sbits(num_bits)? as i16; color_transform.a_add = self.read_sbits(num_bits)? as i16; } Ok(color_transform) } fn read_matrix(&mut self) -> Result { self.byte_align(); let mut m = Matrix::new(); // Scale if self.read_bit()? { let num_bits = self.read_ubits(5)? as usize; m.scale_x = self.read_fbits(num_bits)?; m.scale_y = self.read_fbits(num_bits)?; } // Rotate/Skew if self.read_bit()? { let num_bits = self.read_ubits(5)? as usize; m.rotate_skew_0 = self.read_fbits(num_bits)?; m.rotate_skew_1 = self.read_fbits(num_bits)?; } // Translate (always present) let num_bits = self.read_ubits(5)? as usize; m.translate_x = self.read_sbits_twips(num_bits)?; m.translate_y = self.read_sbits_twips(num_bits)?; Ok(m) } fn read_language(&mut self) -> Result { Ok(match self.read_u8()? { 0 => Language::Unknown, 1 => Language::Latin, 2 => Language::Japanese, 3 => Language::Korean, 4 => Language::SimplifiedChinese, 5 => Language::TraditionalChinese, _ => return Err(Error::invalid_data("Invalid language code")), }) } fn read_tag_list(&mut self) -> Result> { let mut tags = Vec::new(); loop { match self.read_tag() { Ok(Tag::End) => break, Ok(tag) => tags.push(tag), Err(err) => { // We screwed up reading this tag in some way. return Err(err); } } } Ok(tags) } pub fn read_tag_code_and_length(&mut self) -> Result<(u16, usize)> { let tag_code_and_length = self.read_u16()?; let tag_code = tag_code_and_length >> 6; let mut length = (tag_code_and_length & 0b111111) as usize; if length == 0b111111 { // Extended tag. length = self.read_u32()? as usize; } Ok((tag_code, length)) } pub fn read_define_button_1(&mut self) -> Result