Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

RGM Scene File Format

Scene/map container with sectioned records for placed objects, script metadata, and auxiliary world data. The RA*-prefixed sections (RASC, RAHD, RAAT, RAHK, etc.) form the per-map SOUP scripting layer — see SOUP Scripting for a consolidated map of all script data sources and runtime boundaries.

Section Framing

Each section starts with an 8-byte header. Some sections then include a 4-byte little-endian record_count word at the beginning of section data.

OffsetSizeTypeEndianNameDescription
0x004[u8; 4]—section_nameASCII section tag (for example RAHD, MPOB, MPSO, END )
0x044u32BEdata_lengthPayload size in bytes (0 for END ). Big-endian in section-framed formats (RGM, PVO, ROB, TEXBSI).

For count-prefixed sections (MPOB, MPSO, MPRP, and several others), section payload begins with:

Little-endian.

Relative OffsetSizeTypeNameDescription
+0x004u32record_countNumber of fixed-size records in this section

Sections are parsed sequentially until END .

MPOB (Object Instances)

MPOB starts with a little-endian object count, followed by 66-byte records.

All fields little-endian.

OffsetSizeTypeNameDescription
0x004u32idObject id
0x041u8object_typeObject kind discriminator
0x051u8is_activeActivation flag
0x069[u8; 9]script_nameScript/object name
0x0F9[u8; 9]model_nameModel reference
0x181u8is_staticFor a type-1 placed-model fallback, 1 selects a static model; other values give the animation frame-table length. See placed-object frame sources.
0x192i16reservedNot read at runtime.
0x1B3i24pos_xPosition X (fixed scale)
0x1E1u8pad_xAlignment byte
0x1F3i24pos_yPosition Y (fixed scale)
0x221u8pad_yAlignment byte
0x233u24pos_zPosition Z (fixed scale)
0x264u32angle_xBethesda 2048-unit Euler angle
0x2A4u32angle_yBethesda 2048-unit Euler angle
0x2E4u32angle_zBethesda 2048-unit Euler angle
0x322i16texture_dataPacked texture id/image id
0x342i16intensityLight/intensity-like field
0x362i16radiusRadius-like field
0x382i16model_idModel index/id-like field
0x3A2i16world_idWorld index/id-like field
0x3C2i16redColor channel
0x3E2i16greenColor channel
0x402i16blueColor channel

Position decode used by current exporter:

  • scale constant: 1 / 5120
  • x = -(pos_x * 256) * scale
  • y = -(pos_y * 256) * scale
  • z = -(0x00FF_FFFF - (pos_z * 256)) * scale

Bethesda 2048-unit Euler angles:

2048 discrete units represent a full 360° rotation — a power-of-two binary angle encoding. The raw u32 is reduced modulo 2048 (equivalently masked with & 0x7FF), giving a value in the range [0, 2047]. Each unit equals 180/1024 ≈ 0.176°.

degrees = (value % 2048) * (180.0 / 1024.0)
UnitsDegrees
00°
51290°
1024180°
1536270°
2048360° (wraps to 0)

The original type-1 placement fallback and its message-group assignment are described in actor initialization.

MPOB exporter model lookup behavior:

  • Primary source is model_name (9 bytes, null-trimmed).
  • If model_name is empty, exporter falls back to RAAN using RAHD script metadata (see RAAN).
  • If RAAN also yields no result, script_name is used as a last resort.
  • Example: script_name = FAVIS resolves via RAAN to FVPRA001.

MPSO uses a 12-byte model_name field (no fallback chain — model name is always present).

MPSO (Static Objects)

MPSO starts with a little-endian object count, followed by 66-byte records.

All fields little-endian.

OffsetSizeTypeNameDescription
0x004u32idObject id
0x0412[u8; 12]model_nameModel reference
0x103i24pos_xPosition X
0x131u8pad_xAlignment byte
0x143i24pos_yPosition Y
0x171u8pad_yAlignment byte
0x183u24pos_zPosition Z
0x1B1u8pad_zAlignment byte
0x1C36i32[9]rotation_matrix3x3 Q4.28 rotation matrix
0x402u8[2]unusedAlways 0.

