about summary refs log tree commit diff
path: root/src/engine
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2009-10-27 14:38:53 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2009-10-27 14:38:53 +0000
commit878ede3080ab2cfb627aca505c397d9765052996 (patch)
tree98bff371070e1dca0295f0ca58d64ac4ee8042ce /src/engine
parent9b99ec0e60b60134e46f2f71d707230948f7db03 (diff)
downloadzcatch-878ede3080ab2cfb627aca505c397d9765052996.tar.gz
zcatch-878ede3080ab2cfb627aca505c397d9765052996.zip
major update with stuff
Diffstat (limited to 'src/engine')
-rw-r--r--src/engine/client/ec_client.cpp (renamed from src/engine/client/ec_client.c)396
-rw-r--r--src/engine/client/ec_gfx.c1020
-rw-r--r--src/engine/client/ec_gfx.cpp992
-rw-r--r--src/engine/client/ec_gfx_text.cpp (renamed from src/engine/client/ec_gfx_text.c)29
-rw-r--r--src/engine/client/ec_inp.cpp (renamed from src/engine/client/ec_inp.c)12
-rw-r--r--src/engine/client/ec_snd.cpp (renamed from src/engine/client/ec_snd.c)22
-rw-r--r--src/engine/client/ec_srvbrowse.cpp (renamed from src/engine/client/ec_srvbrowse.c)69
-rw-r--r--src/engine/e_client_interface.h8
-rw-r--r--src/engine/e_common_interface.h8
-rw-r--r--src/engine/e_compression.cpp (renamed from src/engine/e_compression.c)62
-rw-r--r--src/engine/e_compression.h4
-rw-r--r--src/engine/e_config.cpp (renamed from src/engine/e_config.c)0
-rw-r--r--src/engine/e_config.h8
-rw-r--r--src/engine/e_console.cpp (renamed from src/engine/e_console.c)14
-rw-r--r--src/engine/e_console.h14
-rw-r--r--src/engine/e_datafile.cpp (renamed from src/engine/e_datafile.c)0
-rw-r--r--src/engine/e_demorec.cpp (renamed from src/engine/e_demorec.c)33
-rw-r--r--src/engine/e_demorec.h8
-rw-r--r--src/engine/e_engine.cpp (renamed from src/engine/e_engine.c)6
-rw-r--r--src/engine/e_huffman.cpp (renamed from src/engine/e_huffman.c)0
-rw-r--r--src/engine/e_huffman.h8
-rw-r--r--src/engine/e_if_other.h3
-rw-r--r--src/engine/e_jobs.cpp (renamed from src/engine/e_jobs.c)0
-rw-r--r--src/engine/e_keynames.cpp (renamed from src/engine/e_keynames.c)0
-rw-r--r--src/engine/e_linereader.cpp (renamed from src/engine/e_linereader.c)0
-rw-r--r--src/engine/e_map.cpp (renamed from src/engine/e_map.c)0
-rw-r--r--src/engine/e_memheap.cpp (renamed from src/engine/e_memheap.c)10
-rw-r--r--src/engine/e_memheap.h2
-rw-r--r--src/engine/e_msg.cpp (renamed from src/engine/e_msg.c)30
-rw-r--r--src/engine/e_network.c351
-rw-r--r--src/engine/e_network.cpp347
-rw-r--r--src/engine/e_network.h395
-rw-r--r--src/engine/e_network_client.c154
-rw-r--r--src/engine/e_network_client.cpp139
-rw-r--r--src/engine/e_network_conn.c366
-rw-r--r--src/engine/e_network_conn.cpp349
-rw-r--r--src/engine/e_network_internal.h156
-rw-r--r--src/engine/e_network_server.c484
-rw-r--r--src/engine/e_network_server.cpp408
-rw-r--r--src/engine/e_packer.c213
-rw-r--r--src/engine/e_packer.cpp155
-rw-r--r--src/engine/e_packer.h58
-rw-r--r--src/engine/e_ringbuffer.c362
-rw-r--r--src/engine/e_ringbuffer.cpp194
-rw-r--r--src/engine/e_ringbuffer.h67
-rw-r--r--src/engine/e_server_interface.h8
-rw-r--r--src/engine/e_snapshot.c604
-rw-r--r--src/engine/e_snapshot.cpp571
-rw-r--r--src/engine/e_snapshot.h160
-rw-r--r--src/engine/server/es_register.cpp (renamed from src/engine/server/es_register.c)83
-rw-r--r--src/engine/server/es_server.c1380
-rw-r--r--src/engine/server/es_server.cpp1969
52 files changed, 5976 insertions, 5755 deletions
diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.cpp
index 0c707ccf..7ba0a2bb 100644
--- a/src/engine/client/ec_client.c
+++ b/src/engine/client/ec_client.cpp
@@ -28,6 +28,28 @@
 #include <mastersrv/mastersrv.h>
 #include <versionsrv/versionsrv.h>
 
+#include "editor.h"
+#include "graphics.h"
+#include "client.h"
+
+static IEditor *m_pEditor = 0;
+static IEngineGraphics *m_pGraphics = 0;
+IEngineGraphics *Graphics() { return m_pGraphics; }
+
+static IGameClient *m_pGameClient = 0;
+
+
+class CClient : public IEngine
+{
+public:
+	virtual class IGraphics *Graphics()
+	{
+		return m_pGraphics;
+	}
+};
+
+static CClient m_Client;
+
 const int prediction_margin = 1000/50/2; /* magic network prediction value */
 
 /*
@@ -43,7 +65,7 @@ const int prediction_margin = 1000/50/2; /* magic network prediction value */
 */
 
 /* network client, must be accessible from other parts like the server browser */
-NETCLIENT *net;
+CNetClient m_NetClient;
 
 /* TODO: ugly, fix me */
 extern void client_serverbrowse_set(NETADDR *addr, int request, int token, SERVER_INFO *info);
@@ -171,22 +193,22 @@ static void graph_render(GRAPH *g, float x, float y, float w, float h, const cha
 	char buf[32];
 	int i;
 
-	gfx_blend_normal();
+	//m_pGraphics->BlendNormal();
 
 	
-	gfx_texture_set(-1);
+	Graphics()->TextureSet(-1);
 	
-	gfx_quads_begin();
-	gfx_setcolor(0, 0, 0, 0.75f);
-	gfx_quads_drawTL(x, y, w, h);
-	gfx_quads_end();
+	m_pGraphics->QuadsBegin();
+	Graphics()->SetColor(0, 0, 0, 0.75f);
+	Graphics()->QuadsDrawTL(x, y, w, h);
+	m_pGraphics->QuadsEnd();
 		
-	gfx_lines_begin();
-	gfx_setcolor(0.95f, 0.95f, 0.95f, 1.00f);
-	gfx_lines_draw(x, y+h/2, x+w, y+h/2);
-	gfx_setcolor(0.5f, 0.5f, 0.5f, 0.75f);
-	gfx_lines_draw(x, y+(h*3)/4, x+w, y+(h*3)/4);
-	gfx_lines_draw(x, y+h/4, x+w, y+h/4);
+	Graphics()->LinesBegin();
+	Graphics()->SetColor(0.95f, 0.95f, 0.95f, 1.00f);
+	Graphics()->LinesDraw(x, y+h/2, x+w, y+h/2);
+	Graphics()->SetColor(0.5f, 0.5f, 0.5f, 0.75f);
+	Graphics()->LinesDraw(x, y+(h*3)/4, x+w, y+(h*3)/4);
+	Graphics()->LinesDraw(x, y+h/4, x+w, y+h/4);
 	for(i = 1; i < GRAPH_MAX; i++)
 	{
 		float a0 = (i-1)/(float)GRAPH_MAX;
@@ -197,22 +219,22 @@ static void graph_render(GRAPH *g, float x, float y, float w, float h, const cha
 		float v0 = (g->values[i0]-g->min) / (g->max-g->min);
 		float v1 = (g->values[i1]-g->min) / (g->max-g->min);
 		
-		gfx_setcolorvertex(0, g->colors[i0][0], g->colors[i0][1], g->colors[i0][2], 0.75f);
-		gfx_setcolorvertex(1, g->colors[i1][0], g->colors[i1][1], g->colors[i1][2], 0.75f);
-		gfx_lines_draw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h);
+		Graphics()->SetColorVertex(0, g->colors[i0][0], g->colors[i0][1], g->colors[i0][2], 0.75f);
+		Graphics()->SetColorVertex(1, g->colors[i1][0], g->colors[i1][1], g->colors[i1][2], 0.75f);
+		Graphics()->LinesDraw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h);
 
 	}
-	gfx_lines_end();
+	Graphics()->LinesEnd();
 	
 
-	gfx_texture_set(debug_font);	
-	gfx_quads_text(x+2, y+h-16, 16, 1,1,1,1, description);
+	Graphics()->TextureSet(debug_font);	
+	Graphics()->QuadsText(x+2, y+h-16, 16, 1,1,1,1, description);
 
 	str_format(buf, sizeof(buf), "%.2f", g->max);
-	gfx_quads_text(x+w-8*strlen(buf)-8, y+2, 16, 1,1,1,1, buf);
+	Graphics()->QuadsText(x+w-8*strlen(buf)-8, y+2, 16, 1,1,1,1, buf);
 	
 	str_format(buf, sizeof(buf), "%.2f", g->min);
-	gfx_quads_text(x+w-8*strlen(buf)-8, y+h-16, 16, 1,1,1,1, buf);
+	Graphics()->QuadsText(x+w-8*strlen(buf)-8, y+h-16, 16, 1,1,1,1, buf);
 	
 }
 
@@ -336,40 +358,40 @@ enum
 };
 
 /* the game snapshots are modifiable by the game */
-SNAPSTORAGE snapshot_storage;
-static SNAPSTORAGE_HOLDER *snapshots[NUM_SNAPSHOT_TYPES] = {0, 0};
+CSnapshotStorage snapshot_storage;
+static CSnapshotStorage::CHolder *snapshots[NUM_SNAPSHOT_TYPES] = {0, 0};
 
 static int recived_snapshots = 0;
-static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE];
+static char snapshot_incomming_data[CSnapshot::MAX_SIZE];
 
-static SNAPSTORAGE_HOLDER demorec_snapshotholders[NUM_SNAPSHOT_TYPES];
-static char *demorec_snapshotdata[NUM_SNAPSHOT_TYPES][2][MAX_SNAPSHOT_SIZE];
+static CSnapshotStorage::CHolder demorec_snapshotholders[NUM_SNAPSHOT_TYPES];
+static char *demorec_snapshotdata[NUM_SNAPSHOT_TYPES][2][CSnapshot::MAX_SIZE];
 
 /* --- */
 
 void *snap_get_item(int snapid, int index, SNAP_ITEM *item)
 {
-	SNAPSHOT_ITEM *i;
+	CSnapshotItem *i;
 	dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid");
-	i = snapshot_get_item(snapshots[snapid]->alt_snap, index);
-	item->datasize = snapshot_get_item_datasize(snapshots[snapid]->alt_snap, index);
-	item->type = snapitem_type(i);
-	item->id = snapitem_id(i);
-	return (void *)snapitem_data(i);
+	i = snapshots[snapid]->m_pAltSnap->GetItem(index);
+	item->datasize = snapshots[snapid]->m_pAltSnap->GetItemSize(index);
+	item->type = i->Type();
+	item->id = i->ID();
+	return (void *)i->Data();
 }
 
 void snap_invalidate_item(int snapid, int index)
 {
-	SNAPSHOT_ITEM *i;
+	CSnapshotItem *i;
 	dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid");
-	i = snapshot_get_item(snapshots[snapid]->alt_snap, index);
+	i = snapshots[snapid]->m_pAltSnap->GetItem(index);
 	if(i)
 	{
-		if((char *)i < (char *)snapshots[snapid]->alt_snap || (char *)i > (char *)snapshots[snapid]->alt_snap + snapshots[snapid]->snap_size)
+		if((char *)i < (char *)snapshots[snapid]->m_pAltSnap || (char *)i > (char *)snapshots[snapid]->m_pAltSnap + snapshots[snapid]->m_SnapSize)
 			dbg_msg("ASDFASDFASdf", "ASDFASDFASDF");
-		if((char *)i >= (char *)snapshots[snapid]->snap && (char *)i < (char *)snapshots[snapid]->snap + snapshots[snapid]->snap_size)
+		if((char *)i >= (char *)snapshots[snapid]->m_pSnap && (char *)i < (char *)snapshots[snapid]->m_pSnap + snapshots[snapid]->m_SnapSize)
 			dbg_msg("ASDFASDFASdf", "ASDFASDFASDF");
-		i->type_and_id = -1;
+		i->m_TypeAndID = -1;
 	}
 }
 
@@ -381,11 +403,11 @@ void *snap_find_item(int snapid, int type, int id)
 	if(!snapshots[snapid])
 		return 0x0;
 	
-	for(i = 0; i < snapshots[snapid]->snap->num_items; i++)
+	for(i = 0; i < snapshots[snapid]->m_pSnap->m_NumItems; i++)
 	{
-		SNAPSHOT_ITEM *itm = snapshot_get_item(snapshots[snapid]->alt_snap, i);
-		if(snapitem_type(itm) == type && snapitem_id(itm) == id)
-			return (void *)snapitem_data(itm);
+		CSnapshotItem *itm = snapshots[snapid]->m_pAltSnap->GetItem(i);
+		if(itm->Type() == type && itm->ID() == id)
+			return (void *)itm->Data();
 	}
 	return 0x0;
 }
@@ -395,7 +417,7 @@ int snap_num_items(int snapid)
 	dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid");
 	if(!snapshots[snapid])
 		return 0;
-	return snapshots[snapid]->snap->num_items;
+	return snapshots[snapid]->m_pSnap->m_NumItems;
 }
 
 /* ------ time functions ------ */
@@ -412,35 +434,34 @@ float client_localtime() { return (time_get()-local_start_time)/(float)(time_fre
 /* ----- send functions ----- */
 int client_send_msg()
 {
-	const MSG_INFO *info = msg_get_info();
-	NETCHUNK packet;
+	const MSG_INFO *pInfo = msg_get_info();
+	CNetChunk Packet;
 	
-	if(!info)
+	if(!pInfo)
 		return -1;
 
 	if(client_state() == CLIENTSTATE_OFFLINE)
 		return 0;
 	
-		
-	mem_zero(&packet, sizeof(NETCHUNK));
+	mem_zero(&Packet, sizeof(CNetChunk));
 	
-	packet.client_id = 0;
-	packet.data = info->data;
-	packet.data_size = info->size;
+	Packet.m_ClientID = 0;
+	Packet.m_pData = pInfo->data;
+	Packet.m_DataSize = pInfo->size;
 
-	if(info->flags&MSGFLAG_VITAL)
-		packet.flags |= NETSENDFLAG_VITAL;
-	if(info->flags&MSGFLAG_FLUSH)
-		packet.flags |= NETSENDFLAG_FLUSH;
+	if(pInfo->flags&MSGFLAG_VITAL)
+		Packet.m_Flags |= NETSENDFLAG_VITAL;
+	if(pInfo->flags&MSGFLAG_FLUSH)
+		Packet.m_Flags |= NETSENDFLAG_FLUSH;
 		
-	if(info->flags&MSGFLAG_RECORD)
+	if(pInfo->flags&MSGFLAG_RECORD)
 	{
 		if(demorec_isrecording())
-			demorec_record_message(packet.data, packet.data_size);
+			demorec_record_message(Packet.m_pData, Packet.m_DataSize);
 	}
 
-	if(!(info->flags&MSGFLAG_NOSEND))
-		netclient_send(net, &packet);
+	if(!(pInfo->flags&MSGFLAG_NOSEND))
+		m_NetClient.Send(&Packet);
 	return 0;
 }
 
@@ -494,7 +515,7 @@ void client_rcon(const char *cmd)
 
 int client_connection_problems()
 {
-	return netclient_gotproblems(net);
+	return m_NetClient.GotProblems();
 }
 
 void client_direct_input(int *input, int size)
@@ -594,7 +615,7 @@ static void client_on_enter_game()
 	/* reset snapshots */
 	snapshots[SNAP_CURRENT] = 0;
 	snapshots[SNAP_PREV] = 0;
-	snapstorage_purge_all(&snapshot_storage);
+	snapshot_storage.PurgeAll();
 	recived_snapshots = 0;
 	snapshot_parts = 0;
 	current_predtick = 0;
@@ -645,7 +666,7 @@ void client_connect(const char *server_address_str)
 	
 	rcon_authed = 0;
 	server_address.port = port;
-	netclient_connect(net, &server_address);
+	m_NetClient.Connect(&server_address);
 	client_set_state(CLIENTSTATE_CONNECTING);
 	
 	graph_init(&inputtime_margin_graph, -150.0f, 150.0f);
@@ -659,7 +680,7 @@ void client_disconnect_with_reason(const char *reason)
 	
 	/* */
 	rcon_authed = 0;
-	netclient_disconnect(net, reason);
+	m_NetClient.Disconnect(reason);
 	client_set_state(CLIENTSTATE_OFFLINE);
 	map_unload();
 	
@@ -701,7 +722,7 @@ void client_serverinfo_request()
 
 static int client_load_data()
 {
-	debug_font = gfx_load_texture("debug_font.png", IMG_AUTO, TEXLOAD_NORESAMPLE);
+	debug_font = Graphics()->LoadTexture("debug_font.png", IMG_AUTO, TEXLOAD_NORESAMPLE);
 	return 1;
 }
 
@@ -721,9 +742,9 @@ static void client_debug_render()
 	if(!config.debug)
 		return;
 	
-	gfx_blend_normal();
-	gfx_texture_set(debug_font);
-	gfx_mapscreen(0,0,gfx_screenwidth(),gfx_screenheight());
+	//m_pGraphics->BlendNormal();
+	Graphics()->TextureSet(debug_font);
+	Graphics()->MapScreen(0,0,Graphics()->ScreenWidth(),Graphics()->ScreenHeight());
 	
 	if(time_get()-last_snap > time_freq())
 	{
@@ -739,13 +760,13 @@ static void client_debug_render()
 		total = 42
 	*/
 	frametime_avg = frametime_avg*0.9f + frametime*0.1f;
-	str_format(buffer, sizeof(buffer), "ticks: %8d %8d mem %dk %d  gfxmem: %dk  fps: %3d",
+	str_format(buffer, sizeof(buffer), "ticks: %8d %8d mem %dk %d  gfxmem: N/A  fps: %3d",
 		current_tick, current_predtick,
 		mem_stats()->allocated/1024,
 		mem_stats()->total_allocations,
-		gfx_memory_usage()/1024,
+		/*gfx_memory_usage()/1024, */ // TODO: Refactor: Reenable this
 		(int)(1.0f/frametime_avg));
-	gfx_quads_text(2, 2, 16, 1,1,1,1, buffer);
+	Graphics()->QuadsText(2, 2, 16, 1,1,1,1, buffer);
 
 	
 	{
@@ -761,7 +782,7 @@ static void client_debug_render()
 		str_format(buffer, sizeof(buffer), "send: %3d %5d+%4d=%5d (%3d kbps) avg: %5d\nrecv: %3d %5d+%4d=%5d (%3d kbps) avg: %5d",
 			send_packets, send_bytes, send_packets*42, send_total, (send_total*8)/1024, send_bytes/send_packets,
 			recv_packets, recv_bytes, recv_packets*42, recv_total, (recv_total*8)/1024, recv_bytes/recv_packets);
-		gfx_quads_text(2, 14, 16, 1,1,1,1, buffer);
+		Graphics()->QuadsText(2, 14, 16, 1,1,1,1, buffer);
 	}
 	
 	/* render rates */
@@ -774,7 +795,7 @@ static void client_debug_render()
 			{
 				str_format(buffer, sizeof(buffer), "%4d %20s: %8d %8d %8d", i, modc_getitemname(i), snapshot_data_rate[i]/8, snapshot_data_updates[i],
 					(snapshot_data_rate[i]/snapshot_data_updates[i])/8);
-				gfx_quads_text(2, 100+y*12, 16, 1,1,1,1, buffer);
+				Graphics()->QuadsText(2, 100+y*12, 16, 1,1,1,1, buffer);
 				y++;
 			}
 		}
@@ -783,16 +804,16 @@ static void client_debug_render()
 	str_format(buffer, sizeof(buffer), "pred: %d ms  %3.2f", 
 		(int)((st_get(&predicted_time, now)-st_get(&game_time, now))*1000/(float)time_freq()),
 		predicted_time.adjustspeed[1]);
-	gfx_quads_text(2, 70, 16, 1,1,1,1, buffer);
+	Graphics()->QuadsText(2, 70, 16, 1,1,1,1, buffer);
 	
 	/* render graphs */
 	if(config.dbg_graphs)
 	{
-		//gfx_mapscreen(0,0,400.0f,300.0f);
-		float w = gfx_screenwidth()/4.0f;
-		float h = gfx_screenheight()/6.0f;
-		float sp = gfx_screenwidth()/100.0f;
-		float x = gfx_screenwidth()-w-sp;
+		//Graphics()->MapScreen(0,0,400.0f,300.0f);
+		float w = Graphics()->ScreenWidth()/4.0f;
+		float h = Graphics()->ScreenHeight()/6.0f;
+		float sp = Graphics()->ScreenWidth()/100.0f;
+		float x = Graphics()->ScreenWidth()-w-sp;
 
 		graph_scale_max(&fps_graph);
 		graph_scale_min(&fps_graph);
@@ -809,13 +830,13 @@ void client_quit()
 
 const char *client_error_string()
 {
-	return netclient_error_string(net);
+	return m_NetClient.ErrorString();
 }
 
 static void client_render()
 {
 	if(config.gfx_clear)	
-		gfx_clear(1,1,0);
+		Graphics()->Clear(1,1,0);
 
 	modc_render();
 	client_debug_render();
@@ -888,15 +909,15 @@ static int player_score_comp(const void *a, const void *b)
 	return -1;
 }
 
-static void client_process_packet(NETCHUNK *packet)
+static void client_process_packet(CNetChunk *pPacket)
 {
-	if(packet->client_id == -1)
+	if(pPacket->m_ClientID == -1)
 	{
 		/* connectionlesss */
-		if(packet->data_size == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) &&
-			memcmp(packet->data, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0)
+		if(pPacket->m_DataSize == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) &&
+			memcmp(pPacket->m_pData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0)
 		{
-			unsigned char *versiondata = (unsigned char*) packet->data + sizeof(VERSIONSRV_VERSION);
+			unsigned char *versiondata = (unsigned char*)pPacket->m_pData + sizeof(VERSIONSRV_VERSION);
 			int version_match = !memcmp(versiondata, VERSION_DATA, sizeof(VERSION_DATA));
 			
 			dbg_msg("client/version", "version does %s (%d.%d.%d)",
@@ -910,12 +931,12 @@ static void client_process_packet(NETCHUNK *packet)
 			}
 		}
 		
-		if(packet->data_size >= (int)sizeof(SERVERBROWSE_LIST) &&
-			memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0)
+		if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_LIST) &&
+			memcmp(pPacket->m_pData, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0)
 		{
-			int size = packet->data_size-sizeof(SERVERBROWSE_LIST);
+			int size = pPacket->m_DataSize-sizeof(SERVERBROWSE_LIST);
 			int num = size/sizeof(MASTERSRV_ADDR);
-			MASTERSRV_ADDR *addrs = (MASTERSRV_ADDR *)((char*)packet->data+sizeof(SERVERBROWSE_LIST));
+			MASTERSRV_ADDR *addrs = (MASTERSRV_ADDR *)((char*)pPacket->m_pData+sizeof(SERVERBROWSE_LIST));
 			int i;
 
 			for(i = 0; i < num; i++)
@@ -937,47 +958,47 @@ static void client_process_packet(NETCHUNK *packet)
 
 		{
 			int packet_type = 0;
-			if(packet->data_size >= (int)sizeof(SERVERBROWSE_INFO) && memcmp(packet->data, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0)
+			if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_INFO) && memcmp(pPacket->m_pData, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0)
 				packet_type = 2;
 
-			if(packet->data_size >= (int)sizeof(SERVERBROWSE_OLD_INFO) && memcmp(packet->data, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)) == 0)
+			if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_OLD_INFO) && memcmp(pPacket->m_pData, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)) == 0)
 				packet_type = 1;
 			
 			if(packet_type)
 			{
 				/* we got ze info */
-				UNPACKER up;
+				CUnpacker up;
 				SERVER_INFO info = {0};
 				int i;
 				int token = -1;
 				
-				unpacker_reset(&up, (unsigned char*)packet->data+sizeof(SERVERBROWSE_INFO), packet->data_size-sizeof(SERVERBROWSE_INFO));
+				up.Reset((unsigned char*)pPacket->m_pData+sizeof(SERVERBROWSE_INFO), pPacket->m_DataSize-sizeof(SERVERBROWSE_INFO));
 				if(packet_type >= 2)
-					token = atol(unpacker_get_string(&up));
-				str_copy(info.version, unpacker_get_string(&up), sizeof(info.version));
-				str_copy(info.name, unpacker_get_string(&up), sizeof(info.name));
-				str_copy(info.map, unpacker_get_string(&up), sizeof(info.map));
-				str_copy(info.gametype, unpacker_get_string(&up), sizeof(info.gametype));
-				info.flags = atol(unpacker_get_string(&up));
-				info.progression = atol(unpacker_get_string(&up));
-				info.num_players = atol(unpacker_get_string(&up));
-				info.max_players = atol(unpacker_get_string(&up));
+					token = atol(up.GetString());
+				str_copy(info.version, up.GetString(), sizeof(info.version));
+				str_copy(info.name, up.GetString(), sizeof(info.name));
+				str_copy(info.map, up.GetString(), sizeof(info.map));
+				str_copy(info.gametype, up.GetString(), sizeof(info.gametype));
+				info.flags = atol(up.GetString());
+				info.progression = atol(up.GetString());
+				info.num_players = atol(up.GetString());
+				info.max_players = atol(up.GetString());
 				str_format(info.address, sizeof(info.address), "%d.%d.%d.%d:%d",
-					packet->address.ip[0], packet->address.ip[1], packet->address.ip[2],
-					packet->address.ip[3], packet->address.port);
+					pPacket->m_Address.ip[0], pPacket->m_Address.ip[1], pPacket->m_Address.ip[2],
+					pPacket->m_Address.ip[3], pPacket->m_Address.port);
 				
 				for(i = 0; i < info.num_players; i++)
 				{
-					str_copy(info.players[i].name, unpacker_get_string(&up), sizeof(info.players[i].name));
-					info.players[i].score = atol(unpacker_get_string(&up));
+					str_copy(info.players[i].name, up.GetString(), sizeof(info.players[i].name));
+					info.players[i].score = atol(up.GetString());
 				}
 				
-				if(!up.error)
+				if(!up.Error())
 				{
 					/* sort players */
 					qsort(info.players, info.num_players, sizeof(*info.players), player_score_comp);
 					
-					if(net_addr_comp(&server_address, &packet->address) == 0)
+					if(net_addr_comp(&server_address, &pPacket->m_Address) == 0)
 					{
 						mem_copy(&current_server_info, &info, sizeof(current_server_info));
 						current_server_info.netaddr = server_address;
@@ -986,9 +1007,9 @@ static void client_process_packet(NETCHUNK *packet)
 					else
 					{
 						if(packet_type == 2)
-							client_serverbrowse_set(&packet->address, BROWSESET_TOKEN, token, &info);
+							client_serverbrowse_set(&pPacket->m_Address, BROWSESET_TOKEN, token, &info);
 						else
-							client_serverbrowse_set(&packet->address, BROWSESET_OLD_INTERNET, -1, &info);
+							client_serverbrowse_set(&pPacket->m_Address, BROWSESET_OLD_INTERNET, -1, &info);
 					}
 				}
 			}
@@ -997,7 +1018,7 @@ static void client_process_packet(NETCHUNK *packet)
 	else
 	{
 		int sys;
-		int msg = msg_unpack_start(packet->data, packet->data_size, &sys);
+		int msg = msg_unpack_start(pPacket->m_pData, pPacket->m_DataSize, &sys);
 		
 		if(sys)
 		{
@@ -1193,15 +1214,15 @@ static void client_process_packet(NETCHUNK *packet)
 					mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, data, part_size);
 					snapshot_parts |= 1<<part;
 				
-					if(snapshot_parts == (1<<num_parts)-1)
+					if(snapshot_parts == (unsigned)((1<<num_parts)-1))
 					{
-						static SNAPSHOT emptysnap;
-						SNAPSHOT *deltashot = &emptysnap;
+						static CSnapshot emptysnap;
+						CSnapshot *deltashot = &emptysnap;
 						int purgetick;
 						void *deltadata;
 						int deltasize;
-						unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE];
-						unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE];
+						unsigned char tmpbuffer2[CSnapshot::MAX_SIZE];
+						unsigned char tmpbuffer3[CSnapshot::MAX_SIZE];
 						int snapsize;
 						
 						complete_size = (num_parts-1) * MAX_SNAPSHOT_PACKSIZE + part_size;
@@ -1210,13 +1231,13 @@ static void client_process_packet(NETCHUNK *packet)
 						snapshot_parts = 0;
 						
 						/* find snapshot that we should use as delta */
-						emptysnap.data_size = 0;
-						emptysnap.num_items = 0;
+						emptysnap.m_DataSize = 0;
+						emptysnap.m_NumItems = 0;
 						
 						/* find delta */
 						if(delta_tick >= 0)
 						{
-							int deltashot_size = snapstorage_get(&snapshot_storage, delta_tick, 0, &deltashot, 0);
+							int deltashot_size = snapshot_storage.Get(delta_tick, 0, &deltashot, 0);
 							
 							if(deltashot_size < 0)
 							{
@@ -1233,7 +1254,7 @@ static void client_process_packet(NETCHUNK *packet)
 						}
 
 						/* decompress snapshot */
-						deltadata = snapshot_empty_delta();
+						deltadata = CSnapshot::EmptyDelta();
 						deltasize = sizeof(int)*3;
 
 						if(complete_size)
@@ -1249,19 +1270,19 @@ static void client_process_packet(NETCHUNK *packet)
 						
 						/* unpack delta */
 						purgetick = delta_tick;
-						snapsize = snapshot_unpack_delta(deltashot, (SNAPSHOT*)tmpbuffer3, deltadata, deltasize);
+						snapsize = CSnapshot::UnpackDelta(deltashot, (CSnapshot*)tmpbuffer3, deltadata, deltasize);
 						if(snapsize < 0)
 						{
 							dbg_msg("client", "delta unpack failed!");
 							return;
 						}
 						
-						if(msg != NETMSG_SNAPEMPTY && snapshot_crc((SNAPSHOT*)tmpbuffer3) != crc)
+						if(msg != NETMSG_SNAPEMPTY && ((CSnapshot*)tmpbuffer3)->Crc() != crc)
 						{
 							if(config.debug)
 							{
 								dbg_msg("client", "snapshot crc error #%d - tick=%d wantedcrc=%d gotcrc=%d compressed_size=%d delta_tick=%d",
-									snapcrcerrors, game_tick, crc, snapshot_crc((SNAPSHOT*)tmpbuffer3), complete_size, delta_tick);
+									snapcrcerrors, game_tick, crc, ((CSnapshot*)tmpbuffer3)->Crc(), complete_size, delta_tick);
 							}
 								
 							snapcrcerrors++;
@@ -1282,14 +1303,14 @@ static void client_process_packet(NETCHUNK *packet)
 
 						/* purge old snapshots */
 						purgetick = delta_tick;
-						if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->tick < purgetick)
-							purgetick = snapshots[SNAP_PREV]->tick;
-						if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->tick < purgetick)
-							purgetick = snapshots[SNAP_PREV]->tick;
-						snapstorage_purge_until(&snapshot_storage, purgetick);
+						if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->m_Tick < purgetick)
+							purgetick = snapshots[SNAP_PREV]->m_Tick;
+						if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->m_Tick < purgetick)
+							purgetick = snapshots[SNAP_PREV]->m_Tick;
+						snapshot_storage.PurgeUntil(purgetick);
 						
 						/* add new */
-						snapstorage_add(&snapshot_storage, game_tick, time_get(), snapsize, (SNAPSHOT*)tmpbuffer3, 1);
+						snapshot_storage.Add(game_tick, time_get(), snapsize, (CSnapshot*)tmpbuffer3, 1);
 
 						/* add snapshot to demo */
 						if(demorec_isrecording())
@@ -1320,8 +1341,8 @@ static void client_process_packet(NETCHUNK *packet)
 							st_init(&predicted_time, game_tick*time_freq()/50);
 							predicted_time.adjustspeed[1] = 1000.0f;
 							st_init(&game_time, (game_tick-1)*time_freq()/50);
-							snapshots[SNAP_PREV] = snapshot_storage.first;
-							snapshots[SNAP_CURRENT] = snapshot_storage.last;
+							snapshots[SNAP_PREV] = snapshot_storage.m_pFirst;
+							snapshots[SNAP_CURRENT] = snapshot_storage.m_pLast;
 							local_start_time = time_get();
 							client_set_state(CLIENTSTATE_ONLINE);
 						}
@@ -1345,8 +1366,8 @@ static void client_process_packet(NETCHUNK *packet)
 		{
 			/* game message */
 			if(demorec_isrecording())
-				demorec_record_message(packet->data, packet->data_size);
-				/* demorec_record_write("MESG", packet->data_size, ); */
+				demorec_record_message(pPacket->m_pData, pPacket->m_DataSize);
+				/* demorec_record_write("MESG", pPacket->data_size, ); */
 
 			modc_message(msg);
 		}
@@ -1358,22 +1379,21 @@ int client_mapdownload_totalsize() { return mapdownload_totalsize; }
 
 static void client_pump_network()
 {
-	NETCHUNK packet;
 
-	netclient_update(net);
+	m_NetClient.Update();
 
 	if(client_state() != CLIENTSTATE_DEMOPLAYBACK)
 	{
 		/* check for errors */
-		if(client_state() != CLIENTSTATE_OFFLINE && netclient_state(net) == NETSTATE_OFFLINE)
+		if(client_state() != CLIENTSTATE_OFFLINE && m_NetClient.State() == NETSTATE_OFFLINE)
 		{
 			client_set_state(CLIENTSTATE_OFFLINE);
 			client_disconnect();
-			dbg_msg("client", "offline error='%s'", netclient_error_string(net));
+			dbg_msg("client", "offline error='%s'", m_NetClient.ErrorString());
 		}
 
 		/* */
-		if(client_state() == CLIENTSTATE_CONNECTING && netclient_state(net) == NETSTATE_ONLINE)
+		if(client_state() == CLIENTSTATE_CONNECTING && m_NetClient.State() == NETSTATE_ONLINE)
 		{
 			/* we switched to online */
 			dbg_msg("client", "connected, sending info");
@@ -1383,15 +1403,16 @@ static void client_pump_network()
 	}
 	
 	/* process packets */
-	while(netclient_recv(net, &packet))
-		client_process_packet(&packet);
+	CNetChunk Packet;
+	while(m_NetClient.Recv(&Packet))
+		client_process_packet(&Packet);
 }
 
-static void client_democallback_snapshot(void *data, int size)
+static void client_democallback_snapshot(void *pData, int Size)
 {
 	/* update ticks, they could have changed */
 	const DEMOREC_PLAYBACKINFO *info = demorec_playback_info();			
-	SNAPSTORAGE_HOLDER *temp;
+	CSnapshotStorage::CHolder *temp;
 	current_tick = info->current_tick;
 	prev_tick = info->previous_tick;
 	
@@ -1400,8 +1421,8 @@ static void client_democallback_snapshot(void *data, int size)
 	snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
 	snapshots[SNAP_CURRENT] = temp;
 	
-	mem_copy(snapshots[SNAP_CURRENT]->snap, data, size);
-	mem_copy(snapshots[SNAP_CURRENT]->alt_snap, data, size);
+	mem_copy(snapshots[SNAP_CURRENT]->m_pSnap, pData, Size);
+	mem_copy(snapshots[SNAP_CURRENT]->m_pAltSnap, pData, Size);
 	
 	modc_newsnapshot();
 	/*modc_predict();*/
@@ -1476,20 +1497,20 @@ static void client_update()
 
 		while(1)
 		{
-			SNAPSTORAGE_HOLDER *cur = snapshots[SNAP_CURRENT];
-			int64 tickstart = (cur->tick)*time_freq()/50;
+			CSnapshotStorage::CHolder *cur = snapshots[SNAP_CURRENT];
+			int64 tickstart = (cur->m_Tick)*time_freq()/50;
 
 			if(tickstart < now)
 			{
-				SNAPSTORAGE_HOLDER *next = snapshots[SNAP_CURRENT]->next;
+				CSnapshotStorage::CHolder *next = snapshots[SNAP_CURRENT]->m_pNext;
 				if(next)
 				{
 					snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
 					snapshots[SNAP_CURRENT] = next;
 					
 					/* set ticks */
-					current_tick = snapshots[SNAP_CURRENT]->tick;
-					prev_tick = snapshots[SNAP_PREV]->tick;
+					current_tick = snapshots[SNAP_CURRENT]->m_Tick;
+					prev_tick = snapshots[SNAP_PREV]->m_Tick;
 					
 					if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
 					{
@@ -1506,8 +1527,8 @@ static void client_update()
 
 		if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
 		{
-			int64 curtick_start = (snapshots[SNAP_CURRENT]->tick)*time_freq()/50;
-			int64 prevtick_start = (snapshots[SNAP_PREV]->tick)*time_freq()/50;
+			int64 curtick_start = (snapshots[SNAP_CURRENT]->m_Tick)*time_freq()/50;
+			int64 prevtick_start = (snapshots[SNAP_PREV]->m_Tick)*time_freq()/50;
 			/*tg_add(&predicted_time_graph, pred_now, 0); */
 			int prev_pred_tick = (int)(pred_now*50/time_freq());
 			int new_pred_tick = prev_pred_tick+1;
@@ -1520,10 +1541,10 @@ static void client_update()
 			prevtick_start = prev_pred_tick*time_freq()/50;
 			predintratick = (pred_now - prevtick_start) / (float)(curtick_start-prevtick_start);
 			
-			if(new_pred_tick < snapshots[SNAP_PREV]->tick-SERVER_TICK_SPEED || new_pred_tick > snapshots[SNAP_PREV]->tick+SERVER_TICK_SPEED)
+			if(new_pred_tick < snapshots[SNAP_PREV]->m_Tick-SERVER_TICK_SPEED || new_pred_tick > snapshots[SNAP_PREV]->m_Tick+SERVER_TICK_SPEED)
 			{
 				dbg_msg("client", "prediction time reset!");
-				st_init(&predicted_time, snapshots[SNAP_CURRENT]->tick*time_freq()/50);
+				st_init(&predicted_time, snapshots[SNAP_CURRENT]->m_Tick*time_freq()/50);
 			}
 			
 			if(new_pred_tick > current_predtick)
@@ -1606,27 +1627,24 @@ static void client_versionupdate()
 	{
 		if(jobs_status(&version_serveraddr.job) == JOBSTATUS_DONE)
 		{
-			NETCHUNK packet;
+			CNetChunk Packet;
 			
-			mem_zero(&packet, sizeof(NETCHUNK));
+			mem_zero(&Packet, sizeof(Packet));
 			
 			version_serveraddr.addr.port = VERSIONSRV_PORT;
 			
-			packet.client_id = -1;
-			packet.address = version_serveraddr.addr;
-			packet.data = VERSIONSRV_GETVERSION;
-			packet.data_size = sizeof(VERSIONSRV_GETVERSION);
-			packet.flags = NETSENDFLAG_CONNLESS;
+			Packet.m_ClientID = -1;
+			Packet.m_Address = version_serveraddr.addr;
+			Packet.m_pData = VERSIONSRV_GETVERSION;
+			Packet.m_DataSize = sizeof(VERSIONSRV_GETVERSION);
+			Packet.m_Flags = NETSENDFLAG_CONNLESS;
 			
-			netclient_send(net, &packet);
+			m_NetClient.Send(&Packet);
 			state++;
 		}
 	}
 }
 
-extern int editor_update_and_render();
-extern void editor_init();
-
 static void client_run()
 {
 	NETADDR bindaddr;
@@ -1640,14 +1658,16 @@ static void client_run()
 	snapshot_parts = 0;
 	
 	/* init graphics and sound */
-	if(gfx_init() != 0)
+	m_pGraphics = CreateEngineGraphics();
+	if(m_pGraphics->Init() != 0)
 		return;
 
 	/* start refreshing addresses while we load */
 	mastersrv_refresh_addresses();
 	
 	/* init the editor */
-	editor_init();
+	m_pEditor = CreateEditor();
+	m_pEditor->Init(m_pGraphics);
 
 	/* sound is allowed to fail */
 	snd_init();
@@ -1657,12 +1677,13 @@ static void client_run()
 		return;
 
 	/* init the mod */
+	m_pGameClient = CreateGameClient(&m_Client);
 	modc_init();
 	dbg_msg("client", "version %s", modc_net_version());
 	
 	/* open socket */
 	mem_zero(&bindaddr, sizeof(bindaddr));
-	net = netclient_open(bindaddr, 0);
+	m_NetClient.Open(bindaddr, 0);
 	
 	/* connect to the server if wanted */
 	/*
@@ -1714,7 +1735,7 @@ static void client_run()
 		}
 		
 		/* release focus */
-		if(!gfx_window_active())
+		if(!Graphics()->WindowActive())
 		{
 			if(window_must_refocus == 0)
 				inp_mouse_mode_absolute();
@@ -1727,7 +1748,7 @@ static void client_run()
 		}
 
 		/* refocus */
-		if(window_must_refocus && gfx_window_active())
+		if(window_must_refocus && Graphics()->WindowActive())
 		{
 			if(window_must_refocus < 3)
 			{
@@ -1767,8 +1788,8 @@ static void client_run()
 		if(config.cl_editor)
 		{
 			client_update();
-			editor_update_and_render();
-			gfx_swap();
+			m_pEditor->UpdateAndRender();
+			m_pGraphics->Swap();
 		}
 		else
 		{
@@ -1784,7 +1805,7 @@ static void client_run()
 				if((frames%10) == 0)
 				{
 					client_render();
-					gfx_swap();
+					m_pGraphics->Swap();
 				}
 			}
 			else
@@ -1799,7 +1820,7 @@ static void client_run()
 				{
 					static PERFORMACE_INFO scope = {"gfx_swap", 0};
 					perf_start(&scope);
-					gfx_swap();
+					m_pGraphics->Swap();
 					perf_end();
 				}
 			}
@@ -1815,7 +1836,7 @@ static void client_run()
 		/* be nice */
 		if(config.dbg_stress)
 			thread_sleep(5);
-		else if(config.cl_cpu_throttle || !gfx_window_active())
+		else if(config.cl_cpu_throttle || !Graphics()->WindowActive())
 			thread_sleep(1);
 			
 		if(config.dbg_hitch)
@@ -1832,7 +1853,7 @@ static void client_run()
 					frames/(float)(reportinterval/time_freq()),
 					1.0f/frametime_high,
 					1.0f/frametime_low,
-					netclient_state(net));
+					m_NetClient.State());
 			}
 			frametime_low = 1;
 			frametime_high = 0;
@@ -1857,10 +1878,15 @@ static void client_run()
 	modc_shutdown();
 	client_disconnect();
 
-	gfx_shutdown();
+	m_pGraphics->Shutdown();
 	snd_shutdown();
 }
 
+void gfx_swap()
+{
+	m_pGraphics->Swap();
+}
+
 static void con_connect(void *result, void *user_data)
 {
 	str_copy(cmd_connect, console_arg_string(result, 0), sizeof(cmd_connect));
@@ -1886,7 +1912,7 @@ static void con_ping(void *result, void *user_data)
 
 static void con_screenshot(void *result, void *user_data)
 {
-	gfx_screenshot();
+	Graphics()->TakeScreenshot();
 }
 
 static void con_rcon(void *result, void *user_data)
@@ -1911,7 +1937,7 @@ const char *client_demoplayer_play(const char *filename)
 	int crc;
 	const char *error;
 	client_disconnect();
-	netclient_error_string_reset(net);
+	m_NetClient.ResetErrorString();
 	
 	/* try to start playback */
 	demorec_playback_registercallbacks(client_democallback_snapshot, client_democallback_message);
@@ -1939,15 +1965,15 @@ const char *client_demoplayer_play(const char *filename)
 	snapshots[SNAP_CURRENT] = &demorec_snapshotholders[SNAP_CURRENT];
 	snapshots[SNAP_PREV] = &demorec_snapshotholders[SNAP_PREV];
 	
-	snapshots[SNAP_CURRENT]->snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_CURRENT][0];
-	snapshots[SNAP_CURRENT]->alt_snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_CURRENT][1];
-	snapshots[SNAP_CURRENT]->snap_size = 0;
-	snapshots[SNAP_CURRENT]->tick = -1;
+	snapshots[SNAP_CURRENT]->m_pSnap = (CSnapshot *)demorec_snapshotdata[SNAP_CURRENT][0];
+	snapshots[SNAP_CURRENT]->m_pAltSnap = (CSnapshot *)demorec_snapshotdata[SNAP_CURRENT][1];
+	snapshots[SNAP_CURRENT]->m_SnapSize = 0;
+	snapshots[SNAP_CURRENT]->m_Tick = -1;
 	
-	snapshots[SNAP_PREV]->snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_PREV][0];
-	snapshots[SNAP_PREV]->alt_snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_PREV][1];
-	snapshots[SNAP_PREV]->snap_size = 0;
-	snapshots[SNAP_PREV]->tick = -1;
+	snapshots[SNAP_PREV]->m_pSnap = (CSnapshot *)demorec_snapshotdata[SNAP_PREV][0];
+	snapshots[SNAP_PREV]->m_pAltSnap = (CSnapshot *)demorec_snapshotdata[SNAP_PREV][1];
+	snapshots[SNAP_PREV]->m_SnapSize = 0;
+	snapshots[SNAP_PREV]->m_Tick = -1;
 
 	/* enter demo playback state */
 	client_set_state(CLIENTSTATE_DEMOPLAYBACK);
diff --git a/src/engine/client/ec_gfx.c b/src/engine/client/ec_gfx.c
deleted file mode 100644
index 1ea9f407..00000000
--- a/src/engine/client/ec_gfx.c
+++ /dev/null
@@ -1,1020 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-
-#include <base/detect.h>
-
-#include "SDL.h"
-
-#ifdef CONF_FAMILY_WINDOWS
-	#define WIN32_LEAN_AND_MEAN
-	#include <windows.h>
-#endif
-
-#ifdef CONF_PLATFORM_MACOSX
-	#include <OpenGL/gl.h>
-	#include <OpenGL/glu.h>
-#else
-	#include <GL/gl.h>
-	#include <GL/glu.h>
-#endif
-
-#include <base/system.h>
-#include <engine/external/pnglite/pnglite.h>
-
-#include <engine/e_client_interface.h>
-#include <engine/e_engine.h>
-#include <engine/e_config.h>
-#include <engine/e_keys.h>
-
-#include <string.h>
-#include <stdio.h>
-#include <math.h>
-
-/* compressed textures */
-#define GL_COMPRESSED_RGB_ARB 0x84ED
-#define GL_COMPRESSED_RGBA_ARB 0x84EE
-#define GL_COMPRESSED_ALPHA_ARB 0x84E9
-
-#define TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
-
-enum
-{
-	DRAWING_QUADS=1,
-	DRAWING_LINES=2
-};
-
-/* */
-typedef struct { float x, y, z; } VEC3;
-typedef struct { float u, v; } TEXCOORD;
-typedef struct { float r, g, b, a; } COLOR;
-
-typedef struct
-{
-	VEC3 pos;
-	TEXCOORD tex;
-	COLOR color;
-} VERTEX;
-
-const int vertex_buffer_size = 32*1024;
-static VERTEX *vertices = 0;
-static int num_vertices = 0;
-
-static int no_gfx = 0;
-
-static COLOR color[4];
-static TEXCOORD texture[4];
-
-static int do_screenshot = 0;
-static int render_enable = 1;
-
-static int screen_width = -1;
-static int screen_height = -1;
-static float rotation = 0;
-static int drawing = 0;
-
-static float screen_x0 = 0;
-static float screen_y0 = 0;
-static float screen_x1 = 0;
-static float screen_y1 = 0;
-
-static int invalid_texture = 0;
-
-typedef struct
-{
-	GLuint tex;
-	int memsize;
-	int flags;
-	int next;
-} TEXTURE;
-
-enum
-{
-	MAX_TEXTURES = 1024*4
-};
-
-static TEXTURE textures[MAX_TEXTURES];
-static int first_free_texture;
-static int memory_usage = 0;
-
-static SDL_Surface *screen_surface;
-
-static const unsigned char null_texture_data[] = {
-	0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, 
-	0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, 
-	0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, 
-	0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, 
-};
-
-static void flush()
-{
-	if(num_vertices == 0)
-		return;
-		
-	if(no_gfx)
-	{
-		num_vertices = 0;
-		return;
-	}
-	
-		
-	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
-	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-	glVertexPointer(3, GL_FLOAT,
-			sizeof(VERTEX),
-			(char*)vertices);
-	glTexCoordPointer(2, GL_FLOAT,
-			sizeof(VERTEX),
-			(char*)vertices + sizeof(float)*3);
-	glColorPointer(4, GL_FLOAT,
-			sizeof(VERTEX),
-			(char*)vertices + sizeof(float)*5);
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	glEnableClientState(GL_COLOR_ARRAY);
-	
-	if(render_enable)
-	{
-		if(drawing == DRAWING_QUADS)
-			glDrawArrays(GL_QUADS, 0, num_vertices);
-		else if(drawing == DRAWING_LINES)
-			glDrawArrays(GL_LINES, 0, num_vertices);
-	}
-	
-	/* Reset pointer */
-	num_vertices = 0;
-}
-
-static void add_vertices(int count)
-{
-	num_vertices += count;
-	if((num_vertices + count) >= vertex_buffer_size)
-		flush();
-}
-
-static int try_init()
-{
-	const SDL_VideoInfo *info;
-	int flags = SDL_OPENGL;
-	
-	screen_width = config.gfx_screen_width;
-	screen_height = config.gfx_screen_height;
-
-	info = SDL_GetVideoInfo();
-
-	/* set flags */
-	flags  = SDL_OPENGL;
-	flags |= SDL_GL_DOUBLEBUFFER;
-	flags |= SDL_HWPALETTE;
-	if(config.dbg_resizable)
-		flags |= SDL_RESIZABLE;
-
-	if(info->hw_available)
-		flags |= SDL_HWSURFACE;
-	else
-		flags |= SDL_SWSURFACE;
-
-	if(info->blit_hw)
-		flags |= SDL_HWACCEL;
-
-	if(config.gfx_fullscreen)
-		flags |= SDL_FULLSCREEN;
-
-	/* set gl attributes */
-	if(config.gfx_fsaa_samples)
-	{
-		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
-		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config.gfx_fsaa_samples);
-	}
-	else
-	{
-		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
-		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
-	}
-
-	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
-	SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, config.gfx_vsync);
-
-	/* set caption */
-	SDL_WM_SetCaption("Teeworlds", "Teeworlds");
-	
-	/* create window */
-	screen_surface = SDL_SetVideoMode(screen_width, screen_height, 0, flags);
-	if(screen_surface == NULL)
-	{
-		dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError());
-		return -1;
-	}
-	
-	return 0;
-}
-
-void gfx_font_init();
-
-static int gfx_init_window()
-{
-	if(try_init() == 0)
-		return 0;
-	
-	/* try disabling fsaa */
-	while(config.gfx_fsaa_samples)
-	{
-		config.gfx_fsaa_samples--;
-		
-		if(config.gfx_fsaa_samples)
-			dbg_msg("gfx", "lowering FSAA to %d and trying again", config.gfx_fsaa_samples);
-		else
-			dbg_msg("gfx", "disabling FSAA and trying again");
-
-		if(try_init() == 0)
-			return 0;
-	}
-
-	/* try lowering the resolution */
-	if(config.gfx_screen_width != 640 || config.gfx_screen_height != 480)
-	{
-		dbg_msg("gfx", "setting resolution to 640x480 and trying again");
-		config.gfx_screen_width = 640;
-		config.gfx_screen_height = 480;
-
-		if(try_init() == 0)
-			return 0;
-	}
-
-	dbg_msg("gfx", "out of ideas. failed to init graphics");
-					
-	return -1;		
-}
-
-int gfx_init()
-{
-	int i;
-
-	if(config.dbg_stress)
-		no_gfx = 1;
-
-	{
-		int systems = 0;
-		
-		if(!no_gfx)
-			systems |= SDL_INIT_VIDEO;
-			
-		if(config.snd_enable)
-			systems |= SDL_INIT_AUDIO;
-
-		if(config.cl_eventthread)
-			systems |= SDL_INIT_EVENTTHREAD;
-		
-		if(SDL_Init(systems) < 0)
-		{
-			dbg_msg("gfx", "unable to init SDL: %s", SDL_GetError());
-			return -1;
-		}
-	}
-	
-    atexit(SDL_Quit);
-
-	if(!no_gfx)
-	{
-		#ifdef CONF_FAMILY_WINDOWS
-			if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED"))
-				putenv("SDL_VIDEO_WINDOW_POS=8,27");
-		#endif
-		
-		if(gfx_init_window() != 0)
-			return -1;
-	}
-	
-	/* Init vertices */
-	if (vertices)
-		mem_free(vertices);
-	vertices = (VERTEX*)mem_alloc(sizeof(VERTEX) * vertex_buffer_size, 1);
-	num_vertices = 0;
-
-
-	/*
-	dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(),
-											  context.version_minor(),
-											  context.version_rev());*/
-
-	
-	/* Set all z to -5.0f */
-	for (i = 0; i < vertex_buffer_size; i++)
-		vertices[i].pos.z = -5.0f;
-
-	/* init textures */
-	first_free_texture = 0;
-	for(i = 0; i < MAX_TEXTURES; i++)
-		textures[i].next = i+1;
-	textures[MAX_TEXTURES-1].next = -1;
-	
-	if(!no_gfx)
-	{
-		SDL_ShowCursor(0);
-		gfx_mapscreen(0,0,config.gfx_screen_width, config.gfx_screen_height);
-
-		/* set some default settings */	
-		glEnable(GL_BLEND);
-		glDisable(GL_CULL_FACE);
-		glDisable(GL_DEPTH_TEST);
-		glMatrixMode(GL_MODELVIEW);
-		glLoadIdentity();
-		
-		glAlphaFunc(GL_GREATER, 0);
-		glEnable(GL_ALPHA_TEST);
-		glDepthMask(0);
-
-		
-		//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-		//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-	}
-
-	/* init input */
-	inp_init();
-	
-	/* create null texture, will get id=0 */
-	invalid_texture = gfx_load_texture_raw(4,4,IMG_RGBA,null_texture_data,IMG_RGBA,TEXLOAD_NORESAMPLE);
-	dbg_msg("", "invalid texture id: %d %d", invalid_texture, textures[invalid_texture].tex);
-
-
-	/* font init */
-	gfx_font_init();
-	/* perform some tests */
-	/* pixeltest_dotesting(); */
-	
-	/*if(config.dbg_stress)
-		gfx_minimize();*/
-
-	return 0;
-}
-
-float gfx_screenaspect()
-{
-    return gfx_screenwidth()/(float)gfx_screenheight();
-}
-
-int gfx_window_active()
-{
-	return SDL_GetAppState()&SDL_APPINPUTFOCUS;
-}
-
-int gfx_window_open()
-{
-	return SDL_GetAppState()&SDL_APPACTIVE;
-}
-
-VIDEO_MODE fakemodes[] = {
-	{320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8},
-	{720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8},
-	{1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8},
-	{1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8},
-	{1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8},
-	{1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8},
-	{1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8},
-	{1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8},
-	{1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8},
-	{2048,1536,8,8,8},
-		
-	{320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5},
-	{720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5},
-	{1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5},
-	{1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5},
-	{1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5},
-	{1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5},
-	{1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5},
-	{1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5},
-	{1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5},
-	{2048,1536,5,6,5}
-};
-
-int gfx_get_video_modes(VIDEO_MODE *list, int maxcount)
-{
-	int num_modes = sizeof(fakemodes)/sizeof(VIDEO_MODE);
-	SDL_Rect **modes;
-
-	if(config.gfx_display_all_modes)
-	{
-		int count = sizeof(fakemodes)/sizeof(VIDEO_MODE);
-		mem_copy(list, fakemodes, sizeof(fakemodes));
-		if(maxcount < count)
-			count = maxcount;
-		return count;
-	}
-	
-	/* TODO: fix this code on osx or windows */
-		
-	modes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN);
-	if(modes == NULL)
-	{
-		/* no modes */
-		num_modes = 0;
-	}
-	else if(modes == (SDL_Rect**)-1)
-	{
-		/* all modes */
-	}
-	else
-	{
-		int i;
-		num_modes = 0;
-		for(i = 0; modes[i]; ++i)
-		{
-			if(num_modes == maxcount)
-				break;
-			list[num_modes].width = modes[i]->w;
-			list[num_modes].height = modes[i]->h;
-			list[num_modes].red = 8;
-			list[num_modes].green = 8;
-			list[num_modes].blue = 8;
-			num_modes++;
-		}
-	}
-	
-	return num_modes;
-}
-
-int gfx_unload_texture(int index)
-{
-	if(index == invalid_texture)
-		return 0;
-		
-	if(index < 0)
-		return 0;
-		
-	glDeleteTextures(1, &textures[index].tex);
-	textures[index].next = first_free_texture;
-	memory_usage -= textures[index].memsize;
-	first_free_texture = index;
-	return 0;
-}
-
-void gfx_blend_none()
-{
-	if(no_gfx) return;
-	glDisable(GL_BLEND);
-}
-
-void gfx_blend_normal()
-{
-	if(no_gfx) return;
-	glEnable(GL_BLEND);
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-}
-
-void gfx_blend_additive()
-{
-	if(no_gfx) return;
-	glEnable(GL_BLEND);
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
-}
-
-int gfx_memory_usage() { return memory_usage; }
-
-static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset)
-{
-	return (data[(v*w+u)*4+offset]+
-	data[(v*w+u+1)*4+offset]+
-	data[((v+1)*w+u)*4+offset]+
-	data[((v+1)*w+u+1)*4+offset])/4;
-}
-
-int gfx_load_texture_raw(int w, int h, int format, const void *data, int store_format, int flags)
-{
-	int mipmap = 1;
-	unsigned char *texdata = (unsigned char *)data;
-	unsigned char *tmpdata = 0;
-	int oglformat = 0;
-	int store_oglformat = 0;
-	int tex = 0;
-	
-	/* don't waste memory on texture if we are stress testing */
-	if(config.dbg_stress || no_gfx)
-		return 	invalid_texture;
-	
-	/* grab texture */
-	tex = first_free_texture;
-	first_free_texture = textures[tex].next;
-	textures[tex].next = -1;
-	
-	/* resample if needed */
-	if(!(flags&TEXLOAD_NORESAMPLE) && config.gfx_texture_quality==0)
-	{
-		if(w > 16 && h > 16 && format == IMG_RGBA)
-		{
-			unsigned char *tmpdata;
-			int c = 0;
-			int x, y;
-
-			tmpdata = (unsigned char *)mem_alloc(w*h*4, 1);
-
-			w/=2;
-			h/=2;
-
-			for(y = 0; y < h; y++)
-				for(x = 0; x < w; x++, c++)
-				{
-					tmpdata[c*4] = sample(w*2, h*2, texdata, x*2,y*2, 0);
-					tmpdata[c*4+1] = sample(w*2, h*2, texdata, x*2,y*2, 1);
-					tmpdata[c*4+2] = sample(w*2, h*2, texdata, x*2,y*2, 2);
-					tmpdata[c*4+3] = sample(w*2, h*2, texdata, x*2,y*2, 3);
-				}
-			texdata = tmpdata;
-		}
-	}
-	
-	oglformat = GL_RGBA;
-	if(format == IMG_RGB)
-		oglformat = GL_RGB;
-	else if(format == IMG_ALPHA)
-		oglformat = GL_ALPHA;
-	
-	/* upload texture */
-	if(config.gfx_texture_compression)
-	{
-		store_oglformat = GL_COMPRESSED_RGBA_ARB;
-		if(store_format == IMG_RGB)
-			store_oglformat = GL_COMPRESSED_RGB_ARB;
-		else if(store_format == IMG_ALPHA)
-			store_oglformat = GL_COMPRESSED_ALPHA_ARB;
-	}
-	else
-	{
-		store_oglformat = GL_RGBA;
-		if(store_format == IMG_RGB)
-			store_oglformat = GL_RGB;
-		else if(store_format == IMG_ALPHA)
-			store_oglformat = GL_ALPHA;
-	}
-		
-	glGenTextures(1, &textures[tex].tex);
-	glBindTexture(GL_TEXTURE_2D, textures[tex].tex);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
-	gluBuild2DMipmaps(GL_TEXTURE_2D, store_oglformat, w, h, oglformat, GL_UNSIGNED_BYTE, texdata);
-	
-	/* calculate memory usage */
-	{
-		int pixel_size = 4;
-		if(store_format == IMG_RGB)
-			pixel_size = 3;
-		else if(store_format == IMG_ALPHA)
-			pixel_size = 1;
-
-		textures[tex].memsize = w*h*pixel_size;
-		if(mipmap)
-		{
-			while(w > 2 && h > 2)
-			{
-				w>>=1;
-				h>>=1;
-				textures[tex].memsize += w*h*pixel_size;
-			}
-		}
-	}
-	
-	memory_usage += textures[tex].memsize;
-	mem_free(tmpdata);
-	return tex;
-}
-
-/* simple uncompressed RGBA loaders */
-int gfx_load_texture(const char *filename, int store_format, int flags)
-{
-	int l = strlen(filename);
-	int id;
-	IMAGE_INFO img;
-	
-	if(l < 3)
-		return -1;
-	if(gfx_load_png(&img, filename))
-	{
-		if (store_format == IMG_AUTO)
-			store_format = img.format;
-
-		id = gfx_load_texture_raw(img.width, img.height, img.format, img.data, store_format, flags);
-		mem_free(img.data);
-		return id;
-	}
-	
-	return invalid_texture;
-}
-
-int gfx_load_png(IMAGE_INFO *img, const char *filename)
-{
-	char completefilename[512];
-	unsigned char *buffer;
-	png_t png;
-	
-	/* open file for reading */
-	png_init(0,0);
-
-	engine_getpath(completefilename, sizeof(completefilename), filename, IOFLAG_READ);
-	
-	if(png_open_file(&png, completefilename) != PNG_NO_ERROR)
-	{
-		dbg_msg("game/png", "failed to open file. filename='%s'", completefilename);
-		return 0;
-	}
-	
-	if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA))
-	{
-		dbg_msg("game/png", "invalid format. filename='%s'", completefilename);
-		png_close_file(&png);
-        return 0;
-	}
-		
-	buffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1);
-	png_get_data(&png, buffer);
-	png_close_file(&png);
-	
-	img->width = png.width;
-	img->height = png.height;
-	if(png.color_type == PNG_TRUECOLOR)
-		img->format = IMG_RGB;
-	else if(png.color_type == PNG_TRUECOLOR_ALPHA)
-		img->format = IMG_RGBA;
-	img->data = buffer;
-	return 1;
-}
-
-void gfx_shutdown()
-{
-	if (vertices)
-		mem_free(vertices);
-
-	/* TODO: SDL, is this correct? */
-	SDL_Quit();
-}
-
-void gfx_screenshot()
-{
-	do_screenshot = 1;
-}
-
-
-extern int text_render_codepaths_usage[5];
-
-void gfx_swap()
-{
-	/*dbg_msg("", "%d %d %d %d %d",
-		text_render_codepaths_usage[0],
-		text_render_codepaths_usage[1],
-		text_render_codepaths_usage[2],
-		text_render_codepaths_usage[3],
-		text_render_codepaths_usage[4]);
-
-	text_render_codepaths_usage[0] = 0;
-	text_render_codepaths_usage[1] = 0;
-	text_render_codepaths_usage[2] = 0;
-	text_render_codepaths_usage[3] = 0;
-	text_render_codepaths_usage[4] = 0;*/
-
-	if(do_screenshot)
-	{
-		/* find filename */
-		char filename[128];
-		static int index = 1;
-
-		for(; index < 1000; index++)
-		{
-			IOHANDLE io;
-			str_format(filename, sizeof(filename), "screenshots/screenshot%04d.png", index);
-			io = engine_openfile(filename, IOFLAG_READ);
-			if(io)
-				io_close(io);
-			else
-				break;
-		}
-
-		gfx_screenshot_direct(filename);
-	
-		do_screenshot = 0;	
-	}
-	
-	{
-		static PERFORMACE_INFO pscope = {"glfwSwapBuffers", 0};
-		perf_start(&pscope);
-		SDL_GL_SwapBuffers();
-		perf_end();
-	}
-	
-	if(render_enable && config.gfx_finish)
-		glFinish();
-}
-
-void gfx_screenshot_direct(const char *filename)
-{
-	/* fetch image data */
-	int y;
-	int w = screen_width;
-	int h = screen_height;
-	unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*4, 1);
-	unsigned char *temp_row = pixel_data+w*h*4;
-	glReadPixels(0,0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixel_data);
-	
-	/* flip the pixel because opengl works from bottom left corner */
-	for(y = 0; y < h/2; y++)
-	{
-		mem_copy(temp_row, pixel_data+y*w*4, w*4);
-		mem_copy(pixel_data+y*w*4, pixel_data+(h-y-1)*w*4, w*4);
-		mem_copy(pixel_data+(h-y-1)*w*4, temp_row,w*4);
-	}
-	
-	/* find filename */
-	{
-		char wholepath[1024];
-		png_t png;
-
-		engine_savepath(filename, wholepath, sizeof(wholepath));
-	
-		/* save png */
-		dbg_msg("client", "saved screenshot to '%s'", wholepath);
-		png_open_file_write(&png, wholepath);
-		png_set_data(&png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pixel_data);
-		png_close_file(&png);
-	}
-
-	/* clean up */
-	mem_free(pixel_data);
-}
-
-int gfx_screenwidth()
-{
-	return screen_width;
-}
-
-int gfx_screenheight()
-{
-	return screen_height;
-}
-
-void gfx_texture_set(int slot)
-{
-	dbg_assert(drawing == 0, "called gfx_texture_set within begin");
-	if(no_gfx) return;
-	if(slot == -1)
-	{
-		glDisable(GL_TEXTURE_2D);
-	}
-	else
-	{
-		glEnable(GL_TEXTURE_2D);
-		glBindTexture(GL_TEXTURE_2D, textures[slot].tex);
-	}
-}
-
-void gfx_clear(float r, float g, float b)
-{
-	if(no_gfx) return;
-	glClearColor(r,g,b,0.0f);
-	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-}
-
-void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y)
-{
-	screen_x0 = tl_x;
-	screen_y0 = tl_y;
-	screen_x1 = br_x;
-	screen_y1 = br_y;
-	if(no_gfx) return;
-	glMatrixMode(GL_PROJECTION);
-	glLoadIdentity();
-	glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f);
-}
-
-void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y)
-{
-	*tl_x = screen_x0;
-	*tl_y = screen_y0;
-	*br_x = screen_x1;
-	*br_y = screen_y1;
-}
-
-void gfx_quads_begin()
-{
-	dbg_assert(drawing == 0, "called quads_begin twice");
-	drawing = DRAWING_QUADS;
-	
-	gfx_quads_setsubset(0,0,1,1);
-	gfx_quads_setrotation(0);
-	gfx_setcolor(1,1,1,1);
-}
-
-void gfx_quads_end()
-{
-	dbg_assert(drawing == DRAWING_QUADS, "called quads_end without begin");
-	flush();
-	drawing = 0;
-}
-
-
-void gfx_quads_setrotation(float angle)
-{
-	dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setrotation without begin");
-	rotation = angle;
-}
-
-void gfx_setcolorvertex(int i, float r, float g, float b, float a)
-{
-	dbg_assert(drawing != 0, "called gfx_quads_setcolorvertex without begin");
-	color[i].r = r;
-	color[i].g = g;
-	color[i].b = b;
-	color[i].a = a;
-}
-
-void gfx_setcolor(float r, float g, float b, float a)
-{
-	dbg_assert(drawing != 0, "called gfx_quads_setcolor without begin");
-	gfx_setcolorvertex(0, r, g, b, a);
-	gfx_setcolorvertex(1, r, g, b, a);
-	gfx_setcolorvertex(2, r, g, b, a);
-	gfx_setcolorvertex(3, r, g, b, a);
-}
-
-void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v)
-{
-	dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setsubset without begin");
-
-	texture[0].u = tl_u;	texture[1].u = br_u;
-	texture[0].v = tl_v;	texture[1].v = tl_v;
-
-	texture[3].u = tl_u;	texture[2].u = br_u;
-	texture[3].v = br_v;	texture[2].v = br_v;
-}
-
-void gfx_quads_setsubset_free(
-	float x0, float y0, float x1, float y1,
-	float x2, float y2, float x3, float y3)
-{
-	texture[0].u = x0; texture[0].v = y0;
-	texture[1].u = x1; texture[1].v = y1;
-	texture[2].u = x2; texture[2].v = y2;
-	texture[3].u = x3; texture[3].v = y3;
-}
-
-
-static void rotate(VEC3 *center, VEC3 *point)
-{
-	float x = point->x - center->x;
-	float y = point->y - center->y;
-	point->x = x * cosf(rotation) - y * sinf(rotation) + center->x;
-	point->y = x * sinf(rotation) + y * cosf(rotation) + center->y;
-}
-
-void gfx_quads_draw(float x, float y, float w, float h)
-{
-	gfx_quads_drawTL(x-w/2, y-h/2,w,h);
-}
-
-void gfx_quads_drawTL(float x, float y, float width, float height)
-{
-	VEC3 center;
-
-	dbg_assert(drawing == DRAWING_QUADS, "called quads_draw without begin");
-
-	center.x = x + width/2;
-	center.y = y + height/2;
-	center.z = 0;
-	
-	vertices[num_vertices].pos.x = x;
-	vertices[num_vertices].pos.y = y;
-	vertices[num_vertices].tex = texture[0];
-	vertices[num_vertices].color = color[0];
-	rotate(&center, &vertices[num_vertices].pos);
-
-	vertices[num_vertices + 1].pos.x = x+width;
-	vertices[num_vertices + 1].pos.y = y;
-	vertices[num_vertices + 1].tex = texture[1];
-	vertices[num_vertices + 1].color = color[1];
-	rotate(&center, &vertices[num_vertices + 1].pos);
-
-	vertices[num_vertices + 2].pos.x = x + width;
-	vertices[num_vertices + 2].pos.y = y+height;
-	vertices[num_vertices + 2].tex = texture[2];
-	vertices[num_vertices + 2].color = color[2];
-	rotate(&center, &vertices[num_vertices + 2].pos);
-
-	vertices[num_vertices + 3].pos.x = x;
-	vertices[num_vertices + 3].pos.y = y+height;
-	vertices[num_vertices + 3].tex = texture[3];
-	vertices[num_vertices + 3].color = color[3];
-	rotate(&center, &vertices[num_vertices + 3].pos);
-	
-	add_vertices(4);
-}
-
-void gfx_quads_draw_freeform(
-	float x0, float y0, float x1, float y1,
-	float x2, float y2, float x3, float y3)
-{
-	dbg_assert(drawing == DRAWING_QUADS, "called quads_draw_freeform without begin");
-	
-	vertices[num_vertices].pos.x = x0;
-	vertices[num_vertices].pos.y = y0;
-	vertices[num_vertices].tex = texture[0];
-	vertices[num_vertices].color = color[0];
-
-	vertices[num_vertices + 1].pos.x = x1;
-	vertices[num_vertices + 1].pos.y = y1;
-	vertices[num_vertices + 1].tex = texture[1];
-	vertices[num_vertices + 1].color = color[1];
-
-	vertices[num_vertices + 2].pos.x = x3;
-	vertices[num_vertices + 2].pos.y = y3;
-	vertices[num_vertices + 2].tex = texture[3];
-	vertices[num_vertices + 2].color = color[3];
-
-	vertices[num_vertices + 3].pos.x = x2;
-	vertices[num_vertices + 3].pos.y = y2;
-	vertices[num_vertices + 3].tex = texture[2];
-	vertices[num_vertices + 3].color = color[2];
-	
-	add_vertices(4);
-}
-
-void gfx_quads_text(float x, float y, float size, float r, float g, float b, float a, const char *text)
-{
-	float startx = x;
-
-	gfx_quads_begin();
-	gfx_setcolor(r,g,b,a);
-
-	while(*text)
-	{
-		char c = *text;
-		text++;
-		
-		if(c == '\n')
-		{
-			x = startx;
-			y += size;
-		}
-		else
-		{
-			gfx_quads_setsubset(
-				(c%16)/16.0f,
-				(c/16)/16.0f,
-				(c%16)/16.0f+1.0f/16.0f,
-				(c/16)/16.0f+1.0f/16.0f);
-			
-			gfx_quads_drawTL(x,y,size,size);
-			x += size/2;
-		}
-	}
-	
-	gfx_quads_end();
-}
-
-void gfx_lines_begin()
-{
-	dbg_assert(drawing == 0, "called begin twice");
-	drawing = DRAWING_LINES;
-	gfx_setcolor(1,1,1,1);
-}
-
-void gfx_lines_end()
-{
-	dbg_assert(drawing == DRAWING_LINES, "called end without begin");
-	flush();
-	drawing = 0;
-}
-
-void gfx_lines_draw(float x0, float y0, float x1, float y1)
-{
-	dbg_assert(drawing == DRAWING_LINES, "called draw without begin");
-	
-	vertices[num_vertices].pos.x = x0;
-	vertices[num_vertices].pos.y = y0;
-	vertices[num_vertices].tex = texture[0];
-	vertices[num_vertices].color = color[0];
-
-	vertices[num_vertices + 1].pos.x = x1;
-	vertices[num_vertices + 1].pos.y = y1;
-	vertices[num_vertices + 1].tex = texture[1];
-	vertices[num_vertices + 1].color = color[1];
-	
-	add_vertices(2);
-}
-
-void gfx_clip_enable(int x, int y, int w, int h)
-{
-	if(no_gfx) return;
-	glScissor(x, gfx_screenheight()-(y+h), w, h);
-	glEnable(GL_SCISSOR_TEST);
-}
-
-void gfx_clip_disable()
-{
-	if(no_gfx) return;
-	glDisable(GL_SCISSOR_TEST);
-}
-
-void gfx_minimize()
-{
-	SDL_WM_IconifyWindow();
-}
-
-void gfx_maximize()
-{
-	/* TODO: SDL */
-}
diff --git a/src/engine/client/ec_gfx.cpp b/src/engine/client/ec_gfx.cpp
new file mode 100644
index 00000000..5632581a
--- /dev/null
+++ b/src/engine/client/ec_gfx.cpp
@@ -0,0 +1,992 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+
+#include <base/detect.h>
+
+#include "SDL.h"
+
+#ifdef CONF_FAMILY_WINDOWS
+	#define WIN32_LEAN_AND_MEAN
+	#include <windows.h>
+#endif
+
+#ifdef CONF_PLATFORM_MACOSX
+	#include <OpenGL/gl.h>
+	#include <OpenGL/glu.h>
+#else
+	#include <GL/gl.h>
+	#include <GL/glu.h>
+#endif
+
+#include <base/system.h>
+#include <engine/external/pnglite/pnglite.h>
+
+#include <engine/e_client_interface.h>
+#include <engine/e_engine.h>
+#include <engine/e_config.h>
+#include <engine/e_keys.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+/* compressed textures */
+#define GL_COMPRESSED_RGB_ARB 0x84ED
+#define GL_COMPRESSED_RGBA_ARB 0x84EE
+#define GL_COMPRESSED_ALPHA_ARB 0x84E9
+
+#define TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
+
+
+void gfx_font_init();
+
+VIDEO_MODE fakemodes[] = {
+	{320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8},
+	{720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8},
+	{1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8},
+	{1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8},
+	{1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8},
+	{1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8},
+	{1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8},
+	{1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8},
+	{1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8},
+	{2048,1536,8,8,8},
+		
+	{320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5},
+	{720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5},
+	{1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5},
+	{1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5},
+	{1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5},
+	{1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5},
+	{1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5},
+	{1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5},
+	{1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5},
+	{2048,1536,5,6,5}
+};
+
+int gfx_get_video_modes(VIDEO_MODE *list, int maxcount)
+{
+	int num_modes = sizeof(fakemodes)/sizeof(VIDEO_MODE);
+	SDL_Rect **modes;
+
+	if(config.gfx_display_all_modes)
+	{
+		int count = sizeof(fakemodes)/sizeof(VIDEO_MODE);
+		mem_copy(list, fakemodes, sizeof(fakemodes));
+		if(maxcount < count)
+			count = maxcount;
+		return count;
+	}
+	
+	/* TODO: fix this code on osx or windows */
+		
+	modes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN);
+	if(modes == NULL)
+	{
+		/* no modes */
+		num_modes = 0;
+	}
+	else if(modes == (SDL_Rect**)-1)
+	{
+		/* all modes */
+	}
+	else
+	{
+		int i;
+		num_modes = 0;
+		for(i = 0; modes[i]; ++i)
+		{
+			if(num_modes == maxcount)
+				break;
+			list[num_modes].width = modes[i]->w;
+			list[num_modes].height = modes[i]->h;
+			list[num_modes].red = 8;
+			list[num_modes].green = 8;
+			list[num_modes].blue = 8;
+			num_modes++;
+		}
+	}
+	
+	return num_modes;
+}
+
+
+#include "graphics.h"
+
+class CGraphics_OpenGL : public IEngineGraphics
+{
+protected:
+	/* */
+	typedef struct { float x, y, z; } CPoint;
+	typedef struct { float u, v; } CTexCoord;
+	typedef struct { float r, g, b, a; } CColor;
+
+	typedef struct
+	{
+		CPoint m_Pos;
+		CTexCoord m_Tex;
+		CColor m_Color;
+	} CVertex;
+	
+	enum
+	{
+		MAX_VERTICES = 32*1024,
+		MAX_TEXTURES = 1024*4,
+		
+		DRAWING_QUADS=1,
+		DRAWING_LINES=2		
+	};
+
+	CVertex m_aVertices[MAX_VERTICES];
+	int m_NumVertices;
+
+	CColor m_aColor[4];
+	CTexCoord m_aTexture[4];
+
+	bool m_RenderEnable;
+
+	float m_Rotation;
+	int m_Drawing;
+	bool m_DoScreenshot;
+
+	float m_ScreenX0;
+	float m_ScreenY0;
+	float m_ScreenX1;
+	float m_ScreenY1;
+
+	int m_InvalidTexture;
+
+	struct CTexture
+	{
+		GLuint tex;
+		int memsize;
+		int flags;
+		int next;
+	};
+
+	enum
+	{
+		
+	};
+
+	CTexture m_aTextures[MAX_TEXTURES];
+	int m_FirstFreeTexture;
+	int m_TextureMemoryUsage;
+
+
+	void Flush()
+	{
+		if(m_NumVertices == 0)
+			return;
+			
+		//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
+		//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+		glVertexPointer(3, GL_FLOAT,
+				sizeof(CVertex),
+				(char*)m_aVertices);
+		glTexCoordPointer(2, GL_FLOAT,
+				sizeof(CVertex),
+				(char*)m_aVertices + sizeof(float)*3);
+		glColorPointer(4, GL_FLOAT,
+				sizeof(CVertex),
+				(char*)m_aVertices + sizeof(float)*5);
+		glEnableClientState(GL_VERTEX_ARRAY);
+		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+		glEnableClientState(GL_COLOR_ARRAY);
+		
+		if(m_RenderEnable)
+		{
+			if(m_Drawing == DRAWING_QUADS)
+				glDrawArrays(GL_QUADS, 0, m_NumVertices);
+			else if(m_Drawing == DRAWING_LINES)
+				glDrawArrays(GL_LINES, 0, m_NumVertices);
+		}
+		
+		/* Reset pointer */
+		m_NumVertices = 0;
+	}
+
+	void AddVertices(int count)
+	{
+		m_NumVertices += count;
+		if((m_NumVertices + count) >= MAX_VERTICES)
+			Flush();
+	}
+	
+	void Rotate(CPoint *pCenter, CPoint *pPoint)
+	{
+		float x = pPoint->x - pCenter->x;
+		float y = pPoint->y - pCenter->y;
+		pPoint->x = x * cosf(m_Rotation) - y * sinf(m_Rotation) + pCenter->x;
+		pPoint->y = x * sinf(m_Rotation) + y * cosf(m_Rotation) + pCenter->y;
+	}
+	
+	
+
+
+	static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset)
+	{
+		return (data[(v*w+u)*4+offset]+
+		data[(v*w+u+1)*4+offset]+
+		data[((v+1)*w+u)*4+offset]+
+		data[((v+1)*w+u+1)*4+offset])/4;
+	}	
+public:
+	CGraphics_OpenGL()
+	{
+		m_NumVertices = 0;
+		
+		m_ScreenX0 = 0;
+		m_ScreenY0 = 0;
+		m_ScreenX1 = 0;
+		m_ScreenY1 = 0;
+		
+		m_ScreenWidth = -1;
+		m_ScreenHeight = -1;
+		
+		m_Rotation = 0;
+		m_Drawing = 0;
+		m_InvalidTexture = 0;
+		
+		m_TextureMemoryUsage = 0;
+		
+		m_RenderEnable = true;
+		m_DoScreenshot = false;
+	}
+	
+
+	virtual void ClipEnable(int x, int y, int w, int h)
+	{
+		//if(no_gfx) return;
+		glScissor(x, ScreenHeight()-(y+h), w, h);
+		glEnable(GL_SCISSOR_TEST);
+	}
+
+	virtual void ClipDisable()
+	{
+		//if(no_gfx) return;
+		glDisable(GL_SCISSOR_TEST);
+	}
+		
+
+	virtual void BlendNone()
+	{
+		glDisable(GL_BLEND);
+	}
+
+	virtual void BlendNormal()
+	{
+		glEnable(GL_BLEND);
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	}
+
+	virtual void BlendAdditive()
+	{
+		glEnable(GL_BLEND);
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+	}
+
+	//int gfx_memory_usage() { return m_MemoryUsage; }	
+		
+	virtual void MapScreen(float tl_x, float tl_y, float br_x, float br_y)
+	{
+		m_ScreenX0 = tl_x;
+		m_ScreenY0 = tl_y;
+		m_ScreenX1 = br_x;
+		m_ScreenY1 = br_y;
+		glMatrixMode(GL_PROJECTION);
+		glLoadIdentity();
+		glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f);
+	}
+
+	virtual void GetScreen(float *tl_x, float *tl_y, float *br_x, float *br_y)
+	{
+		*tl_x = m_ScreenX0;
+		*tl_y = m_ScreenY0;
+		*br_x = m_ScreenX1;
+		*br_y = m_ScreenY1;
+	}
+
+	virtual void LinesBegin()
+	{
+		dbg_assert(m_Drawing == 0, "called begin twice");
+		m_Drawing = DRAWING_LINES;
+		SetColor(1,1,1,1);
+	}
+
+	virtual void LinesEnd()
+	{
+		dbg_assert(m_Drawing == DRAWING_LINES, "called end without begin");
+		Flush();
+		m_Drawing = 0;
+	}
+
+	virtual void LinesDraw(float x0, float y0, float x1, float y1)
+	{
+		dbg_assert(m_Drawing == DRAWING_LINES, "called draw without begin");
+		
+		m_aVertices[m_NumVertices].m_Pos.x = x0;
+		m_aVertices[m_NumVertices].m_Pos.y = y0;
+		m_aVertices[m_NumVertices].m_Tex = m_aTexture[0];
+		m_aVertices[m_NumVertices].m_Color = m_aColor[0];
+
+		m_aVertices[m_NumVertices + 1].m_Pos.x = x1;
+		m_aVertices[m_NumVertices + 1].m_Pos.y = y1;
+		m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1];
+		m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1];
+		
+		AddVertices(2);
+	}
+	
+
+	
+	virtual int UnloadTexture(int Index)
+	{
+		if(Index == m_InvalidTexture)
+			return 0;
+			
+		if(Index < 0)
+			return 0;
+			
+		glDeleteTextures(1, &m_aTextures[Index].tex);
+		m_aTextures[Index].next = m_FirstFreeTexture;
+		m_TextureMemoryUsage -= m_aTextures[Index].memsize;
+		m_FirstFreeTexture = Index;
+		return 0;
+	}
+
+
+	virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags)
+	{
+		int mipmap = 1;
+		unsigned char *texdata = (unsigned char *)pData;
+		unsigned char *tmpdata = 0;
+		int oglformat = 0;
+		int store_oglformat = 0;
+		int tex = 0;
+		
+		/* don't waste memory on texture if we are stress testing */
+		if(config.dbg_stress)
+			return 	m_InvalidTexture;
+		
+		/* grab texture */
+		tex = m_FirstFreeTexture;
+		m_FirstFreeTexture = m_aTextures[tex].next;
+		m_aTextures[tex].next = -1;
+		
+		/* resample if needed */
+		if(!(Flags&TEXLOAD_NORESAMPLE) && config.gfx_texture_quality==0)
+		{
+			if(Width > 16 && Height > 16 && Format == IMG_RGBA)
+			{
+				unsigned char *tmpdata;
+				int c = 0;
+				int x, y;
+
+				tmpdata = (unsigned char *)mem_alloc(Width*Height*4, 1);
+
+				Width/=2;
+				Height/=2;
+
+				for(y = 0; y < Height; y++)
+					for(x = 0; x < Width; x++, c++)
+					{
+						tmpdata[c*4] = sample(Width*2, Height*2, texdata, x*2,y*2, 0);
+						tmpdata[c*4+1] = sample(Width*2, Height*2, texdata, x*2,y*2, 1);
+						tmpdata[c*4+2] = sample(Width*2, Height*2, texdata, x*2,y*2, 2);
+						tmpdata[c*4+3] = sample(Width*2, Height*2, texdata, x*2,y*2, 3);
+					}
+				texdata = tmpdata;
+			}
+		}
+		
+		oglformat = GL_RGBA;
+		if(Format == IMG_RGB)
+			oglformat = GL_RGB;
+		else if(Format == IMG_ALPHA)
+			oglformat = GL_ALPHA;
+		
+		/* upload texture */
+		if(config.gfx_texture_compression)
+		{
+			store_oglformat = GL_COMPRESSED_RGBA_ARB;
+			if(StoreFormat == IMG_RGB)
+				store_oglformat = GL_COMPRESSED_RGB_ARB;
+			else if(StoreFormat == IMG_ALPHA)
+				store_oglformat = GL_COMPRESSED_ALPHA_ARB;
+		}
+		else
+		{
+			store_oglformat = GL_RGBA;
+			if(StoreFormat == IMG_RGB)
+				store_oglformat = GL_RGB;
+			else if(StoreFormat == IMG_ALPHA)
+				store_oglformat = GL_ALPHA;
+		}
+			
+		glGenTextures(1, &m_aTextures[tex].tex);
+		glBindTexture(GL_TEXTURE_2D, m_aTextures[tex].tex);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
+		gluBuild2DMipmaps(GL_TEXTURE_2D, store_oglformat, Width, Height, oglformat, GL_UNSIGNED_BYTE, texdata);
+		
+		/* calculate memory usage */
+		{
+			int pixel_size = 4;
+			if(StoreFormat == IMG_RGB)
+				pixel_size = 3;
+			else if(StoreFormat == IMG_ALPHA)
+				pixel_size = 1;
+
+			m_aTextures[tex].memsize = Width*Height*pixel_size;
+			if(mipmap)
+			{
+				while(Width > 2 && Height > 2)
+				{
+					Width>>=1;
+					Height>>=1;
+					m_aTextures[tex].memsize += Width*Height*pixel_size;
+				}
+			}
+		}
+		
+		m_TextureMemoryUsage += m_aTextures[tex].memsize;
+		mem_free(tmpdata);
+		return tex;
+	}
+
+	/* simple uncompressed RGBA loaders */
+	virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags)
+	{
+		int l = strlen(pFilename);
+		int id;
+		IMAGE_INFO img;
+		
+		if(l < 3)
+			return -1;
+		if(LoadPNG(&img, pFilename))
+		{
+			if (StoreFormat == IMG_AUTO)
+				StoreFormat = img.format;
+
+			id = LoadTextureRaw(img.width, img.height, img.format, img.data, StoreFormat, Flags);
+			mem_free(img.data);
+			return id;
+		}
+		
+		return m_InvalidTexture;
+	}
+
+	virtual int LoadPNG(IMAGE_INFO *pImg, const char *pFilename)
+	{
+		char aCompleteFilename[512];
+		unsigned char *pBuffer;
+		png_t png;
+		
+		/* open file for reading */
+		png_init(0,0);
+
+		engine_getpath(aCompleteFilename, sizeof(aCompleteFilename), pFilename, IOFLAG_READ);
+		
+		if(png_open_file(&png, aCompleteFilename) != PNG_NO_ERROR)
+		{
+			dbg_msg("game/png", "failed to open file. filename='%s'", aCompleteFilename);
+			return 0;
+		}
+		
+		if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA))
+		{
+			dbg_msg("game/png", "invalid format. filename='%s'", aCompleteFilename);
+			png_close_file(&png);
+			return 0;
+		}
+			
+		pBuffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1);
+		png_get_data(&png, pBuffer);
+		png_close_file(&png);
+		
+		pImg->width = png.width;
+		pImg->height = png.height;
+		if(png.color_type == PNG_TRUECOLOR)
+			pImg->format = IMG_RGB;
+		else if(png.color_type == PNG_TRUECOLOR_ALPHA)
+			pImg->format = IMG_RGBA;
+		pImg->data = pBuffer;
+		return 1;
+	}
+
+	void ScreenshotDirect(const char *filename)
+	{
+		/* fetch image data */
+		int y;
+		int w = m_ScreenWidth;
+		int h = m_ScreenHeight;
+		unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*4, 1);
+		unsigned char *temp_row = pixel_data+w*h*4;
+		glReadPixels(0,0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixel_data);
+		
+		/* flip the pixel because opengl works from bottom left corner */
+		for(y = 0; y < h/2; y++)
+		{
+			mem_copy(temp_row, pixel_data+y*w*4, w*4);
+			mem_copy(pixel_data+y*w*4, pixel_data+(h-y-1)*w*4, w*4);
+			mem_copy(pixel_data+(h-y-1)*w*4, temp_row,w*4);
+		}
+		
+		/* find filename */
+		{
+			char wholepath[1024];
+			png_t png;
+
+			engine_savepath(filename, wholepath, sizeof(wholepath));
+		
+			/* save png */
+			dbg_msg("client", "saved screenshot to '%s'", wholepath);
+			png_open_file_write(&png, wholepath);
+			png_set_data(&png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pixel_data);
+			png_close_file(&png);
+		}
+
+		/* clean up */
+		mem_free(pixel_data);
+	}
+
+	virtual void TextureSet(int TextureID)
+	{
+		dbg_assert(m_Drawing == 0, "called Graphics()->TextureSet within begin");
+		if(TextureID == -1)
+		{
+			glDisable(GL_TEXTURE_2D);
+		}
+		else
+		{
+			glEnable(GL_TEXTURE_2D);
+			glBindTexture(GL_TEXTURE_2D, m_aTextures[TextureID].tex);
+		}
+	}
+
+	virtual void Clear(float r, float g, float b)
+	{
+		glClearColor(r,g,b,0.0f);
+		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	}
+
+	virtual void QuadsBegin()
+	{
+		dbg_assert(m_Drawing == 0, "called quads_begin twice");
+		m_Drawing = DRAWING_QUADS;
+		
+		QuadsSetSubset(0,0,1,1);
+		QuadsSetRotation(0);
+		SetColor(1,1,1,1);
+	}
+
+	virtual void QuadsEnd()
+	{
+		dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_end without begin");
+		Flush();
+		m_Drawing = 0;
+	}
+
+	virtual void QuadsSetRotation(float Angle)
+	{
+		dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetRotation without begin");
+		m_Rotation = Angle;
+	}
+
+	virtual void SetColorVertex(int i, float r, float g, float b, float a)
+	{
+		dbg_assert(m_Drawing != 0, "called gfx_quads_setcolorvertex without begin");
+		m_aColor[i].r = r;
+		m_aColor[i].g = g;
+		m_aColor[i].b = b;
+		m_aColor[i].a = a;
+	}
+
+	virtual void SetColor(float r, float g, float b, float a)
+	{
+		dbg_assert(m_Drawing != 0, "called gfx_quads_setcolor without begin");
+		SetColorVertex(0, r, g, b, a);
+		SetColorVertex(1, r, g, b, a);
+		SetColorVertex(2, r, g, b, a);
+		SetColorVertex(3, r, g, b, a);
+	}
+
+	virtual void QuadsSetSubset(float tl_u, float tl_v, float br_u, float br_v)
+	{
+		dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetSubset without begin");
+
+		m_aTexture[0].u = tl_u;	m_aTexture[1].u = br_u;
+		m_aTexture[0].v = tl_v;	m_aTexture[1].v = tl_v;
+
+		m_aTexture[3].u = tl_u;	m_aTexture[2].u = br_u;
+		m_aTexture[3].v = br_v;	m_aTexture[2].v = br_v;
+	}
+
+	virtual void QuadsSetSubsetFree(
+		float x0, float y0, float x1, float y1,
+		float x2, float y2, float x3, float y3)
+	{
+		m_aTexture[0].u = x0; m_aTexture[0].v = y0;
+		m_aTexture[1].u = x1; m_aTexture[1].v = y1;
+		m_aTexture[2].u = x2; m_aTexture[2].v = y2;
+		m_aTexture[3].u = x3; m_aTexture[3].v = y3;
+	}
+
+	virtual void QuadsDraw(float x, float y, float w, float h)
+	{
+		QuadsDrawTL(x-w/2, y-h/2,w,h);
+	}
+
+	virtual void QuadsDrawTL(float x, float y, float w, float h)
+	{
+		CPoint Center;
+
+		dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw without begin");
+
+		Center.x = x + w/2;
+		Center.y = y + h/2;
+		Center.z = 0;
+		
+		m_aVertices[m_NumVertices].m_Pos.x = x;
+		m_aVertices[m_NumVertices].m_Pos.y = y;
+		m_aVertices[m_NumVertices].m_Tex = m_aTexture[0];
+		m_aVertices[m_NumVertices].m_Color = m_aColor[0];
+		Rotate(&Center, &m_aVertices[m_NumVertices].m_Pos);
+
+		m_aVertices[m_NumVertices + 1].m_Pos.x = x+w;
+		m_aVertices[m_NumVertices + 1].m_Pos.y = y;
+		m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1];
+		m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1];
+		Rotate(&Center, &m_aVertices[m_NumVertices + 1].m_Pos);
+
+		m_aVertices[m_NumVertices + 2].m_Pos.x = x + w;
+		m_aVertices[m_NumVertices + 2].m_Pos.y = y+h;
+		m_aVertices[m_NumVertices + 2].m_Tex = m_aTexture[2];
+		m_aVertices[m_NumVertices + 2].m_Color = m_aColor[2];
+		Rotate(&Center, &m_aVertices[m_NumVertices + 2].m_Pos);
+
+		m_aVertices[m_NumVertices + 3].m_Pos.x = x;
+		m_aVertices[m_NumVertices + 3].m_Pos.y = y+h;
+		m_aVertices[m_NumVertices + 3].m_Tex = m_aTexture[3];
+		m_aVertices[m_NumVertices + 3].m_Color = m_aColor[3];
+		Rotate(&Center, &m_aVertices[m_NumVertices + 3].m_Pos);
+		
+		AddVertices(4);
+	}
+
+	void QuadsDrawFreeform(
+		float x0, float y0, float x1, float y1,
+		float x2, float y2, float x3, float y3)
+	{
+		dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw_freeform without begin");
+		
+		m_aVertices[m_NumVertices].m_Pos.x = x0;
+		m_aVertices[m_NumVertices].m_Pos.y = y0;
+		m_aVertices[m_NumVertices].m_Tex = m_aTexture[0];
+		m_aVertices[m_NumVertices].m_Color = m_aColor[0];
+
+		m_aVertices[m_NumVertices + 1].m_Pos.x = x1;
+		m_aVertices[m_NumVertices + 1].m_Pos.y = y1;
+		m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1];
+		m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1];
+
+		m_aVertices[m_NumVertices + 2].m_Pos.x = x3;
+		m_aVertices[m_NumVertices + 2].m_Pos.y = y3;
+		m_aVertices[m_NumVertices + 2].m_Tex = m_aTexture[3];
+		m_aVertices[m_NumVertices + 2].m_Color = m_aColor[3];
+
+		m_aVertices[m_NumVertices + 3].m_Pos.x = x2;
+		m_aVertices[m_NumVertices + 3].m_Pos.y = y2;
+		m_aVertices[m_NumVertices + 3].m_Tex = m_aTexture[2];
+		m_aVertices[m_NumVertices + 3].m_Color = m_aColor[2];
+		
+		AddVertices(4);
+	}
+
+	virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText)
+	{
+		float startx = x;
+
+		QuadsBegin();
+		SetColor(r,g,b,a);
+
+		while(*pText)
+		{
+			char c = *pText;
+			pText++;
+			
+			if(c == '\n')
+			{
+				x = startx;
+				y += Size;
+			}
+			else
+			{
+				QuadsSetSubset(
+					(c%16)/16.0f,
+					(c/16)/16.0f,
+					(c%16)/16.0f+1.0f/16.0f,
+					(c/16)/16.0f+1.0f/16.0f);
+				
+				QuadsDrawTL(x,y,Size,Size);
+				x += Size/2;
+			}
+		}
+		
+		QuadsEnd();
+	}
+	
+	virtual bool Init()
+	{
+		/* Set all z to -5.0f */
+		for(int i = 0; i < MAX_VERTICES; i++)
+			m_aVertices[i].m_Pos.z = -5.0f;
+
+		/* init textures */
+		m_FirstFreeTexture = 0;
+		for(int i = 0; i < MAX_TEXTURES; i++)
+			m_aTextures[i].next = i+1;
+		m_aTextures[MAX_TEXTURES-1].next = -1;
+
+		/* set some default settings */	
+		glEnable(GL_BLEND);
+		glDisable(GL_CULL_FACE);
+		glDisable(GL_DEPTH_TEST);
+		glMatrixMode(GL_MODELVIEW);
+		glLoadIdentity();
+		
+		glAlphaFunc(GL_GREATER, 0);
+		glEnable(GL_ALPHA_TEST);
+		glDepthMask(0);
+
+		/* create null texture, will get id=0 */
+		static const unsigned char aNullTextureData[] = {
+			0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, 
+			0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, 
+			0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, 
+			0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, 
+		};
+		
+		m_InvalidTexture = LoadTextureRaw(4,4,IMG_RGBA,aNullTextureData,IMG_RGBA,TEXLOAD_NORESAMPLE);
+		dbg_msg("", "invalid texture id: %d %d", m_InvalidTexture, m_aTextures[m_InvalidTexture].tex);
+		
+		return true;
+	}
+};
+
+class CGraphics_SDL : public CGraphics_OpenGL
+{
+	SDL_Surface *m_pScreenSurface;	
+	
+	int TryInit()
+	{
+		const SDL_VideoInfo *pInfo;
+		int Flags = SDL_OPENGL;
+		
+		m_ScreenWidth = config.gfx_screen_width;
+		m_ScreenHeight = config.gfx_screen_height;
+
+		pInfo = SDL_GetVideoInfo();
+
+		/* set flags */
+		Flags  = SDL_OPENGL;
+		Flags |= SDL_GL_DOUBLEBUFFER;
+		Flags |= SDL_HWPALETTE;
+		if(config.dbg_resizable)
+			Flags |= SDL_RESIZABLE;
+
+		if(pInfo->hw_available)
+			Flags |= SDL_HWSURFACE;
+		else
+			Flags |= SDL_SWSURFACE;
+
+		if(pInfo->blit_hw)
+			Flags |= SDL_HWACCEL;
+
+		if(config.gfx_fullscreen)
+			Flags |= SDL_FULLSCREEN;
+
+		/* set gl attributes */
+		if(config.gfx_fsaa_samples)
+		{
+			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
+			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config.gfx_fsaa_samples);
+		}
+		else
+		{
+			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
+			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
+		}
+
+		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+		SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, config.gfx_vsync);
+
+		/* set caption */
+		SDL_WM_SetCaption("Teeworlds", "Teeworlds");
+		
+		/* create window */
+		m_pScreenSurface = SDL_SetVideoMode(m_ScreenWidth, m_ScreenHeight, 0, Flags);
+		if(m_pScreenSurface == NULL)
+		{
+			dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError());
+			return -1;
+		}
+		
+		return 0;
+	}
+
+
+	int InitWindow()
+	{
+		if(TryInit() == 0)
+			return 0;
+		
+		/* try disabling fsaa */
+		while(config.gfx_fsaa_samples)
+		{
+			config.gfx_fsaa_samples--;
+			
+			if(config.gfx_fsaa_samples)
+				dbg_msg("gfx", "lowering FSAA to %d and trying again", config.gfx_fsaa_samples);
+			else
+				dbg_msg("gfx", "disabling FSAA and trying again");
+
+			if(TryInit() == 0)
+				return 0;
+		}
+
+		/* try lowering the resolution */
+		if(config.gfx_screen_width != 640 || config.gfx_screen_height != 480)
+		{
+			dbg_msg("gfx", "setting resolution to 640x480 and trying again");
+			config.gfx_screen_width = 640;
+			config.gfx_screen_height = 480;
+
+			if(TryInit() == 0)
+				return 0;
+		}
+
+		dbg_msg("gfx", "out of ideas. failed to init graphics");
+						
+		return -1;		
+	}
+
+	
+public:
+	CGraphics_SDL()
+	{
+		m_pScreenSurface = 0;
+	}
+
+	virtual bool Init()
+	{
+		{
+			int Systems = SDL_INIT_VIDEO;
+			
+			if(config.snd_enable)
+				Systems |= SDL_INIT_AUDIO;
+
+			if(config.cl_eventthread)
+				Systems |= SDL_INIT_EVENTTHREAD;
+			
+			if(SDL_Init(Systems) < 0)
+			{
+				dbg_msg("gfx", "unable to init SDL: %s", SDL_GetError());
+				return -1;
+			}
+		}
+		
+		atexit(SDL_Quit);
+
+		#ifdef CONF_FAMILY_WINDOWS
+			if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED"))
+				putenv("SDL_VIDEO_WINDOW_POS=8,27");
+		#endif
+		
+		if(InitWindow() != 0)
+			return -1;
+
+		SDL_ShowCursor(0);
+			
+		CGraphics_OpenGL::Init();
+		
+		MapScreen(0,0,config.gfx_screen_width, config.gfx_screen_height);
+
+		/* init input */
+		inp_init();
+
+		/* font init */
+		gfx_font_init();
+
+		return 0;
+	}
+	
+	virtual void Shutdown()
+	{
+		/* TODO: SDL, is this correct? */
+		SDL_Quit();
+	}
+
+	virtual void Minimize()
+	{
+		SDL_WM_IconifyWindow();
+	}
+
+	virtual void Maximize()
+	{
+		/* TODO: SDL */
+	}
+
+	virtual int WindowActive()
+	{
+		return SDL_GetAppState()&SDL_APPINPUTFOCUS;
+	}
+
+	virtual int WindowOpen()
+	{
+		return SDL_GetAppState()&SDL_APPACTIVE;
+
+	}
+	
+	virtual void TakeScreenshot()
+	{
+		m_DoScreenshot = true;
+	}
+	
+	virtual void Swap()
+	{
+		if(m_DoScreenshot)
+		{
+			/* find filename */
+			char filename[128];
+			static int index = 1;
+
+			for(; index < 1000; index++)
+			{
+				IOHANDLE io;
+				str_format(filename, sizeof(filename), "screenshots/screenshot%04d.png", index);
+				io = engine_openfile(filename, IOFLAG_READ);
+				if(io)
+					io_close(io);
+				else
+					break;
+			}
+
+			ScreenshotDirect(filename);
+			m_DoScreenshot = false;
+		}
+		
+		{
+			static PERFORMACE_INFO pscope = {"glfwSwapBuffers", 0};
+			perf_start(&pscope);
+			SDL_GL_SwapBuffers();
+			perf_end();
+		}
+		
+		if(config.gfx_finish)
+			glFinish();		
+	}
+};
+
+extern IEngineGraphics *CreateEngineGraphics() { return new CGraphics_SDL(); }
diff --git a/src/engine/client/ec_gfx_text.c b/src/engine/client/ec_gfx_text.cpp
index de40e391..d17d1bed 100644
--- a/src/engine/client/ec_gfx_text.c
+++ b/src/engine/client/ec_gfx_text.cpp
@@ -1,7 +1,9 @@
 #include <base/system.h>
 #include <string.h>
 #include <engine/e_client_interface.h>
+#include <engine/client/graphics.h>
 
+extern IEngineGraphics *Graphics();
 
 #ifdef CONF_PLATFORM_MACOSX
 	#include <OpenGL/gl.h>
@@ -138,8 +140,7 @@ typedef struct FONT
 
 static int font_get_index(int pixelsize)
 {
-	int i;
-	for(i = 0; i < NUM_FONT_SIZES; i++)
+	for(unsigned i = 0; i < NUM_FONT_SIZES; i++)
 	{
 		if(font_sizes[i] >= pixelsize)
 			return i;
@@ -150,8 +151,7 @@ static int font_get_index(int pixelsize)
 
 FONT *gfx_font_load(const char *filename)
 {
-	int i;
-	FONT *font = mem_alloc(sizeof(FONT), 1);
+	FONT *font = (FONT *)mem_alloc(sizeof(FONT), 1);
 	
 	mem_zero(font, sizeof(*font));
 	str_copy(font->filename, filename, sizeof(font->filename));
@@ -162,7 +162,7 @@ FONT *gfx_font_load(const char *filename)
 		return NULL;
 	}
 
-	for(i = 0; i < NUM_FONT_SIZES; i++)
+	for(unsigned i = 0; i < NUM_FONT_SIZES; i++)
 		font->sizes[i].font_size = -1;
 		
 	return font;
@@ -484,6 +484,7 @@ static float font_kerning(FONT *font, int left, int right)
 	return (kerning.x>>6);
 }
 
+
 void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
 {
 	FONT *font = cursor->font;
@@ -503,10 +504,10 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
 	float size = cursor->font_size;
 
 	/* to correct coords, convert to screen coords, round, and convert back */
-	gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1);
+	Graphics()->GetScreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1);
 	
-	fake_to_screen_x = (gfx_screenwidth()/(screen_x1-screen_x0));
-	fake_to_screen_y = (gfx_screenheight()/(screen_y1-screen_y0));
+	fake_to_screen_x = (Graphics()->ScreenWidth()/(screen_x1-screen_x0));
+	fake_to_screen_y = (Graphics()->ScreenHeight()/(screen_y1-screen_y0));
 	actual_x = cursor->x * fake_to_screen_x;
 	actual_y = cursor->y * fake_to_screen_y;
 
@@ -554,11 +555,11 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
 			else
 				glBindTexture(GL_TEXTURE_2D, sizedata->textures[0]);
 
-			gfx_quads_begin();
+			Graphics()->QuadsBegin();
 			if (i == 0)
-				gfx_setcolor(0.0f, 0.0f, 0.0f, 0.3f*text_a);
+				Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.3f*text_a);
 			else
-				gfx_setcolor(text_r, text_g, text_b, text_a);
+				Graphics()->SetColor(text_r, text_g, text_b, text_a);
 		}
 
 		while(current < end)
@@ -629,8 +630,8 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
 				{
 					if(cursor->flags&TEXTFLAG_RENDER)
 					{
-						gfx_quads_setsubset(chr->uvs[0], chr->uvs[1], chr->uvs[2], chr->uvs[3]);
-						gfx_quads_drawTL(draw_x+chr->offset_x*size, draw_y+chr->offset_y*size, chr->width*size, chr->height*size);
+						Graphics()->QuadsSetSubset(chr->uvs[0], chr->uvs[1], chr->uvs[2], chr->uvs[3]);
+						Graphics()->QuadsDrawTL(draw_x+chr->offset_x*size, draw_y+chr->offset_y*size, chr->width*size, chr->height*size);
 					}
 
 					advance = chr->advance_x + font_kerning(font, character, nextcharacter)/size;
@@ -658,7 +659,7 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
 		}
 
 		if(cursor->flags&TEXTFLAG_RENDER)
-			gfx_quads_end();
+			Graphics()->QuadsEnd();
 	}
 
 	cursor->x = draw_x;
diff --git a/src/engine/client/ec_inp.c b/src/engine/client/ec_inp.cpp
index 495614d6..cf956471 100644
--- a/src/engine/client/ec_inp.c
+++ b/src/engine/client/ec_inp.cpp
@@ -5,6 +5,7 @@
 #include <base/system.h>
 #include <engine/e_client_interface.h>
 #include <engine/e_config.h>
+#include <engine/client/graphics.h>
 
 static struct
 {
@@ -20,6 +21,9 @@ static int input_grabbed = 0;
 static unsigned int last_release = 0;
 static unsigned int release_delta = -1;
 
+// TODO: Refactor: Remove this
+extern IEngineGraphics *Graphics();
+
 void inp_mouse_relative(int *x, int *y)
 {
 	int nx = 0, ny = 0;
@@ -32,8 +36,8 @@ void inp_mouse_relative(int *x, int *y)
 		if(input_grabbed)
 		{
 			SDL_GetMouseState(&nx,&ny);
-			SDL_WarpMouse(gfx_screenwidth()/2,gfx_screenheight()/2);
-			nx -= gfx_screenwidth()/2; ny -= gfx_screenheight()/2;
+			SDL_WarpMouse(Graphics()->ScreenWidth()/2,Graphics()->ScreenHeight()/2);
+			nx -= Graphics()->ScreenWidth()/2; ny -= Graphics()->ScreenHeight()/2;
 		}
 	}
 
@@ -138,10 +142,10 @@ void inp_update()
 {
 	int i;
 	
-	if(input_grabbed && !gfx_window_active())
+	if(input_grabbed && !Graphics()->WindowActive())
 		inp_mouse_mode_absolute();
 
-	/*if(!input_grabbed && gfx_window_active())
+	/*if(!input_grabbed && Graphics()->WindowActive())
 		inp_mouse_mode_relative();*/
 	
 	/* clear and begin count on the other one */
diff --git a/src/engine/client/ec_snd.c b/src/engine/client/ec_snd.cpp
index ac41ec59..3baea982 100644
--- a/src/engine/client/ec_snd.c
+++ b/src/engine/client/ec_snd.cpp
@@ -1,12 +1,15 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 #include <base/system.h>
 #include <engine/e_client_interface.h>
+#include <engine/client/graphics.h>
 #include <engine/e_config.h>
 #include <engine/e_engine.h>
 
 #include "SDL.h"
 
-#include <engine/external/wavpack/wavpack.h>
+extern "C" { // wavpack
+	#include <engine/external/wavpack/wavpack.h>
+}
 #include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
@@ -140,7 +143,6 @@ static int iabs(int i)
 static void mix(short *final_out, unsigned frames)
 {
 	int mix_buffer[MAX_FRAMES*2] = {0};
-	int i, s;
 	int master_vol;
 
 	/* aquire lock while we are mixing */
@@ -148,7 +150,7 @@ static void mix(short *final_out, unsigned frames)
 	
 	master_vol = sound_volume;
 	
-	for(i = 0; i < NUM_VOICES; i++)
+	for(unsigned i = 0; i < NUM_VOICES; i++)
 	{
 		if(voices[i].snd)
 		{
@@ -160,7 +162,7 @@ static void mix(short *final_out, unsigned frames)
 			short *in_l = &v->snd->data[v->tick*step];
 			short *in_r = &v->snd->data[v->tick*step+1];
 			
-			int end = v->snd->num_frames-v->tick;
+			unsigned end = v->snd->num_frames-v->tick;
 
 			int rvol = v->channel->vol;
 			int lvol = v->channel->vol;
@@ -202,7 +204,7 @@ static void mix(short *final_out, unsigned frames)
 			}
 
 			/* process all frames */
-			for(s = 0; s < end; s++)
+			for(unsigned s = 0; s < end; s++)
 			{
 				*out++ += (*in_l)*lvol;
 				*out++ += (*in_r)*rvol;
@@ -225,7 +227,7 @@ static void mix(short *final_out, unsigned frames)
 	{
 		/* clamp accumulated values */
 		/* TODO: this seams slow */
-		for(i = 0; i < frames; i++)
+		for(unsigned i = 0; i < frames; i++)
 		{
 			int j = i<<1;
 			int vl = ((mix_buffer[j]*master_vol)/101)>>8;
@@ -281,12 +283,16 @@ int snd_init()
 	return 0;
 }
 
+// TODO: Refactor: Remove this
+extern IEngineGraphics *Graphics();
+
+
 int snd_update()
 {
 	/* update volume */
 	int wanted_volume = config.snd_volume;
 	
-	if(!gfx_window_active() && config.snd_nonactive_mute)
+	if(!Graphics()->WindowActive() && config.snd_nonactive_mute)
 		wanted_volume = 0;
 	
 	if(wanted_volume != sound_volume)
@@ -332,7 +338,7 @@ static void rate_convert(int sid)
 
 	/* allocate new data */
 	num_frames = (int)((snd->num_frames/(float)snd->rate)*mixing_rate);
-	new_data = mem_alloc(num_frames*snd->channels*sizeof(short), 1);
+	new_data = (short *)mem_alloc(num_frames*snd->channels*sizeof(short), 1);
 	
 	for(i = 0; i < num_frames; i++)
 	{
diff --git a/src/engine/client/ec_srvbrowse.c b/src/engine/client/ec_srvbrowse.cpp
index 4a85e778..1b04937a 100644
--- a/src/engine/client/ec_srvbrowse.c
+++ b/src/engine/client/ec_srvbrowse.cpp
@@ -11,7 +11,7 @@
 #include <string.h>
 #include <stdlib.h>
 
-extern NETCLIENT *net;
+extern CNetClient m_NetClient;
 
 
 /* ------ server browse ---- */
@@ -150,7 +150,7 @@ static void client_serverbrowse_filter()
 		if(sorted_serverlist)
 			mem_free(sorted_serverlist);
 		num_sorted_servers_capacity = num_servers;
-		sorted_serverlist = mem_alloc(num_sorted_servers_capacity*sizeof(int), 1);
+		sorted_serverlist = (int *)mem_alloc(num_sorted_servers_capacity*sizeof(int), 1);
 	}
 	
 	/* filter the servers */
@@ -401,7 +401,7 @@ SERVERENTRY *client_serverbrowse_add(NETADDR *addr)
 	{
 		SERVERENTRY **newlist;
 		num_server_capacity += 100;
-		newlist = mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1);
+		newlist = (SERVERENTRY **)mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1);
 		mem_copy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*));
 		mem_free(serverlist);
 		serverlist = newlist;
@@ -506,28 +506,28 @@ void client_serverbrowse_refresh(int type)
 
 	if(type == BROWSETYPE_LAN)
 	{
-		unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1];
-		NETCHUNK packet;
+		unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1];
+		CNetChunk Packet;
 		int i;
 		
-		mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
-		buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;
+		mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
+		Buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;
 			
-		packet.client_id = -1;
-		mem_zero(&packet, sizeof(packet));
-		packet.address.ip[0] = 255;
-		packet.address.ip[1] = 255;
-		packet.address.ip[2] = 255;
-		packet.address.ip[3] = 255;
-		packet.flags = NETSENDFLAG_CONNLESS;
-		packet.data_size = sizeof(buffer);
-		packet.data = buffer;
+		Packet.m_ClientID = -1;
+		mem_zero(&Packet, sizeof(Packet));
+		Packet.m_Address.ip[0] = 255;
+		Packet.m_Address.ip[1] = 255;
+		Packet.m_Address.ip[2] = 255;
+		Packet.m_Address.ip[3] = 255;
+		Packet.m_Flags = NETSENDFLAG_CONNLESS;
+		Packet.m_DataSize = sizeof(Buffer);
+		Packet.m_pData = Buffer;
 		broadcast_time = time_get();
 
 		for(i = 8303; i <= 8310; i++)
 		{
-			packet.address.port = i;
-			netclient_send(net, &packet);
+			Packet.m_Address.port = i;
+			m_NetClient.Send(&Packet);
 		}
 
 		if(config.debug)
@@ -537,8 +537,7 @@ void client_serverbrowse_refresh(int type)
 		need_refresh = 1;
 	else if(type == BROWSETYPE_FAVORITES)
 	{
-		int i;
-		for(i = 0; i < num_favorite_servers; i++)
+		for(int i = 0; i < num_favorite_servers; i++)
 			client_serverbrowse_set(&favorite_servers[i], BROWSESET_FAV_ADD, -1, 0);
 	}
 }
@@ -546,7 +545,7 @@ void client_serverbrowse_refresh(int type)
 static void client_serverbrowse_request_impl(NETADDR *addr, SERVERENTRY *entry)
 {
 	/*unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1];*/
-	NETCHUNK p;
+	CNetChunk Packet;
 
 	if(config.debug)
 	{
@@ -558,17 +557,17 @@ static void client_serverbrowse_request_impl(NETADDR *addr, SERVERENTRY *entry)
 	/*mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
 	buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;*/
 	
-	p.client_id = -1;
-	p.address = *addr;
-	p.flags = NETSENDFLAG_CONNLESS;
+	Packet.m_ClientID = -1;
+	Packet.m_Address = *addr;
+	Packet.m_Flags = NETSENDFLAG_CONNLESS;
 	/*p.data_size = sizeof(buffer);
 	p.data = buffer;
 	netclient_send(net, &p);*/
 
 	/* send old requtest style aswell */	
-	p.data_size = sizeof(SERVERBROWSE_OLD_GETINFO);
-	p.data = SERVERBROWSE_OLD_GETINFO;
-	netclient_send(net, &p);
+	Packet.m_DataSize = sizeof(SERVERBROWSE_OLD_GETINFO);
+	Packet.m_pData = SERVERBROWSE_OLD_GETINFO;
+	m_NetClient.Send(&Packet);
 	
 	if(entry)
 		entry->request_time = time_get();
@@ -591,16 +590,16 @@ void client_serverbrowse_update()
 	if(need_refresh && !mastersrv_refreshing())
 	{
 		NETADDR addr;
-		NETCHUNK p;
+		CNetChunk Packet;
 		int i;
 		
 		need_refresh = 0;
 		
-		mem_zero(&p, sizeof(p));
-		p.client_id = -1;
-		p.flags = NETSENDFLAG_CONNLESS;
-		p.data_size = sizeof(SERVERBROWSE_GETLIST);
-		p.data = SERVERBROWSE_GETLIST;
+		mem_zero(&Packet, sizeof(Packet));
+		Packet.m_ClientID = -1;
+		Packet.m_Flags = NETSENDFLAG_CONNLESS;
+		Packet.m_DataSize = sizeof(SERVERBROWSE_GETLIST);
+		Packet.m_pData = SERVERBROWSE_GETLIST;
 		
 		for(i = 0; i < MAX_MASTERSERVERS; i++)
 		{
@@ -608,8 +607,8 @@ void client_serverbrowse_update()
 			if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3])
 				continue;
 			
-			p.address = addr;
-			netclient_send(net, &p);
+			Packet.m_Address = addr;
+			m_NetClient.Send(&Packet);
 		}
 
 		if(config.debug)
diff --git a/src/engine/e_client_interface.h b/src/engine/e_client_interface.h
index 7ec5c93d..079eabca 100644
--- a/src/engine/e_client_interface.h
+++ b/src/engine/e_client_interface.h
@@ -2,10 +2,6 @@
 #ifndef ENGINE_CLIENT_INTERFACE_H
 #define ENGINE_CLIENT_INTERFACE_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 #include "e_if_other.h"
 #include "e_if_client.h"
 #include "e_if_snd.h"
@@ -17,8 +13,4 @@ extern "C" {
 #include "e_console.h"
 #include "e_config.h"
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/src/engine/e_common_interface.h b/src/engine/e_common_interface.h
index 498bb6b4..9c95b48b 100644
--- a/src/engine/e_common_interface.h
+++ b/src/engine/e_common_interface.h
@@ -2,15 +2,7 @@
 #ifndef ENGINE_COMMON_INTERFACE_H
 #define ENGINE_COMMON_INTERFACE_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 #include "e_if_other.h"
 #include "e_if_msg.h"
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/src/engine/e_compression.c b/src/engine/e_compression.cpp
index 6a534feb..f4d6e0c0 100644
--- a/src/engine/e_compression.c
+++ b/src/engine/e_compression.cpp
@@ -84,65 +84,3 @@ long intpack_compress(const void *src_, int size, void *dst_)
 	return (long)(dst-(unsigned char *)dst_);
 }
 
-
-/* */
-long zerobit_compress(const void *src_, int size, void *dst_)
-{
-	unsigned char *src = (unsigned char *)src_;
-	unsigned char *dst = (unsigned char *)dst_;
-	
-	while(size)
-	{
-		unsigned char bit = 0x80;
-		unsigned char mask = 0;
-		int dst_move = 1;
-		int chunk = size < 8 ? size : 8;
-		int b;
-		size -= chunk;
-		
-		for(b = 0; b < chunk; b++, bit>>=1)
-		{
-			if(*src)
-			{
-				dst[dst_move] = *src;
-				mask |= bit;
-				dst_move++;
-			}
-			
-			src++;
-		}
-		
-		*dst = mask;
-		dst += dst_move;
-	}
-	
-	return (long)(dst-(unsigned char *)dst_);
-}
-
-long zerobit_decompress(const void *src_, int size, void *dst_)
-{
-	unsigned char *src = (unsigned char *)src_;
-	unsigned char *dst = (unsigned char *)dst_;
-	unsigned char *end = src + size;
-	
-	while(src < end)
-	{
-		unsigned char bit = 0x80;
-		unsigned char mask = *src++;
-		int b;
-		
-		for(b = 0; b < 8; b++, bit>>=1)
-		{
-			if(mask&bit)
-				*dst++ = *src++;
-			else
-				*dst++ = 0;
-		}
-
-		if(src > end)
-			return -1;
-	}
-	
-	return (long)(dst-(unsigned char *)dst_);
-}
-
diff --git a/src/engine/e_compression.h b/src/engine/e_compression.h
index 39ffd596..be5bf78f 100644
--- a/src/engine/e_compression.h
+++ b/src/engine/e_compression.h
@@ -5,7 +5,3 @@ unsigned char *vint_pack(unsigned char *dst, int i);
 const unsigned char *vint_unpack(const unsigned char *src, int *inout);
 long intpack_compress(const void *src, int size, void *dst);
 long intpack_decompress(const void *src, int size, void *dst);
-
-/* zerobit packing */
-long zerobit_compress(const void *src, int size, void *dst);
-long zerobit_decompress(const void *src, int size, void *dst);
diff --git a/src/engine/e_config.c b/src/engine/e_config.cpp
index 67a4c81a..67a4c81a 100644
--- a/src/engine/e_config.c
+++ b/src/engine/e_config.cpp
diff --git a/src/engine/e_config.h b/src/engine/e_config.h
index 965e08f0..6ca7a8ee 100644
--- a/src/engine/e_config.h
+++ b/src/engine/e_config.h
@@ -2,10 +2,6 @@
 #ifndef _CONFIG_H
 #define _CONFIG_H
 
-#ifdef __cplusplus
-extern "C"{
-#endif
-
 typedef struct
 { 
     #define MACRO_CONFIG_INT(name,def,min,max,save,desc) int name;
@@ -45,8 +41,4 @@ typedef void (*CONFIG_STR_SETTER)(CONFIGURATION *c, const char *str);
 #undef MACRO_CONFIG_INT
 #undef MACRO_CONFIG_STR
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/src/engine/e_console.c b/src/engine/e_console.cpp
index 5c0f37f3..c641289d 100644
--- a/src/engine/e_console.c
+++ b/src/engine/e_console.cpp
@@ -151,7 +151,7 @@ static int console_parse_args(PARSE_RESULT *result, const char *format)
 	return error;
 }
 
-const char *console_arg_string(void *res, int index)
+const char *console_arg_string(void *res, unsigned index)
 {
 	PARSE_RESULT *result = (PARSE_RESULT *)res;
 	if (index < 0 || index >= result->num_args)
@@ -159,7 +159,7 @@ const char *console_arg_string(void *res, int index)
 	return result->args[index];
 }
 
-int console_arg_int(void *res, int index)
+int console_arg_int(void *res, unsigned index)
 {
 	PARSE_RESULT *result = (PARSE_RESULT *)res;
 	if (index < 0 || index >= result->num_args)
@@ -167,7 +167,7 @@ int console_arg_int(void *res, int index)
 	return atoi(result->args[index]);
 }
 
-float console_arg_float(void *res, int index)
+float console_arg_float(void *res, unsigned index)
 {
 	PARSE_RESULT *result = (PARSE_RESULT *)res;
 	if (index < 0 || index >= result->num_args)
@@ -354,7 +354,7 @@ struct EXECFILE
 void console_execute_file(const char *filename)
 {
 	static struct EXECFILE *first = 0;
-	struct EXECFILE this;
+	struct EXECFILE this_file;
 	struct EXECFILE *cur;
 	struct EXECFILE *prev;
 
@@ -365,9 +365,9 @@ void console_execute_file(const char *filename)
 	
 	/* push this one to the stack */
 	prev = first;
-	this.filename = filename;
-	this.next = first;
-	first = &this;
+	this_file.filename = filename;
+	this_file.next = first;
+	first = &this_file;
 	
 	/* execute file */
 	console_execute_file_real(filename);
diff --git a/src/engine/e_console.h b/src/engine/e_console.h
index b37931b2..a45bac10 100644
--- a/src/engine/e_console.h
+++ b/src/engine/e_console.h
@@ -1,10 +1,6 @@
 #ifndef _CONSOLE_H
 #define _CONSOLE_H
 
-#ifdef __cplusplus
-extern "C"{
-#endif
-
 typedef void (*CONSOLE_CALLBACK)(void *result, void *user_data);
 typedef void (*CONSOLE_CHAIN_CALLBACK)(void *result, void *user_data, CONSOLE_CALLBACK cb, void *cbuser);
 
@@ -42,15 +38,11 @@ void console_register_print_callback(void (*callback)(const char *, void *user_d
 int console_result_int(void *result, int index, int *i);
 int console_result_float(void *result, int index, float *f);*/
 
-const char *console_arg_string(void *result, int index);
-int console_arg_int(void *result, int index);
-float console_arg_float(void *result, int index);
+const char *console_arg_string(void *result, unsigned index);
+int console_arg_int(void *result, unsigned index);
+float console_arg_float(void *result, unsigned index);
 int console_arg_num(void *result);
 
 #define MACRO_REGISTER_COMMAND(name, params, flags, func, ptr, help) { static COMMAND cmd = { name, params, flags, func, ptr, help, 0x0}; console_register(&cmd); }
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/src/engine/e_datafile.c b/src/engine/e_datafile.cpp
index 0317f9d0..0317f9d0 100644
--- a/src/engine/e_datafile.c
+++ b/src/engine/e_datafile.cpp
diff --git a/src/engine/e_demorec.c b/src/engine/e_demorec.cpp
index e66173e4..1bab1b8d 100644
--- a/src/engine/e_demorec.c
+++ b/src/engine/e_demorec.cpp
@@ -1,4 +1,3 @@
-
 #include <base/system.h>
 #include "e_demorec.h"
 #include "e_memheap.h"
@@ -14,7 +13,7 @@ static const unsigned char header_marker[8] = {'T', 'W', 'D', 'E', 'M', 'O', 0,
 /* Record */
 static int record_lasttickmarker = -1;
 static int record_lastkeyframe;
-static unsigned char record_lastsnapshotdata[MAX_SNAPSHOT_SIZE];
+static unsigned char record_lastsnapshotdata[CSnapshot::MAX_SIZE];
 
 int demorec_isrecording() { return record_file != 0; }
 
@@ -121,7 +120,7 @@ static void demorec_record_write(int type, const void *data, int size)
 	while(size&3)
 		buffer2[size++] = 0;
 	size = intpack_compress(buffer2, size, buffer); /* buffer2 -> buffer */
-	size = netcommon_compress(buffer, size, buffer2, sizeof(buffer2)); /* buffer -> buffer2 */
+	size = CNetBase::Compress(buffer, size, buffer2, sizeof(buffer2)); /* buffer -> buffer2 */
 	
 	
 	chunk[0] = ((type&0x3)<<5);
@@ -166,13 +165,13 @@ void demorec_record_snapshot(int tick, const void *data, int size)
 	else
 	{
 		/* create delta, prepend tick */
-		char delta_data[MAX_SNAPSHOT_SIZE+sizeof(int)];
+		char delta_data[CSnapshot::MAX_SIZE+sizeof(int)];
 		int delta_size;
 
 		/* write tickmarker */
 		demorec_record_write_tickmarker(tick, 0);
 		
-		delta_size = snapshot_create_delta((SNAPSHOT*)record_lastsnapshotdata, (SNAPSHOT*)data, &delta_data);
+		delta_size = CSnapshot::CreateDelta((CSnapshot*)record_lastsnapshotdata, (CSnapshot*)data, &delta_data);
 		if(delta_size)
 		{
 			/* record delta */
@@ -217,7 +216,7 @@ static DEMOREC_PLAYCALLBACK play_callback_message = 0;
 static KEYFRAME *keyframes = 0;
 
 static DEMOREC_PLAYBACKINFO playbackinfo;
-static unsigned char playback_lastsnapshotdata[MAX_SNAPSHOT_SIZE];
+static unsigned char playback_lastsnapshotdata[CSnapshot::MAX_SIZE];
 static int playback_lastsnapshotdata_size = -1;
 
 
@@ -316,7 +315,7 @@ static void scan_file()
 				KEYFRAME_SEARCH *key;
 				
 				/* save the position */
-				key = memheap_allocate(heap, sizeof(KEYFRAME_SEARCH));
+				key = (KEYFRAME_SEARCH *)memheap_allocate(heap, sizeof(KEYFRAME_SEARCH));
 				key->frame.filepos = current_pos;
 				key->frame.tick = chunk_tick;
 				key->next = 0;
@@ -349,10 +348,10 @@ static void scan_file()
 
 static void do_tick()
 {
-	static char compresseddata[MAX_SNAPSHOT_SIZE];
-	static char decompressed[MAX_SNAPSHOT_SIZE];
-	static char data[MAX_SNAPSHOT_SIZE];
-	int chunk_size, chunk_type, chunk_tick;
+	static char compresseddata[CSnapshot::MAX_SIZE];
+	static char decompressed[CSnapshot::MAX_SIZE];
+	static char data[CSnapshot::MAX_SIZE];
+	int chunk_type, chunk_tick, chunk_size;
 	int data_size;
 	int got_snapshot = 0;
 
@@ -374,7 +373,7 @@ static void do_tick()
 		/* read the chunk */
 		if(chunk_size)
 		{
-			if(io_read(play_file, compresseddata, chunk_size) != chunk_size)
+			if(io_read(play_file, compresseddata, chunk_size) != (unsigned)chunk_size)
 			{
 				/* stop on error or eof */
 				dbg_msg("demorec", "error reading chunk");
@@ -382,7 +381,7 @@ static void do_tick()
 				break;
 			}
 			
-			data_size = netcommon_decompress(compresseddata, chunk_size, decompressed, sizeof(decompressed));
+			data_size = CNetBase::Decompress(compresseddata, chunk_size, decompressed, sizeof(decompressed));
 			if(data_size < 0)
 			{
 				/* stop on error or eof */
@@ -404,11 +403,11 @@ static void do_tick()
 		if(chunk_type == CHUNKTYPE_DELTA)
 		{
 			/* process delta snapshot */
-			static char newsnap[MAX_SNAPSHOT_SIZE];
+			static char newsnap[CSnapshot::MAX_SIZE];
 			
 			got_snapshot = 1;
 			
-			data_size = snapshot_unpack_delta((SNAPSHOT*)playback_lastsnapshotdata, (SNAPSHOT*)newsnap, data, data_size);
+			data_size = CSnapshot::UnpackDelta((CSnapshot*)playback_lastsnapshotdata, (CSnapshot*)newsnap, data, data_size);
 			
 			if(data_size >= 0)
 			{
@@ -625,7 +624,7 @@ int demorec_playback_update()
 	return 0;
 }
 
-int demorec_playback_stop(const char *filename)
+int demorec_playback_stop()
 {
 	if(!play_file)
 		return -1;
@@ -637,3 +636,5 @@ int demorec_playback_stop(const char *filename)
 	keyframes = 0;
 	return 0;
 }
+
+
diff --git a/src/engine/e_demorec.h b/src/engine/e_demorec.h
index 482debb4..9716b463 100644
--- a/src/engine/e_demorec.h
+++ b/src/engine/e_demorec.h
@@ -2,10 +2,6 @@
 #ifndef _DEMOREC_H
 #define _DEMOREC_H
 
-#ifdef __cplusplus
-extern "C"{
-#endif
-
 typedef struct DEMOREC_HEADER
 {
 	char marker[8];
@@ -70,8 +66,4 @@ const DEMOREC_PLAYBACKINFO *demorec_playback_info();
 int demorec_isplaying();
 int demorec_playback_stop();
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/src/engine/e_engine.c b/src/engine/e_engine.cpp
index 7afbb2ce..4475478a 100644
--- a/src/engine/e_engine.c
+++ b/src/engine/e_engine.cpp
@@ -25,7 +25,7 @@ static void con_dbg_dumpmem(void *result, void *user_data)
 
 static void con_dbg_lognetwork(void *result, void *user_data)
 {
-	netcommon_openlog("network_sent.dat", "network_recv.dat");
+	CNetBase::OpenLog("network_sent.dat", "network_recv.dat");
 }
 
 
@@ -55,7 +55,7 @@ void engine_init(const char *appname)
 
 	/* init the network */
 	net_init();
-	netcommon_init();
+	CNetBase::Init();
 	
 	/* create storage location */
 	{
@@ -246,7 +246,7 @@ static IOHANDLE config_file = 0;
 
 int engine_config_write_start()
 {
-	config_save("settings.cfg");
+	config_save();
 	config_file = engine_openfile("settings.cfg", IOFLAG_WRITE);
 	if(config_file == 0)
 		return -1;
diff --git a/src/engine/e_huffman.c b/src/engine/e_huffman.cpp
index 43914010..43914010 100644
--- a/src/engine/e_huffman.c
+++ b/src/engine/e_huffman.cpp
diff --git a/src/engine/e_huffman.h b/src/engine/e_huffman.h
index c4e20223..635c74a1 100644
--- a/src/engine/e_huffman.h
+++ b/src/engine/e_huffman.h
@@ -1,10 +1,6 @@
 #ifndef __HUFFMAN_HEADER__
 #define __HUFFMAN_HEADER__
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 enum
 {
 	HUFFMAN_EOF_SYMBOL = 256,
@@ -84,8 +80,4 @@ int huffman_compress(HUFFMAN_STATE *huff, const void *input, int input_size, voi
 */
 int huffman_decompress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size);
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* __HUFFMAN_HEADER__ */
diff --git a/src/engine/e_if_other.h b/src/engine/e_if_other.h
index 5c839750..3fbbd81e 100644
--- a/src/engine/e_if_other.h
+++ b/src/engine/e_if_other.h
@@ -69,11 +69,12 @@ void perf_init();
 void perf_next();
 void perf_start(PERFORMACE_INFO *info);
 void perf_end();
-void perf_dump();
+void perf_dump(PERFORMACE_INFO *top);
 
 int gfx_init();
 void gfx_shutdown();
 void gfx_swap();
+
 int gfx_window_active();
 int gfx_window_open();
 
diff --git a/src/engine/e_jobs.c b/src/engine/e_jobs.cpp
index e87b395a..e87b395a 100644
--- a/src/engine/e_jobs.c
+++ b/src/engine/e_jobs.cpp
diff --git a/src/engine/e_keynames.c b/src/engine/e_keynames.cpp
index c81744b9..c81744b9 100644
--- a/src/engine/e_keynames.c
+++ b/src/engine/e_keynames.cpp
diff --git a/src/engine/e_linereader.c b/src/engine/e_linereader.cpp
index 57ba9a85..57ba9a85 100644
--- a/src/engine/e_linereader.c
+++ b/src/engine/e_linereader.cpp
diff --git a/src/engine/e_map.c b/src/engine/e_map.cpp
index a2048310..a2048310 100644
--- a/src/engine/e_map.c
+++ b/src/engine/e_map.cpp
diff --git a/src/engine/e_memheap.c b/src/engine/e_memheap.cpp
index 7589e2c1..fe157e86 100644
--- a/src/engine/e_memheap.c
+++ b/src/engine/e_memheap.cpp
@@ -1,18 +1,18 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 #include <base/system.h>
 
-typedef struct CHUNK_t
+struct CHUNK
 {
 	char *memory;
 	char *current;
 	char *end;
-	struct CHUNK_t *next;
-} CHUNK;
+	CHUNK *next;
+};
 
-typedef struct 
+struct HEAP
 {
 	CHUNK *current;
-} HEAP;
+};
 
 /* how large each chunk should be */
 static const int chunksize = 1024*64;
diff --git a/src/engine/e_memheap.h b/src/engine/e_memheap.h
index af3c0b29..b4391ec7 100644
--- a/src/engine/e_memheap.h
+++ b/src/engine/e_memheap.h
@@ -1,6 +1,6 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 
-typedef struct HEAP_t HEAP;
+struct HEAP;
 HEAP *memheap_create();
 void memheap_destroy(HEAP *heap);
 void *memheap_allocate(HEAP *heap, unsigned int size);
diff --git a/src/engine/e_msg.c b/src/engine/e_msg.cpp
index f9efc2bf..999a0ff0 100644
--- a/src/engine/e_msg.c
+++ b/src/engine/e_msg.cpp
@@ -3,17 +3,17 @@
 #include "e_packer.h"
 
 /* message packing */
-static PACKER msg_packer;
+static CPacker msg_packer;
 static MSG_INFO pack_info;
 static int packer_failed = 0;
 
-void msg_pack_int(int i) { packer_add_int(&msg_packer, i); }
-void msg_pack_string(const char *p, int limit) { packer_add_string(&msg_packer, p, limit); }
-void msg_pack_raw(const void *data, int size) { packer_add_raw(&msg_packer, (const unsigned char *)data, size); }
+void msg_pack_int(int i) { msg_packer.AddInt(i); }
+void msg_pack_string(const char *p, int limit) { msg_packer.AddString(p, limit); }
+void msg_pack_raw(const void *data, int size) { msg_packer.AddRaw((const unsigned char *)data, size); }
 
 void msg_pack_start_system(int msg, int flags)
 {
-	packer_reset(&msg_packer);
+	msg_packer.Reset();
 	pack_info.msg = (msg<<1)|1;
 	pack_info.flags = flags;
 	packer_failed = 0;
@@ -23,7 +23,7 @@ void msg_pack_start_system(int msg, int flags)
 
 void msg_pack_start(int msg, int flags)
 {
-	packer_reset(&msg_packer);
+	msg_packer.Reset();
 	pack_info.msg = msg<<1;
 	pack_info.flags = flags;
 	packer_failed = 0;
@@ -33,7 +33,7 @@ void msg_pack_start(int msg, int flags)
 
 void msg_pack_end()
 {
-	if(msg_packer.error)
+	if(msg_packer.Error())
 	{
 		packer_failed = 1;
 		pack_info.size = 0;
@@ -41,8 +41,8 @@ void msg_pack_end()
 	}
 	else
 	{
-		pack_info.size = packer_size(&msg_packer);
-		pack_info.data = packer_data(&msg_packer);
+		pack_info.size = msg_packer.Size();
+		pack_info.data = msg_packer.Data();
 	}
 }
 
@@ -54,17 +54,17 @@ const MSG_INFO *msg_get_info()
 }
 
 /* message unpacking */
-static UNPACKER msg_unpacker;
+static CUnpacker msg_unpacker;
 int msg_unpack_start(const void *data, int data_size, int *system)
 {
 	int msg;
-	unpacker_reset(&msg_unpacker, (const unsigned char *)data, data_size);
+	msg_unpacker.Reset((const unsigned char *)data, data_size);
 	msg = msg_unpack_int();
 	*system = msg&1;
 	return msg>>1;
 }
 
-int msg_unpack_int() { return unpacker_get_int(&msg_unpacker); }
-const char *msg_unpack_string() { return unpacker_get_string(&msg_unpacker); }
-const unsigned char *msg_unpack_raw(int size)  { return unpacker_get_raw(&msg_unpacker, size); }
-int msg_unpack_error() { return msg_unpacker.error; }
+int msg_unpack_int() { return msg_unpacker.GetInt(); }
+const char *msg_unpack_string() { return msg_unpacker.GetString(); }
+const unsigned char *msg_unpack_raw(int size)  { return msg_unpacker.GetRaw(size); }
+int msg_unpack_error() { return msg_unpacker.Error(); }
diff --git a/src/engine/e_network.c b/src/engine/e_network.c
deleted file mode 100644
index 3c32de1d..00000000
--- a/src/engine/e_network.c
+++ /dev/null
@@ -1,351 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <base/system.h>
-
-#include <string.h> /* strlen */
-
-#include "e_config.h"
-#include "e_engine.h"
-#include "e_network.h"
-#include "e_network_internal.h"
-#include "e_huffman.h"
-
-void recvinfo_clear(NETRECVINFO *info)
-{
-	info->valid = 0;
-}
-
-void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid)
-{
-	info->addr = *addr;
-	info->conn = conn;
-	info->client_id = cid;
-	info->current_chunk = 0;
-	info->valid = 1;
-}
-
-
-int seq_in_backroom(int seq, int ack)
-{
-	int bottom = (ack-NET_MAX_SEQUENCE/2);
-	if(bottom < 0)
-	{
-		if(seq <= ack)
-			return 1;
-		if(seq >= (bottom + NET_MAX_SEQUENCE))
-			return 1;
-	}
-	else
-	{
-		if(seq <= ack && seq >= bottom)
-			return 1;
-	}
-	
-	return 0;
-}
-
-/* TODO: rename this function */
-int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk)
-{
-	NETCHUNKHEADER header;
-	unsigned char *end = info->data.chunk_data + info->data.data_size;
-	int i;
-	
-	while(1)
-	{
-		unsigned char *data = info->data.chunk_data;
-		
-		/* check for old data to unpack */
-		if(!info->valid || info->current_chunk >= info->data.num_chunks)
-		{
-			recvinfo_clear(info);
-			return 0;
-		}
-		
-		/* TODO: add checking here so we don't read too far */
-		for(i = 0; i < info->current_chunk; i++)
-		{
-			data = unpack_chunk_header(data, &header);
-			data += header.size;
-		}
-		
-		/* unpack the header */	
-		data = unpack_chunk_header(data, &header);
-		info->current_chunk++;
-		
-		if(data+header.size > end)
-		{
-			recvinfo_clear(info);
-			return 0;
-		}
-		
-		/* handle sequence stuff */
-		if(info->conn && (header.flags&NET_CHUNKFLAG_VITAL))
-		{
-			if(header.sequence == (info->conn->ack+1)%NET_MAX_SEQUENCE)
-			{
-				/* in sequence */
-				info->conn->ack = (info->conn->ack+1)%NET_MAX_SEQUENCE;
-			}
-			else
-			{
-				/* old packet that we already got */
-				if(seq_in_backroom(header.sequence, info->conn->ack))
-					continue;
-
-				/* out of sequence, request resend */
-				if(config.debug)
-					dbg_msg("conn", "asking for resend %d %d", header.sequence, (info->conn->ack+1)%NET_MAX_SEQUENCE);
-				conn_want_resend(info->conn);
-				continue; /* take the next chunk in the packet */
-			}
-		}
-		
-		/* fill in the info */
-		chunk->client_id = info->client_id;
-		chunk->address = info->addr;
-		chunk->flags = 0;
-		chunk->data_size = header.size;
-		chunk->data = data;
-		return 1;
-	}
-}
-
-
-static IOHANDLE datalog_sent = 0;
-static IOHANDLE datalog_recv = 0;
-static HUFFMAN_STATE huffmanstate;
-
-#define COMPRESSION 1
-
-/* packs the data tight and sends it */
-void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size)
-{
-	unsigned char buffer[NET_MAX_PACKETSIZE];
-	buffer[0] = 0xff;
-	buffer[1] = 0xff;
-	buffer[2] = 0xff;
-	buffer[3] = 0xff;
-	buffer[4] = 0xff;
-	buffer[5] = 0xff;
-	mem_copy(&buffer[6], data, data_size);
-	net_udp_send(socket, addr, buffer, 6+data_size);
-}
-
-int netcommon_compress(const void *data, int data_size, void *output, int output_size)
-{
-	return huffman_compress(&huffmanstate, data, data_size, output, output_size);
-}
-
-int netcommon_decompress(const void *data, int data_size, void *output, int output_size)
-{
-	return huffman_decompress(&huffmanstate, data, data_size, output, output_size);
-}
-
-void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet)
-{
-	unsigned char buffer[NET_MAX_PACKETSIZE];
-	int compressed_size = -1;
-	int final_size = -1;
-
-	/* log the data */
-	if(datalog_sent)
-	{
-		int type = 1;
-		io_write(datalog_sent, &type, sizeof(type));
-		io_write(datalog_sent, &packet->data_size, sizeof(packet->data_size));
-		io_write(datalog_sent, &packet->chunk_data, packet->data_size);
-		io_flush(datalog_sent);
-	}
-	
-	/* compress if its enabled */
-	if(COMPRESSION)
-		compressed_size = huffman_compress(&huffmanstate, packet->chunk_data, packet->data_size, &buffer[3], NET_MAX_PACKETSIZE-4);
-
-	/* check if the compression was enabled, successful and good enough	*/
-	if(compressed_size > 0 && compressed_size < packet->data_size)
-	{
-		final_size = compressed_size;
-		packet->flags |= NET_PACKETFLAG_COMPRESSION;
-	}
-	else
-	{
-		/* use uncompressed data */
-		final_size = packet->data_size;
-		mem_copy(&buffer[3], packet->chunk_data, packet->data_size);
-		packet->flags &= ~NET_PACKETFLAG_COMPRESSION;
-	}
-
-	/* set header and send the packet if all things are good */
-	if(final_size >= 0)
-	{
-		final_size += NET_PACKETHEADERSIZE;
-		buffer[0] = ((packet->flags<<4)&0xf0)|((packet->ack>>8)&0xf);
-		buffer[1] = packet->ack&0xff;
-		buffer[2] = packet->num_chunks;
-		net_udp_send(socket, addr, buffer, final_size);
-
-		/* log raw socket data */
-		if(datalog_sent)
-		{
-			int type = 0;
-			io_write(datalog_sent, &type, sizeof(type));
-			io_write(datalog_sent, &final_size, sizeof(final_size));
-			io_write(datalog_sent, buffer, final_size);
-			io_flush(datalog_sent);
-		}
-	}
-}
-
-/* TODO: rename this function */
-int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet)
-{
-	/* check the size */
-	if(size < NET_PACKETHEADERSIZE || size > NET_MAX_PACKETSIZE)
-	{
-		dbg_msg("", "packet too small, %d", size);
-		return -1;
-	}
-
-	/* log the data */
-	if(datalog_recv)
-	{
-		int type = 0;
-		io_write(datalog_recv, &type, sizeof(type));
-		io_write(datalog_recv, &size, sizeof(size));
-		io_write(datalog_recv, buffer, size);
-		io_flush(datalog_recv);
-	}
-	
-	/* read the packet */
-	packet->flags = buffer[0]>>4;
-	packet->ack = ((buffer[0]&0xf)<<8) | buffer[1];
-	packet->num_chunks = buffer[2];
-	packet->data_size = size - NET_PACKETHEADERSIZE;
-
-	if(packet->flags&NET_PACKETFLAG_CONNLESS)
-	{
-		if(size < 6)
-		{
-			dbg_msg("", "connection less packet too small, %d", size);
-			return -1;
-		}
-			
-		packet->flags = NET_PACKETFLAG_CONNLESS;
-		packet->ack = 0;
-		packet->num_chunks = 0;
-		packet->data_size = size - 6;
-		mem_copy(packet->chunk_data, &buffer[6], packet->data_size);
-	}
-	else
-	{
-		if(packet->flags&NET_PACKETFLAG_COMPRESSION)
-			packet->data_size = huffman_decompress(&huffmanstate, &buffer[3], packet->data_size, packet->chunk_data, sizeof(packet->chunk_data));
-		else
-			mem_copy(packet->chunk_data, &buffer[3], packet->data_size);
-	}
-
-	/* check for errors */	
-	if(packet->data_size < 0)
-	{
-		if(config.debug)
-			dbg_msg("network", "error during packet decoding");
-		return -1;
-	}
-
-	/* log the data */
-	if(datalog_recv)
-	{
-		int type = 1;
-		io_write(datalog_recv, &type, sizeof(type));
-		io_write(datalog_recv, &packet->data_size, sizeof(packet->data_size));
-		io_write(datalog_recv, packet->chunk_data, packet->data_size);
-		io_flush(datalog_recv);
-	}
-		
-	/* return success */
-	return 0;
-}
-
-
-/* TODO: change the arguments of this function */
-unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence)
-{
-	data[0] = ((flags&3)<<6)|((size>>4)&0x3f);
-	data[1] = (size&0xf);
-	if(flags&NET_CHUNKFLAG_VITAL)
-	{
-		data[1] |= (sequence>>2)&0xf0;
-		data[2] = sequence&0xff;
-		return data + 3;
-	}
-	return data + 2;
-}
-
-unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header)
-{
-	header->flags = (data[0]>>6)&3;
-	header->size = ((data[0]&0x3f)<<4) | (data[1]&0xf);
-	header->sequence = -1;
-	if(header->flags&NET_CHUNKFLAG_VITAL)
-	{
-		header->sequence = ((data[1]&0xf0)<<2) | data[2];
-		return data + 3;
-	}
-	return data + 2;
-}
-
-
-void send_controlmsg(NETSOCKET socket, NETADDR *addr, int ack, int controlmsg, const void *extra, int extra_size)
-{
-	NETPACKETCONSTRUCT construct;
-	construct.flags = NET_PACKETFLAG_CONTROL;
-	construct.ack = ack;
-	construct.num_chunks = 0;
-	construct.data_size = 1+extra_size;
-	construct.chunk_data[0] = controlmsg;
-	mem_copy(&construct.chunk_data[1], extra, extra_size);
-	
-	/* send the control message */
-	send_packet(socket, addr, &construct);
-}
-
-void netcommon_openlog(const char *sentlog, const char *recvlog)
-{
-	if(sentlog)
-	{
-		datalog_sent = engine_openfile(sentlog, IOFLAG_WRITE);
-		if(datalog_sent)
-			dbg_msg("network", "logging sent packages to '%s'", sentlog);
-		else
-			dbg_msg("network", "failed to open for logging '%s'", sentlog);
-	}
-	
-	if(recvlog)
-	{
-		datalog_recv = engine_openfile(recvlog, IOFLAG_WRITE);
-		if(recvlog)
-			dbg_msg("network", "logging recv packages to '%s'", recvlog);
-		else
-			dbg_msg("network", "failed to open for logging '%s'", recvlog);
-	}
-}
-
-static const unsigned freq_table[256+1] = {
-	1<<30,4545,2657,431,1950,919,444,482,2244,617,838,542,715,1814,304,240,754,212,647,186,
-	283,131,146,166,543,164,167,136,179,859,363,113,157,154,204,108,137,180,202,176,
-	872,404,168,134,151,111,113,109,120,126,129,100,41,20,16,22,18,18,17,19,
-	16,37,13,21,362,166,99,78,95,88,81,70,83,284,91,187,77,68,52,68,
-	59,66,61,638,71,157,50,46,69,43,11,24,13,19,10,12,12,20,14,9,
-	20,20,10,10,15,15,12,12,7,19,15,14,13,18,35,19,17,14,8,5,
-	15,17,9,15,14,18,8,10,2173,134,157,68,188,60,170,60,194,62,175,71,
-	148,67,167,78,211,67,156,69,1674,90,174,53,147,89,181,51,174,63,163,80,
-	167,94,128,122,223,153,218,77,200,110,190,73,174,69,145,66,277,143,141,60,
-	136,53,180,57,142,57,158,61,166,112,152,92,26,22,21,28,20,26,30,21,
-	32,27,20,17,23,21,30,22,22,21,27,25,17,27,23,18,39,26,15,21,
-	12,18,18,27,20,18,15,19,11,17,33,12,18,15,19,18,16,26,17,18,
-	9,10,25,22,22,17,20,16,6,16,15,20,14,18,24,335,1517};
-
-void netcommon_init()
-{
-	huffman_init(&huffmanstate, freq_table);
-}
diff --git a/src/engine/e_network.cpp b/src/engine/e_network.cpp
new file mode 100644
index 00000000..ac753e50
--- /dev/null
+++ b/src/engine/e_network.cpp
@@ -0,0 +1,347 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <base/system.h>
+
+#include <string.h> /* strlen */
+
+#include "e_config.h"
+#include "e_engine.h"
+#include "e_network.h"
+#include "e_huffman.h"
+
+void CNetRecvUnpacker::Clear()
+{
+	m_Valid = false;
+}
+
+void CNetRecvUnpacker::Start(const NETADDR *pAddr, CNetConnection *pConnection, int ClientID)
+{
+	m_Addr = *pAddr;
+	m_pConnection = pConnection;
+	m_ClientID = ClientID;
+	m_CurrentChunk = 0;
+	m_Valid = true;
+}
+
+/* TODO: rename this function */
+int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk)
+{
+	CNetChunkHeader Header;
+	unsigned char *pEnd = m_Data.m_aChunkData + m_Data.m_DataSize;
+	
+	while(1)
+	{
+		unsigned char *pData = m_Data.m_aChunkData;
+		
+		/* check for old data to unpack */
+		if(!m_Valid || m_CurrentChunk >= m_Data.m_NumChunks)
+		{
+			Clear();
+			return 0;
+		}
+		
+		/* TODO: add checking here so we don't read too far */
+		for(int i = 0; i < m_CurrentChunk; i++)
+		{
+			pData = Header.Unpack(pData);
+			pData += Header.m_Size;
+		}
+		
+		/* unpack the header */
+		pData = Header.Unpack(pData);
+		m_CurrentChunk++;
+		
+		if(pData+Header.m_Size > pEnd)
+		{
+			Clear();
+			return 0;
+		}
+		
+		/* handle sequence stuff */
+		if(m_pConnection && (Header.m_Flags&NET_CHUNKFLAG_VITAL))
+		{
+			if(Header.m_Sequence == (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE)
+			{
+				/* in sequence */
+				m_pConnection->m_Ack = (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE;
+			}
+			else
+			{
+				/* old packet that we already got */
+				if(CNetBase::IsSeqInBackroom(Header.m_Sequence, m_pConnection->m_Ack))
+					continue;
+
+				/* out of sequence, request resend */
+				if(config.debug)
+					dbg_msg("conn", "asking for resend %d %d", Header.m_Sequence, (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE);
+				m_pConnection->SignalResend();
+				continue; /* take the next chunk in the packet */
+			}
+		}
+		
+		/* fill in the info */
+		pChunk->m_ClientID = m_ClientID;
+		pChunk->m_Address = m_Addr;
+		pChunk->m_Flags = 0;
+		pChunk->m_DataSize = Header.m_Size;
+		pChunk->m_pData = pData;
+		return 1;
+	}
+}
+
+/* packs the data tight and sends it */
+void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize)
+{
+	unsigned char aBuffer[NET_MAX_PACKETSIZE];
+	aBuffer[0] = 0xff;
+	aBuffer[1] = 0xff;
+	aBuffer[2] = 0xff;
+	aBuffer[3] = 0xff;
+	aBuffer[4] = 0xff;
+	aBuffer[5] = 0xff;
+	mem_copy(&aBuffer[6], pData, DataSize);
+	net_udp_send(Socket, pAddr, aBuffer, 6+DataSize);
+}
+
+void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket)
+{
+	unsigned char aBuffer[NET_MAX_PACKETSIZE];
+	int CompressedSize = -1;
+	int FinalSize = -1;
+
+	/* log the data */
+	if(ms_DataLogSent)
+	{
+		int type = 1;
+		io_write(ms_DataLogSent, &type, sizeof(type));
+		io_write(ms_DataLogSent, &pPacket->m_DataSize, sizeof(pPacket->m_DataSize));
+		io_write(ms_DataLogSent, &pPacket->m_aChunkData, pPacket->m_DataSize);
+		io_flush(ms_DataLogSent);
+	}
+	
+	/* compress */
+	CompressedSize = huffman_compress(&ms_HuffmanState, pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4);
+
+	/* check if the compression was enabled, successful and good enough	*/
+	if(CompressedSize > 0 && CompressedSize < pPacket->m_DataSize)
+	{
+		FinalSize = CompressedSize;
+		pPacket->m_Flags |= NET_PACKETFLAG_COMPRESSION;
+	}
+	else
+	{
+		/* use uncompressed data */
+		FinalSize = pPacket->m_DataSize;
+		mem_copy(&aBuffer[3], pPacket->m_aChunkData, pPacket->m_DataSize);
+		pPacket->m_Flags &= ~NET_PACKETFLAG_COMPRESSION;
+	}
+
+	/* set header and send the packet if all things are good */
+	if(FinalSize >= 0)
+	{
+		FinalSize += NET_PACKETHEADERSIZE;
+		aBuffer[0] = ((pPacket->m_Flags<<4)&0xf0)|((pPacket->m_Ack>>8)&0xf);
+		aBuffer[1] = pPacket->m_Ack&0xff;
+		aBuffer[2] = pPacket->m_NumChunks;
+		net_udp_send(Socket, pAddr, aBuffer, FinalSize);
+
+		/* log raw socket data */
+		if(ms_DataLogSent)
+		{
+			int type = 0;
+			io_write(ms_DataLogSent, &type, sizeof(type));
+			io_write(ms_DataLogSent, &FinalSize, sizeof(FinalSize));
+			io_write(ms_DataLogSent, aBuffer, FinalSize);
+			io_flush(ms_DataLogSent);
+		}
+	}
+}
+
+/* TODO: rename this function */
+int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket)
+{
+	/* check the size */
+	if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE)
+	{
+		dbg_msg("", "packet too small, %d", Size);
+		return -1;
+	}
+
+	/* log the data */
+	if(ms_DataLogRecv)
+	{
+		int type = 0;
+		io_write(ms_DataLogRecv, &type, sizeof(type));
+		io_write(ms_DataLogRecv, &Size, sizeof(Size));
+		io_write(ms_DataLogRecv, pBuffer, Size);
+		io_flush(ms_DataLogRecv);
+	}
+	
+	/* read the packet */
+	pPacket->m_Flags = pBuffer[0]>>4;
+	pPacket->m_Ack = ((pBuffer[0]&0xf)<<8) | pBuffer[1];
+	pPacket->m_NumChunks = pBuffer[2];
+	pPacket->m_DataSize = Size - NET_PACKETHEADERSIZE;
+
+	if(pPacket->m_Flags&NET_PACKETFLAG_CONNLESS)
+	{
+		if(Size < 6)
+		{
+			dbg_msg("", "connection less packet too small, %d", Size);
+			return -1;
+		}
+			
+		pPacket->m_Flags = NET_PACKETFLAG_CONNLESS;
+		pPacket->m_Ack = 0;
+		pPacket->m_NumChunks = 0;
+		pPacket->m_DataSize = Size - 6;
+		mem_copy(pPacket->m_aChunkData, &pBuffer[6], pPacket->m_DataSize);
+	}
+	else
+	{
+		if(pPacket->m_Flags&NET_PACKETFLAG_COMPRESSION)
+			pPacket->m_DataSize = huffman_decompress(&ms_HuffmanState, &pBuffer[3], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData));
+		else
+			mem_copy(pPacket->m_aChunkData, &pBuffer[3], pPacket->m_DataSize);
+	}
+
+	/* check for errors */	
+	if(pPacket->m_DataSize < 0)
+	{
+		if(config.debug)
+			dbg_msg("network", "error during packet decoding");
+		return -1;
+	}
+
+	/* log the data */
+	if(ms_DataLogRecv)
+	{
+		int type = 1;
+		io_write(ms_DataLogRecv, &type, sizeof(type));
+		io_write(ms_DataLogRecv, &pPacket->m_DataSize, sizeof(pPacket->m_DataSize));
+		io_write(ms_DataLogRecv, pPacket->m_aChunkData, pPacket->m_DataSize);
+		io_flush(ms_DataLogRecv);
+	}
+		
+	/* return success */
+	return 0;
+}
+
+
+void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize)
+{
+	CNetPacketConstruct Construct;
+	Construct.m_Flags = NET_PACKETFLAG_CONTROL;
+	Construct.m_Ack = Ack;
+	Construct.m_NumChunks = 0;
+	Construct.m_DataSize = 1+ExtraSize;
+	Construct.m_aChunkData[0] = ControlMsg;
+	mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize);
+	
+	/* send the control message */
+	CNetBase::SendPacket(Socket, pAddr, &Construct);
+}
+
+
+
+unsigned char *CNetChunkHeader::Pack(unsigned char *pData)
+{
+	pData[0] = ((m_Flags&3)<<6)|((m_Size>>4)&0x3f);
+	pData[1] = (m_Size&0xf);
+	if(m_Flags&NET_CHUNKFLAG_VITAL)
+	{
+		pData[1] |= (m_Sequence>>2)&0xf0;
+		pData[2] = m_Sequence&0xff;
+		return pData + 3;
+	}
+	return pData + 2;
+}
+
+unsigned char *CNetChunkHeader::Unpack(unsigned char *pData)
+{
+	m_Flags = (pData[0]>>6)&3;
+	m_Size = ((pData[0]&0x3f)<<4) | (pData[1]&0xf);
+	m_Sequence = -1;
+	if(m_Flags&NET_CHUNKFLAG_VITAL)
+	{
+		m_Sequence = ((pData[1]&0xf0)<<2) | pData[2];
+		return pData + 3;
+	}
+	return pData + 2;
+}
+
+
+int CNetBase::IsSeqInBackroom(int Seq, int Ack)
+{
+	int Bottom = (Ack-NET_MAX_SEQUENCE/2);
+	if(Bottom < 0)
+	{
+		if(Seq <= Ack)
+			return 1;
+		if(Seq >= (Bottom + NET_MAX_SEQUENCE))
+			return 1;
+	}
+	else
+	{
+		if(Seq <= Ack && Seq >= Bottom)
+			return 1;
+	}
+	
+	return 0;
+}
+
+IOHANDLE CNetBase::ms_DataLogSent = 0;
+IOHANDLE CNetBase::ms_DataLogRecv = 0;
+HUFFMAN_STATE CNetBase::ms_HuffmanState;
+
+
+void CNetBase::OpenLog(const char *pSentLog, const char *pRecvLog)
+{
+	if(pSentLog)
+	{
+		ms_DataLogSent = engine_openfile(pSentLog, IOFLAG_WRITE);
+		if(ms_DataLogSent)
+			dbg_msg("network", "logging sent packages to '%s'", pSentLog);
+		else
+			dbg_msg("network", "failed to open for logging '%s'", pSentLog);
+	}
+	
+	if(pRecvLog)
+	{
+		ms_DataLogRecv = engine_openfile(pRecvLog, IOFLAG_WRITE);
+		if(ms_DataLogRecv)
+			dbg_msg("network", "logging recv packages to '%s'", pRecvLog);
+		else
+			dbg_msg("network", "failed to open for logging '%s'", pRecvLog);
+	}
+}
+
+int CNetBase::Compress(const void *pData, int DataSize, void *pOutput, int OutputSize)
+{
+	return huffman_compress(&ms_HuffmanState, pData, DataSize, pOutput, OutputSize);
+}
+
+int CNetBase::Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize)
+{
+	return huffman_decompress(&ms_HuffmanState, pData, DataSize, pOutput, OutputSize);
+}
+
+
+static const unsigned gs_aFreqTable[256+1] = {
+	1<<30,4545,2657,431,1950,919,444,482,2244,617,838,542,715,1814,304,240,754,212,647,186,
+	283,131,146,166,543,164,167,136,179,859,363,113,157,154,204,108,137,180,202,176,
+	872,404,168,134,151,111,113,109,120,126,129,100,41,20,16,22,18,18,17,19,
+	16,37,13,21,362,166,99,78,95,88,81,70,83,284,91,187,77,68,52,68,
+	59,66,61,638,71,157,50,46,69,43,11,24,13,19,10,12,12,20,14,9,
+	20,20,10,10,15,15,12,12,7,19,15,14,13,18,35,19,17,14,8,5,
+	15,17,9,15,14,18,8,10,2173,134,157,68,188,60,170,60,194,62,175,71,
+	148,67,167,78,211,67,156,69,1674,90,174,53,147,89,181,51,174,63,163,80,
+	167,94,128,122,223,153,218,77,200,110,190,73,174,69,145,66,277,143,141,60,
+	136,53,180,57,142,57,158,61,166,112,152,92,26,22,21,28,20,26,30,21,
+	32,27,20,17,23,21,30,22,22,21,27,25,17,27,23,18,39,26,15,21,
+	12,18,18,27,20,18,15,19,11,17,33,12,18,15,19,18,16,26,17,18,
+	9,10,25,22,22,17,20,16,6,16,15,20,14,18,24,335,1517};
+
+void CNetBase::Init()
+{
+	huffman_init(&ms_HuffmanState, gs_aFreqTable);
+}
diff --git a/src/engine/e_network.h b/src/engine/e_network.h
index 04453cf9..19eca8da 100644
--- a/src/engine/e_network.h
+++ b/src/engine/e_network.h
@@ -2,38 +2,26 @@
 #define ENGINE_NETWORK_H
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 
+#include "e_ringbuffer.h"
+#include "e_huffman.h"
 
-typedef struct
-{
-	/* -1 means that it's a stateless packet */
-	/* 0 on the client means the server */
-	int client_id;
-	NETADDR address; /* only used when client_id == -1 */
-	int flags;
-	int data_size;
-	const void *data;
-} NETCHUNK;
-
-
-typedef struct
-{
-	NETADDR addr;
-	int expires;
-} NETBANINFO;
+/*
 
-/*typedef struct
-{
-	int send_bytes;
-	int recv_bytes;
-	int send_packets;
-	int recv_packets;
-	
-	int resend_packets;
-	int resend_bytes;
-} NETSTATS;*/
+CURRENT:
+	packet header: 3 bytes
+		unsigned char flags_ack; // 4bit flags, 4bit ack
+		unsigned char ack; // 8 bit ack
+		unsigned char num_chunks; // 8 bit chunks
+		
+		(unsigned char padding[3])	// 24 bit extra incase it's a connection less packet
+									// this is to make sure that it's compatible with the
+									// old protocol
 
-typedef struct NETSERVER NETSERVER;
-typedef struct NETCLIENT NETCLIENT;
+	chunk header: 2-3 bytes
+		unsigned char flags_size; // 2bit flags, 6 bit size
+		unsigned char size_seq; // 4bit size, 4bit seq
+		(unsigned char seq;) // 8bit seq, if vital flag is set
+*/
 
 enum
 {
@@ -50,97 +38,310 @@ enum
 	NETBANTYPE_DROP=2
 };
 
+
+enum
+{
+	NET_VERSION = 2,
+
+	NET_MAX_CHUNKSIZE = 1024,
+	NET_MAX_PAYLOAD = NET_MAX_CHUNKSIZE+16,
+	NET_MAX_PACKETSIZE = NET_MAX_PAYLOAD+16,
+	NET_MAX_CHUNKHEADERSIZE = 5,
+	NET_PACKETHEADERSIZE = 3,
+	NET_MAX_CLIENTS = 16,
+	NET_MAX_SEQUENCE = 1<<10,
+	NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1,
+
+	NET_CONNSTATE_OFFLINE=0,
+	NET_CONNSTATE_CONNECT=1,
+	NET_CONNSTATE_PENDING=2,
+	NET_CONNSTATE_ONLINE=3,
+	NET_CONNSTATE_ERROR=4,
+
+	NET_PACKETFLAG_CONTROL=1,
+	NET_PACKETFLAG_CONNLESS=2,
+	NET_PACKETFLAG_RESEND=4,
+	NET_PACKETFLAG_COMPRESSION=8,
+
+	NET_CHUNKFLAG_VITAL=1,
+	NET_CHUNKFLAG_RESEND=2,
+	
+	NET_CTRLMSG_KEEPALIVE=0,
+	NET_CTRLMSG_CONNECT=1,
+	NET_CTRLMSG_CONNECTACCEPT=2,
+	NET_CTRLMSG_ACCEPT=3,
+	NET_CTRLMSG_CLOSE=4,
+	
+	NET_SERVER_MAXBANS=1024,
+	
+	NET_CONN_BUFFERSIZE=1024*16,
+	
+	NET_ENUM_TERMINATOR
+};
+
+
 typedef int (*NETFUNC_DELCLIENT)(int cid, void *user);
 typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user);
 
-/* both */
-void netcommon_openlog(const char *sentlog, const char *recvlog);
-void netcommon_init();
-int netcommon_compress(const void *data, int data_size, void *output, int output_size);
-int netcommon_decompress(const void *data, int data_size, void *output, int output_size);
+struct CNetChunk
+{
+	/* -1 means that it's a stateless packet */
+	/* 0 on the client means the server */
+	int m_ClientID;
+	NETADDR m_Address; /* only used when client_id == -1 */
+	int m_Flags;
+	int m_DataSize;
+	const void *m_pData;
+};
 
-/* server side */
-NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags);
-int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user);
-int netserver_recv(NETSERVER *s, NETCHUNK *chunk);
-int netserver_send(NETSERVER *s, NETCHUNK *chunk);
-int netserver_close(NETSERVER *s);
-int netserver_update(NETSERVER *s);
-NETSOCKET netserver_socket(NETSERVER *s);
-int netserver_drop(NETSERVER *s, int client_id, const char *reason);
-int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr);
-int netserver_max_clients(NETSERVER *s);
-
-int netserver_ban_add(NETSERVER *s, NETADDR addr, int seconds);
-int netserver_ban_remove(NETSERVER *s, NETADDR addr);
-int netserver_ban_num(NETSERVER *s); /* caution, slow */
-int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info); /* caution, slow */
-
-/*void netserver_stats(NETSERVER *s, NETSTATS *stats);*/
+class CNetChunkHeader
+{
+public:
+	int m_Flags;
+	int m_Size;
+	int m_Sequence;
+	
+	unsigned char *Pack(unsigned char *pData);
+	unsigned char *Unpack(unsigned char *pData);
+};
 
-/* client side */
-NETCLIENT *netclient_open(NETADDR bindaddr, int flags);
-int netclient_disconnect(NETCLIENT *c, const char *reason);
-int netclient_connect(NETCLIENT *c, NETADDR *addr);
-int netclient_recv(NETCLIENT *c, NETCHUNK *chunk);
-int netclient_send(NETCLIENT *c, NETCHUNK *chunk);
-int netclient_close(NETCLIENT *c);
-int netclient_update(NETCLIENT *c);
-int netclient_state(NETCLIENT *c);
-int netclient_flush(NETCLIENT *c);
-int netclient_gotproblems(NETCLIENT *c);
-/*void netclient_stats(NETCLIENT *c, NETSTATS *stats);*/
-int netclient_error_string_reset(NETCLIENT *c);
-const char *netclient_error_string(NETCLIENT *c);
-
-#ifdef __cplusplus
-class net_server
+class CNetChunkResend
+{
+public:
+	int m_Flags;
+	int m_DataSize;
+	unsigned char *m_pData;
+
+	int m_Sequence;
+	int64 m_LastSendTime;
+	int64 m_FirstSendTime;
+};
+
+class CNetPacketConstruct
 {
-	NETSERVER *ptr;
 public:
-	net_server() : ptr(0) {}
-	~net_server() { close(); }
+	int m_Flags;
+	int m_Ack;
+	int m_NumChunks;
+	int m_DataSize;
+	unsigned char m_aChunkData[NET_MAX_PAYLOAD];
+};
+
+
+class CNetConnection
+{
+	// TODO: is this needed because this needs to be aware of
+	// the ack sequencing number and is also responible for updating
+	// that. this should be fixed.
+	friend class CNetRecvUnpacker;
+private:
+	unsigned short m_Sequence;
+	unsigned short m_Ack;
+	unsigned m_State;
+	
+	int m_Token;
+	int m_RemoteClosed;
+	
+	TStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> m_Buffer;
+	
+	int64 m_LastUpdateTime;
+	int64 m_LastRecvTime;
+	int64 m_LastSendTime;
+	
+	char m_ErrorString[256];
 	
-	int open(NETADDR bindaddr, int max, int flags) { ptr = netserver_open(bindaddr, max, flags); return ptr != 0; }
-	int close() { int r = netserver_close(ptr); ptr = 0; return r; }
+	CNetPacketConstruct m_Construct;
 	
-	int set_callbacks(NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user)
-	{ return netserver_set_callbacks(ptr, new_client, del_client, user); }
+	NETADDR m_PeerAddr;
+	NETSOCKET m_Socket;
+	NETSTATS m_Stats;
 	
-	int recv(NETCHUNK *chunk) { return netserver_recv(ptr, chunk); }
-	int send(NETCHUNK *chunk) { return netserver_send(ptr, chunk); }
-	int update() { return netserver_update(ptr); }
+	//
+	void Reset();
+	void ResetStats();
+	void SetError(const char *pString);
+	void AckChunks(int Ack);
 	
-	int drop(int client_id, const char *reason) { return netserver_drop(ptr, client_id, reason); } 
+	void QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence);
+	void SendControl(int ControlMsg, const void *pExtra, int ExtraSize);
+	void ResendChunk(CNetChunkResend *pResend);
+	void Resend();
 
-	int max_clients() { return netserver_max_clients(ptr); }
-	/*void stats(NETSTATS *stats) { netserver_stats(ptr, stats); }*/
+public:
+	void Init(NETSOCKET Socket);
+	int Connect(NETADDR *pAddr);
+	void Disconnect(const char *pReason);
+
+	int Update();
+	int Flush();	
+
+	int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr);
+	void QueueChunk(int Flags, int DataSize, const void *pData);
+
+	const char *ErrorString();
+	void SignalResend();
+	int State() const { return m_State; }
+	NETADDR PeerAddress() const { return m_PeerAddr; }
+	
+	void ResetErrorString() { m_ErrorString[0] = 0; }
+	const char *ErrorString() const { return m_ErrorString; }
+	
+	// Needed for GotProblems in NetClient
+	int64 LastRecvTime() const { return m_LastRecvTime; }
+	
+	int AckSequence() const { return m_Ack; }
+};
+
+struct CNetRecvUnpacker
+{
+public:
+	bool m_Valid;
+	
+	NETADDR m_Addr;
+	CNetConnection *m_pConnection;
+	int m_CurrentChunk;
+	int m_ClientID;
+	CNetPacketConstruct m_Data;
+	unsigned char m_aBuffer[NET_MAX_PACKETSIZE];
+
+	CNetRecvUnpacker() { Clear(); }
+	void Clear();
+	void Start(const NETADDR *pAddr, CNetConnection *pConnection, int ClientID);
+	int FetchChunk(CNetChunk *pChunk);	
+};
+
+/* server side */
+class CNetServer
+{
+public:
+	struct CBanInfo
+	{
+		NETADDR m_Addr;
+		int m_Expires;
+	};
+	
+private:
+	class CSlot
+	{
+	public:
+		CNetConnection m_Connection;
+	};
+	
+	class CBan
+	{
+	public:
+		CBanInfo m_Info;
+		
+		/* hash list */
+		CBan *m_pHashNext;
+		CBan *m_pHashPrev;
+		
+		/* used or free list */
+		CBan *m_pNext;
+		CBan *m_pPrev;
+	};
+	
+	
+	NETSOCKET m_Socket;
+	CSlot m_aSlots[NET_MAX_CLIENTS];
+	int m_MaxClients;
+
+	CBan *m_aBans[256];
+	CBan m_BanPool[NET_SERVER_MAXBANS];
+	CBan *m_BanPool_FirstFree;
+	CBan *m_BanPool_FirstUsed;
+
+	NETFUNC_NEWCLIENT m_pfnNewClient;
+	NETFUNC_DELCLIENT m_pfnDelClient;
+	void *m_UserPtr;
+	
+	CNetRecvUnpacker m_RecvUnpacker;
+	
+	void BanRemoveByObject(CBan *ban);
+	
+public:
+	int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
+
+	//
+	bool Open(NETADDR bindaddr, int MaxClients, int Flags);
+	int Close();
+	
+	//
+	int Recv(CNetChunk *pChunk);
+	int Send(CNetChunk *pChunk);
+	int Update();
+	
+	//
+	int Drop(int ClientID, const char *Reason);
+
+	// banning
+	int BanAdd(NETADDR Addr, int Seconds);
+	int BanRemove(NETADDR Addr);
+	int BanNum(); /* caution, slow */
+	int BanGet(int Index, CBanInfo *pInfo); /* caution, slow */
+
+	// status requests
+	NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); }
+	NETSOCKET Socket() const { return m_Socket; }
+	int MaxClients() const { return m_MaxClients; }
 };
 
 
-class net_client
+
+/* client side */
+class CNetClient
 {
-	NETCLIENT *ptr;
+	NETADDR m_ServerAddr;
+	CNetConnection m_Connection;
+	CNetRecvUnpacker m_RecvUnpacker;
+	NETSOCKET m_Socket;
 public:
-	net_client() : ptr(0) {}
-	~net_client() { close(); }
+	// openness
+	bool Open(NETADDR BindAddr, int Flags);
+	int Close();
 	
-	int open(NETADDR bindaddr, int flags) { ptr = netclient_open(bindaddr, flags); return ptr != 0; }
-	int close() { int r = netclient_close(ptr); ptr = 0; return r; }
+	// connection state
+	int Disconnect(const char *Reason);
+	int Connect(NETADDR *Addr);
 	
-	int connect(NETADDR *addr) { return netclient_connect(ptr, addr); }
-	int disconnect(const char *reason) { return netclient_disconnect(ptr, reason); }
+	// communication
+	int Recv(CNetChunk *Chunk);
+	int Send(CNetChunk *Chunk);
 	
-	int recv(NETCHUNK *chunk) { return netclient_recv(ptr, chunk); }
-	int send(NETCHUNK *chunk) { return netclient_send(ptr, chunk); }
-	int update() { return netclient_update(ptr); }
+	// pumping
+	int Update();
+	int Flush();
+
+	int ResetErrorString();
 	
-	const char *error_string() { return netclient_error_string(ptr); }
+	// error and state
+	int State();
+	int GotProblems();
+	const char *ErrorString();
+};
+
+
+
+// TODO: both, fix these. This feels like a junk class for stuff that doesn't fit anywere
+class CNetBase
+{
+	static IOHANDLE ms_DataLogSent;
+	static IOHANDLE ms_DataLogRecv;
+	static HUFFMAN_STATE ms_HuffmanState;
+public:
+	static void OpenLog(const char *pSentlog, const char *pRecvlog);
+	static void Init();
+	static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize);
+	static int Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize);
 	
-	int state() { return netclient_state(ptr); }
-	/*void stats(NETSTATS *stats) { netclient_stats(ptr, stats); }*/
+	static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize);
+	static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize);
+	static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket);
+	static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket);
+
+	/* The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not */
+	static int IsSeqInBackroom(int Seq, int Ack);	
 };
-#endif
 
 
 #endif
diff --git a/src/engine/e_network_client.c b/src/engine/e_network_client.c
deleted file mode 100644
index 75f7c538..00000000
--- a/src/engine/e_network_client.c
+++ /dev/null
@@ -1,154 +0,0 @@
-#include <base/system.h>
-#include "e_network.h"
-#include "e_network_internal.h"
-
-struct NETCLIENT
-{
-	NETADDR server_addr;
-	NETSOCKET socket;
-	
-	NETRECVINFO recv;
-	NETCONNECTION conn;
-};
-
-NETCLIENT *netclient_open(NETADDR bindaddr, int flags)
-{
-	NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1);
-	mem_zero(client, sizeof(NETCLIENT));
-	client->socket = net_udp_create(bindaddr);
-	conn_init(&client->conn, client->socket);
-	return client;
-}
-
-int netclient_close(NETCLIENT *c)
-{
-	/* TODO: implement me */
-	return 0;
-}
-
-
-int netclient_disconnect(NETCLIENT *c, const char *reason)
-{
-	dbg_msg("netclient", "disconnected. reason=\"%s\"", reason);
-	conn_disconnect(&c->conn, reason);
-	return 0;
-}
-
-int netclient_update(NETCLIENT *c)
-{
-	conn_update(&c->conn);
-	if(c->conn.state == NET_CONNSTATE_ERROR)
-		netclient_disconnect(c, conn_error(&c->conn));
-	return 0;
-}
-
-int netclient_connect(NETCLIENT *c, NETADDR *addr)
-{
-	conn_connect(&c->conn, addr);
-	return 0;
-}
-
-int netclient_error_string_reset(NETCLIENT *c)
-{
-	mem_zero(c->conn.error_string, sizeof(c->conn.error_string));
-	return 0;
-}
-
-int netclient_recv(NETCLIENT *c, NETCHUNK *chunk)
-{
-	while(1)
-	{
-		NETADDR addr;
-		int bytes;
-			
-		/* check for a chunk */
-		if(recvinfo_fetch_chunk(&c->recv, chunk))
-			return 1;
-		
-		/* TODO: empty the recvinfo */
-		bytes = net_udp_recv(c->socket, &addr, c->recv.buffer, NET_MAX_PACKETSIZE);
-
-		/* no more packets for now */
-		if(bytes <= 0)
-			break;
-
-		if(unpack_packet(c->recv.buffer, bytes, &c->recv.data) == 0)
-		{
-			if(c->recv.data.flags&NET_PACKETFLAG_CONNLESS)
-			{
-				chunk->flags = NETSENDFLAG_CONNLESS;
-				chunk->client_id = -1;
-				chunk->address = addr;
-				chunk->data_size = c->recv.data.data_size;
-				chunk->data = c->recv.data.chunk_data;
-				return 1;
-			}
-			else
-			{
-				if(conn_feed(&c->conn, &c->recv.data, &addr))
-					recvinfo_start(&c->recv, &addr, &c->conn, 0);
-			}
-		}
-	}
-	return 0;
-}
-
-int netclient_send(NETCLIENT *c, NETCHUNK *chunk)
-{
-	if(chunk->data_size >= NET_MAX_PAYLOAD)
-	{
-		dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", chunk->data_size);
-		return -1;
-	}
-	
-	if(chunk->flags&NETSENDFLAG_CONNLESS)
-	{
-		/* send connectionless packet */
-		send_packet_connless(c->socket, &chunk->address, chunk->data, chunk->data_size);
-	}
-	else
-	{
-		int f = 0;
-		dbg_assert(chunk->client_id == 0, "errornous client id");
-		
-		if(chunk->flags&NETSENDFLAG_VITAL)
-			f = NET_CHUNKFLAG_VITAL;
-		
-		conn_queue_chunk(&c->conn, f, chunk->data_size, chunk->data);
-
-		if(chunk->flags&NETSENDFLAG_FLUSH)
-			conn_flush(&c->conn);
-	}
-	return 0;
-}
-
-int netclient_state(NETCLIENT *c)
-{
-	if(c->conn.state == NET_CONNSTATE_ONLINE)
-		return NETSTATE_ONLINE;
-	if(c->conn.state == NET_CONNSTATE_OFFLINE)
-		return NETSTATE_OFFLINE;
-	return NETSTATE_CONNECTING;
-}
-
-int netclient_flush(NETCLIENT *c)
-{
-	return conn_flush(&c->conn);
-}
-
-int netclient_gotproblems(NETCLIENT *c)
-{
-	if(time_get() - c->conn.last_recv_time > time_freq())
-		return 1;
-	return 0;
-}
-
-void netclient_stats(NETCLIENT *c, NETSTATS *stats)
-{
-	*stats = c->conn.stats;
-}
-
-const char *netclient_error_string(NETCLIENT *c)
-{
-	return conn_error(&c->conn);
-}
diff --git a/src/engine/e_network_client.cpp b/src/engine/e_network_client.cpp
new file mode 100644
index 00000000..ce243b32
--- /dev/null
+++ b/src/engine/e_network_client.cpp
@@ -0,0 +1,139 @@
+#include <base/system.h>
+#include "e_network.h"
+
+bool CNetClient::Open(NETADDR BindAddr, int Flags)
+{
+	// clean it
+	mem_zero(this, sizeof(*this));
+
+	// open socket
+	m_Socket = net_udp_create(BindAddr);
+	m_Connection.Init(m_Socket);
+	return true;
+}
+
+int CNetClient::Close()
+{
+	/* TODO: implement me */
+	return 0;
+}
+
+
+int CNetClient::Disconnect(const char *pReason)
+{
+	dbg_msg("netclient", "disconnected. reason=\"%s\"", pReason);
+	m_Connection.Disconnect(pReason);
+	return 0;
+}
+
+int CNetClient::Update()
+{
+	m_Connection.Update();
+	if(m_Connection.State() == NET_CONNSTATE_ERROR)
+		Disconnect(m_Connection.ErrorString());
+	return 0;
+}
+
+int CNetClient::Connect(NETADDR *pAddr)
+{
+	m_Connection.Connect(pAddr);
+	return 0;
+}
+
+int CNetClient::ResetErrorString()
+{
+	m_Connection.ResetErrorString();
+	return 0;
+}
+
+int CNetClient::Recv(CNetChunk *pChunk)
+{
+	while(1)
+	{
+		/* check for a chunk */
+		if(m_RecvUnpacker.FetchChunk(pChunk))
+			return 1;
+		
+		/* TODO: empty the recvinfo */
+		NETADDR Addr;
+		int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE);
+
+		/* no more packets for now */
+		if(Bytes <= 0)
+			break;
+
+		if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0)
+		{
+			if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS)
+			{
+				pChunk->m_Flags = NETSENDFLAG_CONNLESS;
+				pChunk->m_ClientID = -1;
+				pChunk->m_Address = Addr;
+				pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize;
+				pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData;
+				return 1;
+			}
+			else
+			{
+				if(m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr))
+					m_RecvUnpacker.Start(&Addr, &m_Connection, 0);
+			}
+		}
+	}
+	return 0;
+}
+
+int CNetClient::Send(CNetChunk *pChunk)
+{
+	if(pChunk->m_DataSize >= NET_MAX_PAYLOAD)
+	{
+		dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", pChunk->m_DataSize);
+		return -1;
+	}
+	
+	if(pChunk->m_Flags&NETSENDFLAG_CONNLESS)
+	{
+		/* send connectionless packet */
+		CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize);
+	}
+	else
+	{
+		int Flags = 0;
+		dbg_assert(pChunk->m_ClientID == 0, "errornous client id");
+		
+		if(pChunk->m_Flags&NETSENDFLAG_VITAL)
+			Flags = NET_CHUNKFLAG_VITAL;
+		
+		m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData);
+
+		if(pChunk->m_Flags&NETSENDFLAG_FLUSH)
+			m_Connection.Flush();
+	}
+	return 0;
+}
+
+int CNetClient::State()
+{
+	if(m_Connection.State() == NET_CONNSTATE_ONLINE)
+		return NETSTATE_ONLINE;
+	if(m_Connection.State() == NET_CONNSTATE_OFFLINE)
+		return NETSTATE_OFFLINE;
+	return NETSTATE_CONNECTING;
+}
+
+int CNetClient::Flush()
+{
+	return m_Connection.Flush();
+}
+
+int CNetClient::GotProblems()
+{
+	if(time_get() - m_Connection.LastRecvTime() > time_freq())
+		return 1;
+	return 0;
+}
+
+const char *CNetClient::ErrorString()
+{
+	return m_Connection.ErrorString();
+}
diff --git a/src/engine/e_network_conn.c b/src/engine/e_network_conn.c
deleted file mode 100644
index 583dbe65..00000000
--- a/src/engine/e_network_conn.c
+++ /dev/null
@@ -1,366 +0,0 @@
-#include <base/system.h>
-#include <string.h>
-#include "e_config.h"
-#include "e_network_internal.h"
-
-static void conn_reset_stats(NETCONNECTION *conn)
-{
-	mem_zero(&conn->stats, sizeof(conn->stats));
-}
-
-static void conn_reset(NETCONNECTION *conn)
-{
-	conn->seq = 0;
-	conn->ack = 0;
-	conn->remote_closed = 0;
-	
-	conn->state = NET_CONNSTATE_OFFLINE;
-	conn->last_send_time = 0;
-	conn->last_recv_time = 0;
-	conn->last_update_time = 0;
-	conn->token = -1;
-	mem_zero(&conn->peeraddr, sizeof(conn->peeraddr));
-	
-	conn->buffer = ringbuf_init(conn->buffer_memory, sizeof(conn->buffer_memory), 0);
-	
-	mem_zero(&conn->construct, sizeof(conn->construct));
-}
-
-
-const char *conn_error(NETCONNECTION *conn)
-{
-	return conn->error_string;
-}
-
-static void conn_set_error(NETCONNECTION *conn, const char *str)
-{
-	str_copy(conn->error_string, str, sizeof(conn->error_string));
-}
-
-void conn_init(NETCONNECTION *conn, NETSOCKET socket)
-{
-	conn_reset(conn);
-	conn_reset_stats(conn);
-	conn->socket = socket;
-	mem_zero(conn->error_string, sizeof(conn->error_string));
-}
-
-
-static void conn_ack(NETCONNECTION *conn, int ack)
-{
-	
-	while(1)
-	{
-		NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer);
-		if(!resend)
-			break;
-		
-		if(seq_in_backroom(resend->sequence, ack))
-			ringbuf_popfirst(conn->buffer);
-		else
-			break;
-	}
-}
-
-void conn_want_resend(NETCONNECTION *conn)
-{
-	conn->construct.flags |= NET_PACKETFLAG_RESEND;
-}
-
-int conn_flush(NETCONNECTION *conn)
-{
-	int num_chunks = conn->construct.num_chunks;
-	if(!num_chunks && !conn->construct.flags)
-		return 0;
-
-	/* send of the packets */	
-	conn->construct.ack = conn->ack;
-	send_packet(conn->socket, &conn->peeraddr, &conn->construct);
-	
-	/* update send times */
-	conn->last_send_time = time_get();
-	
-	/* clear construct so we can start building a new package */
-	mem_zero(&conn->construct, sizeof(conn->construct));
-	return num_chunks;
-}
-
-static void conn_queue_chunk_ex(NETCONNECTION *conn, int flags, int data_size, const void *data, int sequence)
-{
-	unsigned char *chunk_data;
-	
-	/* check if we have space for it, if not, flush the connection */
-	if(conn->construct.data_size + data_size + NET_MAX_CHUNKHEADERSIZE > sizeof(conn->construct.chunk_data))
-		conn_flush(conn);
-
-	/* pack all the data */
-	chunk_data = &conn->construct.chunk_data[conn->construct.data_size];
-	chunk_data = pack_chunk_header(chunk_data, flags, data_size, sequence);
-	mem_copy(chunk_data, data, data_size);
-	chunk_data += data_size;
-
-	/* */
-	conn->construct.num_chunks++;
-	conn->construct.data_size = (int)(chunk_data-conn->construct.chunk_data);
-	
-	/* set packet flags aswell */
-	
-	if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND))
-	{
-		/* save packet if we need to resend */
-		NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_allocate(conn->buffer, sizeof(NETCHUNKDATA)+data_size);
-		if(resend)
-		{
-			resend->sequence = sequence;
-			resend->flags = flags;
-			resend->data_size = data_size;
-			resend->data = (unsigned char *)(resend+1);
-			resend->first_send_time = time_get();
-			resend->last_send_time = resend->first_send_time;
-			mem_copy(resend->data, data, data_size);
-		}
-		else
-		{
-			/* out of buffer */
-			conn_disconnect(conn, "too weak connection (out of buffer)");
-		}
-	}
-}
-
-void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data)
-{
-	if(flags&NET_CHUNKFLAG_VITAL)
-		conn->seq = (conn->seq+1)%NET_MAX_SEQUENCE;
-	conn_queue_chunk_ex(conn, flags, data_size, data, conn->seq);
-}
-
-
-static void conn_send_control(NETCONNECTION *conn, int controlmsg, const void *extra, int extra_size)
-{
-	/* send the control message */
-	conn->last_send_time = time_get();
-	send_controlmsg(conn->socket, &conn->peeraddr, conn->ack, controlmsg, extra, extra_size);
-}
-
-static void conn_resend_chunk(NETCONNECTION *conn, NETCHUNKDATA *resend)
-{
-	conn_queue_chunk_ex(conn, resend->flags|NET_CHUNKFLAG_RESEND, resend->data_size, resend->data, resend->sequence);
-	resend->last_send_time = time_get();
-}
-
-static void conn_resend(NETCONNECTION *conn)
-{
-	int resend_count = 0;
-	int first = 0, last = 0;
-	void *item = ringbuf_first(conn->buffer);
-	
-	while(item)
-	{
-		NETCHUNKDATA *resend = item;
-		
-		if(resend_count == 0)
-			first = resend->sequence;
-		last = resend->sequence;
-			
-		conn_resend_chunk(conn, resend);
-		item = ringbuf_next(conn->buffer, item);
-		resend_count++;
-	}
-	
-	if(config.debug)
-		dbg_msg("conn", "resent %d packets (%d to %d)", resend_count, first, last);
-}
-
-int conn_connect(NETCONNECTION *conn, NETADDR *addr)
-{
-	if(conn->state != NET_CONNSTATE_OFFLINE)
-		return -1;
-	
-	/* init connection */
-	conn_reset(conn);
-	conn->peeraddr = *addr;
-	mem_zero(conn->error_string, sizeof(conn->error_string));
-	conn->state = NET_CONNSTATE_CONNECT;
-	conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0);
-	return 0;
-}
-
-void conn_disconnect(NETCONNECTION *conn, const char *reason)
-{
-	if(conn->state == NET_CONNSTATE_OFFLINE)
-		return;
-
-	if(conn->remote_closed == 0)
-	{
-		if(reason)
-			conn_send_control(conn, NET_CTRLMSG_CLOSE, reason, strlen(reason)+1);
-		else
-			conn_send_control(conn, NET_CTRLMSG_CLOSE, 0, 0);
-
-		conn->error_string[0] = 0;
-		if(reason)
-			str_copy(conn->error_string, reason, sizeof(conn->error_string));
-	}
-	
-	conn_reset(conn);
-}
-
-int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr)
-{
-	int64 now = time_get();
-	conn->last_recv_time = now;
-	
-	/* check if resend is requested */
-	if(packet->flags&NET_PACKETFLAG_RESEND)
-		conn_resend(conn);
-
-	/* */									
-	if(packet->flags&NET_PACKETFLAG_CONTROL)
-	{
-		int ctrlmsg = packet->chunk_data[0];
-		
-		if(ctrlmsg == NET_CTRLMSG_CLOSE)
-		{
-			conn->state = NET_CONNSTATE_ERROR;
-			conn->remote_closed = 1;
-			
-			if(packet->data_size)
-			{
-				/* make sure to sanitize the error string form the other party*/
-				char str[128];
-				if(packet->data_size < 128)
-					str_copy(str, (char *)packet->chunk_data, packet->data_size);
-				else
-					str_copy(str, (char *)packet->chunk_data, 128);
-				str_sanitize_strong(str);
-				
-				/* set the error string */
-				conn_set_error(conn, str);
-			}
-			else
-				conn_set_error(conn, "no reason given");
-				
-			if(config.debug)
-				dbg_msg("conn", "closed reason='%s'", conn_error(conn));
-			return 0;			
-		}
-		else
-		{
-			if(conn->state == NET_CONNSTATE_OFFLINE)
-			{
-				if(ctrlmsg == NET_CTRLMSG_CONNECT)
-				{
-					/* send response and init connection */
-					conn_reset(conn);
-					conn->state = NET_CONNSTATE_PENDING;
-					conn->peeraddr = *addr;
-					conn->last_send_time = now;
-					conn->last_recv_time = now;
-					conn->last_update_time = now;
-					conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0);
-					if(config.debug)
-						dbg_msg("connection", "got connection, sending connect+accept");			
-				}
-			}
-			else if(conn->state == NET_CONNSTATE_CONNECT)
-			{
-				/* connection made */
-				if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT)
-				{
-					conn_send_control(conn, NET_CTRLMSG_ACCEPT, 0, 0);
-					conn->state = NET_CONNSTATE_ONLINE;
-					if(config.debug)
-						dbg_msg("connection", "got connect+accept, sending accept. connection online");
-				}
-			}
-			else if(conn->state == NET_CONNSTATE_ONLINE)
-			{
-				/* connection made */
-				/*
-				if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT)
-				{
-					
-				}*/
-			}
-		}
-	}
-	else
-	{
-		if(conn->state == NET_CONNSTATE_PENDING)
-		{
-			conn->state = NET_CONNSTATE_ONLINE;
-			if(config.debug)
-				dbg_msg("connection", "connecting online");
-		}
-	}
-	
-	if(conn->state == NET_CONNSTATE_ONLINE)
-	{
-		
-		conn_ack(conn, packet->ack);
-	}
-	
-	return 1;
-}
-
-int conn_update(NETCONNECTION *conn)
-{
-	int64 now = time_get();
-
-	if(conn->state == NET_CONNSTATE_OFFLINE || conn->state == NET_CONNSTATE_ERROR)
-		return 0;
-	
-	/* check for timeout */
-	if(conn->state != NET_CONNSTATE_OFFLINE &&
-		conn->state != NET_CONNSTATE_CONNECT &&
-		(now-conn->last_recv_time) > time_freq()*10)
-	{
-		conn->state = NET_CONNSTATE_ERROR;
-		conn_set_error(conn, "timeout");
-	}
-
-	/* fix resends */
-	if(ringbuf_first(conn->buffer))
-	{
-		NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer);
-
-		/* check if we have some really old stuff laying around and abort if not acked */
-		if(now-resend->first_send_time > time_freq()*10)
-		{
-			conn->state = NET_CONNSTATE_ERROR;
-			conn_set_error(conn, "too weak connection (not acked for 10 seconds)");
-		}
-		else
-		{
-			/* resend packet if we havn't got it acked in 1 second */
-			if(now-resend->last_send_time > time_freq())
-				conn_resend_chunk(conn, resend);
-		}
-	}
-	
-	/* send keep alives if nothing has happend for 250ms */
-	if(conn->state == NET_CONNSTATE_ONLINE)
-	{
-		if(time_get()-conn->last_send_time > time_freq()/2) /* flush connection after 500ms if needed */
-		{
-			int num_flushed_chunks = conn_flush(conn);
-			if(num_flushed_chunks && config.debug)
-				dbg_msg("connection", "flushed connection due to timeout. %d chunks.", num_flushed_chunks);
-		}
-			
-		if(time_get()-conn->last_send_time > time_freq())
-			conn_send_control(conn, NET_CTRLMSG_KEEPALIVE, 0, 0);
-	}
-	else if(conn->state == NET_CONNSTATE_CONNECT)
-	{
-		if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */
-			conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0);
-	}
-	else if(conn->state == NET_CONNSTATE_PENDING)
-	{
-		if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */
-			conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0);
-	}
-	
-	return 0;
-}
diff --git a/src/engine/e_network_conn.cpp b/src/engine/e_network_conn.cpp
new file mode 100644
index 00000000..a54c9ce3
--- /dev/null
+++ b/src/engine/e_network_conn.cpp
@@ -0,0 +1,349 @@
+#include <base/system.h>
+#include <string.h>
+#include "e_config.h"
+#include "e_network.h"
+
+void CNetConnection::ResetStats()
+{
+	mem_zero(&m_Stats, sizeof(m_Stats));
+}
+
+void CNetConnection::Reset()
+{
+	m_Sequence = 0;
+	m_Ack = 0;
+	m_RemoteClosed = 0;
+	
+	m_State = NET_CONNSTATE_OFFLINE;
+	m_LastSendTime = 0;
+	m_LastRecvTime = 0;
+	m_LastUpdateTime = 0;
+	m_Token = -1;
+	mem_zero(&m_PeerAddr, sizeof(m_PeerAddr));
+	
+	m_Buffer.Init();
+	
+	mem_zero(&m_Construct, sizeof(m_Construct));
+}
+
+const char *CNetConnection::ErrorString()
+{
+	return m_ErrorString;
+}
+
+void CNetConnection::SetError(const char *pString)
+{
+	str_copy(m_ErrorString, pString, sizeof(m_ErrorString));
+}
+
+void CNetConnection::Init(NETSOCKET Socket)
+{
+	Reset();
+	ResetStats();
+	
+	m_Socket = Socket;
+	mem_zero(m_ErrorString, sizeof(m_ErrorString));
+}
+
+void CNetConnection::AckChunks(int Ack)
+{
+	while(1)
+	{
+		CNetChunkResend *pResend = m_Buffer.First();
+		if(!pResend)
+			break;
+		
+		if(CNetBase::IsSeqInBackroom(pResend->m_Sequence, Ack))
+			m_Buffer.PopFirst();
+		else
+			break;
+	}
+}
+
+void CNetConnection::SignalResend()
+{
+	m_Construct.m_Flags |= NET_PACKETFLAG_RESEND;
+}
+
+int CNetConnection::Flush()
+{
+	int NumChunks = m_Construct.m_NumChunks;
+	if(!NumChunks && !m_Construct.m_Flags)
+		return 0;
+
+	/* send of the packets */	
+	m_Construct.m_Ack = m_Ack;
+	CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct);
+	
+	/* update send times */
+	m_LastSendTime = time_get();
+	
+	/* clear construct so we can start building a new package */
+	mem_zero(&m_Construct, sizeof(m_Construct));
+	return NumChunks;
+}
+
+void CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence)
+{
+	unsigned char *pChunkData;
+	
+	/* check if we have space for it, if not, flush the connection */
+	if(m_Construct.m_DataSize + DataSize + NET_MAX_CHUNKHEADERSIZE > (int)sizeof(m_Construct.m_aChunkData))
+		Flush();
+
+	/* pack all the data */
+	CNetChunkHeader Header;
+	Header.m_Flags = Flags;
+	Header.m_Size = DataSize;
+	Header.m_Sequence = Sequence;
+	pChunkData = &m_Construct.m_aChunkData[m_Construct.m_DataSize];
+	pChunkData = Header.Pack(pChunkData);
+	mem_copy(pChunkData, pData, DataSize);
+	pChunkData += DataSize;
+
+	/* */
+	m_Construct.m_NumChunks++;
+	m_Construct.m_DataSize = (int)(pChunkData-m_Construct.m_aChunkData);
+	
+	/* set packet flags aswell */
+	
+	if(Flags&NET_CHUNKFLAG_VITAL && !(Flags&NET_CHUNKFLAG_RESEND))
+	{
+		/* save packet if we need to resend */
+		CNetChunkResend *pResend = m_Buffer.Allocate(sizeof(CNetChunkResend)+DataSize);
+		if(pResend)
+		{
+			pResend->m_Sequence = Sequence;
+			pResend->m_Flags = Flags;
+			pResend->m_DataSize = DataSize;
+			pResend->m_pData = (unsigned char *)(pResend+1);
+			pResend->m_FirstSendTime = time_get();
+			pResend->m_LastSendTime = pResend->m_FirstSendTime;
+			mem_copy(pResend->m_pData, pData, DataSize);
+		}
+		else
+		{
+			/* out of buffer */
+			Disconnect("too weak connection (out of buffer)");
+		}
+	}
+}
+
+void CNetConnection::QueueChunk(int Flags, int DataSize, const void *pData)
+{
+	if(Flags&NET_CHUNKFLAG_VITAL)
+		m_Sequence = (m_Sequence+1)%NET_MAX_SEQUENCE;
+	QueueChunkEx(Flags, DataSize, pData, m_Sequence);
+}
+
+void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSize)
+{
+	/* send the control message */
+	m_LastSendTime = time_get();
+	CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize);
+}
+
+void CNetConnection::ResendChunk(CNetChunkResend *pResend)
+{
+	QueueChunkEx(pResend->m_Flags|NET_CHUNKFLAG_RESEND, pResend->m_DataSize, pResend->m_pData, pResend->m_Sequence);
+	pResend->m_LastSendTime = time_get();
+}
+
+void CNetConnection::Resend()
+{
+	for(CNetChunkResend *pResend = m_Buffer.First(); pResend; m_Buffer.Next(pResend))
+		ResendChunk(pResend);
+}
+
+int CNetConnection::Connect(NETADDR *pAddr)
+{
+	if(State() != NET_CONNSTATE_OFFLINE)
+		return -1;
+	
+	/* init connection */
+	Reset();
+	m_PeerAddr = *pAddr;
+	mem_zero(m_ErrorString, sizeof(m_ErrorString));
+	m_State = NET_CONNSTATE_CONNECT;
+	SendControl(NET_CTRLMSG_CONNECT, 0, 0);
+	return 0;
+}
+
+void CNetConnection::Disconnect(const char *pReason)
+{
+	if(State() == NET_CONNSTATE_OFFLINE)
+		return;
+
+	if(m_RemoteClosed == 0)
+	{
+		if(pReason)
+			SendControl(NET_CTRLMSG_CLOSE, pReason, strlen(pReason)+1);
+		else
+			SendControl(NET_CTRLMSG_CLOSE, 0, 0);
+
+		m_ErrorString[0] = 0;
+		if(pReason)
+			str_copy(m_ErrorString, pReason, sizeof(m_ErrorString));
+	}
+	
+	Reset();
+}
+
+int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr)
+{
+	int64 now = time_get();
+	m_LastRecvTime = now;
+	
+	/* check if resend is requested */
+	if(pPacket->m_Flags&NET_PACKETFLAG_RESEND)
+		Resend();
+
+	/* */									
+	if(pPacket->m_Flags&NET_PACKETFLAG_CONTROL)
+	{
+		int CtrlMsg = pPacket->m_aChunkData[0];
+		
+		if(CtrlMsg == NET_CTRLMSG_CLOSE)
+		{
+			m_State = NET_CONNSTATE_ERROR;
+			m_RemoteClosed = 1;
+			
+			if(pPacket->m_DataSize)
+			{
+				/* make sure to sanitize the error string form the other party*/
+				char Str[128];
+				if(pPacket->m_DataSize < 128)
+					str_copy(Str, (char *)pPacket->m_aChunkData, pPacket->m_DataSize);
+				else
+					str_copy(Str, (char *)pPacket->m_aChunkData, sizeof(Str));
+				str_sanitize_strong(Str);
+				
+				/* set the error string */
+				SetError(Str);
+			}
+			else
+				SetError("no reason given");
+				
+			if(config.debug)
+				dbg_msg("conn", "closed reason='%s'", ErrorString());
+			return 0;			
+		}
+		else
+		{
+			if(State() == NET_CONNSTATE_OFFLINE)
+			{
+				if(CtrlMsg == NET_CTRLMSG_CONNECT)
+				{
+					/* send response and init connection */
+					Reset();
+					m_State = NET_CONNSTATE_PENDING;
+					m_PeerAddr = *pAddr;
+					m_LastSendTime = now;
+					m_LastRecvTime = now;
+					m_LastUpdateTime = now;
+					SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0);
+					if(config.debug)
+						dbg_msg("connection", "got connection, sending connect+accept");			
+				}
+			}
+			else if(State() == NET_CONNSTATE_CONNECT)
+			{
+				/* connection made */
+				if(CtrlMsg == NET_CTRLMSG_CONNECTACCEPT)
+				{
+					SendControl(NET_CTRLMSG_ACCEPT, 0, 0);
+					m_State = NET_CONNSTATE_ONLINE;
+					if(config.debug)
+						dbg_msg("connection", "got connect+accept, sending accept. connection online");
+				}
+			}
+			else if(State() == NET_CONNSTATE_ONLINE)
+			{
+				/* connection made */
+				/*
+				if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT)
+				{
+					
+				}*/
+			}
+		}
+	}
+	else
+	{
+		if(State() == NET_CONNSTATE_PENDING)
+		{
+			m_State = NET_CONNSTATE_ONLINE;
+			if(config.debug)
+				dbg_msg("connection", "connecting online");
+		}
+	}
+	
+	if(State() == NET_CONNSTATE_ONLINE)
+	{
+		AckChunks(pPacket->m_Ack);
+	}
+	
+	return 1;
+}
+
+int CNetConnection::Update()
+{
+	int64 now = time_get();
+
+	if(State() == NET_CONNSTATE_OFFLINE || State() == NET_CONNSTATE_ERROR)
+		return 0;
+	
+	/* check for timeout */
+	if(State() != NET_CONNSTATE_OFFLINE &&
+		State() != NET_CONNSTATE_CONNECT &&
+		(now-m_LastRecvTime) > time_freq()*10)
+	{
+		m_State = NET_CONNSTATE_ERROR;
+		SetError("timeout");
+	}
+
+	/* fix resends */
+	if(m_Buffer.First())
+	{
+		CNetChunkResend *pResend = m_Buffer.First();
+
+		/* check if we have some really old stuff laying around and abort if not acked */
+		if(now-pResend->m_FirstSendTime > time_freq()*10)
+		{
+			m_State = NET_CONNSTATE_ERROR;
+			SetError("too weak connection (not acked for 10 seconds)");
+		}
+		else
+		{
+			/* resend packet if we havn't got it acked in 1 second */
+			if(now-pResend->m_LastSendTime > time_freq())
+				ResendChunk(pResend);
+		}
+	}
+	
+	/* send keep alives if nothing has happend for 250ms */
+	if(State() == NET_CONNSTATE_ONLINE)
+	{
+		if(time_get()-m_LastSendTime > time_freq()/2) /* flush connection after 500ms if needed */
+		{
+			int NumFlushedChunks = Flush();
+			if(NumFlushedChunks && config.debug)
+				dbg_msg("connection", "flushed connection due to timeout. %d chunks.", NumFlushedChunks);
+		}
+			
+		if(time_get()-m_LastSendTime > time_freq())
+			SendControl(NET_CTRLMSG_KEEPALIVE, 0, 0);
+	}
+	else if(State() == NET_CONNSTATE_CONNECT)
+	{
+		if(time_get()-m_LastSendTime > time_freq()/2) /* send a new connect every 500ms */
+			SendControl(NET_CTRLMSG_CONNECT, 0, 0);
+	}
+	else if(State() == NET_CONNSTATE_PENDING)
+	{
+		if(time_get()-m_LastSendTime > time_freq()/2) /* send a new connect/accept every 500ms */
+			SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0);
+	}
+	
+	return 0;
+}
diff --git a/src/engine/e_network_internal.h b/src/engine/e_network_internal.h
deleted file mode 100644
index f1a25b31..00000000
--- a/src/engine/e_network_internal.h
+++ /dev/null
@@ -1,156 +0,0 @@
-#include <base/system.h>
-#include "e_network.h"
-#include "e_ringbuffer.h"
-
-/*
-
-CURRENT:
-	packet header: 3 bytes
-		unsigned char flags_ack; // 4bit flags, 4bit ack
-		unsigned char ack; // 8 bit ack
-		unsigned char num_chunks; // 8 bit chunks
-		
-		(unsigned char padding[3])	// 24 bit extra incase it's a connection less packet
-									// this is to make sure that it's compatible with the
-									// old protocol
-
-	chunk header: 2-3 bytes
-		unsigned char flags_size; // 2bit flags, 6 bit size
-		unsigned char size_seq; // 4bit size, 4bit seq
-		(unsigned char seq;) // 8bit seq, if vital flag is set
-
-
-*/
-
-enum
-{
-	NET_VERSION = 2,
-
-	NET_MAX_CHUNKSIZE = 1024,
-	NET_MAX_PAYLOAD = NET_MAX_CHUNKSIZE+16,
-	NET_MAX_PACKETSIZE = NET_MAX_PAYLOAD+16,
-	NET_MAX_CHUNKHEADERSIZE = 5,
-	NET_PACKETHEADERSIZE = 3,
-	NET_MAX_CLIENTS = 16,
-	NET_MAX_SEQUENCE = 1<<10,
-	NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1,
-
-	NET_CONNSTATE_OFFLINE=0,
-	NET_CONNSTATE_CONNECT=1,
-	NET_CONNSTATE_PENDING=2,
-	NET_CONNSTATE_ONLINE=3,
-	NET_CONNSTATE_ERROR=4,
-
-	NET_PACKETFLAG_CONTROL=1,
-	NET_PACKETFLAG_CONNLESS=2,
-	NET_PACKETFLAG_RESEND=4,
-	NET_PACKETFLAG_COMPRESSION=8,
-
-	NET_CHUNKFLAG_VITAL=1,
-	NET_CHUNKFLAG_RESEND=2,
-	
-	NET_CTRLMSG_KEEPALIVE=0,
-	NET_CTRLMSG_CONNECT=1,
-	NET_CTRLMSG_CONNECTACCEPT=2,
-	NET_CTRLMSG_ACCEPT=3,
-	NET_CTRLMSG_CLOSE=4,
-	
-	NET_SERVER_MAXBANS=1024,
-	
-	NET_CONN_BUFFERSIZE=1024*16,
-	
-	NET_ENUM_TERMINATOR
-};
-
-
-typedef struct NETPACKETCONSTRUCT
-{
-	int flags;
-	int ack;
-	int num_chunks;
-	int data_size;
-	unsigned char chunk_data[NET_MAX_PAYLOAD];
-} NETPACKETCONSTRUCT;
-
-typedef struct NETCHUNKHEADER
-{
-	int flags;
-	int size;
-	int sequence;
-} NETCHUNKHEADER;
-
-typedef struct
-{
-	int flags;
-	int data_size;
-	unsigned char *data;
-
-	int sequence;
-	int64 last_send_time;
-	int64 first_send_time;
-} NETCHUNKDATA;
-
-typedef struct
-{
-	unsigned short seq;
-	unsigned short ack;
-	unsigned state;
-	
-	int token;
-	int remote_closed;
-	
-	RINGBUFFER *buffer;
-	
-	int64 last_update_time;
-	int64 last_recv_time;
-	int64 last_send_time;
-	
-	char error_string[256];
-	
-	NETPACKETCONSTRUCT construct;
-	
-	NETADDR peeraddr;
-	NETSOCKET socket;
-	NETSTATS stats;
-
-	char buffer_memory[NET_CONN_BUFFERSIZE];
-} NETCONNECTION;
-
-typedef struct NETRECVINFO
-{
-	NETADDR addr;
-	NETCONNECTION *conn;
-	int current_chunk;
-	int client_id;
-	int valid;
-	NETPACKETCONSTRUCT data;
-	unsigned char buffer[NET_MAX_PACKETSIZE];
-} NETRECVINFO;
-
-/* */
-
-/* connection functions */
-void conn_init(NETCONNECTION *conn, NETSOCKET socket);
-int conn_connect(NETCONNECTION *conn, NETADDR *addr);
-void conn_disconnect(NETCONNECTION *conn, const char *reason);
-int conn_update(NETCONNECTION *conn);
-int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr);
-void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data);
-const char *conn_error(NETCONNECTION *conn);
-void conn_want_resend(NETCONNECTION *conn);
-int conn_flush(NETCONNECTION *conn);
-
-/* recvinfo functions */
-void recvinfo_clear(NETRECVINFO *info);
-void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid);
-int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk);
-
-/* misc helper functions */
-/* The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not */
-int seq_in_backroom(int seq, int ack);
-void send_controlmsg(NETSOCKET socket, NETADDR *addr, int ack, int controlmsg, const void *extra, int extra_size);
-void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size);
-void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet);
-int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet);
-unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence);
-unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header);
diff --git a/src/engine/e_network_server.c b/src/engine/e_network_server.c
deleted file mode 100644
index 8ec65504..00000000
--- a/src/engine/e_network_server.c
+++ /dev/null
@@ -1,484 +0,0 @@
-#include <base/system.h>
-#include "e_network.h"
-#include "e_network_internal.h"
-
-typedef struct
-{
-	NETCONNECTION conn;
-} NETSLOT;
-
-typedef struct NETBAN
-{
-	NETBANINFO info;
-	
-	/* hash list */
-	struct NETBAN *hashnext;
-	struct NETBAN *hashprev;
-	
-	/* used or free list */
-	struct NETBAN *next;
-	struct NETBAN *prev;
-} NETBAN;
-
-#define MACRO_LIST_LINK_FIRST(object, first, prev, next) \
-	{ if(first) first->prev = object; \
-	object->prev = (void*)0; \
-	object->next = first; \
-	first = object; }
-	
-#define MACRO_LIST_LINK_AFTER(object, after, prev, next) \
-	{ object->prev = after; \
-	object->next = after->next; \
-	after->next = object; \
-	if(object->next) \
-		object->next->prev = object; \
-	}
-
-#define MACRO_LIST_UNLINK(object, first, prev, next) \
-	{ if(object->next) object->next->prev = object->prev; \
-	if(object->prev) object->prev->next = object->next; \
-	else first = object->next; \
-	object->next = 0; object->prev = 0; }
-	
-#define MACRO_LIST_FIND(start, next, expression) \
-	{ while(start && !(expression)) start = start->next; }
-
-struct NETSERVER
-{
-	NETSOCKET socket;
-	NETSLOT slots[NET_MAX_CLIENTS];
-	int max_clients;
-
-	NETBAN *bans[256];
-	NETBAN banpool[NET_SERVER_MAXBANS];
-	NETBAN *banpool_firstfree;
-	NETBAN *banpool_firstused;
-
-	NETFUNC_NEWCLIENT new_client;
-	NETFUNC_NEWCLIENT del_client;
-	void *user_ptr;
-	
-	NETRECVINFO recv;
-};
-
-NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags)
-{
-	int i;
-	NETSERVER *server;
-	NETSOCKET socket = net_udp_create(bindaddr);
-	if(socket == NETSOCKET_INVALID)
-		return 0;
-	
-	server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1);
-	mem_zero(server, sizeof(NETSERVER));
-	server->socket = socket;
-	server->max_clients = max_clients;
-	if(server->max_clients > NET_MAX_CLIENTS)
-		server->max_clients = NET_MAX_CLIENTS;
-	if(server->max_clients < 1)
-		server->max_clients = 1;
-	
-	for(i = 0; i < NET_MAX_CLIENTS; i++)
-		conn_init(&server->slots[i].conn, server->socket);
-	
-	/* setup all pointers for bans */
-	for(i = 1; i < NET_SERVER_MAXBANS-1; i++)
-	{
-		server->banpool[i].next = &server->banpool[i+1];
-		server->banpool[i].prev = &server->banpool[i-1];
-	}
-	
-	server->banpool[0].next = &server->banpool[1];
-	server->banpool[NET_SERVER_MAXBANS-1].prev = &server->banpool[NET_SERVER_MAXBANS-2];
-	server->banpool_firstfree = &server->banpool[0];
-
-	return server;
-}
-
-int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user)
-{
-	s->new_client = new_client;
-	s->del_client = del_client;
-	s->user_ptr = user;
-	return 0;
-}
-
-int netserver_max_clients(NETSERVER *s)
-{
-	return s->max_clients;
-}
-
-int netserver_close(NETSERVER *s)
-{
-	/* TODO: implement me */
-	return 0;
-}
-
-int netserver_drop(NETSERVER *s, int client_id, const char *reason)
-{
-	/* TODO: insert lots of checks here */
-	NETADDR addr;
-	netserver_client_addr(s, client_id, &addr);
-	
-	dbg_msg("net_server", "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"",
-		client_id,
-		addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3],
-		reason
-		);
-	conn_disconnect(&s->slots[client_id].conn, reason);
-
-	if(s->del_client)
-		s->del_client(client_id, s->user_ptr);
-		
-	return 0;
-}
-
-int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info)
-{
-	NETBAN *ban;
-	for(ban = s->banpool_firstused; ban && index; ban = ban->next, index--)
-		{}
-		
-	if(!ban)
-		return 0;
-	*info = ban->info;
-	return 1;
-}
-
-int netserver_ban_num(NETSERVER *s)
-{
-	int count = 0;
-	NETBAN *ban;
-	for(ban = s->banpool_firstused; ban; ban = ban->next)
-		count++;
-	return count;
-}
-
-static void netserver_ban_remove_by_object(NETSERVER *s, NETBAN *ban)
-{
-	int iphash = (ban->info.addr.ip[0]+ban->info.addr.ip[1]+ban->info.addr.ip[2]+ban->info.addr.ip[3])&0xff;
-	dbg_msg("netserver", "removing ban on %d.%d.%d.%d",
-		ban->info.addr.ip[0], ban->info.addr.ip[1], ban->info.addr.ip[2], ban->info.addr.ip[3]);
-	MACRO_LIST_UNLINK(ban, s->banpool_firstused, prev, next);
-	MACRO_LIST_UNLINK(ban, s->bans[iphash], hashprev, hashnext);
-	MACRO_LIST_LINK_FIRST(ban, s->banpool_firstfree, prev, next);
-}
-
-int netserver_ban_remove(NETSERVER *s, NETADDR addr)
-{
-	int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff;
-	NETBAN *ban = s->bans[iphash];
-	
-	MACRO_LIST_FIND(ban, hashnext, net_addr_comp(&ban->info.addr, &addr) == 0);
-	
-	if(ban)
-	{
-		netserver_ban_remove_by_object(s, ban);
-		return 0;
-	}
-	
-	return -1;
-}
-
-int netserver_ban_add(NETSERVER *s, NETADDR addr, int seconds)
-{
-	int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff;
-	unsigned stamp = 0xffffffff;
-	NETBAN *ban;
-	
-	/* remove the port */
-	addr.port = 0;
-	
-	if(seconds)
-		stamp = time_timestamp() + seconds;
-		
-	/* search to see if it already exists */
-	ban = s->bans[iphash];
-	MACRO_LIST_FIND(ban, hashnext, net_addr_comp(&ban->info.addr, &addr) == 0);
-	if(ban)
-	{
-		/* adjust the ban */
-		ban->info.expires = stamp;
-		return 0;
-	}
-	
-	if(!s->banpool_firstfree)
-		return -1;
-
-	/* fetch and clear the new ban */
-	ban = s->banpool_firstfree;
-	MACRO_LIST_UNLINK(ban, s->banpool_firstfree, prev, next);
-	
-	/* setup the ban info */
-	ban->info.expires = stamp;
-	ban->info.addr = addr;
-	
-	/* add it to the ban hash */
-	MACRO_LIST_LINK_FIRST(ban, s->bans[iphash], hashprev, hashnext);
-	
-	/* insert it into the used list */
-	{
-		if(s->banpool_firstused)
-		{
-			NETBAN *insert_after = s->banpool_firstused;
-			MACRO_LIST_FIND(insert_after, next, stamp < insert_after->info.expires);
-			
-			if(insert_after)
-				insert_after = insert_after->prev;
-			else
-			{
-				/* add to last */
-				insert_after = s->banpool_firstused;
-				while(insert_after->next)
-					insert_after = insert_after->next;
-			}
-			
-			if(insert_after)
-			{
-				MACRO_LIST_LINK_AFTER(ban, insert_after, prev, next);
-			}
-			else
-			{
-				MACRO_LIST_LINK_FIRST(ban, s->banpool_firstused, prev, next);
-			}
-		}
-		else
-		{
-			MACRO_LIST_LINK_FIRST(ban, s->banpool_firstused, prev, next);
-		}
-	}
-
-	/* drop banned clients */	
-	{
-		char buf[128];
-		int i;
-		NETADDR banaddr;
-		
-		if(seconds)
-			str_format(buf, sizeof(buf), "you have been banned for %d minutes", seconds/60);
-		else
-			str_format(buf, sizeof(buf), "you have been banned for life");
-		
-		for(i = 0; i < s->max_clients; i++)
-		{
-			banaddr = s->slots[i].conn.peeraddr;
-			banaddr.port = 0;
-			
-			if(net_addr_comp(&addr, &banaddr) == 0)
-				netserver_drop(s, i, buf);
-		}
-	}
-	return 0;
-}
-
-int netserver_update(NETSERVER *s)
-{
-	unsigned now = time_timestamp();
-	
-	int i;
-	for(i = 0; i < s->max_clients; i++)
-	{
-		conn_update(&s->slots[i].conn);
-		if(s->slots[i].conn.state == NET_CONNSTATE_ERROR)
-			netserver_drop(s, i, conn_error(&s->slots[i].conn));
-	}
-	
-	/* remove expired bans */
-	while(s->banpool_firstused && s->banpool_firstused->info.expires < now)
-	{
-		NETBAN *ban = s->banpool_firstused;
-		netserver_ban_remove_by_object(s, ban);
-	}
-	
-	(void)now;
-	
-	return 0;
-}
-
-/*
-	TODO: chopp up this function into smaller working parts
-*/
-int netserver_recv(NETSERVER *s, NETCHUNK *chunk)
-{
-	unsigned now = time_timestamp();
-	
-	while(1)
-	{
-		NETADDR addr;
-		int i, bytes, found;
-			
-		/* check for a chunk */
-		if(recvinfo_fetch_chunk(&s->recv, chunk))
-			return 1;
-		
-		/* TODO: empty the recvinfo */
-		bytes = net_udp_recv(s->socket, &addr, s->recv.buffer, NET_MAX_PACKETSIZE);
-
-		/* no more packets for now */
-		if(bytes <= 0)
-			break;
-		
-		if(unpack_packet(s->recv.buffer, bytes, &s->recv.data) == 0)
-		{
-			NETBAN *ban = 0;
-			NETADDR banaddr = addr;
-			int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff;
-			found = 0;
-			banaddr.port = 0;
-			
-			/* search a ban */
-			for(ban = s->bans[iphash]; ban; ban = ban->hashnext)
-			{
-				if(net_addr_comp(&ban->info.addr, &banaddr) == 0)
-					break;
-			}
-			
-			/* check if we just should drop the packet */
-			if(ban)
-			{
-				// banned, reply with a message
-				char banstr[128];
-				if(ban->info.expires)
-				{
-					int mins = ((ban->info.expires - now)+59)/60;
-					if(mins == 1)
-						str_format(banstr, sizeof(banstr), "banned for %d minute", mins);
-					else
-						str_format(banstr, sizeof(banstr), "banned for %d minutes", mins);
-				}
-				else
-					str_format(banstr, sizeof(banstr), "banned for life");
-				send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, banstr, str_length(banstr)+1);
-				continue;
-			}
-			
-			if(s->recv.data.flags&NET_PACKETFLAG_CONNLESS)
-			{
-				chunk->flags = NETSENDFLAG_CONNLESS;
-				chunk->client_id = -1;
-				chunk->address = addr;
-				chunk->data_size = s->recv.data.data_size;
-				chunk->data = s->recv.data.chunk_data;
-				return 1;
-			}
-			else
-			{			
-				/* TODO: check size here */
-				if(s->recv.data.flags&NET_PACKETFLAG_CONTROL && s->recv.data.chunk_data[0] == NET_CTRLMSG_CONNECT)
-				{
-					found = 0;
-				
-					/* check if we already got this client */
-					for(i = 0; i < s->max_clients; i++)
-					{
-						if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE &&
-							net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0)
-						{
-							found = 1; /* silent ignore.. we got this client already */
-							break;
-						}
-					}
-					
-					/* client that wants to connect */
-					if(!found)
-					{
-						for(i = 0; i < s->max_clients; i++)
-						{
-							if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE)
-							{
-								found = 1;
-								conn_feed(&s->slots[i].conn, &s->recv.data, &addr);
-								if(s->new_client)
-									s->new_client(i, s->user_ptr);
-								break;
-							}
-						}
-						
-						if(!found)
-						{
-							const char fullmsg[] = "server is full";
-							send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, fullmsg, sizeof(fullmsg));
-						}
-					}
-				}
-				else
-				{
-					/* normal packet, find matching slot */
-					for(i = 0; i < s->max_clients; i++)
-					{
-						if(net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0)
-						{
-							if(conn_feed(&s->slots[i].conn, &s->recv.data, &addr))
-							{
-								if(s->recv.data.data_size)
-									recvinfo_start(&s->recv, &addr, &s->slots[i].conn, i);
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-	return 0;
-}
-
-int netserver_send(NETSERVER *s, NETCHUNK *chunk)
-{
-	if(chunk->data_size >= NET_MAX_PAYLOAD)
-	{
-		dbg_msg("netserver", "packet payload too big. %d. dropping packet", chunk->data_size);
-		return -1;
-	}
-	
-	if(chunk->flags&NETSENDFLAG_CONNLESS)
-	{
-		/* send connectionless packet */
-		send_packet_connless(s->socket, &chunk->address, chunk->data, chunk->data_size);
-	}
-	else
-	{
-		int f = 0;
-		dbg_assert(chunk->client_id >= 0, "errornous client id");
-		dbg_assert(chunk->client_id < s->max_clients, "errornous client id");
-		
-		if(chunk->flags&NETSENDFLAG_VITAL)
-			f = NET_CHUNKFLAG_VITAL;
-		
-		conn_queue_chunk(&s->slots[chunk->client_id].conn, f, chunk->data_size, chunk->data);
-
-		if(chunk->flags&NETSENDFLAG_FLUSH)
-			conn_flush(&s->slots[chunk->client_id].conn);
-	}
-	return 0;
-}
-
-void netserver_stats(NETSERVER *s, NETSTATS *stats)
-{
-	int num_stats = sizeof(NETSTATS)/sizeof(int);
-	int *istats = (int *)stats;
-	int c, i;
-
-	mem_zero(stats, sizeof(NETSTATS));
-	
-	for(c = 0; c < s->max_clients; c++)
-	{
-		if(s->slots[c].conn.state != NET_CONNSTATE_OFFLINE)
-		{
-			int *sstats = (int *)(&(s->slots[c].conn.stats));
-			for(i = 0; i < num_stats; i++)
-				istats[i] += sstats[i];
-		}
-	}
-}
-
-NETSOCKET netserver_socket(NETSERVER *s)
-{
-	return s->socket;
-}
-
-
-int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr)
-{
-	*addr = s->slots[client_id].conn.peeraddr;
-	return 1;
-}
diff --git a/src/engine/e_network_server.cpp b/src/engine/e_network_server.cpp
new file mode 100644
index 00000000..995290ef
--- /dev/null
+++ b/src/engine/e_network_server.cpp
@@ -0,0 +1,408 @@
+#include <base/system.h>
+#include "e_network.h"
+
+#define MACRO_LIST_LINK_FIRST(object, first, prev, next) \
+	{ if(first) first->prev = object; \
+	object->prev = (struct CBan *)0; \
+	object->next = first; \
+	first = object; }
+	
+#define MACRO_LIST_LINK_AFTER(object, after, prev, next) \
+	{ object->prev = after; \
+	object->next = after->next; \
+	after->next = object; \
+	if(object->next) \
+		object->next->prev = object; \
+	}
+
+#define MACRO_LIST_UNLINK(object, first, prev, next) \
+	{ if(object->next) object->next->prev = object->prev; \
+	if(object->prev) object->prev->next = object->next; \
+	else first = object->next; \
+	object->next = 0; object->prev = 0; }
+	
+#define MACRO_LIST_FIND(start, next, expression) \
+	{ while(start && !(expression)) start = start->next; }
+
+bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int Flags)
+{
+	// zero out the whole structure
+	mem_zero(this, sizeof(*this));
+	
+	// open socket
+	m_Socket = net_udp_create(BindAddr);
+	if(m_Socket == NETSOCKET_INVALID)
+		return false;
+	
+	// clamp clients
+	m_MaxClients = MaxClients;
+	if(m_MaxClients > NET_MAX_CLIENTS)
+		m_MaxClients = NET_MAX_CLIENTS;
+	if(m_MaxClients < 1)
+		m_MaxClients = 1;
+	
+	for(int i = 0; i < NET_MAX_CLIENTS; i++)
+		m_aSlots[i].m_Connection.Init(m_Socket);
+	
+	/* setup all pointers for bans */
+	for(int i = 1; i < NET_SERVER_MAXBANS-1; i++)
+	{
+		m_BanPool[i].m_pNext = &m_BanPool[i+1];
+		m_BanPool[i].m_pPrev = &m_BanPool[i-1];
+	}
+	
+	m_BanPool[0].m_pNext = &m_BanPool[1];
+	m_BanPool[NET_SERVER_MAXBANS-1].m_pPrev = &m_BanPool[NET_SERVER_MAXBANS-2];
+	m_BanPool_FirstFree = &m_BanPool[0];
+
+	return true;
+}
+
+int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
+{
+	m_pfnNewClient = pfnNewClient;
+	m_pfnDelClient = pfnDelClient;
+	m_UserPtr = pUser;
+	return 0;
+}
+
+int CNetServer::Close()
+{
+	/* TODO: implement me */
+	return 0;
+}
+
+int CNetServer::Drop(int ClientID, const char *pReason)
+{
+	/* TODO: insert lots of checks here */
+	NETADDR Addr = ClientAddr(ClientID);
+
+	dbg_msg("net_server", "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"",
+		ClientID,
+		Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3],
+		pReason
+		);
+		
+	m_aSlots[ClientID].m_Connection.Disconnect(pReason);
+
+	if(m_pfnDelClient)
+		m_pfnDelClient(ClientID, m_UserPtr);
+		
+	return 0;
+}
+
+int CNetServer::BanGet(int Index, CBanInfo *pInfo)
+{
+	CBan *pBan;
+	for(pBan = m_BanPool_FirstUsed; pBan && Index; pBan = pBan->m_pNext, Index--)
+		{}
+		
+	if(!pBan)
+		return 0;
+	*pInfo = pBan->m_Info;
+	return 1;
+}
+
+int CNetServer::BanNum()
+{
+	int Count = 0;
+	CBan *pBan;
+	for(pBan = m_BanPool_FirstUsed; pBan; pBan = pBan->m_pNext)
+		Count++;
+	return Count;
+}
+
+void CNetServer::BanRemoveByObject(CBan *pBan)
+{
+	int iphash = (pBan->m_Info.m_Addr.ip[0]+pBan->m_Info.m_Addr.ip[1]+pBan->m_Info.m_Addr.ip[2]+pBan->m_Info.m_Addr.ip[3])&0xff;
+	dbg_msg("netserver", "removing ban on %d.%d.%d.%d",
+		pBan->m_Info.m_Addr.ip[0], pBan->m_Info.m_Addr.ip[1], pBan->m_Info.m_Addr.ip[2], pBan->m_Info.m_Addr.ip[3]);
+	MACRO_LIST_UNLINK(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext);
+	MACRO_LIST_UNLINK(pBan, m_aBans[iphash], m_pHashPrev, m_pHashNext);
+	MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext);
+}
+
+int CNetServer::BanRemove(NETADDR Addr)
+{
+	int iphash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff;
+	CBan *pBan = m_aBans[iphash];
+	
+	MACRO_LIST_FIND(pBan, m_pHashNext, net_addr_comp(&pBan->m_Info.m_Addr, &Addr) == 0);
+	
+	if(pBan)
+	{
+		BanRemoveByObject(pBan);
+		return 0;
+	}
+	
+	return -1;
+}
+
+int CNetServer::BanAdd(NETADDR Addr, int Seconds)
+{
+	int iphash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff;
+	int Stamp = -1;
+	CBan *pBan;
+	
+	/* remove the port */
+	Addr.port = 0;
+	
+	if(Seconds)
+		Stamp = time_timestamp() + Seconds;
+		
+	/* search to see if it already exists */
+	pBan = m_aBans[iphash];
+	MACRO_LIST_FIND(pBan, m_pHashNext, net_addr_comp(&pBan->m_Info.m_Addr, &Addr) == 0);
+	if(pBan)
+	{
+		/* adjust the ban */
+		pBan->m_Info.m_Expires = Stamp;
+		return 0;
+	}
+	
+	if(!m_BanPool_FirstFree)
+		return -1;
+
+	/* fetch and clear the new ban */
+	pBan = m_BanPool_FirstFree;
+	MACRO_LIST_UNLINK(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext);
+	
+	/* setup the ban info */
+	pBan->m_Info.m_Expires = Stamp;
+	pBan->m_Info.m_Addr = Addr;
+	
+	/* add it to the ban hash */
+	MACRO_LIST_LINK_FIRST(pBan, m_aBans[iphash], m_pHashPrev, m_pHashNext);
+	
+	/* insert it into the used list */
+	{
+		if(m_BanPool_FirstUsed)
+		{
+			CBan *pInsertAfter = m_BanPool_FirstUsed;
+			MACRO_LIST_FIND(pInsertAfter, m_pNext, Stamp < pInsertAfter->m_Info.m_Expires);
+			
+			if(pInsertAfter)
+				pInsertAfter = pInsertAfter->m_pPrev;
+			else
+			{
+				/* add to last */
+				pInsertAfter = m_BanPool_FirstUsed;
+				while(pInsertAfter->m_pNext)
+					pInsertAfter = pInsertAfter->m_pNext;
+			}
+			
+			if(pInsertAfter)
+			{
+				MACRO_LIST_LINK_AFTER(pBan, pInsertAfter, m_pPrev, m_pNext);
+			}
+			else
+			{
+				MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext);
+			}
+		}
+		else
+		{
+			MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext);
+		}
+	}
+
+	/* drop banned clients */	
+	{
+		char Buf[128];
+		NETADDR BanAddr;
+		
+		if(Seconds)
+			str_format(Buf, sizeof(Buf), "you have been banned for %d minutes", Seconds/60);
+		else
+			str_format(Buf, sizeof(Buf), "you have been banned for life");
+		
+		for(int i = 0; i < MaxClients(); i++)
+		{
+			BanAddr = m_aSlots[i].m_Connection.PeerAddress();
+			BanAddr.port = 0;
+			
+			if(net_addr_comp(&Addr, &BanAddr) == 0)
+				Drop(i, Buf);
+		}
+	}
+	return 0;
+}
+
+int CNetServer::Update()
+{
+	int Now = time_timestamp();
+	for(int i = 0; i < MaxClients(); i++)
+	{
+		m_aSlots[i].m_Connection.Update();
+		if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR)
+			Drop(i, m_aSlots[i].m_Connection.ErrorString());
+	}
+	
+	/* remove expired bans */
+	while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires < Now)
+	{
+		CBan *pBan = m_BanPool_FirstUsed;
+		BanRemoveByObject(pBan);
+	}
+	
+	return 0;
+}
+
+/*
+	TODO: chopp up this function into smaller working parts
+*/
+int CNetServer::Recv(CNetChunk *pChunk)
+{
+	unsigned now = time_timestamp();
+	
+	while(1)
+	{
+		NETADDR Addr;
+			
+		/* check for a chunk */
+		if(m_RecvUnpacker.FetchChunk(pChunk))
+			return 1;
+		
+		/* TODO: empty the recvinfo */
+		int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE);
+
+		/* no more packets for now */
+		if(Bytes <= 0)
+			break;
+		
+		if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0)
+		{
+			CBan *pBan = 0;
+			NETADDR BanAddr = Addr;
+			int iphash = (BanAddr.ip[0]+BanAddr.ip[1]+BanAddr.ip[2]+BanAddr.ip[3])&0xff;
+			int Found = 0;
+			BanAddr.port = 0;
+			
+			/* search a ban */
+			for(pBan = m_aBans[iphash]; pBan; pBan = pBan->m_pHashNext)
+			{
+				if(net_addr_comp(&pBan->m_Info.m_Addr, &BanAddr) == 0)
+					break;
+			}
+			
+			/* check if we just should drop the packet */
+			if(pBan)
+			{
+				// banned, reply with a message
+				char BanStr[128];
+				if(pBan->m_Info.m_Expires)
+				{
+					int Mins = ((pBan->m_Info.m_Expires - now)+59)/60;
+					if(Mins == 1)
+						str_format(BanStr, sizeof(BanStr), "banned for %d minute", Mins);
+					else
+						str_format(BanStr, sizeof(BanStr), "banned for %d minutes", Mins);
+				}
+				else
+					str_format(BanStr, sizeof(BanStr), "banned for life");
+				CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, BanStr, str_length(BanStr)+1);
+				continue;
+			}
+			
+			if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS)
+			{
+				pChunk->m_Flags = NETSENDFLAG_CONNLESS;
+				pChunk->m_ClientID = -1;
+				pChunk->m_Address = Addr;
+				pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize;
+				pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData;
+				return 1;
+			}
+			else
+			{			
+				/* TODO: check size here */
+				if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT)
+				{
+					Found = 0;
+				
+					/* check if we already got this client */
+					for(int i = 0; i < MaxClients(); i++)
+					{
+						NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress();
+						if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE &&
+							net_addr_comp(&PeerAddr, &Addr) == 0)
+						{
+							Found = 1; /* silent ignore.. we got this client already */
+							break;
+						}
+					}
+					
+					/* client that wants to connect */
+					if(!Found)
+					{
+						for(int i = 0; i < MaxClients(); i++)
+						{
+							if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE)
+							{
+								Found = 1;
+								m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr);
+								if(m_pfnNewClient)
+									m_pfnNewClient(i, m_UserPtr);
+								break;
+							}
+						}
+						
+						if(!Found)
+						{
+							const char FullMsg[] = "server is full";
+							CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg));
+						}
+					}
+				}
+				else
+				{
+					/* normal packet, find matching slot */
+					for(int i = 0; i < MaxClients(); i++)
+					{
+						NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress();
+						if(net_addr_comp(&PeerAddr, &Addr) == 0)
+						{
+							if(m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr))
+							{
+								if(m_RecvUnpacker.m_Data.m_DataSize)
+									m_RecvUnpacker.Start(&Addr, &m_aSlots[i].m_Connection, i);
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+int CNetServer::Send(CNetChunk *pChunk)
+{
+	if(pChunk->m_DataSize >= NET_MAX_PAYLOAD)
+	{
+		dbg_msg("netserver", "packet payload too big. %d. dropping packet", pChunk->m_DataSize);
+		return -1;
+	}
+	
+	if(pChunk->m_Flags&NETSENDFLAG_CONNLESS)
+	{
+		/* send connectionless packet */
+		CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize);
+	}
+	else
+	{
+		int Flags = 0;
+		dbg_assert(pChunk->m_ClientID >= 0, "errornous client id");
+		dbg_assert(pChunk->m_ClientID < MaxClients(), "errornous client id");
+		
+		if(pChunk->m_Flags&NETSENDFLAG_VITAL)
+			Flags = NET_CHUNKFLAG_VITAL;
+		
+		m_aSlots[pChunk->m_ClientID].m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData);
+
+		if(pChunk->m_Flags&NETSENDFLAG_FLUSH)
+			m_aSlots[pChunk->m_ClientID].m_Connection.Flush();
+	}
+	return 0;
+}
+
diff --git a/src/engine/e_packer.c b/src/engine/e_packer.c
deleted file mode 100644
index aee08aed..00000000
--- a/src/engine/e_packer.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <stdlib.h> /* rand() */
-#include <base/system.h>
-
-#include "e_packer.h"
-#include "e_compression.h"
-#include "e_engine.h"
-#include "e_config.h"
-
-/* useful for debugging */
-#if 0
-	#define packing_error(p) p->error = 1; dbg_break()
-#else
-	#define packing_error(p) p->error = 1
-#endif
-
-static int stress_get_int()
-{
-	static const int nasty[] = {-1, 0, 1, 66000, -66000, (-1<<31), 0x7fffffff};
-	if(rand()&1)
-		return rand();
-	return nasty[rand()%6];
-}
-
-static const char *stress_get_string(int *size)
-{
-	static char noise[1024];
-	int i;
-	int s;
-	s = (rand()%1024)-1;
-	for(i = 0; i < s; i++)
-		noise[i] = (rand()%254)+1;
-	noise[s] = 0;
-	if(size)
-		*size = s;
-	return noise;
-}
-
-
-static int stress_prob(float probability)
-{
-	if(!config.dbg_stress_network)
-		return 0;
-	if(rand()/(float)RAND_MAX < probability)
-		return 1;
-	return 0;
-}
-
-
-void packer_reset(PACKER *p)
-{
-	p->error = 0;
-	p->current = p->buffer;
-	p->end = p->current + PACKER_BUFFER_SIZE;
-}
-
-void packer_add_int(PACKER *p, int i)
-{
-	if(p->error)
-		return;
-		
-	if(stress_prob(0.025f))
-		i = stress_get_int();
-	
-	/* make sure that we have space enough */
-	if(p->end - p->current < 6)
-	{
-		dbg_break();
-		p->error = 1;
-	}
-	else
-		p->current = vint_pack(p->current, i);
-}
-
-void packer_add_string(PACKER *p, const char *str, int limit)
-{
-	if(p->error)
-		return;
-		
-	if(stress_prob(0.1f))
-	{
-		str = stress_get_string(0);
-		limit = 0;
-	}
-	
-	/* */
-	if(limit > 0)
-	{
-		while(*str && limit != 0)
-		{
-			*p->current++ = *str++;
-			limit--;
-			
-			if(p->current >= p->end)
-			{
-				packing_error(p);
-				break;
-			}
-		}
-		*p->current++ = 0;
-	}
-	else
-	{
-		while(*str)
-		{
-			*p->current++ = *str++;
-
-			if(p->current >= p->end)
-			{
-				packing_error(p);
-				break;
-			}
-		}
-		*p->current++ = 0;
-	}
-}
-
-void packer_add_raw(PACKER *p, const unsigned char *data, int size)
-{
-	if(p->error)
-		return;
-		
-	if(p->current+size >= p->end)
-	{
-		packing_error(p);
-		return;
-	}
-	
-	while(size)
-	{
-		*p->current++ = *data++;
-		size--;
-	}
-}
-
-int packer_size(PACKER *p)
-{
-	return (const unsigned char *)p->current-(const unsigned char *)p->buffer;
-}
-
-const unsigned char *packer_data(PACKER *p)
-{
-	return (const unsigned char *)p->buffer;
-}
-
-void unpacker_reset(UNPACKER *p, const unsigned char *data, int size)
-{
-	p->error = 0;
-	p->start = data;
-	p->end = p->start + size;
-	p->current = p->start;
-}
-
-int unpacker_get_int(UNPACKER *p)
-{
-	int i;
-	if(p->error)
-		return 0;
-	if(p->current >= p->end)
-	{
-		packing_error(p);
-		return 0;
-	}
-	
-	p->current = vint_unpack(p->current, &i);
-	if(p->current > p->end)
-	{
-		packing_error(p);
-		return 0;
-	}
-	return i;
-}
-
-const char *unpacker_get_string(UNPACKER *p)
-{
-	char *ptr;
-	if(p->error || p->current >= p->end)
-		return "";
-		
-	ptr = (char *)p->current;
-	while(*p->current) /* skip the string */
-	{
-		p->current++;
-		if(p->current == p->end)
-		{
-			packing_error(p);
-			return "";
-		}
-	}
-	p->current++;
-	
-	/* sanitize all strings */
-	str_sanitize(ptr);
-	return ptr;
-}
-
-const unsigned char *unpacker_get_raw(UNPACKER *p, int size)
-{
-	const unsigned char *ptr = p->current;
-	if(p->error)
-		return 0;
-	
-	/* check for nasty sizes */
-	if(size < 0 || p->current+size > p->end)
-	{
-		packing_error(p);
-		return 0;
-	}
-
-	/* "unpack" the data */	
-	p->current += size;
-	return ptr;
-}
diff --git a/src/engine/e_packer.cpp b/src/engine/e_packer.cpp
new file mode 100644
index 00000000..0d8aeab3
--- /dev/null
+++ b/src/engine/e_packer.cpp
@@ -0,0 +1,155 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <stdlib.h> /* rand() */
+#include <base/system.h>
+
+#include "e_packer.h"
+#include "e_compression.h"
+#include "e_engine.h"
+#include "e_config.h"
+
+void CPacker::Reset()
+{
+	m_Error = 0;
+	m_pCurrent = m_aBuffer;
+	m_pEnd = m_pCurrent + PACKER_BUFFER_SIZE;
+}
+
+void CPacker::AddInt(int i)
+{
+	if(m_Error)
+		return;
+		
+	/* make sure that we have space enough */
+	if(m_pEnd - m_pCurrent < 6)
+	{
+		dbg_break();
+		m_Error = 1;
+	}
+	else
+		m_pCurrent = vint_pack(m_pCurrent, i);
+}
+
+void CPacker::AddString(const char *pStr, int Limit)
+{
+	if(m_Error)
+		return;
+	
+	/* */
+	if(Limit > 0)
+	{
+		while(*pStr && Limit != 0)
+		{
+			*m_pCurrent++ = *pStr++;
+			Limit--;
+			
+			if(m_pCurrent >= m_pEnd)
+			{
+				m_Error = 1;
+				break;
+			}
+		}
+		*m_pCurrent++ = 0;
+	}
+	else
+	{
+		while(*pStr)
+		{
+			*m_pCurrent++ = *pStr++;
+
+			if(m_pCurrent >= m_pEnd)
+			{
+				m_Error = 1;
+				break;
+			}
+		}
+		*m_pCurrent++ = 0;
+	}
+}
+
+void CPacker::AddRaw(const unsigned char *pData, int Size)
+{
+	if(m_Error)
+		return;
+		
+	if(m_pCurrent+Size >= m_pEnd)
+	{
+		m_Error = 1;
+		return;
+	}
+	
+	while(Size)
+	{
+		*m_pCurrent++ = *pData++;
+		Size--;
+	}
+}
+
+
+void CUnpacker::Reset(const unsigned char *pData, int Size)
+{
+	m_Error = 0;
+	m_pStart = pData;
+	m_pEnd = m_pStart + Size;
+	m_pCurrent = m_pStart;
+}
+
+int CUnpacker::GetInt()
+{
+	if(m_Error)
+		return 0;
+		
+	if(m_pCurrent >= m_pEnd)
+	{
+		m_Error = 1;
+		return 0;
+	}
+	
+	int i;
+	m_pCurrent = vint_unpack(m_pCurrent, &i);
+	if(m_pCurrent > m_pEnd)
+	{
+		m_Error = 1;
+		return 0;
+	}
+	return i;
+}
+
+const char *CUnpacker::GetString()
+{
+	if(m_Error || m_pCurrent >= m_pEnd)
+		return "";
+		
+	char *pPtr = (char *)m_pCurrent;
+	while(*m_pCurrent) /* skip the string */
+	{
+		m_pCurrent++;
+		if(m_pCurrent == m_pEnd)
+		{
+			m_Error = 1;;
+			return "";
+		}
+	}
+	m_pCurrent++;
+	
+	/* sanitize all strings */
+	str_sanitize(pPtr);
+	return pPtr;
+}
+
+const unsigned char *CUnpacker::GetRaw(int Size)
+{
+	const unsigned char *pPtr = m_pCurrent;
+	if(m_Error)
+		return 0;
+	
+	/* check for nasty sizes */
+	if(Size < 0 || m_pCurrent+Size > m_pEnd)
+	{
+		m_Error = 1;
+		return 0;
+	}
+
+	/* "unpack" the data */	
+	m_pCurrent += Size;
+	return pPtr;
+}
diff --git a/src/engine/e_packer.h b/src/engine/e_packer.h
index 71b99ff7..5dc80e7a 100644
--- a/src/engine/e_packer.h
+++ b/src/engine/e_packer.h
@@ -1,35 +1,37 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 
-enum
+class CPacker
 {
-	PACKER_BUFFER_SIZE=1024*2
-};
+	enum
+	{
+		PACKER_BUFFER_SIZE=1024*2
+	};
 
-typedef struct
-{
+	unsigned char m_aBuffer[PACKER_BUFFER_SIZE];
+	unsigned char *m_pCurrent;
+	unsigned char *m_pEnd;
+	int m_Error;
+public:
+	void Reset();
+	void AddInt(int i);
+	void AddString(const char *pStr, int Limit);
+	void AddRaw(const unsigned char *pData, int Size);
 	
-	unsigned char buffer[PACKER_BUFFER_SIZE];
-	unsigned char *current;
-	unsigned char *end;
-	int error;
-} PACKER;
+	int Size() const { return (int)(m_pCurrent-m_aBuffer); }
+	const unsigned char *Data() const { return m_aBuffer; }
+	bool Error() const { return m_Error; }
+};
 
-typedef struct
+class CUnpacker
 {
-	const unsigned char *current;
-	const unsigned char *start;
-	const unsigned char *end;
-	int error;
-} UNPACKER;
-
-void packer_reset(PACKER *p);
-void packer_add_int(PACKER *p, int i);
-void packer_add_string(PACKER *p, const char *str, int limit);
-void packer_add_raw(PACKER *p, const unsigned char *data, int size);
-int packer_size(PACKER *p);
-const unsigned char *packer_data(PACKER *p);
-
-void unpacker_reset(UNPACKER *p, const unsigned char *data, int size);
-int unpacker_get_int(UNPACKER *p);
-const char *unpacker_get_string(UNPACKER *p);
-const unsigned char *unpacker_get_raw(UNPACKER *p, int size);
+	const unsigned char *m_pStart;
+	const unsigned char *m_pCurrent;
+	const unsigned char *m_pEnd;
+	int m_Error;
+public:
+	void Reset(const unsigned char *pData, int Size);
+	int GetInt();
+	const char *GetString();
+	const unsigned char *GetRaw(int Size);
+	bool Error() const { return m_Error; }
+};
diff --git a/src/engine/e_ringbuffer.c b/src/engine/e_ringbuffer.c
deleted file mode 100644
index 39dfa3e9..00000000
--- a/src/engine/e_ringbuffer.c
+++ /dev/null
@@ -1,362 +0,0 @@
-#include <base/system.h>
-
-#include "e_ringbuffer.h"
-
-typedef struct RBITEM
-{
-    struct RBITEM *prev;
-    struct RBITEM *next;
-    int free;
-    int size;
-} RBITEM;
-
-/*
-
-*/
-struct RINGBUFFER
-{
-	RBITEM *produce;
-	RBITEM *consume;
-	
-	RBITEM *first;
-	RBITEM *last;
-	void *memory;
-	int size;
-	int flags;
-};
-
-RINGBUFFER *ringbuf_init(void *memory, int size, int flags)
-{
-	RINGBUFFER *rb = (RINGBUFFER *)memory;
-	mem_zero(memory, size);
-	
-	rb->memory = rb+1;
-	rb->size = (size-sizeof(RINGBUFFER))/sizeof(RBITEM)*sizeof(RBITEM);
-	rb->first = (RBITEM *)rb->memory;
-	rb->first->free = 1;
-	rb->first->size = rb->size;
-	rb->last = rb->first;
-	rb->produce = rb->first;
-	rb->consume = rb->first;
-	
-	rb->flags = flags;
-	
-	return rb;
-}
-
-static RBITEM *ringbuf_nextblock(RINGBUFFER *rb, RBITEM *item)
-{
-	if(item->next)
-		return item->next;
-	return rb->first;
-}
-
-static RBITEM *ringbuf_prevblock(RINGBUFFER *rb, RBITEM *item)
-{
-	if(item->prev)
-		return item->prev;
-	return rb->last;
-}
-
-static RBITEM *ringbuf_mergeback(RINGBUFFER *rb, RBITEM *item)
-{
-	/* make sure that this block and previous block is free */
-	if(!item->free || !item->prev || !item->prev->free)
-		return item;
-
-	/* merge the blocks */
-	item->prev->size += item->size;
-	item->prev->next = item->next;
-	
-	/* fixup pointers */
-	if(item->next)
-		item->next->prev = item->prev;
-	else
-		rb->last = item->prev;
-		
-	if(item == rb->produce)
-		rb->produce = item->prev;
-	
-	if(item == rb->consume)
-		rb->consume = item->prev;
-	
-	/* return the current block */
-	return item->prev;
-}
-
-int ringbuf_popfirst(RINGBUFFER *rb)
-{
-	if(rb->consume->free)
-		return 0;
-	
-	/* set the free flag */
-	rb->consume->free = 1;
-	
-	/* previous block is also free, merge them */
-	rb->consume = ringbuf_mergeback(rb, rb->consume);
-	
-	/* advance the consume pointer */
-	rb->consume = ringbuf_nextblock(rb, rb->consume);
-	while(rb->consume->free && rb->consume != rb->produce)
-	{
-		rb->consume = ringbuf_mergeback(rb, rb->consume);
-		rb->consume = ringbuf_nextblock(rb, rb->consume);
-	}
-		
-	/* in the case that we have catched up with the produce pointer */
-	/* we might stand on a free block so merge em */
-	ringbuf_mergeback(rb, rb->consume);
-	return 1;
-}
-
-void *ringbuf_allocate(RINGBUFFER *rb, int size)
-{
-	int wanted_size = (size+sizeof(RBITEM)+sizeof(RBITEM)-1)/sizeof(RBITEM)*sizeof(RBITEM);
-	RBITEM *block = 0;
-
-	/* check if we even can fit this block */
-	if(wanted_size > rb->size)
-		return 0;
-
-	while(1)	
-	{
-		/* check for space */
-		if(rb->produce->free)
-		{
-			if(rb->produce->size >= wanted_size)
-				block = rb->produce;
-			else
-			{
-				/* wrap around to try to find a block */
-				if(rb->first->free && rb->first->size >= wanted_size)
-					block = rb->first;
-			}
-		}
-		
-		if(block)
-			break;
-		else
-		{
-			/* we have no block, check our policy and see what todo */
-			if(rb->flags&RINGBUF_FLAG_RECYCLE)
-			{
-				if(!ringbuf_popfirst(rb))
-					return 0;
-			}
-			else
-				return 0;
-		}
-	}
-	
-	/* okey, we have our block */
-	
-	/* split the block if needed */
-	if(block->size > wanted_size)
-	{
-		RBITEM *new_item = (RBITEM *)((char *)block + wanted_size);
-		new_item->prev = block;
-		new_item->next = block->next;
-		if(new_item->next)
-			new_item->next->prev = new_item;
-		block->next = new_item;
-		
-		new_item->free = 1;
-		new_item->size = block->size - wanted_size;
-		block->size = wanted_size;
-		
-		if(!new_item->next)
-			rb->last = new_item;
-	}
-	
-	
-	/* set next block */
-	rb->produce = ringbuf_nextblock(rb, block);
-	
-	/* set as used and return the item pointer */
-	block->free = 0;
-	return block+1;
-}
-
-void *ringbuf_prev(RINGBUFFER *rb, void *current)
-{
-	RBITEM *item = ((RBITEM *)current) - 1;
-	
-	while(1)
-	{
-		item = ringbuf_prevblock(rb, item);
-		if(item == rb->produce)
-			return 0;
-		if(!item->free)
-			return item+1;
-	}
-}
-
-void *ringbuf_next(RINGBUFFER *rb, void *current)
-{
-	RBITEM *item = ((RBITEM *)current) - 1;
-	
-	while(1)
-	{
-		item = ringbuf_nextblock(rb, item);
-		if(item == rb->produce)
-			return 0;
-		if(!item->free)
-			return item+1;
-	}
-}
-
-void *ringbuf_first(RINGBUFFER *rb)
-{
-	if(rb->consume->free)
-		return 0;
-	return rb->consume+1;
-}
-
-void *ringbuf_last(RINGBUFFER *rb)
-{
-	if(!rb->produce->free)
-		return rb->produce+1;
-	return ringbuf_prev(rb, rb->produce+1);
-}
-
-
-/* debugging and testing stuff */
-
-static void ringbuf_debugdump(RINGBUFFER *rb, const char *msg)
-{
-	RBITEM *cur = rb->first;
-	
-	dbg_msg("ringbuf", "-- dumping --");
-	
-	while(cur)
-	{
-		char flags[4] = "   ";
-		if(cur->free)
-			flags[0] = 'F';
-		if(cur == rb->consume)
-			flags[1] = '>';
-		if(cur == rb->produce)
-			flags[2] = '<';
-		dbg_msg("ringbuf", "%s %d", flags, cur->size);
-		cur = cur->next;
-	}
-	
-	dbg_msg("ringbuf", "--  --");
-	
-	if(msg)
-		dbg_assert(0, msg);
-}
-
-
-
-static void ringbuf_validate(RINGBUFFER *rb)
-{
-	RBITEM *prev = 0;
-	RBITEM *cur = rb->first;
-	int freechunks = 0;
-	int got_consume = 0;
-	int got_produce = 0;
-	
-	while(cur)
-	{
-		
-		if(cur->free)
-			freechunks++;
-		
-		if(freechunks > 2) ringbuf_debugdump(rb, "too many free chunks");
-		if(prev && prev->free && cur->free) ringbuf_debugdump(rb, "two free chunks next to each other");
-		if(cur == rb->consume) got_consume = 1;
-		if(cur == rb->produce) got_produce = 1;
-
-		dbg_assert(cur->prev == prev, "prev pointers doesn't match");
-		dbg_assert(!prev || prev->next == cur, "next pointers doesn't match");
-		dbg_assert(cur->next || cur == rb->last, "last isn't last");
-
-		prev = cur;
-		cur = cur->next;
-	}
-
-	if(!got_consume) ringbuf_debugdump(rb, "consume pointer isn't pointing at a valid block");
-	if(!got_produce) ringbuf_debugdump(rb, "produce pointer isn't pointing at a valid block");
-}
-
-int ringbuf_test()
-{
-	char buffer[256];
-	RINGBUFFER *rb;
-	int i, s, k, m;
-	int count;
-	int testcount = 0;
-	
-	void *item;
-	int before;
-	
-	
-	for(k = 100; k < sizeof(buffer); k++)
-	{
-		if((k%10) == 0)
-			dbg_msg("ringbuf", "testing at %d", k);
-		rb = ringbuf_init(buffer, k, 0);
-		count = 0;
-		
-		for(s = 1; s < sizeof(buffer); s++)
-		{
-			for(i = 0; i < k*8; i++, testcount++)
-			{
-				for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
-				before = m;
-				
-				if(ringbuf_allocate(rb, s))
-				{
-					count++;
-					for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
-					if(before+1 != m) ringbuf_debugdump(rb, "alloc error");
-					if(count != m) ringbuf_debugdump(rb, "count error");
-				}
-				else
-				{
-					if(ringbuf_popfirst(rb))
-					{
-						count--;
-						
-						for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
-						if(before-1 != m) dbg_msg("", "popping error %d %d", before, m);
-						if(count != m) ringbuf_debugdump(rb, "count error");
-					}
-				}
-				
-				/* remove an item every 10 */
-				if((i%10) == 0)
-				{
-					for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
-					before = m;
-					
-					if(ringbuf_popfirst(rb))
-					{
-						count--;
-						for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
-						if(before-1 != m) dbg_msg("", "popping error %d %d", before, m);
-						dbg_assert(count == m, "count error");
-					}
-				}
-				
-				/* count items */
-				for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
-				if(m != count) ringbuf_debugdump(rb, "wrong number of items during forward count");
-
-				for(m = 0, item = ringbuf_last(rb); item; item = ringbuf_prev(rb, item), m++);
-				if(m != count) ringbuf_debugdump(rb, "wrong number of items during backward count");
-					
-				ringbuf_validate(rb);
-			}
-
-			/* empty the ring buffer */			
-			while(ringbuf_first(rb))
-				ringbuf_popfirst(rb);
-			ringbuf_validate(rb);
-			count = 0;
-		}
-	}
-	
-	return 0;
-}
diff --git a/src/engine/e_ringbuffer.cpp b/src/engine/e_ringbuffer.cpp
new file mode 100644
index 00000000..eb8a8af4
--- /dev/null
+++ b/src/engine/e_ringbuffer.cpp
@@ -0,0 +1,194 @@
+#include <base/system.h>
+
+#include "e_ringbuffer.h"
+	
+CRingBufferBase::CItem *CRingBufferBase::NextBlock(CItem *pItem)
+{
+	if(pItem->m_pNext)
+		return pItem->m_pNext;
+	return m_pFirst;
+}
+
+CRingBufferBase::CItem *CRingBufferBase::PrevBlock(CItem *pItem)
+{
+	if(pItem->m_pPrev)
+		return pItem->m_pPrev;
+	return m_pLast;
+}
+
+CRingBufferBase::CItem *CRingBufferBase::MergeBack(CItem *pItem)
+{
+	/* make sure that this block and previous block is free */
+	if(!pItem->m_Free || !pItem->m_pPrev || !pItem->m_pPrev->m_Free)
+		return pItem;
+
+	/* merge the blocks */
+	pItem->m_pPrev->m_Size += pItem->m_Size;
+	pItem->m_pPrev->m_pNext = pItem->m_pNext;
+	
+	/* fixup pointers */
+	if(pItem->m_pNext)
+		pItem->m_pNext->m_pPrev = pItem->m_pPrev;
+	else
+		m_pLast = pItem->m_pPrev;
+		
+	if(pItem == m_pProduce)
+		m_pProduce = pItem->m_pPrev;
+	
+	if(pItem == m_pConsume)
+		m_pConsume = pItem->m_pPrev;
+	
+	/* return the current block */
+	return pItem->m_pPrev;
+}
+
+void CRingBufferBase::Init(void *pMemory, int Size, int Flags)
+{
+	mem_zero(pMemory, Size);
+	m_Size = (Size)/sizeof(CItem)*sizeof(CItem);
+	m_pFirst = (CItem *)pMemory;
+	m_pFirst->m_Free = 1;
+	m_pFirst->m_Size = m_Size;
+	m_pLast = m_pFirst;
+	m_pProduce = m_pFirst;
+	m_pConsume = m_pFirst;
+	m_Flags = Flags;		
+	
+}
+
+void *CRingBufferBase::Allocate(int Size)
+{
+	int WantedSize = (Size+sizeof(CItem)+sizeof(CItem)-1)/sizeof(CItem)*sizeof(CItem);
+	CItem *pBlock = 0;
+
+	/* check if we even can fit this block */
+	if(WantedSize > m_Size)
+		return 0;
+
+	while(1)	
+	{
+		/* check for space */
+		if(m_pProduce->m_Free)
+		{
+			if(m_pProduce->m_Size >= WantedSize)
+				pBlock = m_pProduce;
+			else
+			{
+				/* wrap around to try to find a block */
+				if(m_pFirst->m_Free && m_pFirst->m_Size >= WantedSize)
+					pBlock = m_pFirst;
+			}
+		}
+		
+		if(pBlock)
+			break;
+		else
+		{
+			/* we have no block, check our policy and see what todo */
+			if(m_Flags&FLAG_RECYCLE)
+			{
+				if(!PopFirst())
+					return 0;
+			}
+			else
+				return 0;
+		}
+	}
+	
+	/* okey, we have our block */
+	
+	/* split the block if needed */
+	if(pBlock->m_Size > WantedSize)
+	{
+		CItem *pNewItem = (CItem *)((char *)pBlock + WantedSize);
+		pNewItem->m_pPrev = pBlock;
+		pNewItem->m_pNext = pBlock->m_pNext;
+		if(pNewItem->m_pNext)
+			pNewItem->m_pNext->m_pPrev = pNewItem;
+		pBlock->m_pNext = pNewItem;
+		
+		pNewItem->m_Free = 1;
+		pNewItem->m_Size = pBlock->m_Size - WantedSize;
+		pBlock->m_Size = WantedSize;
+		
+		if(!pNewItem->m_pNext)
+			m_pLast = pNewItem;
+	}
+	
+	
+	/* set next block */
+	m_pProduce = NextBlock(pBlock);
+	
+	/* set as used and return the item pointer */
+	pBlock->m_Free = 0;
+	return (void *)(pBlock+1);
+}
+
+int CRingBufferBase::PopFirst()
+{
+	if(m_pConsume->m_Free)
+		return 0;
+	
+	/* set the free flag */
+	m_pConsume->m_Free = 1;
+	
+	/* previous block is also free, merge them */
+	m_pConsume = MergeBack(m_pConsume);
+	
+	/* advance the consume pointer */
+	m_pConsume = NextBlock(m_pConsume);
+	while(m_pConsume->m_Free && m_pConsume != m_pProduce)
+	{
+		m_pConsume = MergeBack(m_pConsume);
+		m_pConsume = NextBlock(m_pConsume);
+	}
+		
+	/* in the case that we have catched up with the produce pointer */
+	/* we might stand on a free block so merge em */
+	MergeBack(m_pConsume);
+	return 1;
+}
+
+
+void *CRingBufferBase::Prev(void *pCurrent)
+{
+	CItem *pItem = ((CItem *)pCurrent) - 1;
+	
+	while(1)
+	{
+		pItem = PrevBlock(pItem);
+		if(pItem == m_pProduce)
+			return 0;
+		if(!pItem->m_Free)
+			return pItem+1;
+	}
+}
+
+void *CRingBufferBase::Next(void *pCurrent)
+{
+	CItem *pItem = ((CItem *)pCurrent) - 1;
+	
+	while(1)
+	{
+		pItem = NextBlock(pItem);
+		if(pItem == m_pProduce)
+			return 0;
+		if(!pItem->m_Free)
+			return pItem+1;
+	}
+}
+
+void *CRingBufferBase::First()
+{
+	if(m_pConsume->m_Free)
+		return 0;
+	return (void *)(m_pConsume+1);
+}
+
+void *CRingBufferBase::Last()
+{
+	if(!m_pProduce->m_Free)
+		return m_pProduce+1;
+	return Prev(m_pProduce+1);
+}
+
diff --git a/src/engine/e_ringbuffer.h b/src/engine/e_ringbuffer.h
index 6113c19b..b9f7219c 100644
--- a/src/engine/e_ringbuffer.h
+++ b/src/engine/e_ringbuffer.h
@@ -3,21 +3,64 @@
 
 typedef struct RINGBUFFER RINGBUFFER;
 
-enum
+class CRingBufferBase
 {
-	/* Will start to destroy items to try to fit the next one */
-	RINGBUF_FLAG_RECYCLE=1
+	class CItem
+	{
+	public:
+		CItem *m_pPrev;
+		CItem *m_pNext;
+		int m_Free;
+		int m_Size;
+	};
+	
+	CItem *m_pProduce;
+	CItem *m_pConsume;
+	
+	CItem *m_pFirst;
+	CItem *m_pLast;
+	
+	void *m_pMemory;
+	int m_Size;
+	int m_Flags;
+	
+	CItem *NextBlock(CItem *pItem);
+	CItem *PrevBlock(CItem *pItem);
+	CItem *MergeBack(CItem *pItem);
+protected:
+	void *Allocate(int Size);
+	
+	void *Prev(void *pCurrent);
+	void *Next(void *pCurrent);
+	void *First();
+	void *Last();
+	
+	void Init(void *pMemory, int Size, int Flags);
+	int PopFirst();
+public:
+	enum
+	{
+		/* Will start to destroy items to try to fit the next one */
+		FLAG_RECYCLE=1
+	};
 };
- 
-RINGBUFFER *ringbuf_init(void *memory, int size, int flags);
-void ringbuf_clear(RINGBUFFER *rb);
-void *ringbuf_allocate(RINGBUFFER *rb, int size);
 
-void *ringbuf_prev(RINGBUFFER *rb, void *current);
-void *ringbuf_next(RINGBUFFER *rb, void *current);
-void *ringbuf_first(RINGBUFFER *rb);
-void *ringbuf_last(RINGBUFFER *rb);
+template<typename T, int TSIZE, int TFLAGS=0>
+class TStaticRingBuffer : public CRingBufferBase
+{
+	unsigned char m_aBuffer[TSIZE];
+public:
+	TStaticRingBuffer() { Init(); }
+	
+	void Init() { CRingBufferBase::Init(m_aBuffer, TSIZE, TFLAGS); }
+	
+	T *Allocate(int Size) { return (T*)CRingBufferBase::Allocate(Size); }
+	int PopFirst() { return CRingBufferBase::PopFirst(); }
 
-int ringbuf_popfirst(RINGBUFFER *rb);
+	T *Prev(T *pCurrent) { return (T*)CRingBufferBase::Prev(pCurrent); }
+	T *Next(T *pCurrent) { return (T*)CRingBufferBase::Next(pCurrent); }
+	T *First() { return (T*)CRingBufferBase::First(); }
+	T *Last() { return (T*)CRingBufferBase::Last(); }
+};
 
 #endif
diff --git a/src/engine/e_server_interface.h b/src/engine/e_server_interface.h
index 5b1e6327..c325b9a1 100644
--- a/src/engine/e_server_interface.h
+++ b/src/engine/e_server_interface.h
@@ -2,10 +2,6 @@
 #ifndef ENGINE_SERVER_INTERFACE_H
 #define ENGINE_SERVER_INTERFACE_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 #include "e_if_other.h"
 #include "e_if_server.h"
 #include "e_if_msg.h"
@@ -13,8 +9,4 @@ extern "C" {
 
 #include "e_console.h" /* TODO: clean this up*/
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
diff --git a/src/engine/e_snapshot.c b/src/engine/e_snapshot.c
deleted file mode 100644
index cce8a06e..00000000
--- a/src/engine/e_snapshot.c
+++ /dev/null
@@ -1,604 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <stdlib.h>
-#include "e_snapshot.h"
-#include "e_engine.h"
-#include "e_compression.h"
-#include "e_common_interface.h"
-
-
-/* TODO: strange arbitrary number */
-static short item_sizes[64] = {0};
-
-void snap_set_staticsize(int itemtype, int size)
-{
-	item_sizes[itemtype] = size;
-}
-
-int *snapitem_data(SNAPSHOT_ITEM *item) { return (int *)(item+1); }
-int snapitem_type(SNAPSHOT_ITEM *item) { return item->type_and_id>>16; }
-int snapitem_id(SNAPSHOT_ITEM *item) { return item->type_and_id&0xffff; }
-int snapitem_key(SNAPSHOT_ITEM *item) { return item->type_and_id; }
-
-int *snapshot_offsets(SNAPSHOT *snap) { return (int *)(snap+1); }
-char *snapshot_datastart(SNAPSHOT *snap) { return (char*)(snapshot_offsets(snap)+snap->num_items); }
-
-SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index)
-{ return (SNAPSHOT_ITEM *)(snapshot_datastart(snap) + snapshot_offsets(snap)[index]); }
-
-int snapshot_get_item_datasize(SNAPSHOT *snap, int index)
-{
-    if(index == snap->num_items-1)
-        return (snap->data_size - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM);
-    return (snapshot_offsets(snap)[index+1] - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM);
-}
-
-int snapshot_get_item_index(SNAPSHOT *snap, int key)
-{
-    /* TODO: OPT: this should not be a linear search. very bad */
-    int i;
-    for(i = 0; i < snap->num_items; i++)
-    {
-        if(snapitem_key(snapshot_get_item(snap, i)) == key)
-            return i;
-    }
-    return -1;
-}
-typedef struct 
-{
-	int num;
-	int keys[64];
-	int index[64];
-} ITEMLIST;
-static ITEMLIST sorted[256];
-
-static int snapshot_generate_hash(SNAPSHOT *snap)
-{
-	int i, key, hashid;
-
-    for(i = 0; i < 256; i++)
-    	sorted[i].num = 0;
-    	
-    for(i = 0; i < snap->num_items; i++)
-    {
-    	key = snapitem_key(snapshot_get_item(snap, i));
-    	hashid = ((key>>8)&0xf0) | (key&0xf);
-    	if(sorted[hashid].num != 64)
-    	{
-			sorted[hashid].index[sorted[hashid].num] = i;
-			sorted[hashid].keys[sorted[hashid].num] = key;
-			sorted[hashid].num++;
-		}
-	}
-    return 0;
-}
-
-int snapshot_get_item_index_hashed(SNAPSHOT *snap, int key)
-{
-   	int hashid = ((key>>8)&0xf0) | (key&0xf);
-   	int i;
-   	for(i = 0; i < sorted[hashid].num; i++)
-   	{
-   		if(sorted[hashid].keys[i] == key)
-   			return sorted[hashid].index[i];
-	}
-	
-	return -1;
-}
-
-typedef struct
-{
-	int num_deleted_items;
-	int num_update_items;
-	int num_temp_items; /* needed? */
-	int data[1];
-	
-	/*
-	char *data_start() { return (char *)&offsets[num_deleted_items+num_update_items+num_temp_items]; }
-	
-	int deleted_item(int index) { return offsets[index]; }
-	item *update_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+index]); }
-	item *temp_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+num_update_items+index]); }
-	*/
-} SNAPSHOT_DELTA;
-
-
-static const int MAX_ITEMS = 512;
-static SNAPSHOT_DELTA empty = {0,0,0,{0}};
-
-void *snapshot_empty_delta()
-{
-	return &empty;
-}
-
-int snapshot_crc(SNAPSHOT *snap)
-{
-	int crc = 0;
-	int i, b;
-	SNAPSHOT_ITEM *item;
-	int size;
-	
-	for(i = 0; i < snap->num_items; i++)
-	{
-		item = snapshot_get_item(snap, i);
-		size = snapshot_get_item_datasize(snap, i);
-		
-		for(b = 0; b < size/4; b++)
-			crc += snapitem_data(item)[b];
-	}
-	return crc;
-}
-
-void snapshot_debug_dump(SNAPSHOT *snap)
-{
-	int size, i, b;
-	SNAPSHOT_ITEM *item;
-	
-	dbg_msg("snapshot", "data_size=%d num_items=%d", snap->data_size, snap->num_items);
-	for(i = 0; i < snap->num_items; i++)
-	{
-		item = snapshot_get_item(snap, i);
-		size = snapshot_get_item_datasize(snap, i);
-		dbg_msg("snapshot", "\ttype=%d id=%d", snapitem_type(item), snapitem_id(item));
-		for(b = 0; b < size/4; b++)
-			dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, snapitem_data(item)[b], snapitem_data(item)[b]);
-	}
-}
-
-static int diff_item(int *past, int *current, int *out, int size)
-{
-	int needed = 0;
-	while(size)
-	{
-		*out = *current-*past;
-		needed |= *out;
-		out++;
-		past++;
-		current++;
-		size--;
-	}
-	
-	return needed;
-}
-
-int snapshot_data_rate[0xffff] = {0};
-int snapshot_data_updates[0xffff] = {0};
-static int snapshot_current = 0;
-
-static void undiff_item(int *past, int *diff, int *out, int size)
-{
-	while(size)
-	{
-		*out = *past+*diff;
-		
-		if(*diff == 0)
-			snapshot_data_rate[snapshot_current] += 1;
-		else
-		{
-			unsigned char buf[16];
-			unsigned char *end = vint_pack(buf,  *diff);
-			snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8;
-		}
-		
-		out++;
-		past++;
-		diff++;
-		size--;
-	}
-}
-
-
-/* TODO: OPT: this should be made much faster */
-int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *dstdata)
-{
-	static PERFORMACE_INFO hash_scope = {"hash", 0};
-	SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)dstdata;
-	int *data = (int *)delta->data;
-	int i, itemsize, pastindex;
-	SNAPSHOT_ITEM *fromitem;
-	SNAPSHOT_ITEM *curitem;
-	SNAPSHOT_ITEM *pastitem;
-	int count = 0;
-	int size_count = 0;
-	
-	delta->num_deleted_items = 0;
-	delta->num_update_items = 0;
-	delta->num_temp_items = 0;
-	
-	perf_start(&hash_scope);
-	snapshot_generate_hash(to);
-	perf_end();
-
-	/* pack deleted stuff */
-	{
-		static PERFORMACE_INFO scope = {"delete", 0};
-		perf_start(&scope);
-		
-		for(i = 0; i < from->num_items; i++)
-		{
-			fromitem = snapshot_get_item(from, i);
-			if(snapshot_get_item_index_hashed(to, (snapitem_key(fromitem))) == -1)
-			{
-				/* deleted */
-				delta->num_deleted_items++;
-				*data = snapitem_key(fromitem);
-				data++;
-			}
-		}
-		
-		perf_end();
-	}
-	
-	perf_start(&hash_scope);
-	snapshot_generate_hash(from);
-	perf_end();
-	
-	/* pack updated stuff */
-	{
-		static PERFORMACE_INFO scope = {"update", 0};
-		int pastindecies[1024];
-		perf_start(&scope);
-
-		/* fetch previous indices */
-		/* we do this as a separate pass because it helps the cache */
-		{
-			static PERFORMACE_INFO scope = {"find", 0};
-			perf_start(&scope);
-			for(i = 0; i < to->num_items; i++)
-			{
-				curitem = snapshot_get_item(to, i);  /* O(1) .. O(n) */
-				pastindecies[i] = snapshot_get_item_index_hashed(from, snapitem_key(curitem)); /* O(n) .. O(n^n)*/
-			}
-			perf_end();
-		}		
-			
-		for(i = 0; i < to->num_items; i++)
-		{
-			/* do delta */
-			itemsize = snapshot_get_item_datasize(to, i); /* O(1) .. O(n) */
-			curitem = snapshot_get_item(to, i);  /* O(1) .. O(n) */
-			pastindex = pastindecies[i];
-			
-			if(pastindex != -1)
-			{
-				static PERFORMACE_INFO scope = {"diff", 0};
-				int *item_data_dst = data+3;
-				perf_start(&scope);
-		
-				pastitem = snapshot_get_item(from, pastindex);
-				
-				if(item_sizes[snapitem_type(curitem)])
-					item_data_dst = data+2;
-				
-				if(diff_item((int*)snapitem_data(pastitem), (int*)snapitem_data(curitem), item_data_dst, itemsize/4))
-				{
-					
-					*data++ = snapitem_type(curitem);
-					*data++ = snapitem_id(curitem);
-					if(!item_sizes[snapitem_type(curitem)])
-						*data++ = itemsize/4;
-					data += itemsize/4;
-					delta->num_update_items++;
-				}
-				perf_end();
-			}
-			else
-			{
-				static PERFORMACE_INFO scope = {"copy", 0};
-				perf_start(&scope);
-				
-				*data++ = snapitem_type(curitem);
-				*data++ = snapitem_id(curitem);
-				if(!item_sizes[snapitem_type(curitem)])
-					*data++ = itemsize/4;
-				
-				mem_copy(data, snapitem_data(curitem), itemsize);
-				size_count += itemsize;
-				data += itemsize/4;
-				delta->num_update_items++;
-				count++;
-				
-				perf_end();
-			}
-		}
-		
-		perf_end();
-	}
-	
-	if(0)
-	{
-		dbg_msg("snapshot", "%d %d %d",
-			delta->num_deleted_items,
-			delta->num_update_items,
-			delta->num_temp_items);
-	}
-
-	/*
-	// TODO: pack temp stuff
-	
-	// finish
-	//mem_copy(delta->offsets, deleted, delta->num_deleted_items*sizeof(int));
-	//mem_copy(&(delta->offsets[delta->num_deleted_items]), update, delta->num_update_items*sizeof(int));
-	//mem_copy(&(delta->offsets[delta->num_deleted_items+delta->num_update_items]), temp, delta->num_temp_items*sizeof(int));
-	//mem_copy(delta->data_start(), data, data_size);
-	//delta->data_size = data_size;
-	* */
-	
-	if(!delta->num_deleted_items && !delta->num_update_items && !delta->num_temp_items)
-		return 0;
-	
-	return (int)((char*)data-(char*)dstdata);
-}
-
-static int range_check(void *end, void *ptr, int size)
-{
-	if((const char *)ptr + size > (const char *)end)
-		return -1;
-	return 0;
-}
-
-int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_size)
-{
-	SNAPBUILD builder;
-	SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)srcdata;
-	int *data = (int *)delta->data;
-	int *end = (int *)(((char *)srcdata + data_size));
-	
-	SNAPSHOT_ITEM *fromitem;
-	int i, d, keep, itemsize;
-	int *deleted;
-	int id, type, key;
-	int fromindex;
-	int *newdata;
-			
-	snapbuild_init(&builder);
-	
-	/* unpack deleted stuff */
-	deleted = data;
-	data += delta->num_deleted_items;
-	if(data > end)
-		return -1;
-
-	/* copy all non deleted stuff */
-	for(i = 0; i < from->num_items; i++)
-	{
-		/* dbg_assert(0, "fail!"); */
-		fromitem = snapshot_get_item(from, i);
-		itemsize = snapshot_get_item_datasize(from, i);
-		keep = 1;
-		for(d = 0; d < delta->num_deleted_items; d++)
-		{
-			if(deleted[d] == snapitem_key(fromitem))
-			{
-				keep = 0;
-				break;
-			}
-		}
-		
-		if(keep)
-		{
-			/* keep it */
-			mem_copy(
-				snapbuild_new_item(&builder, snapitem_type(fromitem), snapitem_id(fromitem), itemsize),
-				snapitem_data(fromitem), itemsize);
-		}
-	}
-		
-	/* unpack updated stuff */
-	for(i = 0; i < delta->num_update_items; i++)
-	{
-		if(data+2 > end)
-			return -1;
-		
-		type = *data++;
-		id = *data++;
-		if(item_sizes[type])
-			itemsize = item_sizes[type];
-		else
-		{
-			if(data+1 > end)
-				return -2;
-			itemsize = (*data++) * 4;
-		}
-		snapshot_current = type;
-		
-		if(range_check(end, data, itemsize) || itemsize < 0) return -3;
-		
-		key = (type<<16)|id;
-		
-		/* create the item if needed */
-		newdata = snapbuild_get_item_data(&builder, key);
-		if(!newdata)
-			newdata = (int *)snapbuild_new_item(&builder, key>>16, key&0xffff, itemsize);
-
-		/*if(range_check(end, newdata, itemsize)) return -4;*/
-			
-		fromindex = snapshot_get_item_index(from, key);
-		if(fromindex != -1)
-		{
-			/* we got an update so we need to apply the diff */
-			undiff_item((int *)snapitem_data(snapshot_get_item(from, fromindex)), data, newdata, itemsize/4);
-			snapshot_data_updates[snapshot_current]++;
-		}
-		else /* no previous, just copy the data */
-		{
-			mem_copy(newdata, data, itemsize);
-			snapshot_data_rate[snapshot_current] += itemsize*8;
-			snapshot_data_updates[snapshot_current]++;
-		}
-			
-		data += itemsize/4;
-	}
-	
-	/* finish up */
-	return snapbuild_finish(&builder, to);
-}
-
-/* SNAPSTORAGE */
-
-void snapstorage_init(SNAPSTORAGE *ss)
-{
-	ss->first = 0;
-}
-
-void snapstorage_purge_all(SNAPSTORAGE *ss)
-{
-	SNAPSTORAGE_HOLDER *h = ss->first;
-	SNAPSTORAGE_HOLDER *next;
-
-	while(h)
-	{
-		next = h->next;
-		mem_free(h);
-		h = next;
-	}
-
-	/* no more snapshots in storage */
-	ss->first = 0;
-	ss->last = 0;
-}
-
-void snapstorage_purge_until(SNAPSTORAGE *ss, int tick)
-{
-	SNAPSTORAGE_HOLDER *next;
-	SNAPSTORAGE_HOLDER *h = ss->first;
-	
-	while(h)
-	{
-		next = h->next;
-		if(h->tick >=  tick)
-			return; /* no more to remove */
-		mem_free(h);
-		
-		/* did we come to the end of the list? */
-        if (!next)
-            break;
-
-		ss->first = next;
-		next->prev = 0x0;
-		
-		h = next;
-	}
-	
-	/* no more snapshots in storage */
-	ss->first = 0;
-	ss->last = 0;
-}
-
-void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data, int create_alt)
-{
-	/* allocate memory for holder + snapshot_data */
-	SNAPSTORAGE_HOLDER *h;
-	int total_size = sizeof(SNAPSTORAGE_HOLDER)+data_size;
-	
-	if(create_alt)
-		total_size += data_size;
-	
-	h = (SNAPSTORAGE_HOLDER *)mem_alloc(total_size, 1);
-	
-	/* set data */
-	h->tick = tick;
-	h->tagtime = tagtime;
-	h->snap_size = data_size;
-	h->snap = (SNAPSHOT*)(h+1);
-	mem_copy(h->snap, data, data_size);
-
-	if(create_alt) /* create alternative if wanted */
-	{
-		h->alt_snap = (SNAPSHOT*)(((char *)h->snap) + data_size);
-		mem_copy(h->alt_snap, data, data_size);
-	}
-	else
-		h->alt_snap = 0;
-		
-	
-	/* link */
-	h->next = 0;
-	h->prev = ss->last;
-	if(ss->last)
-		ss->last->next = h;
-	else
-		ss->first = h;
-	ss->last = h;
-}
-
-int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data, SNAPSHOT **alt_data)
-{
-	SNAPSTORAGE_HOLDER *h = ss->first;
-	
-	while(h)
-	{
-		if(h->tick == tick)
-		{
-			if(tagtime)
-				*tagtime = h->tagtime;
-			if(data)
-				*data = h->snap;
-			if(alt_data)
-				*alt_data = h->alt_snap;
-			return h->snap_size;
-		}
-		
-		h = h->next;
-	}
-	
-	return -1;
-}
-
-/* SNAPBUILD */
-
-void snapbuild_init(SNAPBUILD *sb)
-{
-	sb->data_size = 0;
-	sb->num_items = 0;
-}
-
-SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index) 
-{
-	return (SNAPSHOT_ITEM *)&(sb->data[sb->offsets[index]]);
-}
-
-int *snapbuild_get_item_data(SNAPBUILD *sb, int key)
-{
-	int i;
-	for(i = 0; i < sb->num_items; i++)
-	{
-		if(snapitem_key(snapbuild_get_item(sb, i)) == key)
-			return (int *)snapitem_data(snapbuild_get_item(sb, i));
-	}
-	return 0;
-}
-
-int snapbuild_finish(SNAPBUILD *sb, void *snapdata)
-{
-	/* flattern and make the snapshot */
-	SNAPSHOT *snap = (SNAPSHOT *)snapdata;
-	int offset_size = sizeof(int)*sb->num_items;
-	snap->data_size = sb->data_size;
-	snap->num_items = sb->num_items;
-	mem_copy(snapshot_offsets(snap), sb->offsets, offset_size);
-	mem_copy(snapshot_datastart(snap), sb->data, sb->data_size);
-	return sizeof(SNAPSHOT) + offset_size + sb->data_size;
-}
-
-void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size)
-{
-	SNAPSHOT_ITEM *obj = (SNAPSHOT_ITEM *)(sb->data+sb->data_size);
-
-	/*if(stress_prob(0.01f))
-	{
-		size += ((rand()%5) - 2)*4;
-		if(size < 0)
-			size = 0;
-	}*/
-
-	mem_zero(obj, sizeof(SNAPSHOT_ITEM) + size);
-	obj->type_and_id = (type<<16)|id;
-	sb->offsets[sb->num_items] = sb->data_size;
-	sb->data_size += sizeof(SNAPSHOT_ITEM) + size;
-	sb->num_items++;
-	
-	dbg_assert(sb->data_size < MAX_SNAPSHOT_SIZE, "too much data");
-	dbg_assert(sb->num_items < SNAPBUILD_MAX_ITEMS, "too many items");
-
-	return snapitem_data(obj);
-}
diff --git a/src/engine/e_snapshot.cpp b/src/engine/e_snapshot.cpp
new file mode 100644
index 00000000..65487665
--- /dev/null
+++ b/src/engine/e_snapshot.cpp
@@ -0,0 +1,571 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <stdlib.h>
+#include "e_snapshot.h"
+#include "e_engine.h"
+#include "e_compression.h"
+#include "e_common_interface.h"
+
+
+/* TODO: strange arbitrary number */
+static short item_sizes[64] = {0};
+
+void snap_set_staticsize(int itemtype, int size)
+{
+	item_sizes[itemtype] = size;
+}
+
+CSnapshotItem *CSnapshot::GetItem(int Index)
+{
+	return (CSnapshotItem *)(DataStart() + Offsets()[Index]);
+}
+
+int CSnapshot::GetItemSize(int Index)
+{
+    if(Index == m_NumItems-1)
+        return (m_DataSize - Offsets()[Index]) - sizeof(CSnapshotItem);
+    return (Offsets()[Index+1] - Offsets()[Index]) - sizeof(CSnapshotItem);
+}
+
+int CSnapshot::GetItemIndex(int Key)
+{
+    /* TODO: OPT: this should not be a linear search. very bad */
+    for(int i = 0; i < m_NumItems; i++)
+    {
+        if(GetItem(i)->Key() == Key)
+            return i;
+    }
+    return -1;
+}
+
+// TODO: clean up this
+typedef struct 
+{
+	int num;
+	int keys[64];
+	int index[64];
+} ITEMLIST;
+
+static ITEMLIST sorted[256];
+
+int CSnapshot::GenerateHash()
+{
+    for(int i = 0; i < 256; i++)
+    	sorted[i].num = 0;
+    	
+    for(int i = 0; i < m_NumItems; i++)
+    {
+    	int Key = GetItem(i)->Key();
+    	int HashID = ((Key>>8)&0xf0) | (Key&0xf);
+    	if(sorted[HashID].num != 64)
+    	{
+			sorted[HashID].index[sorted[HashID].num] = i;
+			sorted[HashID].keys[sorted[HashID].num] = Key;
+			sorted[HashID].num++;
+		}
+	}
+    return 0;
+}
+
+int CSnapshot::GetItemIndexHashed(int Key)
+{
+   	int HashID = ((Key>>8)&0xf0) | (Key&0xf);
+   	for(int i = 0; i < sorted[HashID].num; i++)
+   	{
+   		if(sorted[HashID].keys[i] == Key)
+   			return sorted[HashID].index[i];
+	}
+	
+	return -1;
+}
+
+
+
+static const int MAX_ITEMS = 512;
+static CSnapshotDelta empty = {0,0,0,{0}};
+
+CSnapshotDelta *CSnapshot::EmptyDelta()
+{
+	return &empty;
+}
+
+int CSnapshot::Crc()
+{
+	int crc = 0;
+	
+	for(int i = 0; i < m_NumItems; i++)
+	{
+		CSnapshotItem *pItem = GetItem(i);
+		int Size = GetItemSize(i);
+		
+		for(int b = 0; b < Size/4; b++)
+			crc += pItem->Data()[b];
+	}
+	return crc;
+}
+
+void CSnapshot::DebugDump()
+{
+	dbg_msg("snapshot", "data_size=%d num_items=%d", m_DataSize, m_NumItems);
+	for(int i = 0; i < m_NumItems; i++)
+	{
+		CSnapshotItem *pItem = GetItem(i);
+		int Size = GetItemSize(i);
+		dbg_msg("snapshot", "\ttype=%d id=%d", pItem->Type(), pItem->ID());
+		for(int b = 0; b < Size/4; b++)
+			dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, pItem->Data()[b], pItem->Data()[b]);
+	}
+}
+
+
+// TODO: remove these
+int snapshot_data_rate[0xffff] = {0};
+int snapshot_data_updates[0xffff] = {0};
+static int snapshot_current = 0;
+
+static int diff_item(int *past, int *current, int *out, int size)
+{
+	int needed = 0;
+	while(size)
+	{
+		*out = *current-*past;
+		needed |= *out;
+		out++;
+		past++;
+		current++;
+		size--;
+	}
+	
+	return needed;
+}
+
+static void undiff_item(int *past, int *diff, int *out, int size)
+{
+	while(size)
+	{
+		*out = *past+*diff;
+		
+		if(*diff == 0)
+			snapshot_data_rate[snapshot_current] += 1;
+		else
+		{
+			unsigned char buf[16];
+			unsigned char *end = vint_pack(buf,  *diff);
+			snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8;
+		}
+		
+		out++;
+		past++;
+		diff++;
+		size--;
+	}
+}
+
+
+/* TODO: OPT: this should be made much faster */
+int CSnapshot::CreateDelta(CSnapshot *from, CSnapshot *to, void *dstdata)
+{
+	static PERFORMACE_INFO hash_scope = {"hash", 0};
+	CSnapshotDelta *delta = (CSnapshotDelta *)dstdata;
+	int *data = (int *)delta->m_pData;
+	int i, itemsize, pastindex;
+	CSnapshotItem *pFromItem;
+	CSnapshotItem *pCurItem;
+	CSnapshotItem *pPastItem;
+	int count = 0;
+	int size_count = 0;
+	
+	delta->m_NumDeletedItems = 0;
+	delta->m_NumUpdateItems = 0;
+	delta->m_NumTempItems = 0;
+	
+	perf_start(&hash_scope);
+	to->GenerateHash();
+	perf_end();
+
+	/* pack deleted stuff */
+	{
+		static PERFORMACE_INFO scope = {"delete", 0};
+		perf_start(&scope);
+		
+		for(i = 0; i < from->m_NumItems; i++)
+		{
+			pFromItem = from->GetItem(i);
+			if(to->GetItemIndexHashed(pFromItem->Key()) == -1)
+			{
+				/* deleted */
+				delta->m_NumDeletedItems++;
+				*data = pFromItem->Key();
+				data++;
+			}
+		}
+		
+		perf_end();
+	}
+	
+	perf_start(&hash_scope);
+	from->GenerateHash();
+	perf_end();
+	
+	/* pack updated stuff */
+	{
+		static PERFORMACE_INFO scope = {"update", 0};
+		int pastindecies[1024];
+		perf_start(&scope);
+
+		/* fetch previous indices */
+		/* we do this as a separate pass because it helps the cache */
+		{
+			static PERFORMACE_INFO scope = {"find", 0};
+			perf_start(&scope);
+			for(i = 0; i < to->m_NumItems; i++)
+			{
+				pCurItem = to->GetItem(i);  /* O(1) .. O(n) */
+				pastindecies[i] = from->GetItemIndexHashed(pCurItem->Key()); /* O(n) .. O(n^n)*/
+			}
+			perf_end();
+		}		
+			
+		for(i = 0; i < to->m_NumItems; i++)
+		{
+			/* do delta */
+			itemsize = to->GetItemSize(i); /* O(1) .. O(n) */
+			pCurItem = to->GetItem(i);  /* O(1) .. O(n) */
+			pastindex = pastindecies[i];
+			
+			if(pastindex != -1)
+			{
+				static PERFORMACE_INFO scope = {"diff", 0};
+				int *item_data_dst = data+3;
+				perf_start(&scope);
+		
+				pPastItem = from->GetItem(pastindex);
+				
+				if(item_sizes[pCurItem->Type()])
+					item_data_dst = data+2;
+				
+				if(diff_item((int*)pPastItem->Data(), (int*)pCurItem->Data(), item_data_dst, itemsize/4))
+				{
+					
+					*data++ = pCurItem->Type();
+					*data++ = pCurItem->ID();
+					if(!item_sizes[pCurItem->Type()])
+						*data++ = itemsize/4;
+					data += itemsize/4;
+					delta->m_NumUpdateItems++;
+				}
+				perf_end();
+			}
+			else
+			{
+				static PERFORMACE_INFO scope = {"copy", 0};
+				perf_start(&scope);
+				
+				*data++ = pCurItem->Type();
+				*data++ = pCurItem->ID();
+				if(!item_sizes[pCurItem->Type()])
+					*data++ = itemsize/4;
+				
+				mem_copy(data, pCurItem->Data(), itemsize);
+				size_count += itemsize;
+				data += itemsize/4;
+				delta->m_NumUpdateItems++;
+				count++;
+				
+				perf_end();
+			}
+		}
+		
+		perf_end();
+	}
+	
+	if(0)
+	{
+		dbg_msg("snapshot", "%d %d %d",
+			delta->m_NumDeletedItems,
+			delta->m_NumUpdateItems,
+			delta->m_NumTempItems);
+	}
+
+	/*
+	// TODO: pack temp stuff
+	
+	// finish
+	//mem_copy(delta->offsets, deleted, delta->num_deleted_items*sizeof(int));
+	//mem_copy(&(delta->offsets[delta->num_deleted_items]), update, delta->num_update_items*sizeof(int));
+	//mem_copy(&(delta->offsets[delta->num_deleted_items+delta->num_update_items]), temp, delta->num_temp_items*sizeof(int));
+	//mem_copy(delta->data_start(), data, data_size);
+	//delta->data_size = data_size;
+	* */
+	
+	if(!delta->m_NumDeletedItems && !delta->m_NumUpdateItems && !delta->m_NumTempItems)
+		return 0;
+	
+	return (int)((char*)data-(char*)dstdata);
+}
+
+static int range_check(void *end, void *ptr, int size)
+{
+	if((const char *)ptr + size > (const char *)end)
+		return -1;
+	return 0;
+}
+
+int CSnapshot::UnpackDelta(CSnapshot *from, CSnapshot *to, void *srcdata, int data_size)
+{
+	CSnapshotBuilder builder;
+	CSnapshotDelta *delta = (CSnapshotDelta *)srcdata;
+	int *data = (int *)delta->m_pData;
+	int *end = (int *)(((char *)srcdata + data_size));
+	
+	CSnapshotItem *fromitem;
+	int i, d, keep, itemsize;
+	int *deleted;
+	int id, type, key;
+	int fromindex;
+	int *newdata;
+			
+	builder.Init();
+	
+	/* unpack deleted stuff */
+	deleted = data;
+	data += delta->m_NumDeletedItems;
+	if(data > end)
+		return -1;
+
+	/* copy all non deleted stuff */
+	for(i = 0; i < from->m_NumItems; i++)
+	{
+		/* dbg_assert(0, "fail!"); */
+		fromitem = from->GetItem(i);
+		itemsize = from->GetItemSize(i); 
+		keep = 1;
+		for(d = 0; d < delta->m_NumDeletedItems; d++)
+		{
+			if(deleted[d] == fromitem->Key())
+			{
+				keep = 0;
+				break;
+			}
+		}
+		
+		if(keep)
+		{
+			/* keep it */
+			mem_copy(
+				builder.NewItem(fromitem->Type(), fromitem->ID(), itemsize),
+				fromitem->Data(), itemsize);
+		}
+	}
+		
+	/* unpack updated stuff */
+	for(i = 0; i < delta->m_NumUpdateItems; i++)
+	{
+		if(data+2 > end)
+			return -1;
+		
+		type = *data++;
+		id = *data++;
+		if(item_sizes[type])
+			itemsize = item_sizes[type];
+		else
+		{
+			if(data+1 > end)
+				return -2;
+			itemsize = (*data++) * 4;
+		}
+		snapshot_current = type;
+		
+		if(range_check(end, data, itemsize) || itemsize < 0) return -3;
+		
+		key = (type<<16)|id;
+		
+		/* create the item if needed */
+		newdata = builder.GetItemData(key);
+		if(!newdata)
+			newdata = (int *)builder.NewItem(key>>16, key&0xffff, itemsize);
+
+		/*if(range_check(end, newdata, itemsize)) return -4;*/
+			
+		fromindex = from->GetItemIndex(key);
+		if(fromindex != -1)
+		{
+			/* we got an update so we need to apply the diff */
+			undiff_item((int *)from->GetItem(fromindex)->Data(), data, newdata, itemsize/4);
+			snapshot_data_updates[snapshot_current]++;
+		}
+		else /* no previous, just copy the data */
+		{
+			mem_copy(newdata, data, itemsize);
+			snapshot_data_rate[snapshot_current] += itemsize*8;
+			snapshot_data_updates[snapshot_current]++;
+		}
+			
+		data += itemsize/4;
+	}
+	
+	/* finish up */
+	return builder.Finish(to);
+}
+
+/* CSnapshotStorage */
+
+void CSnapshotStorage::Init()
+{
+	m_pFirst = 0;
+	m_pLast = 0;
+}
+
+void CSnapshotStorage::PurgeAll()
+{
+	CHolder *pHolder = m_pFirst;
+	CHolder *pNext;
+
+	while(pHolder)
+	{
+		pNext = pHolder->m_pNext;
+		mem_free(pHolder);
+		pHolder = pNext;
+	}
+
+	/* no more snapshots in storage */
+	m_pFirst = 0;
+	m_pLast = 0;
+}
+
+void CSnapshotStorage::PurgeUntil(int Tick)
+{
+	CHolder *pHolder = m_pFirst;
+	CHolder *pNext;
+	
+	while(pHolder)
+	{
+		pNext = pHolder->m_pNext;
+		if(pHolder->m_Tick >= Tick)
+			return; /* no more to remove */
+		mem_free(pHolder);
+		
+		/* did we come to the end of the list? */
+        if (!pNext)
+            break;
+
+		m_pFirst = pNext;
+		pNext->m_pPrev = 0x0;
+		
+		pHolder = pNext;
+	}
+	
+	/* no more snapshots in storage */
+	m_pFirst = 0;
+	m_pLast = 0;
+}
+
+void CSnapshotStorage::Add(int Tick, int64 Tagtime, int DataSize, void *pData, int CreateAlt)
+{
+	/* allocate memory for holder + snapshot_data */
+	int TotalSize = sizeof(CHolder)+DataSize;
+	
+	if(CreateAlt)
+		TotalSize += DataSize;
+	
+	CHolder *pHolder = (CHolder *)mem_alloc(TotalSize, 1);
+	
+	/* set data */
+	pHolder->m_Tick = Tick;
+	pHolder->m_Tagtime = Tagtime;
+	pHolder->m_SnapSize = DataSize;
+	pHolder->m_pSnap = (CSnapshot*)(pHolder+1);
+	mem_copy(pHolder->m_pSnap, pData, DataSize);
+
+	if(CreateAlt) /* create alternative if wanted */
+	{
+		pHolder->m_pAltSnap = (CSnapshot*)(((char *)pHolder->m_pSnap) + DataSize);
+		mem_copy(pHolder->m_pAltSnap, pData, DataSize);
+	}
+	else
+		pHolder->m_pAltSnap = 0;
+		
+	
+	/* link */
+	pHolder->m_pNext = 0;
+	pHolder->m_pPrev = m_pLast;
+	if(m_pLast)
+		m_pLast->m_pNext = pHolder;
+	else
+		m_pFirst = pHolder;
+	m_pLast = pHolder;
+}
+
+int CSnapshotStorage::Get(int Tick, int64 *pTagtime, CSnapshot **ppData, CSnapshot **ppAltData)
+{
+	CHolder *pHolder = m_pFirst;
+	
+	while(pHolder)
+	{
+		if(pHolder->m_Tick == Tick)
+		{
+			if(pTagtime)
+				*pTagtime = pHolder->m_Tagtime;
+			if(ppData)
+				*ppData = pHolder->m_pSnap;
+			if(ppAltData)
+				*ppData = pHolder->m_pAltSnap;
+			return pHolder->m_SnapSize;
+		}
+		
+		pHolder = pHolder->m_pNext;
+	}
+	
+	return -1;
+}
+
+/* CSnapshotBuilder */
+
+void CSnapshotBuilder::Init()
+{
+	m_DataSize = 0;
+	m_NumItems = 0;
+}
+
+CSnapshotItem *CSnapshotBuilder::GetItem(int Index) 
+{
+	return (CSnapshotItem *)&(m_aData[m_aOffsets[Index]]);
+}
+
+int *CSnapshotBuilder::GetItemData(int key)
+{
+	int i;
+	for(i = 0; i < m_NumItems; i++)
+	{
+		if(GetItem(i)->Key() == key)
+			return (int *)GetItem(i)->Data();
+	}
+	return 0;
+}
+
+int CSnapshotBuilder::Finish(void *snapdata)
+{
+	/* flattern and make the snapshot */
+	CSnapshot *pSnap = (CSnapshot *)snapdata;
+	int OffsetSize = sizeof(int)*m_NumItems;
+	pSnap->m_DataSize = m_DataSize;
+	pSnap->m_NumItems = m_NumItems;
+	mem_copy(pSnap->Offsets(), m_aOffsets, OffsetSize);
+	mem_copy(pSnap->DataStart(), m_aData, m_DataSize);
+	return sizeof(CSnapshot) + OffsetSize + m_DataSize;
+}
+
+void *CSnapshotBuilder::NewItem(int Type, int ID, int Size)
+{
+	CSnapshotItem *pObj = (CSnapshotItem *)(m_aData + m_DataSize);
+
+	mem_zero(pObj, sizeof(CSnapshotItem) + Size);
+	pObj->m_TypeAndID = (Type<<16)|ID;
+	m_aOffsets[m_NumItems] = m_DataSize;
+	m_DataSize += sizeof(CSnapshotItem) + Size;
+	m_NumItems++;
+	
+	dbg_assert(m_DataSize < CSnapshot::MAX_SIZE, "too much data");
+	dbg_assert(m_NumItems < MAX_ITEMS, "too many items");
+
+	return pObj->Data();
+}
diff --git a/src/engine/e_snapshot.h b/src/engine/e_snapshot.h
index f57c95c0..60dc254c 100644
--- a/src/engine/e_snapshot.h
+++ b/src/engine/e_snapshot.h
@@ -4,89 +4,115 @@
 
 #include <base/system.h>
 
-/* SNAPSHOT */
+/* CSnapshot */
 
-enum
+
+
+class CSnapshotItem
 {
-	MAX_SNAPSHOT_SIZE=64*1024
+public:
+	int m_TypeAndID;
+	
+	int *Data() { return (int *)(this+1); }
+	int Type() { return m_TypeAndID>>16; }
+	int ID() { return m_TypeAndID&0xffff; }
+	int Key() { return m_TypeAndID; }
 };
 
-typedef struct
+class CSnapshotDelta
 {
-	int type_and_id;
-} SNAPSHOT_ITEM;
+public:
+	int m_NumDeletedItems;
+	int m_NumUpdateItems;
+	int m_NumTempItems; /* needed? */
+	int m_pData[1];
+};
 
-typedef struct 
+// TODO: hide a lot of these members
+class CSnapshot
 {
-	int data_size;
-	int num_items;
-} SNAPSHOT;
-
-int *snapitem_data(SNAPSHOT_ITEM *item);
-int snapitem_type(SNAPSHOT_ITEM *item);
-int snapitem_id(SNAPSHOT_ITEM *item);
-int snapitem_key(SNAPSHOT_ITEM *item);
-
-int *snapshot_offsets(SNAPSHOT *snap);
-char *snapshot_datastart(SNAPSHOT *snap);
-
-SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index);
-int snapshot_get_item_datasize(SNAPSHOT *snap, int index);
-int snapshot_get_item_index(SNAPSHOT *snap, int key);
+public:
+	enum
+	{
+		MAX_SIZE=64*1024
+	};
+
+	int m_DataSize;
+	int m_NumItems;
+	
+	int *Offsets() const { return (int *)(this+1); }
+	char *DataStart() const { return (char*)(Offsets()+m_NumItems); }
+	CSnapshotItem *GetItem(int Index);
+	int GetItemSize(int Index);
+	int GetItemIndex(int Key);
+
+	int Crc();
+	void DebugDump();
+
+	// TODO: move these
+	int GetItemIndexHashed(int Key);
+	int GenerateHash();
+	
+	//
+	static CSnapshotDelta *EmptyDelta();
+	static int CreateDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pData);
+	static int UnpackDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pData, int DataSize);
+};
 
-void *snapshot_empty_delta();
-int snapshot_crc(SNAPSHOT *snap);
-void snapshot_debug_dump(SNAPSHOT *snap);
-int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *data);
-int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *data, int data_size);
+/* CSnapshotStorage */
 
-/* SNAPSTORAGE */
 
-typedef struct SNAPSTORAGE_HOLDER_t
+class CSnapshotStorage
 {
-	struct SNAPSTORAGE_HOLDER_t *prev;
-	struct SNAPSTORAGE_HOLDER_t *next;
-	
-	int64 tagtime;
-	int tick;
-	
-	int snap_size;
-	SNAPSHOT *snap;
-	SNAPSHOT *alt_snap;
-} SNAPSTORAGE_HOLDER;
- 
-typedef struct SNAPSTORAGE_t
+public:
+	class CHolder
+	{
+	public:
+		CHolder *m_pPrev;
+		CHolder *m_pNext;
+		
+		int64 m_Tagtime;
+		int m_Tick;
+		
+		int m_SnapSize;
+		CSnapshot *m_pSnap;
+		CSnapshot *m_pAltSnap;
+	};
+	 
+
+	CHolder *m_pFirst;
+	CHolder *m_pLast;
+
+	void Init();
+	void PurgeAll();
+	void PurgeUntil(int Tick);
+	void Add(int Tick, int64 Tagtime, int DataSize, void *pData, int CreateAlt);
+	int Get(int Tick, int64 *Tagtime, CSnapshot **pData, CSnapshot **ppAltData);
+};
+
+class CSnapshotBuilder
 {
-	SNAPSTORAGE_HOLDER *first;
-	SNAPSTORAGE_HOLDER *last;
-} SNAPSTORAGE;
+	enum
+	{
+		MAX_ITEMS = 1024*2
+	};
 
-void snapstorage_init(SNAPSTORAGE *ss);
-void snapstorage_purge_all(SNAPSTORAGE *ss);
-void snapstorage_purge_until(SNAPSTORAGE *ss, int tick);
-void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data, int create_alt);
-int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data, SNAPSHOT **alt_data);
+	char m_aData[CSnapshot::MAX_SIZE];
+	int m_DataSize;
 
-/* SNAPBUILD */
+	int m_aOffsets[MAX_ITEMS];
+	int m_NumItems;
 
-enum
-{
-	SNAPBUILD_MAX_ITEMS = 1024*2
+public:
+	void Init();
+	
+	void *NewItem(int Type, int ID, int Size);
+	
+	CSnapshotItem *GetItem(int Index);
+	int *GetItemData(int Key);
+	
+	int Finish(void *Snapdata);
 };
 
-typedef struct SNAPBUILD
-{
-	char data[MAX_SNAPSHOT_SIZE];
-	int data_size;
-
-	int offsets[SNAPBUILD_MAX_ITEMS];
-	int num_items;
-} SNAPBUILD;
-
-void snapbuild_init(SNAPBUILD *sb);
-SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index);
-int *snapbuild_get_item_data(SNAPBUILD *sb, int key);
-int snapbuild_finish(SNAPBUILD *sb, void *snapdata);
-void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size);
 
 #endif /* ENGINE_SNAPSHOT_H */
diff --git a/src/engine/server/es_register.c b/src/engine/server/es_register.cpp
index b3ac70a6..0eb5dba5 100644
--- a/src/engine/server/es_register.c
+++ b/src/engine/server/es_register.cpp
@@ -6,7 +6,7 @@
 
 #include <mastersrv/mastersrv.h>
 
-extern NETSERVER *net;
+extern CNetServer *m_pNetServer;
 
 enum
 {
@@ -29,48 +29,48 @@ static void register_new_state(int state)
 	register_state_start = time_get();
 }
 
-static void register_send_fwcheckresponse(NETADDR *addr)
+static void register_send_fwcheckresponse(NETADDR *pAddr)
 {
-	NETCHUNK packet;
-	packet.client_id = -1;
-	packet.address = *addr;
-	packet.flags = NETSENDFLAG_CONNLESS;
-	packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE);
-	packet.data = SERVERBROWSE_FWRESPONSE;
-	netserver_send(net, &packet);
+	CNetChunk Packet;
+	Packet.m_ClientID = -1;
+	Packet.m_Address = *pAddr;
+	Packet.m_Flags = NETSENDFLAG_CONNLESS;
+	Packet.m_DataSize = sizeof(SERVERBROWSE_FWRESPONSE);
+	Packet.m_pData = SERVERBROWSE_FWRESPONSE;
+	m_pNetServer->Send(&Packet);
 }
 	
 static void register_send_heartbeat(NETADDR addr)
 {
 	static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2];
 	unsigned short port = config.sv_port;
-	NETCHUNK packet;
+	CNetChunk Packet;
 	
 	mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT));
 	
-	packet.client_id = -1;
-	packet.address = addr;
-	packet.flags = NETSENDFLAG_CONNLESS;
-	packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2;
-	packet.data = &data;
+	Packet.m_ClientID = -1;
+	Packet.m_Address = addr;
+	Packet.m_Flags = NETSENDFLAG_CONNLESS;
+	Packet.m_DataSize = sizeof(SERVERBROWSE_HEARTBEAT) + 2;
+	Packet.m_pData = &data;
 
 	/* supply the set port that the master can use if it has problems */	
 	if(config.sv_external_port)
 		port = config.sv_external_port;
 	data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8;
 	data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff;
-	netserver_send(net, &packet);
+	m_pNetServer->Send(&Packet);
 }
 
-static void register_send_count_request(NETADDR addr)
+static void register_send_count_request(NETADDR Addr)
 {
-	NETCHUNK packet;
-	packet.client_id = -1;
-	packet.address = addr;
-	packet.flags = NETSENDFLAG_CONNLESS;
-	packet.data_size = sizeof(SERVERBROWSE_GETCOUNT);
-	packet.data = SERVERBROWSE_GETCOUNT;
-	netserver_send(net, &packet);
+	CNetChunk Packet;
+	Packet.m_ClientID = -1;
+	Packet.m_Address = Addr;
+	Packet.m_Flags = NETSENDFLAG_CONNLESS;
+	Packet.m_DataSize = sizeof(SERVERBROWSE_GETCOUNT);
+	Packet.m_pData = SERVERBROWSE_GETCOUNT;
+	m_pNetServer->Send(&Packet);
 }
 
 typedef struct
@@ -221,50 +221,49 @@ void register_update()
 	}
 }
 
-static void register_got_count(NETCHUNK *p)
+static void register_got_count(CNetChunk *pChunk)
 {
-	unsigned char *data = (unsigned char *)p->data;
-	int count = (data[sizeof(SERVERBROWSE_COUNT)]<<8) | data[sizeof(SERVERBROWSE_COUNT)+1];
-	int i;
+	unsigned char *pData = (unsigned char *)pChunk->m_pData;
+	int Count = (pData[sizeof(SERVERBROWSE_COUNT)]<<8) | pData[sizeof(SERVERBROWSE_COUNT)+1];
 
-	for(i = 0; i < MAX_MASTERSERVERS; i++)
+	for(int i = 0; i < MAX_MASTERSERVERS; i++)
 	{
-		if(net_addr_comp(&masterserver_info[i].addr, &p->address) == 0)
+		if(net_addr_comp(&masterserver_info[i].addr, &pChunk->m_Address) == 0)
 		{
-			masterserver_info[i].count = count;
+			masterserver_info[i].count = Count;
 			break;
 		}
 	}
 }
 
-int register_process_packet(NETCHUNK *packet)
+int register_process_packet(CNetChunk *pPacket)
 {
-	if(packet->data_size == sizeof(SERVERBROWSE_FWCHECK) &&
-		memcmp(packet->data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
+	if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) &&
+		memcmp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
 	{
-		register_send_fwcheckresponse(&packet->address);
+		register_send_fwcheckresponse(&pPacket->m_Address);
 		return 1;
 	}
-	else if(packet->data_size == sizeof(SERVERBROWSE_FWOK) &&
-		memcmp(packet->data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0)
+	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWOK) &&
+		memcmp(pPacket->m_pData, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0)
 	{
 		if(register_first)
 			dbg_msg("register", "no firewall/nat problems detected");
 		register_new_state(REGISTERSTATE_REGISTERED);
 		return 1;
 	}
-	else if(packet->data_size == sizeof(SERVERBROWSE_FWERROR) &&
-		memcmp(packet->data, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0)
+	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWERROR) &&
+		memcmp(pPacket->m_pData, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0)
 	{
 		dbg_msg("register", "ERROR: the master server reports that clients can not connect to this server.");
 		dbg_msg("register", "ERROR: configure your firewall/nat to let through udp on port %d.", config.sv_port);
 		register_new_state(REGISTERSTATE_ERROR);
 		return 1;
 	}
-	else if(packet->data_size == sizeof(SERVERBROWSE_COUNT)+2 &&
-		memcmp(packet->data, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0)
+	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_COUNT)+2 &&
+		memcmp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0)
 	{
-		register_got_count(packet);
+		register_got_count(pPacket);
 		return 1;
 	}
 
diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c
deleted file mode 100644
index 8763351d..00000000
--- a/src/engine/server/es_server.c
+++ /dev/null
@@ -1,1380 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include <base/system.h>
-
-#include <engine/e_config.h>
-#include <engine/e_engine.h>
-#include <engine/e_server_interface.h>
-
-#include <engine/e_protocol.h>
-#include <engine/e_snapshot.h>
-
-#include <engine/e_compression.h>
-
-#include <engine/e_network.h>
-#include <engine/e_config.h>
-#include <engine/e_packer.h>
-#include <engine/e_datafile.h>
-#include <engine/e_demorec.h>
-
-#include <mastersrv/mastersrv.h>
-
-#if defined(CONF_FAMILY_WINDOWS) 
-	#define _WIN32_WINNT 0x0500 
-	#include <windows.h> 
-#endif 
-
-static SNAPBUILD builder;
-
-static int64 game_start_time;
-static int current_tick = 0;
-static int run_server = 1;
-
-static char browseinfo_gametype[16] = {0};
-static int browseinfo_progression = -1;
-
-static int64 lastheartbeat;
-/*static NETADDR4 master_server;*/
-
-static char current_map[64];
-static int current_map_crc;
-static unsigned char *current_map_data = 0;
-static int current_map_size = 0;
-
-void *snap_new_item(int type, int id, int size)
-{
-	dbg_assert(type >= 0 && type <=0xffff, "incorrect type");
-	dbg_assert(id >= 0 && id <=0xffff, "incorrect id");
-	return snapbuild_new_item(&builder, type, id, size);
-}
-
-typedef struct
-{
-	short next;
-	short state; /* 0 = free, 1 = alloced, 2 = timed */
-	int timeout;
-} SNAP_ID;
-
-static const int MAX_IDS = 16*1024; /* should be lowered */
-static SNAP_ID snap_ids[16*1024];
-static int snap_first_free_id;
-static int snap_first_timed_id;
-static int snap_last_timed_id;
-static int snap_id_usage;
-static int snap_id_inusage;
-static int snap_id_inited = 0;
-
-enum
-{
-	SRVCLIENT_STATE_EMPTY = 0,
-	SRVCLIENT_STATE_AUTH,
-	SRVCLIENT_STATE_CONNECTING,
-	SRVCLIENT_STATE_READY,
-	SRVCLIENT_STATE_INGAME,
-	
-	SRVCLIENT_SNAPRATE_INIT=0,
-	SRVCLIENT_SNAPRATE_FULL,
-	SRVCLIENT_SNAPRATE_RECOVER
-};
-
-typedef struct 
-{
-	int data[MAX_INPUT_SIZE];
-	int game_tick; /* the tick that was chosen for the input */
-} CLIENT_INPUT;
-	
-/* */
-typedef struct
-{
-	/* connection state info */
-	int state;
-	int latency;
-	int snap_rate;
-	
-	int last_acked_snapshot;
-	int last_input_tick;
-	SNAPSTORAGE snapshots;
-	
-	CLIENT_INPUT latestinput;
-	CLIENT_INPUT inputs[200]; /* TODO: handle input better */
-	int current_input;
-	
-	char name[MAX_NAME_LENGTH];
-	char clan[MAX_CLANNAME_LENGTH];
-	int score;
-	int authed;
-} CLIENT;
-
-static CLIENT clients[MAX_CLIENTS];
-NETSERVER *net;
-
-static void snap_init_id()
-{
-	int i;
-	if(snap_id_inited)
-		return;
-	
-	for(i = 0; i < MAX_IDS; i++)
-	{
-		snap_ids[i].next = i+1;
-		snap_ids[i].state = 0;
-	}
-		
-	snap_ids[MAX_IDS-1].next = -1;
-	snap_first_free_id = 0;
-	snap_first_timed_id = -1;
-	snap_last_timed_id = -1;
-	snap_id_usage = 0;
-	snap_id_inusage = 0;
-	
-	snap_id_inited = 1;
-}
-
-static void snap_remove_first_timeout()
-{
-	int next_timed = snap_ids[snap_first_timed_id].next;
-	
-	/* add it to the free list */
-	snap_ids[snap_first_timed_id].next = snap_first_free_id;
-	snap_ids[snap_first_timed_id].state = 0;
-	snap_first_free_id = snap_first_timed_id;
-	
-	/* remove it from the timed list */
-	snap_first_timed_id = next_timed;
-	if(snap_first_timed_id == -1)
-		snap_last_timed_id = -1;
-		
-	snap_id_usage--;
-}
-
-int snap_new_id()
-{
-	int id;
-	int64 now = time_get();
-	if(!snap_id_inited)
-		snap_init_id();
-	
-	/* process timed ids */
-	while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now)
-		snap_remove_first_timeout();
-	
-	id = snap_first_free_id;
-	dbg_assert(id != -1, "id error");
-	snap_first_free_id = snap_ids[snap_first_free_id].next;
-	snap_ids[id].state = 1;
-	snap_id_usage++;
-	snap_id_inusage++;
-	return id;
-}
-
-void snap_timeout_ids()
-{
-	/* process timed ids */
-	while(snap_first_timed_id != -1)
-		snap_remove_first_timeout();
-}
-
-void snap_free_id(int id)
-{
-	dbg_assert(snap_ids[id].state == 1, "id is not alloced");
-
-	snap_id_inusage--;
-	snap_ids[id].state = 2;
-	snap_ids[id].timeout = time_get()+time_freq()*5;
-	snap_ids[id].next = -1;
-	
-	if(snap_last_timed_id != -1)
-	{
-		snap_ids[snap_last_timed_id].next = id;
-		snap_last_timed_id = id;
-	}
-	else
-	{
-		snap_first_timed_id = id;
-		snap_last_timed_id = id;
-	}
-}
-
-int *server_latestinput(int client_id, int *size)
-{
-	if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
-		return 0;
-	return clients[client_id].latestinput.data;
-}
-
-const char *server_clientname(int client_id)
-{
-	if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
-		return "(invalid client)";
-	return clients[client_id].name;
-}
-
-static const char *str_ltrim(const char *str)
-{
-	while(*str && *str <= 32)
-		str++;
-	return str;
-}
-
-static void str_rtrim(char *str)
-{
-	int i = str_length(str);
-	while(i >= 0)
-	{
-		if(str[i] > 32)
-			break;
-		str[i] = 0;
-		i--;
-	}
-}
-
-static int server_try_setclientname(int client_id, const char *name)
-{
-	int i;
-	char trimmed_name[64];
-
-	/* trim the name */
-	str_copy(trimmed_name, str_ltrim(name), sizeof(trimmed_name));
-	str_rtrim(trimmed_name);
-	dbg_msg("", "'%s' -> '%s'", name, trimmed_name);
-	name = trimmed_name;
-	
-	
-	/* check for empty names */
-	if(!name[0])
-		return -1;
-	
-	/* make sure that two clients doesn't have the same name */
-	for(i = 0; i < MAX_CLIENTS; i++)
-		if(i != client_id && clients[i].state >= SRVCLIENT_STATE_READY)
-		{
-			if(strcmp(name, clients[i].name) == 0)
-				return -1;
-		}
-
-	/* set the client name */
-	str_copy(clients[client_id].name, name, MAX_NAME_LENGTH);
-	return 0;
-}
-
-void server_setclientname(int client_id, const char *name)
-{
-	char nametry[MAX_NAME_LENGTH];
-	int i;
-	if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
-		return;
-		
-	if(!name)
-		return;
-		
-	str_copy(nametry, name, MAX_NAME_LENGTH);
-	if(server_try_setclientname(client_id, nametry))
-	{
-		/* auto rename */
-		for(i = 1;; i++)
-		{
-			str_format(nametry, MAX_NAME_LENGTH, "(%d)%s", i, name);
-			if(server_try_setclientname(client_id, nametry) == 0)
-				break;
-		}
-	}
-}
-
-void server_setclientscore(int client_id, int score)
-{
-	if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
-		return;
-	clients[client_id].score = score;
-}
-
-void server_setbrowseinfo(const char *game_type, int progression)
-{
-	str_copy(browseinfo_gametype, game_type, sizeof(browseinfo_gametype));
-	browseinfo_progression = progression;
-	if(browseinfo_progression > 100)
-		browseinfo_progression = 100;
-	if(browseinfo_progression < -1)
-		browseinfo_progression = -1;
-}
-
-void server_kick(int client_id, const char *reason)
-{
-	if(client_id < 0 || client_id > MAX_CLIENTS)
-		return;
-		
-	if(clients[client_id].state != SRVCLIENT_STATE_EMPTY)
-		netserver_drop(net, client_id, reason);
-}
-
-int server_tick()
-{
-	return current_tick;
-}
-
-int64 server_tick_start_time(int tick)
-{
-	return game_start_time + (time_freq()*tick)/SERVER_TICK_SPEED;
-}
-
-int server_tickspeed()
-{
-	return SERVER_TICK_SPEED;
-}
-
-int server_init()
-{
-	int i;
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		clients[i].state = SRVCLIENT_STATE_EMPTY;
-		clients[i].name[0] = 0;
-		clients[i].clan[0] = 0;
-		snapstorage_init(&clients[i].snapshots);
-	}
-
-	current_tick = 0;
-
-	return 0;
-}
-
-int server_getclientinfo(int client_id, CLIENT_INFO *info)
-{
-	dbg_assert(client_id >= 0 && client_id < MAX_CLIENTS, "client_id is not valid");
-	dbg_assert(info != 0, "info can not be null");
-
-	if(clients[client_id].state == SRVCLIENT_STATE_INGAME)
-	{
-		info->name = clients[client_id].name;
-		info->latency = clients[client_id].latency;
-		return 1;
-	}
-	return 0;
-}
-
-int server_send_msg(int client_id)
-{
-	const MSG_INFO *info = msg_get_info();
-	NETCHUNK packet;
-	if(!info)
-		return -1;
-		
-	mem_zero(&packet, sizeof(NETCHUNK));
-	
-	packet.client_id = client_id;
-	packet.data = info->data;
-	packet.data_size = info->size;
-
-	if(info->flags&MSGFLAG_VITAL)
-		packet.flags |= NETSENDFLAG_VITAL;
-	if(info->flags&MSGFLAG_FLUSH)
-		packet.flags |= NETSENDFLAG_FLUSH;
-	
-	/* write message to demo recorder */
-	if(!(info->flags&MSGFLAG_NORECORD))
-		demorec_record_message(info->data, info->size);
-
-	if(!(info->flags&MSGFLAG_NOSEND))
-	{
-		if(client_id == -1)
-		{
-			/* broadcast */
-			int i;
-			for(i = 0; i < MAX_CLIENTS; i++)
-				if(clients[i].state == SRVCLIENT_STATE_INGAME)
-				{
-					packet.client_id = i;
-					netserver_send(net, &packet);
-				}
-		}
-		else
-			netserver_send(net, &packet);
-	}
-	return 0;
-}
-
-static void server_do_snap()
-{
-	int i;
-	
-	{
-		static PERFORMACE_INFO scope = {"presnap", 0};
-		perf_start(&scope);
-		mods_presnap();
-		perf_end();
-	}
-	
-	/* create snapshot for demo recording */
-	if(demorec_isrecording())
-	{
-		char data[MAX_SNAPSHOT_SIZE];
-		int snapshot_size;
-
-		/* build snap and possibly add some messages */
-		snapbuild_init(&builder);
-		mods_snap(-1);
-		snapshot_size = snapbuild_finish(&builder, data);
-		
-		/* write snapshot */
-		demorec_record_snapshot(server_tick(), data, snapshot_size);
-	}
-
-	/* create snapshots for all clients */
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		/* client must be ingame to recive snapshots */
-		if(clients[i].state != SRVCLIENT_STATE_INGAME)
-			continue;
-			
-		/* this client is trying to recover, don't spam snapshots */
-		if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_RECOVER && (server_tick()%50) != 0)
-			continue;
-			
-		/* this client is trying to recover, don't spam snapshots */
-		if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_INIT && (server_tick()%10) != 0)
-			continue;
-			
-		{
-			char data[MAX_SNAPSHOT_SIZE];
-			char deltadata[MAX_SNAPSHOT_SIZE];
-			char compdata[MAX_SNAPSHOT_SIZE];
-			int snapshot_size;
-			int crc;
-			static SNAPSHOT emptysnap;
-			SNAPSHOT *deltashot = &emptysnap;
-			int deltashot_size;
-			int delta_tick = -1;
-			int deltasize;
-			static PERFORMACE_INFO scope = {"build", 0};
-			perf_start(&scope);
-
-			snapbuild_init(&builder);
-
-			{
-				static PERFORMACE_INFO scope = {"modsnap", 0};
-				perf_start(&scope);
-				mods_snap(i);
-				perf_end();
-			}
-
-			/* finish snapshot */
-			snapshot_size = snapbuild_finish(&builder, data);
-			crc = snapshot_crc((SNAPSHOT*)data);
-
-			/* remove old snapshos */
-			/* keep 3 seconds worth of snapshots */
-			snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED*3);
-			
-			/* save it the snapshot */
-			snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data, 0);
-			
-			/* find snapshot that we can preform delta against */
-			emptysnap.data_size = 0;
-			emptysnap.num_items = 0;
-			
-			{
-				deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot, 0);
-				if(deltashot_size >= 0)
-					delta_tick = clients[i].last_acked_snapshot;
-				else
-				{
-					/* no acked package found, force client to recover rate */
-					if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_FULL)
-						clients[i].snap_rate = SRVCLIENT_SNAPRATE_RECOVER;
-				}
-			}
-			
-			/* create delta */
-			{
-				static PERFORMACE_INFO scope = {"delta", 0};
-				perf_start(&scope);
-				deltasize = snapshot_create_delta(deltashot, (SNAPSHOT*)data, deltadata);
-				perf_end();
-			}
-
-			
-			if(deltasize)
-			{
-				/* compress it */
-				int snapshot_size;
-				const int max_size = MAX_SNAPSHOT_PACKSIZE;
-				int numpackets;
-				int n, left;
-
-				{				
-					static PERFORMACE_INFO scope = {"compress", 0};
-					/*char buffer[512];*/
-					perf_start(&scope);
-					snapshot_size = intpack_compress(deltadata, deltasize, compdata);
-					
-					/*str_hex(buffer, sizeof(buffer), compdata, snapshot_size);
-					dbg_msg("", "deltasize=%d -> %d : %s", deltasize, snapshot_size, buffer);*/
-					
-					perf_end();
-				}
-
-				numpackets = (snapshot_size+max_size-1)/max_size;
-				
-				for(n = 0, left = snapshot_size; left; n++)
-				{
-					int chunk = left < max_size ? left : max_size;
-					left -= chunk;
-
-					if(numpackets == 1)
-						msg_pack_start_system(NETMSG_SNAPSINGLE, MSGFLAG_FLUSH);
-					else
-						msg_pack_start_system(NETMSG_SNAP, MSGFLAG_FLUSH);
-						
-					msg_pack_int(current_tick);
-					msg_pack_int(current_tick-delta_tick); /* compressed with */
-					
-					if(numpackets != 1)
-					{
-						msg_pack_int(numpackets);
-						msg_pack_int(n);
-					}
-					
-					msg_pack_int(crc);
-					msg_pack_int(chunk);
-					msg_pack_raw(&compdata[n*max_size], chunk);
-					msg_pack_end();
-					server_send_msg(i);
-				}
-			}
-			else
-			{
-				msg_pack_start_system(NETMSG_SNAPEMPTY, MSGFLAG_FLUSH);
-				msg_pack_int(current_tick);
-				msg_pack_int(current_tick-delta_tick); /* compressed with */
-				msg_pack_end();
-				server_send_msg(i);
-			}
-			
-			perf_end();
-		}
-	}
-
-	mods_postsnap();
-}
-
-
-static void reset_client(int cid)
-{
-	/* reset input */
-	int i;
-	for(i = 0; i < 200; i++)
-		clients[cid].inputs[i].game_tick = -1;
-	clients[cid].current_input = 0;
-	mem_zero(&clients[cid].latestinput, sizeof(clients[cid].latestinput));
-
-	snapstorage_purge_all(&clients[cid].snapshots);
-	clients[cid].last_acked_snapshot = -1;
-	clients[cid].last_input_tick = -1;
-	clients[cid].snap_rate = SRVCLIENT_SNAPRATE_INIT;
-	clients[cid].score = 0;
-
-}
-
-static int new_client_callback(int cid, void *user)
-{
-	clients[cid].state = SRVCLIENT_STATE_AUTH;
-	clients[cid].name[0] = 0;
-	clients[cid].clan[0] = 0;
-	clients[cid].authed = 0;
-	reset_client(cid);
-	return 0;
-}
-
-static int del_client_callback(int cid, void *user)
-{
-	/* notify the mod about the drop */
-	if(clients[cid].state >= SRVCLIENT_STATE_READY)
-		mods_client_drop(cid);
-	
-	clients[cid].state = SRVCLIENT_STATE_EMPTY;
-	clients[cid].name[0] = 0;
-	clients[cid].clan[0] = 0;
-	clients[cid].authed = 0;
-	snapstorage_purge_all(&clients[cid].snapshots);
-	return 0;
-}
-
-static void server_send_map(int cid)
-{
-	msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH);
-	msg_pack_string(config.sv_map, 0);
-	msg_pack_int(current_map_crc);
-	msg_pack_end();
-	server_send_msg(cid);
-}
-
-static void server_send_rcon_line(int cid, const char *line)
-{
-	msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL);
-	msg_pack_string(line, 512);
-	msg_pack_end();
-	server_send_msg(cid);
-}
-
-static void server_send_rcon_line_authed(const char *line, void *user_data)
-{
-	static volatile int reentry_guard = 0;
-	int i;
-	
-	if(reentry_guard) return;
-	reentry_guard++;
-	
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		if(clients[i].state != SRVCLIENT_STATE_EMPTY && clients[i].authed)
-			server_send_rcon_line(i, line);
-	}
-	
-	reentry_guard--;
-}
-
-static void server_process_client_packet(NETCHUNK *packet)
-{
-	int cid = packet->client_id;
-	NETADDR addr;
-	
-	int sys;
-	int msg = msg_unpack_start(packet->data, packet->data_size, &sys);
-	
-	if(clients[cid].state == SRVCLIENT_STATE_AUTH)
-	{
-		if(sys && msg == NETMSG_INFO)
-		{
-			char version[64];
-			const char *password;
-			str_copy(version, msg_unpack_string(), 64);
-			if(strcmp(version, mods_net_version()) != 0)
-			{
-				/* OH FUCK! wrong version, drop him */
-				char reason[256];
-				str_format(reason, sizeof(reason), "wrong version. server is running '%s' and client '%s'.", mods_net_version(), version);
-				netserver_drop(net, cid, reason);
-				return;
-			}
-			
-			str_copy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH);
-			str_copy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH);
-			password = msg_unpack_string();
-			
-			if(config.password[0] != 0 && strcmp(config.password, password) != 0)
-			{
-				/* wrong password */
-				netserver_drop(net, cid, "wrong password");
-				return;
-			}
-			
-			clients[cid].state = SRVCLIENT_STATE_CONNECTING;
-			server_send_map(cid);
-		}
-	}
-	else
-	{
-		if(sys)
-		{
-			/* system message */
-			if(msg == NETMSG_REQUEST_MAP_DATA)
-			{
-				int chunk = msg_unpack_int();
-				int chunk_size = 1024-128;
-				int offset = chunk * chunk_size;
-				int last = 0;
-				
-				/* drop faulty map data requests */
-				if(chunk < 0 || offset > current_map_size)
-					return;
-				
-				if(offset+chunk_size >= current_map_size)
-				{
-					chunk_size = current_map_size-offset;
-					if(chunk_size < 0)
-						chunk_size = 0;
-					last = 1;
-				}
-				
-				msg_pack_start_system(NETMSG_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH);
-				msg_pack_int(last);
-				msg_pack_int(current_map_size);
-				msg_pack_int(chunk_size);
-				msg_pack_raw(&current_map_data[offset], chunk_size);
-				msg_pack_end();
-				server_send_msg(cid);
-				
-				if(config.debug)
-					dbg_msg("server", "sending chunk %d with size %d", chunk, chunk_size);
-			}
-			else if(msg == NETMSG_READY)
-			{
-				if(clients[cid].state == SRVCLIENT_STATE_CONNECTING)
-				{
-					netserver_client_addr(net, cid, &addr);
-					
-					dbg_msg("server", "player is ready. cid=%x ip=%d.%d.%d.%d",
-						cid,
-						addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3]
-						);
-					clients[cid].state = SRVCLIENT_STATE_READY;
-					mods_connected(cid);
-				}
-			}
-			else if(msg == NETMSG_ENTERGAME)
-			{
-				if(clients[cid].state == SRVCLIENT_STATE_READY)
-				{
-					netserver_client_addr(net, cid, &addr);
-					
-					dbg_msg("server", "player has entered the game. cid=%x ip=%d.%d.%d.%d",
-						cid,
-						addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3]
-						);
-					clients[cid].state = SRVCLIENT_STATE_INGAME;
-					mods_client_enter(cid);
-				}
-			}
-			else if(msg == NETMSG_INPUT)
-			{
-				int tick, size, i;
-				CLIENT_INPUT *input;
-				int64 tagtime;
-				
-				clients[cid].last_acked_snapshot = msg_unpack_int();
-				tick = msg_unpack_int();
-				size = msg_unpack_int();
-				
-				/* check for errors */
-				if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE)
-					return;
-
-				if(clients[cid].last_acked_snapshot > 0)
-					clients[cid].snap_rate = SRVCLIENT_SNAPRATE_FULL;
-					
-				if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0, 0) >= 0)
-					clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq());
-
-				/* add message to report the input timing */
-				/* skip packets that are old */
-				if(tick > clients[cid].last_input_tick)
-				{
-					int time_left = ((server_tick_start_time(tick)-time_get())*1000) / time_freq();
-					msg_pack_start_system(NETMSG_INPUTTIMING, 0);
-					msg_pack_int(tick);
-					msg_pack_int(time_left);
-					msg_pack_end();
-					server_send_msg(cid);
-				}
-
-				clients[cid].last_input_tick = tick;
-
-				input = &clients[cid].inputs[clients[cid].current_input];
-				
-				if(tick <= server_tick())
-					tick = server_tick()+1;
-
-				input->game_tick = tick;
-				
-				for(i = 0; i < size/4; i++)
-					input->data[i] = msg_unpack_int();
-				
-				mem_copy(clients[cid].latestinput.data, input->data, MAX_INPUT_SIZE*sizeof(int));
-				
-				clients[cid].current_input++;
-				clients[cid].current_input %= 200;
-			
-				/* call the mod with the fresh input data */
-				if(clients[cid].state == SRVCLIENT_STATE_INGAME)
-					mods_client_direct_input(cid, clients[cid].latestinput.data);
-			}
-			else if(msg == NETMSG_RCON_CMD)
-			{
-				const char *cmd = msg_unpack_string();
-				
-				if(msg_unpack_error() == 0 && clients[cid].authed)
-				{
-					dbg_msg("server", "cid=%d rcon='%s'", cid, cmd);
-					console_execute_line(cmd);
-				}
-			}
-			else if(msg == NETMSG_RCON_AUTH)
-			{
-				const char *pw;
-				msg_unpack_string(); /* login name, not used */
-				pw = msg_unpack_string();
-				
-				if(msg_unpack_error() == 0)
-				{
-					if(config.sv_rcon_password[0] == 0)
-					{
-						server_send_rcon_line(cid, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
-					}
-					else if(strcmp(pw, config.sv_rcon_password) == 0)
-					{
-						msg_pack_start_system(NETMSG_RCON_AUTH_STATUS, MSGFLAG_VITAL);
-						msg_pack_int(1);
-						msg_pack_end();
-						server_send_msg(cid);
-						
-						clients[cid].authed = 1;
-						server_send_rcon_line(cid, "Authentication successful. Remote console access granted.");
-						dbg_msg("server", "cid=%d authed", cid);
-					}
-					else
-					{
-						server_send_rcon_line(cid, "Wrong password.");
-					}
-				}
-			}
-			else if(msg == NETMSG_PING)
-			{
-				msg_pack_start_system(NETMSG_PING_REPLY, 0);
-				msg_pack_end();
-				server_send_msg(cid);
-			}
-			else
-			{
-				char hex[] = "0123456789ABCDEF";
-				char buf[512];
-				int b;
-
-				for(b = 0; b < packet->data_size && b < 32; b++)
-				{
-					buf[b*3] = hex[((const unsigned char *)packet->data)[b]>>4];
-					buf[b*3+1] = hex[((const unsigned char *)packet->data)[b]&0xf];
-					buf[b*3+2] = ' ';
-					buf[b*3+3] = 0;
-				}
-
-				dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size);
-				dbg_msg("server", "%s", buf);
-				
-			}
-		}
-		else
-		{
-			/* game message */
-			if(clients[cid].state >= SRVCLIENT_STATE_READY)
-				mods_message(msg, cid);
-		}
-	}
-}
-
-
-int server_ban_add(NETADDR addr, int seconds)
-{
-	return netserver_ban_add(net, addr, seconds);	
-}
-
-int server_ban_remove(NETADDR addr)
-{
-	return netserver_ban_remove(net, addr);
-}
-
-static void server_send_serverinfo(NETADDR *addr, int token)
-{
-	NETCHUNK packet;
-	PACKER p;
-	char buf[128];
-
-	/* count the players */	
-	int player_count = 0;
-	int i;
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		if(clients[i].state != SRVCLIENT_STATE_EMPTY)
-			player_count++;
-	}
-	
-	packer_reset(&p);
-
-	if(token >= 0)
-	{
-		/* new token based format */
-		packer_add_raw(&p, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO));
-		str_format(buf, sizeof(buf), "%d", token);
-		packer_add_string(&p, buf, 6);
-	}
-	else
-	{
-		/* old format */
-		packer_add_raw(&p, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO));
-	}
-	
-	packer_add_string(&p, mods_version(), 32);
-	packer_add_string(&p, config.sv_name, 64);
-	packer_add_string(&p, config.sv_map, 32);
-
-	/* gametype */
-	packer_add_string(&p, browseinfo_gametype, 16);
-
-	/* flags */
-	i = 0;
-	if(config.password[0])   /* password set */
-		i |= SRVFLAG_PASSWORD;
-	str_format(buf, sizeof(buf), "%d", i);
-	packer_add_string(&p, buf, 2);
-
-	/* progression */
-	str_format(buf, sizeof(buf), "%d", browseinfo_progression);
-	packer_add_string(&p, buf, 4);
-	
-	str_format(buf, sizeof(buf), "%d", player_count); packer_add_string(&p, buf, 3);  /* num players */
-	str_format(buf, sizeof(buf), "%d", netserver_max_clients(net)); packer_add_string(&p, buf, 3); /* max players */
-
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		if(clients[i].state != SRVCLIENT_STATE_EMPTY)
-		{
-			packer_add_string(&p, clients[i].name, 48);  /* player name */
-			str_format(buf, sizeof(buf), "%d", clients[i].score); packer_add_string(&p, buf, 6);  /* player score */
-		}
-	}
-	
-	
-	packet.client_id = -1;
-	packet.address = *addr;
-	packet.flags = NETSENDFLAG_CONNLESS;
-	packet.data_size = packer_size(&p);
-	packet.data = packer_data(&p);
-	netserver_send(net, &packet);
-}
-
-extern int register_process_packet(NETCHUNK *packet);
-extern int register_update();
-
-static void server_pump_network()
-{
-	NETCHUNK packet;
-
-	netserver_update(net);
-	
-	/* process packets */
-	while(netserver_recv(net, &packet))
-	{
-		if(packet.client_id == -1)
-		{
-			/* stateless */
-			if(!register_process_packet(&packet))
-			{
-				if(packet.data_size == sizeof(SERVERBROWSE_GETINFO)+1 &&
-					memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
-				{
-					server_send_serverinfo(&packet.address, ((unsigned char *)packet.data)[sizeof(SERVERBROWSE_GETINFO)]);
-				}
-				
-				
-				if(packet.data_size == sizeof(SERVERBROWSE_OLD_GETINFO) &&
-					memcmp(packet.data, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0)
-				{
-					server_send_serverinfo(&packet.address, -1);
-				}
-			}
-		}
-		else
-			server_process_client_packet(&packet);
-	}
-}
-
-static int server_load_map(const char *mapname)
-{
-	DATAFILE *df;
-	char buf[512];
-	str_format(buf, sizeof(buf), "maps/%s.map", mapname);
-	df = datafile_load(buf);
-	if(!df)
-		return 0;
-	
-	/* stop recording when we change map */
-	demorec_record_stop();
-	
-	/* reinit snapshot ids */
-	snap_timeout_ids();
-	
-	/* get the crc of the map */
-	current_map_crc = datafile_crc(buf);
-	dbg_msg("server", "%s crc is %08x", buf, current_map_crc);
-		
-	str_copy(current_map, mapname, sizeof(current_map));
-	map_set(df);
-	
-	/* load compelate map into memory for download */
-	{
-		IOHANDLE file = engine_openfile(buf, IOFLAG_READ);
-		current_map_size = (int)io_length(file);
-		if(current_map_data)
-			mem_free(current_map_data);
-		current_map_data = (unsigned char *)mem_alloc(current_map_size, 1);
-		io_read(file, current_map_data, current_map_size);
-		io_close(file);
-	}
-	return 1;
-}
-
-static int server_run()
-{
-	NETADDR bindaddr;
-
-	snap_init_id();
-	net_init();
-	
-	/* */
-	console_register_print_callback(server_send_rcon_line_authed, 0);
-
-	/* load map */
-	if(!server_load_map(config.sv_map))
-	{
-		dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
-		return -1;
-	}
-	
-	/* start server */
-	/* TODO: IPv6 support */
-	if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0)
-	{
-		/* sweet! */
-		bindaddr.port = config.sv_port;
-	}
-	else
-	{
-		mem_zero(&bindaddr, sizeof(bindaddr));
-		bindaddr.port = config.sv_port;
-	}
-	
-	net = netserver_open(bindaddr, config.sv_max_clients, 0);
-	if(!net)
-	{
-		dbg_msg("server", "couldn't open socket. port might already be in use");
-		return -1;
-	}
-	
-	netserver_set_callbacks(net, new_client_callback, del_client_callback, 0);
-	
-	dbg_msg("server", "server name is '%s'", config.sv_name);
-	
-	mods_init();
-	dbg_msg("server", "version %s", mods_net_version());
-
-	/* start game */
-	{
-		int64 reporttime = time_get();
-		int reportinterval = 3;
-	
-		lastheartbeat = 0;
-		game_start_time = time_get();
-	
-		if(config.debug)
-			dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024);
-
-		while(run_server)
-		{
-			static PERFORMACE_INFO rootscope = {"root", 0};
-			int64 t = time_get();
-			int new_ticks = 0;
-			
-			perf_start(&rootscope);
-			
-			/* load new map TODO: don't poll this */
-			if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload)
-			{
-				config.sv_map_reload = 0;
-				
-				/* load map */
-				if(server_load_map(config.sv_map))
-				{
-					int c;
-					
-					/* new map loaded */
-					mods_shutdown();
-					
-					for(c = 0; c < MAX_CLIENTS; c++)
-					{
-						if(clients[c].state == SRVCLIENT_STATE_EMPTY)
-							continue;
-						
-						server_send_map(c);
-						reset_client(c);
-						clients[c].state = SRVCLIENT_STATE_CONNECTING;
-					}
-					
-					game_start_time = time_get();
-					current_tick = 0;
-					mods_init();
-				}
-				else
-				{
-					dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
-					config_set_sv_map(&config, current_map);
-				}
-			}
-			
-			while(t > server_tick_start_time(current_tick+1))
-			{
-				current_tick++;
-				new_ticks++;
-				
-				/* apply new input */
-				{
-					static PERFORMACE_INFO scope = {"input", 0};
-					int c, i;
-					
-					perf_start(&scope);
-					
-					for(c = 0; c < MAX_CLIENTS; c++)
-					{
-						if(clients[c].state == SRVCLIENT_STATE_EMPTY)
-							continue;
-						for(i = 0; i < 200; i++)
-						{
-							if(clients[c].inputs[i].game_tick == server_tick())
-							{
-								if(clients[c].state == SRVCLIENT_STATE_INGAME)
-									mods_client_predicted_input(c, clients[c].inputs[i].data);
-								break;
-							}
-						}
-					}
-					
-					perf_end();
-				}
-				
-				/* progress game */
-				{
-					static PERFORMACE_INFO scope = {"tick", 0};
-					perf_start(&scope);
-					mods_tick();
-					perf_end();
-				}
-			}
-			
-			/* snap game */
-			if(new_ticks)
-			{
-				if(config.sv_high_bandwidth || (current_tick%2) == 0)
-				{
-					static PERFORMACE_INFO scope = {"snap", 0};
-					perf_start(&scope);
-					server_do_snap();
-					perf_end();
-				}
-			}
-			
-			/* master server stuff */
-			register_update();
-	
-
-			{
-				static PERFORMACE_INFO scope = {"net", 0};
-				perf_start(&scope);
-				server_pump_network();
-				perf_end();
-			}
-
-			perf_end();
-	
-			if(reporttime < time_get())
-			{
-				if(config.debug)
-				{
-					/*
-					static NETSTATS prev_stats;
-					NETSTATS stats;
-					netserver_stats(net, &stats);
-					
-					perf_next();
-					
-					if(config.dbg_pref)
-						perf_dump(&rootscope);
-
-					dbg_msg("server", "send=%8d recv=%8d",
-						(stats.send_bytes - prev_stats.send_bytes)/reportinterval,
-						(stats.recv_bytes - prev_stats.recv_bytes)/reportinterval);
-						
-					prev_stats = stats;
-					*/
-				}
-	
-				reporttime += time_freq()*reportinterval;
-			}
-			
-			/* wait for incomming data */
-			net_socket_read_wait(netserver_socket(net), 5);
-		}
-	}
-
-	mods_shutdown();
-	map_unload();
-
-	if(current_map_data)
-		mem_free(current_map_data);
-	return 0;
-}
-
-static void con_kick(void *result, void *user_data)
-{
-	server_kick(console_arg_int(result, 0), "kicked by console");
-}
-
-static int str_allnum(const char *str)
-{
-	while(*str)
-	{
-		if(!(*str >= '0' && *str <= '9'))
-			return 0;
-		str++;
-	}
-	return 1;
-}
-
-static void con_ban(void *result, void *user_data)
-{
-	NETADDR addr;
-	char addrstr[128];
-	const char *str = console_arg_string(result, 0);
-	int minutes = 30;
-	
-	if(console_arg_num(result) > 1)
-		minutes = console_arg_int(result, 1);
-	
-	if(net_addr_from_str(&addr, str) == 0)
-		server_ban_add(addr, minutes*60);
-	else if(str_allnum(str))
-	{
-		NETADDR addr;
-		int cid = atoi(str);
-
-		if(cid < 0 || cid > MAX_CLIENTS || clients[cid].state == SRVCLIENT_STATE_EMPTY)
-		{
-			dbg_msg("server", "invalid client id");
-			return;
-		}
-
-		netserver_client_addr(net, cid, &addr);
-		server_ban_add(addr, minutes*60);
-	}
-	
-	addr.port = 0;
-	net_addr_str(&addr, addrstr, sizeof(addrstr));
-	
-	if(minutes)
-		dbg_msg("server", "banned %s for %d minutes", addrstr, minutes);
-	else
-		dbg_msg("server", "banned %s for life", addrstr);
-}
-
-static void con_unban(void *result, void *user_data)
-{
-	NETADDR addr;
-	const char *str = console_arg_string(result, 0);
-	
-	if(net_addr_from_str(&addr, str) == 0)
-		server_ban_remove(addr);
-	else
-		dbg_msg("server", "invalid network address");
-}
-
-static void con_bans(void *result, void *user_data)
-{
-	int i;
-	unsigned now = time_timestamp();
-	NETBANINFO info;
-	NETADDR addr;
-	int num = netserver_ban_num(net);
-	for(i = 0; i < num; i++)
-	{
-		unsigned t;
-		netserver_ban_get(net, i, &info);
-		addr = info.addr;
-		
-		if(info.expires == 0xffffffff)
-		{
-			dbg_msg("server", "#%d %d.%d.%d.%d for life", i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3]);
-		}
-		else
-		{
-			t = info.expires - now;
-			dbg_msg("server", "#%d %d.%d.%d.%d for %d minutes and %d seconds", i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], t/60, t%60);
-		}
-	}
-	dbg_msg("server", "%d ban(s)", num);
-}
-
-static void con_status(void *result, void *user_data)
-{
-	int i;
-	NETADDR addr;
-	for(i = 0; i < MAX_CLIENTS; i++)
-	{
-		if(clients[i].state == SRVCLIENT_STATE_INGAME)
-		{
-			netserver_client_addr(net, i, &addr);
-			dbg_msg("server", "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%d",
-				i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port,
-				clients[i].name, clients[i].score);
-		}
-	}
-}
-
-static void con_shutdown(void *result, void *user_data)
-{
-	run_server = 0;
-}
-
-static void con_record(void *result, void *user_data)
-{
-	char filename[512];
-	str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(result, 0));
-	demorec_record_start(filename, mods_net_version(), current_map, current_map_crc, "server");
-}
-
-static void con_stoprecord(void *result, void *user_data)
-{
-	demorec_record_stop();
-}
-
-static void server_register_commands()
-{
-	MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, con_kick, 0, "");
-	MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, con_ban, 0, "");
-	MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, con_unban, 0, "");
-	MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, con_bans, 0, "");
-	MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, con_status, 0, "");
-	MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, con_shutdown, 0, "");
-
-	MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_SERVER, con_record, 0, "");
-	MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, con_stoprecord, 0, "");
-}
-
-int main(int argc, char **argv)
-{
-#if defined(CONF_FAMILY_WINDOWS)
-	int i;
-	for(i = 1; i < argc; i++)
-	{
-		if(strcmp("-s", argv[i]) == 0 || strcmp("--silent", argv[i]) == 0)
-		{
-			ShowWindow(GetConsoleWindow(), SW_HIDE);
-			break;
-		}
-	}
-#endif
-
-	/* init the engine */
-	dbg_msg("server", "starting...");
-	engine_init("Teeworlds");
-	
-	/* register all console commands */
-	server_register_commands();
-	mods_console_init();
-	
-	/* parse the command line arguments */
-	engine_parse_arguments(argc, argv);
-	
-	/* run the server */
-	server_run();
-	return 0;
-}
-
diff --git a/src/engine/server/es_server.cpp b/src/engine/server/es_server.cpp
new file mode 100644
index 00000000..cc0e6e0f
--- /dev/null
+++ b/src/engine/server/es_server.cpp
@@ -0,0 +1,1969 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <base/system.h>
+
+#include <engine/e_config.h>
+#include <engine/e_engine.h>
+#include <engine/e_server_interface.h>
+
+#include <engine/e_protocol.h>
+#include <engine/e_snapshot.h>
+
+#include <engine/e_compression.h>
+
+#include <engine/e_network.h>
+#include <engine/e_config.h>
+#include <engine/e_packer.h>
+#include <engine/e_datafile.h>
+#include <engine/e_demorec.h>
+
+#include <mastersrv/mastersrv.h>
+
+#if defined(CONF_FAMILY_WINDOWS) 
+	#define _WIN32_WINNT 0x0500 
+	#include <windows.h> 
+#endif
+
+
+extern int register_process_packet(CNetChunk *pPacket);
+extern int register_update();
+
+static const char *str_ltrim(const char *str)
+{
+	while(*str && *str <= 32)
+		str++;
+	return str;
+}
+
+static void str_rtrim(char *str)
+{
+	int i = str_length(str);
+	while(i >= 0)
+	{
+		if(str[i] > 32)
+			break;
+		str[i] = 0;
+		i--;
+	}
+}
+
+
+class CSnapIDPool
+{
+	enum
+	{
+		MAX_IDS = 16*1024,
+	};
+
+	class CID
+	{
+	public:
+		short m_Next;
+		short m_State; /* 0 = free, 1 = alloced, 2 = timed */
+		int m_Timeout;
+	};
+
+	CID m_aIDs[MAX_IDS];
+		
+	int m_FirstFree;
+	int m_FirstTimed;
+	int m_LastTimed;
+	int m_Usage;
+	int m_InUsage;
+
+public:	
+
+	CSnapIDPool()
+	{
+		Reset();
+	}
+	
+	void Reset()
+	{
+		for(int i = 0; i < MAX_IDS; i++)
+		{
+			m_aIDs[i].m_Next = i+1;
+			m_aIDs[i].m_State = 0;
+		}
+			
+		m_aIDs[MAX_IDS-1].m_Next = -1;
+		m_FirstFree = 0;
+		m_FirstTimed = -1;
+		m_LastTimed = -1;
+		m_Usage = 0;
+		m_InUsage = 0;
+	}
+	
+
+	void RemoveFirstTimeout()
+	{
+		int NextTimed = m_aIDs[m_FirstTimed].m_Next;
+		
+		/* add it to the free list */
+		m_aIDs[m_FirstTimed].m_Next = m_FirstFree;
+		m_aIDs[m_FirstTimed].m_State = 0;
+		m_FirstFree = m_FirstTimed;
+		
+		/* remove it from the timed list */
+		m_FirstTimed = NextTimed;
+		if(m_FirstTimed == -1)
+			m_LastTimed = -1;
+			
+		m_Usage--;
+	}
+
+	int NewID()
+	{
+		int64 now = time_get();
+		
+		/* process timed ids */
+		while(m_FirstTimed != -1 && m_aIDs[m_FirstTimed].m_Timeout < now)
+			RemoveFirstTimeout();
+		
+		int id = m_FirstFree;
+		dbg_assert(id != -1, "id error");
+		m_FirstFree = m_aIDs[m_FirstFree].m_Next;
+		m_aIDs[id].m_State = 1;
+		m_Usage++;
+		m_InUsage++;
+		return id;
+	}
+
+	void TimeoutIDs()
+	{
+		/* process timed ids */
+		while(m_FirstTimed != -1)
+			RemoveFirstTimeout();
+	}
+
+	void FreeID(int id)
+	{
+		dbg_assert(m_aIDs[id].m_State == 1, "id is not alloced");
+
+		m_InUsage--;
+		m_aIDs[id].m_State = 2;
+		m_aIDs[id].m_Timeout = time_get()+time_freq()*5;
+		m_aIDs[id].m_Next = -1;
+		
+		if(m_LastTimed != -1)
+		{
+			m_aIDs[m_LastTimed].m_Next = id;
+			m_LastTimed = id;
+		}
+		else
+		{
+			m_FirstTimed = id;
+			m_LastTimed = id;
+		}
+	}
+};
+
+#if 0
+class IGameServer
+{
+public:
+	/*
+		Function: mods_console_init
+			TODO
+	*/
+	virtual void ConsoleInit();
+
+	/*
+		Function: Init
+			Called when the server is started.
+		
+		Remarks:
+			It's called after the map is loaded so all map items are available.
+	*/
+	void Init() = 0;
+
+	/*
+		Function: Shutdown
+			Called when the server quits.
+		
+		Remarks:
+			Should be used to clean up all resources used.
+	*/
+	void Shutdown() = 0;
+
+	/*
+		Function: ClientEnter
+			Called when a client has joined the game.
+			
+		Arguments:
+			cid - Client ID. Is 0 - MAX_CLIENTS.
+		
+		Remarks:
+			It's called when the client is finished loading and should enter gameplay.
+	*/
+	void ClientEnter(int cid) = 0;
+
+	/*
+		Function: ClientDrop
+			Called when a client drops from the server.
+
+		Arguments:
+			cid - Client ID. Is 0 - MAX_CLIENTS
+	*/
+	void ClientDrop(int cid) = 0;
+
+	/*
+		Function: ClientDirectInput
+			Called when the server recives new input from a client.
+
+		Arguments:
+			cid - Client ID. Is 0 - MAX_CLIENTS.
+			input - Pointer to the input data.
+			size - Size of the data. (NOT IMPLEMENTED YET)
+	*/
+	void ClientDirectInput(int cid, void *input) = 0;
+
+	/*
+		Function: ClientPredictedInput
+			Called when the server applys the predicted input on the client.
+
+		Arguments:
+			cid - Client ID. Is 0 - MAX_CLIENTS.
+			input - Pointer to the input data.
+			size - Size of the data. (NOT IMPLEMENTED YET)
+	*/
+	void ClientPredictedInput(int cid, void *input) = 0;
+
+
+	/*
+		Function: Tick
+			Called with a regular interval to progress the gameplay.
+			
+		Remarks:
+			The SERVER_TICK_SPEED tells the number of ticks per second.
+	*/
+	void Tick() = 0;
+
+	/*
+		Function: Presnap
+			Called before the server starts to construct snapshots for the clients.
+	*/
+	void Presnap() = 0;
+
+	/*
+		Function: Snap
+			Called to create the snapshot for a client.
+		
+		Arguments:
+			cid - Client ID. Is 0 - MAX_CLIENTS.
+		
+		Remarks:
+			The game should make a series of calls to <snap_new_item> to construct
+			the snapshot for the client.
+	*/
+	void Snap(int cid) = 0;
+
+	/*
+		Function: PostSnap
+			Called after the server is done sending the snapshots.
+	*/
+	void PostSnap() = 0;
+
+	/*
+		Function: ClientConnected
+			TODO
+		
+		Arguments:
+			arg1 - desc
+		
+		Returns:
+
+		See Also:
+			<other_func>
+	*/
+	void ClientConnected(int client_id) = 0;
+
+
+	/*
+		Function: NetVersion
+			TODO
+		
+		Arguments:
+			arg1 - desc
+		
+		Returns:
+
+		See Also:
+			<other_func>
+	*/
+	const char *NetVersion() = 0;
+
+	/*
+		Function: Version
+			TODO
+		
+		Arguments:
+			arg1 - desc
+		
+		Returns:
+
+		See Also:
+			<other_func>
+	*/
+	const char *Version() = 0;
+
+	/*
+		Function: Message
+			TODO
+		
+		Arguments:
+			arg1 - desc
+		
+		Returns:
+
+		See Also:
+			<other_func>
+	*/
+	void Message(int msg, int client_id) = 0;
+};
+
+#endif
+/*
+class IServer
+{
+public:
+	BanAdd
+	BanRemove
+	TickSpeed
+	Tick
+	Kick
+	SetBrowseInfo
+	SetClientScore
+	SetClientName
+	GetClientInfo
+	LatestInput
+	ClientName
+	
+	SendMessage()
+	
+	Map
+	
+	virtual int NewSnapID() = 0;
+	virtual int FreeSnapID(int i) = 0;
+};*/
+
+class CServer
+{
+public:
+	/* */
+	class CClient
+	{
+	public:
+	
+		enum
+		{
+			STATE_EMPTY = 0,
+			STATE_AUTH,
+			STATE_CONNECTING,
+			STATE_READY,
+			STATE_INGAME,
+			
+			SNAPRATE_INIT=0,
+			SNAPRATE_FULL,
+			SNAPRATE_RECOVER
+		};
+	
+		class CInput
+		{
+		public:
+			int m_aData[MAX_INPUT_SIZE];
+			int m_GameTick; /* the tick that was chosen for the input */
+		};
+	
+		/* connection state info */
+		int m_State;
+		int m_Latency;
+		int m_SnapRate;
+		
+		int m_LastAckedSnapshot;
+		int m_LastInputTick;
+		CSnapshotStorage m_Snapshots;
+		
+		CInput m_LatestInput;
+		CInput m_aInputs[200]; /* TODO: handle input better */
+		int m_CurrentInput;
+		
+		char m_aName[MAX_NAME_LENGTH];
+		char m_aClan[MAX_CLANNAME_LENGTH];
+		int m_Score;
+		int m_Authed;
+		
+		void Reset()
+		{
+			/* reset input */
+			for(int i = 0; i < 200; i++)
+				m_aInputs[i].m_GameTick = -1;
+			m_CurrentInput = 0;
+			mem_zero(&m_LatestInput, sizeof(m_LatestInput));
+
+			m_Snapshots.PurgeAll();
+			m_LastAckedSnapshot = -1;
+			m_LastInputTick = -1;
+			m_SnapRate = CClient::SNAPRATE_INIT;
+			m_Score = 0;
+		}
+	};
+	
+	CClient m_aClients[MAX_CLIENTS];
+
+	CSnapshotBuilder m_SnapshotBuilder;
+	CSnapIDPool m_IDPool;
+	CNetServer m_NetServer;
+
+	int64 m_GameStartTime;
+	int m_CurrentTick;
+	int m_RunServer;
+
+	char m_aBrowseinfoGametype[16];
+	int m_BrowseinfoProgression;
+
+	int64 m_Lastheartbeat;
+	/*static NETADDR4 master_server;*/
+
+	char m_aCurrentMap[64];
+	int m_CurrentMapCrc;
+	unsigned char *m_pCurrentMapData;
+	int m_CurrentMapSize;	
+	
+	CServer()
+	{
+		m_CurrentTick = 0;
+		m_RunServer = 1;
+
+		mem_zero(m_aBrowseinfoGametype, sizeof(m_aBrowseinfoGametype));
+		m_BrowseinfoProgression = -1;
+
+		m_pCurrentMapData = 0;
+		m_CurrentMapSize = 0;	
+	}
+	
+
+	int TrySetClientName(int ClientID, const char *pName)
+	{
+		int i;
+		char aTrimmedName[64];
+
+		/* trim the name */
+		str_copy(aTrimmedName, str_ltrim(pName), sizeof(aTrimmedName));
+		str_rtrim(aTrimmedName);
+		dbg_msg("", "'%s' -> '%s'", pName, aTrimmedName);
+		pName = aTrimmedName;
+		
+		
+		/* check for empty names */
+		if(!pName[0])
+			return -1;
+		
+		/* make sure that two clients doesn't have the same name */
+		for(i = 0; i < MAX_CLIENTS; i++)
+			if(i != ClientID && m_aClients[i].m_State >= CClient::STATE_READY)
+			{
+				if(strcmp(pName, m_aClients[i].m_aName) == 0)
+					return -1;
+			}
+
+		/* set the client name */
+		str_copy(m_aClients[ClientID].m_aName, pName, MAX_NAME_LENGTH);
+		return 0;
+	}
+
+
+
+	void SetClientName(int ClientID, const char *pName)
+	{
+		if(ClientID < 0 || ClientID > MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY)
+			return;
+			
+		if(!pName)
+			return;
+			
+		char aNameTry[MAX_NAME_LENGTH];
+		str_copy(aNameTry, pName, MAX_NAME_LENGTH);
+		if(TrySetClientName(ClientID, aNameTry))
+		{
+			/* auto rename */
+			for(int i = 1;; i++)
+			{
+				str_format(aNameTry, MAX_NAME_LENGTH, "(%d)%s", i, pName);
+				if(TrySetClientName(ClientID, aNameTry) == 0)
+					break;
+			}
+		}
+	}
+
+	void SetClientScore(int ClientID, int Score)
+	{
+		if(ClientID < 0 || ClientID > MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY)
+			return;
+		m_aClients[ClientID].m_Score = Score;
+	}
+
+	void SetBrowseInfo(const char *pGameType, int Progression)
+	{
+		str_copy(m_aBrowseinfoGametype, pGameType, sizeof(m_aBrowseinfoGametype));
+		m_BrowseinfoProgression = Progression;
+		if(m_BrowseinfoProgression > 100)
+			m_BrowseinfoProgression = 100;
+		if(m_BrowseinfoProgression < -1)
+			m_BrowseinfoProgression = -1;
+	}
+
+	void Kick(int ClientID, const char *pReason)
+	{
+		if(ClientID < 0 || ClientID > MAX_CLIENTS)
+			return;
+			
+		if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY)
+			m_NetServer.Drop(ClientID, pReason);
+	}
+
+	int Tick()
+	{
+		return m_CurrentTick;
+	}
+
+	int64 TickStartTime(int Tick)
+	{
+		return m_GameStartTime + (time_freq()*Tick)/SERVER_TICK_SPEED;
+	}
+
+	int TickSpeed()
+	{
+		return SERVER_TICK_SPEED;
+	}
+
+	int Init()
+	{
+		int i;
+		for(i = 0; i < MAX_CLIENTS; i++)
+		{
+			m_aClients[i].m_State = CClient::STATE_EMPTY;
+			m_aClients[i].m_aName[0] = 0;
+			m_aClients[i].m_aClan[0] = 0;
+			m_aClients[i].m_Snapshots.Init();
+		}
+
+		m_CurrentTick = 0;
+
+		return 0;
+	}
+
+	int GetClientInfo(int ClientID, CLIENT_INFO *pInfo)
+	{
+		dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid");
+		dbg_assert(pInfo != 0, "info can not be null");
+
+		if(m_aClients[ClientID].m_State == CClient::STATE_INGAME)
+		{
+			pInfo->name = m_aClients[ClientID].m_aName;
+			pInfo->latency = m_aClients[ClientID].m_Latency;
+			return 1;
+		}
+		return 0;
+	}
+
+	int SendMsg(int ClientID)
+	{
+		const MSG_INFO *pInfo = msg_get_info();
+		CNetChunk Packet;
+		if(!pInfo)
+			return -1;
+			
+		mem_zero(&Packet, sizeof(CNetChunk));
+		
+		Packet.m_ClientID = ClientID;
+		Packet.m_pData = pInfo->data;
+		Packet.m_DataSize = pInfo->size;
+
+		if(pInfo->flags&MSGFLAG_VITAL)
+			Packet.m_Flags |= NETSENDFLAG_VITAL;
+		if(pInfo->flags&MSGFLAG_FLUSH)
+			Packet.m_Flags |= NETSENDFLAG_FLUSH;
+		
+		/* write message to demo recorder */
+		if(!(pInfo->flags&MSGFLAG_NORECORD))
+			demorec_record_message(pInfo->data, pInfo->size);
+
+		if(!(pInfo->flags&MSGFLAG_NOSEND))
+		{
+			if(ClientID == -1)
+			{
+				/* broadcast */
+				int i;
+				for(i = 0; i < MAX_CLIENTS; i++)
+					if(m_aClients[i].m_State == CClient::STATE_INGAME)
+					{
+						Packet.m_ClientID = i;
+						m_NetServer.Send(&Packet);
+					}
+			}
+			else
+				m_NetServer.Send(&Packet);
+		}
+		return 0;
+	}
+
+	void DoSnapshot()
+	{
+		int i;
+		
+		{
+			static PERFORMACE_INFO scope = {"presnap", 0};
+			perf_start(&scope);
+			mods_presnap();
+			perf_end();
+		}
+		
+		/* create snapshot for demo recording */
+		if(demorec_isrecording())
+		{
+			char data[CSnapshot::MAX_SIZE];
+			int snapshot_size;
+
+			/* build snap and possibly add some messages */
+			m_SnapshotBuilder.Init();
+			mods_snap(-1);
+			snapshot_size = m_SnapshotBuilder.Finish(data);
+			
+			/* write snapshot */
+			demorec_record_snapshot(Tick(), data, snapshot_size);
+		}
+
+		/* create snapshots for all clients */
+		for(i = 0; i < MAX_CLIENTS; i++)
+		{
+			/* client must be ingame to recive snapshots */
+			if(m_aClients[i].m_State != CClient::STATE_INGAME)
+				continue;
+				
+			/* this client is trying to recover, don't spam snapshots */
+			if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_RECOVER && (Tick()%50) != 0)
+				continue;
+				
+			/* this client is trying to recover, don't spam snapshots */
+			if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_INIT && (Tick()%10) != 0)
+				continue;
+				
+			{
+				char data[CSnapshot::MAX_SIZE];
+				char deltadata[CSnapshot::MAX_SIZE];
+				char compdata[CSnapshot::MAX_SIZE];
+				int snapshot_size;
+				int crc;
+				static CSnapshot emptysnap;
+				CSnapshot *deltashot = &emptysnap;
+				int deltashot_size;
+				int delta_tick = -1;
+				int deltasize;
+				static PERFORMACE_INFO scope = {"build", 0};
+				perf_start(&scope);
+
+				m_SnapshotBuilder.Init();
+
+				{
+					static PERFORMACE_INFO scope = {"modsnap", 0};
+					perf_start(&scope);
+					mods_snap(i);
+					perf_end();
+				}
+
+				/* finish snapshot */
+				snapshot_size = m_SnapshotBuilder.Finish(data);
+				crc = ((CSnapshot*)data)->Crc();
+
+				/* remove old snapshos */
+				/* keep 3 seconds worth of snapshots */
+				m_aClients[i].m_Snapshots.PurgeUntil(m_CurrentTick-SERVER_TICK_SPEED*3);
+				
+				/* save it the snapshot */
+				m_aClients[i].m_Snapshots.Add(m_CurrentTick, time_get(), snapshot_size, data, 0);
+				
+				/* find snapshot that we can preform delta against */
+				emptysnap.m_DataSize = 0;
+				emptysnap.m_NumItems = 0;
+				
+				{
+					deltashot_size = m_aClients[i].m_Snapshots.Get(m_aClients[i].m_LastAckedSnapshot, 0, &deltashot, 0);
+					if(deltashot_size >= 0)
+						delta_tick = m_aClients[i].m_LastAckedSnapshot;
+					else
+					{
+						/* no acked package found, force client to recover rate */
+						if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_FULL)
+							m_aClients[i].m_SnapRate = CClient::SNAPRATE_RECOVER;
+					}
+				}
+				
+				/* create delta */
+				{
+					static PERFORMACE_INFO scope = {"delta", 0};
+					perf_start(&scope);
+					deltasize = CSnapshot::CreateDelta(deltashot, (CSnapshot*)data, deltadata);
+					perf_end();
+				}
+
+				
+				if(deltasize)
+				{
+					/* compress it */
+					int snapshot_size;
+					const int max_size = MAX_SNAPSHOT_PACKSIZE;
+					int numpackets;
+					int n, left;
+
+					{				
+						static PERFORMACE_INFO scope = {"compress", 0};
+						/*char buffer[512];*/
+						perf_start(&scope);
+						snapshot_size = intpack_compress(deltadata, deltasize, compdata);
+						
+						/*str_hex(buffer, sizeof(buffer), compdata, snapshot_size);
+						dbg_msg("", "deltasize=%d -> %d : %s", deltasize, snapshot_size, buffer);*/
+						
+						perf_end();
+					}
+
+					numpackets = (snapshot_size+max_size-1)/max_size;
+					
+					for(n = 0, left = snapshot_size; left; n++)
+					{
+						int chunk = left < max_size ? left : max_size;
+						left -= chunk;
+
+						if(numpackets == 1)
+							msg_pack_start_system(NETMSG_SNAPSINGLE, MSGFLAG_FLUSH);
+						else
+							msg_pack_start_system(NETMSG_SNAP, MSGFLAG_FLUSH);
+							
+						msg_pack_int(m_CurrentTick);
+						msg_pack_int(m_CurrentTick-delta_tick); /* compressed with */
+						
+						if(numpackets != 1)
+						{
+							msg_pack_int(numpackets);
+							msg_pack_int(n);
+						}
+						
+						msg_pack_int(crc);
+						msg_pack_int(chunk);
+						msg_pack_raw(&compdata[n*max_size], chunk);
+						msg_pack_end();
+						SendMsg(i);
+					}
+				}
+				else
+				{
+					msg_pack_start_system(NETMSG_SNAPEMPTY, MSGFLAG_FLUSH);
+					msg_pack_int(m_CurrentTick);
+					msg_pack_int(m_CurrentTick-delta_tick); /* compressed with */
+					msg_pack_end();
+					SendMsg(i);
+				}
+				
+				perf_end();
+			}
+		}
+
+		mods_postsnap();
+	}
+
+
+	static int NewClientCallback(int cid, void *pUser)
+	{
+		CServer *pThis = (CServer *)pUser;
+		pThis->m_aClients[cid].m_State = CClient::STATE_AUTH;
+		pThis->m_aClients[cid].m_aName[0] = 0;
+		pThis->m_aClients[cid].m_aClan[0] = 0;
+		pThis->m_aClients[cid].m_Authed = 0;
+		pThis->m_aClients[cid].Reset();
+		return 0;
+	}
+
+	static int DelClientCallback(int cid, void *pUser)
+	{
+		CServer *pThis = (CServer *)pUser;
+		
+		/* notify the mod about the drop */
+		if(pThis->m_aClients[cid].m_State >= CClient::STATE_READY)
+			mods_client_drop(cid);
+		
+		pThis->m_aClients[cid].m_State = CClient::STATE_EMPTY;
+		pThis->m_aClients[cid].m_aName[0] = 0;
+		pThis->m_aClients[cid].m_aClan[0] = 0;
+		pThis->m_aClients[cid].m_Authed = 0;
+		pThis->m_aClients[cid].m_Snapshots.PurgeAll();
+		return 0;
+	}
+
+	void SendMap(int cid)
+	{
+		msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH);
+		msg_pack_string(config.sv_map, 0);
+		msg_pack_int(m_CurrentMapCrc);
+		msg_pack_end();
+		server_send_msg(cid);
+	}
+
+	void SendRconLine(int cid, const char *pLine)
+	{
+		msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL);
+		msg_pack_string(pLine, 512);
+		msg_pack_end();
+		server_send_msg(cid);
+	}
+
+	static void SendRconLineAuthed(const char *pLine, void *pUser)
+	{
+		CServer *pThis = (CServer *)pUser;
+		static volatile int reentry_guard = 0;
+		int i;
+		
+		if(reentry_guard) return;
+		reentry_guard++;
+		
+		for(i = 0; i < MAX_CLIENTS; i++)
+		{
+			if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed)
+				pThis->SendRconLine(i, pLine);
+		}
+		
+		reentry_guard--;
+	}
+	
+	void ProcessClientPacket(CNetChunk *pPacket)
+	{
+		int cid = pPacket->m_ClientID;
+		NETADDR addr;
+		
+		int sys;
+		int msg = msg_unpack_start(pPacket->m_pData, pPacket->m_DataSize, &sys);
+		
+		if(m_aClients[cid].m_State == CClient::STATE_AUTH)
+		{
+			if(sys && msg == NETMSG_INFO)
+			{
+				char version[64];
+				const char *password;
+				str_copy(version, msg_unpack_string(), 64);
+				if(strcmp(version, mods_net_version()) != 0)
+				{
+					/* OH FUCK! wrong version, drop him */
+					char reason[256];
+					str_format(reason, sizeof(reason), "wrong version. server is running '%s' and client '%s'.", mods_net_version(), version);
+					m_NetServer.Drop(cid, reason);
+					return;
+				}
+				
+				str_copy(m_aClients[cid].m_aName, msg_unpack_string(), MAX_NAME_LENGTH);
+				str_copy(m_aClients[cid].m_aClan, msg_unpack_string(), MAX_CLANNAME_LENGTH);
+				password = msg_unpack_string();
+				
+				if(config.password[0] != 0 && strcmp(config.password, password) != 0)
+				{
+					/* wrong password */
+					m_NetServer.Drop(cid, "wrong password");
+					return;
+				}
+				
+				m_aClients[cid].m_State = CClient::STATE_CONNECTING;
+				SendMap(cid);
+			}
+		}
+		else
+		{
+			if(sys)
+			{
+				/* system message */
+				if(msg == NETMSG_REQUEST_MAP_DATA)
+				{
+					int chunk = msg_unpack_int();
+					int chunk_size = 1024-128;
+					int offset = chunk * chunk_size;
+					int last = 0;
+					
+					/* drop faulty map data requests */
+					if(chunk < 0 || offset > m_CurrentMapSize)
+						return;
+					
+					if(offset+chunk_size >= m_CurrentMapSize)
+					{
+						chunk_size = m_CurrentMapSize-offset;
+						if(chunk_size < 0)
+							chunk_size = 0;
+						last = 1;
+					}
+					
+					msg_pack_start_system(NETMSG_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH);
+					msg_pack_int(last);
+					msg_pack_int(m_CurrentMapSize);
+					msg_pack_int(chunk_size);
+					msg_pack_raw(&m_pCurrentMapData[offset], chunk_size);
+					msg_pack_end();
+					server_send_msg(cid);
+					
+					if(config.debug)
+						dbg_msg("server", "sending chunk %d with size %d", chunk, chunk_size);
+				}
+				else if(msg == NETMSG_READY)
+				{
+					if(m_aClients[cid].m_State == CClient::STATE_CONNECTING)
+					{
+						addr = m_NetServer.ClientAddr(cid);
+						
+						dbg_msg("server", "player is ready. cid=%x ip=%d.%d.%d.%d",
+							cid,
+							addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3]
+							);
+						m_aClients[cid].m_State = CClient::STATE_READY;
+						mods_connected(cid);
+					}
+				}
+				else if(msg == NETMSG_ENTERGAME)
+				{
+					if(m_aClients[cid].m_State == CClient::STATE_READY)
+					{
+						addr = m_NetServer.ClientAddr(cid);
+						
+						dbg_msg("server", "player has entered the game. cid=%x ip=%d.%d.%d.%d",
+							cid,
+							addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3]
+							);
+						m_aClients[cid].m_State = CClient::STATE_INGAME;
+						mods_client_enter(cid);
+					}
+				}
+				else if(msg == NETMSG_INPUT)
+				{
+					int tick, size, i;
+					CClient::CInput *pInput;
+					int64 tagtime;
+					
+					m_aClients[cid].m_LastAckedSnapshot = msg_unpack_int();
+					tick = msg_unpack_int();
+					size = msg_unpack_int();
+					
+					/* check for errors */
+					if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE)
+						return;
+
+					if(m_aClients[cid].m_LastAckedSnapshot > 0)
+						m_aClients[cid].m_SnapRate = CClient::SNAPRATE_FULL;
+						
+					if(m_aClients[cid].m_Snapshots.Get(m_aClients[cid].m_LastAckedSnapshot, &tagtime, 0, 0) >= 0)
+						m_aClients[cid].m_Latency = (int)(((time_get()-tagtime)*1000)/time_freq());
+
+					/* add message to report the input timing */
+					/* skip packets that are old */
+					if(tick > m_aClients[cid].m_LastInputTick)
+					{
+						int time_left = ((TickStartTime(tick)-time_get())*1000) / time_freq();
+						msg_pack_start_system(NETMSG_INPUTTIMING, 0);
+						msg_pack_int(tick);
+						msg_pack_int(time_left);
+						msg_pack_end();
+						server_send_msg(cid);
+					}
+
+					m_aClients[cid].m_LastInputTick = tick;
+
+					pInput = &m_aClients[cid].m_aInputs[m_aClients[cid].m_CurrentInput];
+					
+					if(tick <= server_tick())
+						tick = server_tick()+1;
+
+					pInput->m_GameTick = tick;
+					
+					for(i = 0; i < size/4; i++)
+						pInput->m_aData[i] = msg_unpack_int();
+					
+					mem_copy(m_aClients[cid].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int));
+					
+					m_aClients[cid].m_CurrentInput++;
+					m_aClients[cid].m_CurrentInput %= 200;
+				
+					/* call the mod with the fresh input data */
+					if(m_aClients[cid].m_State == CClient::STATE_INGAME)
+						mods_client_direct_input(cid, m_aClients[cid].m_LatestInput.m_aData);
+				}
+				else if(msg == NETMSG_RCON_CMD)
+				{
+					const char *cmd = msg_unpack_string();
+					
+					if(msg_unpack_error() == 0 && m_aClients[cid].m_Authed)
+					{
+						dbg_msg("server", "cid=%d rcon='%s'", cid, cmd);
+						console_execute_line(cmd);
+					}
+				}
+				else if(msg == NETMSG_RCON_AUTH)
+				{
+					const char *pw;
+					msg_unpack_string(); /* login name, not used */
+					pw = msg_unpack_string();
+					
+					if(msg_unpack_error() == 0)
+					{
+						if(config.sv_rcon_password[0] == 0)
+						{
+							SendRconLine(cid, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
+						}
+						else if(strcmp(pw, config.sv_rcon_password) == 0)
+						{
+							msg_pack_start_system(NETMSG_RCON_AUTH_STATUS, MSGFLAG_VITAL);
+							msg_pack_int(1);
+							msg_pack_end();
+							server_send_msg(cid);
+							
+							m_aClients[cid].m_Authed = 1;
+							SendRconLine(cid, "Authentication successful. Remote console access granted.");
+							dbg_msg("server", "cid=%d authed", cid);
+						}
+						else
+						{
+							SendRconLine(cid, "Wrong password.");
+						}
+					}
+				}
+				else if(msg == NETMSG_PING)
+				{
+					msg_pack_start_system(NETMSG_PING_REPLY, 0);
+					msg_pack_end();
+					server_send_msg(cid);
+				}
+				else
+				{
+					char hex[] = "0123456789ABCDEF";
+					char buf[512];
+					int b;
+
+					for(b = 0; b < pPacket->m_DataSize && b < 32; b++)
+					{
+						buf[b*3] = hex[((const unsigned char *)pPacket->m_pData)[b]>>4];
+						buf[b*3+1] = hex[((const unsigned char *)pPacket->m_pData)[b]&0xf];
+						buf[b*3+2] = ' ';
+						buf[b*3+3] = 0;
+					}
+
+					dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, pPacket->m_DataSize);
+					dbg_msg("server", "%s", buf);
+					
+				}
+			}
+			else
+			{
+				/* game message */
+				if(m_aClients[cid].m_State >= CClient::STATE_READY)
+					mods_message(msg, cid);
+			}
+		}
+	}
+		
+	void SendServerInfo(NETADDR *addr, int token)
+	{
+		CNetChunk Packet;
+		CPacker p;
+		char buf[128];
+
+		/* count the players */	
+		int player_count = 0;
+		int i;
+		for(i = 0; i < MAX_CLIENTS; i++)
+		{
+			if(m_aClients[i].m_State != CClient::STATE_EMPTY)
+				player_count++;
+		}
+		
+		p.Reset();
+
+		if(token >= 0)
+		{
+			/* new token based format */
+			p.AddRaw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO));
+			str_format(buf, sizeof(buf), "%d", token);
+			p.AddString(buf, 6);
+		}
+		else
+		{
+			/* old format */
+			p.AddRaw(SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO));
+		}
+		
+		p.AddString(mods_version(), 32);
+		p.AddString(config.sv_name, 64);
+		p.AddString(config.sv_map, 32);
+
+		/* gametype */
+		p.AddString(m_aBrowseinfoGametype, 16);
+
+		/* flags */
+		i = 0;
+		if(config.password[0])   /* password set */
+			i |= SRVFLAG_PASSWORD;
+		str_format(buf, sizeof(buf), "%d", i);
+		p.AddString(buf, 2);
+
+		/* progression */
+		str_format(buf, sizeof(buf), "%d", m_BrowseinfoProgression);
+		p.AddString(buf, 4);
+		
+		str_format(buf, sizeof(buf), "%d", player_count); p.AddString(buf, 3);  /* num players */
+		str_format(buf, sizeof(buf), "%d", m_NetServer.MaxClients()); p.AddString(buf, 3); /* max players */
+
+		for(i = 0; i < MAX_CLIENTS; i++)
+		{
+			if(m_aClients[i].m_State != CClient::STATE_EMPTY)
+			{
+				p.AddString(m_aClients[i].m_aName, 48);  /* player name */
+				str_format(buf, sizeof(buf), "%d", m_aClients[i].m_Score); p.AddString(buf, 6);  /* player score */
+			}
+		}
+		
+		
+		Packet.m_ClientID = -1;
+		Packet.m_Address = *addr;
+		Packet.m_Flags = NETSENDFLAG_CONNLESS;
+		Packet.m_DataSize = p.Size();
+		Packet.m_pData = p.Data();
+		m_NetServer.Send(&Packet);
+	}
+
+	int BanAdd(NETADDR Addr, int Seconds)
+	{
+		return m_NetServer.BanAdd(Addr, Seconds);	
+	}
+
+	int BanRemove(NETADDR Addr)
+	{
+		return m_NetServer.BanRemove(Addr);
+	}
+		
+
+	void PumpNetwork()
+	{
+		CNetChunk Packet;
+
+		m_NetServer.Update();
+		
+		/* process packets */
+		while(m_NetServer.Recv(&Packet))
+		{
+			if(Packet.m_ClientID == -1)
+			{
+				/* stateless */
+				if(!register_process_packet(&Packet))
+				{
+					if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 &&
+						memcmp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
+					{
+						SendServerInfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]);
+					}
+					
+					
+					if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) &&
+						memcmp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0)
+					{
+						SendServerInfo(&Packet.m_Address, -1);
+					}
+				}
+			}
+			else
+				ProcessClientPacket(&Packet);
+		}
+	}
+
+	int LoadMap(const char *pMapName)
+	{
+		DATAFILE *df;
+		char buf[512];
+		str_format(buf, sizeof(buf), "maps/%s.map", pMapName);
+		df = datafile_load(buf);
+		if(!df)
+			return 0;
+		
+		/* stop recording when we change map */
+		demorec_record_stop();
+		
+		/* reinit snapshot ids */
+		m_IDPool.TimeoutIDs();
+		
+		/* get the crc of the map */
+		m_CurrentMapCrc = datafile_crc(buf);
+		dbg_msg("server", "%s crc is %08x", buf, m_CurrentMapCrc);
+			
+		str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap));
+		map_set(df);
+		
+		/* load compelate map into memory for download */
+		{
+			IOHANDLE file = engine_openfile(buf, IOFLAG_READ);
+			m_CurrentMapSize = (int)io_length(file);
+			if(m_pCurrentMapData)
+				mem_free(m_pCurrentMapData);
+			m_pCurrentMapData = (unsigned char *)mem_alloc(m_CurrentMapSize, 1);
+			io_read(file, m_pCurrentMapData, m_CurrentMapSize);
+			io_close(file);
+		}
+		return 1;
+	}
+
+	int Run()
+	{
+		NETADDR bindaddr;
+
+		//snap_init_id();
+		net_init();
+		
+		/* */
+		console_register_print_callback(SendRconLineAuthed, this);
+
+		/* load map */
+		if(!LoadMap(config.sv_map))
+		{
+			dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
+			return -1;
+		}
+		
+		/* start server */
+		/* TODO: IPv6 support */
+		if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0)
+		{
+			/* sweet! */
+			bindaddr.port = config.sv_port;
+		}
+		else
+		{
+			mem_zero(&bindaddr, sizeof(bindaddr));
+			bindaddr.port = config.sv_port;
+		}
+		
+		
+		if(!m_NetServer.Open(bindaddr, config.sv_max_clients, 0))
+		{
+			dbg_msg("server", "couldn't open socket. port might already be in use");
+			return -1;
+		}
+
+		m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this);
+		
+		dbg_msg("server", "server name is '%s'", config.sv_name);
+		
+		mods_init();
+		dbg_msg("server", "version %s", mods_net_version());
+
+		/* start game */
+		{
+			int64 reporttime = time_get();
+			int reportinterval = 3;
+		
+			m_Lastheartbeat = 0;
+			m_GameStartTime = time_get();
+		
+			if(config.debug)
+				dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024);
+
+			while(m_RunServer)
+			{
+				static PERFORMACE_INFO rootscope = {"root", 0};
+				int64 t = time_get();
+				int new_ticks = 0;
+				
+				perf_start(&rootscope);
+				
+				/* load new map TODO: don't poll this */
+				if(strcmp(config.sv_map, m_aCurrentMap) != 0 || config.sv_map_reload)
+				{
+					config.sv_map_reload = 0;
+					
+					/* load map */
+					if(LoadMap(config.sv_map))
+					{
+						int c;
+						
+						/* new map loaded */
+						mods_shutdown();
+						
+						for(c = 0; c < MAX_CLIENTS; c++)
+						{
+							if(m_aClients[c].m_State == CClient::STATE_EMPTY)
+								continue;
+							
+							SendMap(c);
+							m_aClients[c].Reset();
+							m_aClients[c].m_State = CClient::STATE_CONNECTING;
+						}
+						
+						m_GameStartTime = time_get();
+						m_CurrentTick = 0;
+						mods_init();
+					}
+					else
+					{
+						dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
+						config_set_sv_map(&config, m_aCurrentMap);
+					}
+				}
+				
+				while(t > TickStartTime(m_CurrentTick+1))
+				{
+					m_CurrentTick++;
+					new_ticks++;
+					
+					/* apply new input */
+					{
+						static PERFORMACE_INFO scope = {"input", 0};
+						int c, i;
+						
+						perf_start(&scope);
+						
+						for(c = 0; c < MAX_CLIENTS; c++)
+						{
+							if(m_aClients[c].m_State == CClient::STATE_EMPTY)
+								continue;
+							for(i = 0; i < 200; i++)
+							{
+								if(m_aClients[c].m_aInputs[i].m_GameTick == server_tick())
+								{
+									if(m_aClients[c].m_State == CClient::STATE_INGAME)
+										mods_client_predicted_input(c, m_aClients[c].m_aInputs[i].m_aData);
+									break;
+								}
+							}
+						}
+						
+						perf_end();
+					}
+					
+					/* progress game */
+					{
+						static PERFORMACE_INFO scope = {"tick", 0};
+						perf_start(&scope);
+						mods_tick();
+						perf_end();
+					}
+				}
+				
+				/* snap game */
+				if(new_ticks)
+				{
+					if(config.sv_high_bandwidth || (m_CurrentTick%2) == 0)
+					{
+						static PERFORMACE_INFO scope = {"snap", 0};
+						perf_start(&scope);
+						DoSnapshot();
+						perf_end();
+					}
+				}
+				
+				/* master server stuff */
+				register_update();
+		
+
+				{
+					static PERFORMACE_INFO scope = {"net", 0};
+					perf_start(&scope);
+					PumpNetwork();
+					perf_end();
+				}
+
+				perf_end();
+		
+				if(reporttime < time_get())
+				{
+					if(config.debug)
+					{
+						/*
+						static NETSTATS prev_stats;
+						NETSTATS stats;
+						netserver_stats(net, &stats);
+						
+						perf_next();
+						
+						if(config.dbg_pref)
+							perf_dump(&rootscope);
+
+						dbg_msg("server", "send=%8d recv=%8d",
+							(stats.send_bytes - prev_stats.send_bytes)/reportinterval,
+							(stats.recv_bytes - prev_stats.recv_bytes)/reportinterval);
+							
+						prev_stats = stats;
+						*/
+					}
+		
+					reporttime += time_freq()*reportinterval;
+				}
+				
+				/* wait for incomming data */
+				net_socket_read_wait(m_NetServer.Socket(), 5);
+			}
+		}
+
+		mods_shutdown();
+		map_unload();
+
+		if(m_pCurrentMapData)
+			mem_free(m_pCurrentMapData);
+		return 0;
+	}
+
+	static void ConKick(void *pResult, void *pUser)
+	{
+		((CServer *)pUser)->Kick(console_arg_int(pResult, 0), "kicked by console");
+	}
+
+	static int str_allnum(const char *str)
+	{
+		while(*str)
+		{
+			if(!(*str >= '0' && *str <= '9'))
+				return 0;
+			str++;
+		}
+		return 1;
+	}
+
+	static void ConBan(void *pResult, void *pUser)
+	{
+		NETADDR addr;
+		char addrstr[128];
+		const char *str = console_arg_string(pResult, 0);
+		int minutes = 30;
+		
+		if(console_arg_num(pResult) > 1)
+			minutes = console_arg_int(pResult, 1);
+		
+		if(net_addr_from_str(&addr, str) == 0)
+			((CServer *)pUser)->BanAdd(addr, minutes*60);
+		else if(str_allnum(str))
+		{
+			NETADDR addr;
+			int cid = atoi(str);
+
+			if(cid < 0 || cid > MAX_CLIENTS || ((CServer *)pUser)->m_aClients[cid].m_State == CClient::STATE_EMPTY)
+			{
+				dbg_msg("server", "invalid client id");
+				return;
+			}
+
+			addr = ((CServer *)pUser)->m_NetServer.ClientAddr(cid);
+			((CServer *)pUser)->BanAdd(addr, minutes*60);
+		}
+		
+		addr.port = 0;
+		net_addr_str(&addr, addrstr, sizeof(addrstr));
+		
+		if(minutes)
+			dbg_msg("server", "banned %s for %d minutes", addrstr, minutes);
+		else
+			dbg_msg("server", "banned %s for life", addrstr);
+	}
+
+	static void ConUnban(void *result, void *pUser)
+	{
+		NETADDR addr;
+		const char *str = console_arg_string(result, 0);
+		
+		if(net_addr_from_str(&addr, str) == 0)
+			((CServer *)pUser)->BanRemove(addr);
+		else
+			dbg_msg("server", "invalid network address");
+	}
+
+	static void ConBans(void *pResult, void *pUser)
+	{
+		unsigned now = time_timestamp();
+		
+		int num = ((CServer *)pUser)->m_NetServer.BanNum();
+		for(int i = 0; i < num; i++)
+		{
+			CNetServer::CBanInfo Info;
+			((CServer *)pUser)->m_NetServer.BanGet(i, &Info);
+			NETADDR Addr = Info.m_Addr;
+			
+			if(Info.m_Expires == -1)
+			{
+				dbg_msg("server", "#%d %d.%d.%d.%d for life", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
+			}
+			else
+			{
+				unsigned t = Info.m_Expires - now;
+				dbg_msg("server", "#%d %d.%d.%d.%d for %d minutes and %d seconds", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], t/60, t%60);
+			}
+		}
+		dbg_msg("server", "%d ban(s)", num);
+	}
+
+	static void ConStatus(void *pResult, void *pUser)
+	{
+		int i;
+		NETADDR addr;
+		for(i = 0; i < MAX_CLIENTS; i++)
+		{
+			if(((CServer *)pUser)->m_aClients[i].m_State == CClient::STATE_INGAME)
+			{
+				addr = ((CServer *)pUser)->m_NetServer.ClientAddr(i);
+				dbg_msg("server", "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%d",
+					i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port,
+					((CServer *)pUser)->m_aClients[i].m_aName, ((CServer *)pUser)->m_aClients[i].m_Score);
+			}
+		}
+	}
+
+	static void ConShutdown(void *pResult, void *pUser)
+	{
+		((CServer *)pUser)->m_RunServer = 0;
+	}
+
+	static void ConRecord(void *pResult, void *pUser)
+	{
+		char filename[512];
+		str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(pResult, 0));
+		demorec_record_start(filename, mods_net_version(), ((CServer *)pUser)->m_aCurrentMap, ((CServer *)pUser)->m_CurrentMapCrc, "server");
+	}
+
+	static void ConStopRecord(void *pResult, void *pUser)
+	{
+		demorec_record_stop();
+	}
+	
+
+	void RegisterCommands()
+	{
+		MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, ConKick, this, "");
+		MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, ConBan, this, "");
+		MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, ConUnban, this, "");
+		MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, ConBans, this, "");
+		MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, ConStatus, this, "");
+		MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, "");
+
+		MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_SERVER, ConRecord, this, "");
+		MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, "");
+	}	
+	
+};
+
+
+// UGLY UGLY HACK for now
+CServer g_Server;
+CNetServer *m_pNetServer;
+
+int server_tick() { return g_Server.Tick(); }
+int server_tickspeed() { return g_Server.TickSpeed(); }
+int snap_new_id() { return g_Server.m_IDPool.NewID(); }
+void snap_free_id(int id) { return g_Server.m_IDPool.FreeID(id); }
+int server_send_msg(int client_id) { return g_Server.SendMsg(client_id); }
+void server_setbrowseinfo(const char *game_type, int progression) { g_Server.SetBrowseInfo(game_type, progression); }
+void server_setclientname(int client_id, const char *name) { g_Server.SetClientName(client_id, name); }
+void server_setclientscore(int client_id, int score) { g_Server.SetClientScore(client_id, score); }
+
+int server_getclientinfo(int client_id, CLIENT_INFO *info) { return g_Server.GetClientInfo(client_id, info); }
+
+void *snap_new_item(int type, int id, int size)
+{
+	dbg_assert(type >= 0 && type <=0xffff, "incorrect type");
+	dbg_assert(id >= 0 && id <=0xffff, "incorrect id");
+	return g_Server.m_SnapshotBuilder.NewItem(type, id, size);
+}
+
+int *server_latestinput(int client_id, int *size)
+{
+	if(client_id < 0 || client_id > MAX_CLIENTS || g_Server.m_aClients[client_id].m_State < CServer::CClient::STATE_READY)
+		return 0;
+	return g_Server.m_aClients[client_id].m_LatestInput.m_aData;
+}
+
+const char *server_clientname(int client_id)
+{
+	if(client_id < 0 || client_id > MAX_CLIENTS || g_Server.m_aClients[client_id].m_State < CServer::CClient::STATE_READY)
+		return "(invalid client)";
+	return g_Server.m_aClients[client_id].m_aName;
+}
+
+#if 0
+
+
+static void reset_client(int cid)
+{
+	/* reset input */
+	int i;
+	for(i = 0; i < 200; i++)
+		m_aClients[cid].m_aInputs[i].m_GameTick = -1;
+	m_aClients[cid].m_CurrentInput = 0;
+	mem_zero(&m_aClients[cid].m_Latestinput, sizeof(m_aClients[cid].m_Latestinput));
+
+	m_aClients[cid].m_Snapshots.PurgeAll();
+	m_aClients[cid].m_LastAckedSnapshot = -1;
+	m_aClients[cid].m_LastInputTick = -1;
+	m_aClients[cid].m_SnapRate = CClient::SNAPRATE_INIT;
+	m_aClients[cid].m_Score = 0;
+
+}
+
+
+static int new_client_callback(int cid, void *user)
+{
+	m_aClients[cid].state = CClient::STATE_AUTH;
+	m_aClients[cid].name[0] = 0;
+	m_aClients[cid].clan[0] = 0;
+	m_aClients[cid].authed = 0;
+	reset_client(cid);
+	return 0;
+}
+
+static int del_client_callback(int cid, void *user)
+{
+	/* notify the mod about the drop */
+	if(m_aClients[cid].state >= CClient::STATE_READY)
+		mods_client_drop(cid);
+	
+	m_aClients[cid].state = CClient::STATE_EMPTY;
+	m_aClients[cid].name[0] = 0;
+	m_aClients[cid].clan[0] = 0;
+	m_aClients[cid].authed = 0;
+	m_aClients[cid].snapshots.PurgeAll();
+	return 0;
+}
+static void server_send_map(int cid)
+{
+	msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH);
+	msg_pack_string(config.sv_map, 0);
+	msg_pack_int(current_map_crc);
+	msg_pack_end();
+	server_send_msg(cid);
+}
+
+static void server_send_rcon_line(int cid, const char *line)
+{
+	msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL);
+	msg_pack_string(line, 512);
+	msg_pack_end();
+	server_send_msg(cid);
+}
+
+static void server_send_rcon_line_authed(const char *line, void *user_data)
+{
+	static volatile int reentry_guard = 0;
+	int i;
+	
+	if(reentry_guard) return;
+	reentry_guard++;
+	
+	for(i = 0; i < MAX_CLIENTS; i++)
+	{
+		if(m_aClients[i].state != CClient::STATE_EMPTY && m_aClients[i].authed)
+			server_send_rcon_line(i, line);
+	}
+	
+	reentry_guard--;
+}
+
+static void server_pump_network()
+{
+	CNetChunk Packet;
+
+	m_NetServer.Update();
+	
+	/* process packets */
+	while(m_NetServer.Recv(&Packet))
+	{
+		if(Packet.m_ClientID == -1)
+		{
+			/* stateless */
+			if(!register_process_packet(&Packet))
+			{
+				if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 &&
+					memcmp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
+				{
+					server_send_serverinfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]);
+				}
+				
+				
+				if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) &&
+					memcmp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0)
+				{
+					server_send_serverinfo(&Packet.m_Address, -1);
+				}
+			}
+		}
+		else
+			server_process_client_packet(&Packet);
+	}
+}
+
+static int server_load_map(const char *mapname)
+{
+	DATAFILE *df;
+	char buf[512];
+	str_format(buf, sizeof(buf), "maps/%s.map", mapname);
+	df = datafile_load(buf);
+	if(!df)
+		return 0;
+	
+	/* stop recording when we change map */
+	demorec_record_stop();
+	
+	/* reinit snapshot ids */
+	snap_timeout_ids();
+	
+	/* get the crc of the map */
+	current_map_crc = datafile_crc(buf);
+	dbg_msg("server", "%s crc is %08x", buf, current_map_crc);
+		
+	str_copy(current_map, mapname, sizeof(current_map));
+	map_set(df);
+	
+	/* load compelate map into memory for download */
+	{
+		IOHANDLE file = engine_openfile(buf, IOFLAG_READ);
+		current_map_size = (int)io_length(file);
+		if(current_map_data)
+			mem_free(current_map_data);
+		current_map_data = (unsigned char *)mem_alloc(current_map_size, 1);
+		io_read(file, current_map_data, current_map_size);
+		io_close(file);
+	}
+	return 1;
+}
+
+static int server_run()
+{
+	NETADDR bindaddr;
+
+	snap_init_id();
+	net_init();
+	
+	/* */
+	console_register_print_callback(server_send_rcon_line_authed, 0);
+
+	/* load map */
+	if(!server_load_map(config.sv_map))
+	{
+		dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
+		return -1;
+	}
+	
+	/* start server */
+	/* TODO: IPv6 support */
+	if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0)
+	{
+		/* sweet! */
+		bindaddr.port = config.sv_port;
+	}
+	else
+	{
+		mem_zero(&bindaddr, sizeof(bindaddr));
+		bindaddr.port = config.sv_port;
+	}
+	
+	
+	if(!m_NetServer.Open(bindaddr, config.sv_max_clients, 0))
+	{
+		dbg_msg("server", "couldn't open socket. port might already be in use");
+		return -1;
+	}
+
+	m_NetServer.SetCallbacks(new_client_callback, del_client_callback, this);
+	
+	dbg_msg("server", "server name is '%s'", config.sv_name);
+	
+	mods_init();
+	dbg_msg("server", "version %s", mods_net_version());
+
+	/* start game */
+	{
+		int64 reporttime = time_get();
+		int reportinterval = 3;
+	
+		lastheartbeat = 0;
+		game_start_time = time_get();
+	
+		if(config.debug)
+			dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024);
+
+		while(run_server)
+		{
+			static PERFORMACE_INFO rootscope = {"root", 0};
+			int64 t = time_get();
+			int new_ticks = 0;
+			
+			perf_start(&rootscope);
+			
+			/* load new map TODO: don't poll this */
+			if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload)
+			{
+				config.sv_map_reload = 0;
+				
+				/* load map */
+				if(server_load_map(config.sv_map))
+				{
+					int c;
+					
+					/* new map loaded */
+					mods_shutdown();
+					
+					for(c = 0; c < MAX_CLIENTS; c++)
+					{
+						if(m_aClients[c].state == CClient::STATE_EMPTY)
+							continue;
+						
+						server_send_map(c);
+						reset_client(c);
+						m_aClients[c].state = CClient::STATE_CONNECTING;
+					}
+					
+					game_start_time = time_get();
+					current_tick = 0;
+					mods_init();
+				}
+				else
+				{
+					dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
+					config_set_sv_map(&config, current_map);
+				}
+			}
+			
+			while(t > server_tick_start_time(current_tick+1))
+			{
+				current_tick++;
+				new_ticks++;
+				
+				/* apply new input */
+				{
+					static PERFORMACE_INFO scope = {"input", 0};
+					int c, i;
+					
+					perf_start(&scope);
+					
+					for(c = 0; c < MAX_CLIENTS; c++)
+					{
+						if(m_aClients[c].state == CClient::STATE_EMPTY)
+							continue;
+						for(i = 0; i < 200; i++)
+						{
+							if(m_aClients[c].inputs[i].game_tick == server_tick())
+							{
+								if(m_aClients[c].state == CClient::STATE_INGAME)
+									mods_client_predicted_input(c, m_aClients[c].inputs[i].data);
+								break;
+							}
+						}
+					}
+					
+					perf_end();
+				}
+				
+				/* progress game */
+				{
+					static PERFORMACE_INFO scope = {"tick", 0};
+					perf_start(&scope);
+					mods_tick();
+					perf_end();
+				}
+			}
+			
+			/* snap game */
+			if(new_ticks)
+			{
+				if(config.sv_high_bandwidth || (current_tick%2) == 0)
+				{
+					static PERFORMACE_INFO scope = {"snap", 0};
+					perf_start(&scope);
+					server_do_snap();
+					perf_end();
+				}
+			}
+			
+			/* master server stuff */
+			register_update();
+	
+
+			{
+				static PERFORMACE_INFO scope = {"net", 0};
+				perf_start(&scope);
+				server_pump_network();
+				perf_end();
+			}
+
+			perf_end();
+	
+			if(reporttime < time_get())
+			{
+				if(config.debug)
+				{
+					/*
+					static NETSTATS prev_stats;
+					NETSTATS stats;
+					netserver_stats(net, &stats);
+					
+					perf_next();
+					
+					if(config.dbg_pref)
+						perf_dump(&rootscope);
+
+					dbg_msg("server", "send=%8d recv=%8d",
+						(stats.send_bytes - prev_stats.send_bytes)/reportinterval,
+						(stats.recv_bytes - prev_stats.recv_bytes)/reportinterval);
+						
+					prev_stats = stats;
+					*/
+				}
+	
+				reporttime += time_freq()*reportinterval;
+			}
+			
+			/* wait for incomming data */
+			net_socket_read_wait(m_NetServer.Socket(), 5);
+		}
+	}
+
+	mods_shutdown();
+	map_unload();
+
+	if(current_map_data)
+		mem_free(current_map_data);
+	return 0;
+}
+
+
+#endif
+
+int main(int argc, char **argv)
+{
+	
+	m_pNetServer = &g_Server.m_NetServer;
+#if defined(CONF_FAMILY_WINDOWS)
+	int i;
+	for(i = 1; i < argc; i++)
+	{
+		if(strcmp("-s", argv[i]) == 0 || strcmp("--silent", argv[i]) == 0)
+		{
+			ShowWindow(GetConsoleWindow(), SW_HIDE);
+			break;
+		}
+	}
+#endif
+
+	/* init the engine */
+	dbg_msg("server", "starting...");
+	engine_init("Teeworlds");
+	
+	/* register all console commands */
+	
+	g_Server.RegisterCommands();
+	mods_console_init();
+	
+	/* parse the command line arguments */
+	engine_parse_arguments(argc, argv);
+	
+	/* run the server */
+	g_Server.Run();
+	return 0;
+}
+