MENU file format

Applies to:
BGEE, BG2EE, IWDEE (patch 2.0 and later)

General Description
MENU files (resource type 0x0408, extension .menu) define the user interface of the Enhanced Editions since patch 2.0. A MENU file is plain text. It declares menus (screens and panels), the items (controls) they contain, and the Lua code that drives them.

The engine loads exactly one MENU resource by itself: UI.MENU, read once at start-up after the engine's own Lua functions have been registered. The file is lexed and parsed in a single pass. Lua code embedded in it is compiled into the same global Lua state that the rest of the user interface uses. The Lua functions available to menu code are listed on the MENU Lua interface page, and the other globals the engine defines on the Engine Lua globals page.

This page describes the parser and the data structures it builds, as implemented in patches 2.6.6.0 and 2.7.3.0 of BGEE, BG2EE and IWDEE. All six of these builds were examined. Their keyword tables, grammars and parser tables are identical, and the few differences that exist are listed under Version differences. PSTEE and other patch levels were not examined.

Descriptions marked † are inferred from the engine's own field and function names; the code that consumes them was not traced in detail. Everything else on this page was checked against the executables.

The file is read as one NUL-terminated string and split into tokens as follows:

Token Rules
White space Any character accepted by the C function isspace (space, tab, CR, LF, VT, FF) separates tokens.
Comment -- starts a comment that runs to the end of the line. A single - is a minus sign.
{ } Open and close a block.
String Text between a pair of double quotes ("…") or a pair of single quotes ('…'). The string ends at the next occurrence of its opening quote character, so the other quote character can appear inside it. There are no escape sequences, and a string can span several lines. An unterminated string runs to the end of the file.
Number A run of decimal digits, converted with atoi. There are no fractions or hexadecimal numbers: 1.5 is read as 1, a skipped . and 5. A negative value is written as - followed by a number, and the sign may be repeated (- -5 is 5).
Word A run of ASCII letters and digits that does not start with a digit. If the word matches one of the keywords, compared case-insensitively, it is that keyword; otherwise it is treated exactly like a string. Underscores are not part of a word: unquoted My_Menu is the string My followed by the keyword menu. Always quote names. A word of 64 or more characters makes the engine abort.
Inline Lua Text between two backticks (`…`) is Lua code. The lexer runs it as soon as it reaches it, under the chunk name menu, so it runs before any part of the file that follows it is parsed. It produces no token. The engine drops the last character before the closing backtick, so end every inline block with a line break or a space. A compile error or a runtime error is logged and shown in a message box, and parsing continues.
Anything else Any other character (for example , ; = ( .) is silently skipped, so area 0, 0, 10, 10 is the same as area 0 0 10 10.

Syntax errors. When a token cannot be accepted, the engine logs Invalid token in menu: [token] on line L, character C. A Menu Error message box is shown as well when the Debug Mode option of the Program Options section is set. The line number counts the line breaks before the token, so the first line of the file is line 0, and on that line the character number is meaningless. The offending token is discarded and parsing continues. Further errors are not reported until three tokens have been accepted again.

The parser is generated by the Lemon parser generator. The grammar below was rebuilt from its tables (157 rules) and is identical in all examined builds. Quoted symbols are literal tokens. + means one or more and | separates alternatives. Keywords are case-insensitive.

file        ::= menu+
menu        ::= 'menu' '{' menu_prop+ item+ '}'
item        ::= item_type '{' item_prop+ '}'
              | 'list' '{' column+ item_prop+ '}'
item_type   ::= 'label' | 'text' | 'button' | 'edit' | 'movie' | 'slider'
              | 'handle' | 'slot' | 'map' | 'template'
column      ::= 'column' '{' column_prop+ item+ '}'
column_prop ::= 'width' value
item_prop   ::= one of the item properties below
              | item                         -- a nested item
menu_prop   ::= one of the menu properties below

number      ::= NUMBER | '-' number
value       ::= number | STRING | 'lua' STRING
color       ::= value | number number number number
halign      ::= 'left' | 'top' | 'right' | 'bottom' | 'center'
This grammar implies the following rules:
  • Every menu needs at least one menu property and at least one item, and all menu properties must come before the first item.
  • Every item needs at least one property. An empty block such as label { } is a syntax error.
  • In a list, all column blocks come first and the list's own properties follow them. Each column starts with one or more width entries and then holds one or more items.
  • The parser accepts every item property in every item type. The type only decides which fields are used when the item is drawn and handled.
  • Alignment words are interchangeable: left and top both mean 0, right and bottom both mean 1, and center means 2.

The keyword table has 124 entries for 122 distinct words, and it is identical in all examined builds. Keywords are matched case-insensitively; the spelling below is the engine's. name and top each appear twice in the table with the same meaning, and transparent is the same keyword as usealpha.

action, actionalt, actionbar, actiondbl, actiondrag, actionenter, actionexit, actionHold, actionScroll, actionsimpledrag, actionsimpledrop, actionUpdate, align, area, areamap, background, bam, bitmap, bottom, button, center, clickable, clunkyScroll, color, colordisplay, column, count, dynamic, edit, enabled, encumbrance, fill, font, force, frame, frameTimes, full, func, glow, greyscale, handle, hide, hidehighlight, highlight, highlightgroup, icon, ignoreEsc, ignoreEvents, label, left, list, loop, lower, lua, map, maxchars, maxlines, memorizedSpellInfo, menu, modal, mosaic, movie, name, offset, on, onclose, onopen, opacity, overlayTint, pad, palette, panel, paperdoll, placeholder, point, portrait, position, progressbar, pulse, queuedMovie, rectangle, res, respectClipping, respectConstraints, right, rowbackground, rowheight, rowwidth, scaleToClip, scrollbar, seperator, sequence, sequenceonce, settings, shadow, size, skipReset, slider, sliderBackground, slot, slotinfo, sound, spellInfo, state, style, subtitle, table, template, text, tint, toggle, tooltip, top, transparent, upper, usages, usealpha, useFontZoom, useOverlayTint, var, width, worldmap

Form Meaning
NUMBER A literal integer. In a value it is stored as an integer constant.
STRING A literal string, used exactly as written. Where a property takes Lua code as a string (marked statement or expression in the tables), the string is compiled into a Lua function. See How a menu becomes Lua.
lua STRING A Lua expression. It is compiled into a function that returns the expression, and the function is called every time the engine needs the value.
value Any of the three forms above. A property that needs a number reads an integer constant directly, or calls the Lua function and takes its result: a number is truncated to an integer and a boolean becomes 0 or 1, while anything else, or a Lua error, gives 0. A property that needs a string reads a string constant directly, or calls the function and takes its result as a string. Integer and string constants are not converted into each other, so use the form the property expects.
color Four numbers a b c d are packed into the 32-bit value a + b×28 + c×216 + d×224. A single number or string is a key into the global Lua table fontcolors (a number is converted to its decimal text). The table entry must be a hexadecimal string, and it is read while the file is parsed. For a number key with no matching entry the colour is 0xFFFFFFFF; a string key must exist. lua "…" gives a colour that is evaluated at run time.

A menu is identified by its name together with its panel and state numbers. Names are compared case-insensitively. Menus are kept sorted by these three keys and found by binary search, so if two menus share all three, which one is found is unspecified. Every menu should have a name, because a missing name is passed to the string comparison as a null pointer. The engine stores at most 256 menus and does not check this limit.

Syntax Description
name STRINGMenu name, used by Infinity_PushMenu and similar functions.
panel numberPanel number: the second part of the menu's identity (default 0).
state numberState number: the third part of the menu's identity (default 0).
modal
modal value
Marks the menu as modal. Without an argument the value is 1. †
opacity valueMenu opacity. †
greyscale valueDraws the menu in greyscale. †
onopen STRINGLua statement run when the menu opens.
onclose STRINGLua statement run when the menu closes.
enabled STRINGLua expression that decides whether the menu is active. †
offset number numberMenu offset x y. It can be read and changed with Infinity_GetOffset and Infinity_SetOffset.
align halign halignHorizontal and vertical alignment of the menu (0, 1 or 2; see Grammar). †
size number numberMenu width and height. †
ignoreEscSets the menu's ignoreEsc flag, so the menu ignores the Escape key. †

Keyword Engine type Notes
labelITEM_NONE (0)Static item: text, images, rectangles.
textITEM_TEXT (1)Text area.
buttonITEM_BUTTON (2)Clickable item.
listITEM_LIST (3)Table of rows built from column blocks. Each list also creates an empty global Lua table; see How a menu becomes Lua.
editITEM_EDIT (4)Text input bound to a Lua variable (var).
movieITEM_MOVIE (5)Plays a WBM movie. See Infinity_PlayMovie.
sliderITEM_SLIDER (6)Slider bound to a Lua variable (position).
handleITEM_HANDLE (7)Drag handle. †
slotITEM_SLOT (9)Inventory or spell slot.
mapITEM_MAP (10)Area map or world map, selected with areamap or worldmap.
templateITEM_TEMPLATE (11)Holds one nested item, which serves as the prototype for instances created with Infinity_InstanceAnimation.

The engine's type list also contains ITEM_DRAGGABLE (8), but no keyword creates it.

Nested items. Any item can contain another item block. The parser builds items in one shared, static work record and only copies it into a new item when the closing } is reached. A nested block therefore receives every property the enclosing item declared before it, and the enclosing item starts again from defaults. Only the most recent nested block is remembered (in the enclosing item's template slot). This is why a template is written with its nested item first and its own properties after it:

template
{
	label
	{
		bam "B3TBACKG"
		scaleToClip
	}
	name "B3Timer_Menu_TEMPLATE_Background"   -- after the nested item
}
Items are kept in file order. Only top-level items are entered in nameToItem; nested items and items inside list columns are not.

The group headings name the engine sub-record the property writes to. Properties that write the same field overwrite each other, and the last one wins.

Syntax Description
General
name STRINGItem name. Top-level items with a name are entered in nameToItem, which the Infinity_* functions use to find them.
area x y w hPosition and size. All four are numbers.
pad number number number numberPadding, as four values. †
align halign halign
text align halign halign
Horizontal and vertical alignment (both forms set the same fields).
enabled STRINGLua expression. Before it is evaluated the engine sets the global instanceId to the item's instance number. While the expression is false, the item is not drawn.
ignoreEventsThe item ignores input events. †
opacity valueItem opacity. †
glow valueGlow effect. †
pulse valuePulse effect. †
fill colorFill colour. †
rectangle numberDraws a frame rectangle of style number. The engine stores number + 1 and treats 0 as “none”. †
rectangle opacity valueOpacity of that rectangle. †
useOverlayTint STRINGLua expression that switches the overlay tint on and off. †
overlayTint number number numberOverlay tint, as three components. †
Text (text record)
text STRINGText, translated while the file is parsed by calling the global Lua function getUiString (2.7.3.0) or t (2.6.6.0) with the string. That function must already exist. The untranslated string is kept so the menus can be translated again when the language changes.
text numberText given as a string reference into dialog.tlk.
text lua STRINGLua expression that returns the text.
text font STRINGFont resource. †
text point numberFont size in points. †
text useFontZoom numberWhether the font-zoom option applies. †
text color colorText colour.
text upper / text lowerUpper-case or lower-case text. †
text shadow valueText shadow. †
text highlight valueText highlight. †
text style STRINGCopies a style from the global Lua table styles[STRING], read while the file is parsed. The fields color (a key into fontcolors), font, point, useFontZoom, halign and valign are always read, so a style should define all six. halign and valign are strings: "left"/"top" = 0, "right"/"bottom" = 1, any other string = 2 (case-insensitive). If upper is present at all (even false), upper case is switched on. If pad is present, its entries 1 to 4 are read as the four padding values. The style overwrites earlier text, alignment and padding settings; later properties can override it.
Images (bam, mosaic, bmp records)
bam valueBAM resource name.
frame valueBAM frame. †
sequence valueBAM sequence (cycle), animated. †
sequenceonce valueBAM sequence played once. †
scaleToClipScales the BAM to the item area. †
greyscale valueDraws the BAM in greyscale. †
usealpha value
transparent value
Alpha blending for the BAM. transparent is the same keyword as usealpha; see the note below.
mosaic valueMOS resource name.
respectClippingThe mosaic respects clipping. †
bitmap valueBitmap resource name. †
bitmap res STRINGLua expression that gives the bitmap resource. †
bitmap area x y w hSource area within the bitmap. †
Tooltip (tooltip record)
tooltip valueTooltip text.
tooltip force valueForces the tooltip to show. †
tooltip force topSets the separate “force top” flag. †
tooltip position numberTooltip position. †
Actions (all take a Lua string; see How a menu becomes Lua)
action STRINGStatement. The main action, e.g. a left click on a button. †
actionalt STRINGStatement. Alternative action (right mouse button). †
actiondbl STRINGStatement. Double click. †
actiondrag STRINGExpression. Dragging †. Unlike the other actions, this one is compiled as an expression, so its return value is available to the engine.
actionenter STRINGStatement. The mouse enters the item. †
actionexit STRINGStatement. The mouse leaves the item. †
actionUpdate STRINGStatement. Update while the item is captured. †
actionsimpledrag STRING
actionsimpledrop STRING
Statement. Start and end of a simple drag-and-drop. †
actionHold STRINGStatement. The mouse button is held down. †
actionScroll STRINGStatement. Mouse-wheel scrolling over the item. †
Lists (list record)
table STRINGLua expression that returns the table of rows. The engine walks this table with next, so the row order is Lua's table order. While each row is processed, the global rowNumber holds the row's key and rowSelected holds the list's selected row.
rowheight number
rowheight dynamic
Fixed row height, or rows sized to their content. †
rowwidth numberRow width. †
rowbackground valueRow background. †
highlight color colorColour of the highlighted row. †
hidehighlightHides the row highlight. The highlight is shown by default.
seperator STRINGLua expression for row separators (the misspelling is the engine's). †
column { width value … items }A column of the list (see Grammar).
Scroll bar (scrollbar record)
scrollbar STRINGBAM resource for the scroll bar. †
scrollbar func STRINGName of a scroll function (stored as text). †
scrollbar hide valueHides the scroll bar. †
scrollbar respectConstraintsFlag. †
scrollbar clunkyScroll numberScroll step. †
scrollbar skipResetKeeps the scroll position when the content is reset. †
Edit fields (edit record)
var STRINGName of the global Lua variable that holds the text (Infinity_EnterEdit assigns this variable).
maxlines numberMaximum number of lines. †
maxchars numberMaximum number of characters. †
placeholder STRINGPlaceholder text, translated while the file is parsed, like text STRING.
Buttons (button record)
clickable valueWhether the button can be clicked. This is the value Infinity_IsItemEnabled reports.
toggle STRINGName of a Lua variable for a toggle button (stored as text). †
on STRINGValue of the toggle variable that means “on” (stored as text). †
sound STRINGClick sound resource. †
highlightgroup STRINGHighlight group name. When the item is created and no global Lua variable of that name exists yet, the engine creates one that holds the item (as light userdata), so the global refers to the group's first item.
portrait valuePortrait. †
paperdoll valuePaper doll. †
encumbranceEncumbrance display. †
colordisplay numberColour display. †
frameTimesFlag. †
actionbar valueAction bar button. †
Sliders (slider record)
position STRINGName of the Lua variable that holds the slider position (stored as text). †
settings valueNumber of slider settings. †
sliderBackground STRINGBackground resource. †
palette numberPalette. †
Movies (movie record)
loopLoops the movie (used by Infinity_PlayMovie).
queuedMovie valueNext movie to play. †
subtitle area x y w hSubtitle area. †
subtitle background number number number numberSubtitle background colour, packed like a four-number color. †
Slots (slot record)
icon valueIcon. †
count valueStack count. †
usages valueCharges. †
highlight valueSlot highlight. †
tint valueSlot tint. †
slotinfo STRINGShorthand. With T as the string, it generates:
icon lua "T.item.icon", count lua "T.item.count", usages lua "T.item.usages", highlight lua "T.highlight", tint lua "T.item.tint", tooltip lua "T.tooltip", enabled "T.valid", action = expression Infinity_SwapSlot(T.id), actionalt = statement showItemDescriptionInventory(T.slotName), actiondbl = statement slotDoubleClick(T.slotName).
spellInfo STRINGShorthand. With T as the string, it generates:
enabled "T ~= nil", icon lua "T.icon", tooltip lua "Infinity_FetchString(T.name)", greyscale lua "T.castable == 0", bam lua "'IVSLOT'", action = expression memorizeSpell(T), actionalt = statement showSpellDetails(T, false).
memorizedSpellInfo STRINGSame as spellInfo, except that action = expression unmemorizeSpell(T) and actionalt = statement showSpellDetails(T, true).
Other
progressbar valueProgress-bar percentage. †
progressbar color color
progressbar full color
Bar colour, and the bar colour when full. †
areamap / worldmapMap type: 1 = area map, 2 = world map.

Note on transparent. The engine's grammar has a rule transparent number that would set a movie's transparency, but the keyword table maps the word transparent to the same token as usealpha. That rule can never be reached: transparent n sets the BAM alpha flag, and a movie's transparency (read by Infinity_PlayMovie) cannot be set from a MENU file.

Each property that takes Lua code is compiled while the file is parsed, by running a generated chunk that defines a global function. A reference to that function is then stored in the item. The global name is a fixed prefix followed by a memory address in hexadecimal (the C %p format). It is unique within one run, but it is not stable between runs, so never refer to these globals by name. Two chunk shapes are used:

function <prefix><address> () <code> end          -- statement form
function <prefix><address> () return <code> end   -- expression form
Form Properties (prefix of the generated global in parentheses)
Statementonopen (onopen), onclose (onclose), action (action), actionalt (actionalt), actiondbl (actiondbl), actionenter (actionenter), actionexit (actionexit), actionUpdate (actionupdate), actionsimpledrag (actionsimpledrag), actionsimpledrop (actionsimpledrop), actionHold (actionhold), actionScroll (actionscroll)
Expressionmenu and item enabled (enabled), actiondrag (actiondrag), table (table), bitmap res (resname), seperator (seperator), useOverlayTint (useOverlayTint), every lua STRING value (variant), and the values generated by slotinfo/spellInfo (variant_icon, variant_count, variant_usages, variant_highlight, variant_tint, variant_tooltip, variant_greyscale, variant_bam, valid, enabled, action)

The generated functions take no arguments. The code text is pasted in unchanged, so a Lua syntax error in it just leaves the function undefined; the error is not reported. Code in a property string is only compiled at load time; it runs later, so it can call functions that are defined further down in the file. In contrast, the following are evaluated during parsing and must exist before the menu that uses them: the translation function used by text STRING and placeholder, the styles table used by text style, and the fontcolors table used by color values. The usual way to provide them is an inline `…` block earlier in the file.

The parser also creates these globals:
  • One empty table per list item, named list followed by an address.
  • One global per new highlightgroup name (see Buttons above).
  • After the whole file has been parsed, nameToItem = {} is rebuilt. Then, for every top-level item with a name (menus in their sorted order, items in file order), the engine runs nameToItem['name'] = nameToItemPointer, where nameToItemPointer holds the item as light userdata. A later item with the same name replaces an earlier one. A name that contains ' breaks the generated statement and is skipped.
See the MENU Lua interface page for the globals that are set at run time and for the Lua functions that operate on menus and items.

Limit Behaviour
Menus: 256Size of the engine's menu array. The parser does not check it, so exceeding it overwrites the menu stack that follows the array in memory.
Menu stack: 256 entriesSize of the stack of open menus.
Word length: 63 charactersA longer unquoted word aborts the game. Quoted strings are not limited.
Parser stack: 100 entriesOn overflow the parser empties its stack. Ordinary nesting stays far below this limit.

Keywords, grammar, parser tables and the property-to-field mapping are identical in all six examined builds (BGEE, BG2EE and IWDEE at 2.6.6.0 and 2.7.3.0). The only difference in the parser is this:

Behaviour 2.6.6.0 2.7.3.0
Translation function called for text STRING and placeholder STRINGtgetUiString

`
-- inline Lua: runs when the lexer reaches it (note the line break before the closing backtick)
myMenuTitle = "Hello"
`

menu
{
	name "MY_MENU"            -- quoted: an unquoted name cannot contain '_'
	align center center
	ignoreEsc
	onopen "Infinity_Log('opened')"

	label
	{
		area 0 0 200 40
		text lua "myMenuTitle"    -- evaluated at run time
		text align center center
	}

	button
	{
		name "MY_MENU_CLOSE"
		area 0 50 200 40
		bam "MYBUTTON"            -- a BAM resource of your own
		enabled "true"
		action "Infinity_PopMenu()"
	}
}