diff --git a/MarathonData.md b/MarathonData.md index 4bd1fc7..d43cafa 100644 --- a/MarathonData.md +++ b/MarathonData.md @@ -380,14 +380,14 @@ Map tags: | `LITE` | Array of Light | | `NOTE` | Array of Annotation | | `OBJS` | Array of Object | -| `påth` | Not analyzed (guardpaths) (å is $8C) | | `plac` | Array of Object Frequency | -| `door` | No test data (extra door data) | | `plat` | Array of Platform Data | | `medi` | Array of Media Data | | `ambi` | Array of Ambient Sound | | `bonk` | Array of Random Sound | | `term` | Array of Terminal | +| `påth` | Unused, supposed to be guardpaths (å is $8C) | +| `door` | Unused, supposed to be extra door data | Map files can be identified by the `Minf` chunk. @@ -395,7 +395,8 @@ Maps will always have either a `PNTS` or `EPNT` chunk, depending on what the map (and editor) use. `PNTS` are plain and have no more information than the actual position, while `EPNT` can be loaded directly into memory by the engine. `EPNT` also tells the engine that the map is preprocessed and that `iidx` and -`PLAT` chunks also exist. +`PLAT` chunks also exist. With `DataVersion` as `DataM1`, the format must +always be preprocessed. Physics tags: @@ -416,7 +417,7 @@ Image tags: | Name | Description | | ---- | ----------- | | `PICT` | Picture Resource | -| `clut` | Unused? | +| `clut` | Banished to the shadow realm | Images can be identified by the `PICT` chunk. @@ -427,7 +428,6 @@ Save file tags: | `plyr` | Not analyzed (saved player data) | | `dwol` | Not analyzed (saved dynamic world data) | | `mobj` | Not analyzed (saved object data) | -| `door` | Not analyzed (saved door data) | | `iidx` | Not analyzed (saved map indices) | | `alin` | Not analyzed (saved automap lines) | | `apol` | Not analyzed (saved automap polygons) | @@ -607,8 +607,8 @@ Light Function is 14 bytes. | `Type` | `u16` | `0` | | `Period` | `u16` | `2` | | `DeltaPeriod` | `u16` | `4` | -| `Value` | `u16` | `6` | -| `DeltaValue` | `u16` | `10` | +| `Value` | `fixed` | `6` | +| `DeltaValue` | `fixed` | `10` | - `Type` is a Light Function enumeration. @@ -701,14 +701,16 @@ Side is 64 bytes. One possibly textured side of a line segment. - `Type` is a Side Type enumeration. - `Flags` is a Side Flags bit field. - `TexPri`, `TexSec` and `TexTra` are Side Texture structures representing the -primary, secondary and transparent (middle) textures. +primary, secondary and transparent (middle) textures. Middle textures are not +supported if `DataVersion` is `DataM1` and so `TexTra` must be set to none. - `ExTopL`, `ExTopR`, `ExBotL` and `ExBotR` are Point structures representing the collision bounding rectangle. - `PanelType` is a control panel preset number. - `PanelPerm` is the permutation for this control panel (if any.) - `XferPri`, `XferSec` and `XferTra` are Transfer Mode enumerations for each respective texture. -- `Shade` is the ambient shading used primarily for visual contrast. +- `Shade` is the ambient shading used primarily for visual contrast. If +`DataVersion` is `DataM1`, this must be set to 0. ### Polygon ### @@ -718,6 +720,8 @@ yourself and the map compiler will not help you with this process. (It is a planned feature of Maraiah to allow the user to draw polygons of arbitrary shape and automatically split them.) +Note that `u16opt`s not available with `DataM1` must be set to none. + | Name | Type | Offset | | ---- | ---- | ------ | | `Type` | `u16` | `0` | @@ -744,6 +748,11 @@ shape and automatically split them.) | `NeighborNum` | `u16` | `86` | | `Center` | `struct` | `88` | | `SideArray` | `u16[8]` | `92` | + +If `DataVersion` is not `DataM1`: + +| Name | Type | Offset | +| ---- | ---- | ------ | | `OrigFlr` | `struct` | `108` | | `OrigCei` | `struct` | `112` | | `Media` | `u16opt` | `116` | @@ -752,7 +761,8 @@ shape and automatically split them.) | `SoundAmbient` | `u16opt` | `122` | | `SoundRandom` | `u16opt` | `124` | -- `Type` is a Polygon Type enumeration. +- `Type` is a Polygon Type enumeration, unless `DataVersion` is `DataM1`, where +it is instead an Old Polygon Type enumeration. - `Flags` is a Polygon Flags bit field. - `Area` is the power-of-two area of the polygon. - `ObjectFst` must be `65535`. @@ -762,7 +772,7 @@ shape and automatically split them.) ### Light ### -Light is 100 bytes. +Light is 100 bytes. If `DataVersion` is `DataM1` this is an Old Light. | Name | Type | Offset | | ---- | ---- | ------ | @@ -782,6 +792,37 @@ Light is 100 bytes. - `ActivPri`, `ActivSec` and `ActivMid` are Light Function structures. - `InactPri`, `InactSec` and `InactMid` are Light Function structures. +### Old Light ### + +Old Light is 32 bytes. The old lighting system not only sucked, but there was +no Media system, so it was even more useless as it couldn't be used as a +controller for liquids. So, because of these issues, the new lighting system +was put in place, but it was incompatible data-wise because it had too many +extensions. + + +| Name | Type | Offset | +| ---- | ---- | ------ | +| `Type` | `u16` | `2` | +| `Mode` | `u16` | `4` | +| `Phase` | `u16` | `6` | +| `ValueMin` | `fixed` | `8` | +| `ValueMax` | `fixed` | `12` | +| `Period` | `u16` | `16` | +| `ValueCur` | `fixed` | `18` | + +- `Type` is an Old Light Type enumeration. To create a new light from this, you +will need to write a lookup table which imitates each light type in the new +lighting system. This table is tedious to write, so please reference either +Aleph One or Maraiah for a full table of translations. +- `Mode` is an Old Light Mode enumeration. +- `Phase` is ignored in Aleph One. +- If `Type` is `Strobe`, you must set each resulting `Period` to this +definition's `Period` divided by 4 plus one. Otherwise, `Period` is ignored. +- For each of the new definition's functions, if the intensity of it is more +than 0, it should be set to `ValueMax`, otherwise `ValueMin`. +- `ValueCur` is ignored in Aleph One. + ### Map Annotation ### Map Annotation is 72 bytes. @@ -928,7 +969,8 @@ such as media (liquid) presets. - `Name` is the level name, intended to be 65 bytes, but one padding byte is left over, so the real length is 66. - `EntryFlags` is an Entry Point Flags bit field. It is unknown why this is 32 -bits wide when it could fit in even 8 bits. +bits wide when it could fit in even 8 bits. If `DataVersion` is `DataM1` and +this is `0`, this implies the value is `Solo`. ## Terminal ## @@ -1716,6 +1758,27 @@ exit upon reading them. | `MustExplore` | `17` | Must be entered for Exploration | None | | `AutoExit` | `18` | Teleports to next level if success | None | +### Old Polygon Type ### + +| Name | Value | Description | Permutation | +| ---- | ----- | ----------- | ----------- | +| `Normal` | `0` | Normal, no effects | None | +| `ImpassItem` | `1` | Items may not pass | None | +| `ImpassMons` | `2` | Monsters may not pass | None | +| `MinorOuch` | `3` | Damages the player a little | None | +| `MajorOuch` | `4` | Damages the player a lot | Team | +| `Platform` | `5` | Platform | Plat index | +| `TrigLightOn` | `6` | Triggers light on | Light index | +| `TrigPlatOn` | `7` | Triggers platform on | Plat index | +| `TrigLightOff` | `8` | Triggers light off | Poly index | +| `TrigPlatOff` | `9` | Triggers platform off | Poly index | +| `Teleporter` | `10` | Teleports to polygon centroid | Poly index | +| `Glue` | `11` | Slows the player down | None | +| `GlueTrigger` | `12` | TODO | TODO | +| `SuperGlue` | `13` | Slows the player down a lot | None | +| `MustExplore` | `14` | Must be entered for Exploration | None | +| `AutoExit` | `15` | Teleports to next level if success | None | + ### Control Panel Type ### This is used internally for each control panel preset and determines the @@ -1793,6 +1856,32 @@ Marathon 2 itself acknowledges how redundant this enumeration is. | `Strobe` | `1` | Strobe light | | `Media` | `2` | Media light | +### Old Light Type ### + +| Name | Value | +| ---- | ----- | +| `Normal` | `0` | +| `Rheostat` | `1` | +| `Flourescent` | `2` | +| `Strobe` | `3` | +| `Flickers` | `4` | +| `Pulsates` | `5` | +| `Annoying` | `6` | +| `EnergyEfficient` | `7` | + +### Old Light Mode ### + +| Name | Value | +| ---- | ----- | +| `TurningOn` | `0` | +| `Active` | `1` | +| `TurningOff` | `2` | +| `Inactive` | `3` | +| `Toggle` | `4` | + +- `TurningOn` and `Active` mean the new light created from this should have the +`InitActive` flag. Others do not mean anything. + ### Wad Version ### Used to determine how the engine loads the Wad file. @@ -2005,6 +2094,8 @@ relative to the point's lines ### Side Flags ### +If `DataVersion` is `DataM1`, then `ItemOpt` must be set by the client. + | Name | Bit | | ---- | --- | | `Status` | `0` | diff --git a/src/durandal/bin.rs b/src/durandal/bin.rs index c8517cf..03d9c92 100644 --- a/src/durandal/bin.rs +++ b/src/durandal/bin.rs @@ -1,6 +1,6 @@ //! Binary data conversion utilities. -use crate::durandal::err::*; +use crate::durandal::{err::*, text::mac_roman_conv}; use std::{fmt, num::NonZeroU16}; #[doc(hidden)] @@ -79,7 +79,7 @@ macro_rules! _durandal_read_impl { let $nam = $b[$n] as i8; }; ($_:ident $b:ident $nam:ident Ident $n:expr) => { - let $nam = [$b[$n], $b[$n + 1], $b[$n + 2], $b[$n + 3]]; + let $nam = Ident([$b[$n], $b[$n + 1], $b[$n + 2], $b[$n + 3]]); }; ($_:ident $b:ident $nam:ident $f:ident $n:expr) => { let $nam = $f(&$b[$n])?; @@ -155,7 +155,7 @@ macro_rules! read_data { /// 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]]} +pub fn ident(b: &[u8]) -> Ident {Ident([b[0], b[1], b[2], b[3]])} /// Applies `u32::from_be_bytes` to a slice. /// @@ -232,6 +232,9 @@ pub fn rd_ofstable(b: &[u8], impl OptU16 { + /// Creates an `OptU16` representing `None`. + pub const fn none() -> Self {OptU16(None)} + /// Creates an `OptU16` from a `u16`. pub fn from_repr(n: u16) -> Self { @@ -272,8 +275,17 @@ impl fmt::Debug for OptU16 } } +impl fmt::Debug for Ident +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "\"{}\"", mac_roman_conv(&self.0)) + } +} + /// A four-character-code identifier. -pub type Ident = [u8; 4]; +#[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. diff --git a/src/durandal/cenum.rs b/src/durandal/cenum.rs index 8904dcf..7a80725 100644 --- a/src/durandal/cenum.rs +++ b/src/durandal/cenum.rs @@ -19,6 +19,7 @@ macro_rules! c_enum } ) => { $(#[$outer])* + #[derive(Copy, Clone)] $V enum $E { $($Enum,)+ diff --git a/src/durandal/fixed.rs b/src/durandal/fixed.rs index c616c7f..9b078fc 100644 --- a/src/durandal/fixed.rs +++ b/src/durandal/fixed.rs @@ -9,7 +9,7 @@ macro_rules! define_fixed_type { struct $Type:ident ($IT:ident) : $UT:ident, $LT:ident, $FracBits:expr; ) => { $(#[$outer])* - #[derive(Clone, PartialEq, serde::Serialize)] + #[derive(Copy, Clone, PartialEq, PartialOrd, serde::Serialize)] pub struct $Type($IT); impl $Type @@ -25,6 +25,13 @@ macro_rules! define_fixed_type { #[inline] pub const fn from_bits(bits: $UT) -> Self {$Type(bits as $IT)} + /// Creates a value of this type with the integral portion `n`. + #[inline] + pub const fn from_int(n: $LT) -> Self + { + $Type(n as ($IT) << Self::FRACBITS) + } + /// Returns the raw bit pattern. #[inline] pub fn to_bits(&self) -> $UT {self.0 as $UT} @@ -71,7 +78,7 @@ macro_rules! define_fixed_type { impl From<$LT> for $Type { #[inline] - fn from(n: $LT) -> Self {$Type($IT::from(n) << Self::FRACBITS)} + fn from(n: $LT) -> Self {$Type::from_int(n)} } impl ops::Add for $Type diff --git a/src/marathon/map.rs b/src/marathon/map.rs index fba0e7d..2c276ce 100644 --- a/src/marathon/map.rs +++ b/src/marathon/map.rs @@ -4,27 +4,6 @@ use crate::{durandal::{bin::*, err::*, fixed::*, text::mac_roman_conv}, marathon::xfer::TransferMode}; use bitflags::bitflags; -/// Reads a `Minf` chunk. -pub fn read_minf(b: &[u8]) -> ResultS -{ - read_data! { - 88, BE in b => - env_code = u16[0]; - physi_id = u16[2]; - music_id = u16[4]; - missi_flags = u16[6]; - envir_flags = u16[8]; - level_name = mac_roman_conv[18..84] nt; - entry_flags = u32[84]; - } - - let missi_flags = flag_ok!(MsnFlags, missi_flags)?; - let envir_flags = flag_ok!(EnvFlags, envir_flags)?; - let entry_flags = flag_ok!(EntFlags, entry_flags)?; - Ok(Minf{env_code, physi_id, music_id, missi_flags, envir_flags, entry_flags, - level_name}) -} - /// Reads a `LightFunc` object. pub fn read_lightfunc(b: &[u8]) -> ResultS { @@ -33,8 +12,8 @@ pub fn read_lightfunc(b: &[u8]) -> ResultS ftype = u16[0]; prd_nrm = u16[2]; prd_dta = u16[4]; - val_nrm = u16[6]; - val_dta = u16[10]; + val_nrm = Fixed[6]; + val_dta = Fixed[10]; } let ftype = LightFuncType::from_repr(ftype)?; @@ -66,6 +45,30 @@ pub fn read_point(b: &[u8]) -> ResultS Ok(Point{x, y}) } +/// Reads a `Minf` chunk. +pub fn read_minf(b: &[u8]) -> ResultS +{ + read_data! { + 88, BE in b => + env_code = u16[0]; + physi_id = u16[2]; + music_id = u16[4]; + missi_flags = u16[6]; + envir_flags = u16[8]; + level_name = mac_roman_conv[18..84] nt; + entry_flags = u32[84]; + } + + let missi_flags = flag_ok!(MsnFlags, missi_flags)?; + let envir_flags = flag_ok!(EnvFlags, envir_flags)?; + let entry_flags = flag_ok!(EntFlags, entry_flags)?; + Ok(Minf{env_code, physi_id, music_id, missi_flags, envir_flags, entry_flags, + level_name}) +} + +/// Reads an `iidx` chunk. +pub fn read_iidx(b: &[u8]) -> ResultS<(u16, usize)> {Ok((u16b(b), 2))} + /// Reads an `EPNT` chunk. pub fn read_epnt(b: &[u8]) -> ResultS<(Point, usize)> { @@ -130,6 +133,18 @@ pub fn read_sids(b: &[u8]) -> ResultS<(Side, usize)> xfer_pri, xfer_sec, xfer_tra, shade}, 64)) } +/// Reads an old `SIDS` chunk. +pub fn read_old_sids(b: &[u8]) -> ResultS<(Side, usize)> +{ + let (mut side, siz) = read_sids(b)?; + + side.tex_tra.tex_id = OptU16::none(); + side.shade = Fixed::from_int(0); + side.flags.insert(SideFlags::ItemOpt); + + Ok((side, siz)) +} + /// Reads a `POLY` chunk. pub fn read_poly(b: &[u8]) -> ResultS<(Polygon, usize)> { @@ -163,6 +178,31 @@ pub fn read_poly(b: &[u8]) -> ResultS<(Polygon, usize)> snd_ind, snd_amb, snd_rnd}, 128)) } +/// Reads an old `POLY` chunk. +pub fn read_old_poly(b: &[u8]) -> ResultS<(Polygon, usize)> +{ + let (mut poly, siz) = read_poly(b)?; + + poly.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, + }; + + poly.ori_flr = Point{x: Unit::from_int(0), y: Unit::from_int(0)}; + poly.ori_cei = Point{x: Unit::from_int(0), y: Unit::from_int(0)}; + poly.med_ind = OptU16::none(); + poly.snd_amb = OptU16::none(); + poly.snd_rnd = OptU16::none(); + + Ok((poly, siz)) +} + /// Reads a `LITE` chunk. pub fn read_lite(b: &[u8]) -> ResultS<(Light, usize)> { @@ -187,6 +227,49 @@ pub fn read_lite(b: &[u8]) -> ResultS<(Light, usize)> ina_mid, tag}, 100)) } +/// Reads an old `LITE` chunk. +pub fn read_old_lite(b: &[u8]) -> ResultS<(Light, usize)> +{ + read_data! { + 32, BE in b => + ltype = u16[2] as usize; + mode = u16[4]; + phase = i16[6]; + min = Fixed[8]; + max = Fixed[12]; + prd = u16[16]; + } + + if OLD_LIGHT_DEFINITIONS.len() < ltype { + bail!("bad old light type"); + } + + let lite = &OLD_LIGHT_DEFINITIONS[ltype]; + let on = mode == 0 || mode == 1; + let strobe = ltype == 3; + let flags = if on {lite.flags | LightFlags::InitActive} else {lite.flags}; + + // modify each old light function accordingly + let old_lfun = move |func: &LightFunc| -> LightFunc { + LightFunc{ftype: func.ftype, + prd_nrm: if strobe {prd / 4 + 1} else {func.prd_nrm}, + prd_dta: func.prd_dta, + val_nrm: if func.val_nrm > Fixed::from_int(0) {max} else {min}, + val_dta: func.val_dta} + }; + + Ok((Light{ltype: lite.ltype, + flags, + phase, + act_pri: old_lfun(&lite.act_pri), + act_sec: old_lfun(&lite.act_sec), + act_mid: old_lfun(&lite.act_mid), + ina_pri: old_lfun(&lite.ina_pri), + ina_sec: old_lfun(&lite.ina_sec), + ina_mid: old_lfun(&lite.ina_mid), + tag: 0}, 32)) +} + /// Reads an `OBJS` chunk. pub fn read_objs(b: &[u8]) -> ResultS<(Object, usize)> { @@ -399,8 +482,8 @@ pub struct LightFunc pub ftype: LightFuncType, pub prd_nrm: u16, pub prd_dta: u16, - pub val_nrm: u16, - pub val_dta: u16, + pub val_nrm: Fixed, + pub val_dta: Fixed, } /// A dynamic polygon light. @@ -710,6 +793,11 @@ c_enum! { 16 => TrigItems, 17 => MustExplore, 18 => AutoExit, + 19 => OuchMinor, + 20 => OuchMajor, + 21 => Glue, + 22 => GlueTrigger, + 23 => GlueSuper, } } @@ -722,6 +810,8 @@ c_enum! { 1 => Linear, 2 => Smooth, 3 => Flicker, + 4 => Random, + 5 => Fluorescent, } } @@ -756,4 +846,296 @@ impl std::fmt::Debug for Point } } +pub const TICKS_PER_SECOND: u16 = 30; + +const OLD_LIGHT_DEFINITIONS: [Light; 8] = [ + // Normal + Light{ltype: LightType::Normal, + flags: LightFlags::SlaveValue, + phase: 0, + act_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_mid: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: 1, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + ina_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_mid: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: 1, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + tag: 0}, + + // Rheostat + Light{ltype: LightType::Normal, + flags: LightFlags::SlaveValue, + phase: 0, + act_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_mid: LightFunc{ftype: LightFuncType::Smooth, + prd_nrm: TICKS_PER_SECOND * 3, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + ina_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_mid: LightFunc{ftype: LightFuncType::Smooth, + prd_nrm: TICKS_PER_SECOND * 3, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + tag: 0}, + + // Flourescent + Light{ltype: LightType::Normal, + flags: LightFlags::SlaveValue, + phase: 0, + act_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_mid: LightFunc{ftype: LightFuncType::Fluorescent, + prd_nrm: TICKS_PER_SECOND * 3, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + ina_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_mid: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: 1, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + tag: 0}, + + // Strobe + Light{ltype: LightType::Normal, + flags: LightFlags::SlaveValue, + phase: 0, + act_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + act_mid: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: 1, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + ina_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + ina_mid: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: 1, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + tag: 0}, + + // Flicker + Light{ltype: LightType::Normal, + flags: LightFlags::SlaveValue, + phase: 0, + act_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_mid: LightFunc{ftype: LightFuncType::Flicker, + prd_nrm: TICKS_PER_SECOND * 3, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + ina_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_mid: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: 1, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + tag: 0}, + + // Pulsate + Light{ltype: LightType::Normal, + flags: LightFlags::SlaveValue, + phase: 0, + act_pri: LightFunc{ftype: LightFuncType::Smooth, + prd_nrm: TICKS_PER_SECOND * 2, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_sec: LightFunc{ftype: LightFuncType::Smooth, + prd_nrm: TICKS_PER_SECOND * 2 - 1, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + act_mid: LightFunc{ftype: LightFuncType::Smooth, + prd_nrm: TICKS_PER_SECOND * 2 - 1, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + ina_pri: LightFunc{ftype: LightFuncType::Smooth, + prd_nrm: TICKS_PER_SECOND * 2, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_sec: LightFunc{ftype: LightFuncType::Smooth, + prd_nrm: TICKS_PER_SECOND * 2 - 1, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + ina_mid: LightFunc{ftype: LightFuncType::Smooth, + prd_nrm: TICKS_PER_SECOND * 2, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + tag: 0}, + + // Annoying + Light{ltype: LightType::Normal, + flags: LightFlags::SlaveValue, + phase: 0, + act_pri: LightFunc{ftype: LightFuncType::Random, + prd_nrm: 2, + prd_dta: 1, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: 2, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + act_mid: LightFunc{ftype: LightFuncType::Random, + prd_nrm: 1, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + ina_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_mid: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + tag: 0}, + + // Energy Efficient + Light{ltype: LightType::Normal, + flags: LightFlags::SlaveValue, + phase: 0, + act_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + act_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + act_mid: LightFunc{ftype: LightFuncType::Linear, + prd_nrm: TICKS_PER_SECOND * 2, + prd_dta: 0, + val_nrm: Fixed::from_int(1), + val_dta: Fixed::from_int(0)}, + ina_pri: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_sec: LightFunc{ftype: LightFuncType::Constant, + prd_nrm: TICKS_PER_SECOND, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + ina_mid: LightFunc{ftype: LightFuncType::Linear, + prd_nrm: TICKS_PER_SECOND * 2, + prd_dta: 0, + val_nrm: Fixed::from_int(0), + val_dta: Fixed::from_int(0)}, + tag: 0}, +]; + // EOF diff --git a/src/marathon/snd.rs b/src/marathon/snd.rs index 63cce3b..461d334 100644 --- a/src/marathon/snd.rs +++ b/src/marathon/snd.rs @@ -95,7 +95,7 @@ pub fn read_sounds(b: &[u8]) -> ResultS> snd_num = u16[10] as usize; } - if version != 1 || magic != *b"snd2" { + if version != 1 || magic.0 != *b"snd2" { bail!("bad sound header"); } diff --git a/src/marathon/wad.rs b/src/marathon/wad.rs index a5e93a2..28e6fbc 100644 --- a/src/marathon/wad.rs +++ b/src/marathon/wad.rs @@ -5,11 +5,16 @@ use crate::marathon::{map, phy, pict, trm}; use std::collections::BTreeMap; /// Reads all chunks in an entry. -pub fn read_chunks(b: &[u8], siz_cnk: usize) -> ResultS> +pub fn read_chunks(b: &[u8], old_dat: bool, siz_cnk: usize) + -> ResultS> { let mut chunks = Vec::new(); let mut p = 0; + 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}; + while p < b.len() { read_data! { p + siz_cnk, BE in b => @@ -21,16 +26,17 @@ pub fn read_chunks(b: &[u8], siz_cnk: usize) -> ResultS> let end = beg + size; let data = &b[beg..end]; - let chunk = match &iden { + chunks.push(match &iden.0 { b"PICT" => Chunk::Pict(pict::load_pict(data)?), b"Minf" => Chunk::Minf(map::read_minf(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)?), 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"SIDS" => Chunk::Sids(rd_array(data, map_read_sides)?), + b"POLY" => Chunk::Poly(rd_array(data, map_read_polys)?), b"OBJS" => Chunk::Objs(rd_array(data, map::read_objs)?), + b"LITE" => Chunk::Lite(rd_array(data, map_read_light)?), 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)?), @@ -44,9 +50,7 @@ pub fn read_chunks(b: &[u8], siz_cnk: usize) -> ResultS> 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; } @@ -56,7 +60,8 @@ pub fn read_chunks(b: &[u8], siz_cnk: usize) -> ResultS> /// Reads all entries in a `Wad`. pub fn read_entries(b: &[u8], - is_old: bool, + old_wad: bool, + old_dat: bool, siz_app: usize, siz_ent: usize, siz_cnk: usize) @@ -79,9 +84,9 @@ pub fn read_entries(b: &[u8], index = u16[p + 8]; } - let index = if is_old {i as u16} else {index}; + let index = if old_wad {i as u16} else {index}; - let chunks = read_chunks(&b[offset..offset + size], siz_cnk)?; + let chunks = read_chunks(&b[offset..offset + size], old_dat, siz_cnk)?; let appdata = b[p..p + siz_app].to_vec(); entries.insert(index, Entry{chunks, appdata}); @@ -107,25 +112,26 @@ pub fn read_wad(b: &[u8]) -> ResultS let ver_wad = Ver::from_repr(ver_wad)?; - let is_old = match ver_wad { + let old_wad = match ver_wad { Ver::Base => true, _ => false, }; + let old_dat = ver_dat == 0; - let siz_ent = if is_old {8 } else {10}; - let siz_cnk = if is_old {12} else {16}; + let siz_ent = if old_wad {8 } else {10}; + let siz_cnk = if old_wad {12} else {16}; - if siz_ent != siz_went { + if !old_wad && siz_ent != siz_went { bail!("invalid entry size"); } - if siz_cnk != siz_wcnk { + if !old_wad && siz_cnk != siz_wcnk { bail!("invalid chunk size"); } - let entries = read_entries(b, is_old, siz_app, siz_ent, siz_cnk)?; + let entries = read_entries(b, old_wad, old_dat, siz_app, siz_ent, siz_cnk)?; - Ok(Wad{head: WadHeader{ver_wad, ver_dat, siz_app, name}, entries}) + Ok(Wad{head: WadHeader{ver_wad, old_dat, siz_app, name}, entries}) } /// Any kind of chunk in an `Entry`. @@ -134,6 +140,7 @@ pub enum Chunk { Pict(image::Image8), Minf(map::Minf), + Iidx(Vec), Pnts(Vec), Lins(Vec), Sids(Vec), @@ -168,7 +175,7 @@ pub struct Entry pub struct WadHeader { pub ver_wad: Ver, - pub ver_dat: u16, + pub old_dat: bool, pub name: String, pub siz_app: usize, }