395 lines
8.6 KiB
Rust
395 lines
8.6 KiB
Rust
//! 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<Vec<Vec<ColorShp>>>
|
|
{
|
|
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<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], bmp_ofs: usize, bmp_num: usize) -> ResultS<Vec<Bitmap>>
|
|
{
|
|
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<Collection>
|
|
{
|
|
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<Vec<CollectionDef>>
|
|
{
|
|
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<u8>,
|
|
alpha: bool,
|
|
cmajr: bool,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ImageShp<'a>
|
|
{
|
|
bmp: Bitmap,
|
|
clut: &'a [ColorShp],
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Collection
|
|
{
|
|
pub tabs: Vec<Vec<ColorShp>>,
|
|
pub bmps: Vec<Bitmap>,
|
|
}
|
|
|
|
pub type CollectionDef = (Option<Collection>, Option<Collection>);
|
|
|
|
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
|