//===========================================================================//
// File:	NetGenericMessages.cpp											
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 01/06/1999  JSE  Inital coding
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Microsoft Corp.										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//


#include "MW4Headers.hpp"

#include "NetGenericMessages.hpp"
#include "MWEntityManager.hpp"
#include "Objective.hpp"
#include "Flag.hpp"
#include "MWApplication.hpp"
#include "MWPlayer.hpp"
#include "MWMission.hpp"

#include "NavPoint.hpp"
#include <Adept\NameTable.hpp>
#include <Adept\Map.hpp>



#include <GameOS\Network.hpp>
#include <Adept\DamageObject.hpp>
#include <Adept\GlobalPointerManager.hpp>
#include "MW4\Bucket.hpp"
#include "MW4\MWDamageObject.hpp"
#include "MW4\VehicleInterface.hpp"
#include "MW4\Vehicle.hpp"
#include "MW4\Mech.hpp"
#include "MW4\WeaponMover.hpp"
#include "MW4\HeatManager.hpp"
#include "MW4\JumpJet.hpp"
#include "MW4\Weapon.hpp"
#include "MW4\NetMovement.hpp"
#include "MW4\NetWeapon.hpp"







//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void MissionObjectiveUpdate::Decode(Entity *entity, MemoryStream *memory_stream, Time till, Scalar packet_latency, int connection, int debug_level, int bandwidth)
{
	Check_Object(entity);
	Check_Object(memory_stream);

	NameTable *table = NameTable::GetInstance();
	Check_Object (table);


	int size = table->nameTableArray[NameTable::ObjectiveArray].GetLength ();
	for (int objective=0;objective<size;objective++)
	{
		NameTableEntry *data;

		data = &table->nameTableArray [NameTable::ObjectiveArray][objective];

		Objective *obj;
		
		Entity *ent;
		ent = data->dataPointer->GetCurrent ();
		if (ent)
		{

			obj = Cast_Object (Objective *,ent);
			Check_Object (obj);

			bool visibile;
			memory_stream->ReadBit(visibile);
			
			if (visibile)
			{	
				obj->Reveal();

				int status = 0; // jcem: critical error - should clear before ReadBits(...)...
				memory_stream->ReadBits(&status, 2);

				//SPEW(("jerryeds", "%d : 1-%d", objective, status));

				switch(status)
				{
				case Objective::Objective_Failed:
					obj->Fail();
					break;

				case Objective::Objective_Succeeded:
					obj->Succeed();
					break;

				case Objective::Objective_Neutral:
					//should never switch to neutral
					//and should always start in neutral
					break;

				}
			}
			else
			{
				obj->Hide();
				//SPEW(("jerryeds", "%d : 0", objective));
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void MissionObjectiveUpdate::Skip(MemoryStream *memory_stream, int size, Time till, int connection, int debug_level, int bandwidth)
{
	Check_Object(memory_stream);

	NameTable *table = NameTable::GetInstance();
	Check_Object (table);

	int table_size = table->nameTableArray[NameTable::ObjectiveArray].GetLength ();
	for (int objective=0;objective<table_size;objective++)
	{
		NameTableEntry *data;
		data = &table->nameTableArray [NameTable::ObjectiveArray][objective];

		Entity *ent;
		ent = data->dataPointer->GetCurrent ();
		if (ent)
		{

			bool visibile;
			memory_stream->ReadBit(visibile);
			
			if (visibile)
			{
				int status = 0; // jcem: critical error - should clear before ReadBits(...)...
				memory_stream->ReadBits(&status, 2);
			}
		}
	}
}



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int MissionObjectiveUpdate::MaxSize(Entity *entity, int debug_level, int bandwidth)
{

	NameTable *table = NameTable::GetInstance();
	Check_Object (table);

	int table_size = table->nameTableArray[NameTable::ObjectiveArray].GetLength ();

	return table_size*3;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void MissionObjectiveUpdate::Encode(Entity *entity, DynamicMemoryStream *memory_stream, int connection, int debug_level, int bandwidth)
{
	Check_Object(entity);
	Check_Object(memory_stream);


	NameTable *table = NameTable::GetInstance();
	Check_Object (table);

	int size = table->nameTableArray[NameTable::ObjectiveArray].GetLength ();
	for (int objective=0;objective<size;objective++)
	{
		NameTableEntry *data;

		data = &table->nameTableArray [NameTable::ObjectiveArray][objective];

		Objective *obj;
		
		Entity *ent;
		ent = data->dataPointer->GetCurrent ();
		if (ent)
		{

			obj = Cast_Object (Objective *,ent);
			Check_Object (obj);
		
			if (obj->Visible())
			{
				memory_stream->WriteBit(true);
				int status = obj->Status();
				memory_stream->WriteBits(&status, 2);

				//SPEW(("jerryeds", "%d : 1-%d", objective, status));

			}
			else
			{
				//SPEW(("jerryeds", "%d : 0", objective));
				memory_stream->WriteBit(false);
			}
		}
	}

	//SPEW(("jerryeds",""));

}

//====================================================================================================================================================
//



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void NavPointUpdate::Decode(Entity *entity, MemoryStream *memory_stream, Time till, Scalar packet_latency, int connection, int debug_level, int bandwidth)
{
	Check_Object(entity);
	Check_Object(memory_stream);

	NameTable *table = NameTable::GetInstance();
	Check_Object (table);
	

	int size = table->nameTableArray[NameTable::NavArray].GetLength ();
	for (int objective=0;objective<size;objective++)
	{
		NameTableEntry *data;

		data = &table->nameTableArray [NameTable::NavArray][objective];

		NavPoint *obj;
		
		Entity *ent;
		ent = data->dataPointer->GetCurrent ();
		if (ent)
		{

			obj = Cast_Object (NavPoint *,ent);
			Check_Object (obj);

			bool visibile;
			memory_stream->ReadBit(visibile);
			
			if (visibile)
			{	
				unsigned char radarid = 0; // jcem: critical error - should clear before ReadBits(...)...
				obj->Reveal();
				memory_stream->ReadBits (&radarid,3);
				if (radarid == 7)
					obj->m_RadarID = -1;
				else
					obj->m_RadarID = radarid;
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void NavPointUpdate::Skip(MemoryStream *memory_stream, int size, Time till, int connection, int debug_level, int bandwidth)
{
	Check_Object(memory_stream);

	NameTable *table = NameTable::GetInstance();
	Check_Object (table);

	int table_size = table->nameTableArray[NameTable::NavArray].GetLength ();
	for (int objective=0;objective<table_size;objective++)
	{
		NameTableEntry *data;
		data = &table->nameTableArray [NameTable::NavArray][objective];

		Entity *ent;
		ent = data->dataPointer->GetCurrent ();
		if (ent)
		{

			bool visibile;
			memory_stream->ReadBit(visibile);
			if (visibile)
			{
				BYTE temp = 0; // jcem: critical error - should clear before ReadBits(...)...
				memory_stream->ReadBits (&temp,3);
			}
		}
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int NavPointUpdate::MaxSize(Entity *entity, int debug_level, int bandwidth)
{

	NameTable *table = NameTable::GetInstance();
	Check_Object (table);

	int table_size = table->nameTableArray[NameTable::NavArray].GetLength ();

	return (table_size*4);			// 1 for visible, 3 for radarid
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void NavPointUpdate::Encode(Entity *entity, DynamicMemoryStream *memory_stream, int connection, int debug_level, int bandwidth)
{
	Check_Object(entity);
	Check_Object(memory_stream);


	NameTable *table = NameTable::GetInstance();
	Check_Object (table);

	int size = table->nameTableArray[NameTable::NavArray].GetLength ();
	for (int objective=0;objective<size;objective++)
	{
		NameTableEntry *data;

		data = &table->nameTableArray [NameTable::NavArray][objective];

		NavPoint *obj;
		
		Entity *ent;
		ent = data->dataPointer->GetCurrent ();
		if (ent)
		{

			obj = Cast_Object (NavPoint *,ent);
			Check_Object (obj);
		


			if (obj->IsVisible())
			{
				memory_stream->WriteBit(true);
				unsigned char radarid;
				if (obj->m_RadarID == -1)
					radarid = 7;
				else
					radarid = (unsigned char) obj->m_RadarID;
				memory_stream->WriteBits (&radarid,3);

				//SPEW(("jerryeds", "%d : 1-%d", objective, status));

			}
			else
			{
				//SPEW(("jerryeds", "%d : 0", objective));
				memory_stream->WriteBit(false);
			}
		}
	}

	//SPEW(("jerryeds",""));

}

//====================================================================================================================================================
//


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void FlagUpdate::Decode(Entity *entity, MemoryStream *memory_stream, Time till, Scalar packet_latency, int connection, int debug_level, int bandwidth)
{
	NameTable *table = NameTable::GetInstance();
	Check_Object (table);

	MWMission* mwmission = Cast_Object(MWMission*,MWMission::GetInstance());

	bool flags_as_nav_points(mwmission->GetFlagsAsNavPoints());
	memory_stream->ReadBit(flags_as_nav_points);
	mwmission->SetFlagsAsNavPoints(flags_as_nav_points);

	int temp(0);	
	memory_stream->ReadBits(&temp,2);

	MWMission::FLAG_ENABLE_STATE flags_enabled = (MWMission::FLAG_ENABLE_STATE)temp;

	mwmission->SetFlagsEnabled(flags_enabled);

	int size = table->nameTableArray[NameTable::FlagArray].GetLength ();
	for (int objective=0;objective<size;objective++)
	{
		NameTableEntry *data;

		data = &table->nameTableArray [NameTable::FlagArray][objective];

		Flag *flag;
		
		Entity *ent;
		ent = data->dataPointer->GetCurrent ();
		if (ent)
		{
			
			flag = Cast_Object (Flag *,ent);
			Check_Object (flag);

			bool visible;
			memory_stream->ReadBit(visible);

			if (visible)
			{
				flag->RevealFlag();

				int last_event = 0;
				memory_stream->ReadBits(&last_event,2);
				flag->SetLastFlagEvent((Flag::FlagEvent)last_event);

				int mode = 0;
				memory_stream->ReadBits(&mode,2);

				if (mode == GroundMode)
				{
					if (flag->Attached())
					{
						flag->Attach(NULL);
					}

					Point3D position;
					position = flag->GetLocalToWorld();

					Scalar min_z, max_z, min_x, max_x;
					Map::GetInstance()->GetMapExtents(&min_z, &max_z, &min_x, &max_x);

					memory_stream->ReadBitsToScaledFloat(position.x, min_x, max_x, BitDepthManager::BitDepth(UpdateType,XPositionUpdateEntryID));
					memory_stream->ReadBitsToScaledFloat(position.z, min_z, max_z, BitDepthManager::BitDepth(UpdateType,ZPositionUpdateEntryID));

					UnitQuaternion rot;
					rot = flag->GetLocalToWorld();

					LinearMatrix4D new_mat;
					new_mat.BuildTranslation(position);
					new_mat.BuildRotation(rot);

					flag->SetNewLocalToParent(new_mat);
					flag->SyncMatrices(true);

				}
				else if (mode == ServerObjectMode)
				{
					
					WORD object_id;
					memory_stream->ReadBits(&object_id, 16);

					ReplicatorID obj_id(Connection::Server->GetID(), object_id);

					// if we are not connected to the right object
					if (flag->Attached() && flag->Attached()->GetReplicatorID().localID != object_id)
					{
						flag->Attach(NULL);
					}

					// if we are not connected
					if (flag->Attached() == NULL)
					{
						// find the object...

						Connection *conn = Network::GetInstance()->GetConnection(Connection::Server->GetID());
						
						if (conn != NULL)
						{
							Entity *entity = Cast_Object(Entity*, conn->FindReplicator(obj_id));
							if (entity)
								flag->Attach(entity);
						}
					}
				}
				else
				{
					WORD object_id = 0; // jcem: critical error - should clear before ReadBits(...)...
					memory_stream->ReadBits(&object_id, 8);

					if (flag->Attached() && flag->Attached()->GetReplicatorID().connectionID != object_id)
					{
						flag->Attach(NULL);
					}

					if (flag->Attached() == NULL)
					{
					

						Entity *entity = NULL;
						if (MWApplication::GetInstance()->servedConnectionData[object_id].clientPlayer)
							entity = MWApplication::GetInstance()->servedConnectionData[object_id].clientPlayer->vehicle;
					
						if (entity)
							flag->Attach(entity);
					}
				}
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void FlagUpdate::Skip(MemoryStream *memory_stream, int , Time till, int connection, int debug_level, int bandwidth)
{
	Check_Object(memory_stream);

	NameTable *table = NameTable::GetInstance();
	Check_Object (table);

	bool flags_as_nav_points;	
	memory_stream->ReadBit(flags_as_nav_points);

	int flags_enabled(0);
	memory_stream->ReadBits(&flags_enabled,2);

	int size = table->nameTableArray[NameTable::FlagArray].GetLength ();
	for (int objective=0;objective<size;objective++)
	{
		NameTableEntry *data;

		data = &table->nameTableArray [NameTable::FlagArray][objective];

		Flag *flag;
		
		Entity *ent;
		ent = data->dataPointer->GetCurrent ();
		if (ent)
		{
			
			flag = Cast_Object (Flag *,ent);
			Check_Object (flag);
			
			bool visible;
			
			memory_stream->ReadBit(visible);

			if (visible)
			{
				int last_event = 0;
				memory_stream->ReadBits(&last_event,2);

				int mode = 0;
				memory_stream->ReadBits(&mode,2);

				if (mode == GroundMode)
				{
					Scalar min_z, max_z, min_x, max_x;
					Map::GetInstance()->GetMapExtents(&min_z, &max_z, &min_x, &max_x);

					int throw_away;
					memory_stream->ReadBits(&throw_away, BitDepthManager::BitDepth(UpdateType,XPositionUpdateEntryID));
					memory_stream->ReadBits(&throw_away, BitDepthManager::BitDepth(UpdateType,ZPositionUpdateEntryID));

				}
				else if (mode == ServerObjectMode)
				{
					
					int object_id;
					memory_stream->ReadBits(&object_id, 16);

				}
				else
				{
					
					int object_id;
					memory_stream->ReadBits(&object_id, 8);
				}
			}
		}
	}

}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int FlagUpdate::MaxSize(Entity *entity, int debug_level, int bandwidth)
{
	NameTable *table = NameTable::GetInstance();
	Check_Object (table);

	int table_size = table->nameTableArray[NameTable::NavArray].GetLength ();

	int size_per_entry = BitDepthManager::BitDepth(UpdateType,XPositionUpdateEntryID) + 5;

	return ((table_size * size_per_entry) + 3);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void FlagUpdate::Encode(Entity *entity, DynamicMemoryStream *memory_stream, int connection, int debug_level, int bandwidth)
{
	Check_Object(entity);
	Check_Object(memory_stream);


	NameTable *table = NameTable::GetInstance();
	Check_Object (table);

	MWMission* mwmission = Cast_Object(MWMission*,MWMission::GetInstance());

	memory_stream->WriteBit(mwmission->GetFlagsAsNavPoints());

	MWMission::FLAG_ENABLE_STATE flags_enabled(mwmission->GetFlagsEnabled());
	memory_stream->WriteBits(&flags_enabled,2);

	int size = table->nameTableArray[NameTable::FlagArray].GetLength ();
	for (int objective=0;objective<size;objective++)
	{
		NameTableEntry *data;

		data = &table->nameTableArray [NameTable::FlagArray][objective];

		Flag *flag;

		Entity *ent;
		ent = data->dataPointer->GetCurrent ();
		if (ent)
		{

			flag = Cast_Object (Flag *,ent);
			Check_Object (flag);

			bool visible = flag->IsVisible();

			memory_stream->WriteBit(visible);

			if (visible)
			{
				int last_event = (int)flag->GetLastFlagEvent();
				memory_stream->WriteBits(&last_event,2);
				
				Entity *attached_to = flag->Attached();

				int mode = GroundMode;

				if (!attached_to)
				{
					mode = GroundMode;
					memory_stream->WriteBits(&mode,2);

					Point3D position;
					position = flag->GetLocalToWorld();

					Scalar min_z, max_z, min_x, max_x;
					Map::GetInstance()->GetMapExtents(&min_z, &max_z, &min_x, &max_x);

					memory_stream->WriteScaledFloatToBits(position.x, min_x, max_x, BitDepthManager::BitDepth(UpdateType,XPositionUpdateEntryID));
					memory_stream->WriteScaledFloatToBits(position.z, min_z, max_z, BitDepthManager::BitDepth(UpdateType,ZPositionUpdateEntryID));

				}
				else if (attached_to->GetReplicatorID().connectionID == Connection::Server->GetID())
				{
					mode = ServerObjectMode;
					memory_stream->WriteBits(&mode,2);

					int object_id = attached_to->GetReplicatorID().localID;
					memory_stream->WriteBits(&object_id, 16);

				}
				else
				{
					mode = PlayerMode;
					memory_stream->WriteBits(&mode,2);

					int object_id = attached_to->GetReplicatorID().connectionID;
					memory_stream->WriteBits(&object_id, 8);
				}
			}
		}
	}
}	




//====================================================================================================================================================
//



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void TimeUpdate::Decode(Entity *entity, MemoryStream *memory_stream, Time till, Scalar packet_latency, int connection, int debug_level, int bandwidth)
{
	Check_Object(entity);
	Check_Object(memory_stream);

	Scalar mission_time;
	memory_stream->ReadBits(&mission_time, 32);

	mission_time += packet_latency;

	MWMission *mission = Cast_Object(MWMission*, MWMission::GetInstance());
	mission->SetNetworkTime(mission_time);

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void TimeUpdate::Skip(MemoryStream *memory_stream, int size, Time till, int connection, int debug_level, int bandwidth)
{
	Check_Object(memory_stream);


	Scalar mission_time;
	memory_stream->ReadBits(&mission_time, 32);

}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int TimeUpdate::MaxSize(Entity *entity, int debug_level, int bandwidth)
{
	return 32;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void TimeUpdate::Encode(Entity *entity, DynamicMemoryStream *memory_stream, int connection, int debug_level, int bandwidth)
{
	Check_Object(entity);
	Check_Object(memory_stream);


	//SPEW(("jerryeds",""));

	MWMission *mission = Cast_Object(MWMission*, MWMission::GetInstance());

	Scalar mission_time;
	mission_time = mission->GetMissionTime();
	memory_stream->WriteBits(&mission_time, 32);


}
//====================================================================================================================================================
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void PingUpdate::Decode(Entity *entity, MemoryStream *memory_stream, Time till, Scalar packet_latency, int connection, int debug_level, int bandwidth)
{
	Check_Object(entity);
	Check_Object(memory_stream);


	Scalar ping_time;
	
	memory_stream->ReadBitsToScaledFloat(ping_time, 0.0f, 10000.0f, 8);
	MWApplication::GetInstance()->servedConnectionData[entity->GetReplicatorID().connectionID].effectivePing = ping_time;

	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void PingUpdate::Skip(MemoryStream *memory_stream, int size, Time till, int connection, int debug_level, int bandwidth)
{
	Check_Object(memory_stream);

	
	Scalar ping_time;
	memory_stream->ReadBits(&ping_time, 8);

}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int PingUpdate::MaxSize(Entity *entity, int debug_level, int bandwidth)
{
	return 8;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void PingUpdate::Encode(Entity *entity, DynamicMemoryStream *memory_stream, int connection, int debug_level, int bandwidth)
{
	Check_Object(entity);
	Check_Object(memory_stream);

	Scalar ping_time;
	ping_time = MWApplication::GetInstance()->servedConnectionData[entity->GetReplicatorID().connectionID].averagePing.CalculateAverage();

	Clamp(ping_time, 0, 10000.0f);
	memory_stream->WriteScaledFloatToBits(ping_time, 0.0f, 10000.0f, 8);



}
//====================================================================================================================================================
//

void __cdecl MechWarrior4::NetGenericMessagesSecurityCheckStart(void)
{
	_asm
	{
		nop
	}
}



//define USE_HACK_CRC 1

#ifdef USE_HACK_CRC

	#include "crckey.hpp"

	void *security_array[] = 
	{
		(void*)&sequirity_0[0],
		(void*)&sequirity_1[0],
		(void*)&sequirity_2[0],
		(void*)&sequirity_3[0],
		(void*)&sequirity_4[0],
		(void*)&sequirity_5[0],
		(void*)&sequirity_6[0],
		(void*)&sequirity_7[0],
		(void*)&sequirity_8[0],
		(void*)&sequirity_9[0],
		(void*)&sequirity_10[0],
		(void*)&sequirity_11[0],
		(void*)&sequirity_12[0],
		(void*)&sequirity_13[0],
		(void*)&sequirity_14[0]
	};

	DWORD security_length[] = 
	{
		(DWORD)ELEMENTS(sequirity_0)<<2,
		(DWORD)ELEMENTS(sequirity_1)<<2,
		(DWORD)ELEMENTS(sequirity_2)<<2,
		(DWORD)ELEMENTS(sequirity_3)<<2,
		(DWORD)ELEMENTS(sequirity_4)<<2,
		(DWORD)ELEMENTS(sequirity_5)<<2,
		(DWORD)ELEMENTS(sequirity_6)<<2,
		(DWORD)ELEMENTS(sequirity_7)<<2,
		(DWORD)ELEMENTS(sequirity_8)<<2,
		(DWORD)ELEMENTS(sequirity_9)<<2,
		(DWORD)ELEMENTS(sequirity_10)<<2,
		(DWORD)ELEMENTS(sequirity_11)<<2,
		(DWORD)ELEMENTS(sequirity_12)<<2,
		(DWORD)ELEMENTS(sequirity_13)<<2,
		(DWORD)ELEMENTS(sequirity_14)<<2
	};

#else

	void *security_array[] = 
	{
		(void*)&MD5SecurityCheckStart,
		(void*)&AdeptDamageSecurityCheckStart,
		(void*)&AdeptGlobalPointerSecurityCheckStart,
		(void*)&BucketSecurityCheckStart,
		(void*)&MW4DamageSecurityCheckStart,
		(void*)&VehicleInterfaceSecurityCheckStart,
		(void*)&VehicleSecurityCheckStart,
		(void*)&MechSecurityCheckStart,
		(void*)&WeaponMoverSecurityCheckStart,
		(void*)&HeatSecurityCheckStart,
		(void*)&JumpJetSecurityCheckStart,
		(void*)&WeaponSecurityCheckStart,
		(void*)&NetMovementSecurityCheckStart,
		(void*)&NetWeaponSecurityCheckStart,
		(void*)&NetGenericMessagesSecurityCheckStart
	};



	DWORD security_length[] = 
	{
		(DWORD)&MD5SecurityCheckStop -	(DWORD)&MD5SecurityCheckStart,
		(DWORD)&AdeptDamageSecurityCheckStop -	(DWORD)&AdeptDamageSecurityCheckStart,
		(DWORD)&AdeptGlobalPointerSecurityCheckStop -	(DWORD)&AdeptGlobalPointerSecurityCheckStart,
		(DWORD)&BucketSecurityCheckStop - (DWORD)&BucketSecurityCheckStart,
		(DWORD)&MW4DamageSecurityCheckStop - (DWORD)&MW4DamageSecurityCheckStart,
		(DWORD)&VehicleInterfaceSecurityCheckStop - (DWORD)&VehicleInterfaceSecurityCheckStart,
		(DWORD)&VehicleSecurityCheckStop -	(DWORD)&VehicleSecurityCheckStart,
		(DWORD)&MechSecurityCheckStop - (DWORD)&MechSecurityCheckStart,
		(DWORD)&WeaponMoverSecurityCheckStop -	(DWORD)&WeaponMoverSecurityCheckStart,
		(DWORD)&HeatSecurityCheckStop - (DWORD)&HeatSecurityCheckStart,
		(DWORD)&JumpJetSecurityCheckStop -	(DWORD)&JumpJetSecurityCheckStart,
		(DWORD)&WeaponSecurityCheckStop - (DWORD)&WeaponSecurityCheckStart,
		(DWORD)&NetMovementSecurityCheckStop -	(DWORD)&NetMovementSecurityCheckStart,
		(DWORD)&NetWeaponSecurityCheckStop - (DWORD)&NetWeaponSecurityCheckStart,
		(DWORD)&NetGenericMessagesSecurityCheckStop - (DWORD)&NetGenericMessagesSecurityCheckStart
	};

#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void SecurityQuery::Decode(Entity *entity, MemoryStream *memory_stream, Time till, Scalar packet_latency, int connection, int debug_level, int bandwidth)
{
	//Check_Object(entity);
	Check_Object(memory_stream);

	int key = 0;
	memory_stream->ReadBits(&key, 16);

	SecurityQueryInfo *seq_info; 
	MWEntityManager *manger = (MWEntityManager*)MWEntityManager::GetInstance();
	if (MWApplication::GetInstance()->serverFlag)
	{
		seq_info = &manger->serverController->securityQuery[connection];
	}
	else
	{
		seq_info = &manger->clientController->securityQuery;
	}


	seq_info->otherSidesKey = key;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void SecurityQuery::Skip(MemoryStream *memory_stream, int size, Time till, int connection, int debug_level, int bandwidth)
{
	Check_Object(memory_stream);


	int key = 0;
	memory_stream->ReadBits(&key, 16);


}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int SecurityQuery::MaxSize(Entity *entity, int debug_level, int bandwidth)
{
	return 16;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void SecurityQuery::Encode(Entity *entity, DynamicMemoryStream *memory_stream, int connection, int debug_level, int bandwidth)
{

	SecurityQueryInfo *seq_info; 
	MWEntityManager *manger = (MWEntityManager*)MWEntityManager::GetInstance();
	if (MWApplication::GetInstance()->serverFlag)
	{
		seq_info = &manger->serverController->securityQuery[connection];
	}
	else
	{
		seq_info = &manger->clientController->securityQuery;
	}

	
	memory_stream->WriteBits(&seq_info->securityKey, 16);

}



void KickPlayer(int connection)
{
	//SPEW(("jerryeds", "KICK PLAYER"));

	if (MWApplication::GetInstance()->serverFlag)
	{
		MWApplication::GetInstance()->servedConnectionData[connection].clientQuedForKick = true;	
	}
	else
	{
		MWApplication::GetInstance()->networkExitCode = MWApplication::BadServerExitCode;
		MWApplication::GetInstance()->QueStopGame(true);
	}

}


//====================================================================================================================================================
//


void SecurityResponse::Decode(Entity *entity, MemoryStream *memory_stream, Time till, Scalar packet_latency, int connection, int debug_level, int bandwidth)
{
	//Check_Object(entity);
	Check_Object(memory_stream);



	int key = 0;
	WORD client_answer = 0;

	memory_stream->ReadBits(&key, 16);
	memory_stream->ReadBits(&client_answer, 16);


	bool server_flag = MWApplication::GetInstance()->serverFlag;


	SecurityQueryInfo *seq_info; 
	MWEntityManager *manger = (MWEntityManager*)MWEntityManager::GetInstance();
	if (server_flag)
	{
		seq_info = &manger->serverController->securityQuery[connection];
	}
	else
	{
		seq_info = &manger->clientController->securityQuery;
	}


	GUID null_hash;
	memset(&null_hash, 0, sizeof(null_hash));


	if (seq_info->needInit)
	{
			seq_info->needInit = false;
		// we need an init!
			MD5_CTX resource_context;
			GUID new_code_hash;
			unsigned char * buff=(unsigned char *) &new_code_hash;

						
			MD5Init(&resource_context);
			int new_key = 0;
			
			for (int i = 0; i < ELEMENTS(security_array); ++i)
			{
				MD5Update(&resource_context, (unsigned char *) security_array[i], security_length[i]);
				MD5Update(&resource_context, (unsigned char *) &key, 4);
			}

			MD5Final(buff, &resource_context);

			seq_info->securityKey = new_key;
			seq_info->securityAnswer = new_code_hash;
			seq_info->securityTimer = (Scalar)gos_GetElapsedTime();
			seq_info->lastSecurityKey = new_key;
			seq_info->lastSecurityAnswer = new_code_hash;
	
	}

	
	if (key == seq_info->securityKey)
	{
		//if (memcmp(&code_hash, &seq_info->securityAnswer, sizeof(code_hash)) != 0)

		WORD server_answer=0;
		WORD *break_down = (WORD*)&seq_info->securityAnswer;
		for (int i = 0; i < 8; ++i)
		{
			server_answer ^= break_down[i];
		}

		if (client_answer != server_answer)
		{
			// KICK PLAYER
			//STOP(("KICK PLAYER"));

			KickPlayer(connection);
		}
		else
		{
			// roll a new key
			seq_info->lastSecurityKey = seq_info->securityKey;
			seq_info->lastSecurityAnswer = seq_info->securityAnswer;


			MD5_CTX resource_context;
			GUID new_code_hash;
			unsigned char * buff=(unsigned char *) &new_code_hash;

						
			MD5Init(&resource_context);
			int temp = Random::GetInt();

			int new_key = temp >> 16;
			new_key ^= (temp&0xffff);
			
			
			for (int i = 0; i < ELEMENTS(security_array); ++i)
			{
				MD5Update(&resource_context, (unsigned char *) security_array[i], security_length[i]);
				MD5Update(&resource_context, (unsigned char *) &new_key, 4);
			}

			MD5Final(buff, &resource_context);


			seq_info->securityKey = new_key;
			seq_info->securityAnswer = new_code_hash;
			seq_info->securityTimer = (Scalar)gos_GetElapsedTime();


		}
	}
	else if (key == seq_info->lastSecurityKey)
	{
		WORD server_answer=0;
		WORD *break_down = (WORD*)&seq_info->lastSecurityAnswer;
		for (int i = 0; i < 8; ++i)
		{
			server_answer ^= break_down[i];
		}

		if (client_answer != server_answer)
		{
			// KICK PLAYER
			KickPlayer(connection);
		}
		else
		{
			// check the timer...
			Scalar current_time = (Scalar)gos_GetElapsedTime();
			if (current_time-seq_info->securityTimer > 75.0f + packet_latency)
			{
				KickPlayer(connection);
			}
		}
	}
	else 
	{
		KickPlayer(connection);
	}

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void SecurityResponse::Skip(MemoryStream *memory_stream, int size, Time till, int connection, int debug_level, int bandwidth)
{
	Check_Object(memory_stream);

	

	int key = 0;

	memory_stream->ReadBits(&key, 16);
	memory_stream->ReadBits(&key, 16);
	


}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int SecurityResponse::MaxSize(Entity *entity, int debug_level, int bandwidth)
{
	return 32;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
void SecurityResponse::Encode(Entity *entity, DynamicMemoryStream *memory_stream, int connection, int debug_level, int bandwidth)
{
	//Check_Object(entity);
	Check_Object(memory_stream);


	Verify(ELEMENTS(security_array) == ELEMENTS(security_length));

	MD5_CTX resource_context;
	GUID code_hash;
	unsigned char * buff=(unsigned char *) &code_hash;


	SecurityQueryInfo *seq_info;
	MWEntityManager *manger = (MWEntityManager*)MWEntityManager::GetInstance();
	if (MWApplication::GetInstance()->serverFlag)
	{
		seq_info = &manger->serverController->securityQuery[connection];
	}
	else
	{
		seq_info = &manger->clientController->securityQuery;
	}


	MD5Init(&resource_context);
	int key = seq_info->otherSidesKey;
	
	for (int i = 0; i < ELEMENTS(security_array); ++i)
	{
		MD5Update(&resource_context, (unsigned char *) security_array[i], security_length[i]);
		MD5Update(&resource_context, (unsigned char *) &key, 4);	
	}
	MD5Final(buff, &resource_context);

	WORD *break_down = (WORD*)&code_hash;
	memory_stream->WriteBits(&key, 16);
	
	WORD result = 0; 
	for (i = 0; i < 8; ++i)
	{
		result ^= break_down[i];
	}
	
	
	memory_stream->WriteBits(&result, 16);

	//SPEW(("jerryeds", "SEC_RESP ENCODE : %d :%d,  %u-%u-%u-%u", Connection::Local->GetID(), key, break_down[0], break_down[1],break_down[2],break_down[3]));

}



__int64 MechWarrior4::CalculateCodeHash(int key)
{
	MD5_CTX resource_context;
	GUID code_hash;
	unsigned char * buff=(unsigned char *) &code_hash;


	MD5Init(&resource_context);
	
	
	
	
	for (int i = 0; i < ELEMENTS(security_array); ++i)
	{



		MD5Update(&resource_context, (unsigned char *) security_array[i], security_length[i]);
		MD5Update(&resource_context, (unsigned char *) &key, 4);	
	}
	MD5Final(buff, &resource_context);

	__int64 *break_down = (__int64*)&code_hash;

	__int64 final = break_down[0] ^ break_down[1];

	return final;

}


void __cdecl MechWarrior4::NetGenericMessagesSecurityCheckStop()
{
	_asm
	{
		nop
	}	
}


