1179 lines
42 KiB
Markdown
1179 lines
42 KiB
Markdown
# 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 -->
|