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 |
| `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` |

View File

@ -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.

View File

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

View File

@ -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

View File

@ -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

View File

@ -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");
}

View File

@ -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,
}