Skip to main content

.mgb field names — the LoadVisitor join

Verified via reverse engineering

Companion to the .mgb format page, which carries the wire layout. This page is the per-field working record: which object offset each wire field lands on, and therefore which authored name belongs to it.

magma::LoadVisitor — the engine's XML loader for Magma's .mgm source format — is a complete 1:1 mirror of BinaryLoadVisitor: ~55 ReadX methods, one per class, parsing named XML elements into the same object offsets the binary visitor writes. That makes field names recoverable rather than guessable:

  • BinaryLoadVisitor::VisitX gives (wire order, width) → object offset
  • LoadVisitor::ReadX gives XML element name → object offset
  • joining on the object offset names each wire field

Addresses are FarCry2_server.

Fields are listed in wire order. "off" is the object offset both visitors agree on.


State hierarchy

StateReadState @ 0x0a066400

#wireoffnamenotes
0u32+0x08INTERPOLATIONFLAGS%u; defaults to ALL_INTERPOLATION_FLAGS when absent
1u32+0x10STATECOLOR%d %d %d %d → packed ARGB

Correction: earlier passes called these start/end (a time range). They are not.

Correction: an earlier pass called every colour word here "packed RGBA". It is ARGB0xAARRGGBB, alpha in the high byte, authored A R G B. ReadState packs the four %d components first-component-highest (c1 << 24 | c2 << 16 | c3 << 8 | c4), which fixes the order but not which one is alpha; the shipped data fixes that. Across the 500 vanilla packages the two commonest state colours are 0xFFFFFFFF (80,240 uses) and 0x00FFFFFF (7,010), and every other colour in use repeats the pattern — 0xFFA5BDC5/0x00A5BDC5, 0xFFC0C0C0/0x00C0C0C0, 0xFF9CB1B8/0x009CB1B8 — matched pairs of the same low three bytes at high byte FF and 00, i.e. the two ends of a fade. Read as RGBA those pairs would be "opaque white" and "opaque cyan".

RotationStateReadRotationState @ 0x0a066980 (base State)

#wireoffname
2f32+0x18ROTATION (atof)
3u16+0x1cORIGIN x (%d %d)
4u16+0x1eORIGIN y

PosStateReadPosState @ 0x0a066050 (base RotationState)

#wireoffname
5u16+0x24POSITION x
6u16+0x26POSITION y

ScaleStateReadScaleState @ 0x0a0654c0 (base PosState)

#wireoffname
7f32+0x2cSCALEX
8f32+0x30SCALEY

RectStateReadRectState @ 0x0a065130 (base RotationState)

#wireoffname
5u16+0x24LEFT
6u16+0x26RIGHT
7u16+0x28TOP
8u16+0x2aBOTTOM

Note the order is left, right, top, bottom — not l/t/r/b. PosState and RectState are siblings that reuse the same +0x24/+0x26 storage (POSITION x/y == LEFT/RIGHT).

TextBaseStateReadTextBaseState @ 0x0a064fb0 (base RectState)

#wireoffname
9f32+0x30OFFSETY (%f)
10u16+0x34ABSOFFSETY (%d)

TextStateReadTextState @ 0x0a06eca0 (base TextBaseState)

#wireoffname
11u32+0x44SHADOWCOLOR
12u16+0x40HEIGHT (stored as float)
13u8+0x48SHADOWOFFSETX
14u8+0x49SHADOWOFFSETY
15u16+0x3cLEADING
16u16+0x3eTRACKING

TEXTCOLOR (alias COLOR) writes +0x10, i.e. it is the inherited STATECOLOR under another name in the XML — not a separate wire field.

ImageStateReadImageState @ 0x0a06e490 (base RectState)

