rewrite wad code to be more idiomatic

png-branch
an 2019-03-01 04:27:28 -05:00
parent fc27d2d0f4
commit 35452cd908
2 changed files with 175 additions and 164 deletions

View File

@ -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

View File

@ -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