46
edits
m (fixed formatting) |
(Update "precondition" to "query" and "internal" to "property" based on terminology found in NSO Playtest.) |
||
(11 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
'''AINB''' ('''AI''' '''N'''ode '''B'''inary) is a file format | '''AINB''' ('''AI''' '''N'''ode '''B'''inary) is a file format implementing behavior trees for game-specific code. At time of writing it has only appeared in titles on the ModuleSystem game engine. This article is primarily aimed at the version that appears in ''Tears of the Kingdom,'' ''Super Mario Bros. Wonder, Mario vs Donkey Kong,'' and ''Nintendo Switch Online: Playtest Program'' (v0x407). An older version appears in ''Nintendo Switch Sports'' and ''Splatoon 3'' (v0x404). ModuleSystem provides 3 built-in file categories that appear in their own dedicated folders in a title's romfs, being "AI" for actor behavior, "Logic" for actor ai group behavior, and "Sequence" for scene behavior, individual games are free to create their own file categories. | ||
=== File Structure === | === File Structure === | ||
AINB is | AINB is little endian. An AINB file defines a set of commands to evaluate a tree of built-in or game-specific node classes. The built-in "Element_ModuleCaller" node and supporting structures allow a "root" AINB file to call out to an external "module" AINB file, module's can call further modules, allowing AINB files to form a tree of tree's. An AINB's file extension typically designates whether it is a root <code>.root.ainb</code> or module <code>.module.ainb</code>. For AI and Sequence files, the file's entry point is one or more commands which is linked to one or two child nodes. All nodes are accessed by their index (references to query nodes will use their query node index which is local to the amount of query nodes in the file). | ||
All string offsets in the file are relative to the beginning of the string pool and name hashes are 32-bit murmur3 hashes. There are six possible types for AINB parameters: int (signed 32-bit integer), bool, float (32-bit), vector3f, and pointer. Pointer parameters are pointers to objects. | All string offsets in the file are relative to the beginning of the string pool and name hashes are 32-bit murmur3 hashes. There are six possible data types for AINB parameters: int (signed 32-bit integer), bool, float (32-bit), vector3f, and pointer. Pointer parameters are pointers to objects. | ||
==== Section Order ==== | ==== Section Order ==== | ||
Line 14: | Line 14: | ||
# Node Bodies | # Node Bodies | ||
# Attachment Parameters | # Attachment Parameters | ||
# | # Property Parameters | ||
# Input/Output Parameters | # Input/Output Parameters | ||
# Multi-Parameters | # Multi-Parameters | ||
# | # Active Node Update Array | ||
# 0x50 Section (Unused in ''TotK'') | # 0x50 Section (Unused in ''TotK'') | ||
# | # Query Node Array | ||
# Expression Binary | # Expression Binary | ||
# | # AINB Modules | ||
# | # External Action Array | ||
# File Hashes | # File Hashes | ||
# Child Replacement Table | # Child Replacement Table | ||
Line 68: | Line 68: | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
| | |Query Node Count | ||
|- | |- | ||
|0x18 | |0x18 | ||
Line 98: | Line 98: | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
| | |Property Parameters Offset | ||
|- | |- | ||
|0x30 | |0x30 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
| | |Active Node Update Array Offset | ||
|- | |- | ||
|0x34 | |0x34 | ||
Line 138: | Line 138: | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
| | |Query Node Array Offset | ||
|- | |- | ||
|0x50 | |0x50 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Unknown (unused in ''TotK'', always the same as the | |Unknown (unused in ''TotK'', always the same as the Active Node Update Array Offset) | ||
|- | |- | ||
|0x54 | |0x54 | ||
Line 158: | Line 158: | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
| | |AINB Modules Array Offset | ||
|- | |- | ||
|0x60 | |0x60 | ||
Line 168: | Line 168: | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|File Category (0 = AI, 1 = Logic, 2 = Sequence) | |File Category (0 = AI, 1 = Logic, 2 = Sequence) (''Splatoon 3'' game specific, 3 = UniqueSequenceSPL) | ||
|- | |- | ||
|0x68 | |0x68 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
| | |External Action Array Offset | ||
|- | |- | ||
|0x6C | |0x6C | ||
Line 216: | Line 216: | ||
|0x02 | |0x02 | ||
|u16 | |u16 | ||
| | |Main Node Index | ||
|- | |- | ||
|0x16 | |0x16 | ||
|0x02 | |0x02 | ||
|u16 | |u16 | ||
| | |Secondary Node Index (value offset by 1, 0 is reserved) | ||
|} | |} | ||
Command GUIDs are only used for debug messages. Right Node Index will be -1 if the command only has one child node. | Command GUIDs are only used for debug messages. Right Node Index will be -1 if the command only has one child node. | ||
Line 307: | Line 307: | ||
|0x02 | |0x02 | ||
|u16 | |u16 | ||
|Base | |Base Query Node | ||
|- | |- | ||
|0x26 | |0x26 | ||
|0x02 | |0x02 | ||
|u16 | |u16 | ||
| | |Query Node Count | ||
|- | |- | ||
|0x28 | |0x28 | ||
Line 483: | Line 483: | ||
|- | |- | ||
|Element_Fork | |Element_Fork | ||
|See | |See Active Node Update Array | ||
|- | |- | ||
|Element_Join | |Element_Join | ||
|See | |See Active Node Update Array | ||
|- | |- | ||
|Element_Alert | |Element_Alert | ||
Line 495: | Line 495: | ||
|- | |- | ||
|Element_ModuleIF_Input_S32 | |Element_ModuleIF_Input_S32 | ||
| | |Receives a signed int input from the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Input_F32 | |Element_ModuleIF_Input_F32 | ||
| | |Receives a 32-bit float input from the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Input_Vec3f | |Element_ModuleIF_Input_Vec3f | ||
| | |Receives a vector3f input from the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Input_String | |Element_ModuleIF_Input_String | ||
| | |Receives a string input from the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Input_Bool | |Element_ModuleIF_Input_Bool | ||
| | |Receives a bool input from the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Input_Ptr | |Element_ModuleIF_Input_Ptr | ||
| | |Receives an object pointer input from the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Output_S32 | |Element_ModuleIF_Output_S32 | ||
| | |Returns a signed int output from the module to the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Output_F32 | |Element_ModuleIF_Output_F32 | ||
| | |Returns a 32-bit float output from the module to the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Output_Vec3f | |Element_ModuleIF_Output_Vec3f | ||
| | |Returns a vector3f output from the module to the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Output_String | |Element_ModuleIF_Output_String | ||
| | |Returns a string output from the module to the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Output_Bool | |Element_ModuleIF_Output_Bool | ||
| | |Returns a bool output from the module to the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Output_Ptr | |Element_ModuleIF_Output_Ptr | ||
| | |Returns an object pointer output from the module to the calling AINB file | ||
|- | |- | ||
|Element_ModuleIF_Child | |Element_ModuleIF_Child | ||
| | |Returns the node connection name from the module to the calling AINB file | ||
|- | |- | ||
|Element_StateEnd | |Element_StateEnd | ||
|Termination node | |Termination node, specifies what resident node to return to in the calling AINB file | ||
|- | |- | ||
|Element_SplitTiming | |Element_SplitTiming | ||
Line 545: | Line 545: | ||
|- | |- | ||
|1 | |1 | ||
|Is | |Is Query Node | ||
|- | |- | ||
|1 | |1 | ||
|Is | |Is Module | ||
|- | |- | ||
|1 | |1 | ||
|Is Resident Node | |Is Resident Node | ||
|- | |- | ||
| | |1 | ||
|MultiParam Type 2 | |||
|- | |||
|4 | |||
| | | | ||
|} | |} | ||
Line 623: | Line 626: | ||
|Is File Reference Valid | |Is File Reference Valid | ||
|} | |} | ||
The default value entries vary based on the parameter's data type. For int and | The default value entries vary based on the parameter's data type. For int, float, and bool parameters, the entry is a four byte immediate value. String entries are a four byte string value offset. Vector3f entries are 12 bytes. Note that pointer parameters do not have default value entries and do not have default values. | ||
{| class="wikitable" | {| class="wikitable" | ||
|+File Reference Entry | |+File Reference Entry | ||
Line 644: | Line 647: | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
| | |Filename Hash (no extension) | ||
|- | |- | ||
|0x0C | |0x0C | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
| | |File Extension Hash (no .) | ||
|} | |} | ||
Line 663: | Line 666: | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Int | |Int Property Parameter Base Index | ||
|- | |- | ||
|0x04 | |0x04 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Int | |Int Property Parameter Count | ||
|- | |- | ||
|0x08 | |0x08 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Bool | |Bool Property Parameter Base Index | ||
|- | |- | ||
|0x0C | |0x0C | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Bool | |Bool Property Parameter Count | ||
|- | |- | ||
|0x10 | |0x10 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Float | |Float Property Parameter Base Index | ||
|- | |- | ||
|0x14 | |0x14 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Float | |Float Property Parameter Count | ||
|- | |- | ||
|0x18 | |0x18 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|String | |String Property Parameter Base Index | ||
|- | |- | ||
|0x1C | |0x1C | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|String | |String Property Parameter Count | ||
|- | |- | ||
|0x20 | |0x20 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Vector3f | |Vector3f Property Parameter Base Index | ||
|- | |- | ||
|0x24 | |0x24 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Vector3f | |Vector3f Property Parameter Count | ||
|- | |- | ||
|0x28 | |0x28 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Pointer | |Pointer Property Parameter Base Index | ||
|- | |- | ||
|0x2C | |0x2C | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Pointer | |Pointer Property Parameter Count | ||
|- | |- | ||
|0x30 | |0x30 | ||
Line 880: | Line 883: | ||
|0x01 | |0x01 | ||
|u8 | |u8 | ||
| | |Active Node Update Count | ||
|- | |- | ||
|0x07 | |0x07 | ||
|0x01 | |0x01 | ||
|u8 | |u8 | ||
| | |Active Node Update Base Index | ||
|- | |- | ||
|0x08 | |0x08 | ||
Line 965: | Line 968: | ||
|Value | |Value | ||
|} | |} | ||
The interpretation of the value depends on the connection type. For input or output connections, the value is a string offset to the parameter name. For standard connections, the value is a string offset to the connection name. For | The interpretation of the value depends on the connection type. For input or output connections, the value is a string offset to the parameter name. For standard connections, the value is a string offset to the connection name. For active node update connections, the value is an index into the active node update array. Certain node/connection types will also extend the entry length. Selector-type nodes excluding <code>Element_BoolSelector</code> and <code>Element_F32Selector</code> will have an extra four bytes which stores the condition for to link to the child node. For <code>Element_S32Selector</code>, this is an immediate value. For <code>Element_RandomSelector</code>, this is a weight and for <code>Element_StringSelector</code> it is a u32 string offset. The condition for <code>Element_BoolSelector</code> is the entry's value string. <code>Element_F32Selector</code> adds 24 bytes to each entry, consisting of four eight-byte sub-entries. | ||
{| class="wikitable" | {| class="wikitable" | ||
|+<code>Element_F32Selector Condition Entry</code> | |+<code>Element_F32Selector Condition Entry</code> | ||
Line 1,034: | Line 1,037: | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Int | |Int Property Parameter Base Index | ||
|- | |- | ||
|0x08 | |0x08 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Int | |Int Property Parameter Count | ||
|- | |- | ||
|0x0C | |0x0C | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Bool | |Bool Property Parameter Base Index | ||
|- | |- | ||
|0x10 | |0x10 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Bool | |Bool Property Parameter Count | ||
|- | |- | ||
|0x14 | |0x14 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Float | |Float Property Parameter Base Index | ||
|- | |- | ||
|0x18 | |0x18 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Float | |Float Property Parameter Count | ||
|- | |- | ||
|0x1C | |0x1C | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|String | |String Property Parameter Base Index | ||
|- | |- | ||
|0x20 | |0x20 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|String | |String Property Parameter Count | ||
|- | |- | ||
|0x24 | |0x24 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Vector3f | |Vector3f Property Parameter Base Index | ||
|- | |- | ||
|0x28 | |0x28 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Vector3f | |Vector3f Property Parameter Count | ||
|- | |- | ||
|0x2C | |0x2C | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Pointer | |Pointer Property Parameter Base Index | ||
|- | |- | ||
|0x30 | |0x30 | ||
|0x04 | |0x04 | ||
|u32 | |u32 | ||
|Pointer | |Pointer Property Parameter Count | ||
|- | |- | ||
|0x34 | |0x34 | ||
Line 1,152: | Line 1,155: | ||
|} | |} | ||
==== | ==== Property Parameters ==== | ||
This section contains an array of | This section contains an array of property parameters for nodes. The section begins with a six u32 relative offsets to the first entry for each data type. The data type order for this section is int, bool, float, string, vector3f, and pointer. | ||
{| class="wikitable" | {| class="wikitable" | ||
|+Parameter Entry | |+Parameter Entry | ||
Line 1,223: | Line 1,226: | ||
==== Input/Output Parameters ==== | ==== Input/Output Parameters ==== | ||
This section contains an array of input and output parameter entries. Similarly to the | This section contains an array of input and output parameter entries. These appear to be canonically referred to as "plugs". Similarly to the property parameters section, the section begins with u32 relative offsets to the first entry of each data type. However, instead of six offsets, there are 12 - two for each data type (one input, one output). The order of parameters in this section is int input, int output, bool input, bool output, float intput, float output, string input, string output, vector3f input, vector3f output, pointer input, and pointer output. | ||
{| class="wikitable" | {| class="wikitable" | ||
|+Input Parameter Entry | |+Input Parameter Entry | ||
Line 1,383: | Line 1,386: | ||
|} | |} | ||
==== | ==== Active Node Update Array ==== | ||
This section is an array of entries for | This section is an array of entries for updates to the active node array. The first four bytes specify the number of entries. Each entry is four bytes. | ||
{| class="wikitable" | {| class="wikitable" | ||
|+ | |+ | ||
Line 1,397: | Line 1,400: | ||
|- | |- | ||
|1 | |1 | ||
|Update Post | |Update Post Calc | ||
|} | |} | ||
If the first byte of the flags is set to false, then the entry contains another four bytes with a string offset (purpose unknown). | If the first byte of the flags is set to false, then the entry contains another four bytes with a string offset (purpose unknown). Otherwise, the entry is considered valid if the first byte equals 1. When processing a node graph, the game maintains an array of active root nodes. When a command calculated, the root node of the command is added to this array and it and its child nodes are calculated recursively. An active node update will change the current active root node to the specified node. This usually happens right before the next command's calculation occurs, but if the top bit is set, then it will occur immediately after the current command's calculation finishes (if the source node is an Element_Join nodes, it will always update the active node post current command calculation). This is primarily used for "subroutines" that run a single time then return to the main execution routine. This system is also used for Element_Fork and Element_Join nodes. An Element_Fork node, unlike normal nodes, will replace the entire current active node array with its array of active node updates (essentially forking execution into multiple different paths). The node at the end of each of these paths will optionally also have an active node update referencing a specific Element_Join node. Each of these nodes will push the Element_Join into the active node array. However, Element_Join nodes have an property parameter called InFlowNum and until it has been pushed onto the array that many times in a single calculation, it will not activate and remain dormant. However, once this threshold is reached, it will begin execution (with a guard to make sure only one of the instances added to the array will execute). | ||
==== 0x50 Section ==== | ==== 0x50 Section ==== | ||
This section is unused in ''Tears of the Kingdom''. | This section is unused in ''Tears of the Kingdom''. | ||
==== | ==== Query Node Array ==== | ||
This section is an array of entries of | This section is an array of entries of query nodes present in the file. | ||
{| class="wikitable" | {| class="wikitable" | ||
|+ | |+Query Node Entry | ||
!Offset | !Offset | ||
!Size | !Size | ||
Line 1,416: | Line 1,419: | ||
|0x02 | |0x02 | ||
|u16 | |u16 | ||
| | |Query Node Index (local to number of query nodes) | ||
|- | |- | ||
|0x02 | |0x02 | ||
Line 1,853: | Line 1,856: | ||
The string pool is an array of null-terminated strings encoded with UTF-8. All string offsets in this section are relative to the beginning of the string pool. | The string pool is an array of null-terminated strings encoded with UTF-8. All string offsets in this section are relative to the beginning of the string pool. | ||
==== | ==== AINB Modules ==== | ||
This section contains an array of all | This section contains an array of all AINB modules linked to the current AINB file. The first four bytes specify the entry count. | ||
{| class="wikitable" | {| class="wikitable" | ||
|+ | |+AINB Module Entry | ||
!Offset | !Offset | ||
!Size | !Size | ||
Line 1,878: | Line 1,881: | ||
|} | |} | ||
==== | ==== External Action Array ==== | ||
This section appears to interact with XLink. The first four bytes specify the entry count. | |||
{| class="wikitable" | {| class="wikitable" | ||
|+Entry String Entry | |+Entry String Entry |
edits