====== SOOL Engine ====== SOOL (Scenaric Object Oriented Language) is a proprietary scripting language and engine used by Mission Impossible. * [[https://hackmd.io/@shygoo/Byq2TYWkr|SOOL Interpreter partial decomp]] ---- ===== SOOL File structure ===== ==== File header ==== ^Offset^Type^Name^Description^ |0x00|char|signature[4] |"SOOL"| |0x04|u32 |fileSize |Size of the sool file.| |0x08|u16 |numSections |Number of interactor sections.| |0x0A|u8 |padding[2] |Alignment padding.| |0x0C|u32 |sectionOffsets[]|Array of interactor section offsets. The length of this array is defined by the ''numSections'' field.| |....|u32 |unknownData[] |Unknown data. Always the same size as ''sectionOffsets''.| ==== Interactor sections ==== ^Offset^Type^Name^Description^ |0x00|u16 |offsVars |Offset of the interactor's variables.| |0x02|u16 |offsVarsEnd |End offset of the interactor's variables.| |0x04|u16 |offsElements |Offset of the interactor's elements. 0xFFFF if ''numElements'' is zero.| |0x06|u16 |numElements |Number of elements.| |0x08|u16 |unk08 |Unknown - always 0xFFFF?| |0x0A|u16 |specialMethodIdx|An index of ''methodOffsets''. Unknown purpose.| |0x0C|u16 |offsMethodsEnd |End offset of the method code.| |0x0E|u16 |numMethods |Number of methods.| |0x10|u16 |methodOffsets[] |Array of method offsets. The length of this array is defined by the ''numMethods'' field.| |....|u16 |variables[] |Variable definitions. The position and length of this array are defined by the ''offsVars'' and ''offsVarsEnd'' fields.| |....|u8 |methodCode[] |Method bytecode. See commands below.| |....|u8 |garbage[] |If ''offsMethodsEnd'' is uneven, there will be one padding byte of an arbitrary value here for alignment.| |....|elem|elements[] |Element lookup table. Each entry contains two u16 fields for the key and value.| ---- ===== Commands ===== ====00: exit ==== ''00'' ASM Summary: Sets flag 1 and does not increment the command pointer. ---- ====01: unk_ret0 ==== ''01'' Unknown purpose. ASM Summary: Returns 0 in the native opcode processing function and does not increment command pointer. ---- ====02: return ==== ''02'' Return from method call: Pop offset and context ID from the stack, set the current execution context to the context specified by ''returnCtxId'' and jump to ''returnOffs''. This command is at the end of every method that would be invoked via a call_method or call_ctx_method command. Stack: ''[...] [returnCtxId] [returnOffs] -> [...]'' ---- ====03: branch_imm_cond ==== ''03 [XX XX]'' XXXX=branchOffs Pop value from the stack. If value is non-zero, branch to immediate offset. Stack: ''[...] [value] -> [...]'' ---- ====04: branch_imm ==== ''04 [XX XX]'' XXXX=branchOffs Branch to immediate offset. ---- ====05: load_imm ==== ''05 [XX XX]'' XXXX=value Push immediate value to the stack. Stack: ''[...] -> [...] [value]'' ---- ====06: load_imm__06 ==== ''06 [XX XX]'' XXXX=value Push immediate value to the stack. Stack: ''[...] -> [...] [value]'' ---- ====07: load_imm_var ==== ''07 [XX XX]'' XXXX=varOffset Push variable from immediate offset to the stack. Stack: ''[...] -> [...] [varValue]'' ---- ====08: load_imm_ctx_var ==== ''08 [XX XX] [YY YY]'' XXXX=ctxId, YYYY=varOffset Push variable from immediate offset in context to the stack. Stack: ''[...] -> [...] [varValue]'' ---- ====09: load_imm__09 ==== ''09 [XX XX]'' XXXX=value Push immediate value to the stack. Stack: ''[...] -> [...] [immValue]'' ---- ====0A: load_imm2 ==== ''0A [XX XX] [YY YY]'' XXXX=immValue1, YYYY=immValue2 Push two immediate values to the stack. Stack: ''[...] -> [...] [immValue1] [immValue2]'' ---- ====0B: load_ctx_elem ==== ''0B'' Pop element key and context id from the stack, push context's element value to the stack. Stack: ''[...] [ctxId] [elemKey] -> [...] [elemValue]'' ---- ====0C: load_ctx_elem_keep_ctx ==== ''0C'' Pop element key from the stack, load context id from top of stack (do not pop context id). Push context's element value to the stack. Stack: ''[...] [ctxId] [elemKey] -> [...] [ctxId] [elemValue]'' ---- ====0D: load_ctx_elem_var ==== ''0D'' Pop element key and context id from the stack. Using the element's value as a variable offset, push context's variable to the stack. Stack: ''[...] [ctxId] [elemKey] -> [...] [varValue]'' ---- ====0E: unk0E ==== ''0E [XXXX] [YYYY]'' Branch to YYYY if (m_UnkA4 == XXXX), else push XXXX to the stack. Stack (if branch is not taken): [...] -> [...] [XXXX] ---- ====0F: unk0F ==== ''0F'' ASM Summary: if (v2 == 0) call this->func_8006261C(v1), else do nothing. Stack: ''[...] [v1] [v2] -> [...]'' ---- ====10: set_flag_2 ==== ''10'' Unknown purpose. Set flag 2. ---- ====11: clear_flag_2 ==== ''11'' Unknown purpose. Clear flag 2. ---- ====12: unk12 ==== ''12'' Stack: ''[...] [value] -> [result]'' ASM Summary: Pop value from the stack. If (value == m_ExecCtx->GetA4()), push 1, else push 0. ---- ====13: unk13 ==== ''13'' Stack: ''[...] [value] [ctxId] -> [result]'' ASM Summary: Pop value from the stack. If (value == ctx->GetA4()), push 1, else push 0. ---- ====14: leqz ==== ''14'' Pop value from the stack. If (value <= 0), push 1, else push 0. Stack: ''[...] [value] -> [value <= 0]'' ---- ====15: cmp_eq ==== ''15'' Pop 2 values from the stack, compare (val1 == val2), push 1 if true, 0 if false. Stack: ''[...] [value2] [value1] -> [...] [value1 == value2]'' ---- ====16: cmp_neq ==== ''16'' Pop 2 values from the stack, compare (val1 != val2), push 1 if true, 0 if false. Stack: ''[...] [value2] [value1] -> [...] [value1 != value2]'' ---- ====17: lgc_and ==== ''17'' Logical and (''&&''). Pop 2 values from the stack, if both values are non-zero, push 1, else push 0. Stack: ''[...] [value2] [value1] -> [...] [value1 != 0 && value2 != 0]'' ---- ====18: lgc_or ==== ''18'' Logical or (''||''). Pop 2 values from the stack, if either value is non-zero, push 1, else push 0. Stack: ''[...] [value2] [value1] -> [...] [value1 != 0 || value2 != 0]'' ---- ====19: sub ==== ''19'' Pop two values from the stack, subtract second value from the first, push result to the stack. Stack: ''[...] [value2] [value1] -> [...] [value1 - value2]'' ---- ====1A: add ==== ''1A'' Pop two values from the stack, sum them, push result to the stack. Stack: ''[...] [value2] [value1] -> [...] [value1 + value2]'' ---- ====1B: mod ==== ''1B'' Pop two values from the stack, mod divide second value by the first, push result to the stack. Stack: ''[...] [value2] [value1] -> [...] [value2 % value1]'' ---- ====1C: mul ==== ''1C'' Pop two values from the stack, multiply them, push result to the stack. Stack: ''[...] [value2] [value1] -> [...] [value2 * value1]'' ---- ====1D: div ==== ''1D'' Pop two values from the stack, divide second value by the first, push result to the stack. Stack: ''[...] [value2] [value1] -> [...] [value2 / value1]'' ---- ====1E: cmp_lt ==== ''1E'' Pop two values from the stack, compare (val1 < val2), push 1 to the stack if true, 0 if false. Stack: ''[...] [value2] [value1] -> [...] [value1 < value2]'' ---- ====1F: cmp_gt ==== ''1F'' Pop two values from the stack, compare (val1 > val2), push 1 to the stack if true, 0 if false. Stack: ''[...] [value2] [value1] -> [...] [value1 < value2]'' ---- ====20: cmp_lte ==== ''20'' Pop two values from the stack, compare (val1 <= val2), push 1 to the stack if true, 0 if false. Stack: ''[...] [value2] [value1] -> [...] [value1 <= value2]'' ---- ====21: cmp_gte ==== ''21'' Pop two values from the stack, compare (val1 >= val2), push 1 to the stack if true, 0 if false. Stack: ''[...] [value2] [value1] -> [...] [value1 >= value2]'' ---- ====22: inv ==== ''22'' Pop value from the stack, invert value, push result to the stack. Stack: ''[...] [value] -> [...] [-value]'' ---- ====23: and ==== ''23'' Pop two values from the stack, bitwise AND the first value with the second, push result to the stack. Stack: ''[...] [value2] [value1] -> [...] [value1 & value2]'' ---- ====24: or ==== ''24'' Pop two values from the stack, bitwise OR them, push result to the stack. Stack: ''[...] [value2] [value1] -> [...] [value1 | value2]'' ---- ====25: lsh ==== ''25'' Pop two values from the stack, bitwise shift first value left by second value, push result to the stack. Stack: ''[...] [value2] [value1] -> [...] [value1 << value2]'' ---- ====26: rsh ==== ''26'' Pop two values from the stack, bitwise shift first value right by second value, push result to the stack. Stack: ''[...] [value2] [value1] -> [...] [value1 >> value2]'' ---- ====28: store_var ==== ''28'' Pop variable offset from the stack, copy value from top of stack to variable (do not pop value). Stack: ''[...] [value] [varOffset] -> [...] [value]'' ---- ====29: store_ctx_var ==== ''29'' Pop variable offset and context id from the stack, copy value from top of stack to context's variable (do not pop value). Stack: ''[...] [value] [ctxId] [varOffset] -> [...] [value]'' ---- ====2A: inc_var ==== ''2A'' Pop variable offset from the stack. Increment variable by 1. Stack: ''[...] [varOffset] -> [...]'' ---- ====2B: inc_ctx_var ==== ''2B'' Pop variable offset and context id from the stack. Increment context's variable by 1. Stack: ''[...] [ctxId] [varOffset] -> [...]'' ---- ====2C: dec_var ==== ''2C'' Pop variable offset from the stack. Decrement variable by 1. Stack: ''[...] [varOffset] -> [...]'' ---- ====2D: dec_ctx_var ==== ''2D'' Pop variable offset and context id from the stack. Decrement context's variable by 1. Stack: ''[...] [ctxId] [varOffset] -> [...]'' ---- ====2E: copy_ctx_var ==== ''2E'' Pop two variable offsets from the stack. Copy value from the first popped variable to the second. Stack: ''[...] [varOffsetDst] [varOffsetSrc] -> [...]'' ---- ====2F: add_ctx_var ==== ''2F'' Pop variable offset, context id, and value from the stack stack. Add value to context's variable. Stack: ''[...] [value] [ctxId] [varOffset] -> [...]'' ---- ====30: sub_var ==== ''30'' Pop variable offset and value from the stack. Subtract value from variable. Stack: ''[...] [value] [varOffset] -> [...]'' ---- ====31: sub_ctx_var ==== ''31'' Pop variable offset, context id, and value from the stack. Subtract value from context's variable. Stack: ''[...] [value] [ctxId] [varOffset] -> [...]'' ---- ====32: clear_flag_3 ==== ''32'' Unknown purpose. Clears internal flag 3. ---- ====34: set_flag_0_clear_flag_3 ==== ''34'' Unknown purpose. Sets internal flag 1, clears flag 3. ---- ====35: clear_ctx_flags_0_1 ==== ''35'' Unknown purpose. Pop context id from the stack and clear context's 0 and 1 flags. Stack: ''[...] [ctxId] -> [...]'' ---- ====36: set_ctx_flag_1 ==== ''36'' Unknown purpose. Pop context id from the stack and set context's 1 flag. Stack: ''[...] [ctxId] -> [...]'' ---- ====37: clear_flag_3_unk_cond ==== ''37'' Unknown purpose. Stack: ''[...] [value] -> [...]'' ASM Summary: If m_ExecCtx->unkA8 is nonzero after calling m_ExecCtx->func_8006261C(value) clear 3 flag. ---- ====38: clear_ctx_flag_3_unk_cond ==== ''38'' Unknown purpose. Stack: ''[...] [value] [ctxId] -> [...]'' ASM Summary: If ctx->unkA8 is nonzero after calling ctx->func_8006261C(value) clear context's 3 flag. ---- ====39: call_method ==== ''39'' Call local context's method: Pop method id from the stack. Push the current context ID and return offset to the stack and jump to the method's offset. The called method will pop ''returnCtxId'' and ''returnOffs'' from the stack before returning; they are not visible to the caller. Stack: ''[...] [methodId] -> [...] [returnCtxId] [returnOffs] -> [...]'' ---- ====3A: call_ctx_method ==== ''3A'' Call another context's method: Pop method ID and context ID from the stack. Push current execution context ID and return offset to the stack. Set the current execution context to the context specified by ctxId and jump to the method's offset. The called method will pop ''returnCtxId'' and ''returnOffs'' from the stack before returning; they are not visible to the caller. Stack: ''[...] [methodId] [ctxId] -> [...] [returnCtxId] [returnOffs] -> [...]'' ---- ====3B: pop_nop ==== ''3B'' Pop value from the stack and do nothing with it. ---- ====3C...4A: call_vnative==== ''3C''...''4A'' Call a native void function with ''n'' arguments on the stack where ''n'' = ''(command byte - 0x3C)''. The function to call is specified by the function ID at the top of the stack. Function IDs 0x00 through 0x98 are valid. Stack: ''[...] [argument(s)] [functionId] -> [...]'' ---- ====50...5E: call_native ==== ''50''...''5E'' Call a native function with ''n'' arguments on the stack where ''n'' = ''(command byte - 0x50)'', push the return value to the stack. The function to call is specified by a function ID at the top of the stack. Function IDs 0x00 through 0x47 are valid. Stack: ''[...] [argument(s)] [functionId] -> [...] [result]'' ---- ====Invalid opcodes ==== 27, 33, 4B, 4C, 4D, 4E, 4F, 5F...FF.