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

#ifdef WIN32

#include <winsock2.h>
#include <windows.h>
#include <winuser.h>
#include <io.h>
#include <direct.h>
#include <sys/types.h>
#include <time.h>
#include <sys/timeb.h>
#include <process.h>

#define MSG_NOSIGNAL	0

#endif

#ifdef LINUX
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
#include <sys/time.h>

#define MAX_PATH	1024

#endif


#include "remote.h"
#include "global.h"
#include "zip.h"
#include "errcode.h"
#include "network.h"
#include "dbstruct.h"
#include "lowlevel.h"

#define WORDLEN                 16
#define DATALEN                 5
#define FLASH_DATAPTR           6
#define MAXLEN                  500


#define INSTALL_OK				1
#define WRONG_LAN				2
#define UPGRADE_IRDB			4
#define UPGRADE_2X				8
#define DOWNGRADE				16
#define DEVICETYPE				32
#define NO_LAN					64
#define EEPROM_INIT				128

#define CRC_INITIAL_VALUE 0x92f3

#define MIN_LOADER_VERSION	1
#define MAX_LOADER_VERSION	2

#ifdef WIN32
extern WSAEVENT IrtLanEvent;
#endif

extern SOCKET irtlan_socket;
extern SOCKET irtlan_outbound;

#ifndef uint8
#define uint8  unsigned char
#endif

#ifndef uint32
#define uint32 unsigned long int
#endif

typedef struct
{
    uint32 esk[32];     /* DES encryption subkeys */
    uint32 dsk[32];     /* DES decryption subkeys */
}
des_context;

typedef struct
{
    uint32 esk[96];     /* Triple-DES encryption subkeys */
    uint32 dsk[96];     /* Triple-DES decryption subkeys */
}
des3_context;

int  des_set_key( des_context *ctx, uint8 key[8] );
void des_encrypt( des_context *ctx, uint8 input[8], uint8 output[8] );
void des_decrypt( des_context *ctx, uint8 input[8], uint8 output[8] );

int  des3_set_2keys( des3_context *ctx, uint8 key1[8], uint8 key2[8] );
int  des3_set_3keys( des3_context *ctx, uint8 key1[8], uint8 key2[8],
                                        uint8 key3[8] );

void des3_encrypt( des3_context *ctx,  uint8 output[8],uint8 init[8] );
void des3_decrypt( des3_context *ctx, uint8 input[8], uint8 output[8] );

unsigned char kxxx[3][8] =
{

	{ 0xDA, 0x61, 0xF1, 0x91, 0xA8, 0xAB, 0x2F, 0xBA },
	{ 0xC4, 0xCE, 0xAD, 0xC8, 0x46, 0x89, 0x02, 0x83 },
	{ 0x25, 0x08, 0x6D, 0xC7, 0x13, 0x4A, 0xCB, 0x80 }
};
unsigned char db[8] = {0xA1, 0xA3, 0xEC, 0xD9, 0xAB, 0x9A, 0x0C, 0xA7};
unsigned char x2[8] = {0xA8, 0x27, 0xCB, 0x99, 0xf0, 0x20, 0x1a, 0xcc};


/*

5.03.17		OK				Len = 21
			28.06.06		Len = 21
			27.07.06		Len = 21
			06.09.06		Len = 21
			07.09.06		Len = 24
			22.09.06		Len = 24
			28.12.06		Len = 24
			2.07.07			Len = 24
5.08.08		OK
5.09.02		OK
5.11.16		OK
5.09.07		OK				Len = 24
6.02.03		

*/

#ifndef NO_FIRMWARE

typedef struct {
	char ir_name[100];
	char ir_version[20];
	char devicetype[25];
	unsigned int *cap_pnt;
	unsigned int *serno;
	int pagesize;
	int flashlen;
	int maxmem;
	word *checksum;
	int crypt_start;
	word flashsum;
	int pages;
	byte funcflagex;
	byte *mac;

	byte net_type;
	char net_name[100];
	char net_version[20];

	int firmware_flag;

	word ir_buffer[65536 * 4];
} FIRMWARE_INFO;



#define LAN_TYPE_OLD		1
#define LAN_TYPE_NEW		2
#define LAN_TYPE_MS			3
#define LAN_TYPE_WLAN		100



int ReadFirmwareFile (struct zip *zipfile,int fileidx, unsigned short flashBuf[], long bufLimit);
char *zip_fgets (char *nm,int num,struct zip_file *fw);
int GetFirmwareInfo (char *path,char *nm,int bus,FIRMWARE_INFO *info);
int OpenIRFirmware (char *nm,FIRMWARE_INFO *info);
int GetFirmwareOffset (FIRMWARE_INFO *info);
int calc_offset (int cpuoffset,FIRMWARE_INFO *info);
void GetHardwareType (int bus);
void GetFirmwarePath (char path[]);
void SendFlashStatus (int client,char message[],int net,int level);
void SendFlashError (int client,char message[],word net,word level);
int GetLANVersions (int bus,char IRVersion[],char LANVersion[],int timeout);
int FlashIRFirmware (int bus,int client,struct zip *zipfile,FIRMWARE_INFO *info,char upg_key[]);
int FlashEthernet (int bus,int client,struct zip *zipfile,char fwname[]);
int lan_flash (int bus,int client,FIRMWARE_INFO *info);
int direct_flash (int bus,int client,FIRMWARE_INFO *info);
void WriteDeviceData (int bus);
void RemoveSecfile ();
int lan_flash_load (int bus,int client,FIRMWARE_INFO *info);
word ip_sum_calc(word len_ip_header, byte buff[]);
byte start_flash (int client,int bus,int cnt);
byte check_crc (int bus);
int init_lan (int bus);
void end_lan (int bus);
int send_lan_data (int bus,byte data[],int len);
void LANLargeSend (int bus,char data[],word len);
int GetIRVersion (DEVICEINFO *dev,char IRVersion[]);
void flash_wlan (int bus,int client,struct zip *zipfile,FIRMWARE_INFO *info);
int upload_wlanrom (int bus,int client,struct zip *zipfile,FIRMWARE_INFO *info,char fname[],byte mode);
int GetLANType (int device);
int test_html_memory (int bus);
int readFlashFilePage(struct zip *zipfile,char flashPath[], unsigned short flashBuf[8][32768], long bufLimit,word *page) ;
int GetUpgradeKey (int bus,byte initialVector[],char userkey[]);
int CheckEEPROMVersion (char oldversion[],char newversion[]);
void GetSWVersions (struct zip *zipfile,char msg[],int maxlen);
int	IRTransLanFlash (DEVICEINFO *dev,IRDATA_LAN_FLASH *ird,int len,uint32_t ip);
void SwapNetworkstatus (void *pnt);
word CRC (byte *Data, int Length,word init);


extern NETWORKCLIENT sockinfo[CLIENT_COUNT];


FIRMWARELINE *stat_fw_pnt;
int firmware_file_cnt;




int ListFirmwareFiles (int bus,short start,FIRMWAREBUFFER *buf)
{
	int res;
	int cnt,cnt_total;
	char path[MAX_PATH],nm[512];
	FIRMWARE_INFO info;

#ifdef LINUX
	off64_t off = 0;
	int fd,len,pos;
    struct dirent64 *di;
	char st[2048];
#endif

#ifdef WIN32
	struct _finddata_t c_file;
#ifdef _M_X64
    intptr_t hFile;
#else
    int hFile;
#endif
#endif



	memset (buf,0,sizeof (FIRMWAREBUFFER));
	buf->statustype = STATUS_FIRMWARELIST;
	buf->statuslen = sizeof (FIRMWAREBUFFER);
	buf->offset = start;
	cnt = cnt_total = 0;

#ifdef LINUX


	if (!start) {
		GetHardwareType (bus);
		//printf ("Type: %d\n",IRDevices[bus].fw_capabilities4);
	
		GetFirmwarePath (path);
		fd = open ("../Firmware",0);

		do {
			len = getdirentries64 (fd,st,2048,&off);

			pos = 0;
			while (pos < len) {
				di = (struct dirent64 *)&st[pos];
				cnt_total++;
				pos += di -> d_reclen;
			}
		} while (len);

		if (stat_fw_pnt) free (stat_fw_pnt);
		stat_fw_pnt = malloc (cnt_total * sizeof (FIRMWARELINE));
		firmware_file_cnt = 0;
		
		lseek (fd,0,SEEK_SET);
		
		do {
			len = getdirentries64 (fd,st,2048,&off);

			pos = 0;
			while (pos < len) {
				di = (struct dirent64 *)&st[pos];

				res = GetFirmwareInfo (path,di -> d_name,bus,&info);
				if (res != ERR_OPEN) {
					if (cnt_total >= start && cnt < 40) {
						strcpy (stat_fw_pnt[cnt].filename,di -> d_name);
						strcpy (stat_fw_pnt[cnt].ir_version,info.ir_version);
						strcpy (stat_fw_pnt[cnt].lan_version,info.net_version);
						strcpy (stat_fw_pnt[cnt].devicetype,info.devicetype);
						stat_fw_pnt[cnt].firmware_flags = info.firmware_flag;
						stat_fw_pnt[cnt].net_flag = info.net_type;
						cnt++;
					}
				}
				pos += di -> d_reclen;
			}
		} while (len);

		close (fd);
		firmware_file_cnt = cnt;
	}

#endif

#ifdef WIN32

	if (!start) {
		GetHardwareType (bus);
		//printf ("Type: %d\n",IRDevices[bus].fw_capabilities4);

		GetFirmwarePath (path);

		sprintf (nm,"%s*.zip",path);

		if((hFile = _findfirst(nm, &c_file )) != -1L) {
			do {
				cnt_total++;
			} while( _findnext( hFile, &c_file ) == 0);
			_findclose( hFile );
		}

		if (stat_fw_pnt) free (stat_fw_pnt);
		stat_fw_pnt = malloc (cnt_total * sizeof (FIRMWARELINE));
		firmware_file_cnt = 0;

		cnt = 0;
		if((hFile = _findfirst(nm, &c_file )) != -1L) {
			do {
				res = GetFirmwareInfo (path,c_file.name,bus,&info);
				if (res != ERR_OPEN) {
					strcpy (stat_fw_pnt[cnt].filename,c_file.name);
					strcpy (stat_fw_pnt[cnt].ir_version,info.ir_version);
					strcpy (stat_fw_pnt[cnt].lan_version,info.net_version);
					strcpy (stat_fw_pnt[cnt].devicetype,info.devicetype);
					stat_fw_pnt[cnt].firmware_flags = info.firmware_flag;
					stat_fw_pnt[cnt].net_flag = info.net_type;
					cnt++;
				}
			} while( _findnext( hFile, &c_file ) == 0);
			_findclose( hFile );
		}
		firmware_file_cnt = cnt;
	}

#endif
	if ((firmware_file_cnt - start) > 40) cnt = 40;
	else cnt = firmware_file_cnt - start;

	memcpy (buf->firmware,stat_fw_pnt + start,cnt * sizeof (FIRMWARELINE));

	buf->count_buffer = cnt;
	buf->count_total = firmware_file_cnt;
	if (cnt == 40) buf->count_remaining = firmware_file_cnt-cnt;
	else buf->count_remaining = 0;

	return (0);
}


void GetHardwareType (int bus)
{
	if ((IRDevices[bus].fw_capabilities4 & FN_DEVICETYPE_MASK) == FN4_DEVICETYPE_USB_3_6 && (IRDevices[bus].fw_capabilities2 & FN2_V38))
	IRDevices[bus].fw_capabilities4 = (IRDevices[bus].fw_capabilities4 & ~FN_DEVICETYPE_MASK) | FN4_DEVICETYPE_USB_3_8;

	if (IRDevices[bus].fw_capabilities4 & FN_DEVICETYPE_MASK) return;																		// Device Type set

	if (IRDevices[bus].version[0] == 'U' || IRDevices[bus].version[0] == 'V') {																// IRTrans USB
		if (IRDevices[bus].fw_capabilities2 & FN2_V38) IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_USB_3_8;
		else IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_USB_3_6;
		return;
	}
	if (IRDevices[bus].version[0] == 'S' || IRDevices[bus].version[0] == 'T') {																// IRTrans RS232
		if (IRDevices[bus].fw_capabilities2 & FN2_V38) IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_RS232_3_8;
		else IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_RS232_3_6;
		return;
	}
	if (IRDevices[bus].version[0] == 'B' || IRDevices[bus].version[0] == 'C') {																// IRTrans IR Bus
		if (IRDevices[bus].fw_capabilities2 & FN2_V38) IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_BUS_3_8;
		else IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_BUS_3_5;
		return;
	}
	if (IRDevices[bus].version[0] == 'E' || IRDevices[bus].version[0] == 'G') {																// IRTrans LAN
		if (IRDevices[bus].fw_capabilities3 & FN3_MULTISTREAM) {																			// LAN Controller MS16
			IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_LANC_MS_16;
			return;
		}
		if (IRDevices[bus].fw_capabilities & FN_DUALRCV) {																					// LAN Controller
			if (IRDevices[bus].fw_capabilities & FN_MULTISEND8) {																			// XL
				IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_LANC_XL;
				return;
			}
			if (IRDevices[bus].fw_capabilities2 & FN2_MULTISEND16) {																		// XXL
				IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_LANC_XXL;
				return;
			}
			IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_LANC;																			// LAN Controller
			return;
		}
		if (IRDevices[bus].fw_capabilities2 & FN2_MULTIRELAY2) {																			// LAN I/O
			IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_LANIO_1_5;
			return;
		}
		if (test_html_memory (bus) == -1) {																									// LAN 1.2
			IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_LAN_1_2;
			return;
		}
		if (IRDevices[bus].version[0] == 'E' && IRDevices[bus].fw_capabilities & FN_EXT_SW) {												// LAN 2.3
			IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_LAN_2_3;
			return;
		}
		IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_LAN_2_2;																			// LAN 2.2
		return;
	}
	if (IRDevices[bus].version[0] == 'W') {																									// IRTrans WLAN
		IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_WIFI_1_5;
		return;
	}
	if (IRDevices[bus].version[0] == 'X') {																									// IRTrans Translator
		if (IRDevices[bus].fw_capabilities2 & FN_MULTISEND2) {																				// Translator XL
			IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_TRANS_XL;
			return;
		}
		if (IRDevices[bus].fw_capabilities2 & FN2_EXT_RCV) {																				// Translator 3.2
			IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_TRANS_3_2;
			return;
		}
		IRDevices[bus].fw_capabilities4 |= FN4_DEVICETYPE_TRANS_2_0;
		return;

	}
		
}


int GetFirmwareInfo (char *path,char *nm,int bus,FIRMWARE_INFO *info)
{
	int len,lantype;
	char file[512],st[256];
	unsigned int oldcap;
	unsigned long function_flash_mask;
	unsigned long function_flash_mask_upgrade;

	memset (info,0,sizeof (FIRMWARE_INFO));
	memset (info->ir_buffer,0xff,sizeof (info->ir_buffer));
	info->flashsum = 0x3f65;
	function_flash_mask = FUNCTION_FLASH_MASK;

	sprintf (file,"%s%s",path,nm);
	
	len = OpenIRFirmware (file,info);
	sprintf (st,"OpenIRFirmware %s [%d]\n",file,len);
	log_print (st,LOG_DEBUG);
		
	if (len <= 0) return (ERR_OPEN);																			// Error

	GetDeviceType (info->cap_pnt,info->devicetype,25);

	oldcap = IRDevices[bus].fw_capabilities;
	oldcap &= ~FN_FLASH128;



	if (IRDevices[bus].version[0] == 'E' || IRDevices[bus].version[0] == 'G') {									// Ethernet Device
		if (*info->ir_version != 'E') return (ERR_DEVICETYPE);
		function_flash_mask &= ~FN_TRANSL;
		function_flash_mask &= ~FN_FLASH128;
//		function_flash_mask &= ~FN_HTML;
		function_flash_mask &= ~FN_SOFTID;
	}
	else if (IRDevices[bus].version[0] == 'U' || IRDevices[bus].version[0] == 'V') {							// USB Device
		if (*info->ir_version != 'U') return (ERR_DEVICETYPE);
	}
	else if (IRDevices[bus].version[0] == 'S' || IRDevices[bus].version[0] == 'T') {							// RS232 Device
		if (*info->ir_version != 'S') return (ERR_DEVICETYPE);
	}
	else if (IRDevices[bus].version[0] == 'X') {																// IR Translator
		if (*info->ir_version != 'X') return (ERR_DEVICETYPE);
	}
	else if (IRDevices[bus].version[0] == 'W') {																// IRTrans WLAN
		if (*info->ir_version != 'W') return (ERR_DEVICETYPE);
	}
	else return (ERR_DEVICETYPE);

	if (IRDevices[bus].fw_capabilities4 && (info->cap_pnt[3] & IRDevices[bus].fw_capabilities4)) {
		if ((IRDevices[bus].fw_capabilities4 & FN_DEVICETYPE_MASK) != (info->cap_pnt[3] & IRDevices[bus].fw_capabilities4)) return (ERR_DEVICETYPE);
	}

	if (memcmp (IRDevices[bus].version + 1,"6.00.00",7) < 0 && memcmp (info->ir_version + 1,"6.00.00",7) >= 0) {		// Update auf FW Version 6
		if (IRDevices[bus].version[0] == 'V') oldcap &= ~FN_EXT_SW;												// B&O Flag ausblenden
		if (IRDevices[bus].version[0] == 'T') oldcap &= ~FN_EXT_SW;												// B&O Flag ausblenden
		if (IRDevices[bus].version[0] == 'G') oldcap &= ~FN_EXT_SW;												// B&O Flag ausblenden
	}

	if (memcmp (IRDevices[bus].version + 1,info->ir_version + 1,7) >= 0) info->firmware_flag |= DOWNGRADE;


//	printf ("Dev: %x  File: %x   %s\n",oldcap,info->cap_pnt[0],nm);

	if (memcmp (IRDevices[bus].version + 1,"6.00.00",7) >= 0 && IRDevices[bus].fw_capabilities2 && 
		(info->cap_pnt[1] & FUNCTION_FLASH_MASK_2) != (IRDevices[bus].fw_capabilities2 & FUNCTION_FLASH_MASK_2)) {
			info->firmware_flag |= DEVICETYPE;
			return (ERR_DEVICETYPE);
	}

	if ((info->cap_pnt[0] & function_flash_mask) != (oldcap & function_flash_mask)) {

		function_flash_mask_upgrade = function_flash_mask & ~(FN_IRDB | FN_HTML);
		if ((info->cap_pnt[0] & function_flash_mask_upgrade) == (oldcap & function_flash_mask)) {
			info->firmware_flag |= UPGRADE_IRDB;
			goto IR_Found;
		}
	
		function_flash_mask_upgrade = function_flash_mask & ~FN_MULTISEND2;
		if ((info->cap_pnt[0] & function_flash_mask_upgrade) == (oldcap & function_flash_mask)) {
			info->firmware_flag |= UPGRADE_2X;
			goto IR_Found;
		}

		if ((info->firmware_flag & (UPGRADE_IRDB | UPGRADE_2X)) == 0) info->firmware_flag |= DEVICETYPE;

		return (ERR_DEVICETYPE);
	}
	else info->firmware_flag |= INSTALL_OK;

	// Hinweis auf EEPROM Init
	if (CheckEEPROMVersion (IRDevices[bus].version,info->ir_version)) info->firmware_flag |= EEPROM_INIT;

IR_Found:
	lantype = GetLANType (IRDevices[bus].fw_capabilities4 & FN_DEVICETYPE_MASK);

	if (lantype && !info->net_type) {
		info->firmware_flag |= NO_LAN;
		return (ERR_DEVICETYPE_NO_LAN);
	}
	if (lantype != info->net_type) {
		info->firmware_flag |= WRONG_LAN;
		return (ERR_DEVICETYPE_LAN);
	}

	return (0);
}


int GetLANType (int device)
{
	if (device == FN4_DEVICETYPE_LAN_1_2 || device == FN4_DEVICETYPE_LANC) return LAN_TYPE_OLD;
	if (device == FN4_DEVICETYPE_LAN_2_2 || device == FN4_DEVICETYPE_LAN_2_3 || device == FN4_DEVICETYPE_LAN_3 ||
		device == FN4_DEVICETYPE_LANIO_1_5 || device == FN4_DEVICETYPE_LANIO_CTL_1_5 || device == FN4_DEVICETYPE_LANC_XL ||
		device == FN4_DEVICETYPE_LANC_XXL) return LAN_TYPE_NEW;
	if (device == FN4_DEVICETYPE_LANC_MS_16) return LAN_TYPE_MS;
	if (device == FN4_DEVICETYPE_WIFI_1_5) return LAN_TYPE_WLAN;

	return 0;
}

int OpenIRFirmware (char *nm,FIRMWARE_INFO *info)
{
	int cpuoffset;
//	int flashlen = -2;
	struct zip *zipfile;
	int i,error,count,idx_IR = -1;
	byte *bbuffer;

	zipfile = zip_open (nm,0,&error);


	if (zipfile == NULL) return (-ERR_OPEN);
	

	info->flashlen = -2;
//	count = zip_get_num_entries (zipfile,0);
	count = zip_get_num_files (zipfile);

	for (i=0;i < count;i++) {
		nm = (char *)zip_get_name (zipfile,i,0);
		if (nm) {
			if (!strcmp (nm + strlen (nm) - 4,".rom")) {
				strcpy (info->ir_name,nm);
				idx_IR = i;
			}
			if (!strcmp (nm + strlen (nm) - 4,".hex") && !memcmp (nm,"eth_",4)) {
				info->net_type = LAN_TYPE_OLD;
				strcpy (info->net_name,nm);
				info->net_version[0] = 'L';
				strncpy (info->net_version + 1,nm + 4,7);
			}
			if (!strcmp (nm + strlen (nm) - 7,".hexnew") && !memcmp (nm,"eth_",4)) {
				info->net_type = LAN_TYPE_NEW;
				strcpy (info->net_name,nm);
				info->net_version[0] = 'L';
				strncpy (info->net_version + 1,nm + 4,7);
			}
			if (!strcmp (nm + strlen (nm) - 7,".hexmst") && !memcmp (nm,"eth_",4)) {
				info->net_type = LAN_TYPE_MS;
				strcpy (info->net_name,nm);
				info->net_version[0] = 'L';
				strncpy (info->net_version + 1,nm + 4,7);
			}
			if (!strcmp (nm + strlen (nm) - 5,".info") && !memcmp (nm,"wifi_",5)) {
				info->net_type = LAN_TYPE_WLAN;
				strcpy (info->net_name,nm);
				info->net_version[0] = 'R';
				strncpy (info->net_version + 1,nm + 5,strlen (nm) - 10);
			}
		}
	}

	if (idx_IR == -1) return (-ERR_FORMAT);


	info->flashlen = ReadFirmwareFile (zipfile,idx_IR,info->ir_buffer,131072) + 1;
	zip_close (zipfile);

	if (info->flashlen == 2) return (-ERR_FORMAT);

	cpuoffset = GetFirmwareOffset (info);

	if (cpuoffset <= 0) return (-ERR_FORMAT);

	bbuffer = (byte *)info->ir_buffer;
	info->mac = bbuffer + cpuoffset + 20;

	if (*info->ir_version == 'B' || *info->ir_version == 'C') info->flashsum = 0x5d37;

	if (*info->ir_version == 'E' || *info->ir_version == 'G') info->flashsum = 0x2f25;

	if (*info->ir_version == 'E' && info->pagesize == 256) {	// LAN Controller
		info->flashsum = 0xa643;
		info->maxmem = 0xF000;
	}

	if (*info->ir_version == 'F' && info->pagesize == 256) {	// LAN Controller
		info->flashsum = 0x8b24;
		info->maxmem = 0xF000;
	}
	if (*info->ir_version == 'W') info->flashsum = 0xa643;


	info->pages = info->flashlen / (info->pagesize / 2);
	if (info->flashlen % (info->pagesize / 2)) info->pages++;

	return (info->flashlen);
}

int GetFirmwareOffset (FIRMWARE_INFO *info)
{
	int cpuoffset;

	info->pagesize = 128;
	info->maxmem = 0x3800;
	info->crypt_start = 256;

	if (cpuoffset = calc_offset (84,info)) {
		if (*info->ir_version == 'E' || *info->ir_version == 'G' || *info->checksum == 1) {
			*info->checksum = 0;
			info->maxmem = 0x7800;
		}
		if (cpuoffset == 84 + 12) info->funcflagex = 1;
	}

	else if (cpuoffset = calc_offset (104,info)) {
		*info->checksum = 0;
		info->maxmem = 0x7000;
		if (cpuoffset == 104 + 12) info->funcflagex = 1;
	}

	else if (cpuoffset = calc_offset (140,info)) {
		if (*info->ir_version == 'W') {							// Micro WiFi
			info->pagesize = 256;
			info->maxmem = 0x1F000;
		}
		else {
			info->pagesize = 256;
			info->maxmem = 0xF800;
		}
		if (cpuoffset == 140 + 12) info->funcflagex = 1;
	}

	else if (cpuoffset = calc_offset (112,info)) {
		info->pagesize = 128;
		info->maxmem = 0x3800;
		if (cpuoffset == 112 + 12) info->funcflagex = 1;
	}

	else if (cpuoffset = calc_offset (124,info)) {
		info->pagesize = 256;
		info->maxmem = 0xF000;
		if (cpuoffset == 124 + 12) info->funcflagex = 1;
	}
	
	else if (cpuoffset = calc_offset (204,info)) {
		info->pagesize = 256;
		info->maxmem = 0x3F000;
		info->crypt_start = 512;
		if (cpuoffset == 204 + 12) info->funcflagex = 1;
	}

	else if (cpuoffset = calc_offset (0xf4,info)) {
		info->pagesize = 256;
		info->maxmem = 0x3F000;
		info->crypt_start = 512;
		if (cpuoffset == 0xf4 + 12) info->funcflagex = 1;
	}

	if (cpuoffset && (*info->checksum != 0 || *info->serno != 0x11223344)) return (-1);

	return (cpuoffset);
}

int test_html_memory (int bus)
{
	int p;
	IRDATA_LAN_FLASH ird;

	memset (&ird,1,sizeof (IRDATA_LAN_FLASH));

	ird.adr = 0;
	ird.len = 0;

	ird.netcommand = COMMAND_FLASH_HTML;
#ifdef WIN32
	p = IRTransLanFlash (IRDevices + bus,&ird,195,IRDevices[bus].io.IPAddr[0].sin_addr.S_un.S_addr);
#else
	p = IRTransLanFlash (IRDevices + bus,&ird,195,IRDevices[bus].io.IPAddr[0].sin_addr.s_addr);
#endif

	return (p);
}



char *zip_fgets (char *nm,int num,struct zip_file *fw)
{
	int res,i = 0;

	while (i < num - 1) {
		res = (int)zip_fread (fw,nm + i,1);
		if (res != 1) {
			if (!i) return (NULL);
			nm[++i] = 0;
			return (nm);
		}
		if (nm[i] == '\n') {
			nm[i++] = 0;
			return (nm);
		}
		i++;
	}
	nm[i] = 0;
	return (nm);
}

int calc_offset (int cpuoffset,FIRMWARE_INFO *info)
{
	byte *bbuffer = (byte *)info->ir_buffer;

	memcpy (info->ir_version,(char *)(bbuffer + cpuoffset),8);
	info->cap_pnt = (unsigned int *)(bbuffer + cpuoffset + 8);
	info->serno = (unsigned int *)(bbuffer + cpuoffset + 12);
	info->checksum = (unsigned short *)(bbuffer + cpuoffset + 18);

	if ((*info->checksum == 0 || *info->checksum == 1) && *info->serno == 0x11223344) {
		return (cpuoffset);
	}

	cpuoffset += 12;

	info->serno = (unsigned int *)(bbuffer + cpuoffset + 12);
	info->checksum = (unsigned short *)(bbuffer + cpuoffset + 18);

	if ((*info->checksum == 0 || *info->checksum == 1) && *info->serno == 0x11223344) {
		return (cpuoffset);
	}

	return (0);
}

/* EEPROM Versions:

11.06.11			6.07.10					19
03.06.09			6.04.01					18
03.11.08			5.20.01					17
07.01.07			5.05.01					16
23.08.06			5.04.33					15
28.06.06			5.04.23					14 */


int CheckEEPROMVersion (char oldversion[],char newversion[])
{
	if (memcmp (oldversion + 1,"6.07.10",7) < 0 && memcmp (newversion + 1,"6.07.10",7) >= 0) return (1);		// Upgrade
	if (memcmp (oldversion + 1,"6.04.01",7) < 0 && memcmp (newversion + 1,"6.04.01",7) >= 0) return (1);
	if (memcmp (oldversion + 1,"5.20.01",7) < 0 && memcmp (newversion + 1,"5.20.01",7) >= 0) return (1);
	if (memcmp (oldversion + 1,"5.05.01",7) < 0 && memcmp (newversion + 1,"5.05.01",7) >= 0) return (1);
	if (memcmp (oldversion + 1,"5.04.33",7) < 0 && memcmp (newversion + 1,"5.04.33",7) >= 0) return (1);
	if (memcmp (oldversion + 1,"5.04.23",7) < 0 && memcmp (newversion + 1,"5.04.23",7) >= 0) return (1);

	if (memcmp (oldversion + 1,"6.07.10",7) >= 0 && memcmp (newversion + 1,"6.07.10",7) < 0) return (1);		// Downgrade
	if (memcmp (oldversion + 1,"6.04.01",7) >= 0 && memcmp (newversion + 1,"6.04.01",7) < 0) return (1);
	if (memcmp (oldversion + 1,"5.20.01",7) >= 0 && memcmp (newversion + 1,"5.20.01",7) < 0) return (1);
	if (memcmp (oldversion + 1,"5.05.01",7) >= 0 && memcmp (newversion + 1,"5.05.01",7) < 0) return (1);
	if (memcmp (oldversion + 1,"5.04.33",7) >= 0 && memcmp (newversion + 1,"5.04.33",7) < 0) return (1);
	if (memcmp (oldversion + 1,"5.04.23",7) >= 0 && memcmp (newversion + 1,"5.04.23",7) < 0) return (1);

	return (0);
}

void GetSWVersions (struct zip *zipfile,char msg[],int maxlen)
{
	int len;
	struct zip_file *zfp;

	msg[0] = 0;

	zfp = zip_fopen(zipfile,"sw_versions",0);
	if (zfp == NULL) return;

	len = (int)zip_fread (zfp,msg,maxlen - 1);
	msg[len] = 0;

	zip_fclose (zfp);
}

// Tests:
// Linux:
// - USB					OK
// - LAN New				OK
// - LAN Old				OK
// - WiFi					OK
// - RS232

// Windows x86				OK

// Windows:
// - USB					OK
// - LAN New				OK
// - LAN Old				OK
// - WiFi					OK
// - RS232					OK


int FlashDevice (int bus,char filename[],char upg_key[],NETWORKSTATUS *stat,int client)
{
	int len,res = 0;
	int error;
	char file[512];
	char path[MAX_PATH];
	FIRMWARE_INFO info;
	struct zip *zipfile;
	NETWORKSTATUS nstat;

	GetHardwareType (bus);
	GetFirmwarePath (path);

	res = GetFirmwareInfo (path,filename,bus,&info);

	if (IRDevices[bus].io.if_type != IF_USB && IRDevices[bus].version[0] == 'W') {
		SendFlashError (client,"Update of WiFi devices only supported via USB",ERR_UPDATE,FATAL);
		return (0);
	}

	if (res && res != ERR_DEVICETYPE_NO_LAN) {
		SendFlashError (client,"Wrong Firmwarefile for this device",ERR_UPDATE,FATAL);
		return (res);
	}

	sprintf (file,"%s%s",path,filename);
			
	
	zipfile = zip_open (file,0,&error);
	if (zipfile == NULL) {
		SendFlashError (client,"Cannot open FW Update package",ERR_UPDATE,FATAL);
		return (ERR_OPEN);
	}

	if (info.firmware_flag & UPGRADE_IRDB) {
		if (GetUpgradeKey (bus,db,upg_key)) {
			SendFlashError (client,"Wrong upgrade key",ERR_UPDATE,FATAL);
			zip_close (zipfile);
			return (0);
		}
	}
	
	if (info.firmware_flag & UPGRADE_2X) {
		if (GetUpgradeKey (bus,x2,upg_key)) {
			SendFlashError (client,"Wrong upgrade key",ERR_UPDATE,FATAL);
			zip_close (zipfile);
			return (0);
		}
	}

	if (!(info.firmware_flag & INSTALL_OK) && !(info.firmware_flag & UPGRADE_IRDB) && !(info.firmware_flag & UPGRADE_2X) && 
		!(info.firmware_flag & NO_LAN)     && !(info.firmware_flag & DOWNGRADE)) {
		SendFlashError (client,"Wrong Firmwarefile for this device",ERR_UPDATE,FATAL);
		zip_close (zipfile);
		return (0);
	}

	// Hinweis auf Server / GUI Client SW Versionen
	GetSWVersions (zipfile,path,250);

	file[0] = 0;
	if (path[0]) {
		strcpy (file,"\nMinimum Software Versions for this Firmware Version:\n\n");
		strcat (file,path);
		strcat (file,"\n\nIf the installed SW is not current please update after installing the new Firmware.");
		SendFlashError (client,file,0,INFORMATION);
	}


	memset (&nstat,0,sizeof (NETWORKSTATUS));
	nstat.statuslen = 8;
	nstat.clientid = sockinfo[client].clientid;
	len = nstat.statuslen;
	SwapNetworkstatus (&nstat);
	send (sockinfo[client].fd,((char *)&nstat),len,MSG_NOSIGNAL);

	if (!(info.firmware_flag & NO_LAN) && info.net_type >= LAN_TYPE_OLD && info.net_type <= LAN_TYPE_MS) {				// Flash Ethernet
		res = FlashEthernet (bus,client,zipfile,info.net_name);
		if (res) {
			zip_close (zipfile);
			return (0);
		}
	}

	WriteDeviceData (bus);

	res = FlashIRFirmware (bus,client,zipfile,&info,upg_key);
	if (res) {
		zip_close (zipfile);
		return (0);
	}

	if (info.ir_version[0] == 'W') flash_wlan (bus,client,zipfile,&info);

	// Nur ohne Error
	SendFlashError (client,"Firmware Update finished",0,INFORMATION);

	// Capabilities nach Upgrade neu lesen
	res = IRDevices[bus].fw_capabilities & FN_FLASH128;						// Save Flash Memory Size
	IRDevices[bus].fw_capabilities = info.cap_pnt[0] | res;
	IRDevices[bus].fw_capabilities2 = info.cap_pnt[1];
	IRDevices[bus].fw_capabilities3 = info.cap_pnt[2];
	IRDevices[bus].fw_capabilities4 = info.cap_pnt[3];

	zip_close (zipfile);
	return (0);
}

int GetUpgradeKey (int bus,byte initialVector[],char userkey[])
	{
	int i,p,val;
	char st[10];
	byte upkey[10];
	des3_context ctx3;
	unsigned char inikey[8];

	upkey[0] = IRDevices[bus].mac_adr[3];
	upkey[1] = IRDevices[bus].mac_adr[1];
	upkey[2] = IRDevices[bus].mac_adr[2];
	upkey[3] = IRDevices[bus].mac_adr[5];
	upkey[4] = IRDevices[bus].mac_adr[0];
	upkey[5] = IRDevices[bus].mac_adr[4];
	upkey[6] = 0x77;
	upkey[7] = 224;


	memset (&ctx3,0,sizeof (ctx3));
	memcpy( inikey, initialVector, 8 );
	des3_set_3keys( &ctx3, kxxx[0],kxxx[1],kxxx[2] );

	des3_encrypt( &ctx3,upkey,inikey);

	p = 0;
	st[3] = 0;
	for (i=0;i < 8;i++) {
		while ((size_t)p < strlen (userkey) && userkey[p] < '0') p++;
		memcpy (st,userkey + p,2);
		p += 2;
		val = strtol (st,NULL,16);
		if (val != upkey[i]) return (1);
	}

	return (0);
}

void flash_wlan (int bus,int client,struct zip *zipfile,FIRMWARE_INFO *info)
{
	int len;
	byte mem[1024];
	struct zip_file *zfp;

	zfp = zip_fopen(zipfile,"taim1",0);
	if (zfp == NULL) {
		SendFlashError (client,"Error opening WLAN FW File",ERR_UPDATE,FATAL);
		return;
	}
	len = (int)zip_fread (zfp,mem,1024);
	if (len != 1024) {
		SendFlashError (client,"Error reading WLAN FW File",ERR_UPDATE,FATAL);
		zip_fclose (zfp);
		return;
	}
	zip_fclose (zfp);

	zfp = zip_fopen(zipfile,"taim2",0);
	if (zfp == NULL) {
		SendFlashError (client,"Error opening WLAN FW File",ERR_UPDATE,FATAL);
		return;
	}
	len = (int)zip_fread (zfp,mem,1024);
	if (len != 1024) {
		SendFlashError (client,"Error reading WLAN FW File",ERR_UPDATE,FATAL);
		zip_fclose (zfp);
		return;
	}
	zip_fclose (zfp);

	zfp = zip_fopen(zipfile,"tadm1",0);
	if (zfp == NULL) {
		SendFlashError (client,"Error opening WLAN FW File",ERR_UPDATE,FATAL);
		return;
	}
	len = (int)zip_fread (zfp,mem,1024);
	if (len != 1024) {
		SendFlashError (client,"Error reading WLAN FW File",ERR_UPDATE,FATAL);
		zip_fclose (zfp);
		return;
	}
	zip_fclose (zfp);

	zfp = zip_fopen(zipfile,"tadm2",0);
	if (zfp == NULL) {
		SendFlashError (client,"Error opening WLAN FW File",ERR_UPDATE,FATAL);
		return;
	}
	len = (int)zip_fread (zfp,mem,1024);
	if (len != 1024) {
		SendFlashError (client,"Error reading WLAN FW File",ERR_UPDATE,FATAL);
		zip_fclose (zfp);
		return;
	}
	zip_fclose (zfp);

	if (upload_wlanrom (bus,client,zipfile,info,"taim1",2)) return;
	if (upload_wlanrom (bus,client,zipfile,info,"taim2",3)) return;
	if (upload_wlanrom (bus,client,zipfile,info,"tadm1",4)) return;
	if (upload_wlanrom (bus,client,zipfile,info,"tadm2",5)) return;

	// WiFi Version auslesen
}

/*
64742
Flash Upload done: 1   1	
Flash Transfer done: 2    20		3407
Flash Activate done: 3   47			2398

28590
Uploading page 0 - Done.  1350155149
Flash Upload done: 1   1
Flash Transfer done: 2    9
Flash Activate done: 3   22

24576
Uploading page 0 - Done.  1350155174
Flash Upload done: 1   0
Flash Transfer done: 2    6
Flash Activate done: 3   16

36864
Uploading page 0 - Done.  1350155195
Flash Upload done: 1   0
Flash Transfer done: 2    10
Flash Activate done: 3   26
*/

int upload_wlanrom (int bus,int client,struct zip *zipfile,FIRMWARE_INFO *info,char fname[],byte mode)
{
	char st[100];
	struct zip_file *zfp;
	byte flashmem[65536 * 2];
	WIFI_FLASH_TABLE *tbl;
	int cnt,pnt,flash_end,len,res;
	int stat_cnt,stat_step,stat_next,stat_pos;

	SendFlashStatus (client,"Updating WiFi Firmware",3,0);

	sprintf (st,"Sending File: %s",fname);
	SendFlashStatus (client,st,4,0);

	memset (flashmem,0,sizeof (flashmem));

	tbl = (WIFI_FLASH_TABLE *)flashmem;

	zfp = zip_fopen(zipfile,fname,0);

	strcpy (tbl->name,fname);

	tbl->len = (unsigned int)zip_fread (zfp,tbl->data,65536 * 2 - 12);
	zip_fclose (zfp);

	flash_end = tbl->len + sizeof (WIFI_FLASH_TABLE) - 2;

	tbl->type = FLASH_TYPE_WLAN;
	
	tbl->crc = CRC (tbl->data,tbl->len,CRC_INITIAL_VALUE);


	pnt = 0;
	cnt = 0;
	res = 0;

	stat_cnt = flash_end / 128;
	stat_step = stat_cnt / 50;
	stat_pos = 0;
	stat_next = 0;

	while (pnt < flash_end) {
		len = 128;
		if (pnt + len > flash_end) len = flash_end - pnt;
//		printf ("Uploading page %d%c",cnt++,13);
//		fflush (stdout);

		if (stat_pos >= stat_next) {
			SendFlashStatus (client,"Uploading WiFi Firmware",1,20 * stat_pos / stat_step);
			stat_next += stat_step;
		}

		if ((pnt + 128) >= flash_end) {
			res |= TransferFlashdataEx (bus,(word *)(flashmem+pnt),pnt / 2,len,mode,0);
		}
		else res |= TransferFlashdataEx (bus,(word *)(flashmem+pnt),pnt / 2,len,0,0);
		pnt += 128;
		stat_pos++;
	}
	
//	printf ("Uploading page %d - Done.  %d\n",cnt,len);
	if (res) SendFlashError (client,"A flash error occured - the procedure needs to be repeated",ERR_UPDATE,FATAL);
	if (res) return (res);

	cnt = ReadIRStringEx (IRDevices + bus,flashmem,1,1000);

	if (!cnt || *flashmem > 200) {
		SendFlashError (client,"A flash upload error occured - the procedure needs to be repeated",ERR_UPDATE,FATAL);
		return (1);
	}

	len = 1000 /  (flash_end / 1703);
	for (cnt=0;cnt < 1000;cnt += len) {
		SendFlashStatus (client,"Transferrig WiFi Firmware",1,cnt);
		msSleep (500);
	}

	cnt = ReadIRStringEx (IRDevices + bus,flashmem,1,100000);

	if (!cnt || *flashmem > 200) {
		SendFlashError (client,"A flash upload error occured - the procedure needs to be repeated",ERR_UPDATE,FATAL);
		return (1);
	}

	len = 1000 /  (flash_end / 1199);
	for (cnt=0;cnt < 1000;cnt += len) {
		SendFlashStatus (client,"Writing WiFi Firmware",1,cnt);
		msSleep (500);
	}

	cnt = ReadIRStringEx (IRDevices + bus,flashmem,1,100000);

	SendFlashStatus (client,"Writing WiFi Firmware - Done",1,1000);

	return (0);
}



int FlashIRFirmware (int bus,int client,struct zip *zipfile,FIRMWARE_INFO *info,char upg_key[])
{
	int oldsum,newsum;
	word *pnt;

	oldsum = 0;

	if (memcmp (IRDevices[bus].version + 1,"6.00.00",7) < 0 && memcmp (info->ir_version + 1,"6.00.00",7) >= 0) {
		info->cap_pnt[2] |= 10;																															// Default
		if (*IRDevices[bus].version == 'E' && (IRDevices[bus].fw_capabilities & FN_DUALRCV)) info->cap_pnt[2] = (info->cap_pnt[2] & ~0xff) | 0x4A;		// LAN Controller
		if (*IRDevices[bus].version == 'X') info->cap_pnt[2] = (info->cap_pnt[2] & ~0xff) | 0xCA;														// Translator
		if (*IRDevices[bus].version == 'Y') info->cap_pnt[2] = (info->cap_pnt[2] & ~0xff) | 0xD2;														// Detoma

		if (*IRDevices[bus].version == 'V') {
			IRDevices[bus].fw_capabilities &= ~FN_EXT_SW;						// B&O Flag ausblenden
			*IRDevices[bus].version = 'U';
			info->cap_pnt[2] = (info->cap_pnt[2] & ~0xff) | 12;
		}
		if (*IRDevices[bus].version == 'T') {
			IRDevices[bus].fw_capabilities &= ~FN_EXT_SW;						// B&O Flag ausblenden
			*IRDevices[bus].version = 'S';
			info->cap_pnt[2] = (info->cap_pnt[2] & ~0xff) | 12;
		}
		if (*IRDevices[bus].version == 'G') {
			IRDevices[bus].fw_capabilities &= ~FN_EXT_SW;						// B&O Flag ausblenden
			*IRDevices[bus].version = 'E';
			info->cap_pnt[2] = (info->cap_pnt[2] & ~0xff) | 12;
		}
		if (*IRDevices[bus].version == 'C') {
			IRDevices[bus].fw_capabilities &= ~FN_EXT_SW;						// B&O Flag ausblenden
			*IRDevices[bus].version = 'B';
			info->cap_pnt[2] = (info->cap_pnt[2] & ~0xff) | 12;
		}
		oldsum += info->cap_pnt[2] & 0xff;
	}
	if (memcmp (IRDevices[bus].version + 1,"6.00.00",7) >= 0 && memcmp (info->ir_version + 1,"6.00.00",7) >= 0) {
		info->cap_pnt[2] = (info->cap_pnt[2] & ~0xff) | (IRDevices[bus].fw_capabilities3 & 0xff);
		oldsum += info->cap_pnt[2] & 0xff;
	}

	if (*info->ir_version != *IRDevices[bus].version) {
		SendFlashError (client,"Wrong Firmwarefile for this device",ERR_UPDATE,FATAL);
		return (ERR_DEVICETYPE);
	}


	if (!memcmp (info->ir_version,IRDevices[bus].version,8) && !(info->firmware_flag & UPGRADE_IRDB) && !(info->firmware_flag & UPGRADE_2X)) {
		SendFlashStatus (client,"IR Firmware is current - no Update",2,0);
		return (0);
	}

	pnt = (word *)info->serno;
	newsum = pnt[0] + pnt[1];

	pnt = (word *)info->mac;

	if (IRDevices[bus].io.if_type == IF_LAN) newsum += pnt[0] + pnt[1] + pnt[2];

	pnt = (word *)&IRDevices[bus].fw_serno;
	oldsum += pnt[0] + pnt[1];
	
	pnt = (word *)IRDevices[bus].mac_adr;

	if (IRDevices[bus].io.if_type == IF_LAN) oldsum += pnt[0] + pnt[1] + pnt[2];

	*info->checksum = (word)0 - (word)oldsum + (word)newsum;
	*info->serno = IRDevices[bus].fw_serno;
	
	if (IRDevices[bus].io.if_type == IF_LAN) {
		memcpy (info->mac,IRDevices[bus].mac_adr,6);
		return (lan_flash (bus,client,info));
	}

	return (direct_flash (bus,client,info));
}

int FlashEthernet (int bus,int client,struct zip *zipfile,char fwname[])
{
	char IRVersion[10];
	char LANVersion[10];
	char st[256];
	byte *flashmem;
	byte *mem[8];
	byte *rawmem;
	word page[8];
	FLASH_TABLE *tbl;
	int stat_cnt,stat_step,stat_next,stat_pos;
	int cnt,max,pnt,start,dir,flash_end,pages,pg,len,res = 0;

	sprintf (st,"Flash ETH %s - %x\n",fwname,zipfile);
	log_print (st,LOG_DEBUG);

	if (!memcmp (fwname + 4,IRDevices[bus].lan_version + 1,7)) {
		SendFlashStatus (client,"Ethernet Firmware is current - no Update",2,0);
		return (0);
	}

	rawmem = malloc (8 * 65536);
	memset (rawmem,0xff,8 * 65536);
	for (cnt=0;cnt < 8;cnt++) mem[cnt] = rawmem + cnt * 65536;

	flashmem = malloc (65536);
	memset (flashmem,0,65536);
	tbl = (FLASH_TABLE *)flashmem;

	max = 0x10000;

	pages = readFlashFilePage (zipfile,fwname,(unsigned short (*)[32768])rawmem,32768,page);

	if (pages == -1) {
		free (rawmem);
		free (flashmem);
		SendFlashError (client,"Unable to open flashfile",ERR_UPDATE,FATAL);
		return (ERR_OPEN);
	}

	pg = dir = 0;
	tbl->type = FLASH_TYPE_LAN;
	tbl->dir[0].flash_adr = FLASH_TABLE_LEN;

	while (pg <= pages) {
		pnt = 0;

		while (pnt < max) {
			while (mem[pg][pnt] == 0xff  && pnt < max) pnt++;
			if (pnt == max) break;
			start = pnt;

//			printf ("\nBlock: 0x%x (%d)\n",start,pg);

			while (mem[pg][pnt] != 0xff || mem[pg][pnt+1] != 0xff || mem[pg][pnt+2] != 0xff || mem[pg][pnt+3] != 0xff || mem[pg][pnt+4] != 0xff || mem[pg][pnt+5] != 0xff) pnt++;

			tbl->dir[dir].page = page[pg];
			tbl->dir[dir].adr = start;
			tbl->dir[dir].len = pnt - start;

//			printf ("%5x  %5x  %5x\n",tbl->dir[dir].adr,tbl->dir[dir].len,tbl->dir[dir].flash_adr);
			
			flash_end = (tbl->dir[dir+1].flash_adr = tbl->dir[dir].flash_adr + (pnt + 1 - start) / 2) * 2;
			memcpy (flashmem+(tbl->dir[dir].flash_adr*2),mem[pg]+start,pnt-start);
			dir++;

//			printf ("End: 0x%x / LEN: 0x%x\n",pnt,pnt-start);
		}
		pg++;
	}

	pg = 0;
	while (tbl->dir[pg].len) {
		tbl->dir[pg].crc = CRC (flashmem+(tbl->dir[pg].flash_adr*2),tbl->dir[pg].len,CRC_INITIAL_VALUE);
		pg++;
	}

	tbl->crc = CRC ((byte *)&tbl->type,sizeof (FLASH_TABLE)-2,CRC_INITIAL_VALUE);

	pnt = 0;
	cnt = 0;

	stat_cnt = flash_end / 128;
	stat_step = stat_cnt / 50;
	stat_pos = 0;
	stat_next = 0;

	SendFlashStatus (client,"Updating Ethernet Firmware",2,0);

	while (pnt < flash_end) {
		len = 128;
		if (pnt + len > flash_end) len = flash_end - pnt;
		if (stat_pos >= stat_next) {
			SendFlashStatus (client,"Uploading Ethernet Firmware",1,stat_pos / stat_step);
			stat_next += stat_step;
		}

		if ((pnt + 128) >= flash_end) res |= TransferFlashdataEx (bus,(word *)(flashmem+pnt),pnt / 2,len,1 /* AUTOSTART*/,0);
		else res |= TransferFlashdataEx (bus,(word *)(flashmem+pnt),pnt / 2,len,0,0);
		pnt += 128;
		stat_pos ++;
	}
	
	if (res) {
		SendFlashError (client,"An upload error occured - the procedure needs to be repeated",ERR_UPDATE,FATAL);
		free (rawmem);
		free (flashmem);
		return (ERR_UPDATE);
	}

	for (cnt=0;cnt < 50;cnt++) {
		SendFlashStatus (client,"Writing Ethernet Firmware",1,cnt);
		msSleep (600);
	}
	res = 1;
	for (cnt=0;cnt < 50 && res;cnt++) {
		SendFlashStatus (client,"Verifying Ethernet Firmware",1,cnt);
		res = GetLANVersions (bus,IRVersion,LANVersion,1000);
	}
	if (res || memcmp (fwname + 4,LANVersion + 1,7)) {
		SendFlashError (client,"The Firmwareupdate did not work.",ERR_UPDATE,FATAL);
		free (rawmem);
		free (flashmem);
		return (ERR_UPDATE);
	}

	SendFlashStatus (client,"Verifying Ethernet Firmware - Done.",1,50);
	SendFlashStatus (client,"Updating Ethernet Firmware - Done",2,0);
	strcpy (IRDevices[bus].lan_version,LANVersion);

	sprintf (st,"Flash ETH Done: %s\n",LANVersion);
	log_print (st,LOG_DEBUG);

	free (rawmem);
	free (flashmem);

	return (0);
}

void SendFlashError (int client,char message[],word net,word level)
{
	int len;
	NETWORKSTATUS stat;

	memset (&stat,0,sizeof (NETWORKSTATUS));

	stat.clientid = sockinfo[client].clientid;
	stat.statuslen = sizeof (NETWORKSTATUS);
	stat.statustype = STATUS_MESSAGE;
	stat.netstatus = net;
	stat.statuslevel = level;
	strcpy (stat.message,message);

	len = stat.statuslen;
	SwapNetworkstatus (&stat);

	send (sockinfo[client].fd,((char *)&stat),len,MSG_NOSIGNAL);
}


void SendFlashStatus (int client,char message[],int net,int level)
{
	int len;
	NETWORKSTATUS stat;

	memset (&stat,0,sizeof (NETWORKSTATUS));

	stat.clientid = sockinfo[client].clientid;
	stat.statuslen = sizeof (NETWORKSTATUS);
	stat.statustype = STATUS_FIRMWAREFLASH;
	stat.netstatus = net;
	stat.statuslevel = level;
	strcpy (stat.message,message);

	len = stat.statuslen;
	SwapNetworkstatus (&stat);

	send (sockinfo[client].fd,((char *)&stat),len,MSG_NOSIGNAL);
}

void GetFirmwarePath (char path[])
{
#ifdef WIN32
	strcpy (path,"..\\Firmware\\");
#endif
#ifdef LINUX
	strcpy (path,"../Firmware/");
#endif
}

int direct_flash (int bus,int client,FIRMWARE_INFO *info)
{
	byte magic[20];
	byte usbbuffer[300];
	byte pversion;
	byte *bbuffer;
	char IRVersion[10];
	int i,t1,t2,res,pos,cnt;
	int stat_step,stat_pos;

	for (t1=0;t1 < 10;t1++) {
		ResetTransceiverEx (bus);

		magic[0] = 0;
		magic[1] = 0;
		magic[2] = 0;
		magic[3] = 0;
		magic[4] = 0;
		magic[5] = 0;
		magic[6] = 0;
		magic[7] = 0x66;
		magic[8] = 0x77;
		magic[9] = 1;
		magic[10] = 0xFA;
		magic[11] = 0x43;

		if (IRDevices[bus].io.if_type == IF_USB && (info->cap_pnt[3] & 63) != FN4_DEVICETYPE_MICRO_WIFI) WriteIRStringEx (IRDevices + bus,magic,12);
		else {
			WriteIRStringEx (IRDevices + bus,magic + 6,1);
			msSleep (100);
			WriteIRStringEx (IRDevices + bus,magic + 5,7);
		}

		t2 = 0;
		do {
			t2++;
			*magic = 0;
			res = ReadIRStringEx (IRDevices + bus,magic,1,1000);
		} while (t2 < 5 && res && (*magic < MIN_LOADER_VERSION || *magic > MAX_LOADER_VERSION));

		if (res && *magic >= MIN_LOADER_VERSION && *magic <= MAX_LOADER_VERSION) break;
	}
	
	if (!res || (*magic < MIN_LOADER_VERSION || *magic > MAX_LOADER_VERSION)) {
		SendFlashError (client,"Could not find device. Aborting.",0,FATAL);
		return (1);
	}
	pversion = *magic;

	pos = 0;
	bbuffer = (byte *)info->ir_buffer;

	usbbuffer[0] = 0x77;
	usbbuffer[1] = 0;
	usbbuffer[2] = 0;
	usbbuffer[3] = 0;

	SendFlashStatus (client," ",4,0);

	stat_step = 1000 / (info->maxmem / info->pagesize);
	stat_pos = 0;
	if (stat_step == 0) stat_step = 1;

	for (i=0;i < info->pages;i++) {

		if (pos < info->crypt_start || (i == (info->pages - 1) && (info->flashlen % (info->pagesize / 2)))) usbbuffer[3] = 0;
		else usbbuffer[3] = 0x44;

		usbbuffer[1] = i & 255;
		usbbuffer[2] = (i & 0xff00) >> 8;
		memcpy (usbbuffer+4,bbuffer+pos,info->pagesize);

		SendFlashStatus (client,"Flashing IR Firmware",1,stat_pos);
		stat_pos += stat_step;

		WriteIRStringEx (IRDevices + bus,usbbuffer,info->pagesize + 4);
		res = ReadIRStringEx (IRDevices + bus,magic,1,10000);

		if (*magic != pversion) {
			SendFlashError (client,"Error programming the device....abort.",ERR_UPDATE,FATAL);
			return (1);
		}

		pos += info->pagesize;
	}

	memset (usbbuffer+4,0xff,info->pagesize);
	usbbuffer[3] = 0;
	while (i < (info->maxmem / info->pagesize)) {
		usbbuffer[1] = i & 255;
		usbbuffer[2] = (i & 0xff00) >> 8;

		WriteIRStringEx (IRDevices + bus,usbbuffer,info->pagesize + 4);
		res = ReadIRStringEx (IRDevices + bus,magic,1,10000);

		if (*magic != pversion) {
			SendFlashError (client,"Error programming the device....abort.",ERR_UPDATE,FATAL);
			return (1);
		}

		SendFlashStatus (client,"Flashing IR Firmware",1,stat_pos);
		stat_pos += stat_step;

		pos += info->pagesize;
		i++;
	}

	SendFlashStatus (client,"Flashing IR Firmware",1,1000);
	SendFlashStatus (client,"Flashing IR Firmware - Done",2,0);

	SendFlashStatus (client,"Waiting for the device to reboot",1,0);

	res = 1;
	for (cnt=0;cnt < 1000 && res;cnt += 25) {
		SendFlashStatus (client,"Waiting for the device to reboot",1,cnt);
		res = GetIRVersion (IRDevices + bus,IRVersion);
	}

	if (res || memcmp (info->ir_version,IRVersion,8)) {
		SendFlashError (client,"The Firmwareupdate did not work.",ERR_UPDATE,FATAL);
		return (1);
	}
	memcpy (IRDevices[bus].version,IRVersion,8);

	return (0);
}


int lan_flash (int bus,int client,FIRMWARE_INFO *info)
{
	char IRVersion[10];
	char LANVersion[10];
	int i,pos,res,cnt;
	byte usbbuffer[300];
	byte *bbuffer;
	int stat_step,stat_pos;


	if (info->net_type >= LAN_TYPE_NEW) {
		res = lan_flash_load (bus,client,info);
		return res;
	}

	if (init_lan (bus)) {
		SendFlashError (client,"Could not find device. Aborting.",0,FATAL);
		return (1);
	}

	SendFlashStatus (client," ",4,0);

	pos = 0;
	bbuffer = (byte *)info->ir_buffer;

	usbbuffer[0] = 0x77;
	usbbuffer[1] = 0;
	usbbuffer[2] = 0;
	usbbuffer[3] = 0;

	stat_step = 1000 / (info->maxmem / info->pagesize);
	stat_pos = 0;

	for (i=0;i < info->pages;i++) {
		usbbuffer[1] = i;
		memcpy (usbbuffer+4,bbuffer+pos,info->pagesize);


		if (pos < info->crypt_start || (i == (info->pages - 1) && (info->flashlen % (info->pagesize / 2)))) usbbuffer[3] = 0;
		else usbbuffer[3] = 0x44;

		res = send_lan_data (bus,usbbuffer,info->pagesize + 4);
		if (res) {
			SendFlashError (client,"Flash failed!\nRepeat the procedure.\nDO NOT EXIT THE SOFTWARE OR POWER OFF THE IRTRANS!",0,FATAL);
			return (1);
		}

		SendFlashStatus (client,"Flashing IR Firmware",1,stat_pos);
		stat_pos += stat_step;

		pos += info->pagesize;
	}

	memset (usbbuffer+4,0xff,info->pagesize);
	usbbuffer[3] = 0;
	while (i < (info->maxmem / info->pagesize)) {
		usbbuffer[1] = i;
		send_lan_data (bus,usbbuffer,info->pagesize + 4);

		SendFlashStatus (client,"Flashing IR Firmware",1,stat_pos);
		stat_pos += stat_step;

		pos += info->pagesize;
		i++;
	}

	msSleep (500);
	end_lan (bus);

	SendFlashStatus (client,"Waiting for the device to reboot",1,0);

	res = 1;
	for (cnt=0;cnt < 1000 && res;cnt += 25) {
		SendFlashStatus (client,"Waiting for the device to reboot",1,cnt);
		res = GetLANVersions (bus,IRVersion,LANVersion,1000);
	}

	if (res || memcmp (info->ir_version,IRVersion,8)) {
		SendFlashError (client,"The Firmwareupdate did not work.",ERR_UPDATE,FATAL);
		return (1);
	}
	else SendFlashError (client,"Firmware Update finished",0,0);
	memcpy (IRDevices[bus].version,IRVersion,8);

	RemoveSecfile ();

	return (0);
}


int readFlashFilePage(struct zip *zipfile,char flashPath[], unsigned short flashBuf[8][32768], long bufLimit,word *page) 
{

	struct zip_file *flashBufPtr;
	char wordstring[WORDLEN];
	long address;
	unsigned int data;
	char buffer[MAXLEN];
	long offsetAddress = 0L;
	int dataByteCount;
	unsigned int lineAddress;
	int recordType;
	int idx,pg = 0,pageadr = 0;
	int checksum;
	int tmp;
	int maxadr = 0;

	if ((flashBufPtr = zip_fopen(zipfile,flashPath,0)) == NULL) return(ERR_OPEN);

	if (zip_fgets(wordstring, WORDLEN, flashBufPtr) != NULL) {
		if (wordstring[FLASH_DATAPTR] == ':') {
			zip_fclose (flashBufPtr);
			flashBufPtr = zip_fopen(zipfile,flashPath,0);
			while (zip_fgets(wordstring, WORDLEN, flashBufPtr) != NULL) {
			   sscanf(wordstring, "%lx", &address);
			   if (wordstring[FLASH_DATAPTR] != ':') {
				  return(0);
			   }
			   sscanf(&wordstring[FLASH_DATAPTR + 1], "%x", &data);
			   if (address < bufLimit)
				  flashBuf[pg][address] = data;
			   else
				  return(0);
			}
			zip_fclose(flashBufPtr);
		}
		if (wordstring[0] == ':') {
			page[pg] = pageadr;
			zip_fclose (flashBufPtr);
			flashBufPtr = zip_fopen(zipfile,flashPath,0);
			while (zip_fgets(buffer, MAXLEN, flashBufPtr) != NULL) {
			   checksum = 0;
			   sscanf(&buffer[1], "%2x", &dataByteCount);
			   checksum += dataByteCount;
			   sscanf(&buffer[3], "%4x", &lineAddress);
			   sscanf(&buffer[3], "%2x", &tmp);
			   checksum += tmp;
			   sscanf(&buffer[5], "%2x", &tmp);
			   checksum += tmp;
			   sscanf(&buffer[7], "%2x", &recordType);
			   checksum += recordType;

			   if (recordType == 4) {
				  pg++;
				  sscanf(&buffer[9], "%4lx", &offsetAddress);
				  page[pg] = (unsigned short)offsetAddress;
				  offsetAddress = lineAddress = 0;

			   }
			  /*
			   * Record type 02 means update offset address
			   */
			   if (recordType == 2) {
				  sscanf(&buffer[9], "%4lx", &offsetAddress);
				  sscanf(&buffer[9], "%2x", &tmp);
				  checksum += tmp;
				  sscanf(&buffer[11], "%2x", &tmp);
				  checksum += tmp;
				  checksum = (unsigned char) (0x100 - (unsigned char) checksum);
				  sscanf(&buffer[13], "%2x", &tmp);
				  if (checksum != tmp)
					 return(0);
			   }
			   if (recordType == 0) {
				  for (idx = 9; idx < 9 + dataByteCount * 2; idx += 2) {
					 sscanf(&buffer[idx], "%2x", &data);
					 /*
					  * Use word addresses for the AT90 2-byte data format
					  */
					 address = lineAddress / 2;
					 address =  address + (offsetAddress * 0x10) / 2;
					 if (address < bufLimit) { 
						if (lineAddress & 0x0001)   /* is it an odd address? */
													/* then set MSB of data  */
							flashBuf[pg][address] &= (data << 8) | 0x00ff;
						 else                       /* else set LSB of data  */
							flashBuf[pg][address] &= data | 0xff00;
						 if (address > maxadr) maxadr = address;
					 } else {
						return(0);
					 }
					 lineAddress++;
					 checksum += data;
				  }                     
				  checksum = (unsigned char) (0x100 - (unsigned char) checksum);
				  sscanf(&buffer[idx], "%2x", &tmp);
				  if (checksum != tmp)
					 return(0);
			   }
			}
			return(pg);
		 }
	}
   return(1);
}


int ReadFirmwareFile (struct zip *zipfile,int fileidx, unsigned short flashBuf[], long bufLimit)
{
	char wordstring[WORDLEN];
	struct zip_file *fw;
	long address;
	unsigned int data;
	char buffer[MAXLEN];
	long offsetAddress = 0L;
	int dataByteCount;
	unsigned int lineAddress;
	int recordType;
	int idx;
	int checksum;
	int tmp;
	int maxadr = 0;

	fw = zip_fopen_index (zipfile,fileidx,0);

	if (fw == NULL) return (0);

	if (zip_fgets (wordstring,WORDLEN,fw) != NULL) {
		// Try Atmel generic format (default of the Atmel assembler).
		if (wordstring[FLASH_DATAPTR] == ':') {
			zip_fclose (fw);
			fw = zip_fopen_index (zipfile,fileidx,0);
			while (zip_fgets (wordstring, WORDLEN, fw) != NULL) {
				sscanf(wordstring, "%lx", &address);
				if (wordstring[FLASH_DATAPTR] != ':') return(1);
				sscanf(&wordstring[FLASH_DATAPTR + 1], "%x", &data);
				if (address < bufLimit) flashBuf[address] = data;
				else return(1);
			}
			zip_fclose (fw);
		}
		// Try Intel HEX format.
		if (wordstring[0] == ':') {
			zip_fclose (fw);
			fw = zip_fopen_index (zipfile,fileidx,0);
			while (zip_fgets (buffer, MAXLEN, fw) != NULL) {
				checksum = 0;
				sscanf(&buffer[1], "%2x", &dataByteCount);
				checksum += dataByteCount;
				sscanf(&buffer[3], "%4x", &lineAddress);
				sscanf(&buffer[3], "%2x", &tmp);
				checksum += tmp;
				sscanf(&buffer[5], "%2x", &tmp);
				checksum += tmp;
				sscanf(&buffer[7], "%2x", &recordType);
				checksum += recordType;

				if (recordType == 2) {
					sscanf(&buffer[9], "%4lx", &offsetAddress);
					sscanf(&buffer[9], "%2x", &tmp);
					checksum += tmp;
					sscanf(&buffer[11], "%2x", &tmp);
					checksum += tmp;
					checksum = (unsigned char) (0x100 - (unsigned char) checksum);
					sscanf(&buffer[13], "%2x", &tmp);
					if (checksum != tmp) {
						zip_fclose (fw);
						return (1);
					}
				}
				if (recordType == 0) {
					for (idx = 9; idx < 9 + dataByteCount * 2; idx += 2) {
						sscanf(&buffer[idx], "%2x", &data);
						address = lineAddress / 2;
						address =  address + (offsetAddress * 0x10) / 2;
						if (address < bufLimit) { 
							if (lineAddress & 0x0001) flashBuf[address] &= (data << 8) | 0x00ff;
							else flashBuf[address] &= data | 0xff00;
							if (address > maxadr) maxadr = address;
						} 
						else {
							zip_fclose (fw);
							return (1);
						}
						lineAddress++;
						checksum += data;
					}                     
					checksum = (unsigned char) (0x100 - (unsigned char) checksum);
					sscanf(&buffer[idx], "%2x", &tmp);
					if (checksum != tmp) {
						zip_fclose (fw);
						return(1);
					}
				}
			}
			zip_fclose (fw);
			return(maxadr);
		}
	}
	zip_fclose (fw);
	return (1);
}


GetLANVersions (int bus,char IRVersion[],char LANVersion[],int timeout)
{
	int res,i;
	IRDATA ir;
	GETVERSION_LAN_EX ver;
	struct sockaddr_in send_adr;

#ifdef LINUX
	fd_set events;
	int maxfd,wait;
	struct timeval tv;
#endif

	ir.command = HOST_VERSION;
	ir.len = 8;

	ir.address = VERSION_MAGIC_1;
	ir.target_mask = VERSION_MAGIC_2;

	ir.checksumme = get_checksumme (&ir);

	IRTransLanSend (IRDevices + bus,&ir);

#ifdef WIN32
	res = WaitForSingleObject (IrtLanEvent,timeout);
	if (res == WAIT_TIMEOUT)  return (ERR_READVERSION);
	WSAResetEvent (IrtLanEvent);
#endif

#ifdef LINUX
	FD_ZERO (&events);

	FD_SET (irtlan_socket,&events);
	maxfd = irtlan_socket + 1;

	tv.tv_sec = timeout / 1000;
	tv.tv_usec = (timeout % 1000) * 1000;

	wait = select (maxfd,&events,NULL,NULL,&tv);
	if (!wait) return (ERR_READVERSION);
#endif

	i = sizeof (send_adr);
	res = recvfrom(irtlan_socket,(byte *)&ver,sizeof (GETVERSION_LAN_EX),MSG_NOSIGNAL,(struct sockaddr *)&send_adr,&i);
	if (res <= 0 || res != ver.len + 1 || ver.netcommand != RESULT_GETVERSION) return (ERR_READVERSION);

	IRVersion[8] = 0;
	memcpy (IRVersion,ver.ir_version,8);
	LANVersion[8] = 0;
	memcpy (LANVersion,ver.lan_version,8);

	return (0);
	}

int GetIRVersion (DEVICEINFO *dev,char IRVersion[])
{
	int res;
	char data[100];
	IRDATA ir;

#ifdef LINUX
	fd_set events;
	int maxfd,wait;
	struct timeval tv;
#endif

	if (dev->io.if_type == IF_RS232) {
		ir.command = HOST_VERSION;
		ir.len = 6;

		ir.address = VERSION_MAGIC_1;
		ir.target_mask = VERSION_MAGIC_2;

		ir.checksumme = get_checksumme (&ir);

		FlushIoEx (dev);
		res = WriteTransceiverEx (dev,&ir);
		FlushIoEx (dev);
	}

	ir.command = HOST_VERSION;
	ir.len = 8;

	ir.address = VERSION_MAGIC_5;
	ir.target_mask = VERSION_MAGIC_2;
	ir.ir_length = VERSION_MAGIC_3;
	ir.transmit_freq = VERSION_MAGIC_4;

	ir.checksumme = get_checksumme (&ir);

	FlushIoEx (dev);

	IRVersion[0] = 0;

	res = WriteTransceiverEx (dev,&ir);
	IRVersion[8] = 0;
	res = ReadIRStringEx (dev,IRVersion,8,500);
	if (res != 8) return (ERR_READVERSION);

	if (strcmp (dev->version+1,"6.04.46") >= 0) {
		res = ReadIRStringEx (dev,data,68,500);
		if (res != 68) return (ERR_READVERSION);
	}

	else if (strcmp (dev->version+1,"5.10.01") >= 0) {
		res = ReadIRStringEx (dev,data,24,500);
		if (res != 20 && res != 24) return (ERR_READVERSION);
	}

	else if (strcmp (dev->version+1,"2.24.01") >= 0) {
		res = ReadIRStringEx (dev,data,8,500);
		if (res != 8) return (ERR_READVERSION);

	}
	return (0);
}

void WriteDeviceData (int bus)
{
	char *tmp;
	FILE *fp;
	char secfile[256];

	secfile[0] = 0;
	tmp = (char *)getenv ("TMP");
	if (!tmp) return;

	sprintf (secfile,"%s\\.kwrxx43d53df.tmp",tmp);

	fp = fopen (secfile,"w");
	fprintf (fp,"%s\n%u\n%u\n%u\n%u\n%u\n%u\n%u\n%u\n%u\n%u\n%u\n",IRDevices[bus].version,IRDevices[bus].fw_capabilities,IRDevices[bus].fw_capabilities2,
			 IRDevices[bus].fw_capabilities3,IRDevices[bus].fw_capabilities4,IRDevices[bus].fw_serno,IRDevices[bus].mac_adr[0],IRDevices[bus].mac_adr[1],
			 IRDevices[bus].mac_adr[2],IRDevices[bus].mac_adr[3],IRDevices[bus].mac_adr[4],IRDevices[bus].mac_adr[5]);
	fclose (fp);
}


void RemoveSecfile ()
{
	char *tmp;
	char secfile[256];

	secfile[0] = 0;
	tmp = (char *)getenv ("TMP");
	if (!tmp) return;

	sprintf (secfile,"%s\\.kwrxx43d53df.tmp",tmp);

#ifdef WIN32
	_unlink (secfile);
#else
	unlink (secfile);
#endif
}

int lan_flash_load (int bus,int client,FIRMWARE_INFO *info)
{
	char IRVersion[10];
	char LANVersion[10];
	int i,p,pos,adr,pc,cnt,res;
	int len,flashpage;
	FLASH_IR_DATA fdata;
	IRDATA_LAN_FLASH ird;
	int stat_cnt,stat_step,stat_next,stat_pos;

	
	memset (&ird,0,sizeof (IRDATA_LAN_FLASH));
	memset (&fdata,0,sizeof (fdata));

	fdata.magic = IR_MAGIC;
	SwapWordN (&fdata.magic);

	fdata.pagesize = info->pagesize;
	SwapWordN (&fdata.pagesize);


	fdata.cryptstart = info->crypt_start / info->pagesize;
	if (info->flashlen % (info->pagesize / 2)) fdata.cryptend = info->pages - 1;
	else fdata.cryptend = info->pages;

	info->pages += 8;
	if (info->pages > (info->maxmem / info->pagesize)) info->pages = (info->maxmem / info->pagesize);

	info->pages = info->maxmem / info->pagesize;

	fdata.pagecnt = info->pages;
	SwapWordN (&fdata.pagecnt);

	memcpy (fdata.data,info->ir_buffer,sizeof (fdata.data));

	fdata.crc = ip_sum_calc ((word)(info->pages * info->pagesize + 8),(byte *)&fdata.cryptstart);
	SwapWordN (&fdata.crc);
	

	len = info->pages * info->pagesize + 12;
	pos = len / 4;

		
	ird.netcommand = COMMAND_FLASH_HTML;
	flashpage = 128;

	SendFlashStatus (client,"Uploading IR Firmware",3,0);

	stat_cnt = len / 128;
	stat_step = stat_cnt / 50;
	stat_pos = 0;
	stat_next = 0;


	pc = p = adr = 0;
	while (adr < pos && p != 'E') {
		i = 0;
		pc++;
		do {
			ird.adr = adr;
			ird.len = flashpage;
		
			SwapWordN (&ird.adr);
			SwapWordN (&ird.len);

			memcpy (ird.data,(byte *)(&fdata) + adr * 4,flashpage);

#ifdef WIN32
			p = IRTransLanFlash (IRDevices + bus,&ird,flashpage,IRDevices[bus].io.IPAddr[0].sin_addr.S_un.S_addr);
#else
			p = IRTransLanFlash (IRDevices + bus,&ird,flashpage,IRDevices[bus].io.IPAddr[0].sin_addr.s_addr);
#endif

			if (p > 0) {
				if (stat_pos >= stat_next) {
					SendFlashStatus (client,"Uploading IR Firmware",1,stat_pos / stat_step);
					stat_next += stat_step;
				}
			}
			i++;
			stat_pos ++;
			if (i == 3) msSleep (2000);
		} while (p == 'E' && i < 5);
		adr += flashpage / 4;
	}

	if (p == 'E') {
		SendFlashError (client,"Upload Error - retry the update",ERR_UPDATE,FATAL);
		return (1);
	}

	if (check_crc (bus) == 'O') {
		SendFlashStatus (client,"Uploading IR Firmware - CRC Check OK",3,0);
	}
	else {
		SendFlashError (client,"CRC Error - retry the update",ERR_UPDATE,FATAL);
		return (1);
	}
	start_flash (client,bus,stat_cnt / 2);

	SendFlashStatus (client,"Waiting for the device to reboot",1,0);

	res = 1;
	for (cnt=0;cnt < 1000 && res;cnt += 25) {
		SendFlashStatus (client,"Waiting for the device to reboot",1,cnt);
		res = GetLANVersions (bus,IRVersion,LANVersion,1000);
	}

	if (res || memcmp (info->ir_version,IRVersion,8)) {
		SendFlashError (client,"The Firmwareupdate did not work.",ERR_UPDATE,FATAL);
		return (1);
	}
	else SendFlashError (client,"Firmware Update finished",0,0);
	memcpy (IRDevices[bus].version,IRVersion,8);

	return (0);
}




word ip_sum_calc(word len_ip_header, byte buff[])
{
word word16;
unsigned long sum=0;
word i;
    
	// make 16 bit words out of every two adjacent 8 bit words in the packet
	// and add them up
	for (i=0;i<len_ip_header;i=i+2){
		word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF);
		sum = sum + (unsigned long) word16;	
	}
	
	// take only 16 bits out of the 32 bit sum
	while (sum>>16)
	  sum = (sum & 0xFFFF)+(sum >> 16);

	// one's complement the result
	
return ((word) sum);
}


byte check_crc (int bus)
{
	int res,cnt,sz;
	IRDATA_LAN ird;
	struct sockaddr_in target;

	memset (&ird,0,sizeof (IRDATA_LAN));

	ird.netcommand = COMMAND_FLASH_CRC;
	ird.ir_data.len = 3;
	sz = ird.ir_data.len + sizeof (IRDATA_LAN) - sizeof (IRDATA);

	memcpy (&target,&IRDevices[bus].io.IPAddr[0],sizeof (struct sockaddr_in));
	if (connect (irtlan_outbound,(struct sockaddr *)&target,sizeof (struct sockaddr_in)) < 0) return ('E');

	res = cnt = 0;
	while (res <= 0 && cnt < 2) {	
		send (irtlan_outbound,(char *)&ird,sz,0);
#ifdef WIN32
		res = rcv_status_timeout (2000,IRDevices[bus].io.IPAddr[0].sin_addr.S_un.S_addr);
#else
		res = rcv_status_timeout (2000,IRDevices[bus].io.IPAddr[0].sin_addr.s_addr);
#endif
		cnt++;
	}

	return (res);
}

byte start_flash (int client,int bus,int stat_cnt)
{
	int res,cnt,sz;
	IRDATA_LAN ird;
	struct sockaddr_in target;
	int stat_step,stat_pos;

	SendFlashStatus (client,"Flashing IR Firmware",4,0);

	memset (&ird,0,sizeof (IRDATA_LAN));

	ird.netcommand = COMMAND_FLASH_IR;
	ird.ir_data.len = 3;
	sz = ird.ir_data.len + sizeof (IRDATA_LAN) - sizeof (IRDATA);

	memcpy (&target,&IRDevices[bus].io.IPAddr[0],sizeof (struct sockaddr_in));
	if (connect (irtlan_outbound,(struct sockaddr *)&target,sizeof (struct sockaddr_in)) < 0) {
		return (0);
	}

	send (irtlan_outbound,(char *)&ird,sz,0);
	
	stat_step = 1000 / stat_cnt;
	stat_pos = 0;

	res = 1;
	cnt = 0;
	while (res != -1 && res != 'O' && res != 'E') {	
#ifdef WIN32
		res = rcv_status_timeout (5000,IRDevices[bus].io.IPAddr[0].sin_addr.S_un.S_addr);
#else
		res = rcv_status_timeout (5000,IRDevices[bus].io.IPAddr[0].sin_addr.s_addr);
#endif
		SendFlashStatus (client,"Flashing IR Firmware",1,stat_pos);

		stat_pos += stat_step;
	}
	SendFlashStatus (client,"Flashing IR Firmware",1,1000);
	SendFlashStatus (client,"Flashing IR Firmware - Done",4,0);

	return (res == 'O');
}

int init_lan (int bus)
{
	int res;
	IRDATA ird;

	memset (&ird,0,sizeof (IRDATA));

	ird.command = START_FLASH_MODE;
	ird.len = 3;
	IRTransLanSend (IRDevices + bus,&ird);
#ifdef WIN32
	if ((res = rcv_status_timeout (5000,IRDevices[bus].io.IPAddr[0].sin_addr.S_un.S_addr)) != COMMAND_FLASH_ACK) {
#else
	if ((res = rcv_status_timeout (5000,IRDevices[bus].io.IPAddr[0].sin_addr.s_addr)) != COMMAND_FLASH_ACK) {
#endif
		return (ERR_TIMEOUT);
	}

	return (0);
}

void end_lan (int bus)
{
	IRDATA ird;

	memset (&ird,0,sizeof (IRDATA));

	ird.command = START_FLASH_MODE;
	ird.len = 0;
	IRTransLanSend (IRDevices + bus,&ird);
}




int send_lan_data (int bus,byte data[],int len)
{
	int stat;
	char buffer[512];
	IRDATA *ird;

	ird = (IRDATA *)buffer;

	memset (ird,0,512);

	ird->command = TRANSFER_FLASH;
	if (len < 200) 	ird->len = (byte)((sizeof (IRDATA) - CODE_LEN) + len);
	memcpy (ird->data,data,len);

	len += sizeof (IRDATA) - CODE_LEN;

	LANLargeSend (bus,buffer,(word)len);

#ifdef WIN32
	if ((stat = rcv_status_timeout (3000,IRDevices[bus].io.IPAddr[0].sin_addr.S_un.S_addr)) != COMMAND_FLASH_ACK) {
#else
	if ((stat = rcv_status_timeout (3000,IRDevices[bus].io.IPAddr[0].sin_addr.s_addr)) != COMMAND_FLASH_ACK) {
#endif
		LANLargeSend (bus,buffer,(word)len);

#ifdef WIN32
		if (stat = rcv_status_timeout (3000,IRDevices[bus].io.IPAddr[0].sin_addr.S_un.S_addr) != COMMAND_FLASH_ACK) {
#else
		if (stat = rcv_status_timeout (3000,IRDevices[bus].io.IPAddr[0].sin_addr.s_addr) != COMMAND_FLASH_ACK) {
#endif
			return (ERR_TIMEOUT);
		}
	}
	return (0);
}

void LANLargeSend (int bus,char data[],word len)
{
	IRDATA_LAN_LARGE irdlan;
	
	memset (&irdlan,0,sizeof (IRDATA_LAN_LARGE));

	irdlan.netcommand = COMMAND_FLASH_DATA;

	memcpy (&(irdlan.ir_data),data,len);

	send (irtlan_outbound,(char *)&irdlan,len + sizeof (IRDATA_LAN) - sizeof (IRDATA),0);

	msSleep (30);
}




/* the eight DES S-boxes */

uint32 SB1[64] =
{
    0x01010400, 0x00000000, 0x00010000, 0x01010404,
    0x01010004, 0x00010404, 0x00000004, 0x00010000,
    0x00000400, 0x01010400, 0x01010404, 0x00000400,
    0x01000404, 0x01010004, 0x01000000, 0x00000004,
    0x00000404, 0x01000400, 0x01000400, 0x00010400,
    0x00010400, 0x01010000, 0x01010000, 0x01000404,
    0x00010004, 0x01000004, 0x01000004, 0x00010004,
    0x00000000, 0x00000404, 0x00010404, 0x01000000,
    0x00010000, 0x01010404, 0x00000004, 0x01010000,
    0x01010400, 0x01000000, 0x01000000, 0x00000400,
    0x01010004, 0x00010000, 0x00010400, 0x01000004,
    0x00000400, 0x00000004, 0x01000404, 0x00010404,
    0x01010404, 0x00010004, 0x01010000, 0x01000404,
    0x01000004, 0x00000404, 0x00010404, 0x01010400,
    0x00000404, 0x01000400, 0x01000400, 0x00000000,
    0x00010004, 0x00010400, 0x00000000, 0x01010004
};

static uint32 SB2[64] =
{
    0x80108020, 0x80008000, 0x00008000, 0x00108020,
    0x00100000, 0x00000020, 0x80100020, 0x80008020,
    0x80000020, 0x80108020, 0x80108000, 0x80000000,
    0x80008000, 0x00100000, 0x00000020, 0x80100020,
    0x00108000, 0x00100020, 0x80008020, 0x00000000,
    0x80000000, 0x00008000, 0x00108020, 0x80100000,
    0x00100020, 0x80000020, 0x00000000, 0x00108000,
    0x00008020, 0x80108000, 0x80100000, 0x00008020,
    0x00000000, 0x00108020, 0x80100020, 0x00100000,
    0x80008020, 0x80100000, 0x80108000, 0x00008000,
    0x80100000, 0x80008000, 0x00000020, 0x80108020,
    0x00108020, 0x00000020, 0x00008000, 0x80000000,
    0x00008020, 0x80108000, 0x00100000, 0x80000020,
    0x00100020, 0x80008020, 0x80000020, 0x00100020,
    0x00108000, 0x00000000, 0x80008000, 0x00008020,
    0x80000000, 0x80100020, 0x80108020, 0x00108000
};

static uint32 SB3[64] =
{
    0x00000208, 0x08020200, 0x00000000, 0x08020008,
    0x08000200, 0x00000000, 0x00020208, 0x08000200,
    0x00020008, 0x08000008, 0x08000008, 0x00020000,
    0x08020208, 0x00020008, 0x08020000, 0x00000208,
    0x08000000, 0x00000008, 0x08020200, 0x00000200,
    0x00020200, 0x08020000, 0x08020008, 0x00020208,
    0x08000208, 0x00020200, 0x00020000, 0x08000208,
    0x00000008, 0x08020208, 0x00000200, 0x08000000,
    0x08020200, 0x08000000, 0x00020008, 0x00000208,
    0x00020000, 0x08020200, 0x08000200, 0x00000000,
    0x00000200, 0x00020008, 0x08020208, 0x08000200,
    0x08000008, 0x00000200, 0x00000000, 0x08020008,
    0x08000208, 0x00020000, 0x08000000, 0x08020208,
    0x00000008, 0x00020208, 0x00020200, 0x08000008,
    0x08020000, 0x08000208, 0x00000208, 0x08020000,
    0x00020208, 0x00000008, 0x08020008, 0x00020200
};

static uint32 SB4[64] =
{
    0x00802001, 0x00002081, 0x00002081, 0x00000080,
    0x00802080, 0x00800081, 0x00800001, 0x00002001,
    0x00000000, 0x00802000, 0x00802000, 0x00802081,
    0x00000081, 0x00000000, 0x00800080, 0x00800001,
    0x00000001, 0x00002000, 0x00800000, 0x00802001,
    0x00000080, 0x00800000, 0x00002001, 0x00002080,
    0x00800081, 0x00000001, 0x00002080, 0x00800080,
    0x00002000, 0x00802080, 0x00802081, 0x00000081,
    0x00800080, 0x00800001, 0x00802000, 0x00802081,
    0x00000081, 0x00000000, 0x00000000, 0x00802000,
    0x00002080, 0x00800080, 0x00800081, 0x00000001,
    0x00802001, 0x00002081, 0x00002081, 0x00000080,
    0x00802081, 0x00000081, 0x00000001, 0x00002000,
    0x00800001, 0x00002001, 0x00802080, 0x00800081,
    0x00002001, 0x00002080, 0x00800000, 0x00802001,
    0x00000080, 0x00800000, 0x00002000, 0x00802080
};

static uint32 SB5[64] =
{
    0x00000100, 0x02080100, 0x02080000, 0x42000100,
    0x00080000, 0x00000100, 0x40000000, 0x02080000,
    0x40080100, 0x00080000, 0x02000100, 0x40080100,
    0x42000100, 0x42080000, 0x00080100, 0x40000000,
    0x02000000, 0x40080000, 0x40080000, 0x00000000,
    0x40000100, 0x42080100, 0x42080100, 0x02000100,
    0x42080000, 0x40000100, 0x00000000, 0x42000000,
    0x02080100, 0x02000000, 0x42000000, 0x00080100,
    0x00080000, 0x42000100, 0x00000100, 0x02000000,
    0x40000000, 0x02080000, 0x42000100, 0x40080100,
    0x02000100, 0x40000000, 0x42080000, 0x02080100,
    0x40080100, 0x00000100, 0x02000000, 0x42080000,
    0x42080100, 0x00080100, 0x42000000, 0x42080100,
    0x02080000, 0x00000000, 0x40080000, 0x42000000,
    0x00080100, 0x02000100, 0x40000100, 0x00080000,
    0x00000000, 0x40080000, 0x02080100, 0x40000100
};

static uint32 SB6[64] =
{
    0x20000010, 0x20400000, 0x00004000, 0x20404010,
    0x20400000, 0x00000010, 0x20404010, 0x00400000,
    0x20004000, 0x00404010, 0x00400000, 0x20000010,
    0x00400010, 0x20004000, 0x20000000, 0x00004010,
    0x00000000, 0x00400010, 0x20004010, 0x00004000,
    0x00404000, 0x20004010, 0x00000010, 0x20400010,
    0x20400010, 0x00000000, 0x00404010, 0x20404000,
    0x00004010, 0x00404000, 0x20404000, 0x20000000,
    0x20004000, 0x00000010, 0x20400010, 0x00404000,
    0x20404010, 0x00400000, 0x00004010, 0x20000010,
    0x00400000, 0x20004000, 0x20000000, 0x00004010,
    0x20000010, 0x20404010, 0x00404000, 0x20400000,
    0x00404010, 0x20404000, 0x00000000, 0x20400010,
    0x00000010, 0x00004000, 0x20400000, 0x00404010,
    0x00004000, 0x00400010, 0x20004010, 0x00000000,
    0x20404000, 0x20000000, 0x00400010, 0x20004010
};

static uint32 SB7[64] =
{
    0x00200000, 0x04200002, 0x04000802, 0x00000000,
    0x00000800, 0x04000802, 0x00200802, 0x04200800,
    0x04200802, 0x00200000, 0x00000000, 0x04000002,
    0x00000002, 0x04000000, 0x04200002, 0x00000802,
    0x04000800, 0x00200802, 0x00200002, 0x04000800,
    0x04000002, 0x04200000, 0x04200800, 0x00200002,
    0x04200000, 0x00000800, 0x00000802, 0x04200802,
    0x00200800, 0x00000002, 0x04000000, 0x00200800,
    0x04000000, 0x00200800, 0x00200000, 0x04000802,
    0x04000802, 0x04200002, 0x04200002, 0x00000002,
    0x00200002, 0x04000000, 0x04000800, 0x00200000,
    0x04200800, 0x00000802, 0x00200802, 0x04200800,
    0x00000802, 0x04000002, 0x04200802, 0x04200000,
    0x00200800, 0x00000000, 0x00000002, 0x04200802,
    0x00000000, 0x00200802, 0x04200000, 0x00000800,
    0x04000002, 0x04000800, 0x00000800, 0x00200002
};

static uint32 SB8[64] =
{
    0x10001040, 0x00001000, 0x00040000, 0x10041040,
    0x10000000, 0x10001040, 0x00000040, 0x10000000,
    0x00040040, 0x10040000, 0x10041040, 0x00041000,
    0x10041000, 0x00041040, 0x00001000, 0x00000040,
    0x10040000, 0x10000040, 0x10001000, 0x00001040,
    0x00041000, 0x00040040, 0x10040040, 0x10041000,
    0x00001040, 0x00000000, 0x00000000, 0x10040040,
    0x10000040, 0x10001000, 0x00041040, 0x00040000,
    0x00041040, 0x00040000, 0x10041000, 0x00001000,
    0x00000040, 0x10040040, 0x00001000, 0x00041040,
    0x10001000, 0x00000040, 0x10000040, 0x10040000,
    0x10040040, 0x10000000, 0x00040000, 0x10001040,
    0x00000000, 0x10041040, 0x00040040, 0x10000040,
    0x10040000, 0x10001000, 0x10001040, 0x00000000,
    0x10041040, 0x00041000, 0x00041000, 0x00001040,
    0x00001040, 0x00040040, 0x10000000, 0x10041000
};

/* PC1: left and right halves bit-swap */

static uint32 LHs[16] =
{
    0x00000000, 0x00000001, 0x00000100, 0x00000101,
    0x00010000, 0x00010001, 0x00010100, 0x00010101,
    0x01000000, 0x01000001, 0x01000100, 0x01000101,
    0x01010000, 0x01010001, 0x01010100, 0x01010101
};

static uint32 RHs[16] =
{
    0x00000000, 0x01000000, 0x00010000, 0x01010000,
    0x00000100, 0x01000100, 0x00010100, 0x01010100,
    0x00000001, 0x01000001, 0x00010001, 0x01010001,
    0x00000101, 0x01000101, 0x00010101, 0x01010101,
};

/* platform-independant 32-bit integer manipulation macros */

#define GET_UINT32(n,b,i)                       \
{                                               \
    (n) = ( (uint32) (b)[(i)    ] << 24 )       \
        | ( (uint32) (b)[(i) + 1] << 16 )       \
        | ( (uint32) (b)[(i) + 2] <<  8 )       \
        | ( (uint32) (b)[(i) + 3]       );      \
}

#define PUT_UINT32(n,b,i)                       \
{                                               \
    (b)[(i)    ] = (uint8) ( (n) >> 24 );       \
    (b)[(i) + 1] = (uint8) ( (n) >> 16 );       \
    (b)[(i) + 2] = (uint8) ( (n) >>  8 );       \
    (b)[(i) + 3] = (uint8) ( (n)       );       \
}

/* Initial Permutation macro */

#define DES_IP(X,Y)                                             \
{                                                               \
    T = ((X >>  4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T <<  4);   \
    T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16);   \
    T = ((Y >>  2) ^ X) & 0x33333333; X ^= T; Y ^= (T <<  2);   \
    T = ((Y >>  8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T <<  8);   \
    Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF;                    \
    T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T;                   \
    X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF;                    \
}

/* Final Permutation macro */

#define DES_FP(X,Y)                                             \
{                                                               \
    X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF;                    \
    T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T;                   \
    Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF;                    \
    T = ((Y >>  8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T <<  8);   \
    T = ((Y >>  2) ^ X) & 0x33333333; X ^= T; Y ^= (T <<  2);   \
    T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16);   \
    T = ((X >>  4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T <<  4);   \
}

/* DES round macro */

#define DES_ROUND(X,Y)                          \
{                                               \
    T = *SK++ ^ X;                              \
    Y ^= SB8[ (T      ) & 0x3F ] ^              \
         SB6[ (T >>  8) & 0x3F ] ^              \
         SB4[ (T >> 16) & 0x3F ] ^              \
         SB2[ (T >> 24) & 0x3F ];               \
                                                \
    T = *SK++ ^ ((X << 28) | (X >> 4));         \
    Y ^= SB7[ (T      ) & 0x3F ] ^              \
         SB5[ (T >>  8) & 0x3F ] ^              \
         SB3[ (T >> 16) & 0x3F ] ^              \
         SB1[ (T >> 24) & 0x3F ];               \
}

/* DES key schedule */

int des_main_ks( uint32 SK[32], uint8 key[8] )
{
    int i;
    uint32 X, Y, T;

    GET_UINT32( X, key, 0 );
    GET_UINT32( Y, key, 4 );

    /* Permuted Choice 1 */

    T =  ((Y >>  4) ^ X) & 0x0F0F0F0F;  X ^= T; Y ^= (T <<  4);
    T =  ((Y      ) ^ X) & 0x10101010;  X ^= T; Y ^= (T      );

    X =   (LHs[ (X      ) & 0xF] << 3) | (LHs[ (X >>  8) & 0xF ] << 2)
        | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ]     )
        | (LHs[ (X >>  5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6)
        | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4);

    Y =   (RHs[ (Y >>  1) & 0xF] << 3) | (RHs[ (Y >>  9) & 0xF ] << 2)
        | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ]     )
        | (RHs[ (Y >>  4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6)
        | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4);

    X &= 0x0FFFFFFF;
    Y &= 0x0FFFFFFF;

    /* calculate subkeys */

    for( i = 0; i < 16; i++ )
    {
        if( i < 2 || i == 8 || i == 15 )
        {
            X = ((X <<  1) | (X >> 27)) & 0x0FFFFFFF;
            Y = ((Y <<  1) | (Y >> 27)) & 0x0FFFFFFF;
        }
        else
        {
            X = ((X <<  2) | (X >> 26)) & 0x0FFFFFFF;
            Y = ((Y <<  2) | (Y >> 26)) & 0x0FFFFFFF;
        }

        *SK++ =   ((X <<  4) & 0x24000000) | ((X << 28) & 0x10000000)
                | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000)
                | ((X <<  6) & 0x01000000) | ((X <<  9) & 0x00200000)
                | ((X >>  1) & 0x00100000) | ((X << 10) & 0x00040000)
                | ((X <<  2) & 0x00020000) | ((X >> 10) & 0x00010000)
                | ((Y >> 13) & 0x00002000) | ((Y >>  4) & 0x00001000)
                | ((Y <<  6) & 0x00000800) | ((Y >>  1) & 0x00000400)
                | ((Y >> 14) & 0x00000200) | ((Y      ) & 0x00000100)
                | ((Y >>  5) & 0x00000020) | ((Y >> 10) & 0x00000010)
                | ((Y >>  3) & 0x00000008) | ((Y >> 18) & 0x00000004)
                | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001);

        *SK++ =   ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000)
                | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000)
                | ((X >>  2) & 0x02000000) | ((X <<  1) & 0x01000000)
                | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000)
                | ((X <<  3) & 0x00080000) | ((X >>  6) & 0x00040000)
                | ((X << 15) & 0x00020000) | ((X >>  4) & 0x00010000)
                | ((Y >>  2) & 0x00002000) | ((Y <<  8) & 0x00001000)
                | ((Y >> 14) & 0x00000808) | ((Y >>  9) & 0x00000400)
                | ((Y      ) & 0x00000200) | ((Y <<  7) & 0x00000100)
                | ((Y >>  7) & 0x00000020) | ((Y >>  3) & 0x00000011)
                | ((Y <<  2) & 0x00000004) | ((Y >> 21) & 0x00000002);
    }

    return( 0 );
}




int des3_set_3keys( des3_context *ctx, uint8 key1[8], uint8 key2[8],
                                       uint8 key3[8] )
{
    int i;

    des_main_ks( ctx->esk     , key1 );
    des_main_ks( ctx->dsk + 32, key2 );
    des_main_ks( ctx->esk + 64, key3 );

    for( i = 0; i < 32; i += 2 )
    {
        ctx->dsk[i     ] = ctx->esk[94 - i];
        ctx->dsk[i +  1] = ctx->esk[95 - i];

        ctx->esk[i + 32] = ctx->dsk[62 - i];
        ctx->esk[i + 33] = ctx->dsk[63 - i];

        ctx->dsk[i + 64] = ctx->esk[30 - i];
        ctx->dsk[i + 65] = ctx->esk[31 - i];
    }

    return( 0 );
}

/* Triple-DES 64-bit block encryption/decryption */

void des3_crypt( uint32 SK[96], uint8 input[8], uint8 output[8] )
{
    uint32 X, Y, T;

    GET_UINT32( X, input, 0 );
    GET_UINT32( Y, input, 4 );

    DES_IP( X, Y );

    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );

    DES_ROUND( X, Y );  DES_ROUND( Y, X );
    DES_ROUND( X, Y );  DES_ROUND( Y, X );
    DES_ROUND( X, Y );  DES_ROUND( Y, X );
    DES_ROUND( X, Y );  DES_ROUND( Y, X );
    DES_ROUND( X, Y );  DES_ROUND( Y, X );
    DES_ROUND( X, Y );  DES_ROUND( Y, X );
    DES_ROUND( X, Y );  DES_ROUND( Y, X );
    DES_ROUND( X, Y );  DES_ROUND( Y, X );

    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );
    DES_ROUND( Y, X );  DES_ROUND( X, Y );

    DES_FP( Y, X );

    PUT_UINT32( Y, output, 0 );
    PUT_UINT32( X, output, 4 );
}

void xor(unsigned char size, unsigned char *left, unsigned char *right)
{
	do
	{
		*left++ ^= *right++;
	}
	while (--size);
}



void des3_encrypt( des3_context *ctx, uint8 input[8], uint8 chainBlock[8] )
{
	xor(8, input, chainBlock);

    des3_crypt( ctx->esk, input, input );
	memcpy (chainBlock,input,8);
}

#endif