Difference between revisions of "Open Spiral Bitcode"

From Spiral Framework
Jump to navigation Jump to search
(Created page with "'''O'''pen '''S'''piral '''B'''itcode (OSB) is an intermediary representative format designed to store OSL instructions in a form that makes them readable quickly and efficien...")
(No difference)

Revision as of 10:38, 1 March 2020

Open Spiral Bitcode (OSB) is an intermediary representative format designed to store OSL instructions in a form that makes them readable quickly and efficiently, without having to bundle ANTLR in it's entirety.

Specification

OSB Opcodes
Hex Opcode Name Argument Count Comments
00 Set Version 3
01 Add Dialogue 1
02 Add Dialogue (Variable Speaker) 1
03 Add Function Call 1+
04 Add If Check 3+ This is a meta check; use Add Flag Check for in game flag checks
05 Add Flag Check 3+
06 Add Tree 0
06 Add Load Map 0
...
70 Add Opcode 1+
71 Add Opcode (Variable Argument(s)) 1+
72 Add Named Opcode 1+
73 Add Named Opcode (Variable Argument(s)) 1+
...
80 Add Label Not currently implemented; see tracking issue
81 Add Parameter Not currently implemented; see tracking issue
82 Add Text Not currently implemented; see tracking issue
...
8F Set Variable 2

00 - Set Version

Sets the version of OSB to use. Only necessary if you plan on changing implementations based on version.

Reads 3 bytes - major, minor, patch.

The current version is 2.0.0

01 - Add Dialogue

Adds a line of dialogue to the current script. Reads a null terminated UTF-8 String as the speaker name, and an argument (See #Read Argument) for the dialogue.

How the speaker name is handled is up to the visitor.

02 - Add Dialogue (Variable Speaker)

Adds a line of dialogue to the current script. Reads a null terminated UTF-8 String as the variable name, and an argument (See #Read Argument) for the dialogue.

This opcode should get the data corresponding to the variable passed in, and then pass that value on to the speaker.

03 - Add Function Call

Calls a function. Reads a null terminated UTF-8 String as the function name, and then a single byte as the argument count, then reads (argument count) function parameters.

A function parameter is defined as a null terminated UTF-8 String (which may be just a single null terminator to omit it) as the parameter name, then the value (See #Read Argument).

The function calls that are exposed are up to the individual visitor, however a list of standards is provided here.

04 - Add If Check

Runs an expression, and only parses the following block if the expression is true.

Reads the following values:

  • Value to compare (See #Read Argument)
  • Comparison method (single byte)
  • Value to compare against (See #Read Argument)
  • Else If Branch Count (single byte, 0-127)

If a comparison of the two values with the provided method is true, then we run the first provided block. Otherwise, we repeat until we get a check that returns true.

If the Branch Count has the ELSE flag set (0x80), then the final block is an else block, and will be run if none of the checks returned true.

A block is simply an int32 for length, followed by more OSB code. If the block should be run, create a subparser with the visitor and an incremented level, otherwise simply skip that many bytes.

05 - Add Flag Check

Adds a flag check to the game.

Reads the following values:

  • Condition count (int8, must be at least 1)
  • Flag to compare (See #Read Argument)
  • Flag comparison method (int8)
  • Flag value to compare against (See #Read Argument)
  • An array of subconditions (condition count - 1)
    • Logical operator (int8)
    • Flag to compare (See #Read Argument)
    • Flag comparison method (int8)
    • Flag value to compare against (See #Read Argument)
  • Else Check Flag Branch Count (single byte, 0-127)

For each flag check, read an int32 for length, then that many bytes for the eventual block that will be parsed.

Each else check flag block also first must read it's own check:

  • Condition count (int8, must be at least 1)
  • Flag to compare (See #Read Argument)
  • Flag comparison method (int8)
  • Flag value to compare against (See #Read Argument)
  • An array of subconditions (condition count - 1)
    • Logical operator (int8)
    • Flag to compare (See #Read Argument)
    • Flag comparison method (int8)
    • Flag value to compare against (See #Read Argument)

If the Branch Count has the ELSE flag set (0x80), then the final block is an else block.

06 - Add Tree

Adds a type of tree. A tree here is defined as a block of OSB code that operates within a certain context, such as scripts that need to use the Branch opcode

Reads an int8 as the tree type, then an int32 as the block length, then that many bytes of OSB code.

Tree Types
Hex Opcode Name
0 Present Selection

07 - Load Map

Adds a call to load a map. While this is currently lin-centric, it will eventually encompass DRv3 as well (see tracking issue).

Reads three arguments (See #Read Argument) - map ID, state, and an unknown. State defaults to 0, and unknown defaults to 0xFF, if a null-like argument is provided, however three arguments must be provided!.

An int32 is then read as the length of the following block, then that many bytes of OSB code is read.

70 - Add Opcode

Adds a numerical opcode. Reads an int8 as the opcode, an int8 as the argument count, then that many variable int16's.

71 - Add Opcode (Variable Arguments)

Adds a numerical opcode. Reads an int8 as the opcode, an int8 as the argument count, then that many arguments (See #Read Argument).

72 - Add Opcode

Adds a named opcode. Reads a UTF-8 null terminated string as the opcode name, an int8 as the argument count, then that many variable int16's.

73 - Add Opcode (Variable Arguments)

Adds a named opcode. Reads a UTF-8 null terminated string as the opcode name, an int8 as the argument count, then that many arguments (See #Read Argument).

8F - Set Variable

Sets a variable value. Reads a UTF-8 null terminated string as the variable name, and an argument (See #Read Argument).

---

Arguments

OSB has a few clever ways of supporting varying argument types and supplying them to the underlying compiler.

Read Argument

Read an int8 for the argument type, then check the corresponding entry for how to handle it

Argument Types
Hex Opcode Name
60 Label
61 Parameter
62 Text
63 Long Label
64 Long Parameter
65 Long Reference
...
6A Boolean
6B Function Call
6C Variable Reference
...
6F Null
70 Int8
71 Int16LE
72 Int16BE
73 Int24LE
74 Int24BE
75 Int32LE
76 Int32BE
...
7E Arbitrary Integer
7F Arbitrary Float

Label Argument

Read a null terminated UTF-8 String.

Parameter Argument

Read a null terminated UTF-8 String.

Text Argument

Read a null terminated UTF-8 String.

Boolean Argument

Read an int8 as a truthy (0 is false, anything else is true) value.

Function Call Argument

Reads a null terminated UTF-8 String as the function name, and then a single byte as the argument count, then reads (argument count) function parameters.

A function parameter is defined as a null terminated UTF-8 String (which may be just a single null terminator to omit it) as the parameter name, then the value (See #Read Argument).

Variable Reference Argument

Reads a null terminated UTF-8 String as the variable name.

Numerical Arguments

All fixed-type numerical arguments (int8, int16, int32) are read as expected. Arbitrary integers are read as an int64LE, and arbitrary decimals are read as a float32LE.

Variable Int16

Read the first byte. If that byte is less than 0x80, that is the value.

If it's greater than 0x80, get the value returned by performing a bitwise AND with 0x7F.

Read the second byte, and shift it right by 7 bits, and perform a bitwise OR with the first value.

In Python:

(first_byte & 0x7F) | (f.get_u8() << 7)