//////

#define NB_ELEM 75

class SOOLStck;

union SOOLFile
{
    struct
    {
        /*00*/ char signature[4];
        /*04*/ u32  size;
        /*08*/ u16  numSections;
        /*0A*/ u8   _pad[2];
        /*0C   u32  sectionOffsets[count]; */
    } hdr;
    u8 bytes[];
};

union SOOLSctn
{
    struct
    {
        /*00*/ u16 offsVars;
        /*02*/ u16 offsVarsEnd;
        /*04*/ u16 offsVirtualElements;
        /*06*/ u16 numVirtualElements;
        /*08*/ u16 unk08;
        /*0A*/ u16 numPublicMethods; // ? or could be an index of a particular method
        /*0C*/ u16 offsMethodsEnd; // offsMethodsEnd
        /*0E*/ u16 numMethods; // numMethods ?
        /*10   u16 methodOffsets[numMethods]; */
    } hdr;
    u8 bytes[];
};

struct SOOLElem
{
    /*00*/ u16 key;
    /*02*/ u16 value;
};

class SOOLStck
{
private:
    /*0x00*/ s16 m_Array[NB_ELEM];
    /*0x96*/ u8  _pad[2];
    /*0x98*/ s32 m_Count;
    
public:
    void Reset(void); // func_800628A0
    bool IsFull(void); // func_800628A8
    bool IsEmpty(void); // func_800628B8
    bool Push(s16 value); // func_800628C4
    s16  Pop(void); // func_80062924
    void Trim(s32 n); // func_80062980
    s16  Top(s32 i); // func_800629E0
    void Reset2(void); // func_80062A40
    void PrintValues(void); // func_80062A48
};

typedef void (*UnkC8Func)(void*);

extern SOOLCtx *func_8005A820(void *unk, s16 ctxId);
extern void io_print(const char*); // func_80057130

class SOOLCtx
{
private:
    /*00*/ u32        m_CtxId;
    /*04*/ void      *m_unk04;    // passed to unkC8
    /*08*/ SOOLStck   m_Stack;
    /*A4*/ u16        m_unkA4;    // index of m_MethodOffsets
    /*A6*/ u16        m_unkA6;    // set to m_Section->hdr.numPublicMethods;
    /*A8*/ s32        m_unkA8;
    /*AC*/ SOOLCtx   *m_ExecCtx;
    /*B0*/ s16        m_Acc;      // scratch value? // can be set to m_MethodOffsets[m_unkA4];
    /*B2*/ s16        m_unkB2;
    /*B4*/ u8         m_Flags;    // flags
    /*B5*/ u8         _pad[3];    // probably padding
    /*B8*/ SOOLSctn  *m_Section;  // pointer to variables/commands, has a header struct
    /*BC*/ u16       *m_MethodOffsets;    // &m_Section->bytes[sizeof(SOOLSctn)], table of method offsets
    /*C0*/ s32        m_unkC0;
    /*C4*/ void      *m_unkC4;
    /*C8*/ UnkC8Func  m_unkC8;   // a function pointer, receives m_unk04
    
public:
    SOOLCtx(/*a1*/u32 ctxId, /*a2*/struct SOOLFile *file, u32 sctnOffset, s32 sp10, void *sp14); // func_80061280
    void      func_800612E0(void); // func_800612E0
    s32       Step(s16 *offs); // func_80061314
    u16       GetElementValue(u16 key); // GetElementValue
    s8        GetFlags(void); // func_800625DC
    u16       GetA4(void); // func_800625F0
    void      SetA4(u16 value); // func_800625F8
    u16       GetA6(void); // func_80062604
    s32       GetA8(void); // func_8006260C
    s32       GetCtxId(void); // func_80062614
    void      func_8006261C(u16 a1); // func_8006261C
    void      CheckMethod(u16 methodId); // func_80062680
    s32       Get04(void); // func_80062718
    void      Set04(void *ptr); // func_8006272C
    void     *GetC4(void); // func_80062734
    void      CheckVariable(s16 offset); // func_8006273C
    u8       *GetSectionData(u16 offset); // func_800627DC
    void      SetC8(UnkC8Func ptr); // func_800627EC
    void      SetFlags(u8 flags, bool bSet); // func_800627F4
    void      func_80062818(void); // func_80062818
    void      func_80062854(void) // func_80062854
    void      func_80062864(void) // func_80062864
    void     *GetAccPtr() // func_80062880
    void     *GetB2Ptr() // func_80062888
    SOOLSctn *GetSection() // func_80062890
    SOOLStck *GetStack(void) // func_80062898
};

/*****************************************************************/

SOOLCtx::SOOLCtx(/*a1*/u32 ctxId, /*a2*/struct SOOLFile *file, u32 stcnOffset, s32 sp10, void *sp14) // func_80061280
{
    m_CtxId = ctxId;
    m_unk04 = NULL;
    m_unkA4 = 0xFFFF;
    m_unkA6 = 0;
    m_unkA8 = 0;
    m_ExecCtx = this;
    m_Acc = 0;
    m_unkB2 = 0;
    m_Section = (SOOLSctn*) &file.bytes[stcnOffset]; // sctnOffset = an offset from the list after file header
    m_MethodOffsets = (m_Section->hdr.numMethods != 0) ? &m_Section->bytes[sizeof(SOOLSctn)] : NULL;
    m_unkC0 = sp10;
    m_unkC4 = sp14;
    m_unkC8 = NULL;
}

