/*
 * Softcam plugin to VDR (C++)
 *
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 */

// -- cAuxSrv ------------------------------------------------------------------

#define HAS_AUXSRV

static int auxPort=7777;
static char auxAddr[80]="localhost";
static char auxPassword[250]="auxserver";
static int auxUseEmu = 1;

#include "map57.h"

class cAuxSrv : public cMutex
{
private:
  cNetSocket so;
  //
  bool Login(void);
public:
  cAuxSrv(void);
  bool Map(int map, unsigned char *data, int len, int outlen);
};

cAuxSrv::cAuxSrv(void)
:so(DEFAULT_CONNECT_TIMEOUT,7,DEFAULT_IDLE_TIMEOUT)
{}

bool cAuxSrv::Login()
{
  unsigned char buff[256];
  dn(printf("auxsrv: connecting to %s:%d\n",auxAddr,auxPort))
  if(so.Connect(auxAddr,auxPort)) {
    buff[0]=0xA7;
    buff[1]=0x7A;
    buff[2]=0;
    int l=strlen(auxPassword);
    buff[3]=l;
    memcpy(&buff[4],auxPassword,l);
    buff[4+l]=0xFF;
    if(so.Write(buff,l+5)==l+5) return true;
    dn(printf("auxsrv: login write failed\n"))
    }
  so.Disconnect();
  return false;
}

bool cAuxSrv::Map(int map, unsigned char *data, int len, int outlen)
{
  if(len>500 || outlen>500) return false;
  cMutexLock lock(this);
  if(!so.Connected() && !Login()) return false;
  dn(printf("auxsrv: calling map%02x\n",map))
  unsigned char buff[512];
  buff[0]=0xA7;
  buff[1]=0x7A;
  buff[2]=((len+1)>>8) & 0xff;
  buff[3]=(len+1)&0xff;
  buff[4]=map;
  memcpy(&buff[5],data,len);
  buff[len+5]=0xFF;
  if(so.Write(buff,len+6)==len+6) {
    if((len=so.Read(buff,sizeof(buff)))>0) {
      if(buff[0]==0x7A && buff[1]==0xA7) {
        if(buff[4]==0x00) {
          int l=buff[2]*256+buff[3];
          if(len>=l+5 && l==outlen+1) {
            if(buff[l+4]==0xFF) {
              memcpy(data,buff+5,outlen);
              return true;
              }
            else dn(printf("auxsrv: bad footer in map%02x response\n",map))
            }
          else dn(printf("auxsrv: bad length in map%02x response (got=%d want=%d)\n",map,l-1,outlen))
          }
        else dn(printf("auxsrv: map%02x not successfull (unsupported?)\n",map))
        }
      else dn(printf("auxsrv: bad response to map%02x\n",map))
      }
    else dn(printf("auxsrv: map%02x read failed\n",map))
    }
  else  dn(printf("auxsrv: map%02x write failed\n",map))
  so.Disconnect();
  return false;
}

// -- cN2Map0101 ----------------------------------------------------------------

class cN2Map0101 {
private:
  cBN keymulinv, keybig, ukey, reinput;
  cBN ushift;
  cBN sum, num1, num2;
  cBNctx ctx;
void mod_add(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4);
void bn_cmplx1(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3,
          BIGNUM *arg4, BIGNUM *arg5);
void bn_cmplx1a(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3,
          BIGNUM *arg4, BIGNUM *arg5);
void mod_sub(void);
void bn_func1(BIGNUM *arg0);
void bn_func2(int arg0);
void bn_func3(int arg0);
void bn_cmplx7(void);
void bn_cmplx2(BIGNUM *var1, BIGNUM *var2, BIGNUM *var3,
          BIGNUM *var4, BIGNUM *var5, BIGNUM *var6);
BIGNUM *bn_glb0, *bn_glb1, *bn_glb3, *bn_glb5, *bn_glb6, *bn_glb7;
BIGNUM *bn_glb_a, *bn_glb_b, *bn_glb_c, *bn_glb_d, *bn_glb_e, *bn_glb_f, *bn_glb_g;
BIGNUM *bn_glb_h, *bn_glb_i, *bn_glb_j, *bn_glb_k, *bn_glb_l, *bn_glb_m;
BIGNUM *glb2pow128, *mask128, *glb2pow64, *mask64;
BN_CTX *t1;
protected:
  void Map3b(unsigned char *data);
  void Map57(unsigned char *data);
public:
  cN2Map0101(void);
  void DoMap(int f, unsigned char *data, int l);
  };

cN2Map0101::cN2Map0101(void)
{
  BN_set_bit(ushift,128);
}

void cN2Map0101::mod_add(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4)
{
  BN_add(arg1, arg2, arg3);
  if(BN_cmp(arg1, arg4) >= 0) {
    BN_sub(arg1, arg1, arg4);
  }
  BN_mask_bits(arg1, 128);
}

