Skip to main content

Authoring reference

The vocabulary is the engine's own

Element and attribute names are the authored names recovered by joining magma::BinaryLoadVisitor (wire order → object offset) against magma::LoadVisitor (XML name → object offset). Names marked (provisional) come from a class's XML vocabulary rather than the per-field join — their width and position are verified, only the label is a guess. Full working record: .mgb field names.

Frequencies quoted as "n/N" are counts over the 50-package vanilla corpus.

This page is the complete set of things you can write in a jackall mgb decode document. The reader is strict — an attribute it does not define, or an element in a place it does not define, is an error, so anything not listed here will be rejected.

Document skeleton

Order inside a list matters; named scopes are found by name. Every element listed is required unless marked optional, including the empty ones.

<MagmaPackage sentinel version flag POOLCOUNTS PAGESIZE.w PAGESIZE.h
DISPLAYOFFSET.x DISPLAYOFFSET.y DEFAULTMATERIAL>
<TYPES> <TYPE id/>×166
<USERDATA> the package's own property list
<MATERIALS materialExtra> <Material name texture REGION/>×n
<FONTSUBSTS> <FONTSUBST slot type fontData FONTSUBST/>×n (embedded font blobs)
<FONTS> <FONT slot type name file/>×n
<FONTFAMILIES> <FONTFAMILY name font PACKAGE/>×n
<CHILDREN> <Area slot type …/>×n
<STRINGTABLE> optional
<GENERICOBJECTTABLE> optional — but see below
AttributeValue
sentinelCD0000AB. Only the last byte is checked; a different value flips the reader to big-endian.
version2010000 exactly. Any other value fails the load with error 5.
flagHeader byte 13. false everywhere in the corpus; purpose unknown.
POOLCOUNTS65 space-separated u32 memory-pool hints. All-zero is safe.
PAGESIZE.w/hThe design canvas. 1280×800 in 49/50 shipped packages (fonts.mgb is 1280×720); the 4:3 pc set uses 1024×768.
DISPLAYOFFSET.x/y160 40 on the widescreen set, 32 24 on 4:3.
DEFAULTMATERIALUsually empty.

materialExtra is the number of distinct texture paths among the materials — a setter argument, not a loop count. Preserve it when editing; set it to the distinct-path count when authoring.

Type slots

slot="…" is a raw index into the package's <TYPES> table, and the shipped table is identical in all 50 packages, so these numbers are constants. type="…" beside it is decoration the reader ignores — the slot is authoritative.

ClassslotClassslotClassslot
Area44Placeholder77ActionExecuter133
Page101RectShape76ActionExecuterEvent136
Button99Image74ActionExecuterInputable137
CheckBox100Text50ActionExecuterFocusable138
Cursor68Window97ActionExecuterEditbox139
Element69AreaInstance33ActionExecuterListbox140
Focusable66AutonomousAreaInstance57ActionExecuterPage141
Keyframe126PageInstance58ActionExecuterPageInstance142
PixmapFont106ButtonInstance78ActionExecuterSlider143
EventTriggeredTimingStrategy149CheckBoxInstance79ListBox61
TickTimingStrategy150RadioButtonInstance80EditBox92
NoTimingStrategy151SyncTimingStrategy152Slider64

Slots may be appended (the ceiling is 254, against the 167 shipped), but there is no reason to: every class the engine can construct is already in the table.

Areas

An <Area> is one entry of <CHILDREN>. Corpus mix: Area 371, Page 244, Button 141, Cursor 15, CheckBox 2.

<Area slot="101" type="Page" FRAMERATE="30" CURRENTFRAME="0" STATICBOX="0 1280 0 800" …tail…>
<USERDATA name=""><PROPERTIES/></USERDATA>
<ACTIONEXECUTER /> <!-- optional -->
<CHILDREN>…elements…</CHILDREN>
…tail elements…
</Area>
FieldMeaning
FRAMERATEFrames per second of this area's timeline. 30 (484) and 10 (201) cover 90% of the corpus; the engine stores 1000/FRAMERATE.
CURRENTFRAMEFrame the area starts on. 0 everywhere.
STATICBOXThe area's declared box, in LEFT RIGHT TOP BOTTOM order. 0 0 0 0 is common for pure containers; Button areas carry their real extent (9 191 7 31 for the standard menu button) and that is what the mouse tests against (inferred).

