Compare commits
3 Commits
87bfe2869a
...
35452cd908
Author | SHA1 | Date |
---|---|---|
an | 35452cd908 | |
an | fc27d2d0f4 | |
an | 789e3de93f |
|
@ -1,6 +1,6 @@
|
|||
/target
|
||||
/out
|
||||
data/*.res
|
||||
*.res
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
perf.data*
|
||||
|
|
|
@ -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>);
|
||||
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Library for various utilities.
|
||||
//! Library for utilities.
|
||||
|
||||
#[macro_use]
|
||||
pub mod err;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
use maraiah::{durandal::{bin::*, err::*, file::*, image::*, sound::*, text::*},
|
||||
marathon::{machdr, map, phy, pict, shp, snd, trm, wad}};
|
||||
use std::{collections::HashSet,
|
||||
fs,
|
||||
use maraiah::{durandal::{err::*, file::*, image::*, sound::*, text::*},
|
||||
marathon::{machdr, shp, snd, wad}};
|
||||
use std::{fs,
|
||||
io::{self, Write}};
|
||||
|
||||
fn make_tga(fname: &str, im: &impl Image) -> ResultS<()>
|
||||
fn make_tga(_opt: &Options, fname: &str, im: &impl Image) -> ResultS<()>
|
||||
{
|
||||
let mut out = io::BufWriter::new(fs::File::create(fname)?);
|
||||
write_tga(&mut out, im)
|
||||
}
|
||||
|
||||
fn make_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
||||
fn make_data(_opt: &Options, fname: &str, data: &[u8]) -> ResultS<()>
|
||||
{
|
||||
let cid = mac_roman_conv(&cid);
|
||||
let fname = format!("{}/{:04}{}.bin", opt.out_dir, eid, cid);
|
||||
let mut out = io::BufWriter::new(fs::File::create(&fname)?);
|
||||
out.write_all(cnk)?;
|
||||
let mut out = io::BufWriter::new(fs::File::create(fname)?);
|
||||
out.write_all(data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -37,42 +34,23 @@ fn make_wav(fname: &str, snd: &impl Sound) -> ResultS<()>
|
|||
write_wav(&mut out, snd)
|
||||
}
|
||||
|
||||
fn dump_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
||||
fn dump_chunk(opt: &Options, cnk: &wad::Chunk, eid: u16) -> ResultS<()>
|
||||
{
|
||||
if opt.wad_all {
|
||||
make_chunk(opt, cid, cnk, eid)?;
|
||||
}
|
||||
|
||||
if opt.wad_wrt_all || opt.wad_chunks.contains(&cid) {
|
||||
match &cid {
|
||||
b"PICT" => {
|
||||
let im = pict::load_pict(cnk)?;
|
||||
make_tga(&format!("{}/pict_{}.tga", opt.out_dir, eid), &im)?;
|
||||
if opt.wad_wrt_all {
|
||||
match cnk {
|
||||
wad::Chunk::Pict(im) => {
|
||||
let fname = format!("{}/pict_{}.tga", opt.out_dir, eid);
|
||||
make_tga(opt, &fname, im)?;
|
||||
}
|
||||
b"Minf" => make_yaml(opt, &map::read_minf(cnk)?)?,
|
||||
b"EPNT" => make_yaml(opt, &rd_array(cnk, map::read_epnt)?)?,
|
||||
b"PNTS" => make_yaml(opt, &rd_array(cnk, map::read_pnts)?)?,
|
||||
b"LINS" => make_yaml(opt, &rd_array(cnk, map::read_lins)?)?,
|
||||
b"SIDS" => make_yaml(opt, &rd_array(cnk, map::read_sids)?)?,
|
||||
b"POLY" => make_yaml(opt, &rd_array(cnk, map::read_poly)?)?,
|
||||
b"LITE" => make_yaml(opt, &rd_array(cnk, map::read_lite)?)?,
|
||||
b"OBJS" => make_yaml(opt, &rd_array(cnk, map::read_objs)?)?,
|
||||
b"plac" => make_yaml(opt, &rd_array(cnk, map::read_plac)?)?,
|
||||
b"ambi" => make_yaml(opt, &rd_array(cnk, map::read_ambi)?)?,
|
||||
b"bonk" => make_yaml(opt, &rd_array(cnk, map::read_bonk)?)?,
|
||||
b"medi" => make_yaml(opt, &rd_array(cnk, map::read_medi)?)?,
|
||||
b"plat" => make_yaml(opt, &rd_array(cnk, map::read_plat)?)?,
|
||||
b"term" => make_yaml(opt, &rd_array(cnk, trm::read_term)?)?,
|
||||
b"FXpx" => make_yaml(opt, &rd_array(cnk, phy::read_fxpx)?)?,
|
||||
b"MNpx" => make_yaml(opt, &rd_array(cnk, phy::read_mnpx)?)?,
|
||||
b"PRpx" => make_yaml(opt, &rd_array(cnk, phy::read_prpx)?)?,
|
||||
b"PXpx" => make_yaml(opt, &rd_array(cnk, phy::read_pxpx)?)?,
|
||||
b"WPpx" => make_yaml(opt, &rd_array(cnk, phy::read_wppx)?)?,
|
||||
_ => {
|
||||
if opt.wad_unknown && !opt.wad_all {
|
||||
make_chunk(opt, cid, cnk, eid)?;
|
||||
wad::Chunk::Data{iden, data} => {
|
||||
if opt.wad_unknown {
|
||||
let iden = mac_roman_conv(iden);
|
||||
let fname = format!("{}/{:04}{}.bin", opt.out_dir, eid, iden);
|
||||
make_data(opt, &fname, data)?;
|
||||
}
|
||||
make_yaml(opt, cnk)?;
|
||||
}
|
||||
_ => make_yaml(opt, cnk)?,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,15 +59,15 @@ fn dump_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
|||
|
||||
fn process_wad(opt: &Options, b: &[u8]) -> ResultS<()>
|
||||
{
|
||||
let wad = wad::Wad::new(b)?;
|
||||
let wad = wad::read_wad(b)?;
|
||||
|
||||
if opt.wad_header {
|
||||
make_yaml(opt, &wad.head)?;
|
||||
}
|
||||
|
||||
for (eid, ent) in wad.entries {
|
||||
for (cid, cnk) in ent.chunks {
|
||||
dump_chunk(opt, cid, cnk, eid)?;
|
||||
for cnk in ent.chunks {
|
||||
dump_chunk(opt, &cnk, eid)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,11 +84,11 @@ fn dump_bitmaps(opt: &Options, c: &shp::Collection, i: usize) -> ResultS<()>
|
|||
if opt.shp_bmp_all {
|
||||
for (k, tab) in c.tabs.iter().enumerate() {
|
||||
let fname = format!("{}/shape{}_{}_{}.tga", opt.out_dir, i, j, k);
|
||||
make_tga(&fname, &shp::ImageShp::new(bmp, &tab))?;
|
||||
make_tga(opt, &fname, &shp::ImageShp::new(bmp, &tab))?;
|
||||
}
|
||||
} else {
|
||||
let fname = format!("{}/shape{}_{}.tga", opt.out_dir, i, j);
|
||||
make_tga(&fname, &shp::ImageShp::new(bmp, &c.tabs[0]))?;
|
||||
make_tga(opt, &fname, &shp::ImageShp::new(bmp, &c.tabs[0]))?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,11 +223,6 @@ fn main() -> ResultS<()>
|
|||
StoreTrue,
|
||||
"snd: Dump all sounds to WAVE files");
|
||||
|
||||
arg!("--wad-dump-all",
|
||||
opt.wad_all,
|
||||
StoreTrue,
|
||||
"wad: Dump all chunks into a folder");
|
||||
|
||||
arg!("--wad-write-all",
|
||||
opt.wad_wrt_all,
|
||||
StoreTrue,
|
||||
|
@ -265,11 +238,6 @@ fn main() -> ResultS<()>
|
|||
StoreTrue,
|
||||
"wad: Dump header info as YAML to standard output");
|
||||
|
||||
arg!("--wad-write-chunks",
|
||||
opt.wad_c_temp,
|
||||
Store,
|
||||
"wad: Dump specified chunks in various formats");
|
||||
|
||||
arg!("--out-dir",
|
||||
opt.out_dir,
|
||||
Store,
|
||||
|
@ -291,12 +259,6 @@ fn main() -> ResultS<()>
|
|||
opt.out_dir = ".".to_string();
|
||||
}
|
||||
|
||||
if !opt.wad_c_temp.is_empty() {
|
||||
for ctyp in opt.wad_c_temp.split(',') {
|
||||
opt.wad_chunks.insert(ident(ctyp.as_bytes()));
|
||||
}
|
||||
}
|
||||
|
||||
validate_folder_path(&opt.out_dir)?;
|
||||
|
||||
for arg in &opt.inputs {
|
||||
|
@ -334,12 +296,9 @@ struct Options
|
|||
shp_seq: bool,
|
||||
snd_dump: bool,
|
||||
snd_write: bool,
|
||||
wad_all: bool,
|
||||
wad_unknown: bool,
|
||||
wad_wrt_all: bool,
|
||||
wad_header: bool,
|
||||
wad_chunks: HashSet<Ident>,
|
||||
wad_c_temp: String,
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -1,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
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Library for file data formats.
|
||||
//! Library for file format data readers.
|
||||
|
||||
pub mod machdr;
|
||||
pub mod map;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
fn mk_btn_new(b: >k::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: >k::Builder, app: gtk::Application)
|
||||
{
|
||||
let btn: gtk::MenuItem = get_obj(b, "btn-quit");
|
||||
|
||||
btn.connect_activate(move |_| app.quit());
|
||||
}
|
||||
|
||||
fn mk_btn_about(b: >k::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: >k::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: >k::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.
|
@ -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>
|
||||
|
|
|
@ -1,53 +1,38 @@
|
|||
use gtk::prelude::*;
|
||||
use gdk::prelude::*;
|
||||
use gio::prelude::*;
|
||||
use gtk::prelude::*;
|
||||
use maraiah::durandal::err::*;
|
||||
|
||||
fn run_app(app: >k::Application) -> ResultS<()>
|
||||
include!("windows.rs");
|
||||
include!("buttons.rs");
|
||||
include!("map_draw.rs");
|
||||
|
||||
fn run_app(app: >k::Application)
|
||||
{
|
||||
fn load_img(path: &'static str) -> ResultS<gdk_pixbuf::Pixbuf>
|
||||
{
|
||||
Ok(gdk_pixbuf::Pixbuf::new_from_resource(path)?)
|
||||
}
|
||||
|
||||
fn get_obj<T>(b: >k::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: >k::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::<>k::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(())
|
||||
|
|
|
@ -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: >k::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
|
|
@ -0,0 +1,39 @@
|
|||
fn hide_on_delete(win: >k::Window, _: &gdk::Event) -> Inhibit
|
||||
{
|
||||
win.hide();
|
||||
Inhibit(true)
|
||||
}
|
||||
|
||||
fn mk_win_map_tools(b: >k::Builder)
|
||||
{
|
||||
let win: gtk::Window = get_obj(b, "win-map-tools");
|
||||
|
||||
win.connect_delete_event(hide_on_delete);
|
||||
}
|
||||
|
||||
fn mk_win_map_view(b: >k::Builder)
|
||||
{
|
||||
let win: gtk::Window = get_obj(b, "win-map-view");
|
||||
|
||||
win.connect_delete_event(hide_on_delete);
|
||||
}
|
||||
|
||||
fn mk_win_about(b: >k::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: >k::Builder, app: >k::Application)
|
||||
{
|
||||
let win: gtk::Window = get_obj(b, "win-main");
|
||||
|
||||
win.set_application(app);
|
||||
win.show_all();
|
||||
}
|
||||
|
||||
// EOF
|
Loading…
Reference in New Issue