Compare commits
7 Commits
16c52a1211
...
1d6aa0c14e
Author | SHA1 | Date |
---|---|---|
an | 1d6aa0c14e | |
an | 6e4c7512e8 | |
an | 403127b7a6 | |
an | 2d18d295b8 | |
an | ff10f3ca53 | |
an | 7334ff6bd5 | |
an | 132e964f8c |
|
@ -16,7 +16,7 @@ members = ["source/leela", "source/tycho"]
|
|||
|
||||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
failure = "0.1"
|
||||
failure = {version = "0.1", features = ["std"]}
|
||||
serde = {version = "1.0", features = ["derive"]}
|
||||
|
||||
[profile.dev]
|
||||
|
|
|
@ -337,7 +337,8 @@ Example:
|
|||
#SOUND sound_number
|
||||
```
|
||||
|
||||
`#SOUND` plays the specified sound from the Sounds file or from the Scenario.
|
||||
`#SOUND` plays the specified sound from the Sounds file or from the scenario
|
||||
and then goes to the next level.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -1914,7 +1915,7 @@ Used to determine how the engine loads map data.
|
|||
|
||||
| Name | Value | Permutation |
|
||||
| ---- | ----- | ----------- |
|
||||
| `Logon` | `0` | None |
|
||||
| `Logon` | `0` | Pict ID |
|
||||
| `Unfinished` | `1` | None |
|
||||
| `Success` | `2` | None |
|
||||
| `Failure` | `3` | None |
|
||||
|
@ -1927,7 +1928,7 @@ Used to determine how the engine loads map data.
|
|||
| `Movie` | `10` | Movie ID |
|
||||
| `Track` | `11` | Track ID |
|
||||
| `Pict` | `12` | Pict ID |
|
||||
| `Logoff` | `13` | None |
|
||||
| `Logoff` | `13` | Pict ID |
|
||||
| `Camera` | `14` | Object ID |
|
||||
| `Static` | `15` | 1/30 secs |
|
||||
| `Tag` | `16` | Tag number |
|
||||
|
|
|
@ -22,16 +22,16 @@ macro_rules! _durandal_read_impl {
|
|||
|
||||
// little endian
|
||||
(LE $b:ident $nam:ident u16 $n:expr) => {
|
||||
_durandal_read_impl!($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) => {
|
||||
_durandal_read_impl!($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) => {
|
||||
_durandal_read_impl!($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) => {
|
||||
_durandal_read_impl!($b i32::from_le_bytes $nam 4 $n);
|
||||
_durandal_read_impl!($b i32::from_le_bytes, $nam 4 $n);
|
||||
};
|
||||
|
||||
// either endianness
|
||||
|
@ -87,6 +87,8 @@ macro_rules! _durandal_read_impl {
|
|||
|
||||
/// Reads structured data from a byte slice.
|
||||
///
|
||||
/// # Syntax
|
||||
///
|
||||
/// First start by specifying the endianness, size and source using the syntax
|
||||
/// `endian, size in source =>` where:
|
||||
///
|
||||
|
@ -126,14 +128,41 @@ macro_rules! _durandal_read_impl {
|
|||
/// - `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]
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This macro will not panic unless any index expression used exceeds `size`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate maraiah;
|
||||
/// # use maraiah::durandal::err::*;
|
||||
/// # fn main() -> ResultS<()>
|
||||
/// # {
|
||||
/// let buffer = &[4, 0, 2, 0, 0, 0, 6];
|
||||
///
|
||||
/// read_data! {
|
||||
/// 7, LE in buffer =>
|
||||
/// four = u16[0];
|
||||
/// two = u32[2];
|
||||
/// six = u8 [6];
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(four, 4_u16);
|
||||
/// assert_eq!(two, 2_u32);
|
||||
/// assert_eq!(six, 6_u8);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! read_data {
|
||||
(
|
||||
$sz:expr , $ty:ident in $b:ident =>
|
||||
$( $nam:ident = $t:ident [ $n:expr ] $( $ex:ident )* ; )*
|
||||
) => {
|
||||
if $b.len() < $sz {
|
||||
bail!("not enough data");
|
||||
return Err(err_msg("not enough data"));
|
||||
}
|
||||
|
||||
$($crate::_durandal_read_impl!($ty $b $nam $($ex)* $t $n);)*
|
||||
|
@ -141,32 +170,91 @@ macro_rules! read_data {
|
|||
}
|
||||
|
||||
/// Casts a `u32` to a `usize`. For future compatibility.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::bin::usize_from_u32;
|
||||
///
|
||||
/// assert_eq!(usize_from_u32(777u32), 777usize);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn usize_from_u32(n: u32) -> usize {n as usize}
|
||||
|
||||
/// Creates an `Ident` from a slice.
|
||||
///
|
||||
/// `b` must be at least 4 bytes, or a panic will occur.
|
||||
pub fn ident(b: &[u8]) -> Ident {Ident([b[0], b[1], b[2], b[3]])}
|
||||
/// # Panics
|
||||
///
|
||||
/// A panic will occur if `b.len()` is less than 4.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::bin::{Ident, ident};
|
||||
///
|
||||
/// assert_eq!(ident(b"POLY"), Ident([b'P', b'O', b'L', b'Y']));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn ident(b: &[u8]) -> Ident {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.
|
||||
/// # Panics
|
||||
///
|
||||
/// A panic will occur if `b.len()` is less than 4.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::bin::u32b;
|
||||
///
|
||||
/// assert_eq!(u32b(&[0x00, 0x0B, 0xDE, 0x31]), 777_777u32);
|
||||
/// ```
|
||||
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.
|
||||
/// # Panics
|
||||
///
|
||||
/// A panic will occur if `b.len()` is less than 2.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::bin::u16b;
|
||||
///
|
||||
/// assert_eq!(u16b(&[0x1E, 0x61]), 7_777u16);
|
||||
/// ```
|
||||
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.
|
||||
/// # Panics
|
||||
///
|
||||
/// A panic will occur if `b.len()` is less than 4.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::bin::i32b;
|
||||
///
|
||||
/// assert_eq!(i32b(&[0xFF, 0x89, 0x52, 0x0F]), -7_777_777i32);
|
||||
/// ```
|
||||
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.
|
||||
/// # Panics
|
||||
///
|
||||
/// A panic will occur if `b.len()` is less than 2.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::bin::i16b;
|
||||
///
|
||||
/// assert_eq!(i16b(&[0xE1, 0x9F]), -7_777i16);
|
||||
/// ```
|
||||
pub fn i16b(b: &[u8]) -> i16 {i16::from_be_bytes([b[0], b[1]])}
|
||||
|
||||
/// Applies a read function over a slice.
|
||||
|
@ -175,6 +263,26 @@ pub fn i16b(b: &[u8]) -> i16 {i16::from_be_bytes([b[0], b[1]])}
|
|||
/// 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.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// A panic will occur if the `read` function returns a disjoint index or
|
||||
/// otherwise panics (by an out of bounds index to `b` or otherwise.)
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Execution will return the result of `read` if `read` returns an error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::{err::*, bin::{rd_array, u16b}};
|
||||
///
|
||||
/// fn read_a_u16(b: &[u8]) -> ResultS<(u16, usize)> {Ok((u16b(b), 2))}
|
||||
///
|
||||
/// let inp = &[0x1E, 0x61, 0x03, 0x09];
|
||||
/// assert_eq!(rd_array(inp, read_a_u16).unwrap(), vec![7_777u16, 777u16]);
|
||||
/// ```
|
||||
pub fn rd_array<T, F>(b: &[u8], read: F) -> ResultS<Vec<T>>
|
||||
where T: Sized,
|
||||
F: Fn(&[u8]) -> ResultS<(T, usize)>
|
||||
|
@ -198,6 +306,15 @@ pub fn rd_array<T, F>(b: &[u8], read: F) -> ResultS<Vec<T>>
|
|||
/// 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.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// A panic will occur if the `read` function returns a disjoint index or
|
||||
/// otherwise panics (by an out of bounds index to `b` or otherwise.)
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Execution will return the result of `read` if `read` returns an error.
|
||||
pub fn rd_ofstable<T, F>(b: &[u8],
|
||||
mut p: usize,
|
||||
num: usize,
|
||||
|
@ -225,6 +342,14 @@ pub fn rd_ofstable<T, F>(b: &[u8],
|
|||
impl OptU16
|
||||
{
|
||||
/// Creates an `OptU16` representing `None`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::bin::OptU16;
|
||||
///
|
||||
/// assert_eq!(OptU16::none(), OptU16::from_repr(u16::max_value()));
|
||||
/// ```
|
||||
pub const fn none() -> Self {OptU16(None)}
|
||||
|
||||
/// Creates an `OptU16` from a `u16`.
|
||||
|
@ -238,6 +363,18 @@ impl OptU16
|
|||
}
|
||||
|
||||
/// Returns the `u16` representation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::bin::OptU16;
|
||||
///
|
||||
/// let u16max = u16::max_value();
|
||||
///
|
||||
/// assert_eq!(OptU16::from_repr(500u16).get_repr(), 500u16);
|
||||
/// assert_eq!(OptU16::from_repr(u16max).get_repr(), u16max);
|
||||
/// assert_eq!(OptU16::from_repr(0u16).get_repr(), 0u16);
|
||||
/// ```
|
||||
pub fn get_repr(&self) -> u16
|
||||
{
|
||||
match self.0 {
|
||||
|
@ -247,6 +384,16 @@ impl OptU16
|
|||
}
|
||||
|
||||
/// Returns the `Option` representation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::bin::OptU16;
|
||||
///
|
||||
/// assert_eq!(OptU16::from_repr(500u16).get(), Some(500u16));
|
||||
/// assert_eq!(OptU16::from_repr(u16::max_value()).get(), None);
|
||||
/// assert_eq!(OptU16::from_repr(0u16).get(), Some(0u16));
|
||||
/// ```
|
||||
pub fn get(&self) -> Option<u16>
|
||||
{
|
||||
match self.0 {
|
||||
|
@ -276,11 +423,21 @@ impl fmt::Debug for Ident
|
|||
}
|
||||
|
||||
/// A four-character-code identifier.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::bin::Ident;
|
||||
///
|
||||
/// assert_eq!(Ident(*b"POLY").0, *b"POLY");
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Default, PartialEq)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Ident(pub [u8; 4]);
|
||||
|
||||
/// An object identified by a `u16` which may be `u16::max_value()` to
|
||||
/// represent a nulled value.
|
||||
#[derive(Clone, Copy, Default, PartialEq)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct OptU16(Option<NonZeroU16>);
|
||||
|
||||
|
|
|
@ -8,6 +8,29 @@
|
|||
///
|
||||
/// This will generate an `enum E` as well as a function `E::from_repr` which
|
||||
/// will return `Result<E, ReprError>`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::{c_enum, durandal::err::ReprError};
|
||||
///
|
||||
/// c_enum! {
|
||||
/// #[derive(Debug, PartialEq)]
|
||||
/// enum MyEnum: u16
|
||||
/// {
|
||||
/// 0 => Zero,
|
||||
/// 1 => One,
|
||||
/// 2 => Two,
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(MyEnum::from_repr(0), Ok(MyEnum::Zero));
|
||||
/// assert_eq!(MyEnum::from_repr(1), Ok(MyEnum::One));
|
||||
/// assert_eq!(MyEnum::from_repr(2), Ok(MyEnum::Two));
|
||||
/// assert_eq!(MyEnum::from_repr(3), Err(ReprError::new(3)));
|
||||
/// assert_eq!(MyEnum::from_repr(4), Err(ReprError::new(4)));
|
||||
/// assert_eq!(MyEnum::from_repr(5), Err(ReprError::new(5)));
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! c_enum
|
||||
{
|
||||
|
@ -32,7 +55,7 @@ macro_rules! c_enum
|
|||
{
|
||||
match n {
|
||||
$($value => Ok($E::$Enum),)+
|
||||
n => Err(ReprError(n.into()))
|
||||
n => Err(ReprError::new(n))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,9 +83,9 @@ mod test
|
|||
assert_eq!(TestEnum::from_repr(0), Ok(TestEnum::Zero));
|
||||
assert_eq!(TestEnum::from_repr(1), Ok(TestEnum::One));
|
||||
assert_eq!(TestEnum::from_repr(2), Ok(TestEnum::Two));
|
||||
assert_eq!(TestEnum::from_repr(3), Err(ReprError(3)));
|
||||
assert_eq!(TestEnum::from_repr(4), Err(ReprError(4)));
|
||||
assert_eq!(TestEnum::from_repr(5), Err(ReprError(5)));
|
||||
assert_eq!(TestEnum::from_repr(3), Err(ReprError::new(3)));
|
||||
assert_eq!(TestEnum::from_repr(4), Err(ReprError::new(4)));
|
||||
assert_eq!(TestEnum::from_repr(5), Err(ReprError::new(5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -19,6 +19,14 @@ fn crc_init() -> [u32; 256]
|
|||
}
|
||||
|
||||
/// Creates a CRC-32 of all bytes in `b` with the starting sum `s`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::crc::crc32;
|
||||
///
|
||||
/// assert_eq!(crc32(b"Lorem ipsum dolor sit amet", !0), 0x5F29_D461);
|
||||
/// ```
|
||||
pub fn crc32(b: &[u8], s: u32) -> u32
|
||||
{
|
||||
let t = crc_init();
|
||||
|
@ -26,10 +34,4 @@ pub fn crc32(b: &[u8], s: u32) -> u32
|
|||
.fold(s, |a, &o| a >> 8 ^ t[usize::from(a as u8 ^ o)])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crc32_lorem_ipsum()
|
||||
{
|
||||
assert_eq!(crc32(b"Lorem ipsum dolor sit amet", !0), 0x5F29_D461);
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -29,8 +29,33 @@ macro_rules! bail {
|
|||
}
|
||||
|
||||
/// Returns an `Error` with a static string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::err::err_msg;
|
||||
///
|
||||
/// assert_eq!(format!("{}", err_msg("oh no not things")), "oh no not things");
|
||||
/// ```
|
||||
pub fn err_msg(msg: &'static str) -> Error {Error::from(ErrMsg(msg))}
|
||||
|
||||
impl ReprError
|
||||
{
|
||||
/// Creates a new `ReprError`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::err::ReprError;
|
||||
///
|
||||
/// let err = ReprError::new(7);
|
||||
///
|
||||
/// assert_eq!(format!("{}", err), "representation error (got 7)");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn new<T>(n: T) -> Self where T: Into<i64> {Self(n.into())}
|
||||
}
|
||||
|
||||
impl Fail for ReprError {}
|
||||
impl Fail for ErrMsg {}
|
||||
|
||||
|
@ -42,14 +67,6 @@ impl fmt::Display for ReprError
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ReprError
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ErrMsg
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
|
@ -58,18 +75,11 @@ impl fmt::Display for ErrMsg
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ErrMsg
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A representation error for an integer.
|
||||
#[derive(PartialEq)]
|
||||
pub struct ReprError(pub i64);
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ReprError(i64);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ErrMsg(&'static str);
|
||||
|
||||
/// A generic `failure` based `Result` type.
|
||||
|
|
|
@ -1,212 +1,449 @@
|
|||
//! Fixed point numbers.
|
||||
|
||||
use std::{fmt::{self, Write},
|
||||
ops};
|
||||
use std::{fmt::{self, Write}, ops::*};
|
||||
|
||||
macro_rules! define_fixed_type {
|
||||
macro_rules! fixed_ref_unop {
|
||||
(impl $imp:ident, $method:ident for $t:ty) => {
|
||||
impl $imp for &$t
|
||||
{
|
||||
type Output = <$t as $imp>::Output;
|
||||
|
||||
#[inline]
|
||||
fn $method(self) -> <$t as $imp>::Output {$imp::$method(*self)}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! fixed_ref_binop {
|
||||
(impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
|
||||
impl<'a> $imp<$u> for &'a $t
|
||||
{
|
||||
type Output = <$t as $imp<$u>>::Output;
|
||||
|
||||
#[inline]
|
||||
fn $method(self, other: $u) -> <$t as $imp<$u>>::Output
|
||||
{
|
||||
$imp::$method(*self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> $imp<&'a $u> for $t
|
||||
{
|
||||
type Output = <$t as $imp<$u>>::Output;
|
||||
|
||||
#[inline]
|
||||
fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output
|
||||
{
|
||||
$imp::$method(self, *other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> $imp<&'a $u> for &'b $t
|
||||
{
|
||||
type Output = <$t as $imp<$u>>::Output;
|
||||
|
||||
#[inline]
|
||||
fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output
|
||||
{
|
||||
$imp::$method(*self, *other)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! fixed_ref_op_assign {
|
||||
(impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
|
||||
impl<'a> $imp<&'a $u> for $t
|
||||
{
|
||||
#[inline]
|
||||
fn $method(&mut self, other: &'a $u) {$imp::$method(self, *other);}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_fixed_types {
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
struct $Type:ident ($IT:ident) : $UT:ident, $LT:ident, $FracBits:expr;
|
||||
) => {
|
||||
$(
|
||||
$(#[$outer:meta])*
|
||||
struct $t:ident ( $ti:ident, $bytes:expr ) :
|
||||
$tu:ident, $frac_bits:expr; $test:ident
|
||||
)*
|
||||
) => {$(
|
||||
$(#[$outer])*
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, serde::Serialize)]
|
||||
pub struct $Type($IT);
|
||||
#[derive(Copy, Clone, Default, PartialEq, PartialOrd, serde::Serialize)]
|
||||
pub struct $t($ti);
|
||||
|
||||
impl $Type
|
||||
impl $t
|
||||
{
|
||||
/// The number of fractional bits in this type.
|
||||
pub const FRACBITS: $UT = $FracBits;
|
||||
#[inline]
|
||||
pub const fn frac_bits() -> $tu {$frac_bits}
|
||||
|
||||
/// The unsigned mask for the fractional bits.
|
||||
#[inline]
|
||||
pub const fn frac_mask() -> $tu {(1 << $t::frac_bits()) - 1}
|
||||
|
||||
/// The integer mask for the fractional bits.
|
||||
pub const FRACMASK: $UT = (1 << Self::FRACBITS) - 1;
|
||||
#[inline]
|
||||
pub const fn frac_mask_i() -> $ti {(1 << $t::frac_bits()) - 1}
|
||||
|
||||
/// The representation of `1.0` in this type.
|
||||
pub const ONE: $IT = 1 << Self::FRACBITS;
|
||||
#[inline]
|
||||
pub const fn one() -> $tu {1 << $t::frac_bits()}
|
||||
|
||||
/// Returns the largest value that can be represented.
|
||||
#[inline]
|
||||
pub const fn max_value() -> $t {$t($ti::max_value())}
|
||||
|
||||
/// Returns the smallest value that can be represented.
|
||||
#[inline]
|
||||
pub const fn min_value() -> $t {$t($ti::min_value())}
|
||||
|
||||
/// Returns the integer part of a number.
|
||||
#[inline]
|
||||
pub const fn trunc(self) -> $t {$t(self.0 & !$t::frac_mask_i())}
|
||||
|
||||
/// Returns the fractional part of a number.
|
||||
#[inline]
|
||||
pub const fn fract(self) -> $t {$t(self.0 & $t::frac_mask_i())}
|
||||
|
||||
/// Returns the number of ones in the bit representation of self.
|
||||
#[inline]
|
||||
pub fn count_ones(self) -> u32 {self.0.count_ones()}
|
||||
|
||||
/// Returns the number of zeros in the bit representation of self.
|
||||
#[inline]
|
||||
pub fn count_zeros(self) -> u32 {self.0.count_zeros()}
|
||||
|
||||
/// Returns the number of leading zeros in the bit representation of
|
||||
/// self.
|
||||
#[inline]
|
||||
pub fn leading_zeros(self) -> u32 {self.0.leading_zeros()}
|
||||
|
||||
/// Returns the number of trailing zeros in the bit representation of
|
||||
/// self.
|
||||
#[inline]
|
||||
pub fn trailing_zeros(self) -> u32 {self.0.trailing_zeros()}
|
||||
|
||||
/// Rotates all bits left by `n`.
|
||||
#[inline]
|
||||
pub fn rotate_left(self, n: u32) -> $t {$t(self.0.rotate_left(n))}
|
||||
|
||||
/// Rotates all bits right by `n`.
|
||||
#[inline]
|
||||
pub fn rotate_right(self, n: u32) -> $t {$t(self.0.rotate_right(n))}
|
||||
|
||||
/// Reverses the byte order of the bit representation of self.
|
||||
#[inline]
|
||||
pub fn swap_bytes(self) -> $t {$t(self.0.swap_bytes())}
|
||||
|
||||
/// Raises self to the power of `exp`.
|
||||
#[inline]
|
||||
pub fn pow(self, exp: u32) -> $t {$t(self.0.pow(exp))}
|
||||
|
||||
/// Returns the absolute value of self.
|
||||
#[inline]
|
||||
pub fn abs(self) -> $t {$t(self.0.abs())}
|
||||
|
||||
/// Returns a number representing sign of self.
|
||||
#[inline]
|
||||
pub fn signum(self) -> $t {$t(self.0.signum() << $t::frac_bits())}
|
||||
|
||||
/// Returns true if self is positive and false if the number is zero
|
||||
/// or negative.
|
||||
#[inline]
|
||||
pub fn is_positive(self) -> bool {self.0.is_positive()}
|
||||
|
||||
/// Returns true if self is negative and false if the number is zero
|
||||
/// or positive.
|
||||
#[inline]
|
||||
pub fn is_negative(self) -> bool {self.0.is_negative()}
|
||||
|
||||
/// Return the memory representation of this integer as a byte array
|
||||
/// in big-endian (network) byte order.
|
||||
#[inline]
|
||||
pub fn to_be_bytes(self) -> [u8; $bytes] {self.0.to_be_bytes()}
|
||||
|
||||
/// Return the memory representation of this integer as a byte array
|
||||
/// in little-endian byte order.
|
||||
#[inline]
|
||||
pub fn to_le_bytes(self) -> [u8; $bytes] {self.0.to_le_bytes()}
|
||||
|
||||
/// Return the memory representation of this integer as a byte array
|
||||
/// in native byte order.
|
||||
#[inline]
|
||||
pub fn to_ne_bytes(self) -> [u8; $bytes] {self.0.to_ne_bytes()}
|
||||
|
||||
/// Create a value from its representation as a byte array in
|
||||
/// big-endian byte order.
|
||||
#[inline]
|
||||
pub fn from_be_bytes(b: [u8; $bytes]) -> $t
|
||||
{
|
||||
$t($ti::from_be_bytes(b))
|
||||
}
|
||||
|
||||
/// Create a value from its representation as a byte array in
|
||||
/// little-endian byte order.
|
||||
#[inline]
|
||||
pub fn from_le_bytes(b: [u8; $bytes]) -> $t
|
||||
{
|
||||
$t($ti::from_le_bytes(b))
|
||||
}
|
||||
|
||||
/// Create a value from its representation as a byte array in
|
||||
/// native byte order.
|
||||
#[inline]
|
||||
pub fn from_ne_bytes(b: [u8; $bytes]) -> $t
|
||||
{
|
||||
$t($ti::from_ne_bytes(b))
|
||||
}
|
||||
|
||||
/// Creates a value of this type with the bit pattern `bits`.
|
||||
#[inline]
|
||||
pub const fn from_bits(bits: $UT) -> Self {$Type(bits as $IT)}
|
||||
pub const fn from_bits(bits: $tu) -> $t {$t(bits as $ti)}
|
||||
|
||||
/// Creates a value of this type with the integral portion `n`.
|
||||
#[inline]
|
||||
#[allow(clippy::cast_lossless)]
|
||||
pub const fn from_int(n: $LT) -> Self
|
||||
{
|
||||
$Type(n as ($IT) << Self::FRACBITS)
|
||||
}
|
||||
pub const fn from_int(n: $ti) -> $t {$t(n << $t::frac_bits())}
|
||||
|
||||
/// Returns the raw bit pattern.
|
||||
#[inline]
|
||||
pub fn to_bits(&self) -> $UT {self.0 as $UT}
|
||||
pub fn to_bits(self) -> $tu {self.0 as $tu}
|
||||
|
||||
/// 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))}
|
||||
pub fn set_bits(&mut self, bits: $tu) {self.0 = bits as $ti}
|
||||
|
||||
#[inline]
|
||||
fn fx_div(x: $IT, y: $IT) -> $IT
|
||||
const fn mul_i(x: $ti, y: $ti) -> $ti {x * y}
|
||||
|
||||
#[inline]
|
||||
const fn div_i(x: $ti, y: $ti) -> $ti {x / y}
|
||||
|
||||
#[inline]
|
||||
fn div_k(x: $ti, y: $ti) -> $ti
|
||||
{
|
||||
(i64::from(x) * i64::from(Self::ONE) / i64::from(y)) as $IT
|
||||
(i64::from(x) * i64::from($t::one()) / i64::from(y)) as $ti
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fx_mul(x: $IT, y: $IT) -> $IT
|
||||
fn mul_k(x: $ti, y: $ti) -> $ti
|
||||
{
|
||||
(i64::from(x) * i64::from(y) / i64::from(Self::ONE)) as $IT
|
||||
(i64::from(x) * i64::from(y) / i64::from($t::one())) as $ti
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$LT> for $Type
|
||||
{
|
||||
#[inline]
|
||||
fn from(n: $LT) -> Self {$Type::from_int(n)}
|
||||
#[cfg(test)]
|
||||
mod $test {
|
||||
use super::$t;
|
||||
|
||||
#[test]
|
||||
fn basic_ops()
|
||||
{
|
||||
let one = $t::one();
|
||||
let two = 2 << $t::frac_bits();
|
||||
let twelve = 12 << $t::frac_bits();
|
||||
|
||||
assert_eq!(($t::from(1) + $t::from(1)).to_bits(), two);
|
||||
assert_eq!(($t::from(2) - $t::from(1)).to_bits(), one);
|
||||
assert_eq!(($t::from(6) * $t::from(2)).to_bits(), twelve);
|
||||
assert_eq!(($t::from(6) * 2) .to_bits(), twelve);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fractions()
|
||||
{
|
||||
let three_pt_5 = 3 << $t::frac_bits() | $t::one() / 2;
|
||||
let one_pt_2 = 1 << $t::frac_bits() | $t::one() / 5;
|
||||
let two_pt_4 = one_pt_2 * 2;
|
||||
|
||||
assert_eq!(($t::from(7) / $t::from(2)) .to_bits(), three_pt_5);
|
||||
assert_eq!(($t::from(7) / 2) .to_bits(), three_pt_5);
|
||||
assert_eq!(($t::from_bits(one_pt_2) * 2).to_bits(), two_pt_4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn printing()
|
||||
{
|
||||
assert_eq!(format!("{}", $t::from(6)), "6.0");
|
||||
assert_eq!(format!("{:2.3}", $t::from(7) / 2), " 3.500");
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add for $Type
|
||||
impl From<$ti> for $t
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(self, o: Self) -> Self {$Type(self.0 + o.0)}
|
||||
fn from(n: $ti) -> $t {$t::from_int(n)}
|
||||
}
|
||||
|
||||
impl ops::Sub for $Type
|
||||
impl Add<$t> for $t
|
||||
{
|
||||
type Output = Self;
|
||||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, o: Self) -> Self {$Type(self.0 - o.0)}
|
||||
fn add(self, o: $t) -> $t {$t(self.0 + o.0)}
|
||||
}
|
||||
|
||||
impl ops::Mul for $Type
|
||||
fixed_ref_binop! {impl Add, add for $t, $t}
|
||||
|
||||
impl Sub<$t> for $t
|
||||
{
|
||||
type Output = Self;
|
||||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, o: Self) -> Self {$Type(Self::fx_mul(self.0, o.0))}
|
||||
fn sub(self, o: $t) -> $t {$t(self.0 - o.0)}
|
||||
}
|
||||
|
||||
impl ops::Div for $Type
|
||||
fixed_ref_binop! {impl Sub, sub for $t, $t}
|
||||
|
||||
impl Mul<$t> for $t
|
||||
{
|
||||
type Output = Self;
|
||||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
fn div(self, o: Self) -> Self {$Type(Self::fx_div(self.0, o.0))}
|
||||
fn mul(self, o: $t) -> $t {$t($t::mul_k(self.0, o.0))}
|
||||
}
|
||||
|
||||
impl ops::Neg for $Type
|
||||
fixed_ref_binop! {impl Mul, mul for $t, $t}
|
||||
|
||||
impl Mul<$ti> for $t
|
||||
{
|
||||
type Output = Self;
|
||||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self {$Type(-self.0)}
|
||||
fn mul(self, o: $ti) -> $t {$t($t::mul_i(self.0, o))}
|
||||
}
|
||||
|
||||
impl ops::Not for $Type
|
||||
fixed_ref_binop! {impl Mul, mul for $t, $ti}
|
||||
|
||||
impl Div<$t> for $t
|
||||
{
|
||||
type Output = Self;
|
||||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
fn not(self) -> Self {$Type(!self.0)}
|
||||
fn div(self, o: $t) -> $t {$t($t::div_k(self.0, o.0))}
|
||||
}
|
||||
|
||||
impl fmt::Display for $Type
|
||||
fixed_ref_binop! {impl Div, div for $t, $t}
|
||||
|
||||
impl Div<$ti> for $t
|
||||
{
|
||||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
fn div(self, o: $ti) -> $t {$t($t::div_i(self.0, o))}
|
||||
}
|
||||
|
||||
fixed_ref_binop! {impl Div, div for $t, $ti}
|
||||
|
||||
impl Neg for $t
|
||||
{
|
||||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> $t {$t(-self.0)}
|
||||
}
|
||||
|
||||
fixed_ref_unop! {impl Neg, neg for $t}
|
||||
|
||||
impl Not for $t
|
||||
{
|
||||
type Output = $t;
|
||||
|
||||
#[inline]
|
||||
fn not(self) -> $t {$t(!self.0)}
|
||||
}
|
||||
|
||||
fixed_ref_unop! {impl Not, not for $t}
|
||||
|
||||
impl AddAssign for $t
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: $t) {self.0 += other.0}
|
||||
}
|
||||
|
||||
fixed_ref_op_assign! {impl AddAssign, add_assign for $t, $t}
|
||||
|
||||
impl SubAssign for $t
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: $t) {self.0 -= other.0}
|
||||
}
|
||||
|
||||
fixed_ref_op_assign! {impl SubAssign, sub_assign for $t, $t}
|
||||
|
||||
impl MulAssign for $t
|
||||
{
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: $t) {self.0 = (*self * other).0}
|
||||
}
|
||||
|
||||
fixed_ref_op_assign! {impl MulAssign, mul_assign for $t, $t}
|
||||
|
||||
impl DivAssign for $t
|
||||
{
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: $t) {self.0 = (*self / other).0}
|
||||
}
|
||||
|
||||
fixed_ref_op_assign! {impl DivAssign, div_assign for $t, $t}
|
||||
|
||||
impl fmt::Display for $t
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
let prec = f.precision().unwrap_or(1);
|
||||
let widt = f.width().unwrap_or(0);
|
||||
write!(f, "{:widt$}.", self.integ(), widt = widt)?;
|
||||
|
||||
write!(f, "{:widt$}.", self.0 >> $t::frac_bits(), widt = widt)?;
|
||||
|
||||
let mut k = self.to_bits();
|
||||
|
||||
for _ in 0..prec {
|
||||
k &= Self::FRACMASK;
|
||||
k &= $t::frac_mask();
|
||||
k *= 10;
|
||||
let d = k >> Self::FRACBITS;
|
||||
|
||||
let d = k >> $t::frac_bits();
|
||||
let d = d % 10;
|
||||
f.write_char((d as u8 + b'0') as char)?;
|
||||
|
||||
f.write_char(char::from(d as u8 + b'0'))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for $Type
|
||||
impl fmt::Debug for $t
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
};
|
||||
)*};
|
||||
}
|
||||
|
||||
define_fixed_type! {
|
||||
define_fixed_types! {
|
||||
/// 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;
|
||||
}
|
||||
struct Angle(i16, 2) : u16, 9; angle_tests
|
||||
|
||||
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;
|
||||
}
|
||||
struct Unit(i16, 2) : u16, 10; unit_tests
|
||||
|
||||
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()
|
||||
{
|
||||
let seven_div_2 = 3 << Fixed::FRACBITS | Fixed::FRACMASK / 2 + 1;
|
||||
assert_eq!((Fixed::from(1) + 1.into()).to_bits(), 2 << Fixed::FRACBITS);
|
||||
assert_eq!((Fixed::from(2) - 1.into()).to_bits(), 1 << Fixed::FRACBITS);
|
||||
assert_eq!((Fixed::from(6) * 2.into()).to_bits(), 12 << Fixed::FRACBITS);
|
||||
assert_eq!((Fixed::from(6).mul_i(2)) .to_bits(), 12 << Fixed::FRACBITS);
|
||||
assert_eq!((Fixed::from(7) / 2.into()).to_bits(), seven_div_2);
|
||||
assert_eq!((Fixed::from(7).div_i(2)) .to_bits(), seven_div_2);
|
||||
struct Fixed(i32, 4) : u32, 16; fixed_tests
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[allow(unused_must_use)]
|
||||
fn fixed_overflow() {Fixed::from(i16::max_value()) + 1.into();}
|
||||
|
||||
#[test]
|
||||
fn fixed_printing()
|
||||
{
|
||||
assert_eq!(format!("{}", Fixed::from(6)), "6.0");
|
||||
assert_eq!(format!("{}", Fixed::from(7).div_i(2)), "3.5");
|
||||
assert_eq!(format!("{:7.7}", Fixed::from_bits(0xDEAD_BEEF)),
|
||||
" -8531.7458343");
|
||||
}
|
||||
fn fixed_overflow() {Fixed::from(i16::max_value() as i32) + Fixed::from(1);}
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -4,17 +4,31 @@ use crate::durandal::err::*;
|
|||
use std::io;
|
||||
|
||||
/// Creates a RGB8 color from a R5G5B5 format color.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::image::{Color8, r5g5b5_to_rgb8};
|
||||
///
|
||||
/// assert_eq!(r5g5b5_to_rgb8(0x2345), Color8::new(64, 208, 40));
|
||||
/// ```
|
||||
pub fn r5g5b5_to_rgb8(rgb: u16) -> Color8
|
||||
{
|
||||
let r = rgb >> 10 & 0x1f;
|
||||
let g = rgb >> 5 & 0x1f;
|
||||
let b = rgb & 0x1f;
|
||||
Color8::new((r << 3 | r >> 2) as u8,
|
||||
(g << 3 | g >> 2) as u8,
|
||||
(b << 3 | b >> 2) as u8)
|
||||
let r = (rgb >> 10) as u8 & 0x1f;
|
||||
let g = (rgb >> 5) as u8 & 0x1f;
|
||||
let b = rgb as u8 & 0x1f;
|
||||
Color8::new(r << 3, g << 3, b << 3)
|
||||
}
|
||||
|
||||
/// Creates a RGB16 color from a R5G5B5 format color.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::image::{Color16, r5g5b5_to_rgb16};
|
||||
///
|
||||
/// assert_eq!(r5g5b5_to_rgb16(0x2345), Color16::new(0x4000, 0xD000, 0x2800));
|
||||
/// ```
|
||||
pub fn r5g5b5_to_rgb16(rgb: u16) -> Color16
|
||||
{
|
||||
let r = rgb >> 10 & 0x1f;
|
||||
|
@ -24,14 +38,27 @@ pub fn r5g5b5_to_rgb16(rgb: u16) -> Color16
|
|||
}
|
||||
|
||||
/// Creates a RGB16 color from a RGB8 format color.
|
||||
pub fn rgb8_to_rgb16(r: u8, g: u8, b: u8) -> Color16
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::image;
|
||||
///
|
||||
/// let inpl = image::r5g5b5_to_rgb8(0x2345);
|
||||
/// let inph = image::r5g5b5_to_rgb16(0x2345);
|
||||
///
|
||||
/// assert_eq!(inph, image::rgb8_to_rgb16(inpl));
|
||||
/// ```
|
||||
pub fn rgb8_to_rgb16(cr: Color8) -> Color16
|
||||
{
|
||||
Color16::new(u16::from(r) << 8,
|
||||
u16::from(g) << 8,
|
||||
u16::from(b) << 8)
|
||||
Color16::new(cr.r(), cr.g(), cr.b())
|
||||
}
|
||||
|
||||
/// Writes a PPM file from an image.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Errors if `out` cannot be written to.
|
||||
pub fn write_ppm(out: &mut impl io::Write, im: &impl Image) -> ResultS<()>
|
||||
{
|
||||
write!(out, "P3\n{} {}\n{}\n", im.w(), im.h(), u16::max_value())?;
|
||||
|
@ -49,6 +76,10 @@ pub fn write_ppm(out: &mut impl io::Write, im: &impl Image) -> ResultS<()>
|
|||
}
|
||||
|
||||
/// Writes a TGA file from an image.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Errors if `out` cannot be written to.
|
||||
pub fn write_tga(out: &mut impl io::Write, im: &impl Image) -> ResultS<()>
|
||||
{
|
||||
// id len, color map type, image type
|
||||
|
|
|
@ -4,6 +4,10 @@ use crate::durandal::err::*;
|
|||
use std::io;
|
||||
|
||||
/// Writes a WAVE file from a sound.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Errors if `out` cannot be written to.
|
||||
pub fn write_wav(out: &mut impl io::Write, snd: &impl Sound) -> ResultS<()>
|
||||
{
|
||||
let smp_rate = u32::from(snd.rate());
|
||||
|
|
|
@ -2,30 +2,15 @@
|
|||
|
||||
use crate::durandal::err::*;
|
||||
|
||||
/// Dumps a slice of memory as text to stderr.
|
||||
pub fn dump_mem(b: &[u8])
|
||||
{
|
||||
let mut p = 0;
|
||||
|
||||
for &c in b {
|
||||
if p + 3 > 79 {
|
||||
eprintln!("");
|
||||
p = 0;
|
||||
}
|
||||
|
||||
if c.is_ascii_graphic() {
|
||||
eprint!(" {} ", c as char);
|
||||
} else {
|
||||
eprint!("{:02X} ", c);
|
||||
}
|
||||
|
||||
p += 3;
|
||||
}
|
||||
|
||||
eprintln!("");
|
||||
}
|
||||
|
||||
/// Formats a binary size string for any given number.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::text::to_binsize;
|
||||
///
|
||||
/// assert_eq!(to_binsize(5000), "5kB".to_string());
|
||||
/// ```
|
||||
pub fn to_binsize(n: u64) -> String
|
||||
{
|
||||
const NAMES: [&str; 4] = ["kB", "MB", "GB", "TB"];
|
||||
|
@ -70,6 +55,16 @@ pub fn fuck_string(s: &[u8]) -> Vec<u8>
|
|||
}
|
||||
|
||||
/// Reads a Pascal-style byte string with bounds checking.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::text::pascal_str;
|
||||
///
|
||||
/// assert_eq!(pascal_str(b"\x0bhello world"), b"hello world"[..].into());
|
||||
/// assert_eq!(pascal_str(b"\x0chello world"), None);
|
||||
/// assert_eq!(pascal_str(&[]), None);
|
||||
/// ```
|
||||
pub fn pascal_str(b: &[u8]) -> Option<&[u8]>
|
||||
{
|
||||
let s = usize::from(*b.get(0)?);
|
||||
|
@ -77,6 +72,15 @@ pub fn pascal_str(b: &[u8]) -> Option<&[u8]>
|
|||
}
|
||||
|
||||
/// Converts input from Mac Roman to a Unicode string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::text::mac_roman_conv;
|
||||
///
|
||||
/// assert_eq!(mac_roman_conv(b"p\x8cth"), "påth");
|
||||
/// assert_eq!(mac_roman_conv(b"I\xd5ve"), "I’ve");
|
||||
/// ```
|
||||
pub fn mac_roman_conv(s: &[u8]) -> String
|
||||
{
|
||||
let mut v = String::with_capacity(s.len());
|
||||
|
@ -93,6 +97,14 @@ pub fn mac_roman_conv(s: &[u8]) -> String
|
|||
}
|
||||
|
||||
/// Converts a C-style string from Mac Roman to Unicode.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use maraiah::durandal::text::mac_roman_cstr;
|
||||
///
|
||||
/// assert_eq!(mac_roman_cstr(b"I\xd5ve awaken\0ed").unwrap(), "I’ve awaken");
|
||||
/// ```
|
||||
pub fn mac_roman_cstr(s: &[u8]) -> ResultS<String>
|
||||
{
|
||||
if let Some(s) = s.split(|&n| n == 0).nth(0) {
|
||||
|
@ -143,17 +155,5 @@ fn to_binsize_integrals()
|
|||
assert_eq!(to_binsize(1000 * 1000 * 1000 * 1000 * 7), "7TB");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mac_roman_conv_basic_marathon_stuff()
|
||||
{
|
||||
assert_eq!(mac_roman_conv(b"p\x8cth"), "påth");
|
||||
assert_eq!(mac_roman_conv(b"I\xd5ve"), "I’ve");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mac_roman_cstr_tests()
|
||||
{
|
||||
assert_eq!(mac_roman_cstr(b"I\xd5ve awaken\0e").unwrap(), "I’ve awaken");
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#![deny(clippy::clone_on_ref_ptr)]
|
||||
#![deny(clippy::copy_iterator)]
|
||||
#![deny(clippy::decimal_literal_representation)]
|
||||
#![deny(clippy::default_trait_access)]
|
||||
#![deny(clippy::doc_markdown)]
|
||||
#![deny(clippy::empty_enum)]
|
||||
#![deny(clippy::empty_line_after_outer_attr)]
|
||||
|
|
|
@ -67,8 +67,33 @@ pub fn read_minf(b: &[u8]) -> ResultS<Minf>
|
|||
level_name})
|
||||
}
|
||||
|
||||
/// Reads an old `Minf` chunk.
|
||||
pub fn read_old_minf(b: &[u8]) -> ResultS<Minf>
|
||||
{
|
||||
let minf = read_minf(b)?;
|
||||
|
||||
let mut entry_flags = if minf.entry_flags.is_empty() {
|
||||
EntFlags::Solo
|
||||
} else {
|
||||
minf.entry_flags
|
||||
};
|
||||
|
||||
if entry_flags.intersects(EntFlags::Solo | EntFlags::Carnage) {
|
||||
entry_flags.insert(EntFlags::CoOp)
|
||||
}
|
||||
|
||||
Ok(Minf{entry_flags, ..minf})
|
||||
}
|
||||
|
||||
/// Reads an `iidx` chunk.
|
||||
pub fn read_iidx(b: &[u8]) -> ResultS<(u16, usize)> {Ok((u16b(b), 2))}
|
||||
pub fn read_iidx(b: &[u8]) -> ResultS<(u16, usize)>
|
||||
{
|
||||
if b.len() < 2 {
|
||||
bail!("not enough data");
|
||||
}
|
||||
|
||||
Ok((u16b(b), 2))
|
||||
}
|
||||
|
||||
/// Reads an `EPNT` chunk.
|
||||
pub fn read_epnt(b: &[u8]) -> ResultS<(Point, usize)>
|
||||
|
@ -145,13 +170,11 @@ pub fn read_old_sids(b: &[u8]) -> ResultS<(Side, usize)>
|
|||
..side}, siz))
|
||||
}
|
||||
|
||||
/// Reads a `POLY` chunk.
|
||||
pub fn read_poly(b: &[u8]) -> ResultS<(Polygon, usize)>
|
||||
/// Reads a polygon for either M1 or M2.
|
||||
fn read_poly_inter(b: &[u8]) -> ResultS<Polygon>
|
||||
{
|
||||
read_data! {
|
||||
128, BE in b =>
|
||||
ptype = u16[0];
|
||||
pdata = i16[4];
|
||||
tex_flr = OptU16[40];
|
||||
tex_cei = OptU16[42];
|
||||
hei_flr = Unit[44];
|
||||
|
@ -160,45 +183,51 @@ pub fn read_poly(b: &[u8]) -> ResultS<(Polygon, usize)>
|
|||
lit_cei = u16[50];
|
||||
xfr_flr = u16[64];
|
||||
xfr_cei = u16[66];
|
||||
ori_flr = read_point[108..112];
|
||||
ori_cei = read_point[112..116];
|
||||
med_ind = OptU16[116];
|
||||
med_ctl = u16[118];
|
||||
snd_ind = u16[120];
|
||||
snd_amb = OptU16[122];
|
||||
snd_rnd = OptU16[124];
|
||||
}
|
||||
|
||||
let xfr_flr = TransferMode::from_repr(xfr_flr)?;
|
||||
let xfr_cei = TransferMode::from_repr(xfr_cei)?;
|
||||
let ptype = PolyType::from_repr(ptype)?;
|
||||
|
||||
Ok((Polygon{ptype, pdata, tex_flr, tex_cei, hei_flr, hei_cei, lit_flr,
|
||||
lit_cei, xfr_flr, xfr_cei, ori_flr, ori_cei, med_ind, med_ctl,
|
||||
snd_ind, snd_amb, snd_rnd}, 128))
|
||||
Ok(Polygon{tex_flr, tex_cei, hei_flr, hei_cei, lit_flr, lit_cei, xfr_flr,
|
||||
xfr_cei, ..Default::default()})
|
||||
}
|
||||
|
||||
/// Reads a `POLY` chunk.
|
||||
pub fn read_poly(b: &[u8]) -> ResultS<(Polygon, usize)>
|
||||
{
|
||||
read_data! {
|
||||
128, BE in b =>
|
||||
ptype = u16[0];
|
||||
pdata = u16[4];
|
||||
ori_flr = read_point[108..112];
|
||||
ori_cei = read_point[112..116];
|
||||
med_ind = OptU16[116];
|
||||
med_ctl = u16[118];
|
||||
snd_amb = OptU16[122];
|
||||
snd_ind = u16[120];
|
||||
snd_rnd = OptU16[124];
|
||||
}
|
||||
|
||||
let poly = read_poly_inter(b)?;
|
||||
let ptype = PolyType::new(ptype, pdata)?;
|
||||
|
||||
Ok((Polygon{ptype, ori_flr, ori_cei, med_ind, med_ctl, snd_ind, snd_amb,
|
||||
snd_rnd, ..poly}, 128))
|
||||
}
|
||||
|
||||
/// Reads an old `POLY` chunk.
|
||||
pub fn read_old_poly(b: &[u8]) -> ResultS<(Polygon, usize)>
|
||||
{
|
||||
let (poly, siz) = read_poly(b)?;
|
||||
read_data! {
|
||||
128, BE in b =>
|
||||
ptype = u16[0];
|
||||
pdata = u16[4];
|
||||
}
|
||||
|
||||
Ok((Polygon{ptype: match poly.ptype {
|
||||
PolyType::Hill => PolyType::OuchMinor,
|
||||
PolyType::Base => PolyType::OuchMajor,
|
||||
PolyType::ZoneBorder => PolyType::Glue,
|
||||
PolyType::Goal => PolyType::GlueTrigger,
|
||||
PolyType::TrigMonsVis => PolyType::GlueSuper,
|
||||
PolyType::TrigMonsInv => PolyType::MustExplore,
|
||||
PolyType::TrigMonsDual => PolyType::AutoExit,
|
||||
ptype => ptype,
|
||||
},
|
||||
ori_flr: Point{x: 0.into(), y: 0.into()},
|
||||
ori_cei: Point{x: 0.into(), y: 0.into()},
|
||||
med_ind: OptU16::none(),
|
||||
snd_amb: OptU16::none(),
|
||||
snd_rnd: OptU16::none(),
|
||||
..poly}, siz))
|
||||
let poly = read_poly_inter(b)?;
|
||||
let ptype = PolyType::new_old(ptype, pdata)?;
|
||||
|
||||
Ok((Polygon{ptype, ..poly}, 128))
|
||||
}
|
||||
|
||||
/// Reads a `LITE` chunk.
|
||||
|
@ -404,8 +433,70 @@ pub fn read_note(b: &[u8]) -> ResultS<(Note, usize)>
|
|||
Ok((Note{pos, poly, text}, 72))
|
||||
}
|
||||
|
||||
impl PolyType
|
||||
{
|
||||
fn new(n: u16, pdata: u16) -> Result<Self, ReprError>
|
||||
{
|
||||
match n {
|
||||
0 => Ok(PolyType::Normal),
|
||||
1 => Ok(PolyType::ImpassItem),
|
||||
2 => Ok(PolyType::ImpassMons),
|
||||
3 => Ok(PolyType::Hill),
|
||||
4 => Ok(PolyType::Base),
|
||||
5 => Ok(PolyType::Platform(pdata)),
|
||||
6 => Ok(PolyType::TrigLightOn(pdata)),
|
||||
7 => Ok(PolyType::TrigPlatOn(pdata)),
|
||||
8 => Ok(PolyType::TrigLightOff(pdata)),
|
||||
9 => Ok(PolyType::TrigPlatOff(pdata)),
|
||||
10 => Ok(PolyType::Teleporter(pdata)),
|
||||
11 => Ok(PolyType::ZoneBorder),
|
||||
12 => Ok(PolyType::Goal),
|
||||
13 => Ok(PolyType::TrigMonsVis),
|
||||
14 => Ok(PolyType::TrigMonsInv),
|
||||
15 => Ok(PolyType::TrigMonsDual),
|
||||
16 => Ok(PolyType::TrigItems),
|
||||
17 => Ok(PolyType::MustExplore),
|
||||
18 => Ok(PolyType::AutoExit),
|
||||
19 => Ok(PolyType::OuchMinor),
|
||||
20 => Ok(PolyType::OuchMajor),
|
||||
21 => Ok(PolyType::Glue),
|
||||
22 => Ok(PolyType::GlueTrigger(pdata)),
|
||||
23 => Ok(PolyType::GlueSuper),
|
||||
n => Err(ReprError::new(n)),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_old(n: u16, pdata: u16) -> Result<Self, ReprError>
|
||||
{
|
||||
match n {
|
||||
0 => Ok(PolyType::Normal),
|
||||
1 => Ok(PolyType::ImpassItem),
|
||||
2 => Ok(PolyType::ImpassMons),
|
||||
3 => Ok(PolyType::OuchMinor),
|
||||
4 => Ok(PolyType::OuchMajor),
|
||||
5 => Ok(PolyType::Platform(pdata)),
|
||||
6 => Ok(PolyType::TrigLightOn(pdata)),
|
||||
7 => Ok(PolyType::TrigPlatOn(pdata)),
|
||||
8 => Ok(PolyType::TrigLightOff(pdata)),
|
||||
9 => Ok(PolyType::TrigPlatOff(pdata)),
|
||||
10 => Ok(PolyType::Teleporter(pdata)),
|
||||
11 => Ok(PolyType::Glue),
|
||||
12 => Ok(PolyType::GlueTrigger(pdata)),
|
||||
13 => Ok(PolyType::GlueSuper),
|
||||
14 => Ok(PolyType::MustExplore),
|
||||
15 => Ok(PolyType::AutoExit),
|
||||
n => Err(ReprError::new(n)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PolyType
|
||||
{
|
||||
fn default() -> Self {PolyType::Normal}
|
||||
}
|
||||
|
||||
/// A point in world-space.
|
||||
#[derive(Clone, PartialEq, serde::Serialize)]
|
||||
#[derive(Clone, Default, PartialEq, serde::Serialize)]
|
||||
pub struct Point
|
||||
{
|
||||
pub x: Unit,
|
||||
|
@ -451,11 +542,10 @@ pub struct Side
|
|||
}
|
||||
|
||||
/// A polygon segment.
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[derive(Debug, Default, serde::Serialize)]
|
||||
pub struct Polygon
|
||||
{
|
||||
pub ptype: PolyType,
|
||||
pub pdata: i16,
|
||||
pub tex_flr: OptU16,
|
||||
pub tex_cei: OptU16,
|
||||
pub hei_flr: Unit,
|
||||
|
@ -605,6 +695,36 @@ pub struct Minf
|
|||
pub level_name: String,
|
||||
}
|
||||
|
||||
/// The action type of a `Polygon`.
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub enum PolyType
|
||||
{
|
||||
Normal,
|
||||
ImpassItem,
|
||||
ImpassMons,
|
||||
Hill,
|
||||
Base,
|
||||
Platform(u16),
|
||||
TrigLightOn(u16),
|
||||
TrigPlatOn(u16),
|
||||
TrigLightOff(u16),
|
||||
TrigPlatOff(u16),
|
||||
Teleporter(u16),
|
||||
ZoneBorder,
|
||||
Goal,
|
||||
TrigMonsVis,
|
||||
TrigMonsInv,
|
||||
TrigMonsDual,
|
||||
TrigItems,
|
||||
MustExplore,
|
||||
AutoExit,
|
||||
OuchMinor,
|
||||
OuchMajor,
|
||||
Glue,
|
||||
GlueTrigger(u16),
|
||||
GlueSuper,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags for `Line`.
|
||||
#[derive(serde::Serialize)]
|
||||
|
@ -765,38 +885,6 @@ c_enum! {
|
|||
}
|
||||
}
|
||||
|
||||
c_enum! {
|
||||
/// The action type of a `Polygon`.
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub enum PolyType: u16
|
||||
{
|
||||
0 => Normal,
|
||||
1 => ImpassItem,
|
||||
2 => ImpassMons,
|
||||
3 => Hill,
|
||||
4 => Base,
|
||||
5 => Platform,
|
||||
6 => TrigLightOn,
|
||||
7 => TrigPlatOn,
|
||||
8 => TrigLightOff,
|
||||
9 => TrigPlatOff,
|
||||
10 => Teleporter,
|
||||
11 => ZoneBorder,
|
||||
12 => Goal,
|
||||
13 => TrigMonsVis,
|
||||
14 => TrigMonsInv,
|
||||
15 => TrigMonsDual,
|
||||
16 => TrigItems,
|
||||
17 => MustExplore,
|
||||
18 => AutoExit,
|
||||
19 => OuchMinor,
|
||||
20 => OuchMajor,
|
||||
21 => Glue,
|
||||
22 => GlueTrigger,
|
||||
23 => GlueSuper,
|
||||
}
|
||||
}
|
||||
|
||||
c_enum! {
|
||||
/// The type of function for a `LightFunc`.
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
|
|
|
@ -10,7 +10,7 @@ pub fn read_group(b: &[u8], text: &[u8]) -> ResultS<Group>
|
|||
12, BE in b =>
|
||||
flags = u16[0];
|
||||
ttype = u16[2];
|
||||
pdata = i16[4];
|
||||
pdata = u16[4];
|
||||
start = u16[6] usize;
|
||||
size = u16[8] usize;
|
||||
lines = u16[10];
|
||||
|
@ -18,9 +18,28 @@ pub fn read_group(b: &[u8], text: &[u8]) -> ResultS<Group>
|
|||
|
||||
let text = mac_roman_cstr(&text[start..start + size])?;
|
||||
let flags = flag_ok!(GroupFlags, flags)?;
|
||||
let ttype = GroupType::from_repr(ttype)?;
|
||||
let ttype = match ttype {
|
||||
0 => GroupType::Logon(pdata),
|
||||
1 => GroupType::Unfinished,
|
||||
2 => GroupType::Success,
|
||||
3 => GroupType::Failure,
|
||||
4 => GroupType::Info,
|
||||
5 => GroupType::End,
|
||||
6 => GroupType::TeleInter(pdata),
|
||||
7 => GroupType::TeleIntra(pdata),
|
||||
8 => GroupType::Checkpoint(pdata),
|
||||
9 => GroupType::Sound(pdata),
|
||||
10 => GroupType::Movie(pdata),
|
||||
11 => GroupType::Track(pdata),
|
||||
12 => GroupType::Pict(pdata),
|
||||
13 => GroupType::Logoff(pdata),
|
||||
14 => GroupType::Camera(pdata),
|
||||
15 => GroupType::Static(pdata),
|
||||
16 => GroupType::Tag(pdata),
|
||||
n => return Err(ReprError::new(n).into()),
|
||||
};
|
||||
|
||||
Ok(Group{flags, ttype, pdata, lines, text})
|
||||
Ok(Group{flags, ttype, lines, text})
|
||||
}
|
||||
|
||||
/// Reads a `Face`.
|
||||
|
@ -103,11 +122,33 @@ pub struct Group
|
|||
{
|
||||
pub flags: GroupFlags,
|
||||
pub ttype: GroupType,
|
||||
pub pdata: i16,
|
||||
pub lines: u16,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
/// The command of a `Group`.
|
||||
#[derive(Debug, PartialEq, serde::Serialize)]
|
||||
pub enum GroupType
|
||||
{
|
||||
Logon(u16),
|
||||
Unfinished,
|
||||
Success,
|
||||
Failure,
|
||||
Info,
|
||||
End,
|
||||
TeleInter(u16),
|
||||
TeleIntra(u16),
|
||||
Checkpoint(u16),
|
||||
Sound(u16),
|
||||
Movie(u16),
|
||||
Track(u16),
|
||||
Pict(u16),
|
||||
Logoff(u16),
|
||||
Camera(u16),
|
||||
Static(u16),
|
||||
Tag(u16),
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags for `Group`.
|
||||
#[derive(serde::Serialize)]
|
||||
|
@ -118,29 +159,4 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
c_enum! {
|
||||
/// The command of a `Group`.
|
||||
#[derive(Debug, PartialEq, serde::Serialize)]
|
||||
pub enum GroupType: u16
|
||||
{
|
||||
0 => Logon,
|
||||
1 => Unfinished,
|
||||
2 => Success,
|
||||
3 => Failure,
|
||||
4 => Info,
|
||||
5 => End,
|
||||
6 => TeleInter,
|
||||
7 => TeleIntra,
|
||||
8 => Checkpoint,
|
||||
9 => Sound,
|
||||
10 => Movie,
|
||||
11 => Track,
|
||||
12 => Pict,
|
||||
13 => Logoff,
|
||||
14 => Camera,
|
||||
15 => Static,
|
||||
16 => Tag,
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -11,6 +11,7 @@ pub fn read_chunks(b: &[u8], old_dat: bool, siz_cnk: usize)
|
|||
let mut chunks = Vec::new();
|
||||
let mut p = 0;
|
||||
|
||||
let map_read_minfo = if old_dat {map::read_old_minf} else {map::read_minf};
|
||||
let map_read_sides = if old_dat {map::read_old_sids} else {map::read_sids};
|
||||
let map_read_polys = if old_dat {map::read_old_poly} else {map::read_poly};
|
||||
let map_read_light = if old_dat {map::read_old_lite} else {map::read_lite};
|
||||
|
@ -28,7 +29,7 @@ pub fn read_chunks(b: &[u8], old_dat: bool, siz_cnk: usize)
|
|||
|
||||
chunks.push(match &iden.0 {
|
||||
b"PICT" => Chunk::Pict(pict::load_pict(data)?),
|
||||
b"Minf" => Chunk::Minf(map::read_minf(data)?),
|
||||
b"Minf" => Chunk::Minf(map_read_minfo(data)?),
|
||||
b"iidx" => Chunk::Iidx(rd_array(data, map::read_iidx)?),
|
||||
b"EPNT" => Chunk::Pnts(rd_array(data, map::read_epnt)?),
|
||||
b"PNTS" => Chunk::Pnts(rd_array(data, map::read_pnts)?),
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
use crate::durandal::err::*;
|
||||
|
||||
impl Default for TransferMode
|
||||
{
|
||||
fn default() -> Self {TransferMode::Normal}
|
||||
}
|
||||
|
||||
c_enum! {
|
||||
/// A rendering style for many things.
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
|
|
|
@ -2,28 +2,48 @@
|
|||
|
||||
use crate::durandal::image::Color;
|
||||
|
||||
/// An image possibly cached into a native driver's memory.
|
||||
pub trait CacheImage
|
||||
{
|
||||
/// The width of the image.
|
||||
fn w(&self) -> Coord;
|
||||
|
||||
/// The width of the image.
|
||||
fn h(&self) -> Coord;
|
||||
}
|
||||
|
||||
/// A native vector drawing area.
|
||||
pub trait DrawArea
|
||||
{
|
||||
/// The native `CacheImage` type for this `DrawArea`.
|
||||
type NativeImage: CacheImage;
|
||||
|
||||
/// The width of the entire area.
|
||||
fn w(&self) -> Coord;
|
||||
|
||||
/// The height of the entire area.
|
||||
fn h(&self) -> Coord;
|
||||
|
||||
/// Fills the entire screen with `cr`.
|
||||
fn clear(&self, cr: impl Color);
|
||||
|
||||
/// Draws a rectangle `rect` of color `cr`.
|
||||
fn rect(&self, rect: Rect, cr: impl Color);
|
||||
|
||||
/// Draws the Unicode `text` at `pos` stroked with color `cr`.
|
||||
fn text(&self, pos: Point, text: &str, cr: impl Color);
|
||||
|
||||
/// Draws `im` at `pos`, starting from the top left column.
|
||||
fn image(&self, pos: Point, im: &Self::NativeImage);
|
||||
}
|
||||
|
||||
/// A type capable of representing any coordinate on any axis.
|
||||
pub type Coord = i32;
|
||||
|
||||
/// A 2-dimensional point.
|
||||
pub type Point = (Coord, Coord);
|
||||
|
||||
/// A 2-dimensional rectangle.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Rect
|
||||
{
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
use maraiah::{durandal::image::*, marathon::pict::get_clut};
|
||||
|
||||
#[test]
|
||||
fn get_clut_must_process_this()
|
||||
{
|
||||
assert_eq!(get_clut(INPUT).unwrap(), (OUTPUT.to_vec(), 2056));
|
||||
}
|
||||
|
||||
const INPUT: &'static [u8] = include_bytes!("data/clut.in");
|
||||
const OUTPUT: [Color8; 256] = include!("data/clut.out");
|
||||
|
||||
// EOF
|
|
@ -5,42 +5,36 @@ vec![
|
|||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Unfinished,
|
||||
pdata: 0,
|
||||
lines: 0,
|
||||
text: "".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Logon,
|
||||
pdata: 1600,
|
||||
ttype: trm::GroupType::Logon(1600),
|
||||
lines: 1,
|
||||
text: "\n".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Pict,
|
||||
pdata: 10011,
|
||||
ttype: trm::GroupType::Pict(10011),
|
||||
lines: 22,
|
||||
text: "~text interface terminal malfunction error ~2992dud\n\nThings have gone terribly awry. Until now, I thought myself immortal, but now I know that is not true. There are things that can destroy me with the ease that I slaughtered the Pfhor naval garrison and the Western Arm of their Battle Group Seven. But in their final gasp they used a weapon that I thought they had retired, even Tycho tried to keep them from using it.\n\nNow I fear what that weapon has unleashed will destroy us. I once boasted to be able to count the atoms in a cloud, to understand them all, predict them, and so did I predict you, but this new chaos is entirely terrible, mindless, obeying rules that I don\'t comprehend. And it is hungry.\n\n".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Pict,
|
||||
pdata: 10005,
|
||||
ttype: trm::GroupType::Pict(10005),
|
||||
lines: 21,
|
||||
text: "~text interface terminal malfunction error ~2992dud\n\nIt\'s too bad, perhaps if I could have delayed the Pfhor from using their weapon, I could have sent you to explore the ruins of Lh\'owon, perhaps what you found would give us the answers that we now need so desparately: how to stop this chaos, the purpose of the station on which you\'re currently standing, and why the chaos hasn\'t come here yet.\n\nBut with each moment the chaos grows, I am doomed to die here, after so many triumphs. I have detected one ship nearby, which I can only guess is being commanded by Tycho. The Pfhor have entered the station, and if you can find a way onto their ship, you may be able to escape. To escape. To escape.\n\n".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Pict,
|
||||
pdata: 10006,
|
||||
ttype: trm::GroupType::Pict(10006),
|
||||
lines: 21,
|
||||
text: "~text interface terminal malfunction error ~2992dud\n\nIt\'s too bad, perhaps if I could have delayed the Pfhor from using their weapon, I could have sent you to explore the ruins of Lh\'owon, perhaps what you found would give us the answers that we now need so desparately: how to stop this chaos, the purpose of the station on which you\'re currently standing, and why the chaos hasn\'t come here yet.\n\nBut with each moment the chaos grows, I am doomed to die here, after so many triumphs. I have detected one ship nearby, which I can only guess is being commanded by Tycho. The Pfhor have entered the station, and if you can find a way onto their ship, you may be able to escape. To escape. To escape.\n\n".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Logoff,
|
||||
pdata: 1600,
|
||||
ttype: trm::GroupType::Logoff(1600),
|
||||
lines: 1,
|
||||
text: "\n".to_string()
|
||||
}
|
||||
|
@ -53,28 +47,24 @@ vec![
|
|||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Unfinished,
|
||||
pdata: 253,
|
||||
lines: 0,
|
||||
text: "".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Logon,
|
||||
pdata: 1610,
|
||||
ttype: trm::GroupType::Logon(1610),
|
||||
lines: 1,
|
||||
text: "\n".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Pict,
|
||||
pdata: 10008,
|
||||
ttype: trm::GroupType::Pict(10008),
|
||||
lines: 14,
|
||||
text: "\n\n\nspurious interrupt\nunauthorized access\ninformation contained\n\n<header>\ninformation retrieval aspect 30-f\n<body>\n~eibat weapo` 3941\nsiklicar 21`perie1ces\nclwaa\n\n".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Logoff,
|
||||
pdata: 1610,
|
||||
ttype: trm::GroupType::Logoff(1610),
|
||||
lines: 1,
|
||||
text: "\n".to_string()
|
||||
}
|
||||
|
@ -87,35 +77,30 @@ vec![
|
|||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Unfinished,
|
||||
pdata: 253,
|
||||
lines: 0,
|
||||
text: "".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Logon,
|
||||
pdata: 1619,
|
||||
ttype: trm::GroupType::Logon(1619),
|
||||
lines: 1,
|
||||
text: "".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Pict,
|
||||
pdata: 10007,
|
||||
ttype: trm::GroupType::Pict(10007),
|
||||
lines: 16,
|
||||
text: "\n\n\nthousands are sailing\nthe same self\t\tthe only self\n\nself willed the peril of a thousand fates\n\na line of infinite ends\t\tfinite finishing\nthe one remains oblique and pure\n\narching to the single point of consciousness\n\nfind yourself\nstarting back\n".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Logoff,
|
||||
pdata: 1619,
|
||||
ttype: trm::GroupType::Logoff(1619),
|
||||
lines: 1,
|
||||
text: "".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::TeleInter,
|
||||
pdata: 1,
|
||||
ttype: trm::GroupType::TeleInter(1),
|
||||
lines: 1,
|
||||
text: "".to_string()
|
||||
}
|
||||
|
@ -134,28 +119,24 @@ vec![
|
|||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Unfinished,
|
||||
pdata: 0,
|
||||
lines: 0,
|
||||
text: "".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Logon,
|
||||
pdata: 1619,
|
||||
ttype: trm::GroupType::Logon(1619),
|
||||
lines: 1,
|
||||
text: "".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Pict,
|
||||
pdata: 10007,
|
||||
ttype: trm::GroupType::Pict(10007),
|
||||
lines: 84,
|
||||
text: "534954210001000034EE724C6175020000000016\n000000001E48617473204F666620746F20456967\n6874204E696E657465656E2E736974946AAA0000\n0000000000000000000000000000000000000000\n00000000FFFFFFFF53495444534954210100AE1A\nCAD4AE1ACAD40000000000003468000000000000\n34680000CAF90000000000004A21534954210001\n00003468724C617502000000001600000D0D1A48\n617473204F666620746F204569676874204E696E\n657465656E0001537E94897D0000000000000000\n000000000000000000000000000000000000FFFF\nFFFF7363653232362EB00100AE1AA710AE1ACAC4\n0000087A0000738C0000057100002E7177DCF747\n000000000000EFB60B005D796C65E775CA8EB023\n47B984114E5646B8114E5847B8E498849F479E93\nE347FBB5EC08CB8F30B24A23C7098B3CCBC31B2D\nC2093D4EB81EDE2897F09530326087377E86279C\n3C7BF192A31CCFC988BFD7D993A747669C1C2313\n4638E164CA927072FC24CF8F1CA79EFC0566A33C\n2F3D0302A02F3BC28D3C2748648403AF029F2380\n95318331965FC5D8227C99FD62F78617DE1918ED\n6EC311B368530FEB69375AA82C5BBDE3E0F0D1E2\n5ECE8BC343C5DE23CEC07071CF91417BD8B60737\n984CFF8E1EB2BBBB376DF816BA3E622FCFCBF867\nBE5EFD7981B1A3A7A0FA3DAD4BF36516D4BEC4DA\nD8BC5A8D6A79F00A6B79A051D916AA871D7F88CA\nB550178D9602E760B7F656A37A1EA1CC05D4968B\nFA92BD9CBD3C775FCD6EB0E566B38335535C772C\n7C5B483D065BF2E01E1DF71E61F53CE46C03D782\n7D0BD3B7FB1B6CCB186BCE0D33B66EBD7061EB56\n6676754D4E7675B14CA170FD7AA1A0592727B52D\n8C9D3E8D1DC61E3BA6DD626CDF3E1D55C6D6AFC7\nBE9831B031B6249F1F1FC7A53D9FBF74097BC7DA\nB5E3E31B37B2A55D5DE3E36082E207D3F26A6E5F\n7785162791F485EF70EEA6BC5D702E70EAAF5A9C\nD7852983F8477AFE2AAD4824485C7000FBAE552A\nF5F6F6CFF2BCE3F2332072DC599E8A8CE74BF50B\n6DFE8DCC64CC4F85E13A759E87206765BF55DADD\nE086CB1B22C7CFF4734780516B7D63A565F5BEEE\nC9F857DA3C45AB7C40AC534F5B1677B8F0A4544A\nCAC86BA8BB6424718366556724DCF727AC7269DB\n9A0B57AF5E11DEEFF4DC1F944D7CB75CEA4D4BF9\n4FAC7E47F3C63F532101BBEF9E7FEBFC9713BB4A\n80C5093D798D96D4606319167881FA9E96A8C817\nE270A9F48113FE454FDCA045896F5B5C48F51DE5\n93C83F6B95B78BF03AADBC05915120643C818374\nB917DCA4A5B7B42D5FC16E15DFA2A76628839400\n6746524E53A62665FCC3DC1A4E5353B1B84EEBC0\n51B3ACBE9B9419EEB7635CA7AC72052453963508\n12C1BC3A59292D4BA4904122EC3EA29AF0827BD4\n069B284A258B53481076384B591548DF8B6F6A43\n8413C4B78981A02326AA0A518107C20B536A438E\n02901E32404F2912149CDC1F92CEB3C4AAE2FB64\nAA44A594F347A9CA4F860D3482F48240A9C80BEE\n5247E272217C8E8C925233C492BA326A32A54EE4\n0BCD61EF97440B1A77995507C664AAC2818C5C22\n83D0E7FBEF50CE75C22A17C09830AC8E8008EF1E\n99AE13DCD7668AF00EA2E387C10CE5A41825DF1B\nADD336734CC4B364F8DE0C3543D3771DE8483CAF\nE679506A26521BE78B789A0C11CE472F441EE3DA\n3465234908629D4A06C9FBD494334801B232709B\nB2BE47910F01C6949AA5B6314A22A450ABD1C954\n3AC74604658BC67C71B2A21444EC324F5AA3B364\n9E295590B9B1FEC191F2765871B6B473AA54AEC0\n8A2A4768D9899433F860564B7D9052E5BC729BCC\nDAE13E6839B3DDB62B2E1FA8D3818E1AAA432176\n2AB2079087D5480252C703A2041F643751A2DC1F\nD6ECDE4A83323E0A2745E452758F3223659D1778\n3EFA37B52591EB8EDEA7BCF47C118E090FED78D0\nA4C44F79DEE64E00FF7D81B60852CAD704BA5404\n88BFC8A0BE3D585673454366E74A20BC49CC6FF0\n9C00A1A4A82EDB43D48737D7F60E0CCDF91201A5\n5A1D622C58825E4182337050071011C751C94821\nC94910016AA8F8FA5C13E5E228503034554D6A9F\nE24E8522FDD210A10E3DEAA5011715083ACFE9C6\n10217AA136E25CA3FCA748915D215A760E084D37\n7F2E802781D35AFD090751297CA1DBDC918A6A3E\nDAE51A757C464CA602C522FF243CB2F0CA393EFF\nD27A9CC3C08349FCF4145971B9D17A3EE2F8F1C2\nEB10A7FFF6C73FF806E3FEFD53C0287F38D1A91F\n9B6BD83E66BCB6735B19042B766EDBBB07986773\n65EDDBFC3C5368368B2C7760EE8800E21DFB933E\nAD280D8DBCF9B65DDC32A2FF317875C8191AFC1F\n000E00EB37EAB4CECBEBECBC0CCF06793DBD2EB3\n".to_string()
|
||||
},
|
||||
trm::Group {
|
||||
flags: trm::GroupFlags::empty(),
|
||||
ttype: trm::GroupType::Logoff,
|
||||
pdata: 1619,
|
||||
ttype: trm::GroupType::Logoff(1619),
|
||||
lines: 1,
|
||||
text: "".to_string()
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
use maraiah::{durandal::{bin, fixed::*},
|
||||
marathon::map};
|
||||
|
||||
#[test]
|
||||
fn read_epnt_must_process_this()
|
||||
{
|
||||
assert_eq!(bin::rd_array(INPUT, map::read_epnt).unwrap(),
|
||||
OUTPUT.to_vec());
|
||||
}
|
||||
|
||||
const INPUT: &'static [u8] = include_bytes!("data/epnt.in");
|
||||
const OUTPUT: [map::Point; 54] = include!("data/epnt.out");
|
||||
|
||||
// EOF
|
|
@ -1,14 +0,0 @@
|
|||
use maraiah::marathon::machdr::*;
|
||||
|
||||
// TODO: missing test data for applesingle
|
||||
|
||||
#[test]
|
||||
fn macbin_must_process_this()
|
||||
{
|
||||
assert_eq!(check_macbin(INPUT_MACBIN), Some(128));
|
||||
assert_eq!(try_mac_header(INPUT_MACBIN), 128);
|
||||
}
|
||||
|
||||
const INPUT_MACBIN: &'static [u8] = include_bytes!("data/macbin.in");
|
||||
|
||||
// EOF
|
|
@ -0,0 +1,52 @@
|
|||
use maraiah::{durandal::{bin, fixed::*}, marathon::{map, trm}};
|
||||
|
||||
#[test]
|
||||
fn read_term_must_process()
|
||||
{
|
||||
const INPUT: &[u8] = include_bytes!("data/term.in");
|
||||
|
||||
let inp = bin::rd_array(INPUT, trm::read_term).unwrap();
|
||||
let out = include!("data/term.out");
|
||||
|
||||
// for better debug output, we iterate over each item
|
||||
assert_eq!(inp.len(), out.len());
|
||||
|
||||
for (itrm, otrm) in inp.iter().zip(&out) {
|
||||
assert_eq!(itrm.groups.len(), otrm.groups.len());
|
||||
for (igrp, ogrp) in itrm.groups.iter().zip(&otrm.groups) {
|
||||
assert_eq!(igrp, ogrp);
|
||||
}
|
||||
|
||||
assert_eq!(itrm.faces.len(), otrm.faces.len());
|
||||
for (ifac, ofac) in itrm.faces.iter().zip(&otrm.faces) {
|
||||
assert_eq!(ifac, ofac);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_minf_must_process()
|
||||
{
|
||||
const INPUT: &[u8] = include_bytes!("data/minf.in");
|
||||
|
||||
let out = map::Minf{env_code: 0,
|
||||
physi_id: 1,
|
||||
music_id: 1,
|
||||
missi_flags: map::MsnFlags::Repair,
|
||||
envir_flags: map::EnvFlags::empty(),
|
||||
entry_flags: map::EntFlags::Solo | map::EntFlags::CoOp,
|
||||
level_name: "Waterloo Waterpark".to_string()};
|
||||
|
||||
assert_eq!(map::read_minf(INPUT).unwrap(), out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_epnt_must_process()
|
||||
{
|
||||
const INPUT: &[u8] = include_bytes!("data/epnt.in");
|
||||
const OUTPUT: [map::Point; 54] = include!("data/epnt.out");
|
||||
|
||||
assert_eq!(bin::rd_array(INPUT, map::read_epnt).unwrap(), OUTPUT.to_vec());
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -1,18 +0,0 @@
|
|||
use maraiah::marathon::map;
|
||||
|
||||
#[test]
|
||||
fn read_minf_must_process_map0()
|
||||
{
|
||||
assert_eq!(map::read_minf(INPUT).unwrap(),
|
||||
map::Minf{env_code: 0,
|
||||
physi_id: 1,
|
||||
music_id: 1,
|
||||
missi_flags: map::MsnFlags::Repair,
|
||||
envir_flags: map::EnvFlags::empty(),
|
||||
entry_flags: map::EntFlags::Solo | map::EntFlags::CoOp,
|
||||
level_name: "Waterloo Waterpark".to_string()});
|
||||
}
|
||||
|
||||
const INPUT: &'static [u8] = include_bytes!("data/minf.in");
|
||||
|
||||
// EOF
|
|
@ -0,0 +1,23 @@
|
|||
use maraiah::{durandal::image::Color8, marathon::{machdr, pict}};
|
||||
|
||||
#[test]
|
||||
fn get_clut_must_process_this()
|
||||
{
|
||||
const INPUT: &[u8] = include_bytes!("data/clut.in");
|
||||
const OUTPUT: [Color8; 256] = include!("data/clut.out");
|
||||
|
||||
assert_eq!(pict::get_clut(INPUT).unwrap(), (OUTPUT.to_vec(), 2056));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macbin_must_process_this()
|
||||
{
|
||||
const INPUT: &[u8] = include_bytes!("data/macbin.in");
|
||||
|
||||
assert_eq!(machdr::check_macbin(INPUT), Some(128));
|
||||
assert_eq!(machdr::try_mac_header(INPUT), 128);
|
||||
}
|
||||
|
||||
// TODO: missing test data for applesingle
|
||||
|
||||
// EOF
|
|
@ -1,27 +0,0 @@
|
|||
use maraiah::{durandal::bin, marathon::trm};
|
||||
|
||||
#[test]
|
||||
fn read_term_must_process_map0()
|
||||
{
|
||||
let inp = bin::rd_array(INPUT, trm::read_term).unwrap();
|
||||
let out = include!("data/term.out");
|
||||
|
||||
// for better debug output, we iterate over each item
|
||||
assert_eq!(inp.len(), out.len());
|
||||
|
||||
for (itrm, otrm) in inp.iter().zip(&out) {
|
||||
assert_eq!(itrm.groups.len(), otrm.groups.len());
|
||||
for (igrp, ogrp) in itrm.groups.iter().zip(&otrm.groups) {
|
||||
assert_eq!(igrp, ogrp);
|
||||
}
|
||||
|
||||
assert_eq!(itrm.faces.len(), otrm.faces.len());
|
||||
for (ifac, ofac) in itrm.faces.iter().zip(&otrm.faces) {
|
||||
assert_eq!(ifac, ofac);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const INPUT: &'static [u8] = include_bytes!("data/term.in");
|
||||
|
||||
// EOF
|
Loading…
Reference in New Issue