add marathon 1 data loaders

png-branch
an 2019-03-01 23:04:20 -05:00
parent 49253773a6
commit 17a882cb20
7 changed files with 563 additions and 63 deletions

View File

@ -380,14 +380,14 @@ Map tags:
| `LITE` | Array of Light | | `LITE` | Array of Light |
| `NOTE` | Array of Annotation | | `NOTE` | Array of Annotation |
| `OBJS` | Array of Object | | `OBJS` | Array of Object |
| `påth` | Not analyzed (guardpaths) (å is $8C) |
| `plac` | Array of Object Frequency | | `plac` | Array of Object Frequency |
| `door` | No test data (extra door data) |
| `plat` | Array of Platform Data | | `plat` | Array of Platform Data |
| `medi` | Array of Media Data | | `medi` | Array of Media Data |
| `ambi` | Array of Ambient Sound | | `ambi` | Array of Ambient Sound |
| `bonk` | Array of Random Sound | | `bonk` | Array of Random Sound |
| `term` | Array of Terminal | | `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. 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 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. 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 `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: Physics tags:
@ -416,7 +417,7 @@ Image tags:
| Name | Description | | Name | Description |
| ---- | ----------- | | ---- | ----------- |
| `PICT` | Picture Resource | | `PICT` | Picture Resource |
| `clut` | Unused? | | `clut` | Banished to the shadow realm |
Images can be identified by the `PICT` chunk. Images can be identified by the `PICT` chunk.
@ -427,7 +428,6 @@ Save file tags:
| `plyr` | Not analyzed (saved player data) | | `plyr` | Not analyzed (saved player data) |
| `dwol` | Not analyzed (saved dynamic world data) | | `dwol` | Not analyzed (saved dynamic world data) |
| `mobj` | Not analyzed (saved object data) | | `mobj` | Not analyzed (saved object data) |
| `door` | Not analyzed (saved door data) |
| `iidx` | Not analyzed (saved map indices) | | `iidx` | Not analyzed (saved map indices) |
| `alin` | Not analyzed (saved automap lines) | | `alin` | Not analyzed (saved automap lines) |
| `apol` | Not analyzed (saved automap polygons) | | `apol` | Not analyzed (saved automap polygons) |
@ -607,8 +607,8 @@ Light Function is 14 bytes.
| `Type` | `u16` | `0` | | `Type` | `u16` | `0` |
| `Period` | `u16` | `2` | | `Period` | `u16` | `2` |
| `DeltaPeriod` | `u16` | `4` | | `DeltaPeriod` | `u16` | `4` |
| `Value` | `u16` | `6` | | `Value` | `fixed` | `6` |
| `DeltaValue` | `u16` | `10` | | `DeltaValue` | `fixed` | `10` |
- `Type` is a Light Function enumeration. - `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. - `Type` is a Side Type enumeration.
- `Flags` is a Side Flags bit field. - `Flags` is a Side Flags bit field.
- `TexPri`, `TexSec` and `TexTra` are Side Texture structures representing the - `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 - `ExTopL`, `ExTopR`, `ExBotL` and `ExBotR` are Point structures representing
the collision bounding rectangle. the collision bounding rectangle.
- `PanelType` is a control panel preset number. - `PanelType` is a control panel preset number.
- `PanelPerm` is the permutation for this control panel (if any.) - `PanelPerm` is the permutation for this control panel (if any.)
- `XferPri`, `XferSec` and `XferTra` are Transfer Mode enumerations for each - `XferPri`, `XferSec` and `XferTra` are Transfer Mode enumerations for each
respective texture. 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 ### ### 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 planned feature of Maraiah to allow the user to draw polygons of arbitrary
shape and automatically split them.) shape and automatically split them.)
Note that `u16opt`s not available with `DataM1` must be set to none.
| Name | Type | Offset | | Name | Type | Offset |
| ---- | ---- | ------ | | ---- | ---- | ------ |
| `Type` | `u16` | `0` | | `Type` | `u16` | `0` |
@ -744,6 +748,11 @@ shape and automatically split them.)
| `NeighborNum` | `u16` | `86` | | `NeighborNum` | `u16` | `86` |
| `Center` | `struct` | `88` | | `Center` | `struct` | `88` |
| `SideArray` | `u16[8]` | `92` | | `SideArray` | `u16[8]` | `92` |
If `DataVersion` is not `DataM1`:
| Name | Type | Offset |
| ---- | ---- | ------ |
| `OrigFlr` | `struct` | `108` | | `OrigFlr` | `struct` | `108` |
| `OrigCei` | `struct` | `112` | | `OrigCei` | `struct` | `112` |
| `Media` | `u16opt` | `116` | | `Media` | `u16opt` | `116` |
@ -752,7 +761,8 @@ shape and automatically split them.)
| `SoundAmbient` | `u16opt` | `122` | | `SoundAmbient` | `u16opt` | `122` |
| `SoundRandom` | `u16opt` | `124` | | `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. - `Flags` is a Polygon Flags bit field.
- `Area` is the power-of-two area of the polygon. - `Area` is the power-of-two area of the polygon.
- `ObjectFst` must be `65535`. - `ObjectFst` must be `65535`.
@ -762,7 +772,7 @@ shape and automatically split them.)
### Light ### ### Light ###
Light is 100 bytes. Light is 100 bytes. If `DataVersion` is `DataM1` this is an Old Light.
| Name | Type | Offset | | Name | Type | Offset |
| ---- | ---- | ------ | | ---- | ---- | ------ |
@ -782,6 +792,37 @@ Light is 100 bytes.
- `ActivPri`, `ActivSec` and `ActivMid` are Light Function structures. - `ActivPri`, `ActivSec` and `ActivMid` are Light Function structures.
- `InactPri`, `InactSec` and `InactMid` 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 ###
Map Annotation is 72 bytes. 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 - `Name` is the level name, intended to be 65 bytes, but one padding byte is
left over, so the real length is 66. left over, so the real length is 66.
- `EntryFlags` is an Entry Point Flags bit field. It is unknown why this is 32 - `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 ## ## Terminal ##
@ -1716,6 +1758,27 @@ exit upon reading them.
| `MustExplore` | `17` | Must be entered for Exploration | None | | `MustExplore` | `17` | Must be entered for Exploration | None |
| `AutoExit` | `18` | Teleports to next level if success | 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 ### ### Control Panel Type ###
This is used internally for each control panel preset and determines the 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 | | `Strobe` | `1` | Strobe light |
| `Media` | `2` | Media 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 ### ### Wad Version ###
Used to determine how the engine loads the Wad file. Used to determine how the engine loads the Wad file.
@ -2005,6 +2094,8 @@ relative to the point's lines
### Side Flags ### ### Side Flags ###
If `DataVersion` is `DataM1`, then `ItemOpt` must be set by the client.
| Name | Bit | | Name | Bit |
| ---- | --- | | ---- | --- |
| `Status` | `0` | | `Status` | `0` |

