It's pretty good but there is some mistake in the format
I've cross referenced Aonatsu Line chinese demo and Aonatsu line trial. So i have a pretty good understanding of the script format
I also reverse engineered the bin loading function and all the opcodes.
NeXAS seems to separate numeric and string variables you named those variable VariableDeclarations and ParametersDeclarations but those aren't what they are. It's a table of variable names used for debugging, one for int and one for strings.
The unknown block after that are bank arguments, i have documented what a bank is below (i haven't invented the name thats how it is named in the unity version aonatsu line chinese demo).
All the formats for the DAT files are also documented in the unity version.
Anyway here is my personal document if that can help.
I'm also working on my own thing but i'm not very good at programming.
Banks:
Banks are entry point, most script only have one. But title and system tends to have multiple (for menu interaction)
The START (id) and END opcode are the boundary of each bank.
The Start ID reference the bank structure in the file format and get the arguments from there (max 8 arguments)
In the bank structure when the args aren't -1(which mean no arg), you can get the argument name by getting the int variable name table and using the arg value to index into it.
struct Bank {
unsigned int32 id;
int32 args[8];
byte pad[32]; // I've not seen this used anywhere
}
Registers:R0,
R1
Scope:GlobalScriptLocal
Opcode:0x0 - VAL (value u32)
Move literal value into R0
0x4 - PUSH (register_num u32)
Push the value register[register_num] into the calculation stack
0x5 - PARAM (type u32)
Push parameter into the arg stack if type is 0 it's a a numeric value if it's 1 it's a string
0x6 - POP (register_num u32)
POP the value at the top of the calculation stack into register[register_num]
0x7 - CALL (id u16, arg_count u16)
Call the function(native or script) if the id & 0x8000 != 0, it's a script function you need to xor it with 0x8000 to get the id, else it's a native function implemented in the engine you can just take the id as is.
0x8 - GET_VAR (register_num u32)
Get an int variable at a specific scope and return into into Register[register_num]
Register value is an index into an array + a flags that determine the scope
if (index & 0x40000000 != 0) => Global scope
else
if (callStack.size != 0) Local Scope
else Script scope
0x9 - ADD/APPEND (type u32)
If type == 0 (int)
R0 = R0 + R1
If type == 1 (string) R0 and R1 are string
Append R0 to R1 and return string variable index to R0
0xA - SUB
R0 = R1 - R0
0xB - MUL
R0 = R0 * R1
0xC - DIV
R0 = R1 / R0
0xD - MOD
R0 = R1 % R0
0xE - SET_VAR (operand u32)That won't be a complete explanation. This opcode is pretty complex with scoping rules
If operand == -1 // 2 Ways of getting the values
i32 index = R0
i32 value = R1
else
i32 index = (i32)operand
i32 value = R0
// there's flags for scope in the index/value but it would make the explanation too complicated
// use ida and look for aonatsu line trial (paid version is protected), aquarium is optimized it's harder to read
// but it's very close to the same code
// i can provide an idb
if index >= 0 // if it's positive it set an int variable
GET_INT_VAR(index) = value
else // if it's not positive it's a string variable
index = index ^ 0x80000000
GET_STRING_VAR(index) = GET_STRING(value) // GET_STRING can get value from a variable or from the string table
if type == 1
R0 = compare_string_equal(r1, r0)
else if type == 0
R0 = R0 == R1
0x1A - CMP_NE (type u32)
if type == 1
R0 = compare_string_not_equal(r1, r0)
else if type == 0
R0 = R0 != R1
0x1B - START (bank_id u32)
Start of a bank
bank_id reference a specific index in the bank_table
0x1C - END
End of a bank
0x1D - ???MARKER???(operand u32)
Not sure what this is, it set a bit in a bit array everytime it's called.
It also seems to be present at the boundary of each basic block that's why i call it marker
But it's not called in functions only in the main script
But i can't guarantee it's what it is
0x22 - INC(operand i32)
0x2B - CMP_ZERO
R0 = R0 == 0
0x2C - LINE_NUMBER (operand u32)
Set vm->source_line_number to operand
0x2D - SAR
Shift arithmetic right
R0 = R1 >> R0
0x2E - SHL
Shift Logical Left
R0 = R1 << R0
0x2D - SHR
Shift Logical Right
R0 = (u32)R1 >> R0
Here the functions are a similar to previous ones0x30 - Variable ADD/APPEND (operand u32)
Similar to SET_VAR but instead of setting the variable it add/append to it
0x31 - Variable SUB(operand i32)
0x32 - Variable MUL(operand i32)
Similar to SUB
0x33 - Variable DIV(operand i32)
Similar to SUB
0x34 - Variable MOD(operand i32)
Similar to SUB
0x35 - Variable OR(operand i32)
Similar to SUB
0x36 - Variable AND(operand i32)
Similar to SUB
0x37 - Variable XOR(operand i32)
Similar to SUB
0x38 - Variable SAR(operand i32)
Similar to SUB
0x39 - Variable SHL(operand i32)
Similar to SUB
0x3A - Variable SHR(operand i32)
Similar to SUB
0x3F - TO_STRING(register_num u32)
Convert int register[register_num] to string and return the variable index to register[register_num]
0x40 - RETURN (type u32)
Set return value to r0 (if operand is 1 string value, if operand is 0 int value)
0x41 - GOTO(offset u32)
Set program counter to instruction number (not address)
0x42 - BRFALSE(offset u32)
Set program counter to instruction number (not address)
Condition in R0
0x43 - BRTRUE(offset u32)
Set program counter to instruction number (not address)
Condition in R0
0x44 - TO_INT(register_num u32)
Same as To_String
It's pretty good but there is some mistake in the format I've cross referenced Aonatsu Line chinese demo and Aonatsu line trial. So i have a pretty good understanding of the script format I also reverse engineered the bin loading function and all the opcodes. NeXAS seems to separate numeric and string variables you named those variable VariableDeclarations and ParametersDeclarations but those aren't what they are. It's a table of variable names used for debugging, one for int and one for strings. The unknown block after that are bank arguments, i have documented what a bank is below (i haven't invented the name thats how it is named in the unity version aonatsu line chinese demo).
All the formats for the DAT files are also documented in the unity version. Anyway here is my personal document if that can help. I'm also working on my own thing but i'm not very good at programming.
Banks: Banks are entry point, most script only have one. But title and system tends to have multiple (for menu interaction) The START (id) and END opcode are the boundary of each bank. The Start ID reference the bank structure in the file format and get the arguments from there (max 8 arguments) In the bank structure when the args aren't -1(which mean no arg), you can get the argument name by getting the int variable name table and using the arg value to index into it.
Registers:
R0
,R1
Scope:
Global
Script
Local
Opcode:
0x0 - VAL (value u32)
Move literal value into R00x4 - PUSH (register_num u32)
Push the value register[register_num] into the calculation stack0x5 - PARAM (type u32)
Push parameter into the arg stack if type is 0 it's a a numeric value if it's 1 it's a string0x6 - POP (register_num u32)
POP the value at the top of the calculation stack into register[register_num]0x7 - CALL (id u16, arg_count u16)
Call the function(native or script) if the id & 0x8000 != 0, it's a script function you need to xor it with 0x8000 to get the id, else it's a native function implemented in the engine you can just take the id as is.0x8 - GET_VAR (register_num u32)
Get an int variable at a specific scope and return into into Register[register_num] Register value is an index into an array + a flags that determine the scope0x9 - ADD/APPEND (type u32)
0xA - SUB
R0 = R1 - R00xB - MUL
R0 = R0 * R10xC - DIV
R0 = R1 / R00xD - MOD
R0 = R1 % R00xE - SET_VAR (operand u32)
That won't be a complete explanation. This opcode is pretty complex with scoping rules0xF - OR
R0 = R0 || R10x10 - AND
R0 = R0 && R10x11 - BIT_OR
R0 = R0 | R10x12 - BIT_AND
R0 = R0 & R10x13 - XOR
R0 = R0 ^ R10x14 - NOT
R0 = !R00x15 - CMP_LE
R0 = R0 <= R10x16 - CMP_GE
R0 = R0 >= R10x17- CMP_LT
R0 = R0 < R10x18 - CMP_GT
R0 = R0 > R10x19 - CMP_EQ (type u32)
0x1A - CMP_NE (type u32)
0x1B - START (bank_id u32)
Start of a bank bank_id reference a specific index in the bank_table0x1C - END
End of a bank0x1D - ???MARKER???(operand u32)
Not sure what this is, it set a bit in a bit array everytime it's called. It also seems to be present at the boundary of each basic block that's why i call it marker But it's not called in functions only in the main script But i can't guarantee it's what it is0x22 - INC(operand i32)
0x23 - DEC(operand i32)
0x2B - CMP_ZERO
R0 = R0 == 00x2C - LINE_NUMBER (operand u32)
Set vm->source_line_number to operand0x2D - SAR
Shift arithmetic right R0 = R1 >> R00x2E - SHL
Shift Logical Left R0 = R1 << R00x2D - SHR
Shift Logical Right R0 = (u32)R1 >> R0Here the functions are a similar to previous ones
0x30 - Variable ADD/APPEND (operand u32)
Similar to SET_VAR but instead of setting the variable it add/append to it0x31 - Variable SUB(operand i32)
0x32 - Variable MUL(operand i32)
Similar to SUB0x33 - Variable DIV(operand i32)
Similar to SUB0x34 - Variable MOD(operand i32)
Similar to SUB0x35 - Variable OR(operand i32)
Similar to SUB0x36 - Variable AND(operand i32)
Similar to SUB0x37 - Variable XOR(operand i32)
Similar to SUB0x38 - Variable SAR(operand i32)
Similar to SUB0x39 - Variable SHL(operand i32)
Similar to SUB0x3A - Variable SHR(operand i32)
Similar to SUB0x3F - TO_STRING(register_num u32)
Convert int register[register_num] to string and return the variable index to register[register_num]0x40 - RETURN (type u32)
Set return value to r0 (if operand is 1 string value, if operand is 0 int value)0x41 - GOTO(offset u32)
Set program counter to instruction number (not address)0x42 - BRFALSE(offset u32)
Set program counter to instruction number (not address) Condition in R00x43 - BRTRUE(offset u32)
Set program counter to instruction number (not address) Condition in R00x44 - TO_INT(register_num u32)
Same as To_String0x47 to 0x49
Not reversed It's new