Maraiah/doc/data/3-struct.md

42 KiB

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

  • AppDatabytes. Following this structure isAppData` 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 u16opts 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:

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.