450 lines
9.2 KiB
Rust
450 lines
9.2 KiB
Rust
//! Marathon Shapes format handling.
|
|
|
|
use crate::{durandal::{bin::*, err::*, fixed::*, image::*, text::*},
|
|
marathon::xfer::TransferMode};
|
|
use bitflags::bitflags;
|
|
use serde::Serialize;
|
|
|
|
fn color(b: &[u8], clut: &mut [ColorShp]) -> ResultS<()>
|
|
{
|
|
read_data! {
|
|
8, BE in b =>
|
|
l = u8[0];
|
|
i = u8[1];
|
|
r = u16[2];
|
|
g = u16[4];
|
|
b = u16[6];
|
|
}
|
|
|
|
let l = match l {
|
|
128 => Ok(true),
|
|
0 => Ok(false),
|
|
_ => Err(err_msg("invalid flag in color")),
|
|
}?;
|
|
|
|
let cr = ColorShp::Opaque{r, g, b, l};
|
|
|
|
if let Some(cl) = clut.get_mut(i as usize) {
|
|
*cl = cr;
|
|
Ok(())
|
|
} else {
|
|
bail!("bad index");
|
|
}
|
|
}
|
|
|
|
fn color_tables(b: &[u8],
|
|
tab_ofs: usize,
|
|
tab_num: usize,
|
|
clr_num: usize)
|
|
-> ResultS<Vec<Vec<ColorShp>>>
|
|
{
|
|
let end = tab_num * clr_num * 8;
|
|
|
|
if b.len() < tab_ofs + end {
|
|
bail!("not enough data");
|
|
}
|
|
|
|
let b = &b[tab_ofs..tab_ofs + end];
|
|
|
|
let mut v = vec![vec![ColorShp::Translucent; clr_num]; tab_num];
|
|
let mut p = 0;
|
|
|
|
for clut in v.iter_mut().take(tab_num) {
|
|
for _ in 0..clr_num {
|
|
color(&b[p..p + 8], clut)?;
|
|
p += 8;
|
|
}
|
|
}
|
|
|
|
Ok(v)
|
|
}
|
|
|
|
fn bitmap(b: &[u8]) -> ResultS<Bitmap>
|
|
{
|
|
read_data! {
|
|
26, BE in b =>
|
|
width = u16[0] as usize;
|
|
height = u16[2] as usize;
|
|
compr = u16[4];
|
|
flags = u16[6];
|
|
depth = u16[8];
|
|
}
|
|
|
|
let compr = compr == u16::max_value();
|
|
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 {
|
|
read_data! {
|
|
p + 4, BE in b =>
|
|
fst = u16[p] as usize;
|
|
lst = u16[p + 2] as usize;
|
|
}
|
|
let end = lst - fst;
|
|
|
|
p += 4;
|
|
|
|
if lst < fst || fst > pitch || lst > pitch || p + end >= b.len() {
|
|
bail!("invalid compressed scanline");
|
|
}
|
|
|
|
for _ in 0..fst {
|
|
bmp.cr.push(0);
|
|
}
|
|
|
|
bmp.cr.extend_from_slice(&b[p..p + end]);
|
|
|
|
for _ in lst..pitch {
|
|
bmp.cr.push(0);
|
|
}
|
|
|
|
p += end;
|
|
}
|
|
} else {
|
|
// simple copy
|
|
if p + width * height >= b.len() {
|
|
bail!("invalid scanline");
|
|
}
|
|
|
|
bmp.cr.extend_from_slice(&b[p..p + width * height]);
|
|
}
|
|
|
|
Ok(bmp)
|
|
}
|
|
|
|
fn frame(b: &[u8]) -> ResultS<Frame>
|
|
{
|
|
read_data! {
|
|
36, BE in b =>
|
|
flags = u16[0];
|
|
min_lt = u32[2];
|
|
bmp_ind = u16[6] as usize;
|
|
wrl_l = u16[16];
|
|
wrl_r = u16[18];
|
|
wrl_t = u16[20];
|
|
wrl_b = u16[22];
|
|
wrl_x = u16[24];
|
|
wrl_y = u16[26];
|
|
}
|
|
|
|
let flags = ok!(FrameFlags::from_bits(flags), "bad flag")?;
|
|
let min_lt = Fixed::from_bits(min_lt);
|
|
let wrl_l = Unit::from_bits(wrl_l);
|
|
let wrl_r = Unit::from_bits(wrl_r);
|
|
let wrl_t = Unit::from_bits(wrl_t);
|
|
let wrl_b = Unit::from_bits(wrl_b);
|
|
let wrl_x = Unit::from_bits(wrl_x);
|
|
let wrl_y = Unit::from_bits(wrl_y);
|
|
|
|
Ok(Frame{flags, min_lt, bmp_ind, wrl_l, wrl_r, wrl_t, wrl_b, wrl_x, wrl_y})
|
|
}
|
|
|
|
fn sequence(b: &[u8]) -> ResultS<Sequence>
|
|
{
|
|
read_data! {
|
|
88, BE in b =>
|
|
name = u8[4..38] array;
|
|
v_type = u16[38];
|
|
frames = u16[40];
|
|
ticks = u16[42];
|
|
key = u16[44];
|
|
xfer = u16[46];
|
|
xfer_pd = u16[48];
|
|
snd_beg = u16[50];
|
|
snd_key = u16[52];
|
|
snd_end = u16[54];
|
|
loop_f = u16[58];
|
|
}
|
|
|
|
let name = mac_roman_conv(ok!(pascal_str(name), "bad string")?);
|
|
let xfer = TransferMode::from_repr(xfer)?;
|
|
let v_type = ViewType::from_repr(v_type)?;
|
|
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 collection(b: &[u8]) -> ResultS<Collection>
|
|
{
|
|
read_data! {
|
|
544, BE in b =>
|
|
version = u16[0];
|
|
cl_type = u16[2];
|
|
clr_num = u16[6] as usize;
|
|
tab_num = u16[8] as usize;
|
|
tab_ofs = u32[10] as usize;
|
|
seq_num = u16[14] as usize;
|
|
seq_ofs = u32[16] as usize;
|
|
frm_num = u16[20] as usize;
|
|
frm_ofs = u32[22] as usize;
|
|
bmp_num = u16[26] as usize;
|
|
bmp_ofs = u32[28] as usize;
|
|
}
|
|
|
|
let cl_type = CollectionType::from_repr(cl_type)?;
|
|
|
|
if version != 3 {
|
|
bail!("invalid collection definition");
|
|
}
|
|
|
|
let tabs = color_tables(b, tab_ofs, tab_num, clr_num)?;
|
|
let bmps = rd_ofstable(b, bmp_ofs, bmp_num, bitmap)?;
|
|
let frms = rd_ofstable(b, frm_ofs, frm_num, frame)?;
|
|
let seqs = rd_ofstable(b, seq_ofs, seq_num, sequence)?;
|
|
|
|
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 {
|
|
read_data! {
|
|
p + 32, BE in b =>
|
|
lo_ofs = u32[p + 4] as usize;
|
|
lo_len = u32[p + 8] as usize;
|
|
hi_ofs = u32[p + 12] as usize;
|
|
hi_len = u32[p + 16] as usize;
|
|
}
|
|
|
|
let c_lo = if lo_ofs != u32::max_value() as usize {
|
|
if b.len() < lo_ofs + lo_len {
|
|
bail!("not enough data");
|
|
} else {
|
|
Some(collection(&b[lo_ofs..lo_ofs + lo_len])?)
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let c_hi = if hi_ofs != u32::max_value() as usize {
|
|
if b.len() < hi_ofs + hi_len {
|
|
bail!("not enough data");
|
|
} else {
|
|
Some(collection(&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: Fixed,
|
|
bmp_ind: usize,
|
|
wrl_l: Unit,
|
|
wrl_r: Unit,
|
|
wrl_t: Unit,
|
|
wrl_b: Unit,
|
|
wrl_x: Unit,
|
|
wrl_y: Unit,
|
|
}
|
|
|
|
#[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! {
|
|
struct BmpFlags: u16
|
|
{
|
|
const Transparent = 1 << 14;
|
|
const ColumnMajor = 1 << 15;
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
#[derive(Serialize)]
|
|
pub struct FrameFlags: u16
|
|
{
|
|
const Obscure = 1 << 13;
|
|
const FlipY = 1 << 14;
|
|
const FlipX = 1 << 15;
|
|
}
|
|
}
|
|
|
|
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
|