Compare commits

...

3 Commits

Author SHA1 Message Date
an 35452cd908 rewrite wad code to be more idiomatic 2019-03-01 04:27:28 -05:00
an fc27d2d0f4 add documentation for everything 2019-03-01 04:27:14 -05:00
an 789e3de93f more tycho stuff 2019-03-01 04:25:42 -05:00
26 changed files with 850 additions and 405 deletions

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
/target
/out
data/*.res
*.res
**/*.rs.bk
Cargo.lock
perf.data*

View File

@ -5,67 +5,70 @@ use serde::Serialize;
use std::{fmt, num::NonZeroU16};
#[doc(hidden)]
macro_rules! rd_1 {
#[macro_export]
macro_rules! _durandal_read_impl {
// big endian
(BE $b:ident $nam:ident u16 $n:expr) => {
rd_1!($b u16::from_be_bytes, $nam 2 $n);
_durandal_read_impl!($b u16::from_be_bytes, $nam 2 $n);
};
(BE $b:ident $nam:ident i16 $n:expr) => {
rd_1!($b i16::from_be_bytes, $nam 2 $n);
_durandal_read_impl!($b i16::from_be_bytes, $nam 2 $n);
};
(BE $b:ident $nam:ident u32 $n:expr) => {
rd_1!($b u32::from_be_bytes, $nam 4 $n);
_durandal_read_impl!($b u32::from_be_bytes, $nam 4 $n);
};
(BE $b:ident $nam:ident i32 $n:expr) => {
rd_1!($b i32::from_be_bytes, $nam 4 $n);
_durandal_read_impl!($b i32::from_be_bytes, $nam 4 $n);
};
(BE $b:ident $nam:ident as usize u16 $n:expr) => {
rd_1!($b u16::from_be_bytes, $nam 2 $n);
_durandal_read_impl!($b u16::from_be_bytes, $nam 2 $n);
let $nam = $nam as usize;
};
(BE $b:ident $nam:ident as usize u32 $n:expr) => {
rd_1!($b u32::from_be_bytes, $nam 4 $n);
_durandal_read_impl!($b u32::from_be_bytes, $nam 4 $n);
let $nam = $nam as usize;
};
(BE $b:ident $nam:ident Angle $n:expr) => {
rd_1!($b u16::from_be_bytes, $nam 2 $n);
let $nam = Angle::from_bits($nam);
};
(BE $b:ident $nam:ident Fixed $n:expr) => {
rd_1!($b u32::from_be_bytes, $nam 4 $n);
let $nam = Fixed::from_bits($nam);
};
(BE $b:ident $nam:ident Unit $n:expr) => {
rd_1!($b u16::from_be_bytes, $nam 2 $n);
let $nam = Unit::from_bits($nam);
};
(BE $b:ident $nam:ident OptU16 $n:expr) => {
rd_1!($b u16::from_be_bytes, $nam 2 $n);
let $nam = OptU16::from_repr($nam);
};
// little endian
(LE $b:ident $nam:ident u16 $n:expr) => {
rd_1!($b u16::from_le_bytes $nam 2 $n);
_durandal_read_impl!($b u16::from_le_bytes $nam 2 $n);
};
(LE $b:ident $nam:ident i16 $n:expr) => {
rd_1!($b i16::from_le_bytes $nam 2 $n);
_durandal_read_impl!($b i16::from_le_bytes $nam 2 $n);
};
(LE $b:ident $nam:ident u32 $n:expr) => {
rd_1!($b u32::from_le_bytes $nam 4 $n);
_durandal_read_impl!($b u32::from_le_bytes $nam 4 $n);
};
(LE $b:ident $nam:ident i32 $n:expr) => {
rd_1!($b i32::from_le_bytes $nam 4 $n);
_durandal_read_impl!($b i32::from_le_bytes $nam 4 $n);
};
(LE $b:ident $nam:ident as usize u16 $n:expr) => {
rd_1!($b u16::from_le_bytes $nam 2 $n);
_durandal_read_impl!($b u16::from_le_bytes $nam 2 $n);
let $nam = $nam as usize;
};
(LE $b:ident $nam:ident as usize u32 $n:expr) => {
rd_1!($b u32::from_le_bytes $nam 4 $n);
_durandal_read_impl!($b u32::from_le_bytes $nam 4 $n);
let $nam = $nam as usize;
};
// either endianness
($e:ident $b:ident $nam:ident Angle $n:expr) => {
_durandal_read_impl!($e $b $nam u16 $n);
let $nam = Angle::from_bits($nam);
};
($e:ident $b:ident $nam:ident Fixed $n:expr) => {
_durandal_read_impl!($e $b $nam u32 $n);
let $nam = Fixed::from_bits($nam);
};
($e:ident $b:ident $nam:ident Unit $n:expr) => {
_durandal_read_impl!($e $b $nam u16 $n);
let $nam = Unit::from_bits($nam);
};
($e:ident $b:ident $nam:ident OptU16 $n:expr) => {
_durandal_read_impl!($e $b $nam u16 $n);
let $nam = OptU16::from_repr($nam);
};
// generic endianness
($_:ident $b:ident $nam:ident u8 $n:expr) => {
let $nam = $b[$n];
@ -76,7 +79,7 @@ macro_rules! rd_1 {
($_:ident $b:ident $nam:ident i8 $n:expr) => {
let $nam = $b[$n] as i8;
};
($_:ident $b:ident $nam:ident iden $n:expr) => {
($_:ident $b:ident $nam:ident Ident $n:expr) => {
let $nam = [$b[$n], $b[$n + 1], $b[$n + 2], $b[$n + 3]];
};
($_:ident $b:ident $nam:ident $f:ident $n:expr) => {
@ -95,6 +98,48 @@ macro_rules! rd_1 {
};
}
/// Reads structured data from a byte array.
///
/// First start by specifying the endianness, size and source using the syntax
/// `endian, size in source =>` where:
///
/// - `endian` is `BE` or `LE` for big- or little-endian respectively.
/// - `size` is an expression specifying the last index that should be used by
/// this macro in `source`.
/// - `source` is a `u8` slice to read data from.
///
/// After the initializer line, all lines have the syntax
/// `name = type[place] opts;` where:
///
/// - `name` is the binding to put the resulting data in.
/// - `type` is one of:
/// - `u8` or `i8`: one byte will be read at `place`.
/// - `u16` or `i16`: two bytes will be read at `place` with `from_*_bytes`.
/// - `u32` or `i32`: four bytes will be read at `place` with `from_*_bytes`.
/// - `Ident`: four bytes will be read at `place` into an array, disregarding
/// endianness, creating an `Ident` object.
/// - `Angle`: same as `u16`, but the result is passed to
/// `fixed::Angle::from_bits`, resulting in a `fixed::Angle` object.
/// - `Fixed`: same as `u32`, but the result is passed to
/// `fixed::Fixed::from_bits`, resulting in a `fixed::Fixed` object.
/// - `Unit`: same as `u16`, but the result is passed to
/// `fixed::Unit::from_bits`, resulting in a `fixed::Unit` object.
/// - `OptU16`: same as `u16`, but the result is passed to
/// `OptU16::from_repr`, resulting in an `OptU16` object.
/// - The name of a function, which is passed `&source[place]` as its only
/// argument. The function's result has the `?` operator applied to it.
/// - `opts` may be one of:
/// - `array` when `type` is `u8`: `place` is a range specifying a `u8` slice
/// to be taken from `source`.
/// - `as usize` when `type` is `u16` or `u32`: converts the resulting
/// integer to `usize` by primitive cast.
/// - `nt` when `type` is a function name: does not use the `?` operator on
/// the resulting function call
/// - Nothing at all.
/// - `place` is either an integer literal which must be representable as
/// `usize`, or a range, which may only be used when `type` is a function
/// name.
#[macro_export]
macro_rules! read_data {
(
$sz:expr , $ty:ident in $b:ident =>
@ -104,16 +149,41 @@ macro_rules! read_data {
bail!("not enough data");
}
$(rd_1!($ty $b $nam $($ex)* $t $n);)*
$($crate::_durandal_read_impl!($ty $b $nam $($ex)* $t $n);)*
};
}
/// Creates an `Ident` from a slice.
///
/// `b` must be at least 4 bytes, or a panic will occur.
pub fn ident(b: &[u8]) -> Ident {[b[0], b[1], b[2], b[3]]}
/// Applies `u32::from_be_bytes` to a slice.
///
/// `b` must be at least 4 bytes, or a panic will occur.
pub fn u32b(b: &[u8]) -> u32 {u32::from_be_bytes([b[0], b[1], b[2], b[3]])}
/// Applies `u16::from_be_bytes` to a slice.
///
/// `b` must be at least 2 bytes, or a panic will occur.
pub fn u16b(b: &[u8]) -> u16 {u16::from_be_bytes([b[0], b[1]])}
/// Applies `i32::from_be_bytes` to a slice.
///
/// `b` must be at least 4 bytes, or a panic will occur.
pub fn i32b(b: &[u8]) -> i32 {i32::from_be_bytes([b[0], b[1], b[2], b[3]])}
/// Applies `i16::from_be_bytes` to a slice.
///
/// `b` must be at least 2 bytes, or a panic will occur.
pub fn i16b(b: &[u8]) -> i16 {i16::from_be_bytes([b[0], b[1]])}
/// Applies a read function over a slice.
///
/// Applies `read` over `b`, resulting in a vector of its return values. Each
/// iteration will pass a slice of `b` to `read` for it to read from, and then
/// increments the slice index by the second return value. When there is no
/// data left in `b`, the function returns.
pub fn rd_array<T, F>(b: &[u8], read: F) -> ResultS<Vec<T>>
where T: Sized,
F: Fn(&[u8]) -> ResultS<(T, usize)>
@ -130,6 +200,13 @@ pub fn rd_array<T, F>(b: &[u8], read: F) -> ResultS<Vec<T>>
Ok(v)
}
/// Applies a read function over a slice with an offset table.
///
/// Applies `read` over each offset in `b`, of which there are `num` amount of
/// starting at `p`, resulting in a vector of its return values. Each iteration
/// reads a 32-bit big endian offset from `b`, and then passes a slice of `b`
/// to `read` starting at that offset. When all offsets have been read, the
/// function returns.
pub fn rd_ofstable<T, F>(b: &[u8],
mut p: usize,
num: usize,
@ -141,7 +218,7 @@ pub fn rd_ofstable<T, F>(b: &[u8],
let mut v = Vec::with_capacity(num);
for _ in 0..num {
let ofs = u32b(&b[p..]) as usize;
let ofs = u32b(&b[p..p + 4]) as usize;
if ofs >= b.len() {
bail!("not enough data");
@ -166,7 +243,7 @@ impl OptU16
}
}
/// Returns the `u16` representation of an `OptU16`.
/// Returns the `u16` representation.
pub fn get_repr(&self) -> u16
{
match self.0 {
@ -175,7 +252,7 @@ impl OptU16
}
}
/// Returns the `Option` representation of an `OptU16`.
/// Returns the `Option` representation.
pub fn get(&self) -> Option<u16>
{
match self.0 {
@ -200,7 +277,7 @@ impl fmt::Debug for OptU16
pub type Ident = [u8; 4];
/// An object identified by a `u16` which may be `u16::max_value()` to
/// represent None.
/// represent a nulled value.
#[derive(Serialize)]
pub struct OptU16(Option<NonZeroU16>);

View File

@ -1,7 +1,13 @@
//! C representation enumerations.
#![doc(hidden)]
/// Creates an enumeration and function for converting a representation into
/// the enumeration type.
///
/// The syntax is similar to the `bitflags` macro, but each value has the
/// syntax `value => enumeration`. `enum` is used instead of `struct`.
///
/// This will generate an `enum E` as well as a function `E::from_repr` which
/// will return `Result<E, ReprError>`.
#[macro_export]
macro_rules! c_enum
{
@ -20,11 +26,12 @@ macro_rules! c_enum
impl $E
{
$V fn from_repr(n: $T) -> Result<$E, ReprError>
/// Returns, if representable, the variant of `Self` from `n`.
$V fn from_repr(n: $T) -> Result<Self, ReprError>
{
match n {
$($value => Ok($E::$Enum),)+
n => Err(ReprError(n.into()))
$($value => Ok($E::$Enum),)+
n => Err(ReprError(n.into()))
}
}
}

View File

@ -18,6 +18,7 @@ fn crc_init() -> [u32; 256]
t
}
/// Creates a CRC-32 of all bytes in `b` with the starting sum `s`.
pub fn crc32(b: &[u8], s: u32) -> u32
{
let t = crc_init();

View File

@ -28,6 +28,7 @@ macro_rules! bail {
};
}
/// Returns an `Error` with a static string.
pub fn err_msg(msg: &'static str) -> Error {Error::from(ErrMsg(msg))}
impl Fail for ReprError {}
@ -62,11 +63,13 @@ impl fmt::Debug for ErrMsg
}
}
/// A representation error for an integer.
#[derive(PartialEq)]
pub struct ReprError(pub i64);
struct ErrMsg(&'static str);
/// A generic `failure` based `Result` type.
pub type ResultS<T> = Result<T, Error>;
// EOF

View File

@ -3,6 +3,7 @@
use crate::durandal::err::*;
use std::fs;
/// Confirms that the path `p` is a folder.
pub fn validate_folder_path(p: &str) -> ResultS<()>
{
let at = fs::metadata(p)?;

View File

@ -5,23 +5,55 @@ use std::{fmt::{self, Write},
ops};
macro_rules! define_fixed_type {
($Type:ident : $IT:ident, $UT:ident, $LT:ident, $FracBits:expr) => {
(
$(#[$outer:meta])*
struct $Type:ident ($IT:ident) : $UT:ident, $LT:ident, $FracBits:expr;
) => {
$(#[$outer])*
#[derive(Clone, PartialEq, Serialize)]
pub struct $Type($IT);
impl $Type
{
const FRACBITS: $UT = $FracBits;
const FRACMASK: $UT = (1 << Self::FRACBITS) - 1;
const ONE: $IT = 1 << Self::FRACBITS;
/// The number of fractional bits in this type.
pub const FRACBITS: $UT = $FracBits;
/// The integer mask for the fractional bits.
pub const FRACMASK: $UT = (1 << Self::FRACBITS) - 1;
/// The representation of `1.0` in this type.
pub const ONE: $IT = 1 << Self::FRACBITS;
pub fn to_bits(&self) -> $UT {self.0 as $UT}
pub fn set_bits(&mut self, bits: $UT) {self.0 = bits as $IT}
/// Creates a value of this type with the bit pattern `bits`.
#[inline]
pub const fn from_bits(bits: $UT) -> Self {$Type(bits as $IT)}
/// Returns the raw bit pattern.
#[inline]
pub fn to_bits(&self) -> $UT {self.0 as $UT}
/// Sets the raw bit pattern to `bits`.
#[inline]
pub fn set_bits(&mut self, bits: $UT) {self.0 = bits as $IT}
/// Returns the integral portion.
#[inline]
pub fn integ(&self) -> $LT {(self.0 >> Self::FRACBITS) as $LT}
/// Returns the fractional portion.
///
/// This is a value of `1` to `FRACMASK`.
#[allow(trivial_numeric_casts)]
pub fn fract(&self) -> u16 {(self.0 as $UT & Self::FRACMASK) as u16}
/// Returns a multiplication by integer.
///
/// Panics if the result overflows.
#[inline]
pub fn mul_i(&self, n: $LT) -> Self {$Type(self.0 * $IT::from(n))}
/// Returns a division by integer.
///
/// Panics if `n` is `0`.
#[inline]
pub fn div_i(&self, n: $LT) -> Self {$Type(self.0 / $IT::from(n))}
#[inline]
@ -39,6 +71,7 @@ macro_rules! define_fixed_type {
impl From<$LT> for $Type
{
#[inline]
fn from(n: $LT) -> Self {$Type($IT::from(n) << Self::FRACBITS)}
}
@ -46,6 +79,7 @@ macro_rules! define_fixed_type {
{
type Output = Self;
#[inline]
fn add(self, o: Self) -> Self {$Type(self.0 + o.0)}
}
@ -53,6 +87,7 @@ macro_rules! define_fixed_type {
{
type Output = Self;
#[inline]
fn sub(self, o: Self) -> Self {$Type(self.0 - o.0)}
}
@ -60,6 +95,7 @@ macro_rules! define_fixed_type {
{
type Output = Self;
#[inline]
fn mul(self, o: Self) -> Self {$Type(Self::fx_mul(self.0, o.0))}
}
@ -67,6 +103,7 @@ macro_rules! define_fixed_type {
{
type Output = Self;
#[inline]
fn div(self, o: Self) -> Self {$Type(Self::fx_div(self.0, o.0))}
}
@ -74,6 +111,7 @@ macro_rules! define_fixed_type {
{
type Output = Self;
#[inline]
fn neg(self) -> Self {$Type(-self.0)}
}
@ -81,6 +119,7 @@ macro_rules! define_fixed_type {
{
type Output = Self;
#[inline]
fn not(self) -> Self {$Type(!self.0)}
}
@ -115,9 +154,27 @@ macro_rules! define_fixed_type {
};
}
define_fixed_type!(Fixed: i32, u32, i16, 16);
define_fixed_type!(Unit: i16, u16, i8, 10);
define_fixed_type!(Angle: i16, u16, i8, 9);
define_fixed_type! {
/// A fixed point type representing an angle.
///
/// The format of this type is `0.9s`, but because of the implementation,
/// the real format is `7.9s`.
struct Angle(i16) : u16, i8, 9;
}
define_fixed_type! {
/// A fixed point type representing a world unit.
///
/// The format of this type is `5.10s`. This has caused eternal suffering.
struct Unit(i16) : u16, i8, 10;
}
define_fixed_type! {
/// A generic fixed point type.
///
/// The format of this type is `15.16s`.
struct Fixed(i32) : u32, i16, 16;
}
#[test]
fn fixed_basic_ops()

View File

@ -1,6 +1,7 @@
//! Image and color representations.
use crate::durandal::err::*;
use serde::Serialize;
use std::io;
/// Creates a RGB8 color from a R5G5B5 format color.
@ -70,14 +71,25 @@ pub fn write_tga(out: &mut impl io::Write, im: &impl Image) -> ResultS<()>
Ok(())
}
/// A generic color matrix image.
pub trait Image
{
/// The type of color this image uses for each pixel.
type Output: Color;
/// Returns the width of the image.
fn w(&self) -> usize;
/// Returns the height of the image.
fn h(&self) -> usize;
/// Returns the color of the pixel at column `x` at row `y`.
///
/// Panics if `x` is greater than the width of the image or `y` is greater
/// than the height of the image.
fn index(&self, x: usize, y: usize) -> &Self::Output;
/// The same as `index`, but will not panic if out of bounds.
fn get(&self, x: usize, y: usize) -> Option<&Self::Output>
{
if x < self.w() && y < self.h() {
@ -88,11 +100,19 @@ pub trait Image
}
}
/// Any color which may be represented as RGBA16.
pub trait Color
{
/// Returns the red component.
fn r(&self) -> u16;
/// Returns the green component.
fn g(&self) -> u16;
/// Returns the blue component.
fn b(&self) -> u16;
/// Returns the alpha component.
fn a(&self) -> u16;
}
@ -166,15 +186,16 @@ impl Color for Color8
fn a(&self) -> u16 {u16::max_value()}
}
/// A RGB16 color.
#[derive(Clone, Debug, PartialEq)]
/// An RGB16 color.
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct Color16(u16, u16, u16);
/// A RGB8 color.
#[derive(Clone, Debug, PartialEq)]
/// An RGB8 color.
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct Color8(u8, u8, u8);
/// RGB16 image.
/// An RGB16 image.
#[derive(Debug, Serialize)]
pub struct Image16
{
w: usize,
@ -182,7 +203,8 @@ pub struct Image16
pub cr: Vec<Color16>,
}
/// RGB16 image.
/// An RGB8 image.
#[derive(Debug, Serialize)]
pub struct Image8
{
w: usize,

View File

@ -1,4 +1,4 @@
//! Library for various utilities.
//! Library for utilities.
#[macro_use]
pub mod err;

View File

@ -3,6 +3,7 @@
use crate::durandal::err::*;
use std::io;
/// Writes a WAVE file from a sound.
pub fn write_wav(out: &mut impl io::Write, snd: &impl Sound) -> ResultS<()>
{
let rate = u32::from(snd.rate());
@ -32,20 +33,34 @@ pub fn write_wav(out: &mut impl io::Write, snd: &impl Sound) -> ResultS<()>
Ok(())
}
/// Any PCM stream which may be represented as a 16-bit PCM stream.
pub trait Sound
{
/// Returns the sample rate.
fn rate(&self) -> u16;
/// Returns the number of samples.
fn len(&self) -> usize;
fn index(&self, p: usize) -> i16;
/// Returns the `n`th sample.
///
/// May panic if `n` exceeds the length of this sound.
fn index(&self, n: usize) -> i16;
/// Returns the number of the first sample of the loop, or `0`.
fn lp_beg(&self) -> usize;
/// Returns the number of the last sample of the loop, or `0`.
fn lp_end(&self) -> usize;
/// Returns `true` if there are no samples in this sound.
fn is_empty(&self) -> bool {self.len() == 0}
fn get(&self, p: usize) -> Option<i16>
/// The same as `index`, but will not panic if out of bounds.
fn get(&self, n: usize) -> Option<i16>
{
if p < self.len() {
Some(self.index(p))
if n < self.len() {
Some(self.index(n))
} else {
None
}
@ -99,6 +114,7 @@ impl Sound for Sound16
fn lp_end(&self) -> usize {self.lp_end}
}
/// A 16-bit PCM stream.
pub struct Sound16
{
rate: u16,

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,8 +1,11 @@
//! Structures used by Marathon's Map format.
use crate::{durandal::{bin::*, err::*, fixed::*, text::mac_roman_conv},
marathon::xfer::TransferMode};
use bitflags::bitflags;
use serde::Serialize;
/// Reads a `Minf` chunk.
pub fn read_minf(b: &[u8]) -> ResultS<Minf>
{
read_data! {
@ -23,6 +26,7 @@ pub fn read_minf(b: &[u8]) -> ResultS<Minf>
level_name})
}
/// Reads a `LightFunc` object.
pub fn read_lightfunc(b: &[u8]) -> ResultS<LightFunc>
{
read_data! {
@ -39,6 +43,7 @@ pub fn read_lightfunc(b: &[u8]) -> ResultS<LightFunc>
Ok(LightFunc{ftype, prd_nrm, prd_dta, val_nrm, val_dta})
}
/// Reads a `SideTex` object.
pub fn read_sidetex(b: &[u8]) -> ResultS<SideTex>
{
read_data! {
@ -50,6 +55,7 @@ pub fn read_sidetex(b: &[u8]) -> ResultS<SideTex>
Ok(SideTex{offs, tex_id})
}
/// Reads a `Point` object.
pub fn read_point(b: &[u8]) -> ResultS<Point>
{
read_data! {
@ -61,6 +67,7 @@ pub fn read_point(b: &[u8]) -> ResultS<Point>
Ok(Point{x, y})
}
/// Reads an `EPNT` chunk.
pub fn read_epnt(b: &[u8]) -> ResultS<(Point, usize)>
{
read_data! {
@ -71,11 +78,13 @@ pub fn read_epnt(b: &[u8]) -> ResultS<(Point, usize)>
Ok((pnt, 16))
}
/// Reads a `PNTS` chunk.
pub fn read_pnts(b: &[u8]) -> ResultS<(Point, usize)>
{
Ok((read_point(b)?, 4))
}
/// Reads a `LINS` chunk.
pub fn read_lins(b: &[u8]) -> ResultS<(Line, usize)>
{
read_data! {
@ -94,6 +103,7 @@ pub fn read_lins(b: &[u8]) -> ResultS<(Line, usize)>
Ok((Line{flags, pnt_beg, pnt_end, side_fr, side_bk, poly_fr, poly_bk}, 32))
}
/// Reads a `SIDS` chunk.
pub fn read_sids(b: &[u8]) -> ResultS<(Side, usize)>
{
read_data! {
@ -121,6 +131,7 @@ pub fn read_sids(b: &[u8]) -> ResultS<(Side, usize)>
xfer_pri, xfer_sec, xfer_tra, shade}, 64))
}
/// Reads a `POLY` chunk.
pub fn read_poly(b: &[u8]) -> ResultS<(Polygon, usize)>
{
read_data! {
@ -153,6 +164,7 @@ pub fn read_poly(b: &[u8]) -> ResultS<(Polygon, usize)>
snd_ind, snd_amb, snd_rnd}, 128))
}
/// Reads a `LITE` chunk.
pub fn read_lite(b: &[u8]) -> ResultS<(Light, usize)>
{
read_data! {
@ -176,6 +188,7 @@ pub fn read_lite(b: &[u8]) -> ResultS<(Light, usize)>
ina_mid, tag}, 100))
}
/// Reads an `OBJS` chunk.
pub fn read_objs(b: &[u8]) -> ResultS<(Object, usize)>
{
read_data! {
@ -198,6 +211,7 @@ pub fn read_objs(b: &[u8]) -> ResultS<(Object, usize)>
Ok((Object{group, index, angle, poly, pos_x, pos_y, pos_z, flags, bias}, 16))
}
/// Reads a `plac` chunk.
pub fn read_plac(b: &[u8]) -> ResultS<(ObjectFreq, usize)>
{
read_data! {
@ -215,6 +229,7 @@ pub fn read_plac(b: &[u8]) -> ResultS<(ObjectFreq, usize)>
Ok((ObjectFreq{rnd_loc, cnt_ini, cnt_min, cnt_max, cnt_rnd, chance}, 12))
}
/// Reads an `ambi` chunk.
pub fn read_ambi(b: &[u8]) -> ResultS<(SoundAmbi, usize)>
{
read_data! {
@ -226,6 +241,7 @@ pub fn read_ambi(b: &[u8]) -> ResultS<(SoundAmbi, usize)>
Ok((SoundAmbi{index, volume}, 16))
}
/// Reads a `bonk` chunk.
pub fn read_bonk(b: &[u8]) -> ResultS<(SoundRand, usize)>
{
read_data! {
@ -248,6 +264,7 @@ pub fn read_bonk(b: &[u8]) -> ResultS<(SoundRand, usize)>
yaw_dta, pit_nrm, pit_dta}, 32))
}
/// Reads a `medi` chunk.
pub fn read_medi(b: &[u8]) -> ResultS<(Media, usize)>
{
read_data! {
@ -274,6 +291,7 @@ pub fn read_medi(b: &[u8]) -> ResultS<(Media, usize)>
min_lt, texture, xfer}, 32))
}
/// Reads a `plat` chunk.
pub fn read_plat(b: &[u8]) -> ResultS<(Platform, usize)>
{
read_data! {
@ -293,6 +311,7 @@ pub fn read_plat(b: &[u8]) -> ResultS<(Platform, usize)>
Ok((Platform{ptype, speed, delay, hei_min, hei_max, flags, index, tag}, 32))
}
/// A point in world-space.
#[derive(Clone, PartialEq, Serialize)]
pub struct Point
{
@ -300,6 +319,7 @@ pub struct Point
pub y: Unit,
}
/// A line segment.
#[derive(Debug, Serialize)]
pub struct Line
{
@ -312,6 +332,7 @@ pub struct Line
pub poly_bk: OptU16,
}
/// The texture of a side segment.
#[derive(Debug, Serialize)]
pub struct SideTex
{
@ -319,6 +340,7 @@ pub struct SideTex
pub tex_id: OptU16,
}
/// One side of a line segment.
#[derive(Debug, Serialize)]
pub struct Side
{
@ -335,6 +357,7 @@ pub struct Side
pub shade: Fixed,
}
/// A polygon segment.
#[derive(Debug, Serialize)]
pub struct Polygon
{
@ -357,6 +380,7 @@ pub struct Polygon
pub snd_rnd: OptU16,
}
/// A light function.
#[derive(Debug, Serialize)]
pub struct LightFunc
{
@ -367,6 +391,7 @@ pub struct LightFunc
pub val_dta: u16,
}
/// A dynamic polygon light.
#[derive(Debug, Serialize)]
pub struct Light
{
@ -382,6 +407,7 @@ pub struct Light
pub tag: u16,
}
/// An object in the world.
#[derive(Debug, Serialize)]
pub struct Object
{
@ -396,6 +422,7 @@ pub struct Object
pub bias: u16,
}
/// The difficulty definition for various object types.
#[derive(Debug, Serialize)]
pub struct ObjectFreq
{
@ -407,6 +434,7 @@ pub struct ObjectFreq
pub chance: u16,
}
/// An ambient sound definition.
#[derive(Debug, Serialize)]
pub struct SoundAmbi
{
@ -414,6 +442,7 @@ pub struct SoundAmbi
pub volume: u16,
}
/// A randomly played sound definition.
#[derive(Debug, Serialize)]
pub struct SoundRand
{
@ -429,6 +458,7 @@ pub struct SoundRand
pub pit_dta: Fixed,
}
/// A media, as in a part of a polygon which goes up the middle of the wall.
#[derive(Debug, Serialize)]
pub struct Media
{
@ -446,6 +476,7 @@ pub struct Media
pub xfer: TransferMode,
}
/// Extra information for polygons with platforms.
#[derive(Debug, Serialize)]
pub struct Platform
{
@ -459,6 +490,7 @@ pub struct Platform
pub tag: u16,
}
/// Static map information.
#[derive(Debug, PartialEq, Serialize)]
pub struct Minf
{
@ -472,6 +504,7 @@ pub struct Minf
}
bitflags! {
/// Flags for `Line`.
#[derive(Serialize)]
pub struct LineFlags: u16
{
@ -485,6 +518,7 @@ bitflags! {
}
bitflags! {
/// Flags for `Side`.
#[derive(Serialize)]
pub struct SideFlags: u16
{
@ -500,6 +534,7 @@ bitflags! {
}
bitflags! {
/// Static environment flags.
#[derive(Serialize)]
pub struct EnvFlags: u16
{
@ -520,6 +555,7 @@ bitflags! {
}
bitflags! {
/// Static entry point flags.
#[derive(Serialize)]
pub struct EntFlags: u32
{
@ -535,6 +571,7 @@ bitflags! {
}
bitflags! {
/// Static mission flags.
#[derive(Serialize)]
pub struct MsnFlags: u16
{
@ -547,6 +584,7 @@ bitflags! {
}
bitflags! {
/// Flags for `Polygon`.
#[derive(Serialize)]
pub struct PolyFlags: u16
{
@ -555,6 +593,7 @@ bitflags! {
}
bitflags! {
/// Flags for `Light`.
#[derive(Serialize)]
pub struct LightFlags: u16
{
@ -565,6 +604,7 @@ bitflags! {
}
bitflags! {
/// Flags for `Object`.
#[derive(Serialize)]
pub struct ObjectFlags: u16
{
@ -578,6 +618,7 @@ bitflags! {
}
bitflags! {
/// Flags for `Platform`.
#[derive(Serialize)]
pub struct PlatformFlags: u32
{
@ -612,6 +653,7 @@ bitflags! {
}
c_enum! {
/// The texture type of a `Side`.
#[derive(Debug, Serialize)]
pub enum SideType: u16
{
@ -624,6 +666,7 @@ c_enum! {
}
c_enum! {
/// The action type of a `Polygon`.
#[derive(Debug, Serialize)]
pub enum PolyType: u16
{
@ -650,6 +693,7 @@ c_enum! {
}
c_enum! {
/// The type of function for a `LightFunc`.
#[derive(Debug, Serialize)]
pub enum LightFuncType: u16
{
@ -661,6 +705,7 @@ c_enum! {
}
c_enum! {
/// The type of a `Light`.
#[derive(Debug, Serialize)]
pub enum LightType: u16
{
@ -671,6 +716,7 @@ c_enum! {
}
c_enum! {
/// The liquid type of a `Media`.
#[derive(Debug, Serialize)]
pub enum MediaType: u16
{

View File

@ -1,4 +1,4 @@
//! Library for file data formats.
//! Library for file format data readers.
pub mod machdr;
pub mod map;

View File

@ -1,7 +1,10 @@
//! Structures used by Marathon's Physics format.
use crate::{durandal::{bin::*, err::*, fixed::*}};
use bitflags::bitflags;
use serde::Serialize;
/// Reads a `PXpx` chunk.
pub fn read_pxpx(b: &[u8]) -> ResultS<(Physics, usize)>
{
read_data! {
@ -40,6 +43,7 @@ pub fn read_pxpx(b: &[u8]) -> ResultS<(Physics, usize)>
vel_bkw, vel_fwd, vel_prp, vel_rec, vel_trm}, 104))
}
/// Reads a `FXpx` chunk.
pub fn read_fxpx(b: &[u8]) -> ResultS<(Effect, usize)>
{
read_data! {
@ -57,6 +61,7 @@ pub fn read_fxpx(b: &[u8]) -> ResultS<(Effect, usize)>
Ok((Effect{collection, shape, pitch, flags, delay, delay_snd}, 14))
}
/// Reads a `WPpx` chunk.
pub fn read_wppx(b: &[u8]) -> ResultS<(Weapon, usize)>
{
read_data! {
@ -98,6 +103,7 @@ pub fn read_wppx(b: &[u8]) -> ResultS<(Weapon, usize)>
typ_powerup, typ_weapon, wid_idle}, 134))
}
/// Reads a `PRpx` chunk.
pub fn read_prpx(b: &[u8]) -> ResultS<(Projectile, usize)>
{
read_data! {
@ -128,6 +134,7 @@ pub fn read_prpx(b: &[u8]) -> ResultS<(Projectile, usize)>
flags, speed, range, snd_pitch, snd_fly, snd_bounce}, 48))
}
/// Reads a `MNpx` chunk.
pub fn read_mnpx(b: &[u8]) -> ResultS<(Monster, usize)>
{
read_data! {
@ -208,6 +215,7 @@ pub fn read_mnpx(b: &[u8]) -> ResultS<(Monster, usize)>
atk_frequency, atk_melee, atk_range}, 156))
}
/// Reads a `Trigger` object.
fn read_trigger(b: &[u8]) -> ResultS<Trigger>
{
read_data! {
@ -239,6 +247,7 @@ fn read_trigger(b: &[u8]) -> ResultS<Trigger>
tic_recover, tic_round, typ_ammo, typ_casing, typ_proj})
}
/// Reads a `Damage` object.
fn read_damage(b: &[u8]) -> ResultS<Damage>
{
read_data! {
@ -256,6 +265,7 @@ fn read_damage(b: &[u8]) -> ResultS<Damage>
Ok(Damage{dtype, alien, dmg_base, dmg_rand, scale})
}
/// Reads an `Attack` object.
fn read_attack(b: &[u8]) -> ResultS<Attack>
{
read_data! {
@ -273,6 +283,7 @@ fn read_attack(b: &[u8]) -> ResultS<Attack>
Ok(Attack{ptype, rep, error, range, shape, ofs_x, ofs_y, ofs_z})
}
/// Static physics information.
#[derive(Debug, Serialize)]
pub struct Physics
{
@ -304,6 +315,7 @@ pub struct Physics
pub vel_trm: Fixed,
}
/// An effect definition.
#[derive(Debug, Serialize)]
pub struct Effect
{
@ -315,6 +327,7 @@ pub struct Effect
pub delay_snd: OptU16,
}
/// A weapon definition.
#[derive(Debug, Serialize)]
pub struct Weapon
{
@ -345,6 +358,7 @@ pub struct Weapon
pub wid_idle: Fixed,
}
/// The definition of one of two triggers for a weapon.
#[derive(Debug, Serialize)]
pub struct Trigger
{
@ -368,6 +382,7 @@ pub struct Trigger
pub typ_proj: u16,
}
/// A projectile definition.
#[derive(Debug, Serialize)]
pub struct Projectile
{
@ -390,6 +405,7 @@ pub struct Projectile
pub snd_bounce: OptU16,
}
/// A monster definition.
#[derive(Debug, Serialize)]
pub struct Monster
{
@ -446,6 +462,7 @@ pub struct Monster
pub atk_range: Attack,
}
/// A damage definition.
#[derive(Debug, Serialize)]
pub struct Damage
{
@ -456,6 +473,7 @@ pub struct Damage
pub scale: Fixed,
}
/// The definition of a monster's attack.
#[derive(Debug, Serialize)]
pub struct Attack
{
@ -470,6 +488,7 @@ pub struct Attack
}
bitflags! {
/// Flags for an effect.
#[derive(Serialize)]
pub struct EffectFlags: u16
{
@ -482,6 +501,7 @@ bitflags! {
}
bitflags! {
/// Flags for a weapon.
#[derive(Serialize)]
pub struct WeaponFlags: u16
{
@ -500,6 +520,7 @@ bitflags! {
}
bitflags! {
/// Flags for a projectile.
#[derive(Serialize)]
pub struct ProjectileFlags: u32
{
@ -528,6 +549,7 @@ bitflags! {
}
bitflags! {
/// Flags for a monster.
#[derive(Serialize)]
pub struct MonsterFlags: u32
{
@ -563,6 +585,7 @@ bitflags! {
}
bitflags! {
/// The composite type of a monster.
#[derive(Serialize)]
pub struct MonsterClass: u32
{
@ -586,6 +609,7 @@ bitflags! {
}
bitflags! {
/// The composite type of damage taken by something.
#[derive(Serialize)]
pub struct DamageTypeFlags: u32
{
@ -617,6 +641,7 @@ bitflags! {
}
c_enum! {
/// A bullet shell casing emitted by a weapon.
#[derive(Debug, Serialize)]
pub enum CasingType: u16
{
@ -630,6 +655,7 @@ c_enum! {
}
c_enum! {
/// The type of functionality a weapon provides.
#[derive(Debug, Serialize)]
pub enum WeaponType: u16
{
@ -642,6 +668,7 @@ c_enum! {
}
c_enum! {
/// A named type of damage taken by something.
#[derive(Debug, Serialize)]
pub enum DamageType: u16
{

View File

@ -5,7 +5,8 @@ use crate::{durandal::{bin::*, err::*, fixed::*, image::*, text::*},
use bitflags::bitflags;
use serde::Serialize;
fn color(b: &[u8], clut: &mut [ColorShp]) -> ResultS<()>
/// Reads a color from a color table into `clut`.
fn read_color(b: &[u8], clut: &mut [ColorShp]) -> ResultS<()>
{
read_data! {
8, BE in b =>
@ -27,11 +28,12 @@ fn color(b: &[u8], clut: &mut [ColorShp]) -> ResultS<()>
Ok(())
}
fn color_tables(b: &[u8],
tab_ofs: usize,
tab_num: usize,
clr_num: usize)
-> ResultS<Vec<Vec<ColorShp>>>
/// Reads all color tables.
pub 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;
@ -42,7 +44,7 @@ fn color_tables(b: &[u8],
for clut in v.iter_mut().take(tab_num) {
for _ in 0..clr_num {
color(&b[p..p + 8], clut)?;
read_color(&b[p..p + 8], clut)?;
p += 8;
}
}
@ -50,7 +52,8 @@ fn color_tables(b: &[u8],
Ok(v)
}
fn bitmap(b: &[u8]) -> ResultS<Bitmap>
/// Reads a `Bitmap`.
pub fn read_bitmap(b: &[u8]) -> ResultS<Bitmap>
{
read_data! {
26, BE in b =>
@ -116,7 +119,8 @@ fn bitmap(b: &[u8]) -> ResultS<Bitmap>
Ok(bmp)
}
fn frame(b: &[u8]) -> ResultS<Frame>
/// Reads a `Frame`.
pub fn read_frame(b: &[u8]) -> ResultS<Frame>
{
read_data! {
36, BE in b =>
@ -136,7 +140,8 @@ fn frame(b: &[u8]) -> ResultS<Frame>
Ok(Frame{flags, min_lt, bmp_ind, wrl_l, wrl_r, wrl_t, wrl_b, wrl_x, wrl_y})
}
fn sequence(b: &[u8]) -> ResultS<Sequence>
/// Reads a `Sequence`.
pub fn read_sequence(b: &[u8]) -> ResultS<Sequence>
{
read_data! {
88, BE in b =>
@ -161,7 +166,8 @@ fn sequence(b: &[u8]) -> ResultS<Sequence>
snd_key, snd_end, loop_f})
}
fn collection(b: &[u8]) -> ResultS<Collection>
/// Reads a `Collection`.
pub fn read_collection(b: &[u8]) -> ResultS<Collection>
{
read_data! {
544, BE in b =>
@ -185,13 +191,14 @@ fn collection(b: &[u8]) -> ResultS<Collection>
}
let tabs = color_tables(b, tab_ofs, tab_num, clr_num)?;
let bmps = rd_ofstable(b, bmp_ofs, bmp_num, bitmap)?;
let frms = rd_ofstable(b, frm_ofs, frm_num, frame)?;
let seqs = rd_ofstable(b, seq_ofs, seq_num, sequence)?;
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.
pub fn read_shapes(b: &[u8]) -> ResultS<Vec<CollectionDef>>
{
let mut cl = Vec::with_capacity(32);
@ -209,13 +216,13 @@ pub fn read_shapes(b: &[u8]) -> ResultS<Vec<CollectionDef>>
let c_lo = if lo_ofs == u32::max_value() as usize {
None
} else {
Some(collection(&b[lo_ofs..lo_ofs + lo_len])?)
Some(read_collection(&b[lo_ofs..lo_ofs + lo_len])?)
};
let c_hi = if hi_ofs == u32::max_value() as usize {
None
} else {
Some(collection(&b[hi_ofs..hi_ofs + hi_len])?)
Some(read_collection(&b[hi_ofs..hi_ofs + hi_len])?)
};
cl.push((c_lo, c_hi));
@ -228,7 +235,8 @@ pub fn read_shapes(b: &[u8]) -> ResultS<Vec<CollectionDef>>
impl Bitmap
{
fn new(w: usize, h: usize, alpha: bool, cmajr: bool) -> Self
/// 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)}
}
@ -236,6 +244,7 @@ impl Bitmap
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}
@ -302,6 +311,7 @@ impl Color for ColorShp
}
}
/// A color in an `ImageShp`'s color table.
#[derive(Clone, Debug, Serialize)]
pub enum ColorShp
{
@ -315,6 +325,7 @@ pub enum ColorShp
},
}
/// An unpacked Shape bitmap.
#[derive(Debug)]
pub struct Bitmap
{
@ -325,12 +336,15 @@ pub struct Bitmap
cmajr: bool,
}
/// An image from a Shape. This mainly just exists so that `Bitmap` can use the
/// `Image` trait.
pub struct ImageShp<'a, 'b>
{
bmp: &'a Bitmap,
clut: &'b [ColorShp],
}
/// A frame, also known as a low level shape.
#[derive(Debug, Serialize)]
pub struct Frame
{
@ -345,6 +359,7 @@ pub struct Frame
wrl_y: Unit,
}
/// A sequence, also known as a high level shape.
#[derive(Debug, Serialize)]
pub struct Sequence
{
@ -361,6 +376,7 @@ pub struct Sequence
loop_f: u16,
}
/// A collection of color tables, bitmaps, frames and sequences.
#[derive(Debug)]
pub struct Collection
{
@ -371,6 +387,7 @@ pub struct Collection
pub seqs: Vec<Sequence>,
}
/// A collection, which may have low- and high-definition variations, or none.
pub type CollectionDef = (Option<Collection>, Option<Collection>);
bitflags! {
@ -382,6 +399,7 @@ bitflags! {
}
bitflags! {
/// Flags for `Frame`.
#[derive(Serialize)]
pub struct FrameFlags: u16
{
@ -392,6 +410,7 @@ bitflags! {
}
c_enum! {
/// The type of a collection.
#[derive(Debug, Serialize)]
pub enum CollectionType: u16
{
@ -404,6 +423,7 @@ c_enum! {
}
c_enum! {
/// The type of or number of views for a sequence.
#[derive(Debug, Serialize)]
pub enum ViewType: u16
{

View File

@ -3,9 +3,10 @@
use crate::durandal::{bin::*, err::*, fixed::*, sound::*};
use bitflags::bitflags;
use serde::Serialize;
use std::collections::HashMap;
use std::collections::BTreeMap;
fn sound(b: &[u8]) -> ResultS<Sound16>
/// Reads a sound.
pub fn read_sound(b: &[u8]) -> ResultS<Sound16>
{
read_data! {
21, BE in b =>
@ -44,7 +45,8 @@ fn sound(b: &[u8]) -> ResultS<Sound16>
}
}
fn sound_def(b: &[u8]) -> ResultS<Option<(Vec<usize>, u16, SoundDef)>>
/// Reads a sound definition.
pub fn read_sound_def(b: &[u8]) -> ResultS<Option<(Vec<usize>, u16, SoundDef)>>
{
read_data! {
64, BE in b =>
@ -83,12 +85,13 @@ fn sound_def(b: &[u8]) -> ResultS<Option<(Vec<usize>, u16, SoundDef)>>
Ok(Some((ofs, index, SoundDef{header, sounds})))
}
/// Reads all sounds from a Sound file.
pub fn read_sounds(b: &[u8]) -> ResultS<Vec<SoundTable>>
{
read_data! {
260, BE in b =>
version = u32[0];
magic = iden[4];
magic = Ident[4];
src_num = u16[8] as usize;
snd_num = u16[10] as usize;
}
@ -101,12 +104,12 @@ pub fn read_sounds(b: &[u8]) -> ResultS<Vec<SoundTable>>
let mut p = 260;
for _ in 0..src_num {
let mut st = HashMap::with_capacity(snd_num);
let mut st = BTreeMap::new();
for _ in 0..snd_num {
if let Some((ofs, idx, mut def)) = sound_def(&b[p..p + 64])? {
if let Some((ofs, idx, mut def)) = read_sound_def(&b[p..p + 64])? {
for &ofs in &ofs {
def.sounds.push(sound(&b[ofs..])?);
def.sounds.push(read_sound(&b[ofs..])?);
}
st.insert(idx, def);
@ -121,6 +124,7 @@ pub fn read_sounds(b: &[u8]) -> ResultS<Vec<SoundTable>>
Ok(sc)
}
/// The header of a sound definition.
#[derive(Debug, Serialize)]
pub struct SoundHead
{
@ -131,15 +135,18 @@ pub struct SoundHead
pub pitch_hi: Fixed,
}
/// A sound definition containing one, many or no sounds.
pub struct SoundDef
{
pub header: SoundHead,
pub sounds: Vec<Sound16>,
}
pub type SoundTable = HashMap<u16, SoundDef>;
/// A table of sound definitions.
pub type SoundTable = BTreeMap<u16, SoundDef>;
bitflags! {
/// Flags for `SoundHead`.
#[derive(Serialize)]
pub struct SoundFlags: u16
{
@ -154,6 +161,7 @@ bitflags! {
}
c_enum! {
/// The type of volume this sound has.
#[derive(Debug, Serialize)]
pub enum Volume: u16
{

View File

@ -1,9 +1,12 @@
//! Structures used by Marathon's Map format's terminal definitions.
use crate::durandal::{err::*, text::*};
use bitflags::bitflags;
use serde::Serialize;
use std::fmt;
fn read_group(b: &[u8], text: &[u8]) -> ResultS<Group>
/// Reads a `Group`.
pub fn read_group(b: &[u8], text: &[u8]) -> ResultS<Group>
{
read_data! {
12, BE in b =>
@ -22,7 +25,8 @@ fn read_group(b: &[u8], text: &[u8]) -> ResultS<Group>
Ok(Group{flags, ttype, pdata, lines, text})
}
fn read_face(b: &[u8]) -> ResultS<Face>
/// Reads a `Face`.
pub fn read_face(b: &[u8]) -> ResultS<Face>
{
read_data! {
6, BE in b =>
@ -34,6 +38,7 @@ fn read_face(b: &[u8]) -> ResultS<Face>
Ok(Face{start, face, color})
}
/// Reads a `term` chunk.
pub fn read_term(b: &[u8]) -> ResultS<(Terminal, usize)>
{
const SIZE_GROUP: usize = 12;
@ -76,6 +81,7 @@ pub fn read_term(b: &[u8]) -> ResultS<(Terminal, usize)>
Ok((Terminal{lines, groups, faces}, end))
}
/// A terminal definition, with collections of groups and faces.
#[derive(Debug, Serialize)]
pub struct Terminal
{
@ -84,6 +90,7 @@ pub struct Terminal
faces: Vec<Face>,
}
/// A text face.
#[derive(Debug, Serialize)]
pub struct Face
{
@ -92,6 +99,7 @@ pub struct Face
color: u16,
}
/// A terminal command grouping.
#[derive(Serialize)]
pub struct Group
{
@ -103,6 +111,7 @@ pub struct Group
}
bitflags! {
/// Flags for `Group`.
#[derive(Serialize)]
pub struct GroupFlags: u16
{
@ -112,6 +121,7 @@ bitflags! {
}
c_enum! {
/// The command of a `Group`.
#[derive(Debug, Serialize)]
pub enum GroupType: u16
{

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

View File

@ -1,7 +1,10 @@
//! Transfer Mode type.
use crate::durandal::err::*;
use serde::Serialize;
c_enum! {
/// A rendering style for many things.
#[derive(Debug, Serialize)]
pub enum TransferMode: u16
{

View File

@ -21,7 +21,7 @@ gio-sys = "0.8"
glib = "0.7"
glib-sys = "0.8"
gobject-sys = "0.8"
gtk = "0.6"
gtk = {version = "0.6", features = ["v3_16"]}
gtk-sys = "0.8"
pango = "0.6"
pango-sys = "0.8"

48
src/tycho/buttons.rs Normal file
View File

@ -0,0 +1,48 @@
fn mk_btn_new(b: &gtk::Builder)
{
let btn: gtk::MenuItem = get_obj(b, "btn-new");
let tools: gtk::Window = get_obj(b, "win-map-tools");
let view: gtk::Window = get_obj(b, "win-map-view");
btn.connect_activate(move |_| {
// TODO: actually make a new document
tools.show_all();
view.show_all();
});
}
fn mk_btn_quit(b: &gtk::Builder, app: gtk::Application)
{
let btn: gtk::MenuItem = get_obj(b, "btn-quit");
btn.connect_activate(move |_| app.quit());
}
fn mk_btn_about(b: &gtk::Builder)
{
let btn: gtk::MenuItem = get_obj(b, "btn-about");
let win: gtk::AboutDialog = get_obj(b, "win-about");
btn.connect_activate(move |_| {
win.run();
win.hide();
});
}
fn mk_btn_show_map_view(b: &gtk::Builder)
{
let btn: gtk::MenuItem = get_obj(b, "btn-show-map-view");
let win: gtk::Window = get_obj(b, "win-map-view");
btn.connect_activate(move |_| win.show_all());
}
fn mk_btn_show_map_tools(b: &gtk::Builder)
{
let btn: gtk::MenuItem = get_obj(b, "btn-show-map-tools");
let win: gtk::Window = get_obj(b, "win-map-tools");
btn.connect_activate(move |_| win.show_all());
}
// EOF

Binary file not shown.

View File

@ -27,33 +27,75 @@ Author: Alison Sanderson
-->
<interface>
<requires lib="gtk+" version="3.10"/>
<requires lib="gtk+" version="3.16"/>
<!-- interface-license-type mit -->
<!-- interface-name Maraiah Tycho -->
<!-- interface-description Tycho map editor for Maraiah. -->
<!-- interface-copyright 2018-2019 Alison Sanderson -->
<!-- interface-authors Alison Sanderson -->
<object class="GtkImage" id="im_lines">
<object class="GtkAdjustment" id="adj-map-horz">
<property name="upper">100</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adj-map-vert">
<property name="upper">100</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkWindow" id="win-map-view">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Map View</property>
<property name="default_width">600</property>
<property name="default_height">400</property>
<child type="titlebar">
<placeholder/>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">always</property>
<property name="vscrollbar_policy">always</property>
<property name="window_placement">top-right</property>
<property name="overlay_scrolling">False</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hadjustment">adj-map-horz</property>
<property name="vadjustment">adj-map-vert</property>
<child>
<object class="GtkDrawingArea" id="draw-area">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<object class="GtkImage" id="img-lines">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="resource">/net/greyserv/maraiah/tycho/lines</property>
</object>
<object class="GtkImage" id="im_points">
<object class="GtkImage" id="img-points">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="resource">/net/greyserv/maraiah/tycho/points</property>
</object>
<object class="GtkImage" id="im_polys">
<object class="GtkImage" id="img-polys">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="resource">/net/greyserv/maraiah/tycho/polys</property>
</object>
<object class="GtkWindow" id="win_tools">
<object class="GtkWindow" id="win-map-tools">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Tool Palette</property>
<property name="default_height">250</property>
<property name="skip_taskbar_hint">True</property>
<property name="has_resize_grip">True</property>
<child>
<placeholder/>
</child>
@ -68,14 +110,14 @@ Author: Alison Sanderson
<property name="can_focus">False</property>
<property name="label" translatable="yes">Geometry</property>
<child>
<object class="GtkToolButton" id="btn_point">
<object class="GtkToolButton" id="btn-point">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Points</property>
<property name="use_underline">True</property>
<property name="icon_widget">im_points</property>
<property name="icon_widget">img-points</property>
<child internal-child="accessible">
<object class="AtkObject" id="btn_point-atkobject">
<object class="AtkObject" id="btn-point-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Points Tool</property>
<property name="AtkObject::accessible-description" translatable="yes">The tool that modifies points in the map.</property>
<property name="AtkObject::accessible-role" translatable="yes">push-button</property>
@ -87,14 +129,14 @@ Author: Alison Sanderson
</packing>
</child>
<child>
<object class="GtkToolButton" id="btn_lines">
<object class="GtkToolButton" id="btn-lines">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Lines</property>
<property name="use_underline">True</property>
<property name="icon_widget">im_lines</property>
<property name="icon_widget">img-lines</property>
<child internal-child="accessible">
<object class="AtkObject" id="btn_lines-atkobject">
<object class="AtkObject" id="btn-lines-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Lines Tool</property>
<property name="AtkObject::accessible-description" translatable="yes">The tool which modifies line segments on the map.</property>
<property name="AtkObject::accessible-role" translatable="yes">push-button</property>
@ -107,14 +149,14 @@ Author: Alison Sanderson
</packing>
</child>
<child>
<object class="GtkToolButton" id="btn_polys">
<object class="GtkToolButton" id="btn-polys">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Polygons</property>
<property name="use_underline">True</property>
<property name="icon_widget">im_polys</property>
<property name="icon_widget">img-polys</property>
<child internal-child="accessible">
<object class="AtkObject" id="btn_polys-atkobject">
<object class="AtkObject" id="btn-polys-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Polygon Tool</property>
<property name="AtkObject::accessible-description" translatable="yes">The tool which modifies polygon shapes on the map.</property>
<property name="AtkObject::accessible-role" translatable="yes">push-button</property>
@ -141,17 +183,16 @@ Author: Alison Sanderson
</object>
</child>
<child internal-child="accessible">
<object class="AtkObject" id="win_tools-atkobject">
<object class="AtkObject" id="win-map-tools-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Tycho Tool Palette</property>
<property name="AtkObject::accessible-description" translatable="yes">Tycho's tool palette window.</property>
<property name="AtkObject::accessible-role" translatable="yes">window</property>
</object>
</child>
</object>
<object class="GtkAboutDialog" id="win_about">
<object class="GtkAboutDialog" id="win-about">
<property name="can_focus">False</property>
<property name="modal">True</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
<property name="program_name">Tycho Map Editor</property>
<property name="copyright" translatable="yes">Copyright © 2018-2019 Alison Sanderson</property>
@ -184,18 +225,19 @@ Author: Alison Sanderson
</object>
</child>
<child internal-child="accessible">
<object class="AtkObject" id="win_about-atkobject">
<object class="AtkObject" id="win-about-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">About Screen</property>
<property name="AtkObject::accessible-description" translatable="yes">The about screen for Tycho.</property>
<property name="AtkObject::accessible-role" translatable="yes">dialog</property>
</object>
</child>
</object>
<object class="GtkWindow" id="win_menus">
<object class="GtkWindow" id="win-main">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Tycho</property>
<property name="resizable">False</property>
<property name="default_width">120</property>
<property name="default_height">250</property>
<property name="has_resize_grip">True</property>
<child>
<placeholder/>
</child>
@ -215,39 +257,30 @@ Author: Alison Sanderson
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-new</property>
<object class="GtkMenuItem" id="btn-new">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Create a new project.</property>
<property name="label" translatable="yes">_New Project</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-open</property>
<object class="GtkMenuItem" id="btn-open">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Opens any type of project file.</property>
<property name="label" translatable="yes">_Open</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-save</property>
<object class="GtkMenuItem" id="btn-save">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Saves the currently open project.</property>
<property name="label" translatable="yes">_Save</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-save-as</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
@ -257,62 +290,12 @@ Author: Alison Sanderson
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-quit</property>
<object class="GtkMenuItem" id="btn-quit">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Quit the application.</property>
<property name="label" translatable="yes">_Quit</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-cut</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-copy</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-paste</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-delete</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
</object>
@ -325,6 +308,30 @@ Author: Alison Sanderson
<property name="can_focus">False</property>
<property name="label" translatable="yes">_View</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="btn-show-map-view">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Opens the map view window.</property>
<property name="label" translatable="yes">Show _Map View</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="btn-show-map-tools">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Opens the map toolbox window.</property>
<property name="label" translatable="yes">Show Map _Tools</property>
<property name="use_underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
@ -345,12 +352,12 @@ Author: Alison Sanderson
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem" id="btn_about">
<property name="label">gtk-about</property>
<object class="GtkMenuItem" id="btn-about">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Opens a window with information about this program.</property>
<property name="label" translatable="yes">_About</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
</object>

View File

@ -1,53 +1,38 @@
use gtk::prelude::*;
use gdk::prelude::*;
use gio::prelude::*;
use gtk::prelude::*;
use maraiah::durandal::err::*;
fn run_app(app: &gtk::Application) -> ResultS<()>
include!("windows.rs");
include!("buttons.rs");
include!("map_draw.rs");
fn run_app(app: &gtk::Application)
{
fn load_img(path: &'static str) -> ResultS<gdk_pixbuf::Pixbuf>
{
Ok(gdk_pixbuf::Pixbuf::new_from_resource(path)?)
}
fn get_obj<T>(b: &gtk::Builder, name: &str) -> ResultS<T>
where T: glib::object::IsA<glib::object::Object>
{
match b.get_object(name) {
Some(w) => Ok(w),
None => Err(err_msg("no object")),
}
}
// one fallible call, which should never fail anyhow
let b = gtk::Builder::new_from_resource("/net/greyserv/maraiah/tycho/ui");
let win_menus: gtk::Window = get_obj(&b, "win_menus")?;
let win_tools: gtk::Window = get_obj(&b, "win_tools")?;
let win_about: gtk::AboutDialog = get_obj(&b, "win_about")?;
let btn_about: gtk::MenuItem = get_obj(&b, "btn_about")?;
//t draw_area: gtk::DrawingArea = get_obj(&b, "draw_area")?;
//t btn_point: gtk::ToolButton = get_obj(&b, "btn_point")?;
//t btn_lines: gtk::ToolButton = get_obj(&b, "btn_lines")?;
//t btn_polys: gtk::ToolButton = get_obj(&b, "btn_polys")?;
mk_btn_new(&b);
mk_btn_quit(&b, app.clone());
mk_btn_about(&b);
mk_btn_show_map_view(&b);
mk_btn_show_map_tools(&b);
mk_draw_area(&b);
mk_win_map_tools(&b);
mk_win_map_view(&b);
mk_win_about(&b);
mk_win_main(&b, app);
}
let authors: Vec<_> = env!("CARGO_PKG_AUTHORS").split(';').collect();
fn load_img(path: &'static str) -> gdk_pixbuf::Pixbuf
{
gdk_pixbuf::Pixbuf::new_from_resource(path).unwrap()
}
win_about.set_authors(&authors);
win_about.set_version(env!("CARGO_PKG_VERSION"));
win_about.set_website(env!("CARGO_PKG_HOMEPAGE"));
win_about.set_logo(&load_img("/net/greyserv/maraiah/tycho/tycho2")?);
btn_about.connect_activate(move |_| {
win_about.run();
win_about.hide();
});
win_tools.set_deletable(false);
win_tools.show_all();
win_menus.set_application(app);
win_menus.show_all();
Ok(())
fn get_obj<T>(b: &gtk::Builder, name: &str) -> T
where T: glib::object::IsA<glib::object::Object>
{
b.get_object(name).unwrap()
}
fn main() -> ResultS<()>
@ -69,21 +54,7 @@ fn main() -> ResultS<()>
let app = gtk::Application::new("net.greyserv.maraiah.tycho",
gio::ApplicationFlags::empty())?;
app.connect_activate(|app| {
match run_app(app) {
Ok(()) => (),
Err(e) => {
// print out an error if init failed somehow, otherwise the main
// loop will proceed as normal (this is just to prevent panics in
// weird circumstances such as breaking the builder while devving)
gtk::MessageDialog::new(None::<&gtk::Window>,
gtk::DialogFlags::empty(),
gtk::MessageType::Error,
gtk::ButtonsType::Ok,
&format!("{:?}", e)).run();
}
}
});
app.connect_activate(run_app);
let ret = if app.run(&[]) == 0 {
Ok(())

71
src/tycho/map_draw.rs Normal file
View File

@ -0,0 +1,71 @@
fn draw_clear(cr: &cairo::Context, w: f64, h: f64)
{
use cairo::{FontSlant, FontWeight};
// set up for text
cr.select_font_face("Sans", FontSlant::Normal, FontWeight::Normal);
cr.set_font_size(14.0);
// clear view
cr.set_source_rgb(0.0, 0.0, 0.0);
cr.rectangle(0.0, 0.0, w, h);
cr.fill();
}
fn draw_map_none(cr: &cairo::Context, im: &gdk_pixbuf::Pixbuf, w: f64, h: f64)
{
let im_w = f64::from(im.get_width());
let im_h = f64::from(im.get_height());
// draw middle image
cr.set_source_pixbuf(im, w / 2.0 - im_w / 2.0, h / 2.0 - im_h / 2.0);
cr.paint();
// draw top border (these are separate so the bottom draws over the top)
cr.set_source_rgb(0.28, 0.0, 0.0);
cr.rectangle(0.0, 0.0, w, 18.0);
cr.fill();
// draw top text
cr.set_source_rgb(1.0, 0.0, 0.0);
cr.move_to(4.0, 14.0);
cr.show_text("Map Required To Proceed");
// draw bottom border
cr.set_source_rgb(0.28, 0.0, 0.0);
cr.rectangle(0.0, h - 18.0, w, h);
cr.fill();
// draw bottom text
cr.set_source_rgb(1.0, 0.0, 0.0);
cr.move_to(4.0, h - 4.0);
cr.show_text("CAS.qterm//CyberAcme Systems Inc.");
}
fn mk_draw_area(b: &gtk::Builder)
{
let area: gtk::DrawingArea = get_obj(b, "draw-area");
let ax: gtk::Adjustment = get_obj(b, "adj-map-horz");
let ay: gtk::Adjustment = get_obj(b, "adj-map-vert");
let im = load_img("/net/greyserv/maraiah/tycho/tycho1");
area.connect_draw(move |area, cr| {
let w = f64::from(area.get_allocated_width());
let h = f64::from(area.get_allocated_height());
ax.set_lower(0.0);
ax.set_upper(w);
ay.set_lower(0.0);
ay.set_upper(h);
draw_clear(&cr, w, h);
draw_map_none(&cr, &im, w, h);
Inhibit(true)
});
}
// EOF

39
src/tycho/windows.rs Normal file
View File

@ -0,0 +1,39 @@
fn hide_on_delete(win: &gtk::Window, _: &gdk::Event) -> Inhibit
{
win.hide();
Inhibit(true)
}
fn mk_win_map_tools(b: &gtk::Builder)
{
let win: gtk::Window = get_obj(b, "win-map-tools");
win.connect_delete_event(hide_on_delete);
}
fn mk_win_map_view(b: &gtk::Builder)
{
let win: gtk::Window = get_obj(b, "win-map-view");
win.connect_delete_event(hide_on_delete);
}
fn mk_win_about(b: &gtk::Builder)
{
let win: gtk::AboutDialog = get_obj(b, "win-about");
win.set_authors(&env!("CARGO_PKG_AUTHORS").split(';').collect::<Vec<_>>());
win.set_version(env!("CARGO_PKG_VERSION"));
win.set_website(env!("CARGO_PKG_HOMEPAGE"));
win.set_logo(&load_img("/net/greyserv/maraiah/tycho/tycho2"));
}
fn mk_win_main(b: &gtk::Builder, app: &gtk::Application)
{
let win: gtk::Window = get_obj(b, "win-main");
win.set_application(app);
win.show_all();
}
// EOF