void cN2Map0101::bn_cmplx1(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3,
          BIGNUM *arg4, BIGNUM *arg5)
{
  int j;
  BIGNUM *var44, *var64, *var84, *vara4;
  var44 = BN_new();
  var64 = BN_new();
  var84 = BN_new();
  vara4 = BN_new();
  BN_copy(var44, arg2);
  BN_copy(var64, arg3);
  BN_clear(vara4);
  for(j=0; j<2; j++) {
    BN_copy(var84, var64);
    BN_mask_bits(var84, 64);
    BN_rshift(var64, var64, 64);
    BN_mul(var84, var84, var44, t1);
    BN_add(vara4, vara4, var84);
    BN_copy(var84, vara4);
    BN_mask_bits(var84, 128);
    BN_mul(var84, vara4, arg4, t1);
    BN_mask_bits(var84, 64);
    BN_mul(var84, var84, arg5, t1);
    BN_add(vara4, vara4, var84);
    BN_rshift(vara4, vara4, 64);
    if(BN_cmp(vara4, arg5) >= 0) {
      BN_sub(vara4, vara4, arg5);
    }
    BN_mask_bits(vara4, 128);
  }
  BN_copy(arg1, vara4);
  BN_free(var44);
  BN_free(var64);
  BN_free(var84);
  BN_free(vara4);
}

void cN2Map0101::bn_cmplx1a(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3,
          BIGNUM *arg4, BIGNUM *arg5)
{
  int j;
  BIGNUM *var44, *var64, *var84, *vara4;
  var44 = BN_new();
  var64 = BN_new();
  var84 = BN_new();
  vara4 = BN_new();
  BN_copy(var44, arg2);
  BN_copy(var64, arg3);
  BN_clear(vara4);
  for(j=0; j<2; j++) {
    BN_copy(var84, var64);
    BN_mask_bits(var84, 64);
    BN_rshift(var64, var64, 64);
    BN_mul(var84, var84, var44, t1);
    BN_add(vara4, vara4, var84);
    BN_copy(var84, vara4);
    BN_mask_bits(var84, 128);
    BN_mul(var84, vara4, arg4, t1);
    BN_mask_bits(var84, 64);
    BN_mul(var84, var84, arg5, t1);
    BN_add(vara4, vara4, var84);
    BN_rshift(vara4, vara4, 64);
    if(j==0 && BN_cmp(vara4, arg5) >= 0) {
      BN_sub(vara4, vara4, arg5);
    }
    BN_mask_bits(vara4, 128);
  }
  BN_copy(arg1, vara4);
  BN_free(var44);
  BN_free(var64);
  BN_free(var84);
  BN_free(vara4);
}

//uses 3, 1, glb2pow128
//sets 1, 0 (unused)
void cN2Map0101::mod_sub()
{
  BN_copy(bn_glb0, bn_glb3);
  BN_mod_sub(bn_glb1, bn_glb3, bn_glb1, glb2pow128, t1);
  BN_mask_bits(bn_glb1, 128);
}

//uses 1, 3, 6
//sets  1, 0 (unused), 7(unused)
void cN2Map0101::bn_func1(BIGNUM *arg0) {
  BIGNUM *var30 = BN_new();
  BIGNUM *var50 = BN_new();
  BN_copy(var30,arg0);
  BN_mask_bits(var30, 8);
  unsigned int x = BN_get_word(var30);
  BN_copy(var30,arg0);
  if( x != 0) {
    BN_clear(var50);
    BN_set_word(var50, 2);
    BN_sub(var30, var30, var50);
  } else {
    BN_clear(var50);
    BN_set_word(var50, 0xfe);
    BN_add(var30, var30, var50);
  }
  BN_copy(bn_glb7, bn_glb1);
  if(BN_is_zero(arg0)) {
    BN_clear(bn_glb7);
    BN_set_word(bn_glb7, 1);
    BN_clear(bn_glb0);

    mod_add(bn_glb1, bn_glb7, bn_glb0, bn_glb3);
    BN_free(var30);
    BN_free(var50);
    return;
  } else {
    int msb = BN_num_bits(var30) -1;
    while (msb > 0) {

      bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
      msb--;
      if(BN_is_bit_set(var30, msb)) {

        bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
      }
    }
    BN_clear(bn_glb7);
    BN_set_word(bn_glb7, 1);

    bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
    BN_clear(bn_glb0);
  }
  BN_free(var30);
  BN_free(var50);
}