void SOOLCtx::func_800612E0(void) // func_800612E0
{
    if(m_Section != NULL)
    {
        m_Section = NULL;
    }
    
    if(m_unkC8 != NULL)
    {
        m_unkC8(m_unk04);
    }
}

s32 SOOLCtx::Step(s16 *offs) // func_80061314 
{
    // j 80062518: a0->Push(a1); *offs++; return 1;
    // j 80062518: *offs++; return 1;
    // j 80062534: return 1;
    // j 80062538: return;
    // j 80061570: a0->Push(a1); *offs += 3; return 1;
    // j 80061578: *offs += 3; return 1;
    // s4 = this
    // s5 = offs (&this->unkB0)

    // huge switch with all the SOOL operations
    switch(m_Section->bytes[*offs]) // jump table at 800926D8
    {
    case 0x00: // 80061370
        SetFlags((1 << 1), true);
        return 1;
        
    case 0x01: // 80061388
        return 0;
        
    case 0x02: // 80061390
        // 02
        // pop offset from the stack and branch to it.
        // pop value from stack and ?
        struct SOOLStck *stck = GetStack();
        *offs = stck->Pop();
        m_ExecCtx = func_8005A820(GetC4(), stck->Pop());
        return 1;
        
    case 0x03: // 800613D0
        // 03 [XX XX]
        // pop value from stack. if value is nonzero, branch to XXXX
        if((GetStack()->Pop() << 16) != 0)
        {
            //goto L80061578;
            *offs += 3;
            return 1;
        }
        // fallthrough
         
    case 0x04: // 800613EC
        // 04 [XX XX]
        // branch to XXXX
        u8 *mem = &m_ExecCtx->m_Section->bytes[*offs];
        *offs = mem[1] | (mem[2] << 8);
        return 1;
    
    case 0x05: // 8006153C
    case 0x06:
    case 0x09:
        // 05 [XX XX]
        // 06 [XX XX]
        // 09 [XX XX]
        // byteswap XXXX and push to stack
        u8 *mem = &m_ExecCtx->m_Section->bytes[*offs];
        GetStack()->Push((s16)(cmd[1] | (cmd[2] << 8)));
        *offs += 3;
        return 1;
        
    case 0x07: // 80061414
        // 07 [XX XX]
        // load variable from offset XXXX, push variable to stack.
        u8 *cmd = m_ExecCtx->m_Section->bytes[*offs];
        m_ExecCtx->CheckVariable(cmd[1] | (cmd[2] << 8)); // print error message if var is out of bounds
        cmd = m_ExecCtx->m_Section->bytes[*offs];
        s16 *unk = m_ExecCtx->GetSectionData(cmd[1] | (cmd[2] << 8));
        GetStack()->Push(unk[0]);
        *offs += 3;
        return 1;
    
    case 0x08: // 80061474
        // 08 [XX XX] [YY YY]
        // XXXX=ctxId,YYYY=varOffset
        // load variable from offset YYYY in context specified by XXXX, push variable to stack
        u8 *cmd = &m_ExecCtx->m_Section.bytes[*offs];
        SOOLCtx *ctx = func_8005A820(GetC4(), cmd[1] | (cmd[2] << 8));
        cmd = &m_ExecCtx->m_Section.bytes[*offs];
        ctx->CheckVariable(cmd[3] | (cmd[4] << 8));
        cmd = &m_ExecCtx->m_Section.bytes[*offs];
        ctx = func_8005A820(GetC4(), cmd[1] | (cmd[2] << 8));
        cmd = &m_ExecCtx->m_Section.bytes[*offs];
        u16 *var = ctx->GetSectionData(cmd[3] | (cmd[4] << 8));
        GetStack()->Push(*var);
        *offs = *offs + 5;
        return 1;
        
    case 0x0A: // 80061588
        // 0A [XX XX] [YY YY]
        // push two immediate values to the stack
        SOOLStck *stck = GetStack();
        u8 *cmd = m_ExecCtx->m_Section.bytes[*offs];
        stck->Push(cmd[1] | cmd[2] << 8);
        stck = GetStack();
        cmd = m_ExecCtx.m_Section.bytes[*offs];
        stck.Push(cmd[3] | cmd[4] << 8);
        *offs = *offs + 5;
        return 1;
    
    case 0x0B: // 800615E8
        SOOLStck *stck = GetStack();
        u16 key = stck->Pop();
        u16 value = func_8005B410(GetC4(), stck->Pop(), key); // element value
        stck->Push(value);
        *offs = *offs + 1;
        return 1;

    case 0x0C: // 80061634
        SOOLStck *stck = GetStack();
        u16 key = stck->Pop(); // element key
        u16 value = func_8005B410(GetC4(), stck->Top(1), key) // element value
        stck->Push(value);
        *offs = *offs + 1;
        return 1;
        
    case 0x0D: // 80061684
        SOOLStack *stck = GetStack()
        u16 key = stck->Pop(); // element key
        u16 ctxId = stck->Pop();
        SOOLCtx *ctx = func_8005A820(GetC4(), ctxId);
        ctx->CheckVariable(func_8005B410(GetC4(), ctxId, key));
        ctx = func_8005A820(GetC4(), ctxId);
        s16 *var = ctx->GetSectionData(func_8005B410(GetC4(), ctxId, key))
        stck->Push(*var);
        *offs = *offs + 1;
        return 1;
        
    case 0x0E: // 80061734
        // 0E [XXXX] [YYYY]
        u8 *cmd = &m_ExecCtx->m_Section.bytes[*offs];
        if(m_unkA4 == cmd[1] | (cmd[2] << 8))
        {
            *offs = (cmd[3] | cmd[4] << 8)
            return 1;
        }
        cmd = &m_ExecCtx->m_Section.bytes[*offs];
        GetStack()->Push(cmd[2] << 8 | cmd[1])
        *offs = *offs + 5;
        return 1;
    
    case 0x0F: // 800617C4
        SOOLStck *stck = GetStack()

        if(stck->Pop() != 0)
        {
            this->func_8006261C(stck->Pop());
            *offs = *offs + 1;
            return 1;
        }

        stck->Pop();
        *offs = *offs + 1;
        return 1;
    
    case 0x10: // 80061810
        SetFlags(1 << 2, true);
        *offs = *offs + 1;
        return 1;

    case 0x11: break; // 80061828
        SetFlags(1 << 2, false);
        *offs = *offs + 1;
        return 1;
    
    case 0x12: // 80061840
        SOOLStck *stck = GetStack()

        if(m_ExecCtx->GetA4() == s1->Pop())
        {
            s1->Push(1);
        }
        else
        {
            s1->Push(0);
        }
        *offs = *offs + 1;
        return 1;
    
    case 0x13: // 80061880
        SOOLStck *stck = GetStack()

        if(func_8005A820(GetC4(), s1->Pop())->GetA4() == s1->Pop())
        {
            stck->Push(1);
        }
        else
        {
            stck->Push(0)
        }
        *offs = *offs + 1;
        return 1;
    
    case 0x14: // 800618E4
        SOOLStck *stck = GetStack()
        stck->Push((s32)(stck->Pop() << 16) < 1);
        *offs = *offs + 1;
        return 1;
    
    case 0x15: // 80061908
        // 15
        // pop 2 values from the stack. push 1 if values are equal, else push 0.
        SOOLStck *stck = GetStack()
        stck->Push(((stck->Pop() ^ stck->Pop()) << 16) < 1);
        *offs = *offs + 1;
        return 1;
        
    case 0x16: break; // 8006193C
        // 16
        // pop 2 values from the stack. push 1 if values are not equal, else push 0.
        SOOLStck *stck = GetStack();
        stck->Push(0 < (stck->Pop() ^ stck-Pop()) << 16)
        *offs = *offs + 1;
        return 1;
    
    case 0x17: // 80061970
        SOOLStck *stck = GetStack()

        if(stck->Pop() << 16 != 0)
        {
            if(stck->Pop() << 16 != 0)
            {
                stck->Push(1)
                *offs = *offs + 1;
                return 1;
            }
            else
            {
                stck->Push(0)
                *offs = *offs + 1;
                return 1;
            }
        }
        stck->Pop();
        stck->Push(0);
        *offs = *offs + 1;
        return 1;

    case 0x18: break; // 800619C0
        SOOLSTck *stck = GetStack()

        if(stck->Pop() << 16 != 0)
        {
            stck->Pop();
            stck->Push(1);
            *offs = *offs + 1;
            return 1;
        }
        else
        {
            if(stck->Pop() << 16 != 0)
            {
                stck->Pop();
                stck->Push(1);
                *offs = *offs + 1;
                return 1;
            }
        }
        stck->Pop();
        stck->Push(0);
        *offs = *offs + 1;
        return 1;

    case 0x19: // 80061A18
        // 19
        // pop 2 values from the stack, subtract them, push result to stack
        SOOLStck *stck = GetStack();
        stck->Push(stck->Pop() - stck->Pop());
        *acc++;
        return 1;
    
    case 0x1A: // 80061A4C
        // 1A
        // pop 2 values from the stack, sum them, push result to stack
        SOOLStck *stck = GetStack();
        stck->Push(stck->Pop() + stck->Pop());
        *acc++;
        return 1;
    
    case 0x1B: // 80061A80
        // 1B
        // pop 2 values from the stack, mod divide them, push result to stack
        SOOLStck *stck = GetStack();
        s16 temp = stck->Pop();

        if(stck->Top(1) << 16 == 0)
        {
            io_print("SOOL Engine: you have tried to divide an expression by zero.");
        }

        stck->Push(temp % stck->Pop());
        *acc++;
        return 1;
    
    case 0x1C: // 80061B10
        // 1C
        // pop 2 values from the stack, multiply them, push result to stack
        SOOLStck *stck = GetStack(); // s1
        stck->Push(stck->Pop() * stck->Pop());
        *acc++;
        return 1;

    case 0x1D: // 80061B4C
        // 1D
        // pop 2 values from the stack, divide them, push result to stack
        SOOLStck *stck = GetStack();
        s16 temp = stck->Pop();

        if(stck->Top(1) << 16 == 0)
        {
            io_print("SOOL Engine: you have tried to divide an expression by zero.");
        }

        stck->Push(temp / stck->Pop());
        *acc++;
        return 1;
    
    case 0x1E: // 80061BDC
        // 1E
        // pop 2 values from the stack, push (val1 < val2) to the stack
        SOOLStck *stck = GetStack();
        s16 val1 = stck->Pop();
        s16 val2 = stck->Pop();
        stck->Push(val1 < val2);
        *offs++;
        return 1;
    
    case 0x1F: // 80061C18
        // 1F
        // pop 2 values from the stack, push (val2 < val1) to the stack
        SOOLStck *stck = GetStack();
        s16 val1 = stck->Pop();
        s16 val2 = stck->Pop();
        stck->Push(val2 < val1);
        *offs++;
        return 1;
    
    case 0x20: // 80061C54
        // 20
        // pop 2 values from the stack, push !(val2 < val1) to the stack
        SOOLStck *stck = GetStack();
        s16 val1 = stck->Pop();
        s16 val2 = stck->Pop();
        stck->Push(!(val2 < val1));
        *offs++;
        return 1;

    case 0x21: // 80061C94
        // 21
        // pop 2 values from the stack, push !(val1 < val2) to the stack
        SOOLStck *stck = GetStack();
        s16 val1 = stck->Pop();
        s16 val2 = stck->Pop();
        stck->Push(!(val1 < val2));
        *offs++;
        return 1;
        
    case 0x22: // 80061CD4
        // 22
        // pop value from the stack, negate value, push result to stack
        SOOLStck *stck = GetStack();
        stck->Push(-stck->Pop());
        *offs++;
        return 1;
        
    case 0x23: // 80061CFC
        // 23
        // pop 2 values from the stack, bitwise AND them, push result to stack
        SOOLStck *stck = GetStack();
        stck->Push(stck->Pop() & stck->Pop());
        *offs++;
        return 1;
        
    case 0x24: // 80061D30
        // 24
        // pop 2 values from the stack, bitwise OR them, push result to stack
        SOOLStck *stck = GetStack();
        stck->Push(stck->Pop() | stck->Pop());
        *offs++;
        return 1;
    
    case 0x25: // 80061D64
        // 25
        // pop 2 values from the stack, shift first value left by second value, push result to stack
        SOOLStck *stck = GetStack();
        stck->Push(stck->Pop() << stck->Pop());
        *offs++;
        return 1;
    
    case 0x26: // 80061DA0
        // 26
        // pop 2 values from the stack, shift first value right by second value, push result to stack
        SOOLStck *stck = GetStack();
        stck->Push(stck->Pop() >> stck->Pop());
        *offs++;
        return 1;
        
    case 0x28: // 80061DD4
        // 28
        // pop variable offset from the stack, copy value from stack top to variable (do not pop value)
        SOOLStck *stck = GetStack();
        s16 varOffset = stck->Pop();
        s16 value = stck->Top(1);
        m_ExecCtx->CheckVariable(s0);
        u16 *var = m_ExecCtx->GetSectionData(varOffset);
        *var = value;
        *offs++;
        return 1;
    
    case 0x29: // 80061E20
        // 29
        // pop variable offset, context id (? unchecked), from stack
        // copy value from top of stack to context's variable (do not pop value)
        SOOLStck *stck = GetStack();
        s16 varOffset = stck->Pop();
        s16 ctxId = stck->Pop(); // ?????
        s16 value = stck->Top();
        func_8005A820(GetC4(), ctxId)->CheckVariable(varOffset);
        u16 *var = func_8005A820(GetC4(), ctxId)->GetSectionData(varOffset);
        *var = value;
        *offs++;
        return 1;
        
    case 0x2A: // 80061EA4
        // 2A
        // pop variable offset, increment variable in memory by 1
        u16 varOffset = GetStack()->Pop();
        // the code fetches the var pointer twice for some reason. could indicate macro usage.
        u16 value = *m_ExecCtx->GetSectionData(varOffset); 
        u16 *var = m_ExecCtx->GetSectionData(varOffset);
        *var = value + 1;
        *offs = *offs + 1;
        return 1;
    
    case 0x2B: // 80061EE0
        // 2B
        // pop variable offset and context id. increment context's variable by 1
        SOOLStck *stck = GetStack();
        u16 varOffset = stck->Pop();
        u16 ctxId = stck->Pop();
        func_8005A820(GetC4(), ctxId)->CheckVariable(varOffset)
        u16 *var = func_8005A820(GetC4(), ctxId)->GetSectionData(varOffset);
        *var = *var + 1;
        *offs = *offs + 1;
        return 1;
    
    case 0x2C: // 80061F5C
        // 2C
        // pop variable offset, decrement variable by 1
        u16 varOffset = GetStack()->Pop();
        u16 value = *m_ExecCtx->GetSectionData(varOffset);
        u16 *var = m_ExecCtx->GetSectionData(varOffset);
        *var = value - 1;
        *offs = *offs + 1;
        return 1;
    
    case 0x2D: // 80061F98
        // 2D
        // pop variable offset and context id from the stack, decrement context's variable by 1
        SOOLStck *stck = GetStack();
        u16 varOffset = stck->Pop();
        u16 ctxId = stck->Pop();
        func_8005A820(GetC4(), ctxId)->CheckVariable(varOffset);
        u16 *var = func_8005A820(GetC4(), ctxId)->GetSectionData(varOffset);
        *var = *var - 1;
        *offs++;
        return 1;
    
    case 0x2E: // 80062014
        // 2E
        // pop 2 variable offsets from the stack, copy first variable to the second
        SOOLStck *stck = GetStack();
        u16 *var1 = m_ExecCtx->GetSectionData(stck->Pop());
        u16 *var2 = m_ExecCtx->GetSectionData(stck->Pop());
        *var2 = *var1;
        *offs++;
        return 1;
    
    case 0x2F: // 8006205C
        // 2F
        // pop variable offset, context id, and value from stack. add value to context's variable
        SOOLStck *stck = GetStack();
        u16 varOffset = stck->Pop();
        u16 ctxId = stck->Pop();
        func_8005A820(GetC4(), ctxId)->CheckVariable(varOffset);
        u16 *var = func_8005A820(GetC4(), ctxId)->GetSectionData(varOffset);
        *var = *var + stck->Pop();
        *offs = *offs + 1;
        return 1;
        
    case 0x30: break; // 800620E4
        // 30
        // pop variable offset and value from the stack. Subtract value from variable.
        SOOLStck *stck = GetStack()
        u16 varOffset = stck->Pop();
        u16 *var = m_ExecCtx->GetSectionData(varOffset);
        u16 value = *var;
        u16 newValue = value - stck->Pop();
        var = m_ExecCtx->GetSectionData(varOffset);
        *var = newValue;
        *offs = *offs + 1;
        return 1;
        
    case 0x31: // 8006212C
        // 31
        // pop variable offset, context id, and value from the stack. Subtract value from context's variable.
        SOOLStck *stck = GetStack();
        u16 varOffset = stck->Pop();
        u16 ctxId = stck->Pop();
        func_8005A820(GetC4(), ctxId)->CheckVariable();
        u16 *var = func_8005A820(GetC4(), ctxId)->GetSectionData(varOffset);
        *var = *var - stck->Pop();
        *offs = *offs + 1;
        return 1;
    
    case 0x32: // 8006220C
        SetFlags(1 << 3, false);
        *offs = *offs + 1;
        return 1;
    
    case 0x34: // 80062224
        // 34
        // set flag 0, clear flag 3
        m_ExecCtx->SetFlags(1 << 0, true);
        m_ExecCtx->SetFlags(1 << 3, false);
        *offs = *offs + 1;
        return 1;

    case 0x35: // 800621B4
        // 35
        // pop context id. clear flags 0 and 1 from context.
        SOOLCtx *ctx = func_8005A820(GetC4(), GetStack()->Pop());
        ctx->SetFlags(1 << 0, false);
        ctx->SetFlags(1 << 0, false);
        *offs = *offs + 1;
        return 1;

    case 0x36: // 8006224C
        func_8005A820(GetC4, GetStack->Pop())->SetFlags(1, 1)
        *offs = *offs + 1;
        return 1;

    case 0x37: // 80062290
        m_ExecCtx->func_8006261C(GetStack()->Pop());
        if(m_ExecCtx->GetA8() != 0)
        {
            m_ExecCtx->SetFlags(1 << 3, false);
        }
        *offs = *offs + 1;
        return 1;
    
    case 0x38: break; // 800622D4
        SOOLStck *stck = GetStack()
        SOOLCtx *ctx = func_8005A820(GetC4(), stck->Pop())
        func_8006261C(ctx, stck->Pop())
        if(ctx->GetA8() != 0)
        {
            ctx->SetFlags(1 << 3, false)
        }
        *offs = *offs + 1;
        return 1;
        
    case 0x39: // 80062340
        SOOLStck *stck = GetStack();
        u16 idx = stck->Pop();
        m_ExecCtx->CheckMethod(idx);
        stck->Push(m_ExecCtx->GetCtxId())
        *offs = *offs + 1;
        stck->Push(*offs);
        *offs = m_ExecCtx->unkBC[idx];
        return 1;
    
    case 0x3A: break; // 800623A4
        SOOLStck *stck = GetStack()
        u16 ctxId = stck->Pop()
        u16 methodId = stck->Pop()
        stck->Push(m_ExecCtx->GetCtxId())
        *offs = *offs + 1;
        stck->Push(*offs);
        m_ExecCtx = func_8005A820(GetC4(), ctxId);
        m_ExecCtx->CheckMethod(methodId);
        *offs = m_ExecCtx->unkBC[methodId];
        return 1;
    
    case 0x3B: // 80062440
        // pop value from stack, do nothing with it
        SOOLStck *stck = GetStack();
        stck->Pop();
        *acc++;
        return 1;
    
    case 0x3C + 0: // 80062458
    case 0x3C + 1:
    case 0x3C + 2:
    case 0x3C + 3:
    case 0x3C + 4:
    case 0x3C + 5:
    case 0x3C + 6:
    case 0x3C + 7:
    case 0x3C + 8:
    case 0x3C + 9:
    case 0x3C + 10:
    case 0x3C + 11:
    case 0x3C + 12:
    case 0x3C + 13:
    case 0x3C + 14:
        // call function with (cmdbyte-0x3B) args provided in the stack?
        s32 sp10 = 0;
        struct SOOLStck *stck = GetStack();
        funcptr_8008C514(this, stck, &sp10);
        stck->Trim(m_ExecCtx->m_Section->bytes[*acc] + (sp10 + -0x3B));
        *acc++;
        return 1;
        
    case 0x27:
    case 0x33:
    case 0x4B:
    case 0x4C:
    case 0x4D:
    case 0x4E:
    case 0x4F:
        // 80062528
        io_print("SOOL Engine: Encountered an unknown operator code.");
        break;
    
    case 0x50 + 0:
    case 0x50 + 1:
    case 0x50 + 2:
    case 0x50 + 3:
    case 0x50 + 4:
    case 0x50 + 5:
    case 0x50 + 6:
    case 0x50 + 7:
    case 0x50 + 8:
    case 0x50 + 9:
    case 0x50 + 10:
    case 0x50 + 11:
    case 0x50 + 12:
    case 0x50 + 13:
    case 0x50 + 14:
        // 800624B0
        // note func_8005B55C initializes funcptr_8008C510 = func_80057140
        // call function with (cmdbyte-0x4F) args provided in the stack, push return value to stack?
        s32 sp14 = 0;
        struct SOOLStck *stck = GetStack();
        s16 retval = funcptr_8008C510(this, stck, &sp14); // always 80057140?
        stck->Trim(m_ExecCtx->m_Section->bytes[*acc] + (sp14 - 0x4F));
        stck->Push(retval);
        *acc++;
        return 1;
    }
}

