finish up module refactoring
parent
11ebd77e6a
commit
8c9415b867
|
@ -13,6 +13,7 @@
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use maraiah::{c_enum, durandal::err::ReprError};
|
/// use maraiah::{c_enum, durandal::err::ReprError};
|
||||||
|
/// use std::convert::TryFrom;
|
||||||
///
|
///
|
||||||
/// c_enum! {
|
/// c_enum! {
|
||||||
/// #[derive(Debug)]
|
/// #[derive(Debug)]
|
||||||
|
@ -52,14 +53,14 @@ macro_rules! c_enum
|
||||||
#[allow(unused_qualifications)]
|
#[allow(unused_qualifications)]
|
||||||
impl std::convert::TryFrom<$ti> for $t
|
impl std::convert::TryFrom<$ti> for $t
|
||||||
{
|
{
|
||||||
type Error = ReprError;
|
type Error = $crate::durandal::err::ReprError;
|
||||||
|
|
||||||
/// Returns, if representable, the variant of `Self` from `n`.
|
/// Returns, if representable, the variant of `Self` from `n`.
|
||||||
fn try_from(n: $ti) -> Result<Self, Self::Error>
|
fn try_from(n: $ti) -> Result<Self, Self::Error>
|
||||||
{
|
{
|
||||||
match n {
|
match n {
|
||||||
$($va => Ok($t::$en),)+
|
$($va => Ok($t::$en),)+
|
||||||
n => Err(ReprError::new(n))
|
n => Err(Self::Error::new(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +71,7 @@ macro_rules! c_enum
|
||||||
mod test
|
mod test
|
||||||
{
|
{
|
||||||
use crate::durandal::err::ReprError;
|
use crate::durandal::err::ReprError;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
c_enum! {
|
c_enum! {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use maraiah::{durandal::{err::*, file::*, image::*, sound::*},
|
use maraiah::{durandal::{err::*, file::*, image::*, sound::*},
|
||||||
marathon::{machdr, ppm, shp, snd, tga, wad, wav}};
|
marathon::{machdr, map, ppm, shp, snd, tga, wav}};
|
||||||
use std::{fs, io, slice::from_ref};
|
use std::{fs, io, slice::from_ref};
|
||||||
|
|
||||||
fn make_tga(_opt: &Options, fname: &str, im: &impl Image) -> ResultS<()>
|
fn make_tga(_opt: &Options, fname: &str, im: &impl Image) -> ResultS<()>
|
||||||
|
@ -37,7 +37,7 @@ fn make_wav(fname: &str, snd: &impl Sound) -> ResultS<()>
|
||||||
|
|
||||||
fn process_wad(opt: &Options, b: &[u8]) -> ResultS<()>
|
fn process_wad(opt: &Options, b: &[u8]) -> ResultS<()>
|
||||||
{
|
{
|
||||||
let wad = wad::read_wad(b)?;
|
let wad = map::read(b)?;
|
||||||
|
|
||||||
if opt.wad_wrt_all {
|
if opt.wad_wrt_all {
|
||||||
make_yaml(opt, &wad)?;
|
make_yaml(opt, &wad)?;
|
||||||
|
@ -46,7 +46,8 @@ fn process_wad(opt: &Options, b: &[u8]) -> ResultS<()>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_bitmaps(opt: &Options, c: &shp::Collection, i: usize) -> ResultS<()>
|
fn dump_bitmaps(opt: &Options, c: &shp::coll::Collection, i: usize)
|
||||||
|
-> ResultS<()>
|
||||||
{
|
{
|
||||||
if !opt.shp_bmp {
|
if !opt.shp_bmp {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -56,18 +57,18 @@ fn dump_bitmaps(opt: &Options, c: &shp::Collection, i: usize) -> ResultS<()>
|
||||||
if opt.shp_bmp_all {
|
if opt.shp_bmp_all {
|
||||||
for (k, tab) in c.tabs.iter().enumerate() {
|
for (k, tab) in c.tabs.iter().enumerate() {
|
||||||
let fname = format!("{}/shape{}_{}_{}.tga", opt.out_dir, i, j, k);
|
let fname = format!("{}/shape{}_{}_{}.tga", opt.out_dir, i, j, k);
|
||||||
make_tga(opt, &fname, &shp::ImageShp::new(bmp, &tab))?;
|
make_tga(opt, &fname, &shp::bmap::ImageShp::new(bmp, &tab))?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let fname = format!("{}/shape{}_{}.tga", opt.out_dir, i, j);
|
let fname = format!("{}/shape{}_{}.tga", opt.out_dir, i, j);
|
||||||
make_tga(opt, &fname, &shp::ImageShp::new(bmp, &c.tabs[0]))?;
|
make_tga(opt, &fname, &shp::bmap::ImageShp::new(bmp, &c.tabs[0]))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_shp_objs(opt: &Options, cl: &shp::Collection) -> ResultS<()>
|
fn write_shp_objs(opt: &Options, cl: &shp::coll::Collection) -> ResultS<()>
|
||||||
{
|
{
|
||||||
if opt.shp_tab {make_yaml(opt, &cl.tabs)?;}
|
if opt.shp_tab {make_yaml(opt, &cl.tabs)?;}
|
||||||
if opt.shp_frm {make_yaml(opt, &cl.frms)?;}
|
if opt.shp_frm {make_yaml(opt, &cl.frms)?;}
|
||||||
|
@ -78,7 +79,7 @@ fn write_shp_objs(opt: &Options, cl: &shp::Collection) -> ResultS<()>
|
||||||
|
|
||||||
fn process_shp(opt: &Options, b: &[u8]) -> ResultS<()>
|
fn process_shp(opt: &Options, b: &[u8]) -> ResultS<()>
|
||||||
{
|
{
|
||||||
for (i, cl) in shp::read_shapes(b)?.iter().enumerate() {
|
for (i, cl) in shp::read(b)?.iter().enumerate() {
|
||||||
if let Some(cl) = &cl.0 {
|
if let Some(cl) = &cl.0 {
|
||||||
dump_bitmaps(opt, cl, i)?;
|
dump_bitmaps(opt, cl, i)?;
|
||||||
write_shp_objs(opt, cl)?;
|
write_shp_objs(opt, cl)?;
|
||||||
|
@ -111,7 +112,7 @@ fn dump_sounds(opt: &Options, st: &snd::SoundTable, c: usize) -> ResultS<()>
|
||||||
|
|
||||||
fn process_snd(opt: &Options, b: &[u8]) -> ResultS<()>
|
fn process_snd(opt: &Options, b: &[u8]) -> ResultS<()>
|
||||||
{
|
{
|
||||||
for (c, st) in snd::read_sounds(b)?.iter().enumerate() {
|
for (c, st) in snd::read(b)?.iter().enumerate() {
|
||||||
dump_sounds(opt, st, c)?;
|
dump_sounds(opt, st, c)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,12 @@
|
||||||
pub mod defl;
|
pub mod defl;
|
||||||
pub mod machdr;
|
pub mod machdr;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
pub mod phy;
|
|
||||||
pub mod pict;
|
pub mod pict;
|
||||||
pub mod ppm;
|
pub mod ppm;
|
||||||
pub mod shp;
|
pub mod shp;
|
||||||
pub mod snd;
|
pub mod snd;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod tga;
|
pub mod tga;
|
||||||
pub mod wad;
|
|
||||||
pub mod wav;
|
pub mod wav;
|
||||||
pub mod xfer;
|
pub mod xfer;
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,101 @@
|
||||||
//! Structures used by Marathon's Map format.
|
//! Marathon Map format handling.
|
||||||
|
|
||||||
pub mod ambi;
|
pub mod ambi;
|
||||||
|
pub mod attk;
|
||||||
pub mod bonk;
|
pub mod bonk;
|
||||||
|
pub mod chnk;
|
||||||
|
pub mod damg;
|
||||||
|
pub mod entr;
|
||||||
pub mod epnt;
|
pub mod epnt;
|
||||||
|
pub mod fxpx;
|
||||||
pub mod iidx;
|
pub mod iidx;
|
||||||
pub mod lins;
|
pub mod lins;
|
||||||
pub mod lite;
|
pub mod lite;
|
||||||
pub mod ltfn;
|
pub mod ltfn;
|
||||||
pub mod medi;
|
pub mod medi;
|
||||||
pub mod minf;
|
pub mod minf;
|
||||||
|
pub mod mnpx;
|
||||||
pub mod note;
|
pub mod note;
|
||||||
pub mod objs;
|
pub mod objs;
|
||||||
pub mod plac;
|
pub mod plac;
|
||||||
pub mod plat;
|
pub mod plat;
|
||||||
pub mod pnts;
|
pub mod pnts;
|
||||||
pub mod poly;
|
pub mod poly;
|
||||||
|
pub mod prpx;
|
||||||
|
pub mod pxpx;
|
||||||
pub mod sids;
|
pub mod sids;
|
||||||
pub mod stex;
|
pub mod stex;
|
||||||
pub mod term;
|
pub mod term;
|
||||||
|
pub mod trig;
|
||||||
pub mod trmf;
|
pub mod trmf;
|
||||||
pub mod trmg;
|
pub mod trmg;
|
||||||
|
pub mod wppx;
|
||||||
|
|
||||||
|
use crate::{durandal::err::*, marathon::text::mac_roman_cstr};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
/// Reads a Map file.
|
||||||
|
pub fn read(b: &[u8]) -> ResultS<Wad>
|
||||||
|
{
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: 128, start: 0, data {
|
||||||
|
let ver_wad = u16[0] enum Ver;
|
||||||
|
let ver_dat = u16[2];
|
||||||
|
let name = mac_roman_cstr[4; 64] no_try;
|
||||||
|
let siz_app = u16[78] usize;
|
||||||
|
let siz_wcnk = u16[80] usize;
|
||||||
|
let siz_went = u16[82] usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let old_dat = ver_dat == 0;
|
||||||
|
let old_wad = match ver_wad {
|
||||||
|
Ver::Base => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let siz_ent = if old_wad {8 } else {10};
|
||||||
|
let siz_cnk = if old_wad {12} else {16};
|
||||||
|
|
||||||
|
if !old_wad && siz_ent != siz_went {
|
||||||
|
bail!("invalid entry size");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !old_wad && siz_cnk != siz_wcnk {
|
||||||
|
bail!("invalid chunk size");
|
||||||
|
}
|
||||||
|
|
||||||
|
let entries = entr::read(b, old_wad, old_dat, siz_app, siz_ent, siz_cnk)?;
|
||||||
|
|
||||||
|
Ok(Wad{name, siz_app, entries})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Map file containing entries.
|
||||||
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct Wad
|
||||||
|
{
|
||||||
|
/// The original name of this file.
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// The size of each `Entry`'s `appdata` field.
|
||||||
|
pub siz_app: usize,
|
||||||
|
|
||||||
|
/// All of the entries in this `Wad`.
|
||||||
|
pub entries: entr::EntryMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
c_enum! {
|
||||||
|
/// The version of a `Wad`.
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Ver: u16
|
||||||
|
{
|
||||||
|
Base = 0,
|
||||||
|
Dir = 1,
|
||||||
|
Over = 2,
|
||||||
|
Inf = 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The number of game ticks per second.
|
/// The number of game ticks per second.
|
||||||
pub const TICKS_PER_SECOND: u16 = 30;
|
pub const TICKS_PER_SECOND: u16 = 30;
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
//! Wad file chunk type.
|
||||||
|
|
||||||
|
use crate::{durandal::{bin::*, err::*, image}, marathon::{map, pict}};
|
||||||
|
|
||||||
|
/// Reads all chunks in an entry.
|
||||||
|
pub fn read(b: &[u8], old: bool, siz_cnk: usize) -> ResultS<Vec<Chunk>>
|
||||||
|
{
|
||||||
|
let mut chunks = Vec::new();
|
||||||
|
let mut p = 0;
|
||||||
|
|
||||||
|
let read_chunk_minf = if old {map::minf::read_old} else {map::minf::read};
|
||||||
|
let read_chunk_sids = if old {map::sids::read_old} else {map::sids::read};
|
||||||
|
let read_chunk_poly = if old {map::poly::read_old} else {map::poly::read};
|
||||||
|
let read_chunk_lite = if old {map::lite::read_old} else {map::lite::read};
|
||||||
|
|
||||||
|
while p < b.len() {
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: siz_cnk, start: p, data {
|
||||||
|
let iden = Ident[0];
|
||||||
|
let size = u32[8] usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let beg = p + siz_cnk;
|
||||||
|
let end = beg + size;
|
||||||
|
let data = ok!(b.get(beg..end), "not enough data")?;
|
||||||
|
|
||||||
|
chunks.push(match &iden.0 {
|
||||||
|
b"PICT" => Chunk::Pict(pict::load_pict(data)?),
|
||||||
|
b"Minf" => Chunk::Minf(read_chunk_minf(data)?),
|
||||||
|
b"iidx" => Chunk::Iidx(rd_array(data, map::iidx::read)?),
|
||||||
|
b"EPNT" => Chunk::Epnt(rd_array(data, map::epnt::read)?),
|
||||||
|
b"PNTS" => Chunk::Pnts(rd_array(data, map::pnts::read)?),
|
||||||
|
b"LINS" => Chunk::Lins(rd_array(data, map::lins::read)?),
|
||||||
|
b"SIDS" => Chunk::Sids(rd_array(data, read_chunk_sids)?),
|
||||||
|
b"POLY" => Chunk::Poly(rd_array(data, read_chunk_poly)?),
|
||||||
|
b"OBJS" => Chunk::Objs(rd_array(data, map::objs::read)?),
|
||||||
|
b"LITE" => Chunk::Lite(rd_array(data, read_chunk_lite)?),
|
||||||
|
b"plac" => Chunk::Plac(rd_array(data, map::plac::read)?),
|
||||||
|
b"ambi" => Chunk::Ambi(rd_array(data, map::ambi::read)?),
|
||||||
|
b"bonk" => Chunk::Bonk(rd_array(data, map::bonk::read)?),
|
||||||
|
b"medi" => Chunk::Medi(rd_array(data, map::medi::read)?),
|
||||||
|
b"plat" => Chunk::Plat(rd_array(data, map::plat::read)?),
|
||||||
|
b"NOTE" => Chunk::Note(rd_array(data, map::note::read)?),
|
||||||
|
b"term" => Chunk::Term(rd_array(data, map::term::read)?),
|
||||||
|
b"FXpx" => Chunk::Fxpx(rd_array(data, map::fxpx::read)?),
|
||||||
|
b"MNpx" => Chunk::Mnpx(rd_array(data, map::mnpx::read)?),
|
||||||
|
b"PRpx" => Chunk::Prpx(rd_array(data, map::prpx::read)?),
|
||||||
|
b"PXpx" => Chunk::Pxpx(rd_array(data, map::pxpx::read)?),
|
||||||
|
b"WPpx" => Chunk::Wppx(rd_array(data, map::wppx::read)?),
|
||||||
|
_ => Chunk::Data{iden, data: data.to_vec()},
|
||||||
|
});
|
||||||
|
|
||||||
|
p = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(chunks)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Any kind of chunk in an `Entry`.
|
||||||
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub enum Chunk
|
||||||
|
{
|
||||||
|
/** A `PICT` chunk. */ Pict(image::Image8),
|
||||||
|
/** A `Minf` chunk. */ Minf(map::minf::Minf),
|
||||||
|
/** An `iidx` chunk. */ Iidx(Vec<u16>),
|
||||||
|
/** An `EPNT` chunk. */ Epnt(Vec<map::epnt::Endpoint>),
|
||||||
|
/** A `PNTS` chunk. */ Pnts(Vec<map::pnts::Point>),
|
||||||
|
/** A `LINS` chunk. */ Lins(Vec<map::lins::Line>),
|
||||||
|
/** A `SIDS` chunk. */ Sids(Vec<map::sids::Side>),
|
||||||
|
/** A `POLY` chunk. */ Poly(Vec<map::poly::Polygon>),
|
||||||
|
/** A `LITE` chunk. */ Lite(Vec<map::lite::Light>),
|
||||||
|
/** An `OBJS` chunk. */ Objs(Vec<map::objs::Object>),
|
||||||
|
/** A `plac` chunk. */ Plac(Vec<map::plac::ObjectFreq>),
|
||||||
|
/** An `ambi` chunk. */ Ambi(Vec<map::ambi::SoundAmbi>),
|
||||||
|
/** A `bonk` chunk. */ Bonk(Vec<map::bonk::SoundRand>),
|
||||||
|
/** A `medi` chunk. */ Medi(Vec<map::medi::Media>),
|
||||||
|
/** A `plat` chunk. */ Plat(Vec<map::plat::Platform>),
|
||||||
|
/** A `NOTE` chunk. */ Note(Vec<map::note::Note>),
|
||||||
|
/** A `term` chunk. */ Term(Vec<map::term::Terminal>),
|
||||||
|
/** A `FXpx` chunk. */ Fxpx(Vec<map::fxpx::Effect>),
|
||||||
|
/** A `MNpx` chunk. */ Mnpx(Vec<map::mnpx::Monster>),
|
||||||
|
/** A `PRpx` chunk. */ Prpx(Vec<map::prpx::Projectile>),
|
||||||
|
/** A `PXpx` chunk. */ Pxpx(Vec<map::pxpx::Physics>),
|
||||||
|
/** A `WPpx` chunk. */ Wppx(Vec<map::wppx::Weapon>),
|
||||||
|
|
||||||
|
/// Any other type of chunk, which may have arbitrary data in it.
|
||||||
|
Data{/** The name of the chunk. */ iden: Ident,
|
||||||
|
/** The data. */ data: Vec<u8>},
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,63 @@
|
||||||
|
//! Wad file entry type.
|
||||||
|
|
||||||
|
use crate::durandal::{bin::usize_from_u32, err::*};
|
||||||
|
use super::chnk;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
/// Reads all entries in a `Wad`.
|
||||||
|
pub fn read(b: &[u8],
|
||||||
|
old_wad: bool,
|
||||||
|
old_dat: bool,
|
||||||
|
siz_app: usize,
|
||||||
|
siz_ent: usize,
|
||||||
|
siz_cnk: usize)
|
||||||
|
-> ResultS<EntryMap>
|
||||||
|
{
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: 128, start: 0, data {
|
||||||
|
let dirofs = u32[72] usize;
|
||||||
|
let numents = u16[76] usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut entries = EntryMap::new();
|
||||||
|
let mut p = dirofs;
|
||||||
|
|
||||||
|
for i in 0..numents {
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: siz_ent, start: p, data {
|
||||||
|
let offset = u32[0] usize;
|
||||||
|
let size = u32[4] usize;
|
||||||
|
let index = u16[8];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = if old_wad {i as u16} else {index};
|
||||||
|
|
||||||
|
let chunks = chnk::read(&b[offset..offset + size], old_dat, siz_cnk)?;
|
||||||
|
let appdata = b[p..p + siz_app].to_vec();
|
||||||
|
|
||||||
|
entries.insert(index, Entry{chunks, appdata});
|
||||||
|
|
||||||
|
p += siz_ent + siz_app;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An entry containing chunks and application-specific data.
|
||||||
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct Entry
|
||||||
|
{
|
||||||
|
/// All of the chunks in this `Entry`.
|
||||||
|
pub chunks: Vec<chnk::Chunk>,
|
||||||
|
|
||||||
|
/// The application specific data for this Entry.
|
||||||
|
pub appdata: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A map of indexed entries.
|
||||||
|
pub type EntryMap = BTreeMap<u16, Entry>;
|
||||||
|
|
||||||
|
// EOF
|
|
@ -1,12 +0,0 @@
|
||||||
//! Structures used by Marathon's Physics format.
|
|
||||||
|
|
||||||
pub mod attk;
|
|
||||||
pub mod damg;
|
|
||||||
pub mod fxpx;
|
|
||||||
pub mod mnpx;
|
|
||||||
pub mod prpx;
|
|
||||||
pub mod pxpx;
|
|
||||||
pub mod trig;
|
|
||||||
pub mod wppx;
|
|
||||||
|
|
||||||
// EOF
|
|
|
@ -1,203 +1,28 @@
|
||||||
//! Marathon Shapes format handling.
|
//! Marathon Shapes format handling.
|
||||||
|
|
||||||
use crate::{durandal::{bin::*, err::*, fixed::*, image::*},
|
pub mod bmap;
|
||||||
marathon::{text::*, xfer::TransferMode}};
|
pub mod clut;
|
||||||
use bitflags::bitflags;
|
pub mod coll;
|
||||||
use std::convert::TryFrom;
|
pub mod fram;
|
||||||
|
pub mod sequ;
|
||||||
|
|
||||||
// Reads a color from a color table into `clut`.
|
use crate::durandal::{bin::usize_from_u32, err::*};
|
||||||
fn read_color(b: &[u8], clut: &mut [ColorShp]) -> ResultS<()>
|
|
||||||
|
/// Reads a collection at an offset provided by the Shapes header.
|
||||||
|
pub fn read_coll_at_offset(b: &[u8], ofs: u32, len: usize)
|
||||||
|
-> ResultS<Option<coll::Collection>>
|
||||||
{
|
{
|
||||||
read_data! {
|
if ofs != u32::max_value() {
|
||||||
endian: BIG, buf: b, size: 8, start: 0, data {
|
let ofs = usize_from_u32(ofs);
|
||||||
let flag = u8[0];
|
let dat = ok!(b.get(ofs..ofs + len), "bad offset")?;
|
||||||
let ind = u8[1];
|
Ok(Some(coll::read(dat)?))
|
||||||
let r = u16[2];
|
|
||||||
let g = u16[4];
|
|
||||||
let b = u16[6];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reads all color tables.
|
|
||||||
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;
|
|
||||||
|
|
||||||
let b = ok!(b.get(tab_ofs..tab_ofs + end), "bad offset")?;
|
|
||||||
|
|
||||||
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 {
|
|
||||||
read_color(ok!(b.get(p..p + 8), "not enough data")?, clut)?;
|
|
||||||
p += 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a `Bitmap`.
|
|
||||||
pub fn read_bitmap(b: &[u8]) -> ResultS<Bitmap>
|
|
||||||
{
|
|
||||||
read_data! {
|
|
||||||
endian: BIG, buf: b, size: 26, start: 0, data {
|
|
||||||
let width = u16[0] usize;
|
|
||||||
let height = u16[2] usize;
|
|
||||||
let compr = u16[4];
|
|
||||||
let flags = u16[6] flag BmpFlags;
|
|
||||||
let depth = u16[8];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let compr = compr == u16::max_value();
|
|
||||||
let alpha = flags.contains(BmpFlags::TRANSPARENT);
|
|
||||||
let cmajr = flags.contains(BmpFlags::COLUMN_MAJOR);
|
|
||||||
|
|
||||||
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! {
|
|
||||||
endian: BIG, buf: b, size: 4, start: p, data {
|
|
||||||
let fst = u16[0] usize;
|
|
||||||
let lst = u16[2] usize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let end = lst - fst;
|
|
||||||
|
|
||||||
p += 4;
|
|
||||||
|
|
||||||
if lst < fst || fst > pitch || lst > pitch {
|
|
||||||
bail!("invalid compressed scanline");
|
|
||||||
}
|
|
||||||
|
|
||||||
for _ in 0..fst {
|
|
||||||
bmp.cr.push(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bmp.cr.extend(ok!(b.get(p..p + end), "not enough data")?);
|
|
||||||
|
|
||||||
for _ in lst..pitch {
|
|
||||||
bmp.cr.push(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
p += end;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// simple copy
|
Ok(None)
|
||||||
bmp.cr.extend(ok!(b.get(p..p + width * height), "not enough data")?);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(bmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a `Frame`.
|
|
||||||
pub fn read_frame(b: &[u8]) -> ResultS<Frame>
|
|
||||||
{
|
|
||||||
read_data! {
|
|
||||||
endian: BIG, buf: b, size: 36, start: 0, data {
|
|
||||||
let flags = u16[0] flag FrameFlags;
|
|
||||||
let min_lt = Fixed[2];
|
|
||||||
let bmp_ind = u16[6] usize;
|
|
||||||
let wrl_l = Unit[16];
|
|
||||||
let wrl_r = Unit[18];
|
|
||||||
let wrl_t = Unit[20];
|
|
||||||
let wrl_b = Unit[22];
|
|
||||||
let wrl_x = Unit[24];
|
|
||||||
let wrl_y = Unit[26];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Frame{flags, min_lt, bmp_ind, wrl_l, wrl_r, wrl_t, wrl_b, wrl_x, wrl_y})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a `Sequence`.
|
|
||||||
pub fn read_sequence(b: &[u8]) -> ResultS<Sequence>
|
|
||||||
{
|
|
||||||
read_data! {
|
|
||||||
endian: BIG, buf: b, size: 88, start: 0, data {
|
|
||||||
let name = u8[4; 34];
|
|
||||||
let v_type = u16[38] enum ViewType;
|
|
||||||
let frames = u16[40];
|
|
||||||
let ticks = u16[42];
|
|
||||||
let key = u16[44];
|
|
||||||
let xfer = u16[46] enum TransferMode;
|
|
||||||
let xfer_pd = u16[48];
|
|
||||||
let snd_beg = OptU16[50];
|
|
||||||
let snd_key = OptU16[52];
|
|
||||||
let snd_end = OptU16[54];
|
|
||||||
let loop_f = u16[58];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = mac_roman_conv(ok!(pascal_str(name), "bad string")?);
|
|
||||||
|
|
||||||
Ok(Sequence{name, v_type, frames, ticks, key, xfer, xfer_pd, snd_beg,
|
|
||||||
snd_key, snd_end, loop_f})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a `Collection`.
|
|
||||||
pub fn read_collection(b: &[u8]) -> ResultS<Collection>
|
|
||||||
{
|
|
||||||
read_data! {
|
|
||||||
endian: BIG, buf: b, size: 544, start: 0, data {
|
|
||||||
let version = u16[0];
|
|
||||||
let cl_type = u16[2] enum CollectionType;
|
|
||||||
let clr_num = u16[6] usize;
|
|
||||||
let tab_num = u16[8] usize;
|
|
||||||
let tab_ofs = u32[10] usize;
|
|
||||||
let seq_num = u16[14] usize;
|
|
||||||
let seq_ofs = u32[16] usize;
|
|
||||||
let frm_num = u16[20] usize;
|
|
||||||
let frm_ofs = u32[22] usize;
|
|
||||||
let bmp_num = u16[26] usize;
|
|
||||||
let bmp_ofs = u32[28] usize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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, read_bitmap)?;
|
|
||||||
let frms = rd_ofstable(b, frm_ofs, frm_num, read_frame)?;
|
|
||||||
let seqs = rd_ofstable(b, seq_ofs, seq_num, read_sequence)?;
|
|
||||||
|
|
||||||
Ok(Collection{ctyp: cl_type, tabs, bmps, frms, seqs})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read all of the collections in a Shapes file.
|
/// Read all of the collections in a Shapes file.
|
||||||
pub fn read_shapes(b: &[u8]) -> ResultS<Vec<CollectionDef>>
|
pub fn read(b: &[u8]) -> ResultS<Vec<CollectionDef>>
|
||||||
{
|
{
|
||||||
let mut cl = Vec::with_capacity(32);
|
let mut cl = Vec::with_capacity(32);
|
||||||
let mut p = 0;
|
let mut p = 0;
|
||||||
|
@ -205,28 +30,17 @@ pub fn read_shapes(b: &[u8]) -> ResultS<Vec<CollectionDef>>
|
||||||
for _ in 0..32 {
|
for _ in 0..32 {
|
||||||
read_data! {
|
read_data! {
|
||||||
endian: BIG, buf: b, size: 32, start: p, data {
|
endian: BIG, buf: b, size: 32, start: p, data {
|
||||||
let lo_ofs = u32[4] usize;
|
let lo_ofs = u32[4];
|
||||||
let lo_len = u32[8] usize;
|
let lo_len = u32[8] usize;
|
||||||
let hi_ofs = u32[12] usize;
|
let hi_ofs = u32[12];
|
||||||
let hi_len = u32[16] usize;
|
let hi_len = u32[16] usize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let c_lo = if lo_ofs == usize_from_u32(u32::max_value()) {
|
let lo = read_coll_at_offset(b, lo_ofs, lo_len)?;
|
||||||
None
|
let hi = read_coll_at_offset(b, hi_ofs, hi_len)?;
|
||||||
} else {
|
|
||||||
let dat = ok!(b.get(lo_ofs..lo_ofs + lo_len), "bad offset")?;
|
|
||||||
Some(read_collection(dat)?)
|
|
||||||
};
|
|
||||||
|
|
||||||
let c_hi = if hi_ofs == usize_from_u32(u32::max_value()) {
|
cl.push((lo, hi));
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let dat = ok!(b.get(hi_ofs..hi_ofs + hi_len), "bad offset")?;
|
|
||||||
Some(read_collection(dat)?)
|
|
||||||
};
|
|
||||||
|
|
||||||
cl.push((c_lo, c_hi));
|
|
||||||
|
|
||||||
p += 32;
|
p += 32;
|
||||||
}
|
}
|
||||||
|
@ -234,274 +48,7 @@ pub fn read_shapes(b: &[u8]) -> ResultS<Vec<CollectionDef>>
|
||||||
Ok(cl)
|
Ok(cl)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bitmap
|
|
||||||
{
|
|
||||||
/// Creates an empty bitmap.
|
|
||||||
pub 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>
|
|
||||||
{
|
|
||||||
/// Creates an `ImageShp` with the given bitmap.
|
|
||||||
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 = usize::from(if self.bmp.cmajr {
|
|
||||||
self.bmp.cr[y + x * self.bmp.h]
|
|
||||||
} else {
|
|
||||||
self.bmp.cr[x + y * self.bmp.w]
|
|
||||||
});
|
|
||||||
|
|
||||||
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, ..} |
|
|
||||||
ColorShp::Lit {r, ..} => r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn g(&self) -> u16
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
ColorShp::Translucent => 0,
|
|
||||||
ColorShp::Opaque{g, ..} |
|
|
||||||
ColorShp::Lit {g, ..} => g,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn b(&self) -> u16
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
ColorShp::Translucent => 0,
|
|
||||||
ColorShp::Opaque{b, ..} |
|
|
||||||
ColorShp::Lit {b, ..} => b,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn a(&self) -> u16
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
ColorShp::Translucent => 0,
|
|
||||||
ColorShp::Opaque{..} |
|
|
||||||
ColorShp::Lit {..} => u16::max_value(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A color in an `ImageShp`'s color table.
|
|
||||||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub enum ColorShp
|
|
||||||
{
|
|
||||||
/// A completely translucent color.
|
|
||||||
Translucent,
|
|
||||||
|
|
||||||
/// 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},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An unpacked Shape bitmap.
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub struct Bitmap
|
|
||||||
{
|
|
||||||
w: usize,
|
|
||||||
h: usize,
|
|
||||||
cr: Vec<u8>,
|
|
||||||
alpha: bool,
|
|
||||||
cmajr: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An image from a Shape. This mainly just exists so that `Bitmap` can use the
|
|
||||||
/// `Image` trait.
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub struct ImageShp<'a, 'b>
|
|
||||||
{
|
|
||||||
bmp: &'a Bitmap,
|
|
||||||
clut: &'b [ColorShp],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A frame, also known as a low level shape.
|
|
||||||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub struct Frame
|
|
||||||
{
|
|
||||||
/// 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,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A sequence, also known as a high level shape.
|
|
||||||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub struct Sequence
|
|
||||||
{
|
|
||||||
/// 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,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A collection of color tables, bitmaps, frames and sequences.
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub struct Collection
|
|
||||||
{
|
|
||||||
/// The type of collection this is.
|
|
||||||
pub ctyp: CollectionType,
|
|
||||||
|
|
||||||
/// All of the color tables in this collection.
|
|
||||||
pub tabs: Vec<Vec<ColorShp>>,
|
|
||||||
|
|
||||||
/// All of the bitmaps in this collection.
|
|
||||||
pub bmps: Vec<Bitmap>,
|
|
||||||
|
|
||||||
/// All of the frames in this collection.
|
|
||||||
pub frms: Vec<Frame>,
|
|
||||||
|
|
||||||
/// All of the sequences in this collection.
|
|
||||||
pub seqs: Vec<Sequence>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A collection, which may have low- and high-definition variations, or none.
|
/// A collection, which may have low- and high-definition variations, or none.
|
||||||
pub type CollectionDef = (Option<Collection>, Option<Collection>);
|
pub type CollectionDef = (Option<coll::Collection>, Option<coll::Collection>);
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
struct BmpFlags: u16
|
|
||||||
{
|
|
||||||
const TRANSPARENT = 1 << 14;
|
|
||||||
const COLUMN_MAJOR = 1 << 15;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
/// Flags for `Frame`.
|
|
||||||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
||||||
pub struct FrameFlags: u16
|
|
||||||
{
|
|
||||||
/// The player's torso will obscure the player's legs.
|
|
||||||
const OBSCURE = 1 << 13;
|
|
||||||
/// The bitmap will be flipped on the vertical axis.
|
|
||||||
const FLIP_Y = 1 << 14;
|
|
||||||
/// The bitmap will be flipped on the horizontal axis.
|
|
||||||
const FLIP_X = 1 << 15;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c_enum! {
|
|
||||||
/// The type of a collection.
|
|
||||||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum CollectionType: u16
|
|
||||||
{
|
|
||||||
Unused = 0,
|
|
||||||
Wall = 1,
|
|
||||||
Object = 2,
|
|
||||||
Interface = 3,
|
|
||||||
Scenery = 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c_enum! {
|
|
||||||
/// The type of or number of views for a sequence.
|
|
||||||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ViewType: u16
|
|
||||||
{
|
|
||||||
Anim = 1,
|
|
||||||
Anim8from2 = 2,
|
|
||||||
Anim4from3 = 3,
|
|
||||||
Anim4 = 4,
|
|
||||||
Anim8from5 = 5,
|
|
||||||
Anim8 = 8,
|
|
||||||
Anim5from3 = 9,
|
|
||||||
Still = 10,
|
|
||||||
Anim5 = 11,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
//! Shapes file bitmap type.
|
||||||
|
|
||||||
|
use crate::durandal::{err::*, image::Image};
|
||||||
|
use super::clut;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
/// Reads a `Bitmap`.
|
||||||
|
pub fn read(b: &[u8]) -> ResultS<Bitmap>
|
||||||
|
{
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: 26, start: 0, data {
|
||||||
|
let width = u16[0] usize;
|
||||||
|
let height = u16[2] usize;
|
||||||
|
let compr = u16[4];
|
||||||
|
let flags = u16[6] flag BmpFlags;
|
||||||
|
let depth = u16[8];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let compr = compr == u16::max_value();
|
||||||
|
let alpha = flags.contains(BmpFlags::TRANSPARENT);
|
||||||
|
let cmajr = flags.contains(BmpFlags::COLUMN_MAJOR);
|
||||||
|
|
||||||
|
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! {
|
||||||
|
endian: BIG, buf: b, size: 4, start: p, data {
|
||||||
|
let fst = u16[0] usize;
|
||||||
|
let lst = u16[2] usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let end = lst - fst;
|
||||||
|
|
||||||
|
p += 4;
|
||||||
|
|
||||||
|
if lst < fst || fst > pitch || lst > pitch {
|
||||||
|
bail!("invalid compressed scanline");
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..fst {
|
||||||
|
bmp.cr.push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bmp.cr.extend(ok!(b.get(p..p + end), "not enough data")?);
|
||||||
|
|
||||||
|
for _ in lst..pitch {
|
||||||
|
bmp.cr.push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
p += end;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// simple copy
|
||||||
|
bmp.cr.extend(ok!(b.get(p..p + width * height), "not enough data")?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(bmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bitmap
|
||||||
|
{
|
||||||
|
/// Creates an empty bitmap.
|
||||||
|
pub 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>
|
||||||
|
{
|
||||||
|
/// Creates an `ImageShp` with the given bitmap.
|
||||||
|
pub fn new(bmp: &'a Bitmap, clut: &'b [clut::ColorShp]) -> Self
|
||||||
|
{
|
||||||
|
Self{bmp, clut}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Image for ImageShp<'_, '_>
|
||||||
|
{
|
||||||
|
type Output = clut::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: clut::ColorShp = clut::ColorShp::Translucent;
|
||||||
|
|
||||||
|
let cr = usize::from(if self.bmp.cmajr {
|
||||||
|
self.bmp.cr[y + x * self.bmp.h]
|
||||||
|
} else {
|
||||||
|
self.bmp.cr[x + y * self.bmp.w]
|
||||||
|
});
|
||||||
|
|
||||||
|
if self.bmp.alpha && cr == 0 {
|
||||||
|
&TRANSLUCENT_COLOR
|
||||||
|
} else {
|
||||||
|
self.clut.get(cr).unwrap_or(&TRANSLUCENT_COLOR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An unpacked Shape bitmap.
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct Bitmap
|
||||||
|
{
|
||||||
|
w: usize,
|
||||||
|
h: usize,
|
||||||
|
cr: Vec<u8>,
|
||||||
|
alpha: bool,
|
||||||
|
cmajr: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An image from a Shape. This mainly just exists so that `Bitmap` can use the
|
||||||
|
/// `Image` trait.
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct ImageShp<'a, 'b>
|
||||||
|
{
|
||||||
|
bmp: &'a Bitmap,
|
||||||
|
clut: &'b [clut::ColorShp],
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
struct BmpFlags: u16
|
||||||
|
{
|
||||||
|
const TRANSPARENT = 1 << 14;
|
||||||
|
const COLUMN_MAJOR = 1 << 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,113 @@
|
||||||
|
//! Shapes file color lookup tables.
|
||||||
|
|
||||||
|
use crate::durandal::{err::*, image::Color};
|
||||||
|
|
||||||
|
/// Reads a color from a color table into `clut`.
|
||||||
|
pub fn read_color(b: &[u8], clut: &mut [ColorShp]) -> ResultS<()>
|
||||||
|
{
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: 8, start: 0, data {
|
||||||
|
let flag = u8[0];
|
||||||
|
let ind = u8[1];
|
||||||
|
let r = u16[2];
|
||||||
|
let g = u16[4];
|
||||||
|
let b = u16[6];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads color tables from `b`.
|
||||||
|
pub fn read(b: &[u8], tab_ofs: usize, tab_num: usize, clr_num: usize)
|
||||||
|
-> ResultS<Vec<Clut>>
|
||||||
|
{
|
||||||
|
let end = tab_num * clr_num * 8;
|
||||||
|
|
||||||
|
let b = ok!(b.get(tab_ofs..tab_ofs + end), "bad offset")?;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
read_color(ok!(b.get(p..p + 8), "not enough data")?, clut)?;
|
||||||
|
p += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Color for ColorShp
|
||||||
|
{
|
||||||
|
fn r(&self) -> u16
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
ColorShp::Translucent => 0,
|
||||||
|
ColorShp::Opaque{r, ..} |
|
||||||
|
ColorShp::Lit {r, ..} => r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn g(&self) -> u16
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
ColorShp::Translucent => 0,
|
||||||
|
ColorShp::Opaque{g, ..} |
|
||||||
|
ColorShp::Lit {g, ..} => g,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn b(&self) -> u16
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
ColorShp::Translucent => 0,
|
||||||
|
ColorShp::Opaque{b, ..} |
|
||||||
|
ColorShp::Lit {b, ..} => b,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn a(&self) -> u16
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
ColorShp::Translucent => 0,
|
||||||
|
ColorShp::Opaque{..} |
|
||||||
|
ColorShp::Lit {..} => u16::max_value(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A color in a `Clut`.
|
||||||
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum ColorShp
|
||||||
|
{
|
||||||
|
/// A completely translucent color.
|
||||||
|
Translucent,
|
||||||
|
|
||||||
|
/// 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},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A color collection.
|
||||||
|
pub type Clut = Vec<ColorShp>;
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,72 @@
|
||||||
|
//! Shapes file collection type.
|
||||||
|
|
||||||
|
use crate::durandal::{bin::{rd_ofstable, usize_from_u32}, err::*};
|
||||||
|
use super::{bmap, clut, fram, sequ};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
/// Reads a `Collection`.
|
||||||
|
pub fn read(b: &[u8]) -> ResultS<Collection>
|
||||||
|
{
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: 544, start: 0, data {
|
||||||
|
let version = u16[0];
|
||||||
|
let cl_type = u16[2] enum CollectionType;
|
||||||
|
let clr_num = u16[6] usize;
|
||||||
|
let tab_num = u16[8] usize;
|
||||||
|
let tab_ofs = u32[10] usize;
|
||||||
|
let seq_num = u16[14] usize;
|
||||||
|
let seq_ofs = u32[16] usize;
|
||||||
|
let frm_num = u16[20] usize;
|
||||||
|
let frm_ofs = u32[22] usize;
|
||||||
|
let bmp_num = u16[26] usize;
|
||||||
|
let bmp_ofs = u32[28] usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if version != 3 {
|
||||||
|
bail!("invalid collection definition");
|
||||||
|
}
|
||||||
|
|
||||||
|
let tabs = clut::read(b, tab_ofs, tab_num, clr_num)?;
|
||||||
|
let bmps = rd_ofstable(b, bmp_ofs, bmp_num, bmap::read)?;
|
||||||
|
let frms = rd_ofstable(b, frm_ofs, frm_num, fram::read)?;
|
||||||
|
let seqs = rd_ofstable(b, seq_ofs, seq_num, sequ::read)?;
|
||||||
|
|
||||||
|
Ok(Collection{ctyp: cl_type, tabs, bmps, frms, seqs})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A collection of color tables, bitmaps, frames and sequences.
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct Collection
|
||||||
|
{
|
||||||
|
/// The type of collection this is.
|
||||||
|
pub ctyp: CollectionType,
|
||||||
|
|
||||||
|
/// All of the color tables in this collection.
|
||||||
|
pub tabs: Vec<clut::Clut>,
|
||||||
|
|
||||||
|
/// All of the bitmaps in this collection.
|
||||||
|
pub bmps: Vec<bmap::Bitmap>,
|
||||||
|
|
||||||
|
/// All of the frames in this collection.
|
||||||
|
pub frms: Vec<fram::Frame>,
|
||||||
|
|
||||||
|
/// All of the sequences in this collection.
|
||||||
|
pub seqs: Vec<sequ::Sequence>,
|
||||||
|
}
|
||||||
|
|
||||||
|
c_enum! {
|
||||||
|
/// The type of a collection.
|
||||||
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CollectionType: u16
|
||||||
|
{
|
||||||
|
Unused = 0,
|
||||||
|
Wall = 1,
|
||||||
|
Object = 2,
|
||||||
|
Interface = 3,
|
||||||
|
Scenery = 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,73 @@
|
||||||
|
//! Shapes file frame type.
|
||||||
|
|
||||||
|
use crate::durandal::{err::*, fixed::*};
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
/// Reads a `Frame`.
|
||||||
|
pub fn read(b: &[u8]) -> ResultS<Frame>
|
||||||
|
{
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: 36, start: 0, data {
|
||||||
|
let flags = u16[0] flag FrameFlags;
|
||||||
|
let min_lt = Fixed[2];
|
||||||
|
let bmp_ind = u16[6] usize;
|
||||||
|
let wrl_l = Unit[16];
|
||||||
|
let wrl_r = Unit[18];
|
||||||
|
let wrl_t = Unit[20];
|
||||||
|
let wrl_b = Unit[22];
|
||||||
|
let wrl_x = Unit[24];
|
||||||
|
let wrl_y = Unit[26];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Frame{flags, min_lt, bmp_ind, wrl_l, wrl_r, wrl_t, wrl_b, wrl_x, wrl_y})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A frame, also known as a low level shape.
|
||||||
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct Frame
|
||||||
|
{
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Flags for `Frame`.
|
||||||
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||||
|
pub struct FrameFlags: u16
|
||||||
|
{
|
||||||
|
/// The player's torso will obscure the player's legs.
|
||||||
|
const OBSCURE = 1 << 13;
|
||||||
|
/// The bitmap will be flipped on the vertical axis.
|
||||||
|
const FLIP_Y = 1 << 14;
|
||||||
|
/// The bitmap will be flipped on the horizontal axis.
|
||||||
|
const FLIP_X = 1 << 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,90 @@
|
||||||
|
//! Shapes file sequence type.
|
||||||
|
|
||||||
|
use crate::{durandal::{bin::OptU16, err::*},
|
||||||
|
marathon::{text::{mac_roman_conv, pascal_str},
|
||||||
|
xfer::TransferMode}};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
/// Reads a `Sequence`.
|
||||||
|
pub fn read(b: &[u8]) -> ResultS<Sequence>
|
||||||
|
{
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: 88, start: 0, data {
|
||||||
|
let name = u8[4; 34];
|
||||||
|
let v_type = u16[38] enum ViewType;
|
||||||
|
let frames = u16[40];
|
||||||
|
let ticks = u16[42];
|
||||||
|
let key = u16[44];
|
||||||
|
let xfer = u16[46] enum TransferMode;
|
||||||
|
let xfer_pd = u16[48];
|
||||||
|
let snd_beg = OptU16[50];
|
||||||
|
let snd_key = OptU16[52];
|
||||||
|
let snd_end = OptU16[54];
|
||||||
|
let loop_f = u16[58];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = mac_roman_conv(ok!(pascal_str(name), "bad string")?);
|
||||||
|
|
||||||
|
Ok(Sequence{name, v_type, frames, ticks, key, xfer, xfer_pd, snd_beg,
|
||||||
|
snd_key, snd_end, loop_f})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A sequence, also known as a high level shape.
|
||||||
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct Sequence
|
||||||
|
{
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
c_enum! {
|
||||||
|
/// The type of or number of views for a sequence.
|
||||||
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ViewType: u16
|
||||||
|
{
|
||||||
|
Anim = 1,
|
||||||
|
Anim8from2 = 2,
|
||||||
|
Anim4from3 = 3,
|
||||||
|
Anim4 = 4,
|
||||||
|
Anim8from5 = 5,
|
||||||
|
Anim8 = 8,
|
||||||
|
Anim5from3 = 9,
|
||||||
|
Still = 10,
|
||||||
|
Anim5 = 11,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -1,91 +1,13 @@
|
||||||
//! Marathon Sounds format handling.
|
//! Marathon Sounds format handling.
|
||||||
|
|
||||||
use crate::durandal::{bin::*, err::*, fixed::*, sound::*};
|
pub mod defs;
|
||||||
use bitflags::bitflags;
|
pub mod snds;
|
||||||
use std::{collections::BTreeMap, convert::TryFrom};
|
|
||||||
|
|
||||||
/// Reads a sound.
|
use crate::durandal::{bin::Ident, err::*};
|
||||||
pub fn read_sound(b: &[u8]) -> ResultS<Sound16>
|
use std::collections::BTreeMap;
|
||||||
{
|
|
||||||
read_data! {
|
|
||||||
endian: BIG, buf: b, size: 21, start: 0, data {
|
|
||||||
let len = u32[4] usize;
|
|
||||||
let rate = u16[8];
|
|
||||||
let lp_beg = u32[12] usize;
|
|
||||||
let lp_end = u32[16] usize;
|
|
||||||
let magic = u8[20];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match magic {
|
|
||||||
0 => {
|
|
||||||
let stream = &b[22..22 + len];
|
|
||||||
Ok(Sound16::new_from_8(rate, lp_beg, lp_end, stream))
|
|
||||||
}
|
|
||||||
0xFF => {
|
|
||||||
read_data! {
|
|
||||||
endian: BIG, buf: b, size: 42, start: 22, data {
|
|
||||||
let len = u32[0] usize;
|
|
||||||
let bps = u16[26];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match bps {
|
|
||||||
16 => {
|
|
||||||
let stream = &b[63..63 + len * 2];
|
|
||||||
Ok(Sound16::new_from_16(rate, lp_beg, lp_end, stream))
|
|
||||||
}
|
|
||||||
8 => {
|
|
||||||
let stream = &b[63..63 + len];
|
|
||||||
Ok(Sound16::new_from_8(rate, lp_beg, lp_end, stream))
|
|
||||||
}
|
|
||||||
_ => bail!("bad bits per sample"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => bail!("invalid magic number"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a sound definition.
|
|
||||||
pub fn read_sound_def(b: &[u8]) -> ResultS<Option<(Vec<usize>, u16, SoundDef)>>
|
|
||||||
{
|
|
||||||
read_data! {
|
|
||||||
endian: BIG, buf: b, size: 64, start: 0, data {
|
|
||||||
let index = u16[0];
|
|
||||||
let volume = u16[2] enum Volume;
|
|
||||||
let flags = u16[4] flag SoundFlags;
|
|
||||||
let chance = u16[6];
|
|
||||||
let pitch_lo = Fixed[8];
|
|
||||||
let pitch_hi = Fixed[12];
|
|
||||||
let n_sounds = u16[16] usize;
|
|
||||||
let grp_ofs = u32[20] usize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if index == u16::max_value() {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if n_sounds > 5 {
|
|
||||||
bail!("too many sounds");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ofs = Vec::with_capacity(n_sounds);
|
|
||||||
let mut p = 36;
|
|
||||||
|
|
||||||
for _ in 0..n_sounds {
|
|
||||||
ofs.push(grp_ofs + usize_from_u32(u32b(&b[p..])));
|
|
||||||
p += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sounds = Vec::with_capacity(n_sounds);
|
|
||||||
|
|
||||||
Ok(Some((ofs, index,
|
|
||||||
SoundDef{volume, flags, chance, pitch_lo, pitch_hi, sounds})))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads all sounds from a Sound file.
|
/// Reads all sounds from a Sound file.
|
||||||
pub fn read_sounds(b: &[u8]) -> ResultS<Vec<SoundTable>>
|
pub fn read(b: &[u8]) -> ResultS<Vec<SoundTable>>
|
||||||
{
|
{
|
||||||
read_data! {
|
read_data! {
|
||||||
endian: BIG, buf: b, size: 260, start: 0, data {
|
endian: BIG, buf: b, size: 260, start: 0, data {
|
||||||
|
@ -104,12 +26,12 @@ pub fn read_sounds(b: &[u8]) -> ResultS<Vec<SoundTable>>
|
||||||
let mut p = 260;
|
let mut p = 260;
|
||||||
|
|
||||||
for _ in 0..src_num {
|
for _ in 0..src_num {
|
||||||
let mut st = BTreeMap::new();
|
let mut st = SoundTable::new();
|
||||||
|
|
||||||
for _ in 0..snd_num {
|
for _ in 0..snd_num {
|
||||||
if let Some((ofs, idx, mut def)) = read_sound_def(&b[p..p + 64])? {
|
if let Some((ofs, idx, mut def)) = defs::read(&b[p..p + 64])? {
|
||||||
for &ofs in &ofs {
|
for &ofs in &ofs {
|
||||||
def.sounds.push(read_sound(&b[ofs..])?);
|
def.sounds.push(snds::read(&b[ofs..])?);
|
||||||
}
|
}
|
||||||
|
|
||||||
st.insert(idx, def);
|
st.insert(idx, def);
|
||||||
|
@ -124,64 +46,7 @@ pub fn read_sounds(b: &[u8]) -> ResultS<Vec<SoundTable>>
|
||||||
Ok(sc)
|
Ok(sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A sound definition containing one, many or no sounds.
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub struct SoundDef
|
|
||||||
{
|
|
||||||
/// The volume type for this sound.
|
|
||||||
pub volume: Volume,
|
|
||||||
|
|
||||||
/// The flags for this sound.
|
|
||||||
pub flags: SoundFlags,
|
|
||||||
|
|
||||||
/// The chance out of `u16::max_value()` that this sound will not play.
|
|
||||||
pub chance: u16,
|
|
||||||
|
|
||||||
/// The low random pitch bound.
|
|
||||||
pub pitch_lo: Fixed,
|
|
||||||
|
|
||||||
/// The high random pitch bound.
|
|
||||||
pub pitch_hi: Fixed,
|
|
||||||
|
|
||||||
/// All of the sounds in this collection.
|
|
||||||
pub sounds: Vec<Sound16>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A table of sound definitions.
|
/// A table of sound definitions.
|
||||||
pub type SoundTable = BTreeMap<u16, SoundDef>;
|
pub type SoundTable = BTreeMap<u16, defs::SoundDef>;
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
/// Flags for `SoundDef`.
|
|
||||||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
||||||
pub struct SoundFlags: u16
|
|
||||||
{
|
|
||||||
/// The sound will not restart when trying to play over itself.
|
|
||||||
const NO_RESTART = 1;
|
|
||||||
/// The sound will not switch channels when trying to play over itself.
|
|
||||||
const NO_CHANNEL_SWITCH = 1 << 1;
|
|
||||||
/// The pitch variance will be halved.
|
|
||||||
const LESS_PITCH_CHANGE = 1 << 2;
|
|
||||||
/// The pitch variance will be nullified.
|
|
||||||
const NO_PITCH_CHANGE = 1 << 3;
|
|
||||||
/// The sound will play even when completely obstructed by walls.
|
|
||||||
const NO_OBSTRUCTION = 1 << 4;
|
|
||||||
/// The sound will play even when completely obstructed by media.
|
|
||||||
const NO_MEDIA_OBSTRUCT = 1 << 5;
|
|
||||||
/// The sound will have special stereo effects.
|
|
||||||
const AMBIENT = 1 << 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c_enum! {
|
|
||||||
/// The type of volume this sound has.
|
|
||||||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Volume: u16
|
|
||||||
{
|
|
||||||
Quiet = 0,
|
|
||||||
Normal = 1,
|
|
||||||
Loud = 2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
//! Sounds format definition type.
|
||||||
|
|
||||||
|
use crate::durandal::{bin::{u32b, usize_from_u32},
|
||||||
|
err::*,
|
||||||
|
fixed::*,
|
||||||
|
sound::Sound16};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
/// Reads a sound definition.
|
||||||
|
pub fn read(b: &[u8]) -> ResultS<Option<(Vec<usize>, u16, SoundDef)>>
|
||||||
|
{
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: 64, start: 0, data {
|
||||||
|
let index = u16[0];
|
||||||
|
let volume = u16[2] enum Volume;
|
||||||
|
let flags = u16[4] flag SoundFlags;
|
||||||
|
let chance = u16[6];
|
||||||
|
let pitch_lo = Fixed[8];
|
||||||
|
let pitch_hi = Fixed[12];
|
||||||
|
let n_sounds = u16[16] usize;
|
||||||
|
let grp_ofs = u32[20] usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == u16::max_value() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
if n_sounds > 5 {
|
||||||
|
bail!("too many sounds");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ofs = Vec::with_capacity(n_sounds);
|
||||||
|
let mut p = 36;
|
||||||
|
|
||||||
|
for _ in 0..n_sounds {
|
||||||
|
ofs.push(grp_ofs + usize_from_u32(u32b(&b[p..])));
|
||||||
|
p += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sounds = Vec::with_capacity(n_sounds);
|
||||||
|
|
||||||
|
Ok(Some((ofs, index,
|
||||||
|
SoundDef{volume, flags, chance, pitch_lo, pitch_hi, sounds})))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A sound definition containing one, many or no sounds.
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct SoundDef
|
||||||
|
{
|
||||||
|
/// The volume type for this sound.
|
||||||
|
pub volume: Volume,
|
||||||
|
|
||||||
|
/// The flags for this sound.
|
||||||
|
pub flags: SoundFlags,
|
||||||
|
|
||||||
|
/// The chance out of `u16::max_value()` that this sound will not play.
|
||||||
|
pub chance: u16,
|
||||||
|
|
||||||
|
/// The low random pitch bound.
|
||||||
|
pub pitch_lo: Fixed,
|
||||||
|
|
||||||
|
/// The high random pitch bound.
|
||||||
|
pub pitch_hi: Fixed,
|
||||||
|
|
||||||
|
/// All of the sounds in this collection.
|
||||||
|
pub sounds: Vec<Sound16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Flags for `SoundDef`.
|
||||||
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||||
|
pub struct SoundFlags: u16
|
||||||
|
{
|
||||||
|
/// The sound will not restart when trying to play over itself.
|
||||||
|
const NO_RESTART = 1;
|
||||||
|
/// The sound will not switch channels when trying to play over itself.
|
||||||
|
const NO_CHANNEL_SWITCH = 1 << 1;
|
||||||
|
/// The pitch variance will be halved.
|
||||||
|
const LESS_PITCH_CHANGE = 1 << 2;
|
||||||
|
/// The pitch variance will be nullified.
|
||||||
|
const NO_PITCH_CHANGE = 1 << 3;
|
||||||
|
/// The sound will play even when completely obstructed by walls.
|
||||||
|
const NO_OBSTRUCTION = 1 << 4;
|
||||||
|
/// The sound will play even when completely obstructed by media.
|
||||||
|
const NO_MEDIA_OBSTRUCT = 1 << 5;
|
||||||
|
/// The sound will have special stereo effects.
|
||||||
|
const AMBIENT = 1 << 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c_enum! {
|
||||||
|
/// The type of volume this sound has.
|
||||||
|
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Volume: u16
|
||||||
|
{
|
||||||
|
Quiet = 0,
|
||||||
|
Normal = 1,
|
||||||
|
Loud = 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,47 @@
|
||||||
|
//! Sounds file audio type.
|
||||||
|
|
||||||
|
use crate::durandal::{bin::usize_from_u32, err::*, sound::Sound16};
|
||||||
|
|
||||||
|
/// Reads a sound.
|
||||||
|
pub fn read(b: &[u8]) -> ResultS<Sound16>
|
||||||
|
{
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: 21, start: 0, data {
|
||||||
|
let len = u32[4] usize;
|
||||||
|
let rate = u16[8];
|
||||||
|
let lp_beg = u32[12] usize;
|
||||||
|
let lp_end = u32[16] usize;
|
||||||
|
let magic = u8[20];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match magic {
|
||||||
|
0 => {
|
||||||
|
let stream = &b[22..22 + len];
|
||||||
|
Ok(Sound16::new_from_8(rate, lp_beg, lp_end, stream))
|
||||||
|
}
|
||||||
|
0xFF => {
|
||||||
|
read_data! {
|
||||||
|
endian: BIG, buf: b, size: 42, start: 22, data {
|
||||||
|
let len = u32[0] usize;
|
||||||
|
let bps = u16[26];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match bps {
|
||||||
|
16 => {
|
||||||
|
let stream = &b[63..63 + len * 2];
|
||||||
|
Ok(Sound16::new_from_16(rate, lp_beg, lp_end, stream))
|
||||||
|
}
|
||||||
|
8 => {
|
||||||
|
let stream = &b[63..63 + len];
|
||||||
|
Ok(Sound16::new_from_8(rate, lp_beg, lp_end, stream))
|
||||||
|
}
|
||||||
|
_ => bail!("bad bits per sample"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => bail!("invalid magic number"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -1,211 +0,0 @@
|
||||||
//! Marathon Wad format handling.
|
|
||||||
|
|
||||||
use crate::{durandal::{bin::*, err::*, image},
|
|
||||||
marathon::{map, phy, pict, text::mac_roman_cstr}};
|
|
||||||
use std::{collections::BTreeMap, convert::TryFrom};
|
|
||||||
|
|
||||||
/// Reads all chunks in an entry.
|
|
||||||
pub fn read_chunks(b: &[u8], old: bool, siz_cnk: usize) -> ResultS<Vec<Chunk>>
|
|
||||||
{
|
|
||||||
let mut chunks = Vec::new();
|
|
||||||
let mut p = 0;
|
|
||||||
|
|
||||||
let read_chunk_minf = if old {map::minf::read_old} else {map::minf::read};
|
|
||||||
let read_chunk_sids = if old {map::sids::read_old} else {map::sids::read};
|
|
||||||
let read_chunk_poly = if old {map::poly::read_old} else {map::poly::read};
|
|
||||||
let read_chunk_lite = if old {map::lite::read_old} else {map::lite::read};
|
|
||||||
|
|
||||||
while p < b.len() {
|
|
||||||
read_data! {
|
|
||||||
endian: BIG, buf: b, size: siz_cnk, start: p, data {
|
|
||||||
let iden = Ident[0];
|
|
||||||
let size = u32[8] usize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let beg = p + siz_cnk;
|
|
||||||
let end = beg + size;
|
|
||||||
let data = &b[beg..end];
|
|
||||||
|
|
||||||
chunks.push(match &iden.0 {
|
|
||||||
b"PICT" => Chunk::Pict(pict::load_pict(data)?),
|
|
||||||
b"Minf" => Chunk::Minf(read_chunk_minf(data)?),
|
|
||||||
b"iidx" => Chunk::Iidx(rd_array(data, map::iidx::read)?),
|
|
||||||
b"EPNT" => Chunk::Epnt(rd_array(data, map::epnt::read)?),
|
|
||||||
b"PNTS" => Chunk::Pnts(rd_array(data, map::pnts::read)?),
|
|
||||||
b"LINS" => Chunk::Lins(rd_array(data, map::lins::read)?),
|
|
||||||
b"SIDS" => Chunk::Sids(rd_array(data, read_chunk_sids)?),
|
|
||||||
b"POLY" => Chunk::Poly(rd_array(data, read_chunk_poly)?),
|
|
||||||
b"OBJS" => Chunk::Objs(rd_array(data, map::objs::read)?),
|
|
||||||
b"LITE" => Chunk::Lite(rd_array(data, read_chunk_lite)?),
|
|
||||||
b"plac" => Chunk::Plac(rd_array(data, map::plac::read)?),
|
|
||||||
b"ambi" => Chunk::Ambi(rd_array(data, map::ambi::read)?),
|
|
||||||
b"bonk" => Chunk::Bonk(rd_array(data, map::bonk::read)?),
|
|
||||||
b"medi" => Chunk::Medi(rd_array(data, map::medi::read)?),
|
|
||||||
b"plat" => Chunk::Plat(rd_array(data, map::plat::read)?),
|
|
||||||
b"NOTE" => Chunk::Note(rd_array(data, map::note::read)?),
|
|
||||||
b"term" => Chunk::Term(rd_array(data, map::term::read)?),
|
|
||||||
b"FXpx" => Chunk::Fxpx(rd_array(data, phy::fxpx::read)?),
|
|
||||||
b"MNpx" => Chunk::Mnpx(rd_array(data, phy::mnpx::read)?),
|
|
||||||
b"PRpx" => Chunk::Prpx(rd_array(data, phy::prpx::read)?),
|
|
||||||
b"PXpx" => Chunk::Pxpx(rd_array(data, phy::pxpx::read)?),
|
|
||||||
b"WPpx" => Chunk::Wppx(rd_array(data, phy::wppx::read)?),
|
|
||||||
_ => Chunk::Data{iden, data: data.to_vec()},
|
|
||||||
});
|
|
||||||
|
|
||||||
p = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(chunks)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads all entries in a `Wad`.
|
|
||||||
pub fn read_entries(b: &[u8],
|
|
||||||
old_wad: bool,
|
|
||||||
old_dat: bool,
|
|
||||||
siz_app: usize,
|
|
||||||
siz_ent: usize,
|
|
||||||
siz_cnk: usize)
|
|
||||||
-> ResultS<BTreeMap<u16, Entry>>
|
|
||||||
{
|
|
||||||
read_data! {
|
|
||||||
endian: BIG, buf: b, size: 128, start: 0, data {
|
|
||||||
let dirofs = u32[72] usize;
|
|
||||||
let numents = u16[76] usize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut entries = BTreeMap::new();
|
|
||||||
let mut p = dirofs;
|
|
||||||
|
|
||||||
for i in 0..numents {
|
|
||||||
read_data! {
|
|
||||||
endian: BIG, buf: b, size: siz_ent, start: p, data {
|
|
||||||
let offset = u32[0] usize;
|
|
||||||
let size = u32[4] usize;
|
|
||||||
let index = u16[8];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let index = if old_wad {i as u16} else {index};
|
|
||||||
|
|
||||||
let chunks = read_chunks(&b[offset..offset + size], old_dat, siz_cnk)?;
|
|
||||||
let appdata = b[p..p + siz_app].to_vec();
|
|
||||||
|
|
||||||
entries.insert(index, Entry{chunks, appdata});
|
|
||||||
|
|
||||||
p += siz_ent + siz_app;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(entries)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a Map file.
|
|
||||||
pub fn read_wad(b: &[u8]) -> ResultS<Wad>
|
|
||||||
{
|
|
||||||
read_data! {
|
|
||||||
endian: BIG, buf: b, size: 128, start: 0, data {
|
|
||||||
let ver_wad = u16[0] enum Ver;
|
|
||||||
let ver_dat = u16[2];
|
|
||||||
let name = mac_roman_cstr[4; 64] no_try;
|
|
||||||
let siz_app = u16[78] usize;
|
|
||||||
let siz_wcnk = u16[80] usize;
|
|
||||||
let siz_went = u16[82] usize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let old_dat = ver_dat == 0;
|
|
||||||
let old_wad = match ver_wad {
|
|
||||||
Ver::Base => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let siz_ent = if old_wad {8 } else {10};
|
|
||||||
let siz_cnk = if old_wad {12} else {16};
|
|
||||||
|
|
||||||
if !old_wad && siz_ent != siz_went {
|
|
||||||
bail!("invalid entry size");
|
|
||||||
}
|
|
||||||
|
|
||||||
if !old_wad && siz_cnk != siz_wcnk {
|
|
||||||
bail!("invalid chunk size");
|
|
||||||
}
|
|
||||||
|
|
||||||
let entries = read_entries(b, old_wad, old_dat, siz_app, siz_ent, siz_cnk)?;
|
|
||||||
|
|
||||||
Ok(Wad{name, siz_app, entries})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Any kind of chunk in an `Entry`.
|
|
||||||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub enum Chunk
|
|
||||||
{
|
|
||||||
/** A `PICT` chunk. */ Pict(image::Image8),
|
|
||||||
/** A `Minf` chunk. */ Minf(map::minf::Minf),
|
|
||||||
/** An `iidx` chunk. */ Iidx(Vec<u16>),
|
|
||||||
/** An `EPNT` chunk. */ Epnt(Vec<map::epnt::Endpoint>),
|
|
||||||
/** A `PNTS` chunk. */ Pnts(Vec<map::pnts::Point>),
|
|
||||||
/** A `LINS` chunk. */ Lins(Vec<map::lins::Line>),
|
|
||||||
/** A `SIDS` chunk. */ Sids(Vec<map::sids::Side>),
|
|
||||||
/** A `POLY` chunk. */ Poly(Vec<map::poly::Polygon>),
|
|
||||||
/** A `LITE` chunk. */ Lite(Vec<map::lite::Light>),
|
|
||||||
/** An `OBJS` chunk. */ Objs(Vec<map::objs::Object>),
|
|
||||||
/** A `plac` chunk. */ Plac(Vec<map::plac::ObjectFreq>),
|
|
||||||
/** An `ambi` chunk. */ Ambi(Vec<map::ambi::SoundAmbi>),
|
|
||||||
/** A `bonk` chunk. */ Bonk(Vec<map::bonk::SoundRand>),
|
|
||||||
/** A `medi` chunk. */ Medi(Vec<map::medi::Media>),
|
|
||||||
/** A `plat` chunk. */ Plat(Vec<map::plat::Platform>),
|
|
||||||
/** A `NOTE` chunk. */ Note(Vec<map::note::Note>),
|
|
||||||
/** A `term` chunk. */ Term(Vec<map::term::Terminal>),
|
|
||||||
/** A `FXpx` chunk. */ Fxpx(Vec<phy::fxpx::Effect>),
|
|
||||||
/** A `MNpx` chunk. */ Mnpx(Vec<phy::mnpx::Monster>),
|
|
||||||
/** A `PRpx` chunk. */ Prpx(Vec<phy::prpx::Projectile>),
|
|
||||||
/** A `PXpx` chunk. */ Pxpx(Vec<phy::pxpx::Physics>),
|
|
||||||
/** A `WPpx` chunk. */ Wppx(Vec<phy::wppx::Weapon>),
|
|
||||||
|
|
||||||
/// Any other type of chunk, which may have arbitrary data in it.
|
|
||||||
Data{/** The name of the chunk. */ iden: Ident,
|
|
||||||
/** The data. */ data: Vec<u8>},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An entry containing chunks and application-specific data.
|
|
||||||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub struct Entry
|
|
||||||
{
|
|
||||||
/// All of the chunks in this `Entry`.
|
|
||||||
pub chunks: Vec<Chunk>,
|
|
||||||
|
|
||||||
/// The application specific data for this Entry.
|
|
||||||
pub appdata: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Map file containing entries.
|
|
||||||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub struct Wad
|
|
||||||
{
|
|
||||||
/// The original name of this file.
|
|
||||||
pub name: String,
|
|
||||||
|
|
||||||
/// The size of each `Entry`'s `appdata` field.
|
|
||||||
pub siz_app: usize,
|
|
||||||
|
|
||||||
/// All of the entries in this `Wad`.
|
|
||||||
pub entries: BTreeMap<u16, Entry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
c_enum! {
|
|
||||||
/// The version of a `Wad`.
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Ver: u16
|
|
||||||
{
|
|
||||||
Base = 0,
|
|
||||||
Dir = 1,
|
|
||||||
Over = 2,
|
|
||||||
Inf = 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
|
|
@ -1,7 +1,5 @@
|
||||||
//! Transfer Mode type.
|
//! Transfer Mode type.
|
||||||
|
|
||||||
use crate::durandal::err::*;
|
|
||||||
|
|
||||||
impl Default for TransferMode
|
impl Default for TransferMode
|
||||||
{
|
{
|
||||||
fn default() -> Self {TransferMode::Normal}
|
fn default() -> Self {TransferMode::Normal}
|
||||||
|
|
|
@ -9,7 +9,7 @@ mod block;
|
||||||
pub use block::*;
|
pub use block::*;
|
||||||
|
|
||||||
use super::{color, draw::*};
|
use super::{color, draw::*};
|
||||||
use crate::{durandal::image::*, marathon::{machdr, wad}};
|
use crate::{durandal::image::*, marathon::{machdr, map}};
|
||||||
|
|
||||||
impl MapEditor
|
impl MapEditor
|
||||||
{
|
{
|
||||||
|
@ -24,12 +24,12 @@ impl MapEditor
|
||||||
{
|
{
|
||||||
// TODO: handle errors gracefully
|
// TODO: handle errors gracefully
|
||||||
let b = &b[machdr::try_mac_header(b)..];
|
let b = &b[machdr::try_mac_header(b)..];
|
||||||
let wad = wad::read_wad(b).unwrap();
|
let wad = map::read(b).unwrap();
|
||||||
|
|
||||||
let ent = wad.entries.iter().nth(0).unwrap().1;
|
let ent = wad.entries.iter().nth(0).unwrap().1;
|
||||||
let info = ent.chunks.iter().find_map(|cnk| {
|
let info = ent.chunks.iter().find_map(|cnk| {
|
||||||
match cnk {
|
match cnk {
|
||||||
wad::Chunk::Minf(info) => Some(info),
|
map::chnk::Chunk::Minf(info) => Some(info),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}).unwrap().clone();
|
}).unwrap().clone();
|
||||||
|
|
19
tests/map.rs
19
tests/map.rs
|
@ -87,4 +87,23 @@ fn map_must_not_panic()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn phy_must_not_process()
|
||||||
|
{
|
||||||
|
for inp in &RANDOM {
|
||||||
|
bin::rd_array(inp, map::fxpx::read).err().unwrap();
|
||||||
|
bin::rd_array(inp, map::mnpx::read).err().unwrap();
|
||||||
|
bin::rd_array(inp, map::prpx::read).err().unwrap();
|
||||||
|
bin::rd_array(inp, map::wppx::read).err().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn phy_must_not_panic()
|
||||||
|
{
|
||||||
|
for inp in &RANDOM {
|
||||||
|
drop(bin::rd_array(inp, map::pxpx::read));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
|
24
tests/phy.rs
24
tests/phy.rs
|
@ -1,24 +0,0 @@
|
||||||
use maraiah::{durandal::bin, marathon::phy};
|
|
||||||
|
|
||||||
include!("data/rand.rs");
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn phy_must_not_process()
|
|
||||||
{
|
|
||||||
for inp in &RANDOM {
|
|
||||||
bin::rd_array(inp, phy::fxpx::read).err().unwrap();
|
|
||||||
bin::rd_array(inp, phy::mnpx::read).err().unwrap();
|
|
||||||
bin::rd_array(inp, phy::prpx::read).err().unwrap();
|
|
||||||
bin::rd_array(inp, phy::wppx::read).err().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn phy_must_not_panic()
|
|
||||||
{
|
|
||||||
for inp in &RANDOM {
|
|
||||||
drop(bin::rd_array(inp, phy::pxpx::read));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
|
10
tests/shp.rs
10
tests/shp.rs
|
@ -6,11 +6,11 @@ include!("data/rand.rs");
|
||||||
fn shp_must_not_process()
|
fn shp_must_not_process()
|
||||||
{
|
{
|
||||||
for inp in &RANDOM {
|
for inp in &RANDOM {
|
||||||
assert!(shp::read_bitmap(inp).is_err());
|
assert!(shp::bmap::read(inp).is_err());
|
||||||
assert!(shp::read_collection(inp).is_err());
|
assert!(shp::coll::read(inp).is_err());
|
||||||
assert!(shp::read_frame(inp).is_err());
|
assert!(shp::fram::read(inp).is_err());
|
||||||
assert!(shp::read_sequence(inp).is_err());
|
assert!(shp::sequ::read(inp).is_err());
|
||||||
assert!(shp::read_shapes(inp).is_err());
|
assert!(shp::read(inp).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@ include!("data/rand.rs");
|
||||||
fn snd_must_not_process()
|
fn snd_must_not_process()
|
||||||
{
|
{
|
||||||
for inp in &RANDOM {
|
for inp in &RANDOM {
|
||||||
assert!(snd::read_sound(inp).is_err());
|
assert!(snd::defs::read(inp).is_err());
|
||||||
assert!(snd::read_sound_def(inp).is_err());
|
assert!(snd::snds::read(inp).is_err());
|
||||||
assert!(snd::read_sounds(inp).is_err());
|
assert!(snd::read(inp).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue