use crate::low_level::MAGIC_BYTE;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::{io, u16};
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Version {
V0 = 0,
V2 = 2,
V3 = 3,
V4 = 4,
V5 = 5,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Header {
pub version: Version,
pub is_compressed: bool,
pub bit_depth: u8,
pub size: (u16, u16),
pub start: (u16, u16),
pub dpi: (u16, u16),
pub palette: [[u8; 3]; 16],
pub number_of_color_planes: u8,
pub lane_length: u16,
}
fn error<T>(msg: &str) -> io::Result<T> {
Err(io::Error::new(io::ErrorKind::InvalidData, msg))
}
fn lane_proper_length(width: u16, bit_depth: u8) -> u16 {
(u32::from(width) * u32::from(bit_depth)).div_ceil(8) as u16
}
impl Header {
pub fn load<R: io::Read>(stream: &mut R) -> io::Result<Self> {
let magic = stream.read_u8()?;
if magic != MAGIC_BYTE {
return error("not a PCX file");
}
let version = match stream.read_u8()? {
0 => Version::V0,
2 => Version::V2,
3 => Version::V3,
4 => Version::V4,
5 => Version::V5,
_ => return error("PCX: unknown version"),
};
let encoding = stream.read_u8()?;
if encoding != 0 && encoding != 1 {
return error("PCX: unknown encoding");
}
let bit_depth = stream.read_u8()?;
let x_start = stream.read_u16::<LittleEndian>()?;
let y_start = stream.read_u16::<LittleEndian>()?;
let x_end = stream.read_u16::<LittleEndian>()?;
let y_end = stream.read_u16::<LittleEndian>()?;
if x_end < x_start
|| y_end < y_start
|| x_end - x_start == u16::MAX
|| y_end - y_start == u16::MAX
{
return error("PCX: invalid dimensions");
}
let (width, height) = (x_end - x_start + 1, y_end - y_start + 1);
if width == 0 || height == 0 {
return error("PCX: invalid dimensions");
}
let x_dpi = stream.read_u16::<LittleEndian>()?;
let y_dpi = stream.read_u16::<LittleEndian>()?;
let mut palette = [[0; 3]; 16];
for palette_entry in &mut palette {
stream.read_exact(palette_entry)?;
}
let _reserved_0 = stream.read_u8()?;
let number_of_color_planes = stream.read_u8()?;
let lane_length = stream.read_u16::<LittleEndian>()?;
let _palette_kind = stream.read_u16::<LittleEndian>()?;
let mut _reserved_1 = [0; 58];
stream.read_exact(&mut _reserved_1)?;
match (number_of_color_planes, bit_depth) {
| (3, 8) // 24-bit RGB
| (1, 1) | (1, 2) | (1, 4) | (1, 8) | (2, 1)
| (3, 1) | (4, 1) => {},
_ => return error("PCX: invalid or unsupported color format"),
}
if lane_length < lane_proper_length(width, bit_depth) {
return error("PCX: invalid lane length");
}
Ok(Header {
version,
is_compressed: encoding == 1,
bit_depth,
size: (width, height),
start: (x_start, y_start),
dpi: (x_dpi, y_dpi),
palette,
number_of_color_planes,
lane_length,
})
}
pub fn lane_proper_length(&self) -> u16 {
lane_proper_length(self.size.0, self.bit_depth)
}
pub fn lane_padding(&self) -> u16 {
self.lane_length - self.lane_proper_length()
}
pub fn palette_length(&self) -> Option<u16> {
match (self.number_of_color_planes, self.bit_depth) {
(3, 8) => None,
(number_of_color_planes, bit_depth) => {
Some(1 << (u16::from(bit_depth) * u16::from(number_of_color_planes)))
}
}
}
}
pub fn write<W: io::Write>(
stream: &mut W,
paletted: bool,
size: (u16, u16),
dpi: (u16, u16),
) -> io::Result<()> {
if size.0 == 0xFFFF {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"cannot save PCX with width equal to 0xFFFF",
));
}
if size.0 == 0 || size.1 == 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"cannot save PCX with zero size",
));
}
stream.write_u8(MAGIC_BYTE)?;
stream.write_u8(Version::V5 as u8)?;
stream.write_u8(1)?; stream.write_u8(8)?; stream.write_u16::<LittleEndian>(0)?; stream.write_u16::<LittleEndian>(0)?; stream.write_u16::<LittleEndian>(size.0 - 1)?;
stream.write_u16::<LittleEndian>(size.1 - 1)?;
stream.write_u16::<LittleEndian>(dpi.0)?;
stream.write_u16::<LittleEndian>(dpi.1)?;
stream.write_all(&[0u8; 16 * 3])?;
let lane_length = size.0 + (size.0 & 1);
stream.write_u8(0)?; stream.write_u8(if paletted { 1 } else { 3 })?; stream.write_u16::<LittleEndian>(lane_length)?;
stream.write_u16::<LittleEndian>(1)?;
stream.write_all(&[0u8; 58])?;
Ok(())
}
#[test]
fn fuzzer_test_case() {
let mut data: &[u8] = &[
0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff,
];
assert!(Header::load(&mut data).is_err());
}