/*
 * 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
 */

#ifdef VIACCESS

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "common.h"
//#include "sc.h"
#include "system.h"
#include "system-common.h"
#include "data.h"
#include "misc.h"
#include "crypto.h"

#include "..\\main.h"

#define SYSTEM_VIACCESS      0x0500

#define SYSTEM_NAME          "Viaccess"
#define SYSTEM_PRI           -10
#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_VIACCESS)

// -- cPlainKeyVia -------------------------------------------------------------

#define VIA1_KEYLEN 8
#define VIA2_KEYLEN 16
#define VIATPS_KEYLEN 16

#define WRITE(x) {m_DLLInstance.x;}
class cPlainKeyVia : public cPlainKeyStd {
public:
  cPlainKeyVia(bool Super);
  virtual bool Parse(char *line);
  };

static cPlainKeyTypeReg<cPlainKeyVia,'V'> KeyReg;

cPlainKeyVia::cPlainKeyVia(bool Super)
:cPlainKeyStd(Super)
{}

bool cPlainKeyVia::Parse(char *line)
{
  unsigned char sid[3], skeynr, skey[VIA2_KEYLEN];
#ifdef DEBUG
  char *sline=line;
#endif
/*
  int len, klen;
  if(GetChar(line,&type,1) && (len=GetHex(line,sid,3,false)) &&
     GetHex(line,&skeynr,1) && (klen=GetHex(line,skey,VIA2_KEYLEN,false)) &&
     (klen==VIA1_KEYLEN || klen==VIA2_KEYLEN)) {
    type=toupper(type); id=Bin2Int(sid,len); keynr=skeynr;
    SetKey(skey,klen);
    return true;
    }
*/
  int len;
//  char text[100];
  if(GetChar(line,&type,1) && (len=GetHex(line,sid,3,false))) {
     type=toupper(type); id=Bin2Int(sid,len);
     line=skipspace(line);
     bool ok=false;
     if(!strncasecmp(line,"TPS",3)) {
       line+=3;
       keynr=MBC3('T','P','S');
       ok=(len=GetHex(line,skey,VIATPS_KEYLEN));
       }
     else if(GetHex(line,&skeynr,1)) {
       keynr=skeynr;
       ok=((len=GetHex(line,skey,VIA2_KEYLEN,false)) && (len==VIA1_KEYLEN || len==VIA2_KEYLEN));
       }
     if(ok) {
       SetKeyFromBin(skey,len);
       return true;
       }
    }
  m_DLLInstance.PushMessage(MSG_MESSAGE,"[ERROR-Viaccess] Key format error in key file");
  return false;
}

// -- cViaccessCardInfo --------------------------------------------------------

class cViaccessCardInfo : public cListObject {
public:
  unsigned char ID[3], UA[5], SA[4], KEYNO[1], KEY[8];
  //
  bool Parse(char *line);
  bool Save(FILE *f) { return true; }
  bool IsUpdated(void) { return false; }
  void Updated(void) {}
  bool Cmp(cViaccessCardInfo *ci) { return false; }
  };

bool cViaccessCardInfo::Parse(char *line)
{
 //line[strlen(line)-1]='\0';
  while(*line && isspace(*line)) line++;
  if(*line!=';') {
    if(GetHex(line,ID,sizeof(ID)) && 
       GetHex(line,UA,sizeof(UA)) &&
       GetHex(line,SA,sizeof(SA)) &&
       GetHex(line,KEYNO,sizeof(KEYNO)) && 
       GetHex(line,KEY,sizeof(KEY))){
      return true;}
    }
  return false;
}

// -- cViaccessCardInfos -------------------------------------------------------

class cViaccessCardInfos : public cCardInfos<cViaccessCardInfo> {
public:
  cViaccessCardInfos(void):cCardInfos<cViaccessCardInfo>(SYSTEM_NAME) {}
  };

static cViaccessCardInfos Vcards;

// -- cViaccess ----------------------------------------------------------------

class cViaccess : protected cDes {
private:
  unsigned char v2key[8];
  bool v2mode;
  //
  int HashNanos(const unsigned char *data, int len);
  void Via2Mod(const unsigned char *key2, unsigned char *data);
protected:
  unsigned char hbuff[8], hkey[8];
  int pH;
  //
  void SetV2Mode(const unsigned char *key2);
  void SetHashKey(const unsigned char *key);
  void HashByte(unsigned char c);
  void HashClear(void);
  void Hash(void);
  //
  void Decode(unsigned char *data, const unsigned char *key);
  bool Decrypt(const unsigned char *work_key, const unsigned char *data, int len, unsigned char *des_data1, unsigned char *des_data2);
  //
  virtual unsigned int Mod(unsigned int R, unsigned int key7);
public:
  cViaccess(void);
  };

