use std::{ fmt::Debug, io::{Cursor, Read}, }; use mime::Mime; use crate::{ common::picturetype::PictureType, flac::errors::{FlacDecodeError, FlacEncodeError}, }; use super::{FlacMetablockDecode, FlacMetablockEncode, FlacMetablockHeader, FlacMetablockType}; /// A picture metablock in a flac file pub struct FlacPictureBlock { /// The type of this picture pub picture_type: PictureType, /// The format of this picture pub mime: Mime, /// The description of this picture pub description: String, /// The width of this picture, in px pub width: u32, /// The height of this picture, in px pub height: u32, /// The bit depth of this picture pub bit_depth: u32, /// The color count of this picture (if indexed) pub color_count: u32, /// The image data pub img_data: Vec, } impl Debug for FlacPictureBlock { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("FlacPicture") .field("type", &self.picture_type) .field("mime", &self.mime) .field("img_data.len()", &self.img_data.len()) .finish() } } impl FlacMetablockDecode for FlacPictureBlock { fn decode(data: &[u8]) -> Result { let mut d = Cursor::new(data); // This is re-used whenever we need to read four bytes let mut block = [0u8; 4]; #[expect(clippy::map_err_ignore)] d.read_exact(&mut block) .map_err(|_| FlacDecodeError::MalformedBlock)?; let picture_type = PictureType::from_idx(u32::from_be_bytes(block))?; // Image format let mime = { #[expect(clippy::map_err_ignore)] d.read_exact(&mut block) .map_err(|_| FlacDecodeError::MalformedBlock)?; let mime_length = u32::from_be_bytes(block).try_into().unwrap(); let mut mime = vec![0u8; mime_length]; #[expect(clippy::map_err_ignore)] d.read_exact(&mut mime) .map_err(|_| FlacDecodeError::MalformedBlock)?; String::from_utf8(mime) .ok() .and_then(|x| x.parse().ok()) .unwrap_or(mime::APPLICATION_OCTET_STREAM) }; // Image description let description = { #[expect(clippy::map_err_ignore)] d.read_exact(&mut block) .map_err(|_| FlacDecodeError::MalformedBlock)?; let desc_length = u32::from_be_bytes(block).try_into().unwrap(); let mut desc = vec![0u8; desc_length]; #[expect(clippy::map_err_ignore)] d.read_exact(&mut desc) .map_err(|_| FlacDecodeError::MalformedBlock)?; String::from_utf8(desc)? }; // Image width #[expect(clippy::map_err_ignore)] d.read_exact(&mut block) .map_err(|_| FlacDecodeError::MalformedBlock)?; let width = u32::from_be_bytes(block); // Image height #[expect(clippy::map_err_ignore)] d.read_exact(&mut block) .map_err(|_| FlacDecodeError::MalformedBlock)?; let height = u32::from_be_bytes(block); // Image bit depth #[expect(clippy::map_err_ignore)] d.read_exact(&mut block) .map_err(|_| FlacDecodeError::MalformedBlock)?; let depth = u32::from_be_bytes(block); // Color count for indexed images #[expect(clippy::map_err_ignore)] d.read_exact(&mut block) .map_err(|_| FlacDecodeError::MalformedBlock)?; let color_count = u32::from_be_bytes(block); // Image data length let img_data = { #[expect(clippy::map_err_ignore)] d.read_exact(&mut block) .map_err(|_| FlacDecodeError::MalformedBlock)?; let data_length = u32::from_be_bytes(block).try_into().unwrap(); let mut img_data = vec![0u8; data_length]; #[expect(clippy::map_err_ignore)] d.read_exact(&mut img_data) .map_err(|_| FlacDecodeError::MalformedBlock)?; img_data }; Ok(Self { picture_type, mime, description, width, height, bit_depth: depth, color_count, img_data, }) } } impl FlacMetablockEncode for FlacPictureBlock { fn get_len(&self) -> u32 { (4 + (4 + self.mime.to_string().len()) + (4 + self.description.len()) + 4 + 4 + 4 + 4 + (4 + self.img_data.len())) .try_into() .unwrap() } fn encode( &self, is_last: bool, with_header: bool, target: &mut impl std::io::Write, ) -> Result<(), FlacEncodeError> { if with_header { let header = FlacMetablockHeader { block_type: FlacMetablockType::Picture, length: self.get_len(), is_last, }; header.encode(target)?; } target.write_all(&self.picture_type.to_idx().to_be_bytes())?; let mime = self.mime.to_string(); target.write_all(&u32::try_from(mime.len()).unwrap().to_be_bytes())?; target.write_all(self.mime.to_string().as_bytes())?; drop(mime); target.write_all(&u32::try_from(self.description.len()).unwrap().to_be_bytes())?; target.write_all(self.description.as_bytes())?; target.write_all(&self.width.to_be_bytes())?; target.write_all(&self.height.to_be_bytes())?; target.write_all(&self.bit_depth.to_be_bytes())?; target.write_all(&self.color_count.to_be_bytes())?; target.write_all(&u32::try_from(self.img_data.len()).unwrap().to_be_bytes())?; target.write_all(&self.img_data)?; return Ok(()); } }