make the commandline application actually useful

png-branch
an 2019-02-12 17:32:16 -05:00
parent 3329fde767
commit 5b3c8fcbe1
11 changed files with 211 additions and 59 deletions

View File

@ -18,6 +18,8 @@ failure = "0.1"
generic-array = "0.12"
#gtk = "0.4"
memmap = "0.6"
serde = {version = "1.0", features = ["derive"]}
serde_yaml = "0.8"
[profile.dev]
opt-level = 1

View File

@ -1,6 +1,7 @@
//! Binary data conversion utilities.
use crate::durandal::err::*;
use serde::Serialize;
use std::{fmt, num::NonZeroU16, slice::SliceIndex};
/// Returns a byte array from `b` at `i`.
@ -144,6 +145,7 @@ pub type Ident = [u8; 4];
/// An object identified by a `u16` which may be `u16::max_value()` to
/// represent None.
#[derive(Serialize)]
pub struct ObjID(Option<NonZeroU16>);
// EOF

16
src/durandal/file.rs Normal file
View File

@ -0,0 +1,16 @@
//! File utilities.
use crate::durandal::err::*;
use std::fs;
pub fn validate_folder_path(p: &str) -> ResultS<()>
{
let at = fs::metadata(p)?;
if !at.is_dir() {
Err(err_msg("not a directory"))
} else {
Ok(())
}
}
// EOF

View File

@ -1,6 +1,8 @@
use serde::Serialize;
use std::{fmt::{self, Write},
ops};
#[derive(Serialize)]
pub struct Fx32(i32);
impl Fx32

View File

@ -8,6 +8,7 @@ pub mod err;
pub mod bin;
pub mod chunk;
pub mod crc;
pub mod file;
pub mod fx32;
pub mod image;
pub mod text;

View File

