./ MultiCS.r82 / ecmdata.c
#include "common.h"

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>


#ifdef WIN32

#include <windows.h>
#include <sys/types.h>
#include <sys/_default_fcntl.h>
#include <sys/poll.h>
#include <cygwin/types.h>
#include <cygwin/socket.h>
#include <sys/errno.h>
#include <cygwin/in.h>
#include <sched.h>
#include <netdb.h>
#include <netinet/tcp.h>

#else

#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#include <pthread.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <poll.h>

#endif

#include "debug.h"
#include "convert.h"
#include "tools.h"
#include "threads.h"
#include "dcw.h"
#include "ecmdata.h"

#ifdef CCCAM
#include "msg-cccam.h"
#endif

#include "config.h"
extern struct config_data cfg;

struct ecm_request *ecmdata = NULL;

int ecmindex = 0;


void init_ecmdata()
{
	ecmdata = NULL;
}

uint32_t hashCode( uint8_t *buf, int count)
{
	int h = 0;
	int i;
    for (i = 0; i < count; i++) h = 31*h + buf[i];
    return h;
}

uint8_t checkECMD5(uint8_t *ecmd5)
{
	int8_t i;
	for (i=0;i<16;i++)
		if (ecmd5[i]) return 1;
	return 0;
}

uint32_t ecm_getprovid( uint8_t *ecm, uint16_t caid )
{
	int32_t i, len, descriptor_length = 0;
	uint32_t provid = 0;

	switch(caid >> 8) {
		case 0x01: // seca
			provid = (ecm[3]<<8)|ecm[4];
			break;

		case 0x05:
			// viaccess
			i = (ecm[4] == 0xD2) ? ecm[5]+2 : 0;  // skip d2 nano
			if((ecm[5+i] == 3) && ((ecm[4+i] == 0x90) || (ecm[4+i] == 0x40)))
				provid = (ecm[i+6]<<16)|(ecm[i+7]<<8)|(ecm[i+8]&0xF0);

			i = (ecm[6] == 0xD2) ? ecm[7]+2 : 0;  // skip d2 nano long ecm
			if((ecm[7+i] == 7) && ((ecm[6+i] == 0x90) || (ecm[6+i] == 0x40)))
				provid = (ecm[i+8]<<16)|(ecm[i+9]<<8)|(ecm[i+10]&0xF0);
			break;

		case 0x0D:
			// cryptoworks
			len = (((ecm[1] & 0xf) << 8) | ecm[2])+3;
			for(i=8; i<len; i+=descriptor_length+2) {
				descriptor_length = ecm[i+1];
				if (ecm[i] == 0x83) {
					provid = (uint32_t)ecm[i+2] & 0xFE;
					break;
				}
			}
			break;
	}
	return(provid);
}

uint16_t ecm_getchid( uint8_t *ecm, uint16_t caid )
{
	if ( (caid>>8)==0x06 )	return (ecm[6]<<8)|ecm[7];
	return 0;
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

void cs_addecmdata(struct cardserver_data *cs, struct ecm_request *new )
{
	if (!cs->ecmdata) {
		new->csnext = NULL;
		new->csprev = NULL;
		cs->ecmdata = new;
	}
	else if (!cs->ecmdata->csnext) {
		cs->ecmdata->csnext = new;
		cs->ecmdata->csprev = new;
		new->csnext = cs->ecmdata;
		new->csprev = cs->ecmdata;
		cs->ecmdata = new;
	}
	else {
		struct ecm_request *prev = cs->ecmdata->csprev;
		struct ecm_request *next = cs->ecmdata;
		new->csnext = next;
		new->csprev = prev;
		prev->csnext = new;
		next->csprev = new;
		cs->ecmdata = new;
	}
	cs->totalecm++;
	//debugf(0," [%s] new ecmdata %p size = %d\n", cs->name, cs->ecmdata, cs->totalecm);
}

void cs_delecmdata(struct cardserver_data *cs, struct ecm_request *old )
{
	if (!cs->ecmdata) {
		old->csprev = NULL;
		old->csnext = NULL;
		return;
	}
	if (cs->ecmdata==old) {
		// move to next
		if (!cs->ecmdata->csnext) {
			cs->ecmdata = NULL;
		}
		else {
			cs->ecmdata = old->csnext;
			if (old->csnext==old->csprev) { // only 2 ecmdata
				cs->ecmdata->csnext = NULL;
				cs->ecmdata->csprev = NULL;
			}
			else {
				old->csprev->csnext = old->csnext;
				old->csnext->csprev = old->csprev;
			}
		}
	}
	else if (old->csnext==old->csprev) {
		cs->ecmdata->csnext = NULL;
		cs->ecmdata->csprev = NULL;
	}
	else {
		old->csprev->csnext = old->csnext;
		old->csnext->csprev = old->csprev;
	}

	old->csprev = NULL;
	old->csnext = NULL;
	cs->totalecm--;
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

int totalecm = 0;

struct ecm_request *store_ecmdata(struct cardserver_data *cs,uint8_t *ecm,int ecmlen, unsigned short sid, unsigned short caid, unsigned int provid)
{
	struct ecm_request *new;
	uint32_t ticks = GetTickCount();
	// add new or use dead data
	if (!ecmdata) {
		// nothing so add new
		//debugf(0," first ecmdata\n");
		totalecm++;
		new = malloc( sizeof(struct ecm_request) );
		memset( new, 0,  sizeof(struct ecm_request) );
		new->next = NULL;
		new->prev = NULL;
		ecmdata = new; // it becomes the current one
	}
	else if (!ecmdata->next) {
		//debugf(0," second ecmdata\n");
		totalecm++;
		new = malloc( sizeof(struct ecm_request) );
		memset( new, 0,  sizeof(struct ecm_request) );

		new->prev = ecmdata;
		new->next = ecmdata;

		ecmdata->prev = new;
		ecmdata->next = new;

		ecmdata = new; // it becomes the current one
	}
	else {
		new = ecmdata->prev;
		if ( (new->recvtime+TIME_ECMALIVE*2) < ticks ) {
			cs_delecmdata(new->cs, new);
			// cache is dead, add data to this one without allocating new data
			struct ecm_request *tmp = new->prev; // store previous
			memset( new, 0,  sizeof(struct ecm_request) );
			new->prev = tmp;
			new->next = ecmdata;
			ecmdata = new; // it becomes the current one			
		}
		else { // allocate new data
			totalecm++;
			//debugf(0," new ecmdata size = %d\n", totalecm);
			new = malloc( sizeof(struct ecm_request) );
			memset( new, 0,  sizeof(struct ecm_request) );
			new->prev = ecmdata->prev;
			new->next = ecmdata;
			//
			new->prev->next = new;
			//
			ecmdata->prev = new;
			//
			ecmdata = new; // it becomes the current one			
		}
	}
	cs_addecmdata(cs, new);
	//
	memcpy( new->ecm, ecm, ecmlen);
	new->cs = cs;
	new->chid = ecm_getchid(ecm, caid);
	new->ecmlen = ecmlen;
	new->recvtime = ticks;
	new->lastrecvtime = ticks;
	new->hash = hashCode(ecm+3, ecmlen-3);
#ifdef CACHEEX
	int32_t offset = 3;
	if ( (caid>>8)==0x17 ) offset = 13;
	MD5( ecm+offset, ecmlen-offset, new->ecmd5);
#endif
	new->dcwstatus = STAT_DCW_WAIT;
	new->sid = sid;
	new->caid = caid;
	new->provid = provid;
//	new->id = ecmindex;
	memset( &new->server, 0, sizeof(new->server) );
	new->waitcache = 0;
	new->cachestatus = 0; //ECM_CACHE_NONE;
	//new->dcwsrvtype = DCW_SOURCE_NONE;

	new->period = 1; // First try

#ifdef CHECK_NEXTDCW
	//checkfreeze_storeECM(new);
#endif
	return new;
}

////////////////////////////////////////////////////////////////////////////////
// SEARCH FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

struct ecm_request *search_ecmdata_dcw( uint8_t *ecm, int ecmlen, unsigned short sid)
{
	uint32_t ticks = GetTickCount();
	uint32_t hash =  hashCode(ecm+3, ecmlen-3);
	struct ecm_request *req = ecmdata;
	while (req) {
		if ( (req->recvtime+TIME_ECMALIVE) < ticks ) return NULL;
		if (req->dcwstatus!=STAT_DCW_FAILED)
		if (hash==req->hash)
		if (ecm[0]==req->ecm[0])
		if (ecmlen==req->ecmlen)
		if (sid==req->sid)
		if ( !memcmp(ecm+3, req->ecm+3, ecmlen-3) ) return req;
		req = req->next;
		if (req==ecmdata) break;
	}
	return NULL;
}


struct ecm_request *search_ecmdata_any(struct cardserver_data *cs, uint8_t *ecm, int ecmlen, unsigned short sid, unsigned short caid)
{
	uint32_t hash =  hashCode(ecm+3, ecmlen-3);
	uint32_t ticks = GetTickCount();
	struct ecm_request *req = cs->ecmdata;
	while (req) {
		if ( (req->recvtime+TIME_ECMALIVE) < ticks ) return NULL;

		if (hash==req->hash)
		if (ecmlen==req->ecmlen)
		if (sid==req->sid)
		if (caid==req->caid)
		if ( !memcmp(ecm, req->ecm, ecmlen) ) return req;

		req = req->csnext;
		if (req==cs->ecmdata) break;
	}
	return NULL;
}


struct ecm_request *search_ecmdata_byhash( uint16_t caid, uint16_t sid,uint32_t hash )
{
	uint32_t ticks = GetTickCount();
	struct ecm_request *req = ecmdata;
	while (req) {
		if ( (req->recvtime+TIME_ECMALIVE) < ticks ) return NULL;

		if (hash==req->hash)
		if (caid==req->caid)
		if (sid==req->sid) return req;

		req = req->next;
		if (req==ecmdata) break;
	}
	return NULL;
}

#ifdef CACHEEX
struct ecm_request *search_ecmdata_byecmd5( uint16_t caid, uint32_t provid, uint8_t ecmd5[16] )
{
	uint32_t ticks = GetTickCount();
	struct ecm_request *req = ecmdata;
	while (req) {
		if ( (req->recvtime+TIME_ECMALIVE) < ticks ) return NULL;

		if (caid==req->caid)
		if (provid==req->provid)
		if ( !memcmp(ecmd5, req->ecmd5, 16) ) return req;

		req = req->next;
		if (req==ecmdata) break;
	}
	return NULL;
}
#endif

///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////

int ecmdata_check_cw( uint8_t tag, uint32_t hash, unsigned short caid, unsigned int provid , unsigned short sid, uint8_t cw[16], int cwpart )
{
	struct ecm_request *req = ecmdata;
	while (req) {

		if (req->dcwstatus==STAT_DCW_SUCCESS)
		if (hash!=req->hash)
		if (provid==req->provid)
		if (caid==req->caid)
		if (sid==req->sid) {
			switch (cwpart) {
				case 0:
					if ( dcwcmp8(req->cw,cw) ) return 0;
					if ( dcwcmp8(req->cw+8,cw) ) return 0;
					break;
				case 1:
					if ( dcwcmp8(req->cw,cw+8) ) return 0;
					if ( dcwcmp8(req->cw+8,cw+8) ) return 0;
					break;
				case 2:
					if ( dcwcmp16(req->cw,cw) ) return 0;
					break;
			}
		}

		req = req->next;
		if (req==ecmdata) break;
	}
	return 1;
}


////////////////////////////////////////////////////////////////////////////////
// IP FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

// Client IP
void ecm_addip( ECM_DATA *ecm, unsigned int ip)
{
	register int i;
	for(i=0; i<20; i++) {
		if (!ecm->iplist[i]) {
			ecm->iplist[i] = ip;
			return;
		}
		if (ecm->iplist[i]==ip) return;
	}
}

int ecm_checkip(ECM_DATA *ecm, unsigned int ip)
{
	register int i;
	for(i=0; i<20; i++) {
		if (!ecm->iplist[i]) return FALSE;
		if (ecm->iplist[i]==ip) return TRUE; // found
	}
	return FALSE;
}

//SRV IP
void ecm_addsrvip(ECM_DATA *ecm, unsigned int ip)
{
	register int i;
	for(i=0; i<20; i++) {
		if (!ecm->srviplist[i]) {
			ecm->srviplist[i] = ip;
			return;
		}
		if (ecm->srviplist[i]==ip) return;
	}
}

int ecm_checksrvip(ECM_DATA *ecm, unsigned int ip)
{
	register int i;
	for(i=0; i<20; i++) {
		if (!ecm->srviplist[i]) return FALSE;
		if (ecm->srviplist[i]==ip) return TRUE; // found
	}
	return FALSE;
}

////////////////////////////////////////////////////////////////////////////////
// SERVER FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

int ecm_nbservers(ECM_DATA *ecm)
{
	int i;
	int count=0;
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			count++;
		} else break;
	}
	return count;
}

int ecm_nbsentsrv(ECM_DATA *ecm)
{
	int i;
	int count=0;
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			if (ecm->server[i].flag!=ECM_SRV_EXCLUDE) count++;
		} else break;
	}
	return count;
}

int ecm_nbwaitsrv(ECM_DATA *ecm)
{
	int i;
	int count=0;
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			if (ecm->server[i].flag==ECM_SRV_REQUEST) count++;
		} else break;
	}
	return count;
}

int ecm_addsrv(ECM_DATA *ecm, unsigned int srvid)
{
	int i;
	uint32_t ticks = GetTickCount();
	for(i=0; i<20; i++) {
		if (!ecm->server[i].srvid) {
			ecm->server[i].srvid = srvid;
			ecm->server[i].flag = ECM_SRV_REQUEST;
			ecm->server[i].sendtime = ticks;
			ecm->server[i].statustime = ticks; // last changed status time
			ecm->server_totalsent = ecm_nbsentsrv(ecm);
			ecm->server_totalwait = ecm_nbwaitsrv(ecm);
			return 1;
		}
	}
	return 0;
}

int ecm_setsrvflag(ECM_DATA *ecm, unsigned int srvid, int flag)
{
	int i;
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			if (ecm->server[i].srvid==srvid) {
				ecm->server[i].flag=flag;
				ecm->server[i].statustime = GetTickCount();
				ecm->server_totalsent = ecm_nbsentsrv(ecm);
				ecm->server_totalwait = ecm_nbwaitsrv(ecm);
				return 1;
			}
		} else break;
	}
	return 0;
}

int ecm_setsrvflagdcw(ECM_DATA *ecm, unsigned int srvid, int flag, uint8_t dcw[16])
{
	int i;
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			if (ecm->server[i].srvid==srvid) {
				ecm->server[i].flag=flag;
				ecm->server[i].statustime = GetTickCount();
				memcpy(ecm->server[i].dcw, dcw, 16);
				ecm->server_totalsent = ecm_nbsentsrv(ecm);
				ecm->server_totalwait = ecm_nbwaitsrv(ecm);
				return 1;
			}
		} else break;
	}
	return 0;
}

int ecm_getsrvflag(ECM_DATA *ecm, unsigned int srvid)
{
	int i;
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			if (ecm->server[i].srvid==srvid) {
				return ecm->server[i].flag;
			}
		} else break;
	}
	return 0;
}


///////////////////////////////////////////////////////////////////////////////
// DCW CHECK
///////////////////////////////////////////////////////////////////////////////

#ifdef CHECK_NEXTDCW

// Get Last DCW for the same Channel
void checkfreeze_storeECM(ECM_DATA *ecm)
{
	struct ecm_request *xecm = NULL;

	// find after storing ecm
	if (!ecm) return;

	if ( (!ecm->lastdecode.ecm)&&(ecm->dcwstatus!=STAT_DCW_SUCCESS) ) {
		//debugf(0," \n[SROTE ECM] New (%04x:%06x:%04x/%08x)\n",ecm->caid, ecm->provid, ecm->sid, ecm->hash);

		uint32_t ticks = GetTickCount();
		struct ecm_request *oldecm = ecm->csnext;
		while (oldecm) {
			if ( (oldecm->recvtime+TIME_ECMALIVE*2) < ticks ) break;

			if ( (oldecm->ecmlen==ecm->ecmlen)&&(oldecm->caid==ecm->caid)&&(oldecm->provid==ecm->provid)&&(oldecm->sid==ecm->sid) ) {

				if ( (oldecm->dcwstatus==STAT_DCW_SUCCESS)&&(oldecm->hash!=ecm->hash)&&(oldecm->ecm[0]!=ecm->ecm[0]) ) {
					//ecm->lastdecode.request = oldecm;
					if ( (oldecm->lastdecode.ecm)&&(oldecm->lastdecode.counter>1) ) {
						if ((ecm->recvtime-oldecm->recvtime)<(oldecm->lastdecode.dcwchangetime*3/2)) xecm = oldecm;
						break;
					}
					else if (!xecm) xecm = oldecm;
				}

				else if ( (oldecm->dcwstatus==STAT_DCW_FAILED)&&(oldecm->hash==ecm->hash)&& !memcmp(oldecm->ecm+3, ecm->ecm+3, ecm->ecmlen-3) ) { // the same channel but we have freeze
					memcpy( &ecm->lastdecode, &oldecm->lastdecode, sizeof(ecm->lastdecode) );
					return; // copy and exit
					//fdebugf(" Updating Cycles data after freeze ch %04x:%06x:%04x:%08x (%d)\n",ecm->caid, ecm->provid, ecm->sid, ecm->hash, ecm->lastdecode.counter);
				}

			}
			oldecm = oldecm->csnext;
			if (oldecm==ecm) break;
		}


		if (xecm) {
			ecm->lastdecode.ecm = xecm; // status -> last ecm
			ecm->lastdecode.counter = xecm->lastdecode.counter;
			ecm->lastdecode.dcwchangetime = (xecm->lastdecode.dcwchangetime*xecm->lastdecode.counter+(ecm->recvtime-xecm->recvtime)) / (xecm->lastdecode.counter+1);
			ecm->lastdecode.dcwchangetime = ((ecm->lastdecode.dcwchangetime+500)/1000)*1000;
			memcpy( ecm->lastdecode.dcw, xecm->lastdecode.dcw, 16); // Store latest DCW
#ifdef TESTCHANNEL
			int testchannel = ( (ecm->caid==cfg.testchn.caid) && (ecm->provid==cfg.testchn.provid) && (ecm->sid==cfg.testchn.sid) );
			if (testchannel) {
				char dump[64];
				array2hex( xecm->cw, dump, 16);
				fdebugf(" Update Cycle ch %04x:%06x:%04x %02x:%08x ( %02x:%08x %s/%d)\n",ecm->caid, ecm->provid, ecm->sid, ecm->ecm[0],ecm->hash,
					xecm->ecm[0], xecm->hash, dump, ecm->lastdecode.counter);
			}
#endif
		}

	}
}

void checkfreeze_checkECM( ECM_DATA *ecm, ECM_DATA *oldecm )
{
	if (!ecm) return;
	if (!oldecm) return;
	if (ecm->lastdecode.ecm==oldecm) return;

	if ( (ecm->recvtime-oldecm->recvtime) > TIME_ECMALIVE*2) return;

	if ( (oldecm->ecmlen==ecm->ecmlen) && (oldecm->caid==ecm->caid) && (oldecm->provid==ecm->provid) && (oldecm->sid==ecm->sid) ) {

		if ( (oldecm->dcwstatus==STAT_DCW_SUCCESS) && (oldecm->ecm[0]!=ecm->ecm[0]) && (oldecm->hash!=ecm->hash) ) {
			if ( (oldecm->lastdecode.ecm)&&(oldecm->lastdecode.counter>1) ) {
				if ((ecm->recvtime-oldecm->recvtime)<(oldecm->lastdecode.dcwchangetime*2)) {
					// Setup Cw Cycle
					if (oldecm->lastdecode.cwcycle=='0') ecm->lastdecode.cwcycle = '1';
					else if (oldecm->lastdecode.cwcycle=='1') ecm->lastdecode.cwcycle = '0';
					else return;
					// check for cw1cycle
					if (ecm->cw1cycle==0x80) { // cw1 cycle on tag=0x80
						if ( (ecm->ecm[0]==0x80)&&(ecm->lastdecode.cwcycle!='1') ) return;
						if ( (ecm->ecm[0]==0x81)&&(ecm->lastdecode.cwcycle!='0') ) return;
					}
					else if (ecm->cw1cycle==0x81) { // cw1 cycle on tag=0x81
						if ( (ecm->ecm[0]==0x81)&&(ecm->lastdecode.cwcycle!='1') ) return;
						if ( (ecm->ecm[0]==0x80)&&(ecm->lastdecode.cwcycle!='0') ) return;
					}
					//
					ecm->lastdecode.ecm = oldecm;
					ecm->lastdecode.counter = oldecm->lastdecode.counter;
					// get average of ecm change time
					ecm->lastdecode.dcwchangetime = (oldecm->lastdecode.dcwchangetime*oldecm->lastdecode.counter+(ecm->recvtime-oldecm->recvtime)) / (oldecm->lastdecode.counter+1);
					ecm->lastdecode.dcwchangetime = ((ecm->lastdecode.dcwchangetime+300)/1000)*1000;
					memcpy( ecm->lastdecode.dcw, oldecm->cw, 16); // Store latest DCW
				}
			}
			else { //if (!ecm->lastdecode.ecm) {
				// maybe??
				ecm->lastdecode.ecm = oldecm;
				ecm->lastdecode.counter = oldecm->lastdecode.counter;
				ecm->lastdecode.dcwchangetime = ecm->recvtime-oldecm->recvtime;
				memcpy( ecm->lastdecode.dcw, oldecm->cw, 16); // Store latest DCW
			}
#ifdef TESTCHANNEL
			int testchannel = ( (ecm->caid==cfg.testchn.caid) && (ecm->provid==cfg.testchn.provid) && (ecm->sid==cfg.testchn.sid) );
			if (testchannel) {
				char dump[64];
				array2hex( oldecm->cw, dump, 16);
				fdebugf(" Update Cycle ch %04x:%06x:%04x %02x:%08x ( %02x:%08x %s/%d)\n",ecm->caid, ecm->provid, ecm->sid, ecm->ecm[0],ecm->hash,
					oldecm->ecm[0], oldecm->hash, dump, ecm->lastdecode.counter);
			}
#endif
		}
		else if ( (oldecm->dcwstatus==STAT_DCW_FAILED) && (oldecm->hash==ecm->hash) && (oldecm->ecm[0]==ecm->ecm[0]) && !memcmp(oldecm->ecm+3, ecm->ecm+3, ecm->ecmlen-3) ){ // the same channel but we have freeze
			memcpy( &ecm->lastdecode, &oldecm->lastdecode, sizeof(ecm->lastdecode) );
#ifdef TESTCHANNEL
			int testchannel = ( (ecm->caid==cfg.testchn.caid) && (ecm->provid==cfg.testchn.provid) && (ecm->sid==cfg.testchn.sid) );
			if (testchannel) {
				char dump[64];
				array2hex( oldecm->cw, dump, 16);
				fdebugf(" Update Cycle after freeze ch %04x:%06x:%04x:%08x (%s/%d)\n",ecm->caid, ecm->provid, ecm->sid, ecm->hash, dump, ecm->lastdecode.counter);
			}
#endif
		}
	}
}


// 1: success
// 2: inc
// 4: cwcycle
// return 0:wrong dcw, 1: good dcw
int checkfreeze_setdcw( ECM_DATA *ecm, uint8_t dcw[16] )
{
	char nullcw[8] = "\0\0\0\0\0\0\0\0";

	if (!ecm) return 0;

	if (!ecm->lastdecode.ecm) return 1; // no old successful decode, random select

	if ( dcwcmp16(dcw,ecm->lastdecode.dcw) ) return 0;

	if ( dcwcmp8(dcw,nullcw) || dcwcmp8(dcw+8,nullcw) ) return 0;

	if ( ecm->cw1cycle ) {
		if ( dcwcmp8(dcw,ecm->lastdecode.dcw) && !dcwcmp8(dcw+8,ecm->lastdecode.dcw+8) && (ecm->cw1cycle==ecm->ecm[0]) ) return 7;
		else if ( !dcwcmp8(dcw,ecm->lastdecode.dcw) && dcwcmp8(dcw+8,ecm->lastdecode.dcw+8) && (ecm->cw1cycle!=ecm->ecm[0]) ) return 3;
		else if (ecm->lastdecode.counter>0) return 0;
		else return 1;
	}

	else if ( ecm->lastdecode.cwcycle ) {
		if ( dcwcmp8(dcw,ecm->lastdecode.dcw) && !dcwcmp8(dcw+8,ecm->lastdecode.dcw+8) && (ecm->lastdecode.cwcycle=='1') ) return 7;
		else if ( !dcwcmp8(dcw,ecm->lastdecode.dcw) && dcwcmp8(dcw+8,ecm->lastdecode.dcw+8) && (ecm->lastdecode.cwcycle=='0') ) return 3;
		else if (ecm->lastdecode.counter>0) return 0;
		else return 1;
	}

	else {
		if ( dcwcmp8(dcw,ecm->lastdecode.dcw) && !dcwcmp8(dcw+8,ecm->lastdecode.dcw+8) ) return 7;
		else if ( !dcwcmp8(dcw,ecm->lastdecode.dcw) && dcwcmp8(dcw+8,ecm->lastdecode.dcw+8) ) return 3;
		else return 1;
	}

	return 0;
}


#endif