/* Bra86.c -- Converter for x86 code (BCJ)
2013-11-12 : Igor Pavlov : Public domain */

#include "Precomp.h"

#include "Bra.h"

#define Test86MSByte(b) ((((b) + 1) & 0xFE) == 0)

SizeT x86_Convert(Byte* data, SizeT size, UInt32 ip, UInt32* state, int encoding)
{
    SizeT pos = 0;
    UInt32 mask = *state & 7;
    if (size < 5)
        return 0;
    size -= 4;
    ip += 5;

    for (;;)
    {
        Byte* p = data + pos;
        const Byte* limit = data + size;
        for (; p < limit; p++)
            if ((*p & 0xFE) == 0xE8)
                break;

        {
            SizeT d = (SizeT)(p - data - pos);
            pos = (SizeT)(p - data);
            if (p >= limit)
            {
                *state = (d > 2 ? 0 : mask >> (unsigned)d);
                return pos;
            }
            if (d > 2)
                mask = 0;
            else
            {
                mask >>= (unsigned)d;
                if (mask != 0 && (mask > 4 || mask == 3 || Test86MSByte(p[(mask >> 1) + 1])))
                {
                    mask = (mask >> 1) | 4;
                    pos++;
                    continue;
                }
            }
        }

        if (Test86MSByte(p[4]))
        {
            UInt32 v = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]);
            UInt32 cur = ip + (UInt32)pos;
            pos += 5;
            if (encoding)
                v += cur;
            else
                v -= cur;
            if (mask != 0)
            {
                unsigned sh = (mask & 6) << 2;
                if (Test86MSByte((Byte)(v >> sh)))
                {
                    v ^= (((UInt32)0x100 << sh) - 1);
                    if (encoding)
                        v += cur;
                    else
                        v -= cur;
                }
                mask = 0;
            }
            p[1] = (Byte)v;
            p[2] = (Byte)(v >> 8);
            p[3] = (Byte)(v >> 16);
            p[4] = (Byte)(0 - ((v >> 24) & 1));
        }
        else
        {
            mask = (mask >> 1) | 4;
            pos++;
        }
    }
}