//uses 3, 6, a, b, c, l, glb2pow128
//sets 0, 1, 5, 7, a, b, c, f, g
void cN2Map0101::bn_func2(int arg0)
{
  BN_copy(bn_glb1, bn_glb_b);

  mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
  BN_copy(bn_glb7, bn_glb1);
  BN_copy(bn_glb5, bn_glb_c);
  BN_mask_bits(bn_glb1, 128);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  BN_copy(bn_glb_g, bn_glb1);
  BN_copy(bn_glb1, bn_glb7);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
  BN_copy(bn_glb7, bn_glb_a);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
  mod_sub();
  BN_copy(bn_glb_f, bn_glb1);
  BN_copy(bn_glb1, bn_glb7);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
  BN_copy(bn_glb7, bn_glb1);

  mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);

  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
  BN_copy(bn_glb7, bn_glb1);
  BN_copy(bn_glb1, bn_glb_c);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
  BN_copy(bn_glb5, bn_glb_l);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);

  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
  BN_copy(bn_glb7, bn_glb1);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
  BN_copy(bn_glb0, bn_glb_f);

  mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
  mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
  if(arg0 == 0) {
    BN_copy(bn_glb_a, bn_glb1);
  } else {
    BN_copy(bn_glb_f, bn_glb1);
  }

  mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
  BN_copy(bn_glb7, bn_glb1);
  BN_copy(bn_glb1, bn_glb_b);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);

  mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);

  mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);

  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
  mod_sub();
  if(arg0 == 0) {
    BN_copy(bn_glb_b, bn_glb1);
    BN_copy(bn_glb_c, bn_glb_g);
  } else {
    BN_copy(bn_glb_f, bn_glb1);
    BN_copy(bn_glb_f, bn_glb_g);
  }
}

//uses 3, 6, a, b, c, d, e, k
//sets 0, 1, 5, 7, a, b, c, f, g, h, i, j
void cN2Map0101::bn_func3(int arg0)
{
  BN_copy(bn_glb1, bn_glb_c);
  BN_copy(bn_glb7, bn_glb1);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);

  bn_cmplx1(bn_glb0, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
  BN_copy(bn_glb_f, bn_glb0);
  BN_copy(bn_glb5, bn_glb_d);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  BN_copy(bn_glb7, bn_glb1);
  mod_sub();
  BN_copy(bn_glb0, bn_glb_a);

  mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
  BN_copy(bn_glb_g, bn_glb1);
  BN_copy(bn_glb5, bn_glb_c);

  bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  if(arg0 == 0) {
    BN_copy(bn_glb_c, bn_glb0);
  } else {
    BN_copy(bn_glb_g, bn_glb0);
  }

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
  BN_copy(bn_glb_h, bn_glb1);
  BN_copy(bn_glb0, bn_glb_a);

  mod_add(bn_glb0, bn_glb0, bn_glb7, bn_glb3);
  BN_copy(bn_glb7, bn_glb0);

  //NOTE: don't 'mod' bn_glb1, but DO 'mod' glb_i
  bn_cmplx1(bn_glb7, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
  bn_cmplx1a(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
  BN_copy(bn_glb_i, bn_glb7);
  BN_copy(bn_glb1, bn_glb_e);
  BN_copy(bn_glb5, bn_glb_f);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  BN_copy(bn_glb_f, bn_glb1);
  mod_sub();
  BN_copy(bn_glb0, bn_glb_b);

  mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
  BN_copy(bn_glb_j, bn_glb1);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
  BN_copy(bn_glb0, bn_glb1);
  BN_copy(bn_glb1, bn_glb7);
  BN_copy(bn_glb7, bn_glb0);
  mod_sub();

  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
  if(arg0 == 0) {
    BN_copy(bn_glb_a, bn_glb1);
  } else {
    BN_copy(bn_glb_f, bn_glb1);
  }

  mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
  mod_sub();
  BN_copy(bn_glb7, bn_glb_i);

  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
  BN_copy(bn_glb5, bn_glb_j);

  bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  BN_copy(bn_glb1, bn_glb_f);
  BN_copy(bn_glb_f, bn_glb0);
  BN_copy(bn_glb7, bn_glb_b);

  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
  BN_copy(bn_glb7, bn_glb1);
  BN_copy(bn_glb1, bn_glb_g);
  BN_copy(bn_glb5, bn_glb_h);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
  mod_sub();
  BN_copy(bn_glb7, bn_glb_f);

  mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
  BN_copy(bn_glb5, bn_glb_k);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  if(arg0 == 0) {
    BN_copy(bn_glb_b, bn_glb1);
  } else {
    BN_copy(bn_glb_f, bn_glb1);
  }
}

//uses c, d, e, m
//sets 0, a, b, c
void cN2Map0101::bn_cmplx7()
{
  BIGNUM *var1;
  var1 = BN_new();
  BN_copy(bn_glb0, bn_glb_c);
  if(BN_is_zero(bn_glb_c)) {
    BN_copy(bn_glb_a, bn_glb_d);
    BN_copy(bn_glb_b, bn_glb_e);
    BN_copy(bn_glb_c, bn_glb_m);
    bn_func3(1);
  } else {
    BN_clear(var1);
    BN_set_word(var1, 0xFFFFFFFF);
    BN_mask_bits(bn_glb_a, 32);
    BN_lshift(var1, bn_glb_m, 0x20);
    BN_add(bn_glb_a, bn_glb_a, var1);
    BN_mask_bits(bn_glb_a, 128);
    bn_func3(0);
  }
  BN_free(var1);
}

void cN2Map0101::bn_cmplx2(BIGNUM *var1, BIGNUM *var2, BIGNUM *var3,
          BIGNUM *var4, BIGNUM *var5, BIGNUM *var6) {
  BIGNUM *var48;
  int len = BN_num_bits(var6);
  int i;
  if(len < 2)
    return;

  if(BN_is_zero(var2) && BN_is_zero(var3) && BN_is_zero(var4))
    return;
  var48 = BN_new();
  BN_copy(bn_glb3, var1);

  BN_copy(bn_glb6, bn_glb3);
  BN_set_bit(bn_glb6, 0);
  BN_sub(bn_glb6, glb2pow128, bn_glb6);
  BN_mod_inverse(bn_glb6, bn_glb6, glb2pow64, t1);
  BN_clear(bn_glb0);
  //
  if(! BN_is_zero(bn_glb3)) {
    BN_clear(bn_glb1);
    BN_set_word(bn_glb1, 2);
    BN_clear(bn_glb_k);
    BN_set_word(bn_glb_k, 0x88);
    BN_mod_exp(bn_glb1, bn_glb1, bn_glb_k, bn_glb3, t1);
  }
  //
  for(i=0; i < 4; i++) {

    bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
  }
  //
  BN_clear(bn_glb7);
  BN_set_word(bn_glb7, 1);
  BN_add(bn_glb0, bn_glb3, bn_glb7);
  BN_copy(bn_glb_k, bn_glb0);
  BN_rshift(bn_glb_k, bn_glb_k, 1);
  BN_copy(bn_glb7, bn_glb1);
  BN_copy(bn_glb5, bn_glb_k);
  BN_mask_bits(bn_glb5, 128);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  BN_copy(bn_glb_k, bn_glb1);

  BN_copy(bn_glb1, var5);
  BN_mask_bits(bn_glb1, 128);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
  BN_copy(bn_glb_l, bn_glb1);
  BN_copy(bn_glb1, bn_glb7);
  BN_clear(bn_glb5);
  BN_set_word(bn_glb5, 1);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  BN_copy(bn_glb_c, bn_glb1);
  BN_copy(bn_glb_m, bn_glb1);
  BN_copy(bn_glb1, bn_glb7);

  BN_copy(bn_glb5, var2);
  BN_mask_bits(bn_glb5, 128);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  BN_copy(bn_glb_a, bn_glb1);
  BN_copy(bn_glb1, bn_glb7);

  BN_copy(bn_glb5, var3);
  BN_mask_bits(bn_glb5, 128);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  BN_copy(bn_glb_b, bn_glb1);
  BN_copy(bn_glb_d, bn_glb_a);
  BN_copy(bn_glb_e, bn_glb_b);

  int x = len -1;
  while(x > 0) {
    x--;
    bn_func2(0);
    if(BN_is_bit_set(var6, x)) {
      bn_cmplx7();
    }
  }

  BN_copy(bn_glb1, bn_glb_c);
  BN_mask_bits(bn_glb1, 128);
  BN_copy(bn_glb7, bn_glb1);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
  BN_clear(bn_glb7);
  BN_set_word(bn_glb7, 1);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
  BN_copy(bn_glb0, bn_glb1);
  BN_clear(bn_glb7);
  BN_set_word(bn_glb7, 1);
  BN_copy(bn_glb1, bn_glb0);
  BN_clear(bn_glb0);
  bn_func1(var1);
  BN_copy(bn_glb5, bn_glb_b);
  BN_mask_bits(bn_glb5, 128);

  bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);

  BN_copy(bn_glb7, bn_glb0);
  BN_copy(bn_glb5, bn_glb_c);
  BN_mask_bits(bn_glb5, 128);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);

  BN_copy(bn_glb5, bn_glb_a);
  BN_mask_bits(bn_glb5, 128);

  bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);

  BN_clear(bn_glb5);
  BN_set_word(bn_glb5, 1);

  bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  BN_copy(bn_glb_a, bn_glb0);
  BN_copy(bn_glb1, bn_glb7);

  BN_clear(bn_glb5);
  BN_set_word(bn_glb5, 1);

  bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
  BN_copy(bn_glb_b, bn_glb0);
  BN_free(var48);
}
 