Matrix entries are row-major: row 0 is [m0, m1, m2], row 1 is [m3, m4, m5], and row 2 is [m6, m7, m8]. Loading converts each signed Q4.28 entry to a float in place without transposition or normalization. The matrix maps local column vectors into world coordinates, before adding the placement position. Camera transformation multiplies this matrix on the left.

MPRP (Rope Chains)

MPRP starts with a little-endian record count, followed by 80-byte records.

All fields little-endian.

OffsetSizeTypeNameDescription
0x004u32idRope/object id
0x041u8reservedNot read at runtime.
0x053i24pos_xBase position X
0x081u8pad_xAlignment byte
0x093i24pos_yBase position Y
0x0C1u8pad_yAlignment byte
0x0D3i24pos_zBase position Z
0x104i32angle_yRope heading field
0x144i32typeType/discriminator
0x184i32swingSwing parameter
0x1C4i32speedSpeed parameter
0x202i16lengthNumber of rope links
0x229[u8; 9]static_modelOptional terminal model
0x2B9[u8; 9]rope_modelLink model name (for example ROPELINK)
0x344i32sound_1Rope effect for low swing speed; 0 = none.
0x384i32sound_2Rope effect for medium swing speed; 0 = none.
0x3C4i32sound_3Rope effect for high swing speed; 0 = none.
0x4016i32[4]unknownRead at runtime; zero in all 29 shipped records.

Rope instancing behavior:

  • Decode base translation with the same MPOB scale/sign rules.
  • Spawn length copies of rope_model. Every link and the end model are yawed by angle_y (0..2047, 2048 per turn) with the same convention as an MPOB object’s angle_y; pitch and roll are zero for a rope at rest.
  • Link 0 is placed at the base position; each following link is 0.8 lower in Y (the height of the link model, shipped ROPELINK and CHAINLNK are both 0.8 tall and centered).
  • If static_model is present, place one additional instance 0.8 below the last link.
  • Rope swing sounds use sound_1–sound_3; see positional sound.

Current parser behavior: MPRP is parsed into typed 80-byte records only when section payload is an exact fit for record_count; otherwise raw fallback is kept.

RALC (Location Data)

RALC contains scripted coordinate offsets for objects (e.g. the Boatman’s waypoints). Records are 12-byte entries.

All fields little-endian.

OffsetSizeTypeNameDescription
0x004s32offset_xX coordinate offset (applied to MPOB translated position)
0x044s32offset_yY coordinate offset
0x084s32offset_zZ coordinate offset

Actor setup first stores the original runtime home position as location 0, then appends RAHD.ralc_count entries starting at RAHD.ralc_offset. Each appended location is the home position plus its signed RALC offsets, with wrapping 32-bit addition. Home coordinates are the original MPOB position multiplied by 256, before family-local conversion. Authored offsets already use runtime coordinate units. Locations belong to each actor instance. See location movement.

RAVC (VCollide)

RAVC uses 9-byte entries and appears only in a subset of maps (CATACOMB and DRINT — collision data for the dragon and golem).

All fields little-endian.

OffsetSizeTypeNameDescription
0x001i8offset_xLocal collision offset X
0x011i8offset_yLocal collision offset Y
0x021i8offset_zLocal collision offset Z
0x032u16vertexModel vertex index used as reference point for the collision sphere
0x054u32radiusCollision sphere radius

Current parser behavior: records are parsed as fixed 9-byte entries only when section payload is an exact fit; otherwise raw fallback is kept. RAVC is flat-out missing (not just empty) in RGM files without collision objects.

WDNM (Walk Node Map)

WDNM defines walk node maps for AI pathfinding. Count-prefixed: record count is the number of walk-map blocks.

All fields little-endian.

WalkMap Record

Relative OffsetSizeTypeNameDescription
+0x004u32map_lengthPayload byte length after this length word
+0x044u32node_countNumber of walk-nodes in this map
+0x084u32node_count_dupDuplicate of node_count
+0x0C3s24map_pos_xMap position X
+0x0F1u8pad_xAlignment byte
+0x103s24map_pos_yMap position Y
+0x131u8pad_yAlignment byte
+0x143s24map_pos_zMap position Z
+0x171u8pad_zAlignment byte
+0x184u32radiusMap bounding radius
+0x1C…—walk_nodesnode_count × WalkNode records

The map length excludes its own four-byte length word. Its payload contains the remaining 24-byte map header and all length-prefixed nodes. Embedded maps are loaded in file order and prepended to the runtime map list, so traversal visits them in reverse file order.

WalkNode Record

Relative OffsetSizeTypeNameDescription
+0x004u32node_lengthPayload byte length after this length word
+0x042u16node_pos_xLocal position X
+0x062s16node_pos_yLocal position Y
+0x082u16node_pos_zLocal position Z
+0x0A1u8reservedFile value discarded; runtime storage holds node ownership.
+0x0B1u8route_countNumber of routes from this node
+0x0C…—routesroute_count × NodeRoute records

The node length excludes its own four-byte length word and equals 8 + 4 * route_count. Loading copies the packed node coordinates directly; the map header’s position is not added to them.

The loader clears the reserved byte before using the node. Runtime reservation sets that byte to the low byte of the owning actor identifier. An unowned node or a node already owned by the requesting actor can be acquired; another actor’s reservation prevents acquisition. Releasing a reservation clears the byte only when the releasing actor still owns it. The periodic sound-maintenance reset clears all node ownership bytes regardless of owner.

Runtime node selection

WDNM maps load in file order and each new map is prepended to the runtime map list. Runtime traversal therefore visits maps in reverse file order. Nodes within each map retain their file index order. Map identifiers are assigned in load order independently of runtime list position.

The nearest-node query traverses the loaded map list and each map’s nodes in index order. It compares the sum of absolute X, Y and Z differences, using unsigned X/Z and signed Y from the node record. The query uses unscaled node coordinate units.

Only distances strictly below 4096 qualify. Equal distances retain the first candidate. When a new candidate has distance strictly below 12, the query returns immediately, even if a later node would be closer. Otherwise it returns the best candidate after traversing all maps. With no qualifying node, it returns no map and node index -1. Node reservations do not filter this selection query.

NodeRoute Record (4 bytes)

Relative OffsetSizeTypeNameDescription
+0x002u16target_node_idDestination walk-node index
+0x022u16costRoute traversal cost

RAHD (Actor Header)

RAHD is a count-prefixed section with 165-byte records. Each record provides per-actor metadata: script name, bytecode location in RASC, string/variable table pointers, animation references, collision data, and attribute hooks.

Section payload starts with a 4-byte LE record count, followed by 4 fixed bytes (1B 80 37 00), then count × 165 bytes of records. The engine reads the count and prefix separately, then bulk-reads count × 165 bytes as the record array. Offsets below are within each 165-byte record (starting at payload offset 8 + i × 165). The Rust parser in this repo starts records 4 bytes earlier (at 4 + i × 165) and adds 4 to all field offsets.

At load time, the engine converts most offset fields into absolute pointers by adding the corresponding section’s data pointer (rebasing). Offset 0x00 is overwritten with a linked-list next-pointer at runtime.

All typed fields are little-endian.

OffsetSizeTypeNameDescription
0x004—field_00Overwritten at runtime (linked-list next-pointer)
0x049[u8; 9]script_nameScript/actor name, null-padded
0x0D2u16instancesNumber of instances for this actor
0x0F2—paddingAlways 0.
0x114i32instance_counterRuntime instance counter (incremented during setup; advances variable_offset by num_variables × 4 per instance)
0x154u8[4]anim_speedRead as individual bytes. Byte 0x15: frame limit — animation advances only while frame_counter < byte_0x15. Byte 0x16: frame increment added per tick.
0x194u32ranm_offsetByte offset into RANM section data (rebased to pointer at load)
0x1D4u32raat_offsetByte offset into RAAT section data (rebased to pointer at load)
0x214i32raan_countNumber of RAAN entries for this actor
0x254u32raan_data_sizeTotal byte size of this actor’s RAAN entries. Zero when raan_count = 0.
0x294i32raan_offsetByte offset into RAAN section data (rebased to pointer at load)
0x2D4—anim_control_prefixLow byte stored at animation control struct offset +0x12 during RAGR loading. The complete signed field is compared with −1 when selecting the static-model shortcut; see model loading.
0x314u32ragr_offsetByte offset into RAGR section data (rebased to pointer at load)
0x358—paddingAlways 0.
0x3D4u32rafs_indexIndex into RAFS data (vestigial — RAFS is 1 byte in all shipped files)
0x414u32num_stringsNumber of strings used by this actor’s script
0x454—paddingAlways 0.
0x494u32string_offsets_indexByte offset into RASB section data
0x4D4u32script_lengthByte length of this actor’s bytecode block in RASC
0x514u32script_data_offsetByte offset into RASC section data (rebased to pointer at load)
0x554u32script_pcExecution start address; rebased at load to script_data_offset + script_pc (absolute pointer)
0x594—anim_buffer_swapRead as byte at 0x59. Boolean: non-zero triggers animation frame buffer swap (copies between offsets +0xCB and +0x108 in actor struct). Zero uses primary buffer only.
0x5D4u32rahk_offsetByte offset into RAHK section data (rebased to pointer at load)
0x614i32ralc_countNumber of authored location offsets appended after the actor’s initial home location.
0x654—field_65Auxiliary actor-header data.
0x694u32ralc_offsetByte offset into RALC section data (rebased: ralc_data + (offset ÷ 12) × 12)
0x6D4u8[4]actor_flagsRead as individual bytes. Byte 0x6D: animation state ID loaded into a global during dialogue setup, compared against hook data at +0x247 for state matching. Byte 0x6E: item/equipment flag (toggled at runtime). Byte 0x6F: passed to animation/sound function.
0x714u32raex_offsetByte offset into RAEX section data (rebased to pointer at load)
0x754u32num_variablesNumber of local variables for this actor
0x794u8[4]visibility_flagsRead as individual bytes. Byte 0x79: visibility test bypass (non-zero = always visible, skip LOD culling). Byte 0x7B: LOD culling mode (0 = fixed distance threshold, non-zero = dynamic distance threshold).
0x7D4u32variable_offsetByte offset into RAVA section data (÷ 4 = variable array index)
0x814u32variable_offset_dupRuntime copy of variable_offset; advanced by num_variables × 4 per instance
0x854u32anim_frame_dataAnimation frame count or group index. Upper 16 bits used as count (× 11 bytes per frame for allocation).
0x894i32bounds_primaryPrimary MPSZ record index; −1 derives bounds from the model.
0x8D4i32bounds_secondarySecondary MPSZ record index; −1 = absent.
0x914i32bounds_tertiaryAlternate MPSZ record index; −1 = absent.
0x952i16combat_flagCombat/state flag.
0x972i16raex_statStored at actor state +0x97 after SOUP function lookup. -1 = disabled.
0x992i16reserved_99Always -1. Not read at runtime.
0x9B2i16texture_idActor texture set. Equal to the RAEX texture_id of the same actor in every shipped file; zero when the actor has none.
0x9D4i32ravc_offsetByte offset into RAVC section data (rebased to pointer at load; -1 = none)
0xA14i32ravc_countNumber of RAVC collision entries for this actor

Total record size: 165 bytes (0xA5).

RAAN (Animation File References)

RAAN contains animation/model file path entries. Records are variable-length null-terminated strings with a 6-byte prefix.

Entry structure at a given byte offset (from RAHD raan_offset):

OffsetSizeTypeNameDescription
0x004u32reservedNot read by any engine function. Skipped during entry iteration.
0x041u8frame_countUsed as loop count for animation handle table entries. Capped at 255.
0x051u8model_typeType flag, converted to lowercase at load. Values: 0x63 (ASCII ‘c’) and 0x73 (ASCII ‘s’).
0x06var[u8]file_pathNull-terminated file path string (e.g. 3dart\cyrsa001.3d)

The engine iterates RAAN entries by skipping the 6-byte prefix, then scanning forward to the null terminator of file_path. The 4-byte dword at offset 0x00 is NOT used for seeking — the next entry is found purely by string scan.

The model name is extracted by stripping directory separators, file extension, and uppercasing the stem.

Model Fallback Resolution

When an MPOB record has an empty model_name, the exporter resolves a model via RAHD/RAAN:

  1. Look up script_name in the RAHD index to get (raan_offset, raan_count).
  2. Parse the RAAN entry at raan_offset to extract the file path.
  3. Strip the path to a bare filename stem (e.g. fxart\FVPRA001.3DC → FVPRA001).
  4. Use the stem as the model name for asset lookup.
  5. If no RAHD/RAAN match, fall back to using script_name as the model name.

RAFS (Actor FSphere Data)

RAFS was intended to store pre-computed fsphere (combat/collision sphere) data for actors. The engine only loads this section when its size exceeds 10 bytes. In all 27 shipped RGM files, RAFS is exactly 1 byte — the section is vestigial and never loaded at runtime. FSpheres are instead computed from MPSZ bounding volume records indexed via RAHD.

RAST (String Data)

RAST contains all script string literals as null-terminated strings concatenated into a single blob. No count prefix; it is a flat byte array. Individual strings are located by offsets stored in RASB.

During loading, RASB offsets are rebased by adding the RAST data pointer, converting relative offsets into direct pointers.

RASB (String Offset Table)

RASB contains u32 LE offsets into RAST, one per string per actor. Each actor’s portion starts at string_offsets_index (from RAHD) and contains num_strings entries.

OffsetSizeTypeNameDescription
+0x004u32string_offsetByte offset into RAST where the null-terminated string begins

Total section length: sum of all actors’ num_strings × 4.

RAVA (Local Variables)

RAVA contains initial values for local script variables as a flat array of i32 LE integers. The first 4 bytes are always zero (sentinel).

Each actor’s portion starts at byte offset variable_offset (from RAHD; divide by 4 for the array index) and contains num_variables entries. When an actor has multiple instances, variables are replicated instances times. Variables are addressed by index in script bytecode (opcode 0x0A).

RASC (Script Bytecode)

RASC contains compiled SOUP386 scripting bytecode as a contiguous byte blob. Per-actor instruction blocks are located via RAHD offsets (script_data_offset, script_length, script_pc) and executed by the SOUP virtual machine.

For VM architecture, opcode encoding, operator tables, and execution semantics, see SOUP Scripting. For the definition-file format, see SOUP386.DEF.

Section Layout

RegionSizeDescription
Preamblescript_data_offset bytesZero-padded address space used by rebased hook/offset references
Script blocksremainderConcatenated per-actor bytecode blocks in RAHD order

Total payload = first actor script_data_offset + sum of all actors’ script_length values. For each actor, execution starts at script_pc relative to that actor’s block.

Other RA* sections provide separate actor data; their layouts and offset bases are section-specific.

RAHK (Hook Data)

RAHK contains native call records. An actor’s nonzero RAHD.rahk_offset is a byte offset into this section’s payload; the loader adds the RAHK payload base. Zero means no hook. The ordinary actor script wrapper dispatches the referenced record through the native function table, separately from its RASC entry and main continuations.

A stored record begins with a little-endian u16 native function index and a u8 argument count, followed by five-byte encoded arguments. Each argument has a one-byte parameter-mode tag and four operand bytes. Function indices use the installed SOUP definition table. Parameter evaluation uses the receiving actor for local-variable references and the executing actor’s string context.

The native hook is not a table of RASC offsets and has no group-indexed array. Its execution gates are described in SOUP ordinary update gates.

RAEX (Extra Data)

RAEX contains per-actor extra data with 30-byte fixed-size records (15 × i16 LE fields). The engine requires this section during loading. Record count is section_data_length ÷ 30. Per-actor RAEX offsets are stored in RAHD (raex_offset).

Field names Grip0 through RangeMax are from the in-game debug console.

All fields little-endian.

OffsetSizeTypeNameDescription
0x002i16grip0First source-model vertex for the drawn-weapon attachment.
0x022i16grip1Second source-model vertex for the drawn-weapon attachment.
0x042i16scabbard0First source-model vertex for the stowed-weapon attachment.
0x062i16scabbard1Second source-model vertex for the stowed-weapon attachment.
0x082i16anim_frame_refMatches RAHD anim_frame_data at 0x85. Set on mobile actors only.
0x0A2u16texture_idActor texture set (TEXBSI number) for skin variants; zero selects the models’ own textures. See actor texture sets.
0x0C2i16v_vertexModel vertex used by combat contact queries.
0x0E2i16v_sizeCombat contact radius in model units; copied to runtime Q8 units.
0x102i16taunt_idFirst taunt animation id; additional taunts count up from this value.
0x122i16collision_sphere_scaleEight-fractional-bit radius scale for actor collision spheres; nonpositive values select 256.
0x142i16field_14Source value for RAHD raex_stat at 0x97. Set on combat actors.
0x162i16field_16Set on some combat actors.
0x182i16range_minCombat engagement minimum range. Multiply by 256 for world units. Only set on dragon, golem, serpent.
0x1A2i16range_idealCombat ideal range. Same scaling.
0x1C2i16range_maxCombat maximum range. Same scaling.

Total record size: 30 bytes (0x1E). Record count is section_data_length / 30.

Actor texture sets

A nonzero RAEX texture_id applies to every multi-frame model the actor draws, including each RAAN frame-source model. The engine keeps one copy of a frame-source model per texture set and shares it between actors with the same set. Each face texture word of the copy is bank << 7 | image; words whose bank is greater than 1 take the actor’s texture set as their bank and keep their image index. Words with bank 0 or 1 are unchanged. Geometry, frame data and normals are those of the source model.

RAAT (Attribute Data)

RAAT contains per-actor attribute tables. Each actor has a 256-byte attribute block, ordered sequentially (actor 0 at offset 0, actor 1 at offset 256, etc.).

Attribute names are defined in the auto…endauto section of SOUP386.DEF (see SOUP386.DEF). Each byte is a named attribute value; zero means unset. Attributes are read/written by the script functions GetAttribute and SetAttribute.

Total section length: record_count × 256.

RAGR (Animation Groups)

RAGR contains animation group definitions that link actors to their animation data in RAAN. RAGR provides the RGM-embedded equivalent of the AIAN section found in standalone .AI files; the engine selects one or the other source based on a runtime mode flag.

Per-actor RAGR data is located via RAHD.ragr_offset (offset 0x31). Entries are size-prefixed: the first u16 is the entry payload size (excluding itself); a value of 0 terminates the list. Advance to the next entry: current_position + 2 + entry_size.

The prefix byte used by the AIAN (standalone .AI) path is NOT present in RGM RAGR — instead, that value comes from RAHD offset 0x2D.

Animation Group Entry

All fields little-endian.

Relative OffsetSizeTypeNameDescription
+0x002u16entry_sizePayload size in bytes after this field. 0 = end of groups. Should equal 8 + frame_count × 3.
+0x022u16group_indexAnimation group slot (0–177; validated ≤ 0xB1 at load)
+0x042u16anim_idAnimation identifier
+0x062u16anim_typeAnimation type (only low byte used). Values: 0 = interruptible (idle/panic), 1 = must complete (combat), 2 = no panic revert (ledge-hang loops).
+0x082u16frame_countNumber of animation frames in this group
+0x0Avar[u8; frame_count × 3]commandsPacked 3-byte animation commands, one per frame

Animation command (3 bytes, packed LE)

The low four bits select the opcode. A file ShowFrame command, opcode zero, uses all remaining twenty bits as an unsigned frame reference. It does not encode an attachment vertex.

RAAN entries form one concatenated frame table in file order. An entry with frame count N contributes its model’s frames zero through N−1. The frame reference selects one entry in that table. For example, model A with three frames followed by model B with two frames gives references 0–2 for A and 3–4 for B. Reference 4 selects model B, frame 1.

The loader rewrites each ShowFrame into two ten-bit fields: runtime model handle and frame index. These patched runtime commands have a different meaning from file commands. Both resolved fields fit in a byte. Raw file commands must be resolved through RAAN before they can be played.

Opcodes 4 (PlaySound) and 10 (ChangeAnimGroup) use two ten-bit fields in the file. Their parameters are sound settings and animation jump targets, respectively. See animation playback and the opcode table.

RANM (Namespace)

RANM contains object namespace strings used for cross-script object references. Each actor’s portion is located by the RAHD ranm_offset field at 0x19. The extracted string provides the actor’s canonical name for ObjDot* opcodes using selector byte 4 (named object from string table).

MPSL (Lights)

MPSL starts with a little-endian record count, followed by 42-byte records. All fields are little-endian.

OffsetSizeTypeNameDescription
0x004u32linkSerialized linkage field, replaced when the light enters a spatial list.
0x044u32world_idWorld ID used to select the light at load time. Reused as linkage after selection.
0x084i32pos_xX position in engine coordinates.
0x0C4i32pos_yY position in engine coordinates.
0x104i32pos_zZ position in engine coordinates.
0x142i16radiusLocal-light radius in engine coordinates.
0x162i16field_16Auxiliary field.
0x186[i16; 3]rgbSigned red, green and blue strengths. Negative channels subtract light.
0x1E1u8field_1eAuxiliary byte.
0x1F1u8activeMust equal 1 for load-time selection.
0x206[u8; 6]field_20Auxiliary bytes.
0x264[u8; 4]field_26Auxiliary bytes.

With map lights enabled, the loader selects records whose active byte equals 1 and whose world_id matches the selected WORLD.INI entry. It inserts those records into spatial lists and replaces the first two words with list links. The position, radius and RGB fields retain their file values. The first three bytes are not a color, and the RGB words are not a direction vector.

Local-light selection for each model is described in model lighting.

MPMK (Markers)

MPMK starts with a little-endian record count, followed by a contiguous array of 12-byte position records, then a separate array of one auxiliary byte per marker. The payload length is 4 + count * 12 + count. The arrays are not interleaved.

All fields little-endian.

OffsetSizeTypeNameDescription
0x003i24pos_xPosition X
0x031u8pad_xAlignment byte
0x043i24pos_yPosition Y
0x071u8pad_yAlignment byte
0x083i24pos_zPosition Z
0x0B1u8pad_zAlignment byte

Position fields use the same i24+pad encoding as MPOB/MPSO. No explicit record ID field. The auxiliary table begins at 4 + count * 12; byte i belongs to position record i. A lookup in a loaded world requires the auxiliary byte to equal 1 and the index to be below the marker count; a zero byte identifies an unavailable marker. All 27 shipped maps hold at most 49 markers and use only the values 0 and 1. See node-marker lookup. The metadata exporter retains it as reserved for compatibility. map_position retains the three signed map coordinates before export-axis conversion and scaling; position retains the existing converted coordinates. Runtime placement can use map_position without recovering integers from floating-point export coordinates.

MPSZ (Bounding Volumes)

MPSZ is an array of 49-byte bounding volume records used to build per-actor fspheres (combat/collision bounding spheres) at runtime. Not count-prefixed — record count is section_size / 49. RAHD fields at 0x89, 0x8D and 0x91 store per-actor indices into this table. A primary index of −1 derives bounds from the model; −1 in either alternate index means absent.

Record Layout (49 bytes)

OffsetSizeTypeNameDescription
0x001u8flagsInitially zero; runtime sphere construction selects the dominant axis here
0x014i32total_xTotal extent X (pos_x + neg_x)
0x054i32total_yTotal extent Y (pos_y + neg_y)
0x094i32total_zTotal extent Z (pos_z + neg_z)
0x0D4i32center_xCenter offset X
0x114i32center_yCenter offset Y
0x154i32center_zCenter offset Z
0x194i32pos_xPositive half-extent X
0x1D4i32pos_yPositive half-extent Y
0x214i32pos_zPositive half-extent Z
0x254i32neg_xAbsolute negative half-extent X
0x294i32neg_yAbsolute negative half-extent Y
0x2D4i32neg_zAbsolute negative half-extent Z

Invariant: total = neg + pos for each axis. Center is always zero in shipped files. Bounds can be asymmetric (neg != pos).

At runtime, the engine copies these 49 bytes and calls the fsphere builder to create a 3D bounding volume for the actor. RAHD provides three index fields per actor, allowing reference to different bounding records. Only a subset of actors have direct RAHD indices.

Flags are zero in shipped files. The flag byte precedes the integers; placing it last shifts every decoded field.

ISLAND CYRUS references primary record 120 and secondary record 121, with no tertiary record. Its primary totals are (48, 128, 49), positive extents (24, 62, 11), and negative extents (24, 66, 38).

MPSF (Flat Objects)

MPSF starts with a little-endian record count, followed by 24-byte records. Each record places a textured quad in the scene.

All fields little-endian.

OffsetSizeTypeNameDescription
0x004u32idObject id
0x044i32reservedNot read at runtime.
0x083i24pos_xPosition X
0x0B1u8pad_xAlignment byte
0x0C3i24pos_yPosition Y
0x0F1u8pad_yAlignment byte
0x103u24pos_zPosition Z
0x131u8pad_zAlignment byte
0x142u16texture_dataPacked: texture_id = data >> 7, image_id = data & 0x7F
0x162i16reservedNot read at runtime.

Position decode uses the same MPOB scale/sign rules. MPSF records are submitted as camera-facing textured quads centered on their placed position. Their full width and height in engine units are the source BSI image dimensions multiplied by the BSI texture scale divided by 256. A zero texture scale selects 256. The ordinary flat path uses geometry scale 1 and copies the camera orientation; it does not use a fixed-size model quad or an authored object rotation. Texture-coordinate extents are computed separately from geometry size. For unreduced textures, they cover the complete source image, excluding upload padding. The first two corners use the image top edge, and the next two use the bottom edge. The FLAT attributes below modify the submission flags and alpha.

FLAT

FLAT stores per-texture drawing attributes, separate from the placed objects in MPSF. Its payload begins with 512 little-endian u16 record counts, one for each texture archive ID. The records follow in ascending archive order. Each record is three bytes: image ID, drawing flags, and alpha byte. Archives with a zero count contribute no records. There is no separate total-count word.

Texture lookup splits the packed texture reference into an archive ID and seven-bit image ID, then searches that archive’s records for the first matching image. A missing archive or image has no attribute override.

For an MPSF draw, the base flags are 1 and the default alpha input is 255. A matching attribute record is ORed into the flags and replaces the alpha input with alpha_byte + 1, computed without byte wrapping. Thus a stored zero supplies 1, and a stored 255 supplies 256. The geometry scale and texture coordinate scale inputs remain 1. The base flag selects a centered quad; attribute flags do not replace that anchor selection.

FLAT appears in all 27 shipped RGM files as the last section before END . Its payload is 1111 bytes in 26 files and 1108 bytes in HIDEOUT.RGM.

External References