use crate::Vector2D;
use crate::error::Error;
use crate::reader::Accessor;
use crate::{AccessorOpt, Properties, TryFromAccessor};
#[cfg(feature = "image")]
use image::*;
use std::fmt::{Display, Formatter};
use std::io::{Read, SeekFrom};
use std::ops::Deref;
use std::time::Duration;
const ZLIB_HEADER_BYTE: u16 = 0x9C78;
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
pub enum CanvasFormat {
BGRA4444,
BGRA8888,
Gray,
ARGB1555,
RGB565,
RGB565Thumb,
DXT1,
DXT3,
DXT5,
ALPHA8,
RGBA1010102,
BC7,
RGBAFloat,
Unknown(i32),
}
impl From<i32> for CanvasFormat {
fn from(value: i32) -> Self {
match value {
1 => CanvasFormat::BGRA4444,
2 => CanvasFormat::BGRA8888,
3 => CanvasFormat::Gray,
257 => CanvasFormat::ARGB1555,
513 => CanvasFormat::RGB565,
517 => CanvasFormat::RGB565Thumb,
1026 => CanvasFormat::DXT3,
2050 => CanvasFormat::DXT5,
2304 => CanvasFormat::ALPHA8,
2562 => CanvasFormat::RGBA1010102,
4097 => CanvasFormat::DXT1,
4098 => CanvasFormat::BC7,
4100 => CanvasFormat::RGBAFloat,
_ => CanvasFormat::Unknown(value),
}
}
}
impl CanvasFormat {
fn data_size(&self, width: i32, height: i32) -> i32 {
match self {
CanvasFormat::BGRA4444 | CanvasFormat::ARGB1555 | CanvasFormat::RGB565 => {
width * height * 2
}
CanvasFormat::BGRA8888 | CanvasFormat::RGBA1010102 => width * height * 4,
CanvasFormat::DXT3 | CanvasFormat::DXT5 | CanvasFormat::Gray | CanvasFormat::BC7 => {
((width + 3) / 4) * ((height + 3) / 4) * 16
}
CanvasFormat::DXT1 => ((width + 3) / 4) * ((height + 3) / 4) * 8,
CanvasFormat::ALPHA8 => width * height,
CanvasFormat::RGBAFloat => width * height * 16,
CanvasFormat::RGB565Thumb => (width * height) / 128,
CanvasFormat::Unknown(_) => 0,
}
}
}
impl Display for CanvasFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
CanvasFormat::Unknown(_) => f.write_str("Unknown"),
CanvasFormat::BGRA4444 => f.write_str("BGRA4444"),
CanvasFormat::BGRA8888 => f.write_str("BGRA8888"),
CanvasFormat::Gray => f.write_str("Gray"),
CanvasFormat::ARGB1555 => f.write_str("ARGB1555"),
CanvasFormat::RGB565 => f.write_str("RGB565"),
CanvasFormat::RGB565Thumb => f.write_str("RGB565Thumb"),
CanvasFormat::DXT3 => f.write_str("DXT3"),
CanvasFormat::DXT5 => f.write_str("DXT5"),
CanvasFormat::DXT1 => f.write_str("DXT1"),
CanvasFormat::ALPHA8 => f.write_str("A8"),
CanvasFormat::RGBA1010102 => f.write_str("RGBAAAA2"),
CanvasFormat::BC7 => f.write_str("BC7"),
CanvasFormat::RGBAFloat => f.write_str("RGBAFloat"),
}
}
}
#[derive(Clone)]
pub struct CanvasAttribute {
pub property: Option<Properties>,
pub size: Vector2D,
pub format: CanvasFormat,
pub data_size: usize,
}
impl TryFromAccessor for CanvasAttribute {
type Error = Error;
fn try_from_accessor(
opt: AccessorOpt,
accessor: &mut dyn Accessor,
) -> Result<Self, Self::Error> {
let property = Properties::builtin(opt, accessor)?;
let size = Vector2D::try_from_accessor(opt, accessor)?;
let format = CanvasFormat::from(accessor.get_var_i32_le() + accessor.get_u8() as i32);
if let CanvasFormat::Unknown(format) = format {
return Err(Error::UnexpectedData(format!("canvas format {format}")));
}
accessor.advance(4);
let data_size = (accessor.get_i32_le() - 1) as usize;
accessor.advance(1);
Ok(CanvasAttribute {
property,
size,
format,
data_size,
})
}
}
#[derive(Clone)]
pub struct Canvas {
pub attr: CanvasAttribute,
data: Vec<u8>,
}
impl Canvas {
#[inline]
pub fn into_inner(self) -> Vec<u8> {
self.data
}
#[inline]
pub fn data(&self) -> &[u8] {
&self.data
}
}
impl TryFromAccessor for Canvas {
type Error = Error;
fn try_from_accessor(
opt: AccessorOpt,
accessor: &mut dyn Accessor,
) -> Result<Self, Self::Error> {
let attr = CanvasAttribute::try_from_accessor(opt, accessor)?;
let flag = accessor.get_u16_le();
accessor.try_seek(SeekFrom::Current(-2))?;
let data = if flag != ZLIB_HEADER_BYTE {
let mut de_data = Vec::with_capacity(attr.data_size);
let mut progress = 0;
while accessor.has_remaining() && progress < attr.data_size {
let size = accessor.get_i32_le() as usize;
let off = de_data.len();
unsafe {
de_data.set_len(off + size);
}
accessor.decrypt_to_slice(&mut de_data[off..off + size]);
progress += size + 4;
}
de_data
} else {
accessor.copy_to_vec(attr.data_size)
};
let raw_data_size = attr.format.data_size(attr.size.x, attr.size.y) as usize;
let mut zlib_dec = flate2::read::ZlibDecoder::new_with_buf(
&*data,
vec![0; raw_data_size.max(attr.data_size).min(32 * 1024)],
);
let mut raw_data = vec![0; raw_data_size];
let mut de_size = 0;
while de_size < raw_data_size {
let deflate_size = zlib_dec.read(&mut raw_data[de_size..])?;
if deflate_size == 0 {
break;
}
de_size += deflate_size;
}
if de_size != raw_data_size {
return Err(Error::BrokenFile);
}
Ok(Canvas {
attr,
data: raw_data,
})
}
}
#[cfg(feature = "image")]
macro_rules! to_bit8 {
(5,$val:expr) => {{
let val = $val as u8;
val << 3 | val >> 2
}};
(6,$val:expr) => {{
let val = $val as u8;
val << 2 | val >> 4
}};
(4,$val:expr) => {{
let val = $val as u8;
val | val << 4
}};
(1,$val:expr) => {{ if $val & 1 == 1 { u8::MAX } else { u8::MIN } }};
}
#[cfg(feature = "image")]
impl Canvas {
const DXT3_5_DECODED_BYTES_PER_BLOCK: usize = 64;
const DXT3_5_ENCODED_BYTES_PER_BLOCK: usize = 16;
pub fn image(&self) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>> {
match self.attr.format {
CanvasFormat::RGB565 => self.rgb565_to_rgba(),
CanvasFormat::RGB565Thumb => self.rgb565_thumb_to_rgba(),
CanvasFormat::BGRA4444 => self.bgra4444_to_rgba(),
CanvasFormat::BGRA8888 => self.bgra8888_to_rgba(),
CanvasFormat::ARGB1555 => self.argb1555_to_rgba(),
CanvasFormat::Gray | CanvasFormat::DXT3 => self.dxt3_to_rgba(),
CanvasFormat::DXT5 => self.dxt5_to_rgba(),
CanvasFormat::RGBA1010102 => self.rgba1010102_to_rgba(),
CanvasFormat::BC7 => {
None
}
_ => None,
}
}
#[inline]
fn process_chunk<F, U>(&self, size: usize, cb: F) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>>
where
F: Fn(&[u8]) -> U,
U: IntoIterator<Item = u8>,
{
let data = self
.data
.chunks_exact(size)
.flat_map(cb)
.collect::<Vec<_>>();
ImageBuffer::from_raw(self.attr.size.x as u32, self.attr.size.y as u32, data)
}
fn bgra4444_to_rgba(&self) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>> {
self.process_chunk(2, |chunk| {
let bits = u16::from_le_bytes([chunk[0], chunk[1]]);
[
to_bit8!(4, (bits >> 8) & 0xF),
to_bit8!(4, (bits >> 4) & 0xF),
to_bit8!(4, (bits & 0xF)),
to_bit8!(4, (bits >> 12) & 0xF),
]
})
}
fn bgra8888_to_rgba(&self) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>> {
self.process_chunk(4, |chunk| [chunk[2], chunk[1], chunk[0], chunk[3]])
}
fn dxt3_to_rgba(&self) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>> {
use crate::entry::canvas::dxt;
let width_block = (self.attr.size.x / 4) as usize;
let per_line_size = Self::DXT3_5_DECODED_BYTES_PER_BLOCK * width_block;
self.process_chunk(
Self::DXT3_5_ENCODED_BYTES_PER_BLOCK * width_block,
|chunk| {
let mut block = vec![0u8; per_line_size];
dxt::decode_dxt3_row(chunk, &mut block);
block
},
)
}
fn argb1555_to_rgba(&self) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>> {
self.process_chunk(2, |chunk| {
let color = u16::from_le_bytes([chunk[0], chunk[1]]);
[
to_bit8!(5, (color >> 10) & 0x1F),
to_bit8!(5, (color >> 5) & 0x1F),
to_bit8!(5, (color & 0x1F)),
to_bit8!(1, color >> 0xF),
]
})
}
#[inline]
fn rgb565_to_rgba_color(chunk: &[u8]) -> [u8; 4] {
let color = u16::from_le_bytes([chunk[0], chunk[1]]);
[
to_bit8!(5, (color >> 11) & 0x1F),
to_bit8!(6, (color >> 5) & 0x3F),
to_bit8!(5, color & 0x1F),
u8::MAX,
]
}
fn rgb565_to_rgba(&self) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>> {
self.process_chunk(2, Self::rgb565_to_rgba_color)
}
fn rgb565_thumb_to_rgba(&self) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>> {
self.process_chunk(((self.attr.size.x / 16) * 2) as usize, |block| {
block
.chunks_exact(2)
.flat_map(|chunk| Self::rgb565_to_rgba_color(chunk).repeat(16))
.collect::<Vec<_>>()
.repeat(16)
})
}
fn dxt5_to_rgba(&self) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>> {
use crate::entry::canvas::dxt;
let width_block = (self.attr.size.x / 4) as usize;
let per_line_size = Self::DXT3_5_DECODED_BYTES_PER_BLOCK * width_block;
self.process_chunk(
Self::DXT3_5_ENCODED_BYTES_PER_BLOCK * width_block,
|chunk| {
let mut block = vec![0u8; per_line_size];
dxt::decode_dxt5_row(chunk, &mut block);
block
},
)
}
fn rgba1010102_to_rgba(&self) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>> {
self.process_chunk(4, |chunk| {
let color = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
[
((color >> 24) & 0xff) as u8,
((color >> 14) & 0xff) as u8,
((color >> 4) & 0xff) as u8,
((color & 0x03) * 85) as u8,
]
})
}
}
const MCV0: u32 = 0x3056434D;
#[derive(Default, Clone)]
pub struct Metadata {
pub data_offset: usize,
pub data_size: usize,
pub alpha_data_offset: usize,
pub alpha_data_size: usize,
pub delay: Duration,
pub start_time: Duration,
}
pub const ALPHA_MAP: u8 = 1;
pub const PER_FRAME_DEALY: u8 = 1 << 1;
pub const PER_FRAME_TIME_LINE: u8 = 1 << 2;
#[derive(Clone)]
pub struct VideoAttribute {
pub properties: Option<Properties>,
pub size: usize,
pub fourcc: u32,
pub width: u16,
pub height: u16,
pub frame_cnt: u32,
pub mcv_flag: u8,
pub frame_dealy_unit: u64,
pub default_delay: u32,
pub metadata: Vec<Metadata>,
}
impl TryFromAccessor for VideoAttribute {
type Error = Error;
fn try_from_accessor(
opt: AccessorOpt,
accessor: &mut dyn Accessor,
) -> Result<Self, Self::Error> {
let properties = Properties::builtin(opt, accessor)?;
let offset = accessor.pos();
accessor.advance(1);
let size = accessor.get_var_i32_le() as usize;
let flag = accessor.get_u32_le();
if flag != MCV0 {
return Err(Error::UnexpectedData(format!("video flag {flag}")));
}
accessor.advance(2);
let header_len = accessor.get_u16_le();
let fourcc = accessor.get_u32_le() ^ 0xa5a5a5a5;
let width = accessor.get_u16_le();
let height = accessor.get_u16_le();
let frame_cnt = accessor.get_u32_le();
let mcv_flag = accessor.get_u8();
accessor.advance(3);
let frame_dealy_unit = accessor.get_u64_le();
let default_delay = accessor.get_u32_le();
accessor.seek(SeekFrom::Start((offset + header_len as usize) as u64));
let mut metadata = vec![Metadata::default(); frame_cnt as usize];
metadata.iter_mut().for_each(|frame| {
frame.data_offset = accessor.get_i32_le() as usize;
frame.data_size = accessor.get_i32_le() as usize;
});
let has_alpha_map = mcv_flag & ALPHA_MAP == ALPHA_MAP;
if has_alpha_map {
metadata.iter_mut().for_each(|frame| {
frame.alpha_data_offset = accessor.get_i32_le() as usize;
frame.alpha_data_size = accessor.get_i32_le() as usize;
});
}
metadata.iter_mut().for_each(|frame| {
if mcv_flag & PER_FRAME_DEALY == PER_FRAME_DEALY {
frame.delay = Duration::from_nanos(
(accessor.get_u32_le() as u64).wrapping_mul(frame_dealy_unit),
);
} else {
frame.delay =
Duration::from_nanos((default_delay as u64).wrapping_mul(frame_dealy_unit));
}
});
metadata.iter_mut().fold(0, |time, frame| {
if mcv_flag & PER_FRAME_TIME_LINE == PER_FRAME_TIME_LINE {
frame.start_time =
Duration::from_nanos(accessor.get_u64_le().wrapping_mul(frame_dealy_unit));
time
} else {
frame.start_time = Duration::from_nanos(time);
time + frame.delay.as_nanos() as u64
}
});
let data_offset = accessor.pos();
metadata.iter_mut().for_each(|frame| {
frame.data_offset += data_offset;
if has_alpha_map {
frame.alpha_data_offset += data_offset;
}
});
Ok(VideoAttribute {
properties,
size,
fourcc,
width,
height,
frame_cnt,
mcv_flag,
frame_dealy_unit,
default_delay,
metadata,
})
}
}
#[derive(Clone)]
pub struct Frame {
pub data: Vec<u8>,
pub alpha_data: Vec<u8>,
}
#[derive(Clone)]
pub struct Video {
attribute: VideoAttribute,
frames: Vec<Frame>,
}
impl Deref for Video {
type Target = [Frame];
fn deref(&self) -> &Self::Target {
&self.frames
}
}
impl Video {
#[inline]
pub fn attr(&self) -> &VideoAttribute {
&self.attribute
}
#[inline]
pub fn into_inner(self) -> Vec<Frame> {
self.frames
}
}
impl TryFromAccessor for Video {
type Error = Error;
fn try_from_accessor(
opt: AccessorOpt,
accessor: &mut dyn Accessor,
) -> Result<Self, Self::Error> {
let attribute = VideoAttribute::try_from_accessor(opt, accessor)?;
let frames = attribute
.metadata
.iter()
.map(|metadata| {
accessor.seek(SeekFrom::Start(metadata.data_offset as u64));
let mut data = vec![0; attribute.size];
accessor.copy_to_slice(&mut data);
let alpha_data = if attribute.mcv_flag & ALPHA_MAP == ALPHA_MAP {
accessor.seek(SeekFrom::Start(metadata.alpha_data_offset as u64));
let mut buffer = vec![0; metadata.alpha_data_size];
accessor.copy_to_slice(&mut buffer);
buffer
} else {
Vec::with_capacity(0)
};
Frame { data, alpha_data }
})
.collect::<Vec<_>>();
Ok(Video { attribute, frames })
}
}