u16 SOOLCtx::GetElementValue(u16 key)
{
    s32 index;

    if(m_Section->hdr.numVirtualElements != 0)
    {
        struct SOOLElem *table = (struct SOOLElem *) &m_Section->bytes[m_Section->hdr.offsVirtualElements];

        for(index = 0; index < m_Section->hdr.numVirtualElements; index++)
        {
            if(table[index].key == key)
            {
                break;
            }
        }

        if(index != m_Section->hdr.numVirtualElements)
        {
            return table[index].value;
        }
    }
    
    io_print("SOOL Engine: unable to find the virtual element in the concerned interactor.");
    return 0;
}

s8 SOOLCtx::GetFlags(void) // func_800625DC
{
    return m_Flags;
}

u16 SOOLCtx::GetA4(void) // func_800625F0
{
    return m_unkA4;
}

void SOOLCtx::SetA4(u16 value) // func_800625F8
{
    m_unkA4 = value;
    m_unkA8 = 0;
}

u16 SOOLCtx::GetA6(void) // func_80062604
{
    return m_unkA6;
}

s32 SOOLCtx::GetA8(void) // func_8006260C
{
    return 1;
}

s32 SOOLCtx::GetCtxId(void) // func_80062614
{
    return m_CtxId;
}

/* check if last command of a method is 0
 * if true, set m_unkA6 to the method id
 * if the m_unkA6's value is new, set m_unkA8 to 1
 */