Per-type tails, appended after </CHILDREN>:

TypeTail
Areanone
Page<DEFAULT_ELEMENTS><DEFAULT_ELEMENT CONTROLLER ID/>…</DEFAULT_ELEMENTS> then SINGLE_GLOBAL_SELECTION (attribute on <Area>). CONTROLLER="255" (any controller) in all 169 corpus entries; ID is the element that takes focus when the page opens.
CursorHOTSPOT.x, HOTSPOT.y (stored negated by the engine)
ButtonTIMINGS6 frame indices
CheckBoxTIMINGS12 frame indices

TIMINGS is the whole of Magma's interactive-widget model; see the button recipe.

Elements

<Element slot="74" type="Image" HIDDEN="false" ISDUPLICATABLE="true" MASKMODE="NOMASK">
<USERDATA name=""><PROPERTIES/></USERDATA>
<ACTIONEXECUTER /> <!-- optional -->
<KEYFRAMES><Keyframe /></KEYFRAMES>
<Image ></Image> <!-- the widget body; element name == widget class -->
<FOCUSABLE INPUTFILTER="255"><NEIGHBORS/></FOCUSABLE> <!-- only for focusable widgets -->
</Element>
FieldMeaning
HIDDENInverted into Element::SetVisible. false 5,337 / true 85.
ISDUPLICATABLEWhether the instancing pass may clone this element. true on all 5,422 corpus elements — treat false as untested.
MASKMODENOMASK (5,347) / SETMASK (26) / USEMASK (49) / USEMASK_INVERTED (0). See masking.

The 14 widget classes, the Element subclass each gets, and therefore whether a <FOCUSABLE> tail follows:

WidgetWrapper<FOCUSABLE>Corpus uses
ImageElementno2,127
TextElementno810
AreaInstanceElementno711
PlaceholderElementno548
RectShapeElementno480
PageInstancePageFocusableyes428
ButtonInstanceFocusableyes131
ListBoxFocusableyes101
EditBoxFocusableyes43
SliderFocusableyes33
CheckBoxInstanceCheckableyes10
AutonomousAreaInstanceElementno0
RadioButtonInstanceRadioableyes0
WindowElementno0

<FOCUSABLE> carries the explicit navigation graph:

<FOCUSABLE INPUTFILTER="255">
<NEIGHBORS>
<NEIGHBOR CONTROLLER="255" DIRECTION="1" ID="b_next" />
</NEIGHBORS>
</FOCUSABLE>

DIRECTION: 0 UP, 1 DOWN, 2 LEFT, 3 RIGHT. CONTROLLER="255" means any controller, and is the only value in the corpus; likewise INPUTFILTER="255" on all 746 focusables. 592 of them declare no neighbours at all — a ListBox navigates its own rows internally, so an explicit graph is only needed between separate focusable elements.

Widget bodies

Attribute order is irrelevant; presence is not.

Placeholder

<Placeholder /> — no fields. A named anchor with geometry and nothing drawn. Present as the first child of most areas (527 of them named action), where it serves as the area's origin marker (inferred).

RectShape

<RectShape ISOUTLINED="true" ISFILLED="true" BLENDINGMODE="Normal" />

Colours live in the keyframe (FILLCOLOR1..4 corner colours, OUTLINECOLOR, OUTLINEWEIGHT), not here. The only primitive Magma can draw without a texture.

Image

<Image BLENDINGMODE="Normal" ALPHABLENDFIRST="false"
ADDRESSINGMODEU="Clamp" ADDRESSINGMODEV="Clamp">
<MATERIALLINK present="true" id="notebook" PACKAGE="" />
</Image>

MATERIALLINK names a <Material> — in this package when PACKAGE="" (1,234 uses), or in another by path (\common.mgb 815, \hud.mgb 45). The material's own texture path is UI-root relative with a leading backslash and resolves against the owning package's name.

Text