cViaccess::cViaccess(void)
:cDes()
{
  v2mode=false;
}

/* viaccess DES modification */

unsigned int cViaccess::Mod(unsigned int R, unsigned int key7)
{
  if(key7!=0) {
    const unsigned int key5=(R>>24)&0xff;
    unsigned int al=key7*key5 + key7 + key5;
    al=(al&0xff)-((al>>8)&0xff);
    if(al&0x100) al++;
    R=(R&0x00ffffffL) + (al<<24);
    }
  return R;
}

/* viaccess2 modification. Extracted from "Russian wafer" card.
   A lot of thanks to it's author :) */

void cViaccess::Via2Mod(const unsigned char *key2, unsigned char *data)
{
  int kb, db;
  for(db=7; db>=0; db--) {
    for(kb=7; kb>3; kb--) {
      int a0=kb^db;
      int pos=7;
      if(a0&4) { a0^=7; pos^=7; }
      a0=(a0^(kb&3)) + (kb&3);
      if(!(a0&4)) data[db]^=(key2[kb] ^ ((data[kb^pos]*key2[kb^4]) & 0xFF));
      }
    }
  for(db=0; db<8; db++) {
    for(kb=0; kb<4; kb++) {
      int a0=kb^db;
      int pos=7;
      if(a0&4) { a0^=7; pos^=7; }
      a0=(a0^(kb&3)) + (kb&3);
      if(!(a0&4)) data[db]^=(key2[kb] ^ ((data[kb^pos]*key2[kb^4]) & 0xFF));
      }
    }
}

void cViaccess::Decode(unsigned char *data, const unsigned char *key)
{
  if(v2mode) Via2Mod(v2key,data);
  Des(data,key,VIA_DES);
  if(v2mode) Via2Mod(v2key,data);
}

void cViaccess::SetV2Mode(const unsigned char *key2)
{
  if(key2) {
    memcpy(v2key,key2,sizeof(v2key));
    v2mode=true;
    }
  else v2mode=false;
}


void cViaccess::SetHashKey(const unsigned char *key)
{
  memcpy(hkey,key,sizeof(hkey));
}

void cViaccess::HashByte(unsigned char c)
{
  hbuff[pH++]^=c;
  if(pH==8) { pH=0; Hash(); }
}

void cViaccess::HashClear(void)
{
  memset(hbuff,0,sizeof(hbuff));
  pH=0;
}

void cViaccess::Hash(void)
{
  if(v2mode) Via2Mod(v2key,hbuff);
  Des(hbuff,hkey,VIA_DES_HASH);
  if(v2mode) Via2Mod(v2key,hbuff);
}

int cViaccess::HashNanos(const unsigned char *data, int len)
{
  int i=0;
  pH=0;
  if(data[0]==0x9f) {
    HashByte(data[i++]);
    HashByte(data[i++]);
    for(int j=0; j<data[1]; j++) HashByte(data[i++]);
    while(pH!=0) HashByte(0);
    }
  for(; i<len; i++) HashByte(data[i]);
  return i;
}

