/* Bra86.c -- Converter for x86 code (BCJ)
2017-04-03 : 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[(size_t)(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++;
    }
  }
}