@ -1,98 +1,149 @@
use maraiah::{durandal::{bin::*, chunk::*, err::*, image::*, text::*},
use maraiah::{durandal::{bin::*, chunk::*, err::*, file::*, image::*, text::*},
marathon::{machdr, map, pict, shp, term, wad}};
use std::{fs,
use std::{collections::HashSet,
fs,
io::{self, Write}};
fn write_chunk(cid: &Ident, cnk: &[u8], eid: u16) -> ResultS<()>
fn make_tga(fname: &str, im: &impl Image) -> ResultS<()>
{
let fname = format!("out/{:04}{}.bin", eid, mac_roman_conv(cid));
let out = fs::File::create(fname)?;
let mut out = io::BufWriter::new(out);
write_tga(&mut out, im)
}
fn make_chunk(opt: &Options, cid: &Ident, cnk: &[u8], eid: u16) -> ResultS<()>
{
let cid = mac_roman_conv(cid);
let fname = format!("{}/{:04}{}.bin", opt.out_dir, eid, cid);
let out = fs::File::create(&fname)?;
let mut out = io::BufWriter::new(out);
out.write(cnk)?;
Ok(())
}
fn read_chunk(cid: &Ident, cnk: &[u8], eid: u16) -> ResultS<()>
fn make_yaml(data: &impl serde::Serialize) -> ResultS<()>
{
serde_yaml::to_writer(io::stdout(), &data)?;
println!();
Ok(())
}
fn dump_chunk(opt: &Options, cid: &Ident, cnk: &[u8], eid: u16) -> ResultS<()>
{
if opt.wad_all {
make_chunk(opt, cid, cnk, eid)?;
return Ok(());
}
match cid {
b"PICT" => {
let im = pict::load_pict(cnk)?;
eprintln!("entry {} has PICT {}x{}", eid, im.w(), im.h());
let out = fs::File::create(&format!("out/{}.ppm", eid))?;
let mut out = io::BufWriter::new(out);
write_ppm(&mut out, &im)?;
if opt.wad_chunks.contains(cid) {
let im = pict::load_pict(cnk)?;
make_tga(&format!("{}/pict_{}.tga", opt.out_dir, eid), &im)?;
}
}
b"Minf" => {
let minf = map::Minf::chunk(cnk)?;
eprintln!("entry {} has {:#?}", eid, minf);
if opt.wad_chunks.contains(cid) {
make_yaml(&map::Minf::chunk(cnk)?)?;
}
}
b"EPNT" => {
let epnt = map::Endpoint::chunk(cnk)?;
eprintln!("entry {} has EPNT {:#?}", eid, epnt);
if opt.wad_chunks.contains(cid) {
make_yaml(&map::Endpoint::chunk(cnk)?)?;
}
}
b"PNTS" => {
let epnt = map::Point::chunk(cnk)?;
eprintln!("entry {} has PNTS {:#?}", eid, epnt);
if opt.wad_chunks.contains(cid) {
make_yaml(&map::Point::chunk(cnk)?)?;
}
}
b"LINS" => {
let line = map::Line::chunk(cnk)?;
eprintln!("entry {} has LINS {:#?}", eid, line);
if opt.wad_chunks.contains(cid) {
make_yaml(&map::Line::chunk(cnk)?)?;
}
}
b"SIDS" => {
let line = map::Side::chunk(cnk)?;
eprintln!("entry {} has SIDS {:#?}", eid, line);
if opt.wad_chunks.contains(cid) {
make_yaml(&map::Side::chunk(cnk)?)?;
}
}
b"term" => {
let term = term::Terminal::chunk(cnk)?;
eprintln!("entry {} has term {:#?}", eid, term);
if opt.wad_chunks.contains(cid) {
make_yaml(&term::Terminal::chunk(cnk)?)?;
}
}
cid => {
write_chunk(cid, cnk, eid)?;
if opt.wad_unknown {
make_chunk(opt, cid, cnk, eid)?;
}
}
}
Ok(())
}
fn process_wad(b: &[u8]) -> ResultS<()>
fn process_wad(opt: &Options, b: &[u8]) -> ResultS<()>
{
let wad = wad::Wad::new(b)?;
eprintln!("{:#?}", wad);
if opt.wad_header {
make_yaml(&wad)?;
}
for (eid, ent) in wad.entries {
for (cid, cnk) in ent.chunks {
read_chunk(&cid, cnk, eid)?;
dump_chunk(opt, &cid, cnk, eid)?;
}
}
Ok(())
}
fn dump_bitmaps(c: &shp::Collection, i: usize) -> ResultS<()>
fn dump_bitmaps(opt: &Options, c: &shp::Collection, i: usize) -> ResultS<()>
{
if !opt.shp_bmp {
return Ok(());
}
for (j, bmp) in c.bmps.iter().enumerate() {
for (k, tab) in c.tabs.iter().enumerate() {
let fname = format!("out/shape{}_{}_{}.tga", i, j, k);
let out = fs::File::create(&fname)?;
let mut out = io::BufWriter::new(out);
write_tga(&mut out, &shp::ImageShp::new(bmp, &tab))?;
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))?;
}
} else {
let fname = format!("{}/shape{}_{}.tga", opt.out_dir, i, j);
make_tga(&fname, &shp::ImageShp::new(bmp, &c.tabs[0]))?;
}
}
Ok(())
}
fn process_shp(b: &[u8]) -> ResultS<()>
fn write_shp_objs(opt: &Options, cl: &shp::Collection) -> ResultS<()>
{
if opt.shp_tab {
make_yaml(&cl.tabs)?;
}
if opt.shp_frm {
make_yaml(&cl.frms)?;
}
if opt.shp_seq {
make_yaml(&cl.seqs)?;
}
Ok(())
}
fn process_shp(opt: &Options, b: &[u8]) -> ResultS<()>
{
for (i, cl) in shp::read_shapes(b)?.iter().enumerate() {
if let Some(cl) = &cl.0 {
dump_bitmaps(cl, i)?;
eprintln!("<{} lo> {:#?}\n{:#?}", i, cl.frms, cl.seqs);
dump_bitmaps(opt, cl, i)?;
write_shp_objs(opt, cl)?;
}
if let Some(cl) = &cl.1 {
dump_bitmaps(cl, i + 100)?;
eprintln!("<{} hi> {:#?}\n{:#?}", i, cl.frms, cl.seqs);
dump_bitmaps(opt, cl, i + 100)?;
write_shp_objs(opt, cl)?;
}
}
@ -104,7 +155,7 @@ fn main() -> ResultS<()>
use argparse::*;
use memmap::Mmap;
let mut args: Vec<String> = Vec::new();
let mut opt: Options = Default::default();
{
let mut ap = ArgumentParser::new();
ap.set_description(env!("CARGO_PKG_DESCRIPTION"));
@ -113,12 +164,58 @@ fn main() -> ResultS<()>
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION"))),
"Show the version");
ap.refer(&mut args)
.add_argument("inputs", List, "Input files");
ap.refer(&mut opt.shp_tab)
.add_option(&["--shp-write-tab"], StoreTrue,
"shp: Dump all CLUTs as YAML to standard output");
ap.refer(&mut opt.shp_bmp)
.add_option(&["--shp-dump-bitmaps"], StoreTrue,
"shp: Dump bitmaps into a folder");
ap.refer(&mut opt.shp_bmp_all)
.add_option(&["--shp-dump-more-bitmaps"], StoreTrue,
"shp: Dump all color variations of each bitmap");
ap.refer(&mut opt.shp_frm)
.add_option(&["--shp-write-frm"], StoreTrue,
"shp: Dump all frames as YAML to standard output");
ap.refer(&mut opt.shp_seq)
.add_option(&["--shp-write-seq"], StoreTrue,
"shp: Dump all sequences as YAML to standard output");
ap.refer(&mut opt.wad_all)
.add_option(&["--wad-dump-all"], StoreTrue,
"wad: Dump all chunks into a folder");
ap.refer(&mut opt.wad_unknown)
.add_option(&["--wad-dump-unknown"], StoreTrue,
"wad: Dump all unknown chunks into a folder");
ap.refer(&mut opt.wad_header)
.add_option(&["--wad-write-header"], StoreTrue,
"wad: Dump header info as YAML to standard output");
ap.refer(&mut opt.wad_c_temp)
.add_option(&["--wad-write-chunks"], Store,
"wad: Dump specified chunks in various formats");
ap.refer(&mut opt.out_dir)
.add_option(&["--out-dir"], Store,
"Sets output directory for dump options");
ap.refer(&mut opt.inputs)
.add_argument("inputs", Collect, "Input files");
ap.parse_args_or_exit();
}
for arg in &args {
if opt.shp_bmp_all {
opt.shp_bmp = true;
}
if opt.out_dir.is_empty() {
opt.out_dir = ".".to_string();
}
if !opt.wad_c_temp.is_empty() {
for ctyp in opt.wad_c_temp.split(',') {
opt.wad_chunks.insert(c_iden(ctyp.as_bytes(), 0)?);
}
}
validate_folder_path(&opt.out_dir)?;
for arg in &opt.inputs {
let (typ, fna) = if let Some(st) = arg.find(':') {
arg.split_at(st + 1)
} else {
@ -130,8 +227,8 @@ fn main() -> ResultS<()>
let b = c_data(&mm, machdr::try_mac_header(&mm)..)?;
match typ {
"wad:" => process_wad(b),
"shp:" => process_shp(b),
"wad:" => process_wad(&opt, b),
"shp:" => process_shp(&opt, b),
_ => Err(err_msg("invalid file type specified on commandline")),
}?;
}
@ -139,4 +236,21 @@ fn main() -> ResultS<()>
Ok(())
}
#[derive(Default)]
struct Options
{
inputs: Vec<String>,
out_dir: String,
shp_tab: bool,
shp_bmp: bool,
shp_bmp_all: bool,
shp_frm: bool,
shp_seq: bool,
wad_all: bool,
wad_unknown: bool,
wad_header: bool,
wad_chunks: HashSet<Ident>,
wad_c_temp: String,
}
// EOF

View File

@ -2,6 +2,7 @@ use crate::{durandal::{bin::*, chunk::*, err::*, fx32::*,
text::mac_roman_conv},
marathon::xfer::TransferMode};
use bitflags::bitflags;
use serde::Serialize;
use std::fmt;
impl Chunked<Point> for Point
@ -118,13 +119,14 @@ impl Chunker<Minf> for Minf
type Unit = i16;
#[derive(Serialize)]
pub struct Point
{
x: Unit,
y: Unit,
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct Endpoint
{
flags: EndpFlags,
@ -134,7 +136,7 @@ pub struct Endpoint
support: u16,
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct Line
{
flags: LineFlags,
@ -149,14 +151,14 @@ pub struct Line
poly_b: u16,
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct SideTex
{
offs: Point,
tex_id: ObjID,
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct Side
{
stype: u16,
@ -176,7 +178,7 @@ pub struct Side
shade: Fx32,
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct Minf
{
env_code: u16,
@ -189,6 +191,7 @@ pub struct Minf
}
bitflags! {
#[derive(Serialize)]
pub struct EndpFlags: u16
{
const Solid = 0x00_01;
@ -198,6 +201,7 @@ bitflags! {
}
bitflags! {
#[derive(Serialize)]
pub struct LineFlags: u16
{
const TransSide = 0x02_00;
@ -210,6 +214,7 @@ bitflags! {
}
bitflags! {
#[derive(Serialize)]
pub struct SideFlags: u16
{
const Status = 0x00_01;
@ -224,6 +229,7 @@ bitflags! {
}
bitflags! {
#[derive(Serialize)]
pub struct EnvFlags: u16
{
const Vacuum = 0x00_01;
@ -243,6 +249,7 @@ bitflags! {
}
bitflags! {
#[derive(Serialize)]
pub struct EntFlags: u32
{
const Solo = 0x00_01;
@ -257,6 +264,7 @@ bitflags! {
}
bitflags! {
#[derive(Serialize)]
pub struct MsnFlags: u16
{
const Extermination = 0x00_01;

View File

@ -3,6 +3,7 @@
use crate::{durandal::{bin::*, err::*, fx32::*, image::*, text::*},
marathon::xfer::TransferMode};
use bitflags::bitflags;
use serde::Serialize;
fn color(b: &[u8]) -> ResultS<(usize, ColorShp)>
{
@ -313,7 +314,7 @@ impl Color for ColorShp
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize)]
pub enum ColorShp
{
Translucent,
@ -342,7 +343,7 @@ pub struct ImageShp<'a, 'b>
clut: &'b [ColorShp],
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct Frame
{
flags: FrameFlags,
@ -356,7 +357,7 @@ pub struct Frame
wrl_y: i16,
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct Sequence
{
name: String,
@ -393,6 +394,7 @@ bitflags! {
}
bitflags! {
#[derive(Serialize)]
pub struct FrameFlags: u16
{
const Obscure = 0x20_00;
@ -402,7 +404,7 @@ bitflags! {
}
c_enum! {
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub enum CollectionType: u16
{
0 => Unused,
@ -414,7 +416,7 @@ c_enum! {
}
c_enum! {
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub enum ViewType: u16
{
1 => Anim,

View File

@ -1,4 +1,5 @@
use crate::durandal::{bin::*, chunk::*, err::*, text::*};
use serde::Serialize;
use std::fmt;
fn read_group(b: &[u8], text: &[u8]) -> ResultS<Group>
@ -78,7 +79,7 @@ impl Chunker<Vec<Terminal>> for Terminal
}
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct Terminal
{
lines: u16,
@ -86,7 +87,7 @@ pub struct Terminal
faces: Vec<Face>,
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct Face
{
start: usize,
@ -94,6 +95,7 @@ pub struct Face
color: u16,
}
#[derive(Serialize)]
pub struct Group
{
ttype: GroupType,
@ -103,7 +105,7 @@ pub struct Group
}
c_enum! {
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub enum GroupType: u16
{
0 => Logon,

View File

@ -1,6 +1,7 @@
//! Marathon Wad format handling.
use crate::durandal::{bin::*, err::*, text::mac_roman_conv};
use serde::Serialize;
use std::{collections::BTreeMap, fmt};
impl Wad<'_>
@ -85,18 +86,19 @@ pub struct Entry<'a>
pub appdata: &'a [u8],
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct Wad<'a>
{
wadver: Ver,
dataver: u16,
origname: String,
appsize: usize,
#[serde(skip)]
pub entries: EntryMap<'a>,
}
c_enum! {
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub enum Ver: u16
{
0 => Base,

View File

@ -1,7 +1,8 @@
use crate::durandal::err::*;
use serde::Serialize;
c_enum! {
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub enum TransferMode: u16
{
0 => Normal,