#![allow( clippy::float_cmp, clippy::inconsistent_digit_grouping, clippy::unreadable_literal )] use crate::types::*; use byteorder::{LittleEndian, ReadBytesExt}; use std::collections::HashSet; use std::io::{Error, ErrorKind, Read, Result}; /// 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 (header, mut reader) = read_swf_header(input)?; // Decompress all of SWF into memory at once. let mut data = Vec::new(); let version = reader.version; reader.get_mut().read_to_end(&mut data)?; 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 (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>)> { // Read SWF header. let compression = Reader::read_compression_type(&mut input)?; let version = input.read_u8()?; let _uncompressed_length = input.read_u32::()?; // Now the SWF switches to a compressed stream. let decompressed_input: Box = match compression { Compression::None => Box::new(input), Compression::Zlib => make_zlib_reader(input)?, Compression::Lzma => make_lzma_reader(input)?, }; 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((header, 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::new( ErrorKind::InvalidData, "Support for Zlib compressed SWFs is not enabled.", )) } #[cfg(feature = "lzma-support")] fn make_lzma_reader<'a, R: Read + 'a>(mut input: R) -> Result> { // Flash uses a mangled LZMA header, so we have to massage it into the normal // format. use byteorder::WriteBytesExt; use std::io::{Cursor, Write}; use xz2::stream::{Action, Stream}; input.read_u32::()?; // Compressed length let mut lzma_properties = [0u8; 5]; input.read_exact(&mut lzma_properties)?; let mut lzma_header = Cursor::new(Vec::with_capacity(13)); lzma_header.write_all(&lzma_properties)?; lzma_header.write_u64::(uncompressed_length as u64)?; let mut lzma_stream = Stream::new_lzma_decoder(u64::max_value())?; lzma_stream.process(&lzma_header.into_inner(), &mut [0u8; 1], Action::Run)?; Ok(Box::new(XzDecoder::new_stream(input, lzma_stream))) } #[cfg(not(feature = "lzma-support"))] fn make_lzma_reader<'a, R: Read + 'a>(_input: R) -> Result> { Err(Error::new( ErrorKind::InvalidData, "Support for LZMA compressed SWFs is not enabled.", )) } pub trait SwfRead { fn get_inner(&mut self) -> &mut R; fn read_u8(&mut self) -> Result { self.get_inner().read_u8() } fn read_u16(&mut self) -> Result { self.get_inner().read_u16::() } fn read_u32(&mut self) -> Result { self.get_inner().read_u32::() } fn read_i8(&mut self) -> Result { self.get_inner().read_i8() } fn read_i16(&mut self) -> Result { self.get_inner().read_i16::() } fn read_i32(&mut self) -> Result { self.get_inner().read_i32::() } fn read_fixed8(&mut self) -> Result { self.read_i16().map(|n| f32::from(n) / 256f32) } fn read_fixed16(&mut self) -> Result { self.read_i32().map(|n| f64::from(n) / 65536f64) } fn read_f32(&mut self) -> Result { self.get_inner().read_f32::() } fn read_f64(&mut self) -> 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::new(ErrorKind::InvalidData, "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) -> Result { self.byte_align(); self.input.read_u8() } fn read_u16(&mut self) -> Result { self.byte_align(); self.input.read_u16::() } fn read_u32(&mut self) -> Result { self.byte_align(); self.input.read_u32::() } fn read_i8(&mut self) -> Result { self.byte_align(); self.input.read_i8() } fn read_i16(&mut self) -> Result { self.byte_align(); self.input.read_i16::() } fn read_i32(&mut self) -> Result { self.byte_align(); self.input.read_i32::() } fn read_f32(&mut self) -> Result { self.byte_align(); self.input.read_f32::() } fn read_f64(&mut self) -> 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 (header, mut reader) = swf::read_swf_header(&data[..]).unwrap(); /// while let Some(tag) = reader.read_tag().unwrap() { /// println!("Tag: {:?}", tag); /// } /// ``` pub fn read_tag(&mut self) -> Result> { 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); use crate::tag_code::TagCode; let tag = match TagCode::from_u16(tag_code) { Some(TagCode::End) => return Ok(None), 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) => { let button_id = tag_reader.read_u16()?; let sound_id = tag_reader.read_u16()?; let over_to_up_sound = if sound_id != 0 { Some((sound_id, tag_reader.read_sound_info()?)) } else { None }; let sound_id = tag_reader.read_u16()?; let up_to_over_sound = if sound_id != 0 { Some((sound_id, tag_reader.read_sound_info()?)) } else { None }; let sound_id = tag_reader.read_u16()?; let over_to_down_sound = if sound_id != 0 { Some((sound_id, tag_reader.read_sound_info()?)) } else { None }; let sound_id = tag_reader.read_u16()?; let down_to_over_sound = if sound_id != 0 { Some((sound_id, tag_reader.read_sound_info()?)) } else { None }; Tag::DefineButtonSound(Box::new(ButtonSounds { id: button_id, over_to_up_sound, up_to_over_sound, over_to_down_sound, down_to_over_sound, })) } Some(TagCode::DefineEditText) => 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()?)), 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) => Tag::Metadata(tag_reader.read_c_string()?), 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::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_i16()?, 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) => { let num_exports = tag_reader.read_u16()?; let mut exports = Vec::with_capacity(num_exports as usize); for _ in 0..num_exports { exports.push(ExportedAsset { id: tag_reader.read_u16()?, name: tag_reader.read_c_string()?, }); } Tag::ExportAssets(exports) } 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_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::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 Read, self.version); sprite_reader.read_define_sprite()? } Some(TagCode::PlaceObject) => { Tag::PlaceObject(Box::new(tag_reader.read_place_object()?)) } 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()?, _ => { let size = length as usize; let mut data = vec![0; size]; tag_reader.input.read_exact(&mut data[..])?; Tag::Unknown { tag_code, data } } }; if cfg!(debug_assertions) && 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, we probably screwed up, so panic in debug builds. panic!("Error reading tag {:?}", tag_code); } Ok(Some(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::new(ErrorKind::InvalidData, "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 { self.read_u16() } 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::new(ErrorKind::InvalidData, "Invalid language code.")), }) } fn read_tag_list(&mut self) -> Result> { let mut tags = Vec::new(); loop { match self.read_tag() { Ok(Some(tag)) => tags.push(tag), Ok(None) => break, Err(err) => { // We screwed up reading this tag in some way. // TODO: We could recover more gracefully in some way. // Simply throw away this tag, but keep going. if cfg!(debug_assertions) { panic!("Error reading tag"); } 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