SeString
The game uses a custom null-terminated string implementation, which allows strings to carry binary payloads.
The name SeString was invented at the time of its discovery and has been in use in the plugin development community ever since. At some point RTTI data was embedded into the exe and since then we know the class implementing them is actually called Utf8String.
Internally, the game has a MacroEncoder to convert a string representation of the binary payload (what we call macro string) to binary data, and a MacroDecoder, which can decode binary payloads to execute whatever the macro specific implementation should do.
This guide will use the most up-to-date C# implementation to parse and create
SeStrings, which is Luminas ReadOnlySeString
or ReadOnlySeStringSpan
and the SeStringBuilder
.
The class Lumina.Text.SeString
is an old implementation and should no longer
be used.
The same applies to the Dalamuds SeString and SeStringBuilder classes, but since
many Dalamuds APIs still require Dalamuds SeString type, you can convert
Lumina.Text.ReadOnly.ReadOnlySeString
and
Lumina.Text.ReadOnly.ReadOnlySeStringSpan
to
Dalamud.Game.Text.SeStringHandling.SeString
using the ToDalamudString()
extensions
found in the Dalamud.Utility
namespace.
Dalamuds SeString class is missing a lot of payloads types, has incorrect payload/macro names, and has no support for expressions as explained in this guide.
ReadOnlySeString
is an owning type that manages the underlying data buffer. In
contrast, ReadOnlySeStringSpan
is a lightweight, non-owning view, useful for
scenarios like parsing a SeString from a raw pointer without taking ownership.
Payloads
Payloads have several use cases, such as changing the text color, play chat
sound effects, add interactable links, add logic to them with if
and switch
macros and they can even be used to fill in local or global parameters (like the
players name or the current time).
Each payload has the following structure:
- start byte (
0x02
) - macro code (1 byte)
- length of the macro (integer expression)
- macro-specific expressions
- end byte (
0x03
)
Let's see it in action by writing text in bold letters.
To demonstrate this, we're going to use the SeString Creator widget that can be
found in /xldata
. It allows us to experiment with macro strings, preview and
inspect the evaluated result.
The macro string <bold(1)>
enables bold text, and <bold(0)>
disables bold
text.
In binary form, this would look like this:
0x57, 0x65, 0x6C, 0x63, 0x6F, 0x6D, 0x65, 0x20, // Text "Welcome "
0x02, // Payload start
0x19, // Macro code for bold
0x02, // Integer expression for the payload length (1)
0x02, // Integer expression for the enable state (on)
0x03, // Payload end
0x50, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x20, 0x4E, 0x61, 0x6D, 0x65, // Text "Player Name"
0x02, // Payload start
0x19, // Macro code for bold
0x02, // Integer expression for the payload length (1)
0x01, // Integer expression for the enable state (off)
0x03, // Payload end
0x21 // Text "!"
This can be done programmatically:
- Variant 1
- Variant 2
- Variant 3
var example = new SeStringBuilder()
.Append("Welcome ")
.BeginMacro(MacroCode.Bold)
.AppendIntExpression(1)
.EndMacro()
.Append("Player Name")
.BeginMacro(MacroCode.Bold)
.AppendIntExpression(0)
.EndMacro()
.Append("!")
.ToReadOnlySeString();
var example = new SeStringBuilder()
.Append("Welcome ")
.AppendSetBold(true)
.Append("Player Name")
.AppendSetBold(false)
.Append("!")
.ToReadOnlySeString();
var example = new SeStringBuilder()
.Append("Welcome ")
.AppendBold("Player Name")
.Append("!")
.ToReadOnlySeString();
But before we get to macros, let's have a look at expressions first.
Expressions
There are several types of expressions and they are identified by the value range in the first byte.
Integer Expressions
If the first and only byte is > 0 and < 0xD0, it's as simple as subtracting
1 from the byte for the actual integer value.
This is done to avoid byte 0x00, which would be intepreted as null-terminator.
Whether the value is used as unsigned or signed integer depends on the
implementation of the macro parsing it.
Example <num(3)>
:
- Binary Form
- Code
0x02, // Payload start
0x20, // Macro code for num
0x02, // Integer expression for the payload length (1)
0x04, // Integer expression for the value (3)
0x03 // Payload end
var example = new SeStringBuilder()
.BeginMacro(MacroCode.Num)
.AppendIntExpression(3)
.EndMacro()
.ToReadOnlySeString();
If the first byte is >= 0xF0 and <= 0xFE, the integer expression is of variable length. The number of bytes to read is encoded in the lower 4 bits of the type byte. See SeExpressionUtilities.TryDecodeUInt for more details.
Example <num(300000)>
:
- Binary Form
- Code
0x02, // Payload start
0x20, // Macro code for num
0x05, // Integer expression for the payload length (3)
0xF6, // Integer expression for the value
0x04, 0x93, 0xE0, // 0x0493E0 = 300000
0x03 // Payload end
var example = new SeStringBuilder()
.BeginMacro(MacroCode.Num)
.AppendIntExpression(300000)
.EndMacro()
.ToReadOnlySeString();
Placeholder Expressions
Between the integer expression types, placeholder expressions snuck in.
If the type byte is >= 0xD0 and <= 0xDF or 0xEC, the following placeholders
are used:
Type Byte | Macro String | Description |
---|---|---|
0xD8 | t_msec | Uses the millisecond value in the contextual time storage. |
0xD9 | t_sec | Uses the second value in the contextual time storage. |
0xDA | t_min | Uses the minute value in the contextual time storage. |
0xDB | t_hour | Uses the hour value in the contextual time storage, ranging from 0 to 23. |
0xDC | t_day | Uses the day of month value in the contextual time storage. |
0xDD | t_wday | Uses the weekday value in the contextual time storage, ranging from 1 (Sunday) to 7 (Saturday). |
0xDE | t_mon | Uses the month value in the contextual time storage, ranging from 0 (January) to 11 (December). |
0xDF | t_year | Uses the year value in the contextual time storage, where 0 means the year 1900. |
0xEC | stackcolor | Uses the last color value pushed. |
Placeholder types 0xD0 to 0xD7 are currently unknown.
Time-related placeholders can use the time set via the settime
or
setresettime
macros.
Example The current time is: <settime(1743880207)><num(t_hour)>:<num(t_min)>
(The current time is: 21:10):
- Binary Form
- Code
0x54, 0x68, 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x20, 0x69, 0x73, 0x3A, 0x20, // Text "The current time is: "
0x02, // Payload start
0x07, // Macro code for settime
0x06, // Integer expression for the payload length (5)
0xFE, 0x67, 0xF1, 0x80, 0x0F, // Integer expression for the timestamp (1743880207)
0x03, // Payload end
0x02, // Payload start
0x20, // Macro code for num
0x02, // Integer expression for the payload length (1)
0xDB, // Placeholder expression for t_hour
0x03, // Payload end
0x3A,
0x02, // Payload start
0x20, // Macro code for num
0x02, // Integer expression for the payload length (1)
0xDA, // Placeholder expression for t_min
0x03 // Payload end
var example = new SeStringBuilder()
.Append("The current time is: ")
.BeginMacro(MacroCode.SetTime)
.AppendIntExpression(1743880207)
.EndMacro()
.BeginMacro(MacroCode.Num)
.AppendNullaryExpression(ExpressionType.Hour)
.EndMacro()
.Append(":")
.BeginMacro(MacroCode.Num)
.AppendNullaryExpression(ExpressionType.Minute)
.EndMacro()
.ToReadOnlySeString();
Binary Expressions
If the type byte is >= 0xE0 and <= 0xE5, the following comparison and equality operators are used:
Type Byte | Macro String | Description |
---|---|---|
0xE0 | [val1>=val2] | Tests if the evaluated result from first sub-expression is greater than or equal to the evaluated result from second sub-expression. |
0xE1 | [val1>val2] | Tests if the evaluated result from first sub-expression is greater than the evaluated result from second sub-expression. |
0xE2 | [val1<=val2] | Tests if the evaluated result from first sub-expression is less than or equal to the evaluated result from second sub-expression. |
0xE3 | [val1<val2] | Tests if the evaluated result from first sub-expression is less than the evaluated result from second sub-expression. |
0xE4 | [val1==val2] | Tests if the evaluated result from first sub-expression is equal to the evaluated result from second sub-expression. |
0xE5 | [val1!=val2] | Tests if the evaluated result from first sub-expression is not equal to the evaluated result from second sub-expression. |
The type byte is followed by 2 expressions for each of the operands.
Example 1 == 1 = <if([1==1],true,false)>
:
- Binary Form
- Code
0x31, 0x20, 0x3D, 0x3D, 0x20, 0x31, 0x20, 0x3D, 0x20, // Text "1 == 1 = "
0x02, // Payload start
0x08, // Macro code for if
0x11, // Integer expression for the payload length (16)
0xE4, // Binary expression for equality check
0x02, // Integer expression for the first value (1)
0x02, // Integer expression for the second value (1)
0xFF, 0x05, 0x74, 0x72, 0x75, 0x65, // String expression used when condition is true (Text "true")
0xFF, 0x06, 0x66, 0x61, 0x6C, 0x73, 0x65, // String expression used when condition is false (Text "false")
0x03 // Payload end
var example = new SeStringBuilder()
.Append("1 == 1 = ")
.BeginMacro(MacroCode.If)
.BeginBinaryExpression(ExpressionType.Equal)
.AppendIntExpression(1)
.AppendIntExpression(1)
.EndExpression()
.AppendStringExpression("true")
.AppendStringExpression("false")
.EndMacro()
.ToReadOnlySeString();
Parameter Expressions
If the type byte is >= 0xE8 and <= 0xEB, the following parameters are used:
Type Byte | Macro String | Description |
---|---|---|
0xE8 | lnum# | Uses a numeric value at the specified index in the local parameter storage. |
0xE9 | gnum# | Uses a numeric value at the specified index in the global parameter storage. |
0xEA | lstr# | Uses a SeString value at the specified index in the local parameter storage. |
0xEB | gstr# | Uses a SeString value at the specified index in the global parameter storage. |
Following the type byte is a byte indicating the index of the parameter. The first index is 1.
Local Parameters
Local parameters have to be passed to the evaluator function. See Evaluating SeStrings below.
Global Parameters
Global parameters are stored and automatically resolved in the games MacroDecoder (note that the index for the StdDeque GlobalParameters in the MacroDecoder starts at 0).
Expand for a list of the currently known global parameters
Index | Type | Label |
---|---|---|
1 | String | Local Player's Name |
2 | String | Temp Player 1 Name |
3 | String | Temp Player 2 Name |
4 | Integer | Local Player's Sex |
5 | Integer | Temp Player 1 Sex |
6 | Integer | Temp Player 2 Sex |
7 | Integer | Temp Player 1 Unk 1 |
8 | Integer | Temp Player 2 Unk 1 |
11 | Integer | Eorzea Time Hours |
12 | Integer | Eorzea Time Minutes |
13 | Integer | ConfigOption ColorSay |
14 | Integer | ConfigOption ColorShout |
15 | Integer | ConfigOption ColorTell |
16 | Integer | ConfigOption ColorParty |
17 | Integer | ConfigOption ColorAlliance |
18 | Integer | ConfigOption ColorLS1 |
19 | Integer | ConfigOption ColorLS2 |
20 | Integer | ConfigOption ColorLS3 |
21 | Integer | ConfigOption ColorLS4 |
22 | Integer | ConfigOption ColorLS5 |
23 | Integer | ConfigOption ColorLS6 |
24 | Integer | ConfigOption ColorLS7 |
25 | Integer | ConfigOption ColorLS8 |
26 | Integer | ConfigOption ColorFCompany |
27 | Integer | ConfigOption ColorPvPGroup |
28 | Integer | ConfigOption ColorPvPGroupAnnounce |
29 | Integer | ConfigOption ColorBeginner |
30 | Integer | ConfigOption ColorEmoteUser |
31 | Integer | ConfigOption ColorEmote |
32 | Integer | ConfigOption ColorYell |
33 | Integer | ConfigOption ColorFCAnnounce |
34 | Integer | ConfigOption ColorBeginnerAnnounce |
35 | Integer | ConfigOption ColorCWLS |
36 | Integer | ConfigOption ColorAttackSuccess |
37 | Integer | ConfigOption ColorAttackFailure |
38 | Integer | ConfigOption ColorAction |
39 | Integer | ConfigOption ColorItem |
40 | Integer | ConfigOption ColorCureGive |
41 | Integer | ConfigOption ColorBuffGive |
42 | Integer | ConfigOption ColorDebuffGive |
43 | Integer | ConfigOption ColorEcho |
44 | Integer | ConfigOption ColorSysMsg |
52 | Integer | Player Grand Company Rank (Maelstrom) |
53 | Integer | Player Grand Company Rank (Twin Adders) |
54 | Integer | Player Grand Company Rank (Immortal Flames) |
55 | String | Local Player's Companion Name |
56 | String | Content Name |
57 | Integer | ConfigOption ColorSysBattle |
58 | Integer | ConfigOption ColorSysGathering |
59 | Integer | ConfigOption ColorSysErr |
60 | Integer | ConfigOption ColorNpcSay |
61 | Integer | ConfigOption ColorItemNotice |
62 | Integer | ConfigOption ColorGrowup |
63 | Integer | ConfigOption ColorLoot |
64 | Integer | ConfigOption ColorCraft |
65 | Integer | ConfigOption ColorGathering |
66 | Integer | Temp Player 1 Unk 2 |
67 | Integer | Temp Player 2 Unk 2 |
68 | Integer | Local Player's ClassJobId |
69 | Integer | Local Player's Level |
71 | Integer | Local Player's Race |
72 | Integer | Local Player's Synced Level |
78 | Integer | Client/Plattform? |
79 | Integer | Local Player's BirthMonth |
83 | Integer | Datacenter Region |
84 | Integer | ConfigOption ColorCWLS2 |
85 | Integer | ConfigOption ColorCWLS3 |
86 | Integer | ConfigOption ColorCWLS4 |
87 | Integer | ConfigOption ColorCWLS5 |
88 | Integer | ConfigOption ColorCWLS6 |
89 | Integer | ConfigOption ColorCWLS7 |
90 | Integer | ConfigOption ColorCWLS8 |
92 | Integer | Local Player's Grand Company |
93 | Integer | TerritoryType Id |
94 | Integer | Is Soft Keyboard Enabled |
95 | Integer | ConfigOption LogColorRoleTank for LogSetRoleColor 1 |
96 | Integer | ConfigOption LogColorRoleTank for LogSetRoleColor 2 |
97 | Integer | ConfigOption LogColorRoleHealer for LogSetRoleColor 1 |
98 | Integer | ConfigOption LogColorRoleHealer for LogSetRoleColor 2 |
99 | Integer | ConfigOption LogColorRoleDPS for LogSetRoleColor 1 |
100 | Integer | ConfigOption LogColorRoleDPS for LogSetRoleColor 2 |
101 | Integer | ConfigOption LogColorOtherClass for LogSetRoleColor 1 |
102 | Integer | ConfigOption LogColorOtherClass for LogSetRoleColor 2 |
103 | Integer | Has Login Security Token |
String Expressions
If the type byte is 0xFF, this is a String expression. It's followed by an integer expression for its length and the nested SeString.
Example Hello <split(<string(gstr1)>, ,1)>!
:
- Binary Form
- Code
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Text "Hello "
0x02, // Payload start
0x2C, // Macro code for split
0x0D, // Integer expression for the payload length (12)
0xFF, // String expression for the input
0x07, // Integer expression for the length of the following string (6)
0x02, // Payload start
0x29, // Macro code for string
0x03, // Integer expression for the payload length (2)
0xEB, 0x01, // Parameter Expression gstr1 (local player name)
0x03, // Payload end
0xFF, // String expression for the separator
0x02, // Integer expression for the length of the following string (1)
0x20, // Text " "
0x02, // Integer expression for the index (1) of the splitted text (in this case the forename of a local player)
0x03, // Payload end
0x21 // Text "!"
var example = new SeStringBuilder()
.Append("Hello ")
.BeginMacro(MacroCode.Split)
.AppendStringExpression(new SeStringBuilder() // input
.BeginMacro(MacroCode.String)
.AppendGlobalStringExpression(1) // gstr1
.EndMacro()
.ToReadOnlySeString().AsSpan())
.AppendStringExpression(" ") // separator
.AppendIntExpression(1) // index of the splitted text
.EndMacro()
.Append("!")
.ToReadOnlySeString();
See SeExpressionUtilities.TryDecodeString for more details.
Now that we have expressions covered, let's see what we can do with them!
Macros
Macro payloads work kind of like functions: each one performs a specific action, and the expressions attached to them are like the parameters you pass in.
[0x06
] SetResetTime
Sets the reset time to the contextual time storage.
Expressions:
- [0] Integer Expression: Hour
- [1] Integer Expression: WeekDay
Example
<setresettime(7,2)>
→ Sets the contextual time to next Tuesday, 7 am UTC.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.SetResetTime)
.AppendIntExpression(7)
.AppendIntExpression(2)
.EndMacro()
.BeginMacro(MacroCode.Sec)
.AppendNullaryExpression(ExpressionType.Day)
.EndMacro()
.Append(".")
.BeginMacro(MacroCode.Sec)
.AppendNullaryExpression(ExpressionType.Month)
.EndMacro()
.Append(". at ")
.BeginMacro(MacroCode.Sec)
.AppendNullaryExpression(ExpressionType.Hour)
.EndMacro()
.Append(":")
.BeginMacro(MacroCode.Sec)
.AppendNullaryExpression(ExpressionType.Minute)
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example); // similar to "08.04. at 09:00"
[0x07
] SetTime
Sets the specified time to the contextual time storage.
Expressions:
- [0] Integer Expression: Unix Timestamp
Example
<settime(1743880207)>
→ Sets the contextual time to Sat Apr 05 2025 19:10:07 UTC.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.SetTime)
.AppendIntExpression(1743880207)
.EndMacro()
.BeginMacro(MacroCode.Num)
.AppendNullaryExpression(ExpressionType.Hour)
.EndMacro()
.Append(":")
.BeginMacro(MacroCode.Num)
.AppendNullaryExpression(ExpressionType.Minute)
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example); // "21:10"
[0x08
] If
Tests an expression and uses a corresponding subexpression.
Expressions:
- [0] Expression used as condition
- [1] Expression to use if condition is true
- [2] Expression to use if condition is false
Example
Good <if([gnum11<12],<if([gnum11<4],evening,morning)>,<if([gnum11<17],day to you,evening)>)>, friend.
→ Between 4 am and 12 pm Eorzea Time, this will print Good morning, friend.
→ Between 12 pm and 5 pm Eorzea Time, this will print
Good day to you, friend.
→ Between 5 pm and 4 am Eorzea Time, this will print Good evening, friend.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.Append("Good ")
.BeginMacro(MacroCode.If)
.BeginBinaryExpression(ExpressionType.LessThan)
.AppendGlobalNumberExpression(11)
.AppendIntExpression(12)
.EndExpression()
.BeginStringExpression()
.BeginMacro(MacroCode.If)
.BeginBinaryExpression(ExpressionType.LessThan)
.AppendGlobalNumberExpression(11)
.AppendIntExpression(4)
.EndExpression()
.AppendStringExpression("evening")
.AppendStringExpression("morning")
.EndMacro()
.EndExpression()
.BeginStringExpression()
.BeginMacro(MacroCode.If)
.BeginBinaryExpression(ExpressionType.LessThan)
.AppendGlobalNumberExpression(11)
.AppendIntExpression(17)
.EndExpression()
.AppendStringExpression("day to you")
.AppendStringExpression("evening")
.EndMacro()
.EndExpression()
.EndMacro()
.Append(", friend.")
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x09
] Switch
Tests an expression and uses a corresponding subexpression.
Expressions:
- [0] Expression used as condition
- [1] Expression to use if condition is 1
- ...
- [n] Expression to use if condition is n
Example
<switch(lnum1,S,M,L,XL)>
→ Prints S
when lnum1
is 1.
→ Prints M
when lnum1
is 2.
→ Prints L
when lnum1
is 3.
→ Prints XL
when lnum1
is 4.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.Switch)
.AppendLocalNumberExpression(1)
.AppendStringExpression("S")
.AppendStringExpression("M")
.AppendStringExpression("L")
.AppendStringExpression("XL")
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example, [2]); // "M"
[0x0A
] PcName
Adds a characters name.
Expressions:
- [0] Integer Expression: EntityId
Example
<pcname(0x10778681)>
→ Prints the name of the player character with EntityId 0x10778681.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.PcName)
.AppendIntExpression(0x10778681)
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x0B
] IfPcGender
Tests a characters gender.
Expressions:
- [0] Integer Expression: EntityId
- [1] Expression to use if the character is male
- [2] Expression to use if the character is female
Example
Good day, <ifpcgender(0x10778681,Sir,Ma'am)>.
→ Prints Good day, Sir.
if the player character with EntityId 0x10778681 is
male.
→ Prints Good day, Ma'am.
if the player character with EntityId 0x10778681 is
female.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.Append("Good day, ")
.BeginMacro(MacroCode.IfPcGender)
.AppendIntExpression(0x10778681)
.AppendStringExpression("Sir")
.AppendStringExpression("Ma'am")
.EndMacro()
.Append(".")
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x0C
] IfPcName
Tests a characters name.
Expressions:
- [0] Integer Expression: EntityId
- [1] String Expression: The name to test against
- [2] Expression to use if the name matches
- [3] Expression to use if the name doesn't match
Example
Hello, <ifpcname(0x10778681,Local Player,Warrior of Light,adventurer)>!
→ Prints Hello, Warrior of Light!
if the player character with EntityId
0x10778681 is called Local Player.
→ Prints Hello, adventurer!
if the player character with EntityId 0x10778681
is not called Local Player.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.Append("Hello, ")
.BeginMacro(MacroCode.IfPcName)
.AppendIntExpression(0x10778681)
.AppendStringExpression("Local Player")
.AppendStringExpression("Warrior of Light")
.AppendStringExpression("adventurer")
.EndMacro()
.Append("!")
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x0D
] Josa
Determines the type of josa required from the last character of the first expression.
Expressions:
- [0] test string
- [1] eun/i/eul suffix
- [2] neun/ga/reul suffix
[0x0E
] Josaro
Determines the type of josa, ro in particular, required from the last character of the first expression.
Expressions:
- [0] test string
- [1] ro suffix
- [2] euro suffix
[0x0F
] IfSelf
Tests if the character is the local player.
Expressions:
- [0] Integer Expression: EntityId
- [1] Expression to use if the character is the local player
- [2] Expression to use if the character is not the local player
Example
<ifself(0x10778681,You are,<pcname(0x10778681)> is)> KO'd.
→ Prints You are KO'd.
if the player character with EntityId 0x10778681 is the
Local Player.
→ Prints Other Player is KO'd.
if the player character with EntityId
0x10778681 is not the Local Player, called Other Player.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.IfSelf)
.AppendIntExpression(0x10778681)
.AppendStringExpression("You are")
.BeginStringExpression()
.BeginMacro(MacroCode.PcName)
.AppendIntExpression(0x10778681)
.EndMacro()
.Append(" is")
.EndExpression()
.EndMacro()
.Append(" KO'd.")
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x10
] NewLine
Adds a line break.
No expressions.
Example
Hello<br>World
→ Prints
Hello
World
This macro is used for formatting. It is not evaluated by ISeStringEvaluator.
var example = new SeStringBuilder()
.Append("Hello")
.AppendNewLine()
.Append("World")
.ToReadOnlySeString();
or
var example = new SeStringBuilder()
.AppendLine("Hello")
.Append("World")
.ToReadOnlySeString();
[0x11
] Wait
Waits for a specified duration.
Expressions:
- [0] Integer Expression: Delay in seconds
[0x12
] Icon
Adds an icon from common/font/gfdata.gfd
.
See BitmapFontIcon
enum for details.
Expressions:
- [0] Integer Expression: Icon ID
This does not support icons from ui/icon/
.
[0x13
] Color
Pushes the text foreground color.
Expressions:
- [0] An expression resolving to a B8G8R8A8 color, or Placeholder Expression
stackcolor
- If a color was passed, it pushes it onto the stack, similar to
ImGui.PushStyleColor(ImGuiCol.Text, color)
. - If 0 is passed, it re-uses the current color on the stack, similar to
ImGui.PushStyleColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.Text))
. - If
stackcolor
was passed, the color is popped from the stack, similar toImGui.PopStyleColor()
.
- If a color was passed, it pushes it onto the stack, similar to
The expression can be evaluated using ISeStringEvaluator (code).
Always make sure that when you push a color, it is popped from the stack with
stackcolor
!
The ImGui renderer might be forgiving, but the game is not.
[0x14
] EdgeColor
Pushes the text border color.
See Color macro for expression description.
The expression can be evaluated using ISeStringEvaluator (code).
[0x15
] ShadowColor
Pushes the text shadow color.
See Color macro for expression description.
The expression can be evaluated using ISeStringEvaluator (code).
[0x16
] SoftHyphen
Adds a soft hyphen.
No expressions.
Macro string: <->
→ Invisible soft hyphen \u00AD
.
Localized text pulled from the game's Excel sheets, specifically French and
German texts, will almost certainly contain soft hyphens (represented in macro
strings as <->). Soft hyphens indicate potential word break points at
syllables. They are normally invisible and only appear at the end of a line when
a word needs to be broken. In this case, they render as a visible - to mark the
split. However ImGui (for example, ImGui.TextUnformatted()
) doesn't handle
this correctly and will always display soft hyphens, even if no line break
occurs.
You can remove soft hyphens from a string using the
StripSoftHyphen()
extension provided by Dalamud.
[0x17
] Key
Unknown purpose.
[0x18
] Scale
Unknown purpose.
[0x19
] Bold
Sets whether to use bold text effect.
- [0] An expression resolving to a boolean for the enable state. 0 = off, 1 = on
The expression can be evaluated using ISeStringEvaluator (code).
The chat log does not support bold text.
[0x1A
] Italic
Sets whether to use italic text effect.
- [0] An expression resolving to a boolean for the enable state. 0 = off, 1 = on
The expression can be evaluated using ISeStringEvaluator (code).
[0x1B
] Edge
Unknown purpose.
[0x1C
] Shadow
Unknown purpose.
[0x1D
] NonBreakingSpace
Adds a non-breaking space.
No expressions.
Macro string: <nbsp>
→ Prints
(\u00A0
).
[0x1E
] Icon2
Behaves like the icon
macro, but displays the controller buttons based on the
player's Button Configuration.
[0x1F
] Hyphen
Adds a hyphen.
No expressions.
Macro string: <-->
→ Prints -
.
[0x20
] Num
Adds a decimal representation of an integer expression.
Expressions:
- [0] Integer Expression: Value
Example
It's currently hour <num(gnum11)>.
→ Prints It's currently hour 5.
if gnum11
(hour of current Eorzea Time)
is 5.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.Append("It's currently hour ")
.BeginMacro(MacroCode.Num)
.AppendGlobalNumberExpression(11)
.EndMacro()
.Append(".")
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x21
] Hex
Adds a hexadecimal representation of an integer expression.
Expressions:
- [0] Integer Expression: Value
Example
15 in hexadecimal is <hex(15)>.
→ Prints 15 in hexadecimal is 0x0000000F.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.Append("15 in hexadecimal is ")
.BeginMacro(MacroCode.Hex)
.AppendIntExpression(15)
.EndMacro()
.Append(".")
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x22
] Kilo
Adds a decimal representation of an integer expression, separating by thousands.
Expressions:
- [0] Integer Expression: Value
- [1] String Expression: Separator (usually a comma or a dot)
Example
The damage was over <kilo(9000,.)>.
→ Prints The damage was over 9.000.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.Append("The damage was over ")
.BeginMacro(MacroCode.Kilo)
.AppendIntExpression(9000)
.AppendStringExpression(".")
.EndMacro()
.Append(".")
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x23
] Byte
Adds a human-readable byte string (possible suffixes: omitted, K, M, G, T).
Expressions:
- [0] Integer Expression: Value
Example
The file size is <byte(150000)>.
→ Prints The file size is 146.5K.
ISeStringEvaluator does currently not support evaluation for this macro.
var example = new SeStringBuilder()
.Append("The file size is")
.BeginMacro(MacroCode.Byte)
.AppendIntExpression(150000)
.EndMacro()
.Append(".")
.ToReadOnlySeString();
[0x24
] Sec
Adds a zero-padded-to-two-digits decimal representation of an integer expression.
Expressions:
- [0] Integer Expression: Value
Example
The current time is <sec(gnum11)>:<sec(gnum12)>.
→ Prints The current time is 04:32.
when the Eorzea Time is 04:32.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.Append("The current time is ")
.BeginMacro(MacroCode.Sec)
.AppendGlobalNumberExpression(11)
.EndMacro()
.Append(":")
.BeginMacro(MacroCode.Sec)
.AppendGlobalNumberExpression(12)
.EndMacro()
.Append(".")
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x25
] Time
Unknown purpose.
[0x26
] Float
Adds a floating point number as text.
Expressions:
- [0] Integer Expression: Value
- [1] Integer Expression: Radix
- [2] String Expression: Separator
Example
The limit break is filled up to <float(1337,100,.)> %.
→ Prints The limit break is filled up to 13.37 %.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.Append("The limit break is filled up to ")
.BeginMacro(MacroCode.Float)
.AppendIntExpression(1337)
.AppendIntExpression(100)
.AppendStringExpression(".")
.EndMacro()
.Append(" %.")
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x27
] Link
Begins or ends a region of link.
Expressions:
- [0] Integer Expression: LinkMacroPayloadType
- [1] Integer Expression: Usage depends on LinkMacroPayloadType, most of the time it's an ID
- [2] Integer Expression: Usage depends on LinkMacroPayloadType
- [3] Integer Expression: Usage depends on LinkMacroPayloadType
- [4] String Expression: Plain string of the linked thing, used when the text is copied to the clipboard
Example
<link(2,4801,0,0,Dalamud Nut)>Dalamud Nut<link(0xCE,0,0,0)>
→ Prints a clickable Dalamud Nut
item link.
var example = new SeStringBuilder()
.PushLink(LinkMacroPayloadType.Item, 4801, 0, 0, "Dalamud Nut")
.Append("Dalamud Nut")
.PopLink()
.ToReadOnlySeString();
Always make sure that a link payload has a corresponding link terminator payload.
[0x28
] Sheet
Adds a column from a sheet.
Expressions:
- [0] String Expression: Sheet name
- [1] Integer Expression: RowId
- [2] Integer Expression: Column Index
- [3] Expression used as local parameter to evaluate the the columns text
Example
<sheet(Addon,762,0,3)>
, with Addon#762 being <sheet(Town,lnum1,0)>
→ Prints Ul'dah
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.Sheet)
.AppendStringExpression("Addon")
.AppendIntExpression(762)
.AppendIntExpression(0)
.AppendIntExpression(3)
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x29
] String
Adds a string expression as-is. Used for evaluation purposes (like filling in placeholders).
Expressions:
- [0] String Expression: Text
Example
This is <string(lstr1)>!
→ Prints This is amazing!
when lstr1
is amazing
.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.Append("This is ")
.BeginMacro(MacroCode.String)
.AppendLocalStringExpression(1)
.EndMacro()
.Append("!")
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example, ["amazing"]);
[0x2A
] Caps
Adds a string, fully upper cased.
Expressions:
- [0] String Expression: Text
Example
This is <caps(lstr1)>!
→ Prints This is AMAZING!
when lstr1
is amazing
.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.Append("This is ")
.BeginMacro(MacroCode.Caps)
.AppendLocalStringExpression(1)
.EndMacro()
.Append("!")
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example, ["amazing"]);
[0x2B
] Head
Adds a string, first character upper cased.
Expressions:
- [0] String Expression: Text
Example
<head(<sheet(ContentFinderCondition,22,43)>)>
where ContentFinderCondition
RowId 22, Column 43 is the Lost City of Amdapor
→ Prints The Lost City of Amdapor
.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.Head)
.BeginStringExpression()
.BeginMacro(MacroCode.Sheet)
.AppendStringExpression("ContentFinderCondition")
.AppendIntExpression(22)
.AppendIntExpression(43)
.EndMacro()
.EndExpression()
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example, default, ClientLanguage.English);
[0x2C
] Split
Splits the input string by a given separator and returns the element at the specified position.
Expressions:
- [0] String Expression: Text
- [1] String Expression: Separator
- [2] Integer Expression: Index, starting at 1
Example
<split(Forename Lastname, ,2)>
→ Prints Lastname
.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.Split)
.AppendStringExpression("Firstname Lastname")
.AppendStringExpression(" ")
.AppendIntExpression(2)
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x2D
] HeadAll
Adds a string, every words first character upper cased.
Expressions:
- [0] String Expression: Text
Example
<headall(the great gubal library)>
→ Prints The Great Gubal Library
.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.HeadAll)
.AppendStringExpression("the great gubal library")
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x2E
] Fixed
A multi-functional macro to generate auto translated links or texts.
See implementation in SeStringEvaluator for more details.
[0x2F
] Lower
Adds a string, fully lower cased.
Expressions:
- [0] String Expression: Text
Example
<lower(CHOCOBO)>
→ Prints chocobo
.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.Lower)
.AppendStringExpression("CHOCOBO")
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x30
] JaNoun
Adds a properly formatted name based on the games noun system for the Japanese language.
Expressions:
- [0] String Expression: Sheet Name
- [1] Integer Expression: JapaneseArticleType
- [2] Integer Expression: RowId
- [3] Integer Expression: Amount
[0x31
] EnNoun
Adds a properly formatted name based on the games noun system for the English language.
Expressions:
- [0] String Expression: Sheet Name
- [1] Integer Expression: EnglishArticleType
- [2] Integer Expression: RowId
- [3] Integer Expression: Amount
[0x32
] DeNoun
Adds a properly formatted name based on the games noun system for the German language.
Expressions:
- [0] String Expression: Sheet Name
- [1] Integer Expression: GermanArticleType
- [2] Integer Expression: RowId
- [3] Integer Expression: Amount
- [4] Integer Expression: Grammatical Case
- 0 = Nominative
- 1 = Genitive
- 2 = Dative
- 3 = Accusative
[0x33
] FrNoun
Adds a properly formatted name based on the games noun system for the French language.
Expressions:
- [0] String Expression: Sheet Name
- [1] Integer Expression: FrenchArticleType
- [2] Integer Expression: RowId
- [3] Integer Expression: Amount
[0x34
] ChNoun
Not supported in Dalamud.
[0x40
] LowerHead
Adds a string, first character lower cased.
Expressions:
- [0] String Expression: Text
Example
<lowerhead(CHOCOBO)>
→ Prints cHOCOBO
.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.LowerHead)
.AppendStringExpression("CHOCOBO")
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x48
] ColorType
Pushes the text foreground color, referring to a color defined in UIColor sheet.
Expressions:
- [0] Integer Expression: RowId of UIColor sheet
Expression can be evaluated using ISeStringEvaluator (code).
[0x49
] EdgeColorType
Pushes the text border color, referring to a color defined in UIColor sheet.
Expressions:
- [0] Integer Expression: RowId of UIColor sheet
Expression can be evaluated using ISeStringEvaluator (code).
[0x4A
] Ruby
Displays ruby text (furigana, interlinear annotation) above standard text.
Expressions:
- [0] String Expression: Standard text
- [1] String Expression: Ruby text
Presumably only supported in cutscene dialogs.
[0x50
] Digit
Adds a zero-padded number as text.
Expressions:
- [0] Integer Expression: Value
- [1] Integer Expression: Target length
Example
<digit(15,4)>
→ Prints 0015
.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.Digit)
.AppendIntExpression(15)
.AppendIntExpression(4)
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x51
] Ordinal
Adds an ordinal number as text (English only).
Expressions:
- [0] Integer Expression: Value
Example
<ordinal(1)> <ordinal(2)> <ordinal(3)> <ordinal(4)>
→ Prints 1st 2nd 3rd 4th
.
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.Ordinal)
.AppendIntExpression(1)
.EndMacro()
.Append(" ")
.BeginMacro(MacroCode.Ordinal)
.AppendIntExpression(2)
.EndMacro()
.Append(" ")
.BeginMacro(MacroCode.Ordinal)
.AppendIntExpression(3)
.EndMacro()
.Append(" ")
.BeginMacro(MacroCode.Ordinal)
.AppendIntExpression(4)
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
[0x60
] Sound
Adds an invisible sound payload.
Expressions:
- [0] Integer Expression: Bool whether this sound is part of the Jingle sheet
- [1] Integer Expression: Sound Id
[0x61
] LevelPos
Adds a formatted map name and corresponding coordinates.
Expressions:
- [0] Integer Expression: RowId in Level sheet
Example
<levelpos(3871103)>
→ Prints
South Shroud
( 27.7 , 21.1 )
Can be evaluated using ISeStringEvaluator (code):
var example = new SeStringBuilder()
.BeginMacro(MacroCode.LevelPos)
.AppendIntExpression(3871103)
.EndMacro()
.ToReadOnlySeString();
var result = SeStringEvaluator.Evaluate(example);
Evaluating SeStrings
In order to evaluate payloads use the ISeStringEvaluator service.
Let's reuse our placeholder expression example from before, but this time using a local parameter:
var template = ReadOnlySeString.FromMacroString("The current time is: <settime(lnum1)><num(t_hour)>:<num(t_min)>");
var example = SeStringEvaluator.Evaluate(template, [1743880207]);
The payloads of the template
variable are looped over, will be evaluated and
the resulting plain text is added to an internal SeStringBuilder, resulting in a
new ReadOnlySeString. Since all payloads in the example above are evaluated, the
example
variable will now only contain the text The current time is: 21:10
.
The following macro codes are always passed through:
- Hyphen
- Icon
- Icon2
- Link
- NewLine
- NonBreakingSpace
- SoftHyphen
- Sound
- Wait
The expressions for the following macro codes are evaluated, but the payloads are passed through because they are used to format text:
- Color
- EdgeColor
- ShadowColor
- Bold
- Italic
- ColorType
- EdgeColorType
The following macro codes are currently not supported by the evaluator and will be passed through:
- Byte
- ChNoun
- Edge
- Josa
- Josaro
- Key
- Ruby
- Scale
- Shadow
- Time
Rendering SeStrings in ImGui
If you're certain a ReadOnlySeString
doesn't contain macro payloads, you can
safely convert it to a plain C# string using ExtractText()
.
This function strips out all macro payloads except for NewLine,
NonBreakingSpace, Hyphen and SoftHyphen, which are replaced with their
corresponding UTF-8 characters. Make sure to read the note about
SoftHyphen.
Avoid using ToString()
as it generates a macro string, as seen in the examples
throughout this guide. This is a common pitfall, particularly with the Dungeon
the <italic(1)>Whorleater<italic(0)> (Hard)
, where the ship's name is intended
to be displayed in italics.
If you do want to preserve and render formatting payloads, Dalamud provides convenient SeString rendering helpers for ImGui. These handle soft hyphens and formatting properly, so you don’t have to deal with the quirks yourself:
ImGuiHelpers.SeStringWrapped()
forReadOnlySeString
orReadOnlySeStringSpan
,ImGuiHelpers.CompileSeStringWrapped()
for macro strings.