void SOOLCtx::func_8006261C(u16 methodId) // func_8006261C
{
    // methodId = m_unkAC
    if(methodId >= m_Section->hdr.numMethods)
    {
        return;
    }
    
    u8 lastCmdByte;
    
    if(methodId == m_Section->hdr.numMethods - 1), 
    {
        lastCmdByte = m_Section->bytes[m_Section->hdr.offsMethodsEnd - 1];
    }
    else
    {
        lastCmdByte = m_Section->bytes[m_MethodOffsets[methodId+1] - 1];
    }
    
    if(lastCmdByte == 0x00)
    {
        if(methodId != m_unkA6)
        {
            m_unkA8 = 1;
        }
        m_unkA6 = methodId;
    }
}
void SOOLCtx::CheckMethod(u16 methodId) // func_80062680
{
    if(methodId >= m_Section->hdr.numMethods)
    {
        io_print("SOOL Engine: impossible to access a method cause of an incorrect method id.");
    }
    
    u8 *pos;
    
    if(methodId == (m_Section->hdr.numMethods - 1)) // last method
    {
        pos = &m_Section->bytes[m_Section->hdr.offsMethodsEnd];
    }
    else
    {
        pos = &m_Section->bytes[m_MethodOffsets[methodId+1]];
    }
    
    // check if last command of the method is 0x02
    if(pos[-1] != 0x02)
    {
        io_print("SOOL Engine: impossible to access a method cause of an incorrect method id.");
    }
}

