rewrite wad code to be more idiomatic
parent
fc27d2d0f4
commit
35452cd908
|
@ -1,21 +1,18 @@
|
|||
use maraiah::{durandal::{bin::*, err::*, file::*, image::*, sound::*, text::*},
|
||||
marathon::{machdr, map, phy, pict, shp, snd, trm, wad}};
|
||||
use std::{collections::HashSet,
|
||||
fs,
|
||||
use maraiah::{durandal::{err::*, file::*, image::*, sound::*, text::*},
|
||||
marathon::{machdr, shp, snd, wad}};
|
||||
use std::{fs,
|
||||
io::{self, Write}};
|
||||
|
||||
fn make_tga(fname: &str, im: &impl Image) -> ResultS<()>
|
||||
fn make_tga(_opt: &Options, fname: &str, im: &impl Image) -> ResultS<()>
|
||||
{
|
||||
let mut out = io::BufWriter::new(fs::File::create(fname)?);
|
||||
write_tga(&mut out, im)
|
||||
}
|
||||
|
||||
fn make_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
||||
fn make_data(_opt: &Options, fname: &str, data: &[u8]) -> ResultS<()>
|
||||
{
|
||||
let cid = mac_roman_conv(&cid);
|
||||
let fname = format!("{}/{:04}{}.bin", opt.out_dir, eid, cid);
|
||||
let mut out = io::BufWriter::new(fs::File::create(&fname)?);
|
||||
out.write_all(cnk)?;
|
||||
let mut out = io::BufWriter::new(fs::File::create(fname)?);
|
||||
out.write_all(data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -37,42 +34,23 @@ fn make_wav(fname: &str, snd: &impl Sound) -> ResultS<()>
|
|||
write_wav(&mut out, snd)
|
||||
}
|
||||
|
||||
fn dump_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
||||
fn dump_chunk(opt: &Options, cnk: &wad::Chunk, eid: u16) -> ResultS<()>
|
||||
{
|
||||
if opt.wad_all {
|
||||
make_chunk(opt, cid, cnk, eid)?;
|
||||
}
|
||||
|
||||
if opt.wad_wrt_all || opt.wad_chunks.contains(&cid) {
|
||||
match &cid {
|
||||
b"PICT" => {
|
||||
let im = pict::load_pict(cnk)?;
|
||||
make_tga(&format!("{}/pict_{}.tga", opt.out_dir, eid), &im)?;
|
||||
if opt.wad_wrt_all {
|
||||
match cnk {
|
||||
wad::Chunk::Pict(im) => {
|
||||
let fname = format!("{}/pict_{}.tga", opt.out_dir, eid);
|
||||
make_tga(opt, &fname, im)?;
|
||||
}
|
||||
b"Minf" => make_yaml(opt, &map::read_minf(cnk)?)?,
|
||||
b"EPNT" => make_yaml(opt, &rd_array(cnk, map::read_epnt)?)?,
|
||||
b"PNTS" => make_yaml(opt, &rd_array(cnk, map::read_pnts)?)?,
|
||||
b"LINS" => make_yaml(opt, &rd_array(cnk, map::read_lins)?)?,
|
||||
b"SIDS" => make_yaml(opt, &rd_array(cnk, map::read_sids)?)?,
|
||||
b"POLY" => make_yaml(opt, &rd_array(cnk, map::read_poly)?)?,
|
||||
b"LITE" => make_yaml(opt, &rd_array(cnk, map::read_lite)?)?,
|
||||
b"OBJS" => make_yaml(opt, &rd_array(cnk, map::read_objs)?)?,
|
||||
b"plac" => make_yaml(opt, &rd_array(cnk, map::read_plac)?)?,
|
||||
b"ambi" => make_yaml(opt, &rd_array(cnk, map::read_ambi)?)?,
|
||||
b"bonk" => make_yaml(opt, &rd_array(cnk, map::read_bonk)?)?,
|
||||
b"medi" => make_yaml(opt, &rd_array(cnk, map::read_medi)?)?,
|
||||
b"plat" => make_yaml(opt, &rd_array(cnk, map::read_plat)?)?,
|
||||
b"term" => make_yaml(opt, &rd_array(cnk, trm::read_term)?)?,
|
||||
b"FXpx" => make_yaml(opt, &rd_array(cnk, phy::read_fxpx)?)?,
|
||||
b"MNpx" => make_yaml(opt, &rd_array(cnk, phy::read_mnpx)?)?,
|
||||
b"PRpx" => make_yaml(opt, &rd_array(cnk, phy::read_prpx)?)?,
|
||||
b"PXpx" => make_yaml(opt, &rd_array(cnk, phy::read_pxpx)?)?,
|
||||
b"WPpx" => make_yaml(opt, &rd_array(cnk, phy::read_wppx)?)?,
|
||||
_ => {
|
||||
if opt.wad_unknown && !opt.wad_all {
|
||||
make_chunk(opt, cid, cnk, eid)?;
|
||||
wad::Chunk::Data{iden, data} => {
|
||||
if opt.wad_unknown {
|
||||
let iden = mac_roman_conv(iden);
|
||||
let fname = format!("{}/{:04}{}.bin", opt.out_dir, eid, iden);
|
||||
make_data(opt, &fname, data)?;
|
||||
}
|
||||
make_yaml(opt, cnk)?;
|
||||
}
|
||||
_ => make_yaml(opt, cnk)?,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,15 +59,15 @@ fn dump_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
|||
|
||||
fn process_wad(opt: &Options, b: &[u8]) -> ResultS<()>
|
||||
{
|
||||
let wad = wad::Wad::new(b)?;
|
||||
let wad = wad::read_wad(b)?;
|
||||
|
||||
if opt.wad_header {
|
||||
make_yaml(opt, &wad.head)?;
|
||||
}
|
||||
|
||||
for (eid, ent) in wad.entries {
|
||||
for (cid, cnk) in ent.chunks {
|
||||
dump_chunk(opt, cid, cnk, eid)?;
|
||||
for cnk in ent.chunks {
|
||||
dump_chunk(opt, &cnk, eid)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,11 +84,11 @@ fn dump_bitmaps(opt: &Options, c: &shp::Collection, i: usize) -> ResultS<()>
|
|||
if opt.shp_bmp_all {
|
||||
for (k, tab) in c.tabs.iter().enumerate() {
|
||||
let fname = format!("{}/shape{}_{}_{}.tga", opt.out_dir, i, j, k);
|
||||
make_tga(&fname, &shp::ImageShp::new(bmp, &tab))?;
|
||||
make_tga(opt, &fname, &shp::ImageShp::new(bmp, &tab))?;
|
||||
}
|
||||
} else {
|
||||
let fname = format!("{}/shape{}_{}.tga", opt.out_dir, i, j);
|
||||
make_tga(&fname, &shp::ImageShp::new(bmp, &c.tabs[0]))?;
|
||||
make_tga(opt, &fname, &shp::ImageShp::new(bmp, &c.tabs[0]))?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,11 +223,6 @@ fn main() -> ResultS<()>
|
|||
StoreTrue,
|
||||
"snd: Dump all sounds to WAVE files");
|
||||
|
||||
arg!("--wad-dump-all",
|
||||
opt.wad_all,
|
||||
StoreTrue,
|
||||
"wad: Dump all chunks into a folder");
|
||||
|
||||
arg!("--wad-write-all",
|
||||
opt.wad_wrt_all,
|
||||
StoreTrue,
|
||||
|
@ -265,11 +238,6 @@ fn main() -> ResultS<()>
|
|||
StoreTrue,
|
||||
"wad: Dump header info as YAML to standard output");
|
||||
|
||||
arg!("--wad-write-chunks",
|
||||
opt.wad_c_temp,
|
||||
Store,
|
||||
"wad: Dump specified chunks in various formats");
|
||||
|
||||
arg!("--out-dir",
|
||||
opt.out_dir,
|
||||
Store,
|
||||
|
@ -291,12 +259,6 @@ fn main() -> ResultS<()>
|
|||
opt.out_dir = ".".to_string();
|
||||
}
|
||||
|
||||
if !opt.wad_c_temp.is_empty() {
|
||||
for ctyp in opt.wad_c_temp.split(',') {
|
||||
opt.wad_chunks.insert(ident(ctyp.as_bytes()));
|
||||
}
|
||||
}
|
||||
|
||||
validate_folder_path(&opt.out_dir)?;
|
||||
|
||||
for arg in &opt.inputs {
|
||||
|
@ -334,12 +296,9 @@ struct Options
|
|||
shp_seq: bool,
|
||||
snd_dump: bool,
|
||||
snd_write: bool,
|
||||
wad_all: bool,
|
||||
wad_unknown: bool,
|
||||
wad_wrt_all: bool,
|
||||
wad_header: bool,
|
||||
wad_chunks: HashSet<Ident>,
|
||||
wad_c_temp: String,
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -1,85 +1,52 @@
|
|||
//! Marathon Wad format handling.
|
||||
|
||||
use crate::durandal::{bin::*, err::*, text::mac_roman_conv};
|
||||
use crate::durandal::{bin::*, err::*, image, text::mac_roman_conv};
|
||||
use crate::marathon::{map, phy, pict, trm};
|
||||
use serde::Serialize;
|
||||
use std::{collections::BTreeMap, fmt};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl Wad<'_>
|
||||
/// Reads all chunks in an entry.
|
||||
pub fn read_chunks(b: &[u8], siz_cnk: usize) -> ResultS<Vec<Chunk>>
|
||||
{
|
||||
pub fn new(b: &[u8]) -> ResultS<Wad<'_>>
|
||||
{
|
||||
read_data! {
|
||||
128, BE in b =>
|
||||
wadver = u16[0];
|
||||
dataver = u16[2];
|
||||
origname = mac_roman_conv[4..68] nt;
|
||||
dirofs = u32[72] as usize;
|
||||
numents = u16[76] as usize;
|
||||
appsize = u16[78] as usize;
|
||||
wcnksize = u16[80] as usize;
|
||||
wentsize = u16[82] as usize;
|
||||
}
|
||||
|
||||
let wadver = Ver::from_repr(wadver)?;
|
||||
|
||||
let is_old = match wadver {
|
||||
Ver::Base => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let entsize = if is_old {8 } else {10};
|
||||
let cnksize = if is_old {12} else {16};
|
||||
|
||||
if entsize != wentsize {
|
||||
bail!("invalid entry size");
|
||||
}
|
||||
|
||||
if cnksize != wcnksize {
|
||||
bail!("invalid chunk size");
|
||||
}
|
||||
|
||||
let mut entries = EntryMap::new();
|
||||
let mut p = dirofs;
|
||||
|
||||
for i in 0..numents {
|
||||
read_data! {
|
||||
p + entsize, BE in b =>
|
||||
offset = u32[p] as usize;
|
||||
size = u32[p + 4] as usize;
|
||||
index = u16[p + 8];
|
||||
}
|
||||
|
||||
let index = if is_old {i as u16} else {index};
|
||||
|
||||
let cnkdata = &b[offset..offset + size];
|
||||
let chunks = get_chunks(cnkdata, cnksize)?;
|
||||
let appdata = &b[p..p + appsize];
|
||||
|
||||
entries.insert(index, Entry{chunks, appdata});
|
||||
|
||||
p += entsize + appsize;
|
||||
}
|
||||
|
||||
Ok(Wad{head: WadHeader{wadver, dataver, appsize, origname}, entries})
|
||||
}
|
||||
}
|
||||
|
||||
fn get_chunks(b: &[u8], cnksize: usize) -> ResultS<ChunkMap<'_>>
|
||||
{
|
||||
let mut chunks = ChunkMap::new();
|
||||
let mut chunks = Vec::new();
|
||||
let mut p = 0;
|
||||
|
||||
while p < b.len() {
|
||||
read_data! {
|
||||
p + cnksize, BE in b =>
|
||||
iden = iden[p];
|
||||
p + siz_cnk, BE in b =>
|
||||
iden = Ident[p];
|
||||
size = u32[p + 8] as usize;
|
||||
}
|
||||
|
||||
let beg = p + cnksize;
|
||||
let end = beg + size;
|
||||
let beg = p + siz_cnk;
|
||||
let end = beg + size;
|
||||
let data = &b[beg..end];
|
||||
|
||||
chunks.insert(iden, &b[beg..end]);
|
||||
let chunk = match &iden {
|
||||
b"PICT" => Chunk::Pict(pict::load_pict(data)?),
|
||||
b"Minf" => Chunk::Minf(map::read_minf(data)?),
|
||||
b"EPNT" => Chunk::Pnts(rd_array(data, map::read_epnt)?),
|
||||
b"PNTS" => Chunk::Pnts(rd_array(data, map::read_pnts)?),
|
||||
b"LINS" => Chunk::Lins(rd_array(data, map::read_lins)?),
|
||||
b"SIDS" => Chunk::Sids(rd_array(data, map::read_sids)?),
|
||||
b"POLY" => Chunk::Poly(rd_array(data, map::read_poly)?),
|
||||
b"LITE" => Chunk::Lite(rd_array(data, map::read_lite)?),
|
||||
b"OBJS" => Chunk::Objs(rd_array(data, map::read_objs)?),
|
||||
b"plac" => Chunk::Plac(rd_array(data, map::read_plac)?),
|
||||
b"ambi" => Chunk::Ambi(rd_array(data, map::read_ambi)?),
|
||||
b"bonk" => Chunk::Bonk(rd_array(data, map::read_bonk)?),
|
||||
b"medi" => Chunk::Medi(rd_array(data, map::read_medi)?),
|
||||
b"plat" => Chunk::Plat(rd_array(data, map::read_plat)?),
|
||||
b"term" => Chunk::Term(rd_array(data, trm::read_term)?),
|
||||
b"FXpx" => Chunk::Fxpx(rd_array(data, phy::read_fxpx)?),
|
||||
b"MNpx" => Chunk::Mnpx(rd_array(data, phy::read_mnpx)?),
|
||||
b"PRpx" => Chunk::Prpx(rd_array(data, phy::read_prpx)?),
|
||||
b"PXpx" => Chunk::Pxpx(rd_array(data, phy::read_pxpx)?),
|
||||
b"WPpx" => Chunk::Wppx(rd_array(data, phy::read_wppx)?),
|
||||
_ => Chunk::Data{iden, data: data.to_vec()},
|
||||
};
|
||||
|
||||
chunks.push(chunk);
|
||||
|
||||
p = end;
|
||||
}
|
||||
|
@ -87,34 +54,134 @@ fn get_chunks(b: &[u8], cnksize: usize) -> ResultS<ChunkMap<'_>>
|
|||
Ok(chunks)
|
||||
}
|
||||
|
||||
type Chunk<'a> = &'a [u8];
|
||||
type ChunkMap<'a> = BTreeMap<Ident, Chunk<'a>>;
|
||||
type EntryMap<'a> = BTreeMap<u16, Entry<'a>>;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Entry<'a>
|
||||
/// Reads all entries in a `Wad`.
|
||||
pub fn read_entries(b: &[u8],
|
||||
is_old: bool,
|
||||
siz_app: usize,
|
||||
siz_ent: usize,
|
||||
siz_cnk: usize)
|
||||
-> ResultS<BTreeMap<u16, Entry>>
|
||||
{
|
||||
pub chunks: ChunkMap<'a>,
|
||||
pub appdata: &'a [u8],
|
||||
read_data! {
|
||||
128, BE in b =>
|
||||
dirofs = u32[72] as usize;
|
||||
numents = u16[76] as usize;
|
||||
}
|
||||
|
||||
let mut entries = BTreeMap::new();
|
||||
let mut p = dirofs;
|
||||
|
||||
for i in 0..numents {
|
||||
read_data! {
|
||||
p + siz_ent, BE in b =>
|
||||
offset = u32[p] as usize;
|
||||
size = u32[p + 4] as usize;
|
||||
index = u16[p + 8];
|
||||
}
|
||||
|
||||
let index = if is_old {i as u16} else {index};
|
||||
|
||||
let chunks = read_chunks(&b[offset..offset + size], 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! {
|
||||
128, BE in b =>
|
||||
ver_wad = u16[0];
|
||||
ver_dat = u16[2];
|
||||
name = mac_roman_conv[4..68] nt;
|
||||
siz_app = u16[78] as usize;
|
||||
siz_wcnk = u16[80] as usize;
|
||||
siz_went = u16[82] as usize;
|
||||
}
|
||||
|
||||
let ver_wad = Ver::from_repr(ver_wad)?;
|
||||
|
||||
let is_old = match ver_wad {
|
||||
Ver::Base => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let siz_ent = if is_old {8 } else {10};
|
||||
let siz_cnk = if is_old {12} else {16};
|
||||
|
||||
if siz_ent != wentsize {
|
||||
bail!("invalid entry size");
|
||||
}
|
||||
|
||||
if siz_cnk != wcnksize {
|
||||
bail!("invalid chunk size");
|
||||
}
|
||||
|
||||
let entries = read_entries(b, is_old, siz_app, siz_ent, siz_cnk)?;
|
||||
|
||||
Ok(Wad{head: WadHeader{ver_wad, ver_dat, siz_app, name}, entries})
|
||||
}
|
||||
|
||||
/// Any kind of chunk in an `Entry`.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum Chunk
|
||||
{
|
||||
Pict(image::Image8),
|
||||
Minf(map::Minf),
|
||||
Pnts(Vec<map::Point>),
|
||||
Lins(Vec<map::Line>),
|
||||
Sids(Vec<map::Side>),
|
||||
Poly(Vec<map::Polygon>),
|
||||
Lite(Vec<map::Light>),
|
||||
Objs(Vec<map::Object>),
|
||||
Plac(Vec<map::ObjectFreq>),
|
||||
Ambi(Vec<map::SoundAmbi>),
|
||||
Bonk(Vec<map::SoundRand>),
|
||||
Medi(Vec<map::Media>),
|
||||
Plat(Vec<map::Platform>),
|
||||
Term(Vec<trm::Terminal>),
|
||||
Fxpx(Vec<phy::Effect>),
|
||||
Mnpx(Vec<phy::Monster>),
|
||||
Prpx(Vec<phy::Projectile>),
|
||||
Pxpx(Vec<phy::Physics>),
|
||||
Wppx(Vec<phy::Weapon>),
|
||||
Data{iden: Ident, data: Vec<u8>},
|
||||
}
|
||||
|
||||
/// An entry containing chunks and application-specific data.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Entry
|
||||
{
|
||||
pub chunks: Vec<Chunk>,
|
||||
pub appdata: Vec<u8>,
|
||||
}
|
||||
|
||||
/// The header of a `Wad`.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct WadHeader
|
||||
{
|
||||
pub wadver: Ver,
|
||||
pub dataver: u16,
|
||||
pub origname: String,
|
||||
pub appsize: usize,
|
||||
pub ver_wad: Ver,
|
||||
pub ver_dat: u16,
|
||||
pub name: String,
|
||||
pub siz_app: usize,
|
||||
}
|
||||
|
||||
/// A Map file containing entries.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Wad<'a>
|
||||
pub struct Wad
|
||||
{
|
||||
pub head: WadHeader,
|
||||
pub entries: EntryMap<'a>,
|
||||
pub entries: BTreeMap<u16, Entry>,
|
||||
}
|
||||
|
||||
c_enum! {
|
||||
/// The version of a `Wad`.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum Ver: u16
|
||||
{
|
||||
|
@ -125,19 +192,4 @@ c_enum! {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Entry<'_>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
write!(f, "Entry{{ ")?;
|
||||
for iden in self.chunks.keys() {
|
||||
write!(f, "{} ", mac_roman_conv(iden))?;
|
||||
}
|
||||
if !self.appdata.is_empty() {
|
||||
write!(f, "\nappdata: {:?} ", self.appdata)?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
Loading…
Reference in New Issue