<Text useStringTable="false" STRING="OPTIONS_DISPLAY_ASPECT_RATIO"
ALIGNMENTX="LEFT" ALIGNMENTY="TOP"
WRAPPING="false" CLIPPING="false" ELLIPSIS="true" AUTOSIZED="false"
BOLD="false" ITALICS="false" UNDERLINED="false"
BLENDINGMODE="Normal" ALPHABLENDFIRST="false">
<FONTFAMILY present="true" id="Farcry2_25" PACKAGE="\fonts.mgb" />
</Text>
  • useStringTable="true" swaps STRING for TABLEID + RESOURCEID. No corpus file uses it — the string-table path is dead in shipped data.
  • STRING is UTF-16 on the wire. 290 of 810 shipped strings are OASIS localisation keys that resolve at runtime (OPTIONS_DISPLAY_ASPECT_RATIO → "Aspect Ratio"); most of the rest are design-time placeholders (Wwwwwwwwwww width rulers, PLACEHOLDER TEXT…) in templates whose text native code sets. Key resolution being automatic in TextBase is (inferred) from that data, not traced.
  • ALIGNMENTX: LEFT / CENTER / RIGHT / JUSTIFY. ALIGNMENTY: TOP / CENTER / BOTTOM.
  • SLIDERLINK (optional attribute) names a sibling Slider that scrolls the text.
  • Font families come from \fonts.mgb; all 810 shipped Text widgets use one of two families, Farcry2_25 (467) or #24EE0F45 (343 — the family whose font file is arial_cyrillic_25; its own name string is not recoverable).

Area instances — AreaInstance, PageInstance, ButtonInstance, CheckBoxInstance, RadioButtonInstance, AutonomousAreaInstance

<PageInstance LABEL="" INDEXOFFSET="0">
<MATERIALLINK present="false" />
<LINK slot="150" TIMING="TickTimingStrategy" PACKAGE="common"
AREA="#36150990" ISUSINGDUPLICATEDAREA="true" />
<DEFAULTFOCUSES /> <!-- PageInstance only -->
</PageInstance>

All five share one body; they differ only in the wrapper Factory::MakeElement gives them. The instance class must match the target area's class — this holds without exception in the corpus:

Instance widgetTarget area typeUses
AreaInstanceArea711
PageInstancePage428
ButtonInstanceButton124 (+7 with no LINK at all)
CheckBoxInstanceCheckBox10

LABEL is the target area's name as a string — the one place a readable name survives, which is why hashing labels recovers area names. Empty in 1,206 of 1,280 instances.

<LINK> is an AreaLink:

FieldMeaning
TIMINGTiming-strategy slot: TickTimingStrategy (792, advances every tick), NoTimingStrategy (382, frozen on one frame), SyncTimingStrategy (84, follows the parent's frame), EventTriggeredTimingStrategy (15).
PACKAGEName hash of the owning package's bare name (common, not \common.mgb).
AREAOptional; the target area's name hash.
ISUSINGDUPLICATEDAREAtrue (728) gives this instance a private clone of the area — required whenever two instances of the same area must animate independently. false (545) shares one live instance.

INDEXOFFSET is 0 in 1,279 of 1,280 instances.

ListBox (names provisional)

<ListBox HEADERFOOTERPOS="0" AUTOCENTER="true" WRAPAROUND="false" SLIDESELITEM="false"
flag4="false" BUTTONCOUNT="20" ITEMSPACING="0" SLIDERLINK="">
<HEADERLINK /> <!-- wire position 1 — this is the ROW TEMPLATE -->
<ITEMLINK /> <!-- wire position 2 -->
<FOOTERLINK /> <!-- wire position 3 -->
</ListBox>
The element named HEADERLINK is the row template

The three link names are provisional and the evidence says they are mis-ordered. In every corpus list that shows rows, the first link (HEADERLINK) points at the Button area duplicated once per item, and the other two are absent: the 20-row options nav list #36150990 sets only HEADERLINK, to the standard row button #CD72056E. The lists that set all three are always BUTTONCOUNT="1" ITEMSPACING="1" value spinners, where link 1 is the value label and links 2–3 are the left/right arrow buttons. Judge by wire position, not by the name.

BUTTONCOUNT is the number of row widgets the list instantiates — its viewport size, not its item count. The list keeps a viewport and moves it with the selection, clamped at both ends, so a long list scrolls with no scrollbar; SLIDERLINK names a sibling Slider that acts purely as a visual indicator and drag target, and only 12 of 101 corpus lists set one.

HEADERFOOTERPOS holds values (0255) a two-entry enum cannot be, so the name is probably wrong; it is written as a bare number.

EditBox (names provisional)

<EditBox maxLength="15" passwordChar="base64:KgA=">
<FIELDLINK /><CURSORLINK />
</EditBox>

passwordChar is one UTF-16 unit (base64:KgA= is *), omitted when unset. maxLength="0" means unbounded (inferred); 20 of 43 corpus edit boxes use it.

Slider (names provisional)

<Slider RANGEMIN="0" RANGEMAX="10" field2="0" field3="1" field4="1" ORIENTATION="false">
<TRACKLINK /><KNOBLINK /><HEADERLINK /><FOOTERLINK />
</Slider>

ORIENTATION="false" is horizontal (31 of 33). RANGEMAX is 10 in 24 of 33 — sliders report a normalised 0–10 position and the page maps it to a real value. All four links are set in 31 of 33.

Window (untested)

The 9-patch border class: SINGLECORNERMATERIAL, SINGLEEDGEMATERIAL, then nine sections in the engine's own order — FILL, TOP_LEFT_CORNER, TOP_RIGHT_CORNER, BOTTOM_LEFT_CORNER, BOTTOM_RIGHT_CORNER, TOP_EDGE, LEFT_EDGE, RIGHT_EDGE, BOTTOM_EDGE. Each carries a <MATERIAL>, BLENDINGMODE, ALPHABLENDFIRST, FLIPHORIZONTAL, FLIPVERTICAL, ROTATED, plus STRETCHMODE on the stretchable ones (FILL and the four edges). Zero uses in the corpus — the layout is decoded and writable, but nothing shipped exercises it.

Keyframes and states

<Keyframe name="kf_fade_in" IDX="0" INTERPOLATION="Linear">
<ACTIONEXECUTER /> <!-- optional; this is where Stop/GotoFrameIndex go -->
<ImageState /> <!-- class decided by the owning widget -->
</Keyframe>

IDX is the frame number. INTERPOLATION is the easing into the next keyframe: None (7,559), Linear (3,054), Root (178), Sin (131), Circle (25), Square (2), CircleDecel (0).

The state class is fixed by the widget class — you cannot choose it:

WidgetStateWidgetState
ImageImageStatePlaceholder, WindowRectState
TextTextStateevery instance widget, ListBox, EditBox, SliderScaleState
RectShapeRectShapeState

Fields, cumulative down the hierarchy. All coordinates are u16; write a negative as 65536 + v. All colours are ARGB hex.

ClassFields
State (base of all)INTERPOLATIONFLAGS (u32 bitmask of which channels interpolate), STATECOLOR (ARGB tint; alpha is how you fade anything)
+ RotationStateROTATION (float degrees), ORIGIN.x, ORIGIN.y
+ PosStatePOSITION.x, POSITION.y
+ ScaleStateSCALEX, SCALEY (floats, 1 = native)
+ RectStateLEFT, RIGHT, TOP, BOTTOMin that order
+ TextBaseStateOFFSETY (float), ABSOFFSETY
+ TextStateSHADOWCOLOR, HEIGHT (point size), SHADOWOFFSETX/Y, LEADING, TRACKING
+ ImageStateSHADOWCOLOR, SHADOWOFFSETX/Y, TILING.x/y, OFFSET.x/y (floats), FLIPHORIZONTAL, FLIPVERTICAL, ACTUALSIZE, COLOR1COLOR4 (gradient-quad corners)
+ RectShapeStateOUTLINEWEIGHT, OUTLINECOLOR, FILLCOLOR1FILLCOLOR4, SHADOWCOLOR, SHADOWOFFSETX/Y

ScaleState positions an instance; RectState-derived states size a rectangle. PosState and RectState are siblings sharing storage — POSITION.x/y occupy the same offsets as LEFT/RIGHT.

Four different reference forms, easy to confuse:

FormWritten asUsed by
Name hashname="p_menu_nav" or #3D23C3C5every NamedObject: areas, elements, keyframes, property keys
FullLink<LINK slot LASTOBJECTTYPE IDS="a b c"/>UserData link properties, GenericObject targets, action arguments
AreaLink<LINK slot TIMING PACKAGE AREA ISUSINGDUPLICATEDAREA/>area instances, and the ListBox/Slider/EditBox sub-links
Resource ref<MATERIALLINK present id PACKAGE/>materials and font families

A name renders as the readable string only when re-hashing it reproduces the stored value, so #XXXXXXXX in a decode means the name is not recoverable — not that something is wrong. You may write either form.

IDS is a path from the package root, one name hash per level, crossing into instanced areas:

IDS="fcse FCSE_PAGE p_menu_nav #36150990 l_menu_nav_list"
^pkg ^page ^element ^the area ^element inside that area
it instances

LASTOBJECTTYPE is the type slot of the final object. A two-element path (common Cursor) is the common case: package, then area.

UserData properties

<PROPERTIES>
<PROPERTY key="LAYER" type="2" value="10" />
<PROPERTY key="SETTING_LABEL_LIST" type="18">
<LINK slot="66" LASTOBJECTTYPE="Focusable" IDS="fcse FCSE_PAGE p_menu_nav …" />
</PROPERTY>
</PROPERTIES>

type is the wire tag in decimal. Every other tag is legal and carries no payload.

typePayloadCorpus
2 (0x02)value — u32223
7 (0x07)value — float0
12 (0x0c)value — bool22
16 (0x10)value — ANSI string17
17/18/21 (0x11/0x12/0x15)<LINK> — a FullLink115 + others
19 (0x13)<STRINGRESOURCE TABLEID RESOURCEID/>19

Keys seen on areas: LAYER (159 — draw order, 10/50/126 are the common values), RESTRICT_INPUT, TOPLEVEL, SHOWCURSOR, MAINMENU_LISTACTIONS, BINKFILENAME/BINKLOOP/ BINKSTREAM (Magma can host Bink video), and the SETTING_* family — those are the named widget slots a native CSettingsPage looks up by name; see binding to native code.

Actions and events

An ActionCaller may hang off an Area, an Element, or a Keyframe, and where it sits decides what it is for:

SiteExecuter typeUsesWhat it does
KeyframeActionExecuter (bare)1,523Timeline control — fires when playback reaches the frame
ElementActionExecuterFocusable114Input/focus handling for a focusable widget
ElementActionExecuterListbox76…plus list selection
ElementActionExecuterPageInstance62…for an embedded page
ElementActionExecuterEditbox34…plus text change
ElementActionExecuterSlider10…plus value change
AreaActionExecuterPage34Page lifecycle
<ACTIONEXECUTER slot="138" type="ActionExecuterFocusable">
<ACTIONS>
<ACTION ACTIONNAME="NavBar_ButtonActivated">
<USERDATA name="NavBar_ButtonActivated"><PROPERTIES/></USERDATA>
</ACTION>
</ACTIONS>
<EVENTS>
<EVENT /><EVENT /><EVENT /><EVENT /><EVENT /><EVENT /><EVENT /><EVENT />
<EVENT /><EVENT /><EVENT /><EVENT /><EVENT />
<EVENT ACTIONINDEX="0" /> <!-- index 13 = Activate -->
<EVENT />
</EVENTS>
</ACTIONEXECUTER>

<ACTIONS> is a flat pool. <EVENTS> is a positional array: the n-th <EVENT> is event id n, and its ACTIONINDEX lists indices into the pool (space-separated for more than one). Bare ActionExecuter has no <EVENTS> at all — its actions fire unconditionally when the keyframe is reached. The empty groups are placeholders and must be present to keep the positions right; a decode writes them as ACTIONINDEX="" and an omitted attribute means the same thing.

Array lengths, and the event ids the corpus actually populates:

Executer<EVENT> countPopulated ids
ActionExecuterFocusable154, 5, 6, 13, 14
ActionExecuterPage163, 4, 6, 11
ActionExecuterPageInstance163, 4, 11, 12, 13
ActionExecuterSlider1611, 12, 15
ActionExecuterListbox173, 4, 11–15
ActionExecuterEditbox173, 4, 11, 13, 15

Event ids, from Util::GetType groups 19–21 and confirmed by which actions sit at each index:

idFocusable familyActionExecuterPage
3KeyDownKeyDown
4KeyUpKeyUp
5MouseDownMouseDown
6MouseUpMouseUp
7–10MouseDblClick, MouseMove, MouseEnter, MouseLeavesame
11SetFocusEnterPage
12KillFocusExitPage
13Activate — the click handlerOverlapped
14EscapeUnOverlapped
15widget change: ListBox selection, EditBox text, Slider valueTick
16ListBox/EditBox only; unused in the corpus

Ids 0–2 are never populated. The double meaning of 11–15 is exactly why the tag table carries two groups both starting at 11: ActionExecuterPageInstance — despite the name — uses the focus meanings (its id-11/12 actions come in matched SetFocus/KillFocus pairs, and id 13 carries DropDown_Activate), because a PageInstance is a focusable.

The action catalogue

ACTIONNAME is a raw CRC32(name) handed to ActionServer::MakeAction. The full registry — all 6 standard plus 81 game actions, where they are registered and how they dispatch — is on Interop with the Dunia engine. The registry is closed and lives in the game binary — you cannot define a new action from data. Names recovered by hashing every ASCII run in Dunia.dll; the three engine ones first, then the game's:

ActionUsesArguments (UserData keys)
Stop1,279Area to stop (link)
GotoFrameIndex128Area (link), Frame (u32)
Continue125Area to start (link)
GotoKeyframe8Keyframe (link)
SoundEvent45Sound Event Name (string), Sound Event Type (u32)
KeyPressed37Trigger (string)
NavBar_ButtonActivated63
Activated / Escaped35 / 2
ListBox_SelectionChanged38
MenuList_Item_Activated / _Escaped / _SelectItem22 / 21 / 10MenuList_SelectItem: a link
MenuList_Left_KeyDown / _Right_KeyDown10 / 10Trigger
Setting_Next_Value / _Previous_Value / _Activated8 / 7 / 2Trigger
CheckBox_Activate10
Slider_ValueChanged8
EditBox_TextChange4
DropDown_Activate / _Item_Activate / _Item_Escape28 / 3 / 3
ShowSelectionListNomad24Target list (link), Focus? (bool)
SetFocusListNomad / SetFocusNomad11 / 5Target list / Target element (link), Top? (bool)
PlayerPopup_Show / _Next / _Previous5 / 2 / 2Trigger
Vote4Trigger, Yes (bool)
IGE_*, Bazaar_*, Multi_*, Profile_*, CHAT_*1–86screen-specific; see the corpus

Stop, Continue, GotoFrameIndex and GotoKeyframe are the general-purpose ones — they take an area or keyframe link and drive its timeline, and they are what makes data-only animation logic possible. Everything else calls back into compiled game code for a specific screen and will do nothing useful outside it.

Enum values

Quoting the Util::GetType tag table. A value the table does not name stays a bare number, which is how fields the engine masks (BLENDINGMODE low byte, MASKMODE low 3 bits) keep their high bits.

FieldValues
INTERPOLATIONNone, Linear, Square, Root, Sin, Circle, CircleDecel
ALIGNMENTXLEFT, CENTER, RIGHT, JUSTIFY
ALIGNMENTYTOP, CENTER, BOTTOM
MASKMODENOMASK, SETMASK, USEMASK, USEMASK_INVERTED
ADDRESSINGMODEU/VWrap, Mirror, Clamp, Border
BLENDINGMODE27 modes; only six appear in shipped UI — Normal, Add, Modulate, Multiply, Lighten 2X, Lighten 4X (counts over the wider 500-package set are on the field-names page)
DIRECTION (neighbours)0 UP, 1 DOWN, 2 LEFT, 3 RIGHT

Escape hatches

The XML is lossless by construction, so three fallback spellings exist and are always accepted:

  • 0x… for a float whose decimal spelling is not bit-exact (NaN payloads, denormals).
  • base64:… for a string whose bytes cannot survive an XML attribute.
  • #XXXXXXXX for a name that does not resolve.

An absent optional attribute and an empty one mean different things: omit it to mean "not present", because present-with-zero writes different bytes.