add marathon 1 data loaders
parent
49253773a6
commit
17a882cb20
115
MarathonData.md
115
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` |
|
||||
|
|
|
@ -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<T, F>(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.
|
||||
|
|
|
@ -19,6 +19,7 @@ macro_rules! c_enum
|
|||
}
|
||||
) => {
|
||||
$(#[$outer])*
|
||||
#[derive(Copy, Clone)]
|
||||
$V enum $E
|
||||
{
|
||||
$($Enum,)+
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<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.
|
||||
pub fn read_lightfunc(b: &[u8]) -> ResultS<LightFunc>
|
||||
{
|
||||
|
@ -33,8 +12,8 @@ pub fn read_lightfunc(b: &[u8]) -> ResultS<LightFunc>
|
|||
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<Point>
|
|||
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.
|
||||
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
|
||||
|
|
|
@ -95,7 +95,7 @@ pub fn read_sounds(b: &[u8]) -> ResultS<Vec<SoundTable>>
|
|||
snd_num = u16[10] as usize;
|
||||
}
|
||||
|
||||
if version != 1 || magic != *b"snd2" {
|
||||
if version != 1 || magic.0 != *b"snd2" {
|
||||
bail!("bad sound header");
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Vec<Chunk>>
|
||||
pub fn read_chunks(b: &[u8], old_dat: bool, siz_cnk: usize)
|
||||
-> ResultS<Vec<Chunk>>
|
||||
{
|
||||
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<Vec<Chunk>>
|
|||
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<Vec<Chunk>>
|
|||
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<Vec<Chunk>>
|
|||
|
||||
/// 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<Wad>
|
|||
|
||||
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<u16>),
|
||||
Pnts(Vec<map::Point>),
|
||||
Lins(Vec<map::Line>),
|
||||
Sids(Vec<map::Side>),
|
||||
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue