diff --git a/MarathonData.md b/MarathonData.md index 49ad09b..12110d9 100644 --- a/MarathonData.md +++ b/MarathonData.md @@ -431,7 +431,7 @@ The type "`fixed`" refers to a 32-bit fixed point number with the format 15.16s sign.) The type "`angle`" refers to a 16-bit fixed point number with the format 0.9s. -This is used for angles. +This is used for all angles. The type "`unit`" refers to a 16-bit fixed point number with the format 5.10s. This is used for all world coordinates. diff --git a/src/durandal/fixed.rs b/src/durandal/fixed.rs new file mode 100644 index 0000000..8f29a8f --- /dev/null +++ b/src/durandal/fixed.rs @@ -0,0 +1,200 @@ +//! Fixed point numbers. + +use serde::Serialize; +use std::{fmt::{self, Write}, + ops}; + +macro_rules! define_fixed_type { + ( + $Type:ident : $IT:ident, $UT:ident, $LT:ident, $FracBits:expr + ) => { + #[derive(Serialize)] + pub struct $Type($IT); + + impl $Type + { + const FRACBITS: $UT = $FracBits; + const FRACMASK: $UT = (1 << Self::FRACBITS) - 1; + 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 + } + + pub fn from_bits(bits: $UT) -> Self + { + $Type(bits as $IT) + } + + pub fn integ(&self) -> $LT + { + (self.0 >> Self::FRACBITS) as $LT + } + + pub fn fract(&self) -> u16 + { + (self.0 as $UT & Self::FRACMASK) as u16 + } + + pub fn mul_i(&self, n: $LT) -> Self + { + $Type(self.0 * $IT::from(n)) + } + + pub fn div_i(&self, n: $LT) -> Self + { + $Type(self.0 / $IT::from(n)) + } + + #[inline] + fn fx_div(x: $IT, y: $IT) -> $IT + { + (i64::from(x) * i64::from(Self::ONE) / i64::from(y)) as $IT + } + + #[inline] + fn fx_mul(x: $IT, y: $IT) -> $IT + { + (i64::from(x) * i64::from(y) / i64::from(Self::ONE)) as $IT + } + } + + impl From<$LT> for $Type + { + fn from(n: $LT) -> Self + { + $Type($IT::from(n) << Self::FRACBITS) + } + } + + impl ops::Add for $Type + { + type Output = Self; + + fn add(self, o: Self) -> Self + { + $Type(self.0 + o.0) + } + } + + impl ops::Sub for $Type + { + type Output = Self; + + fn sub(self, o: Self) -> Self + { + $Type(self.0 - o.0) + } + } + + impl ops::Mul for $Type + { + type Output = Self; + + fn mul(self, o: Self) -> Self + { + $Type(Self::fx_mul(self.0, o.0)) + } + } + + impl ops::Div for $Type + { + type Output = Self; + + fn div(self, o: Self) -> Self + { + $Type(Self::fx_div(self.0, o.0)) + } + } + + impl ops::Neg for $Type + { + type Output = Self; + + fn neg(self) -> Self + { + $Type(-self.0) + } + } + + impl ops::Not for $Type + { + type Output = Self; + + fn not(self) -> Self + { + $Type(!self.0) + } + } + + impl fmt::Display for $Type + { + 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)?; + + let mut k = self.to_bits(); + for _ in 0..prec { + k &= Self::FRACMASK; + k *= 10; + let d = k >> Self::FRACBITS; + let d = d % 10; + f.write_char((d as u8 + b'0') as char)?; + } + + Ok(()) + } + } + + impl fmt::Debug for $Type + { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result + { + fmt::Display::fmt(self, f) + } + } + }; +} + +define_fixed_type!(Fixed: i32, u32, i16, 16); +define_fixed_type!(Unit: i16, u16, i8, 10); +define_fixed_type!(Angle: i16, u16, i8, 9); + +#[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); +} + +#[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"); +} + +// EOF diff --git a/src/durandal/fx32.rs b/src/durandal/fx32.rs deleted file mode 100644 index 40ed70d..0000000 --- a/src/durandal/fx32.rs +++ /dev/null @@ -1,176 +0,0 @@ -use serde::Serialize; -use std::{fmt::{self, Write}, - ops}; - -#[derive(Serialize)] -pub struct Fx32(i32); - -impl Fx32 -{ - const FRACBITS: u32 = 16; - const FRACMASK: u32 = 0xFFFF; - const ONE: i32 = 1 << Fx32::FRACBITS; - - pub fn to_bits(&self) -> u32 - { - self.0 as u32 - } - - pub fn set_bits(&mut self, bits: u32) - { - self.0 = bits as i32 - } - - pub fn from_bits(bits: u32) -> Fx32 - { - Fx32(bits as i32) - } - - pub fn integ(&self) -> i16 - { - (self.0 >> Fx32::FRACBITS) as i16 - } - - pub fn fract(&self) -> u16 - { - (self.0 as u32 & Fx32::FRACMASK) as u16 - } - - pub fn mul_i(&self, n: i32) -> Fx32 - { - Fx32(self.0 * n) - } - - pub fn div_i(&self, n: i32) -> Fx32 - { - Fx32(self.0 / n) - } -} - -impl From for Fx32 -{ - fn from(n: i32) -> Fx32 - { - Fx32(n << Fx32::FRACBITS) - } -} - -impl ops::Add for Fx32 -{ - type Output = Fx32; - - fn add(self, o: Fx32) -> Fx32 - { - Fx32(self.0 + o.0) - } -} - -impl ops::Sub for Fx32 -{ - type Output = Fx32; - - fn sub(self, o: Fx32) -> Fx32 - { - Fx32(self.0 - o.0) - } -} - -impl ops::Mul for Fx32 -{ - type Output = Fx32; - - fn mul(self, o: Fx32) -> Fx32 - { - Fx32((i64::from(self.0) * i64::from(o.0) / i64::from(Fx32::ONE)) as i32) - } -} - -impl ops::Div for Fx32 -{ - type Output = Fx32; - - fn div(self, o: Fx32) -> Fx32 - { - Fx32((i64::from(self.0) * i64::from(Fx32::ONE) / i64::from(o.0)) as i32) - } -} - -impl ops::Neg for Fx32 -{ - type Output = Fx32; - - fn neg(self) -> Fx32 - { - Fx32(-self.0) - } -} - -impl ops::Not for Fx32 -{ - type Output = Fx32; - - fn not(self) -> Fx32 - { - Fx32(!self.0) - } -} - -impl fmt::Display for Fx32 -{ - 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)?; - - let mut k = self.to_bits(); - for _ in 0..prec { - k &= Fx32::FRACMASK; - k *= 10; - let d = k >> Fx32::FRACBITS; - let d = d % 10; - f.write_char((d as u8 + b'0') as char)?; - } - - Ok(()) - } -} - -impl fmt::Debug for Fx32 -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result - { - fmt::Display::fmt(self, f) - } -} - -#[test] -fn fx32_basic_ops() -{ - let seven_div_2 = 3 << Fx32::FRACBITS | Fx32::FRACMASK / 2 + 1; - assert_eq!((Fx32::from(1) + 1.into()).to_bits(), 2 << Fx32::FRACBITS); - assert_eq!((Fx32::from(2) - 1.into()).to_bits(), 1 << Fx32::FRACBITS); - assert_eq!((Fx32::from(6) * 2.into()).to_bits(), 12 << Fx32::FRACBITS); - assert_eq!((Fx32::from(6).mul_i(2)).to_bits(), 12 << Fx32::FRACBITS); - assert_eq!((Fx32::from(7) / 2.into()).to_bits(), seven_div_2); - assert_eq!((Fx32::from(7).div_i(2)).to_bits(), seven_div_2); -} - -#[test] -#[should_panic] -#[allow(unused_must_use)] -fn fx32_overflow() -{ - Fx32::from(32767) + 1.into(); -} - -#[test] -fn fx32_printing() -{ - assert_eq!(format!("{}", Fx32::from(6)), "6.0"); - assert_eq!(format!("{}", Fx32::from(7).div_i(2)), "3.5"); - assert_eq!(format!("{:7.7}", Fx32::from_bits(0xDEAD_BEEF)), - " -8531.7458343"); -} - -// EOF diff --git a/src/durandal/mod.rs b/src/durandal/mod.rs index c12ccfc..5093d20 100644 --- a/src/durandal/mod.rs +++ b/src/durandal/mod.rs @@ -9,7 +9,7 @@ pub mod bin; pub mod chunk; pub mod crc; pub mod file; -pub mod fx32; +pub mod fixed; pub mod image; pub mod text; diff --git a/src/marathon/map.rs b/src/marathon/map.rs index aa87676..3ccb78d 100644 --- a/src/marathon/map.rs +++ b/src/marathon/map.rs @@ -1,4 +1,4 @@ -use crate::{durandal::{bin::*, chunk::*, err::*, fx32::*, +use crate::{durandal::{bin::*, chunk::*, err::*, fixed::*, text::mac_roman_conv}, marathon::xfer::TransferMode}; use bitflags::bitflags; @@ -11,8 +11,10 @@ impl Chunked for Point fn read(b: &[u8]) -> ResultS { - let x = c_i16b(b, 0)?; - let y = c_i16b(b, 2)?; + let x = c_u16b(b, 0)?; + let y = c_u16b(b, 2)?; + let x = Unit::from_bits(x); + let y = Unit::from_bits(y); Ok(Point{x, y}) } } @@ -24,11 +26,13 @@ impl Chunked for Endpoint fn read(b: &[u8]) -> ResultS { let flags = c_u16b(b, 0)?; - let adj_hi = c_i16b(b, 2)?; - let adj_lo = c_i16b(b, 4)?; + let adj_hi = c_u16b(b, 2)?; + let adj_lo = c_u16b(b, 4)?; let pos = Point::read(c_data(b, 6..10)?)?; let support = c_u16b(b, 14)?; let flags = ok!(EndpFlags::from_bits(flags), "bad EndpFlags")?; + let adj_hi = Unit::from_bits(adj_hi); + let adj_lo = Unit::from_bits(adj_lo); Ok(Endpoint{flags, adj_hi, adj_lo, pos, support}) } } @@ -42,14 +46,17 @@ impl Chunked for Line let epnt_f = c_u16b(b, 0)?; let epnt_b = c_u16b(b, 2)?; let flags = c_u16b(b, 4)?; - let length = c_i16b(b, 6)?; - let adj_hi = c_i16b(b, 8)?; - let adj_lo = c_i16b(b, 10)?; + let length = c_u16b(b, 6)?; + let adj_hi = c_u16b(b, 8)?; + let adj_lo = c_u16b(b, 10)?; let side_f = c_u16b(b, 12)?; let side_b = c_u16b(b, 14)?; let poly_f = c_u16b(b, 16)?; let poly_b = c_u16b(b, 18)?; let flags = ok!(LineFlags::from_bits(flags), "bad LineFlags")?; + let length = Unit::from_bits(length); + let adj_hi = Unit::from_bits(adj_hi); + let adj_lo = Unit::from_bits(adj_lo); Ok(Line{flags, length, adj_hi, adj_lo, epnt_f, epnt_b, side_f, side_b, poly_f, poly_b}) } @@ -91,7 +98,7 @@ impl Chunked for Side let xfer_pri = TransferMode::from_repr(xfer_pri)?; let xfer_sec = TransferMode::from_repr(xfer_sec)?; let xfer_tra = TransferMode::from_repr(xfer_tra)?; - let shade = Fx32::from_bits(shade); + let shade = Fixed::from_bits(shade); Ok(Side{stype, flags, tex_pri, tex_sec, tex_tra, ex_tleft, ex_trigh, ex_bleft, ex_brigh, paneltyp, paneldat, xfer_pri, xfer_sec, xfer_tra, shade}) @@ -117,8 +124,6 @@ impl Chunker for Minf } } -type Unit = i16; - #[derive(Serialize)] pub struct Point { @@ -175,7 +180,7 @@ pub struct Side xfer_pri: TransferMode, xfer_sec: TransferMode, xfer_tra: TransferMode, - shade: Fx32, + shade: Fixed, } #[derive(Debug, Serialize)] diff --git a/src/marathon/shp.rs b/src/marathon/shp.rs index 40c56e8..0331383 100644 --- a/src/marathon/shp.rs +++ b/src/marathon/shp.rs @@ -1,6 +1,6 @@ //! Marathon Shapes format handling. -use crate::{durandal::{bin::*, err::*, fx32::*, image::*, text::*}, +use crate::{durandal::{bin::*, err::*, fixed::*, image::*, text::*}, marathon::xfer::TransferMode}; use bitflags::bitflags; use serde::Serialize; @@ -114,14 +114,20 @@ fn frame(b: &[u8]) -> ResultS let flags = c_u16b(b, 0)?; let min_lt = c_u32b(b, 2)?; let bmp_ind = c_u16b(b, 6)? as usize; - let wrl_l = c_i16b(b, 16)?; - let wrl_r = c_i16b(b, 18)?; - let wrl_t = c_i16b(b, 20)?; - let wrl_b = c_i16b(b, 22)?; - let wrl_x = c_i16b(b, 24)?; - let wrl_y = c_i16b(b, 26)?; + let wrl_l = c_u16b(b, 16)?; + let wrl_r = c_u16b(b, 18)?; + let wrl_t = c_u16b(b, 20)?; + let wrl_b = c_u16b(b, 22)?; + let wrl_x = c_u16b(b, 24)?; + let wrl_y = c_u16b(b, 26)?; let flags = ok!(FrameFlags::from_bits(flags), "bad flag")?; - let min_lt = Fx32::from_bits(min_lt); + let min_lt = Fixed::from_bits(min_lt); + let wrl_l = Unit::from_bits(wrl_l); + let wrl_r = Unit::from_bits(wrl_r); + let wrl_t = Unit::from_bits(wrl_t); + let wrl_b = Unit::from_bits(wrl_b); + let wrl_x = Unit::from_bits(wrl_x); + let wrl_y = Unit::from_bits(wrl_y); Ok(Frame{flags, min_lt, bmp_ind, wrl_l, wrl_r, wrl_t, wrl_b, wrl_x, wrl_y}) } @@ -345,14 +351,14 @@ pub struct ImageShp<'a, 'b> pub struct Frame { flags: FrameFlags, - min_lt: Fx32, + min_lt: Fixed, bmp_ind: usize, - wrl_l: i16, - wrl_r: i16, - wrl_t: i16, - wrl_b: i16, - wrl_x: i16, - wrl_y: i16, + wrl_l: Unit, + wrl_r: Unit, + wrl_t: Unit, + wrl_b: Unit, + wrl_x: Unit, + wrl_y: Unit, } #[derive(Debug, Serialize)]