s32 SOOLCtx::Get04(void) // func_80062718
{
    if(this == NULL)
    {
        return NULL;
    }
    return m_unk04;
}

void SOOLCtx::Set04(void *ptr) // func_8006272C
{
    m_unk04 = ptr;
}

void *SOOLCtx::GetC4(void) // func_80062734
{
    return m_unkC4;
}

void SOOLCtx::CheckVariable(s16 offset) // func_8006273C
{
    if(m_Section->hdr.offsVars == 0xFFFF)
    {
        io_print("SOOL Engine: the interactor doesn't know the variable you want to read or write.");
    }
    if((offset & 0xFFFF) < m_Section->hdr.offsVars)
    {
        io_print("SOOL Engine: the interactor doesn't know the variable you want to read or write.");
    }
    
    if((m_Section->hdr.offsVarsEnd == 0xFFFF && offset >= m_Section->hdr.offsMethodsEnd) ||
       offset >= m_Section->hdr.offsVarsEnd)
    {
        io_print("SOOL Engine: the interactor doesn't know the variable you want to read or write.");
    }
}

u8 *SOOLCtx::GetSectionData(u16 offset) // func_800627DC
{
    return &m_Section->bytes[offset];
}

void SOOLCtx::SetC8(UnkC8Func funcptr) // func_800627EC
{
    m_unkC8 = funcptr;
}

