/*
 * Copyright (C) Volition, Inc. 2005.  All rights reserved.
 * 
 * All source code herein is the property of Volition, Inc. You may not sell 
 * or otherwise commercially exploit the source or things you created based on the 
 * source.
 *
*/

//Pilot tracker client code

#ifndef _WIN32
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cerrno>
#include <netdb.h>
#endif

#ifdef SCP_BSD
#include <sys/socket.h>
#endif

#include "globalincs/pstypes.h"
#include "io/timer.h"
#include "network/multi.h"
#include "network/ptrack.h"
#include "network/psnet2.h"
#include "network/multi_fstracker.h"


// check structs for size compatibility
SDL_COMPILE_TIME_ASSERT(udp_packet_header, sizeof(udp_packet_header) == 517);
SDL_COMPILE_TIME_ASSERT(vmt_fs2open_struct, sizeof(vmt_fs2open_struct) == 480);
SDL_COMPILE_TIME_ASSERT(validate_id_request, sizeof(validate_id_request) == 60);
SDL_COMPILE_TIME_ASSERT(squad_war_request, sizeof(squad_war_request) == 104);
SDL_COMPILE_TIME_ASSERT(squad_war_response, sizeof(squad_war_response) == 256);
SDL_COMPILE_TIME_ASSERT(squad_war_result, sizeof(squad_war_result) == 72);
SDL_COMPILE_TIME_ASSERT(pilot_request, sizeof(pilot_request) == 32);


//Variables

static SOCKADDR_STORAGE ptrackaddr;


static int		FSWriteState;
static int		FSReadState;
static unsigned int	FSLastSentWrite;
static unsigned int	FSFirstSentWrite;
static unsigned int	FSLastSent;
static unsigned int	FSFirstSent;

static int		SWWriteState;
static unsigned int SWLastSentWrite;
static unsigned int SWFirstSentWrite;


static udp_packet_header fs_pilot_req, fs_pilot_write, sw_res_write;
static pilot_request *fs_pr;
static vmt_stats_struct *ReadFSPilot;

// squad war response
static squad_war_response SquadWarWriteResponse;


static int SerializePilotPacket(const udp_packet_header *uph, ubyte *data)
{
	unsigned int packet_size = 0;
	int i;

	PXO_ADD_DATA(uph->type);
	PXO_ADD_USHORT(uph->len);
	PXO_ADD_UINT(uph->code);
	PXO_ADD_USHORT(uph->xcode);
	PXO_ADD_UINT(uph->sig);
	PXO_ADD_UINT(uph->security);

	switch (uph->type) {
		// no extra data for this
		case UNT_CONTROL:
			break;

		case UNT_PILOT_DATA_WRITE_NEW: {
			auto fsos = reinterpret_cast<const vmt_stats_struct *>(&uph->data);

			PXO_ADD_INT(fsos->tracker_id);
			PXO_ADD_DATA(fsos->pilot_name);

			PXO_ADD_INT(fsos->score);
			PXO_ADD_INT(fsos->rank);
			PXO_ADD_INT(fsos->assists);
			PXO_ADD_INT(fsos->kill_count);
			PXO_ADD_INT(fsos->kill_count_ok);
			PXO_ADD_UINT(fsos->p_shots_fired);
			PXO_ADD_UINT(fsos->s_shots_fired);

			PXO_ADD_UINT(fsos->p_shots_hit);
			PXO_ADD_UINT(fsos->s_shots_hit);

			PXO_ADD_UINT(fsos->p_bonehead_hits);
			PXO_ADD_UINT(fsos->s_bonehead_hits);
			PXO_ADD_INT(fsos->bonehead_kills);

			PXO_ADD_UINT(fsos->missions_flown);
			PXO_ADD_UINT(fsos->flight_time);
			PXO_ADD_UINT(fsos->last_flown);

			PXO_ADD_INT(fsos->security);
			PXO_ADD_UINT(fsos->checksum);

			PXO_ADD_DATA(fsos->virgin_pilot);

			// skip 1-byte pad

			PXO_ADD_DATA(fsos->num_medals);
			PXO_ADD_DATA(fsos->num_ships);

			const int count = std::min(static_cast<unsigned short>(fsos->num_medals + fsos->num_ships), MAX_FS2OPEN_COUNTS);

			for (i = 0; i < count; i++) {
				PXO_ADD_USHORT(fsos->counts[i]);
			}

			break;
		}

		case UNT_SW_RESULT_WRITE: {
			auto sw_result = reinterpret_cast<const squad_war_result *>(&uph->data);

			PXO_ADD_DATA(sw_result->match_code);
			PXO_ADD_DATA(sw_result->result);
			PXO_ADD_DATA(sw_result->squad_count1);
			PXO_ADD_DATA(sw_result->squad_count2);
			PXO_ADD_DATA(sw_result->pad);		// junk, for size/alignment

			for (i = 0; i < MAX_SQUAD_PLAYERS; i++) {
				PXO_ADD_INT(sw_result->squad_winners[i]);
			}

			for (i = 0; i < MAX_SQUAD_PLAYERS; i++) {
				PXO_ADD_INT(sw_result->squad_losers[i]);
			}

			break;
		}

		case UNT_PILOT_DATA_READ_NEW:
		case UNT_PILOT_DATA_READ: {
			auto pr = reinterpret_cast<const pilot_request *>(&uph->data);

			PXO_ADD_DATA(pr->pilot_name);
			PXO_ADD_DATA(pr->tracker_id);
			PXO_ADD_DATA(pr->pad);		// junk, for size/alignment

			break;
		}

		// we shouldn't be sending any other packet types
		default:
			Int3();
			break;
	}

	Assert(packet_size >= PACKED_HEADER_ONLY_SIZE);
	Assert(packet_size == uph->len);
	Assert(packet_size <= MAX_PACKET_SIZE);

	return static_cast<int>(packet_size);
}

static void DeserializePilotPacket(const ubyte *data, const int data_size, udp_packet_header *uph)
{
	int offset = 0;
	int i;

	memset(uph, 0, sizeof(udp_packet_header));

	// make sure we received a complete base packet
	if (data_size < static_cast<int>(PACKED_HEADER_ONLY_SIZE)) {
		uph->len = 0;
		uph->type = 0xff;

		return;
	}

	PXO_GET_DATA(uph->type);
	PXO_GET_USHORT(uph->len);
	PXO_GET_UINT(uph->code);
	PXO_GET_USHORT(uph->xcode);
	PXO_GET_UINT(uph->sig);
	PXO_GET_UINT(uph->security);

	// sanity check data size to make sure we reveived all of the expected packet
	// (the -1 is because psnet2 pops off one byte)
	if (static_cast<int>(uph->len)-1 > data_size) {
		uph->len = 0;
		uph->type = 0xff;

		return;
	}

	switch (uph->type) {
		// no extra data for these
		case UNT_PILOT_READ_FAILED:
		case UNT_PILOT_WRITE_SUCCESS:
		case UNT_PILOT_WRITE_FAILED:
			break;

		case UNT_PILOT_DATA_RESPONSE: {
			auto fsos = reinterpret_cast<vmt_stats_struct *>(&uph->data);

			PXO_GET_INT(fsos->tracker_id);
			PXO_GET_DATA(fsos->pilot_name);

			PXO_GET_INT(fsos->score);
			PXO_GET_INT(fsos->rank);
			PXO_GET_INT(fsos->assists);
			PXO_GET_INT(fsos->kill_count);
			PXO_GET_INT(fsos->kill_count_ok);
			PXO_GET_UINT(fsos->p_shots_fired);
			PXO_GET_UINT(fsos->s_shots_fired);

			PXO_GET_UINT(fsos->p_shots_hit);
			PXO_GET_UINT(fsos->s_shots_hit);

			PXO_GET_UINT(fsos->p_bonehead_hits);
			PXO_GET_UINT(fsos->s_bonehead_hits);
			PXO_GET_INT(fsos->bonehead_kills);

			PXO_GET_UINT(fsos->missions_flown);
			PXO_GET_UINT(fsos->flight_time);
			PXO_GET_UINT(fsos->last_flown);

			PXO_GET_INT(fsos->security);
			PXO_GET_UINT(fsos->checksum);

			PXO_GET_DATA(fsos->virgin_pilot);

			// skip 1-byte pad

			PXO_GET_DATA(fsos->num_medals);
			PXO_GET_DATA(fsos->num_ships);

			const int count = std::min(static_cast<unsigned short>(fsos->num_medals + fsos->num_ships), MAX_FS2OPEN_COUNTS);

			for (i = 0; i < count; i++) {
				PXO_GET_USHORT(fsos->counts[i]);
			}

			break;
		}

		case UNT_SW_RESULT_RESPONSE: {
			auto sw_resp = reinterpret_cast<squad_war_response *>(&uph->data);

			PXO_GET_DATA(sw_resp->reason);
			PXO_GET_DATA(sw_resp->accepted);

			break;
		}

		default:
			break;
	}

	//Assert(offset == data_size);
}


int InitPilotTrackerClient()
{
	FSWriteState = STATE_IDLE;
	FSReadState = STATE_IDLE;	
	SWWriteState = STATE_IDLE;
	
	ReadFSPilot = nullptr;

	fs_pr = reinterpret_cast<pilot_request *>(&fs_pilot_req.data);

	psnet_get_addr(Multi_options_g.user_tracker_ip, REGPORT, &ptrackaddr);

	return 1;
}

// Returns:
// -3	Error -- Called with NULL, but no request is waiting
// -2	Error -- Already sending data (hasn't timed out yet)
// -1	Timeout trying to send pilot data
// 0	Sending
// 1	Data succesfully sent
// 2	Send Cancelled (data may still have been written already, we just haven't been ACK'd yet)
// 3	Pilot not written (for some reason)
  
// Call with NULL to poll 
// Call with -1 to cancel send
// Call with valid pointer to a vmt_descent3_struct to initiate send
int SendFSPilotData(vmt_stats_struct *fs_pilot)
{
	//First check the network
	PollPTrackNet();

	if(fs_pilot == nullptr)
	{
		if(FSWriteState == STATE_IDLE)
		{
			return -3;
		}
		if(FSWriteState == STATE_SENDING_PILOT)
		{
			return 0;
		}
		if(FSWriteState == STATE_WROTE_PILOT)
		{
			//We wrote this pilot, and now we are about to inform the app, so back to idle
			FSWriteState = STATE_IDLE;
			return 1;
		}
		if(FSWriteState == STATE_TIMED_OUT)
		{
			//We gave up on sending this pilot, and now we are about to inform the app, so back to idle
			FSWriteState = STATE_IDLE;

			return -1;
		}
		if(FSWriteState == STATE_WRITE_PILOT_FAILED)
		{
			//The tracker said this dude couldn't be written
			FSWriteState = STATE_IDLE;

			return 3;
		}

	}
	else if(fs_pilot == reinterpret_cast<vmt_stats_struct *>(static_cast<uintptr_t>(0xffffffff)))
	{
		if(FSWriteState == STATE_IDLE)
		{
			return -3;
		}
		else
		{
			//Cancel this baby
			FSWriteState = STATE_IDLE;

			return 2;
		}

	}
	else if(FSWriteState == STATE_IDLE)
	{
		//New request, send out the req, and go for it.
		
		FSWriteState = STATE_SENDING_PILOT;
		
		FSLastSentWrite = 0;
		FSFirstSentWrite = timer_get_milliseconds();

		// deal with variable length of vmt_fs2open_struct
		const unsigned short size_offset = (MAX_FS2OPEN_COUNTS - (fs_pilot->num_ships + fs_pilot->num_medals)) * sizeof(unsigned short);

		fs_pilot_write.type = UNT_PILOT_DATA_WRITE_NEW;
		fs_pilot_write.code = CMD_GAME_FS2OPEN;
		fs_pilot_write.xcode = static_cast<unsigned short>(Multi_fs_tracker_game_id);
		fs_pilot_write.len = PACKED_HEADER_ONLY_SIZE+STATS_BLOCK_SIZE-size_offset;
		memcpy(&fs_pilot_write.data,fs_pilot,sizeof(vmt_stats_struct));

		return 0;	
	}
	return -2;
}

// Returns:
// -3	Error -- Called with NULL, but no request is waiting
// -2	Error -- Already sending data (hasn't timed out yet)
// -1	Timeout trying to send pilot data
// 0	Sending
// 1	Data succesfully sent
// 2	Send Cancelled (data may still have been written already, we just haven't been ACK'd yet)
// 3	Pilot not written (for some reason)
  
// Call with NULL to poll 
// Call with -1 to cancel send
// Call with valid pointer to a vmt_descent3_struct to initiate send
int SendSWData(squad_war_result *sw_res, squad_war_response *sw_resp)
{
	//First check the network
	PollPTrackNet();

	if(sw_res == nullptr){
		if(SWWriteState == STATE_IDLE){
			return -3;
		}
		if(SWWriteState == STATE_SENDING_PILOT){
			return 0;
		}

		// fill in the response
		if(SWWriteState == STATE_WROTE_PILOT){
			// We wrote this pilot, and now we are about to inform the app, so back to idle
			SWWriteState = STATE_IDLE;
			
			if(sw_resp != nullptr){
				memcpy(sw_resp, &SquadWarWriteResponse, sizeof(squad_war_response));
			}
			return 1;
		}
		// fill in the response
		if(SWWriteState == STATE_WRITE_PILOT_FAILED){
			// The tracker said this dude couldn't be written		
			SWWriteState = STATE_IDLE;

			if(sw_resp != nullptr){
				memcpy(sw_resp, &SquadWarWriteResponse, sizeof(squad_war_response));
			}
			return 3;
		}

		if(SWWriteState == STATE_TIMED_OUT){
			// We gave up on sending this pilot, and now we are about to inform the app, so back to idle
			SWWriteState = STATE_IDLE;

			return -1;
		}		
	} else if(sw_res == reinterpret_cast<squad_war_result *>(static_cast<uintptr_t>(0xffffffff))){
		if(SWWriteState == STATE_IDLE){
			return -3;
		} else {
			// Cancel this baby
			SWWriteState = STATE_IDLE;

			return 2;
		}
	} else if(SWWriteState == STATE_IDLE) {
		//New request, send out the req, and go for it.
		
		SWWriteState = STATE_SENDING_PILOT;
		
		SWLastSentWrite = 0;
		SWFirstSentWrite = timer_get_milliseconds();

		sw_res_write.len = PACKED_HEADER_ONLY_SIZE+sizeof(squad_war_result);
		sw_res_write.type = UNT_SW_RESULT_WRITE;
		sw_res_write.code = CMD_GAME_FS2OPEN;
		memcpy(&sw_res_write.data, sw_res, sizeof(squad_war_result));

		return 0;	
	}
	return -2;
}


// Returns:
// -3	Error -- Called with NULL, but no request is waiting
// -2	Error -- Already waiting on data (hasn't timed out yet)
// -1	Timeout waiting for pilot data
// 0	Waiting for data
// 1	Data received
// 2	Get Cancelled
// 3	Pilot not found
	
// Call with NULL to poll 
// Call with -1 to cancel wait
// Call with valid pointer to a vmt_descent3_struct to get a response
int GetFSPilotData(vmt_stats_struct *fs_pilot, const char *pilot_name, const char *tracker_id, int get_security)
{
	//First check the network
	PollPTrackNet();

	if(fs_pilot == nullptr)
	{
		if(FSReadState == STATE_IDLE)
		{
			return -3;
		}
		if(FSReadState == STATE_READING_PILOT)
		{
			return 0;
		}
		if(FSReadState == STATE_RECEIVED_PILOT)
		{
			// We got this pilot, and now we are about to inform the app, so back to idle
			FSReadState = STATE_IDLE;
			ReadFSPilot = nullptr;
			return 1;
		}
		if(FSReadState == STATE_TIMED_OUT)
		{
			// We gave up on this pilot, and now we are about to inform the app, so back to idle
			FSReadState = STATE_IDLE;
			ReadFSPilot = nullptr;
			return -1;
		}
		if(FSReadState == STATE_PILOT_NOT_FOUND)
		{
			//The tracker said this dude is not found.
			FSReadState = STATE_IDLE;
			ReadFSPilot = nullptr;
			return 3;
		}

	}
	else if(fs_pilot == reinterpret_cast<vmt_stats_struct *>(static_cast<uintptr_t>(0xffffffff)))
	{
		if(FSReadState == STATE_IDLE)
		{
			return -3;
		}
		else
		{
			//Cancel this baby
			FSReadState = STATE_IDLE;
			ReadFSPilot = nullptr;
			return 2;
		}

	}
	else if(FSReadState == STATE_IDLE)
	{
		//New request, send out the req, and go for it.
		
		FSReadState = STATE_READING_PILOT;
		ReadFSPilot = fs_pilot;
		FSLastSent = 0;
		FSFirstSent = timer_get_milliseconds();

		fs_pilot_req.xcode = static_cast<unsigned short>(Multi_fs_tracker_game_id);
		fs_pilot_req.len = PACKED_HEADER_ONLY_SIZE+sizeof(pilot_request);

		if(get_security){
			fs_pilot_req.type = UNT_PILOT_DATA_READ_NEW;
		} else {
			fs_pilot_req.type = UNT_PILOT_DATA_READ;			
		}

		fs_pilot_req.code = CMD_GAME_FS2OPEN;
		SDL_strlcpy(fs_pr->pilot_name, pilot_name, SDL_arraysize(fs_pr->pilot_name));
		SDL_strlcpy(fs_pr->tracker_id, tracker_id, SDL_arraysize(fs_pr->tracker_id));

		return 0;	
	}
	return -2;

}

// Send an ACK to the server
void AckServer(unsigned int sig)
{
	udp_packet_header ack_pack{};
	ubyte packet_data[sizeof(udp_packet_header)];
	int packet_length = 0;

	ack_pack.type = UNT_CONTROL;
	ack_pack.sig = sig;
	ack_pack.code = CMD_CLIENT_RECEIVED;
	ack_pack.len = PACKED_HEADER_ONLY_SIZE;	

	packet_length = SerializePilotPacket(&ack_pack, packet_data);
	Assert(packet_length == PACKED_HEADER_ONLY_SIZE);
	SENDTO(Psnet_socket, reinterpret_cast<char *>(&packet_data), packet_length, 0,
		   reinterpret_cast<LPSOCKADDR>(&ptrackaddr), sizeof(ptrackaddr), PSNET_TYPE_USER_TRACKER);
}

void IdlePTrack()
{
	ubyte packet_data[sizeof(udp_packet_header)];
	int packet_length = 0;

	PSNET_TOP_LAYER_PROCESS();

	// reading pilot data
	if(FSReadState == STATE_READING_PILOT){
		if((timer_get_milliseconds()-FSFirstSent)>=PILOT_REQ_TIMEOUT){
			FSReadState = STATE_TIMED_OUT;
		} else if((timer_get_milliseconds()-FSLastSent)>=PILOT_REQ_RESEND_TIME){
			//Send 'da packet
			packet_length = SerializePilotPacket(&fs_pilot_req, packet_data);
			SENDTO(Psnet_socket, reinterpret_cast<char *>(&packet_data), packet_length, 0,
				   reinterpret_cast<LPSOCKADDR>(&ptrackaddr), sizeof(ptrackaddr), PSNET_TYPE_USER_TRACKER);

			FSLastSent = timer_get_milliseconds();
		}
	}

	// writing pilot data
	if(FSWriteState == STATE_SENDING_PILOT){
		if((timer_get_milliseconds()-FSFirstSentWrite)>=PILOT_REQ_TIMEOUT){
			FSWriteState = STATE_TIMED_OUT;

		} else if((timer_get_milliseconds()-FSLastSentWrite)>=PILOT_REQ_RESEND_TIME){
			// Send 'da packet
			packet_length = SerializePilotPacket(&fs_pilot_write, packet_data);
			SENDTO(Psnet_socket, reinterpret_cast<char *>(&packet_data), packet_length, 0,
				   reinterpret_cast<LPSOCKADDR>(&ptrackaddr), sizeof(ptrackaddr), PSNET_TYPE_USER_TRACKER);

			FSLastSentWrite = timer_get_milliseconds();
		}
	}

	// writing squad war results
	if(SWWriteState == STATE_SENDING_PILOT){
		if((timer_get_milliseconds()-SWFirstSentWrite) >= PILOT_REQ_TIMEOUT){
			SWWriteState = STATE_TIMED_OUT;
		} else if((timer_get_milliseconds()-SWLastSentWrite) >= PILOT_REQ_RESEND_TIME){
			// Send 'da packet
			packet_length = SerializePilotPacket(&sw_res_write, packet_data);
			SENDTO(Psnet_socket, reinterpret_cast<char *>(&packet_data), packet_length, 0,
				   reinterpret_cast<LPSOCKADDR>(&ptrackaddr), sizeof(ptrackaddr), PSNET_TYPE_USER_TRACKER);

			SWLastSentWrite = timer_get_milliseconds();
		}
	}
}

void PollPTrackNet()
{
	fd_set read_fds;	           
	struct timeval timeout;
	ubyte packet_data[sizeof(udp_packet_header)];

	IdlePTrack();
	
	timeout.tv_sec=0;            
	timeout.tv_usec=0;
	
	FD_ZERO(&read_fds);	// NOLINT
	FD_SET(Psnet_socket, &read_fds);

	if(SELECT(static_cast<int>(Psnet_socket+1), &read_fds,nullptr,nullptr,&timeout, PSNET_TYPE_USER_TRACKER)){
		int bytesin;
		int addrsize;
		SOCKADDR_STORAGE fromaddr;

		udp_packet_header inpacket;

		SDL_zero(inpacket);
		addrsize = sizeof(fromaddr);

		bytesin = RECVFROM(Psnet_socket, reinterpret_cast<char *>(&packet_data), sizeof(udp_packet_header), 0,
						   reinterpret_cast<LPSOCKADDR>(&fromaddr), &addrsize, PSNET_TYPE_USER_TRACKER);

		if (bytesin > 0) {
			DeserializePilotPacket(packet_data, bytesin, &inpacket);

			// decrease packet size by 1
			inpacket.len--;
#ifndef NDEBUG
		} else {
			int wserr=WSAGetLastError();
			mprintf(("recvfrom() failure. WSAGetLastError() returned %d\n",wserr));
#endif
		}

		//Check to make sure the packets ok
		if ( (bytesin > 0) && (bytesin == inpacket.len) ) {
			switch(inpacket.type){
			case UNT_PILOT_DATA_RESPONSE:
				if(inpacket.code == CMD_GAME_FS2OPEN){
					if(FSReadState == STATE_READING_PILOT){
						vmt_stats_struct *stats;
						
						// 9/17/98 MWA.  Compare the tracker id of this packet with the tracker id of
						// what we are expecting.  Only set our state to something different when
						// the tracker id's match.  This fixes possible multiple packets for a single pilto
						// accidentally getting set for the wrong pilot

						stats = reinterpret_cast<vmt_stats_struct *>(&inpacket.data);
						if ( stats->tracker_id == SDL_atoi(fs_pr->tracker_id) ) {
							//Copy the data
							memcpy(ReadFSPilot,&inpacket.data,sizeof(vmt_stats_struct));
							//Set the state 
							FSReadState = STATE_RECEIVED_PILOT;
						}
					}
				} else {
					Int3();
				}
				break;

			case UNT_PILOT_READ_FAILED:
				if(inpacket.code == CMD_GAME_FS2OPEN){
					if(FSReadState == STATE_READING_PILOT){
						FSReadState = STATE_PILOT_NOT_FOUND;
					}
				} else {
					Int3();
				}
				break;

			case UNT_PILOT_WRITE_SUCCESS:
				if(inpacket.code == CMD_GAME_FS2OPEN){
					if(FSWriteState == STATE_SENDING_PILOT){
						FSWriteState = STATE_WROTE_PILOT;
					}
				} else {
					Int3();
				}
				break;

			case UNT_PILOT_WRITE_FAILED:
				if(inpacket.code == CMD_GAME_FS2OPEN){
					if(FSWriteState == STATE_SENDING_PILOT){
						FSWriteState = STATE_WRITE_PILOT_FAILED;
					}
				} else {
					Int3();
				}
				break;

			case UNT_SW_RESULT_RESPONSE:
				if(SWWriteState == STATE_SENDING_PILOT){					
					// copy the data
					Assert((bytesin - PACKED_HEADER_ONLY_SIZE) == sizeof(squad_war_response));
					if((bytesin - PACKED_HEADER_ONLY_SIZE) == sizeof(squad_war_response)){
						memset(&SquadWarWriteResponse, 0, sizeof(squad_war_response));
						memcpy(&SquadWarWriteResponse, inpacket.data, sizeof(squad_war_response));

						// now check to see if we're good
						if(SquadWarWriteResponse.accepted){
							SWWriteState = STATE_WROTE_PILOT;
						} else {
							SWWriteState = STATE_WRITE_PILOT_FAILED;
						}
					} else {
						SWWriteState = STATE_WRITE_PILOT_FAILED;
					}	
				}
				break;

			case UNT_CONTROL:
				Int3();
				break;

			case UNT_CONTROL_VALIDATION:
				Int3();
				break;

			default:
				break;
			}
			AckServer(inpacket.sig);
		}
	}
}