View File

@ -1,6 +1,6 @@
//! Binary data conversion utilities. //! Binary data conversion utilities.
use crate::durandal::err::*; use crate::durandal::{err::*, text::mac_roman_conv};
use std::{fmt, num::NonZeroU16}; use std::{fmt, num::NonZeroU16};
#[doc(hidden)] #[doc(hidden)]
@ -79,7 +79,7 @@ macro_rules! _durandal_read_impl {
let $nam = $b[$n] as i8; let $nam = $b[$n] as i8;
}; };
($_:ident $b:ident $nam:ident Ident $n:expr) => { ($_: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) => { ($_:ident $b:ident $nam:ident $f:ident $n:expr) => {
let $nam = $f(&$b[$n])?; let $nam = $f(&$b[$n])?;
@ -155,7 +155,7 @@ macro_rules! read_data {
/// Creates an `Ident` from a slice. /// Creates an `Ident` from a slice.
/// ///
/// `b` must be at least 4 bytes, or a panic will occur. /// `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. /// Applies `u32::from_be_bytes` to a slice.
/// ///
@ -232,6 +232,9 @@ pub fn rd_ofstable<T, F>(b: &[u8],
impl OptU16 impl OptU16
{ {
/// Creates an `OptU16` representing `None`.
pub const fn none() -> Self {OptU16(None)}
/// Creates an `OptU16` from a `u16`. /// Creates an `OptU16` from a `u16`.
pub fn from_repr(n: u16) -> Self 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. /// 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 /// An object identified by a `u16` which may be `u16::max_value()` to
/// represent a nulled value. /// represent a nulled value.

View File

@ -19,6 +19,7 @@ macro_rules! c_enum
} }
) => { ) => {
$(#[$outer])* $(#[$outer])*
#[derive(Copy, Clone)]
$V enum $E $V enum $E
{ {
$($Enum,)+ $($Enum,)+

View File

@ -9,7 +9,7 @@ macro_rules! define_fixed_type {
struct $Type:ident ($IT:ident) : $UT:ident, $LT:ident, $FracBits:expr; struct $Type:ident ($IT:ident) : $UT:ident, $LT:ident, $FracBits:expr;
) => { ) => {
$(#[$outer])* $(#[$outer])*
#[derive(Clone, PartialEq, serde::Serialize)] #[derive(Copy, Clone, PartialEq, PartialOrd, serde::Serialize)]
pub struct $Type($IT); pub struct $Type($IT);
impl $Type impl $Type
@ -25,6 +25,13 @@ macro_rules! define_fixed_type {
#[inline] #[inline]
pub const fn from_bits(bits: $UT) -> Self {$Type(bits as $IT)} 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. /// Returns the raw bit pattern.
#[inline] #[inline]
pub fn to_bits(&self) -> $UT {self.0 as $UT} pub fn to_bits(&self) -> $UT {self.0 as $UT}
@ -71,7 +78,7 @@ macro_rules! define_fixed_type {
impl From<$LT> for $Type impl From<$LT> for $Type
{ {
#[inline] #[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 impl ops::Add for $Type

View File

@ -4,27 +4,6 @@ use crate::{durandal::{bin::*, err::*, fixed::*, text::mac_roman_conv},
marathon::xfer::TransferMode}; marathon::xfer::TransferMode};
use bitflags::bitflags; use bitflags::bitflags;
/// Reads a `Minf` chunk.
pub fn read_minf(b: &[u8]) -> ResultS<Minf>
{
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. /// Reads a `LightFunc` object.
pub fn read_lightfunc(b: &[u8]) -> ResultS<LightFunc> pub fn read_lightfunc(b: &[u8]) -> ResultS<LightFunc>
{ {
@ -33,8 +12,8 @@ pub fn read_lightfunc(b: &[u8]) -> ResultS<LightFunc>
ftype = u16[0]; ftype = u16[0];
prd_nrm = u16[2]; prd_nrm = u16[2];
prd_dta = u16[4]; prd_dta = u16[4];
val_nrm = u16[6]; val_nrm = Fixed[6];
val_dta = u16[10]; val_dta = Fixed[10];
} }
let ftype = LightFuncType::from_repr(ftype)?; let ftype = LightFuncType::from_repr(ftype)?;
@ -66,6 +45,30 @@ pub fn read_point(b: &[u8]) -> ResultS<Point>
Ok(Point{x, y}) Ok(Point{x, y})
} }
/// Reads a `Minf` chunk.
pub fn read_minf(b: &[u8]) -> ResultS<Minf>
{
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. /// Reads an `EPNT` chunk.
pub fn read_epnt(b: &[u8]) -> ResultS<(Point, usize)> 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)) 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. /// Reads a `POLY` chunk.
pub fn read_poly(b: &[u8]) -> ResultS<(Polygon, usize)> 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)) 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. /// Reads a `LITE` chunk.
pub fn read_lite(b: &[u8]) -> ResultS<(Light, usize)> 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)) 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. /// Reads an `OBJS` chunk.
pub fn read_objs(b: &[u8]) -> ResultS<(Object, usize)> pub fn read_objs(b: &[u8]) -> ResultS<(Object, usize)>
{ {
@ -399,8 +482,8 @@ pub struct LightFunc
pub ftype: LightFuncType, pub ftype: LightFuncType,
pub prd_nrm: u16, pub prd_nrm: u16,
pub prd_dta: u16, pub prd_dta: u16,
pub val_nrm: u16, pub val_nrm: Fixed,
pub val_dta: u16, pub val_dta: Fixed,
} }
/// A dynamic polygon light. /// A dynamic polygon light.
@ -710,6 +793,11 @@ c_enum! {
16 => TrigItems, 16 => TrigItems,
17 => MustExplore, 17 => MustExplore,
18 => AutoExit, 18 => AutoExit,
19 => OuchMinor,
20 => OuchMajor,
21 => Glue,
22 => GlueTrigger,
23 => GlueSuper,
} }
} }
@ -722,6 +810,8 @@ c_enum! {
1 => Linear, 1 => Linear,
2 => Smooth, 2 => Smooth,
3 => Flicker, 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 // EOF

View File

@ -95,7 +95,7 @@ pub fn read_sounds(b: &[u8]) -> ResultS<Vec<SoundTable>>
snd_num = u16[10] as usize; snd_num = u16[10] as usize;
} }
if version != 1 || magic != *b"snd2" { if version != 1 || magic.0 != *b"snd2" {
bail!("bad sound header"); bail!("bad sound header");
} }

View File

@ -5,11 +5,16 @@ use crate::marathon::{map, phy, pict, trm};
use std::collections::BTreeMap; use std::collections::BTreeMap;
/// Reads all chunks in an entry. /// Reads all chunks in an entry.
pub fn read_chunks(b: &[u8], siz_cnk: usize) -> ResultS<Vec<Chunk>> pub fn read_chunks(b: &[u8], old_dat: bool, siz_cnk: usize)
-> ResultS<Vec<Chunk>>
{ {
let mut chunks = Vec::new(); let mut chunks = Vec::new();
let mut p = 0; 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() { while p < b.len() {
read_data! { read_data! {
p + siz_cnk, BE in b => p + siz_cnk, BE in b =>
@ -21,16 +26,17 @@ pub fn read_chunks(b: &[u8], siz_cnk: usize) -> ResultS<Vec<Chunk>>
let end = beg + size; let end = beg + size;
let data = &b[beg..end]; let data = &b[beg..end];
let chunk = match &iden { chunks.push(match &iden.0 {
b"PICT" => Chunk::Pict(pict::load_pict(data)?), b"PICT" => Chunk::Pict(pict::load_pict(data)?),
b"Minf" => Chunk::Minf(map::read_minf(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"EPNT" => Chunk::Pnts(rd_array(data, map::read_epnt)?),
b"PNTS" => Chunk::Pnts(rd_array(data, map::read_pnts)?), b"PNTS" => Chunk::Pnts(rd_array(data, map::read_pnts)?),
b"LINS" => Chunk::Lins(rd_array(data, map::read_lins)?), b"LINS" => Chunk::Lins(rd_array(data, map::read_lins)?),
b"SIDS" => Chunk::Sids(rd_array(data, map::read_sids)?), b"SIDS" => Chunk::Sids(rd_array(data, map_read_sides)?),
b"POLY" => Chunk::Poly(rd_array(data, map::read_poly)?), b"POLY" => Chunk::Poly(rd_array(data, map_read_polys)?),
b"LITE" => Chunk::Lite(rd_array(data, map::read_lite)?),
b"OBJS" => Chunk::Objs(rd_array(data, map::read_objs)?), 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"plac" => Chunk::Plac(rd_array(data, map::read_plac)?),
b"ambi" => Chunk::Ambi(rd_array(data, map::read_ambi)?), b"ambi" => Chunk::Ambi(rd_array(data, map::read_ambi)?),
b"bonk" => Chunk::Bonk(rd_array(data, map::read_bonk)?), b"bonk" => Chunk::Bonk(rd_array(data, map::read_bonk)?),
@ -44,9 +50,7 @@ pub fn read_chunks(b: &[u8], siz_cnk: usize) -> ResultS<Vec<Chunk>>
b"PXpx" => Chunk::Pxpx(rd_array(data, phy::read_pxpx)?), b"PXpx" => Chunk::Pxpx(rd_array(data, phy::read_pxpx)?),
b"WPpx" => Chunk::Wppx(rd_array(data, phy::read_wppx)?), b"WPpx" => Chunk::Wppx(rd_array(data, phy::read_wppx)?),
_ => Chunk::Data{iden, data: data.to_vec()}, _ => Chunk::Data{iden, data: data.to_vec()},
}; });
chunks.push(chunk);
p = end; p = end;
} }
@ -56,7 +60,8 @@ pub fn read_chunks(b: &[u8], siz_cnk: usize) -> ResultS<Vec<Chunk>>
/// Reads all entries in a `Wad`. /// Reads all entries in a `Wad`.
pub fn read_entries(b: &[u8], pub fn read_entries(b: &[u8],
is_old: bool, old_wad: bool,
old_dat: bool,
siz_app: usize, siz_app: usize,
siz_ent: usize, siz_ent: usize,
siz_cnk: usize) siz_cnk: usize)
@ -79,9 +84,9 @@ pub fn read_entries(b: &[u8],
index = u16[p + 8]; 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(); let appdata = b[p..p + siz_app].to_vec();
entries.insert(index, Entry{chunks, appdata}); entries.insert(index, Entry{chunks, appdata});
@ -107,25 +112,26 @@ pub fn read_wad(b: &[u8]) -> ResultS<Wad>
let ver_wad = Ver::from_repr(ver_wad)?; let ver_wad = Ver::from_repr(ver_wad)?;
let is_old = match ver_wad { let old_wad = match ver_wad {
Ver::Base => true, Ver::Base => true,
_ => false, _ => false,
}; };
let old_dat = ver_dat == 0;
let siz_ent = if is_old {8 } else {10}; let siz_ent = if old_wad {8 } else {10};
let siz_cnk = if is_old {12} else {16}; 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"); bail!("invalid entry size");
} }
if siz_cnk != siz_wcnk { if !old_wad && siz_cnk != siz_wcnk {
bail!("invalid chunk size"); 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`. /// Any kind of chunk in an `Entry`.
@ -134,6 +140,7 @@ pub enum Chunk
{ {
Pict(image::Image8), Pict(image::Image8),
Minf(map::Minf), Minf(map::Minf),
Iidx(Vec<u16>),
Pnts(Vec<map::Point>), Pnts(Vec<map::Point>),
Lins(Vec<map::Line>), Lins(Vec<map::Line>),
Sids(Vec<map::Side>), Sids(Vec<map::Side>),
@ -168,7 +175,7 @@ pub struct Entry
pub struct WadHeader pub struct WadHeader
{ {
pub ver_wad: Ver, pub ver_wad: Ver,
pub ver_dat: u16, pub old_dat: bool,
pub name: String, pub name: String,
pub siz_app: usize, pub siz_app: usize,
} }