void SOOLCtx::SetFlags(u8 flags, bool bSet) // func_800627F4
{
    if(bSet) // set bits
    {
        m_Flags |= flags;
    }
    else // clear bits
    {
        m_Flags &= ~flags;
    }
}

void SOOLCtx::func_80062818(void) // func_80062818
{
    m_unkA6 = m_Section->hdr.numPublicMethods;
    m_unkA8 = 1;
    if (m_unkA6 == 0xFFFF)
    {
        m_unkA8 = 0;
        m_Flags = m_Flags | (1 << 1);
    }
} 

void SOOLCtx::func_80062854(void) // func_80062854
{
    m_unkB2 = m_Section->hdr.unk08;
}

void SOOLCtx::func_80062864(void) // func_80062864
{
    m_unkB0 = m_MethodOffsets[m_unkA4];
}

void *SOOLCtx::GetAccPtr() // func_80062880
{
    return &m_Acc;
}

void *SOOLCtx::GetB2Ptr() // func_80062888
{
    return &m_unkB2;
}

void* SOOLCtx::GetSection() // func_80062890
{
    return m_Section;
}

SOOLStck *SOOLCtx::GetStack(void) // func_80062898
{
    return &m_Stack;
}

/*****************************************************************/

void SOOLStck::Reset(void) // func_800628A0
{
    // maybe this is the constructor?
    m_Count = 0;
}