void cN2Map0101::Map57(unsigned char *data) {
  BIGNUM *var38, *var58, *var78, *var98, *varb8, *vard8;
  BN_CTX *t;
  unsigned char tmpdata[256];
  unsigned char res[256];

  t = BN_CTX_new();
  t1 = BN_CTX_new();
  BN_CTX_init(t);

  glb2pow128 = BN_new();
  BN_clear(glb2pow128);
  BN_set_bit(glb2pow128, 128);
  mask128 = BN_new();
  BN_hex2bn(&mask128, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");

  glb2pow64 = BN_new();
  BN_clear(glb2pow64);
  BN_set_bit(glb2pow64, 64);
  mask64 = BN_new();
  BN_hex2bn(&mask64, "FFFFFFFFFFFFFFFF");
  
  bn_glb0=BN_new(); BN_clear(bn_glb0);
  bn_glb1=BN_new(); BN_clear(bn_glb1);
  bn_glb3=BN_new(); BN_clear(bn_glb3);
  bn_glb5=BN_new(); BN_clear(bn_glb5);
  bn_glb6=BN_new(); BN_clear(bn_glb6);
  bn_glb7=BN_new(); BN_clear(bn_glb7);

  bn_glb_a=BN_new(); BN_clear(bn_glb_a);
  bn_glb_b=BN_new(); BN_clear(bn_glb_b);
  bn_glb_c=BN_new(); BN_clear(bn_glb_c);
  bn_glb_d=BN_new(); BN_clear(bn_glb_d);
  bn_glb_e=BN_new(); BN_clear(bn_glb_e);
  bn_glb_f=BN_new(); BN_clear(bn_glb_f);
  bn_glb_g=BN_new(); BN_clear(bn_glb_g);
  bn_glb_h=BN_new(); BN_clear(bn_glb_h);
  bn_glb_i=BN_new(); BN_clear(bn_glb_i);
  bn_glb_j=BN_new(); BN_clear(bn_glb_j);
  bn_glb_k=BN_new(); BN_clear(bn_glb_k);
  bn_glb_l=BN_new(); BN_clear(bn_glb_l);
  bn_glb_m=BN_new(); BN_clear(bn_glb_m);

  var38=BN_new(); BN_clear(var38);
  var58=BN_new(); BN_clear(var58);
  var78=BN_new(); BN_clear(var78);
  var98=BN_new(); BN_clear(var98);
  varb8=BN_new(); BN_clear(varb8);
  vard8=BN_new(); BN_clear(vard8);

  memcpy(tmpdata, data, 0x80);
  RotateBytes(tmpdata, 0x80);
  BN_bin2bn(tmpdata, 16, var78);
  BN_bin2bn(tmpdata+0x10, 16, varb8);
  BN_bin2bn(tmpdata+0x20, 16, var98);
  BN_bin2bn(tmpdata+0x40, 16, vard8);
  BN_bin2bn(tmpdata+0x60, 16, var38);
  BN_bin2bn(tmpdata+0x70, 16, var58);

  bn_cmplx2(varb8, var58, vard8, var38, var78, var98);

  memset(res, 0, 0x80);
  unsigned int *dest = (unsigned int *)res, *src = (unsigned int *)data;
  *dest++ = src[0x03];
  *dest++ = src[0x02];
  *dest++ = src[0x01];
  *dest++ = src[0x00];
  *dest++ = src[0x07];
  *dest++ = src[0x06];
  *dest++ = src[0x05];
  *dest++ = src[0x04];

  memset(tmpdata, 0, 0x20);
  int len = BN_bn2bin(bn_glb_a, tmpdata);
  if(len) {
    RotateBytes(tmpdata, len);
  }
  src = (unsigned int *)tmpdata;
  *dest++ = src[0x03];
  *dest++ = src[0x02];
  *dest++ = src[0x01];
  *dest++ = src[0x00];

  memset(tmpdata, 0, 0x20);
  len = BN_bn2bin(bn_glb_m, tmpdata);
  if(len) {
    RotateBytes(tmpdata, len);
  }
  *dest = src[0x03];
  dest+=4;

  memset(tmpdata, 0, 0x20);
  len = BN_bn2bin(bn_glb_b, tmpdata);
  if(len) {
    RotateBytes(tmpdata, len);
  }
  *dest++ = src[0x03];
  *dest++ = src[0x02];
  *dest++ = src[0x01];
  *dest++ = src[0x00];

  dest+=4;
  src = (unsigned int *)(data+0x60);
  *dest++ = src[0x03];
  *dest++ = src[0x02];
  *dest++ = src[0x01];
  *dest++ = src[0x00];
  *dest++ = src[0x07];
  *dest++ = src[0x06];
  *dest++ = src[0x05];
  *dest++ = src[0x04];

  *(unsigned int *)(data + (8<<2))= *(unsigned int *)(res + (11<<2));
  *(unsigned int *)(data + (9<<2))= *(unsigned int *)(res + (10<<2));
  *(unsigned int *)(data + (10<<2))= *(unsigned int *)(res + (9<<2));
  *(unsigned int *)(data + (11<<2))= *(unsigned int *)(res + (8<<2));
  *(unsigned int *)(data + (12<<2))= *(unsigned int *)(res + (12<<2));
  *(unsigned int *)(data + (13<<2))= *(unsigned int *)(res + (13<<2));
  *(unsigned int *)(data + (14<<2))= *(unsigned int *)(res + (14<<2));
  *(unsigned int *)(data + (15<<2))= *(unsigned int *)(res + (15<<2));
  *(unsigned int *)(data + (16<<2))= *(unsigned int *)(res + (19<<2));
  *(unsigned int *)(data + (17<<2))= *(unsigned int *)(res + (18<<2));
  *(unsigned int *)(data + (18<<2))= *(unsigned int *)(res + (17<<2));
  *(unsigned int *)(data + (19<<2))= *(unsigned int *)(res + (16<<2));
  *(unsigned int *)(data + (20<<2))= *(unsigned int *)(res + (20<<2));
  *(unsigned int *)(data + (21<<2))= *(unsigned int *)(res + (21<<2));
  *(unsigned int *)(data + (22<<2))= *(unsigned int *)(res + (22<<2));
  *(unsigned int *)(data + (23<<2))= *(unsigned int *)(res + (23<<2));

  BN_free(glb2pow128);
  BN_free(mask128);
  BN_free(glb2pow64);
  BN_free(mask64);
  
  BN_free(bn_glb0);
  BN_free(bn_glb1);
  BN_free(bn_glb3);
  BN_free(bn_glb5);
  BN_free(bn_glb6);
  BN_free(bn_glb7);

  BN_free(bn_glb_a);
  BN_free(bn_glb_b);
  BN_free(bn_glb_c);
  BN_free(bn_glb_d);
  BN_free(bn_glb_e);
  BN_free(bn_glb_f);
  BN_free(bn_glb_g);
  BN_free(bn_glb_h);
  BN_free(bn_glb_i);
  BN_free(bn_glb_j);
  BN_free(bn_glb_k);
  BN_free(bn_glb_l);
  BN_free(bn_glb_m);

  BN_free(var38);
  BN_free(var58);
  BN_free(var78);
  BN_free(var98);
  BN_free(varb8);
  BN_free(vard8);

  BN_CTX_free(t);
  BN_CTX_free(t1);
}

//
// Map3b unveiled (by Jagaer)
//

void cN2Map0101::Map3b(unsigned char *data)
{
  unsigned char tmpdat[24];
  RotateBytes(tmpdat,data   ,24); BN_bin2bn(tmpdat,24,keybig);
  RotateBytes(tmpdat,data+24,16); BN_bin2bn(tmpdat,16,ukey);

  BN_zero(num1);
  BN_sub(keymulinv,num1,ukey);
  BN_set_bit(num1,64);
  BN_mod_inverse(keymulinv,keymulinv,num1,ctx);

  BN_set_word(num1,2);
  BN_set_word(num2,132);
  BN_mod_exp(reinput,num1,num2,ukey,ctx);

  for(int i = 0; i<4; i++) {
    BN_copy(num1,reinput);
    BN_mask_bits(num1,64);
    BN_mul(num1,num1,reinput,ctx);

    BN_mul(num2,num1,keymulinv,ctx);
    BN_mask_bits(num2,64);
    BN_mul(num2,ukey,num2,ctx);
    BN_add(num1,num1,num2);
    BN_rshift(num1,num1,64);

    BN_rshift(num2,reinput,64);
    BN_mul(num2,num2,reinput,ctx);
    BN_add(num1,num2,num1);

    BN_mul(num2,num1,keymulinv,ctx);
    BN_mask_bits(num2,64);
    BN_mul(num2,num2,ukey,ctx);

    BN_add(reinput,num1,num2);
    BN_rshift(reinput,reinput,64);
    if(BN_cmp(reinput,ukey)==1 || BN_cmp(reinput,ushift)>=0)
      BN_sub(reinput,reinput,ukey);
    BN_mask_bits(reinput,128);
    }

  BN_zero(sum);
  for(int i=0; i<3; i++) {	
    BN_copy(num1,keybig);
    BN_mask_bits(num1,64);
    BN_rshift(keybig,keybig,64);
    BN_mul(num1,num1,reinput,ctx);
    BN_add(sum,sum,num1);

    BN_copy(num1,sum);
    BN_mask_bits(num1,64);
    BN_mul(num1,num1,keymulinv,ctx);
    if(i==2) {
      BN_lshift(num2,num1,64);
      BN_add(num2,num2,num1);
      }
    else {
      BN_mask_bits(num1,64);
      BN_mul(num1,num1,ukey,ctx);
      BN_add(sum,sum,num1);
      BN_rshift(sum,sum,64);
      if(BN_cmp(sum, ukey)==1 || BN_cmp(sum,ushift)>=0)
         BN_sub(sum,sum,ukey);
      BN_mask_bits(sum,128);
      }
    }

  //Low bytes 
  BN_rshift(num1,num2,2);
  BN_add(num1,num1,sum);
  BN_rshift(num1,num1,52);
  BN_mask_bits(num1,12);
  memset(data, 0, 16);
  int len=BN_bn2bin(num1,data);
  if(len) RotateBytes(data,len);

  //High bytes
  BN_mask_bits(num2,64);
  BN_mul(num2,num2,ukey,ctx);
  BN_add(num2,num2,sum);
  BN_rshift(num2,num2,64);
  BN_lshift(num2,num2,12);
  BN_mask_bits(num2,64);
  len=BN_bn2bin(num2,data+8);
  if(len) RotateBytes(data+8,len);
}

void cN2Map0101::DoMap(int f, unsigned char *data, int l)
{
  dn(printf("map0101: calling function %02X\n",f))
  switch(f) {
    case 0x3b:
      if(l>=80) {
        Map3b(data);
        Map3b(data+40);
        }
      break;
    case 0x57:
	Map57(data);
	break;
    default:
      d(printf("map0101: unsupported call %02x\n",f))
      break;
    }
}

// -- cN2Prov0101 ----------------------------------------------------------------

class cN2Prov0101 : public cMutex, public cN2Prov, public cN2Emu {
private:
  static const unsigned char primes[];
  cAuxSrv aux;
  cN2Map0101 map;
  //
  void MakePrime(BIGNUM *n, unsigned char *residues);
protected:
  virtual bool Algo(int algo, const unsigned char *hd, unsigned char *hw);
  virtual void Stepper(void);
  int mapSize; 
  int lastActiveReg;
  unsigned char mapRegisters[5][2048];
public:
  cN2Prov0101(int Id, int Flags):cN2Prov(Id,Flags) {}
  virtual int ProcessB1(unsigned char *data, int len, int pos);
  };

static cN2ProvLinkReg<cN2Prov0101,0x0101,N2FLAG_MECM|N2FLAG_B1> staticPL0101;

const unsigned char cN2Prov0101::primes[] = {
  0x03,0x05,0x07,0x0B,0x0D,0x11,0x13,0x17,0x1D,0x1F,0x25,0x29,0x2B,0x2F,0x35,0x3B,
  0x3D,0x43,0x47,0x49,0x4F,0x53,0x59,0x61,0x65,0x67,0x6B,0x6D,0x71,0x7F,0x83,0x89,
  0x8B,0x95,0x97,0x9D,0xA3,0xA7,0xAD,0xB3,0xB5,0xBF,0xC1,0xC5,0xC7,0xD3,0xDF,0xE3,
  0xE5,0xE9,0xEF,0xF1,0xFB
  };

void cN2Prov0101::MakePrime(BIGNUM *n, unsigned char *residues)
{
  bool isPrime;
  do {
    BN_add_word(n,2);
    isPrime=true;
    for(int i=0; i<53; i++) {
      residues[i]+=2;
      residues[i]%=primes[i];
      if(residues[i]==0) isPrime=false;
      }
    } while(!isPrime);
}

bool cN2Prov0101::Algo(int algo, const unsigned char *hd, unsigned char *hw)
{
  if(algo==0x40) {
    memcpy(hw,hd,3);
    ExpandInput(hw);
    hw[0x18]|=1; hw[0x40]|=1;
    map.DoMap(0x3b,hw,80);
    RotateBytes(hw,64);
    RotateBytes(&hw[64],64);
    SHA_CTX ctx;
    SHA1_Init(&ctx);
    SHA1_Update(&ctx,hw,128);
    memset(hw,0,128);
    hw[0]=ctx.h3&0xFF;
    hw[1]=(ctx.h3>>8)&0xFF;
    return true;
    }
  else if(algo==0x60) { // map 4D/4E/57
	memcpy(hw,hd,5);
    ExpandInput(hw);
    cBN bh;
    RotateBytes(hw,128);
    BN_bin2bn(hw,128,bh);
    /* MAP 4D */
    unsigned char residues[53];
    BN_set_bit(bh,0x3FF);
    BN_set_bit(bh,0);
    for(int i=0; i<53; i++) residues[i]=BN_mod_word(bh,primes[i]);
    /* MAP 4E */
    MakePrime(bh,residues);
    /* Do some byte jumbling */
    BN_bn2bin(bh,hw);
    RotateBytes(hw,128);
    BN_bin2bn(hw,128,bh);
    /* MAP 4E */
    MakePrime(bh,residues);
    BN_bn2bin(bh,hw);
    RotateBytes(hw,128);
    /* MAP 57 */
    map.DoMap(0x57,hw,128);
    //if(!aux.Map(0x57,hw,128,128)) return false;
    return true;
    }

  d(printf("N2Prov%04X: unknown MECM algo %02x\n",id,algo))
  return false;
}
	
		
int cN2Prov0101::ProcessB1(unsigned char *data, int len, int pos)
{
  cMutexLock lock(this);
  unsigned short nRegister;
  unsigned short addr;
  if(Init(0x0101,102)) {
    SetMem(0x80,data,len);
    SetPc(0x80+pos);
    SetSp(0x0FFF,0x0FF0);
    ClearBreakpoints();
    AddBreakpoint(0x0000);
    AddBreakpoint(0x9569);
	AddBreakpoint(0xA822);
	while (!Run(1000)) {
		if (GetPc()==0x9569)
		{
			printf("B1 Completed Normally\n");
			GetMem(0x80,data,len);
			HexDump(data, len);
			return max((int)a,6);
		}
		if (GetPc()==0xA822) {
				//Map Call
			switch (a) {
				case 0x02: // set map size 
					 mapSize=Get(0x48)*8; 
					 break; 
				case 0x03:
				case 0x04: 
				case 0x05:
				case 0x06:
				case 0x07:
					//Import Ram at [44:45] to Map Registers A-E, E is 0x03 the rest in sequence
					if (mapSize>0) {
						addr=((unsigned short)Get(0x044))+Get(0x45); 
						nRegister = a-0x04;
						if (a==0x03) nRegister=4; //Register E isn't in Sequence
						if (a=0x08) 
							nRegister = lastActiveReg; //Last Active Register op
						else
						    lastActiveReg =  nRegister; //Set Last Active Register
						GetMem(addr,mapRegisters[nRegister],mapSize,0); 
					}
					break;
				case 0x09: //Export Registers A-E with 44:45: 0x09 is E
				case 0x0A:
				case 0x0B:
				case 0x0C:
				case 0x0D:
				case 0x0E:
					if(mapSize>0) { 
						addr=((unsigned short)Get(0x044))+Get(0x45); 
						nRegister = a-0x0A;
						if (a==0x09) nRegister=4; //Register E isn't in Sequence
						if (a=0x0E) 
							nRegister = lastActiveReg; //Last Active Register op
						else
							lastActiveReg =  nRegister; //Set Last Active Register
						SetMem(addr,mapRegisters[nRegister],mapSize,0); 
					}
					break;
				case 0x0F: //Swap Registers A-D with 44:45
				case 0x10:
				case 0x11:
				case 0x12:
					if(mapSize>0) { 
						addr=((unsigned short)Get(0x044))+Get(0x45); 
						nRegister = a-0x0F;
						unsigned char tmp[2048];
						GetMem(addr,tmp,mapSize,0); 
						SetMem(addr,mapRegisters[nRegister],mapSize,0); 
						memcpy(mapRegisters[nRegister],tmp,mapSize); 
						lastActiveReg =  nRegister; //Set Last Active Register
					}
					break;
				case 0x13:
				case 0x14:
				case 0x15:
				case 0x16:
					nRegister = a-0x13;
					lastActiveReg =  nRegister; //Set Last Active Register
					memset(mapRegisters[nRegister], mapSize,0);
					break;
				case 0x17: //copy A to B
					lastActiveReg =  1; //Set Last Active Register
					memcpy(mapRegisters[0], mapRegisters[1], mapSize);
					break;
				case 0x18: //Copy B  to A
					lastActiveReg =  0;
					memcpy(mapRegisters[1], mapRegisters[0], mapSize);
					break;
				case 0x19: //Copy A to C
					lastActiveReg =  2; //Set Last Active Register
					memcpy(mapRegisters[0], mapRegisters[2], mapSize);
					break;
				case 0x1A: //Copy C to A
					lastActiveReg =  0; //Set Last Active Register
					memcpy(mapRegisters[2], mapRegisters[0], mapSize);
				case 0x1B: //Copy C to D
					lastActiveReg =  3; //Set Last Active Register
					memcpy(mapRegisters[2], mapRegisters[3], mapSize);
				case 0x1C: //Copy D to C
					lastActiveReg =  2; //Set Last Active Register
					memcpy(mapRegisters[3], mapRegisters[2], mapSize);
					break;
				default: 
					printf("Unrecognized map call %02x",a); 
					return -1; 
			} 
			PopCr(); PopPc();   
		}
		else {
			printf("B1 Terminated incorrectly\n\n");
			return -1;
		}
	}
  }
    
    
  return -1;
}

void cN2Prov0101::Stepper(void)
{
  unsigned short pc=GetPc();
  if(pc>=0x93 && pc<=0xE0) { 	// pc in EMM data
    unsigned char op=Get(pc);
    if((op&0xF0)==0x00) { 	// current opcode BRCLR/BRSET
      int fake=0; 		// 1=branch -1=non-branch
      if(Get(pc+3)==0x81) 	// next opcode == RTS
        fake=1;			// fake branch
      else {
        unsigned char off=Get(pc+2);
        unsigned short target=pc+3+off;
        if(off&0x80) target-=0x100;
        if(Get(target)==0x81) 	// branch target == RTS
          fake=-1;		// fake non-branch
        }
      if(fake) {
        unsigned char ea=Get(pc+1);
        if(ea<=0x0400) {
          unsigned char val=Get(dr,ea);
          int bit=1<<((op&0xF)>>1);
          // set/clr bit according to fake-mode and opcode
          if((fake>0 && (op&0x01)) || (fake<0 && !(op&0x01)))  {
            if(val&bit) dee(printf("*"))
            val&=~bit;
            }
          else {
            if(!(val&bit)) dee(printf("*"))
            val|=bit;
            }
          Set(dr,ea,val);
          }
        }
      }
    }
}

// -- cN2Prov0901 --------------------------------------------------------------

class cN2Prov0901 : public cN2Prov0101 {
public:
  cN2Prov0901(int Id, int Flags):cN2Prov0101(Id,Flags) {}
  virtual int ProcessB1(unsigned char *data, int len, int pos);
  };

static cN2ProvLinkReg<cN2Prov0901,0x0901,N2FLAG_MECM|N2FLAG_B1> staticPL0901;

int cN2Prov0901::ProcessB1(unsigned char *data, int len, int pos)
{
  if(Init(0x0801,102)) {
    return cN2Prov0101::ProcessB1(data,len,pos);
    }
  return -1;
}
