text: ﻿/* Bcj2.c -- BCJ2 Decoder (Converter for x86 code)
  2021-02-09 : Igor Pavlov : Public domain */
  
  #include "Precomp.h"
  
  #include "Bcj2.h"
  #include "CpuArch.h"
  
  #define CProb UInt16
  
  #define kTopValue ((UInt32)1 << 24)
  #define kNumModelBits 11
  #define kBitModelTotal (1 << kNumModelBits)
  #define kNumMoveBits 5
  
  #define _IF_BIT_0 ttt = *prob; bound = (p->range >> kNumModelBits) * ttt; if (p->code < bound)
  #define _UPDATE_0 p->range = bound; *prob = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits));
  #define _UPDATE_1 p->range -= bound; p->code -= bound; *prob = (CProb)(ttt - (ttt >> kNumMoveBits));
  
  void Bcj2Dec_Init(CBcj2Dec *p)
  {
    unsigned i;
  
    p->state = BCJ2_DEC_STATE_OK;
    p->ip = 0;
    p->temp[3] = 0;
    p->range = 0;
    p->code = 0;
    for (i = 0; i < sizeof(p->probs) / sizeof(p->probs[0]); i++)
      p->probs[i] = kBitModelTotal >> 1;
  }
  
  SRes Bcj2Dec_Decode(CBcj2Dec *p)
  {
    if (p->range <= 5)
    {
      p->state = BCJ2_DEC_STATE_OK;
      for (; p->range != 5; p->range++)
      {
        if (p->range == 1 && p->code != 0)
          return SZ_ERROR_DATA;
        
        if (p->bufs[BCJ2_STREAM_RC] == p->lims[BCJ2_STREAM_RC])
        {
          p->state = BCJ2_STREAM_RC;
          return SZ_OK;
        }
  
        p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++;
      }
      
      if (p->code == 0xFFFFFFFF)
        return SZ_ERROR_DATA;
      
      p->range = 0xFFFFFFFF;
    }
    else if (p->state >= BCJ2_DEC_STATE_ORIG_0)
    {
      while (p->state <= BCJ2_DEC_STATE_ORIG_3)
      {
        Byte *dest = p->dest;
        if (dest == p->destLim)
          return SZ_OK;
        *dest = p->temp[(size_t)p->state - BCJ2_DEC_STATE_ORIG_0];
        p->state++;
        p->dest = dest + 1;
      }
    }
  
    /*
    if (BCJ2_IS_32BIT_STREAM(p->state))
    {
      const Byte *cur = p->bufs[p->state];
      if (cur == p->lims[p->state])
        return SZ_OK;
      p->bufs[p->state] = cur + 4;
      
      {
        UInt32 val;
        Byte *dest;
        SizeT rem;
        
        p->ip += 4;
        val = GetBe32(cur) - p->ip;
        dest = p->dest;
        rem = p->destLim - dest;
        if (rem < 4)
        {
          SizeT i;
          SetUi32(p->temp, val);
          for (i = 0; i < rem; i++)
            dest[i] = p->temp[i];
          p->dest = dest + rem;
          p->state = BCJ2_DEC_STATE_ORIG_0 + (unsigned)rem;
          return SZ_OK;
        }
        SetUi32(dest, val);
        p->temp[3] = (Byte)(val >> 24);
        p->dest = dest + 4;
        p->state = BCJ2_DEC_STATE_OK;
      }
    }
    */
  
    for (;;)
    {
      if (BCJ2_IS_32BIT_STREAM(p->state))
        p->state = BCJ2_DEC_STATE_OK;
      else
      {
        if (p->range < kTopValue)
        {
          if (p->bufs[BCJ2_STREAM_RC] == p->lims[BCJ2_STREAM_RC])
          {
            p->state = BCJ2_STREAM_RC;
            return SZ_OK;
          }
          p->range <<= 8;
          p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++;
        }
  
        {
          const Byte *src = p->bufs[BCJ2_STREAM_MAIN];
          const Byte *srcLim;
          Byte *dest;
          SizeT num = (SizeT)(p->lims[BCJ2_STREAM_MAIN] - src);
          
          if (num == 0)
          {
            p->state = BCJ2_STREAM_MAIN;
            return SZ_OK;
          }
          
          dest = p->dest;
          if (num > (SizeT)(p->destLim - dest))
          {
            num = (SizeT)(p->destLim - dest);
            if (num == 0)
            {
              p->state = BCJ2_DEC_STATE_ORIG;
              return SZ_OK;
            }
          }
         
          srcLim = src + num;
  
          if (p->temp[3] == 0x0F && (src[0] & 0xF0) == 0x80)
            *dest = src[0];
          else for (;;)
          {
            Byte b = *src;
            *dest = b;
            if (b != 0x0F)
            {
              if ((b & 0xFE) == 0xE8)
                break;
              dest++;
              if (++src != srcLim)
                continue;
              break;
            }
            dest++;
            if (++src == srcLim)
              break;
            if ((*src & 0xF0) != 0x80)
              continue;
            *dest = *src;
            break;
          }
          
          num = (SizeT)(src - p->bufs[BCJ2_STREAM_MAIN]);
          
          if (src == srcLim)
          {
            p->temp[3] = src[-1];
            p->bufs[BCJ2_STREAM_MAIN] = src;
            p->ip += (UInt32)num;
            p->dest += num;
            p->state =
              p->bufs[BCJ2_STREAM_MAIN] ==
              p->lims[BCJ2_STREAM_MAIN] ?
                (unsigned)BCJ2_STREAM_MAIN :
                (unsigned)BCJ2_DEC_STATE_ORIG;
            return SZ_OK;
          }
          
          {
            UInt32 bound, ttt;
            CProb *prob;
            Byte b = src[0];
            Byte prev = (Byte)(num == 0 ? p->temp[3] : src[-1]);
            
            p->temp[3] = b;
            p->bufs[BCJ2_STREAM_MAIN] = src + 1;
            num++;
            p->ip += (UInt32)num;
            p->dest += num;
            
            prob = p->probs + (unsigned)(b == 0xE8 ? 2 + (unsigned)prev : (b == 0xE9 ? 1 : 0));
            
            _IF_BIT_0
            {
              _UPDATE_0
              continue;
            }
            _UPDATE_1
              
          }
        }
      }
  
      {
        UInt32 val;
        unsigned cj = (p->temp[3] == 0xE8) ? BCJ2_STREAM_CALL : BCJ2_STREAM_JUMP;
        const Byte *cur = p->bufs[cj];
        Byte *dest;
        SizeT rem;
        
        if (cur == p->lims[cj])
        {
          p->state = cj;
          break;
        }
        
        val = GetBe32(cur);
        p->bufs[cj] = cur + 4;
  
        p->ip += 4;
        val -= p->ip;
        dest = p->dest;
        rem = (SizeT)(p->destLim - dest);
        
        if (rem < 4)
        {
          p->temp[0] = (Byte)val; if (rem > 0) dest[0] = (Byte)val; val >>= 8;
          p->temp[1] = (Byte)val; if (rem > 1) dest[1] = (Byte)val; val >>= 8;
          p->temp[2] = (Byte)val; if (rem > 2) dest[2] = (Byte)val; val >>= 8;
          p->temp[3] = (Byte)val;
          p->dest = dest + rem;
          p->state = BCJ2_DEC_STATE_ORIG_0 + (unsigned)rem;
          break;
        }
        
        SetUi32(dest, val);
        p->temp[3] = (Byte)(val >> 24);
        p->dest = dest + 4;
      }
    }
  
    if (p->range < kTopValue && p->bufs[BCJ2_STREAM_RC] != p->lims[BCJ2_STREAM_RC])
    {
      p->range <<= 8;
      p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++;
    }
  
    return SZ_OK;
  }
  