bool SOOLStck::IsFull(void) // func_800628A8
{
    return (m_Count ^ NB_ELEM) < 1;
}

bool SOOLStck::IsEmpty(void) // func_800628B8
{
    return m_Count < 1;
}

bool SOOLStck::Push(s16 value) // func_800628C4
{
    if(m_Count == NB_ELEM)
    {
        io_print("SOOL Engine: Scenaric stack overflow.\n (if necessary, you can increase the defined value NB_ELEM in SOOLStck.h)");
    }
    m_Array[m_Count++] = value;
    return 1;
}

s16 SOOLStck::Pop(void) // func_80062924
{
    if(m_Count <= 0)
    {
        io_print("SOOL Engine: Trying to Pop() an empty scenaric stack.");
    }
    return m_Array[--m_Count];
}

void SOOLStck::Trim(int n) // func_80062980
{
    if(m_Count <= 0)
    {
        io_print("SOOL Engine: Trying to Pop() an empty scenaric stack.");
    }
    m_Count -= n;
}

s16 SOOLStck::Top(int i) // func_800629E0
{
    if(i <= 0 || (m_Count - i) < 0)
    {
        io_print("SOOL Engine: Invalid stack::Top(i) parameter on a scenaric stack.");
    }
    return m_Array[m_Count - i];
}

void SOOLStck::Reset2(void) // func_80062A40
{
    m_Count = 0;
}

void SOOLStck::PrintValues(void) // func_80062A48
{
    // leftover debug function
    char str[unklength]; // sp10
    for(int i = 0; i < m_Count; i++)
    {
        sprintf(&str, "SOOLStack[%d]=[%d]", i, m_Array[i]);
        func_80057138(&str); // empty stub
    }
}


////////////////////////////////////////////////////////

opcode 0x50+ handler

s16 func_80057140(SOOLCtx *ctx, SOOLStck *stck, s32 *a2)
{
    switch(stck->Top(1)) // jtbl 80092180
    {
        case 0x00: return func_80057918(ctx, stck, a2); // 800571B0
        case 0x01: return func_800579C8(ctx, stck, a2); // 800571C8
        case 0x02: return func_80057A30(ctx, stck, a2); // 800571E0
        case 0x03: return func_80057854(ctx, stck, a2); // 800571F8
        case 0x04: return func_80057B00(ctx, stck, a2); // 80057210
        case 0x05: return func_80057B68(ctx, stck, a2); // 80057228
        case 0x06: return func_80057BD0(ctx, stck, a2); // 80057240
        case 0x07: return func_80057D3C(ctx, stck, a2); // 800572A0
        case 0x08: return func_80057C94(ctx, stck, a2); // 80057270
        case 0x09: return func_80057854(ctx, stck, a2); // 80057288
        case 0x0A: return func_80057DB8(ctx, stck, a2); // 800572B8
        case 0x0B: return func_80058260(ctx, stck, a2); // 800572D0
        case 0x0C: return func_8005891C(ctx, stck, a2); // 800572E8
        case 0x0D: return func_80058A10(ctx, stck, a2); // 80057300
        case 0x0E: return func_80057C38(ctx, stck, a2); // 80057258
        case 0x0F: return func_80058A88(ctx, stck, a2); // 80057318
        case 0x10: return func_80058B18(ctx, stck, a2); // 80057330
        case 0x11: return func_80058C18(ctx, stck, a2); // 80057348
        case 0x12: return func_80058C5C(ctx, stck, a2); // 80057360
        case 0x13: return func_80058E74(ctx, stck, a2); // 80057378
        case 0x14: return func_80058F44(ctx, stck, a2); // 80057390
        case 0x15: return func_80058FC4(ctx, stck, a2); // 800573A8
        case 0x16: return func_8005901C(ctx, stck, a2); // 800573C0
        case 0x17: return func_800590DC(ctx, stck, a2); // 800573D8
        case 0x18: return func_80059138(ctx, stck, a2); // 800573F0
        case 0x19: return func_800591F8(ctx, stck, a2); // 80057408
        case 0x1A: return func_800592E8(ctx, stck, a2); // 80057420
        case 0x1B: return func_80057878(ctx, stck, a2); // 80057198
        case 0x1C: return func_80059384(ctx, stck, a2); // 80057438
        case 0x1D: return func_800593A4(ctx, stck, a2); // 80057450
        case 0x1E: return func_80059404(ctx, stck, a2); // 80057468
        case 0x1F: return func_80059458(ctx, stck, a2); // 80057480
        case 0x20: return func_80059478(ctx, stck, a2); // 80057498
        case 0x21: return func_80059560(ctx, stck, a2); // 800574B0
        case 0x22: return func_800596B8(ctx, stck, a2); // 800574C8
        case 0x23: return func_800596EC(ctx, stck, a2); // 800574E0
        case 0x24: return func_80057898(ctx, stck, a2); // 800574F8
        case 0x25: return func_800578B8(ctx, stck, a2); // 80057510
        case 0x26: return func_80059770(ctx, stck, a2); // 80057528
        case 0x27: return func_800598D8(ctx, stck, a2); // 80057540
        case 0x28: return func_800578D8(ctx, stck, a2); // 80057558
        case 0x29: return func_800578F8(ctx, stck, a2); // 80057570
        case 0x2A: return func_8005990C(ctx, stck, a2); // 80057588
        case 0x2B: return func_80059988(ctx, stck, a2); // 800575A0
        case 0x2C: return func_800599A8(ctx, stck, a2); // 800575B8
        case 0x2D: return func_80059A08(ctx, stck, a2); // 800575D0
        case 0x2E: return func_80059A84(ctx, stck, a2); // 800575E8
        case 0x2F: return func_80059AA4(ctx, stck, a2); // 80057600
        case 0x30: return func_80058C3C(ctx, stck, a2); // 80057618
        case 0x31: return func_80059AC8(ctx, stck, a2); // 80057630
        case 0x32: return func_80059AEC(ctx, stck, a2); // 80057648
        case 0x33: return func_80059B2C(ctx, stck, a2); // 80057660
        case 0x34: return func_80059B80(ctx, stck, a2); // 80057678
        case 0x35: return func_80059BF0(ctx, stck, a2); // 80057690
        case 0x36: return func_80059C50(ctx, stck, a2); // 800576A8
        case 0x37: return func_80059CAC(ctx, stck, a2); // 800576C0
        case 0x38: return func_80059CCC(ctx, stck, a2); // 800576D8
        case 0x39: return func_80059CF0(ctx, stck, a2); // 800576F0
        case 0x3A: return func_80059D50(ctx, stck, a2); // 80057708
        case 0x3B: return func_80059DB0(ctx, stck, a2); // 80057720
        case 0x3C: return func_80059E10(ctx, stck, a2); // 80057738
        case 0x3D: return func_80059E8C(ctx, stck, a2); // 80057750
        case 0x3E: return func_80059EEC(ctx, stck, a2); // 80057768
        case 0x3F: return func_80059F20(ctx, stck, a2); // 80057780
        case 0x40: return func_80059198(ctx, stck, a2); // 80057798
        case 0x41: return func_80059F80(ctx, stck, a2); // 800577B0
        case 0x42: return func_8005A020(ctx, stck, a2); // 800577C8
        case 0x43: return func_8005A074(ctx, stck, a2); // 800577E0
        case 0x44: return func_8005A094(ctx, stck, a2); // 800577F8
        case 0x45: return func_8005A0B4(ctx, stck, a2); // 80057810
        case 0x46: return func_80058598(ctx, stck, a2); // 80057828
        case 0x47: return func_8005A0D8(ctx, stck, a2); // 80057840
    }
}

