Maraiah/source/marathon/shp.rs

505 lines
12 KiB
Rust
Raw Normal View History

2019-02-09 11:02:23 -08:00
//! Marathon Shapes format handling.
2019-02-16 10:08:50 -08:00
use crate::{durandal::{bin::*, err::*, fixed::*, image::*, text::*},
2019-02-12 05:33:23 -08:00
marathon::xfer::TransferMode};
2019-02-09 11:02:23 -08:00
use bitflags::bitflags;
2019-03-01 01:27:14 -08:00
/// Reads a color from a color table into `clut`.
fn read_color(b: &[u8], clut: &mut [ColorShp]) -> ResultS<()>
2019-02-09 11:02:23 -08:00
{
2019-02-18 20:06:34 -08:00
read_data! {
8, BE in b =>
2019-03-09 14:03:20 -08:00
flag = u8[0];
ind = u8[1];
r = u16[2];
g = u16[4];
b = u16[6];
2019-02-18 20:06:34 -08:00
}
2019-03-09 14:03:20 -08:00
let cr = ok!(clut.get_mut(usize::from(ind)), "bad index")?;
*cr = match flag {
128 => ColorShp::Lit {r, g, b},
0 => ColorShp::Opaque{r, g, b},
_ => {
return Err(err_msg("invalid flag in color"));
}
};
2019-02-09 11:02:23 -08:00
2019-02-18 20:18:08 -08:00
Ok(())
2019-02-10 20:29:12 -08:00
}
2019-02-09 11:02:23 -08:00
2019-03-01 01:27:14 -08:00
/// Reads all color tables.
2019-03-04 20:57:25 -08:00
fn color_tables(b: &[u8],
2019-03-01 01:27:14 -08:00
tab_ofs: usize,
tab_num: usize,
clr_num: usize)
2019-03-02 21:44:45 -08:00
-> ResultS<Vec<Vec<ColorShp>>>
2019-02-10 20:29:12 -08:00
{
2019-02-18 20:06:34 -08:00
let end = tab_num * clr_num * 8;
2019-02-10 20:29:12 -08:00
2019-03-04 20:57:25 -08:00
let b = ok!(b.get(tab_ofs..tab_ofs + end), "bad offset")?;
2019-02-18 20:06:34 -08:00
let mut v = vec![vec![ColorShp::Translucent; clr_num]; tab_num];
let mut p = 0;
2019-02-10 20:29:12 -08:00
2019-02-18 20:06:34 -08:00
for clut in v.iter_mut().take(tab_num) {
for _ in 0..clr_num {
2019-03-04 20:57:25 -08:00
read_color(ok!(b.get(p..p + 8), "not enough data")?, clut)?;
2019-02-10 20:29:12 -08:00
p += 8;
}
}
2019-02-18 20:06:34 -08:00
Ok(v)
2019-02-09 11:02:23 -08:00
}
2019-03-01 01:27:14 -08:00
/// Reads a `Bitmap`.
pub fn read_bitmap(b: &[u8]) -> ResultS<Bitmap>
2019-02-11 03:29:01 -08:00
{
2019-02-18 20:06:34 -08:00
read_data! {
26, BE in b =>
2019-03-02 18:31:00 -08:00
width = u16[0] usize;
height = u16[2] usize;
2019-02-18 20:06:34 -08:00
compr = u16[4];
flags = u16[6];
depth = u16[8];
}
2019-02-20 18:33:57 -08:00
let compr = compr == u16::max_value();
2019-02-21 13:12:26 -08:00
let flags = flag_ok!(BmpFlags, flags)?;
2019-03-07 00:12:00 -08:00
let alpha = flags.contains(BmpFlags::TRANSPARENT);
let cmajr = flags.contains(BmpFlags::COLUMN_MAJOR);
2019-02-11 03:29:01 -08:00
2019-02-11 07:33:10 -08:00
if depth != 8 {
2019-02-11 03:29:01 -08:00
bail!("invalid bit depth (should always be 8)");
}
2019-02-12 02:31:20 -08:00
let mut bmp = Bitmap::new(width, height, alpha, cmajr);
let mut p = 30 + if cmajr {4 * width} else {4 * height};
2019-02-11 03:29:01 -08:00
2019-02-12 02:31:20 -08:00
let scanlines = if cmajr {width} else {height};
let pitch = if cmajr {height} else {width};
2019-02-11 03:29:01 -08:00
2019-02-12 02:31:20 -08:00
if compr {
// compressed scanlines (transparency RLE)
for _ in 0..scanlines {
2019-02-18 20:06:34 -08:00
read_data! {
p + 4, BE in b =>
2019-03-02 18:31:00 -08:00
fst = u16[p] usize;
lst = u16[p + 2] usize;
2019-02-12 02:31:20 -08:00
}
2019-02-18 20:06:34 -08:00
let end = lst - fst;
2019-02-12 02:31:20 -08:00
p += 4;
2019-02-18 20:18:08 -08:00
if lst < fst || fst > pitch || lst > pitch || b.len() < p + end {
2019-02-18 20:06:34 -08:00
bail!("invalid compressed scanline");
}
2019-02-12 02:31:20 -08:00
for _ in 0..fst {
bmp.cr.push(0);
}
2019-02-11 03:29:01 -08:00
2019-03-04 20:57:25 -08:00
bmp.cr.extend_from_slice(ok!(b.get(p..p + end), "not enough data")?);
2019-02-12 02:31:20 -08:00
for _ in lst..pitch {
bmp.cr.push(0);
}
p += end;
2019-02-11 03:29:01 -08:00
}
2019-02-12 02:31:20 -08:00
} else {
// simple copy
2019-02-18 20:18:08 -08:00
if b.len() < p + width * height {
2019-02-18 20:06:34 -08:00
bail!("invalid scanline");
}
2019-03-04 20:57:25 -08:00
bmp.cr.extend_from_slice(ok!(b.get(p..p + width * height),
"not enough data")?);
2019-02-11 03:29:01 -08:00
}
2019-02-12 02:31:20 -08:00
Ok(bmp)
2019-02-11 03:29:01 -08:00
}
2019-03-01 01:27:14 -08:00
/// Reads a `Frame`.
pub fn read_frame(b: &[u8]) -> ResultS<Frame>
2019-02-11 03:29:01 -08:00
{
2019-02-18 20:06:34 -08:00
read_data! {
36, BE in b =>
flags = u16[0];
2019-02-20 20:11:57 -08:00
min_lt = Fixed[2];
2019-03-02 18:31:00 -08:00
bmp_ind = u16[6] usize;
2019-02-20 20:11:57 -08:00
wrl_l = Unit[16];
wrl_r = Unit[18];
wrl_t = Unit[20];
wrl_b = Unit[22];
wrl_x = Unit[24];
wrl_y = Unit[26];
2019-02-11 03:29:01 -08:00
}
2019-02-21 13:12:26 -08:00
let flags = flag_ok!(FrameFlags, flags)?;
2019-02-12 03:32:10 -08:00
Ok(Frame{flags, min_lt, bmp_ind, wrl_l, wrl_r, wrl_t, wrl_b, wrl_x, wrl_y})
}
2019-02-09 11:02:23 -08:00
2019-03-01 01:27:14 -08:00
/// Reads a `Sequence`.
pub fn read_sequence(b: &[u8]) -> ResultS<Sequence>
2019-02-12 03:32:10 -08:00
{
2019-02-18 20:06:34 -08:00
read_data! {
88, BE in b =>
2019-03-02 18:31:00 -08:00
name = u8[4..38] slice;
2019-02-18 20:06:34 -08:00
v_type = u16[38];
frames = u16[40];
ticks = u16[42];
key = u16[44];
xfer = u16[46];
xfer_pd = u16[48];
2019-02-21 14:11:48 -08:00
snd_beg = OptU16[50];
snd_key = OptU16[52];
snd_end = OptU16[54];
2019-02-18 20:06:34 -08:00
loop_f = u16[58];
2019-02-12 03:32:10 -08:00
}
2019-02-21 13:12:26 -08:00
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)?;
2019-02-09 11:02:23 -08:00
2019-02-12 03:32:10 -08:00
Ok(Sequence{name, v_type, frames, ticks, key, xfer, xfer_pd, snd_beg,
snd_key, snd_end, loop_f})
}
2019-02-09 11:02:23 -08:00
2019-03-01 01:27:14 -08:00
/// Reads a `Collection`.
pub fn read_collection(b: &[u8]) -> ResultS<Collection>
2019-02-12 03:32:10 -08:00
{
2019-02-18 20:06:34 -08:00
read_data! {
544, BE in b =>
version = u16[0];
cl_type = u16[2];
2019-03-02 18:31:00 -08:00
clr_num = u16[6] usize;
tab_num = u16[8] usize;
tab_ofs = u32[10] usize;
seq_num = u16[14] usize;
seq_ofs = u32[16] usize;
frm_num = u16[20] usize;
frm_ofs = u32[22] usize;
bmp_num = u16[26] usize;
bmp_ofs = u32[28] usize;
2019-02-12 03:32:10 -08:00
}
2019-02-09 11:02:23 -08:00
let cl_type = CollectionType::from_repr(cl_type)?;
if version != 3 {
2019-02-12 02:31:20 -08:00
bail!("invalid collection definition");
2019-02-09 11:02:23 -08:00
}
2019-02-18 20:06:34 -08:00
let tabs = color_tables(b, tab_ofs, tab_num, clr_num)?;
2019-03-01 01:27:14 -08:00
let bmps = rd_ofstable(b, bmp_ofs, bmp_num, read_bitmap)?;
let frms = rd_ofstable(b, frm_ofs, frm_num, read_frame)?;
let seqs = rd_ofstable(b, seq_ofs, seq_num, read_sequence)?;
2019-02-09 11:02:23 -08:00
2019-02-12 03:32:10 -08:00
Ok(Collection{ctyp: cl_type, tabs, bmps, frms, seqs})
2019-02-12 02:31:20 -08:00
}
2019-02-09 11:02:23 -08:00
2019-03-01 01:27:14 -08:00
/// Read all of the collections in a Shapes file.
2019-02-12 06:41:28 -08:00
pub fn read_shapes(b: &[u8]) -> ResultS<Vec<CollectionDef>>
2019-02-09 11:02:23 -08:00
{
2019-02-12 02:31:20 -08:00
let mut cl = Vec::with_capacity(32);
let mut p = 0;
for _ in 0..32 {
2019-02-18 20:06:34 -08:00
read_data! {
p + 32, BE in b =>
2019-03-02 18:31:00 -08:00
lo_ofs = u32[p + 4] usize;
lo_len = u32[p + 8] usize;
hi_ofs = u32[p + 12] usize;
hi_len = u32[p + 16] usize;
2019-02-18 20:06:34 -08:00
}
2019-02-12 02:31:20 -08:00
2019-03-02 18:31:00 -08:00
let c_lo = if lo_ofs == usize_from_u32(u32::max_value()) {
2019-02-12 02:31:20 -08:00
None
2019-02-24 20:34:59 -08:00
} else {
2019-03-04 20:57:25 -08:00
let dat = ok!(b.get(lo_ofs..lo_ofs + lo_len), "bad offset")?;
Some(read_collection(dat)?)
2019-02-12 02:31:20 -08:00
};
2019-03-02 18:31:00 -08:00
let c_hi = if hi_ofs == usize_from_u32(u32::max_value()) {
2019-02-12 02:31:20 -08:00
None
2019-02-24 20:34:59 -08:00
} else {
2019-03-04 20:57:25 -08:00
let dat = ok!(b.get(hi_ofs..hi_ofs + hi_len), "bad offset")?;
Some(read_collection(dat)?)
2019-02-12 02:31:20 -08:00
};
cl.push((c_lo, c_hi));
p += 32;
2019-02-10 20:29:12 -08:00
}
2019-02-12 02:31:20 -08:00
Ok(cl)
2019-02-10 20:29:12 -08:00
}
2019-02-12 02:31:20 -08:00
impl Bitmap
2019-02-11 03:29:01 -08:00
{
2019-03-01 01:27:14 -08:00
/// Creates an empty bitmap.
pub fn new(w: usize, h: usize, alpha: bool, cmajr: bool) -> Self
2019-02-11 03:29:01 -08:00
{
2019-02-12 02:31:20 -08:00
Self{w, h, alpha, cmajr, cr: Vec::with_capacity(w * h)}
2019-02-11 03:29:01 -08:00
}
}
2019-02-12 06:41:28 -08:00
impl<'a, 'b> ImageShp<'a, 'b>
{
2019-03-01 01:27:14 -08:00
/// Creates an `ImageShp` with the given bitmap.
2019-02-12 06:41:28 -08:00
pub fn new(bmp: &'a Bitmap, clut: &'b [ColorShp]) -> Self
{
Self{bmp, clut}
}
}
2019-02-12 03:32:10 -08:00
impl Image for ImageShp<'_, '_>
2019-02-10 20:29:12 -08:00
{
type Output = ColorShp;
2019-02-12 02:31:20 -08:00
fn w(&self) -> usize {self.bmp.w}
fn h(&self) -> usize {self.bmp.h}
2019-02-10 20:29:12 -08:00
2019-02-11 05:44:20 -08:00
fn index(&self, x: usize, y: usize) -> &Self::Output
2019-02-10 20:29:12 -08:00
{
2019-02-11 03:29:01 -08:00
static TRANSLUCENT_COLOR: ColorShp = ColorShp::Translucent;
2019-03-02 18:31:00 -08:00
let cr = usize::from(if self.bmp.cmajr {
self.bmp.cr[y + x * self.bmp.h]
2019-02-12 02:31:20 -08:00
} else {
2019-03-02 18:31:00 -08:00
self.bmp.cr[x + y * self.bmp.w]
});
2019-02-11 03:29:01 -08:00
2019-02-12 02:31:20 -08:00
if self.bmp.alpha && cr == 0 {
2019-02-11 03:29:01 -08:00
&TRANSLUCENT_COLOR
} else {
self.clut.get(cr).unwrap_or(&TRANSLUCENT_COLOR)
}
2019-02-10 20:29:12 -08:00
}
}
impl Color for ColorShp
{
fn r(&self) -> u16
{
2019-02-11 03:29:01 -08:00
match *self {
ColorShp::Translucent => 0,
ColorShp::Opaque{r, ..} => r,
2019-03-09 14:03:20 -08:00
ColorShp::Lit {r, ..} => r,
2019-02-09 11:02:23 -08:00
}
2019-02-10 20:29:12 -08:00
}
fn g(&self) -> u16
{
2019-02-11 03:29:01 -08:00
match *self {
ColorShp::Translucent => 0,
ColorShp::Opaque{g, ..} => g,
2019-03-09 14:03:20 -08:00
ColorShp::Lit {g, ..} => g,
2019-02-09 11:02:23 -08:00
}
}
2019-02-10 20:29:12 -08:00
fn b(&self) -> u16
{
2019-02-11 03:29:01 -08:00
match *self {
ColorShp::Translucent => 0,
ColorShp::Opaque{b, ..} => b,
2019-03-09 14:03:20 -08:00
ColorShp::Lit {b, ..} => b,
2019-02-10 20:29:12 -08:00
}
}
fn a(&self) -> u16
{
2019-02-11 03:29:01 -08:00
match *self {
ColorShp::Translucent => 0,
ColorShp::Opaque{..} => u16::max_value(),
2019-03-09 14:03:20 -08:00
ColorShp::Lit {..} => u16::max_value(),
2019-02-10 20:29:12 -08:00
}
}
}
2019-03-01 01:27:14 -08:00
/// A color in an `ImageShp`'s color table.
2019-03-02 15:24:05 -08:00
#[derive(Copy, Clone, Debug, serde::Serialize)]
2019-02-12 02:31:20 -08:00
pub enum ColorShp
2019-02-10 20:29:12 -08:00
{
2019-03-09 14:03:20 -08:00
/// A completely translucent color.
2019-02-10 20:29:12 -08:00
Translucent,
2019-03-09 14:03:20 -08:00
/// An opaque color which may be shaded.
Opaque{/** The red component. */ r: u16,
/** The green component. */ g: u16,
/** The blue component. */ b: u16},
/// An opaque color which may not be shaded.
Lit{/** The red component. */ r: u16,
/** The green component. */ g: u16,
/** The blue component. */ b: u16},
2019-02-10 20:29:12 -08:00
}
2019-03-01 01:27:14 -08:00
/// An unpacked Shape bitmap.
2019-02-12 03:32:10 -08:00
#[derive(Debug)]
2019-02-12 02:31:20 -08:00
pub struct Bitmap
2019-02-10 20:29:12 -08:00
{
2019-02-11 03:29:01 -08:00
w: usize,
h: usize,
cr: Vec<u8>,
alpha: bool,
2019-02-12 02:31:20 -08:00
cmajr: bool,
2019-02-10 20:29:12 -08:00
}
2019-03-01 01:27:14 -08:00
/// An image from a Shape. This mainly just exists so that `Bitmap` can use the
/// `Image` trait.
2019-02-12 03:32:10 -08:00
pub struct ImageShp<'a, 'b>
{
bmp: &'a Bitmap,
clut: &'b [ColorShp],
}
2019-03-01 01:27:14 -08:00
/// A frame, also known as a low level shape.
2019-03-01 03:22:27 -08:00
#[derive(Debug, serde::Serialize)]
2019-02-12 03:32:10 -08:00
pub struct Frame
{
2019-03-09 14:03:20 -08:00
/// The flags for this frame.
pub flags: FrameFlags,
/// The minimum light level for this frame.
pub min_lt: Fixed,
/// The index of the bitmap this frame uses.
pub bmp_ind: usize,
/// The left translation for this frame.
pub wrl_l: Unit,
/// The right translation for this frame.
pub wrl_r: Unit,
/// The top translation for this frame.
pub wrl_t: Unit,
/// The bottom translation for this frame.
pub wrl_b: Unit,
/// The X translation for this frame.
pub wrl_x: Unit,
/// The Y translation for this frame.
pub wrl_y: Unit,
2019-02-12 03:32:10 -08:00
}
2019-03-01 01:27:14 -08:00
/// A sequence, also known as a high level shape.
2019-03-01 03:22:27 -08:00
#[derive(Debug, serde::Serialize)]
2019-02-12 03:32:10 -08:00
pub struct Sequence
2019-02-10 20:29:12 -08:00
{
2019-03-09 14:03:20 -08:00
/// The display name for this sequence.
pub name: String,
/// The view type for each frame in this sequence.
pub v_type: ViewType,
/// The number of frames in this sequence.
pub frames: u16,
/// The number of ticks each frame in this sequence takes.
pub ticks: u16,
/// The key frame index for this sequence.
pub key: u16,
/// The transfer mode to play over this sequence.
pub xfer: TransferMode,
/// The period in game ticks the transfer mode plays over.
pub xfer_pd: u16,
/// The sound to play at the beginning of this sequence.
pub snd_beg: OptU16,
/// The sound to play at the key frame of this sequence.
pub snd_key: OptU16,
/// The sound to play at the end of this sequence.
pub snd_end: OptU16,
/// Which frame to loop on.
pub loop_f: u16,
2019-02-09 11:02:23 -08:00
}
2019-03-01 01:27:14 -08:00
/// A collection of color tables, bitmaps, frames and sequences.
2019-02-12 02:31:20 -08:00
#[derive(Debug)]
pub struct Collection
{
2019-03-09 14:03:20 -08:00
/// The type of collection this is.
2019-02-12 03:32:10 -08:00
pub ctyp: CollectionType,
2019-03-09 14:03:20 -08:00
/// All of the color tables in this collection.
2019-02-12 02:31:20 -08:00
pub tabs: Vec<Vec<ColorShp>>,
2019-03-09 14:03:20 -08:00
/// All of the bitmaps in this collection.
2019-02-12 02:31:20 -08:00
pub bmps: Vec<Bitmap>,
2019-03-09 14:03:20 -08:00
/// All of the frames in this collection.
2019-02-12 03:32:10 -08:00
pub frms: Vec<Frame>,
2019-03-09 14:03:20 -08:00
/// All of the sequences in this collection.
2019-02-12 03:32:10 -08:00
pub seqs: Vec<Sequence>,
2019-02-12 02:31:20 -08:00
}
2019-03-01 01:27:14 -08:00
/// A collection, which may have low- and high-definition variations, or none.
2019-02-12 02:31:20 -08:00
pub type CollectionDef = (Option<Collection>, Option<Collection>);
2019-02-11 03:29:01 -08:00
bitflags! {
2019-02-16 09:07:28 -08:00
struct BmpFlags: u16
2019-02-11 03:29:01 -08:00
{
2019-03-07 00:12:00 -08:00
const TRANSPARENT = 1 << 14;
const COLUMN_MAJOR = 1 << 15;
2019-02-11 03:29:01 -08:00
}
}
2019-02-09 11:02:23 -08:00
bitflags! {
2019-03-01 01:27:14 -08:00
/// Flags for `Frame`.
2019-03-01 03:22:27 -08:00
#[derive(serde::Serialize)]
2019-02-09 11:02:23 -08:00
pub struct FrameFlags: u16
{
2019-03-09 14:03:20 -08:00
/// The player's torso will obscure the player's legs.
2019-03-07 00:12:00 -08:00
const OBSCURE = 1 << 13;
2019-03-09 14:03:20 -08:00
/// The bitmap will be flipped on the vertical axis.
2019-03-07 00:12:00 -08:00
const FLIP_Y = 1 << 14;
2019-03-09 14:03:20 -08:00
/// The bitmap will be flipped on the horizontal axis.
2019-03-07 00:12:00 -08:00
const FLIP_X = 1 << 15;
2019-02-09 11:02:23 -08:00
}
}
c_enum! {
2019-03-01 01:27:14 -08:00
/// The type of a collection.
2019-03-01 03:22:27 -08:00
#[derive(Debug, serde::Serialize)]
2019-02-09 11:02:23 -08:00
pub enum CollectionType: u16
{
0 => Unused,
1 => Wall,
2 => Object,
3 => Interface,
4 => Scenery,
}
}
2019-02-12 03:32:10 -08:00
c_enum! {
2019-03-01 01:27:14 -08:00
/// The type of or number of views for a sequence.
2019-03-01 03:22:27 -08:00
#[derive(Debug, serde::Serialize)]
2019-02-12 03:32:10 -08:00
pub enum ViewType: u16
{
1 => Anim,
3 => Anim4from3,
4 => Anim4,
9 => Anim5from3,
11 => Anim5,
2 => Anim8from2,
5 => Anim8from5,
8 => Anim8,
10 => Still,
}
}
2019-02-09 11:02:23 -08:00
// EOF