BFEVFL: Difference between revisions

From ZeldaMods (Breath of the Wild)
Jump to navigation Jump to search
imported>Leoetlino
No edit summary
Tags: Mobile edit Mobile web edit
 
(22 intermediate revisions by 3 users not shown)
Line 1: Line 1:
<onlyinclude>'''bfevfl''' is a binary file format for [[Event flow|'''ev'''ent '''fl'''ows]].</onlyinclude>
<onlyinclude>'''BFEVFL''' is a binary file format for [[Event flow|'''ev'''ent '''fl'''ows]].</onlyinclude> In ''Breath of the Wild'' and other Nintendo games such as ''Splatoon 2'' and ''Link's Awakening'', binary event flows are parsed by Nintendo's EventFlow/evfl library.


== Structures ==
== Structures ==
bfevfl is a little endian format, so values are stored in little endian even on Wii U.
BFEVFL is a format that supports both big endian and little endian; however, event flows typically use little endian even on Wii U, as is the case in ''Breath of the Wild''.


bfevfl uses pointers very heavily to refer to strings, to other sections, etc. All pointers are always 64 bit long -- since the same format is used on the Switch which is a 64 bit platform. They must be present in the [[#Relocation table]] if the file is to be loaded correctly by the official bfevfl code.
BFEVFL uses pointers very heavily to refer to strings, to other sections, etc. All pointers are always 64-bit long -- this is because the same format is used on 64-bit platforms like the Switch. Pointers must be present in the [[#Relocation table]] if the file is to be loaded correctly by the official EventFlow code.


As a direct consequence, most sections don't have any fixed order. Therefore, this article will only document the data structures and mention the order Nintendo places sections in. For more details, it is recommended to look at the [https://github.com/leoetlino/evfl <code>evfl</code> library] directly.
As a direct consequence, most sections don't have any fixed order. Therefore, this article will only document the data structures and mention the order Nintendo places sections in. For more details, it is recommended to look at the [https://github.com/leoetlino/evfl <code>evfl</code> library] directly.
Line 16: Line 16:
| 0x0 || char[8] || Magic ("BFEVFL\x00\x00")
| 0x0 || char[8] || Magic ("BFEVFL\x00\x00")
|-
|-
| 0x8 || u16 || Version (0x0300)
| 0x8 || u8 || Version (major)
|-
|-
| 0xa || u8 || Unknown (must be zero)
| 0x9 || u8 || Version (minor)
|-
|-
| 0xb || u8 || Unknown
| 0xa || u8 || Version (patch)
|-
|-
| 0xc || u16 || Byte order mark
| 0xb || u8 || Version (sub-patch)
|-
| 0xc || s16 || Byte order mark
|-
|-
| 0xe || u8 || Alignment (to get the actual value: 1 << raw_value)
| 0xe || u8 || Alignment (to get the actual value: 1 << raw_value)
|-
|-
| 0xf || u8 || Unknown
| 0xf || u8 || Padding
|-
|-
| 0x10 || u32 || File name offset
| 0x10 || int || File name offset
|-
|-
| 0x14 || u16 || Is relocated flag (set in memory)
| 0x14 || u16 || Is relocated flag (set in memory)
Line 34: Line 36:
| 0x16 || u16 || First block offset
| 0x16 || u16 || First block offset
|-
|-
| 0x18 || u32 || Relocation table offset
| 0x18 || int || Relocation table offset
|-
|-
| 0x1c || u32 || File size
| 0x1c || int || File size
|-
|-
| 0x20 || u16 || Number of [[#Flowchart|flowchart]]s
| 0x20 || u16 || Number of [[#Flowchart|flowchart]]s
Line 44: Line 46:
| 0x24 || u32 || Padding
| 0x24 || u32 || Padding
|-
|-
| 0x28 || Flowchart* || Flowchart (nullptr if no flowchart)
| 0x28 || Flowchart** || Flowchart (nullptr if no flowchart)
|-
|-
| 0x30 || [[#Dictionary|Dictionary]]* || Flowchart name dictionary
| 0x30 || [[#Dictionary|Dictionary]]* || Flowchart name dictionary
|-
|-
| 0x38 || Timeline* || Timeline (nullptr if no timeline)
| 0x38 || Timeline** || Timeline (nullptr if no timeline)
|-
|-
| 0x40 || Dictionary* || Timeline name dictionary
| 0x40 || Dictionary* || Timeline name dictionary
|}
|}
Note: 0x0-0x20 are just a standard ore::BinaryFileHeader (which is identical to nn::util::BinaryFileHeader).


=== Relocation table ===
=== Relocation table ===
(The official name for this structure in the EventFlow library is ore::RelocationTable.)
{|class="wikitable"
{|class="wikitable"
! Offset !! Type !! Description
! Offset !! Type !! Description
Line 59: Line 65:
| 0x0 || char[4] || Magic ("RELT")
| 0x0 || char[4] || Magic ("RELT")
|-
|-
| 0x4 || u32 || Offset to relocation table start
| 0x4 || int || Offset to relocation table start (<code>table_start_offset</code>)
|-
|-
| 0x8 || u32 || Number of sections
| 0x8 || int || Number of sections
|-
|-
| 0xc || u32 || Padding
| 0xc || u32 || Padding
|-
|-
| 0x10 || Section[num_sections] || Sections
| 0x10 || Section[num_sections] || Sections
|-
| - || Entry[...] || Section entries (emitted in the same order as each section)
|}
|}


Note: the number of sections is almost always 1 because a single section can already fit 2^32 - 1 entries. If you need more than 4 billion entries, you are probably doing something ''very wrong''.
Note: the number of sections is almost always 1 because a single section can already fit 2^32 - 1 entries. If you need more than 4 billion entries, you are probably doing something ''very wrong''.
The table base pointer is calculated as follows: <code>reinterpret_cast<char*>(&table) - table_start_offset</code>


==== Relocation table section ====
==== Relocation table section ====
Line 74: Line 84:
! Offset !! Type !! Description
! Offset !! Type !! Description
|-
|-
| 0x0 || void* || Alternative base offset (unused by Nintendo)
| 0x0 || void* || Optional base pointer (<code>ptr</code>)
|-
|-
| 0x8 || u32 || Used to calculate the base pointer if an alternative base offset is used
| 0x8 || int || Optional base pointer offset (<code>offset</code>)
|-
|-
| 0xc || u32 || Data end offset (before alignment)
| 0xc || int || Size
|-
|-
| 0x10 || u32 || Number of entries to skip
| 0x10 || int || Index of the first entry in this section
|-
|-
| 0x14 || u32 || Number of entries (includes skipped entries)
| 0x14 || int || Number of entries
|-
| 0x18 || Entry[num_entries] || Entries
|}
|}
The base pointer (<code>base</code>) is calculated as follows:
* If the optional base pointer field is nullptr, the base pointer is equal to the table base pointer.
* Otherwise, it is equal to <code>ptr - offset</code>.


==== Relocation table entry ====
==== Relocation table entry ====
Line 91: Line 103:
! Offset !! Type !! Description
! Offset !! Type !! Description
|-
|-
| 0x0 || u32 || Offset to pointers to relocate
| 0x0 || u32 || Offset to pointers to relocate, relative to the table base pointer
|-
|-
| 0x4 || u32 || Bit field that determines which pointers need to be relocated (up to 32 contiguous pointers starting from the listed offset)
| 0x4 || u32 || Bit field that determines which pointers need to be relocated (up to 32 contiguous pointers starting from the listed offset)
|}
|}
Offsets are treated as signed 32-bit integers.
Pointers are relocated by overwriting each 64-bit pointer field with <code>base + offset</code>.


=== String pool ===
=== String pool ===
(The official name for this structure in the EventFlow library is ore::StringPool.)
The string pool starts with the <code>STR </code> magic and contains 2-byte aligned strings. The magic is not checked at all since strings are directly referred to using pointers.
The string pool starts with the <code>STR </code> magic and contains 2-byte aligned strings. The magic is not checked at all since strings are directly referred to using pointers.


All strings in the pool are Pascal string, i.e. length-prefixed strings. The length is an unsigned 16 bit integer. Strings are stored in reverse order of their binary representation.
All strings in the pool are Pascal strings, i.e. length-prefixed strings. The length is an unsigned 16 bit integer. Strings are stored in reverse order of their binary representation.


=== Dictionary ===
=== Dictionary ===
(The official name for this structure in the EventFlow library is ore::ResDic.)
A dictionary is a data structure that is used to quickly look up the index of an element based on its name. Thus, they are always used in conjunction with an array of elements. However, the way the dictionary and the array are associated depends on the structure.
A dictionary is a data structure that is used to quickly look up the index of an element based on its name. Thus, they are always used in conjunction with an array of elements. However, the way the dictionary and the array are associated depends on the structure.


Line 122: Line 142:
! Offset !! Type !! Description
! Offset !! Type !! Description
|-
|-
| 0x0 || u32 || Compact representation of bit index
| 0x0 || u32 || Bit index
|-
|-
| 0x4 || u16 || Next index if bit is 0
| 0x4 || u16 || Index of next node if bit is 0
|-
|-
| 0x6 || u16 || Next index if bit is 1
| 0x6 || u16 || Index of next node if bit is 1
|-
|-
| 0x8 || PascalString* || Name
| 0x8 || PascalString* || Name
|}
|}
The compact representation of the bit index has two parts:
* Bits 3-7: index of the byte that should be checked
* Bits 0-2: index of the bit in that byte
A bit index can be translated to its compact representation using:
<syntaxhighlight lang="python">
def get_compact_representation(bit_idx: int) -> int:
    byte_idx = bit_idx // 8
    return (byte_idx << 3) | (bit_idx - 8*byte_idx)
</syntaxhighlight>


Example: <code>Hello</code> corresponds to 100100001100101011011000110110001101111. Bits 0-3 are 1, bit 4 is 0, etc.
Example: <code>Hello</code> corresponds to 100100001100101011011000110110001101111. Bits 0-3 are 1, bit 4 is 0, etc.


=== Container ===
=== Container ===
(The official name for this structure in the EventFlow library is ore::ResMetaData.)
Containers are key-value mappings with keys being strings. A [[#Dictionary]] is used to store keys.
Containers are key-value mappings with keys being strings. A [[#Dictionary]] is used to store keys.
==== ContainerDataType enum ====
The following enum definition is complete and all names are official<ref>0x7101DA3EC8 in Switch 1.5.0</ref>.


{|class="wikitable"
{|class="wikitable"
! Value !! Name
! Offset !! Type !! Description
|-
|-
| 0 || Argument
| 0x0 || ContainerItem || Root container structure (data type is <code>Container</code>)
|-
| 1 || Container
|-
| 2 || Int
|-
| 3 || Bool
|-
| 4 || Float
|-
| 5 || String
|-
| 6 || Wide string ("wstring")
|-
| 7 || Int array
|-
| 8 || Bool array
|-
| 9 || Float array
|-
| 10 || String array
|-
| 11 || Wstring array
|-
| 12 || Actor identifier
|}
|}


Line 192: Line 176:
| 0x4 || u32 || Padding
| 0x4 || u32 || Padding
|-
|-
| 0x8 || Dictionary* || Dictionary (for container types)
| 0x8 || Dictionary* || Dictionary (only for the Container data type)
|-
|-
| 0x10 || any || Data
| 0x10 || ContainerItemData || Data
|}
|}


==== Container structure ====
==== ContainerItemData ====
The root container structure is a <code>ContainerItem</code> with the data type set to <code>Container</code>, a non-null dictionary (for item names), followed by pointers to children <code>ContainerItem</code>s.
 
==== Child item structure ====
For all other data types, the dictionary pointer is null.
 
{|class="wikitable"
{|class="wikitable"
! Data type !! Data !! Number of items
! Data type !! Data !! Number of items
Line 208: Line 187:
| Argument || String (not in string pool, follows item structure immediately) || 1
| Argument || String (not in string pool, follows item structure immediately) || 1
|-
|-
| Container || ? || ?
| Container || ContainerItem*[n] || n
|-
|-
| Int || Signed 32-bit integer || 1
| Int || Signed 32-bit integer || 1
Line 218: Line 197:
| String || String (not in string pool, follows item structure immediately) || 1
| String || String (not in string pool, follows item structure immediately) || 1
|-
|-
| Wide string ("wstring") || (not implemented) || ?
| Wide string ("wstring") || Wide string (not in string pool, follows item structure immediately) || 1
|-
|-
| Int array || Int[n] || n
| Int array || Int[n] || n
Line 228: Line 207:
| String array || String[n] (aligned to 8-byte boundaries this time) || n
| String array || String[n] (aligned to 8-byte boundaries this time) || n
|-
|-
| Wstring array || (not implemented) || ?
| Wstring array || WString[n] (aligned to 8-byte boundaries this time) || n
|-
|-
| Actor identifier || Two strings: actor name + secondary name || 2
| Actor identifier || Two strings: actor name + secondary name || 2
|}
Wide strings use wchar_t which is 32-bit long on Switch.
==== ContainerDataType enum ====
The following enum definition is complete and all names are official<ref>0x7101DA3EC8 in Switch 1.5.0</ref>.
{|class="wikitable"
! Value !! Name
|-
| 0 || Argument
|-
| 1 || Container
|-
| 2 || Int
|-
| 3 || Bool
|-
| 4 || Float
|-
| 5 || String
|-
| 6 || Wide string ("wstring")
|-
| 7 || Int array
|-
| 8 || Bool array
|-
| 9 || Float array
|-
| 10 || String array
|-
| 11 || Wstring array
|-
| 12 || Actor identifier
|}
|}


=== Actor ===
=== Actor ===
(The official name for this structure in the EventFlow library is evfl::ResActor.)
{|class="wikitable"
{|class="wikitable"
! Offset !! Type !! Description
! Offset !! Type !! Description
Line 243: Line 259:
| 0x10 || PascalString* || Argument name
| 0x10 || PascalString* || Argument name
|-
|-
| 0x18 || PascalString** || Pointer to array of actions (strings)
| 0x18 || [[#evfl::ResAction]]* || Pointer to array of actions (strings)
|-
|-
| 0x20 || PascalString** || Pointer to array of queries (strings)
| 0x20 || [[#evfl::ResQuery]]* || Pointer to array of queries (strings)
|-
|-
| 0x28 || Container* || Parameters
| 0x28 || Container* || Parameters
Line 255: Line 271:
| 0x34 || u16 || Entry point index for associated entry point (0xffff if none)
| 0x34 || u16 || Entry point index for associated entry point (0xffff if none)
|-
|-
| 0x36 || u16 || Unknown. This is set to 1 for flowcharts. Timeline actors use a different value here. {{check}}
| 0x36 || u8 || Cut number? This is set to 1 for flowcharts. Timeline actors sometimes use a different value here. {{check}} In BotW, this value is passed as the @MA actor parameter<ref>Switch 1.5.0 0x7100DA8628</ref>.
|-
| 0x37 || u8 || Padding
|}
 
==== evfl::ResAction ====
{|class="wikitable"
! Offset !! Type !! Description
|-
| 0x0 || PascalString* || Name
|}
==== evfl::ResQuery ====
{|class="wikitable"
! Offset !! Type !! Description
|-
| 0x0 || PascalString* || Name
|}
|}


=== Event ===
=== Event ===
==== EventType enum ====
==== EventType enum ====
(The official name for this enum in the EventFlow library is evfl::ResEvent::EventType::Type.)
{|class="wikitable"
{|class="wikitable"
! Value !! Name !! Description
! Value !! Name !! Description
Line 275: Line 307:


==== Main event structure ====
==== Main event structure ====
(The official name for this structure in the EventFlow library is evfl::ResEvent.)
{|class="wikitable"
{|class="wikitable"
!rowspan=2|Offset
!rowspan=2|Offset
Line 302: Line 335:


=== Entry point ===
=== Entry point ===
(The official name for this structure in the EventFlow library is evfl::ResEntryPoint.)
{|class="wikitable"
{|class="wikitable"
! Offset !! Type !! Description
! Offset !! Type !! Description
Line 307: Line 341:
| 0x0 || u16* || Sub flow event indices
| 0x0 || u16* || Sub flow event indices
|-
|-
| 0x8 || u64 || Padding? {{check}}
| 0x8 || Dictionary* || VariableDef names (parsed by the evfl lib, but unused by BotW)
|-
|-
| 0x10 || void* || Unknown pointer
| 0x10 || VariableDef* || VariableDefs (parsed by the evfl lib, but unused by BotW)
|-
|-
| 0x18 || u16 || Number of sub flow event indices
| 0x18 || u16 || Number of sub flow event indices
|-
|-
| 0x1a || u16 || Padding? {{check}}
| 0x1a || u16 || Number of variable definitions
|-
|-
| 0x1c || u16 || Main event index
| 0x1c || u16 || Main event index. 0xffff if the entry point doesn't point to any event.
|-
|-
| 0x1e || u16 || Padding? {{check}}
| 0x1e || u16 || Padding
|}
|}


=== Flowchart ===
=== Flowchart ===
(The official name for this structure in the EventFlow library is evfl::ResFlowchart.)
{|class="wikitable"
{|class="wikitable"
! Offset !! Type !! Description
! Offset !! Type !! Description
Line 360: Line 395:


=== Timeline ===
=== Timeline ===
{{empty section}}
(The official name for this structure in the EventFlow library is evfl::ResTimeline.)
{|class="wikitable"
! Offset !! Type !! Description
|-
| 0x0 || char[4] || Magic ("TLIN")
|-
| 0x4 || u32 || String pool offset (relative to this structure)
|-
| 0x8 || u32 || Padding
|-
| 0xc || u32 || Padding
|-
| 0x10 || float || Duration
|-
| 0x14 || u16 || Number of actors
|-
| 0x16 || u16 || Total number of actions
|-
| 0x18 || u16 || Number of clips
|-
| 0x1a || u16 || Number of oneshots
|-
| 0x1c || u16 || Number of subtimelines
|-
| 0x1e || u16 || Number of cuts
|-
| 0x20 || PascalString* || Name
|-
| 0x28 || Actor* || Actors
|-
| 0x30 || [[#Clip]]* || Clips
|-
| 0x38 || [[#Oneshot]]* || Oneshots
|-
| 0x40 || [[#Trigger]]* || Triggers (exactly 2 per clip)
|-
| 0x48 || [[#Subtimeline]]* || Subtimelines
|-
| 0x50 || [[#Cut]]* || Cuts
|-
| 0x58 || Container* || Parameters
|}
 
=== Clip ===
(The official name for this structure in the EventFlow library is evfl::ResClip.)
{|class="wikitable"
! Offset !! Type !! Description
|-
| 0x0 || float || Start time
|-
| 0x4 || float || Duration
|-
| 0x8 || u16 || Actor index
|-
| 0xa || u16 || Actor action index
|-
| 0xc || u8 || ? {{check}}
|-
| 0xd || u8[3] || Padding
|-
| 0x10 || Container* || Parameters
|}
 
=== Oneshot ===
(The official name for this structure in the EventFlow library is evfl::ResOneshot.)
{|class="wikitable"
! Offset !! Type !! Description
|-
| 0x0 || float || Time
|-
| 0x4 || u16 || Actor index
|-
| 0x6 || u16 || Actor action index
|-
| 0x8 || void* || Padding
|-
| 0x10 || Container* || Parameters
|}
 
=== Trigger ===
(The official name for this structure in the EventFlow library is evfl::ResTrigger.)
{|class="wikitable"
! Offset !! Type !! Description
|-
| 0x0 || u16 || Clip index
|-
| 0x2 || [[#TriggerType]] || Trigger type
|-
| 0x3 || u8 || Padding
|}
 
==== TriggerType ====
{|class="wikitable"
! Value !! Description
|-
| 1 || Enter - triggered when a clip starts
|-
| 2 || Leave - triggered when a clip ends
|}
 
=== Cut ===
(The official name for this structure in the EventFlow library is evfl::ResCut.)
{|class="wikitable"
! Offset !! Type !! Description
|-
| 0x0 || float || Start time {{check}}
|-
| 0x4 || u32 || ? {{check}}
|-
| 0x8 || PascalString* || Name
|-
| 0x10 || Container* || Parameters
|}
 
=== Subtimeline ===
(The official name for this structure in the EventFlow library is evfl::ResSubtimeline.)
{|class="wikitable"
! Offset !! Type !! Description
|-
| 0x0 || PascalString* || Subtimeline name
|}


== Section order ==
== Section order ==
Line 375: Line 530:


=== Timeline ===
=== Timeline ===
{{empty section}}
* Actor param containers, evfl::ResAction
* Parameter container
* Timeline header
* Actors
* Clips
* Oneshots {{check}}
* Subtimelines
* Triggers
* Cuts
* Clip param containers
* Oneshot param containers {{check}}
* Cut param containers {{check}}


=== Flowchart ===
=== Flowchart ===
Line 405: Line 571:
* [https://github.com/leoetlino/event-editor EventEditor]: graphical editor for event flows
* [https://github.com/leoetlino/event-editor EventEditor]: graphical editor for event flows


<hr>
== References ==
 
<references/>
<references/>


[[Category:File formats]]
[[Category:File formats]]

Latest revision as of 11:55, 16 April 2023

BFEVFL is a binary file format for event flows. In Breath of the Wild and other Nintendo games such as Splatoon 2 and Link's Awakening, binary event flows are parsed by Nintendo's EventFlow/evfl library.

Structures

BFEVFL is a format that supports both big endian and little endian; however, event flows typically use little endian even on Wii U, as is the case in Breath of the Wild.

BFEVFL uses pointers very heavily to refer to strings, to other sections, etc. All pointers are always 64-bit long -- this is because the same format is used on 64-bit platforms like the Switch. Pointers must be present in the #Relocation table if the file is to be loaded correctly by the official EventFlow code.

As a direct consequence, most sections don't have any fixed order. Therefore, this article will only document the data structures and mention the order Nintendo places sections in. For more details, it is recommended to look at the evfl library directly.

Another consequence is that sections must be aligned to 8-byte boundaries in most cases to avoid unaligned memory accesses (this is not as important for strings).

Header

Offset Type Description
0x0 char[8] Magic ("BFEVFL\x00\x00")
0x8 u8 Version (major)
0x9 u8 Version (minor)
0xa u8 Version (patch)
0xb u8 Version (sub-patch)
0xc s16 Byte order mark
0xe u8 Alignment (to get the actual value: 1 << raw_value)
0xf u8 Padding
0x10 int File name offset
0x14 u16 Is relocated flag (set in memory)
0x16 u16 First block offset
0x18 int Relocation table offset
0x1c int File size
0x20 u16 Number of flowcharts
0x22 u16 Number of timelines
0x24 u32 Padding
0x28 Flowchart** Flowchart (nullptr if no flowchart)
0x30 Dictionary* Flowchart name dictionary
0x38 Timeline** Timeline (nullptr if no timeline)
0x40 Dictionary* Timeline name dictionary

Note: 0x0-0x20 are just a standard ore::BinaryFileHeader (which is identical to nn::util::BinaryFileHeader).

Relocation table

(The official name for this structure in the EventFlow library is ore::RelocationTable.)

Offset Type Description
0x0 char[4] Magic ("RELT")
0x4 int Offset to relocation table start (table_start_offset)
0x8 int Number of sections
0xc u32 Padding
0x10 Section[num_sections] Sections
- Entry[...] Section entries (emitted in the same order as each section)

Note: the number of sections is almost always 1 because a single section can already fit 2^32 - 1 entries. If you need more than 4 billion entries, you are probably doing something very wrong.

The table base pointer is calculated as follows: reinterpret_cast<char*>(&table) - table_start_offset

Relocation table section

Offset Type Description
0x0 void* Optional base pointer (ptr)
0x8 int Optional base pointer offset (offset)
0xc int Size
0x10 int Index of the first entry in this section
0x14 int Number of entries

The base pointer (base) is calculated as follows:

  • If the optional base pointer field is nullptr, the base pointer is equal to the table base pointer.
  • Otherwise, it is equal to ptr - offset.

Relocation table entry

Offset Type Description
0x0 u32 Offset to pointers to relocate, relative to the table base pointer
0x4 u32 Bit field that determines which pointers need to be relocated (up to 32 contiguous pointers starting from the listed offset)

Offsets are treated as signed 32-bit integers.

Pointers are relocated by overwriting each 64-bit pointer field with base + offset.

String pool

(The official name for this structure in the EventFlow library is ore::StringPool.)

The string pool starts with the STR magic and contains 2-byte aligned strings. The magic is not checked at all since strings are directly referred to using pointers.

All strings in the pool are Pascal strings, i.e. length-prefixed strings. The length is an unsigned 16 bit integer. Strings are stored in reverse order of their binary representation.

Dictionary

(The official name for this structure in the EventFlow library is ore::ResDic.)

A dictionary is a data structure that is used to quickly look up the index of an element based on its name. Thus, they are always used in conjunction with an array of elements. However, the way the dictionary and the array are associated depends on the structure.

BFEVFL dictionaries are essentially binary radix trees (also called PATRICIA trees or tries). The structure contains a binary search tree and bit-by-bit comparisons of strings are done to navigate through it. It is extremely similar to the Wii U BFRES "index group" structure, but with significant changes to the algorithm. The Switch BFRES format shares the same algorithm.

Offset Type Description
0x0 char[4] Magic ("DIC ")
0x4 u32 Number of entries (ignoring root entry)
0x8 Entry Root entry (bit index is 0xffffffff)
0x18 Entry[num_entries] Entries

Dictionary entry

Offset Type Description
0x0 u32 Bit index
0x4 u16 Index of next node if bit is 0
0x6 u16 Index of next node if bit is 1
0x8 PascalString* Name

Example: Hello corresponds to 100100001100101011011000110110001101111. Bits 0-3 are 1, bit 4 is 0, etc.

Container

(The official name for this structure in the EventFlow library is ore::ResMetaData.)

Containers are key-value mappings with keys being strings. A #Dictionary is used to store keys.

Offset Type Description
0x0 ContainerItem Root container structure (data type is Container)

ContainerItem

Offset Type Description
0x0 ContainerDataType (u8) Data type
0x1 u8 Padding
0x2 u16 Number of items
0x4 u32 Padding
0x8 Dictionary* Dictionary (only for the Container data type)
0x10 ContainerItemData Data

ContainerItemData

Data type Data Number of items
Argument String (not in string pool, follows item structure immediately) 1
Container ContainerItem*[n] n
Int Signed 32-bit integer 1
Bool 0x80000001 if true, 0x00000000 otherwise 1
Float binary32 floating point number 1
String String (not in string pool, follows item structure immediately) 1
Wide string ("wstring") Wide string (not in string pool, follows item structure immediately) 1
Int array Int[n] n
Bool array Bool[n] n
Float array Float[n] n
String array String[n] (aligned to 8-byte boundaries this time) n
Wstring array WString[n] (aligned to 8-byte boundaries this time) n
Actor identifier Two strings: actor name + secondary name 2

Wide strings use wchar_t which is 32-bit long on Switch.

ContainerDataType enum

The following enum definition is complete and all names are official[1].

Value Name
0 Argument
1 Container
2 Int
3 Bool
4 Float
5 String
6 Wide string ("wstring")
7 Int array
8 Bool array
9 Float array
10 String array
11 Wstring array
12 Actor identifier

Actor

(The official name for this structure in the EventFlow library is evfl::ResActor.)

Offset Type Description
0x0 PascalString* Name
0x8 PascalString* Secondary name
0x10 PascalString* Argument name
0x18 #evfl::ResAction* Pointer to array of actions (strings)
0x20 #evfl::ResQuery* Pointer to array of queries (strings)
0x28 Container* Parameters
0x30 u16 Number of actions
0x32 u16 Number of queries
0x34 u16 Entry point index for associated entry point (0xffff if none)
0x36 u8 Cut number? This is set to 1 for flowcharts. Timeline actors sometimes use a different value here. [check] In BotW, this value is passed as the @MA actor parameter[2].
0x37 u8 Padding

evfl::ResAction

Offset Type Description
0x0 PascalString* Name

evfl::ResQuery

Offset Type Description
0x0 PascalString* Name

Event

EventType enum

(The official name for this enum in the EventFlow library is evfl::ResEvent::EventType::Type.)

Value Name Description
0 Action Invokes an actor function ("action") with no return value.
1 Switch Invokes an actor function ("query") with an int return value and branches execution flow depending on it.
2 Fork Branches execution flow unconditionally.
3 Join Used to reunify execution flow after a fork finishes executing.
4 Sub flow Invokes an entry point in the same or in a different event flow. Similar to a function call.

Main event structure

(The official name for this structure in the EventFlow library is evfl::ResEvent.)

Offset Type Description
Action Switch Fork Join Sub flow
0x0 PascalString* Name
0x8 EventType (u8) Type
0x9 u8 Padding
0xa u16 Next event index Number of cases (>=0) Number of forks (>0) Next event index
0xc u16 Actor index Join event index (required) Unused
0xe u16 Actor action index Actor query index Unused
0x10 void* Container* (optional parameters) u16* (fork event indexes) Unused Container* (optional parameters)
0x18 void* Unused SwitchCases* (0x0: u32 value, 0x4: u16 event index, 0x6: u16 padding) Unused PascalString* (flowchart name)
0x20 void* Unused PascalString* (entry point name)

Entry point

(The official name for this structure in the EventFlow library is evfl::ResEntryPoint.)

Offset Type Description
0x0 u16* Sub flow event indices
0x8 Dictionary* VariableDef names (parsed by the evfl lib, but unused by BotW)
0x10 VariableDef* VariableDefs (parsed by the evfl lib, but unused by BotW)
0x18 u16 Number of sub flow event indices
0x1a u16 Number of variable definitions
0x1c u16 Main event index. 0xffff if the entry point doesn't point to any event.
0x1e u16 Padding

Flowchart

(The official name for this structure in the EventFlow library is evfl::ResFlowchart.)

Offset Type Description
0x0 char[4] Magic ("EVFL")
0x4 u32 String pool offset (relative to this structure)
0x8 u32 Padding
0xc u32 Padding
0x10 u16 Number of actors
0x12 u16 Total number of actions
0x14 u16 Total number of queries
0x16 u16 Number of events
0x18 u16 Number of entry points
0x1a u16 Padding
0x1c u16 Padding
0x1e u16 Padding
0x20 PascalString* Name
0x28 Actor* Actors
0x30 Event* Events
0x38 Dictionary* Entry point dictionary
0x40 EntryPoint* Entry points

Timeline

(The official name for this structure in the EventFlow library is evfl::ResTimeline.)

Offset Type Description
0x0 char[4] Magic ("TLIN")
0x4 u32 String pool offset (relative to this structure)
0x8 u32 Padding
0xc u32 Padding
0x10 float Duration
0x14 u16 Number of actors
0x16 u16 Total number of actions
0x18 u16 Number of clips
0x1a u16 Number of oneshots
0x1c u16 Number of subtimelines
0x1e u16 Number of cuts
0x20 PascalString* Name
0x28 Actor* Actors
0x30 #Clip* Clips
0x38 #Oneshot* Oneshots
0x40 #Trigger* Triggers (exactly 2 per clip)
0x48 #Subtimeline* Subtimelines
0x50 #Cut* Cuts
0x58 Container* Parameters

Clip

(The official name for this structure in the EventFlow library is evfl::ResClip.)

Offset Type Description
0x0 float Start time
0x4 float Duration
0x8 u16 Actor index
0xa u16 Actor action index
0xc u8 ? [check]
0xd u8[3] Padding
0x10 Container* Parameters

Oneshot

(The official name for this structure in the EventFlow library is evfl::ResOneshot.)

Offset Type Description
0x0 float Time
0x4 u16 Actor index
0x6 u16 Actor action index
0x8 void* Padding
0x10 Container* Parameters

Trigger

(The official name for this structure in the EventFlow library is evfl::ResTrigger.)

Offset Type Description
0x0 u16 Clip index
0x2 #TriggerType Trigger type
0x3 u8 Padding

TriggerType

Value Description
1 Enter - triggered when a clip starts
2 Leave - triggered when a clip ends

Cut

(The official name for this structure in the EventFlow library is evfl::ResCut.)

Offset Type Description
0x0 float Start time [check]
0x4 u32 ? [check]
0x8 PascalString* Name
0x10 Container* Parameters

Subtimeline

(The official name for this structure in the EventFlow library is evfl::ResSubtimeline.)

Offset Type Description
0x0 PascalString* Subtimeline name

Section order

File

  • Header (0x48 bytes)
  • Flowchart* array if it exists
  • Flowchart dictionary (always)
  • Timeline* array if it exists
  • Timeline dictionary (always)
  • Timeline
  • Flowchart
  • String pool (STR )
  • Relocation table (RELT)

Timeline

  • Actor param containers, evfl::ResAction
  • Parameter container
  • Timeline header
  • Actors
  • Clips
  • Oneshots [check]
  • Subtimelines
  • Triggers
  • Cuts
  • Clip param containers
  • Oneshot param containers [check]
  • Cut param containers [check]

Flowchart

  • Flowchart header
  • Actors
  • Argument name is put in the string pool.
  • Events
  • Entry point dictionary
  • Entry points
  • Event param containers, fork structs, etc. (in order)
  • Actor param containers, string pointer arrays (in order)
  • Entry point extra data
  • Sub flow event index arrays are written here
  • ptr_x10 data may be written here? (ptr_x10 has always been a nullptr in files that have been checked by leoetlino.)
  • The size for each entry point is sizeof(event_idx_array) rounded up to the nearest multiple of 8 + 0x18 bytes.

Container

  • Container header (variable size)
  • Container dictionary
  • Container items (+ values)

Usage in Breath of the Wild

BFEVFL is used as the underlying format for event flows in the release versions. They are always stored under EventFlow and have "bfevfl" (for flowcharts) and "bfevtm" (for timelines) as their file extensions.

For development versions, it appears that a different, non-binary format was used ("evfl").

Tools

  • evfl: Python library for parsing and writing event flows
  • EventEditor: graphical editor for event flows

References

  1. 0x7101DA3EC8 in Switch 1.5.0
  2. Switch 1.5.0 0x7100DA8628