// 0x1B
s16 func_80057878(SOOLCtx *ctx, SOOLStck *stck, s32 *a2)
{
    return func_8003D0F4();
}

// 0x24
s16 func_80057898(SOOLCtx *ctx, SOOLStck *stck, s32 *a2) 
{
    return func_8003C6FC();
}

// 0x25
s16 func_800578B8(SOOLCtx *ctx, SOOLStck *stck, s32 *a2)
{
    return func_8003C580();
}

// 0x28
s16 func_800578D8(SOOLCtx *ctx, SOOLStck *stck, s32 *a2)
{
    return func_8003C5D4();
}

// 0x29
s16 func_800578F8(SOOLCtx *ctx, SOOLStck *stck, s32 *a2)
{
    return func_8003C628();
}

// 0x00
s16 func_80057918(SOOLCtx *ctx, SOOLStck *stck, s32 *a2)
{
    // func_80082E90 = rand16

    // Stack: [...] [max] [min] [code]

    s16 res = stck->Top(2) + func_80082E90() % ((stck->Top(3) - stck->Top(2)) + 1);

    if(res < 0)
    {
        res = -res;
    }

    return res;
}

/*



3C...4A: call_vnative
    3C...4A
    Call a native function with n arguments provided on the stack where n = (command byte - 0x3C).
    The function to call is determined by a 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 provided on the stack where n = (command byte - 0x50), push the return value to the stack.
   The function to call is determined by a function ID at the top of the stack. Function IDs 0x00 through 0x47 are valid.

   Stack: [...] [argument(s)] [functionId] -> [...] [result]

---------------

00: rand(min, max)
    Return random number between min and max (inclusive).
    Stack: [...] [max] [min] [0x00] -> [...] [result]

*/



////////////////////////////////////////////////////////


SOOLCtx *func_8005A820(a0 /*unkc4*/, s32 ctxId)
{
    s1 = a0 + 0x24

    s32 index;

    for(index = 0; index < func_80056F14(s1); index++)
    {
        if(func_80056EEC(s1, index) == NULL)
            continue;

        if(func_80056EEC(s1, index)->GetCtxId() == ctxId)
            break;
    }

    if(index == func_80056F14(s1))
    {
        func_80057130("SOOL Engine: impossible to access an interactor with an incorrect interactor number.");
    }

    return func_80056EEC(s1, index);
}


SOOLCtx *func_80056EEC(a0, index)
{
    return a0->unk04[index];
}

void func_80056F00(a0, index, a2)
{
    a0->unk04[index] = a2;
}

func_80056F14(a0)
{
    return a0->unk00; // array count
}

func_8005F1C(a0)
{
    a0->unk00 = 0;
    a0->unk04 = NULL;
}



...