bool cViaccess::Decrypt(const unsigned char *work_key, const unsigned char *data, int len, unsigned char *des_data1, unsigned char *des_data2)
{
  int pos=0, encStart=0;
  unsigned char signatur[8];
  while(pos<len) {
    switch(data[pos]) {
      case 0xea:                 // encrypted bytes
        encStart = pos + 2;
        memcpy(des_data1,&data[pos+2],8);
        memcpy(des_data2,&data[pos+2+8],8);
        break;
      case 0xf0:                 // signature
        memcpy(signatur,&data[pos+2],8);
        break;
      }
    pos += data[pos+1]+2;
    }
  HashClear();
  SetHashKey(work_key);
  // key preparation
  unsigned char prepared_key[8];
  if(work_key[7]==0) {
    // 8th key-byte = 0 then like Eurocrypt-M but with viaccess mods
    HashNanos(data,encStart+16);
    memcpy(prepared_key,work_key,sizeof(prepared_key));
    }
  else { // key8 not zero
    // rotate the key 2x left
    prepared_key[0]=work_key[2];
    prepared_key[1]=work_key[3];
    prepared_key[2]=work_key[4];
    prepared_key[3]=work_key[5];
    prepared_key[4]=work_key[6];
    prepared_key[5]=work_key[0];
    prepared_key[6]=work_key[1];
    prepared_key[7]=work_key[7];
    // test if key8 odd
    if(work_key[7]&1) {
      HashNanos(data,encStart);
      // test if low nibble zero
      unsigned char k = ((work_key[7] & 0xf0) == 0) ? 0x5a : 0xa5;
      for(int i=0; i<8; i++) {
        unsigned char tmp=des_data1[i];
        des_data1[i]=(k & hbuff[pH]) ^ tmp;
        HashByte(tmp);
        }
      for(int i=0; i<8; i++) {
        unsigned char tmp=des_data2[i];
        des_data2[i]=(k & hbuff[pH]) ^ tmp;
        HashByte(tmp);
        }
      }
    else {
      HashNanos(data,encStart+16);
      }
    }
  Decode(des_data1,prepared_key);
  Decode(des_data2,prepared_key);
  Hash();
  return (memcmp(signatur,hbuff,8)==0);
}

// -- cSystemViaccess ----------------------------------------------------------

struct ViaccessSOIDData {
  unsigned char ident[3];
  unsigned char keyno;
  };

#define MAX_NEW_KEYS 5

class cSystemViaccess : public cSystem, private cViaccess, private cTPS {
private:
  struct ViaccessSOIDData sd;
  unsigned char emmBuff[256];
  int emmLen;
  bool haveSOID,initTPSkey;
protected:
  virtual void ParseCADescriptor(unsigned short sysId, const unsigned char *data, int len);
  virtual void ParseCAT(const cEcmInfo *ecm, const unsigned char *buffer, int n);
public:
  cSystemViaccess(void);
  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *source);
  virtual void ProcessEMM(int pid, const cEcmInfo *ecm, unsigned char *buffer, int n);
  };

cSystemViaccess::cSystemViaccess(void)
:cSystem(SYSTEM_NAME,SYSTEM_PRI)
{
  haveSOID=false; emmLen=0;
  hasLogger=true;initTPSkey=true;
}

void cSystemViaccess::ParseCADescriptor(unsigned short sysId, const unsigned char *data, int len)
{
  const int pid=WORD(data,2,0x1FFF);
  if(pid>=0xAA && pid<=0xCF) {
    d(m_DLLInstance.Log("system-viaccess: dropped \"fake\" ecm pid 0x%04x\n",pid))
    return;
    }
  int p=4;
  while(p<len) {
    if(data[p]==0x14)
      AddEcm(new cEcmInfo(name,pid,sysId,(data[p+2]<<16)|(data[p+3]<<8)|(data[p+4]&0xF0)));
    p+=2+data[p+1];
    }
}

void cSystemViaccess::ParseCAT(const cEcmInfo *ecm, const unsigned char *buffer, int n)
{
  for(int index=8 ; index<n ;) {
    if(buffer[index]==0x09) {
      if(WORD(buffer,index+2,0xFFFF)==ecm->caId) {
        AddPid(WORD(buffer,index+4,0x1FFF),0x88,0xF8);
        break;
        }
      }
    index+=buffer[index+1]+2;
    }
}

bool cSystemViaccess::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
{
  char text[40] ;
  int length=SCT_LEN(source)-4;
  source+=4;
    
  // get this nano first
/*
  if(source[0]==0xd2) {
    source+=3; length-=3;
    fprintf(vialog,"TPSCrypt prv:%x\n",ecm->provId);
    TpsDecrypt(source,length);
    }
*/
  // next should get 0x90 nano (length 3) containing id and key
  if(initTPSkey) 
  {
    cPlainKey *pk=keys.FindKey('V',ecm->provId,MBC3('T','P','S'));
    if(pk) 
    {
      unsigned char key[16];
      pk->Get(key);
      cTPS::SetV2Key(key);
    }
    initTPSkey=false;
  }
  int eat=cTPS::Decrypt(source,length);
  if(eat<0) 
  {
    m_DLLInstance.Log("[Viaccess] Missing %.4x TPS key\n",ecm->provId);
    
    
    return false;
  }
  else if(eat>0) { source+=eat; length-=eat; }
  
  int keynr=source[4]&0x0F;;
  cKeySnoop ks(this,'V',ecm->provId,keynr);
  if(source[0]==0x90 && source[1]==0x03) {
    source+=5; length-=5;
    cPlainKey *pk;
    while((pk=FastKey('V',ecm->provId,keynr))) {
      unsigned char key[16];
      pk->Get(key);
      
      SetV2Mode(pk->Size()==VIA2_KEYLEN ? &key[VIA1_KEYLEN] : 0);
      if(cViaccess::Decrypt(key,source,length,&cw[0],&cw[8])) {
        ks.OK(pk);

        return true;
        }
      }
    }

  return false;
 
}

void cSystemViaccess::ProcessEMM(int pid, const cEcmInfo *ecm, unsigned char *buffer, int n)
{
  unsigned char ua[5], sa[3], signature[8], addressdata[34];
  unsigned char *scan=0;
  unsigned int updtype=0, scanlen=0;

  bool haveData=false;
  const unsigned int msgLen=buffer[2];
  switch(buffer[0]) {
    case 0x8C: // shared update message
    case 0x8D:
     // dl(m_DLLInstance.Log("logger %d: got SOID data\n",CardNum()))
      if(msgLen<=(int)sizeof(emmBuff)) {
        scan=&buffer[3]; // Skip SA
        unsigned int cnt=0;
        while(cnt<msgLen) {
          if(scan[cnt]==0x90 && scan[cnt+1]==0x03) { // SOID data
            sd.ident[0]=scan[cnt+2];
            sd.ident[1]=scan[cnt+3];
            sd.ident[2]=scan[cnt+4]&0xF0;
            sd.keyno=scan[cnt+4]&0x0F;
            haveSOID=true;
            break;
            }
          cnt+=scan[cnt+1]+2;
          }
        memcpy(emmBuff,&buffer[3],msgLen);
        emmLen=msgLen;
        }
      else d(m_DLLInstance.Log("logger %d: EMM buffer overflow n=%d\n",CardNum(),msgLen))
      return;

    case 0x8E: // shared update data/message
      {

      static const unsigned char addr_nano[] = { 0x9E,0x20 };

      memcpy(sa,&buffer[3],sizeof(sa));
      if((buffer[6]&2)==0) { // constant format, needs section 0x8c or 0x8d
        if(emmLen>0) {       // for messages
          memcpy(addressdata, addr_nano, sizeof(addr_nano));
          memcpy(&addressdata[2],&buffer[7], sizeof(addressdata)-2);
          memcpy(signature, &buffer[msgLen-5], sizeof(signature));
          scan=emmBuff; scanlen=emmLen;
          }
        else {
          d(m_DLLInstance.Log("logger %d: section 0x8e with empty buffer\n",CardNum()))
          return;
          }
        }
      else { // variable format with nanos
        scan=&buffer[7]; // Skip SA
        unsigned int cnt=0;
        while(cnt<msgLen) {
          if(scan[cnt]==0xF0 && scan[cnt+1]==0x08) { // this is the last one in line
            memcpy(signature, &scan[cnt+2], sizeof(signature));
            break;
            }
          if(scan[cnt]==addr_nano[0] && scan[cnt+1]==addr_nano[1]) {
            memcpy(addressdata, &scan[cnt], sizeof(addressdata));             
            }
          // no need to copy the data to emmBuff, we are scanning from
          // buffer anyways
          cnt+=scan[cnt+1]+2;
          }
        scanlen=cnt;
        }
      haveData=true; updtype=1;
      break;
      }

    case 0x88:
      {
      if(msgLen>=0xB4) {
//        d(m_DLLInstance.Log("logger %d: got broken section data, ignoring it\n", cardNum))
        return;
        }
      memcpy(ua,&buffer[3],sizeof(ua));
      scan=&buffer[8]; // Skip UA
      unsigned int cnt=0;
      while(cnt<msgLen) {
        if(scan[cnt]==0xF0 && scan[cnt+1]==0x08) { 
          memcpy(signature, &scan[cnt+2], sizeof(signature));
          break;
          }
        if(scan[cnt]==0x90 && scan[cnt+1]==0x03) { // SOID data
          sd.ident[0]=scan[cnt+2];
          sd.ident[1]=scan[cnt+3];
          sd.ident[2]=scan[cnt+4]&0xF0;
          sd.keyno=scan[cnt+4]&0x0F;
          haveSOID=true;
          }
        // no need to copy the data to emmBuff, we are scanning from
        // buffer anyways
        cnt+=scan[cnt+1]+2;
        }
      scanlen=cnt; haveData=true; updtype=2;
      break;
      }

    default:
      return;
    }

  // we always want to have complete data
  // (but in theory we can't get until here without data)
  if(!haveData || !haveSOID) return;
  
//#if defined(DEBUG_LOG) | defined(DEBUG_LOG_FILE)
  char str[20], str2[20],cardsa[20],cardua[20],txt[80];
  static char *ptext[] = { "TO ALL","SHARED","UNIQUE" };
  const char *addr = (updtype==1) ? HexStr(str,sa,sizeof(sa)) : HexStr(str,ua,sizeof(ua));
//#endif
 //dl(m_DLLInstance.Log("%02x%02x00 %02x %s %s\n",sd.ident[0],sd.ident[1],sd.keyno,HexStr(str,ua,sizeof(ua)),ptext[updtype]))
  //dl(fflush(stdout))
  
  // we are not allowed to modify scan & scanlen inside the following
  // loop or we won't be able to proceed multiple card with the same ident
  cViaccessCardInfo *mkey=Vcards.First();
//  m_DLLInstance.Log("Addr : %s \n",addr);
   sprintf(txt,"%s : %s Prv:%.2X%.2X%.2X",ptext[updtype],addr,sd.ident[0],sd.ident[1],sd.ident[2]);

  while(mkey) {
   
   HexStr(cardsa,mkey->SA,sizeof(mkey->SA));
   HexStr(cardua,mkey->UA,sizeof(mkey->UA)); 

   m_DLLInstance.PushMessage(MSG_ECM,txt);
    if(   !memcmp(mkey->ID,sd.ident,sizeof(sd.ident))
       && mkey->KEYNO[0]==sd.keyno
       && (   (updtype==1 && !memcmp(mkey->SA,sa,sizeof(sa)))
           || (updtype==2 && !memcmp(mkey->UA,ua,sizeof(ua)))
          )
       ) {
      HashClear();
      SetHashKey(mkey->KEY);
      m_DLLInstance.Log("Selected Card: %.2X%.2X%.2X KN:%.2x %s - %s\n",sd.ident[0],sd.ident[1],sd.ident[2],sd.keyno,ptext[updtype],addr);
      switch(updtype) {
        case 1: // shared/normal update
          memcpy(hbuff+5,sa,3);
          Hash();
          for(unsigned int i=0; i<sizeof(addressdata); i++)
            HashByte(addressdata[i]);
          Hash();
          pH=0;
          break;
        case 2: // unique update
          memcpy(hbuff+3,ua,sizeof(ua));
          Hash();
          break;
        }
        
      unsigned char newKey[MAX_NEW_KEYS][8];
      int numKeys=0, updPrv[MAX_NEW_KEYS]={}, updKey[MAX_NEW_KEYS]={};

      unsigned int cnt=0;
      while(cnt<scanlen && numKeys<MAX_NEW_KEYS) {
        const unsigned int parm=scan[cnt++];
        int unsigned plen=scan[cnt++];
        WRITE(Log("Processing nano:0x%X\n",parm))
        switch(parm) {
          case 0x90:
            // we shouldn't process nano 0x90 (SOID) in any way since we parsed it before
            cnt+=plen;
            break;

          case 0x9E:
            // we shouldn't process nano 0x9e in any way since we added it to
            // the hash earlier.  We don't prevent it from being in emmBuff
            // anymore, so we just skip it and get the next nano. This is most
            // important for constant shared updates else the hash will be screwed.
            cnt+=plen;
            break;

          case 0xA1: // keyupdate
            updPrv[numKeys]=(scan[cnt]<<16)+(scan[cnt+1]<<8)+(scan[cnt+2]&0xF0);
            updKey[numKeys]=scan[cnt+2]&0x0F;
            // fall through to "default"

          default:
            HashByte(parm); HashByte(plen);
            while(plen--) HashByte(scan[cnt++]);
            break;

          case 0xEF: // crypted key(s)
            m_DLLInstance.Log("Find crypted key..\n");
            HashByte(parm); HashByte(plen);
            if(plen==sizeof(newKey[0])) {
              const unsigned char k7=mkey->KEY[7];
              for(unsigned int kc=0 ; kc<sizeof(newKey[0]) ; kc++) {
                const unsigned char b=scan[cnt++];
                if(k7&1) newKey[numKeys][kc]=b^(hbuff[pH]&(k7<0x10 ? 0x5a : 0xa5));
                else     newKey[numKeys][kc]=b;
                HashByte(b);
                }
              numKeys++;
              }
            else {
              d(m_DLLInstance.Log("logger %d: key length mismatch %d!=%d\n",CardNum(),plen,sizeof(newKey[0])))
              return;
              }
            break;
          }
        }
      Hash();

      if(numKeys) {
        if(!memcmp(signature,hbuff,sizeof(signature))) {
          unsigned char key[8];
          memcpy(key,mkey->KEY,sizeof(key));
          if(key[7]) { // Rotate key
            const unsigned char t1=key[0], t2=key[1];
            key[0]=key[2]; key[1]=key[3]; key[2]=key[4]; key[3]=key[5]; key[4]=key[6];
            key[5]=t1; key[6]=t2;
            }

          while(numKeys--) {
            dl(m_DLLInstance.Log("%02x%02x %02x %s %s - KEY %06x.%02x\n",sd.ident[0],sd.ident[1],
              sd.keyno,addr,ptext[updtype],updPrv[numKeys],updKey[numKeys]))
            Decode(newKey[numKeys],key);
            dl(m_DLLInstance.Log(" -> %s\n",HexStr(str2,newKey[numKeys],sizeof(newKey[numKeys]))))
#ifdef DEBUG_LOG_FILE
            FILE *f=LogStart();
            if (f) {
              fprintf(f,"%02x%02x %02x %s %s - KEY %06x.%02x -> %s\n",
                        sd.ident[0],sd.ident[1],sd.keyno,addr,ptext[updtype],
                        updPrv[numKeys],updKey[numKeys],HexStr(str2,newKey[numKeys],sizeof(newKey[numKeys])));
              LogEnd(f);
              }
#endif    
          cPlainKey* pk;
          unsigned char oldkey[8];
          
          if(!(pk=FastKey('V',updPrv[numKeys],updKey[numKeys])))
               m_DLLInstance.SaveKey('V',updPrv[numKeys],updKey[numKeys],newKey[numKeys],8,NULL);
          else
          {
              pk->Get(oldkey);
              m_DLLInstance.SaveKey('V',updPrv[numKeys],updKey[numKeys],newKey[numKeys],8,oldkey);
          }
          
          keys.NewKey('V',updPrv[numKeys],updKey[numKeys],newKey[numKeys],8);
          m_DLLInstance.FoundKey();
            }
          }
        else {
          dl(m_DLLInstance.Log("%02x%02x %02x %s %s - FAIL\n",
                     sd.ident[0],sd.ident[1],sd.keyno,addr,ptext[updtype]))

#ifdef DEBUG_LOG_FILE
          FILE *f=LogStart();
          if (f) {
            fprintf(f,"%02x%02x %02x %s %s - FAIL\n",
                      sd.ident[0],sd.ident[1],sd.keyno,addr,ptext[updtype]);
            LogEnd(f);
            }
#endif
          }
        }
      }
    mkey=Vcards.cList<cViaccessCardInfo>::Next(mkey);
    }
}

// -- cSystemLinkViaccess ------------------------------------------------------

class cSystemLinkViaccess : public cSystemLink {
public:
  cSystemLinkViaccess(void):cSystemLink(SYSTEM_NAME,SYSTEM_PRI) {}
  virtual bool CanHandle(unsigned short SysId);
  virtual cSystem *Create(void) { return new cSystemViaccess; }
  virtual bool Init(const char *cfgdir);
  };

static cSystemLinkViaccess staticInit;

bool cSystemLinkViaccess::CanHandle(unsigned short SysId)
{
  SysId&=SYSTEM_MASK;
  return SYSTEM_CAN_HANDLE(SysId);
}

bool cSystemLinkViaccess::Init(const char *cfgdir)
{
  Vcards.Load(cfgdir,SYSTEM_NAME,"Viaccess.KID");
  return true;
}

#else //#ifdef VIACCESS

// add dummy loader for Viaccess keys
#include "system-common.h"
static cPlainKeyTypeReg<cPlainKeyDummy,'V'> KeyReg;

#endif //#ifdef VIACCESS
