Maraiah/src/marathon/shp.rs

433 lines
9.3 KiB
Rust

//! Marathon Shapes format handling.
use crate::{durandal::{bin::*, err::*, fx32::*, image::*, text::*},
marathon::xfer::TransferMode};
use bitflags::bitflags;
use serde::Serialize;
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<Vec<Vec<ColorShp>>>
{
let mut tabs = vec![vec![ColorShp::Translucent; clr_num]; tab_num];
let mut p = 0;
for clut in tabs.iter_mut().take(tab_num) {
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<Bitmap>
{
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], ofs: usize, num: usize) -> ResultS<Vec<Bitmap>>
{
let mut bmps = Vec::with_capacity(num);
let mut p = ofs;
for _ in 0..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<Frame>
{
let flags = c_u16b(b, 0)?;
let min_lt = c_u32b(b, 2)?;
let bmp_ind = c_u16b(b, 6)? as usize;
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 min_lt = Fx32::from_bits(min_lt);
Ok(Frame{flags, min_lt, bmp_ind, wrl_l, wrl_r, wrl_t, wrl_b, wrl_x, wrl_y})
}
fn frm_coll(b: &[u8], ofs: usize, num: usize) -> ResultS<Vec<Frame>>
{
let mut frms = Vec::with_capacity(num);
let mut p = ofs;
for _ in 0..num {
let ofs = c_u32b(b, p)? as usize;
frms.push(frame(c_data(b, ofs..)?)?);
p += 4;
}
Ok(frms)
}
fn sequence(b: &[u8]) -> ResultS<Sequence>
{
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)?;
let loop_f = c_u16b(b, 58)?;
let name = mac_roman_conv(ok!(pascal_str(name), "bad string")?);
let v_type = ViewType::from_repr(v_type)?;
let xfer = TransferMode::from_repr(xfer)?;
let snd_beg = ObjID::from_repr(snd_beg);
let snd_key = ObjID::from_repr(snd_key);
let snd_end = ObjID::from_repr(snd_end);
Ok(Sequence{name, v_type, frames, ticks, key, xfer, xfer_pd, snd_beg,
snd_key, snd_end, loop_f})
}
fn seq_coll(b: &[u8], ofs: usize, num: usize) -> ResultS<Vec<Sequence>>
{
let mut seqs = Vec::with_capacity(num);
let mut p = ofs;
for _ in 0..num {
let ofs = c_u32b(b, p)? as usize;
seqs.push(sequence(c_data(b, ofs..)?)?);
p += 4;
}
Ok(seqs)
}
fn collection(b: &[u8]) -> ResultS<Collection>
{
let version = c_u16b(b, 0)?;
let cl_type = c_u16b(b, 2)?;
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;
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)?;
let frms = frm_coll(b, frm_ofs, frm_num)?;
let seqs = seq_coll(b, seq_ofs, seq_num)?;
Ok(Collection{ctyp: cl_type, tabs, bmps, frms, seqs})
}
pub fn read_shapes(b: &[u8]) -> ResultS<Vec<CollectionDef>>
{
let mut cl = Vec::with_capacity(32);
let mut p = 0;
for _ in 0..32 {
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<'a, 'b> ImageShp<'a, 'b>
{
pub fn new(bmp: &'a Bitmap, clut: &'b [ColorShp]) -> Self
{
Self{bmp, clut}
}
}
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, Serialize)]
pub enum ColorShp
{
Translucent,
Opaque
{
r: u16,
g: u16,
b: u16,
l: bool,
},
}
#[derive(Debug)]
pub struct Bitmap
{
w: usize,
h: usize,
cr: Vec<u8>,
alpha: bool,
cmajr: bool,
}
pub struct ImageShp<'a, 'b>
{
bmp: &'a Bitmap,
clut: &'b [ColorShp],
}
#[derive(Debug, Serialize)]
pub struct Frame
{
flags: FrameFlags,
min_lt: Fx32,
bmp_ind: usize,
wrl_l: i16,
wrl_r: i16,
wrl_t: i16,
wrl_b: i16,
wrl_x: i16,
wrl_y: i16,
}
#[derive(Debug, Serialize)]
pub struct Sequence
{
name: String,
v_type: ViewType,
frames: u16,
ticks: u16,
key: u16,
xfer: TransferMode,
xfer_pd: u16,
snd_beg: ObjID,
snd_key: ObjID,
snd_end: ObjID,
loop_f: u16,
}
#[derive(Debug)]
pub struct Collection
{
pub ctyp: CollectionType,
pub tabs: Vec<Vec<ColorShp>>,
pub bmps: Vec<Bitmap>,
pub frms: Vec<Frame>,
pub seqs: Vec<Sequence>,
}
pub type CollectionDef = (Option<Collection>, Option<Collection>);
bitflags! {
pub struct BmpFlags: u16
{
const ColumnMajor = 0x80_00;
const Transparent = 0x40_00;
}
}
bitflags! {
#[derive(Serialize)]
pub struct FrameFlags: u16
{
const Obscure = 0x20_00;
const FlipY = 0x40_00;
const FlipX = 0x80_00;
}
}
c_enum! {
#[derive(Debug, Serialize)]
pub enum CollectionType: u16
{
0 => Unused,
1 => Wall,
2 => Object,
3 => Interface,
4 => Scenery,
}
}
c_enum! {
#[derive(Debug, Serialize)]
pub enum ViewType: u16
{
1 => Anim,
3 => Anim4from3,
4 => Anim4,
9 => Anim5from3,
11 => Anim5,
2 => Anim8from2,
5 => Anim8from5,
8 => Anim8,
10 => Still,
}
}
// EOF