//! Marathon Shapes format handling. use crate::durandal::{bin::*, err::*, fx32::*, image::*, text::*}; use bitflags::bitflags; fn color(b: &[u8]) -> ResultS<(usize, ColorShp)> { let l = c_byte(b, 0)?; let i = c_byte(b, 1)? as usize; let r = c_u16b(b, 2)?; let g = c_u16b(b, 4)?; let b = c_u16b(b, 6)?; let l = match l { 128 => Ok(true), 0 => Ok(false), _ => Err(err_msg("invalid flag in color")), }?; Ok((i, ColorShp::Opaque{r, g, b, l})) } fn tab_coll(b: &[u8], tab_num: usize, clr_num: usize) -> ResultS>> { let mut tabs = vec![vec![ColorShp::Translucent; clr_num]; tab_num]; let mut p = 0; for i in 0..tab_num { let clut = &mut tabs[i]; for _ in 0..clr_num { let (i, cr) = color(c_data(b, p..)?)?; *ok!(clut.get_mut(i), "invalid index")? = cr; p += 8; } } Ok(tabs) } fn bitmap(b: &[u8]) -> ResultS { let width = c_u16b(b, 0)? as usize; let height = c_u16b(b, 2)? as usize; let compr = c_u16b(b, 4)? == u16::max_value(); let flags = c_u16b(b, 6)?; let depth = c_u16b(b, 8)?; let flags = ok!(BmpFlags::from_bits(flags), "bad BmpFlags")?; let alpha = flags.contains(BmpFlags::Transparent); let cmajr = flags.contains(BmpFlags::ColumnMajor); if depth != 8 { bail!("invalid bit depth (should always be 8)"); } let mut bmp = Bitmap::new(width, height, alpha, cmajr); let mut p = 30 + if cmajr {4 * width} else {4 * height}; let scanlines = if cmajr {width} else {height}; let pitch = if cmajr {height} else {width}; if compr { // compressed scanlines (transparency RLE) for _ in 0..scanlines { let fst = c_u16b(b, p)? as usize; let lst = c_u16b(b, p + 2)? as usize; if lst < fst || fst > pitch || lst > pitch { bail!("invalid compressed scanline"); } p += 4; for _ in 0..fst { bmp.cr.push(0); } let end = lst - fst; bmp.cr.extend_from_slice(c_data(b, p..p + end)?); for _ in lst..pitch { bmp.cr.push(0); } p += end; } } else { // simple copy bmp.cr.extend_from_slice(c_data(b, p..p + width * height)?); } Ok(bmp) } fn bmp_coll(b: &[u8], bmp_ofs: usize, bmp_num: usize) -> ResultS> { let mut bmps = Vec::with_capacity(bmp_num); let mut p = bmp_ofs; for _ in 0..bmp_num { let ofs = c_u32b(b, p)? as usize; bmps.push(bitmap(c_data(b, ofs..)?)?); p += 4; } Ok(bmps) } /* fn frame(b: &[u8]) -> ResultS<()> { let flags = c_u16b(b, 0)?; let minlight = c_u32b(b, 2)?; let bmp_ind = c_u16b(b, 6)?; // orig_x = c_i16b(b, 8)?; // orig_y = c_i16b(b, 10)?; // key_x = c_i16b(b, 12)?; // key_y = c_i16b(b, 14)?; let wrl_l = c_i16b(b, 16)?; let wrl_r = c_i16b(b, 18)?; let wrl_t = c_i16b(b, 20)?; let wrl_b = c_i16b(b, 22)?; let wrl_x = c_i16b(b, 24)?; let wrl_y = c_i16b(b, 26)?; let flags = ok!(FrameFlags::from_bits(flags), "bad flag")?; let minlight = Fx32::from_bits(minlight); dbg!(flags); dbg!(minlight); dbg!(bmp_ind); dbg!(wrl_l); dbg!(wrl_r); dbg!(wrl_t); dbg!(wrl_b); dbg!(wrl_x); dbg!(wrl_y); Ok(()) } fn sequence(b: &[u8]) -> ResultS<()> { // sq_type = c_u16b(b, 0)?; // flags = c_u16b(b, 2)?; let name = c_data(b, 4..38)?; let v_type = c_u16b(b, 38)?; let frames = c_u16b(b, 40)?; let ticks = c_u16b(b, 42)?; let key = c_u16b(b, 44)?; let xfer = c_u16b(b, 46)?; let xfer_pd = c_u16b(b, 48)?; let snd_beg = c_u16b(b, 50)?; let snd_key = c_u16b(b, 52)?; let snd_end = c_u16b(b, 54)?; // xform = c_u16b(b, 56)?; let loop_f = c_u16b(b, 58)?; let name = mac_roman_conv(ok!(pascal_str(name), "bad string")?); let snd_beg = ObjID::from_repr(snd_beg); let snd_key = ObjID::from_repr(snd_key); let snd_end = ObjID::from_repr(snd_end); dbg!(name); dbg!(v_type); dbg!(frames); dbg!(ticks); dbg!(key); dbg!(xfer); dbg!(xfer_pd); dbg!(snd_beg); dbg!(snd_key); dbg!(snd_end); dbg!(loop_f); Ok(()) } */ fn collection(b: &[u8]) -> ResultS { let version = c_u16b(b, 0)?; let cl_type = c_u16b(b, 2)?; // flags = c_u16b(b, 4)?; let clr_num = c_u16b(b, 6)? as usize; let tab_num = c_u16b(b, 8)? as usize; let tab_ofs = c_u32b(b, 10)? as usize; let seq_num = c_u16b(b, 14)? as usize; let seq_ofs = c_u32b(b, 16)? as usize; let frm_num = c_u16b(b, 20)? as usize; let frm_ofs = c_u32b(b, 22)? as usize; let bmp_num = c_u16b(b, 26)? as usize; let bmp_ofs = c_u32b(b, 28)? as usize; // xform = c_i16b(b, 30)?; // size = c_u32b(b, 32)? as usize; let cl_type = CollectionType::from_repr(cl_type)?; if version != 3 { bail!("invalid collection definition"); } let tabs = tab_coll(c_data(b, tab_ofs..)?, tab_num, clr_num)?; let bmps = bmp_coll(b, bmp_ofs, bmp_num)?; // frms = frm_coll(c_data(b, frm_ofs..)?, frm_num)?; // seqs = seq_coll(c_data(b, seq_ofs..)?, seq_num)?; Ok(Collection{tabs, bmps /*, frms, seqs*/}) } pub fn testfn_dump_bitmaps(c: &Collection, i: usize) -> ResultS<()> { use std::{fs, io}; // TODO: output with all CLUTs too for (j, bmp) in c.bmps.iter().enumerate() { let out = fs::File::create(&format!("out/shape{}_{}.tga", i, j))?; let mut out = io::BufWriter::new(out); write_tga(&mut out, &ImageShp{bmp: bmp.clone(), clut: &c.tabs[0]})?; } Ok(()) } pub fn testfn_replaceme(b: &[u8]) -> ResultS> { let mut cl = Vec::with_capacity(32); let mut p = 0; for _ in 0..32 { // status = c_u16b(b, p + 0)?; // flags = c_u16b(b, p + 2)?; let lo_ofs = c_u32b(b, p + 4)? as usize; let lo_len = c_u32b(b, p + 8)? as usize; let hi_ofs = c_u32b(b, p + 12)? as usize; let hi_len = c_u32b(b, p + 16)? as usize; let c_lo = if lo_ofs != u32::max_value() as usize { Some(collection(c_data(b, lo_ofs..lo_ofs + lo_len)?)?) } else { None }; let c_hi = if hi_ofs != u32::max_value() as usize { Some(collection(c_data(b, hi_ofs..hi_ofs + hi_len)?)?) } else { None }; cl.push((c_lo, c_hi)); p += 32; } Ok(cl) } impl Bitmap { fn new(w: usize, h: usize, alpha: bool, cmajr: bool) -> Self { Self{w, h, alpha, cmajr, cr: Vec::with_capacity(w * h)} } } impl Image for ImageShp<'_> { type Output = ColorShp; fn w(&self) -> usize {self.bmp.w} fn h(&self) -> usize {self.bmp.h} fn index(&self, x: usize, y: usize) -> &Self::Output { static TRANSLUCENT_COLOR: ColorShp = ColorShp::Translucent; let cr = if self.bmp.cmajr { self.bmp.cr[y + x * self.bmp.h] as usize } else { self.bmp.cr[x + y * self.bmp.w] as usize }; if self.bmp.alpha && cr == 0 { &TRANSLUCENT_COLOR } else { self.clut.get(cr).unwrap_or(&TRANSLUCENT_COLOR) } } } impl Color for ColorShp { fn r(&self) -> u16 { match *self { ColorShp::Translucent => 0, ColorShp::Opaque{r, ..} => r, } } fn g(&self) -> u16 { match *self { ColorShp::Translucent => 0, ColorShp::Opaque{g, ..} => g, } } fn b(&self) -> u16 { match *self { ColorShp::Translucent => 0, ColorShp::Opaque{b, ..} => b, } } fn a(&self) -> u16 { match *self { ColorShp::Translucent => 0, ColorShp::Opaque{..} => u16::max_value(), } } } #[derive(Clone, Debug)] pub enum ColorShp { Translucent, Opaque { r: u16, g: u16, b: u16, l: bool, }, } #[derive(Clone, Debug)] pub struct Bitmap { w: usize, h: usize, cr: Vec, alpha: bool, cmajr: bool, } #[derive(Debug)] pub struct ImageShp<'a> { bmp: Bitmap, clut: &'a [ColorShp], } #[derive(Debug)] pub struct Collection { pub tabs: Vec>, pub bmps: Vec, } pub type CollectionDef = (Option, Option); bitflags! { pub struct BmpFlags: u16 { const ColumnMajor = 0x80_00; const Transparent = 0x40_00; } } bitflags! { pub struct FrameFlags: u16 { const Obscure = 0x20_00; const FlipY = 0x40_00; const FlipX = 0x80_00; } } c_enum! { #[derive(Debug)] pub enum CollectionType: u16 { 0 => Unused, 1 => Wall, 2 => Object, 3 => Interface, 4 => Scenery, } } // EOF