|
| 1 | +--- |
| 2 | +name: gum-file-format |
| 3 | +description: Reading and safely hand-editing Gum's XML files — enum-to-int mappings for Units/Origins/ChildrenLayout, qualified InstanceName.Property variables, and why hand-edited files silently break. Triggers: editing .gusx/.gucx/.gutx by hand, WidthUnits/XOrigin values, gumcli check errors. |
| 4 | +--- |
| 5 | + |
| 6 | +# Gum File Format |
| 7 | + |
| 8 | +Gum projects are XML, serialized with .NET's `XmlSerializer`. Prefer editing |
| 9 | +them with the Gum tool or `gumcli`; when you must read or hand-edit, this skill |
| 10 | +covers the structure and the traps. **Always run `gumcli check` after a hand |
| 11 | +edit** — several failure modes below are silent otherwise (see **gumcli**). |
| 12 | + |
| 13 | +## File → root element |
| 14 | + |
| 15 | +| Extension | Root element | |
| 16 | +|-----------|--------------| |
| 17 | +| `.gumx` (project) | `<GumProjectSave>` — lists element references, loaded first | |
| 18 | +| `.gusx` (screen) | `<ScreenSave>` | |
| 19 | +| `.gucx` (component) | `<ComponentSave>` | |
| 20 | +| `.gutx` (standard element) | `<StandardElementSave>` | |
| 21 | +| `.behx` (behavior) | `<BehaviorSave>` | |
| 22 | + |
| 23 | +## Element structure |
| 24 | + |
| 25 | +Every file needs the complete root tag **with both XML-schema namespaces** — |
| 26 | +the `xsi:`/`xsd:` prefixes used on every `<Value>` are undefined without them. |
| 27 | +The element has a `<Name>`, an optional `<BaseType>` (what it inherits from, |
| 28 | +e.g. `Container`), one or more `<State>`s (the first is `Default`), and a list |
| 29 | +of `<Instance>`s (child objects). A state holds `<Variable>` entries. This is a |
| 30 | +complete, valid screen (it passes `gumcli check`): |
| 31 | + |
| 32 | +```xml |
| 33 | +<?xml version="1.0" encoding="utf-8"?> |
| 34 | +<ScreenSave xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
| 35 | + <Name>MainMenu</Name> |
| 36 | + <State> |
| 37 | + <Name>Default</Name> |
| 38 | + <Variable Type="ChildrenLayout" Name="MenuStack.ChildrenLayout"><Value xsi:type="xsd:int">1</Value></Variable> |
| 39 | + <Variable Type="string" Name="Title.Parent"><Value xsi:type="xsd:string">MenuStack</Value></Variable> |
| 40 | + <Variable Type="string" Name="Title.Text"><Value xsi:type="xsd:string">Main Menu</Value></Variable> |
| 41 | + </State> |
| 42 | + <Instance><Name>MenuStack</Name><BaseType>Container</BaseType></Instance> |
| 43 | + <Instance><Name>Title</Name><BaseType>Text</BaseType></Instance> |
| 44 | +</ScreenSave> |
| 45 | +``` |
| 46 | + |
| 47 | +**`<Name>`** is the element's name — path-qualified for an element in a |
| 48 | +subfolder (`Elements/Divider`), otherwise bare (`MainMenu`). It must match the |
| 49 | +reference to it in the `.gumx`. |
| 50 | + |
| 51 | +**Qualified variable names.** A variable named `MenuStack.ChildrenLayout` sets |
| 52 | +the `ChildrenLayout` of the instance named `MenuStack`. No dot means an |
| 53 | +element-level value. The name before the dot must match an `<Instance>`'s |
| 54 | +`<Name>`. |
| 55 | + |
| 56 | +**Parenting instances.** Instances are siblings by default — all direct children |
| 57 | +of the element root. To nest one inside another, give the child a `Parent` |
| 58 | +variable whose value is the parent instance's name (`Title.Parent = MenuStack` |
| 59 | +above puts `Title` inside `MenuStack`, so the container's stacking layout |
| 60 | +arranges it). Without it, a stacking container has nothing to lay out. |
| 61 | + |
| 62 | +## Landmine: enum values are stored as integers |
| 63 | + |
| 64 | +For an enum-typed variable, the `Type` attribute holds the enum name but the |
| 65 | +`<Value>` holds its **underlying int** (`xsi:type="xsd:int"`), not the name. |
| 66 | +`WidthUnits` = `2` means `RelativeToParent`. You must know the mappings to read |
| 67 | +or write these correctly: |
| 68 | + |
| 69 | +### WidthUnits / HeightUnits — `DimensionUnitType` |
| 70 | + |
| 71 | +| Int | Name | Int | Name | |
| 72 | +|----|------|----|------| |
| 73 | +| 0 | Absolute | 6 | MaintainFileAspectRatio | |
| 74 | +| 1 | PercentageOfParent | 7 | Ratio | |
| 75 | +| 2 | RelativeToParent | 8 | AbsoluteMultipliedByFontScale | |
| 76 | +| 3 | PercentageOfSourceFile | 9 | ScreenPixel | |
| 77 | +| 4 | RelativeToChildren | 10 | RelativeToMaxParentOrChildren | |
| 78 | +| 5 | PercentageOfOtherDimension | | | |
| 79 | + |
| 80 | +### XUnits / YUnits — `PositionUnitType` |
| 81 | + |
| 82 | +One shared enum; some values apply to the X axis, some to Y. **This is not the |
| 83 | +`GeneralUnitType` enum you use in C# code** — on disk `XUnits`/`YUnits` are |
| 84 | +`PositionUnitType`. Do not copy int values between the two. |
| 85 | + |
| 86 | +| Int | Name | Axis | Int | Name | Axis | |
| 87 | +|----|------|:----:|----|------|:----:| |
| 88 | +| 0 | PixelsFromLeft | X | 5 | PixelsFromBottom | Y | |
| 89 | +| 1 | PixelsFromTop | Y | 6 | PixelsFromCenterX | X | |
| 90 | +| 2 | PercentageWidth | X | 7 | PixelsFromCenterY | Y | |
| 91 | +| 3 | PercentageHeight | Y | 8 | PixelsFromCenterYInverted | Y | |
| 92 | +| 4 | PixelsFromRight | X | 9 | PixelsFromBaseline | Y | |
| 93 | + |
| 94 | +### XOrigin, and Text `HorizontalAlignment` — `HorizontalAlignment` |
| 95 | + |
| 96 | +`0` = Left, `1` = Center, `2` = Right. |
| 97 | + |
| 98 | +### YOrigin, and Text `VerticalAlignment` — `VerticalAlignment` |
| 99 | + |
| 100 | +`0` = Top, `1` = Center, `2` = Bottom, `3` = TextBaseline. |
| 101 | + |
| 102 | +### ChildrenLayout (on a container) — `Type="ChildrenLayout"` |
| 103 | + |
| 104 | +`0` = Regular, `1` = TopToBottomStack, `2` = LeftToRightStack, |
| 105 | +`3` = AutoGridHorizontal, `4` = AutoGridVertical. |
| 106 | + |
| 107 | +## Landmine: two on-disk variable shapes exist |
| 108 | + |
| 109 | +Gum has written variables two ways over time — a compact **attribute form** |
| 110 | +(current tool output) and an expanded **element form** (older files). Both load: |
| 111 | + |
| 112 | +```xml |
| 113 | +<!-- attribute form --> |
| 114 | +<Variable Type="DimensionUnitType" Name="WidthUnits"><Value xsi:type="xsd:int">0</Value></Variable> |
| 115 | +<!-- element form --> |
| 116 | +<Variable><Type>DimensionUnitType</Type><Name>WidthUnits</Name><Value xsi:type="xsd:int">0</Value><SetsValue>true</SetsValue></Variable> |
| 117 | +``` |
| 118 | + |
| 119 | +Do not hand-craft variables from scratch by guessing the shape. Copy the shape |
| 120 | +of an existing variable in the same file, or better, let the tool/`gumcli` |
| 121 | +write them and edit values only. |
| 122 | + |
| 123 | +## Landmine: wrong element names are dropped silently |
| 124 | + |
| 125 | +`XmlSerializer` ignores XML elements it does not recognize — it does not error. |
| 126 | +So `<States>` instead of `<State>`, or `<InstanceSave>` instead of `<Instance>`, |
| 127 | +loads as an **empty** element with no warning, and your UI is silently missing |
| 128 | +content. `gumcli check` is built to catch exactly this. Run it after every hand |
| 129 | +edit. |
| 130 | + |
| 131 | +## Bottom line |
| 132 | + |
| 133 | +Read these files freely; the tables above let you understand and sanity-check |
| 134 | +them. But to *write* them, prefer the Gum tool or `gumcli`, and validate with |
| 135 | +`gumcli check` before trusting the result. Data-model reference: |
| 136 | +<https://docs.flatredball.com/gum/gum-tool/gum-elements>. |
0 commit comments