1,158
edits
imported>Leoetlino |
Tags: Mobile edit Mobile web edit |
||
(19 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
<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 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 [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 || | | 0x8 || u8 || Version (major) | ||
|- | |- | ||
| | | 0x9 || u8 || Version (minor) | ||
|- | |- | ||
| | | 0xa || u8 || Version (patch) | ||
|- | |- | ||
| 0xc || | | 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 || | | 0xf || u8 || Padding | ||
|- | |- | ||
| 0x10 || | | 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 || | | 0x18 || int || Relocation table offset | ||
|- | |- | ||
| 0x1c || | | 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 | ||
Line 63: | Line 65: | ||
| 0x0 || char[4] || Magic ("RELT") | | 0x0 || char[4] || Magic ("RELT") | ||
|- | |- | ||
| 0x4 || | | 0x4 || int || Offset to relocation table start (<code>table_start_offset</code>) | ||
|- | |- | ||
| 0x8 || | | 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 78: | Line 84: | ||
! Offset !! Type !! Description | ! Offset !! Type !! Description | ||
|- | |- | ||
| 0x0 || void* || | | 0x0 || void* || Optional base pointer (<code>ptr</code>) | ||
|- | |- | ||
| | | 0x8 || int || Optional base pointer offset (<code>offset</code>) | ||
|- | |- | ||
| | | 0xc || int || Size | ||
|- | |- | ||
| | | 0x10 || int || Index of the first entry in this section | ||
|- | |- | ||
| | | 0x14 || int || Number of 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 95: | 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 === | ||
Line 130: | Line 142: | ||
! Offset !! Type !! Description | ! Offset !! Type !! Description | ||
|- | |- | ||
| 0x0 || u32 || | | 0x0 || u32 || Bit index | ||
|- | |- | ||
| 0x4 || u16 || | | 0x4 || u16 || Index of next node if bit is 0 | ||
|- | |- | ||
| 0x6 || u16 || | | 0x6 || u16 || Index of next node if bit is 1 | ||
|- | |- | ||
| 0x8 || PascalString* || Name | | 0x8 || PascalString* || Name | ||
|} | |} | ||
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:: | (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. | ||
{|class="wikitable" | {|class="wikitable" | ||
! | ! Offset !! Type !! Description | ||
|- | |- | ||
| | | 0x0 || ContainerItem || Root container structure (data type is <code>Container</code>) | ||
| | |||
| | |||
|} | |} | ||
Line 202: | Line 176: | ||
| 0x4 || u32 || Padding | | 0x4 || u32 || Padding | ||
|- | |- | ||
| 0x8 || Dictionary* || Dictionary (for | | 0x8 || Dictionary* || Dictionary (only for the Container data type) | ||
|- | |- | ||
| 0x10 || | | 0x10 || ContainerItemData || Data | ||
|} | |} | ||
==== | ==== ContainerItemData ==== | ||
{|class="wikitable" | {|class="wikitable" | ||
! Data type !! Data !! Number of items | ! Data type !! Data !! Number of items | ||
Line 218: | 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 228: | 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 | | 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 238: | 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 || ( | | 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 | |||
|} | |} | ||
Line 255: | Line 259: | ||
| 0x10 || PascalString* || Argument name | | 0x10 || PascalString* || Argument name | ||
|- | |- | ||
| 0x18 || | | 0x18 || [[#evfl::ResAction]]* || Pointer to array of actions (strings) | ||
|- | |- | ||
| 0x20 || | | 0x20 || [[#evfl::ResQuery]]* || Pointer to array of queries (strings) | ||
|- | |- | ||
| 0x28 || Container* || Parameters | | 0x28 || Container* || Parameters | ||
Line 267: | 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 || u8 || Cut number? This is set to 1 for flowcharts. Timeline actors sometimes 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 | | 0x37 || u8 || Padding | ||
|} | |||
==== evfl::ResAction ==== | |||
{|class="wikitable" | |||
! Offset !! Type !! Description | |||
|- | |||
| 0x0 || PascalString* || Name | |||
|} | |||
==== evfl::ResQuery ==== | |||
{|class="wikitable" | |||
! Offset !! Type !! Description | |||
|- | |||
| 0x0 || PascalString* || Name | |||
|} | |} | ||
Line 332: | Line 349: | ||
| 0x1a || u16 || Number of variable definitions | | 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 | | 0x1e || u16 || Padding | ||
Line 338: | Line 355: | ||
=== 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 417: | Line 435: | ||
| 0x50 || [[#Cut]]* || Cuts | | 0x50 || [[#Cut]]* || Cuts | ||
|- | |- | ||
| 0x58 || | | 0x58 || Container* || Parameters | ||
|} | |} | ||
Line 437: | Line 455: | ||
| 0xd || u8[3] || Padding | | 0xd || u8[3] || Padding | ||
|- | |- | ||
| 0x10 || | | 0x10 || Container* || Parameters | ||
|} | |} | ||
Line 453: | Line 471: | ||
| 0x8 || void* || Padding | | 0x8 || void* || Padding | ||
|- | |- | ||
| 0x10 || | | 0x10 || Container* || Parameters | ||
|} | |} | ||
Line 463: | Line 481: | ||
| 0x0 || u16 || Clip index | | 0x0 || u16 || Clip index | ||
|- | |- | ||
| 0x2 || TriggerType || Trigger type | | 0x2 || [[#TriggerType]] || Trigger type | ||
|- | |- | ||
| 0x3 || u8 || Padding | | 0x3 || u8 || Padding | ||
|} | |||
==== TriggerType ==== | |||
{|class="wikitable" | |||
! Value !! Description | |||
|- | |||
| 1 || Enter - triggered when a clip starts | |||
|- | |||
| 2 || Leave - triggered when a clip ends | |||
|} | |} | ||
Line 473: | Line 500: | ||
! Offset !! Type !! Description | ! Offset !! Type !! Description | ||
|- | |- | ||
| 0x0 || float || | | 0x0 || float || Start time {{check}} | ||
|- | |- | ||
| 0x4 || u32 || ? {{check}} | | 0x4 || u32 || ? {{check}} | ||
Line 479: | Line 506: | ||
| 0x8 || PascalString* || Name | | 0x8 || PascalString* || Name | ||
|- | |- | ||
| 0x10 || | | 0x10 || Container* || Parameters | ||
|} | |} | ||
Line 503: | Line 530: | ||
=== Timeline === | === 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 === | ||
Line 533: | 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 | ||
== References == | |||
<references/> | <references/> | ||
[[Category:File formats]] | [[Category:File formats]] |