#wireoffname
9u32+0x54SHADOWCOLOR
10u8+0x58SHADOWOFFSETX
11u8+0x59SHADOWOFFSETY
12f32+0x38TILING x
13f32+0x3cTILING y
14f32+0x30OFFSET x
15f32+0x34OFFSET y
16bool+0x40 b0FLIPHORIZONTAL
17bool+0x40 b1FLIPVERTICAL
18bool+0x40 b2ACTUALSIZE
19-22u32 ×4+0x44..+0x50COLOR1..COLOR4 (packed ARGB)

When COLORn (n>1) is absent in XML the loader copies COLOR1 — a gradient quad's corner colours.

RectShapeStateReadRectShapeState @ 0x0a06cff0 (base RectState)

#wireoffname
9u8+0x30OUTLINEWEIGHT
10u32+0x34OUTLINECOLOR
11-14u32 ×4+0x38..+0x44FILLCOLOR1..FILLCOLOR4
15u32+0x48SHADOWCOLOR
16u8+0x4cSHADOWOFFSETX
17u8+0x4dSHADOWOFFSETY

Tree structure

KeyframeReadKeyframe @ 0x0a06c5a0

#wireoffname
u32NamedObject name hash
ActionCaller
0u32+0x18IDX (stored u16) — the frame index
1u32+0x1cINTERPOLATION — the easing curve, a plain tag-group 0 value (ReadKeyframe calls Util::GetType(0, …) at +0xec)

Then the concrete State, chosen by Factory::MakeState from the owning widget's class.

ElementReadElement @ 0x0a06bab0

#wireoffname
UserData, then ActionCaller
0boolvia SetVisibleHIDDEN (inverted into visibility)
1bool+0x2e b0ISDUPLICATABLE
2u32+0x2d b4-6MASKMODE (low 3 bits; XML resolves a name via Util::GetType(0xb, …))
3u32keyframe count (KEYFRAMES/COUNT in XML)

Then the keyframes, then WIDGET — the widget's own body.

AreaReadArea @ 0x0a067b50

#wireoffname
UserData, then ActionCaller
0u32+0x18FRAMERATE (engine stores 1000 / framerate)
1u32CURRENTFRAME
2u32element count (CHILDREN/COUNT in XML)
3-6u16 ×4STATICBOX (%d %d %d %dArea::SetStaticBox)
#wireoffname
0u8TIMING — timing-strategy type slot
1u32+0x14+4PACKAGE
2bool(gate for AREA)
3u32+0x14+8AREA
4bool+0x18 b0ISUSINGDUPLICATEDAREA

FocusableReadFocusable @ 0x0a06a730 (base Element)

#wirename
0u32neighbour count (NEIGHBORS/COUNT)
u8CONTROLLER (per NEIGHBOR; default 8)
u8DIRECTION (per NEIGHBOR; default 4)
u32ID (per NEIGHBOR)
1boolINPUTFILTERFocusable::SetInputController

PageFocusable, Checkable, Radioable are pure forwards to this.


Widgets

RectShapeReadRectShape @ 0x0a06f820

#wireoffname
0bool+0x19 b0ISOUTLINED
1bool+0x19 b1ISFILLED
2u32+0x18BLENDINGMODE (Util::GetType(9, …))

ImageReadImage @ 0x0a072490

#wireoffname
0resource ref+0x1cMATERIALLINK (legacy alias MATERIAL)
1u32+0x18BLENDINGMODE
2bool+0x1a b0ALPHABLENDFIRST
3u32+0x19 lo nibbleADDRESSINGMODEU (Util::GetType(10, …))
4u32+0x19 hi nibbleADDRESSINGMODEV

TextBaseReadTextBase @ 0x0a06ad10

#wireoffname
0booluse-string-table gate
1au32TABLEID (gate = 1)
1bu32RESOURCEID (gate = 1)
1cu32 + UTF-16+0x18STRING (gate = 0)
2u32+0x1cALIGNMENTX (alias ALIGNMENT; Util::GetType(1, …))
3u32+0x20ALIGNMENTY (Util::GetType(2, …))
4bool+0x38 b0WRAPPING (alias WRAP)
5bool+0x38 b1CLIPPING
6bool+0x38 b2ELLIPSIS
7bool+0x38 b3AUTOSIZED
8boolhas-slider-link gate
9u32SLIDERLINK

TextReadText @ 0x0a0713f0 (base TextBase)

#wireoffname
10resource ref+0x40font family (LoadFontFamily)
11bool+0x48 b0BOLD
12bool+0x48 b2ITALICS
13bool+0x48 b1UNDERLINED
14u32+0x50BLENDINGMODE
15bool+0x51 b0ALPHABLENDFIRST

AreaInstanceReadAreaInstance @ 0x0a070910

#wireoffname
0u32 + UTF-16+0x18LABEL (the referenced area's name)
1resource ref+0x38MATERIALLINK
2boolhas-LINK gate
3AreaLink+0x1cLINK
4u32INDEXOFFSET

AutonomousAreaInstance / ButtonInstance / CheckBoxInstance / RadioButtonInstance add nothing.

WindowReadWindow @ 0x0a06c1d0

#wireoffname
0bool+0x100SINGLECORNERMATERIAL
1bool+0x101SINGLEEDGEMATERIAL
2-10section ×9see below

Section index → XML name, matching the binary's own 0–8 order exactly (stretchable ones marked *):

idxnameidxname
0FILL *5TOP_EDGE *
1TOP_LEFT_CORNER6LEFT_EDGE *
2TOP_RIGHT_CORNER7RIGHT_EDGE *
3BOTTOM_LEFT_CORNER8BOTTOM_EDGE *
4BOTTOM_RIGHT_CORNER

WindowSection — setter-name derived (ReadWindowSection @ 0x0a066af0)

MATERIALLINKBLENDINGMODE (SetBlendingMode) → ALPHABLENDFIRST (SetAlphaBlendFirst) → FLIPHORIZONTAL (SetFlipHorizontal) → FLIPVERTICAL (SetFlipVertical) → ROTATED (SetRotated). StretchableWindowSection appends STRETCHMODE (SetStretchMode).


Naming still inferred, not offset-verified

For these the XML vocabulary and the binary field list are both known and the counts line up, but the per-field join was not run. Names are from the vocabulary plus the binary's own setter calls, which are unambiguous in most cases (Slider::SetRange, Page::SetGlobalSelectionMode, PageInstance::AddDefaultFocusTag, EditBox::SetPasswordChar, Material::SetRegion, ListBox::UpdateMetrics). Tighten by decompiling the matching ReadX if a label looks wrong:

Page, PageInstance, Button, CheckBox, EditBox, ListBox, Slider, UserData, Material, FullLink, ActionCaller, ActionExecuter, StringResourceExternalId, Package, StringTable, StringResource, GenericObject, GenericObjectTable, FontFamily.

Vocabulary for them (from the string tables):

ClassElements
ActionCallerACTIONEXECUTER
ActionExecuterACTIONNAME, ACTIONSNB
ActionExecuterEventACTIONINDEX
AreaInstanceINDEXOFFSET, LABEL, MATERIALLINK, SYNCHRONIZED
ButtonSTATES, TIMINGS
CheckBoxTIMINGS
CursorHOTSPOT (%hd %hd)
EditBoxCURSORLINK, FIELDLINK
FocusableCONTROLLER, INPUTFILTER, NEIGHBOR, NEIGHBORS
FullLinkLASTOBJECTTYPE
GenericObjectTableGENERICOBJECT
ImageADDRESSINGMODEU, ADDRESSINGMODEV, ALPHABLENDFIRST, BLENDINGMODE, MATERIALLINK
ListBoxAUTOCENTER, BUTTONCOUNT, FOOTERLINK, HEADERFOOTERPOS, HEADERLINK, ITEMLINK, ITEMSPACING, SLIDERLINK, SLIDESELITEM, VERTICALSPACING, WRAPAROUND
MaterialSTAGE
PackagePAGESIZE, DISPLAYOFFSET, MATERIALS, FONTS, FONTSUBST, FONTFAMILIES, FONTFAMILY, CHILDREN, STRINGTABLE, GENERICOBJECTTABLE, LASTACTIVEAREA, DEFAULTMATERIAL, REPLACES
PageCONTROLLER, DEFAULT_ELEMENT, SINGLE_GLOBAL_SELECTION
PageInstanceCONTROLLER, DEFAULTFOCUS, DEFAULT_FROM_DIRECTION, DEFAULT_FROM_DIRECTION_2
RectShapeBLENDINGMODE, ISFILLED, ISOUTLINED
SliderFOOTERLINK, HANDLELINK, HEADERLINK, KNOBLINK, ORIENTATION, RANGEMAX, RANGEMIN, SURFACELINK, TRACKLINK
StretchableWindowSectionSTRETCHMODE
StringResourceExternalIdRESOURCEID, TABLEID
StringTableSTRINGRESOURCE
TextALIGNMENT, ALPHABLENDFIRST, AUTOSIZED, BLENDINGMODE, CLIPPING, ITALICS, SLIDERLINK, UNDERLINED, WRAPPING
TextBaseALIGNMENT, ALIGNMENTX, ALIGNMENTY, AUTOSIZED, CLIPPING, RESOURCEID, SLIDERLINK, TABLEID, WRAPPING
UserDataUSERDATANB
WindowTOP_LEFT_CORNER, TOP_EDGE, TOP_RIGHT_CORNER, LEFT_EDGE, RIGHT_EDGE, BOTTOM_LEFT_CORNER, BOTTOM_EDGE, BOTTOM_RIGHT_CORNER, SINGLECORNERMATERIAL, SINGLEEDGEMATERIAL
WindowSectionALPHABLENDFIRST, BLENDINGMODE, FLIPHORIZONTAL, FLIPVERTICAL, MATERIALLINK

Util::GetType's tag table — the named value sets

Several fields above are authored in XML as a name, not a number: ReadImage parses BLENDINGMODE with Util::GetType(9, text), ADDRESSINGMODEU/V with group 10, ReadTextBase resolves ALIGNMENTX/ALIGNMENTY with groups 1 and 2, and so on.

Util::GetType (0x0a03ba50) and its inverse Util::GetTag (0x0a03b831) are a pair of linear scans over one static table:

entries = *(Entry**)(ms_tagTable + group * 8 + 4); // Entry { u32 value; const char* name; }
count = *(int*) (ms_tagTable + group * 8);

No code path holds any of these names as a literal, so they were read out of ms_tagTable at 0x0a34ba80 in the debug FarCry2_server ELF (resolving the vaddr through the PT_LOAD headers). Values are as stored — note groups 13 and 19-21 are not 0-based.

#Used byValues
0keyframe interpolation0 None, 1 Linear, 2 Square, 3 Root, 4 Sin, 5 Circle, 6 CircleDecel
1ALIGNMENTX0 LEFT, 1 CENTER, 2 RIGHT, 3 JUSTIFY
2ALIGNMENTY0 TOP, 1 CENTER, 2 BOTTOM
3NEIGHBOR direction0 UP, 1 DOWN, 2 LEFT, 3 RIGHT
4Button STATES/TIMINGS slots0 ENABLED, 1 PRESSED, 2 DISABLED, 3 SELECTED, 4 OVER, 5 OVER_SELECTED
5CheckBox TIMINGS slotsgroups 4's six, then 6 CHKENABLED, 7 CHKPRESSED, 8 CHKDISABLED, 9 CHKSELECTED, 10 CHKOVER, 11 CHKOVER_SELECTED
6HEADERFOOTERPOS0 Top and Bottom, 1 Left and Right
7ORIENTATION0 Horizontal, 1 Vertical
8sort direction0 Ascending, 1 Descending
9BLENDINGMODE0 Normal, 1 Negative, 2 Plain Color, 3 Plain Alpha, 4 Silhouette, 5 Burn, 6 Burn 2X, 7 Burn 4X, 8 Dodge, 9 Dodge 2X, 10 Dodge 4X, 11 Darken, 12 Darken 2X, 13 Darken 4X, 14 Lighten, 15 Lighten 2X, 16 Lighten 4X, 17 Add, 18 Ghost, 19 Invert, 20 Multiply, 21 Modulate, 22 Only Alpha, 23-26 Custom1-Custom4
10ADDRESSINGMODEU/V0 Wrap, 1 Mirror, 2 Clamp, 3 Border
11MASKMODE0 NOMASK, 1 SETMASK, 2 USEMASK, 3 USEMASK_INVERTED
12texture format0 TGA, 1 TGA32, 2 DXT3, 3 PNG
13CONTROLLER255 Any Controller, 0-7 Controller N only
14button state (display form of 4)0 Enabled, 1 Pressed, 2 Disabled, 3 Selected, 4 Over, 5 Over && Selected
15checkbox state (display form of 5)0-5 Unchecked - …, 6-11 Checked - …
16UserData property type0 Area Link, 1 Element Link, 2 Integer, 3 Float, 4 String, 5 String Resource, 6 Pointer, 7 Keyframe, 8 Bool
17loader result0 Success, 1 Out of memory, 2 Open file failed, 3 XML syntax error, 4 Invalid header, 5 Invalid version, 6 Invalid binary format, 7 XML support disabled
18handler phase0 PostLoading, 1 PreDraw, 2 PostDraw
19input events3 KeyDown, 4 KeyUp, 5 MouseDown, 6 MouseUp, 7 MouseDblClick, 8 MouseMove, 9 MouseEnter, 10 MouseLeave
20focus events11 SetFocus, 12 KillFocus, 13 Activate, 14 Escape
21page events11 EnterPage, 12 ExitPage, 13 Overlapped, 14 UnOverlapped, 15 Tick

Group 16 is worth lining up against the UserData type tags: the wire tags there are 0x02 u32, 0x07 float, 0x0c bool, 0x10 string, 0x11/0x12/0x15 links, 0x13 string resource — a different numbering from this authoring-side list, so the two are not interchangeable.

Only six of group 9's 27 appear in the shipped packages: 0 Normal (29,870 uses), 15 Lighten 2X (40), 16 Lighten 4X (10), 17 Add (1,840), 20 Multiply (330) and 21 Modulate (1,000).

Which group each field uses

Util::GetType is cdecl, so every call site pushes its group as a push imm8 immediately before the call. Scanning the binary for calls targeting 0x0a03ba50 and decoding that push gives the group each ReadX actually passes — 24 call sites in total, which is what the mapping below rests on rather than name similarity:

fieldgroupcall site
Keyframe INTERPOLATION0ReadKeyframe+0xec
TextBase ALIGNMENTY2ReadTextBase+0x1e7
TextBase ALIGNMENTX1ReadTextBase+0x459
Element MASKMODE11ReadElement+0x125
RectShape BLENDINGMODE9ReadRectShape+0xc6
Image BLENDINGMODE9ReadImage+0x8d
Image ADDRESSINGMODEU/V10ReadImage+0x142, +0x24e

Correction: an earlier pass described INTERPOLATION as "a timing-strategy type id". It is not — it is a plain group-0 value (None, Linear, Square, Root, Sin, Circle, CircleDecel), and every one of the 105,160 keyframes in the shipped packages holds a value in 0-5. The timing-strategy type slot is a different field: AreaLink's TIMING.

Groups 4/5/14/15 name the slots of Button/CheckBox's TIMINGS array rather than a value any field holds, and group 16's property-type numbering is not the same as the wire tags UserData stores (see above), so neither is a value picker.