Maraiah/doc/data/3-struct.md

1179 lines
42 KiB
Markdown
Raw Permalink Normal View History

2019-07-24 19:14:02 -07:00
# STRUCTURES ##################################################################
All integers here are big-endian unless specified.
All unspecified bytes must be set to `0` when written, although when reading
should not be checked as they may be garbage.
The type "`fixed`" refers to a 32-bit fixed point number with the format 15.16s
(the lower 16 bits are fractional, the upper 15 are integral, and one bit for
sign.)
The type "`angle`" refers to a 16-bit fixed point number with the format 0.9s.
This is used for all angles. Because they're actually 16-bit, the real format is
6.9s, but the integral part is ignored. "No angle" is represented by 65510
(-1.9) for some reason.
The type "`unit`" refers to a 16-bit fixed point number with the format 5.10s.
This is used for all world coordinates.
A "`u16opt`" is a 16-bit integer which references something by index. If all
bits are set, it is to be interpreted as "none." Traditionally, these are signed
integers, but they can be treated as unsigned with no repercussions.
## Wad ##
### Wad File ###
Wad files are structured like:
- Wad Header
- Entries/Chunks
- Directory
It *must* be in this order because the engine assumes that the data directly
after the 128th byte is entry data.
### Wad Header ###
The Wad Header is 128 bytes. The Wad header is always at the very beginning of
the file, except when it's a MacBin file or an AppleSingle file, in which case
it's at the start of the resource fork. You'll have to account for that
yourself.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `WadVersion` | `u16` | `0` |
| `DataVersion` | `u16` | `2` |
| `OriginalName` | `u8[64]` | `4` |
| `Checksum` | `u32` | `68` |
- `WadVersion` is a Wad Version enumeration.
- `DataVersion` is a Data Version enumeration.
- `OriginalName` is the original filename, null byte terminated.
- `Checksum` is a CRC-32 of the entire file with this value set to 0.
If `WadVersion` is greater than or equal to `VerDir`:
| Name | Type | Offset |
| ---- | ---- | ------ |
| `DirOffset` | `u32` | `72` |
| `NumEntries` | `u16` | `76` |
| `AppDataSize` | `u16` | `78` |
| `ChunkSize` | `u16` | `80` |
| `EntrySize` | `u16` | `82` |
- `DirOffset` is the offset to the first Directory Entry structure.
- `NumEntries` is the number of entries in this file.
- `AppDataSize` is the number of bytes to skip for each directory entry.
- `ChunkSize` and `EntrySize` may be zero, in which case they will default to 16
and 10 respectively. They exist for forward compatibility with Wad patching,
but because they were never actually expanded upon, are useless.
If `WadVersion` is greater than or equal to `VerOver`:
| Name | Type | Offset |
| ---- | ---- | ------ |
| `ParentSum` | `u32` | `84` |
- `ParentSum` is the checksum of the file this one modifies, if any.
### Directory Entry ###
Directory Entry is 8 bytes if `WadVersion` is `VerBase`, or else `EntrySize
+ AppData` bytes. Following this structure is `AppData` bytes, supposed to be
| Name | Type | Offset |
| ---- | ---- | ------ |
| `DataOffset` | `u32` | `0` |
| `DataSize` | `u32` | `4` |
- `DataOffset` is the offset to the start of this entry's data (from the start
of the file.)
- `DataSize` is the length of this entry's data.
If `WadVersion` is greater than or equal to `VerDir`:
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Index` | `u16` | `8` |
| `AppData` | `u8[]` | `10` |
- `Index` is the index of this entry, for instance the map or PICT number.
- `AppData` is an arbitrary data array used by editor applications, and will be
ignored by the engine.
### Chunk ###
Chunk is 12 bytes if `WadVersion` is `VerBase`, or `ChunkSize` bytes otherwise.
Most Wad entries are made up of tagged data formats, the engine assumes this for
every entry and so every entry has at least one chunk. These are similar to IFF
or PNG chunks.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Ident` | `u8[4]` | `0` |
| `NextOffset` | `u32` | `4` |
| `DataSize` | `u32` | `8` |
- `Ident` is a four character code identifier for the chunk.
- `NextOffset` is the file offset of the next chunk minus the file header.
- `DataSize` is the size of the chunk (not including this header.)
If `WadVersion` is greater than or equal to `VerDir`:
| Name | Type | Offset |
| ---- | ---- | ------ |
| `PatchOffset` | `u32` | `12` |
## Map ##
### Light Function ###
Light Function is 14 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Type` | `u16` | `0` |
| `Period` | `u16` | `2` |
| `DeltaPeriod` | `u16` | `4` |
| `Value` | `fixed` | `6` |
| `DeltaValue` | `fixed` | `10` |
- `Type` is a Light Function enumeration.
### Side Texture ###
Side Texture is 6 bytes. Just stores a texture and an offset.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `OffsetX` | `unit` | `0` |
| `OffsetY` | `unit` | `2` |
| `TextureId` | `u16opt` | `4` |
- `TextureId` references a Shapes bitmap.
### Point ###
Point is 4 bytes. A geometric point.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `PosX` | `unit` | `0` |
| `PosY` | `unit` | `2` |
### Endpoint ###
Endpoint is 16 bytes. A point structure which can be loaded directly into memory
instead of being calculated at runtime.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Flags` | `u16` | `0` |
| `HeightHi` | `unit` | `2` |
| `HeightLo` | `unit` | `4` |
| `Position` | `struct` | `6` |
| `Support` | `u16` | `14` |
- `Flags` is an Endpoint Flags bit field.
- `HeightHi` and `HeightLo` are the highest adjacent ceiling and lowest adjacent
floor heights, respectively.
- `Position` is a Point structure.
- `Support` is the index of the highest adjacent polygon.
### Line ###
Line is 32 bytes. A geometric line segment.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `PointBeg` | `u16` | `0` |
| `PointEnd` | `u16` | `2` |
| `Flags` | `u16` | `4` |
| `Length` | `unit` | `6` |
| `HeightHi` | `unit` | `8` |
| `HeightLo` | `unit` | `10` |
| `SideFrnt` | `u16opt` | `12` |
| `SideBack` | `u16opt` | `14` |
| `PolyFrnt` | `u16opt` | `16` |
| `PolyBack` | `u16opt` | `18` |
- `PointBeg` and `PointEnd` are the beginning and terminating endpoints.
- `Flags` is a Line Flags bit field.
- `HeightHi` and `HeightLo` are the highest adjacent ceiling and lowest adjacent
floor heights, respectively.
- `SideFrnt` and `SideBack` are indices of the Sides in the front and back.
- `PolyFrnt` and `PolyBack` are indices of the connected Polygons.
### Side ###
Side is 64 bytes. One possibly textured side of a line segment.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Type` | `u16` | `0` |
| `Flags` | `u16` | `2` |
| `TexPri` | `struct` | `4` |
| `TexSec` | `struct` | `10` |
| `TexTra` | `struct` | `16` |
| `ExTopL` | `struct` | `22` |
| `ExTopR` | `struct` | `26` |
| `ExBotL` | `struct` | `30` |
| `ExBotR` | `struct` | `34` |
| `PanelType` | `u16` | `38` |
| `PanelPerm` | `i16` | `40` |
| `XferPri` | `u16` | `42` |
| `XferSec` | `u16` | `44` |
| `XferTra` | `u16` | `46` |
| `Shade` | `fixed` | `48` |
- `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. 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. If
`DataVersion` is `DataM1`, this must be set to 0.
### Polygon ###
Polygon is 128 bytes. A geometric polygon, essentially Doom's "sector," but must
be convex. More similar to a "subsector" since you have to split it 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` |
| `Flags` | `u16` | `2` |
| `Permutation` | `i16` | `4` |
| `VtxNum` | `u16` | `6` |
| `VtxArray` | `u16[8]` | `8` |
| `LinArray` | `u16[8]` | `24` |
| `TexFlr` | `u16` | `40` |
| `TexCei` | `u16` | `42` |
| `HeightFlr` | `unit` | `44` |
| `HeightCei` | `unit` | `46` |
| `LightFlr` | `u16` | `48` |
| `LightCei` | `u16` | `50` |
| `Area` | `i32` | `52` |
| `ObjectFst` | `u16` | `56` |
| `ZoneFst` | `u16` | `58` |
| `ZoneNumLin` | `u16` | `60` |
| `ZoneNumVtx` | `u16` | `62` |
| `XferFlr` | `u16` | `64` |
| `XferCei` | `u16` | `66` |
| `Adjacent` | `u16[8]` | `68` |
| `NeighborFst` | `u16` | `84` |
| `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` |
| `MediaLight` | `u16` | `118` |
| `SoundIndices` | `u16` | `120` |
| `SoundAmbient` | `u16opt` | `122` |
| `SoundRandom` | `u16opt` | `124` |
- `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`.
- `Center` is a Point structure.
- `OrigFlr` is a Point structure for the texture offset of the floor.
- `OrigCei` is a Point structure for the texture offset of the ceiling.
### Light ###
Light is 100 bytes. If `DataVersion` is `DataM1` this is an Old Light.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Type` | `u16` | `0` |
| `Flags` | `u16` | `2` |
| `Phase` | `i16` | `4` |
| `ActivPri` | `struct` | `6` |
| `ActivSec` | `struct` | `20` |
| `ActivMid` | `struct` | `34` |
| `InactPri` | `struct` | `48` |
| `InactSec` | `struct` | `62` |
| `InactMid` | `struct` | `76` |
| `Tag` | `u16` | `90` |
- `Type` is a Light Type enumeration.
- `Flags` is a Light Flags bit field.
- `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.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Type` | `u16` | `0` |
| `Location` | `struct` | `2` |
| `Polygon` | `u16` | `6` |
| `Text` | `u8[64]` | `8` |
- `Type` is an index into the annotation type definition, but there's only one,
so this will always be `0` anyway.
### Object ###
Object is 16 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Group` | `u16` | `0` |
| `Index` | `u16` | `2` |
| `Angle` | `angle` | `4` |
| `Polygon` | `u16` | `6` |
| `PosX` | `unit` | `8` |
| `PosY` | `unit` | `10` |
| `PosZ` | `unit` | `12` |
| `Flags` | `u16` | `14` |
- `Flags` is a Map Object Flags bit field, and the upper 4 bits are the
activation bias for monsters.
### Object Frequency ###
Object Frequency is 12 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Flags` | `u16` | `0` |
| `CountInit` | `u16` | `2` |
| `CountMin` | `u16` | `4` |
| `CountMax` | `u16` | `6` |
| `CountRand` | `u16` | `8` |
| `Chance` | `u16` | `10` |
- `Flags` is an Object Frequency Flags bit field.
### Platform Data ###
Platform Data is 32 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Type` | `u16` | `0` |
| `Speed` | `u16` | `2` |
| `Delay` | `u16` | `4` |
| `HeightMax` | `unit` | `6` |
| `HeightMin` | `unit` | `8` |
| `Flags` | `u32` | `10` |
| `Index` | `u16` | `14` |
| `Tag` | `u16` | `16` |
- `Type` is a Platform Type enumeration.
- `Index` is the polygon this platform is attached to.
- `Flags` is a Static Platform Flags bit field.
### Ambient Sound ###
Ambient Sound is 16 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Index` | `u16` | `2` |
| `Volume` | `u16` | `4` |
- `Volume` is the volume of this sound, in range 0-256.
### Random Sound ###
Random Sound is 32 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Flags` | `u16` | `0` |
| `Index` | `u16` | `2` |
| `Volume` | `u16` | `4` |
| `DeltaVolume` | `u16` | `6` |
| `Period` | `u16` | `8` |
| `DeltaPeriod` | `u16` | `10` |
| `Angle` | `angle` | `12` |
| `DeltaAngle` | `angle` | `14` |
| `Pitch` | `fixed` | `16` |
| `DeltaPitch` | `fixed` | `20` |
| `Phase` | `u16` | `24` |
- `Flags` is a Random Sound Flags bit field.
- `Phase` must be `65535`.
### Media Data ###
Media Data is 32 bytes. "Media" refers to liquids, presumably because of the
plural of the definition of "medium," a "material which waves pass through," in
this case liquid. (This is likely the case of whoever programmed this system
learning physics before rendering, or conflating the two.)
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Type` | `u16` | `0` |
| `Flags` | `u16` | `2` |
| `Control` | `u16` | `4` |
| `Direction` | `angle` | `6` |
| `Magnitude` | `unit` | `8` |
| `Low` | `unit` | `10` |
| `High` | `unit` | `12` |
| `Origin` | `struct` | `14` |
| `Height` | `unit` | `18` |
| `Minimum` | `fixed` | `20` |
| `Texture` | `u16opt` | `24` |
| `XferMode` | `u16` | `26` |
- `Type` is a Media Type enumeration.
- `Flags` is a Media Flags bit field.
- `Control` is the index of a light which is used to control the height of this
media.
- `XferMode` is a Transfer Mode enumeration.
### Static Map Info ###
Static Map Info is 88 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `TextureId` | `u16` | `0` |
| `PhysicsId` | `u16` | `2` |
| `LandscapeId` | `u16` | `4` |
| `MissionFlags` | `u16` | `6` |
| `EnvFlags` | `u16` | `8` |
| `Name` | `u8[66]` | `18` |
| `EntryFlags` | `u32` | `84` |
- `TextureId` is a Texture Collection enumeration. It is a preset number for
texture collections and some other things such as media (liquid) presets.
- `PhysicsId` used to be used for specifying three physics models: one for the
Forge editor, one for the game, and one for low-gravity. However, the first
one ended up being useless and the third was made into a map flag instead
which simply modifies the current physics model. This should always be set to
`1` by new editors.
- `LandscapeId` is a Landscape enumeration. It is the landscape number to use
for the sky. This starts at `0`, since it's an offset into the landscape
collections. If `DataVersion` is `DataM1` or the `Music` flag of `EnvFlags` is
set, then this is used as the music index instead.
- `MissionFlags` is a Mission Flags bit field.
- `EnvFlags` is an Environment Flags bit field.
- `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. If `DataVersion` is `DataM1` and
this is `0`, this implies the value is `Solo`.
## Terminal ##
### Terminal ###
Terminal text can be encoded with some weird xor bullshit for some reason. You
can decode it and encode it with the same method:
```c
for(i = 0; i < len / 4; i++) {p += 2; *p++ ^= 0xFE; *p++ ^= 0xED;}
for(i = 0; i < len % 4; i++) *p++ ^= 0xFE;
```
- Terminal Header
- Terminal Groups
- Text Faces
- Terminal text (null byte terminated)
### Terminal Header ###
Terminal Header is 10 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Size` | `u16` | `0` |
| `Flags` | `u16` | `2` |
| `PageLines` | `u16` | `4` |
| `NumGroups` | `u16` | `6` |
| `NumFaces` | `u16` | `8` |
- `Size` is the total length of the terminal, including this header.
- `Flags` is a Terminal Flags bit field.
- `PageLines` is the number of lines per page, almost always 22.
### Terminal Group ###
Terminal Group is 12 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Flags` | `u16` | `0` |
| `Type` | `u16` | `2` |
| `Permutation` | `i16` | `4` |
| `TextOfs` | `u16` | `6` |
| `TextSize` | `u16` | `8` |
| `MaxLineCount` | `u16` | `10` |
- `Flags` is a Terminal Group Flags bit field.
- `Type` is a Terminal Group Type enumeration.
### Text Face ###
Text Face is 6 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `TextOfs` | `u16` | `0` |
| `Face` | `u16` | `2` |
| `Color` | `u16` | `4` |
- `Color` is a Terminal Color enumeration.
## Physics ##
### Physics Definition ###
Physics Definition is 104 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `VelForw` | `fixed` | `0` |
| `VelBack` | `fixed` | `4` |
| `VelPerp` | `fixed` | `8` |
| `Accel` | `fixed` | `12` |
| `Decel` | `fixed` | `16` |
| `DecelAir` | `fixed` | `20` |
| `AccelGravity` | `fixed` | `24` |
| `AccelClimb` | `fixed` | `28` |
| `VelTerminal` | `fixed` | `32` |
| `DecelExtern` | `fixed` | `36` |
| `AccelAngular` | `fixed` | `40` |
| `DecelAngular` | `fixed` | `44` |
| `VelAngular` | `fixed` | `48` |
| `VelRecenter` | `fixed` | `52` |
| `FastVelAng` | `fixed` | `56` |
| `FastVelMax` | `fixed` | `60` |
| `Elevation` | `fixed` | `64` |
| `DecelAngExt` | `fixed` | `68` |
| `StepDelta` | `fixed` | `72` |
| `StepAmp` | `fixed` | `76` |
| `PlayerRadius` | `fixed` | `80` |
| `PlayerHeight` | `fixed` | `84` |
| `PlayerDeadHi` | `fixed` | `88` |
| `PlayerCamHi` | `fixed` | `92` |
| `PlayerSplash` | `fixed` | `96` |
| `HalfCamSep` | `fixed` | `100` |
### Effect Definition ###
Effect Definition is 14 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Collection` | `u16` | `0` |
| `Shape` | `u16` | `2` |
| `Pitch` | `fixed` | `4` |
| `Flags` | `u16` | `8` |
| `Delay` | `u16` | `10` |
| `DelaySound` | `u16` | `12` |
- `Flags` is an Effect Definition Flags bit field.
### Weapon Definition ###
Weapon Definition is 134 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `ItemType` | `u16` | `0` |
| `PowerupType` | `u16` | `2` |
| `WeaponClass` | `u16` | `4` |
| `Flags` | `u16` | `6` |
| `LightValue` | `fixed` | `8` |
| `LightDecay` | `u16` | `12` |
| `HeightIdle` | `fixed` | `14` |
| `AmpBob` | `fixed` | `18` |
| `HeightKick` | `fixed` | `22` |
| `HeightReload` | `fixed` | `26` |
| `WidthIdle` | `fixed` | `30` |
| `AmpHorz` | `fixed` | `34` |
| `Collection` | `u16` | `38` |
| `FrameIdle` | `u16` | `40` |
| `FrameFiring` | `u16` | `42` |
| `FrameReload` | `u16` | `44` |
| `FrameCharge` | `u16` | `48` |
| `FrameCharged` | `u16` | `50` |
| `TicksReady` | `u16` | `52` |
| `TicksLoadBeg` | `u16` | `54` |
| `TicksLoadMid` | `u16` | `56` |
| `TicksLoadEnd` | `u16` | `58` |
| `TicksPowerup` | `u16` | `60` |
| `TriggerPri` | `struct` | `62` |
| `TriggerSec` | `struct` | `98` |
- `WeaponClass` is a Weapon Type enumeration.
- `Flags` is a Weapon Flags bit field.
- `TriggerPri` and `TriggerSec` are Trigger Definition structures used for the
primary and secondary fire buttons.
### Trigger Definition ###
Trigger Definition is 36 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `MagRounds` | `u16` | `0` |
| `AmmoType` | `u16` | `2` |
| `TicksRound` | `u16` | `4` |
| `TicksRecover` | `u16` | `6` |
| `TicksCharge` | `u16` | `8` |
| `Recoil` | `unit` | `10` |
| `SoundFire` | `u16` | `12` |
| `SoundClick` | `u16` | `14` |
| `SoundCharge` | `u16` | `16` |
| `SoundCasing` | `u16` | `18` |
| `SoundReload` | `u16` | `20` |
| `SoundCharged` | `u16` | `22` |
| `Projectile` | `u16` | `24` |
| TODO | `u16` | `26` |
| TODO | `i16` | `28` |
| TODO | `i16` | `30` |
| `CasingType` | `u16` | `32` |
| `BurstCount` | `u16` | `34` |
- `CasingType` is a Casing Type enumeration.
### Projectile Definition ###
Projectile Definition is 48 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Collection` | `u16opt` | `0` |
| `Shape` | `u16` | `2` |
| `FxExplode` | `u16opt` | `4` |
| `FxExplodeMed` | `u16opt` | `6` |
| `FxTrail` | `u16opt` | `8` |
| `TicksTrail` | `u16` | `10` |
| `MaxTrails` | `u16opt` | `12` |
| `MediaType` | `u16opt` | `14` |
| `Radius` | `unit` | `16` |
| `AreaOfEffect` | `unit` | `18` |
| `Damage` | `struct` | `20` |
| `Flags` | `u32` | `32` |
| `Speed` | `unit` | `36` |
| `Range` | `unit` | `38` |
| `SndPitch` | `fixed` | `40` |
| `SndFly` | `u16opt` | `44` |
| `SndBounce` | `u16opt` | `46` |
- `MediaType` is the type of projectile this becomes when below media.
- `Damage` is a Damage Definition structure.
- `Flags` is a Projectile Flags bit field.
### Monster Definition ###
Monster Definition is 156 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Collection` | `u16` | `0` |
| `Vitality` | `u16` | `2` |
| `ImmuneTo` | `u32` | `4` |
| `WeakTo` | `u32` | `8` |
| `Flags` | `u32` | `12` |
| `MonsterClass` | `u32` | `16` |
| `FriendTo` | `u32` | `20` |
| `EnemyTo` | `u32` | `24` |
| `SndPitch` | `fixed` | `28` |
| `SndSeeEnemy` | `u16opt` | `32` |
| `SndSeeFriend` | `u16opt` | `34` |
| `SndSeeClear` | `u16opt` | `36` |
| `SndKill` | `u16opt` | `38` |
| `SndApologize` | `u16opt` | `40` |
| `SndAmicide` | `u16opt` | `42` |
| `SndFlaming` | `u16opt` | `44` |
| `SndActive` | `u16opt` | `46` |
| `ActiveMask` | `u16` | `48` |
| `DropItem` | `u16opt` | `50` |
| `Radius` | `unit` | `52` |
| `Height` | `unit` | `54` |
| `HoverHeight` | `unit` | `56` |
| `LedgeMin` | `unit` | `58` |
| `LedgeMax` | `unit` | `60` |
| `ExtVelScale` | `fixed` | `62` |
| `FxImpact` | `u16opt` | `66` |
| `FxMeleeImpact`| `u16opt` | `68` |
| `FxTrail` | `u16opt` | `70` |
| `HalfFOVHorz` | `u16` | `72` |
| `HalfFOVVert` | `u16` | `74` |
| `ViewRange` | `unit` | `76` |
| `ViewRangeDark`| `unit` | `78` |
| `Intelligence` | `u16` | `80` |
| `Speed` | `u16` | `82` |
| `Gravity` | `u16` | `84` |
| `TerminalVel` | `u16` | `86` |
| `DoorTryMask` | `u16` | `88` |
| `ExplodeRadius`| `u16opt` | `90` |
| `ExplodeDamage`| `struct` | `92` |
| `SeqHit` | `u16opt` | `104` |
| `SeqHardDying` | `u16opt` | `106` |
| `SeqSoftDying` | `u16opt` | `108` |
| `SeqHardDead` | `u16opt` | `110` |
| `SeqSoftDead` | `u16opt` | `112` |
| `SeqStanding` | `u16` | `114` |
| `SeqMoving` | `u16` | `116` |
| `SeqTeleIn` | `u16opt` | `118` |
| `SeqTeleOut` | `u16opt` | `120` |
| `AtkFrequency` | `u16` | `122` |
| `AtkMelee` | `struct` | `124` |
| `AtkRange` | `struct` | `140` |
- `Flags` is a Monster Flags bit field.
- `MonsterClass` is a Monster Class bit field.
- `ExplodeDamage` is a Damage Definition structure.
- `AtkMelee` and `AtkRange` are Attack Definition structures.
### Damage Definition ###
Damage Definition is 12 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Type` | `u16` | `0` |
| `Flags` | `u16` | `2` |
| `DmgBase` | `u16` | `4` |
| `DmgRand` | `u16` | `6` |
| `Scale` | `fixed` | `8` |
- `Type` is a Damage Type enumeration.
- `Flags` is a Damage Flags enumeration.
### Attack Definition ###
Attack Definition is 16 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Type` | `u16opt` | `0` |
| `Repetitions` | `u16` | `2` |
| `Error` | `angle` | `4` |
| `Range` | `unit` | `6` |
| `Shape` | `u16` | `8` |
| `OfsX` | `unit` | `10` |
| `OfsY` | `unit` | `12` |
| `OfsZ` | `unit` | `14` |
## Images ##
### Picture Resource ###
Pictures are formed with a header and then a variable number of operations. In
other words, a small state machine is used to form an image through effects and
various fill instructions. QuickDraw is horrifying. This is the native image
format. It's a fucking metafile. I suppose this could be worse, considering they
later used PDF files for images.
- Picture Header
- Picture Opcodes
### Picture Header ###
All QuickDraw PICTs begin with a basic 10 byte header as follows.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Size` | `u16` | `0` |
| `Top` | `u16` | `2` |
| `Left` | `u16` | `4` |
| `Height` | `u16` | `6` |
| `Width` | `u16` | `8` |
### CopyBits ###
CopyBits has a variable size. Offsets are not provided as they are variable,
sequential to the current code path.
If direct copy:
| Name | Type | Ignored |
| ---- | ---- | :-----: |
| `BaseAddr` | `u32` | Yes |
Always:
| Name | Type |
| ---- | ---- |
| `PitchFl` | `u16` |
| `Top` | `u16` |
| `Left` | `u16` |
| `Bottom` | `u16` |
| `Right` | `u16` |
- `PitchFl` is the number of bytes per row, and the upper two bits are a
CopyBits Flags bit field.
If PICT2:
| Name | Type |
| ---- | ---- |
| `PixMap` | `struct` |
- `PixMap` is a PixMap structure.
Otherwise, assume pack type is default and bit depth is 1.
If packed:
| Name | Type | Ignored |
| ---- | ---- | :-----: |
| `CLUTIden` | `u32` | Yes |
| `CLUTFlags` | `u16` | No |
| `CLUTNum` | `u16` | No |
| `CLUT` | `struct[]` | No |
- `CLUTFlags` is a Color Table Flags bit field.
- `CLUT` is an array of `CLUTNum` Color Table structures.
Always:
| Name | Type | Ignored |
| ---- | ---- | :-----: |
| `SrcTop` | `u16` | Yes |
| `SrcLeft` | `u16` | Yes |
| `SrcBottom` | `u16` | Yes |
| `SrcRight` | `u16` | Yes |
| `DstTop` | `u16` | Yes |
| `DstLeft` | `u16` | Yes |
| `DstBottom` | `u16` | Yes |
| `DstRight` | `u16` | Yes |
| `XferMode` | `u16` | Yes |
If clip:
| Name | Type | Ignored |
| ---- | ---- | :-----: |
| `ClipRgn` | `struct` | Yes |
- `ClipRgn` is skipped the same as opcode `$0001` in Aleph One.
Image data follows this header.
### PixMap ###
PixMap is 36 bytes.
| Name | Type | Ignored |
| ---- | ---- | :-----: |
| `Version` | `u16` | Yes |
| `PackType` | `u16` | No |
| `PackSize` | `u32` | Yes |
| `HorzDPI` | `u32` | Yes |
| `VertDPI` | `u32` | Yes |
| `Format` | `u16` | Yes |
| `BitDepth` | `u16` | No |
| `Components` | `u16` | Yes |
| `CompDepth` | `u16` | Yes |
| `PlaneOffs` | `u32` | Yes |
| `ClutId` | `u32` | Yes |
### Color Table ###
Color Table is 8 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Index` | `u16` | `0` |
| `Red` | `u16` | `2` |
| `Green` | `u16` | `4` |
| `Blue` | `u16` | `6` |
- `Index` is ignored if device mapping is used.
### Header Op ###
Header Op is 24 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Version` | `u16` | `0` |
| `Width` | `u32` | `4` |
| `Height` | `u32` | `8` |
| `Top` | `u16` | `12` |
| `Left` | `u16` | `14` |
| `Bottom` | `u16` | `16` |
| `Right` | `u16` | `18` |
## Shapes ##
Shapes files start with exactly 32 Collection Headers, followed by everything
else. Use the offsets provided by these structures to find where all of the data
is.
### Collection Header ###
Collection Header is 32 bytes. Each collection may have two versions, lo-res and
hi-res, which are used according to the user's settings. The main purpose of
these is for compatibility with older Macs which might not have enough video
memory for, for instance, the huge two mebibyte sky boxes that Marathon 2 has.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `OffsetLo` | `u32` | `4` |
| `LengthLo` | `u32` | `8` |
| `OffsetHi` | `u32` | `12` |
| `LengthHi` | `u32` | `16` |
### Collection Definition ###
Collection Definition is 544 bytes (no, I'm not kidding, there are actually that
many unused bytes.) The sequences, frames and bitmaps have their individual
offsets stored in tables, which themselves are at offsets specified by this
structure. Note that all offsets (including those in offset tables) are relative
to the start of this structure.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Version` | `u16` | `0` |
| `Type` | `u16` | `2` |
| `Colors` | `u16` | `6` |
| `TabNum` | `u16` | `8` |
| `TabOfs` | `u16` | `10` |
| `SeqNum` | `u16` | `12` |
| `SeqOfs` | `u16` | `14` |
| `FrmNum` | `u16` | `16` |
| `FrmOfs` | `u16` | `18` |
| `BmpNum` | `u16` | `20` |
| `BmpOfs` | `u16` | `22` |
- `Version` should always be `3`, but this isn't checked by the engine.
- `Type` is a Collection Type enumeration.
- `TabNum` and `TabOfs` are the number of and offset to the color tables.
- `SeqNum` and `SeqOfs` are the number of sequences and the offset to their
offset table.
- `FrmNum` and `FrmOfs` are the number of frames and the offset to their offset
table.
- `BmpNum` and `BmpOfs` are the number of bitmaps and the offset to their offset
table.
### Frame ###
Frame is 36 bytes. TODO: document how world transform works.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Flags` | `u16` | `0` |
| `MinLight` | `fixed` | `2` |
| `BmpIndex` | `u16opt` | `6` |
| `WldLeft` | `unit` | `14` |
| `WldRight` | `unit` | `16` |
| `WldTop` | `unit` | `18` |
| `WldBottom` | `unit` | `20` |
| `WldOrigX` | `unit` | `22` |
| `WldOrigY` | `unit` | `24` |
- `Flags` is a Frame Flags bit field.
- `MinLight` is the minimum light intensity (0-1.)
### Sequence ###
Sequence is 88 bytes. A sequence, also known as a "high level shape" in the
engine, is essentially a potentially animated sequence of frames organized into
angular views which may loop or play sounds. Each sequence has a "key frame"
which is used for determining when to run action code or play an attached sound.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Name` | `u8[34]` | `4` |
| `ViewType` | `u16` | `38` |
| `FrameNum` | `u16` | `40` |
| `FrameTick` | `u16` | `42` |
| `FrameKey` | `u16` | `44` |
| `XferMode` | `u16` | `46` |
| `XferPeriod` | `u16` | `48` |
| `SndBeg` | `u16` | `50` |
| `SndKey` | `u16` | `52` |
| `SndEnd` | `u16` | `54` |
| `FrameLoop` | `u16` | `58` |
- `Name` is the sequence name, used mainly by editors, supposed to be 33 bytes,
but due to padding, the real length is 34.
- `ViewType` is a View Type enumeration.
- `FrameNum` is the number of frames in this sequence.
- `FrameTick` is the number of ticks per frame.
- `FrameKey` is the index of the key frame.
- `XferMode` is a Transfer Mode enumeration.
- `XferPeriod` is the period for the transfer mode in 1/30ths seconds.
- `SndBeg`, `SndKey` and `SndEnd` are the sounds played at the first, key and
last frame of this sequence.
### Bitmap Header ###
Bitmap Header is 26 bytes.
Each Bitmap Header is followed by either `Height * 4` or `Width * 4` empty bytes
which must be skipped.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Width` | `u16` | `0` |
| `Height` | `u16` | `2` |
| `Pitch` | `u16` | `4` |
| `Flags` | `u16` | `6` |
| `Depth` | `u16` | `8` |
- `Width` is the number of pixels on the horizontal axis.
- `Height` is the number of pixels on the vertical axis.
- `Pitch` is either the number of pixels per row if row ordered, per column if
column ordered, or `65535` if the data is transparency RLE compressed.
- `Flags` is a Bitmap Flags bit field.
- `Depth` must always be `8`.
## Sounds ##
Sounds files start with a header followed by all of the actual sound
definitions. Each sound starts with a Carbon Sound Header.
### Sounds Header ###
Sounds Header is 260 bytes. (Seriously, what's with these formats and
horrifically excessive padding?)
This header is followed by `SrcNum * SndNum` Sound Definitions.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Version` | `u32` | `0` |
| `Magic` | `u8[4]` | `4` |
| `SrcNum` | `u16` | `8` |
| `SndNum` | `u16` | `10` |
- `Version` is always `1`.
- `Magic` is always `"snd2"`.
- `SrcNum` defines the number of sound formats this file provides, similar to
Shapes' lo- and hi-res collections, although this applies to the entire file
and not just parts of it. `0` is invalid for this field, and in the original
Marathon 2 engine (on Mac only, not on Windows,) it must be `2` because of
QuickTime. A value of `1` means "8-bit 22kHz only" and a value of `2` means
"both 8-bit 22kHz and 16-bit 22kHz."
### Sound Definition ###
Sound Definition is 64 bytes.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Code` | `u16opt` | `0` |
| `Volume` | `u16` | `2` |
| `Flags` | `u16` | `4` |
| `Chance` | `u16` | `6` |
| `PitchLo` | `fixed` | `8` |
| `PitchHi` | `fixed` | `12` |
| `NumOfs` | `u16` | `16` |
| `GroupOffset` | `u32` | `20` |
| `Size` | `u32` | `24` |
| `GroupSize` | `u32` | `28` |
| `AddOffset` | `u32[5]` | `32` |
- `Volume` is a Sound Behaviour enumeration.
- `Flags` is a Sound Definition Flags bit field.
- `Chance` is the chance out of `65535` that the sound will not play.
- `PitchLo` is the lower random pitch bound, if `0` then it will be `1.0`.
- `PitchHi` is the high random pitch bound, if `0` then it will be `PitchLo`.
- `NumOfs` is the number of random sounds to pick from `AddOffset`.
- `GroupOffset` is the starting offset for each additive sound offset.
- `Size` is the sound of an individual sound in the group.
- `GroupSize` is the total size of all sounds in the group.
- `AddOffset` is the offset added to `GroupOffset` to get an individual sound.
While it is an array of `NumOfs` offsets, it has a fixed size in the format.
### Carbon Sound Header ###
Carbon Sound Header is 21 bytes.
The sound format is from Carbon's `SoundHeader` structures. It's used primarily
in System 7 programs as `snd` resources but in OS X it was deprecated in favor
of QuickTime. HFS still has Resource Forks but they aren't used anymore. I don't
imagine this format was ever used for anything else, except for Marathon, which
embeds it in the Sound files directly, instead of using `snd` resources (which
have a larger structure consisting of a resource header and sound commands
rather than just the header and sample data.)
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Size` | `u32` | `4` |
| `SampleRate` | `u16` | `8` |
| `LoopBeg` | `u32` | `12` |
| `LoopEnd` | `u32` | `16` |
| `Magic` | `u8` | `20` |
- If `Magic` is `$00` nothing else needs to be done and raw unsigned 8-bit mono
PCM sample data starts at byte 22. If it is `$FF` it is followed by a Carbon
Extended Sound Header, or if it is `$FE` it is followed by a Carbon Compressed
Sound Header. The compressed sound header is not documented because it is not
actually used by Marathon.
### Carbon Extended Sound Header ###
Carbon Extended Sound Header is 42 bytes.
The extended sound header contains more useless information and even several
fields that do absolutely nothing. Wow. At least it can store 16 bit samples. It
also has an 80-bit float in it, which horrifies me greatly. There's only one
actually useful field.
| Name | Type | Offset |
| ---- | ---- | ------ |
| `Frames` | `u32` | `0` |
| `SampleBits` | `u16` | `26` |
- `Frames` is used instead of `Size` for sounds with this header, since it
represents how many frames there are rather than how many bytes. Even though
this is actually a pointless distinction and `Size` is left at `1` anyway.
- `SampleBits` must be either `16` or `8`. If it is `16` then the sample data is
signed 16-bit *little endian* mono PCM.
<!-- EOF -->