about summary refs log tree commit diff
path: root/src/engine
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2010-05-29 07:25:38 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2010-05-29 07:25:38 +0000
commit72c06a258940696093f255fb1061beb58e1cdd0b (patch)
tree36b9a7712eec2d4f07837eab9c38ef1c5af85319 /src/engine
parente56feb597bc743677633432f77513b02907fd169 (diff)
downloadzcatch-72c06a258940696093f255fb1061beb58e1cdd0b.tar.gz
zcatch-72c06a258940696093f255fb1061beb58e1cdd0b.zip
copied refactor to trunk
Diffstat (limited to 'src/engine')
-rw-r--r--src/engine/client.h159
-rw-r--r--src/engine/client/client.cpp2062
-rw-r--r--src/engine/client/client.h286
-rw-r--r--src/engine/client/ec_client.cpp2087
-rw-r--r--src/engine/client/ec_gfx.cpp992
-rw-r--r--src/engine/client/ec_gfx_text.cpp669
-rw-r--r--src/engine/client/ec_inp.cpp234
-rw-r--r--src/engine/client/ec_snd.cpp471
-rw-r--r--src/engine/client/ec_srvbrowse.cpp736
-rw-r--r--src/engine/client/editor.h10
-rw-r--r--src/engine/client/graphics.cpp938
-rw-r--r--src/engine/client/graphics.h180
-rw-r--r--src/engine/client/input.cpp208
-rw-r--r--src/engine/client/input.h37
-rw-r--r--src/engine/client/keynames.h (renamed from src/engine/e_keynames.cpp)10
-rw-r--r--src/engine/client/sound.cpp485
-rw-r--r--src/engine/client/sound.h39
-rw-r--r--src/engine/client/srvbrowse.cpp721
-rw-r--r--src/engine/client/srvbrowse.h111
-rw-r--r--src/engine/client/text.cpp718
-rw-r--r--src/engine/config.h23
-rw-r--r--src/engine/console.h58
-rw-r--r--src/engine/demo.h29
-rw-r--r--src/engine/e_client_interface.h16
-rw-r--r--src/engine/e_common_interface.h8
-rw-r--r--src/engine/e_compression.cpp86
-rw-r--r--src/engine/e_compression.h7
-rw-r--r--src/engine/e_config.cpp49
-rw-r--r--src/engine/e_config.h44
-rw-r--r--src/engine/e_config_variables.h77
-rw-r--r--src/engine/e_console.cpp464
-rw-r--r--src/engine/e_console.h48
-rw-r--r--src/engine/e_datafile.cpp677
-rw-r--r--src/engine/e_datafile.h29
-rw-r--r--src/engine/e_demorec.cpp640
-rw-r--r--src/engine/e_demorec.h69
-rw-r--r--src/engine/e_engine.cpp596
-rw-r--r--src/engine/e_engine.h51
-rw-r--r--src/engine/e_huffman.cpp276
-rw-r--r--src/engine/e_huffman.h83
-rw-r--r--src/engine/e_if_client.h579
-rw-r--r--src/engine/e_if_gfx.h674
-rw-r--r--src/engine/e_if_inp.h244
-rw-r--r--src/engine/e_if_modc.h145
-rw-r--r--src/engine/e_if_mods.h168
-rw-r--r--src/engine/e_if_msg.h151
-rw-r--r--src/engine/e_if_other.h385
-rw-r--r--src/engine/e_if_server.h140
-rw-r--r--src/engine/e_if_snd.h91
-rw-r--r--src/engine/e_jobs.cpp76
-rw-r--r--src/engine/e_jobs.h33
-rw-r--r--src/engine/e_linereader.cpp62
-rw-r--r--src/engine/e_linereader.h14
-rw-r--r--src/engine/e_map.cpp66
-rw-r--r--src/engine/e_memheap.cpp102
-rw-r--r--src/engine/e_memheap.h6
-rw-r--r--src/engine/e_msg.cpp70
-rw-r--r--src/engine/e_protocol.h73
-rw-r--r--src/engine/e_server_interface.h12
-rw-r--r--src/engine/e_snapshot.cpp571
-rw-r--r--src/engine/editor.h16
-rw-r--r--src/engine/graphics.h151
-rw-r--r--src/engine/input.h89
-rw-r--r--src/engine/kernel.h66
-rw-r--r--src/engine/keys.h (renamed from src/engine/e_keys.h)0
-rw-r--r--src/engine/map.h32
-rw-r--r--src/engine/masterserver.h40
-rw-r--r--src/engine/message.h14
-rw-r--r--src/engine/server.h81
-rw-r--r--src/engine/server/es_register.cpp271
-rw-r--r--src/engine/server/es_server.cpp1969
-rw-r--r--src/engine/server/register.cpp264
-rw-r--r--src/engine/server/register.h48
-rw-r--r--src/engine/server/server.cpp1400
-rw-r--r--src/engine/server/server.h195
-rw-r--r--src/engine/serverbrowser.h93
-rw-r--r--src/engine/shared/compression.cpp88
-rw-r--r--src/engine/shared/compression.h12
-rw-r--r--src/engine/shared/config.cpp111
-rw-r--r--src/engine/shared/config.h22
-rw-r--r--src/engine/shared/config_variables.h80
-rw-r--r--src/engine/shared/console.cpp489
-rw-r--r--src/engine/shared/console.h96
-rw-r--r--src/engine/shared/datafile.cpp643
-rw-r--r--src/engine/shared/datafile.h77
-rw-r--r--src/engine/shared/demorec.cpp623
-rw-r--r--src/engine/shared/demorec.h117
-rw-r--r--src/engine/shared/engine.cpp76
-rw-r--r--src/engine/shared/engine.h23
-rw-r--r--src/engine/shared/huffman.cpp276
-rw-r--r--src/engine/shared/huffman.h89
-rw-r--r--src/engine/shared/jobs.cpp74
-rw-r--r--src/engine/shared/jobs.h51
-rw-r--r--src/engine/shared/kernel.cpp93
-rw-r--r--src/engine/shared/linereader.cpp62
-rw-r--r--src/engine/shared/linereader.h17
-rw-r--r--src/engine/shared/map.cpp45
-rw-r--r--src/engine/shared/masterserver.cpp187
-rw-r--r--src/engine/shared/memheap.cpp96
-rw-r--r--src/engine/shared/memheap.h32
-rw-r--r--src/engine/shared/message.h9
-rw-r--r--src/engine/shared/network.cpp (renamed from src/engine/e_network.cpp)96
-rw-r--r--src/engine/shared/network.h (renamed from src/engine/e_network.h)43
-rw-r--r--src/engine/shared/network_client.cpp (renamed from src/engine/e_network_client.cpp)12
-rw-r--r--src/engine/shared/network_conn.cpp (renamed from src/engine/e_network_conn.cpp)131
-rw-r--r--src/engine/shared/network_server.cpp (renamed from src/engine/e_network_server.cpp)121
-rw-r--r--src/engine/shared/packer.cpp (renamed from src/engine/e_packer.cpp)36
-rw-r--r--src/engine/shared/packer.h (renamed from src/engine/e_packer.h)11
-rw-r--r--src/engine/shared/protocol.h91
-rw-r--r--src/engine/shared/ringbuffer.cpp (renamed from src/engine/e_ringbuffer.cpp)40
-rw-r--r--src/engine/shared/ringbuffer.h (renamed from src/engine/e_ringbuffer.h)6
-rw-r--r--src/engine/shared/snapshot.cpp537
-rw-r--r--src/engine/shared/snapshot.h (renamed from src/engine/e_snapshot.h)78
-rw-r--r--src/engine/shared/storage.cpp196
-rw-r--r--src/engine/sound.h40
-rw-r--r--src/engine/storage.h25
-rw-r--r--src/engine/textrender.h60
117 files changed, 13225 insertions, 14660 deletions
diff --git a/src/engine/client.h b/src/engine/client.h
new file mode 100644
index 00000000..8e5a5577
--- /dev/null
+++ b/src/engine/client.h
@@ -0,0 +1,159 @@
+#ifndef ENGINE_CLIENT_H
+#define ENGINE_CLIENT_H
+#include "kernel.h"
+
+#include "message.h"
+
+class IClient : public IInterface
+{
+	MACRO_INTERFACE("client", 0)
+protected:
+	// quick access to state of the client
+	int m_State;
+
+	// quick access to time variables
+	int m_PrevGameTick;
+	int m_CurGameTick;
+	float m_GameIntraTick;
+	float m_GameTickTime;
+	
+	int m_PredTick;
+	float m_PredIntraTick;
+	
+	float m_LocalTime;
+	float m_FrameTime;
+	
+	int m_GameTickSpeed;
+public:
+
+	class CSnapItem
+	{
+	public:
+		int m_Type;
+		int m_Id;
+		int m_DataSize;
+	};
+
+	/* Constants: Client States
+		STATE_OFFLINE - The client is offline.
+		STATE_CONNECTING - The client is trying to connect to a server.
+		STATE_LOADING - The client has connected to a server and is loading resources.
+		STATE_ONLINE - The client is connected to a server and running the game.
+		STATE_DEMOPLAYBACK - The client is playing a demo
+		STATE_QUITING - The client is quiting.
+	*/
+
+	enum
+	{
+		STATE_OFFLINE=0,
+		STATE_CONNECTING,
+		STATE_LOADING,
+		STATE_ONLINE,
+		STATE_DEMOPLAYBACK,
+		STATE_QUITING,
+	};
+
+	//
+	inline int State() const { return m_State; }
+
+	// tick time access
+	inline int PrevGameTick() const { return m_PrevGameTick; }
+	inline int GameTick() const { return m_CurGameTick; }
+	inline int PredGameTick() const { return m_PredTick; }
+	inline float IntraGameTick() const { return m_GameIntraTick; }
+	inline float PredIntraGameTick() const { return m_PredIntraTick; }
+	inline float GameTickTime() const { return m_GameTickTime; }
+	inline int GameTickSpeed() const { return m_GameTickSpeed; }
+	
+	// other time access
+	inline float FrameTime() const { return m_FrameTime; }
+	inline float LocalTime() const { return m_LocalTime; }
+	
+	// actions
+	virtual void Connect(const char *pAddress) = 0;
+	virtual void Disconnect() = 0;
+	virtual void Quit() = 0;
+	virtual const char *DemoPlayer_Play(const char *pFilename) = 0;
+
+	// networking
+	virtual void EnterGame() = 0;
+
+	//
+	virtual int MapDownloadAmount() = 0;
+	virtual int MapDownloadTotalsize() = 0;
+	
+	// input
+	virtual int *GetInput(int Tick) = 0;
+	
+	// remote console
+	virtual void RconAuth(const char *pUsername, const char *pPassword) = 0;
+	virtual bool RconAuthed() = 0;
+	virtual void Rcon(const char *pLine) = 0;
+	
+	// server info
+	virtual void GetServerInfo(class CServerInfo *pServerInfo) = 0;
+	
+	// snapshot interface
+	
+	enum
+	{
+		SNAP_CURRENT=0,
+		SNAP_PREV=1
+	};
+		
+	// TODO: Refactor: should redo this a bit i think, too many virtual calls
+	virtual int SnapNumItems(int SnapId) = 0;
+	virtual void *SnapFindItem(int SnapId, int Type, int Id) = 0;
+	virtual void *SnapGetItem(int SnapId, int Index, CSnapItem *pItem) = 0;
+	virtual void SnapInvalidateItem(int SnapId, int Index) = 0;
+
+	virtual void SnapSetStaticsize(int ItemType, int Size) = 0;
+
+	virtual int SendMsg(CMsgPacker *pMsg, int Flags) = 0;
+
+	template<class T>
+	int SendPackMsg(T *pMsg, int Flags)
+	{
+		CMsgPacker Packer(pMsg->MsgID());
+		if(pMsg->Pack(&Packer))
+			return -1;
+		return SendMsg(&Packer, Flags);
+	}
+	
+	//
+	virtual const char *UserDirectory() = 0;
+	
+	// 
+	virtual const char *ErrorString() = 0;
+	virtual const char *LatestVersion() = 0;
+	virtual bool ConnectionProblems() = 0;
+};
+
+class IGameClient : public IInterface
+{
+	MACRO_INTERFACE("gameclient", 0)
+protected:
+public:
+	virtual void OnConsoleInit() = 0;
+
+	virtual void OnRconLine(const char *pLine) = 0;
+	virtual void OnInit() = 0;
+	virtual void OnNewSnapshot() = 0;
+	virtual void OnEnterGame() = 0;
+	virtual void OnShutdown() = 0;
+	virtual void OnRender() = 0;
+	virtual void OnStateChange(int NewState, int OldState) = 0;
+	virtual void OnConnected() = 0;
+	virtual void OnMessage(int MsgId, CUnpacker *pUnpacker) = 0;
+	virtual void OnPredict() = 0;
+	
+	virtual int OnSnapInput(int *pData) = 0;
+	
+	virtual const char *GetItemName(int Type) = 0;
+	virtual const char *Version() = 0;
+	virtual const char *NetVersion() = 0;
+
+};
+
+extern IGameClient *CreateGameClient();
+#endif
diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp
new file mode 100644
index 00000000..dbcaa1ed
--- /dev/null
+++ b/src/engine/client/client.cpp
@@ -0,0 +1,2062 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+
+#include <stdlib.h> // qsort
+#include <stdarg.h>
+#include <math.h>
+
+#include <base/system.h>
+#include <engine/shared/engine.h>
+
+#include <engine/shared/protocol.h>
+#include <engine/shared/snapshot.h>
+#include <engine/shared/compression.h>
+#include <engine/shared/network.h>
+#include <engine/shared/config.h>
+#include <engine/shared/packer.h>
+#include <engine/shared/memheap.h>
+#include <engine/shared/datafile.h>
+#include <engine/shared/ringbuffer.h>
+#include <engine/shared/protocol.h>
+
+#include <engine/shared/demorec.h>
+
+#include <mastersrv/mastersrv.h>
+#include <versionsrv/versionsrv.h>
+
+#include "client.h"
+
+
+void CGraph::Init(float Min, float Max)
+{
+	m_Min = Min;
+	m_Max = Max;
+	m_Index = 0;
+}
+
+void CGraph::ScaleMax()
+{
+	int i = 0;
+	m_Max = 0;
+	for(i = 0; i < MAX_VALUES; i++)
+	{
+		if(m_aValues[i] > m_Max)
+			m_Max = m_aValues[i];
+	}
+}
+
+void CGraph::ScaleMin()
+{
+	int i = 0;
+	m_Min = m_Max;
+	for(i = 0; i < MAX_VALUES; i++)
+	{
+		if(m_aValues[i] < m_Min)
+			m_Min = m_aValues[i];
+	}
+}
+
+void CGraph::Add(float v, float r, float g, float b)
+{
+	m_Index = (m_Index+1)&(MAX_VALUES-1);
+	m_aValues[m_Index] = v;
+	m_aColors[m_Index][0] = r;
+	m_aColors[m_Index][1] = g;
+	m_aColors[m_Index][2] = b;
+}
+
+void CGraph::Render(IGraphics *pGraphics, int Font, float x, float y, float w, float h, const char *pDescription)
+{
+	//m_pGraphics->BlendNormal();
+
+
+	pGraphics->TextureSet(-1);
+
+	pGraphics->QuadsBegin();
+	pGraphics->SetColor(0, 0, 0, 0.75f);
+	IGraphics::CQuadItem QuadItem(x, y, w, h);
+	pGraphics->QuadsDrawTL(&QuadItem, 1);
+	pGraphics->QuadsEnd();
+
+	pGraphics->LinesBegin();
+	pGraphics->SetColor(0.95f, 0.95f, 0.95f, 1.00f);
+	IGraphics::CLineItem LineItem(x, y+h/2, x+w, y+h/2);
+	pGraphics->LinesDraw(&LineItem, 1);
+	pGraphics->SetColor(0.5f, 0.5f, 0.5f, 0.75f);
+	IGraphics::CLineItem Array[2] = {
+		IGraphics::CLineItem(x, y+(h*3)/4, x+w, y+(h*3)/4),
+		IGraphics::CLineItem(x, y+h/4, x+w, y+h/4)};
+	pGraphics->LinesDraw(Array, 2);
+	for(int i = 1; i < MAX_VALUES; i++)
+	{
+		float a0 = (i-1)/(float)MAX_VALUES;
+		float a1 = i/(float)MAX_VALUES;
+		int i0 = (m_Index+i-1)&(MAX_VALUES-1);
+		int i1 = (m_Index+i)&(MAX_VALUES-1);
+
+		float v0 = (m_aValues[i0]-m_Min) / (m_Max-m_Min);
+		float v1 = (m_aValues[i1]-m_Min) / (m_Max-m_Min);
+
+		IGraphics::CColorVertex Array[2] = {
+			IGraphics::CColorVertex(0, m_aColors[i0][0], m_aColors[i0][1], m_aColors[i0][2], 0.75f),
+			IGraphics::CColorVertex(1, m_aColors[i1][0], m_aColors[i1][1], m_aColors[i1][2], 0.75f)};
+		pGraphics->SetColorVertex(Array, 2);
+		IGraphics::CLineItem LineItem(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h);
+		pGraphics->LinesDraw(&LineItem, 1);
+
+	}
+	pGraphics->LinesEnd();
+
+	pGraphics->TextureSet(Font);
+	pGraphics->QuadsText(x+2, y+h-16, 16, 1,1,1,1, pDescription);
+
+	char aBuf[32];
+	str_format(aBuf, sizeof(aBuf), "%.2f", m_Max);
+	pGraphics->QuadsText(x+w-8*str_length(aBuf)-8, y+2, 16, 1,1,1,1, aBuf);
+
+	str_format(aBuf, sizeof(aBuf), "%.2f", m_Min);
+	pGraphics->QuadsText(x+w-8*str_length(aBuf)-8, y+h-16, 16, 1,1,1,1, aBuf);
+}
+
+
+void CSmoothTime::Init(int64 Target)
+{
+	m_Snap = time_get();
+	m_Current = Target;
+	m_Target = Target;
+	m_aAdjustSpeed[0] = 0.3f;
+	m_aAdjustSpeed[1] = 0.3f;
+	m_Graph.Init(0.0f, 0.5f);
+}
+
+void CSmoothTime::SetAdjustSpeed(int Direction, float Value)
+{
+	m_aAdjustSpeed[Direction] = Value;
+}
+
+int64 CSmoothTime::Get(int64 Now)
+{
+	int64 c = m_Current + (Now - m_Snap);
+	int64 t = m_Target + (Now - m_Snap);
+
+	// it's faster to adjust upward instead of downward
+	// we might need to adjust these abit
+
+	float AdjustSpeed = m_aAdjustSpeed[0];
+	if(t > c)
+		AdjustSpeed = m_aAdjustSpeed[1];
+
+	float a = ((Now-m_Snap)/(float)time_freq()) * AdjustSpeed;
+	if(a > 1.0f)
+		a = 1.0f;
+
+	int64 r = c + (int64)((t-c)*a);
+
+	m_Graph.Add(a+0.5f,1,1,1);
+
+	return r;
+}
+
+void CSmoothTime::UpdateInt(int64 Target)
+{
+	int64 Now = time_get();
+	m_Current = Get(Now);
+	m_Snap = Now;
+	m_Target = Target;
+}
+
+void CSmoothTime::Update(CGraph *pGraph, int64 Target, int TimeLeft, int AdjustDirection)
+{
+	int UpdateTimer = 1;
+
+	if(TimeLeft < 0)
+	{
+		int IsSpike = 0;
+		if(TimeLeft < -50)
+		{
+			IsSpike = 1;
+
+			m_SpikeCounter += 5;
+			if(m_SpikeCounter > 50)
+				m_SpikeCounter = 50;
+		}
+
+		if(IsSpike && m_SpikeCounter < 15)
+		{
+			// ignore this ping spike
+			UpdateTimer = 0;
+			pGraph->Add(TimeLeft, 1,1,0);
+		}
+		else
+		{
+			pGraph->Add(TimeLeft, 1,0,0);
+			if(m_aAdjustSpeed[AdjustDirection] < 30.0f)
+				m_aAdjustSpeed[AdjustDirection] *= 2.0f;
+		}
+	}
+	else
+	{
+		if(m_SpikeCounter)
+			m_SpikeCounter--;
+
+		pGraph->Add(TimeLeft, 0,1,0);
+
+		m_aAdjustSpeed[AdjustDirection] *= 0.95f;
+		if(m_aAdjustSpeed[AdjustDirection] < 2.0f)
+			m_aAdjustSpeed[AdjustDirection] = 2.0f;
+	}
+
+	if(UpdateTimer)
+		UpdateInt(Target);
+}
+
+
+CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotDelta)
+{
+	m_pEditor = 0;
+	m_pInput = 0;
+	m_pGraphics = 0;
+	m_pSound = 0;
+	m_pGameClient = 0;
+	m_pMap = 0;
+	m_pConsole = 0;
+
+	m_FrameTime = 0.0001f;
+	m_FrameTimeLow = 1.0f;
+	m_FrameTimeHigh = 0.0f;
+	m_Frames = 0;
+
+	m_GameTickSpeed = SERVER_TICK_SPEED;
+
+	m_WindowMustRefocus = 0;
+	m_SnapCrcErrors = 0;
+
+	m_AckGameTick = -1;
+	m_CurrentRecvTick = 0;
+	m_RconAuthed = 0;
+
+	// version-checking
+	m_aVersionStr[0] = '0';
+	m_aVersionStr[1] = 0;
+
+	// pinging
+	m_PingStartTime = 0;
+
+	//
+	m_aCurrentMap[0] = 0;
+	m_CurrentMapCrc = 0;
+
+	//
+	m_aCmdConnect[0] = 0;
+
+	// map download
+	m_aMapdownloadFilename[0] = 0;
+	m_aMapdownloadName[0] = 0;
+	m_MapdownloadFile = 0;
+	m_MapdownloadChunk = 0;
+	m_MapdownloadCrc = 0;
+	m_MapdownloadAmount = -1;
+	m_MapdownloadTotalsize = -1;
+
+	m_CurrentServerInfoRequestTime = -1;
+
+	m_CurrentInput = 0;
+
+	m_State = IClient::STATE_OFFLINE;
+	m_aServerAddressStr[0] = 0;
+
+	mem_zero(m_aSnapshots, sizeof(m_aSnapshots));
+	m_RecivedSnapshots = 0;
+
+	m_VersionInfo.m_State = 0;
+}
+
+// ----- send functions -----
+int CClient::SendMsg(CMsgPacker *pMsg, int Flags)
+{
+	return SendMsgEx(pMsg, Flags, false);
+}
+
+int CClient::SendMsgEx(CMsgPacker *pMsg, int Flags, bool System)
+{
+	CNetChunk Packet;
+
+	if(State() == IClient::STATE_OFFLINE)
+		return 0;
+
+	mem_zero(&Packet, sizeof(CNetChunk));
+
+	Packet.m_ClientID = 0;
+	Packet.m_pData = pMsg->Data();
+	Packet.m_DataSize = pMsg->Size();
+
+	// HACK: modify the message id in the packet and store the system flag
+	if(*((unsigned char*)Packet.m_pData) == 1 && System && Packet.m_DataSize == 1)
+		dbg_break();
+
+	*((unsigned char*)Packet.m_pData) <<= 1;
+	if(System)
+		*((unsigned char*)Packet.m_pData) |= 1;
+
+	if(Flags&MSGFLAG_VITAL)
+		Packet.m_Flags |= NETSENDFLAG_VITAL;
+	if(Flags&MSGFLAG_FLUSH)
+		Packet.m_Flags |= NETSENDFLAG_FLUSH;
+
+	if(Flags&MSGFLAG_RECORD)
+	{
+		if(m_DemoRecorder.IsRecording())
+			m_DemoRecorder.RecordMessage(Packet.m_pData, Packet.m_DataSize);
+	}
+
+	if(!(Flags&MSGFLAG_NOSEND))
+		m_NetClient.Send(&Packet);
+	return 0;
+}
+
+void CClient::SendInfo()
+{
+	CMsgPacker Msg(NETMSG_INFO);
+	Msg.AddString(GameClient()->NetVersion(), 128);
+	Msg.AddString(g_Config.m_PlayerName, 128);
+	Msg.AddString(g_Config.m_ClanName, 128);
+	Msg.AddString(g_Config.m_Password, 128);
+	SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH);
+}
+
+
+void CClient::SendEnterGame()
+{
+	CMsgPacker Msg(NETMSG_ENTERGAME);
+	SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH);
+}
+
+void CClient::SendReady()
+{
+	CMsgPacker Msg(NETMSG_READY);
+	SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH);
+}
+
+bool CClient::RconAuthed()
+{
+	return m_RconAuthed;
+}
+
+void CClient::RconAuth(const char *pName, const char *pPassword)
+{
+	if(RconAuthed())
+		return;
+        
+	CMsgPacker Msg(NETMSG_RCON_AUTH);
+	Msg.AddString(pName, 32);
+	Msg.AddString(pPassword, 32);
+	SendMsgEx(&Msg, MSGFLAG_VITAL);
+}
+
+void CClient::Rcon(const char *pCmd)
+{
+	CMsgPacker Msg(NETMSG_RCON_CMD);
+	Msg.AddString(pCmd, 256);
+	SendMsgEx(&Msg, MSGFLAG_VITAL);
+}
+
+bool CClient::ConnectionProblems()
+{
+	return m_NetClient.GotProblems() != 0;
+}
+
+void CClient::DirectInput(int *pInput, int Size)
+{
+	int i;
+	CMsgPacker Msg(NETMSG_INPUT);
+	Msg.AddInt(m_AckGameTick);
+	Msg.AddInt(m_PredTick);
+	Msg.AddInt(Size);
+
+	for(i = 0; i < Size/4; i++)
+		Msg.AddInt(pInput[i]);
+
+	SendMsgEx(&Msg, 0);
+}
+
+
+void CClient::SendInput()
+{
+	int64 Now = time_get();
+
+	if(m_PredTick <= 0)
+		return;
+
+	// fetch input
+	int Size = GameClient()->OnSnapInput(m_aInputs[m_CurrentInput].m_aData);
+
+	if(!Size)
+		return;
+
+	// pack input
+	CMsgPacker Msg(NETMSG_INPUT);
+	Msg.AddInt(m_AckGameTick);
+	Msg.AddInt(m_PredTick);
+	Msg.AddInt(Size);
+
+	m_aInputs[m_CurrentInput].m_Tick = m_PredTick;
+	m_aInputs[m_CurrentInput].m_PredictedTime = m_PredictedTime.Get(Now);
+	m_aInputs[m_CurrentInput].m_Time = Now;
+
+	// pack it
+	for(int i = 0; i < Size/4; i++)
+		Msg.AddInt(m_aInputs[m_CurrentInput].m_aData[i]);
+
+	m_CurrentInput++;
+	m_CurrentInput%=200;
+
+	SendMsgEx(&Msg, MSGFLAG_FLUSH);
+}
+
+const char *CClient::LatestVersion()
+{
+	return m_aVersionStr;
+}
+
+// TODO: OPT: do this alot smarter!
+int *CClient::GetInput(int Tick)
+{
+	int Best = -1;
+	for(int i = 0; i < 200; i++)
+	{
+		if(m_aInputs[i].m_Tick <= Tick && (Best == -1 || m_aInputs[Best].m_Tick < m_aInputs[i].m_Tick))
+			Best = i;
+	}
+
+	if(Best != -1)
+		return (int *)m_aInputs[Best].m_aData;
+	return 0;
+}
+
+// ------ state handling -----
+void CClient::SetState(int s)
+{
+	int Old = m_State;
+	if(g_Config.m_Debug)
+		dbg_msg("client", "state change. last=%d current=%d", m_State, s);
+	m_State = s;
+	if(Old != s)
+		GameClient()->OnStateChange(m_State, Old);
+}
+
+// called when the map is loaded and we should init for a new round
+void CClient::OnEnterGame()
+{
+	// reset input
+	int i;
+	for(i = 0; i < 200; i++)
+		m_aInputs[i].m_Tick = -1;
+	m_CurrentInput = 0;
+
+	// reset snapshots
+	m_aSnapshots[SNAP_CURRENT] = 0;
+	m_aSnapshots[SNAP_PREV] = 0;
+	m_SnapshotStorage.PurgeAll();
+	m_RecivedSnapshots = 0;
+	m_SnapshotParts = 0;
+	m_PredTick = 0;
+	m_CurrentRecvTick = 0;
+	m_CurGameTick = 0;
+	m_PrevGameTick = 0;
+}
+
+void CClient::EnterGame()
+{
+	if(State() == IClient::STATE_DEMOPLAYBACK)
+		return;
+
+	// now we will wait for two snapshots
+	// to finish the connection
+	SendEnterGame();
+	OnEnterGame();
+}
+
+void CClient::Connect(const char *pAddress)
+{
+	char aBuf[512];
+	const char *pPortStr = 0;
+	int Port = 8303;
+
+	Disconnect();
+
+	str_copy(m_aServerAddressStr, pAddress, sizeof(m_aServerAddressStr));
+
+	dbg_msg("client", "connecting to '%s'", m_aServerAddressStr);
+
+	ServerInfoRequest();
+	str_copy(aBuf, m_aServerAddressStr, sizeof(aBuf));
+
+	for(int k = 0; aBuf[k]; k++)
+	{
+		if(aBuf[k] == ':')
+		{
+			pPortStr = &(aBuf[k+1]);
+			aBuf[k] = 0;
+			break;
+		}
+	}
+
+	if(pPortStr)
+		Port = str_toint(pPortStr);
+
+	// TODO: IPv6 support
+	if(net_host_lookup(aBuf, &m_ServerAddress, NETTYPE_IPV4) != 0)
+		dbg_msg("client", "could not find the address of %s, connecting to localhost", aBuf);
+
+	m_RconAuthed = 0;
+	m_ServerAddress.port = Port;
+	m_NetClient.Connect(&m_ServerAddress);
+	SetState(IClient::STATE_CONNECTING);
+
+	if(m_DemoRecorder.IsRecording())
+		m_DemoRecorder.Stop();
+
+	m_InputtimeMarginGraph.Init(-150.0f, 150.0f);
+	m_GametimeMarginGraph.Init(-150.0f, 150.0f);
+}
+
+void CClient::DisconnectWithReason(const char *pReason)
+{
+	// stop demo playback and recorder
+	m_DemoPlayer.Stop();
+	m_DemoRecorder.Stop();
+
+	//
+	m_RconAuthed = 0;
+	m_NetClient.Disconnect(pReason);
+	SetState(IClient::STATE_OFFLINE);
+	m_pMap->Unload();
+
+	// disable all downloads
+	m_MapdownloadChunk = 0;
+	if(m_MapdownloadFile)
+		io_close(m_MapdownloadFile);
+	m_MapdownloadFile = 0;
+	m_MapdownloadCrc = 0;
+	m_MapdownloadTotalsize = -1;
+	m_MapdownloadAmount = 0;
+
+	// clear the current server info
+	mem_zero(&m_CurrentServerInfo, sizeof(m_CurrentServerInfo));
+	mem_zero(&m_ServerAddress, sizeof(m_ServerAddress));
+
+	// clear snapshots
+	m_aSnapshots[SNAP_CURRENT] = 0;
+	m_aSnapshots[SNAP_PREV] = 0;
+	m_RecivedSnapshots = 0;
+}
+
+void CClient::Disconnect()
+{
+	DisconnectWithReason(0);
+}
+
+
+void CClient::GetServerInfo(CServerInfo *pServerInfo)
+{
+	mem_copy(pServerInfo, &m_CurrentServerInfo, sizeof(m_CurrentServerInfo));
+}
+
+void CClient::ServerInfoRequest()
+{
+	mem_zero(&m_CurrentServerInfo, sizeof(m_CurrentServerInfo));
+	m_CurrentServerInfoRequestTime = 0;
+}
+
+int CClient::LoadData()
+{
+	m_DebugFont = Graphics()->LoadTexture("debug_font.png", CImageInfo::FORMAT_AUTO, IGraphics::TEXLOAD_NORESAMPLE);
+	return 1;
+}
+
+// ---
+
+void *CClient::SnapGetItem(int SnapId, int Index, CSnapItem *pItem)
+{
+	CSnapshotItem *i;
+	dbg_assert(SnapId >= 0 && SnapId < NUM_SNAPSHOT_TYPES, "invalid SnapId");
+	i = m_aSnapshots[SnapId]->m_pAltSnap->GetItem(Index);
+	pItem->m_DataSize = m_aSnapshots[SnapId]->m_pAltSnap->GetItemSize(Index);
+	pItem->m_Type = i->Type();
+	pItem->m_Id = i->ID();
+	return (void *)i->Data();
+}
+
+void CClient::SnapInvalidateItem(int SnapId, int Index)
+{
+	CSnapshotItem *i;
+	dbg_assert(SnapId >= 0 && SnapId < NUM_SNAPSHOT_TYPES, "invalid SnapId");
+	i = m_aSnapshots[SnapId]->m_pAltSnap->GetItem(Index);
+	if(i)
+	{
+		if((char *)i < (char *)m_aSnapshots[SnapId]->m_pAltSnap || (char *)i > (char *)m_aSnapshots[SnapId]->m_pAltSnap + m_aSnapshots[SnapId]->m_SnapSize)
+			dbg_msg("ASDFASDFASdf", "ASDFASDFASDF");
+		if((char *)i >= (char *)m_aSnapshots[SnapId]->m_pSnap && (char *)i < (char *)m_aSnapshots[SnapId]->m_pSnap + m_aSnapshots[SnapId]->m_SnapSize)
+			dbg_msg("ASDFASDFASdf", "ASDFASDFASDF");
+		i->m_TypeAndID = -1;
+	}
+}
+
+void *CClient::SnapFindItem(int SnapId, int Type, int Id)
+{
+	// TODO: linear search. should be fixed.
+	int i;
+
+	if(!m_aSnapshots[SnapId])
+		return 0x0;
+
+	for(i = 0; i < m_aSnapshots[SnapId]->m_pSnap->NumItems(); i++)
+	{
+		CSnapshotItem *pItem = m_aSnapshots[SnapId]->m_pAltSnap->GetItem(i);
+		if(pItem->Type() == Type && pItem->ID() == Id)
+			return (void *)pItem->Data();
+	}
+	return 0x0;
+}
+
+int CClient::SnapNumItems(int SnapId)
+{
+	dbg_assert(SnapId >= 0 && SnapId < NUM_SNAPSHOT_TYPES, "invalid SnapId");
+	if(!m_aSnapshots[SnapId])
+		return 0;
+	return m_aSnapshots[SnapId]->m_pSnap->NumItems();
+}
+
+void CClient::SnapSetStaticsize(int ItemType, int Size)
+{
+	m_SnapshotDelta.SetStaticsize(ItemType, Size);
+}
+
+
+void CClient::DebugRender()
+{
+	static NETSTATS Prev, Current;
+	static int64 LastSnap = 0;
+	static float FrameTimeAvg = 0;
+	int64 Now = time_get();
+	char aBuffer[512];
+
+	if(!g_Config.m_Debug)
+		return;
+
+	//m_pGraphics->BlendNormal();
+	Graphics()->TextureSet(m_DebugFont);
+	Graphics()->MapScreen(0,0,Graphics()->ScreenWidth(),Graphics()->ScreenHeight());
+
+	if(time_get()-LastSnap > time_freq())
+	{
+		LastSnap = time_get();
+		Prev = Current;
+		net_stats(&Current);
+	}
+
+	/*
+		eth = 14
+		ip = 20
+		udp = 8
+		total = 42
+	*/
+	FrameTimeAvg = FrameTimeAvg*0.9f + m_FrameTime*0.1f;
+	str_format(aBuffer, sizeof(aBuffer), "ticks: %8d %8d mem %dk %d  gfxmem: %dk  fps: %3d",
+		m_CurGameTick, m_PredTick,
+		mem_stats()->allocated/1024,
+		mem_stats()->total_allocations,
+		Graphics()->MemoryUsage()/1024,
+		(int)(1.0f/FrameTimeAvg));
+	Graphics()->QuadsText(2, 2, 16, 1,1,1,1, aBuffer);
+
+
+	{
+		int SendPackets = (Current.sent_packets-Prev.sent_packets);
+		int SendBytes = (Current.sent_bytes-Prev.sent_bytes);
+		int SendTotal = SendBytes + SendPackets*42;
+		int RecvPackets = (Current.recv_packets-Prev.recv_packets);
+		int RecvBytes = (Current.recv_bytes-Prev.recv_bytes);
+		int RecvTotal = RecvBytes + RecvPackets*42;
+
+		if(!SendPackets) SendPackets++;
+		if(!RecvPackets) RecvPackets++;
+		str_format(aBuffer, sizeof(aBuffer), "send: %3d %5d+%4d=%5d (%3d kbps) avg: %5d\nrecv: %3d %5d+%4d=%5d (%3d kbps) avg: %5d",
+			SendPackets, SendBytes, SendPackets*42, SendTotal, (SendTotal*8)/1024, SendBytes/SendPackets,
+			RecvPackets, RecvBytes, RecvPackets*42, RecvTotal, (RecvTotal*8)/1024, RecvBytes/RecvPackets);
+		Graphics()->QuadsText(2, 14, 16, 1,1,1,1, aBuffer);
+	}
+
+	// render rates
+	{
+		int y = 0;
+		int i;
+		for(i = 0; i < 256; i++)
+		{
+			if(m_SnapshotDelta.GetDataRate(i))
+			{
+				str_format(aBuffer, sizeof(aBuffer), "%4d %20s: %8d %8d %8d", i, GameClient()->GetItemName(i), m_SnapshotDelta.GetDataRate(i)/8, m_SnapshotDelta.GetDataUpdates(i),
+					(m_SnapshotDelta.GetDataRate(i)/m_SnapshotDelta.GetDataUpdates(i))/8);
+				Graphics()->QuadsText(2, 100+y*12, 16, 1,1,1,1, aBuffer);
+				y++;
+			}
+		}
+	}
+
+	str_format(aBuffer, sizeof(aBuffer), "pred: %d ms",
+		(int)((m_PredictedTime.Get(Now)-m_GameTime.Get(Now))*1000/(float)time_freq()));
+	Graphics()->QuadsText(2, 70, 16, 1,1,1,1, aBuffer);
+
+	// render graphs
+	if(g_Config.m_DbgGraphs)
+	{
+		//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;
+
+		m_FpsGraph.ScaleMax();
+		m_FpsGraph.ScaleMin();
+		m_FpsGraph.Render(Graphics(), m_DebugFont, x, sp*5, w, h, "FPS");
+		m_InputtimeMarginGraph.Render(Graphics(), m_DebugFont, x, sp*5+h+sp, w, h, "Prediction Margin");
+		m_GametimeMarginGraph.Render(Graphics(), m_DebugFont, x, sp*5+h+sp+h+sp, w, h, "Gametime Margin");
+	}
+}
+
+void CClient::Quit()
+{
+	SetState(IClient::STATE_QUITING);
+}
+
+const char *CClient::ErrorString()
+{
+	return m_NetClient.ErrorString();
+}
+
+void CClient::Render()
+{
+	if(g_Config.m_GfxClear)
+		Graphics()->Clear(1,1,0);
+
+	GameClient()->OnRender();
+	DebugRender();
+}
+
+const char *CClient::LoadMap(const char *pName, const char *pFilename, unsigned WantedCrc)
+{
+	static char aErrorMsg[128];
+
+	SetState(IClient::STATE_LOADING);
+
+	if(!m_pMap->Load(pFilename))
+	{
+		str_format(aErrorMsg, sizeof(aErrorMsg), "map '%s' not found", pFilename);
+		return aErrorMsg;
+	}
+
+	// get the crc of the map
+	if(m_pMap->Crc() != WantedCrc)
+	{
+		m_pMap->Unload();
+		str_format(aErrorMsg, sizeof(aErrorMsg), "map differs from the server. %08x != %08x", m_pMap->Crc(), WantedCrc);
+		return aErrorMsg;
+	}
+
+	// stop demo recording if we loaded a new map
+	m_DemoRecorder.Stop();
+
+	dbg_msg("client", "loaded map '%s'", pFilename);
+	m_RecivedSnapshots = 0;
+
+	str_copy(m_aCurrentMap, pName, sizeof(m_aCurrentMap));
+	m_CurrentMapCrc = m_pMap->Crc();
+
+	return 0x0;
+}
+
+
+
+const char *CClient::LoadMapSearch(const char *pMapName, int WantedCrc)
+{
+	const char *pError = 0;
+	char aBuf[512];
+	dbg_msg("client", "loading map, map=%s wanted crc=%08x", pMapName, WantedCrc);
+	SetState(IClient::STATE_LOADING);
+
+	// try the normal maps folder
+	str_format(aBuf, sizeof(aBuf), "maps/%s.map", pMapName);
+	pError = LoadMap(pMapName, aBuf, WantedCrc);
+	if(!pError)
+		return pError;
+
+	// try the downloaded maps
+	str_format(aBuf, sizeof(aBuf), "downloadedmaps/%s_%08x.map", pMapName, WantedCrc);
+	pError = LoadMap(pMapName, aBuf, WantedCrc);
+	return pError;
+}
+
+int CClient::PlayerScoreComp(const void *a, const void *b)
+{
+	CServerInfo::CPlayer *p0 = (CServerInfo::CPlayer *)a;
+	CServerInfo::CPlayer *p1 = (CServerInfo::CPlayer *)b;
+	if(p0->m_Score == p1->m_Score)
+		return 0;
+	if(p0->m_Score < p1->m_Score)
+		return 1;
+	return -1;
+}
+
+void CClient::ProcessPacket(CNetChunk *pPacket)
+{
+	if(pPacket->m_ClientID == -1)
+	{
+		// connectionlesss
+		if(pPacket->m_DataSize == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) &&
+			mem_comp(pPacket->m_pData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0)
+		{
+			unsigned char *pVersionData = (unsigned char*)pPacket->m_pData + sizeof(VERSIONSRV_VERSION);
+			int VersionMatch = !mem_comp(pVersionData, VERSION_DATA, sizeof(VERSION_DATA));
+
+			dbg_msg("client/version", "version does %s (%d.%d.%d)",
+				VersionMatch ? "match" : "NOT match",
+				pVersionData[1], pVersionData[2], pVersionData[3]);
+
+			// assume version is out of date when version-data doesn't match
+			if (!VersionMatch)
+			{
+				str_format(m_aVersionStr, sizeof(m_aVersionStr), "%d.%d.%d", pVersionData[1], pVersionData[2], pVersionData[3]);
+			}
+		}
+
+		if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_LIST) &&
+			mem_comp(pPacket->m_pData, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0)
+		{
+			int Size = pPacket->m_DataSize-sizeof(SERVERBROWSE_LIST);
+			int Num = Size/sizeof(MASTERSRV_ADDR);
+			MASTERSRV_ADDR *pAddrs = (MASTERSRV_ADDR *)((char*)pPacket->m_pData+sizeof(SERVERBROWSE_LIST));
+			int i;
+
+			for(i = 0; i < Num; i++)
+			{
+				NETADDR Addr;
+
+				// convert address
+				mem_zero(&Addr, sizeof(Addr));
+				Addr.type = NETTYPE_IPV4;
+				Addr.ip[0] = pAddrs[i].m_aIp[0];
+				Addr.ip[1] = pAddrs[i].m_aIp[1];
+				Addr.ip[2] = pAddrs[i].m_aIp[2];
+				Addr.ip[3] = pAddrs[i].m_aIp[3];
+				Addr.port = (pAddrs[i].m_aPort[1]<<8) | pAddrs[i].m_aPort[0];
+
+				m_ServerBrowser.Set(Addr, IServerBrowser::SET_MASTER_ADD, -1, 0x0);
+			}
+		}
+
+		{
+			int PacketType = 0;
+			if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_INFO) && mem_comp(pPacket->m_pData, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0)
+				PacketType = 2;
+
+			if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_OLD_INFO) && mem_comp(pPacket->m_pData, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)) == 0)
+				PacketType = 1;
+
+			if(PacketType)
+			{
+				// we got ze info
+				CUnpacker Up;
+				CServerInfo Info = {0};
+				int Token = -1;
+
+				Up.Reset((unsigned char*)pPacket->m_pData+sizeof(SERVERBROWSE_INFO), pPacket->m_DataSize-sizeof(SERVERBROWSE_INFO));
+				if(PacketType >= 2)
+					Token = str_toint(Up.GetString());
+				str_copy(Info.m_aVersion, Up.GetString(), sizeof(Info.m_aVersion));
+				str_copy(Info.m_aName, Up.GetString(), sizeof(Info.m_aName));
+				str_copy(Info.m_aMap, Up.GetString(), sizeof(Info.m_aMap));
+				str_copy(Info.m_aGameType, Up.GetString(), sizeof(Info.m_aGameType));
+				Info.m_Flags = str_toint(Up.GetString());
+				Info.m_Progression = str_toint(Up.GetString());
+				Info.m_NumPlayers = str_toint(Up.GetString());
+				Info.m_MaxPlayers = str_toint(Up.GetString());
+
+				// don't add invalid info to the server browser list
+				if(Info.m_NumPlayers > MAX_CLIENTS || Info.m_MaxPlayers > MAX_CLIENTS)
+					return;
+
+				str_format(Info.m_aAddress, sizeof(Info.m_aAddress), "%d.%d.%d.%d:%d",
+					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(int i = 0; i < Info.m_NumPlayers; i++)
+				{
+					str_copy(Info.m_aPlayers[i].m_aName, Up.GetString(), sizeof(Info.m_aPlayers[i].m_aName));
+					Info.m_aPlayers[i].m_Score = str_toint(Up.GetString());
+				}
+
+				if(!Up.Error())
+				{
+					// sort players
+					qsort(Info.m_aPlayers, Info.m_NumPlayers, sizeof(*Info.m_aPlayers), PlayerScoreComp);
+
+					if(net_addr_comp(&m_ServerAddress, &pPacket->m_Address) == 0)
+					{
+						mem_copy(&m_CurrentServerInfo, &Info, sizeof(m_CurrentServerInfo));
+						m_CurrentServerInfo.m_NetAddr = m_ServerAddress;
+						m_CurrentServerInfoRequestTime = -1;
+					}
+					else
+					{
+						if(PacketType == 2)
+							m_ServerBrowser.Set(pPacket->m_Address, IServerBrowser::SET_TOKEN, Token, &Info);
+						else
+							m_ServerBrowser.Set(pPacket->m_Address, IServerBrowser::SET_OLD_INTERNET, -1, &Info);
+					}
+				}
+			}
+		}
+	}
+	else
+	{
+		CUnpacker Unpacker;
+		Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize);
+
+		// unpack msgid and system flag
+		int Msg = Unpacker.GetInt();
+		int Sys = Msg&1;
+		Msg >>= 1;
+
+		if(Unpacker.Error())
+			return;
+
+		if(Sys)
+		{
+			// system message
+			if(Msg == NETMSG_MAP_CHANGE)
+			{
+				const char *pMap = Unpacker.GetString();
+				int MapCrc = Unpacker.GetInt();
+				const char *pError = 0;
+
+				if(Unpacker.Error())
+					return;
+
+				for(int i = 0; pMap[i]; i++) // protect the player from nasty map names
+				{
+					if(pMap[i] == '/' || pMap[i] == '\\')
+						pError = "strange character in map name";
+				}
+
+				if(pError)
+					DisconnectWithReason(pError);
+				else
+				{
+					pError = LoadMapSearch(pMap, MapCrc);
+
+					if(!pError)
+					{
+						dbg_msg("client/network", "loading done");
+						SendReady();
+						GameClient()->OnConnected();
+					}
+					else
+					{
+						str_format(m_aMapdownloadFilename, sizeof(m_aMapdownloadFilename), "downloadedmaps/%s_%08x.map", pMap, MapCrc);
+
+						dbg_msg("client/network", "starting to download map to '%s'", m_aMapdownloadFilename);
+
+						m_MapdownloadChunk = 0;
+						str_copy(m_aMapdownloadName, pMap, sizeof(m_aMapdownloadName));
+						m_MapdownloadFile = Storage()->OpenFile(m_aMapdownloadFilename, IOFLAG_WRITE);
+						m_MapdownloadCrc = MapCrc;
+						m_MapdownloadTotalsize = -1;
+						m_MapdownloadAmount = 0;
+
+						CMsgPacker Msg(NETMSG_REQUEST_MAP_DATA);
+						Msg.AddInt(m_MapdownloadChunk);
+						SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH);
+
+						if(g_Config.m_Debug)
+							dbg_msg("client/network", "requested chunk %d", m_MapdownloadChunk);
+					}
+				}
+			}
+			else if(Msg == NETMSG_MAP_DATA)
+			{
+				int Last = Unpacker.GetInt();
+				int TotalSize = Unpacker.GetInt();
+				int Size = Unpacker.GetInt();
+				const unsigned char *pData = Unpacker.GetRaw(Size);
+
+				// check fior errors
+				if(Unpacker.Error() || Size <= 0 || TotalSize <= 0 || !m_MapdownloadFile)
+					return;
+
+				io_write(m_MapdownloadFile, pData, Size);
+
+				m_MapdownloadTotalsize = TotalSize;
+				m_MapdownloadAmount += Size;
+
+				if(Last)
+				{
+					const char *pError;
+					dbg_msg("client/network", "download complete, loading map");
+
+					io_close(m_MapdownloadFile);
+					m_MapdownloadFile = 0;
+					m_MapdownloadAmount = 0;
+					m_MapdownloadTotalsize = -1;
+
+					// load map
+					pError = LoadMap(m_aMapdownloadName, m_aMapdownloadFilename, m_MapdownloadCrc);
+					if(!pError)
+					{
+						dbg_msg("client/network", "loading done");
+						SendReady();
+						GameClient()->OnConnected();
+					}
+					else
+						DisconnectWithReason(pError);
+				}
+				else
+				{
+					// request new chunk
+					m_MapdownloadChunk++;
+
+					CMsgPacker Msg(NETMSG_REQUEST_MAP_DATA);
+					Msg.AddInt(m_MapdownloadChunk);
+					SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH);
+
+					if(g_Config.m_Debug)
+						dbg_msg("client/network", "requested chunk %d", m_MapdownloadChunk);
+				}
+			}
+			else if(Msg == NETMSG_PING)
+			{
+				CMsgPacker Msg(NETMSG_PING_REPLY);
+				SendMsgEx(&Msg, 0);
+			}
+			else if(Msg == NETMSG_RCON_AUTH_STATUS)
+			{
+				int Result = Unpacker.GetInt();
+				if(Unpacker.Error() == 0)
+					m_RconAuthed = Result;
+			}
+			else if(Msg == NETMSG_RCON_LINE)
+			{
+				const char *pLine = Unpacker.GetString();
+				if(Unpacker.Error() == 0)
+				{
+					//dbg_msg("remote", "%s", line);
+					GameClient()->OnRconLine(pLine);
+				}
+			}
+			else if(Msg == NETMSG_PING_REPLY)
+				dbg_msg("client/network", "latency %.2f", (time_get() - m_PingStartTime)*1000 / (float)time_freq());
+			else if(Msg == NETMSG_INPUTTIMING)
+			{
+				int InputPredTick = Unpacker.GetInt();
+				int TimeLeft = Unpacker.GetInt();
+
+				// adjust our prediction time
+				int64 Target = 0;
+				for(int k = 0; k < 200; k++)
+				{
+					if(m_aInputs[k].m_Tick == InputPredTick)
+					{
+						Target = m_aInputs[k].m_PredictedTime + (time_get() - m_aInputs[k].m_Time);
+						Target = Target - (int64)(((TimeLeft-PREDICTION_MARGIN)/1000.0f)*time_freq());
+						//st_update(&predicted_time, );
+						break;
+					}
+				}
+
+				if(Target)
+					m_PredictedTime.Update(&m_InputtimeMarginGraph, Target, TimeLeft, 1);
+			}
+			else if(Msg == NETMSG_SNAP || Msg == NETMSG_SNAPSINGLE || Msg == NETMSG_SNAPEMPTY)
+			{
+				//dbg_msg("client/network", "got snapshot");
+				int NumParts = 1;
+				int Part = 0;
+				int GameTick = Unpacker.GetInt();
+				int DeltaTick = GameTick-Unpacker.GetInt();
+				int PartSize = 0;
+				int Crc = 0;
+				int CompleteSize = 0;
+				const char *pData = 0;
+
+				// we are not allowed to process snapshot yet
+				if(State() < IClient::STATE_LOADING)
+					return;
+
+				if(Msg == NETMSG_SNAP)
+				{
+					NumParts = Unpacker.GetInt();
+					Part = Unpacker.GetInt();
+				}
+
+				if(Msg != NETMSG_SNAPEMPTY)
+				{
+					Crc = Unpacker.GetInt();
+					PartSize = Unpacker.GetInt();
+				}
+
+				pData = (const char *)Unpacker.GetRaw(PartSize);
+
+				if(Unpacker.Error())
+					return;
+
+				if(GameTick >= m_CurrentRecvTick)
+				{
+					if(GameTick != m_CurrentRecvTick)
+					{
+						m_SnapshotParts = 0;
+						m_CurrentRecvTick = GameTick;
+					}
+
+					// TODO: clean this up abit
+					mem_copy((char*)m_aSnapshotIncommingData + Part*MAX_SNAPSHOT_PACKSIZE, pData, PartSize);
+					m_SnapshotParts |= 1<<Part;
+
+					if(m_SnapshotParts == (unsigned)((1<<NumParts)-1))
+					{
+						static CSnapshot Emptysnap;
+						CSnapshot *pDeltaShot = &Emptysnap;
+						int PurgeTick;
+						void *pDeltaData;
+						int DeltaSize;
+						unsigned char aTmpBuffer2[CSnapshot::MAX_SIZE];
+						unsigned char aTmpBuffer3[CSnapshot::MAX_SIZE];
+						int SnapSize;
+
+						CompleteSize = (NumParts-1) * MAX_SNAPSHOT_PACKSIZE + PartSize;
+
+						// reset snapshoting
+						m_SnapshotParts = 0;
+
+						// find snapshot that we should use as delta
+						Emptysnap.Clear();
+
+						// find delta
+						if(DeltaTick >= 0)
+						{
+							int DeltashotSize = m_SnapshotStorage.Get(DeltaTick, 0, &pDeltaShot, 0);
+
+							if(DeltashotSize < 0)
+							{
+								// couldn't find the delta snapshots that the server used
+								// to compress this snapshot. force the server to resync
+								if(g_Config.m_Debug)
+									dbg_msg("client", "error, couldn't find the delta snapshot");
+
+								// ack snapshot
+								// TODO: combine this with the input message
+								m_AckGameTick = -1;
+								return;
+							}
+						}
+
+						// decompress snapshot
+						pDeltaData = m_SnapshotDelta.EmptyDelta();
+						DeltaSize = sizeof(int)*3;
+
+						if(CompleteSize)
+						{
+							int IntSize = CVariableInt::Decompress(m_aSnapshotIncommingData, CompleteSize, aTmpBuffer2);
+
+							if(IntSize < 0) // failure during decompression, bail
+								return;
+
+							pDeltaData = aTmpBuffer2;
+							DeltaSize = IntSize;
+						}
+
+						// unpack delta
+						PurgeTick = DeltaTick;
+						SnapSize = m_SnapshotDelta.UnpackDelta(pDeltaShot, (CSnapshot*)aTmpBuffer3, pDeltaData, DeltaSize);
+						if(SnapSize < 0)
+						{
+							dbg_msg("client", "delta unpack failed!");
+							return;
+						}
+
+						if(Msg != NETMSG_SNAPEMPTY && ((CSnapshot*)aTmpBuffer3)->Crc() != Crc)
+						{
+							if(g_Config.m_Debug)
+							{
+								dbg_msg("client", "snapshot crc error #%d - tick=%d wantedcrc=%d gotcrc=%d compressed_size=%d delta_tick=%d",
+									m_SnapCrcErrors, GameTick, Crc, ((CSnapshot*)aTmpBuffer3)->Crc(), CompleteSize, DeltaTick);
+							}
+
+							m_SnapCrcErrors++;
+							if(m_SnapCrcErrors > 10)
+							{
+								// to many errors, send reset
+								m_AckGameTick = -1;
+								SendInput();
+								m_SnapCrcErrors = 0;
+							}
+							return;
+						}
+						else
+						{
+							if(m_SnapCrcErrors)
+								m_SnapCrcErrors--;
+						}
+
+						// purge old snapshots
+						PurgeTick = DeltaTick;
+						if(m_aSnapshots[SNAP_PREV] && m_aSnapshots[SNAP_PREV]->m_Tick < PurgeTick)
+							PurgeTick = m_aSnapshots[SNAP_PREV]->m_Tick;
+						if(m_aSnapshots[SNAP_CURRENT] && m_aSnapshots[SNAP_CURRENT]->m_Tick < PurgeTick)
+							PurgeTick = m_aSnapshots[SNAP_PREV]->m_Tick;
+						m_SnapshotStorage.PurgeUntil(PurgeTick);
+
+						// add new
+						m_SnapshotStorage.Add(GameTick, time_get(), SnapSize, (CSnapshot*)aTmpBuffer3, 1);
+
+						// add snapshot to demo
+						if(m_DemoRecorder.IsRecording())
+						{
+
+							// write tick marker
+							/*
+							DEMOREC_TICKMARKER marker;
+							marker.tick = game_tick;
+							swap_endian(&marker, sizeof(int), sizeof(marker)/sizeof(int));
+							demorec_record_write("TICK", sizeof(marker), &marker);
+							demorec_record_write("SNAP", snapsize, tmpbuffer3);
+							*/
+
+							// write snapshot
+							m_DemoRecorder.RecordSnapshot(GameTick, aTmpBuffer3, SnapSize);
+						}
+
+						// apply snapshot, cycle pointers
+						m_RecivedSnapshots++;
+
+						m_CurrentRecvTick = GameTick;
+
+						// we got two snapshots until we see us self as connected
+						if(m_RecivedSnapshots == 2)
+						{
+							// start at 200ms and work from there
+							m_PredictedTime.Init(GameTick*time_freq()/50);
+							m_PredictedTime.SetAdjustSpeed(1, 1000.0f);
+							m_GameTime.Init((GameTick-1)*time_freq()/50);
+							m_aSnapshots[SNAP_PREV] = m_SnapshotStorage.m_pFirst;
+							m_aSnapshots[SNAP_CURRENT] = m_SnapshotStorage.m_pLast;
+							m_LocalStartTime = time_get();
+							SetState(IClient::STATE_ONLINE);
+						}
+
+						// adjust game time
+						{
+							int64 Now = m_GameTime.Get(time_get());
+							int64 TickStart = GameTick*time_freq()/50;
+							int64 TimeLeft = (TickStart-Now)*1000 / time_freq();
+							//st_update(&game_time, (game_tick-1)*time_freq()/50);
+							m_GameTime.Update(&m_GametimeMarginGraph, (GameTick-1)*time_freq()/50, TimeLeft, 0);
+						}
+
+						// ack snapshot
+						m_AckGameTick = GameTick;
+					}
+				}
+			}
+		}
+		else
+		{
+			// game message
+			if(m_DemoRecorder.IsRecording())
+				m_DemoRecorder.RecordMessage(pPacket->m_pData, pPacket->m_DataSize);
+				// demorec_record_write("MESG", pPacket->data_size, );
+
+			GameClient()->OnMessage(Msg, &Unpacker);
+		}
+	}
+}
+
+void CClient::PumpNetwork()
+{
+	m_NetClient.Update();
+
+	if(State() != IClient::STATE_DEMOPLAYBACK)
+	{
+		// check for errors
+		if(State() != IClient::STATE_OFFLINE && m_NetClient.State() == NETSTATE_OFFLINE)
+		{
+			SetState(IClient::STATE_OFFLINE);
+			Disconnect();
+			dbg_msg("client", "offline error='%s'", m_NetClient.ErrorString());
+		}
+
+		//
+		if(State() == IClient::STATE_CONNECTING && m_NetClient.State() == NETSTATE_ONLINE)
+		{
+			// we switched to online
+			dbg_msg("client", "connected, sending info");
+			SetState(IClient::STATE_LOADING);
+			SendInfo();
+		}
+	}
+
+	// process packets
+	CNetChunk Packet;
+	while(m_NetClient.Recv(&Packet))
+		ProcessPacket(&Packet);
+}
+
+void CClient::OnDemoPlayerSnapshot(void *pData, int Size)
+{
+	// update ticks, they could have changed
+	const CDemoPlayer::CPlaybackInfo *pInfo = m_DemoPlayer.Info();
+	CSnapshotStorage::CHolder *pTemp;
+	m_CurGameTick = pInfo->m_Info.m_CurrentTick;
+	m_PrevGameTick = pInfo->m_PreviousTick;
+
+	// handle snapshots
+	pTemp = m_aSnapshots[SNAP_PREV];
+	m_aSnapshots[SNAP_PREV] = m_aSnapshots[SNAP_CURRENT];
+	m_aSnapshots[SNAP_CURRENT] = pTemp;
+
+	mem_copy(m_aSnapshots[SNAP_CURRENT]->m_pSnap, pData, Size);
+	mem_copy(m_aSnapshots[SNAP_CURRENT]->m_pAltSnap, pData, Size);
+
+	GameClient()->OnNewSnapshot();
+}
+
+void CClient::OnDemoPlayerMessage(void *pData, int Size)
+{
+	CUnpacker Unpacker;
+	Unpacker.Reset(pData, Size);
+
+	// unpack msgid and system flag
+	int Msg = Unpacker.GetInt();
+	int Sys = Msg&1;
+	Msg >>= 1;
+
+	if(Unpacker.Error())
+		return;
+
+	if(!Sys)
+		GameClient()->OnMessage(Msg, &Unpacker);
+}
+/*
+const IDemoPlayer::CInfo *client_demoplayer_getinfo()
+{
+	static DEMOPLAYBACK_INFO ret;
+	const DEMOREC_PLAYBACKINFO *info = m_DemoPlayer.Info();
+	ret.first_tick = info->first_tick;
+	ret.last_tick = info->last_tick;
+	ret.current_tick = info->current_tick;
+	ret.paused = info->paused;
+	ret.speed = info->speed;
+	return &ret;
+}*/
+
+/*
+void DemoPlayer()->SetPos(float percent)
+{
+	demorec_playback_set(percent);
+}
+
+void DemoPlayer()->SetSpeed(float speed)
+{
+	demorec_playback_setspeed(speed);
+}
+
+void DemoPlayer()->SetPause(int paused)
+{
+	if(paused)
+		demorec_playback_pause();
+	else
+		demorec_playback_unpause();
+}*/
+
+void CClient::Update()
+{
+	if(State() == IClient::STATE_DEMOPLAYBACK)
+	{
+		m_DemoPlayer.Update();
+		if(m_DemoPlayer.IsPlaying())
+		{
+			// update timers
+			const CDemoPlayer::CPlaybackInfo *pInfo = m_DemoPlayer.Info();
+			m_CurGameTick = pInfo->m_Info.m_CurrentTick;
+			m_PrevGameTick = pInfo->m_PreviousTick;
+			m_GameIntraTick = pInfo->m_IntraTick;
+			m_GameTickTime = pInfo->m_TickTime;
+		}
+		else
+		{
+			// disconnect on error
+			Disconnect();
+		}
+	}
+	else if(State() != IClient::STATE_OFFLINE && m_RecivedSnapshots >= 3)
+	{
+		// switch snapshot
+		int Repredict = 0;
+		int64 Freq = time_freq();
+		int64 Now = m_GameTime.Get(time_get());
+		int64 PredNow = m_PredictedTime.Get(time_get());
+
+		while(1)
+		{
+			CSnapshotStorage::CHolder *pCur = m_aSnapshots[SNAP_CURRENT];
+			int64 TickStart = (pCur->m_Tick)*time_freq()/50;
+
+			if(TickStart < Now)
+			{
+				CSnapshotStorage::CHolder *pNext = m_aSnapshots[SNAP_CURRENT]->m_pNext;
+				if(pNext)
+				{
+					m_aSnapshots[SNAP_PREV] = m_aSnapshots[SNAP_CURRENT];
+					m_aSnapshots[SNAP_CURRENT] = pNext;
+
+					// set ticks
+					m_CurGameTick = m_aSnapshots[SNAP_CURRENT]->m_Tick;
+					m_PrevGameTick = m_aSnapshots[SNAP_PREV]->m_Tick;
+
+					if(m_aSnapshots[SNAP_CURRENT] && m_aSnapshots[SNAP_PREV])
+					{
+						GameClient()->OnNewSnapshot();
+						Repredict = 1;
+					}
+				}
+				else
+					break;
+			}
+			else
+				break;
+		}
+
+		if(m_aSnapshots[SNAP_CURRENT] && m_aSnapshots[SNAP_PREV])
+		{
+			int64 CurtickStart = (m_aSnapshots[SNAP_CURRENT]->m_Tick)*time_freq()/50;
+			int64 PrevtickStart = (m_aSnapshots[SNAP_PREV]->m_Tick)*time_freq()/50;
+			//tg_add(&predicted_time_graph, pred_now, 0);
+			int PrevPredTick = (int)(PredNow*50/time_freq());
+			int NewPredTick = PrevPredTick+1;
+			static float LastPredintra = 0;
+
+			m_GameIntraTick = (Now - PrevtickStart) / (float)(CurtickStart-PrevtickStart);
+			m_GameTickTime = (Now - PrevtickStart) / (float)Freq; //(float)SERVER_TICK_SPEED);
+
+			CurtickStart = NewPredTick*time_freq()/50;
+			PrevtickStart = PrevPredTick*time_freq()/50;
+			m_PredIntraTick = (PredNow - PrevtickStart) / (float)(CurtickStart-PrevtickStart);
+
+			if(NewPredTick < m_aSnapshots[SNAP_PREV]->m_Tick-SERVER_TICK_SPEED || NewPredTick > m_aSnapshots[SNAP_PREV]->m_Tick+SERVER_TICK_SPEED)
+			{
+				dbg_msg("client", "prediction time reset!");
+				m_PredictedTime.Init(m_aSnapshots[SNAP_CURRENT]->m_Tick*time_freq()/50);
+			}
+
+			if(NewPredTick > m_PredTick)
+			{
+				LastPredintra = m_PredIntraTick;
+				m_PredTick = NewPredTick;
+				Repredict = 1;
+
+				// send input
+				SendInput();
+			}
+
+			LastPredintra = m_PredIntraTick;
+		}
+
+		// only do sane predictions
+		if(Repredict)
+		{
+			if(m_PredTick > m_CurGameTick && m_PredTick < m_CurGameTick+50)
+				GameClient()->OnPredict();
+		}
+
+		// fetch server info if we don't have it
+		if(State() >= IClient::STATE_LOADING &&
+			m_CurrentServerInfoRequestTime >= 0 &&
+			time_get() > m_CurrentServerInfoRequestTime)
+		{
+			m_ServerBrowser.Request(m_ServerAddress);
+			m_CurrentServerInfoRequestTime = time_get()+time_freq()*2;
+		}
+	}
+
+	// STRESS TEST: join the server again
+	if(g_Config.m_DbgStress)
+	{
+		static int64 ActionTaken = 0;
+		int64 Now = time_get();
+		if(State() == IClient::STATE_OFFLINE)
+		{
+			if(Now > ActionTaken+time_freq()*2)
+			{
+				dbg_msg("stress", "reconnecting!");
+				Connect(g_Config.m_DbgStressServer);
+				ActionTaken = Now;
+			}
+		}
+		else
+		{
+			/*if(now > action_taken+time_freq()*(10+config.dbg_stress))
+			{
+				dbg_msg("stress", "disconnecting!");
+				Disconnect();
+				action_taken = now;
+			}*/
+		}
+	}
+
+	// pump the network
+	PumpNetwork();
+
+	// update the maser server registry
+	MasterServer()->Update();
+
+	// update the server browser
+	m_ServerBrowser.Update();
+}
+
+const char *CClient::UserDirectory()
+{
+	static char saPath[1024] = {0};
+	fs_storage_path("Teeworlds", saPath, sizeof(saPath));
+	return saPath;
+}
+
+void CClient::VersionUpdate()
+{
+	if(m_VersionInfo.m_State == 0)
+	{
+		m_Engine.HostLookup(&m_VersionInfo.m_VersionServeraddr, g_Config.m_ClVersionServer);
+		m_VersionInfo.m_State++;
+	}
+	else if(m_VersionInfo.m_State == 1)
+	{
+		if(m_VersionInfo.m_VersionServeraddr.m_Job.Status() == CJob::STATE_DONE)
+		{
+			CNetChunk Packet;
+
+			mem_zero(&Packet, sizeof(Packet));
+
+			m_VersionInfo.m_VersionServeraddr.m_Addr.port = VERSIONSRV_PORT;
+
+			Packet.m_ClientID = -1;
+			Packet.m_Address = m_VersionInfo.m_VersionServeraddr.m_Addr;
+			Packet.m_pData = VERSIONSRV_GETVERSION;
+			Packet.m_DataSize = sizeof(VERSIONSRV_GETVERSION);
+			Packet.m_Flags = NETSENDFLAG_CONNLESS;
+
+			m_NetClient.Send(&Packet);
+			m_VersionInfo.m_State++;
+		}
+	}
+}
+
+void CClient::InitEngine(const char *pAppname)
+{
+	m_Engine.Init(pAppname);
+}
+
+void CClient::RegisterInterfaces()
+{
+	Kernel()->RegisterInterface(static_cast<IDemoPlayer*>(&m_DemoPlayer));
+	Kernel()->RegisterInterface(static_cast<IServerBrowser*>(&m_ServerBrowser));
+}
+
+void CClient::InitInterfaces()
+{
+	// fetch interfaces
+	m_pEditor = Kernel()->RequestInterface<IEditor>();
+	m_pGraphics = Kernel()->RequestInterface<IEngineGraphics>();
+	m_pSound = Kernel()->RequestInterface<IEngineSound>();
+	m_pGameClient = Kernel()->RequestInterface<IGameClient>();
+	m_pInput = Kernel()->RequestInterface<IEngineInput>();
+	m_pMap = Kernel()->RequestInterface<IEngineMap>();
+	m_pMasterServer = Kernel()->RequestInterface<IEngineMasterServer>();
+	m_pStorage = Kernel()->RequestInterface<IStorage>();
+
+	//
+	m_ServerBrowser.SetBaseInfo(&m_NetClient, m_pGameClient->NetVersion());
+}
+
+void CClient::Run()
+{
+	int64 ReportTime = time_get();
+	int64 ReportInterval = time_freq()*1;
+
+	m_LocalStartTime = time_get();
+	m_SnapshotParts = 0;
+
+	// init graphics
+	if(m_pGraphics->Init() != 0)
+		return;
+
+	// init font rendering
+	Kernel()->RequestInterface<IEngineTextRender>()->Init();
+
+	// init the input
+	Input()->Init();
+
+	// start refreshing addresses while we load
+	MasterServer()->RefreshAddresses();
+
+	// init the editor
+	m_pEditor->Init();
+
+	// init sound, allowed to fail
+	Sound()->Init();
+
+	// load data
+	if(!LoadData())
+		return;
+
+	GameClient()->OnInit();
+	dbg_msg("client", "version %s", GameClient()->NetVersion());
+
+	// open socket
+	{
+		NETADDR BindAddr;
+		mem_zero(&BindAddr, sizeof(BindAddr));
+		m_NetClient.Open(BindAddr, 0);
+	}
+
+	// connect to the server if wanted
+	/*
+	if(config.cl_connect[0] != 0)
+		Connect(config.cl_connect);
+	config.cl_connect[0] = 0;
+	*/
+
+	//
+	m_FpsGraph.Init(0.0f, 200.0f);
+
+	// never start with the editor
+	g_Config.m_ClEditor = 0;
+
+	Input()->MouseModeRelative();
+
+	while (1)
+	{
+		int64 FrameStartTime = time_get();
+		m_Frames++;
+
+		//
+		VersionUpdate();
+
+		// handle pending connects
+		if(m_aCmdConnect[0])
+		{
+			str_copy(g_Config.m_UiServerAddress, m_aCmdConnect, sizeof(g_Config.m_UiServerAddress));
+			Connect(m_aCmdConnect);
+			m_aCmdConnect[0] = 0;
+		}
+
+		// update input
+		Input()->Update();
+
+		// update sound
+		Sound()->Update();
+
+		// release focus
+		if(!m_pGraphics->WindowActive())
+		{
+			if(m_WindowMustRefocus == 0)
+				Input()->MouseModeAbsolute();
+			m_WindowMustRefocus = 1;
+		}
+		else if (g_Config.m_DbgFocus && Input()->KeyPressed(KEY_ESCAPE))
+		{
+			Input()->MouseModeAbsolute();
+			m_WindowMustRefocus = 1;
+		}
+
+		// refocus
+		if(m_WindowMustRefocus && m_pGraphics->WindowActive())
+		{
+			if(m_WindowMustRefocus < 3)
+			{
+				Input()->MouseModeAbsolute();
+				m_WindowMustRefocus++;
+			}
+
+			if(Input()->KeyPressed(KEY_MOUSE_1))
+			{
+				Input()->MouseModeRelative();
+				m_WindowMustRefocus = 0;
+			}
+		}
+
+		// panic quit button
+		if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyPressed('q'))
+			break;
+
+		if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyDown('d'))
+			g_Config.m_Debug ^= 1;
+
+		if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyDown('g'))
+			g_Config.m_DbgGraphs ^= 1;
+
+		if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyDown('e'))
+		{
+			g_Config.m_ClEditor = g_Config.m_ClEditor^1;
+			Input()->MouseModeRelative();
+		}
+
+		/*
+		if(!gfx_window_open())
+			break;
+		*/
+
+		// render
+		if(g_Config.m_ClEditor)
+		{
+			Update();
+			m_pEditor->UpdateAndRender();
+			m_pGraphics->Swap();
+		}
+		else
+		{
+			Update();
+
+			if(g_Config.m_DbgStress)
+			{
+				if((m_Frames%10) == 0)
+				{
+					Render();
+					m_pGraphics->Swap();
+				}
+			}
+			else
+			{
+				Render();
+				m_pGraphics->Swap();
+			}
+		}
+
+		// check conditions
+		if(State() == IClient::STATE_QUITING)
+			break;
+
+		// beNice
+		if(g_Config.m_DbgStress)
+			thread_sleep(5);
+		else if(g_Config.m_ClCpuThrottle || !m_pGraphics->WindowActive())
+			thread_sleep(1);
+
+		if(g_Config.m_DbgHitch)
+		{
+			thread_sleep(g_Config.m_DbgHitch);
+			g_Config.m_DbgHitch = 0;
+		}
+
+		if(ReportTime < time_get())
+		{
+			if(0 && g_Config.m_Debug)
+			{
+				dbg_msg("client/report", "fps=%.02f (%.02f %.02f) netstate=%d",
+					m_Frames/(float)(ReportInterval/time_freq()),
+					1.0f/m_FrameTimeHigh,
+					1.0f/m_FrameTimeLow,
+					m_NetClient.State());
+			}
+			m_FrameTimeLow = 1;
+			m_FrameTimeHigh = 0;
+			m_Frames = 0;
+			ReportTime += ReportInterval;
+		}
+
+		// update frametime
+		m_FrameTime = (time_get()-FrameStartTime)/(float)time_freq();
+		if(m_FrameTime < m_FrameTimeLow)
+			m_FrameTimeLow = m_FrameTime;
+		if(m_FrameTime > m_FrameTimeHigh)
+			m_FrameTimeHigh = m_FrameTime;
+
+		m_LocalTime = (time_get()-m_LocalStartTime)/(float)time_freq();
+
+		m_FpsGraph.Add(1.0f/m_FrameTime, 1,1,1);
+	}
+
+	GameClient()->OnShutdown();
+	Disconnect();
+
+	m_pGraphics->Shutdown();
+	m_pSound->Shutdown();
+}
+
+
+void CClient::Con_Connect(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+	str_copy(pSelf->m_aCmdConnect, pResult->GetString(0), sizeof(pSelf->m_aCmdConnect));
+}
+
+void CClient::Con_Disconnect(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+	pSelf->Disconnect();
+}
+
+void CClient::Con_Quit(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+	pSelf->Quit();
+}
+
+void CClient::Con_Minimize(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+	pSelf->Graphics()->Minimize();
+}
+
+void CClient::Con_Ping(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+
+	CMsgPacker Msg(NETMSG_PING);
+	pSelf->SendMsgEx(&Msg, 0);
+	pSelf->m_PingStartTime = time_get();
+}
+
+void CClient::Con_Screenshot(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+	pSelf->Graphics()->TakeScreenshot();
+}
+
+void CClient::Con_Rcon(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+	pSelf->Rcon(pResult->GetString(0));
+}
+
+void CClient::Con_RconAuth(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+	pSelf->RconAuth("", pResult->GetString(0));
+}
+
+void CClient::Con_AddFavorite(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+	NETADDR Addr;
+	if(net_addr_from_str(&Addr, pResult->GetString(0)) == 0)
+		pSelf->m_ServerBrowser.AddFavorite(Addr);
+}
+
+const char *CClient::DemoPlayer_Play(const char *pFilename)
+{
+	int Crc;
+	const char *pError;
+	Disconnect();
+	m_NetClient.ResetErrorString();
+
+	// try to start playback
+	m_DemoPlayer.SetListner(this);
+
+	if(m_DemoPlayer.Load(Storage(), pFilename))
+		return "error loading demo";
+
+	// load map
+	Crc = (m_DemoPlayer.Info()->m_Header.m_aCrc[0]<<24)|
+		(m_DemoPlayer.Info()->m_Header.m_aCrc[1]<<16)|
+		(m_DemoPlayer.Info()->m_Header.m_aCrc[2]<<8)|
+		(m_DemoPlayer.Info()->m_Header.m_aCrc[3]);
+	pError = LoadMapSearch(m_DemoPlayer.Info()->m_Header.m_aMap, Crc);
+	if(pError)
+	{
+		DisconnectWithReason(pError);
+		return pError;
+	}
+
+	GameClient()->OnConnected();
+
+	// setup buffers
+	mem_zero(m_aDemorecSnapshotData, sizeof(m_aDemorecSnapshotData));
+
+	m_aSnapshots[SNAP_CURRENT] = &m_aDemorecSnapshotHolders[SNAP_CURRENT];
+	m_aSnapshots[SNAP_PREV] = &m_aDemorecSnapshotHolders[SNAP_PREV];
+
+	m_aSnapshots[SNAP_CURRENT]->m_pSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_CURRENT][0];
+	m_aSnapshots[SNAP_CURRENT]->m_pAltSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_CURRENT][1];
+	m_aSnapshots[SNAP_CURRENT]->m_SnapSize = 0;
+	m_aSnapshots[SNAP_CURRENT]->m_Tick = -1;
+
+	m_aSnapshots[SNAP_PREV]->m_pSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_PREV][0];
+	m_aSnapshots[SNAP_PREV]->m_pAltSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_PREV][1];
+	m_aSnapshots[SNAP_PREV]->m_SnapSize = 0;
+	m_aSnapshots[SNAP_PREV]->m_Tick = -1;
+
+	// enter demo playback state
+	SetState(IClient::STATE_DEMOPLAYBACK);
+
+	m_DemoPlayer.Play();
+	GameClient()->OnEnterGame();
+
+	return 0;
+}
+
+void CClient::Con_Play(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+	pSelf->DemoPlayer_Play(pResult->GetString(0));
+}
+
+void CClient::Con_Record(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+	if(pSelf->State() != IClient::STATE_ONLINE)
+		dbg_msg("demorec/record", "client is not online");
+	else
+	{
+		char aFilename[512];
+		str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pResult->GetString(0));
+		pSelf->m_DemoRecorder.Start(pSelf->Storage(), aFilename, pSelf->GameClient()->NetVersion(), pSelf->m_aCurrentMap, pSelf->m_CurrentMapCrc, "client");
+	}
+}
+
+void CClient::Con_StopRecord(IConsole::IResult *pResult, void *pUserData)
+{
+	CClient *pSelf = (CClient *)pUserData;
+	pSelf->m_DemoRecorder.Stop();
+}
+
+void CClient::Con_ServerDummy(IConsole::IResult *pResult, void *pUserData)
+{
+	dbg_msg("client", "this command is not available on the client");
+}
+
+void CClient::RegisterCommands()
+{
+	m_pConsole = Kernel()->RequestInterface<IConsole>();
+	// register server dummy commands for tab completion
+	m_pConsole->Register("kick", "i", CFGFLAG_SERVER, Con_ServerDummy, this, "Kick player with specified id");
+	m_pConsole->Register("ban", "s?i", CFGFLAG_SERVER, Con_ServerDummy, this, "Ban player with ip/id for x minutes");
+	m_pConsole->Register("unban", "s", CFGFLAG_SERVER, Con_ServerDummy, this, "Unban ip");
+	m_pConsole->Register("bans", "", CFGFLAG_SERVER, Con_ServerDummy, this, "Show banlist");
+	m_pConsole->Register("status", "", CFGFLAG_SERVER, Con_ServerDummy, this, "List players");
+	m_pConsole->Register("shutdown", "", CFGFLAG_SERVER, Con_ServerDummy, this, "Shut down");
+	m_pConsole->Register("record", "s", CFGFLAG_SERVER, Con_ServerDummy, this, "Record to a file");
+	m_pConsole->Register("stoprecord", "", CFGFLAG_SERVER, Con_ServerDummy, this, "Stop recording");
+
+	m_pConsole->Register("quit", "", CFGFLAG_CLIENT, Con_Quit, this, "Quit Teeworlds");
+	m_pConsole->Register("exit", "", CFGFLAG_CLIENT, Con_Quit, this, "Quit Teeworlds");
+	m_pConsole->Register("minimize", "", CFGFLAG_CLIENT, Con_Minimize, this, "Minimize Teeworlds");
+	m_pConsole->Register("connect", "s", CFGFLAG_CLIENT, Con_Connect, this, "Connect to the specified host/ip");
+	m_pConsole->Register("disconnect", "", CFGFLAG_CLIENT, Con_Disconnect, this, "Disconnect from the server");
+	m_pConsole->Register("ping", "", CFGFLAG_CLIENT, Con_Ping, this, "Ping the current server");
+	m_pConsole->Register("screenshot", "", CFGFLAG_CLIENT, Con_Screenshot, this, "Take a screenshot");
+	m_pConsole->Register("rcon", "r", CFGFLAG_CLIENT, Con_Rcon, this, "Send specified command to rcon");
+	m_pConsole->Register("rcon_auth", "s", CFGFLAG_CLIENT, Con_RconAuth, this, "Authenticate to rcon");
+	m_pConsole->Register("play", "r", CFGFLAG_CLIENT, Con_Play, this, "Play the file specified");
+	m_pConsole->Register("record", "s", CFGFLAG_CLIENT, Con_Record, this, "Record to the file");
+	m_pConsole->Register("stoprecord", "", CFGFLAG_CLIENT, Con_StopRecord, this, "Stop recording");
+
+	m_pConsole->Register("add_favorite", "s", CFGFLAG_CLIENT, Con_AddFavorite, this, "Add a server as a favorite");
+}
+
+static CClient m_Client;
+
+/*
+	Server Time
+	Client Mirror Time
+	Client Predicted Time
+
+	Snapshot Latency
+		Downstream latency
+
+	Prediction Latency
+		Upstream latency
+*/
+
+#if defined(CONF_PLATFORM_MACOSX)
+int SDL_main(int argc, const char **argv) // ignore_convention
+#else
+int main(int argc, const char **argv) // ignore_convention
+#endif
+{
+	// init the engine
+	dbg_msg("client", "starting...");
+	m_Client.InitEngine("Teeworlds");
+
+	IKernel *pKernel = IKernel::Create();
+	pKernel->RegisterInterface(&m_Client);
+	m_Client.RegisterInterfaces();
+
+	// create the components
+	IConsole *pConsole = CreateConsole();
+	IStorage *pStorage = CreateStorage("Teeworlds", argv[0]); // ignore_convention
+	IConfig *pConfig = CreateConfig();
+	IEngineGraphics *pEngineGraphics = CreateEngineGraphics();
+	IEngineSound *pEngineSound = CreateEngineSound();
+	IEngineInput *pEngineInput = CreateEngineInput();
+	IEngineTextRender *pEngineTextRender = CreateEngineTextRender();
+	IEngineMap *pEngineMap = CreateEngineMap();
+	IEngineMasterServer *pEngineMasterServer = CreateEngineMasterServer();
+
+	{
+		bool RegisterFail = false;
+
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IConsole*>(pConsole));
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IConfig*>(pConfig));
+
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineGraphics*>(pEngineGraphics)); // register graphics as both
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IGraphics*>(pEngineGraphics));
+
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineSound*>(pEngineSound)); // register as both
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<ISound*>(pEngineSound));
+
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineInput*>(pEngineInput)); // register as both
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IInput*>(pEngineInput));
+
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineTextRender*>(pEngineTextRender)); // register as both
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<ITextRender*>(pEngineTextRender));
+
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMap*>(pEngineMap)); // register as both
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMap*>(pEngineMap));
+
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMasterServer*>(pEngineMasterServer)); // register as both
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMasterServer*>(pEngineMasterServer));
+
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateEditor());
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateGameClient());
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(pStorage);
+
+		if(RegisterFail)
+			return -1;
+	}
+
+	pConfig->Init();
+	pEngineMasterServer->Init(m_Client.Engine());
+	pEngineMasterServer->Load();
+
+	// register all console commands
+	m_Client.RegisterCommands();
+
+	pKernel->RequestInterface<IGameClient>()->OnConsoleInit();
+
+	// init client's interfaces
+	m_Client.InitInterfaces();
+
+	// execute autoexec file
+	pConsole->ExecuteFile("autoexec.cfg");
+
+	// parse the command line arguments
+	if(argc > 1) // ignore_convention
+		pConsole->ParseArguments(argc-1, &argv[1]); // ignore_convention
+
+	// execute config file
+	pConsole->ExecuteFile("settings.cfg");
+
+	// run the client
+	m_Client.Run();
+
+	// write down the config and quit
+	pConfig->Save();
+
+	return 0;
+}
diff --git a/src/engine/client/client.h b/src/engine/client/client.h
index 64ef6d9b..c7174f85 100644
--- a/src/engine/client/client.h
+++ b/src/engine/client/client.h
@@ -1,17 +1,291 @@
+#ifndef ENGINE_CLIENT_CLIENT_H
+#define ENGINE_CLIENT_CLIENT_H
 
 
-class IEngine
+#include <engine/console.h>
+#include <engine/editor.h>
+#include <engine/graphics.h>
+#include <engine/textrender.h>
+#include <engine/client.h>
+#include <engine/config.h>
+#include <engine/serverbrowser.h>
+#include <engine/sound.h>
+#include <engine/input.h>
+#include <engine/keys.h>
+#include <engine/map.h>
+#include <engine/masterserver.h>
+#include <engine/storage.h>
+
+#include <engine/shared/engine.h>
+#include <engine/shared/protocol.h>
+#include <engine/shared/demorec.h>
+#include <engine/shared/network.h>
+
+#include "srvbrowse.h"
+
+class CGraph
 {
 public:
-	virtual ~IEngine() {}
-	virtual class IGraphics *Graphics() = 0;
+	enum
+	{
+		// restrictions: Must be power of two
+		MAX_VALUES=128,
+	};
+
+	float m_Min, m_Max;
+	float m_aValues[MAX_VALUES];
+	float m_aColors[MAX_VALUES][3];
+	int m_Index;
+
+	void Init(float Min, float Max);
+
+	void ScaleMax();
+	void ScaleMin();
+
+	void Add(float v, float r, float g, float b);
+	void Render(IGraphics *pGraphics, int Font, float x, float y, float w, float h, const char *pDescription);
 };
 
 
-class IGameClient
+class CSmoothTime
 {
+	int64 m_Snap;
+	int64 m_Current;
+	int64 m_Target;
+
+	int64 m_RLast;
+	int64 m_TLast;
+	CGraph m_Graph;
+
+	int m_SpikeCounter;
+
+	float m_aAdjustSpeed[2]; // 0 = down, 1 = up
 public:
-	virtual ~IGameClient() {}
+	void Init(int64 Target);
+	void SetAdjustSpeed(int Direction, float Value);
+
+	int64 Get(int64 Now);
+
+	void UpdateInt(int64 Target);
+	void Update(CGraph *pGraph, int64 Target, int TimeLeft, int AdjustDirection);
 };
 
-extern IGameClient *CreateGameClient(IEngine *pEngine);
+
+class CClient : public IClient, public CDemoPlayer::IListner
+{
+	// needed interfaces
+	IEditor *m_pEditor;
+	IEngineInput *m_pInput;
+	IEngineGraphics *m_pGraphics;
+	IEngineSound *m_pSound;
+	IGameClient *m_pGameClient;
+	IEngineMap *m_pMap;
+	IConsole *m_pConsole;
+	IStorage *m_pStorage;
+	IEngineMasterServer *m_pMasterServer;
+
+	enum
+	{
+		NUM_SNAPSHOT_TYPES=2,
+		PREDICTION_MARGIN=1000/50/2, // magic network prediction value
+	};
+
+	CNetClient m_NetClient;
+	CDemoPlayer m_DemoPlayer;
+	CDemoRecorder m_DemoRecorder;
+	CEngine m_Engine;
+	CServerBrowser m_ServerBrowser;
+
+	char m_aServerAddressStr[256];
+
+	unsigned m_SnapshotParts;
+	int64 m_LocalStartTime;
+
+	int m_DebugFont;
+	float m_FrameTimeLow;
+	float m_FrameTimeHigh;
+	int m_Frames;
+	NETADDR m_ServerAddress;
+	int m_WindowMustRefocus;
+	int m_SnapCrcErrors;
+
+	int m_AckGameTick;
+	int m_CurrentRecvTick;
+	int m_RconAuthed;
+
+	// version-checking
+	char m_aVersionStr[10];
+
+	// pinging
+	int64 m_PingStartTime;
+
+	//
+	char m_aCurrentMap[256];
+	int m_CurrentMapCrc;
+
+	//
+	char m_aCmdConnect[256];
+
+	// map download
+	char m_aMapdownloadFilename[256];
+	char m_aMapdownloadName[256];
+	IOHANDLE m_MapdownloadFile;
+	int m_MapdownloadChunk;
+	int m_MapdownloadCrc;
+	int m_MapdownloadAmount;
+	int m_MapdownloadTotalsize;
+
+	// time
+	CSmoothTime m_GameTime;
+	CSmoothTime m_PredictedTime;
+
+	// input
+	struct // TODO: handle input better
+	{
+		int m_aData[MAX_INPUT_SIZE]; // the input data
+		int m_Tick; // the tick that the input is for
+		int64 m_PredictedTime; // prediction latency when we sent this input
+		int64 m_Time;
+	} m_aInputs[200];
+
+	int m_CurrentInput;
+
+	// graphs
+	CGraph m_InputtimeMarginGraph;
+	CGraph m_GametimeMarginGraph;
+	CGraph m_FpsGraph;
+
+	// the game snapshots are modifiable by the game
+	CSnapshotStorage m_SnapshotStorage;
+	CSnapshotStorage::CHolder *m_aSnapshots[NUM_SNAPSHOT_TYPES];
+
+	int m_RecivedSnapshots;
+	char m_aSnapshotIncommingData[CSnapshot::MAX_SIZE];
+
+	CSnapshotStorage::CHolder m_aDemorecSnapshotHolders[NUM_SNAPSHOT_TYPES];
+	char *m_aDemorecSnapshotData[NUM_SNAPSHOT_TYPES][2][CSnapshot::MAX_SIZE];
+
+	CSnapshotDelta m_SnapshotDelta;
+
+	//
+	CServerInfo m_CurrentServerInfo;
+	int64 m_CurrentServerInfoRequestTime; // >= 0 should request, == -1 got info
+
+	// version info
+	struct
+	{
+		int m_State;
+		CHostLookup m_VersionServeraddr;
+	} m_VersionInfo;
+public:
+	IEngineGraphics *Graphics() { return m_pGraphics; }
+	IEngineInput *Input() { return m_pInput; }
+	IEngineSound *Sound() { return m_pSound; }
+	IGameClient *GameClient() { return m_pGameClient; }
+	IEngineMasterServer *MasterServer() { return m_pMasterServer; }
+	IStorage *Storage() { return m_pStorage; }
+
+	CClient();
+
+	// ----- send functions -----
+	virtual int SendMsg(CMsgPacker *pMsg, int Flags);
+
+	int SendMsgEx(CMsgPacker *pMsg, int Flags, bool System=true);
+	void SendInfo();
+	void SendEnterGame();
+	void SendReady();
+
+	virtual bool RconAuthed();
+	void RconAuth(const char *pName, const char *pPassword);
+	virtual void Rcon(const char *pCmd);
+
+	virtual bool ConnectionProblems();
+
+	void DirectInput(int *pInput, int Size);
+	void SendInput();
+
+	// TODO: OPT: do this alot smarter!
+	virtual int *GetInput(int Tick);
+
+	const char *LatestVersion();
+	void VersionUpdate();
+
+	// ------ state handling -----
+	void SetState(int s);
+
+	// called when the map is loaded and we should init for a new round
+	void OnEnterGame();
+	virtual void EnterGame();
+
+	virtual void Connect(const char *pAddress);
+	void DisconnectWithReason(const char *pReason);
+	virtual void Disconnect();
+
+
+	virtual void GetServerInfo(CServerInfo *pServerInfo);
+	void ServerInfoRequest();
+
+	int LoadData();
+
+	// ---
+
+	void *SnapGetItem(int SnapId, int Index, CSnapItem *pItem);
+	void SnapInvalidateItem(int SnapId, int Index);
+	void *SnapFindItem(int SnapId, int Type, int Id);
+	int SnapNumItems(int SnapId);
+	void SnapSetStaticsize(int ItemType, int Size);
+
+	void Render();
+	void DebugRender();
+
+	virtual void Quit();
+
+	virtual const char *ErrorString();
+
+	const char *LoadMap(const char *pName, const char *pFilename, unsigned WantedCrc);
+	const char *LoadMapSearch(const char *pMapName, int WantedCrc);
+
+	static int PlayerScoreComp(const void *a, const void *b);
+
+	void ProcessPacket(CNetChunk *pPacket);
+
+	virtual int MapDownloadAmount() { return m_MapdownloadAmount; }
+	virtual int MapDownloadTotalsize() { return m_MapdownloadTotalsize; }
+
+	void PumpNetwork();
+
+	virtual void OnDemoPlayerSnapshot(void *pData, int Size);
+	virtual void OnDemoPlayerMessage(void *pData, int Size);
+
+	void Update();
+
+	virtual const char *UserDirectory();
+
+	void InitEngine(const char *pAppname);
+	void RegisterInterfaces();
+	void InitInterfaces();
+
+	void Run();
+
+
+	static void Con_Connect(IConsole::IResult *pResult, void *pUserData);
+	static void Con_Disconnect(IConsole::IResult *pResult, void *pUserData);
+	static void Con_Quit(IConsole::IResult *pResult, void *pUserData);
+	static void Con_Minimize(IConsole::IResult *pResult, void *pUserData);
+	static void Con_Ping(IConsole::IResult *pResult, void *pUserData);
+	static void Con_Screenshot(IConsole::IResult *pResult, void *pUserData);
+	static void Con_Rcon(IConsole::IResult *pResult, void *pUserData);
+	static void Con_RconAuth(IConsole::IResult *pResult, void *pUserData);
+	static void Con_AddFavorite(IConsole::IResult *pResult, void *pUserData);
+	static void Con_Play(IConsole::IResult *pResult, void *pUserData);
+	static void Con_Record(IConsole::IResult *pResult, void *pUserData);
+	static void Con_StopRecord(IConsole::IResult *pResult, void *pUserData);
+	static void Con_ServerDummy(IConsole::IResult *pResult, void *pUserData);
+
+	void RegisterCommands();
+
+	const char *DemoPlayer_Play(const char *pFilename);
+
+	virtual class CEngine *Engine() { return &m_Engine; }
+};
+#endif
diff --git a/src/engine/client/ec_client.cpp b/src/engine/client/ec_client.cpp
deleted file mode 100644
index 7ba0a2bb..00000000
--- a/src/engine/client/ec_client.cpp
+++ /dev/null
@@ -1,2087 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <math.h>
-
-#include <base/system.h>
-#include <engine/e_engine.h>
-#include <engine/e_client_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_memheap.h>
-#include <engine/e_datafile.h>
-#include <engine/e_console.h>
-#include <engine/e_ringbuffer.h>
-
-#include <engine/e_huffman.h>
-
-#include <engine/e_demorec.h>
-
-#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 */
-
-/*
-	Server Time
-	Client Mirror Time
-	Client Predicted Time
-	
-	Snapshot Latency
-		Downstream latency
-	
-	Prediction Latency
-		Upstream latency
-*/
-
-/* network client, must be accessible from other parts like the server browser */
-CNetClient m_NetClient;
-
-/* TODO: ugly, fix me */
-extern void client_serverbrowse_set(NETADDR *addr, int request, int token, SERVER_INFO *info);
-extern void client_serverbrowse_save();
-
-static unsigned snapshot_parts;
-static int64 local_start_time;
-
-static int debug_font;
-static float frametime = 0.0001f;
-static float frametime_low = 1.0f;
-static float frametime_high = 0.0f;
-static int frames = 0;
-static NETADDR server_address;
-static int window_must_refocus = 0;
-static int snapcrcerrors = 0;
-
-static int ack_game_tick = -1;
-static int current_recv_tick = 0;
-static int rcon_authed = 0;
-
-/* version-checking */
-static char versionstr[10] = "0";
-
-/* pinging */
-static int64 ping_start_time = 0;
-
-/* */
-static char current_map[256] = {0};
-static int current_map_crc = 0;
-
-/* */
-static char cmd_connect[256] = {0};
-
-/* map download */
-static char mapdownload_filename[256] = {0};
-static char mapdownload_name[256] = {0};
-static IOHANDLE mapdownload_file = 0;
-static int mapdownload_chunk = 0;
-static int mapdownload_crc = 0;
-static int mapdownload_amount = -1;
-static int mapdownload_totalsize = -1;
-
-/* */
-static SERVER_INFO current_server_info = {0};
-static int64 current_server_info_requesttime = -1; /* >= 0 should request, == -1 got info */
-
-/* current time */
-static int current_tick = 0;
-static float intratick = 0;
-static float ticktime = 0;
-static int prev_tick = 0;
-
-/* */
-/*static int predictiontime_pingspikecounter = 0;
-static int gametime_pingspikecounter = 0;*/
-
-/* predicted time */
-static int current_predtick = 0;
-static float predintratick = 0;
-static int last_input_timeleft = 0;
-
-static struct /* TODO: handle input better */
-{
-	int data[MAX_INPUT_SIZE]; /* the input data */
-	int tick; /* the tick that the input is for */
-	int64 predicted_time; /* prediction latency when we sent this input */
-	int64 time;
-} inputs[200];
-static int current_input = 0;
-
-enum
-{
-	GRAPH_MAX=128
-};
-
-typedef struct
-{
-	float min, max;
-	float values[GRAPH_MAX];
-	float colors[GRAPH_MAX][3];
-	int index;
-} GRAPH;
-
-static void graph_init(GRAPH *g, float min, float max)
-{
-	g->min = min;
-	g->max = max;
-	g->index = 0;
-}
-
-static void graph_scale_max(GRAPH *g)
-{
-	int i = 0;
-	g->max = 0;
-	for(i = 0; i < GRAPH_MAX; i++)
-	{
-		if(g->values[i] > g->max)
-			g->max = g->values[i];
-	}
-}
-
-static void graph_scale_min(GRAPH *g)
-{
-	int i = 0;
-	g->min = g->max;
-	for(i = 0; i < GRAPH_MAX; i++)
-	{
-		if(g->values[i] < g->min)
-			g->min = g->values[i];
-	}
-}
-
-static void graph_add(GRAPH *graph, float v, float r, float g, float b)
-{
-	graph->index = (graph->index+1)&(GRAPH_MAX-1);
-	graph->values[graph->index] = v;
-	graph->colors[graph->index][0] = r;
-	graph->colors[graph->index][1] = g;
-	graph->colors[graph->index][2] = b;
-}
-
-static void graph_render(GRAPH *g, float x, float y, float w, float h, const char *description)
-{
-	char buf[32];
-	int i;
-
-	//m_pGraphics->BlendNormal();
-
-	
-	Graphics()->TextureSet(-1);
-	
-	m_pGraphics->QuadsBegin();
-	Graphics()->SetColor(0, 0, 0, 0.75f);
-	Graphics()->QuadsDrawTL(x, y, w, h);
-	m_pGraphics->QuadsEnd();
-		
-	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;
-		float a1 = i/(float)GRAPH_MAX;
-		int i0 = (g->index+i-1)&(GRAPH_MAX-1);
-		int i1 = (g->index+i)&(GRAPH_MAX-1);
-		
-		float v0 = (g->values[i0]-g->min) / (g->max-g->min);
-		float v1 = (g->values[i1]-g->min) / (g->max-g->min);
-		
-		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);
-
-	}
-	Graphics()->LinesEnd();
-	
-
-	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);
-	Graphics()->QuadsText(x+w-8*strlen(buf)-8, y+2, 16, 1,1,1,1, buf);
-	
-	str_format(buf, sizeof(buf), "%.2f", g->min);
-	Graphics()->QuadsText(x+w-8*strlen(buf)-8, y+h-16, 16, 1,1,1,1, buf);
-	
-}
-
-typedef struct
-{
-	int64 snap;
-	int64 current;
-	int64 target;
-	
-	int64 rlast;
-	int64 tlast;
-	GRAPH graph;
-	
-	int spikecounter;
-	
-	float adjustspeed[2]; /* 0 = down, 1 = up */
-} SMOOTHTIME;
-
-static void st_init(SMOOTHTIME *st, int64 target)
-{
-	st->snap = time_get();
-	st->current = target;
-	st->target = target;
-	st->adjustspeed[0] = 0.3f;
-	st->adjustspeed[1] = 0.3f;
-	graph_init(&st->graph, 0.0f, 0.5f);
-}
-
-static int64 st_get(SMOOTHTIME *st, int64 now)
-{
-	float adjust_speed, a;
-	int64 c = st->current + (now - st->snap);
-	int64 t = st->target + (now - st->snap);
-	int64 r;
-	
-	/* it's faster to adjust upward instead of downward */
-	/* we might need to adjust these abit */
-
-	adjust_speed = st->adjustspeed[0];
-	if(t > c)
-		adjust_speed = st->adjustspeed[1];
-	
-	a = ((now-st->snap)/(float)time_freq()) * adjust_speed;
-	if(a > 1.0f)
-		a = 1.0f;
-		
-	r = c + (int64)((t-c)*a);
-	
-	graph_add(&st->graph, a+0.5f,1,1,1);
-	
-	return r;
-}
-
-static void st_update_int(SMOOTHTIME *st, int64 target)
-{
-	int64 now = time_get();
-	st->current = st_get(st, now);
-	st->snap = now;
-	st->target = target;
-}
-
-static void st_update(SMOOTHTIME *st, GRAPH *graph, int64 target, int time_left, int adjust_direction)
-{
-	int update_timer = 1;
-	
-	if(time_left < 0)
-	{
-		int is_spike = 0;
-		if(time_left < -50)
-		{
-			is_spike = 1;
-			
-			st->spikecounter += 5;
-			if(st->spikecounter > 50)
-				st->spikecounter = 50;
-		}
-		
-		if(is_spike && st->spikecounter < 15)
-		{
-			/* ignore this ping spike */
-			update_timer = 0;
-			graph_add(graph, time_left, 1,1,0);
-		}
-		else
-		{
-			graph_add(graph, time_left, 1,0,0);
-			if(st->adjustspeed[adjust_direction] < 30.0f)
-				st->adjustspeed[adjust_direction] *= 2.0f;
-		}
-	}
-	else
-	{
-		if(st->spikecounter)
-			st->spikecounter--;
-			
-		graph_add(graph, time_left, 0,1,0);
-		
-		st->adjustspeed[adjust_direction] *= 0.95f;
-		if(st->adjustspeed[adjust_direction] < 2.0f)
-			st->adjustspeed[adjust_direction] = 2.0f;
-	}
-	
-	last_input_timeleft = time_left;
-	
-	if(update_timer)
-		st_update_int(st, target);
-}
-
-static SMOOTHTIME game_time;
-static SMOOTHTIME predicted_time;
-
-/* graphs */
-static GRAPH inputtime_margin_graph;
-static GRAPH gametime_margin_graph;
-static GRAPH fps_graph;
-
-/* -- snapshot handling --- */
-enum
-{
-	NUM_SNAPSHOT_TYPES=2
-};
-
-/* the game snapshots are modifiable by the game */
-CSnapshotStorage snapshot_storage;
-static CSnapshotStorage::CHolder *snapshots[NUM_SNAPSHOT_TYPES] = {0, 0};
-
-static int recived_snapshots = 0;
-static char snapshot_incomming_data[CSnapshot::MAX_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)
-{
-	CSnapshotItem *i;
-	dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid");
-	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)
-{
-	CSnapshotItem *i;
-	dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid");
-	i = snapshots[snapid]->m_pAltSnap->GetItem(index);
-	if(i)
-	{
-		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]->m_pSnap && (char *)i < (char *)snapshots[snapid]->m_pSnap + snapshots[snapid]->m_SnapSize)
-			dbg_msg("ASDFASDFASdf", "ASDFASDFASDF");
-		i->m_TypeAndID = -1;
-	}
-}
-
-void *snap_find_item(int snapid, int type, int id)
-{
-	/* TODO: linear search. should be fixed. */
-	int i;
-	
-	if(!snapshots[snapid])
-		return 0x0;
-	
-	for(i = 0; i < snapshots[snapid]->m_pSnap->m_NumItems; i++)
-	{
-		CSnapshotItem *itm = snapshots[snapid]->m_pAltSnap->GetItem(i);
-		if(itm->Type() == type && itm->ID() == id)
-			return (void *)itm->Data();
-	}
-	return 0x0;
-}
-
-int snap_num_items(int snapid)
-{
-	dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid");
-	if(!snapshots[snapid])
-		return 0;
-	return snapshots[snapid]->m_pSnap->m_NumItems;
-}
-
-/* ------ time functions ------ */
-float client_intratick() { return intratick; }
-float client_predintratick() { return predintratick; }
-float client_ticktime() { return ticktime; }
-int client_tick() { return current_tick; }
-int client_prevtick() { return prev_tick; }
-int client_predtick() { return current_predtick; }
-int client_tickspeed() { return SERVER_TICK_SPEED; }
-float client_frametime() { return frametime; }
-float client_localtime() { return (time_get()-local_start_time)/(float)(time_freq()); }
-
-/* ----- send functions ----- */
-int client_send_msg()
-{
-	const MSG_INFO *pInfo = msg_get_info();
-	CNetChunk Packet;
-	
-	if(!pInfo)
-		return -1;
-
-	if(client_state() == CLIENTSTATE_OFFLINE)
-		return 0;
-	
-	mem_zero(&Packet, sizeof(CNetChunk));
-	
-	Packet.m_ClientID = 0;
-	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;
-		
-	if(pInfo->flags&MSGFLAG_RECORD)
-	{
-		if(demorec_isrecording())
-			demorec_record_message(Packet.m_pData, Packet.m_DataSize);
-	}
-
-	if(!(pInfo->flags&MSGFLAG_NOSEND))
-		m_NetClient.Send(&Packet);
-	return 0;
-}
-
-static void client_send_info()
-{
-	msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL|MSGFLAG_FLUSH);
-	msg_pack_string(modc_net_version(), 128);
-	msg_pack_string(config.player_name, 128);
-	msg_pack_string(config.clan_name, 128);
-	msg_pack_string(config.password, 128);
-	msg_pack_end();
-	client_send_msg();
-}
-
-
-static void client_send_entergame()
-{
-	msg_pack_start_system(NETMSG_ENTERGAME, MSGFLAG_VITAL|MSGFLAG_FLUSH);
-	msg_pack_end();
-	client_send_msg();
-}
-
-static void client_send_ready()
-{
-	msg_pack_start_system(NETMSG_READY, MSGFLAG_VITAL|MSGFLAG_FLUSH);
-	msg_pack_end();
-	client_send_msg();
-}
-
-int client_rcon_authed()
-{
-	return rcon_authed;
-}
-
-void client_rcon_auth(const char *name, const char *password)
-{
-	msg_pack_start_system(NETMSG_RCON_AUTH, MSGFLAG_VITAL);
-	msg_pack_string(name, 32);
-	msg_pack_string(password, 32);
-	msg_pack_end();
-	client_send_msg();
-}
-
-void client_rcon(const char *cmd)
-{
-	msg_pack_start_system(NETMSG_RCON_CMD, MSGFLAG_VITAL);
-	msg_pack_string(cmd, 256);
-	msg_pack_end();
-	client_send_msg();
-}
-
-int client_connection_problems()
-{
-	return m_NetClient.GotProblems();
-}
-
-void client_direct_input(int *input, int size)
-{
-	int i;
-	msg_pack_start_system(NETMSG_INPUT, 0);
-	msg_pack_int(ack_game_tick);
-	msg_pack_int(current_predtick);
-	msg_pack_int(size);
-	
-	for(i = 0; i < size/4; i++)
-		msg_pack_int(input[i]);	
-		
-	msg_pack_end();
-	client_send_msg();
-}
-
-
-static void client_send_input()
-{
-	int64 now = time_get();	
-	int i, size;
-
-	if(current_predtick <= 0)
-		return;
-	 
-	/* fetch input */
-	size = modc_snap_input(inputs[current_input].data);
-	
-	if(!size)
-		return;
-	
-	/* pack input */
-	msg_pack_start_system(NETMSG_INPUT, MSGFLAG_FLUSH);
-	msg_pack_int(ack_game_tick);
-	msg_pack_int(current_predtick);
-	msg_pack_int(size);
-
-	inputs[current_input].tick = current_predtick;
-	inputs[current_input].predicted_time = st_get(&predicted_time, now);
-	inputs[current_input].time = now;
-
-	/* pack it */	
-	for(i = 0; i < size/4; i++)
-		msg_pack_int(inputs[current_input].data[i]);
-	
-	current_input++;
-	current_input%=200;
-	
-	msg_pack_end();
-	client_send_msg();
-}
-
-const char *client_latestversion()
-{
-	return versionstr;
-}
-
-/* TODO: OPT: do this alot smarter! */
-int *client_get_input(int tick)
-{
-	int i;
-	int best = -1;
-	for(i = 0; i < 200; i++)
-	{
-		if(inputs[i].tick <= tick && (best == -1 || inputs[best].tick < inputs[i].tick))
-			best = i;
-	}
-	
-	if(best != -1)
-		return (int *)inputs[best].data;
-	return 0;
-}
-
-/* ------ state handling ----- */
-static int state = CLIENTSTATE_OFFLINE;
-int client_state() { return state; }
-static void client_set_state(int s)
-{
-	int old = state;
-	if(config.debug)
-		dbg_msg("client", "state change. last=%d current=%d", state, s);
-	state = s;
-	if(old != s)
-		modc_statechange(state, old);
-}
-
-/* called when the map is loaded and we should init for a new round */
-static void client_on_enter_game()
-{
-	/* reset input */
-	int i;
-	for(i = 0; i < 200; i++)
-		inputs[i].tick = -1;
-	current_input = 0;
-
-	/* reset snapshots */
-	snapshots[SNAP_CURRENT] = 0;
-	snapshots[SNAP_PREV] = 0;
-	snapshot_storage.PurgeAll();
-	recived_snapshots = 0;
-	snapshot_parts = 0;
-	current_predtick = 0;
-	current_recv_tick = 0;
-}
-
-void client_entergame()
-{
-	if(state == CLIENTSTATE_DEMOPLAYBACK)
-		return;
-		
-	/* now we will wait for two snapshots */
-	/* to finish the connection */
-	client_send_entergame();
-	client_on_enter_game();
-}
-
-void client_connect(const char *server_address_str)
-{
-	char buf[512];
-	const char *port_str = 0;
-	int k;
-	int port = 8303;
-	
-	client_disconnect();
-
-	dbg_msg("client", "connecting to '%s'", server_address_str);
-
-	//client_serverinfo_request();
-	str_copy(buf, server_address_str, sizeof(buf));
-
-	for(k = 0; buf[k]; k++)
-	{
-		if(buf[k] == ':')
-		{
-			port_str = &(buf[k+1]);
-			buf[k] = 0;
-			break;
-		}
-	}
-	
-	if(port_str)
-		port = atoi(port_str);
-	
-	/* TODO: IPv6 support */
-	if(net_host_lookup(buf, &server_address, NETTYPE_IPV4) != 0)
-		dbg_msg("client", "could not find the address of %s, connecting to localhost", buf);
-	
-	rcon_authed = 0;
-	server_address.port = port;
-	m_NetClient.Connect(&server_address);
-	client_set_state(CLIENTSTATE_CONNECTING);
-	
-	graph_init(&inputtime_margin_graph, -150.0f, 150.0f);
-	graph_init(&gametime_margin_graph, -150.0f, 150.0f);
-}
-
-void client_disconnect_with_reason(const char *reason)
-{
-	/* stop demo playback */
-	demorec_playback_stop();
-	
-	/* */
-	rcon_authed = 0;
-	m_NetClient.Disconnect(reason);
-	client_set_state(CLIENTSTATE_OFFLINE);
-	map_unload();
-	
-	/* disable all downloads */
-	mapdownload_chunk = 0;
-	if(mapdownload_file)
-		io_close(mapdownload_file);
-	mapdownload_file = 0;
-	mapdownload_crc = 0;
-	mapdownload_totalsize = -1;
-	mapdownload_amount = 0;
-
-	/* clear the current server info */
-	mem_zero(&current_server_info, sizeof(current_server_info));
-	mem_zero(&server_address, sizeof(server_address));
-	
-	/* clear snapshots */
-	snapshots[SNAP_CURRENT] = 0;
-	snapshots[SNAP_PREV] = 0;
-	recived_snapshots = 0;
-}
-
-void client_disconnect()
-{
-	client_disconnect_with_reason(0);
-}
-
-
-void client_serverinfo(SERVER_INFO *serverinfo)
-{
-	mem_copy(serverinfo, &current_server_info, sizeof(current_server_info));
-}
-
-void client_serverinfo_request()
-{
-	mem_zero(&current_server_info, sizeof(current_server_info));
-	current_server_info_requesttime = 0;
-}
-
-static int client_load_data()
-{
-	debug_font = Graphics()->LoadTexture("debug_font.png", IMG_AUTO, TEXLOAD_NORESAMPLE);
-	return 1;
-}
-
-extern int snapshot_data_rate[0xffff];
-extern int snapshot_data_updates[0xffff];
-
-const char *modc_getitemname(int type);
-
-static void client_debug_render()
-{
-	static NETSTATS prev, current;
-	static int64 last_snap = 0;
-	static float frametime_avg = 0;
-	int64 now = time_get();
-	char buffer[512];
-	
-	if(!config.debug)
-		return;
-	
-	//m_pGraphics->BlendNormal();
-	Graphics()->TextureSet(debug_font);
-	Graphics()->MapScreen(0,0,Graphics()->ScreenWidth(),Graphics()->ScreenHeight());
-	
-	if(time_get()-last_snap > time_freq())
-	{
-		last_snap = time_get();
-		prev = current;
-		net_stats(&current);
-	}
-	
-	/*
-		eth = 14
-		ip = 20
-		udp = 8
-		total = 42
-	*/
-	frametime_avg = frametime_avg*0.9f + frametime*0.1f;
-	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, */ // TODO: Refactor: Reenable this
-		(int)(1.0f/frametime_avg));
-	Graphics()->QuadsText(2, 2, 16, 1,1,1,1, buffer);
-
-	
-	{
-		int send_packets = (current.sent_packets-prev.sent_packets);
-		int send_bytes = (current.sent_bytes-prev.sent_bytes);
-		int send_total = send_bytes + send_packets*42;
-		int recv_packets = (current.recv_packets-prev.recv_packets);
-		int recv_bytes = (current.recv_bytes-prev.recv_bytes);
-		int recv_total = recv_bytes + recv_packets*42;
-		
-		if(!send_packets) send_packets++;
-		if(!recv_packets) recv_packets++;
-		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);
-		Graphics()->QuadsText(2, 14, 16, 1,1,1,1, buffer);
-	}
-	
-	/* render rates */
-	{
-		int y = 0;
-		int i;
-		for(i = 0; i < 256; i++)
-		{
-			if(snapshot_data_rate[i])
-			{
-				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);
-				Graphics()->QuadsText(2, 100+y*12, 16, 1,1,1,1, buffer);
-				y++;
-			}
-		}
-	}
-
-	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]);
-	Graphics()->QuadsText(2, 70, 16, 1,1,1,1, buffer);
-	
-	/* render graphs */
-	if(config.dbg_graphs)
-	{
-		//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);
-		graph_render(&fps_graph, x, sp*5, w, h, "FPS");
-		graph_render(&inputtime_margin_graph, x, sp*5+h+sp, w, h, "Prediction Margin");
-		graph_render(&gametime_margin_graph, x, sp*5+h+sp+h+sp, w, h, "Gametime Margin");
-	}
-}
-
-void client_quit()
-{
-	client_set_state(CLIENTSTATE_QUITING);
-}
-
-const char *client_error_string()
-{
-	return m_NetClient.ErrorString();
-}
-
-static void client_render()
-{
-	if(config.gfx_clear)	
-		Graphics()->Clear(1,1,0);
-
-	modc_render();
-	client_debug_render();
-}
-
-static const char *client_load_map(const char *name, const char *filename, int wanted_crc)
-{
-	static char errormsg[128];
-	DATAFILE *df;
-	int crc;
-	
-	client_set_state(CLIENTSTATE_LOADING);
-	
-	df = datafile_load(filename);
-	if(!df)
-	{
-		str_format(errormsg, sizeof(errormsg), "map '%s' not found", filename);
-		return errormsg;
-	}
-	
-	/* get the crc of the map */
-	crc = datafile_crc(filename);
-	if(crc != wanted_crc)
-	{
-		datafile_unload(df);
-		str_format(errormsg, sizeof(errormsg), "map differs from the server. %08x != %08x", crc, wanted_crc);
-		return errormsg;
-	}
-	
-	// stop demo recording if we loaded a new map
-	demorec_record_stop();
-	
-	dbg_msg("client", "loaded map '%s'", filename);
-	recived_snapshots = 0;
-	map_set(df);
-	
-	str_copy(current_map, name, sizeof(current_map));
-	current_map_crc = crc;
-	
-	return NULL;
-}
-
-static const char *client_load_map_search(const char *mapname, int wanted_crc)
-{
-	const char *error = 0;
-	char buf[512];
-	dbg_msg("client", "loading map, map=%s wanted crc=%08x", mapname, wanted_crc);
-	client_set_state(CLIENTSTATE_LOADING);
-	
-	/* try the normal maps folder */
-	str_format(buf, sizeof(buf), "maps/%s.map", mapname);
-	error = client_load_map(mapname, buf, wanted_crc);
-	if(!error)
-		return error;
-
-	/* try the downloaded maps */
-	str_format(buf, sizeof(buf), "downloadedmaps/%s_%8x.map", mapname, wanted_crc);
-	error = client_load_map(mapname, buf, wanted_crc);
-	return error;
-}
-
-static int player_score_comp(const void *a, const void *b)
-{
-	SERVER_INFO_PLAYER *p0 = (SERVER_INFO_PLAYER *)a;
-	SERVER_INFO_PLAYER *p1 = (SERVER_INFO_PLAYER *)b;
-	if(p0->score == p1->score)
-		return 0;
-	if(p0->score < p1->score)
-		return 1;
-	return -1;
-}
-
-static void client_process_packet(CNetChunk *pPacket)
-{
-	if(pPacket->m_ClientID == -1)
-	{
-		/* connectionlesss */
-		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*)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)",
-				version_match ? "match" : "NOT match",
-				versiondata[1], versiondata[2], versiondata[3]);
-			
-			/* assume version is out of date when version-data doesn't match */
-			if (!version_match)
-			{
-				sprintf(versionstr, "%d.%d.%d", versiondata[1], versiondata[2], versiondata[3]);
-			}
-		}
-		
-		if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_LIST) &&
-			memcmp(pPacket->m_pData, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0)
-		{
-			int size = pPacket->m_DataSize-sizeof(SERVERBROWSE_LIST);
-			int num = size/sizeof(MASTERSRV_ADDR);
-			MASTERSRV_ADDR *addrs = (MASTERSRV_ADDR *)((char*)pPacket->m_pData+sizeof(SERVERBROWSE_LIST));
-			int i;
-
-			for(i = 0; i < num; i++)
-			{
-				NETADDR addr;
-				
-				/* convert address */
-				mem_zero(&addr, sizeof(addr));
-				addr.type = NETTYPE_IPV4;
-				addr.ip[0] = addrs[i].ip[0];
-				addr.ip[1] = addrs[i].ip[1];
-				addr.ip[2] = addrs[i].ip[2];
-				addr.ip[3] = addrs[i].ip[3];
-				addr.port = (addrs[i].port[1]<<8) | addrs[i].port[0];
-				
-				client_serverbrowse_set(&addr, BROWSESET_MASTER_ADD, -1, NULL);
-			}
-		}
-
-		{
-			int packet_type = 0;
-			if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_INFO) && memcmp(pPacket->m_pData, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0)
-				packet_type = 2;
-
-			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 */
-				CUnpacker up;
-				SERVER_INFO info = {0};
-				int i;
-				int token = -1;
-				
-				up.Reset((unsigned char*)pPacket->m_pData+sizeof(SERVERBROWSE_INFO), pPacket->m_DataSize-sizeof(SERVERBROWSE_INFO));
-				if(packet_type >= 2)
-					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",
-					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, up.GetString(), sizeof(info.players[i].name));
-					info.players[i].score = atol(up.GetString());
-				}
-				
-				if(!up.Error())
-				{
-					/* sort players */
-					qsort(info.players, info.num_players, sizeof(*info.players), player_score_comp);
-					
-					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;
-						current_server_info_requesttime = -1;
-					}
-					else
-					{
-						if(packet_type == 2)
-							client_serverbrowse_set(&pPacket->m_Address, BROWSESET_TOKEN, token, &info);
-						else
-							client_serverbrowse_set(&pPacket->m_Address, BROWSESET_OLD_INTERNET, -1, &info);
-					}
-				}
-			}
-		}
-	}
-	else
-	{
-		int sys;
-		int msg = msg_unpack_start(pPacket->m_pData, pPacket->m_DataSize, &sys);
-		
-		if(sys)
-		{
-			/* system message */
-			if(msg == NETMSG_MAP_CHANGE)
-			{
-				const char *map = msg_unpack_string();
-				int map_crc = msg_unpack_int();
-				const char *error = 0;
-				int i;
-
-				if(msg_unpack_error())
-					return;
-				
-				for(i = 0; map[i]; i++) /* protect the player from nasty map names */
-				{
-					if(map[i] == '/' || map[i] == '\\')
-						error = "strange character in map name";
-				}
-				
-				if(error)
-					client_disconnect_with_reason(error);
-				else
-				{
-					error = client_load_map_search(map, map_crc);
-
-					if(!error)
-					{
-						dbg_msg("client/network", "loading done");
-						client_send_ready();
-						modc_connected();
-					}
-					else
-					{
-						str_format(mapdownload_filename, sizeof(mapdownload_filename), "downloadedmaps/%s_%08x.map", map, map_crc);
-
-						dbg_msg("client/network", "starting to download map to '%s'", mapdownload_filename);
-						
-						mapdownload_chunk = 0;
-						str_copy(mapdownload_name, map, sizeof(mapdownload_name));
-						mapdownload_file = engine_openfile(mapdownload_filename, IOFLAG_WRITE);
-						mapdownload_crc = map_crc;
-						mapdownload_totalsize = -1;
-						mapdownload_amount = 0;
-						
-						msg_pack_start_system(NETMSG_REQUEST_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH);
-						msg_pack_int(mapdownload_chunk);
-						msg_pack_end();
-						client_send_msg();
-										
-						if(config.debug)
-							dbg_msg("client/network", "requested chunk %d", mapdownload_chunk);
-					}
-				}
-			}
-			else if(msg == NETMSG_MAP_DATA)
-			{
-				int last = msg_unpack_int();
-				int total_size = msg_unpack_int();
-				int size = msg_unpack_int();
-				const unsigned char *data = msg_unpack_raw(size);
-				
-				/* check fior errors */
-				if(msg_unpack_error() || size <= 0 || total_size <= 0 || !mapdownload_file)
-					return;
-				
-				io_write(mapdownload_file, data, size);
-				
-				mapdownload_totalsize = total_size;
-				mapdownload_amount += size;
-				
-				if(last)
-				{
-					const char *error;
-					dbg_msg("client/network", "download complete, loading map");
-					
-					io_close(mapdownload_file);
-					mapdownload_file = 0;
-					mapdownload_amount = 0;
-					mapdownload_totalsize = -1;
-					
-					/* load map */
-					error = client_load_map(mapdownload_name, mapdownload_filename, mapdownload_crc);
-					if(!error)
-					{
-						dbg_msg("client/network", "loading done");
-						client_send_ready();
-						modc_connected();
-					}
-					else
-						client_disconnect_with_reason(error);
-				}
-				else
-				{
-					/* request new chunk */
-					mapdownload_chunk++;
-					msg_pack_start_system(NETMSG_REQUEST_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH);
-					msg_pack_int(mapdownload_chunk);
-					msg_pack_end();
-					client_send_msg();
-
-					if(config.debug)
-						dbg_msg("client/network", "requested chunk %d", mapdownload_chunk);
-				}
-			}
-			else if(msg == NETMSG_PING)
-			{
-				msg_pack_start_system(NETMSG_PING_REPLY, 0);
-				msg_pack_end();
-				client_send_msg();
-			}
-			else if(msg == NETMSG_RCON_AUTH_STATUS)
-			{
-				int result = msg_unpack_int();
-				if(msg_unpack_error() == 0)
-					rcon_authed = result;
-			}
-			else if(msg == NETMSG_RCON_LINE)
-			{
-				const char *line = msg_unpack_string();
-				if(msg_unpack_error() == 0)
-				{
-					/*dbg_msg("remote", "%s", line);*/
-					modc_rcon_line(line);
-				}
-			}
-			else if(msg == NETMSG_PING_REPLY)
-				dbg_msg("client/network", "latency %.2f", (time_get() - ping_start_time)*1000 / (float)time_freq());
-			else if(msg == NETMSG_INPUTTIMING)
-			{
-				int input_predtick = msg_unpack_int();
-				int time_left = msg_unpack_int();
-				
-				/* adjust our prediction time */
-				int k;
-				int64 target = 0;
-				for(k = 0; k < 200; k++)
-				{
-					if(inputs[k].tick == input_predtick)
-					{
-						target = inputs[k].predicted_time + (time_get() - inputs[k].time);
-						target = target - (int64)(((time_left-prediction_margin)/1000.0f)*time_freq());
-						//st_update(&predicted_time, );
-						break;
-					}
-				}
-				
-				if(target)
-					st_update(&predicted_time, &inputtime_margin_graph, target, time_left, 1);
-			}
-			else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSINGLE || msg == NETMSG_SNAPEMPTY)
-			{
-				/*dbg_msg("client/network", "got snapshot"); */
-				int num_parts = 1;
-				int part = 0;
-				int game_tick = msg_unpack_int();
-				int delta_tick = game_tick-msg_unpack_int();
-				int part_size = 0;
-				int crc = 0;
-				int complete_size = 0;
-				const char *data = 0;
-				
-				/* we are not allowed to process snapshot yet */
-				if(client_state() < CLIENTSTATE_LOADING)
-					return;
-				
-				if(msg == NETMSG_SNAP)
-				{
-					num_parts = msg_unpack_int();
-					part = msg_unpack_int();
-				}
-				
-				if(msg != NETMSG_SNAPEMPTY)
-				{
-					crc = msg_unpack_int();
-					part_size = msg_unpack_int();
-				}
-				
-				data = (const char *)msg_unpack_raw(part_size);
-				
-				if(msg_unpack_error())
-					return;
-					
-				if(game_tick >= current_recv_tick)
-				{
-					if(game_tick != current_recv_tick)
-					{
-						snapshot_parts = 0;
-						current_recv_tick = game_tick;
-					}
-						
-					/* TODO: clean this up abit */
-					mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, data, part_size);
-					snapshot_parts |= 1<<part;
-				
-					if(snapshot_parts == (unsigned)((1<<num_parts)-1))
-					{
-						static CSnapshot emptysnap;
-						CSnapshot *deltashot = &emptysnap;
-						int purgetick;
-						void *deltadata;
-						int deltasize;
-						unsigned char tmpbuffer2[CSnapshot::MAX_SIZE];
-						unsigned char tmpbuffer3[CSnapshot::MAX_SIZE];
-						int snapsize;
-						
-						complete_size = (num_parts-1) * MAX_SNAPSHOT_PACKSIZE + part_size;
-
-						/* reset snapshoting */
-						snapshot_parts = 0;
-						
-						/* find snapshot that we should use as delta */
-						emptysnap.m_DataSize = 0;
-						emptysnap.m_NumItems = 0;
-						
-						/* find delta */
-						if(delta_tick >= 0)
-						{
-							int deltashot_size = snapshot_storage.Get(delta_tick, 0, &deltashot, 0);
-							
-							if(deltashot_size < 0)
-							{
-								/* couldn't find the delta snapshots that the server used */
-								/* to compress this snapshot. force the server to resync */
-								if(config.debug)
-									dbg_msg("client", "error, couldn't find the delta snapshot");
-								
-								/* ack snapshot */
-								/* TODO: combine this with the input message */
-								ack_game_tick = -1;
-								return;
-							}
-						}
-
-						/* decompress snapshot */
-						deltadata = CSnapshot::EmptyDelta();
-						deltasize = sizeof(int)*3;
-
-						if(complete_size)
-						{	
-							int intsize = intpack_decompress(snapshot_incomming_data, complete_size, tmpbuffer2);
-
-							if(intsize < 0) /* failure during decompression, bail */
-								return;
-
-							deltadata = tmpbuffer2;
-							deltasize = intsize;
-						}
-						
-						/* unpack delta */
-						purgetick = delta_tick;
-						snapsize = CSnapshot::UnpackDelta(deltashot, (CSnapshot*)tmpbuffer3, deltadata, deltasize);
-						if(snapsize < 0)
-						{
-							dbg_msg("client", "delta unpack failed!");
-							return;
-						}
-						
-						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, ((CSnapshot*)tmpbuffer3)->Crc(), complete_size, delta_tick);
-							}
-								
-							snapcrcerrors++;
-							if(snapcrcerrors > 10)
-							{
-								/* to many errors, send reset */
-								ack_game_tick = -1;
-								client_send_input();
-								snapcrcerrors = 0;
-							}
-							return;
-						}
-						else
-						{
-							if(snapcrcerrors)
-								snapcrcerrors--;
-						}
-
-						/* purge old snapshots */
-						purgetick = delta_tick;
-						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 */
-						snapshot_storage.Add(game_tick, time_get(), snapsize, (CSnapshot*)tmpbuffer3, 1);
-
-						/* add snapshot to demo */
-						if(demorec_isrecording())
-						{
-
-							/* write tick marker */
-							/*
-							DEMOREC_TICKMARKER marker;
-							marker.tick = game_tick;
-							swap_endian(&marker, sizeof(int), sizeof(marker)/sizeof(int));
-							demorec_record_write("TICK", sizeof(marker), &marker);
-							demorec_record_write("SNAP", snapsize, tmpbuffer3);
-							*/
-							
-							/* write snapshot */
-							demorec_record_snapshot(game_tick, tmpbuffer3, snapsize);
-						}
-						
-						/* apply snapshot, cycle pointers */
-						recived_snapshots++;
-
-						current_recv_tick = game_tick;
-						
-						/* we got two snapshots until we see us self as connected */
-						if(recived_snapshots == 2)
-						{
-							/* start at 200ms and work from there */
-							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.m_pFirst;
-							snapshots[SNAP_CURRENT] = snapshot_storage.m_pLast;
-							local_start_time = time_get();
-							client_set_state(CLIENTSTATE_ONLINE);
-						}
-
-						/* adjust game time */
-						{
-							int64 now = st_get(&game_time, time_get());
-							int64 tickstart = game_tick*time_freq()/50;
-							int64 time_left = (tickstart-now)*1000 / time_freq();
-							/*st_update(&game_time, (game_tick-1)*time_freq()/50);*/
-							st_update(&game_time, &gametime_margin_graph, (game_tick-1)*time_freq()/50, time_left, 0);
-						}
-						
-						/* ack snapshot */
-						ack_game_tick = game_tick;
-					}
-				}
-			}
-		}
-		else
-		{
-			/* game message */
-			if(demorec_isrecording())
-				demorec_record_message(pPacket->m_pData, pPacket->m_DataSize);
-				/* demorec_record_write("MESG", pPacket->data_size, ); */
-
-			modc_message(msg);
-		}
-	}
-}
-
-int client_mapdownload_amount() { return mapdownload_amount; }
-int client_mapdownload_totalsize() { return mapdownload_totalsize; }
-
-static void client_pump_network()
-{
-
-	m_NetClient.Update();
-
-	if(client_state() != CLIENTSTATE_DEMOPLAYBACK)
-	{
-		/* check for errors */
-		if(client_state() != CLIENTSTATE_OFFLINE && m_NetClient.State() == NETSTATE_OFFLINE)
-		{
-			client_set_state(CLIENTSTATE_OFFLINE);
-			client_disconnect();
-			dbg_msg("client", "offline error='%s'", m_NetClient.ErrorString());
-		}
-
-		/* */
-		if(client_state() == CLIENTSTATE_CONNECTING && m_NetClient.State() == NETSTATE_ONLINE)
-		{
-			/* we switched to online */
-			dbg_msg("client", "connected, sending info");
-			client_set_state(CLIENTSTATE_LOADING);
-			client_send_info();
-		}
-	}
-	
-	/* process packets */
-	CNetChunk Packet;
-	while(m_NetClient.Recv(&Packet))
-		client_process_packet(&Packet);
-}
-
-static void client_democallback_snapshot(void *pData, int Size)
-{
-	/* update ticks, they could have changed */
-	const DEMOREC_PLAYBACKINFO *info = demorec_playback_info();			
-	CSnapshotStorage::CHolder *temp;
-	current_tick = info->current_tick;
-	prev_tick = info->previous_tick;
-	
-	/* handle snapshots */
-	temp = snapshots[SNAP_PREV];
-	snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
-	snapshots[SNAP_CURRENT] = temp;
-	
-	mem_copy(snapshots[SNAP_CURRENT]->m_pSnap, pData, Size);
-	mem_copy(snapshots[SNAP_CURRENT]->m_pAltSnap, pData, Size);
-	
-	modc_newsnapshot();
-	/*modc_predict();*/
-}
-
-static void client_democallback_message(void *data, int size)
-{
-	int sys = 0;
-	int msg = msg_unpack_start(data, size, &sys);
-	if(!sys)
-		modc_message(msg);
-}
-
-
-const DEMOPLAYBACK_INFO *client_demoplayer_getinfo()
-{
-	static DEMOPLAYBACK_INFO ret;
-	const DEMOREC_PLAYBACKINFO *info = demorec_playback_info();
-	ret.first_tick = info->first_tick;
-	ret.last_tick = info->last_tick;
-	ret.current_tick = info->current_tick;
-	ret.paused = info->paused;
-	ret.speed = info->speed;
-	return &ret;
-}
-
-void client_demoplayer_setpos(float percent)
-{
-	demorec_playback_set(percent);
-}
-
-void client_demoplayer_setspeed(float speed)
-{
-	demorec_playback_setspeed(speed);
-}
-
-void client_demoplayer_setpause(int paused)
-{
-	if(paused)
-		demorec_playback_pause();
-	else
-		demorec_playback_unpause();
-}
-
-static void client_update()
-{
-	if(client_state() == CLIENTSTATE_DEMOPLAYBACK)
-	{
-		demorec_playback_update();
-		if(demorec_isplaying())
-		{
-			/* update timers */
-			const DEMOREC_PLAYBACKINFO *info = demorec_playback_info();			
-			current_tick = info->current_tick;
-			prev_tick = info->previous_tick;
-			intratick = info->intratick;
-			ticktime = info->ticktime;
-		}
-		else
-		{
-			/* disconnect on error */
-			client_disconnect();
-		}
-	}
-	else if(client_state() != CLIENTSTATE_OFFLINE && recived_snapshots >= 3)
-	{
-		/* switch snapshot */
-		int repredict = 0;
-		int64 freq = time_freq();
-		int64 now = st_get(&game_time, time_get());
-		int64 pred_now = st_get(&predicted_time, time_get());
-
-		while(1)
-		{
-			CSnapshotStorage::CHolder *cur = snapshots[SNAP_CURRENT];
-			int64 tickstart = (cur->m_Tick)*time_freq()/50;
-
-			if(tickstart < now)
-			{
-				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]->m_Tick;
-					prev_tick = snapshots[SNAP_PREV]->m_Tick;
-					
-					if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
-					{
-						modc_newsnapshot();
-						repredict = 1;
-					}
-				}
-				else
-					break;
-			}
-			else
-				break;
-		}
-
-		if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
-		{
-			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;
-			static float last_predintra = 0;
-
-			intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start);
-			ticktime = (now - prevtick_start) / (float)freq; /*(float)SERVER_TICK_SPEED);*/
-
-			curtick_start = new_pred_tick*time_freq()/50;
-			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]->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]->m_Tick*time_freq()/50);
-			}
-			
-			if(new_pred_tick > current_predtick)
-			{
-				last_predintra = predintratick;
-				current_predtick = new_pred_tick;
-				repredict = 1;
-				
-				/* send input */
-				client_send_input();
-			}
-			
-			last_predintra = predintratick;
-		}
-
-		/* only do sane predictions */
-		if(repredict)
-		{
-			if(current_predtick > current_tick && current_predtick < current_tick+50)
-				modc_predict();
-		}
-		
-		/* fetch server info if we don't have it */
-		if(client_state() >= CLIENTSTATE_LOADING &&
-			current_server_info_requesttime >= 0 &&
-			time_get() > current_server_info_requesttime)
-		{
-			client_serverbrowse_request(&server_address);
-			current_server_info_requesttime = time_get()+time_freq()*2;
-		}
-	}
-
-	/* STRESS TEST: join the server again */
-	if(config.dbg_stress)
-	{
-		static int64 action_taken = 0;
-		int64 now = time_get();
-		if(client_state() == CLIENTSTATE_OFFLINE)
-		{
-			if(now > action_taken+time_freq()*2)
-			{
-				dbg_msg("stress", "reconnecting!");
-				client_connect(config.dbg_stress_server);
-				action_taken = now;
-			}
-		}
-		else
-		{
-			/*if(now > action_taken+time_freq()*(10+config.dbg_stress))
-			{
-				dbg_msg("stress", "disconnecting!");
-				client_disconnect();
-				action_taken = now;
-			}*/
-		}
-	}
-	
-	/* pump the network */
-	client_pump_network();
-	
-	/* update the maser server registry */
-	mastersrv_update();
-	
-	/* update the server browser */
-	client_serverbrowse_update();
-}
-
-
-static void client_versionupdate()
-{
-	static int state = 0;
-	static HOSTLOOKUP version_serveraddr;
-	
-	if(state == 0)
-	{
-		engine_hostlookup(&version_serveraddr, config.cl_version_server);
-		state++;
-	}
-	else if(state == 1)
-	{
-		if(jobs_status(&version_serveraddr.job) == JOBSTATUS_DONE)
-		{
-			CNetChunk Packet;
-			
-			mem_zero(&Packet, sizeof(Packet));
-			
-			version_serveraddr.addr.port = VERSIONSRV_PORT;
-			
-			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;
-			
-			m_NetClient.Send(&Packet);
-			state++;
-		}
-	}
-}
-
-static void client_run()
-{
-	NETADDR bindaddr;
-	int64 reporttime = time_get();
-	int64 reportinterval = time_freq()*1;
-
-	static PERFORMACE_INFO rootscope = {"root", 0};
-	perf_start(&rootscope);
-
-	local_start_time = time_get();
-	snapshot_parts = 0;
-	
-	/* init graphics and sound */
-	m_pGraphics = CreateEngineGraphics();
-	if(m_pGraphics->Init() != 0)
-		return;
-
-	/* start refreshing addresses while we load */
-	mastersrv_refresh_addresses();
-	
-	/* init the editor */
-	m_pEditor = CreateEditor();
-	m_pEditor->Init(m_pGraphics);
-
-	/* sound is allowed to fail */
-	snd_init();
-
-	/* load data */
-	if(!client_load_data())
-		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));
-	m_NetClient.Open(bindaddr, 0);
-	
-	/* connect to the server if wanted */
-	/*
-	if(config.cl_connect[0] != 0)
-		client_connect(config.cl_connect);
-	config.cl_connect[0] = 0;
-	*/
-
-	/* */
-	graph_init(&fps_graph, 0.0f, 200.0f);
-	
-	/* never start with the editor */
-	config.cl_editor = 0;
-		
-	inp_mouse_mode_relative();
-	
-	while (1)
-	{	
-		static PERFORMACE_INFO rootscope = {"root", 0};
-		int64 frame_start_time = time_get();
-		frames++;
-		
-		perf_start(&rootscope);
-
-		/* */
-		client_versionupdate();
-		
-		/* handle pending connects */
-		if(cmd_connect[0])
-		{
-			client_connect(cmd_connect);
-			cmd_connect[0] = 0;
-		}
-		
-		/* update input */
-		{
-			static PERFORMACE_INFO scope = {"inp_update", 0};
-			perf_start(&scope);
-			inp_update();
-			perf_end();
-		}
-
-		/* update sound */		
-		{
-			static PERFORMACE_INFO scope = {"snd_update", 0};
-			perf_start(&scope);
-			snd_update();
-			perf_end();
-		}
-		
-		/* release focus */
-		if(!Graphics()->WindowActive())
-		{
-			if(window_must_refocus == 0)
-				inp_mouse_mode_absolute();
-			window_must_refocus = 1;
-		}
-		else if (config.dbg_focus && inp_key_pressed(KEY_ESCAPE))
-		{
-			inp_mouse_mode_absolute();
-			window_must_refocus = 1;
-		}
-
-		/* refocus */
-		if(window_must_refocus && Graphics()->WindowActive())
-		{
-			if(window_must_refocus < 3)
-			{
-				inp_mouse_mode_absolute();
-				window_must_refocus++;
-			}
-
-			if(inp_key_pressed(KEY_MOUSE_1))
-			{
-				inp_mouse_mode_relative();
-				window_must_refocus = 0;
-			}
-		}
-
-		/* panic quit button */
-		if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_pressed('q'))
-			break;
-
-		if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('d'))
-			config.debug ^= 1;
-
-		if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('g'))
-			config.dbg_graphs ^= 1;
-
-		if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('e'))
-		{
-			config.cl_editor = config.cl_editor^1;
-			inp_mouse_mode_relative();
-		}
-		
-		/*
-		if(!gfx_window_open())
-			break;
-		*/
-			
-		/* render */
-		if(config.cl_editor)
-		{
-			client_update();
-			m_pEditor->UpdateAndRender();
-			m_pGraphics->Swap();
-		}
-		else
-		{
-			{
-				static PERFORMACE_INFO scope = {"client_update", 0};
-				perf_start(&scope);
-				client_update();
-				perf_end();
-			}
-			
-			if(config.dbg_stress)
-			{
-				if((frames%10) == 0)
-				{
-					client_render();
-					m_pGraphics->Swap();
-				}
-			}
-			else
-			{
-				{
-					static PERFORMACE_INFO scope = {"client_render", 0};
-					perf_start(&scope);
-					client_render();
-					perf_end();
-				}
-
-				{
-					static PERFORMACE_INFO scope = {"gfx_swap", 0};
-					perf_start(&scope);
-					m_pGraphics->Swap();
-					perf_end();
-				}
-			}
-		}
-
-		perf_end();
-
-		
-		/* check conditions */
-		if(client_state() == CLIENTSTATE_QUITING)
-			break;
-
-		/* be nice */
-		if(config.dbg_stress)
-			thread_sleep(5);
-		else if(config.cl_cpu_throttle || !Graphics()->WindowActive())
-			thread_sleep(1);
-			
-		if(config.dbg_hitch)
-		{
-			thread_sleep(config.dbg_hitch);
-			config.dbg_hitch = 0;
-		}
-		
-		if(reporttime < time_get())
-		{
-			if(0 && config.debug)
-			{
-				dbg_msg("client/report", "fps=%.02f (%.02f %.02f) netstate=%d",
-					frames/(float)(reportinterval/time_freq()),
-					1.0f/frametime_high,
-					1.0f/frametime_low,
-					m_NetClient.State());
-			}
-			frametime_low = 1;
-			frametime_high = 0;
-			frames = 0;
-			reporttime += reportinterval;
-			perf_next();
-			
-			if(config.dbg_pref)
-				perf_dump(&rootscope);
-		}
-		
-		/* update frametime */
-		frametime = (time_get()-frame_start_time)/(float)time_freq();
-		if(frametime < frametime_low)
-			frametime_low = frametime;
-		if(frametime > frametime_high)
-			frametime_high = frametime;
-		
-		graph_add(&fps_graph, 1.0f/frametime, 1,1,1);
-	}
-	
-	modc_shutdown();
-	client_disconnect();
-
-	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));
-}
-
-static void con_disconnect(void *result, void *user_data)
-{
-	client_disconnect();
-}
-
-static void con_quit(void *result, void *user_data)
-{
-	client_quit();
-}
-
-static void con_ping(void *result, void *user_data)
-{
-	msg_pack_start_system(NETMSG_PING, 0);
-	msg_pack_end();
-	client_send_msg();
-	ping_start_time = time_get();
-}
-
-static void con_screenshot(void *result, void *user_data)
-{
-	Graphics()->TakeScreenshot();
-}
-
-static void con_rcon(void *result, void *user_data)
-{
-	client_rcon(console_arg_string(result, 0));
-}
-
-static void con_rcon_auth(void *result, void *user_data)
-{
-	client_rcon_auth("", console_arg_string(result, 0));
-}
-
-static void con_addfavorite(void *result, void *user_data)
-{
-	NETADDR addr;
-	if(net_addr_from_str(&addr, console_arg_string(result, 0)) == 0)
-		client_serverbrowse_addfavorite(addr);
-}
-
-const char *client_demoplayer_play(const char *filename)
-{
-	int crc;
-	const char *error;
-	client_disconnect();
-	m_NetClient.ResetErrorString();
-	
-	/* try to start playback */
-	demorec_playback_registercallbacks(client_democallback_snapshot, client_democallback_message);
-	
-	if(demorec_playback_load(filename))
-		return "error loading demo";
-	
-	/* load map */
-	crc = (demorec_playback_info()->header.crc[0]<<24)|
-		(demorec_playback_info()->header.crc[1]<<16)|
-		(demorec_playback_info()->header.crc[2]<<8)|
-		(demorec_playback_info()->header.crc[3]);
-	error = client_load_map_search(demorec_playback_info()->header.map, crc);
-	if(error)
-	{
-		client_disconnect_with_reason(error);	
-		return error;
-	}
-	
-	modc_connected();
-	
-	/* setup buffers */	
-	mem_zero(demorec_snapshotdata, sizeof(demorec_snapshotdata));
-
-	snapshots[SNAP_CURRENT] = &demorec_snapshotholders[SNAP_CURRENT];
-	snapshots[SNAP_PREV] = &demorec_snapshotholders[SNAP_PREV];
-	
-	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]->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);
-	
-	demorec_playback_play();
-	modc_entergame();
-	
-	return 0;
-}
-
-static void con_play(void *result, void *user_data)
-{
-	client_demoplayer_play(console_arg_string(result, 0));
-}
-
-static void con_record(void *result, void *user_data)
-{
-	if(state != CLIENTSTATE_ONLINE)
-		dbg_msg("demorec/record", "client is not online");
-	else
-	{
-		char filename[512];
-		str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(result, 0));
-		demorec_record_start(filename, modc_net_version(), current_map, current_map_crc, "client");
-	}
-}
-
-static void con_stoprecord(void *result, void *user_data)
-{
-	demorec_record_stop();
-}
-
-static void con_serverdummy(void *result, void *user_data)
-{
-	dbg_msg("client", "this command is not available on the client");
-}
-
-static void client_register_commands()
-{
-	MACRO_REGISTER_COMMAND("quit", "", CFGFLAG_CLIENT, con_quit, 0x0, "Quit Teeworlds");
-	MACRO_REGISTER_COMMAND("exit", "", CFGFLAG_CLIENT, con_quit, 0x0, "Quit Teeworlds");
-	MACRO_REGISTER_COMMAND("connect", "s", CFGFLAG_CLIENT, con_connect, 0x0, "Connect to the specified host/ip");
-	MACRO_REGISTER_COMMAND("disconnect", "", CFGFLAG_CLIENT, con_disconnect, 0x0, "Disconnect from the server");
-	MACRO_REGISTER_COMMAND("ping", "", CFGFLAG_CLIENT, con_ping, 0x0, "Ping the current server");
-	MACRO_REGISTER_COMMAND("screenshot", "", CFGFLAG_CLIENT, con_screenshot, 0x0, "Take a screenshot");
-	MACRO_REGISTER_COMMAND("rcon", "r", CFGFLAG_CLIENT, con_rcon, 0x0, "Send specified command to rcon");
-	MACRO_REGISTER_COMMAND("rcon_auth", "s", CFGFLAG_CLIENT, con_rcon_auth, 0x0, "Authenticate to rcon");
-
-	MACRO_REGISTER_COMMAND("play", "r", CFGFLAG_CLIENT, con_play, 0x0, "Play the file specified");
-	MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_CLIENT, con_record, 0, "Record to the file");
-	MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_CLIENT, con_stoprecord, 0, "Stop recording");
-
-	MACRO_REGISTER_COMMAND("add_favorite", "s", CFGFLAG_CLIENT, con_addfavorite, 0x0, "Add a server as a favorite");
-	
-	/* register server dummy commands for tab completion */
-	MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, con_serverdummy, 0, "Kick player with specified id");
-	MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, con_serverdummy, 0, "Ban player with ip/id for x minutes");
-	MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, con_serverdummy, 0, "Unban ip");
-	MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, con_serverdummy, 0, "Show banlist");
-	MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, con_serverdummy, 0, "List players");
-	MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, con_serverdummy, 0, "Shut down");
-	/*MACRO_REGISTER_COMMAND("record", "", CFGFLAG_SERVER, con_serverdummy, 0);
-	MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, con_serverdummy, 0);*/
-}
-
-void client_save_line(const char *line)
-{
-	engine_config_write_line(line);	
-}
-
-const char *client_user_directory()
-{
-	static char path[1024] = {0};
-	fs_storage_path("Teeworlds", path, sizeof(path));
-	return path;
-}
-
-#if defined(CONF_PLATFORM_MACOSX)
-int SDL_main(int argc, char **argv)
-#else
-int main(int argc, char **argv)
-#endif
-{
-	/* init the engine */
-	dbg_msg("client", "starting...");
-	engine_init("Teeworlds");
-	
-	/* register all console commands */
-	client_register_commands();
-	modc_console_init();
-	
-	/* parse the command line arguments */
-	engine_parse_arguments(argc, argv);
-
-	/* execute config file */
-	console_execute_file("settings.cfg");
-	
-	/* run the client*/
-	client_run();
-	
-	/* write down the config and quit */
-	if(engine_config_write_start() == 0)
-	{
-		config_save();
-		client_serverbrowse_save();
-		modc_save_config();
-		engine_config_write_stop();
-	}
-	
-	return 0;
-}
diff --git a/src/engine/client/ec_gfx.cpp b/src/engine/client/ec_gfx.cpp
deleted file mode 100644
index 5632581a..00000000
--- a/src/engine/client/ec_gfx.cpp
+++ /dev/null
@@ -1,992 +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
-
-
-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.cpp b/src/engine/client/ec_gfx_text.cpp
deleted file mode 100644
index d17d1bed..00000000
--- a/src/engine/client/ec_gfx_text.cpp
+++ /dev/null
@@ -1,669 +0,0 @@
-#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>
-	#include <OpenGL/glu.h>
-#else
-	#include <GL/gl.h>
-	#include <GL/glu.h>
-#endif
-
-static int word_length(const char *text)
-{
-	int s = 1;
-	while(1)
-	{
-		if(*text == 0)
-			return s-1;
-		if(*text == '\n' || *text == '\t' || *text == ' ')
-			return s;
-		text++;
-		s++;
-	}
-}
-
-static float text_r=1;
-static float text_g=1;
-static float text_b=1;
-static float text_a=1;
-
-static struct FONT *default_font = 0;
-void gfx_text_set_default_font(struct FONT *font)
-{
-	default_font = font;
-}
-
-
-void gfx_text_set_cursor(TEXT_CURSOR *cursor, float x, float y, float font_size, int flags)
-{
-	mem_zero(cursor, sizeof(*cursor));
-	cursor->font_size = font_size;
-	cursor->start_x = x;
-	cursor->start_y = y;
-	cursor->x = x;
-	cursor->y = y;
-	cursor->line_count = 1;
-	cursor->line_width = -1;
-	cursor->flags = flags;
-	cursor->charcount = 0;
-}
-
-
-void gfx_text(void *font_set_v, float x, float y, float size, const char *text, int max_width)
-{
-	TEXT_CURSOR cursor;
-	gfx_text_set_cursor(&cursor, x, y, size, TEXTFLAG_RENDER);
-	cursor.line_width = max_width;
-	gfx_text_ex(&cursor, text, -1);
-}
-
-float gfx_text_width(void *font_set_v, float size, const char *text, int length)
-{
-	TEXT_CURSOR cursor;
-	gfx_text_set_cursor(&cursor, 0, 0, size, 0);
-	gfx_text_ex(&cursor, text, length);
-	return cursor.x;
-}
-
-void gfx_text_color(float r, float g, float b, float a)
-{
-	text_r = r;
-	text_g = g;
-	text_b = b;
-	text_a = a;
-}
-
-/* ft2 texture */
-#include <ft2build.h>
-#include FT_FREETYPE_H
-
-static FT_Library ft_library;
-
-#define MAX_CHARACTERS 64
-
-
-/* GL_LUMINANCE can be good for debugging*/
-static int font_texture_format = GL_ALPHA;
-
-
-static int font_sizes[] = {8,9,10,11,12,13,14,15,16,17,18,19,20,36};
-#define NUM_FONT_SIZES (sizeof(font_sizes)/sizeof(int))
-
-
-typedef struct FONTCHAR
-{
-	int id;
-	
-	/* these values are scaled to the font size */
-	/* width * font_size == real_size */
-	float width;
-	float height;
-	float offset_x;
-	float offset_y;
-	float advance_x;
-	
-	float uvs[4];
-	int64 touch_time;
-} FONTCHAR;
-
-typedef struct FONTSIZEDATA
-{
-	int font_size;
-	FT_Face *face;
-
-	unsigned textures[2];
-	int texture_width;
-	int texture_height;
-	
-	int num_x_chars;
-	int num_y_chars;
-	
-	int char_max_width;
-	int char_max_height;
-	
-	FONTCHAR characters[MAX_CHARACTERS*MAX_CHARACTERS];
-	
-	int current_character;	
-} FONTSIZEDATA;
-
-typedef struct FONT
-{
-	char filename[128];
-	FT_Face ft_face;
-	FONTSIZEDATA sizes[NUM_FONT_SIZES];
-} FONT;
-
-static int font_get_index(int pixelsize)
-{
-	for(unsigned i = 0; i < NUM_FONT_SIZES; i++)
-	{
-		if(font_sizes[i] >= pixelsize)
-			return i;
-	}
-	
-	return NUM_FONT_SIZES-1;
-}
-
-FONT *gfx_font_load(const char *filename)
-{
-	FONT *font = (FONT *)mem_alloc(sizeof(FONT), 1);
-	
-	mem_zero(font, sizeof(*font));
-	str_copy(font->filename, filename, sizeof(font->filename));
-	
-	if(FT_New_Face(ft_library, font->filename, 0, &font->ft_face))
-	{
-		mem_free(font);
-		return NULL;
-	}
-
-	for(unsigned i = 0; i < NUM_FONT_SIZES; i++)
-		font->sizes[i].font_size = -1;
-		
-	return font;
-};
-
-void gfx_font_destroy(FONT *font)
-{
-	mem_free(font);
-}
-
-void gfx_font_init()
-{
-	FT_Init_FreeType(&ft_library);
-}
-
-static void grow(unsigned char *in, unsigned char *out, int w, int h)
-{
-	int y, x;
-	for(y = 0; y < h; y++) 
-		for(x = 0; x < w; x++) 
-		{ 
-			int c = in[y*w+x]; 
-			int s_y, s_x;
-
-			for(s_y = -1; s_y <= 1; s_y++)
-				for(s_x = -1; s_x <= 1; s_x++)
-				{
-					int get_x = x+s_x;
-					int get_y = y+s_y;
-					if (get_x >= 0 && get_y >= 0 && get_x < w && get_y < h)
-					{
-						int index = get_y*w+get_x;
-						if(in[index] > c)
-							c = in[index]; 
-					}
-				}
-
-			out[y*w+x] = c;
-		}
-}
-
-static void font_init_texture(FONTSIZEDATA *sizedata, int charwidth, int charheight, int xchars, int ychars)
-{
-	static int font_memory_usage = 0;
-	int i;
-	int width = charwidth*xchars;
-	int height = charheight*ychars;
-	void *mem = mem_alloc(width*height, 1);
-	mem_zero(mem, width*height);
-	
-	if(sizedata->textures[0] == 0)
-		glGenTextures(2, sizedata->textures);
-	else
-		font_memory_usage -= sizedata->texture_width*sizedata->texture_height*2;
-	
-	sizedata->num_x_chars = xchars;
-	sizedata->num_y_chars = ychars;
-	sizedata->texture_width = width;
-	sizedata->texture_height = height;
-	sizedata->current_character = 0;
-	
-	for(i = 0; i < 2; i++)
-	{
-		glBindTexture(GL_TEXTURE_2D, sizedata->textures[i]);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-		glTexImage2D(GL_TEXTURE_2D, 0, font_texture_format, width, height, 0, font_texture_format, GL_UNSIGNED_BYTE, mem);
-		font_memory_usage += width*height;
-	}
-	
-	dbg_msg("", "font memory usage: %d", font_memory_usage);
-	
-	mem_free(mem);
-}
-
-static void font_increase_texture_size(FONTSIZEDATA *sizedata)
-{
-	if(sizedata->texture_width < sizedata->texture_height)
-		sizedata->num_x_chars <<= 1;
-	else
-		sizedata->num_y_chars <<= 1;
-	font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, sizedata->num_x_chars, sizedata->num_y_chars);		
-}
-
-static void font_init_index(FONT *font, int index)
-{
-	int outline_thickness = 1;
-	FONTSIZEDATA *sizedata = &font->sizes[index];
-	
-	sizedata->font_size = font_sizes[index];
-	FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size);
-	
-	if(sizedata->font_size >= 18)
-		outline_thickness = 2;
-		
-	{
-		unsigned glyph_index;
-		int charcode;
-		int max_h = 0;
-		int max_w = 0;
-		
-		charcode = FT_Get_First_Char(font->ft_face, &glyph_index);
-		while(glyph_index != 0)
-		{   
-			/* do stuff */
-			FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT);
-			
-			if(font->ft_face->glyph->metrics.width > max_w) max_w = font->ft_face->glyph->metrics.width;
-			if(font->ft_face->glyph->metrics.height > max_h) max_h = font->ft_face->glyph->metrics.height;
-			charcode = FT_Get_Next_Char(font->ft_face, charcode, &glyph_index);
-		}
-		
-		max_w = (max_w>>6)+2+outline_thickness*2;
-		max_h = (max_h>>6)+2+outline_thickness*2;
-		
-		for(sizedata->char_max_width = 1; sizedata->char_max_width < max_w; sizedata->char_max_width <<= 1);
-		for(sizedata->char_max_height = 1; sizedata->char_max_height < max_h; sizedata->char_max_height <<= 1);
-	}
-	
-	//dbg_msg("font", "init size %d, texture size %d %d", font->sizes[index].font_size, w, h);
-	//FT_New_Face(ft_library, "data/fonts/vera.ttf", 0, &font->ft_face);
-	font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, 8, 8);
-}
-
-static FONTSIZEDATA *font_get_size(FONT *font, int pixelsize)
-{
-	int index = font_get_index(pixelsize);
-	if(font->sizes[index].font_size != font_sizes[index])
-		font_init_index(font, index);
-	return &font->sizes[index];
-}
-
-
-static void font_upload_glyph(FONTSIZEDATA *sizedata, int texnum, int slot_id, int chr, const void *data)
-{
-	int x = (slot_id%sizedata->num_x_chars) * (sizedata->texture_width/sizedata->num_x_chars);
-	int y = (slot_id/sizedata->num_x_chars) * (sizedata->texture_height/sizedata->num_y_chars);
-	
-	glBindTexture(GL_TEXTURE_2D, sizedata->textures[texnum]);
-	glTexSubImage2D(GL_TEXTURE_2D, 0, x, y,
-		sizedata->texture_width/sizedata->num_x_chars,
-		sizedata->texture_height/sizedata->num_y_chars,
-		font_texture_format, GL_UNSIGNED_BYTE, data);
-}
-
-/* 8k of data used for rendering glyphs */
-static unsigned char glyphdata[(4096/64) * (4096/64)];
-static unsigned char glyphdata_outlined[(4096/64) * (4096/64)];
-
-static int font_get_slot(FONTSIZEDATA *sizedata)
-{
-	int char_count = sizedata->num_x_chars*sizedata->num_y_chars;
-	if(sizedata->current_character < char_count)
-	{
-		int i = sizedata->current_character;
-		sizedata->current_character++;
-		return i;
-	}
-
-	/* kick out the oldest */
-	/* TODO: remove this linear search */
-	{
-		int oldest = 0;
-		int i;
-		for(i = 1; i < char_count; i++)
-		{
-			if(sizedata->characters[i].touch_time < sizedata->characters[oldest].touch_time)
-				oldest = i;
-		}
-		
-		if(time_get()-sizedata->characters[oldest].touch_time < time_freq())
-		{
-			font_increase_texture_size(sizedata);
-			return font_get_slot(sizedata);
-		}
-		
-		return oldest;
-	}
-}
-
-static int font_render_glyph(FONT *font, FONTSIZEDATA *sizedata, int chr)
-{
-	FT_Bitmap *bitmap;
-	int slot_id = 0;
-	int slot_w = sizedata->texture_width / sizedata->num_x_chars;
-	int slot_h = sizedata->texture_height / sizedata->num_y_chars;
-	int slot_size = slot_w*slot_h;
-	int outline_thickness = 1;
-	int x = 1;
-	int y = 1;
-	int px, py;
-
-	FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size);
-
-	if(FT_Load_Char(font->ft_face, chr, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP))
-	{
-		dbg_msg("font", "error loading glyph %d", chr);
-		return -1;
-	}
-
-	bitmap = &font->ft_face->glyph->bitmap; 
-	
-	/* fetch slot */
-	slot_id = font_get_slot(sizedata);
-	if(slot_id < 0)
-		return -1;
-	
-	/* adjust spacing */
-	if(sizedata->font_size >= 18)
-		outline_thickness = 2;
-	x += outline_thickness;
-	y += outline_thickness;
-
-	/* prepare glyph data */
-	mem_zero(glyphdata, slot_size);
-
-	if(bitmap->pixel_mode == FT_PIXEL_MODE_GRAY)
-	{
-		for(py = 0; py < bitmap->rows; py++) 
-			for(px = 0; px < bitmap->width; px++) 
-				glyphdata[(py+y)*slot_w+px+x] = bitmap->buffer[py*bitmap->pitch+px];
-	}
-	else if(bitmap->pixel_mode == FT_PIXEL_MODE_MONO)
-	{
-		for(py = 0; py < bitmap->rows; py++) 
-			for(px = 0; px < bitmap->width; px++)
-			{
-				if(bitmap->buffer[py*bitmap->pitch+px/8]&(1<<(7-(px%8))))
-					glyphdata[(py+y)*slot_w+px+x] = 255;
-			}
-	}
-
-	if(0) for(py = 0; py < slot_w; py++) 
-		for(px = 0; px < slot_h; px++) 
-			glyphdata[py*slot_w+px] = 255;
-	
-	/* upload the glyph */
-	font_upload_glyph(sizedata, 0, slot_id, chr, glyphdata);
-	
-	if(outline_thickness == 1)
-	{
-		grow(glyphdata, glyphdata_outlined, slot_w, slot_h);
-		font_upload_glyph(sizedata, 1, slot_id, chr, glyphdata_outlined);
-	}
-	else
-	{
-		grow(glyphdata, glyphdata_outlined, slot_w, slot_h);
-		grow(glyphdata_outlined, glyphdata, slot_w, slot_h);
-		font_upload_glyph(sizedata, 1, slot_id, chr, glyphdata);
-	}
-	
-	/* set char info */
-	{
-		FONTCHAR *fontchr = &sizedata->characters[slot_id];
-		float scale = 1.0f/sizedata->font_size;
-		float uscale = 1.0f/sizedata->texture_width;
-		float vscale = 1.0f/sizedata->texture_height;
-		int height = bitmap->rows + outline_thickness*2 + 2;
-		int width = bitmap->width + outline_thickness*2 + 2;
-		
-		fontchr->id = chr;
-		fontchr->height = height * scale;
-		fontchr->width = width * scale;
-		fontchr->offset_x = (font->ft_face->glyph->bitmap_left-1) * scale;
-		fontchr->offset_y = (sizedata->font_size - font->ft_face->glyph->bitmap_top) * scale;
-		fontchr->advance_x = (font->ft_face->glyph->advance.x>>6) * scale;
-		
-		fontchr->uvs[0] = (slot_id%sizedata->num_x_chars) / (float)(sizedata->num_x_chars);
-		fontchr->uvs[1] = (slot_id/sizedata->num_x_chars) / (float)(sizedata->num_y_chars);
-		fontchr->uvs[2] = fontchr->uvs[0] + width*uscale;
-		fontchr->uvs[3] = fontchr->uvs[1] + height*vscale;
-	}
-	
-	return slot_id;
-}
-
-static FONTCHAR *font_get_char(FONT *font, FONTSIZEDATA *sizedata, int chr)
-{
-	FONTCHAR *fontchr = NULL;
-	
-	/* search for the character */
-	/* TODO: remove this linear search */
-	int i;
-	for(i = 0; i < sizedata->current_character; i++)
-	{
-		if(sizedata->characters[i].id == chr)
-		{
-			fontchr = &sizedata->characters[i];
-			break;
-		}
-	}
-	
-	/* check if we need to render the character */
-	if(!fontchr)
-	{
-		int index = font_render_glyph(font, sizedata, chr);
-		if(index >= 0)
-			fontchr = &sizedata->characters[index];
-	}
-	
-	/* touch the character */
-	/* TODO: don't call time_get here */
-	if(fontchr)
-		fontchr->touch_time = time_get();
-		
-	return fontchr;
-}
-
-/* must only be called from the rendering function as the font must be set to the correct size */
-static void font_render_setup(FONT *font, int size)
-{
-	FT_Set_Pixel_Sizes(font->ft_face, 0, size);
-}
-
-static float font_kerning(FONT *font, int left, int right)
-{
-	FT_Vector kerning = {0,0};
-	FT_Get_Kerning(font->ft_face, left, right, FT_KERNING_DEFAULT, &kerning);
-	return (kerning.x>>6);
-}
-
-
-void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
-{
-	FONT *font = cursor->font;
-	FONTSIZEDATA *sizedata = NULL;
-
-	float screen_x0, screen_y0, screen_x1, screen_y1;
-	float fake_to_screen_x, fake_to_screen_y;
-	int actual_x, actual_y;
-
-	int actual_size;
-	int i;
-	int got_new_line = 0;
-	float draw_x, draw_y;
-	float cursor_x, cursor_y;
-	const char *end;
-
-	float size = cursor->font_size;
-
-	/* to correct coords, convert to screen coords, round, and convert back */
-	Graphics()->GetScreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1);
-	
-	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;
-
-	cursor_x = actual_x / fake_to_screen_x;
-	cursor_y = actual_y / fake_to_screen_y;
-
-	/* same with size */
-	actual_size = size * fake_to_screen_y;
-	size = actual_size / fake_to_screen_y;
-
-	/* fetch font data */
-	if(!font)
-		font = default_font;
-	
-	if(!font)
-		return;
-
-	sizedata = font_get_size(font, actual_size);
-	font_render_setup(font, actual_size);
-	
-	/* set length */
-	if(length < 0)
-		length = strlen(text);
-		
-	end = text + length;
-
-	/* if we don't want to render, we can just skip the first outline pass */
-	i = 1;
-	if(cursor->flags&TEXTFLAG_RENDER)
-		i = 0;
-
-	for(;i < 2; i++)
-	{
-		const char *current = (char *)text;
-		const char *end = current+length;
-		draw_x = cursor_x;
-		draw_y = cursor_y;
-
-		if(cursor->flags&TEXTFLAG_RENDER)
-		{
-			// TODO: Make this better
-			glEnable(GL_TEXTURE_2D);
-			if (i == 0)
-				glBindTexture(GL_TEXTURE_2D, sizedata->textures[1]);
-			else
-				glBindTexture(GL_TEXTURE_2D, sizedata->textures[0]);
-
-			Graphics()->QuadsBegin();
-			if (i == 0)
-				Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.3f*text_a);
-			else
-				Graphics()->SetColor(text_r, text_g, text_b, text_a);
-		}
-
-		while(current < end)
-		{
-			int new_line = 0;
-			const char *batch_end = end;
-			if(cursor->line_width > 0 && !(cursor->flags&TEXTFLAG_STOP_AT_END))
-			{
-				int wlen = word_length((char *)current);
-				TEXT_CURSOR compare = *cursor;
-				compare.x = draw_x;
-				compare.y = draw_y;
-				compare.flags &= ~TEXTFLAG_RENDER;
-				compare.line_width = -1;
-				gfx_text_ex(&compare, text, wlen);
-				
-				if(compare.x-draw_x > cursor->line_width)
-				{
-					/* word can't be fitted in one line, cut it */
-					TEXT_CURSOR cutter = *cursor;
-					cutter.charcount = 0;
-					cutter.x = draw_x;
-					cutter.y = draw_y;
-					cutter.flags &= ~TEXTFLAG_RENDER;
-					cutter.flags |= TEXTFLAG_STOP_AT_END;
-					
-					gfx_text_ex(&cutter, (const char *)current, wlen);
-					wlen = cutter.charcount;
-					new_line = 1;
-					
-					if(wlen <= 3) /* if we can't place 3 chars of the word on this line, take the next */
-						wlen = 0;
-				}
-				else if(compare.x-cursor->start_x > cursor->line_width)
-				{
-					new_line = 1;
-					wlen = 0;
-				}
-				
-				batch_end = current + wlen;
-			}
-			
-			while(current < batch_end)
-			{
-				const char *tmp;
-				float advance = 0;
-				int character = 0;
-				int nextcharacter = 0;
-				FONTCHAR *chr;
-
-				// TODO: UTF-8 decode
-				character = str_utf8_decode(&current);
-				tmp = current;
-				nextcharacter = str_utf8_decode(&tmp);
-				
-				if(character == '\n')
-				{
-					draw_x = cursor->start_x;
-					draw_y += size;
-					draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */
-					draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y;
-					continue;
-				}
-
-				chr = font_get_char(font, sizedata, character);
-
-				if(chr)
-				{
-					if(cursor->flags&TEXTFLAG_RENDER)
-					{
-						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;
-				}
-								
-				if(cursor->flags&TEXTFLAG_STOP_AT_END && draw_x+advance*size-cursor->start_x > cursor->line_width)
-				{
-					/* we hit the end of the line, no more to render or count */
-					current = end;
-					break;
-				}
-
-				draw_x += advance*size;
-				cursor->charcount++;
-			}
-			
-			if(new_line)
-			{
-				draw_x = cursor->start_x;
-				draw_y += size;
-				got_new_line = 1;
-				draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */
-				draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y;				
-			}
-		}
-
-		if(cursor->flags&TEXTFLAG_RENDER)
-			Graphics()->QuadsEnd();
-	}
-
-	cursor->x = draw_x;
-	
-	if(got_new_line)
-		cursor->y = draw_y;
-}
diff --git a/src/engine/client/ec_inp.cpp b/src/engine/client/ec_inp.cpp
deleted file mode 100644
index cf956471..00000000
--- a/src/engine/client/ec_inp.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <string.h>
-#include "SDL.h"
-
-#include <base/system.h>
-#include <engine/e_client_interface.h>
-#include <engine/e_config.h>
-#include <engine/client/graphics.h>
-
-static struct
-{
-	unsigned char presses;
-	unsigned char releases;
-} input_count[2][1024] = {{{0}}, {{0}}};
-
-static unsigned char input_state[2][1024] = {{0}, {0}};
-
-static int input_current = 0;
-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;
-	float sens = config.inp_mousesens/100.0f;
-	
-	if(config.inp_grab)
-		SDL_GetRelativeMouseState(&nx, &ny);
-	else
-	{
-		if(input_grabbed)
-		{
-			SDL_GetMouseState(&nx,&ny);
-			SDL_WarpMouse(Graphics()->ScreenWidth()/2,Graphics()->ScreenHeight()/2);
-			nx -= Graphics()->ScreenWidth()/2; ny -= Graphics()->ScreenHeight()/2;
-		}
-	}
-
-	*x = nx*sens;
-	*y = ny*sens;
-}
-
-enum
-{
-	INPUT_BUFFER_SIZE=32
-};
-
-static INPUT_EVENT input_events[INPUT_BUFFER_SIZE];
-static int num_events = 0;
-
-static void add_event(int unicode, int key, int flags)
-{
-	if(num_events != INPUT_BUFFER_SIZE)
-	{
-		input_events[num_events].unicode = unicode;
-		input_events[num_events].key = key;
-		input_events[num_events].flags = flags;
-		num_events++;
-	}
-}
-
-int inp_num_events()
-{
-	return num_events;
-}
-
-void inp_clear_events()
-{
-	num_events = 0;
-}
-
-INPUT_EVENT inp_get_event(int index)
-{
-	if(index < 0 || index >= num_events)
-	{
-		INPUT_EVENT e = {0,0};
-		return e;
-	}
-	
-	return input_events[index];
-}
-
-void inp_init()
-{
-	SDL_EnableUNICODE(1);
-	SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); 
-}
-
-void inp_mouse_mode_absolute()
-{
-	SDL_ShowCursor(1);
-	input_grabbed = 0;
-	if(config.inp_grab)
-		SDL_WM_GrabInput(SDL_GRAB_OFF);
-}
-
-void inp_mouse_mode_relative()
-{
-	SDL_ShowCursor(0);
-	input_grabbed = 1;
-	if(config.inp_grab)
-		SDL_WM_GrabInput(SDL_GRAB_ON);
-}
-
-int inp_mouse_doubleclick()
-{
-	return release_delta < (time_freq() >> 2);
-}
-
-void inp_clear_key_states()
-{
-	mem_zero(input_state, sizeof(input_state));
-	mem_zero(input_count, sizeof(input_count));
-}
-
-int inp_key_presses(int key)
-{
-	return input_count[input_current][key].presses;
-}
-
-int inp_key_releases(int key)
-{
-	return input_count[input_current][key].releases;
-}
-
-int inp_key_state(int key)
-{
-	return input_state[input_current][key];
-}
-
-int inp_key_pressed(int key) { return input_state[input_current][key]; }
-int inp_key_was_pressed(int key) { return input_state[input_current^1][key]; }
-int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); }
-int inp_button_pressed(int button) { return input_state[input_current][button]; }
-
-void inp_update()
-{
-	int i;
-	
-	if(input_grabbed && !Graphics()->WindowActive())
-		inp_mouse_mode_absolute();
-
-	/*if(!input_grabbed && Graphics()->WindowActive())
-		inp_mouse_mode_relative();*/
-	
-	/* clear and begin count on the other one */
-	input_current^=1;
-	mem_zero(&input_count[input_current], sizeof(input_count[input_current]));
-	mem_zero(&input_state[input_current], sizeof(input_state[input_current]));
-	
-	{
-		Uint8 *state = SDL_GetKeyState(&i);
-		if(i >= KEY_LAST)
-			i = KEY_LAST-1;
-		mem_copy(input_state[input_current], state, i);
-	}
-	
-	/* these states must always be updated manually because they are not in the GetKeyState from SDL */
-	i = SDL_GetMouseState(NULL, NULL);
-	if(i&SDL_BUTTON(1)) input_state[input_current][KEY_MOUSE_1] = 1; /* 1 is left */ 
-	if(i&SDL_BUTTON(3)) input_state[input_current][KEY_MOUSE_2] = 1; /* 3 is right */ 
-	if(i&SDL_BUTTON(2)) input_state[input_current][KEY_MOUSE_3] = 1; /* 2 is middle */ 
-	if(i&SDL_BUTTON(4)) input_state[input_current][KEY_MOUSE_4] = 1; 
-	if(i&SDL_BUTTON(5)) input_state[input_current][KEY_MOUSE_5] = 1; 
-	if(i&SDL_BUTTON(6)) input_state[input_current][KEY_MOUSE_6] = 1; 
-	if(i&SDL_BUTTON(7)) input_state[input_current][KEY_MOUSE_7] = 1; 
-	if(i&SDL_BUTTON(8)) input_state[input_current][KEY_MOUSE_8] = 1; 	
-	
-	{
-		SDL_Event event;
-	
-		while(SDL_PollEvent(&event))
-		{
-			int key = -1;
-			int action = INPFLAG_PRESS;
-			switch (event.type)
-			{
-				/* handle keys */
-				case SDL_KEYDOWN:
-					/*if(event.key.keysym.unicode < 255) */
-					add_event(event.key.keysym.unicode, 0, 0);
-					key = event.key.keysym.sym;
-					break;
-				case SDL_KEYUP:
-					action = INPFLAG_RELEASE;
-					key = event.key.keysym.sym;
-					break;
-				
-				/* handle mouse buttons */
-				case SDL_MOUSEBUTTONUP:
-					action = INPFLAG_RELEASE;
-					
-					if(event.button.button == 1)
-					{
-						release_delta = time_get() - last_release;
-						last_release = time_get();
-					}
-					
-					/* fall through */
-				case SDL_MOUSEBUTTONDOWN:
-					if(event.button.button == SDL_BUTTON_LEFT) key = KEY_MOUSE_1;
-					if(event.button.button == SDL_BUTTON_RIGHT) key = KEY_MOUSE_2;
-					if(event.button.button == SDL_BUTTON_MIDDLE) key = KEY_MOUSE_3;
-					if(event.button.button == SDL_BUTTON_WHEELUP) key = KEY_MOUSE_WHEEL_UP;
-					if(event.button.button == SDL_BUTTON_WHEELDOWN) key = KEY_MOUSE_WHEEL_DOWN;
-					if(event.button.button == 6) key = KEY_MOUSE_6;
-					if(event.button.button == 7) key = KEY_MOUSE_7;
-					if(event.button.button == 8) key = KEY_MOUSE_8;
-					break;
-					
-				/* other messages */
-				case SDL_QUIT:
-					/* TODO: cleaner exit */
-					exit(0);
-					break;
-			}
-			
-			/* */
-			if(key != -1)
-			{
-				input_count[input_current][key].presses++;
-				if(action == INPFLAG_PRESS)
-					input_state[input_current][key] = 1;
-				add_event(0, key, action);
-			}
-
-		}
-	}
-}
diff --git a/src/engine/client/ec_snd.cpp b/src/engine/client/ec_snd.cpp
deleted file mode 100644
index 3baea982..00000000
--- a/src/engine/client/ec_snd.cpp
+++ /dev/null
@@ -1,471 +0,0 @@
-/* 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"
-
-extern "C" { // wavpack
-	#include <engine/external/wavpack/wavpack.h>
-}
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-
-enum
-{
-	NUM_SAMPLES = 512,
-	NUM_VOICES = 64,
-	NUM_CHANNELS = 16,
-	
-	MAX_FRAMES = 1024
-};
-
-typedef struct
-{
-	short *data;
-	int num_frames;
-	int rate;
-	int channels;
-	int loop_start;
-	int loop_end;
-} SAMPLE;
-
-typedef struct
-{
-	int vol;
-	int pan;
-} CHANNEL;
-
-typedef struct
-{
-	SAMPLE *snd;
-	CHANNEL *channel;
-	int tick;
-	int vol; /* 0 - 255 */
-	int flags;
-	int x, y;
-} VOICE;
-
-static SAMPLE samples[NUM_SAMPLES] = { {0} };
-static VOICE voices[NUM_VOICES] = { {0} };
-static CHANNEL channels[NUM_CHANNELS] = { {255, 0} };
-
-static LOCK sound_lock = 0;
-static int sound_enabled = 0;
-
-static int center_x = 0;
-static int center_y = 0;
-
-static int mixing_rate = 48000;
-static volatile int sound_volume = 100;
-
-static int next_voice = 0;
-
-void snd_set_channel(int cid, float vol, float pan)
-{
-	channels[cid].vol = (int)(vol*255.0f);
-	channels[cid].pan = (int)(pan*255.0f); /* TODO: this is only on and off right now */
-}
-
-static int play(int cid, int sid, int flags, float x, float y)
-{
-	int vid = -1;
-	int i;
-	
-	lock_wait(sound_lock);
-	
-	/* search for voice */
-	for(i = 0; i < NUM_VOICES; i++)
-	{
-		int id = (next_voice + i) % NUM_VOICES;
-		if(!voices[id].snd)
-		{
-			vid = id;
-			next_voice = id+1;
-			break;
-		}
-	}
-	
-	/* voice found, use it */
-	if(vid != -1)
-	{
-		voices[vid].snd = &samples[sid];
-		voices[vid].channel = &channels[cid];
-		voices[vid].tick = 0;
-		voices[vid].vol = 255;
-		voices[vid].flags = flags;
-		voices[vid].x = (int)x;
-		voices[vid].y = (int)y;
-	}
-	
-	lock_release(sound_lock);
-	return vid;
-}
-
-int snd_play_at(int cid, int sid, int flags, float x, float y)
-{
-	return play(cid, sid, flags|SNDFLAG_POS, x, y);
-}
-
-int snd_play(int cid, int sid, int flags)
-{
-	return play(cid, sid, flags, 0, 0);
-}
-
-void snd_stop(int vid)
-{
-	/* TODO: a nice fade out */
-	lock_wait(sound_lock);
-	voices[vid].snd = 0;
-	lock_release(sound_lock);
-}
-
-/* TODO: there should be a faster way todo this */
-static short int2short(int i)
-{
-	if(i > 0x7fff)
-		return 0x7fff;
-	else if(i < -0x7fff)
-		return -0x7fff;
-	return i;
-}
-
-static int iabs(int i)
-{
-	if(i<0)
-		return -i;
-	return i;
-}
-
-static void mix(short *final_out, unsigned frames)
-{
-	int mix_buffer[MAX_FRAMES*2] = {0};
-	int master_vol;
-
-	/* aquire lock while we are mixing */
-	lock_wait(sound_lock);
-	
-	master_vol = sound_volume;
-	
-	for(unsigned i = 0; i < NUM_VOICES; i++)
-	{
-		if(voices[i].snd)
-		{
-			/* mix voice */
-			VOICE *v = &voices[i];
-			int *out = mix_buffer;
-
-			int step = v->snd->channels; /* setup input sources */
-			short *in_l = &v->snd->data[v->tick*step];
-			short *in_r = &v->snd->data[v->tick*step+1];
-			
-			unsigned end = v->snd->num_frames-v->tick;
-
-			int rvol = v->channel->vol;
-			int lvol = v->channel->vol;
-
-			/* make sure that we don't go outside the sound data */
-			if(frames < end)
-				end = frames;
-			
-			/* check if we have a mono sound */
-			if(v->snd->channels == 1)
-				in_r = in_l;
-
-			/* volume calculation */
-			if(v->flags&SNDFLAG_POS && v->channel->pan)
-			{
-				/* TODO: we should respect the channel panning value */
-				const int range = 1500; /* magic value, remove */
-				int dx = v->x - center_x;
-				int dy = v->y - center_y;
-				int dist = sqrt(dx*dx+dy*dy); /* double here. nasty */
-				int p = iabs(dx);
-				if(dist < range)
-				{
-					/* panning */
-					if(dx > 0)
-						lvol = ((range-p)*lvol)/range;
-					else
-						rvol = ((range-p)*rvol)/range;
-					
-					/* falloff */
-					lvol = (lvol*(range-dist))/range;
-					rvol = (rvol*(range-dist))/range;
-				}
-				else
-				{
-					lvol = 0;
-					rvol = 0;
-				}
-			}
-
-			/* process all frames */
-			for(unsigned s = 0; s < end; s++)
-			{
-				*out++ += (*in_l)*lvol;
-				*out++ += (*in_r)*rvol;
-				in_l += step;
-				in_r += step;
-				v->tick++;
-			}
-			
-			/* free voice if not used any more */
-			if(v->tick == v->snd->num_frames)
-				v->snd = 0;
-			
-		}
-	}
-	
-	
-	/* release the lock */
-	lock_release(sound_lock);
-
-	{
-		/* clamp accumulated values */
-		/* TODO: this seams slow */
-		for(unsigned i = 0; i < frames; i++)
-		{
-			int j = i<<1;
-			int vl = ((mix_buffer[j]*master_vol)/101)>>8;
-			int vr = ((mix_buffer[j+1]*master_vol)/101)>>8;
-
-			final_out[j] = int2short(vl);
-			final_out[j+1] = int2short(vr);
-		}
-	}
-
-#if defined(CONF_ARCH_ENDIAN_BIG)
-	swap_endian(final_out, sizeof(short), frames * 2);
-#endif
-}
-
-static void sdlcallback(void *unused, Uint8 *stream, int len)
-{
-	mix((short *)stream, len/2/2);
-}
-
-int snd_init()
-{
-    SDL_AudioSpec format;
-	
-	sound_lock = lock_create();
-	
-	if(!config.snd_enable)
-		return 0;
-	
-	mixing_rate = config.snd_rate;
-
-    /* Set 16-bit stereo audio at 22Khz */
-    format.freq = config.snd_rate;
-    format.format = AUDIO_S16;
-    format.channels = 2;
-    format.samples = config.snd_buffer_size;
-    format.callback = sdlcallback;
-    format.userdata = NULL;
-
-    /* Open the audio device and start playing sound! */
-    if(SDL_OpenAudio(&format, NULL) < 0)
-	{
-        dbg_msg("client/sound", "unable to open audio: %s", SDL_GetError());
-		return -1;
-    }
-	else
-        dbg_msg("client/sound", "sound init successful");
-
-	SDL_PauseAudio(0);
-	
-	sound_enabled = 1;
-	snd_update(); /* update the volume */
-	return 0;
-}
-
-// TODO: Refactor: Remove this
-extern IEngineGraphics *Graphics();
-
-
-int snd_update()
-{
-	/* update volume */
-	int wanted_volume = config.snd_volume;
-	
-	if(!Graphics()->WindowActive() && config.snd_nonactive_mute)
-		wanted_volume = 0;
-	
-	if(wanted_volume != sound_volume)
-	{
-		lock_wait(sound_lock);
-		sound_volume = wanted_volume;
-		lock_release(sound_lock);
-	}
-	
-	return 0;
-}
-
-int snd_shutdown()
-{
-	SDL_CloseAudio();
-	lock_destroy(sound_lock);
-	return 0;
-}
-
-int snd_alloc_id()
-{
-	/* TODO: linear search, get rid of it */
-	unsigned sid;
-	for(sid = 0; sid < NUM_SAMPLES; sid++)
-	{
-		if(samples[sid].data == 0x0)
-			return sid;
-	}
-
-	return -1;
-}
-
-static void rate_convert(int sid)
-{
-	SAMPLE *snd = &samples[sid];
-	int num_frames = 0;
-	short *new_data = 0;
-	int i;
-	
-	/* make sure that we need to convert this sound */
-	if(!snd->data || snd->rate == mixing_rate)
-		return;
-
-	/* allocate new data */
-	num_frames = (int)((snd->num_frames/(float)snd->rate)*mixing_rate);
-	new_data = (short *)mem_alloc(num_frames*snd->channels*sizeof(short), 1);
-	
-	for(i = 0; i < num_frames; i++)
-	{
-		/* resample TODO: this should be done better, like linear atleast */
-		float a = i/(float)num_frames;
-		int f = (int)(a*snd->num_frames);
-		if(f >= snd->num_frames)
-			f = snd->num_frames-1;
-		
-		/* set new data */
-		if(snd->channels == 1)
-			new_data[i] = snd->data[f];
-		else if(snd->channels == 2)
-		{
-			new_data[i*2] = snd->data[f*2];
-			new_data[i*2+1] = snd->data[f*2+1];
-		}
-	}
-	
-	/* free old data and apply new */
-	mem_free(snd->data);
-	snd->data = new_data;
-	snd->num_frames = num_frames;
-}
-
-
-static IOHANDLE file = NULL;
-
-static int read_data(void *buffer, int size)
-{
-	return io_read(file, buffer, size);
-}
-
-int snd_load_wv(const char *filename)
-{
-	SAMPLE *snd;
-	int sid = -1;
-	char error[100];
-	WavpackContext *context;
-	
-	/* don't waste memory on sound when we are stress testing */
-	if(config.dbg_stress)
-		return -1;
-		
-	/* no need to load sound when we are running with no sound */
-	if(!sound_enabled)
-		return 1;
-
-	file = engine_openfile(filename, IOFLAG_READ); /* TODO: use system.h stuff for this */
-	if(!file)
-	{
-		dbg_msg("sound/wv", "failed to open %s", filename);
-		return -1;
-	}
-
-	sid = snd_alloc_id();
-	if(sid < 0)
-		return -1;
-	snd = &samples[sid];
-
-	context = WavpackOpenFileInput(read_data, error);
-	if (context)
-	{
-		int samples = WavpackGetNumSamples(context);
-		int bitspersample = WavpackGetBitsPerSample(context);
-		unsigned int samplerate = WavpackGetSampleRate(context);
-		int channels = WavpackGetNumChannels(context);
-		int *data;
-		int *src;
-		short *dst;
-		int i;
-
-		snd->channels = channels;
-		snd->rate = samplerate;
-
-		if(snd->channels > 2)
-		{
-			dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename);
-			return -1;
-		}
-
-		/*
-		if(snd->rate != 44100)
-		{
-			dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename);
-			return -1;
-		}*/
-		
-		if(bitspersample != 16)
-		{
-			dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename);
-			return -1;
-		}
-
-		data = (int *)mem_alloc(4*samples*channels, 1);
-		WavpackUnpackSamples(context, data, samples); /* TODO: check return value */
-		src = data;
-		
-		snd->data = (short *)mem_alloc(2*samples*channels, 1);
-		dst = snd->data;
-
-		for (i = 0; i < samples*channels; i++)
-			*dst++ = (short)*src++;
-
-		mem_free(data);
-
-		snd->num_frames = samples;
-		snd->loop_start = -1;
-		snd->loop_end = -1;
-	}
-	else
-	{
-		dbg_msg("sound/wv", "failed to open %s: %s", filename, error);
-	}
-
-	io_close(file);
-	file = NULL;
-
-	if(config.debug)
-		dbg_msg("sound/wv", "loaded %s", filename);
-
-	rate_convert(sid);
-	return sid;
-}
-
-void snd_set_listener_pos(float x, float y)
-{
-	center_x = (int)x;
-	center_y = (int)y;
-}
diff --git a/src/engine/client/ec_srvbrowse.cpp b/src/engine/client/ec_srvbrowse.cpp
deleted file mode 100644
index 1b04937a..00000000
--- a/src/engine/client/ec_srvbrowse.cpp
+++ /dev/null
@@ -1,736 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <base/system.h>
-#include <engine/e_network.h>
-#include <engine/e_client_interface.h>
-#include <engine/e_config.h>
-#include <engine/e_memheap.h>
-#include <engine/e_engine.h>
-
-#include <mastersrv/mastersrv.h>
-
-#include <string.h>
-#include <stdlib.h>
-
-extern CNetClient m_NetClient;
-
-
-/* ------ server browse ---- */
-/* TODO: move all this to a separate file */
-
-typedef struct SERVERENTRY_t SERVERENTRY;
-struct SERVERENTRY_t
-{
-	NETADDR addr;
-	int64 request_time;
-	int got_info;
-	SERVER_INFO info;
-	
-	SERVERENTRY *next_ip; /* ip hashed list */
-	
-	SERVERENTRY *prev_req; /* request list */
-	SERVERENTRY *next_req;
-};
-
-static HEAP *serverlist_heap = 0;
-static SERVERENTRY **serverlist = 0;
-static int *sorted_serverlist = 0;
-
-enum
-{
-	MAX_FAVORITES=256
-};
-
-static NETADDR favorite_servers[MAX_FAVORITES];
-static int num_favorite_servers = 0;
-
-static SERVERENTRY *serverlist_ip[256] = {0}; /* ip hash list */
-
-static SERVERENTRY *first_req_server = 0; /* request list */
-static SERVERENTRY *last_req_server = 0;
-static int num_requests = 0;
-
-static int need_refresh = 0;
-
-static int num_sorted_servers = 0;
-static int num_sorted_servers_capacity = 0;
-static int num_servers = 0;
-static int num_server_capacity = 0;
-
-static int sorthash = 0;
-static char filterstring[64] = {0};
-static char filtergametypestring[128] = {0};
-
-/* the token is to keep server refresh separated from each other */
-static int current_token = 1;
-
-static int serverlist_type = 0;
-static int64 broadcast_time = 0;
-
-int client_serverbrowse_lan() { return serverlist_type == BROWSETYPE_LAN; }
-int client_serverbrowse_num() { return num_servers; }
-
-SERVER_INFO *client_serverbrowse_get(int index)
-{
-	if(index < 0 || index >= num_servers)
-		return 0;
-	return &serverlist[index]->info;
-}
-
-int client_serverbrowse_sorted_num() { return num_sorted_servers; }
-
-SERVER_INFO *client_serverbrowse_sorted_get(int index)
-{
-	if(index < 0 || index >= num_sorted_servers)
-		return 0;
-	return &serverlist[sorted_serverlist[index]]->info;
-}
-
-
-int client_serverbrowse_num_requests()
-{
-	return num_requests;
-}
-
-static int client_serverbrowse_sort_compare_name(const void *ai, const void *bi)
-{
-	SERVERENTRY *a = serverlist[*(const int*)ai];
-	SERVERENTRY *b = serverlist[*(const int*)bi];
-	return strcmp(a->info.name, b->info.name);
-}
-
-static int client_serverbrowse_sort_compare_map(const void *ai, const void *bi)
-{
-	SERVERENTRY *a = serverlist[*(const int*)ai];
-	SERVERENTRY *b = serverlist[*(const int*)bi];
-	return strcmp(a->info.map, b->info.map);
-}
-
-static int client_serverbrowse_sort_compare_ping(const void *ai, const void *bi)
-{
-	SERVERENTRY *a = serverlist[*(const int*)ai];
-	SERVERENTRY *b = serverlist[*(const int*)bi];
-	if(a->info.latency > b->info.latency) return 1;
-	if(a->info.latency < b->info.latency) return -1;
-	return 0;
-}
-
-static int client_serverbrowse_sort_compare_gametype(const void *ai, const void *bi)
-{
-	SERVERENTRY *a = serverlist[*(const int*)ai];
-	SERVERENTRY *b = serverlist[*(const int*)bi];
-	return strcmp(a->info.gametype, b->info.gametype);
-}
-
-static int client_serverbrowse_sort_compare_progression(const void *ai, const void *bi)
-{
-	SERVERENTRY *a = serverlist[*(const int*)ai];
-	SERVERENTRY *b = serverlist[*(const int*)bi];
-	if(a->info.progression > b->info.progression) return 1;
-	if(a->info.progression < b->info.progression) return -1;
-	return 0;
-}
-
-static int client_serverbrowse_sort_compare_numplayers(const void *ai, const void *bi)
-{
-	SERVERENTRY *a = serverlist[*(const int*)ai];
-	SERVERENTRY *b = serverlist[*(const int*)bi];
-	if(a->info.num_players > b->info.num_players) return 1;
-	if(a->info.num_players < b->info.num_players) return -1;
-	return 0;
-}
-
-static void client_serverbrowse_filter()
-{
-	int i = 0, p = 0;
-	num_sorted_servers = 0;
-
-	/* allocate the sorted list */	
-	if(num_sorted_servers_capacity < num_servers)
-	{
-		if(sorted_serverlist)
-			mem_free(sorted_serverlist);
-		num_sorted_servers_capacity = num_servers;
-		sorted_serverlist = (int *)mem_alloc(num_sorted_servers_capacity*sizeof(int), 1);
-	}
-	
-	/* filter the servers */
-	for(i = 0; i < num_servers; i++)
-	{
-		int filtered = 0;
-
-		if(config.b_filter_empty && serverlist[i]->info.num_players == 0)
-			filtered = 1;
-		else if(config.b_filter_full && serverlist[i]->info.num_players == serverlist[i]->info.max_players)
-			filtered = 1;
-		else if(config.b_filter_pw && serverlist[i]->info.flags&SRVFLAG_PASSWORD)
-			filtered = 1;
-		else if(config.b_filter_pure && (strcmp(serverlist[i]->info.gametype, "DM") != 0 && strcmp(serverlist[i]->info.gametype, "TDM") != 0 && strcmp(serverlist[i]->info.gametype, "CTF") != 0))
-			filtered = 1;
-		else if(config.b_filter_pure_map &&
-			!(strcmp(serverlist[i]->info.map, "dm1") == 0 ||
-			strcmp(serverlist[i]->info.map, "dm2") == 0 ||
-			strcmp(serverlist[i]->info.map, "dm6") == 0 ||
-			strcmp(serverlist[i]->info.map, "dm7") == 0 ||
-			strcmp(serverlist[i]->info.map, "dm8") == 0 ||
-			strcmp(serverlist[i]->info.map, "dm9") == 0 ||
-			strcmp(serverlist[i]->info.map, "ctf1") == 0 ||
-			strcmp(serverlist[i]->info.map, "ctf2") == 0 ||
-			strcmp(serverlist[i]->info.map, "ctf3") == 0 ||
-			strcmp(serverlist[i]->info.map, "ctf4") == 0 ||
-			strcmp(serverlist[i]->info.map, "ctf5") == 0)
-		)
-		{
-			filtered = 1;
-		}
-		else if(config.b_filter_ping < serverlist[i]->info.latency)
-			filtered = 1;
-		else if(config.b_filter_compatversion && strncmp(serverlist[i]->info.version, modc_net_version(), 3) != 0)
-			filtered = 1;
-		else 
-		{
-			if(config.b_filter_string[0] != 0)
-			{
-				int matchfound = 0;
-				
-				serverlist[i]->info.quicksearch_hit = 0;
-
-				/* match against server name */
-				if(str_find_nocase(serverlist[i]->info.name, config.b_filter_string))
-				{
-					matchfound = 1;
-					serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_SERVERNAME;
-				}
-
-				/* match against players */				
-				for(p = 0; p < serverlist[i]->info.num_players; p++)
-				{
-					if(str_find_nocase(serverlist[i]->info.players[p].name, config.b_filter_string))
-					{
-						matchfound = 1;
-						serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_PLAYERNAME;
-						break;
-					}
-				}
-				
-				/* match against map */
-				if(str_find_nocase(serverlist[i]->info.map, config.b_filter_string))
-				{
-					matchfound = 1;
-					serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_MAPNAME;
-				}
-				
-				if(!matchfound)
-					filtered = 1;
-			}
-			
-			if(!filtered && config.b_filter_gametype[0] != 0)
-			{
-				/* match against game type */
-				if(!str_find_nocase(serverlist[i]->info.gametype, config.b_filter_gametype))
-					filtered = 1;
-			}
-		}
-
-		if(filtered == 0)
-			sorted_serverlist[num_sorted_servers++] = i;
-	}
-}
-
-static int client_serverbrowse_sorthash()
-{
-	int i = config.b_sort&0xf;
-	i |= config.b_filter_empty<<4;
-	i |= config.b_filter_full<<5;
-	i |= config.b_filter_pw<<6;
-	i |= config.b_sort_order<<7;
-	i |= config.b_filter_compatversion<<8;
-	i |= config.b_filter_pure<<9;
-	i |= config.b_filter_pure_map<<10;
-	i |= config.b_filter_ping<<16;
-	return i;
-}
-
-static void client_serverbrowse_sort()
-{
-	int i;
-	
-	/* create filtered list */
-	client_serverbrowse_filter();
-	
-	/* sort */
-	if(config.b_sort == BROWSESORT_NAME)
-		qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_name);
-	else if(config.b_sort == BROWSESORT_PING)
-		qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_ping);
-	else if(config.b_sort == BROWSESORT_MAP)
-		qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_map);
-	else if(config.b_sort == BROWSESORT_NUMPLAYERS)
-		qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_numplayers);
-	else if(config.b_sort == BROWSESORT_GAMETYPE)
-		qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_gametype);
-	else if(config.b_sort == BROWSESORT_PROGRESSION)
-		qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_progression);
-	
-	/* invert the list if requested */
-	if(config.b_sort_order)
-	{
-		for(i = 0; i < num_sorted_servers/2; i++)
-		{
-			int temp = sorted_serverlist[i];
-			sorted_serverlist[i] = sorted_serverlist[num_sorted_servers-i-1];
-			sorted_serverlist[num_sorted_servers-i-1] = temp;
-		}
-	}
-	
-	/* set indexes */
-	for(i = 0; i < num_sorted_servers; i++)
-		serverlist[sorted_serverlist[i]]->info.sorted_index = i;
-	
-	str_copy(filtergametypestring, config.b_filter_gametype, sizeof(filtergametypestring)); 
-	str_copy(filterstring, config.b_filter_string, sizeof(filterstring)); 
-	sorthash = client_serverbrowse_sorthash();
-}
-
-static void client_serverbrowse_remove_request(SERVERENTRY *entry)
-{
-	if(entry->prev_req || entry->next_req || first_req_server == entry)
-	{
-		if(entry->prev_req)
-			entry->prev_req->next_req = entry->next_req;
-		else
-			first_req_server = entry->next_req;
-			
-		if(entry->next_req)
-			entry->next_req->prev_req = entry->prev_req;
-		else
-			last_req_server = entry->prev_req;
-			
-		entry->prev_req = 0;
-		entry->next_req = 0;
-		num_requests--;
-	}
-}
-
-static SERVERENTRY *client_serverbrowse_find(NETADDR *addr)
-{
-	SERVERENTRY *entry = serverlist_ip[addr->ip[0]];
-	
-	for(; entry; entry = entry->next_ip)
-	{
-		if(net_addr_comp(&entry->addr, addr) == 0)
-			return entry;
-	}
-	return (SERVERENTRY*)0;
-}
-
-void client_serverbrowse_queuerequest(SERVERENTRY *entry)
-{
-	/* add it to the list of servers that we should request info from */
-	entry->prev_req = last_req_server;
-	if(last_req_server)
-		last_req_server->next_req = entry;
-	else
-		first_req_server = entry;
-	last_req_server = entry;
-	
-	num_requests++;
-}
-
-void client_serverbrowse_setinfo(SERVERENTRY *entry, SERVER_INFO *info)
-{
-	int fav = entry->info.favorite;
-	entry->info = *info;
-	entry->info.favorite = fav;
-	entry->info.netaddr = entry->addr;
-	
-	// all these are just for nice compability
-	if(entry->info.gametype[0] == '0' && entry->info.gametype[1] == 0)
-		str_copy(entry->info.gametype, "DM", sizeof(entry->info.gametype));
-	else if(entry->info.gametype[0] == '1' && entry->info.gametype[1] == 0)
-		str_copy(entry->info.gametype, "TDM", sizeof(entry->info.gametype));
-	else if(entry->info.gametype[0] == '2' && entry->info.gametype[1] == 0)
-		str_copy(entry->info.gametype, "CTF", sizeof(entry->info.gametype));
-
-	/*if(!request)
-	{
-		entry->info.latency = (time_get()-entry->request_time)*1000/time_freq();
-		client_serverbrowse_remove_request(entry);
-	}*/
-	
-	entry->got_info = 1;
-	client_serverbrowse_sort();	
-}
-
-SERVERENTRY *client_serverbrowse_add(NETADDR *addr)
-{
-	int hash = addr->ip[0];
-	SERVERENTRY *entry = 0;
-	int i;
-
-	/* create new entry */
-	entry = (SERVERENTRY *)memheap_allocate(serverlist_heap, sizeof(SERVERENTRY));
-	mem_zero(entry, sizeof(SERVERENTRY));
-	
-	/* set the info */
-	entry->addr = *addr;
-	entry->info.netaddr = *addr;
-	
-	entry->info.latency = 999;
-	str_format(entry->info.address, sizeof(entry->info.address), "%d.%d.%d.%d:%d",
-		addr->ip[0], addr->ip[1], addr->ip[2],
-		addr->ip[3], addr->port);
-	str_format(entry->info.name, sizeof(entry->info.name), "\255%d.%d.%d.%d:%d", /* the \255 is to make sure that it's sorted last */
-		addr->ip[0], addr->ip[1], addr->ip[2],
-		addr->ip[3], addr->port);	
-	
-	/*if(serverlist_type == BROWSETYPE_LAN)
-		entry->info.latency = (time_get()-broadcast_time)*1000/time_freq();*/
-
-	/* check if it's a favorite */
-	for(i = 0; i < num_favorite_servers; i++)
-	{
-		if(net_addr_comp(addr, &favorite_servers[i]) == 0)
-			entry->info.favorite = 1;
-	}
-	
-	/* add to the hash list */	
-	entry->next_ip = serverlist_ip[hash];
-	serverlist_ip[hash] = entry;
-	
-	if(num_servers == num_server_capacity)
-	{
-		SERVERENTRY **newlist;
-		num_server_capacity += 100;
-		newlist = (SERVERENTRY **)mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1);
-		mem_copy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*));
-		mem_free(serverlist);
-		serverlist = newlist;
-	}
-	
-	/* add to list */
-	serverlist[num_servers] = entry;
-	entry->info.server_index = num_servers;
-	num_servers++;
-	
-	return entry;
-}
-
-void client_serverbrowse_set(NETADDR *addr, int type, int token, SERVER_INFO *info)
-{
-	SERVERENTRY *entry = 0;
-	if(type == BROWSESET_MASTER_ADD)
-	{
-		if(serverlist_type != BROWSETYPE_INTERNET)
-			return;
-			
-		if(!client_serverbrowse_find(addr))
-		{
-			entry = client_serverbrowse_add(addr);
-			client_serverbrowse_queuerequest(entry);
-		}
-	}
-	else if(type == BROWSESET_FAV_ADD)
-	{
-		if(serverlist_type != BROWSETYPE_FAVORITES)
-			return;
-			
-		if(!client_serverbrowse_find(addr))
-		{
-			entry = client_serverbrowse_add(addr);
-			client_serverbrowse_queuerequest(entry);
-		}
-	}
-	else if(type == BROWSESET_TOKEN)
-	{
-		if(token != current_token)
-			return;
-			
-		entry = client_serverbrowse_find(addr);
-		if(!entry)
-			entry = client_serverbrowse_add(addr);
-		if(entry)
-		{
-			client_serverbrowse_setinfo(entry, info);
-			if(serverlist_type == BROWSETYPE_LAN)
-				entry->info.latency = (time_get()-broadcast_time)*1000/time_freq();
-			else
-				entry->info.latency = (time_get()-entry->request_time)*1000/time_freq();
-			client_serverbrowse_remove_request(entry);				
-		}
-	}
-	else if(type == BROWSESET_OLD_INTERNET)
-	{
-		entry = client_serverbrowse_find(addr);
-		if(entry)
-		{
-			client_serverbrowse_setinfo(entry, info);
-			
-			if(serverlist_type == BROWSETYPE_LAN)
-				entry->info.latency = (time_get()-broadcast_time)*1000/time_freq();
-			else
-				entry->info.latency = (time_get()-entry->request_time)*1000/time_freq();
-			client_serverbrowse_remove_request(entry);				
-		}
-	}
-	else if(type == BROWSESET_OLD_LAN)
-	{
-		entry = client_serverbrowse_find(addr);
-		if(entry)
-		if(!entry)
-			entry = client_serverbrowse_add(addr);
-		if(entry)
-			client_serverbrowse_setinfo(entry, info);
-	}
-	
-	client_serverbrowse_sort();
-}
-
-void client_serverbrowse_refresh(int type)
-{
-	/* clear out everything */
-	if(serverlist_heap)
-		memheap_destroy(serverlist_heap);
-	serverlist_heap = memheap_create();
-	num_servers = 0;
-	num_sorted_servers = 0;
-	mem_zero(serverlist_ip, sizeof(serverlist_ip));
-	first_req_server = 0;
-	last_req_server = 0;
-	num_requests = 0;
-	
-	/* next token */
-	current_token = (current_token+1)&0xff;
-	
-	/* */
-	serverlist_type = type;
-
-	if(type == BROWSETYPE_LAN)
-	{
-		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;
-			
-		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.m_Address.port = i;
-			m_NetClient.Send(&Packet);
-		}
-
-		if(config.debug)
-			dbg_msg("client", "broadcasting for servers");
-	}
-	else if(type == BROWSETYPE_INTERNET)
-		need_refresh = 1;
-	else if(type == BROWSETYPE_FAVORITES)
-	{
-		for(int i = 0; i < num_favorite_servers; i++)
-			client_serverbrowse_set(&favorite_servers[i], BROWSESET_FAV_ADD, -1, 0);
-	}
-}
-
-static void client_serverbrowse_request_impl(NETADDR *addr, SERVERENTRY *entry)
-{
-	/*unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1];*/
-	CNetChunk Packet;
-
-	if(config.debug)
-	{
-		dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d",
-			addr->ip[0], addr->ip[1], addr->ip[2],
-			addr->ip[3], addr->port);
-	}
-	
-	/*mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
-	buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;*/
-	
-	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 */	
-	Packet.m_DataSize = sizeof(SERVERBROWSE_OLD_GETINFO);
-	Packet.m_pData = SERVERBROWSE_OLD_GETINFO;
-	m_NetClient.Send(&Packet);
-	
-	if(entry)
-		entry->request_time = time_get();
-}
-
-void client_serverbrowse_request(NETADDR *addr)
-{
-	client_serverbrowse_request_impl(addr, 0);
-}
-
-
-void client_serverbrowse_update()
-{
-	int64 timeout = time_freq();
-	int64 now = time_get();
-	int count;
-	SERVERENTRY *entry, *next;
-	
-	/* do server list requests */
-	if(need_refresh && !mastersrv_refreshing())
-	{
-		NETADDR addr;
-		CNetChunk Packet;
-		int i;
-		
-		need_refresh = 0;
-		
-		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++)
-		{
-			addr = mastersrv_get(i);
-			if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3])
-				continue;
-			
-			Packet.m_Address = addr;
-			m_NetClient.Send(&Packet);
-		}
-
-		if(config.debug)
-			dbg_msg("client", "requesting server list");
-	}
-	
-	/* do timeouts */
-	entry = first_req_server;
-	while(1)
-	{
-		if(!entry) /* no more entries */
-			break;
-			
-		next = entry->next_req;
-		
-		if(entry->request_time && entry->request_time+timeout < now)
-		{
-			/* timeout */
-			client_serverbrowse_remove_request(entry);
-			num_requests--;
-		}
-			
-		entry = next;
-	}
-
-	/* do timeouts */
-	entry = first_req_server;
-	count = 0;
-	while(1)
-	{
-		if(!entry) /* no more entries */
-			break;
-		
-		/* no more then 10 concurrent requests */
-		if(count == config.b_max_requests)
-			break;
-			
-		if(entry->request_time == 0)
-			client_serverbrowse_request_impl(&entry->addr, entry);
-		
-		count++;
-		entry = entry->next_req;
-	}
-	
-	/* check if we need to resort */
-	/* TODO: remove the strcmp */
-	if(sorthash != client_serverbrowse_sorthash() || strcmp(filterstring, config.b_filter_string) != 0 || strcmp(filtergametypestring, config.b_filter_gametype) != 0)
-		client_serverbrowse_sort();
-}
-
-
-int client_serverbrowse_isfavorite(NETADDR addr)
-{
-	/* search for the address */
-	int i;
-	for(i = 0; i < num_favorite_servers; i++)
-	{
-		if(net_addr_comp(&addr, &favorite_servers[i]) == 0)
-			return 1;
-	}
-	return 0;
-}
-
-void client_serverbrowse_addfavorite(NETADDR addr)
-{
-	int i;
-	SERVERENTRY *entry;
-	
-	if(num_favorite_servers == MAX_FAVORITES)
-		return;
-
-	/* make sure that we don't already have the server in our list */
-	for(i = 0; i < num_favorite_servers; i++)
-	{
-		if(net_addr_comp(&addr, &favorite_servers[i]) == 0)
-			return;
-	}
-	
-	/* add the server to the list */
-	favorite_servers[num_favorite_servers++] = addr;
-	entry = client_serverbrowse_find(&addr);
-	if(entry)
-		entry->info.favorite = 1;
-	dbg_msg("", "added fav, %p", entry);
-}
-
-void client_serverbrowse_removefavorite(NETADDR addr)
-{
-	int i;
-	SERVERENTRY *entry;
-	
-	for(i = 0; i < num_favorite_servers; i++)
-	{
-		if(net_addr_comp(&addr, &favorite_servers[i]) == 0)
-		{
-			mem_move(&favorite_servers[i], &favorite_servers[i+1], num_favorite_servers-(i+1));
-			num_favorite_servers--;
-
-			entry = client_serverbrowse_find(&addr);
-			if(entry)
-				entry->info.favorite = 0;
-
-			return;
-		}
-	}
-}
-
-void client_serverbrowse_save()
-{
-	int i;
-	char addrstr[128];
-	char buffer[256];
-	for(i = 0; i < num_favorite_servers; i++)
-	{
-		net_addr_str(&favorite_servers[i], addrstr, sizeof(addrstr));
-		str_format(buffer, sizeof(buffer), "add_favorite %s", addrstr);
-		engine_config_write_line(buffer);
-	}
-}
-
-
-int client_serverbrowse_refreshingmasters()
-{
-	return mastersrv_refreshing();
-}
diff --git a/src/engine/client/editor.h b/src/engine/client/editor.h
deleted file mode 100644
index 336a46d4..00000000
--- a/src/engine/client/editor.h
+++ /dev/null
@@ -1,10 +0,0 @@
-
-class IEditor
-{
-public:
-	virtual ~IEditor() {}
-	virtual void Init(class IGraphics *pGraphics) = 0;
-	virtual void UpdateAndRender() = 0;
-};
-
-extern IEditor *CreateEditor();
diff --git a/src/engine/client/graphics.cpp b/src/engine/client/graphics.cpp
new file mode 100644
index 00000000..7c355fb8
--- /dev/null
+++ b/src/engine/client/graphics.cpp
@@ -0,0 +1,938 @@
+// 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/shared/engine.h>
+#include <engine/shared/config.h>
+#include <engine/graphics.h>
+#include <engine/storage.h>
+#include <engine/keys.h>
+
+#include <math.h>
+
+#include "graphics.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
+
+
+static CVideoMode g_aFakeModes[] = {
+	{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}
+};
+
+void CGraphics_OpenGL::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 CGraphics_OpenGL::AddVertices(int Count)
+{
+	m_NumVertices += Count;
+	if((m_NumVertices + Count) >= MAX_VERTICES)
+		Flush();
+}
+
+void CGraphics_OpenGL::Rotate4(CPoint *pCenter, CVertex *pPoints)
+{
+	float c = cosf(m_Rotation);
+	float s = sinf(m_Rotation);
+	float x, y;
+	int i;
+
+	for(i = 0; i < 4; i++)
+	{
+		x = pPoints[i].m_Pos.x - pCenter->x;
+		y = pPoints[i].m_Pos.y - pCenter->y;
+		pPoints[i].m_Pos.x = x * c - y * s + pCenter->x;
+		pPoints[i].m_Pos.y = x * s + y * c + pCenter->y;
+	}
+}
+
+unsigned char CGraphics_OpenGL::Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset)
+{
+	return (pData[(v*w+u)*4+Offset]+
+	pData[(v*w+u+1)*4+Offset]+
+	pData[((v+1)*w+u)*4+Offset]+
+	pData[((v+1)*w+u+1)*4+Offset])/4;
+}	
+
+CGraphics_OpenGL::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;
+}
+
+void CGraphics_OpenGL::ClipEnable(int x, int y, int w, int h)
+{
+	//if(no_gfx) return;
+	glScissor(x, ScreenHeight()-(y+h), w, h);
+	glEnable(GL_SCISSOR_TEST);
+}
+
+void CGraphics_OpenGL::ClipDisable()
+{
+	//if(no_gfx) return;
+	glDisable(GL_SCISSOR_TEST);
+}
+	
+void CGraphics_OpenGL::BlendNone()
+{
+	glDisable(GL_BLEND);
+}
+
+void CGraphics_OpenGL::BlendNormal()
+{
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+void CGraphics_OpenGL::BlendAdditive()
+{
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+}
+
+int CGraphics_OpenGL::MemoryUsage() const
+{ 
+	return m_TextureMemoryUsage;
+}	
+	
+void CGraphics_OpenGL::MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY)
+{
+	m_ScreenX0 = TopLeftX;
+	m_ScreenY0 = TopLeftY;
+	m_ScreenX1 = BottomRightX;
+	m_ScreenY1 = BottomRightY;
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(TopLeftX, BottomRightX, BottomRightY, TopLeftY, 1.0f, 10.f);
+}
+
+void CGraphics_OpenGL::GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY)
+{
+	*pTopLeftX = m_ScreenX0;
+	*pTopLeftY = m_ScreenY0;
+	*pBottomRightX = m_ScreenX1;
+	*pBottomRightY = m_ScreenY1;
+}
+
+void CGraphics_OpenGL::LinesBegin()
+{
+	dbg_assert(m_Drawing == 0, "called begin twice");
+	m_Drawing = DRAWING_LINES;
+	SetColor(1,1,1,1);
+}
+
+void CGraphics_OpenGL::LinesEnd()
+{
+	dbg_assert(m_Drawing == DRAWING_LINES, "called end without begin");
+	Flush();
+	m_Drawing = 0;
+}
+
+void CGraphics_OpenGL::LinesDraw(const CLineItem *pArray, int Num)
+{
+	dbg_assert(m_Drawing == DRAWING_LINES, "called draw without begin");
+	
+	for(int i = 0; i < Num; ++i)
+	{
+		m_aVertices[m_NumVertices + 2*i].m_Pos.x = pArray[i].m_X0;
+		m_aVertices[m_NumVertices + 2*i].m_Pos.y = pArray[i].m_Y0;
+		m_aVertices[m_NumVertices + 2*i].m_Tex = m_aTexture[0];
+		m_aVertices[m_NumVertices + 2*i].m_Color = m_aColor[0];
+
+		m_aVertices[m_NumVertices + 2*i + 1].m_Pos.x = pArray[i].m_X1;
+		m_aVertices[m_NumVertices + 2*i + 1].m_Pos.y = pArray[i].m_Y1;
+		m_aVertices[m_NumVertices + 2*i + 1].m_Tex = m_aTexture[1];
+		m_aVertices[m_NumVertices + 2*i + 1].m_Color = m_aColor[1];
+	}
+
+	AddVertices(2*Num);
+}
+
+int CGraphics_OpenGL::UnloadTexture(int Index)
+{
+	if(Index == m_InvalidTexture)
+		return 0;
+		
+	if(Index < 0)
+		return 0;
+		
+	glDeleteTextures(1, &m_aTextures[Index].m_Tex);
+	m_aTextures[Index].m_Next = m_FirstFreeTexture;
+	m_TextureMemoryUsage -= m_aTextures[Index].m_MemSize;
+	m_FirstFreeTexture = Index;
+	return 0;
+}
+
+
+int CGraphics_OpenGL::LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags)
+{
+	int Mipmap = 1;
+	unsigned char *pTexData = (unsigned char *)pData;
+	unsigned char *pTmpData = 0;
+	int Oglformat = 0;
+	int StoreOglformat = 0;
+	int Tex = 0;
+	
+	// don't waste memory on texture if we are stress testing
+	if(g_Config.m_DbgStress)
+		return 	m_InvalidTexture;
+	
+	// grab texture
+	Tex = m_FirstFreeTexture;
+	m_FirstFreeTexture = m_aTextures[Tex].m_Next;
+	m_aTextures[Tex].m_Next = -1;
+	
+	// resample if needed
+	if(!(Flags&TEXLOAD_NORESAMPLE) && g_Config.m_GfxTextureQuality==0)
+	{
+		if(Width > 16 && Height > 16 && Format == CImageInfo::FORMAT_RGBA)
+		{
+			unsigned char *pTmpData;
+			int c = 0;
+			int x, y;
+
+			pTmpData = (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++)
+				{
+					pTmpData[c*4] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 0);
+					pTmpData[c*4+1] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 1);
+					pTmpData[c*4+2] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 2);
+					pTmpData[c*4+3] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 3);
+				}
+			pTexData = pTmpData;
+		}
+	}
+	
+	Oglformat = GL_RGBA;
+	if(Format == CImageInfo::FORMAT_RGB)
+		Oglformat = GL_RGB;
+	else if(Format == CImageInfo::FORMAT_ALPHA)
+		Oglformat = GL_ALPHA;
+	
+	// upload texture
+	if(g_Config.m_GfxTextureCompression)
+	{
+		StoreOglformat = GL_COMPRESSED_RGBA_ARB;
+		if(StoreFormat == CImageInfo::FORMAT_RGB)
+			StoreOglformat = GL_COMPRESSED_RGB_ARB;
+		else if(StoreFormat == CImageInfo::FORMAT_ALPHA)
+			StoreOglformat = GL_COMPRESSED_ALPHA_ARB;
+	}
+	else
+	{
+		StoreOglformat = GL_RGBA;
+		if(StoreFormat == CImageInfo::FORMAT_RGB)
+			StoreOglformat = GL_RGB;
+		else if(StoreFormat == CImageInfo::FORMAT_ALPHA)
+			StoreOglformat = GL_ALPHA;
+	}
+		
+	glGenTextures(1, &m_aTextures[Tex].m_Tex);
+	glBindTexture(GL_TEXTURE_2D, m_aTextures[Tex].m_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, StoreOglformat, Width, Height, Oglformat, GL_UNSIGNED_BYTE, pTexData);
+	
+	// calculate memory usage
+	{
+		int PixelSize = 4;
+		if(StoreFormat == CImageInfo::FORMAT_RGB)
+			PixelSize = 3;
+		else if(StoreFormat == CImageInfo::FORMAT_ALPHA)
+			PixelSize = 1;
+
+		m_aTextures[Tex].m_MemSize = Width*Height*PixelSize;
+		if(Mipmap)
+		{
+			while(Width > 2 && Height > 2)
+			{
+				Width>>=1;
+				Height>>=1;
+				m_aTextures[Tex].m_MemSize += Width*Height*PixelSize;
+			}
+		}
+	}
+	
+	m_TextureMemoryUsage += m_aTextures[Tex].m_MemSize;
+	mem_free(pTmpData);
+	return Tex;
+}
+
+// simple uncompressed RGBA loaders
+int CGraphics_OpenGL::LoadTexture(const char *pFilename, int StoreFormat, int Flags)
+{
+	int l = str_length(pFilename);
+	int Id;
+	CImageInfo Img;
+	
+	if(l < 3)
+		return -1;
+	if(LoadPNG(&Img, pFilename))
+	{
+		if (StoreFormat == CImageInfo::FORMAT_AUTO)
+			StoreFormat = Img.m_Format;
+
+		Id = LoadTextureRaw(Img.m_Width, Img.m_Height, Img.m_Format, Img.m_pData, StoreFormat, Flags);
+		mem_free(Img.m_pData);
+		return Id;
+	}
+	
+	return m_InvalidTexture;
+}
+
+int CGraphics_OpenGL::LoadPNG(CImageInfo *pImg, const char *pFilename)
+{
+	char aCompleteFilename[512];
+	unsigned char *pBuffer;
+	png_t Png; // ignore_convention
+	
+	// open file for reading
+	png_init(0,0); // ignore_convention
+
+	IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, aCompleteFilename, sizeof(aCompleteFilename));
+	if(File)
+		io_close(File);
+	
+	if(png_open_file(&Png, aCompleteFilename) != PNG_NO_ERROR) // ignore_convention
+	{
+		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)) // ignore_convention
+	{
+		dbg_msg("game/png", "invalid format. filename='%s'", aCompleteFilename);
+		png_close_file(&Png); // ignore_convention
+		return 0;
+	}
+		
+	pBuffer = (unsigned char *)mem_alloc(Png.width * Png.height * Png.bpp, 1); // ignore_convention
+	png_get_data(&Png, pBuffer); // ignore_convention
+	png_close_file(&Png); // ignore_convention
+	
+	pImg->m_Width = Png.width; // ignore_convention
+	pImg->m_Height = Png.height; // ignore_convention
+	if(Png.color_type == PNG_TRUECOLOR) // ignore_convention
+		pImg->m_Format = CImageInfo::FORMAT_RGB;
+	else if(Png.color_type == PNG_TRUECOLOR_ALPHA) // ignore_convention
+		pImg->m_Format = CImageInfo::FORMAT_RGBA;
+	pImg->m_pData = pBuffer;
+	return 1;
+}
+
+void CGraphics_OpenGL::ScreenshotDirect(const char *pFilename)
+{
+	// fetch image data
+	int y;
+	int w = m_ScreenWidth;
+	int h = m_ScreenHeight;
+	unsigned char *pPixelData = (unsigned char *)mem_alloc(w*(h+1)*4, 1);
+	unsigned char *pTempRow = pPixelData+w*h*4;
+	glReadPixels(0,0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pPixelData);
+	
+	// flip the pixel because opengl works from bottom left corner
+	for(y = 0; y < h/2; y++)
+	{
+		mem_copy(pTempRow, pPixelData+y*w*4, w*4);
+		mem_copy(pPixelData+y*w*4, pPixelData+(h-y-1)*w*4, w*4);
+		mem_copy(pPixelData+(h-y-1)*w*4, pTempRow,w*4);
+	}
+	
+	// find filename
+	{
+		char aWholePath[1024];
+		png_t Png; // ignore_convention
+
+		IOHANDLE File  = m_pStorage->OpenFile(pFilename, IOFLAG_WRITE, aWholePath, sizeof(aWholePath));
+		if(File)
+			io_close(File);
+	
+		// save png
+		dbg_msg("client", "saved screenshot to '%s'", aWholePath);
+		png_open_file_write(&Png, aWholePath); // ignore_convention
+		png_set_data(&Png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pPixelData); // ignore_convention
+		png_close_file(&Png); // ignore_convention
+	}
+
+	// clean up
+	mem_free(pPixelData);
+}
+
+void CGraphics_OpenGL::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].m_Tex);
+	}
+}
+
+void CGraphics_OpenGL::Clear(float r, float g, float b)
+{
+	glClearColor(r,g,b,0.0f);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+void CGraphics_OpenGL::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);
+}
+
+void CGraphics_OpenGL::QuadsEnd()
+{
+	dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_end without begin");
+	Flush();
+	m_Drawing = 0;
+}
+
+void CGraphics_OpenGL::QuadsSetRotation(float Angle)
+{
+	dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetRotation without begin");
+	m_Rotation = Angle;
+}
+
+void CGraphics_OpenGL::SetColorVertex(const CColorVertex *pArray, int Num)
+{
+	dbg_assert(m_Drawing != 0, "called gfx_quads_setcolorvertex without begin");
+
+	for(int i = 0; i < Num; ++i)
+	{
+		m_aColor[pArray[i].m_Index].r = pArray[i].m_R;
+		m_aColor[pArray[i].m_Index].g = pArray[i].m_G;
+		m_aColor[pArray[i].m_Index].b = pArray[i].m_B;
+		m_aColor[pArray[i].m_Index].a = pArray[i].m_A;
+	}
+}
+
+void CGraphics_OpenGL::SetColor(float r, float g, float b, float a)
+{
+	dbg_assert(m_Drawing != 0, "called gfx_quads_setcolor without begin");
+	CColorVertex Array[4] = {
+		CColorVertex(0, r, g, b, a),
+		CColorVertex(1, r, g, b, a),
+		CColorVertex(2, r, g, b, a),
+		CColorVertex(3, r, g, b, a)};
+	SetColorVertex(Array, 4);
+}
+
+void CGraphics_OpenGL::QuadsSetSubset(float TlU, float TlV, float BrU, float BrV)
+{
+	dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetSubset without begin");
+
+	m_aTexture[0].u = TlU;	m_aTexture[1].u = BrU;
+	m_aTexture[0].v = TlV;	m_aTexture[1].v = TlV;
+
+	m_aTexture[3].u = TlU;	m_aTexture[2].u = BrU;
+	m_aTexture[3].v = BrV;	m_aTexture[2].v = BrV;
+}
+
+void CGraphics_OpenGL::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;
+}
+
+void CGraphics_OpenGL::QuadsDraw(CQuadItem *pArray, int Num)
+{
+	for(int i = 0; i < Num; ++i)
+	{
+		pArray[i].m_X -= pArray[i].m_Width/2;
+		pArray[i].m_Y -= pArray[i].m_Height/2;
+	}
+
+	QuadsDrawTL(pArray, Num);
+}
+
+void CGraphics_OpenGL::QuadsDrawTL(const CQuadItem *pArray, int Num)
+{
+	CPoint Center;
+
+	dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw without begin");
+
+	for(int i = 0; i < Num; ++i)
+	{
+		Center.x = pArray[i].m_X + pArray[i].m_Width/2;
+		Center.y = pArray[i].m_Y + pArray[i].m_Height/2;
+		Center.z = 0;
+		
+		m_aVertices[m_NumVertices + 4*i].m_Pos.x = pArray[i].m_X;
+		m_aVertices[m_NumVertices + 4*i].m_Pos.y = pArray[i].m_Y;
+		m_aVertices[m_NumVertices + 4*i].m_Tex = m_aTexture[0];
+		m_aVertices[m_NumVertices + 4*i].m_Color = m_aColor[0];
+
+		m_aVertices[m_NumVertices + 4*i + 1].m_Pos.x = pArray[i].m_X + pArray[i].m_Width;
+		m_aVertices[m_NumVertices + 4*i + 1].m_Pos.y = pArray[i].m_Y;
+		m_aVertices[m_NumVertices + 4*i + 1].m_Tex = m_aTexture[1];
+		m_aVertices[m_NumVertices + 4*i + 1].m_Color = m_aColor[1];
+
+		m_aVertices[m_NumVertices + 4*i + 2].m_Pos.x = pArray[i].m_X + pArray[i].m_Width;
+		m_aVertices[m_NumVertices + 4*i + 2].m_Pos.y = pArray[i].m_Y + pArray[i].m_Height;
+		m_aVertices[m_NumVertices + 4*i + 2].m_Tex = m_aTexture[2];
+		m_aVertices[m_NumVertices + 4*i + 2].m_Color = m_aColor[2];
+
+		m_aVertices[m_NumVertices + 4*i + 3].m_Pos.x = pArray[i].m_X;
+		m_aVertices[m_NumVertices + 4*i + 3].m_Pos.y = pArray[i].m_Y + pArray[i].m_Height;
+		m_aVertices[m_NumVertices + 4*i + 3].m_Tex = m_aTexture[3];
+		m_aVertices[m_NumVertices + 4*i + 3].m_Color = m_aColor[3];
+
+		if(m_Rotation != 0)
+			Rotate4(&Center, &m_aVertices[m_NumVertices + 4*i]);
+	}
+
+	AddVertices(4*Num);
+}
+
+void CGraphics_OpenGL::QuadsDrawFreeform(const CFreeformItem *pArray, int Num)
+{
+	dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw_freeform without begin");
+	
+	for(int i = 0; i < Num; ++i)
+	{
+		m_aVertices[m_NumVertices + 4*i].m_Pos.x = pArray[i].m_X0;
+		m_aVertices[m_NumVertices + 4*i].m_Pos.y = pArray[i].m_Y0;
+		m_aVertices[m_NumVertices + 4*i].m_Tex = m_aTexture[0];
+		m_aVertices[m_NumVertices + 4*i].m_Color = m_aColor[0];
+
+		m_aVertices[m_NumVertices + 4*i + 1].m_Pos.x = pArray[i].m_X1;
+		m_aVertices[m_NumVertices + 4*i + 1].m_Pos.y = pArray[i].m_Y1;
+		m_aVertices[m_NumVertices + 4*i + 1].m_Tex = m_aTexture[1];
+		m_aVertices[m_NumVertices + 4*i + 1].m_Color = m_aColor[1];
+
+		m_aVertices[m_NumVertices + 4*i + 2].m_Pos.x = pArray[i].m_X3;
+		m_aVertices[m_NumVertices + 4*i + 2].m_Pos.y = pArray[i].m_Y3;
+		m_aVertices[m_NumVertices + 4*i + 2].m_Tex = m_aTexture[3];
+		m_aVertices[m_NumVertices + 4*i + 2].m_Color = m_aColor[3];
+
+		m_aVertices[m_NumVertices + 4*i + 3].m_Pos.x = pArray[i].m_X2;
+		m_aVertices[m_NumVertices + 4*i + 3].m_Pos.y = pArray[i].m_Y2;
+		m_aVertices[m_NumVertices + 4*i + 3].m_Tex = m_aTexture[2];
+		m_aVertices[m_NumVertices + 4*i + 3].m_Color = m_aColor[2];
+	}
+	
+	AddVertices(4*Num);
+}
+
+void CGraphics_OpenGL::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);
+			
+			CQuadItem QuadItem(x, y, Size, Size);
+			QuadsDrawTL(&QuadItem, 1);
+			x += Size/2;
+		}
+	}
+	
+	QuadsEnd();
+}
+
+bool CGraphics_OpenGL::Init()
+{
+	m_pStorage = Kernel()->RequestInterface<IStorage>();
+	
+	// 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].m_Next = i+1;
+	m_aTextures[MAX_TEXTURES-1].m_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,CImageInfo::FORMAT_RGBA,aNullTextureData,CImageInfo::FORMAT_RGBA,TEXLOAD_NORESAMPLE);
+	dbg_msg("", "invalid texture id: %d %d", m_InvalidTexture, m_aTextures[m_InvalidTexture].m_Tex);
+	
+	return true;
+}
+
+int CGraphics_SDL::TryInit()
+{
+	const SDL_VideoInfo *pInfo;
+	int Flags = SDL_OPENGL;
+	
+	m_ScreenWidth = g_Config.m_GfxScreenWidth;
+	m_ScreenHeight = g_Config.m_GfxScreenHeight;
+
+	pInfo = SDL_GetVideoInfo();
+	SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
+
+	// set flags
+	Flags  = SDL_OPENGL;
+	Flags |= SDL_GL_DOUBLEBUFFER;
+	Flags |= SDL_HWPALETTE;
+	if(g_Config.m_DbgResizable)
+		Flags |= SDL_RESIZABLE;
+
+	if(pInfo->hw_available) // ignore_convention
+		Flags |= SDL_HWSURFACE;
+	else
+		Flags |= SDL_SWSURFACE;
+
+	if(pInfo->blit_hw) // ignore_convention
+		Flags |= SDL_HWACCEL;
+
+	if(g_Config.m_GfxFullscreen)
+		Flags |= SDL_FULLSCREEN;
+
+	// set gl attributes
+	if(g_Config.m_GfxFsaaSamples)
+	{
+		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
+		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, g_Config.m_GfxFsaaSamples);
+	}
+	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, g_Config.m_GfxVsync);
+
+	// 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 CGraphics_SDL::InitWindow()
+{
+	if(TryInit() == 0)
+		return 0;
+	
+	// try disabling fsaa
+	while(g_Config.m_GfxFsaaSamples)
+	{
+		g_Config.m_GfxFsaaSamples--;
+		
+		if(g_Config.m_GfxFsaaSamples)
+			dbg_msg("gfx", "lowering FSAA to %d and trying again", g_Config.m_GfxFsaaSamples);
+		else
+			dbg_msg("gfx", "disabling FSAA and trying again");
+
+		if(TryInit() == 0)
+			return 0;
+	}
+
+	// try lowering the resolution
+	if(g_Config.m_GfxScreenWidth != 640 || g_Config.m_GfxScreenHeight != 480)
+	{
+		dbg_msg("gfx", "setting resolution to 640x480 and trying again");
+		g_Config.m_GfxScreenWidth = 640;
+		g_Config.m_GfxScreenHeight = 480;
+
+		if(TryInit() == 0)
+			return 0;
+	}
+
+	dbg_msg("gfx", "out of ideas. failed to init graphics");
+					
+	return -1;		
+}
+
+
+CGraphics_SDL::CGraphics_SDL()
+{
+	m_pScreenSurface = 0;
+}
+
+bool CGraphics_SDL::Init()
+{
+	{
+		int Systems = SDL_INIT_VIDEO;
+		
+		if(g_Config.m_SndEnable)
+			Systems |= SDL_INIT_AUDIO;
+
+		if(g_Config.m_ClEventthread)
+			Systems |= SDL_INIT_EVENTTHREAD;
+		
+		if(SDL_Init(Systems) < 0)
+		{
+			dbg_msg("gfx", "unable to init SDL: %s", SDL_GetError());
+			return true;
+		}
+	}
+	
+	atexit(SDL_Quit); // ignore_convention
+
+	#ifdef CONF_FAMILY_WINDOWS
+		if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) // ignore_convention
+			putenv("SDL_VIDEO_WINDOW_POS=8,27"); // ignore_convention
+	#endif
+	
+	if(InitWindow() != 0)
+		return true;
+
+	SDL_ShowCursor(0);
+		
+	CGraphics_OpenGL::Init();
+	
+	MapScreen(0,0,g_Config.m_GfxScreenWidth, g_Config.m_GfxScreenHeight);
+	return false;
+}
+
+void CGraphics_SDL::Shutdown()
+{
+	// TODO: SDL, is this correct?
+	SDL_Quit();
+}
+
+void CGraphics_SDL::Minimize()
+{
+	SDL_WM_IconifyWindow();
+}
+
+void CGraphics_SDL::Maximize()
+{
+	// TODO: SDL
+}
+
+int CGraphics_SDL::WindowActive()
+{
+	return SDL_GetAppState()&SDL_APPINPUTFOCUS;
+}
+
+int CGraphics_SDL::WindowOpen()
+{
+	return SDL_GetAppState()&SDL_APPACTIVE;
+
+}
+
+void CGraphics_SDL::TakeScreenshot()
+{
+	m_DoScreenshot = true;
+}
+
+void CGraphics_SDL::Swap()
+{
+	if(m_DoScreenshot)
+	{
+		// find filename
+		char aFilename[128];
+		static int Index = 1;
+
+		for(; Index < 10000; Index++)
+		{
+			IOHANDLE io;
+			str_format(aFilename, sizeof(aFilename), "screenshots/screenshot%05d.png", Index);
+			io = m_pStorage->OpenFile(aFilename, IOFLAG_READ);
+			if(io)
+				io_close(io);
+			else
+				break;
+		}
+
+		ScreenshotDirect(aFilename);
+		m_DoScreenshot = false;
+	}
+	
+	SDL_GL_SwapBuffers();
+	
+	if(g_Config.m_GfxFinish)
+		glFinish();		
+}
+
+
+int CGraphics_SDL::GetVideoModes(CVideoMode *pModes, int MaxModes)
+{
+	int NumModes = sizeof(g_aFakeModes)/sizeof(CVideoMode);
+	SDL_Rect **ppModes;
+
+	if(g_Config.m_GfxDisplayAllModes)
+	{
+		int Count = sizeof(g_aFakeModes)/sizeof(CVideoMode);
+		mem_copy(pModes, g_aFakeModes, sizeof(g_aFakeModes));
+		if(MaxModes < Count)
+			Count = MaxModes;
+		return Count;
+	}
+	
+	// TODO: fix this code on osx or windows
+		
+	ppModes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN);
+	if(ppModes == NULL)
+	{
+		// no modes
+		NumModes = 0;
+	}
+	else if(ppModes == (SDL_Rect**)-1)
+	{
+		// all modes
+	}
+	else
+	{
+		NumModes = 0;
+		for(int i = 0; ppModes[i]; ++i)
+		{
+			if(NumModes == MaxModes)
+				break;
+			pModes[NumModes].m_Width = ppModes[i]->w;
+			pModes[NumModes].m_Height = ppModes[i]->h;
+			pModes[NumModes].m_Red = 8;
+			pModes[NumModes].m_Green = 8;
+			pModes[NumModes].m_Blue = 8;
+			NumModes++;
+		}
+	}
+	
+	return NumModes;
+}
+
+extern IEngineGraphics *CreateEngineGraphics() { return new CGraphics_SDL(); }
diff --git a/src/engine/client/graphics.h b/src/engine/client/graphics.h
index 80dbf1b9..ff4c3562 100644
--- a/src/engine/client/graphics.h
+++ b/src/engine/client/graphics.h
@@ -1,69 +1,143 @@
-#include "../e_if_gfx.h"
+#ifndef ENGINE_CLIENT_GRAPHICS_H
+#define ENGINE_CLIENT_GRAPHICS_H
 
-class IGraphics
+class CGraphics_OpenGL : public IEngineGraphics
 {
 protected:
-	int m_ScreenWidth;
-	int m_ScreenHeight;
-public:
-	virtual ~IGraphics() {}
-	
-	int ScreenWidth() const { return m_ScreenWidth; }
-	int ScreenHeight() const { return m_ScreenHeight; }
-	float ScreenAspect() const { return (float)ScreenWidth()/(float)ScreenHeight(); }
-	
-	virtual void Clear(float r, float g, float b) = 0;
-	
-	virtual void ClipEnable(int x, int y, int w, int h) = 0;
-	virtual void ClipDisable() = 0;
-	
-	virtual void MapScreen(float tl_x, float tl_y, float br_x, float br_y) = 0;
-	virtual void GetScreen(float *tl_x, float *tl_y, float *br_x, float *br_y) = 0;
+	class IStorage *m_pStorage;
 	
-	virtual void BlendNone() = 0;
-	virtual void BlendNormal() = 0;
-	virtual void BlendAdditive() = 0;
-	
-	virtual int LoadPNG(IMAGE_INFO *pImg, const char *pFilename) =0;
-	virtual int UnloadTexture(int Index) = 0;
-	virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) = 0;
-	virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags) = 0;
-	virtual void TextureSet(int TextureID) = 0;
+	//
+	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;
 	
-	virtual void LinesBegin() = 0;
-	virtual void LinesEnd() = 0;
-	virtual void LinesDraw(float x0, float y0, float x1, float y1) = 0;
+	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 m_Tex;
+		int m_MemSize;
+		int m_Flags;
+		int m_Next;
+	};
+
+	CTexture m_aTextures[MAX_TEXTURES];
+	int m_FirstFreeTexture;
+	int m_TextureMemoryUsage;
+
+	void Flush();
+	void AddVertices(int Count);
+	void Rotate4(CPoint *pCenter, CVertex *pPoints);
 	
-	virtual void QuadsBegin() = 0;
-	virtual void QuadsEnd() = 0;
-	virtual void QuadsSetRotation(float Angle) = 0;
-	virtual void QuadsSetSubset(float tl_u, float tl_v, float br_u, float br_v) = 0;
-	virtual void QuadsSetSubsetFree(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) = 0;
+	static unsigned char Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset);
+public:
+	CGraphics_OpenGL();
 	
-	virtual void QuadsDraw(float x, float y, float w, float h) = 0;
-	virtual void QuadsDrawTL(float x, float y, float w, float h) = 0;
-	virtual void QuadsDrawFreeform(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) = 0;
-	virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText) = 0;
+	virtual void ClipEnable(int x, int y, int w, int h);
+	virtual void ClipDisable();
+		
+	virtual void BlendNone();
+	virtual void BlendNormal();
+	virtual void BlendAdditive();
+
+	virtual int MemoryUsage() const;
+		
+	virtual void MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY);
+	virtual void GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY);
+
+	virtual void LinesBegin();
+	virtual void LinesEnd();
+	virtual void LinesDraw(const CLineItem *pArray, int Num);
 	
-	virtual void SetColorVertex(int i, float r, float g, float b, float a) = 0;
-	virtual void SetColor(float r, float g, float b, float a) = 0;
+	virtual int UnloadTexture(int Index);
+	virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags);
+
+	// simple uncompressed RGBA loaders
+	virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags);
+	virtual int LoadPNG(CImageInfo *pImg, const char *pFilename);
+
+	void ScreenshotDirect(const char *pFilename);
+
+	virtual void TextureSet(int TextureID);
+
+	virtual void Clear(float r, float g, float b);
+
+	virtual void QuadsBegin();
+	virtual void QuadsEnd();
+	virtual void QuadsSetRotation(float Angle);
+
+	virtual void SetColorVertex(const CColorVertex *pArray, int Num);
+	virtual void SetColor(float r, float g, float b, float a);
+
+	virtual void QuadsSetSubset(float TlU, float TlV, float BrU, float BrV);
+	virtual void QuadsSetSubsetFree(
+		float x0, float y0, float x1, float y1,
+		float x2, float y2, float x3, float y3);
+
+	virtual void QuadsDraw(CQuadItem *pArray, int Num);
+	virtual void QuadsDrawTL(const CQuadItem *pArray, int Num);
+	virtual void QuadsDrawFreeform(const CFreeformItem *pArray, int Num);
+	virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText);
 	
-	virtual void TakeScreenshot() = 0;
+	virtual bool Init();
 };
 
-class IEngineGraphics : public IGraphics
+class CGraphics_SDL : public CGraphics_OpenGL
 {
-public:
-	virtual bool Init() = 0;
-	virtual void Shutdown() = 0;
-	virtual void Swap() = 0;
-	
-	virtual void Minimize() = 0;
-	virtual void Maximize() = 0;
+	SDL_Surface *m_pScreenSurface;	
 	
-	virtual int WindowActive() = 0;
-	virtual int WindowOpen() = 0;
+	int TryInit();
+	int InitWindow();
+public:
+	CGraphics_SDL();
+
+	virtual bool Init();
+	virtual void Shutdown();
+
+	virtual void Minimize();
+	virtual void Maximize();
+
+	virtual int WindowActive();
+	virtual int WindowOpen();
+
+	virtual void TakeScreenshot();
+	virtual void Swap();
+
+	virtual int GetVideoModes(CVideoMode *pModes, int MaxModes);
 	
 };
 
-extern IEngineGraphics *CreateEngineGraphics();
+#endif
diff --git a/src/engine/client/input.cpp b/src/engine/client/input.cpp
new file mode 100644
index 00000000..9f546226
--- /dev/null
+++ b/src/engine/client/input.cpp
@@ -0,0 +1,208 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+#include "SDL.h"
+
+#include <base/system.h>
+#include <engine/shared/config.h>
+#include <engine/graphics.h>
+#include <engine/input.h>
+#include <engine/keys.h>
+
+#include "input.h"
+
+//print >>f, "int inp_key_code(const char *key_name) { int i; if (!strcmp(key_name, \"-?-\")) return -1; else for (i = 0; i < 512; i++) if (!strcmp(key_strings[i], key_name)) return i; return -1; }"
+
+// this header is protected so you don't include it from anywere
+#define KEYS_INCLUDE
+#include "keynames.h"
+#undef KEYS_INCLUDE
+
+void CInput::AddEvent(int Unicode, int Key, int Flags)
+{
+	if(m_NumEvents != INPUT_BUFFER_SIZE)
+	{
+		m_aInputEvents[m_NumEvents].m_Unicode = Unicode;
+		m_aInputEvents[m_NumEvents].m_Key = Key;
+		m_aInputEvents[m_NumEvents].m_Flags = Flags;
+		m_NumEvents++;
+	}
+}
+
+CInput::CInput()
+{
+	mem_zero(m_aInputCount, sizeof(m_aInputCount));
+	mem_zero(m_aInputState, sizeof(m_aInputState));
+	mem_zero(m_Keys, sizeof(m_Keys));
+
+	m_InputCurrent = 0;
+	m_InputGrabbed = 0;
+
+	m_LastRelease = 0;
+	m_ReleaseDelta = -1;
+
+	m_NumEvents = 0;
+}
+
+void CInput::Init()
+{
+	m_pGraphics = Kernel()->RequestInterface<IEngineGraphics>();
+	SDL_EnableUNICODE(1);
+	SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+}
+
+void CInput::MouseRelative(int *x, int *y)
+{
+	int nx = 0, ny = 0;
+	float Sens = g_Config.m_InpMousesens/100.0f;
+
+	if(g_Config.m_InpGrab)
+		SDL_GetRelativeMouseState(&nx, &ny);
+	else
+	{
+		if(m_InputGrabbed)
+		{
+			SDL_GetMouseState(&nx,&ny);
+			SDL_WarpMouse(Graphics()->ScreenWidth()/2,Graphics()->ScreenHeight()/2);
+			nx -= Graphics()->ScreenWidth()/2; ny -= Graphics()->ScreenHeight()/2;
+		}
+	}
+
+	*x = nx*Sens;
+	*y = ny*Sens;
+}
+
+void CInput::MouseModeAbsolute()
+{
+	SDL_ShowCursor(1);
+	m_InputGrabbed = 0;
+	if(g_Config.m_InpGrab)
+		SDL_WM_GrabInput(SDL_GRAB_OFF);
+}
+
+void CInput::MouseModeRelative()
+{
+	SDL_ShowCursor(0);
+	m_InputGrabbed = 1;
+	if(g_Config.m_InpGrab)
+		SDL_WM_GrabInput(SDL_GRAB_ON);
+}
+
+int CInput::MouseDoubleClick()
+{
+	return m_ReleaseDelta < (time_freq() >> 2);
+}
+
+void CInput::ClearKeyStates()
+{
+	mem_zero(m_aInputState, sizeof(m_aInputState));
+	mem_zero(m_aInputCount, sizeof(m_aInputCount));
+}
+
+int CInput::KeyState(int Key)
+{
+	return m_aInputState[m_InputCurrent][Key];
+}
+
+void CInput::Update()
+{
+	if(m_InputGrabbed && !Graphics()->WindowActive())
+		MouseModeAbsolute();
+
+	/*if(!input_grabbed && Graphics()->WindowActive())
+		Input()->MouseModeRelative();*/
+
+	// clear and begin count on the other one
+	m_InputCurrent^=1;
+	mem_zero(&m_aInputCount[m_InputCurrent], sizeof(m_aInputCount[m_InputCurrent]));
+	mem_zero(&m_aInputState[m_InputCurrent], sizeof(m_aInputState[m_InputCurrent]));
+
+	{
+		int i;
+		Uint8 *pState = SDL_GetKeyState(&i);
+		if(i >= KEY_LAST)
+			i = KEY_LAST-1;
+		mem_copy(m_aInputState[m_InputCurrent], pState, i);
+	}
+
+	// these states must always be updated manually because they are not in the GetKeyState from SDL
+	int i = SDL_GetMouseState(NULL, NULL);
+	if(i&SDL_BUTTON(1)) m_aInputState[m_InputCurrent][KEY_MOUSE_1] = 1; // 1 is left
+	if(i&SDL_BUTTON(3)) m_aInputState[m_InputCurrent][KEY_MOUSE_2] = 1; // 3 is right
+	if(i&SDL_BUTTON(2)) m_aInputState[m_InputCurrent][KEY_MOUSE_3] = 1; // 2 is middle
+	if(i&SDL_BUTTON(4)) m_aInputState[m_InputCurrent][KEY_MOUSE_4] = 1;
+	if(i&SDL_BUTTON(5)) m_aInputState[m_InputCurrent][KEY_MOUSE_5] = 1;
+	if(i&SDL_BUTTON(6)) m_aInputState[m_InputCurrent][KEY_MOUSE_6] = 1;
+	if(i&SDL_BUTTON(7)) m_aInputState[m_InputCurrent][KEY_MOUSE_7] = 1;
+	if(i&SDL_BUTTON(8)) m_aInputState[m_InputCurrent][KEY_MOUSE_8] = 1;
+
+	{
+		SDL_Event Event;
+
+		while(SDL_PollEvent(&Event))
+		{
+			int Key = -1;
+			int Action = IInput::FLAG_PRESS;
+			switch (Event.type)
+			{
+				// handle keys
+				case SDL_KEYDOWN:
+					AddEvent(Event.key.keysym.unicode, 0, 0); // ignore_convention
+                    if(Event.key.keysym.unicode != 0 && Event.key.keysym.unicode < 256) // ignore_convention
+                    {
+                        Key = Event.key.keysym.unicode;  // ignore_convention
+                        m_Keys[Event.key.keysym.sym] = Event.key.keysym.unicode; // ignore_convention
+                    }
+                    else
+                        Key = Event.key.keysym.sym;  // ignore_convention
+					break;
+				case SDL_KEYUP:
+					Action = IInput::FLAG_RELEASE;
+					if(m_Keys[Event.key.keysym.sym] != 0) // ignore_convention
+                        Key = m_Keys[Event.key.keysym.sym]; // ignore_convention
+                    else
+                        Key = Event.key.keysym.sym; // ignore_convention
+					break;
+
+				// handle mouse buttons
+				case SDL_MOUSEBUTTONUP:
+					Action = IInput::FLAG_RELEASE;
+
+					if(Event.button.button == 1) // ignore_convention
+					{
+						m_ReleaseDelta = time_get() - m_LastRelease;
+						m_LastRelease = time_get();
+					}
+
+					// fall through
+				case SDL_MOUSEBUTTONDOWN:
+					if(Event.button.button == SDL_BUTTON_LEFT) Key = KEY_MOUSE_1; // ignore_convention
+					if(Event.button.button == SDL_BUTTON_RIGHT) Key = KEY_MOUSE_2; // ignore_convention
+					if(Event.button.button == SDL_BUTTON_MIDDLE) Key = KEY_MOUSE_3; // ignore_convention
+					if(Event.button.button == SDL_BUTTON_WHEELUP) Key = KEY_MOUSE_WHEEL_UP; // ignore_convention
+					if(Event.button.button == SDL_BUTTON_WHEELDOWN) Key = KEY_MOUSE_WHEEL_DOWN; // ignore_convention
+					if(Event.button.button == 6) Key = KEY_MOUSE_6; // ignore_convention
+					if(Event.button.button == 7) Key = KEY_MOUSE_7; // ignore_convention
+					if(Event.button.button == 8) Key = KEY_MOUSE_8; // ignore_convention
+					break;
+
+				// other messages
+				case SDL_QUIT:
+					// TODO: cleaner exit
+					exit(0); // ignore_convention
+					break;
+			}
+
+			//
+			if(Key != -1)
+			{
+				m_aInputCount[m_InputCurrent][Key].m_Presses++;
+				if(Action == IInput::FLAG_PRESS)
+					m_aInputState[m_InputCurrent][Key] = 1;
+				AddEvent(0, Key, Action);
+			}
+
+		}
+	}
+}
+
+
+IEngineInput *CreateEngineInput() { return new CInput; }
diff --git a/src/engine/client/input.h b/src/engine/client/input.h
new file mode 100644
index 00000000..bf7739ab
--- /dev/null
+++ b/src/engine/client/input.h
@@ -0,0 +1,37 @@
+#ifndef ENGINE_CLIENT_INPUT_H
+#define ENGINE_CLIENT_INPUT_H
+
+class CInput : public IEngineInput
+{
+	IEngineGraphics *m_pGraphics;
+
+	int m_InputGrabbed;
+
+	unsigned int m_LastRelease;
+	unsigned int m_ReleaseDelta;
+
+	int m_Keys[1024];
+
+	void AddEvent(int Unicode, int Key, int Flags);
+
+	IEngineGraphics *Graphics() { return m_pGraphics; }
+
+public:
+	CInput();
+
+	virtual void Init();
+
+	virtual void MouseRelative(int *x, int *y);
+	virtual void MouseModeAbsolute();
+	virtual void MouseModeRelative();
+	virtual int MouseDoubleClick();
+
+	void ClearKeyStates();
+	int KeyState(int Key);
+
+	int ButtonPressed(int Button) { return m_aInputState[m_InputCurrent][Button]; }
+
+	virtual void Update();
+};
+
+#endif
diff --git a/src/engine/e_keynames.cpp b/src/engine/client/keynames.h
index c81744b9..2f159a5a 100644
--- a/src/engine/e_keynames.cpp
+++ b/src/engine/client/keynames.h
@@ -1,10 +1,13 @@
 /* AUTO GENERATED! DO NOT EDIT MANUALLY! */
 
+#ifndef KEYS_INCLUDE
+#error do not include this header!
+#endif
+
 #include <string.h>
 
-static const char key_strings[512][16] =
+const char g_aaKeyStrings[512][16] =
 {
-
 	"first",
 	"&1",
 	"&2",
@@ -519,6 +522,3 @@ static const char key_strings[512][16] =
 	"&511",
 };
 
-const char *inp_key_name(int k) { if (k >= 0 && k < 512) return key_strings[k]; else return key_strings[0]; }
-int inp_key_code(const char *key_name) { int i; if (!strcmp(key_name, "-?-")) return -1; else for (i = 0; i < 512; i++) if (!strcmp(key_strings[i], key_name)) return i; return -1; }
-
diff --git a/src/engine/client/sound.cpp b/src/engine/client/sound.cpp
new file mode 100644
index 00000000..df8fa66b
--- /dev/null
+++ b/src/engine/client/sound.cpp
@@ -0,0 +1,485 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+#include <base/system.h>
+#include <engine/shared/config.h>
+
+#include "SDL.h"
+
+#include "sound.h"
+
+extern "C" { // wavpack
+	#include <engine/external/wavpack/wavpack.h>
+}
+#include <math.h>
+
+enum
+{
+	NUM_SAMPLES = 512,
+	NUM_VOICES = 64,
+	NUM_CHANNELS = 16,
+	
+	MAX_FRAMES = 1024
+};
+
+struct CSample
+{
+	short *m_pData;
+	int m_NumFrames;
+	int m_Rate;
+	int m_Channels;
+	int m_LoopStart;
+	int m_LoopEnd;
+};
+
+struct CChannel
+{
+	int m_Vol;
+	int m_Pan;
+} ;
+
+struct CVoice
+{
+	CSample *m_pSample;
+	CChannel *m_pChannel;
+	int m_Tick;
+	int m_Vol; // 0 - 255
+	int m_Flags;
+	int m_X, m_Y;
+} ;
+
+static CSample m_aSamples[NUM_SAMPLES] = { {0} };
+static CVoice m_aVoices[NUM_VOICES] = { {0} };
+static CChannel m_aChannels[NUM_CHANNELS] = { {255, 0} };
+
+static LOCK m_SoundLock = 0;
+static int m_SoundEnabled = 0;
+
+static int m_CenterX = 0;
+static int m_CenterY = 0;
+
+static int m_MixingRate = 48000;
+static volatile int m_SoundVolume = 100;
+
+static int m_NextVoice = 0;
+
+
+// TODO: there should be a faster way todo this
+static short Int2Short(int i)
+{
+	if(i > 0x7fff)
+		return 0x7fff;
+	else if(i < -0x7fff)
+		return -0x7fff;
+	return i;
+}
+
+static int IntAbs(int i)
+{
+	if(i<0)
+		return -i;
+	return i;
+}
+
+static void Mix(short *pFinalOut, unsigned Frames)
+{
+	int aMixBuffer[MAX_FRAMES*2] = {0};
+	int MasterVol;
+
+	// aquire lock while we are mixing
+	lock_wait(m_SoundLock);
+	
+	MasterVol = m_SoundVolume;
+	
+	for(unsigned i = 0; i < NUM_VOICES; i++)
+	{
+		if(m_aVoices[i].m_pSample)
+		{
+			// mix voice
+			CVoice *v = &m_aVoices[i];
+			int *pOut = aMixBuffer;
+
+			int Step = v->m_pSample->m_Channels; // setup input sources
+			short *pInL = &v->m_pSample->m_pData[v->m_Tick*Step];
+			short *pInR = &v->m_pSample->m_pData[v->m_Tick*Step+1];
+			
+			unsigned End = v->m_pSample->m_NumFrames-v->m_Tick;
+
+			int Rvol = v->m_pChannel->m_Vol;
+			int Lvol = v->m_pChannel->m_Vol;
+
+			// make sure that we don't go outside the sound data
+			if(Frames < End)
+				End = Frames;
+			
+			// check if we have a mono sound
+			if(v->m_pSample->m_Channels == 1)
+				pInR = pInL;
+
+			// volume calculation
+			if(v->m_Flags&ISound::FLAG_POS && v->m_pChannel->m_Pan)
+			{
+				// TODO: we should respect the channel panning value
+				const int Range = 1500; // magic value, remove
+				int dx = v->m_X - m_CenterX;
+				int dy = v->m_Y - m_CenterY;
+				int Dist = sqrtf((float)dx*dx+dy*dy); // float here. nasty
+				int p = IntAbs(dx);
+				if(Dist < Range)
+				{
+					// panning
+					if(dx > 0)
+						Lvol = ((Range-p)*Lvol)/Range;
+					else
+						Rvol = ((Range-p)*Rvol)/Range;
+					
+					// falloff
+					Lvol = (Lvol*(Range-Dist))/Range;
+					Rvol = (Rvol*(Range-Dist))/Range;
+				}
+				else
+				{
+					Lvol = 0;
+					Rvol = 0;
+				}
+			}
+
+			// process all frames
+			for(unsigned s = 0; s < End; s++)
+			{
+				*pOut++ += (*pInL)*Lvol;
+				*pOut++ += (*pInR)*Rvol;
+				pInL += Step;
+				pInR += Step;
+				v->m_Tick++;
+			}
+			
+			// free voice if not used any more
+			if(v->m_Tick == v->m_pSample->m_NumFrames)
+				v->m_pSample = 0;
+			
+		}
+	}
+	
+	
+	// release the lock
+	lock_release(m_SoundLock);
+
+	{
+		// clamp accumulated values
+		// TODO: this seams slow
+		for(unsigned i = 0; i < Frames; i++)
+		{
+			int j = i<<1;
+			int vl = ((aMixBuffer[j]*MasterVol)/101)>>8;
+			int vr = ((aMixBuffer[j+1]*MasterVol)/101)>>8;
+
+			pFinalOut[j] = Int2Short(vl);
+			pFinalOut[j+1] = Int2Short(vr);
+		}
+	}
+
+#if defined(CONF_ARCH_ENDIAN_BIG)
+	swap_endian(pFinalOut, sizeof(short), Frames * 2);
+#endif
+}
+
+static void SdlCallback(void *pUnused, Uint8 *pStream, int Len)
+{
+	(void)pUnused;
+	Mix((short *)pStream, Len/2/2);
+}
+
+
+int CSound::Init()
+{
+	m_pGraphics = Kernel()->RequestInterface<IEngineGraphics>();
+	m_pStorage = Kernel()->RequestInterface<IStorage>();
+	
+	SDL_AudioSpec Format;
+	
+	m_SoundLock = lock_create();
+	
+	if(!g_Config.m_SndEnable)
+		return 0;
+	
+	m_MixingRate = g_Config.m_SndRate;
+
+	// Set 16-bit stereo audio at 22Khz
+	Format.freq = g_Config.m_SndRate; // ignore_convention
+	Format.format = AUDIO_S16; // ignore_convention
+	Format.channels = 2; // ignore_convention
+	Format.samples = g_Config.m_SndBufferSize; // ignore_convention
+	Format.callback = SdlCallback; // ignore_convention
+	Format.userdata = NULL; // ignore_convention
+
+	// Open the audio device and start playing sound!
+	if(SDL_OpenAudio(&Format, NULL) < 0)
+	{
+		dbg_msg("client/sound", "unable to open audio: %s", SDL_GetError());
+		return -1;
+	}
+	else
+		dbg_msg("client/sound", "sound init successful");
+
+	SDL_PauseAudio(0);
+	
+	m_SoundEnabled = 1;
+	Update(); // update the volume
+	return 0;
+}
+
+int CSound::Update()
+{
+	// update volume
+	int WantedVolume = g_Config.m_SndVolume;
+	
+	if(!m_pGraphics->WindowActive() && g_Config.m_SndNonactiveMute)
+		WantedVolume = 0;
+	
+	if(WantedVolume != m_SoundVolume)
+	{
+		lock_wait(m_SoundLock);
+		m_SoundVolume = WantedVolume;
+		lock_release(m_SoundLock);
+	}
+	
+	return 0;
+}
+
+int CSound::Shutdown()
+{
+	SDL_CloseAudio();
+	lock_destroy(m_SoundLock);
+	return 0;
+}
+
+int CSound::AllocId()
+{
+	// TODO: linear search, get rid of it
+	for(unsigned SampleId = 0; SampleId < NUM_SAMPLES; SampleId++)
+	{
+		if(m_aSamples[SampleId].m_pData == 0x0)
+			return SampleId;
+	}
+
+	return -1;
+}
+
+void CSound::RateConvert(int SampleId)
+{
+	CSample *pSample = &m_aSamples[SampleId];
+	int NumFrames = 0;
+	short *pNewData = 0;
+	
+	// make sure that we need to convert this sound
+	if(!pSample->m_pData || pSample->m_Rate == m_MixingRate)
+		return;
+
+	// allocate new data
+	NumFrames = (int)((pSample->m_NumFrames/(float)pSample->m_Rate)*m_MixingRate);
+	pNewData = (short *)mem_alloc(NumFrames*pSample->m_Channels*sizeof(short), 1);
+	
+	for(int i = 0; i < NumFrames; i++)
+	{
+		// resample TODO: this should be done better, like linear atleast
+		float a = i/(float)NumFrames;
+		int f = (int)(a*pSample->m_NumFrames);
+		if(f >= pSample->m_NumFrames)
+			f = pSample->m_NumFrames-1;
+		
+		// set new data
+		if(pSample->m_Channels == 1)
+			pNewData[i] = pSample->m_pData[f];
+		else if(pSample->m_Channels == 2)
+		{
+			pNewData[i*2] = pSample->m_pData[f*2];
+			pNewData[i*2+1] = pSample->m_pData[f*2+1];
+		}
+	}
+	
+	// free old data and apply new
+	mem_free(pSample->m_pData);
+	pSample->m_pData = pNewData;
+	pSample->m_NumFrames = NumFrames;
+}
+
+int CSound::ReadData(void *pBuffer, int Size)
+{
+	return io_read(ms_File, pBuffer, Size);
+}
+
+int CSound::LoadWV(const char *pFilename)
+{
+	CSample *pSample;
+	int SampleId = -1;
+	char aError[100];
+	WavpackContext *pContext;
+	
+	// don't waste memory on sound when we are stress testing
+	if(g_Config.m_DbgStress)
+		return -1;
+		
+	// no need to load sound when we are running with no sound
+	if(!m_SoundEnabled)
+		return 1;
+		
+	if(!m_pStorage)
+		return -1;
+
+	ms_File = m_pStorage->OpenFile(pFilename, IOFLAG_READ); // TODO: use system.h stuff for this
+	if(!ms_File)
+	{
+		dbg_msg("sound/wv", "failed to open %s", pFilename);
+		return -1;
+	}
+
+	SampleId = AllocId();
+	if(SampleId < 0)
+		return -1;
+	pSample = &m_aSamples[SampleId];
+
+	pContext = WavpackOpenFileInput(ReadData, aError);
+	if (pContext)
+	{
+		int m_aSamples = WavpackGetNumSamples(pContext);
+		int BitsPerSample = WavpackGetBitsPerSample(pContext);
+		unsigned int SampleRate = WavpackGetSampleRate(pContext);
+		int m_aChannels = WavpackGetNumChannels(pContext);
+		int *pData;
+		int *pSrc;
+		short *pDst;
+		int i;
+
+		pSample->m_Channels = m_aChannels;
+		pSample->m_Rate = SampleRate;
+
+		if(pSample->m_Channels > 2)
+		{
+			dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", pFilename);
+			return -1;
+		}
+
+		/*
+		if(snd->rate != 44100)
+		{
+			dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename);
+			return -1;
+		}*/
+		
+		if(BitsPerSample != 16)
+		{
+			dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", BitsPerSample, pFilename);
+			return -1;
+		}
+
+		pData = (int *)mem_alloc(4*m_aSamples*m_aChannels, 1);
+		WavpackUnpackSamples(pContext, pData, m_aSamples); // TODO: check return value
+		pSrc = pData;
+		
+		pSample->m_pData = (short *)mem_alloc(2*m_aSamples*m_aChannels, 1);
+		pDst = pSample->m_pData;
+
+		for (i = 0; i < m_aSamples*m_aChannels; i++)
+			*pDst++ = (short)*pSrc++;
+
+		mem_free(pData);
+
+		pSample->m_NumFrames = m_aSamples;
+		pSample->m_LoopStart = -1;
+		pSample->m_LoopEnd = -1;
+	}
+	else
+	{
+		dbg_msg("sound/wv", "failed to open %s: %s", pFilename, aError);
+	}
+
+	io_close(ms_File);
+	ms_File = NULL;
+
+	if(g_Config.m_Debug)
+		dbg_msg("sound/wv", "loaded %s", pFilename);
+
+	RateConvert(SampleId);
+	return SampleId;
+}
+
+void CSound::SetListenerPos(float x, float y)
+{
+	m_CenterX = (int)x;
+	m_CenterY = (int)y;
+}
+	
+
+void CSound::SetChannel(int ChannelId, float Vol, float Pan)
+{
+	m_aChannels[ChannelId].m_Vol = (int)(Vol*255.0f);
+	m_aChannels[ChannelId].m_Pan = (int)(Pan*255.0f); // TODO: this is only on and off right now
+}
+
+int CSound::Play(int ChannelId, int SampleId, int Flags, float x, float y)
+{
+	int VoiceId = -1;
+	int i;
+	
+	lock_wait(m_SoundLock);
+	
+	// search for voice
+	for(i = 0; i < NUM_VOICES; i++)
+	{
+		int id = (m_NextVoice + i) % NUM_VOICES;
+		if(!m_aVoices[id].m_pSample)
+		{
+			VoiceId = id;
+			m_NextVoice = id+1;
+			break;
+		}
+	}
+	
+	// voice found, use it
+	if(VoiceId != -1)
+	{
+		m_aVoices[VoiceId].m_pSample = &m_aSamples[SampleId];
+		m_aVoices[VoiceId].m_pChannel = &m_aChannels[ChannelId];
+		m_aVoices[VoiceId].m_Tick = 0;
+		m_aVoices[VoiceId].m_Vol = 255;
+		m_aVoices[VoiceId].m_Flags = Flags;
+		m_aVoices[VoiceId].m_X = (int)x;
+		m_aVoices[VoiceId].m_Y = (int)y;
+	}
+	
+	lock_release(m_SoundLock);
+	return VoiceId;
+}
+
+int CSound::PlayAt(int ChannelId, int SampleId, int Flags, float x, float y)
+{
+	return Play(ChannelId, SampleId, Flags|ISound::FLAG_POS, x, y);
+}
+
+int CSound::Play(int ChannelId, int SampleId, int Flags)
+{
+	return Play(ChannelId, SampleId, Flags, 0, 0);
+}
+
+void CSound::Stop(int VoiceId)
+{
+	// TODO: a nice fade out
+	lock_wait(m_SoundLock);
+	m_aVoices[VoiceId].m_pSample = 0;
+	lock_release(m_SoundLock);
+}
+
+void CSound::StopAll()
+{
+	// TODO: a nice fade out
+	lock_wait(m_SoundLock);
+	for(int i = 0; i < NUM_VOICES; i++)
+	{
+		m_aVoices[i].m_pSample = 0;
+	}
+	lock_release(m_SoundLock);
+}
+
+IOHANDLE CSound::ms_File = 0;
+
+IEngineSound *CreateEngineSound() { return new CSound; }
+
diff --git a/src/engine/client/sound.h b/src/engine/client/sound.h
new file mode 100644
index 00000000..9c94c6ad
--- /dev/null
+++ b/src/engine/client/sound.h
@@ -0,0 +1,39 @@
+#ifndef ENGINE_CLIENT_SOUND_H
+#define ENGINE_CLIENT_SOUND_H
+
+#include <engine/sound.h>
+#include <engine/storage.h>
+#include <engine/graphics.h>
+#include <engine/shared/engine.h>
+
+class CSound : public IEngineSound
+{
+public:
+	IEngineGraphics *m_pGraphics;
+	IStorage *m_pStorage;
+
+	virtual int Init();
+
+	int Update();
+	int Shutdown();
+	int AllocId();
+
+	static void RateConvert(int SampleId);
+
+	// TODO: Refactor: clean this mess up
+	static IOHANDLE ms_File;
+	static int ReadData(void *pBuffer, int Size);
+
+	virtual int LoadWV(const char *pFilename);
+
+	virtual void SetListenerPos(float x, float y);
+	virtual void SetChannel(int ChannelId, float Vol, float Pan);
+
+	int Play(int ChannelId, int SampleId, int Flags, float x, float y);
+	virtual int PlayAt(int ChannelId, int SampleId, int Flags, float x, float y);
+	virtual int Play(int ChannelId, int SampleId, int Flags);
+	virtual void Stop(int VoiceId);
+	virtual void StopAll();
+};
+
+#endif
diff --git a/src/engine/client/srvbrowse.cpp b/src/engine/client/srvbrowse.cpp
new file mode 100644
index 00000000..e0997467
--- /dev/null
+++ b/src/engine/client/srvbrowse.cpp
@@ -0,0 +1,721 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+#include <algorithm> // sort
+
+#include <base/system.h>
+#include <engine/shared/network.h>
+#include <engine/shared/protocol.h>
+#include <engine/shared/config.h>
+#include <engine/shared/memheap.h>
+#include <engine/shared/engine.h>
+
+#include <engine/masterserver.h>
+#include <engine/config.h>
+
+#include <mastersrv/mastersrv.h>
+
+#include "srvbrowse.h"
+
+class SortWrap
+{
+	typedef bool (CServerBrowser::*SortFunc)(int, int) const;
+	SortFunc m_pfnSort;
+	CServerBrowser *m_pThis;
+public:
+	SortWrap(CServerBrowser *t, SortFunc f) : m_pfnSort(f), m_pThis(t) {}
+	bool operator()(int a, int b) { return (m_pThis->*m_pfnSort)(a, b); }
+};
+
+CServerBrowser::CServerBrowser()
+{
+	m_pMasterServer = 0;
+	m_ppServerlist = 0;
+	m_pSortedServerlist = 0;
+
+	m_NumFavoriteServers = 0;
+
+	mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp));
+
+	m_pFirstReqServer = 0; // request list
+	m_pLastReqServer = 0;
+	m_NumRequests = 0;
+
+	m_NeedRefresh = 0;
+
+	m_NumSortedServers = 0;
+	m_NumSortedServersCapacity = 0;
+	m_NumServers = 0;
+	m_NumServerCapacity = 0;
+
+	m_Sorthash = 0;
+	m_aFilterString[0] = 0;
+	m_aFilterGametypeString[0] = 0;
+
+	// the token is to keep server refresh separated from each other
+	m_CurrentToken = 1;
+
+	m_ServerlistType = 0;
+	m_BroadcastTime = 0;
+}
+
+void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVersion)
+{
+	m_pNetClient = pClient;
+	str_copy(m_aNetVersion, pNetVersion, sizeof(m_aNetVersion));
+	m_pMasterServer = Kernel()->RequestInterface<IMasterServer>();
+	IConfig *pConfig = Kernel()->RequestInterface<IConfig>();
+	if(pConfig)
+		pConfig->RegisterCallback(ConfigSaveCallback, this);
+}
+
+const CServerInfo *CServerBrowser::SortedGet(int Index) const
+{
+	if(Index < 0 || Index >= m_NumSortedServers)
+		return 0;
+	return &m_ppServerlist[m_pSortedServerlist[Index]]->m_Info;
+}
+
+
+bool CServerBrowser::SortCompareName(int Index1, int Index2) const
+{
+	CServerEntry *a = m_ppServerlist[Index1];
+	CServerEntry *b = m_ppServerlist[Index2];
+	return str_comp(a->m_Info.m_aName, b->m_Info.m_aName) < 0;
+}
+
+bool CServerBrowser::SortCompareMap(int Index1, int Index2) const
+{
+	CServerEntry *a = m_ppServerlist[Index1];
+	CServerEntry *b = m_ppServerlist[Index2];
+	return str_comp(a->m_Info.m_aMap, b->m_Info.m_aMap) < 0;
+}
+
+bool CServerBrowser::SortComparePing(int Index1, int Index2) const
+{
+	CServerEntry *a = m_ppServerlist[Index1];
+	CServerEntry *b = m_ppServerlist[Index2];
+	return a->m_Info.m_Latency < b->m_Info.m_Latency;
+}
+
+bool CServerBrowser::SortCompareGametype(int Index1, int Index2) const
+{
+	CServerEntry *a = m_ppServerlist[Index1];
+	CServerEntry *b = m_ppServerlist[Index2];
+	return str_comp(a->m_Info.m_aGameType, b->m_Info.m_aGameType) < 0;
+}
+
+bool CServerBrowser::SortCompareProgression(int Index1, int Index2) const
+{
+	CServerEntry *a = m_ppServerlist[Index1];
+	CServerEntry *b = m_ppServerlist[Index2];
+	return a->m_Info.m_Progression < b->m_Info.m_Progression;
+}
+
+bool CServerBrowser::SortCompareNumPlayers(int Index1, int Index2) const
+{
+	CServerEntry *a = m_ppServerlist[Index1];
+	CServerEntry *b = m_ppServerlist[Index2];
+	return a->m_Info.m_NumPlayers < b->m_Info.m_NumPlayers;
+}
+
+void CServerBrowser::Filter()
+{
+	int i = 0, p = 0;
+	m_NumSortedServers = 0;
+
+	// allocate the sorted list
+	if(m_NumSortedServersCapacity < m_NumServers)
+	{
+		if(m_pSortedServerlist)
+			mem_free(m_pSortedServerlist);
+		m_NumSortedServersCapacity = m_NumServers;
+		m_pSortedServerlist = (int *)mem_alloc(m_NumSortedServersCapacity*sizeof(int), 1);
+	}
+
+	// filter the servers
+	for(i = 0; i < m_NumServers; i++)
+	{
+		int Filtered = 0;
+
+		if(g_Config.m_BrFilterEmpty && m_ppServerlist[i]->m_Info.m_NumPlayers == 0)
+			Filtered = 1;
+		else if(g_Config.m_BrFilterFull && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers)
+			Filtered = 1;
+		else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD)
+			Filtered = 1;
+		else if(g_Config.m_BrFilterPure &&
+			(str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 &&
+			str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 &&
+			str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0))
+		{
+			Filtered = 1;
+		}
+		else if(g_Config.m_BrFilterPureMap &&
+			!(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 ||
+			str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0)
+		)
+		{
+			Filtered = 1;
+		}
+		else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency)
+			Filtered = 1;
+		else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0)
+			Filtered = 1;
+		else
+		{
+			if(g_Config.m_BrFilterString[0] != 0)
+			{
+				int MatchFound = 0;
+
+				m_ppServerlist[i]->m_Info.m_QuickSearchHit = 0;
+
+				// match against server name
+				if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrFilterString))
+				{
+					MatchFound = 1;
+					m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME;
+				}
+
+				// match against players
+				for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumPlayers; p++)
+				{
+					if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aPlayers[p].m_aName, g_Config.m_BrFilterString))
+					{
+						MatchFound = 1;
+						m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYERNAME;
+						break;
+					}
+				}
+
+				// match against map
+				if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrFilterString))
+				{
+					MatchFound = 1;
+					m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME;
+				}
+
+				if(!MatchFound)
+					Filtered = 1;
+			}
+
+			if(!Filtered && g_Config.m_BrFilterGametype[0] != 0)
+			{
+				// match against game type
+				if(!str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
+					Filtered = 1;
+			}
+		}
+
+		if(Filtered == 0)
+			m_pSortedServerlist[m_NumSortedServers++] = i;
+	}
+}
+
+int CServerBrowser::SortHash() const
+{
+	int i = g_Config.m_BrSort&0xf;
+	i |= g_Config.m_BrFilterEmpty<<4;
+	i |= g_Config.m_BrFilterFull<<5;
+	i |= g_Config.m_BrFilterPw<<6;
+	i |= g_Config.m_BrSortOrder<<7;
+	i |= g_Config.m_BrFilterCompatversion<<8;
+	i |= g_Config.m_BrFilterPure<<9;
+	i |= g_Config.m_BrFilterPureMap<<10;
+	i |= g_Config.m_BrFilterPing<<16;
+	return i;
+}
+
+void CServerBrowser::Sort()
+{
+	int i;
+
+	// create filtered list
+	Filter();
+
+	// sort
+	if(g_Config.m_BrSort == IServerBrowser::SORT_NAME)
+		std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareName));
+	else if(g_Config.m_BrSort == IServerBrowser::SORT_PING)
+		std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortComparePing));
+	else if(g_Config.m_BrSort == IServerBrowser::SORT_MAP)
+		std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareMap));
+	else if(g_Config.m_BrSort == IServerBrowser::SORT_NUMPLAYERS)
+		std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareNumPlayers));
+	else if(g_Config.m_BrSort == IServerBrowser::SORT_GAMETYPE)
+		std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareGametype));
+	else if(g_Config.m_BrSort == IServerBrowser::SORT_PROGRESSION)
+		std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareProgression));
+
+	// invert the list if requested
+	if(g_Config.m_BrSortOrder)
+	{
+		for(i = 0; i < m_NumSortedServers/2; i++)
+		{
+			int Temp = m_pSortedServerlist[i];
+			m_pSortedServerlist[i] = m_pSortedServerlist[m_NumSortedServers-i-1];
+			m_pSortedServerlist[m_NumSortedServers-i-1] = Temp;
+		}
+	}
+
+	// set indexes
+	for(i = 0; i < m_NumSortedServers; i++)
+		m_ppServerlist[m_pSortedServerlist[i]]->m_Info.m_SortedIndex = i;
+
+	str_copy(m_aFilterGametypeString, g_Config.m_BrFilterGametype, sizeof(m_aFilterGametypeString));
+	str_copy(m_aFilterString, g_Config.m_BrFilterString, sizeof(m_aFilterString));
+	m_Sorthash = SortHash();
+}
+
+void CServerBrowser::RemoveRequest(CServerEntry *pEntry)
+{
+	if(pEntry->m_pPrevReq || pEntry->m_pNextReq || m_pFirstReqServer == pEntry)
+	{
+		if(pEntry->m_pPrevReq)
+			pEntry->m_pPrevReq->m_pNextReq = pEntry->m_pNextReq;
+		else
+			m_pFirstReqServer = pEntry->m_pNextReq;
+
+		if(pEntry->m_pNextReq)
+			pEntry->m_pNextReq->m_pPrevReq = pEntry->m_pPrevReq;
+		else
+			m_pLastReqServer = pEntry->m_pPrevReq;
+
+		pEntry->m_pPrevReq = 0;
+		pEntry->m_pNextReq = 0;
+		m_NumRequests--;
+	}
+}
+
+CServerBrowser::CServerEntry *CServerBrowser::Find(const NETADDR &Addr)
+{
+	CServerEntry *pEntry = m_aServerlistIp[Addr.ip[0]];
+
+	for(; pEntry; pEntry = pEntry->m_pNextIp)
+	{
+		if(net_addr_comp(&pEntry->m_Addr, &Addr) == 0)
+			return pEntry;
+	}
+	return (CServerEntry*)0;
+}
+
+void CServerBrowser::QueueRequest(CServerEntry *pEntry)
+{
+	// add it to the list of servers that we should request info from
+	pEntry->m_pPrevReq = m_pLastReqServer;
+	if(m_pLastReqServer)
+		m_pLastReqServer->m_pNextReq = pEntry;
+	else
+		m_pFirstReqServer = pEntry;
+	m_pLastReqServer = pEntry;
+
+	m_NumRequests++;
+}
+
+void CServerBrowser::SetInfo(CServerEntry *pEntry, const CServerInfo &Info)
+{
+	int Fav = pEntry->m_Info.m_Favorite;
+	pEntry->m_Info = Info;
+	pEntry->m_Info.m_Favorite = Fav;
+	pEntry->m_Info.m_NetAddr = pEntry->m_Addr;
+
+	// all these are just for nice compability
+	if(pEntry->m_Info.m_aGameType[0] == '0' && pEntry->m_Info.m_aGameType[1] == 0)
+		str_copy(pEntry->m_Info.m_aGameType, "DM", sizeof(pEntry->m_Info.m_aGameType));
+	else if(pEntry->m_Info.m_aGameType[0] == '1' && pEntry->m_Info.m_aGameType[1] == 0)
+		str_copy(pEntry->m_Info.m_aGameType, "TDM", sizeof(pEntry->m_Info.m_aGameType));
+	else if(pEntry->m_Info.m_aGameType[0] == '2' && pEntry->m_Info.m_aGameType[1] == 0)
+		str_copy(pEntry->m_Info.m_aGameType, "CTF", sizeof(pEntry->m_Info.m_aGameType));
+
+	/*if(!request)
+	{
+		pEntry->m_Info.latency = (time_get()-pEntry->request_time)*1000/time_freq();
+		RemoveRequest(pEntry);
+	}*/
+
+	pEntry->m_GotInfo = 1;
+	Sort();
+}
+
+CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr)
+{
+	int Hash = Addr.ip[0];
+	CServerEntry *pEntry = 0;
+	int i;
+
+	// create new pEntry
+	pEntry = (CServerEntry *)m_ServerlistHeap.Allocate(sizeof(CServerEntry));
+	mem_zero(pEntry, sizeof(CServerEntry));
+
+	// set the info
+	pEntry->m_Addr = Addr;
+	pEntry->m_Info.m_NetAddr = Addr;
+
+	pEntry->m_Info.m_Latency = 999;
+	str_format(pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), "%d.%d.%d.%d:%d",
+		Addr.ip[0], Addr.ip[1], Addr.ip[2],
+		Addr.ip[3], Addr.port);
+	str_format(pEntry->m_Info.m_aName, sizeof(pEntry->m_Info.m_aName), "\255%d.%d.%d.%d:%d", // the \255 is to make sure that it's sorted last
+		Addr.ip[0], Addr.ip[1], Addr.ip[2],
+		Addr.ip[3], Addr.port);
+
+	/*if(serverlist_type == IServerBrowser::TYPE_LAN)
+		pEntry->m_Info.latency = (time_get()-broadcast_time)*1000/time_freq();*/
+
+	// check if it's a favorite
+	for(i = 0; i < m_NumFavoriteServers; i++)
+	{
+		if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
+			pEntry->m_Info.m_Favorite = 1;
+	}
+
+	// add to the hash list
+	pEntry->m_pNextIp = m_aServerlistIp[Hash];
+	m_aServerlistIp[Hash] = pEntry;
+
+	if(m_NumServers == m_NumServerCapacity)
+	{
+		CServerEntry **ppNewlist;
+		m_NumServerCapacity += 100;
+		ppNewlist = (CServerEntry **)mem_alloc(m_NumServerCapacity*sizeof(CServerEntry*), 1);
+		mem_copy(ppNewlist, m_ppServerlist, m_NumServers*sizeof(CServerEntry*));
+		mem_free(m_ppServerlist);
+		m_ppServerlist = ppNewlist;
+	}
+
+	// add to list
+	m_ppServerlist[m_NumServers] = pEntry;
+	pEntry->m_Info.m_ServerIndex = m_NumServers;
+	m_NumServers++;
+
+	return pEntry;
+}
+
+void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo)
+{
+	CServerEntry *pEntry = 0;
+	if(Type == IServerBrowser::SET_MASTER_ADD)
+	{
+		if(m_ServerlistType != IServerBrowser::TYPE_INTERNET)
+			return;
+
+		if(!Find(Addr))
+		{
+			pEntry = Add(Addr);
+			QueueRequest(pEntry);
+		}
+	}
+	else if(Type == IServerBrowser::SET_FAV_ADD)
+	{
+		if(m_ServerlistType != IServerBrowser::TYPE_FAVORITES)
+			return;
+
+		if(!Find(Addr))
+		{
+			pEntry = Add(Addr);
+			QueueRequest(pEntry);
+		}
+	}
+	else if(Type == IServerBrowser::SET_TOKEN)
+	{
+		if(Token != m_CurrentToken)
+			return;
+
+		pEntry = Find(Addr);
+		if(!pEntry)
+			pEntry = Add(Addr);
+		if(pEntry)
+		{
+			SetInfo(pEntry, *pInfo);
+			if(m_ServerlistType == IServerBrowser::TYPE_LAN)
+				pEntry->m_Info.m_Latency = (time_get()-m_BroadcastTime)*1000/time_freq();
+			else
+				pEntry->m_Info.m_Latency = (time_get()-pEntry->m_RequestTime)*1000/time_freq();
+			RemoveRequest(pEntry);
+		}
+	}
+	else if(Type == IServerBrowser::SET_OLD_INTERNET)
+	{
+		pEntry = Find(Addr);
+		if(pEntry)
+		{
+			SetInfo(pEntry, *pInfo);
+
+			if(m_ServerlistType == IServerBrowser::TYPE_LAN)
+				pEntry->m_Info.m_Latency = (time_get()-m_BroadcastTime)*1000/time_freq();
+			else
+				pEntry->m_Info.m_Latency = (time_get()-pEntry->m_RequestTime)*1000/time_freq();
+			RemoveRequest(pEntry);
+		}
+	}
+	else if(Type == IServerBrowser::SET_OLD_LAN)
+	{
+		pEntry = Find(Addr);
+		if(pEntry)
+		if(!pEntry)
+			pEntry = Add(Addr);
+		if(pEntry)
+			SetInfo(pEntry, *pInfo);
+	}
+
+	Sort();
+}
+
+void CServerBrowser::Refresh(int Type)
+{
+	// clear out everything
+	m_ServerlistHeap.Reset();
+	m_NumServers = 0;
+	m_NumSortedServers = 0;
+	mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp));
+	m_pFirstReqServer = 0;
+	m_pLastReqServer = 0;
+	m_NumRequests = 0;
+
+	// next token
+	m_CurrentToken = (m_CurrentToken+1)&0xff;
+
+	//
+	m_ServerlistType = Type;
+
+	if(Type == IServerBrowser::TYPE_LAN)
+	{
+		unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1];
+		CNetChunk Packet;
+		int i;
+
+		mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
+		Buffer[sizeof(SERVERBROWSE_GETINFO)] = m_CurrentToken;
+
+		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;
+		m_BroadcastTime = time_get();
+
+		for(i = 8303; i <= 8310; i++)
+		{
+			Packet.m_Address.port = i;
+			m_pNetClient->Send(&Packet);
+		}
+
+		if(g_Config.m_Debug)
+			dbg_msg("client", "broadcasting for servers");
+	}
+	else if(Type == IServerBrowser::TYPE_INTERNET)
+		m_NeedRefresh = 1;
+	else if(Type == IServerBrowser::TYPE_FAVORITES)
+	{
+		for(int i = 0; i < m_NumFavoriteServers; i++)
+			Set(m_aFavoriteServers[i], IServerBrowser::SET_FAV_ADD, -1, 0);
+	}
+}
+
+void CServerBrowser::RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) const
+{
+	//unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1];
+	CNetChunk Packet;
+
+	if(g_Config.m_Debug)
+	{
+		dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d",
+			Addr.ip[0], Addr.ip[1], Addr.ip[2],
+			Addr.ip[3], Addr.port);
+	}
+
+	/*mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
+	buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;*/
+
+	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 request style aswell
+	Packet.m_DataSize = sizeof(SERVERBROWSE_OLD_GETINFO);
+	Packet.m_pData = SERVERBROWSE_OLD_GETINFO;
+	m_pNetClient->Send(&Packet);
+
+	if(pEntry)
+		pEntry->m_RequestTime = time_get();
+}
+
+void CServerBrowser::Request(const NETADDR &Addr) const
+{
+	RequestImpl(Addr, 0);
+}
+
+
+void CServerBrowser::Update()
+{
+	int64 Timeout = time_freq();
+	int64 Now = time_get();
+	int Count;
+	CServerEntry *pEntry, *pNext;
+
+	// do server list requests
+	if(m_NeedRefresh && !m_pMasterServer->IsRefreshing())
+	{
+		NETADDR Addr;
+		CNetChunk Packet;
+		int i;
+
+		m_NeedRefresh = 0;
+
+		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 < IMasterServer::MAX_MASTERSERVERS; i++)
+		{
+			Addr = m_pMasterServer->GetAddr(i);
+			if(!Addr.ip[0] && !Addr.ip[1] && !Addr.ip[2] && !Addr.ip[3])
+				continue;
+
+			Packet.m_Address = Addr;
+			m_pNetClient->Send(&Packet);
+		}
+
+		if(g_Config.m_Debug)
+			dbg_msg("client", "requesting server list");
+	}
+
+	// do timeouts
+	pEntry = m_pFirstReqServer;
+	while(1)
+	{
+		if(!pEntry) // no more entries
+			break;
+
+		pNext = pEntry->m_pNextReq;
+
+		if(pEntry->m_RequestTime && pEntry->m_RequestTime+Timeout < Now)
+		{
+			// timeout
+			RemoveRequest(pEntry);
+			m_NumRequests--;
+		}
+
+		pEntry = pNext;
+	}
+
+	// do timeouts
+	pEntry = m_pFirstReqServer;
+	Count = 0;
+	while(1)
+	{
+		if(!pEntry) // no more entries
+			break;
+
+		// no more then 10 concurrent requests
+		if(Count == g_Config.m_BrMaxRequests)
+			break;
+
+		if(pEntry->m_RequestTime == 0)
+			RequestImpl(pEntry->m_Addr, pEntry);
+
+		Count++;
+		pEntry = pEntry->m_pNextReq;
+	}
+
+	// check if we need to resort
+	// TODO: remove the str_comp
+	if(m_Sorthash != SortHash() || str_comp(m_aFilterString, g_Config.m_BrFilterString) != 0 || str_comp(m_aFilterGametypeString, g_Config.m_BrFilterGametype) != 0)
+		Sort();
+}
+
+
+bool CServerBrowser::IsFavorite(const NETADDR &Addr) const
+{
+	// search for the address
+	int i;
+	for(i = 0; i < m_NumFavoriteServers; i++)
+	{
+		if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
+			return true;
+	}
+	return false;
+}
+
+void CServerBrowser::AddFavorite(const NETADDR &Addr)
+{
+	CServerEntry *pEntry;
+
+	if(m_NumFavoriteServers == MAX_FAVORITES)
+		return;
+
+	// make sure that we don't already have the server in our list
+	for(int i = 0; i < m_NumFavoriteServers; i++)
+	{
+		if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
+			return;
+	}
+
+	// add the server to the list
+	m_aFavoriteServers[m_NumFavoriteServers++] = Addr;
+	pEntry = Find(Addr);
+	if(pEntry)
+		pEntry->m_Info.m_Favorite = 1;
+
+    if(g_Config.m_Debug)
+        dbg_msg("", "added fav, %d.%d.%d.%d:%d", Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], Addr.port);
+}
+
+void CServerBrowser::RemoveFavorite(const NETADDR &Addr)
+{
+	int i;
+	CServerEntry *pEntry;
+
+	for(i = 0; i < m_NumFavoriteServers; i++)
+	{
+		if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
+		{
+			mem_move(&m_aFavoriteServers[i], &m_aFavoriteServers[i+1], sizeof(NETADDR)*(m_NumFavoriteServers-(i+1)));
+			m_NumFavoriteServers--;
+
+			pEntry = Find(Addr);
+			if(pEntry)
+				pEntry->m_Info.m_Favorite = 0;
+
+			return;
+		}
+	}
+}
+
+
+bool CServerBrowser::IsRefreshingMasters() const
+{
+	return m_pMasterServer->IsRefreshing();
+}
+
+
+void CServerBrowser::ConfigSaveCallback(IConfig *pConfig, void *pUserData)
+{
+	CServerBrowser *pSelf = (CServerBrowser *)pUserData;
+
+	int i;
+	char aAddrStr[128];
+	char aBuffer[256];
+	for(i = 0; i < pSelf->m_NumFavoriteServers; i++)
+	{
+		net_addr_str(&pSelf->m_aFavoriteServers[i], aAddrStr, sizeof(aAddrStr));
+		str_format(aBuffer, sizeof(aBuffer), "add_favorite %s", aAddrStr);
+		pConfig->WriteLine(aBuffer);
+	}
+}
diff --git a/src/engine/client/srvbrowse.h b/src/engine/client/srvbrowse.h
new file mode 100644
index 00000000..1c255792
--- /dev/null
+++ b/src/engine/client/srvbrowse.h
@@ -0,0 +1,111 @@
+#ifndef ENGINE_CLIENT_SRVBROWSE_H
+#define ENGINE_CLIENT_SRVBROWSE_H
+
+#include <engine/serverbrowser.h>
+
+class CServerBrowser : public IServerBrowser
+{
+public:
+	class CServerEntry
+	{
+	public:
+		NETADDR m_Addr;
+		int64 m_RequestTime;
+		int m_GotInfo;
+		CServerInfo m_Info;
+
+		CServerEntry *m_pNextIp; // ip hashed list
+
+		CServerEntry *m_pPrevReq; // request list
+		CServerEntry *m_pNextReq;
+	};
+
+	enum
+	{
+		MAX_FAVORITES=256
+	};
+
+	CServerBrowser();
+
+	// interface functions
+	void Refresh(int Type);
+	bool IsRefreshingMasters() const;
+
+	int NumServers() const { return m_NumServers; }
+
+	int NumSortedServers() const { return m_NumSortedServers; }
+	const CServerInfo *SortedGet(int Index) const;
+
+	bool IsFavorite(const NETADDR &Addr) const;
+	void AddFavorite(const NETADDR &Addr);
+	void RemoveFavorite(const NETADDR &Addr);
+
+	//
+	void Update();
+	void Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo);
+	void Request(const NETADDR &Addr) const;
+
+	void SetBaseInfo(class CNetClient *pClient, const char *pNetVersion);
+
+private:
+	CNetClient *m_pNetClient;
+	IMasterServer *m_pMasterServer;
+	char m_aNetVersion[128];
+
+	CHeap m_ServerlistHeap;
+	CServerEntry **m_ppServerlist;
+	int *m_pSortedServerlist;
+
+	NETADDR m_aFavoriteServers[MAX_FAVORITES];
+	int m_NumFavoriteServers;
+
+	CServerEntry *m_aServerlistIp[256]; // ip hash list
+
+	CServerEntry *m_pFirstReqServer; // request list
+	CServerEntry *m_pLastReqServer;
+	int m_NumRequests;
+
+	int m_NeedRefresh;
+
+	int m_NumSortedServers;
+	int m_NumSortedServersCapacity;
+	int m_NumServers;
+	int m_NumServerCapacity;
+
+	int m_Sorthash;
+	char m_aFilterString[64];
+	char m_aFilterGametypeString[128];
+
+	// the token is to keep server refresh separated from each other
+	int m_CurrentToken;
+
+	int m_ServerlistType;
+	int64 m_BroadcastTime;
+
+	// sorting criterions
+	bool SortCompareName(int Index1, int Index2) const;
+	bool SortCompareMap(int Index1, int Index2) const;
+	bool SortComparePing(int Index1, int Index2) const;
+	bool SortCompareGametype(int Index1, int Index2) const;
+	bool SortCompareProgression(int Index1, int Index2) const;
+	bool SortCompareNumPlayers(int Index1, int Index2) const;
+
+	//
+	void Filter();
+	void Sort();
+	int SortHash() const;
+
+	CServerEntry *Find(const NETADDR &Addr);
+	CServerEntry *Add(const NETADDR &Addr);
+ 
+	void RemoveRequest(CServerEntry *pEntry);
+	void QueueRequest(CServerEntry *pEntry);
+
+	void RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) const;
+
+	void SetInfo(CServerEntry *pEntry, const CServerInfo &Info);
+
+	static void ConfigSaveCallback(IConfig *pConfig, void *pUserData);
+};
+
+#endif
diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp
new file mode 100644
index 00000000..b05d49f8
--- /dev/null
+++ b/src/engine/client/text.cpp
@@ -0,0 +1,718 @@
+#include <base/system.h>
+#include <base/math.h>
+#include <engine/graphics.h>
+#include <engine/textrender.h>
+
+#ifdef CONF_FAMILY_WINDOWS
+	#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
+
+// ft2 texture
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+// TODO: Refactor: clean this up
+
+
+enum
+{
+	MAX_CHARACTERS = 64,
+};
+
+
+static int aFontSizes[] = {8,9,10,11,12,13,14,15,16,17,18,19,20,36};
+#define NUM_FONT_SIZES (sizeof(aFontSizes)/sizeof(int))
+
+struct CFontChar
+{
+	int m_Id;
+	
+	// these values are scaled to the pFont size
+	// width * font_size == real_size
+	float m_Width;
+	float m_Height;
+	float m_OffsetX;
+	float m_OffsetY;
+	float m_AdvanceX;
+	
+	float m_aUvs[4];
+	int64 m_TouchTime;
+};
+
+struct CFontSizeData
+{
+	int m_FontSize;
+	FT_Face *m_pFace;
+
+	unsigned m_aTextures[2];
+	int m_TextureWidth;
+	int m_TextureHeight;
+	
+	int m_NumXChars;
+	int m_NumYChars;
+	
+	int m_CharMaxWidth;
+	int m_CharMaxHeight;
+	
+	CFontChar m_aCharacters[MAX_CHARACTERS*MAX_CHARACTERS];
+	
+	int m_CurrentCharacter;	
+};
+
+struct CFont
+{
+	char m_aFilename[128];
+	FT_Face m_FtFace;
+	CFontSizeData m_aSizes[NUM_FONT_SIZES];
+};
+
+
+class CTextRender : public IEngineTextRender
+{
+	IGraphics *m_pGraphics;
+	IGraphics *Graphics() { return m_pGraphics; }
+	
+	int WordLength(const char *pText)
+	{
+		int s = 1;
+		while(1)
+		{
+			if(*pText == 0)
+				return s-1;
+			if(*pText == '\n' || *pText == '\t' || *pText == ' ')
+				return s;
+			pText++;
+			s++;
+		}
+	}
+
+	float m_TextR;
+	float m_TextG;
+	float m_TextB;
+	float m_TextA;
+	
+	int m_FontTextureFormat;
+
+	struct CFont *m_pDefaultFont;
+
+	FT_Library m_FTLibrary;
+	
+	int GetFontSizeIndex(int Pixelsize)
+	{
+		for(unsigned i = 0; i < NUM_FONT_SIZES; i++)
+		{
+			if(aFontSizes[i] >= Pixelsize)
+				return i;
+		}
+		
+		return NUM_FONT_SIZES-1;
+	}
+	
+
+
+	void Grow(unsigned char *pIn, unsigned char *pOut, int w, int h)
+	{
+		for(int y = 0; y < h; y++) 
+			for(int x = 0; x < w; x++) 
+			{ 
+				int c = pIn[y*w+x]; 
+
+				for(int sy = -1; sy <= 1; sy++)
+					for(int sx = -1; sx <= 1; sx++)
+					{
+						int GetX = x+sx;
+						int GetY = y+sy;
+						if (GetX >= 0 && GetY >= 0 && GetX < w && GetY < h)
+						{
+							int Index = GetY*w+GetX;
+							if(pIn[Index] > c)
+								c = pIn[Index]; 
+						}
+					}
+
+				pOut[y*w+x] = c;
+			}
+	}
+
+	void InitTexture(CFontSizeData *pSizeData, int CharWidth, int CharHeight, int Xchars, int Ychars)
+	{
+		static int FontMemoryUsage = 0;
+		int Width = CharWidth*Xchars;
+		int Height = CharHeight*Ychars;
+		void *pMem = mem_alloc(Width*Height, 1);
+		mem_zero(pMem, Width*Height);
+		
+		if(pSizeData->m_aTextures[0] == 0)
+			glGenTextures(2, pSizeData->m_aTextures);
+		else
+			FontMemoryUsage -= pSizeData->m_TextureWidth*pSizeData->m_TextureHeight*2;
+		
+		pSizeData->m_NumXChars = Xchars;
+		pSizeData->m_NumYChars = Ychars;
+		pSizeData->m_TextureWidth = Width;
+		pSizeData->m_TextureHeight = Height;
+		pSizeData->m_CurrentCharacter = 0;
+		
+		for(int i = 0; i < 2; i++)
+		{
+			glBindTexture(GL_TEXTURE_2D, pSizeData->m_aTextures[i]);
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+			glTexImage2D(GL_TEXTURE_2D, 0, m_FontTextureFormat, Width, Height, 0, m_FontTextureFormat, GL_UNSIGNED_BYTE, pMem);
+			FontMemoryUsage += Width*Height;
+		}
+		
+		dbg_msg("", "pFont memory usage: %d", FontMemoryUsage);
+		
+		mem_free(pMem);
+	}
+
+	void IncreaseTextureSize(CFontSizeData *pSizeData)
+	{
+		if(pSizeData->m_TextureWidth < pSizeData->m_TextureHeight)
+			pSizeData->m_NumXChars <<= 1;
+		else
+			pSizeData->m_NumYChars <<= 1;
+		InitTexture(pSizeData, pSizeData->m_CharMaxWidth, pSizeData->m_CharMaxHeight, pSizeData->m_NumXChars, pSizeData->m_NumYChars);		
+	}
+	
+	
+	// TODO: Refactor: move this into a pFont class
+	void InitIndex(CFont *pFont, int Index)
+	{
+		int OutlineThickness = 1;
+		CFontSizeData *pSizeData = &pFont->m_aSizes[Index];
+		
+		pSizeData->m_FontSize = aFontSizes[Index];
+		FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, pSizeData->m_FontSize);
+		
+		if(pSizeData->m_FontSize >= 18)
+			OutlineThickness = 2;
+			
+		{
+			unsigned GlyphIndex;
+			int MaxH = 0;
+			int MaxW = 0;
+			
+			int Charcode = FT_Get_First_Char(pFont->m_FtFace, &GlyphIndex);
+			while(GlyphIndex != 0)
+			{   
+				// do stuff
+				FT_Load_Glyph(pFont->m_FtFace, GlyphIndex, FT_LOAD_DEFAULT);
+				
+				if(pFont->m_FtFace->glyph->metrics.width > MaxW) MaxW = pFont->m_FtFace->glyph->metrics.width; // ignore_convention
+				if(pFont->m_FtFace->glyph->metrics.height > MaxH) MaxH = pFont->m_FtFace->glyph->metrics.height; // ignore_convention
+				Charcode = FT_Get_Next_Char(pFont->m_FtFace, Charcode, &GlyphIndex);
+			}
+			
+			MaxW = (MaxW>>6)+2+OutlineThickness*2;
+			MaxH = (MaxH>>6)+2+OutlineThickness*2;
+			
+			for(pSizeData->m_CharMaxWidth = 1; pSizeData->m_CharMaxWidth < MaxW; pSizeData->m_CharMaxWidth <<= 1);
+			for(pSizeData->m_CharMaxHeight = 1; pSizeData->m_CharMaxHeight < MaxH; pSizeData->m_CharMaxHeight <<= 1);
+		}
+		
+		//dbg_msg("pFont", "init size %d, texture size %d %d", pFont->sizes[index].font_size, w, h);
+		//FT_New_Face(m_FTLibrary, "data/fonts/vera.ttf", 0, &pFont->ft_face);
+		InitTexture(pSizeData, pSizeData->m_CharMaxWidth, pSizeData->m_CharMaxHeight, 8, 8);
+	}
+
+	CFontSizeData *GetSize(CFont *pFont, int Pixelsize)
+	{
+		int Index = GetFontSizeIndex(Pixelsize);
+		if(pFont->m_aSizes[Index].m_FontSize != aFontSizes[Index])
+			InitIndex(pFont, Index);
+		return &pFont->m_aSizes[Index];
+	}
+
+
+	void UploadGlyph(CFontSizeData *pSizeData, int Texnum, int SlotId, int Chr, const void *pData)
+	{
+		int x = (SlotId%pSizeData->m_NumXChars) * (pSizeData->m_TextureWidth/pSizeData->m_NumXChars);
+		int y = (SlotId/pSizeData->m_NumXChars) * (pSizeData->m_TextureHeight/pSizeData->m_NumYChars);
+		
+		glBindTexture(GL_TEXTURE_2D, pSizeData->m_aTextures[Texnum]);
+		glTexSubImage2D(GL_TEXTURE_2D, 0, x, y,
+			pSizeData->m_TextureWidth/pSizeData->m_NumXChars,
+			pSizeData->m_TextureHeight/pSizeData->m_NumYChars,
+			m_FontTextureFormat, GL_UNSIGNED_BYTE, pData);
+	}
+
+	// 8k of data used for rendering glyphs
+	unsigned char ms_aGlyphData[(4096/64) * (4096/64)];
+	unsigned char ms_aGlyphDataOutlined[(4096/64) * (4096/64)];
+
+	int GetSlot(CFontSizeData *pSizeData)
+	{
+		int CharCount = pSizeData->m_NumXChars*pSizeData->m_NumYChars;
+		if(pSizeData->m_CurrentCharacter < CharCount)
+		{
+			int i = pSizeData->m_CurrentCharacter;
+			pSizeData->m_CurrentCharacter++;
+			return i;
+		}
+
+		// kick out the oldest
+		// TODO: remove this linear search
+		{
+			int Oldest = 0;
+			for(int i = 1; i < CharCount; i++)
+			{
+				if(pSizeData->m_aCharacters[i].m_TouchTime < pSizeData->m_aCharacters[Oldest].m_TouchTime)
+					Oldest = i;
+			}
+			
+			if(time_get()-pSizeData->m_aCharacters[Oldest].m_TouchTime < time_freq())
+			{
+				IncreaseTextureSize(pSizeData);
+				return GetSlot(pSizeData);
+			}
+			
+			return Oldest;
+		}
+	}
+
+	int RenderGlyph(CFont *pFont, CFontSizeData *pSizeData, int Chr)
+	{
+		FT_Bitmap *pBitmap;
+		int SlotId = 0;
+		int SlotW = pSizeData->m_TextureWidth / pSizeData->m_NumXChars;
+		int SlotH = pSizeData->m_TextureHeight / pSizeData->m_NumYChars;
+		int SlotSize = SlotW*SlotH;
+		int OutlineThickness = 1;
+		int x = 1;
+		int y = 1;
+		int px, py;
+
+		FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, pSizeData->m_FontSize);
+
+		if(FT_Load_Char(pFont->m_FtFace, Chr, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP))
+		{
+			dbg_msg("pFont", "error loading glyph %d", Chr);
+			return -1;
+		}
+
+		pBitmap = &pFont->m_FtFace->glyph->bitmap; // ignore_convention
+		
+		// fetch slot
+		SlotId = GetSlot(pSizeData);
+		if(SlotId < 0)
+			return -1;
+		
+		// adjust spacing
+		if(pSizeData->m_FontSize >= 18)
+			OutlineThickness = 2;
+		x += OutlineThickness;
+		y += OutlineThickness;
+
+		// prepare glyph data
+		mem_zero(ms_aGlyphData, SlotSize);
+
+		if(pBitmap->pixel_mode == FT_PIXEL_MODE_GRAY) // ignore_convention
+		{
+			for(py = 0; py < pBitmap->rows; py++) // ignore_convention
+				for(px = 0; px < pBitmap->width; px++) // ignore_convention
+					ms_aGlyphData[(py+y)*SlotW+px+x] = pBitmap->buffer[py*pBitmap->pitch+px]; // ignore_convention
+		}
+		else if(pBitmap->pixel_mode == FT_PIXEL_MODE_MONO) // ignore_convention
+		{
+			for(py = 0; py < pBitmap->rows; py++)  // ignore_convention
+				for(px = 0; px < pBitmap->width; px++) // ignore_convention
+				{
+					if(pBitmap->buffer[py*pBitmap->pitch+px/8]&(1<<(7-(px%8)))) // ignore_convention
+						ms_aGlyphData[(py+y)*SlotW+px+x] = 255;
+				}
+		}
+
+		if(0) for(py = 0; py < SlotW; py++) 
+			for(px = 0; px < SlotH; px++) 
+				ms_aGlyphData[py*SlotW+px] = 255;
+		
+		// upload the glyph
+		UploadGlyph(pSizeData, 0, SlotId, Chr, ms_aGlyphData);
+		
+		if(OutlineThickness == 1)
+		{
+			Grow(ms_aGlyphData, ms_aGlyphDataOutlined, SlotW, SlotH);
+			UploadGlyph(pSizeData, 1, SlotId, Chr, ms_aGlyphDataOutlined);
+		}
+		else
+		{
+			Grow(ms_aGlyphData, ms_aGlyphDataOutlined, SlotW, SlotH);
+			Grow(ms_aGlyphDataOutlined, ms_aGlyphData, SlotW, SlotH);
+			UploadGlyph(pSizeData, 1, SlotId, Chr, ms_aGlyphData);
+		}
+		
+		// set char info
+		{
+			CFontChar *pFontchr = &pSizeData->m_aCharacters[SlotId];
+			float Scale = 1.0f/pSizeData->m_FontSize;
+			float Uscale = 1.0f/pSizeData->m_TextureWidth;
+			float Vscale = 1.0f/pSizeData->m_TextureHeight;
+			int Height = pBitmap->rows + OutlineThickness*2 + 2; // ignore_convention
+			int Width = pBitmap->width + OutlineThickness*2 + 2; // ignore_convention
+			
+			pFontchr->m_Id = Chr;
+			pFontchr->m_Height = Height * Scale;
+			pFontchr->m_Width = Width * Scale;
+			pFontchr->m_OffsetX = (pFont->m_FtFace->glyph->bitmap_left-1) * Scale; // ignore_convention
+			pFontchr->m_OffsetY = (pSizeData->m_FontSize - pFont->m_FtFace->glyph->bitmap_top) * Scale; // ignore_convention
+			pFontchr->m_AdvanceX = (pFont->m_FtFace->glyph->advance.x>>6) * Scale; // ignore_convention
+			
+			pFontchr->m_aUvs[0] = (SlotId%pSizeData->m_NumXChars) / (float)(pSizeData->m_NumXChars);
+			pFontchr->m_aUvs[1] = (SlotId/pSizeData->m_NumXChars) / (float)(pSizeData->m_NumYChars);
+			pFontchr->m_aUvs[2] = pFontchr->m_aUvs[0] + Width*Uscale;
+			pFontchr->m_aUvs[3] = pFontchr->m_aUvs[1] + Height*Vscale;
+		}
+		
+		return SlotId;
+	}
+
+	CFontChar *GetChar(CFont *pFont, CFontSizeData *pSizeData, int Chr)
+	{
+		CFontChar *pFontchr = NULL;
+		
+		// search for the character
+		// TODO: remove this linear search
+		int i;
+		for(i = 0; i < pSizeData->m_CurrentCharacter; i++)
+		{
+			if(pSizeData->m_aCharacters[i].m_Id == Chr)
+			{
+				pFontchr = &pSizeData->m_aCharacters[i];
+				break;
+			}
+		}
+		
+		// check if we need to render the character
+		if(!pFontchr)
+		{
+			int Index = RenderGlyph(pFont, pSizeData, Chr);
+			if(Index >= 0)
+				pFontchr = &pSizeData->m_aCharacters[Index];
+		}
+		
+		// touch the character
+		// TODO: don't call time_get here
+		if(pFontchr)
+			pFontchr->m_TouchTime = time_get();
+			
+		return pFontchr;
+	}
+
+	// must only be called from the rendering function as the pFont must be set to the correct size
+	void RenderSetup(CFont *pFont, int size)
+	{
+		FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, size);
+	}
+
+	float Kerning(CFont *pFont, int Left, int Right)
+	{
+		FT_Vector Kerning = {0,0};
+		FT_Get_Kerning(pFont->m_FtFace, Left, Right, FT_KERNING_DEFAULT, &Kerning);
+		return (Kerning.x>>6);
+	}
+	
+	
+public:
+	CTextRender()
+	{
+		m_pGraphics = 0;
+
+		m_TextR = 1;
+		m_TextG = 1;
+		m_TextB = 1;
+		m_TextA = 1;
+
+		m_pDefaultFont = 0;
+
+		// GL_LUMINANCE can be good for debugging
+		m_FontTextureFormat = GL_ALPHA;
+	}
+		
+	virtual void Init()
+	{
+		m_pGraphics = Kernel()->RequestInterface<IGraphics>();
+		FT_Init_FreeType(&m_FTLibrary);
+	}
+			
+
+	virtual CFont *LoadFont(const char *pFilename)
+	{
+		CFont *pFont = (CFont *)mem_alloc(sizeof(CFont), 1);
+		
+		mem_zero(pFont, sizeof(*pFont));
+		str_copy(pFont->m_aFilename, pFilename, sizeof(pFont->m_aFilename));
+		
+		if(FT_New_Face(m_FTLibrary, pFont->m_aFilename, 0, &pFont->m_FtFace))
+		{
+			mem_free(pFont);
+			return NULL;
+		}
+
+		for(unsigned i = 0; i < NUM_FONT_SIZES; i++)
+			pFont->m_aSizes[i].m_FontSize = -1;
+		
+		dbg_msg("textrender", "loaded pFont from '%s'", pFilename);
+		return pFont;
+	};
+
+	virtual void DestroyFont(CFont *pFont)
+	{
+		mem_free(pFont);
+	}
+
+	virtual void SetDefaultFont(struct CFont *pFont)
+	{
+		dbg_msg("textrender", "default pFont set %p", pFont);
+		m_pDefaultFont = pFont;
+	}
+		
+		
+	virtual void SetCursor(CTextCursor *pCursor, float x, float y, float FontSize, int Flags)
+	{
+		mem_zero(pCursor, sizeof(*pCursor));
+		pCursor->m_FontSize = FontSize;
+		pCursor->m_StartX = x;
+		pCursor->m_StartY = y;
+		pCursor->m_X = x;
+		pCursor->m_Y = y;
+		pCursor->m_LineCount = 1;
+		pCursor->m_LineWidth = -1;
+		pCursor->m_Flags = Flags;
+		pCursor->m_CharCount = 0;
+	}
+	
+		
+	virtual void Text(void *pFontSetV, float x, float y, float Size, const char *pText, int MaxWidth)
+	{
+		CTextCursor Cursor;
+		SetCursor(&Cursor, x, y, Size, TEXTFLAG_RENDER);
+		Cursor.m_LineWidth = MaxWidth;
+		TextEx(&Cursor, pText, -1);
+	}
+
+	virtual float TextWidth(void *pFontSetV, float Size, const char *pText, int Length)
+	{
+		CTextCursor Cursor;
+		SetCursor(&Cursor, 0, 0, Size, 0);
+		TextEx(&Cursor, pText, Length);
+		return Cursor.m_X;
+	}
+	
+	virtual float TextLineCount(void *pFontSetV, float Size, const char *pText, int LineWidth)
+	{
+		CTextCursor Cursor;
+		SetCursor(&Cursor, 0, 0, Size, 0);
+		Cursor.m_LineWidth = LineWidth;
+		TextEx(&Cursor, pText, -1);
+		return Cursor.m_LineCount;
+	}
+
+	virtual void TextColor(float r, float g, float b, float a)
+	{
+		m_TextR = r;
+		m_TextG = g;
+		m_TextB = b;
+		m_TextA = a;
+	}
+	
+	virtual void TextEx(CTextCursor *pCursor, const char *pText, int Length)
+	{
+		CFont *pFont = pCursor->m_pFont;
+		CFontSizeData *pSizeData = NULL;
+		
+		//dbg_msg("textrender", "rendering text '%s'", text);
+
+		float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
+		float FakeToScreenX, FakeToScreenY;
+		int ActualX, ActualY;
+
+		int ActualSize;
+		int i;
+		int GotNewLine = 0;
+		float DrawX, DrawY;
+		float CursorX, CursorY;
+		const char *pEnd;
+
+		float Size = pCursor->m_FontSize;
+
+		// to correct coords, convert to screen coords, round, and convert back
+		Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
+		
+		FakeToScreenX = (Graphics()->ScreenWidth()/(ScreenX1-ScreenX0));
+		FakeToScreenY = (Graphics()->ScreenHeight()/(ScreenY1-ScreenY0));
+		ActualX = pCursor->m_X * FakeToScreenX;
+		ActualY = pCursor->m_Y * FakeToScreenY;
+
+		CursorX = ActualX / FakeToScreenX;
+		CursorY = ActualY / FakeToScreenY;
+
+		// same with size
+		ActualSize = Size * FakeToScreenY;
+		Size = ActualSize / FakeToScreenY;
+
+		// fetch pFont data
+		if(!pFont)
+			pFont = m_pDefaultFont;
+		
+		if(!pFont)
+			return;
+
+		pSizeData = GetSize(pFont, ActualSize);
+		RenderSetup(pFont, ActualSize);
+		
+		// set length
+		if(Length < 0)
+			Length = str_length(pText);
+			
+		pEnd = pText + Length;
+
+		// if we don't want to render, we can just skip the first outline pass
+		i = 1;
+		if(pCursor->m_Flags&TEXTFLAG_RENDER)
+			i = 0;
+
+		for(;i < 2; i++)
+		{
+			const char *pCurrent = (char *)pText;
+			const char *pEnd = pCurrent+Length;
+			DrawX = CursorX;
+			DrawY = CursorY;
+
+			if(pCursor->m_Flags&TEXTFLAG_RENDER)
+			{
+				// TODO: Make this better
+				glEnable(GL_TEXTURE_2D);
+				if (i == 0)
+					glBindTexture(GL_TEXTURE_2D, pSizeData->m_aTextures[1]);
+				else
+					glBindTexture(GL_TEXTURE_2D, pSizeData->m_aTextures[0]);
+
+				Graphics()->QuadsBegin();
+				if (i == 0)
+					Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.3f*m_TextA);
+				else
+					Graphics()->SetColor(m_TextR, m_TextG, m_TextB, m_TextA);
+			}
+
+			while(pCurrent < pEnd)
+			{
+				int NewLine = 0;
+				const char *pBatchEnd = pEnd;
+				if(pCursor->m_LineWidth > 0 && !(pCursor->m_Flags&TEXTFLAG_STOP_AT_END))
+				{
+					int Wlen = min(WordLength((char *)pCurrent), (int)(pEnd-pCurrent));
+					CTextCursor Compare = *pCursor;
+					Compare.m_X = DrawX;
+					Compare.m_Y = DrawY;
+					Compare.m_Flags &= ~TEXTFLAG_RENDER;
+					Compare.m_LineWidth = -1;
+					TextEx(&Compare, pText, Wlen);
+					
+					if(Compare.m_X-DrawX > pCursor->m_LineWidth)
+					{
+						// word can't be fitted in one line, cut it
+						CTextCursor Cutter = *pCursor;
+						Cutter.m_CharCount = 0;
+						Cutter.m_X = DrawX;
+						Cutter.m_Y = DrawY;
+						Cutter.m_Flags &= ~TEXTFLAG_RENDER;
+						Cutter.m_Flags |= TEXTFLAG_STOP_AT_END;
+						
+						TextEx(&Cutter, (const char *)pCurrent, Wlen);
+						Wlen = Cutter.m_CharCount;
+						NewLine = 1;
+						
+						if(Wlen <= 3) // if we can't place 3 chars of the word on this line, take the next
+							Wlen = 0;
+					}
+					else if(Compare.m_X-pCursor->m_StartX > pCursor->m_LineWidth)
+					{
+						NewLine = 1;
+						Wlen = 0;
+					}
+					
+					pBatchEnd = pCurrent + Wlen;
+				}
+				
+				while(pCurrent < pBatchEnd)
+				{
+					const char *pTmp;
+					float Advance = 0;
+					int Character = 0;
+					int Nextcharacter = 0;
+					CFontChar *pChr;
+
+					// TODO: UTF-8 decode
+					Character = str_utf8_decode(&pCurrent);
+					pTmp = pCurrent;
+					Nextcharacter = str_utf8_decode(&pTmp);
+					
+					if(Character == '\n')
+					{
+						DrawX = pCursor->m_StartX;
+						DrawY += Size;
+						DrawX = (int)(DrawX * FakeToScreenX) / FakeToScreenX; // realign
+						DrawY = (int)(DrawY * FakeToScreenY) / FakeToScreenY;
+						++pCursor->m_LineCount;
+						continue;
+					}
+
+					pChr = GetChar(pFont, pSizeData, Character);
+
+					if(pChr)
+					{
+						if(pCursor->m_Flags&TEXTFLAG_RENDER)
+						{
+							Graphics()->QuadsSetSubset(pChr->m_aUvs[0], pChr->m_aUvs[1], pChr->m_aUvs[2], pChr->m_aUvs[3]);
+							IGraphics::CQuadItem QuadItem(DrawX+pChr->m_OffsetX*Size, DrawY+pChr->m_OffsetY*Size, pChr->m_Width*Size, pChr->m_Height*Size);
+							Graphics()->QuadsDrawTL(&QuadItem, 1);
+						}
+
+						Advance = pChr->m_AdvanceX + Kerning(pFont, Character, Nextcharacter)/Size;
+					}
+									
+					if(pCursor->m_Flags&TEXTFLAG_STOP_AT_END && DrawX+Advance*Size-pCursor->m_StartX > pCursor->m_LineWidth)
+					{
+						// we hit the end of the line, no more to render or count
+						pCurrent = pEnd;
+						break;
+					}
+
+					DrawX += Advance*Size;
+					pCursor->m_CharCount++;
+				}
+				
+				if(NewLine)
+				{
+					DrawX = pCursor->m_StartX;
+					DrawY += Size;
+					GotNewLine = 1;
+					DrawX = (int)(DrawX * FakeToScreenX) / FakeToScreenX; // realign
+					DrawY = (int)(DrawY * FakeToScreenY) / FakeToScreenY;				
+					++pCursor->m_LineCount;
+				}
+			}
+
+			if(pCursor->m_Flags&TEXTFLAG_RENDER)
+				Graphics()->QuadsEnd();
+		}
+
+		pCursor->m_X = DrawX;
+		
+		if(GotNewLine)
+			pCursor->m_Y = DrawY;
+	}
+	
+};
+
+IEngineTextRender *CreateEngineTextRender() { return new CTextRender; }
diff --git a/src/engine/config.h b/src/engine/config.h
new file mode 100644
index 00000000..967d3593
--- /dev/null
+++ b/src/engine/config.h
@@ -0,0 +1,23 @@
+#ifndef ENGINE_CONFIG_H
+#define ENGINE_CONFIG_H
+
+#include "kernel.h"
+
+class IConfig : public IInterface
+{
+	MACRO_INTERFACE("config", 0)
+public:
+	typedef void (*SAVECALLBACKFUNC)(IConfig *pConfig, void *pUserData);
+
+	virtual void Init() = 0;
+	virtual void Reset() = 0;
+	virtual void Save() = 0;
+	
+	virtual void RegisterCallback(SAVECALLBACKFUNC pfnFunc, void *pUserData) = 0;
+	
+	virtual void WriteLine(const char *pLine) = 0;
+};
+
+extern IConfig *CreateConfig();
+
+#endif
diff --git a/src/engine/console.h b/src/engine/console.h
new file mode 100644
index 00000000..74d789e9
--- /dev/null
+++ b/src/engine/console.h
@@ -0,0 +1,58 @@
+#ifndef ENGINE_CONSOLE_H
+#define ENGINE_CONSOLE_H
+
+#include "kernel.h"
+
+class IConsole : public IInterface
+{
+	MACRO_INTERFACE("console", 0)
+public:
+
+	// TODO: rework this interface to reduce the amount of virtual calls
+	class IResult
+	{
+	protected:
+		unsigned m_NumArgs;
+	public:
+		IResult() { m_NumArgs = 0; }
+		virtual ~IResult() {}
+		
+		virtual int GetInteger(unsigned Index) = 0;
+		virtual float GetFloat(unsigned Index) = 0;
+		virtual const char *GetString(unsigned Index) = 0;
+		
+		int NumArguments() const { return m_NumArgs; }
+	};
+	
+	class CCommandInfo
+	{
+	public:
+		const char *m_pName;
+		const char *m_pHelp;
+		const char *m_pParams;
+	};
+
+	typedef void (*FPrintCallback)(const char *pStr, void *pUser);
+	typedef void (*FPossibleCallback)(const char *pCmd, void *pUser);
+	typedef void (*FCommandCallback)(IResult *pResult, void *pUserData);
+	typedef void (*FChainCommandCallback)(IResult *pResult, void *pUserData, FCommandCallback pfnCallback, void *pCallbackUserData);
+
+	virtual CCommandInfo *GetCommandInfo(const char *pName) = 0;
+	virtual void PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser) = 0;
+	virtual void ParseArguments(int NumArgs, const char **ppArguments) = 0;
+
+	virtual void Register(const char *pName, const char *pParams, 
+		int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp) = 0;
+	virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser) = 0;
+	
+	virtual void ExecuteLine(const char *Sptr) = 0;
+	virtual void ExecuteLineStroked(int Stroke, const char *pStr) = 0;
+	virtual void ExecuteFile(const char *pFilename) = 0;
+	
+	virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData) = 0;
+	virtual void Print(const char *pStr) = 0;
+};
+
+extern IConsole *CreateConsole();
+
+#endif // FILE_ENGINE_CONSOLE_H
diff --git a/src/engine/demo.h b/src/engine/demo.h
new file mode 100644
index 00000000..7b353e7c
--- /dev/null
+++ b/src/engine/demo.h
@@ -0,0 +1,29 @@
+#ifndef ENGINE_DEMO_H
+#define ENGINE_DEMO_H
+
+#include "kernel.h"
+
+class IDemoPlayer : public IInterface
+{
+	MACRO_INTERFACE("demoplayer", 0)
+public:
+	class CInfo
+	{
+	public:
+		bool m_Paused;
+		float m_Speed;
+
+		int m_FirstTick;
+		int m_CurrentTick;
+		int m_LastTick;
+	};
+
+	~IDemoPlayer() {}
+	virtual void SetSpeed(float Speed) = 0;
+	virtual int SetPos(float Precent) = 0;
+	virtual void Pause() = 0;
+	virtual void Unpause() = 0;
+	virtual const CInfo *BaseInfo() const = 0;
+};
+
+#endif
diff --git a/src/engine/e_client_interface.h b/src/engine/e_client_interface.h
deleted file mode 100644
index 079eabca..00000000
--- a/src/engine/e_client_interface.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_CLIENT_INTERFACE_H
-#define ENGINE_CLIENT_INTERFACE_H
-
-#include "e_if_other.h"
-#include "e_if_client.h"
-#include "e_if_snd.h"
-#include "e_if_gfx.h"
-#include "e_if_inp.h"
-#include "e_if_msg.h"
-#include "e_if_modc.h"
-
-#include "e_console.h"
-#include "e_config.h"
-
-#endif
diff --git a/src/engine/e_common_interface.h b/src/engine/e_common_interface.h
deleted file mode 100644
index 9c95b48b..00000000
--- a/src/engine/e_common_interface.h
+++ /dev/null
@@ -1,8 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_COMMON_INTERFACE_H
-#define ENGINE_COMMON_INTERFACE_H
-
-#include "e_if_other.h"
-#include "e_if_msg.h"
-
-#endif
diff --git a/src/engine/e_compression.cpp b/src/engine/e_compression.cpp
deleted file mode 100644
index f4d6e0c0..00000000
--- a/src/engine/e_compression.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <base/system.h>
-
-/* Format: ESDDDDDD EDDDDDDD EDD...  Extended, Data, Sign */
-unsigned char *vint_pack(unsigned char *dst, int i) 
-{ 
-	*dst = (i>>25)&0x40; /* set sign bit if i<0 */
-	i = i^(i>>31); /* if(i<0) i = ~i */
-
-	*dst |= i&0x3F; /* pack 6bit into dst */
-	i >>= 6; /* discard 6 bits */
-	if(i)
-	{
-		*dst |= 0x80; /* set extend bit */
-		while(1)
-		{
-			dst++;
-			*dst = i&(0x7F); /* pack 7bit */
-			i >>= 7; /* discard 7 bits */
-			*dst |= (i!=0)<<7; /* set extend bit (may branch) */
-			if(!i)
-				break;
-		}
-	}
-
-	dst++;
-	return dst; 
-} 
- 
-const unsigned char *vint_unpack(const unsigned char *src, int *i)
-{ 
-	int sign = (*src>>6)&1; 
-	*i = *src&0x3F; 
-
-	do
-	{ 
-		if(!(*src&0x80)) break;
-		src++;
-		*i |= (*src&(0x7F))<<(6);
-
-		if(!(*src&0x80)) break;
-		src++;
-		*i |= (*src&(0x7F))<<(6+7);
-
-		if(!(*src&0x80)) break;
-		src++;
-		*i |= (*src&(0x7F))<<(6+7+7);
-
-		if(!(*src&0x80)) break;
-		src++;
-		*i |= (*src&(0x7F))<<(6+7+7+7);
-	} while(0);
-
-	src++;
-	*i ^= -sign; /* if(sign) *i = ~(*i) */
-	return src; 
-} 
-
-
-long intpack_decompress(const void *src_, int size, void *dst_)
-{
-	const unsigned char *src = (unsigned char *)src_;
-	const unsigned char *end = src + size;
-	int *dst = (int *)dst_;
-	while(src < end)
-	{
-		src = vint_unpack(src, dst);
-		dst++;
-	}
-	return (long)((unsigned char *)dst-(unsigned char *)dst_);
-}
-
-long intpack_compress(const void *src_, int size, void *dst_)
-{
-	int *src = (int *)src_;
-	unsigned char *dst = (unsigned char *)dst_;
-	size /= 4;
-	while(size)
-	{
-		dst = vint_pack(dst, *src);
-		size--;
-		src++;
-	}
-	return (long)(dst-(unsigned char *)dst_);
-}
-
diff --git a/src/engine/e_compression.h b/src/engine/e_compression.h
deleted file mode 100644
index be5bf78f..00000000
--- a/src/engine/e_compression.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-
-/* variable int packing */
-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);
diff --git a/src/engine/e_config.cpp b/src/engine/e_config.cpp
deleted file mode 100644
index 67a4c81a..00000000
--- a/src/engine/e_config.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <base/system.h>
-#include "e_if_other.h"
-#include "e_config.h"
-#include "e_linereader.h"
-#include "e_engine.h"
-
-CONFIGURATION config;
-
-void config_reset()
-{
-    #define MACRO_CONFIG_INT(name,def,min,max,flags,desc) config.name = def;
-    #define MACRO_CONFIG_STR(name,len,def,flags,desc) str_copy(config.name, def, len);
- 
-    #include "e_config_variables.h" 
- 
-    #undef MACRO_CONFIG_INT 
-    #undef MACRO_CONFIG_STR 
-}
-
-void config_save()
-{
-	char linebuf[1024*2];
-	
-	#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(linebuf, sizeof(linebuf), "%s %i", #name, config.name); engine_config_write_line(linebuf); }
-	#define MACRO_CONFIG_STR(name,len,def,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(linebuf, sizeof(linebuf), "%s %s", #name, config.name); engine_config_write_line(linebuf); }
-
-	#include "e_config_variables.h" 
-
-	#undef MACRO_CONFIG_INT 
-	#undef MACRO_CONFIG_STR 
-}
-
-#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) int config_get_ ## name (CONFIGURATION *c) { return c->name; }
-#define MACRO_CONFIG_STR(name,len,def,flags,desc) const char *config_get_ ## name (CONFIGURATION *c) { return c->name; }
-#include "e_config_variables.h"
-#undef MACRO_CONFIG_INT
-#undef MACRO_CONFIG_STR
-
-#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) void config_set_ ## name (CONFIGURATION *c, int val) { if(min != max) { if (val < min) val = min; if (max != 0 && val > max) val = max; } c->name = val; }
-#define MACRO_CONFIG_STR(name,len,def,flags,desc) void config_set_ ## name (CONFIGURATION *c, const char *str) { str_copy(c->name, str, len-1); c->name[sizeof(c->name)-1] = 0; }
-#include "e_config_variables.h"
-#undef MACRO_CONFIG_INT
-#undef MACRO_CONFIG_STR
diff --git a/src/engine/e_config.h b/src/engine/e_config.h
deleted file mode 100644
index 6ca7a8ee..00000000
--- a/src/engine/e_config.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef _CONFIG_H
-#define _CONFIG_H
-
-typedef struct
-{ 
-    #define MACRO_CONFIG_INT(name,def,min,max,save,desc) int name;
-    #define MACRO_CONFIG_STR(name,len,def,save,desc) char name[len]; /* Flawfinder: ignore */
-    #include "e_config_variables.h" 
-    #undef MACRO_CONFIG_INT 
-    #undef MACRO_CONFIG_STR 
-} CONFIGURATION;
-
-extern CONFIGURATION config;
-
-void config_init();
-void config_reset();
-void config_save();
-
-enum
-{
-	CFGFLAG_SAVE=1,
-	CFGFLAG_CLIENT=2,
-	CFGFLAG_SERVER=4
-};
-
-typedef int (*CONFIG_INT_GETTER)(CONFIGURATION *c);
-typedef const char *(*CONFIG_STR_GETTER)(CONFIGURATION *c);
-typedef void (*CONFIG_INT_SETTER)(CONFIGURATION *c, int val);
-typedef void (*CONFIG_STR_SETTER)(CONFIGURATION *c, const char *str);
-
-#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) int config_get_ ## name (CONFIGURATION *c);
-#define MACRO_CONFIG_STR(name,len,def,flags,desc) const char *config_get_ ## name (CONFIGURATION *c);
-#include "e_config_variables.h"
-#undef MACRO_CONFIG_INT
-#undef MACRO_CONFIG_STR
-
-#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) void config_set_ ## name (CONFIGURATION *c, int val);
-#define MACRO_CONFIG_STR(name,len,def,flags,desc) void config_set_ ## name (CONFIGURATION *c, const char *str);
-#include "e_config_variables.h"
-#undef MACRO_CONFIG_INT
-#undef MACRO_CONFIG_STR
-
-#endif
diff --git a/src/engine/e_config_variables.h b/src/engine/e_config_variables.h
deleted file mode 100644
index aa3da3f6..00000000
--- a/src/engine/e_config_variables.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-
-/* TODO: remove this */
-#include "../game/variables.hpp"
-
-
-MACRO_CONFIG_STR(player_name, 32, "nameless tee", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Name of the player")
-MACRO_CONFIG_STR(clan_name, 32, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "(not used)")
-MACRO_CONFIG_STR(password, 32, "", CFGFLAG_CLIENT, "Password to the server")
-MACRO_CONFIG_STR(logfile, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filename to log all output to")
-
-MACRO_CONFIG_INT(cl_cpu_throttle, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
-MACRO_CONFIG_INT(cl_editor, 0, 0, 1, CFGFLAG_CLIENT, "")
-
-MACRO_CONFIG_INT(cl_eventthread, 0, 0, 1, CFGFLAG_CLIENT, "Enables the usage of a thread to pump the events")
-
-MACRO_CONFIG_INT(inp_grab, 0, 0, 1, CFGFLAG_CLIENT, "Use forceful input grabbing method")
-
-MACRO_CONFIG_STR(b_filter_string, 64, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Server browser filtering string")
-
-MACRO_CONFIG_INT(b_filter_full, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out full server in browser")
-MACRO_CONFIG_INT(b_filter_empty, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out empty server in browser")
-MACRO_CONFIG_INT(b_filter_pw, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out password protected servers in browser")
-MACRO_CONFIG_INT(b_filter_ping, 999, 0, 999, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Ping to filter by in the server browser")
-MACRO_CONFIG_STR(b_filter_gametype, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Game types to filter")
-MACRO_CONFIG_INT(b_filter_pure, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard servers in browser")
-MACRO_CONFIG_INT(b_filter_pure_map, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard maps in browser")
-MACRO_CONFIG_INT(b_filter_compatversion, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-compatible servers in browser")
-
-MACRO_CONFIG_INT(b_sort, 0, 0, 256, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
-MACRO_CONFIG_INT(b_sort_order, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
-MACRO_CONFIG_INT(b_max_requests, 10, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Number of requests to use when refreshing server browser")
-
-MACRO_CONFIG_INT(snd_buffer_size, 512, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound buffer size")
-MACRO_CONFIG_INT(snd_rate, 48000, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound mixing rate")
-MACRO_CONFIG_INT(snd_enable, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound enable")
-MACRO_CONFIG_INT(snd_volume, 100, 0, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound volume")
-MACRO_CONFIG_INT(snd_device, -1, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "(deprecated) Sound device to use")
-
-MACRO_CONFIG_INT(snd_nonactive_mute, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
-
-MACRO_CONFIG_INT(gfx_screen_width, 800, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution width")
-MACRO_CONFIG_INT(gfx_screen_height, 600, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution height")
-MACRO_CONFIG_INT(gfx_fullscreen, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Fullscreen")
-MACRO_CONFIG_INT(gfx_alphabits, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Alpha bits for framebuffer  (fullscreen only)")
-MACRO_CONFIG_INT(gfx_color_depth, 24, 16, 24, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Colors bits for framebuffer (fullscreen only)")
-MACRO_CONFIG_INT(gfx_clear, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Clear screen before rendering")
-MACRO_CONFIG_INT(gfx_vsync, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Vertical sync")
-MACRO_CONFIG_INT(gfx_display_all_modes, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
-MACRO_CONFIG_INT(gfx_texture_compression, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use texture compression")
-MACRO_CONFIG_INT(gfx_high_detail, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "High detail")
-MACRO_CONFIG_INT(gfx_texture_quality, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
-MACRO_CONFIG_INT(gfx_fsaa_samples, 0, 0, 16, CFGFLAG_SAVE|CFGFLAG_CLIENT, "FSAA Samples")
-MACRO_CONFIG_INT(gfx_refresh_rate, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen refresh rate")
-MACRO_CONFIG_INT(gfx_finish, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
-
-MACRO_CONFIG_INT(inp_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity")
-
-MACRO_CONFIG_STR(sv_name, 128, "unnamed server", CFGFLAG_SERVER, "Server name")
-MACRO_CONFIG_STR(sv_bindaddr, 128, "", CFGFLAG_SERVER, "Address to bind the server to")
-MACRO_CONFIG_INT(sv_port, 8303, 0, 0, CFGFLAG_SERVER, "Port to use for the server")
-MACRO_CONFIG_INT(sv_external_port, 0, 0, 0, CFGFLAG_SERVER, "External port to report to the master servers")
-MACRO_CONFIG_STR(sv_map, 128, "dm1", CFGFLAG_SERVER, "Map to use on the server")
-MACRO_CONFIG_INT(sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients that are allowed on a server")
-MACRO_CONFIG_INT(sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only")
-MACRO_CONFIG_INT(sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing")
-MACRO_CONFIG_STR(sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password")
-MACRO_CONFIG_INT(sv_map_reload, 0, 0, 1, CFGFLAG_SERVER, "Reload the current map")
-
-MACRO_CONFIG_INT(debug, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Debug mode")
-MACRO_CONFIG_INT(dbg_stress, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress systems")
-MACRO_CONFIG_INT(dbg_stress_network, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress network")
-MACRO_CONFIG_INT(dbg_pref, 0, 0, 1, CFGFLAG_SERVER, "Performance outputs")
-MACRO_CONFIG_INT(dbg_graphs, 0, 0, 1, CFGFLAG_CLIENT, "Performance graphs")
-MACRO_CONFIG_INT(dbg_hitch, 0, 0, 0, CFGFLAG_SERVER, "Hitch warnings")
-MACRO_CONFIG_STR(dbg_stress_server, 32, "localhost", CFGFLAG_CLIENT, "Server to stress")
-MACRO_CONFIG_INT(dbg_resizable, 0, 0, 0, CFGFLAG_CLIENT, "Enables window resizing")
diff --git a/src/engine/e_console.cpp b/src/engine/e_console.cpp
deleted file mode 100644
index c641289d..00000000
--- a/src/engine/e_console.cpp
+++ /dev/null
@@ -1,464 +0,0 @@
-#include <base/system.h>
-#include "e_if_other.h"
-#include "e_console.h"
-#include "e_config.h"
-#include "e_engine.h"
-#include "e_linereader.h"
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-
-#define CONSOLE_MAX_STR_LENGTH 1024
-/* the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces */
-#define MAX_PARTS (CONSOLE_MAX_STR_LENGTH+1)/2
-
-typedef struct
-{ 
-	char string_storage[CONSOLE_MAX_STR_LENGTH+1];
-	char *args_start;
-	
-	const char *command;
-	const char *args[MAX_PARTS]; 
-	unsigned int num_args; 
-} PARSE_RESULT;
-
-static char *str_skipblanks(char *str)
-{
-	while(*str && (*str == ' ' || *str == '\t' || *str == '\n'))
-		str++;
-	return str;
-}
-
-static char *str_skiptoblank(char *str)
-{
-	while(*str && (*str != ' ' && *str != '\t' && *str != '\n'))
-		str++;
-	return str;
-}
-
-/* static int digit(char c) { return '0' <= c && c <= '9'; } */
-
-static int console_parse_start(PARSE_RESULT *result, const char *string, int length)
-{
-	char *str;
-	int len = sizeof(result->string_storage);
-	if(length < len)
-		len = length;
-		
-	str_copy(result->string_storage, string, length);
-	str = result->string_storage;
-	
-	/* get command */
-	str = str_skipblanks(str);
-	result->command = str;
-	str = str_skiptoblank(str);
-	
-	if(*str)
-	{
-		str[0] = 0;
-		str++;
-	}
-	
-	result->args_start = str;
-	result->num_args = 0;
-	return 0;
-}
-
-static int console_parse_args(PARSE_RESULT *result, const char *format)
-{
-	char command;
-	char *str;
-	int optional = 0;
-	int error = 0;
-	
-	str = result->args_start;
-
-	while(1)	
-	{
-		/* fetch command */
-		command = *format;
-		format++;
-		
-		if(!command)
-			break;
-		
-		if(command == '?')
-			optional = 1;
-		else
-		{
-			str = str_skipblanks(str);
-		
-			if(!(*str)) /* error, non optional command needs value */
-			{
-				if(!optional)
-					error = 1;
-				break;
-			}
-			
-			/* add token */
-			if(*str == '"')
-			{
-				char *dst;
-				str++;
-				result->args[result->num_args++] = str;
-				
-				dst = str; /* we might have to process escape data */
-				while(1)
-				{
-					if(str[0] == '"')
-						break;
-					else if(str[0] == '\\')
-					{
-						if(str[1] == '\\')
-							str++; /* skip due to escape */
-						else if(str[1] == '"')
-							str++; /* skip due to escape */
-					}
-					else if(str[0] == 0)
-						return 1; /* return error */
-						
-					*dst = *str;
-					dst++;
-					str++;
-				}
-				
-				/* write null termination */
-				*dst = 0;
-			}
-			else
-			{
-				result->args[result->num_args++] = str;
-				
-				if(command == 'r') /* rest of the string */
-					break;
-				else if(command == 'i') /* validate int */
-					str = str_skiptoblank(str);
-				else if(command == 'f') /* validate float */
-					str = str_skiptoblank(str);
-				else if(command == 's') /* validate string */
-					str = str_skiptoblank(str);
-
-				if(str[0] != 0) /* check for end of string */
-				{
-					str[0] = 0;
-					str++;
-				}
-			}
-		}
-	}
-
-	return error;
-}
-
-const char *console_arg_string(void *res, unsigned index)
-{
-	PARSE_RESULT *result = (PARSE_RESULT *)res;
-	if (index < 0 || index >= result->num_args)
-		return "";
-	return result->args[index];
-}
-
-int console_arg_int(void *res, unsigned index)
-{
-	PARSE_RESULT *result = (PARSE_RESULT *)res;
-	if (index < 0 || index >= result->num_args)
-		return 0;
-	return atoi(result->args[index]);
-}
-
-float console_arg_float(void *res, unsigned index)
-{
-	PARSE_RESULT *result = (PARSE_RESULT *)res;
-	if (index < 0 || index >= result->num_args)
-		return 0.0f;
-	return atof(result->args[index]);
-}
-
-int console_arg_num(void *result)
-{
-	return ((PARSE_RESULT *)result)->num_args;
-}
-
-static COMMAND *first_command = 0x0;
-
-COMMAND *console_find_command(const char *name)
-{
-	COMMAND *cmd;
-	for (cmd = first_command; cmd; cmd = cmd->next)
-	{
-		if (strcmp(cmd->name, name) == 0)
-			return cmd;
-	}
-
-	return 0x0;
-}
-
-void console_register(COMMAND *cmd)
-{
-	cmd->next = first_command;
-	first_command = cmd;
-}
-
-static void (*print_callback)(const char *, void *) = 0x0;
-static void *print_callback_userdata;
-
-void console_register_print_callback(void (*callback)(const char *, void *), void *user_data)
-{
-	print_callback = callback;
-	print_callback_userdata = user_data;
-}
-
-void console_print(const char *str)
-{
-	if (print_callback)
-		print_callback(str, print_callback_userdata);
-}
-
-void console_execute_line_stroked(int stroke, const char *str)
-{
-	PARSE_RESULT result;
-	COMMAND *command;
-	
-	char strokestr[2] = {'0', 0};
-	if(stroke)
-		strokestr[0] = '1';
-
-	while(str)
-	{
-		const char *end = str;
-		const char *next_part = 0;
-		int in_string = 0;
-		
-		while(*end)
-		{
-			if(*end == '"')
-				in_string ^= 1;
-			else if(*end == '\\') /* escape sequences */
-			{
-				if(end[1] == '"')
-					end++;
-			}
-			else if(!in_string)
-			{
-				if(*end == ';')  /* command separator */
-				{
-					next_part = end+1;
-					break;
-				}
-				else if(*end == '#')  /* comment, no need to do anything more */
-					break;
-			}
-			
-			end++;
-		}
-		
-		if(console_parse_start(&result, str, (end-str) + 1) != 0)
-			return;
-
-		command = console_find_command(result.command);
-
-		if(command)
-		{
-			int is_stroke_command = 0;
-			if(result.command[0] == '+')
-			{
-				/* insert the stroke direction token */
-				result.args[result.num_args] = strokestr;
-				result.num_args++;
-				is_stroke_command = 1;
-			}
-			
-			if(stroke || is_stroke_command)
-			{
-				if(console_parse_args(&result, command->params))
-				{
-					char buf[256];
-					str_format(buf, sizeof(buf), "Invalid arguments... Usage: %s %s", command->name, command->params);
-					console_print(buf);
-				}
-				else
-					command->callback(&result, command->user_data);
-			}
-		}
-		else
-		{
-			char buf[256];
-			str_format(buf, sizeof(buf), "No such command: %s.", result.command);
-			console_print(buf);
-		}
-		
-		str = next_part;
-	}
-}
-
-void console_possible_commands(const char *str, int flagmask, void (*callback)(const char *cmd, void *user), void *user)
-{
-	COMMAND *cmd;
-	for (cmd = first_command; cmd; cmd = cmd->next)
-	{
-		if(cmd->flags&flagmask)
-		{
-			if(str_find_nocase(cmd->name, str))
-				callback(cmd->name, user);
-		}
-	}	
-}
-
-
-COMMAND *console_get_command(const char *str)
-{
-	COMMAND *cmd;
-	for (cmd = first_command; cmd; cmd = cmd->next)
-	{
-		if(str_comp_nocase(cmd->name, str) == 0)
-			return cmd;
-	}	
-	
-	return 0x0;
-}
-
-void console_execute_line(const char *str)
-{
-	console_execute_line_stroked(1, str);
-}
-
-static void console_execute_file_real(const char *filename)
-{
-	IOHANDLE file;
-	file = engine_openfile(filename, IOFLAG_READ);
-	
-	if(file)
-	{
-		char *line;
-		LINEREADER lr;
-		
-		dbg_msg("console", "executing '%s'", filename);
-		linereader_init(&lr, file);
-
-		while((line = linereader_get(&lr)))
-			console_execute_line(line);
-
-		io_close(file);
-	}
-	else
-		dbg_msg("console", "failed to open '%s'", filename);
-}
-
-struct EXECFILE
-{
-	const char *filename;
-	struct EXECFILE *next;
-};
-
-void console_execute_file(const char *filename)
-{
-	static struct EXECFILE *first = 0;
-	struct EXECFILE this_file;
-	struct EXECFILE *cur;
-	struct EXECFILE *prev;
-
-	/* make sure that this isn't being executed already */	
-	for(cur = first; cur; cur = cur->next)
-		if(strcmp(filename, cur->filename) == 0)
-			return;
-	
-	/* push this one to the stack */
-	prev = first;
-	this_file.filename = filename;
-	this_file.next = first;
-	first = &this_file;
-	
-	/* execute file */
-	console_execute_file_real(filename);
-	
-	/* pop this one from the stack */
-	first = prev;
-}
-
-static void con_echo(void *result, void *user_data)
-{
-	console_print(console_arg_string(result, 0));
-}
-
-static void con_exec(void *result, void *user_data)
-{
-	console_execute_file(console_arg_string(result, 0));
-
-}
-
-
-typedef struct 
-{
-	CONFIG_INT_GETTER getter;
-	CONFIG_INT_SETTER setter;
-} INT_VARIABLE_DATA;
-
-typedef struct
-{
-	CONFIG_STR_GETTER getter;
-	CONFIG_STR_SETTER setter;
-} STR_VARIABLE_DATA;
-
-static void int_variable_command(void *result, void *user_data)
-{
-	INT_VARIABLE_DATA *data = (INT_VARIABLE_DATA *)user_data;
-
-	if(console_arg_num(result))
-		data->setter(&config, console_arg_int(result, 0));
-	else
-	{
-		char buf[1024];
-		str_format(buf, sizeof(buf), "Value: %d", data->getter(&config));
-		console_print(buf);
-	}
-}
-
-static void str_variable_command(void *result, void *user_data)
-{
-	STR_VARIABLE_DATA *data = (STR_VARIABLE_DATA *)user_data;
-
-	if(console_arg_num(result))
-		data->setter(&config, console_arg_string(result, 0));
-	else
-	{
-		char buf[1024];
-		str_format(buf, sizeof(buf), "Value: %s", data->getter(&config));
-		console_print(buf);
-	}
-}
-
-static void console_chain(void *result, void *user_data)
-{
-	COMMANDCHAIN *info = (COMMANDCHAIN *)user_data;
-	info->chain_callback(result, info->user_data, info->callback, info->callback_user_data);
-}
-
-void console_chain_command(const char *cmd, COMMANDCHAIN *chaininfo, CONSOLE_CHAIN_CALLBACK cb, void *user)
-{
-	COMMAND *command = console_get_command(cmd);
-
-	/* store info */
-	chaininfo->chain_callback = cb;
-	chaininfo->callback = command->callback;
-	chaininfo->callback_user_data = command->user_data;
-	chaininfo->user_data = user;
-	
-	/* chain */
-	command->callback = console_chain;
-	command->user_data = chaininfo;
-}
-
-void console_init()
-{
-	MACRO_REGISTER_COMMAND("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_echo, 0x0, "Echo the text");
-	MACRO_REGISTER_COMMAND("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_exec, 0x0, "Execute the specified file");
-
-	#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) { static INT_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?i", flags, int_variable_command, &data, desc) }
-	#define MACRO_CONFIG_STR(name,len,def,flags,desc) { static STR_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?r", flags, str_variable_command, &data, desc) }
-
-	#include "e_config_variables.h" 
-
-	#undef MACRO_CONFIG_INT 
-	#undef MACRO_CONFIG_STR 
-}
diff --git a/src/engine/e_console.h b/src/engine/e_console.h
deleted file mode 100644
index a45bac10..00000000
--- a/src/engine/e_console.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef _CONSOLE_H
-#define _CONSOLE_H
-
-typedef void (*CONSOLE_CALLBACK)(void *result, void *user_data);
-typedef void (*CONSOLE_CHAIN_CALLBACK)(void *result, void *user_data, CONSOLE_CALLBACK cb, void *cbuser);
-
-typedef struct COMMAND_t
-{
-	const char *name;
-	const char *params;
-	int flags;
-	CONSOLE_CALLBACK callback;
-	void *user_data;
-	const char *help;
-	struct COMMAND_t *next;
-} COMMAND;
-
-typedef struct COMMANDCHAIN_t
-{
-	CONSOLE_CHAIN_CALLBACK chain_callback;
-	CONSOLE_CALLBACK callback;
-	void *callback_user_data;
-	void *user_data;
-} COMMANDCHAIN;
-
-void console_init();
-void console_register(COMMAND *cmd);
-void console_execute_line(const char *str);
-void console_execute_line_stroked(int stroke, const char *str);
-void console_execute_file(const char *filename);
-void console_possible_commands(const char *str, int flagmask, void (*callback)(const char *cmd, void *user), void *user);
-COMMAND *console_get_command(const char *cmd);
-void console_chain_command(const char *cmd, COMMANDCHAIN *chaininfo, CONSOLE_CHAIN_CALLBACK cb, void *user);
-void console_print(const char *str);
-void console_register_print_callback(void (*callback)(const char *, void *user_data), void *user_data);
-
-/*int console_result_string(void *result, int index, const char **str);
-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, 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); }
-
-#endif
diff --git a/src/engine/e_datafile.cpp b/src/engine/e_datafile.cpp
deleted file mode 100644
index 0317f9d0..00000000
--- a/src/engine/e_datafile.cpp
+++ /dev/null
@@ -1,677 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <base/system.h>
-#include "e_datafile.h"
-#include "e_engine.h"
-#include <zlib.h>
-
-static const int DEBUG=0;
-
-typedef struct 
-{
-	int type;
-	int start;
-	int num;
-} DATAFILE_ITEM_TYPE;
-
-typedef struct 
-{
-	int type_and_id;
-	int size;
-} DATAFILE_ITEM;
-
-typedef struct
-{
-	char id[4];
-	int version;
-	int size;
-	int swaplen;
-	int num_item_types;
-	int num_items;
-	int num_raw_data;
-	int item_size;
-	int data_size;
-} DATAFILE_HEADER;
-
-typedef struct
-{
-	int num_item_types;
-	int num_items;
-	int num_raw_data;
-	int item_size;
-	int data_size;
-	char start[4];
-} DATAFILE_DATA;
-
-typedef struct
-{
-	DATAFILE_ITEM_TYPE *item_types;
-	int *item_offsets;
-	int *data_offsets;
-	int *data_sizes;
-
-	char *item_start;
-	char *data_start;
-} DATAFILE_INFO;
-
-struct DATAFILE_t
-{
-	IOHANDLE file;
-	DATAFILE_INFO info;
-	DATAFILE_HEADER header;
-	int data_start_offset;
-	char **data_ptrs;
-	char *data;
-};
-
-DATAFILE *datafile_load(const char *filename)
-{
-	DATAFILE *df;
-	IOHANDLE file;
-	DATAFILE_HEADER header;
-	unsigned readsize;
-	
-	unsigned *dst;
-	unsigned char *src;
-	unsigned j;
-	int size = 0;
-	int allocsize = 0;
-	
-	(void)dst;
-	(void)src;
-	(void)j;
-	
-	
-	dbg_msg("datafile", "datafile loading. filename='%s'", filename);
-
-	file = engine_openfile(filename, IOFLAG_READ);
-	if(!file)
-		return 0;
-	
-	/* TODO: change this header */
-	io_read(file, &header, sizeof(header));
-	if(header.id[0] != 'A' || header.id[1] != 'T' || header.id[2] != 'A' || header.id[3] != 'D')
-	{
-		if(header.id[0] != 'D' || header.id[1] != 'A' || header.id[2] != 'T' || header.id[3] != 'A')
-		{
-			dbg_msg("datafile", "wrong signature. %x %x %x %x", header.id[0], header.id[1], header.id[2], header.id[3]);
-			return 0;
-		}
-	}
-
-#if defined(CONF_ARCH_ENDIAN_BIG)
-	swap_endian(&header, sizeof(int), sizeof(header)/sizeof(int));	
-#endif
-	if(header.version != 3 && header.version != 4)
-	{
-		dbg_msg("datafile", "wrong version. version=%x", header.version);
-		return 0;
-	}
-	
-	/* read in the rest except the data */
-	size = 0;
-	size += header.num_item_types*sizeof(DATAFILE_ITEM_TYPE);
-	size += (header.num_items+header.num_raw_data)*sizeof(int);
-	if(header.version == 4)
-		size += header.num_raw_data*sizeof(int); /* v4 has uncompressed data sizes aswell */
-	size += header.item_size;
-	
-	allocsize = size;
-	allocsize += sizeof(DATAFILE); /* add space for info structure */
-	allocsize += header.num_raw_data*sizeof(void*); /* add space for data pointers */
-
-	df = (DATAFILE*)mem_alloc(allocsize, 1);
-	df->header = header;
-	df->data_start_offset = sizeof(DATAFILE_HEADER) + size;
-	df->data_ptrs = (char**)(df+1);
-	df->data = (char *)(df+1)+header.num_raw_data*sizeof(char *);
-	df->file = file;
-	
-	/* clear the data pointers */
-	mem_zero(df->data_ptrs, header.num_raw_data*sizeof(void*));
-	
-	/* read types, offsets, sizes and item data */
-	readsize = io_read(file, df->data, size);
-	if(readsize != size)
-	{
-		dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize);
-		return 0;
-	}
-
-#if defined(CONF_ARCH_ENDIAN_BIG)
-	swap_endian(df->data, sizeof(int), header.swaplen / sizeof(int));
-#endif
-
-	if(DEBUG)
-	{
-		dbg_msg("datafile", "allocsize=%d", allocsize);
-		dbg_msg("datafile", "readsize=%d", readsize);
-		dbg_msg("datafile", "swaplen=%d", header.swaplen);
-		dbg_msg("datafile", "item_size=%d", df->header.item_size);
-	}
-	
-	df->info.item_types = (DATAFILE_ITEM_TYPE *)df->data;
-	df->info.item_offsets = (int *)&df->info.item_types[df->header.num_item_types];
-	df->info.data_offsets = (int *)&df->info.item_offsets[df->header.num_items];
-	df->info.data_sizes = (int *)&df->info.data_offsets[df->header.num_raw_data];
-	
-	if(header.version == 4)
-		df->info.item_start = (char *)&df->info.data_sizes[df->header.num_raw_data];
-	else
-		df->info.item_start = (char *)&df->info.data_offsets[df->header.num_raw_data];
-	df->info.data_start = df->info.item_start + df->header.item_size;
-
-	if(DEBUG)
-		dbg_msg("datafile", "datafile loading done. datafile='%s'", filename);
-
-	if(DEBUG)
-	{
-		/*
-		for(int i = 0; i < df->data.num_raw_data; i++)
-		{
-			void *p = datafile_get_data(df, i);
-			dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&df->data)), size);
-		}
-			
-		for(int i = 0; i < datafile_num_items(df); i++)
-		{
-			int type, id;
-			void *data = datafile_get_item(df, i, &type, &id);
-			dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, df->info.item_offsets[i]);
-			int *idata = (int*)data;
-			for(int k = 0; k < 3; k++)
-				dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]);
-		}
-
-		for(int i = 0; i < df->data.num_item_types; i++)
-		{
-			dbg_msg("map", "\t%d: type=%x start=%d num=%d", i,
-				df->info.item_types[i].type,
-				df->info.item_types[i].start,
-				df->info.item_types[i].num);
-			for(int k = 0; k < df->info.item_types[i].num; k++)
-			{
-				int type, id;
-				datafile_get_item(df, df->info.item_types[i].start+k, &type, &id);
-				if(type != df->info.item_types[i].type)
-					dbg_msg("map", "\tERROR");
-			}
-		}
-		*/
-	}
-		
-	return df;
-}
-
-int datafile_num_data(DATAFILE *df)
-{
-	return df->header.num_raw_data;
-}
-
-/* always returns the size in the file */
-int datafile_get_datasize(DATAFILE *df, int index)
-{
-	if(index == df->header.num_raw_data-1)
-		return df->header.data_size-df->info.data_offsets[index];
-	return  df->info.data_offsets[index+1]-df->info.data_offsets[index];
-}
-
-static void *datafile_get_data_impl(DATAFILE *df, int index, int swap)
-{
-	/* load it if needed */
-	if(!df->data_ptrs[index])
-	{
-		/* fetch the data size */
-		int datasize = datafile_get_datasize(df, index);
-		int swapsize = datasize;
-		
-		if(df->header.version == 4)
-		{
-			/* v4 has compressed data */
-			void *temp = (char *)mem_alloc(datasize, 1);
-			unsigned long uncompressed_size = df->info.data_sizes[index];
-			unsigned long s;
-
-			dbg_msg("datafile", "loading data index=%d size=%d uncompressed=%d", index, datasize, uncompressed_size);
-			df->data_ptrs[index] = (char *)mem_alloc(uncompressed_size, 1);
-			
-			/* read the compressed data */
-			io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START);
-			io_read(df->file, temp, datasize);
-
-			/* decompress the data, TODO: check for errors */
-			s = uncompressed_size;
-			uncompress((Bytef*)df->data_ptrs[index], &s, (Bytef*)temp, datasize);
-			swapsize = s;
-
-			/* clean up the temporary buffers */
-			mem_free(temp);
-		}
-		else
-		{
-			/* load the data */
-			dbg_msg("datafile", "loading data index=%d size=%d", index, datasize);
-			df->data_ptrs[index] = (char *)mem_alloc(datasize, 1);
-			io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START);
-			io_read(df->file, df->data_ptrs[index], datasize);
-		}
-
-#if defined(CONF_ARCH_ENDIAN_BIG)
-		if(swap && swapsize)
-			swap_endian(df->data_ptrs[index], sizeof(int), swapsize/sizeof(int));
-#endif
-	}
-	
-	return df->data_ptrs[index];
-}
-
-void *datafile_get_data(DATAFILE *df, int index)
-{
-	return datafile_get_data_impl(df, index, 0);
-}
-
-void *datafile_get_data_swapped(DATAFILE *df, int index)
-{
-	return datafile_get_data_impl(df, index, 1);
-}
-
-void datafile_unload_data(DATAFILE *df, int index)
-{
-	if(index < 0)
-		return;
-		
-	/* */
-	mem_free(df->data_ptrs[index]);
-	df->data_ptrs[index] = 0x0;
-}
-
-int datafile_get_itemsize(DATAFILE *df, int index)
-{
-	if(index == df->header.num_items-1)
-		return df->header.item_size-df->info.item_offsets[index];
-	return  df->info.item_offsets[index+1]-df->info.item_offsets[index];
-}
-
-void *datafile_get_item(DATAFILE *df, int index, int *type, int *id)
-{
-	DATAFILE_ITEM *i = (DATAFILE_ITEM *)(df->info.item_start+df->info.item_offsets[index]);
-	if(type)
-		*type = (i->type_and_id>>16)&0xffff; /* remove sign extention */
-	if(id)
-		*id = i->type_and_id&0xffff;
-	return (void *)(i+1);
-}
-
-void datafile_get_type(DATAFILE *df, int type, int *start, int *num)
-{
-	int i;
-	for(i = 0; i < df->header.num_item_types; i++)
-	{
-		if(df->info.item_types[i].type == type)
-		{
-			*start = df->info.item_types[i].start;
-			*num = df->info.item_types[i].num;
-			return;
-		}
-	}
-	
-	*start = 0;
-	*num = 0;
-}
-
-void *datafile_find_item(DATAFILE *df, int type, int id)
-{
-	int start, num,i ;
-	int item_id;
-	void *item;
-	
-	datafile_get_type(df, type, &start, &num);
-	for(i = 0; i < num; i++)
-	{
-		item = datafile_get_item(df, start+i,0, &item_id);
-		if(id == item_id)
-			return item;
-	}
-	return 0;
-}
-
-int datafile_num_items(DATAFILE *df)
-{
-	return df->header.num_items;
-}
-
-void datafile_unload(DATAFILE *df)
-{
-	if(df)
-	{
-		/* free the data that is loaded */
-		int i;
-		for(i = 0; i < df->header.num_raw_data; i++)
-			mem_free(df->data_ptrs[i]);
-		
-		io_close(df->file);
-		mem_free(df);
-	}
-}
-
-/* DATAFILE output */
-typedef struct
-{
-	int uncompressed_size;
-	int compressed_size;
-	void *compressed_data;
-} DATA_INFO;
-
-typedef struct
-{
-	int type;
-	int id;
-	int size;
-	int next;
-	int prev;
-	void *data;
-} ITEM_INFO;
-
-typedef struct
-{
-	int num;
-	int first;
-	int last;
-} ITEMTYPE_INFO;
-
-/* */
-struct DATAFILE_OUT_t
-{
-	IOHANDLE file;
-	int num_items;
-	int num_datas;
-	int num_item_types;
-	ITEMTYPE_INFO item_types[0xffff];
-	ITEM_INFO items[1024];
-	DATA_INFO datas[1024];
-};
-
-DATAFILE_OUT *datafile_create(const char *filename)
-{
-	int i;
-	DATAFILE_OUT *df = (DATAFILE_OUT*)mem_alloc(sizeof(DATAFILE_OUT), 1);
-	df->file = engine_openfile(filename, IOFLAG_WRITE);
-	if(!df->file)
-	{
-		mem_free(df);
-		return 0;
-	}
-	
-	df->num_items = 0;
-	df->num_datas = 0;
-	df->num_item_types = 0;
-	mem_zero(&df->item_types, sizeof(df->item_types));
-
-	for(i = 0; i < 0xffff; i++)
-	{
-		df->item_types[i].first = -1;
-		df->item_types[i].last = -1;
-	}
-	
-	return df;
-}
-
-int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data)
-{
-	df->items[df->num_items].type = type;
-	df->items[df->num_items].id = id;
-	df->items[df->num_items].size = size;
-	
-	/*
-	dbg_msg("datafile", "added item type=%d id=%d size=%d", type, id, size);
-	int i;
-	for(i = 0; i < size/4; i++)
-		dbg_msg("datafile", "\t%d: %08x %d", i, ((int*)data)[i], ((int*)data)[i]);
-	*/
-	
-	/* copy data */
-	df->items[df->num_items].data = mem_alloc(size, 1);
-	mem_copy(df->items[df->num_items].data, data, size);
-
-	if(!df->item_types[type].num) /* count item types */
-		df->num_item_types++;
-
-	/* link */
-	df->items[df->num_items].prev = df->item_types[type].last;
-	df->items[df->num_items].next = -1;
-	
-	if(df->item_types[type].last != -1)
-		df->items[df->item_types[type].last].next = df->num_items;
-	df->item_types[type].last = df->num_items;
-	
-	if(df->item_types[type].first == -1)
-		df->item_types[type].first = df->num_items;
-	
-	df->item_types[type].num++;
-		
-	df->num_items++;
-	return df->num_items-1;
-}
-
-int datafile_add_data(DATAFILE_OUT *df, int size, void *data)
-{
-	DATA_INFO *info = &df->datas[df->num_datas];
-	unsigned long s = compressBound(size);
-	void *compdata = mem_alloc(s, 1); /* temporary buffer that we use duing compression */
-
-	int result = compress((Bytef*)compdata, &s, (Bytef*)data, size);
-	if(result != Z_OK)
-	{
-		dbg_msg("datafile", "compression error %d", result);
-		dbg_assert(0, "zlib error");
-	}
-		
-	info->uncompressed_size = size;
-	info->compressed_size = (int)s;
-	info->compressed_data = mem_alloc(info->compressed_size, 1);
-	mem_copy(info->compressed_data, compdata, info->compressed_size);
-	mem_free(compdata);
-
-	df->num_datas++;
-	return df->num_datas-1;
-}
-
-int datafile_add_data_swapped(DATAFILE_OUT *df, int size, void *data)
-{
-#if defined(CONF_ARCH_ENDIAN_BIG)
-	void *swapped = mem_alloc(size, 1); /* temporary buffer that we use duing compression */
-	int index;
-	mem_copy(swapped, data, size);
-	swap_endian(&swapped, sizeof(int), size/sizeof(int));
-	index = datafile_add_data(df, size, swapped);
-	mem_free(swapped);
-	return index;
-#else
-	return datafile_add_data(df, size, data);
-#endif
-}
-
-
-int datafile_finish(DATAFILE_OUT *df)
-{
-	int itemsize = 0;
-	int i, count, offset;
-	int typessize, headersize, offsetsize, filesize, swapsize;
-	int datasize = 0;
-	DATAFILE_ITEM_TYPE info;
-	DATAFILE_ITEM itm;
-	DATAFILE_HEADER header;
-
-	/* we should now write this file! */
-	if(DEBUG)
-		dbg_msg("datafile", "writing");
-
-	/* calculate sizes */
-	for(i = 0; i < df->num_items; i++)
-	{
-		if(DEBUG)
-			dbg_msg("datafile", "item=%d size=%d (%d)", i, df->items[i].size, df->items[i].size+sizeof(DATAFILE_ITEM));
-		itemsize += df->items[i].size + sizeof(DATAFILE_ITEM);
-	}
-	
-	
-	for(i = 0; i < df->num_datas; i++)
-		datasize += df->datas[i].compressed_size;
-	
-	/* calculate the complete size */
-	typessize = df->num_item_types*sizeof(DATAFILE_ITEM_TYPE);
-	headersize = sizeof(DATAFILE_HEADER);
-	offsetsize = df->num_items*sizeof(int) + df->num_datas*sizeof(int);
-	filesize = headersize + typessize + offsetsize + itemsize + datasize;
-	swapsize = filesize - datasize;
-	
-	(void)swapsize;
-	
-	if(DEBUG)
-		dbg_msg("datafile", "num_item_types=%d typessize=%d itemsize=%d datasize=%d", df->num_item_types, typessize, itemsize, datasize);
-	
-	/* construct header */
-	{
-		header.id[0] = 'D';
-		header.id[1] = 'A';
-		header.id[2] = 'T';
-		header.id[3] = 'A';
-		header.version = 4;
-		header.size = filesize - 16;
-		header.swaplen = swapsize - 16;
-		header.num_item_types = df->num_item_types;
-		header.num_items = df->num_items;
-		header.num_raw_data = df->num_datas;
-		header.item_size = itemsize;
-		header.data_size = datasize;
-		
-		/* TODO: apply swapping */
-		/* write header */
-		if(DEBUG)
-			dbg_msg("datafile", "headersize=%d", sizeof(header));
-		io_write(df->file, &header, sizeof(header));
-	}
-	
-	/* write types */
-	for(i = 0, count = 0; i < 0xffff; i++)
-	{
-		if(df->item_types[i].num)
-		{
-			/* write info */
-			info.type = i;
-			info.start = count;
-			info.num = df->item_types[i].num;
-			if(DEBUG)
-				dbg_msg("datafile", "writing type=%x start=%d num=%d", info.type, info.start, info.num);
-			io_write(df->file, &info, sizeof(info));
-			count += df->item_types[i].num;
-		}
-	}
-	
-	/* write item offsets */
-	for(i = 0, offset = 0; i < 0xffff; i++)
-	{
-		if(df->item_types[i].num)
-		{
-			/* write all items in of this type */
-			int k = df->item_types[i].first;
-			while(k != -1)
-			{
-				if(DEBUG)
-					dbg_msg("datafile", "writing item offset num=%d offset=%d", k, offset);
-				io_write(df->file, &offset, sizeof(offset));
-				offset += df->items[k].size + sizeof(DATAFILE_ITEM);
-				
-				/* next */
-				k = df->items[k].next;
-			}
-		}
-	}
-	
-	/* write data offsets */
-	for(i = 0, offset = 0; i < df->num_datas; i++)
-	{
-		if(DEBUG)
-			dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset);
-		io_write(df->file, &offset, sizeof(offset));
-		offset += df->datas[i].compressed_size;
-	}
-
-	/* write data uncompressed sizes */
-	for(i = 0, offset = 0; i < df->num_datas; i++)
-	{
-		/*
-		if(DEBUG)
-			dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset);
-		*/
-		io_write(df->file, &df->datas[i].uncompressed_size, sizeof(int));
-	}
-	
-	/* write items */
-	for(i = 0; i < 0xffff; i++)
-	{
-		if(df->item_types[i].num)
-		{
-			/* write all items in of this type */
-			int k = df->item_types[i].first;
-			while(k != -1)
-			{
-				itm.type_and_id = (i<<16)|df->items[k].id;
-				itm.size = df->items[k].size;
-				if(DEBUG)
-					dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, df->items[k].id, df->items[k].size);
-				
-				io_write(df->file, &itm, sizeof(itm));
-				io_write(df->file, df->items[k].data, df->items[k].size);
-				
-				/* next */
-				k = df->items[k].next;
-			}
-		}
-	}
-	
-	/* write data */
-	for(i = 0; i < df->num_datas; i++)
-	{
-		if(DEBUG)
-			dbg_msg("datafile", "writing data id=%d size=%d", i, df->datas[i].compressed_size);
-		io_write(df->file, df->datas[i].compressed_data, df->datas[i].compressed_size);
-	}
-
-	/* free data */
-	for(i = 0; i < df->num_items; i++)
-		mem_free(df->items[i].data);
-
-	
-	io_close(df->file);
-	mem_free(df);
-
-	if(DEBUG)
-		dbg_msg("datafile", "done");
-	return 0;
-}
-
-#define BUFFER_SIZE 64*1024
-
-int datafile_crc(const char *filename)
-{
-	unsigned char buffer[BUFFER_SIZE];
-	IOHANDLE file;
-	int crc = 0;
-	unsigned bytes = 0;
-	
-	file = engine_openfile(filename, IOFLAG_READ);
-	if(!file)
-		return 0;
-		
-	while(1)
-	{
-		bytes = io_read(file, buffer, BUFFER_SIZE);
-		if(bytes <= 0)
-			break;
-		crc = crc32(crc, buffer, bytes);
-	}
-	
-	io_close(file);
-
-	return crc;	
-}
diff --git a/src/engine/e_datafile.h b/src/engine/e_datafile.h
deleted file mode 100644
index 203f415c..00000000
--- a/src/engine/e_datafile.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-
-/* raw datafile access */
-typedef struct DATAFILE_t DATAFILE;
-
-/* read access */
-DATAFILE *datafile_load(const char *filename);
-DATAFILE *datafile_load_old(const char *filename);
-void *datafile_get_data(DATAFILE *df, int index);
-void *datafile_get_data_swapped(DATAFILE *df, int index); /* makes sure that the data is 32bit LE ints when saved */
-int datafile_get_datasize(DATAFILE *df, int index);
-void datafile_unload_data(DATAFILE *df, int index);
-void *datafile_get_item(DATAFILE *df, int index, int *type, int *id);
-int datafile_get_itemsize(DATAFILE *df, int index);
-void datafile_get_type(DATAFILE *df, int type, int *start, int *num);
-void *datafile_find_item(DATAFILE *df, int type, int id);
-int datafile_num_items(DATAFILE *df);
-int datafile_num_data(DATAFILE *df);
-void datafile_unload(DATAFILE *df);
-
-int datafile_crc(const char *filename);
-
-/* write access */
-typedef struct DATAFILE_OUT_t DATAFILE_OUT;
-DATAFILE_OUT *datafile_create(const char *filename);
-int datafile_add_data(DATAFILE_OUT *df, int size, void *data);
-int datafile_add_data_swapped(DATAFILE_OUT *df, int size, void *data);
-int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data);
-int datafile_finish(DATAFILE_OUT *df);
diff --git a/src/engine/e_demorec.cpp b/src/engine/e_demorec.cpp
deleted file mode 100644
index 1bab1b8d..00000000
--- a/src/engine/e_demorec.cpp
+++ /dev/null
@@ -1,640 +0,0 @@
-#include <base/system.h>
-#include "e_demorec.h"
-#include "e_memheap.h"
-#include "e_snapshot.h"
-#include "e_compression.h"
-#include "e_network.h"
-#include "e_engine.h"
-#include "e_if_other.h"
-
-static IOHANDLE record_file = 0;
-static const unsigned char header_marker[8] = {'T', 'W', 'D', 'E', 'M', 'O', 0, 1};
-
-/* Record */
-static int record_lasttickmarker = -1;
-static int record_lastkeyframe;
-static unsigned char record_lastsnapshotdata[CSnapshot::MAX_SIZE];
-
-int demorec_isrecording() { return record_file != 0; }
-
-int demorec_record_start(const char *filename, const char *netversion, const char *map, int crc, const char *type)
-{
-	DEMOREC_HEADER header;
-	if(record_file)
-		return -1;
-
-	record_file = engine_openfile(filename, IOFLAG_WRITE);
-	
-	if(!record_file)
-	{
-		dbg_msg("demorec/record", "Unable to open '%s' for recording", filename);
-		return -1;
-	}
-	
-	/* write header */
-	mem_zero(&header, sizeof(header));
-	mem_copy(header.marker, header_marker, sizeof(header.marker));
-	str_copy(header.netversion, netversion, sizeof(header.netversion));
-	str_copy(header.map, map, sizeof(header.map));
-	str_copy(header.type, type, sizeof(header.type));
-	header.crc[0] = (crc>>24)&0xff;
-	header.crc[1] = (crc>>16)&0xff;
-	header.crc[2] = (crc>>8)&0xff;
-	header.crc[3] = (crc)&0xff;
-	io_write(record_file, &header, sizeof(header));
-	
-	record_lastkeyframe = -1;
-	record_lasttickmarker = -1;
-	
-	dbg_msg("demorec/record", "Recording to '%s'", filename);
-	return 0;
-}
-
-/*
-	Tickmarker
-		7   = Always set
-		6   = Keyframe flag
-		0-5 = Delta tick
-	
-	Normal
-		7   = Not set
-		5-6 = Type
-		0-4 = Size
-*/
-
-enum
-{
-	CHUNKTYPEFLAG_TICKMARKER = 0x80,
-	CHUNKTICKFLAG_KEYFRAME = 0x40, /* only when tickmarker is set*/
-	
-	CHUNKMASK_TICK = 0x3f,
-	CHUNKMASK_TYPE = 0x60,
-	CHUNKMASK_SIZE = 0x1f,
-	
-	CHUNKTYPE_SNAPSHOT = 1,
-	CHUNKTYPE_MESSAGE = 2,
-	CHUNKTYPE_DELTA = 3,
-
-	CHUNKFLAG_BIGSIZE = 0x10
-};
-
-static void demorec_record_write_tickmarker(int tick, int keyframe)
-{
-	if(record_lasttickmarker == -1 || tick-record_lasttickmarker > 63 || keyframe)
-	{
-		unsigned char chunk[5];
-		chunk[0] = CHUNKTYPEFLAG_TICKMARKER;
-		chunk[1] = (tick>>24)&0xff;
-		chunk[2] = (tick>>16)&0xff;
-		chunk[3] = (tick>>8)&0xff;
-		chunk[4] = (tick)&0xff;
-
-		if(keyframe)
-			chunk[0] |= CHUNKTICKFLAG_KEYFRAME;
-		
-		io_write(record_file, chunk, sizeof(chunk));
-	}
-	else
-	{
-		unsigned char chunk[1];
-		chunk[0] = CHUNKTYPEFLAG_TICKMARKER | (tick-record_lasttickmarker);
-		io_write(record_file, chunk, sizeof(chunk));
-	}	
-
-	record_lasttickmarker = tick;
-}
-
-static void demorec_record_write(int type, const void *data, int size)
-{
-	char buffer[64*1024];
-	char buffer2[64*1024];
-	unsigned char chunk[3];
-	
-	if(!record_file)
-		return;
-
-	
-	/* pad the data with 0 so we get an alignment of 4,
-	else the compression won't work and miss some bytes */
-	mem_copy(buffer2, data, size);
-	while(size&3)
-		buffer2[size++] = 0;
-	size = intpack_compress(buffer2, size, buffer); /* buffer2 -> buffer */
-	size = CNetBase::Compress(buffer, size, buffer2, sizeof(buffer2)); /* buffer -> buffer2 */
-	
-	
-	chunk[0] = ((type&0x3)<<5);
-	if(size < 30)
-	{
-		chunk[0] |= size;
-		io_write(record_file, chunk, 1);
-	}
-	else
-	{
-		if(size < 256)
-		{
-			chunk[0] |= 30;
-			chunk[1] = size&0xff;
-			io_write(record_file, chunk, 2);
-		}
-		else
-		{
-			chunk[0] |= 31;
-			chunk[1] = size&0xff;
-			chunk[2] = size>>8;
-			io_write(record_file, chunk, 3);
-		}
-	}
-	
-	io_write(record_file, buffer2, size);
-}
-
-void demorec_record_snapshot(int tick, const void *data, int size)
-{
-	if(record_lastkeyframe == -1 || (tick-record_lastkeyframe) > SERVER_TICK_SPEED*5)
-	{
-		/* write full tickmarker */
-		demorec_record_write_tickmarker(tick, 1);
-		
-		/* write snapshot */
-		demorec_record_write(CHUNKTYPE_SNAPSHOT, data, size);
-			
-		record_lastkeyframe = tick;
-		mem_copy(record_lastsnapshotdata, data, size);
-	}
-	else
-	{
-		/* create delta, prepend tick */
-		char delta_data[CSnapshot::MAX_SIZE+sizeof(int)];
-		int delta_size;
-
-		/* write tickmarker */
-		demorec_record_write_tickmarker(tick, 0);
-		
-		delta_size = CSnapshot::CreateDelta((CSnapshot*)record_lastsnapshotdata, (CSnapshot*)data, &delta_data);
-		if(delta_size)
-		{
-			/* record delta */
-			demorec_record_write(CHUNKTYPE_DELTA, delta_data, delta_size);
-			mem_copy(record_lastsnapshotdata, data, size);
-		}
-	}
-}
-
-void demorec_record_message(const void *data, int size)
-{
-	demorec_record_write(CHUNKTYPE_MESSAGE, data, size);
-}
-
-int demorec_record_stop()
-{
-	if(!record_file)
-		return -1;
-		
-	dbg_msg("demorec/record", "Stopped recording");
-	io_close(record_file);
-	record_file = 0;
-	return 0;
-}
-
-/* Playback */
-typedef struct KEYFRAME
-{
-	long filepos;
-	int tick;
-} KEYFRAME;
-
-typedef struct KEYFRAME_SEARCH
-{
-	KEYFRAME frame;
-	struct KEYFRAME_SEARCH *next;
-} KEYFRAME_SEARCH;
-
-static IOHANDLE play_file = 0;
-static DEMOREC_PLAYCALLBACK play_callback_snapshot = 0;
-static DEMOREC_PLAYCALLBACK play_callback_message = 0;
-static KEYFRAME *keyframes = 0;
-
-static DEMOREC_PLAYBACKINFO playbackinfo;
-static unsigned char playback_lastsnapshotdata[CSnapshot::MAX_SIZE];
-static int playback_lastsnapshotdata_size = -1;
-
-
-const DEMOREC_PLAYBACKINFO *demorec_playback_info() { return &playbackinfo; }
-int demorec_isplaying() { return play_file != 0; }
-
-int demorec_playback_registercallbacks(DEMOREC_PLAYCALLBACK snapshot_cb, DEMOREC_PLAYCALLBACK message_cb)
-{
-	play_callback_snapshot = snapshot_cb;
-	play_callback_message = message_cb;
-	return 0;
-}
-
-static int read_chunk_header(int *type, int *size, int *tick)
-{
-	unsigned char chunk = 0;
-	
-	*size = 0;
-	*type = 0;
-	
-	if(io_read(play_file, &chunk, sizeof(chunk)) != sizeof(chunk))
-		return -1;
-		
-	if(chunk&CHUNKTYPEFLAG_TICKMARKER)
-	{
-		/* decode tick marker */
-		int tickdelta = chunk&(CHUNKMASK_TICK);
-		*type = chunk&(CHUNKTYPEFLAG_TICKMARKER|CHUNKTICKFLAG_KEYFRAME);
-		
-		if(tickdelta == 0)
-		{
-			unsigned char tickdata[4];
-			if(io_read(play_file, tickdata, sizeof(tickdata)) != sizeof(tickdata))
-				return -1;
-			*tick = (tickdata[0]<<24) | (tickdata[1]<<16) | (tickdata[2]<<8) | tickdata[3];
-		}
-		else
-		{
-			*tick += tickdelta;
-		}
-		
-	}
-	else
-	{
-		/* decode normal chunk */
-		*type = (chunk&CHUNKMASK_TYPE)>>5;
-		*size = chunk&CHUNKMASK_SIZE;
-		
-		if(*size == 30)
-		{
-			unsigned char sizedata[1];
-			if(io_read(play_file, sizedata, sizeof(sizedata)) != sizeof(sizedata))
-				return -1;
-			*size = sizedata[0];
-			
-		}
-		else if(*size == 31)
-		{
-			unsigned char sizedata[2];
-			if(io_read(play_file, sizedata, sizeof(sizedata)) != sizeof(sizedata))
-				return -1;
-			*size = (sizedata[1]<<8) | sizedata[0];
-		}
-	}
-	
-	return 0;
-}
-
-static void scan_file()
-{
-	long start_pos;
-	HEAP *heap = 0;
-	KEYFRAME_SEARCH *first_key = 0;
-	KEYFRAME_SEARCH *current_key = 0;
-	/*DEMOREC_CHUNK chunk;*/
-	int chunk_size, chunk_type, chunk_tick = 0;
-	int i;
-	
-	heap = memheap_create();
-
-	start_pos = io_tell(play_file);
-	playbackinfo.seekable_points = 0;
-
-	while(1)
-	{
-		long current_pos = io_tell(play_file);
-		
-		if(read_chunk_header(&chunk_type, &chunk_size, &chunk_tick))
-			break;
-			
-		/* read the chunk */
-		if(chunk_type&CHUNKTYPEFLAG_TICKMARKER)
-		{
-			if(chunk_type&CHUNKTICKFLAG_KEYFRAME)
-			{
-				KEYFRAME_SEARCH *key;
-				
-				/* save the position */
-				key = (KEYFRAME_SEARCH *)memheap_allocate(heap, sizeof(KEYFRAME_SEARCH));
-				key->frame.filepos = current_pos;
-				key->frame.tick = chunk_tick;
-				key->next = 0;
-				if(current_key)
-					current_key->next = key;
-				if(!first_key)
-					first_key = key;
-				current_key = key;
-				playbackinfo.seekable_points++;
-			}
-			
-			if(playbackinfo.first_tick == -1)
-				playbackinfo.first_tick = chunk_tick;
-			playbackinfo.last_tick = chunk_tick;
-		}
-		else if(chunk_size)
-			io_skip(play_file, chunk_size);
-			
-	}
-
-	/* copy all the frames to an array instead for fast access */
-	keyframes = (KEYFRAME*)mem_alloc(playbackinfo.seekable_points*sizeof(KEYFRAME), 1);
-	for(current_key = first_key, i = 0; current_key; current_key = current_key->next, i++)
-		keyframes[i] = current_key->frame;
-		
-	/* destroy the temporary heap and seek back to the start */
-	memheap_destroy(heap);
-	io_seek(play_file, start_pos, IOSEEK_START);
-}
-
-static void do_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;
-
-	/* update ticks */
-	playbackinfo.previous_tick = playbackinfo.current_tick;
-	playbackinfo.current_tick = playbackinfo.next_tick;
-	chunk_tick = playbackinfo.current_tick;
-
-	while(1)
-	{
-		if(read_chunk_header(&chunk_type, &chunk_size, &chunk_tick))
-		{
-			/* stop on error or eof */
-			dbg_msg("demorec", "end of file");
-			demorec_playback_pause();
-			break;
-		}
-		
-		/* read the chunk */
-		if(chunk_size)
-		{
-			if(io_read(play_file, compresseddata, chunk_size) != (unsigned)chunk_size)
-			{
-				/* stop on error or eof */
-				dbg_msg("demorec", "error reading chunk");
-				demorec_playback_stop();
-				break;
-			}
-			
-			data_size = CNetBase::Decompress(compresseddata, chunk_size, decompressed, sizeof(decompressed));
-			if(data_size < 0)
-			{
-				/* stop on error or eof */
-				dbg_msg("demorec", "error during network decompression");
-				demorec_playback_stop();
-				break;
-			}
-			
-			data_size = intpack_decompress(decompressed, data_size, data);
-
-			if(data_size < 0)
-			{
-				dbg_msg("demorec", "error during intpack decompression");
-				demorec_playback_stop();
-				break;
-			}
-		}
-			
-		if(chunk_type == CHUNKTYPE_DELTA)
-		{
-			/* process delta snapshot */
-			static char newsnap[CSnapshot::MAX_SIZE];
-			
-			got_snapshot = 1;
-			
-			data_size = CSnapshot::UnpackDelta((CSnapshot*)playback_lastsnapshotdata, (CSnapshot*)newsnap, data, data_size);
-			
-			if(data_size >= 0)
-			{
-				if(play_callback_snapshot)
-					play_callback_snapshot(newsnap, data_size);
-
-				playback_lastsnapshotdata_size = data_size;
-				mem_copy(playback_lastsnapshotdata, newsnap, data_size);
-			}
-			else
-				dbg_msg("demorec", "error duing unpacking of delta, err=%d", data_size);
-		}
-		else if(chunk_type == CHUNKTYPE_SNAPSHOT)
-		{
-			/* process full snapshot */
-			got_snapshot = 1;
-			
-			playback_lastsnapshotdata_size = data_size;
-			mem_copy(playback_lastsnapshotdata, data, data_size);
-			if(play_callback_snapshot)
-				play_callback_snapshot(data, data_size);
-		}
-		else
-		{
-			/* if there were no snapshots in this tick, replay the last one */
-			if(!got_snapshot && play_callback_snapshot && playback_lastsnapshotdata_size != -1)
-			{
-				got_snapshot = 1;
-				play_callback_snapshot(playback_lastsnapshotdata, playback_lastsnapshotdata_size);
-			}
-			
-			/* check the remaining types */
-			if(chunk_type&CHUNKTYPEFLAG_TICKMARKER)
-			{
-				playbackinfo.next_tick = chunk_tick;
-				break;
-			}
-			else if(chunk_type == CHUNKTYPE_MESSAGE)
-			{
-				if(play_callback_message)
-					play_callback_message(data, data_size);
-			}
-		}
-	}
-}
-
-void demorec_playback_pause()
-{
-	playbackinfo.paused = 1;
-}
-
-void demorec_playback_unpause()
-{
-	if(playbackinfo.paused)
-	{
-		/*playbackinfo.start_tick = playbackinfo.current_tick;
-		playbackinfo.start_time = time_get();*/
-		playbackinfo.paused = 0;
-	}
-}
-
-int demorec_playback_load(const char *filename)
-{
-	play_file = engine_openfile(filename, IOFLAG_READ);
-	if(!play_file)
-	{
-		dbg_msg("demorec/playback", "could not open '%s'", filename);
-		return -1;
-	}
-	
-	/* clear the playback info */
-	mem_zero(&playbackinfo, sizeof(playbackinfo));
-	playbackinfo.first_tick = -1;
-	playbackinfo.last_tick = -1;
-	/*playbackinfo.start_tick = -1;*/
-	playbackinfo.next_tick = -1;
-	playbackinfo.current_tick = -1;
-	playbackinfo.previous_tick = -1;
-	playbackinfo.speed = 1;
-	
-	playback_lastsnapshotdata_size = -1;
-
-	/* read the header */	
-	io_read(play_file, &playbackinfo.header, sizeof(playbackinfo.header));
-	if(mem_comp(playbackinfo.header.marker, header_marker, sizeof(header_marker)) != 0)
-	{
-		dbg_msg("demorec/playback", "'%s' is not a demo file", filename);
-		io_close(play_file);
-		play_file = 0;
-		return -1;
-	}
-	
-	/* scan the file for interessting points */
-	scan_file();
-	
-	/* ready for playback */	
-	return 0;
-}
-
-int demorec_playback_nextframe()
-{
-	do_tick();
-	return demorec_isplaying();
-}
-
-int demorec_playback_play()
-{
-	/* fill in previous and next tick */
-	while(playbackinfo.previous_tick == -1 && demorec_isplaying())
-		do_tick();
-		
-	/* set start info */
-	/*playbackinfo.start_tick = playbackinfo.previous_tick;
-	playbackinfo.start_time = time_get();*/
-	playbackinfo.current_time = playbackinfo.previous_tick*time_freq()/SERVER_TICK_SPEED;
-	playbackinfo.last_update = time_get();
-	return 0;
-}
-
-int demorec_playback_set(float percent)
-{
-	int keyframe;
-	int wanted_tick;
-	if(!play_file)
-		return -1;
-	
-	/* -5 because we have to have a current tick and previous tick when we do the playback */
-	wanted_tick = playbackinfo.first_tick + (int)((playbackinfo.last_tick-playbackinfo.first_tick)*percent) - 5;
-	
-	keyframe = (int)(playbackinfo.seekable_points*percent);
-
-	if(keyframe < 0 || keyframe >= playbackinfo.seekable_points)
-		return -1;
-	
-	/* get correct key frame */
-	if(keyframes[keyframe].tick < wanted_tick)
-		while(keyframe < playbackinfo.seekable_points-1 && keyframes[keyframe].tick < wanted_tick)
-			keyframe++;
-
-	while(keyframe && keyframes[keyframe].tick > wanted_tick)
-		keyframe--;
-	
-	/* seek to the correct keyframe */
-	io_seek(play_file, keyframes[keyframe].filepos, IOSEEK_START);
-
-	/*playbackinfo.start_tick = -1;*/
-	playbackinfo.next_tick = -1;
-	playbackinfo.current_tick = -1;
-	playbackinfo.previous_tick = -1;
-
-	/* playback everything until we hit our tick */
-	while(playbackinfo.previous_tick < wanted_tick)
-		do_tick();
-	
-	demorec_playback_play();
-	
-	return 0;
-}
-
-void demorec_playback_setspeed(float speed)
-{
-	playbackinfo.speed = speed;
-}
-
-int demorec_playback_update()
-{
-	int64 now = time_get();
-	int64 deltatime = now-playbackinfo.last_update;
-	playbackinfo.last_update = now;
-	
-	if(!demorec_isplaying())
-		return 0;
-	
-	if(playbackinfo.paused)
-	{
-		
-	}
-	else
-	{
-		int64 freq = time_freq();
-		playbackinfo.current_time += (int64)(deltatime*(double)playbackinfo.speed);
-		
-		while(1)
-		{
-			int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED;
-
-			/* break if we are ready */		
-			if(curtick_start > playbackinfo.current_time)
-				break;
-			
-			/* do one more tick */
-			do_tick();
-			
-			if(playbackinfo.paused)
-				return 0;
-		}
-
-		/* update intratick */
-		{	
-			int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED;
-			int64 prevtick_start = (playbackinfo.previous_tick)*freq/SERVER_TICK_SPEED;
-			playbackinfo.intratick = (playbackinfo.current_time - prevtick_start) / (float)(curtick_start-prevtick_start);
-			playbackinfo.ticktime = (playbackinfo.current_time - prevtick_start) / (float)freq;
-		}
-		
-		if(playbackinfo.current_tick == playbackinfo.previous_tick ||
-			playbackinfo.current_tick == playbackinfo.next_tick)
-		{
-			dbg_msg("demorec/playback", "tick error prev=%d cur=%d next=%d",
-				playbackinfo.previous_tick, playbackinfo.current_tick, playbackinfo.next_tick);
-		}
-	}
-	
-	return 0;
-}
-
-int demorec_playback_stop()
-{
-	if(!play_file)
-		return -1;
-		
-	dbg_msg("demorec/playback", "Stopped playback");
-	io_close(play_file);
-	play_file = 0;
-	mem_free(keyframes);
-	keyframes = 0;
-	return 0;
-}
-
-
diff --git a/src/engine/e_demorec.h b/src/engine/e_demorec.h
deleted file mode 100644
index 9716b463..00000000
--- a/src/engine/e_demorec.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef _DEMOREC_H
-#define _DEMOREC_H
-
-typedef struct DEMOREC_HEADER
-{
-	char marker[8];
-	char netversion[64];
-	char map[64];
-	unsigned char crc[4];
-	char type[8];
-} DEMOREC_HEADER;
-
-typedef struct DEMOREC_CHUNK
-{
-	char type[2];
-	unsigned short size;
-} DEMOREC_CHUNK;
-
-typedef struct DEMOREC_TICKMARKER
-{
-	int tick;
-} DEMOREC_TICKMARKER;
-
-typedef struct DEMOREC_PLAYBACKINFO
-{
-	DEMOREC_HEADER header;
-	
-	int paused;
-	float speed;
-	
-	int64 last_update;
-	int64 current_time;
-	
-	int first_tick;
-	int last_tick;
-	
-	int seekable_points;
-	
-	int next_tick;
-	int current_tick;
-	int previous_tick;
-	
-	float intratick;
-	float ticktime;
-} DEMOREC_PLAYBACKINFO;
-
-int demorec_record_start(const char *filename, const char *netversion, const char *map, int map_crc, const char *type);
-int demorec_isrecording();
-void demorec_record_snapshot(int tick, const void *data, int size);
-void demorec_record_message(const void *data, int size);
-int demorec_record_stop();
-
-typedef void (*DEMOREC_PLAYCALLBACK)(void *data, int size);
-
-int demorec_playback_registercallbacks(DEMOREC_PLAYCALLBACK snapshot_cb, DEMOREC_PLAYCALLBACK message_cb);
-int demorec_playback_load(const char *filename);
-int demorec_playback_nextframe();
-int demorec_playback_play();
-void demorec_playback_pause();
-void demorec_playback_unpause();
-void demorec_playback_setspeed(float speed);
-int demorec_playback_set(float precent);
-int demorec_playback_update();
-const DEMOREC_PLAYBACKINFO *demorec_playback_info();
-int demorec_isplaying();
-int demorec_playback_stop();
-
-#endif
diff --git a/src/engine/e_engine.cpp b/src/engine/e_engine.cpp
deleted file mode 100644
index 4475478a..00000000
--- a/src/engine/e_engine.cpp
+++ /dev/null
@@ -1,596 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <base/system.h>
-
-#include <engine/e_server_interface.h>
-#include <engine/e_config.h>
-#include <engine/e_console.h>
-#include <engine/e_engine.h>
-#include <engine/e_network.h>
-#include "e_linereader.h"
-
-/* compiled-in data-dir path */
-#define DATA_DIR "data"
-
-static JOBPOOL hostlookuppool;
-static int engine_find_datadir(char *argv0);
-
-static void con_dbg_dumpmem(void *result, void *user_data)
-{
-	mem_debug_dump();
-}
-
-static void con_dbg_lognetwork(void *result, void *user_data)
-{
-	CNetBase::OpenLog("network_sent.dat", "network_recv.dat");
-}
-
-
-static char application_save_path[512] = {0};
-static char datadir[512] = {0};
-
-const char *engine_savepath(const char *filename, char *buffer, int max)
-{
-	str_format(buffer, max, "%s/%s", application_save_path, filename);
-	return buffer;
-}
-
-void engine_init(const char *appname)
-{
-	dbg_logger_stdout();
-	dbg_logger_debugger();
-	
-	/* */
-	dbg_msg("engine", "running on %s-%s-%s", CONF_FAMILY_STRING, CONF_PLATFORM_STRING, CONF_ARCH_STRING);
-#ifdef CONF_ARCH_ENDIAN_LITTLE
-	dbg_msg("engine", "arch is little endian");
-#elif defined(CONF_ARCH_ENDIAN_BIG)
-	dbg_msg("engine", "arch is big endian");
-#else
-	dbg_msg("engine", "unknown endian");
-#endif
-
-	/* init the network */
-	net_init();
-	CNetBase::Init();
-	
-	/* create storage location */
-	{
-		char path[1024] = {0};
-		fs_storage_path(appname, application_save_path, sizeof(application_save_path));
-		if(fs_makedir(application_save_path) == 0)
-		{		
-			str_format(path, sizeof(path), "%s/screenshots", application_save_path);
-			fs_makedir(path);
-
-			str_format(path, sizeof(path), "%s/maps", application_save_path);
-			fs_makedir(path);
-
-			str_format(path, sizeof(path), "%s/downloadedmaps", application_save_path);
-			fs_makedir(path);
-
-			str_format(path, sizeof(path), "%s/demos", application_save_path);
-			fs_makedir(path);
-		}
-	}
-
-	/* init console and add the console logger */
-	console_init();
-	dbg_logger(console_print);
-	
-	jobs_initpool(&hostlookuppool, 1);
-
-	MACRO_REGISTER_COMMAND("dbg_dumpmem", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_dumpmem, 0x0, "Dump the memory");
-	MACRO_REGISTER_COMMAND("dbg_lognetwork", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_lognetwork, 0x0, "Log the network");
-	
-	/* reset the config */
-	config_reset();
-}
-
-
-void engine_listdir(int types, const char *path, FS_LISTDIR_CALLBACK cb, void *user)
-{
-	char buffer[1024];
-	
-	/* list current directory */
-	if(types&LISTDIRTYPE_CURRENT)
-	{
-		fs_listdir(path, cb, user);
-	}
-	
-	/* list users directory */
-	if(types&LISTDIRTYPE_SAVE)
-	{
-		engine_savepath(path, buffer, sizeof(buffer));
-		fs_listdir(buffer, cb, user);
-	}
-	
-	/* list datadir directory */
-	if(types&LISTDIRTYPE_DATA)
-	{
-		str_format(buffer, sizeof(buffer), "%s/%s", datadir, path);
-		fs_listdir(buffer, cb, user);
-	}
-}
-
-void engine_getpath(char *buffer, int buffer_size, const char *filename, int flags)
-{
-	if(flags&IOFLAG_WRITE)
-		engine_savepath(filename, buffer, buffer_size);
-	else
-	{
-		IOHANDLE handle = 0;
-		
-		/* check current directory */
-		handle = io_open(filename, flags);
-		if(handle)
-		{
-			str_copy(buffer, filename, buffer_size);
-			io_close(handle);
-			return;
-		}
-			
-		/* check user directory */
-		engine_savepath(filename, buffer, buffer_size);
-		handle = io_open(buffer, flags);
-		if(handle)
-		{
-			io_close(handle);
-			return;
-		}
-			
-		/* check normal data directory */
-		str_format(buffer, buffer_size, "%s/%s", datadir, filename);
-		handle = io_open(buffer, flags);
-		if(handle)
-		{
-			io_close(handle);
-			return;
-		}
-	}
-	
-	buffer[0] = 0;
-}
-
-IOHANDLE engine_openfile(const char *filename, int flags)
-{
-	char buffer[1024];
-	
-	if(flags&IOFLAG_WRITE)
-	{
-		engine_savepath(filename, buffer, sizeof(buffer));
-		return io_open(buffer, flags);
-	}
-	else
-	{
-		IOHANDLE handle = 0;
-		
-		/* check current directory */
-		handle = io_open(filename, flags);
-		if(handle)
-			return handle;
-			
-		/* check user directory */
-		engine_savepath(filename, buffer, sizeof(buffer));
-		handle = io_open(buffer, flags);
-		if(handle)
-			return handle;
-			
-		/* check normal data directory */
-		str_format(buffer, sizeof(buffer), "%s/%s", datadir, filename);
-		handle = io_open(buffer, flags);
-		if(handle)
-			return handle;
-	}
-	return 0;		
-}
-
-void engine_parse_arguments(int argc, char **argv)
-{
-	/* load the configuration */
-	int i;
-	
-	/* check for datadir override */
-	for(i = 1; i < argc; i++)
-	{
-		if(argv[i][0] == '-' && argv[i][1] == 'd' && argv[i][2] == 0 && argc - i > 1)
-		{
-			str_copy(datadir, argv[i+1], sizeof(datadir));
-			i++;
-		}
-	}
-	
-	/* search for data directory */
-	engine_find_datadir(argv[0]);
-	
-	dbg_msg("engine/datadir", "paths used:");
-	dbg_msg("engine/datadir", "\t.");
-	dbg_msg("engine/datadir", "\t%s", application_save_path);
-	dbg_msg("engine/datadir", "\t%s", datadir);
-	dbg_msg("engine/datadir", "saving files to: %s", application_save_path);
-
-
-	/* check for scripts to execute */
-	for(i = 1; i < argc; i++)
-	{
-		if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1)
-		{
-			console_execute_file(argv[i+1]);
-			i++;
-		}
-	}
-
-	/* search arguments for overrides */
-	{
-		int i;
-		for(i = 1; i < argc; i++)
-			console_execute_line(argv[i]);
-	}
-
-	console_execute_file("autoexec.cfg");
-
-	/* open logfile if needed */
-	if(config.logfile[0])
-		dbg_logger_file(config.logfile);
-
-	/* set default servers and load from disk*/
-	mastersrv_default();
-	mastersrv_load();
-}
-
-
-static IOHANDLE config_file = 0;
-
-int engine_config_write_start()
-{
-	config_save();
-	config_file = engine_openfile("settings.cfg", IOFLAG_WRITE);
-	if(config_file == 0)
-		return -1;
-	return 0;
-}
-
-void engine_config_write_line(const char *line)
-{
-	if(config_file)
-	{
-#if defined(CONF_FAMILY_WINDOWS)
-		static const char newline[] = "\r\n";
-#else
-		static const char newline[] = "\n";
-#endif
-		io_write(config_file, line, strlen(line));
-		io_write(config_file, newline, sizeof(newline)-1);
-	}
-}
-
-void engine_config_write_stop()
-{
-	io_close(config_file);
-	config_file = 0;
-}
-/*
-void engine_writeconfig()
-{
-}*/
-
-static int perf_tick = 1;
-static PERFORMACE_INFO *current = 0;
-
-void perf_init()
-{
-}
-
-void perf_next()
-{
-	perf_tick++;
-	current = 0;
-}
-
-void perf_start(PERFORMACE_INFO *info)
-{
-	if(info->tick != perf_tick)
-	{
-		info->parent = current;
-		info->first_child = 0;
-		info->next_child = 0;
-		
-		if(info->parent)
-		{
-			info->next_child = info->parent->first_child;
-			info->parent->first_child = info;
-		}
-		
-		info->tick = perf_tick;
-		info->biggest = 0;
-		info->total = 0;
-	}
-	
-	current = info;
-	current->start = time_get();
-}
-
-void perf_end()
-{
-	if(!current)
-		return;
-		
-	current->last_delta = time_get()-current->start;
-	current->total += current->last_delta;
-	
-	if(current->last_delta > current->biggest)
-		current->biggest = current->last_delta;
-	
-	current = current->parent;
-}
-
-static void perf_dump_imp(PERFORMACE_INFO *info, int indent)
-{
-	char buf[512] = {0};
-	int64 freq = time_freq();
-	int i;
-	
-	for(i = 0; i < indent; i++)
-		buf[i] = ' ';
-	
-	str_format(&buf[indent], sizeof(buf)-indent, "%-20s %8.2f %8.2f", info->name, info->total*1000/(float)freq, info->biggest*1000/(float)freq);
-	dbg_msg("perf", "%s", buf);
-	
-	info = info->first_child;
-	while(info)
-	{
-		perf_dump_imp(info, indent+2);
-		info = info->next_child;
-	}
-}
-
-void perf_dump(PERFORMACE_INFO *top)
-{
-	perf_dump_imp(top, 0);
-}
-
-/* master server functions */
-typedef struct
-{
-	char hostname[128];
-	NETADDR addr;
-	
-	HOSTLOOKUP lookup;
-} MASTER_INFO;
-
-static MASTER_INFO master_servers[MAX_MASTERSERVERS] = {{{0}}};
-static int needs_update = -1;
-
-int mastersrv_refresh_addresses()
-{
-	int i;
-	
-	if(needs_update != -1)
-		return 0;
-	
-	dbg_msg("engine/mastersrv", "refreshing master server addresses");
-
-	/* add lookup jobs */
-	for(i = 0; i < MAX_MASTERSERVERS; i++)	
-		engine_hostlookup(&master_servers[i].lookup, master_servers[i].hostname);
-	
-	needs_update = 1;
-	return 0;
-}
-
-void mastersrv_update()
-{
-	int i;
-	
-	/* check if we need to update */
-	if(needs_update != 1)
-		return;
-	needs_update = 0;
-	
-	for(i = 0; i < MAX_MASTERSERVERS; i++)
-	{
-		if(jobs_status(&master_servers[i].lookup.job) != JOBSTATUS_DONE)
-			needs_update = 1;
-		else
-		{
-			master_servers[i].addr = master_servers[i].lookup.addr;
-			master_servers[i].addr.port = 8300;
-		}
-	}
-	
-	if(!needs_update)
-	{
-		dbg_msg("engine/mastersrv", "saving addresses");
-		mastersrv_save();
-	}
-}
-
-int mastersrv_refreshing()
-{
-	return needs_update;
-}
-
-NETADDR mastersrv_get(int index) 
-{
-	return master_servers[index].addr;
-}
-
-const char *mastersrv_name(int index) 
-{
-	return master_servers[index].hostname;
-}
-
-void mastersrv_dump_servers()
-{
-	int i;
-	for(i = 0; i < MAX_MASTERSERVERS; i++)
-	{
-		dbg_msg("mastersrv", "#%d = %d.%d.%d.%d", i,
-			master_servers[i].addr.ip[0], master_servers[i].addr.ip[1],
-			master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]);
-	}
-}
-
-void mastersrv_default()
-{
-	int i;
-	mem_zero(master_servers, sizeof(master_servers));
-	for(i = 0; i < MAX_MASTERSERVERS; i++)
-		sprintf(master_servers[i].hostname, "master%d.teeworlds.com", i+1);
-}
-
-int mastersrv_load()
-{
-	LINEREADER lr;
-	IOHANDLE file;
-	int count = 0;
-	
-	/* try to open file */
-	file = engine_openfile("masters.cfg", IOFLAG_READ);
-	if(!file)
-		return -1;
-	
-	linereader_init(&lr, file);
-	while(1)
-	{
-		MASTER_INFO info = {{0}};
-		int ip[4];
-		const char *line = linereader_get(&lr);
-		if(!line)
-			break;
-
-		/* parse line */		
-		if(sscanf(line, "%s %d.%d.%d.%d", info.hostname, &ip[0], &ip[1], &ip[2], &ip[3]) == 5)
-		{
-			info.addr.ip[0] = (unsigned char)ip[0];
-			info.addr.ip[1] = (unsigned char)ip[1];
-			info.addr.ip[2] = (unsigned char)ip[2];
-			info.addr.ip[3] = (unsigned char)ip[3];
-			info.addr.port = 8300;
-			if(count != MAX_MASTERSERVERS)
-			{
-				master_servers[count] = info;
-				count++;
-			}
-			else
-				dbg_msg("engine/mastersrv", "warning: skipped master server '%s' due to limit of %d", line, MAX_MASTERSERVERS);
-		}
-		else
-			dbg_msg("engine/mastersrv", "warning: couldn't parse master server '%s'", line);
-	}
-	
-	io_close(file);
-	return 0;
-}
-
-int mastersrv_save()
-{
-	IOHANDLE file;
-	int i;
-
-	/* try to open file */
-	file = engine_openfile("masters.cfg", IOFLAG_WRITE);
-	if(!file)
-		return -1;
-
-	for(i = 0; i < MAX_MASTERSERVERS; i++)
-	{
-		char buf[1024];
-		str_format(buf, sizeof(buf), "%s %d.%d.%d.%d\n", master_servers[i].hostname,
-			master_servers[i].addr.ip[0], master_servers[i].addr.ip[1],
-			master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]);
-			
-		io_write(file, buf, strlen(buf));
-	}
-	
-	io_close(file);
-	return 0;
-}
-
-
-int hostlookup_thread(void *user)
-{
-	HOSTLOOKUP *lookup = (HOSTLOOKUP *)user;
-	net_host_lookup(lookup->hostname, &lookup->addr, NETTYPE_IPV4);
-	return 0;
-}
-
-void engine_hostlookup(HOSTLOOKUP *lookup, const char *hostname)
-{
-	str_copy(lookup->hostname, hostname, sizeof(lookup->hostname));
-	jobs_add(&hostlookuppool, &lookup->job, hostlookup_thread, lookup);
-}
-
-static int engine_find_datadir(char *argv0)
-{
-	/* 1) use provided data-dir override */
-	if(datadir[0])
-	{
-		if(fs_is_dir(datadir))
-			return 0;
-		else
-		{
-			dbg_msg("engine/datadir", "specified data-dir '%s' does not exist", datadir);
-			return -1;
-		}
-	}
-	
-	/* 2) use data-dir in PWD if present */
-	if(fs_is_dir("data"))
-	{
-		strcpy(datadir, "data");
-		return 0;
-	}
-	
-	/* 3) use compiled-in data-dir if present */
-	if (fs_is_dir(DATA_DIR))
-	{
-		strcpy(datadir, DATA_DIR);
-		return 0;
-	}
-	
-	/* 4) check for usable path in argv[0] */
-	{
-		unsigned int pos = strrchr(argv0, '/') - argv0;
-		
-		if (pos < sizeof(datadir))
-		{
-			char basedir[sizeof(datadir)];
-			strncpy(basedir, argv0, pos);
-			basedir[pos] = '\0';
-			str_format(datadir, sizeof(datadir), "%s/data", basedir);
-			
-			if (fs_is_dir(datadir))
-				return 0;
-		}
-	}
-	
-#if defined(CONF_FAMILY_UNIX)
-	/* 5) check for all default locations */
-	{
-		const char *sdirs[] = {
-			"/usr/share/teeworlds",
-			"/usr/local/share/teeworlds"
-			"/opt/teeworlds"
-		};
-		const int sdirs_count = sizeof(sdirs) / sizeof(sdirs[0]);
-		
-		int i;
-		for (i = 0; i < sdirs_count; i++)
-		{
-			if (fs_is_dir(sdirs[i]))
-			{
-				strcpy(datadir, sdirs[i]);
-				return 0;
-			}
-		}
-	}
-#endif
-	
-	/* no data-dir found */
-	dbg_msg("engine/datadir", "warning no data directory found");
-	return -1;
-}
diff --git a/src/engine/e_engine.h b/src/engine/e_engine.h
deleted file mode 100644
index 94d49171..00000000
--- a/src/engine/e_engine.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-
-#include "e_jobs.h"
-
-const char *engine_savepath(const char *filename, char *buffer, int max);
-void engine_init(const char *appname);
-void engine_parse_arguments(int argc, char **argv);
-
-int engine_config_write_start();
-void engine_config_write_line(const char *line);
-void engine_config_write_stop();
-
-
-enum
-{
-	LISTDIRTYPE_SAVE=1,
-	LISTDIRTYPE_CURRENT=2,
-	LISTDIRTYPE_DATA=4,
-	LISTDIRTYPE_ALL = ~0
-};
-
-void engine_listdir(int types, const char *path, FS_LISTDIR_CALLBACK cb, void *user);
-IOHANDLE engine_openfile(const char *filename, int flags);
-void engine_getpath(char *buffer, int buffer_size, const char *filename, int flags);
-
-int engine_stress(float probability);
-
-typedef struct HOSTLOOKUP
-{
-	JOB job;
-	char hostname[128];
-	NETADDR addr;
-} HOSTLOOKUP;
-
-void engine_hostlookup(HOSTLOOKUP *lookup, const char *hostname);
-
-enum
-{
-	MAX_MASTERSERVERS=4
-};
-
-void mastersrv_default();
-int mastersrv_load();
-int mastersrv_save();
-
-int mastersrv_refresh_addresses();
-void mastersrv_update();
-int mastersrv_refreshing();
-void mastersrv_dump_servers();
-NETADDR mastersrv_get(int index);
-const char *mastersrv_name(int index);
diff --git a/src/engine/e_huffman.cpp b/src/engine/e_huffman.cpp
deleted file mode 100644
index 43914010..00000000
--- a/src/engine/e_huffman.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
-#include <memory.h> /* memset */
-#include "e_huffman.h"
-
-typedef struct HUFFMAN_CONSTRUCT_NODE
-{
-	unsigned short node_id;
- 	int frequency;
-} HUFFMAN_CONSTRUCT_NODE;
-
-static void huffman_setbits_r(HUFFMAN_STATE *huff, HUFFMAN_NODE *node, int bits, int depth)
-{
-	if(node->leafs[1] != 0xffff)
-		huffman_setbits_r(huff, &huff->nodes[node->leafs[1]], bits|(1<<depth), depth+1);
-	if(node->leafs[0] != 0xffff)
-		huffman_setbits_r(huff, &huff->nodes[node->leafs[0]], bits, depth+1);
-		
-	if(node->num_bits)
-	{
-		node->bits = bits;
-		node->num_bits = depth;
-	}
-}
-
-/* TODO: this should be something faster, but it's enough for now */
-void bubblesort(HUFFMAN_CONSTRUCT_NODE **list, int size)
-{
-	int i, changed = 1;
-	HUFFMAN_CONSTRUCT_NODE *temp;
-	
-	while(changed)
-	{
-		changed = 0;
-		for(i = 0; i < size-1; i++)
-		{
-			if(list[i]->frequency < list[i+1]->frequency)
-			{
-				temp = list[i];
-				list[i] = list[i+1];
-				list[i+1] = temp;
-				changed = 1;
-			}
-		}
-	}
-}
-
-static void huffman_construct_tree(HUFFMAN_STATE *huff, const unsigned *frequencies)
-{
-	HUFFMAN_CONSTRUCT_NODE nodes_left_storage[HUFFMAN_MAX_SYMBOLS];
-	HUFFMAN_CONSTRUCT_NODE *nodes_left[HUFFMAN_MAX_SYMBOLS];
-	int num_nodes_left = HUFFMAN_MAX_SYMBOLS;
-	int i;
-
-	/* add the symbols */
-	for(i = 0; i < HUFFMAN_MAX_SYMBOLS; i++)
-	{
-		huff->nodes[i].num_bits = -1;
-		huff->nodes[i].symbol = i;
-		huff->nodes[i].leafs[0] = -1;
-		huff->nodes[i].leafs[1] = -1;
-
-		if(i == HUFFMAN_EOF_SYMBOL)
-			nodes_left_storage[i].frequency = 1;
-		else
-			nodes_left_storage[i].frequency = frequencies[i];
-		nodes_left_storage[i].node_id = i;
-		nodes_left[i] = &nodes_left_storage[i];
-
-	}
-	huff->num_nodes = HUFFMAN_MAX_SYMBOLS;
-	
-	/* construct the table */
-	while(num_nodes_left > 1)
-	{
-		/* we can't rely on stdlib's qsort for this, it can generate different results on different implementations */
-		bubblesort(nodes_left, num_nodes_left);
-		
-		huff->nodes[huff->num_nodes].num_bits = 0;
-		huff->nodes[huff->num_nodes].leafs[0] = nodes_left[num_nodes_left-1]->node_id;
-		huff->nodes[huff->num_nodes].leafs[1] = nodes_left[num_nodes_left-2]->node_id;
-		nodes_left[num_nodes_left-2]->node_id = huff->num_nodes;
-		nodes_left[num_nodes_left-2]->frequency = nodes_left[num_nodes_left-1]->frequency + nodes_left[num_nodes_left-2]->frequency;
-
-		huff->num_nodes++;
-		num_nodes_left--;
-	}
-
-	/* set start node */
-	huff->start_node = &huff->nodes[huff->num_nodes-1];
-	
-	/* build symbol bits */
-	huffman_setbits_r(huff, huff->start_node, 0, 0);
-}
-
-void huffman_init(HUFFMAN_STATE *huff, const unsigned *frequencies)
-{
-	int i;
-
-	/* make sure to cleanout every thing */
-	memset(huff, 0, sizeof(HUFFMAN_STATE));
-
-	/* construct the tree */
-	huffman_construct_tree(huff, frequencies);
-
-	/* build decode LUT */
-	for(i = 0; i < HUFFMAN_LUTSIZE; i++)
-	{
-		unsigned bits = i;
-		int k;
-		HUFFMAN_NODE *node = huff->start_node;
-		for(k = 0; k < HUFFMAN_LUTBITS; k++)
-		{
-			node = &huff->nodes[node->leafs[bits&1]];
-			bits >>= 1;
-
-			if(!node)
-				break;
-
-			if(node->num_bits)
-			{
-				huff->decode_lut[i] = node;
-				break;
-			}
-		}
-
-		if(k == HUFFMAN_LUTBITS)
-			huff->decode_lut[i] = node;
-	}
-
-}
-
-/*****************************************************************/
-int huffman_compress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size)
-{
-	/* this macro loads a symbol for a byte into bits and bitcount */
-#define HUFFMAN_MACRO_LOADSYMBOL(sym) \
-	bits |= huff->nodes[sym].bits << bitcount; \
-	bitcount += huff->nodes[sym].num_bits;
-
-	/* this macro writes the symbol stored in bits and bitcount to the dst pointer */
-#define HUFFMAN_MACRO_WRITE() \
-	while(bitcount >= 8) \
-	{ \
-		*dst++ = (unsigned char)(bits&0xff); \
-		if(dst == dst_end) \
-			return -1; \
-		bits >>= 8; \
-		bitcount -= 8; \
-	}
-
-	/* setup buffer pointers */
-	const unsigned char *src = (const unsigned char *)input;
-	const unsigned char *src_end = src + input_size;
-	unsigned char *dst = (unsigned char *)output;
-	unsigned char *dst_end = dst + output_size;
-
-	/* symbol variables */
-	unsigned bits = 0;
-	unsigned bitcount = 0;
-
-	/* make sure that we have data that we want to compress */
-	if(input_size)
-	{
-		/* {A} load the first symbol */
-		int symbol = *src++;
-
-		while(src != src_end)
-		{
-			/* {B} load the symbol */
-			HUFFMAN_MACRO_LOADSYMBOL(symbol)
-
-			/* {C} fetch next symbol, this is done here because it will reduce dependency in the code */
-			symbol = *src++;
-
-			/* {B} write the symbol loaded at */
-			HUFFMAN_MACRO_WRITE()
-		}
-
-		/* write the last symbol loaded from {C} or {A} in the case of only 1 byte input buffer */
-		HUFFMAN_MACRO_LOADSYMBOL(symbol)
-		HUFFMAN_MACRO_WRITE()
-	}
-
-	/* write EOF symbol */
-	HUFFMAN_MACRO_LOADSYMBOL(HUFFMAN_EOF_SYMBOL)
-	HUFFMAN_MACRO_WRITE()
-
-	/* write out the last bits */
-	*dst++ = bits;
-
-	/* return the size of the output */
-	return (int)(dst - (const unsigned char *)output);
-
-	/* remove macros */
-#undef HUFFMAN_MACRO_LOADSYMBOL
-#undef HUFFMAN_MACRO_WRITE
-}
-
-/*****************************************************************/
-int huffman_decompress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size)
-{
-	/* setup buffer pointers */
-	unsigned char *dst = (unsigned char *)output;
-	unsigned char *src = (unsigned char *)input;
-	unsigned char *dst_end = dst + output_size;
-	unsigned char *src_end = src + input_size;
-
-	unsigned bits = 0;
-	unsigned bitcount = 0;
-
-	HUFFMAN_NODE *eof = &huff->nodes[HUFFMAN_EOF_SYMBOL];
-	HUFFMAN_NODE *node = 0;
-
-	while(1)
-	{
-		/* {A} try to load a node now, this will reduce dependency at location {D} */
-		node = 0;
-		if(bitcount >= HUFFMAN_LUTBITS)
-			node = huff->decode_lut[bits&HUFFMAN_LUTMASK];
-
-		/* {B} fill with new bits */
-		while(bitcount < 24 && src != src_end)
-		{
-			bits |= (*src++) << bitcount;
-			bitcount += 8;
-		}
-
-		/* {C} load symbol now if we didn't that earlier at location {A} */
-		if(!node)
-			node = huff->decode_lut[bits&HUFFMAN_LUTMASK];
-
-		/* {D} check if we hit a symbol already */
-		if(node->num_bits)
-		{
-			/* remove the bits for that symbol */
-			bits >>= node->num_bits;
-			bitcount -= node->num_bits;
-		}
-		else
-		{
-			/* remove the bits that the lut checked up for us */
-			bits >>= HUFFMAN_LUTBITS;
-			bitcount -= HUFFMAN_LUTBITS;
-
-			/* walk the tree bit by bit */
-			while(1)
-			{
-				/* traverse tree */
-				node = &huff->nodes[node->leafs[bits&1]];
-
-				/* remove bit */
-				bitcount--;
-				bits >>= 1;
-
-				/* check if we hit a symbol */
-				if(node->num_bits)
-					break;
-
-				/* no more bits, decoding error */
-				if(bitcount == 0)
-					return -1;
-			}
-		}
-
-		/* check for eof */
-		if(node == eof)
-			break;
-
-		/* output character */
-		if(dst == dst_end)
-			return -1;
-		*dst++ = node->symbol;
-	}
-
-	/* return the size of the decompressed buffer */
-	return (int)(dst - (const unsigned char *)output);
-}
diff --git a/src/engine/e_huffman.h b/src/engine/e_huffman.h
deleted file mode 100644
index 635c74a1..00000000
--- a/src/engine/e_huffman.h
+++ /dev/null
@@ -1,83 +0,0 @@
-#ifndef __HUFFMAN_HEADER__
-#define __HUFFMAN_HEADER__
-
-enum
-{
-	HUFFMAN_EOF_SYMBOL = 256,
-
-	HUFFMAN_MAX_SYMBOLS=HUFFMAN_EOF_SYMBOL+1,
-	HUFFMAN_MAX_NODES=HUFFMAN_MAX_SYMBOLS*2-1,
-	
-	HUFFMAN_LUTBITS = 10,
-	HUFFMAN_LUTSIZE = (1<<HUFFMAN_LUTBITS),
-	HUFFMAN_LUTMASK = (HUFFMAN_LUTSIZE-1)
-};
-
-typedef struct HUFFMAN_NODE
-{
-	/* symbol */
-	unsigned bits;
-	unsigned num_bits;
-
-	/* don't use pointers for this. shorts are smaller so we can fit more data into the cache */
-	unsigned short leafs[2];
-
-	/* what the symbol represents */
-	unsigned char symbol;
-} HUFFMAN_NODE;
-
-typedef struct HUFFMAN_STATE
-{
-	HUFFMAN_NODE nodes[HUFFMAN_MAX_NODES];
-	HUFFMAN_NODE *decode_lut[HUFFMAN_LUTSIZE];
-	HUFFMAN_NODE *start_node;
-	int num_nodes;
-} HUFFMAN_STATE;
-
-/*
-	Function: huffman_init
-		Inits the compressor/decompressor.
-
-	Parameters:
-		huff - Pointer to the state to init
-		frequencies - A pointer to an array of 256 entries of the frequencies of the bytes
-
-	Remarks:
-		- Does no allocation what so ever.
-		- You don't have to call any cleanup functions when you are done with it
-*/
-void huffman_init(HUFFMAN_STATE *huff, const unsigned *frequencies);
-
-/*
-	Function: huffman_compress
-		Compresses a buffer and outputs a compressed buffer.
-
-	Parameters:
-		huff - Pointer to the huffman state
-		input - Buffer to compress
-		input_size - Size of the buffer to compress
-		output - Buffer to put the compressed data into
-		output_size - Size of the output buffer
-
-	Returns:
-		Returns the size of the compressed data. Negative value on failure.
-*/
-int huffman_compress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size);
-
-/*
-	Function: huffman_decompress
-		Decompresses a buffer
-
-	Parameters:
-		huff - Pointer to the huffman state
-		input - Buffer to decompress
-		input_size - Size of the buffer to decompress
-		output - Buffer to put the uncompressed data into
-		output_size - Size of the output buffer
-
-	Returns:
-		Returns the size of the uncompressed data. Negative value on failure.
-*/
-int huffman_decompress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size);
-
-#endif /* __HUFFMAN_HEADER__ */
diff --git a/src/engine/e_if_client.h b/src/engine/e_if_client.h
deleted file mode 100644
index 86c6f5fc..00000000
--- a/src/engine/e_if_client.h
+++ /dev/null
@@ -1,579 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_IF_CLIENT_H
-#define ENGINE_IF_CLIENT_H
-
-/*
-	Title: Client Interface
-*/
-
-/*
-	Section: Constants
-*/
-
-enum
-{
-	/* Constants: Client States
-		CLIENTSTATE_OFFLINE - The client is offline.
-		CLIENTSTATE_CONNECTING - The client is trying to connect to a server.
-		CLIENTSTATE_LOADING - The client has connected to a server and is loading resources.
-		CLIENTSTATE_ONLINE - The client is connected to a server and running the game.
-		CLIENTSTATE_DEMOPLAYBACK - The client is playing a demo
-		CLIENTSTATE_QUITING - The client is quiting.
-	*/
-	CLIENTSTATE_OFFLINE=0,
-	CLIENTSTATE_CONNECTING,
-	CLIENTSTATE_LOADING,
-	CLIENTSTATE_ONLINE,
-	CLIENTSTATE_DEMOPLAYBACK,
-	CLIENTSTATE_QUITING,
-
-	/* Constants: Image Formats
-		IMG_AUTO - Lets the engine choose the format.
-		IMG_RGB - 8-Bit uncompressed RGB
-		IMG_RGBA - 8-Bit uncompressed RGBA
-		IMG_ALPHA - 8-Bit uncompressed alpha
-	*/
-	IMG_AUTO=-1,
-	IMG_RGB=0,
-	IMG_RGBA=1,
-	IMG_ALPHA=2,
-	
-	/* Constants: Texture Loading Flags
-		TEXLOAD_NORESAMPLE - Prevents the texture from any resampling
-	*/
-	TEXLOAD_NORESAMPLE=1,
-	
-	/* Constants: Server Browser Sorting
-		BROWSESORT_NAME - Sort by name.
-		BROWSESORT_PING - Sort by ping.
-		BROWSESORT_MAP - Sort by map
-		BROWSESORT_GAMETYPE - Sort by game type. DM, TDM etc.
-		BROWSESORT_PROGRESSION - Sort by progression.
-		BROWSESORT_NUMPLAYERS - Sort after how many players there are on the server.
-	*/
-	BROWSESORT_NAME = 0,
-	BROWSESORT_PING,
-	BROWSESORT_MAP,
-	BROWSESORT_GAMETYPE,
-	BROWSESORT_PROGRESSION,
-	BROWSESORT_NUMPLAYERS,
-	
-	BROWSEQUICK_SERVERNAME=1,
-	BROWSEQUICK_PLAYERNAME=2,
-	BROWSEQUICK_MAPNAME=4,
-	
-	BROWSETYPE_INTERNET = 0,
-	BROWSETYPE_LAN = 1,
-	BROWSETYPE_FAVORITES = 2
-};
-
-/*
-	Section: Structures
-*/
-
-/*
-	Structure: SERVER_INFO_PLAYER
-*/
-typedef struct
-{
-	char name[48];
-	int score;
-} SERVER_INFO_PLAYER;
-
-/*
-	Structure: SERVER_INFO
-*/
-typedef struct
-{
-	int sorted_index;
-	int server_index;
-	
-	NETADDR netaddr;
-	
-	int quicksearch_hit;
-	
-	int progression;
-	int max_players;
-	int num_players;
-	int flags;
-	int favorite;
-	int latency; /* in ms */
-	char gametype[16];
-	char name[64];
-	char map[32];
-	char version[32];
-	char address[24];
-	SERVER_INFO_PLAYER players[16];
-} SERVER_INFO;
-
-/*
-	Section: Functions
-*/
-
-/**********************************************************************************
-	Group: Time
-**********************************************************************************/
-/*
-	Function: client_tick
-		Returns the tick of the current snapshot.
-*/
-int client_tick();
-
-/*
-	Function: client_prevtick
-		Returns the tick of the previous snapshot.
-*/
-int client_prevtick();
-
-/*
-	Function: client_intratick
-		Returns the current intratick.
-	
-	Remarks:
-		The intratick is how far gone the time is from the previous snapshot to the current.
-		0.0 means that it on the previous snapshot. 0.5 means that it's halfway to the current,
-		and 1.0 means that it is on the current snapshot. It can go beyond 1.0 which means that
-		the client has started to extrapolate due to lack of data from the server.
-
-	See Also:
-		<client_tick>
-*/
-float client_intratick();
-
-/*
-	Function: client_predtick
-		Returns the current predicted tick.
-*/
-int client_predtick();
-
-/*
-	Function: client_predintratick
-		Returns the current preticted intra tick.
-	
-	Remarks:
-		This is the same as <client_intratick> but for the current predicted tick and
-		previous predicted tick.
-
-	See Also:
-		<client_intratick>
-*/
-float client_predintratick();
-
-/*
-	Function: client_ticktime
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-float client_ticktime();
-
-/*
-	Function: client_tickspeed
-		Returns how many ticks per second the client is doing.
-	
-	Remarks:
-		This will be the same as the server tick speed.
-*/
-int client_tickspeed();
-
-/*
-	Function: client_frametime
-		Returns how long time the last frame took to process.
-*/
-float client_frametime();
-
-/*
-	Function: client_localtime
-		Returns the clients local time.
-	
-	Remarks:
-		The local time is set to 0 when the client starts and when
-		it connects to a server. Can be used for client side effects.
-*/
-float client_localtime();
-
-/**********************************************************************************
-	Group: Server Browser
-**********************************************************************************/
-
-/*
-	Function: client_serverbrowse_refresh
-		Issues a refresh of the server browser.
-	
-	Arguments:
-		type - What type of servers to browse, internet, lan or favorites.
-	
-	Remarks:
-		This will cause a broadcast on the local network if the lan argument is set.
-		Otherwise it call ask all the master servers for their servers lists.
-*/
-void client_serverbrowse_refresh(int type);
-
-/*
-	Function: client_serverbrowse_sorted_get
-		Returns server info from the sorted list.
-	
-	Arguments:
-		index - Zero based index into the sorted list.
-	
-	See Also:
-		<client_serverbrowse_sorted_num>
-*/
-SERVER_INFO *client_serverbrowse_sorted_get(int index);
-
-/*
-	Function: client_serverbrowse_sorted_num
-		Returns how many servers there are in the sorted list.
-	
-	See Also:
-		<client_serverbrowse_sorted_get>
-*/
-int client_serverbrowse_sorted_num();
-
-/*
-	Function: client_serverbrowse_get
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-SERVER_INFO *client_serverbrowse_get(int index);
-
-/*
-	Function: client_serverbrowse_num
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int client_serverbrowse_num();
-
-/*
-	Function: client_serverbrowse_num_requests
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int client_serverbrowse_num_requests();
-
-/*
-	Function: client_serverbrowse_update
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void client_serverbrowse_update();
-
-/*
-	Function: client_serverbrowse_lan
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int client_serverbrowse_lan();
-
-/*
-	Function: client_serverbrowse_isfavorite
-		Asks the server browser is a netaddr is in the favorite list
-	
-	Arguments:
-		addr - Address of the server to ask about.
-		
-	Returns:
-		Returns zero if it's not in the list, non-zero if it is.
-*/
-int client_serverbrowse_isfavorite(NETADDR addr);
-
-/*
-	Function: client_serverbrowse_addfavorite
-		Adds a server to the favorite list
-	
-	Arguments:
-		addr - Address of the favorite server.
-*/
-void client_serverbrowse_addfavorite(NETADDR addr);
-
-/*
-	Function: client_serverbrowse_removefavorite
-		Removes a server to the favorite list
-	
-	Arguments:
-		addr - Address of the favorite server.
-*/
-void client_serverbrowse_removefavorite(NETADDR addr);
-
-/**********************************************************************************
-	Group: Actions
-**********************************************************************************/
-
-
-/*
-	Function: client_connect
-		Connects to a server at the specified address.
-	
-	Arguments:
-		address - Address of the server to connect to.
-	
-	See Also:
-		<client_disconnect>
-*/
-void client_connect(const char *address);
-
-/*
-	Function: client_disconnect
-		Disconnects from the current server.
-	
-	Remarks:
-		Does nothing if not connected to a server.
-
-	See Also:
-		<client_connect, client_quit>
-*/
-void client_disconnect();
-
-/*
-	Function: client_quit
-		Tells to client to shutdown.
-
-	See Also:
-		<client_disconnect>
-*/
-void client_quit();
-
-void client_entergame();
-
-/*
-	Function: client_rcon
-		Sends a command to the server to execute on it's console.
-	
-	Arguments:
-		cmd - The command to send.
-	
-	Remarks:
-		The client must have the correct rcon password to connect.
-
- 	See Also:
-		<client_rcon_auth, client_rcon_authed>
-*/
-void client_rcon(const char *cmd);
-
-/*
-	Function: client_rcon_auth
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<client_rcon, client_rcon_authed>
-*/
-void client_rcon_auth(const char *name, const char *password);
-
-/*
-	Function: client_rcon_authed
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<client_rcon, client_rcon_auth>
-*/
-int client_rcon_authed();
-
-/**********************************************************************************
-	Group: Other
-**********************************************************************************/
-/*
-	Function: client_latestversion
-		Returns 0 if there's no version difference
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-const char *client_latestversion();
-
-/*
-	Function: client_get_input
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int *client_get_input(int tick);
-
-/*
-	Function: client_direct_input
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void client_direct_input(int *input, int size);
-
-/*
-	Function: client_error_string
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-const char *client_error_string();
-
-/*
-	Function: client_connection_problems
-		Returns 1 if the client is connection problems.
-	
-	Remarks:
-		Connections problems usually means that the client havn't recvived any data
-		from the server in a while.
-*/
-int client_connection_problems();
-
-/*
-	Function: client_state
-		Returns the state of the client.
-		
-	See Also:
-		<Client States>
-*/
-int client_state();
-
-/*
-	Function: client_mapdownload_amount
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int client_mapdownload_amount();
-
-/*
-	Function: client_mapdownload_totalsize
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int client_mapdownload_totalsize();
-
-/*
-	Function: client_save_line
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void client_save_line(const char *line);
-
-
-
-
-
-
-enum
-{
-	BROWSESET_MASTER_ADD=1,
-	BROWSESET_FAV_ADD,
-	BROWSESET_TOKEN,
-	BROWSESET_OLD_INTERNET,
-	BROWSESET_OLD_LAN
-};
-
-void client_serverbrowse_set(NETADDR *addr, int type, int token, SERVER_INFO *info);
-
-int client_serverbrowse_refreshingmasters();
-
-
-typedef struct DEMOPLAYBACK_INFO
-{
-	int first_tick;
-	int last_tick;
-	int current_tick;
-	int paused;
-	float speed;
-} DEMOPLAYBACK_INFO;
-
-const char *client_demoplayer_play(const char *filename);
-const DEMOPLAYBACK_INFO *client_demoplayer_getinfo();
-void client_demoplayer_setpos(float percent);
-void client_demoplayer_setpause(int paused);
-void client_demoplayer_setspeed(float speed);
-const char *client_user_directory();
-void client_serverinfo(SERVER_INFO *serverinfo);
-void client_serverinfo_request();
-void client_serverbrowse_request(NETADDR *addr);
-#endif
diff --git a/src/engine/e_if_gfx.h b/src/engine/e_if_gfx.h
deleted file mode 100644
index 3d048e85..00000000
--- a/src/engine/e_if_gfx.h
+++ /dev/null
@@ -1,674 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_IF_GFX_H
-#define ENGINE_IF_GFX_H
-
-/*
-	Title: Graphics
-*/
-
-/*
-	Section: Structures
-*/
-
-/*
-	Structure: FONT
-*/
-struct FONT;
-
-/*
-	Structure: IMAGE_INFO
-*/
-typedef struct
-{
-	/* Variable: width
-		Contains the width of the image */
-	int width;
-	
-	/* Variable: height
-		Contains the height of the image */
-	int height;
-	
-	/* Variable: format
-		Contains the format of the image. See <Image Formats> for more information. */
-	int format;
-
-	/* Variable: data
-		Pointer to the image data. */
-	void *data;
-} IMAGE_INFO;
-
-/*
-	Structure: VIDEO_MODE
-*/
-typedef struct 
-{
-	int width, height;
-	int red, green, blue;
-} VIDEO_MODE;
-
-/*
-	Section: Functions
-*/
-
-/*
-	Group: Quads
-*/
-
-/*
-	Function: gfx_quads_begin
-		Begins a quad drawing session.
-		
-	Remarks:
-		This functions resets the rotation, color and subset.
-		End the session by using <gfx_quads_end>.
-		You can't change texture or blending mode during a session.
-		
-	See Also:
-		<gfx_quads_end>
-*/
-void gfx_quads_begin();
-
-/*
-	Function: gfx_quads_end
-		Ends a quad session.
-		
-	See Also:
-		<gfx_quads_begin>
-*/
-void gfx_quads_end();
-
-/*
-	Function: gfx_quads_setrotation
-		Sets the rotation to use when drawing a quad.
-		
-	Arguments:
-		angle - Angle in radians.
-		
-	Remarks:
-		The angle is reset when <gfx_quads_begin> is called.
-*/
-void gfx_quads_setrotation(float angle);
-
-/*
-	Function: gfx_quads_setsubset
-		Sets the uv coordinates to use.
-		
-	Arguments:
-		tl_u - Top-left U value.
-		tl_v - Top-left V value.
-		br_u - Bottom-right U value.
-		br_v - Bottom-right V value.
-		
-	Remarks:
-		O,0 is top-left of the texture and 1,1 is bottom-right.
-		The color is reset when <gfx_quads_begin> is called.
-*/
-void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v);
-
-/*
-	Function: gfx_quads_setsubset_free
-		TODO
-		
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_quads_setsubset_free(
-	float x0, float y0,
-	float x1, float y1,
-	float x2, float y2,
-	float x3, float y3);
-
-/*
-	Function: gfx_quads_drawTL
-		Draws a quad by specifying the top-left point.
-		
-	Arguments:
-		x - X coordinate of the top-left corner.
-		y - Y coordinate of the top-left corner.
-		width - Width of the quad.
-		height - Height of the quad.
-		
-	Remarks:
-		Rotation still occurs from the center of the quad.
-		You must call <gfx_quads_begin> before calling this function.
-
-	See Also:
-		<gfx_quads_draw, gfx_quads_draw_freeform>
-*/
-void gfx_quads_drawTL(float x, float y, float width, float height);
-
-/*
-	Function: gfx_quads_draw
-		Draws a quad by specifying the center point.
-		
-	Arguments:
-		x - X coordinate of the center.
-		y - Y coordinate of the center.
-		width - Width of the quad.
-		height - Height of the quad.
-
-	Remarks:
-		You must call <gfx_quads_begin> before calling this function.
-
-	See Also:
-		<gfx_quads_drawTL, gfx_quads_draw_freeform>
-*/
-void gfx_quads_draw(float x, float y, float w, float h);
-
-/*
-	Function: gfx_quads_draw_freeform
-		Draws a quad by specifying the corner points.
-	
-	Arguments:
-		x0, y0 - Coordinates of the upper left corner.
-		x1, y1 - Coordinates of the upper right corner.
-		x2, y2 - Coordinates of the lower left corner. // TODO: DOUBLE CHECK!!!
-		x3, y3 - Coordinates of the lower right corner. // TODO: DOUBLE CHECK!!!
-	
-	See Also:
-		<gfx_quads_draw, gfx_quads_drawTL>
-*/
-void gfx_quads_draw_freeform(
-	float x0, float y0,
-	float x1, float y1,
-	float x2, float y2,
-	float x3, float y3);
-
-/*
-	Function: gfx_quads_text
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_quads_text(float x, float y, float size, float r, float g, float b, float a, const char *text);
-
-/*
-	Group: Lines
-*/
-
-/*
-	Function: gfx_lines_begin
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_lines_begin();
-
-/*
-	Function: gfx_lines_draw
-		TODO
-		
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_lines_draw(float x0, float y0, float x1, float y1);
-
-/*
-	Function: gfx_minimize
-		Minimizes the window.
-		
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_minimize();
-
-/*
-	Function: gfx_minimize
-		Maximizes the window.
-		
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_maximize();
-
-/*
-	Function: gfx_lines_end
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_lines_end();
-
-/*
-	Group: Text
-*/
-
-
-/*
-	Function: gfx_text
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-		returns the number of lines written
-	See Also:
-		<other_func>
-*/
-void gfx_text(void *font, float x, float y, float size, const char *text, int max_width);
-
-/*
-	Function: gfx_text_width
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-float gfx_text_width(void *font, float size, const char *text, int length);
-
-/*
-	Function: gfx_text_color
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_text_color(float r, float g, float b, float a);
-
-/*
-	Function: gfx_text_set_default_font
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_text_set_default_font(struct FONT *font);
-
-/*
-	Group: Other
-*/
-
-/*
-	Function: gfx_get_video_modes
-		Fetches a list of all the available video modes.
-		
-	Arguments:
-		list - An array to recive the modes. Must be able to contain maxcount number of modes.
-		maxcount - The maximum number of modes to fetch.
-	
-	Returns:
-		The number of video modes that was fetched.
-*/
-int gfx_get_video_modes(VIDEO_MODE *list, int maxcount);
-
-/* image loaders */
-
-/*
-	Function: gfx_load_png
-		Loads a PNG image from disk.
-	
-	Arguments:
-		img - Pointer to an structure to be filled out with the image information.
-		filename - Filename of the image to load.
-	
-	Returns:
-		Returns non-zero on success and zero on error.
-	
-	Remarks:
-		The caller are responsible for cleaning up the allocated memory in the IMAGE_INFO structure.
-	
-	See Also:
-		<gfx_load_texture>
-*/
-int gfx_load_png(IMAGE_INFO *img, const char *filename);
-
-/* textures */
-/*
-	Function: gfx_load_texture
-		Loads a texture from a file. TGA and PNG supported.
-	
-	Arguments:
-		filename - Null terminated string to the file to load.
-		store_format - What format to store on gfx card as.
-		flags - controls how the texture is uploaded
-	
-	Returns:
-		An ID to the texture. -1 on failure.
-
-	See Also:
-		<gfx_unload_texture, gfx_load_png>
-*/
-int gfx_load_texture(const char *filename, int store_format, int flags);
-
-/*
-	Function: gfx_load_texture_raw
-		Loads a texture from memory.
-	
-	Arguments:
-		w - Width of the texture.
-		h - Height of the texture.
-		data - Pointer to the pixel data.
-		format - Format of the pixel data.
-		store_format - The format to store the texture on the graphics card.
-		flags - controls how the texture is uploaded
-	
-	Returns:
-		An ID to the texture. -1 on failure.
-		
-	Remarks:
-		The pixel data should be in RGBA format with 8 bit per component.
-		So the total size of the data should be w*h*4.
-		
-	See Also:
-		<gfx_unload_texture>
-*/
-int gfx_load_texture_raw(int w, int h, int format, const void *data, int store_format, int flags);
-
-/*
-	Function: gfx_texture_set
-		Sets the active texture.
-	
-	Arguments:
-		id - ID to the texture to set.
-*/
-void gfx_texture_set(int id);
-
-/*
-	Function: gfx_unload_texture
-		Unloads a texture.
-	
-	Arguments:
-		id - ID to the texture to unload.
-		
-	See Also:
-		<gfx_load_texture_tga>, <gfx_load_texture_raw>
-		
-	Remarks:
-		NOT IMPLEMENTED
-*/
-int gfx_unload_texture(int id);
-
-/*
-	Function: gfx_clear
-		Clears the screen with the specified color.
-	
-	Arguments:
-		r - Red component.
-		g - Green component.
-		b - Red component.
-	
-	Remarks:
-		The value of the components are given in 0.0 - 1.0 ranges.
-*/
-void gfx_clear(float r, float g, float b);
-
-/*
-	Function: gfx_screenaspect
-		Returns the aspect ratio between width and height.
-
-	See Also:
-		<gfx_screenwidth>, <gfx_screenheight>
-*/
-float gfx_screenaspect();
-
-/*
-	Function: gfx_screenwidth
-		Returns the screen width.
-	
-	See Also:
-		<gfx_screenheight>
-*/
-int gfx_screenwidth();
-
-/*
-	Function: gfx_screenheight
-		Returns the screen height.
-	
-	See Also:
-		<gfx_screenwidth>
-*/
-int gfx_screenheight();
-
-/*
-	Function: gfx_mapscreen
-		Specifies the coordinate system for the screen.
-		
-	Arguments:
-		tl_x - Top-left X
-		tl_y - Top-left Y
-		br_x - Bottom-right X
-		br_y - Bottom-right y
-*/
-void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y);
-
-/*
-	Function: gfx_blend_normal
-		Set the active blending mode to normal (src, 1-src).
-
-	Remarks:
-		This must be used before calling <gfx_quads_begin>.
-		This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA).
-	
-	See Also:
-		<gfx_blend_additive,gfx_blend_none>
-*/
-void gfx_blend_normal();
-
-/*
-	Function: gfx_blend_additive
-		Set the active blending mode to additive (src, one).
-
-	Remarks:
-		This must be used before calling <gfx_quads_begin>.
-		This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE).
-	
-	See Also:
-		<gfx_blend_normal,gfx_blend_none>
-*/
-void gfx_blend_additive();
-
-/*
-	Function: gfx_blend_none
-		Disables blending
-
-	Remarks:
-		This must be used before calling <gfx_quads_begin>.
-	
-	See Also:
-		<gfx_blend_normal,gfx_blend_additive>
-*/
-void gfx_blend_none();
-
-
-/*
-	Function: gfx_setcolorvertex
-		Sets the color of a vertex.
-		
-	Arguments:
-		i - Index to the vertex.
-		r - Red value.
-		g - Green value.
-		b - Blue value.
-		a - Alpha value.
-		
-	Remarks:
-		The color values are from 0.0 to 1.0.
-		The color is reset when <gfx_quads_begin> is called.
-*/
-void gfx_setcolorvertex(int i, float r, float g, float b, float a);
-
-/*
-	Function: gfx_setcolor
-		Sets the color of all the vertices.
-		
-	Arguments:
-		r - Red value.
-		g - Green value.
-		b - Blue value.
-		a - Alpha value.
-		
-	Remarks:
-		The color values are from 0.0 to 1.0.
-		The color is reset when <gfx_quads_begin> is called.
-*/
-void gfx_setcolor(float r, float g, float b, float a);
-
-/*
-	Function: gfx_getscreen
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y);
-
-/*
-	Function: gfx_memory_usage
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int gfx_memory_usage();
-
-/*
-	Function: gfx_screenshot
-		TODO		
-	
-	Arguments:
-		filename - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_screenshot();
-
-/*
-	Function: gfx_screenshot_direct
-		TODO		
-	
-	Arguments:
-		filename - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_screenshot_direct(const char *filename);
-
-/*
-	Function: gfx_clip_enable
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_clip_enable(int x, int y, int w, int h);
-
-/*
-	Function: gfx_clip_disable
-		TODO	
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void gfx_clip_disable();
-
-
-enum
-{
-	TEXTFLAG_RENDER=1,
-	TEXTFLAG_ALLOW_NEWLINE=2,
-	TEXTFLAG_STOP_AT_END=4
-};
-
-typedef struct
-{
-	int flags;
-	int line_count;
-	int charcount;
-	
-	float start_x;
-	float start_y;
-	float line_width;
-	float x, y;
-	
-	struct FONT *font;
-	float font_size;
-} TEXT_CURSOR;
-
-void gfx_text_set_cursor(TEXT_CURSOR *cursor, float x, float y, float font_size, int flags);
-void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length);
-
-
-struct FONT *gfx_font_load(const char *filename);
-void gfx_font_destroy(struct FONT *font);
-
-#endif
diff --git a/src/engine/e_if_inp.h b/src/engine/e_if_inp.h
deleted file mode 100644
index 919ac0eb..00000000
--- a/src/engine/e_if_inp.h
+++ /dev/null
@@ -1,244 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_IF_INP_H
-#define ENGINE_IF_INP_H
-
-/*
-	Section: Input
-*/
-
-
-/*
-	Structure: INPUT_EVENT
-*/
-enum
-{
-	INPFLAG_PRESS=1,
-	INPFLAG_RELEASE=2,
-	INPFLAG_REPEAT=4
-};
-
-typedef struct
-{
-	int flags;
-	int unicode;
-	int key;
-} INPUT_EVENT;
-
-/*
-	Function: inp_mouse_relative
-		Fetches the mouse movements.
-		
-	Arguments:
-		x - Pointer to the variable that should get the X movement.
-		y - Pointer to the variable that should get the Y movement.
-*/
-void inp_mouse_relative(int *x, int *y);
-
-/*
-	Function: inp_mouse_scroll
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int inp_mouse_scroll();
-
-/*
-	Function: inp_key_pressed
-		Checks if a key is pressed.
-		
-	Arguments:
-		key - Index to the key to check
-		
-	Returns:
-		Returns 1 if the button is pressed, otherwise 0.
-	
-	Remarks:
-		Check keys.h for the keys.
-*/
-int inp_key_pressed(int key);
-
-
-/* input */
-/*
-	Function: inp_key_was_pressed
-		TODO	
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int inp_key_was_pressed(int key);
-
-/*
-	Function: inp_key_down
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int inp_key_down(int key);
-
-
-/*
-	Function: inp_num_events
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int inp_num_events();
-
-/*
-	Function: inp_get_event
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-INPUT_EVENT inp_get_event(int index);
-
-/*
-	Function: inp_clear_events
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void inp_clear_events();
-
-/*
-	Function: inp_mouse_doubleclick
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int inp_mouse_doubleclick();
-
-/*
-	Function: inp_key_presses
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int inp_key_presses(int key);
-
-/*
-	Function: inp_key_releases
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int inp_key_releases(int key);
-
-/*
-	Function: inp_key_state
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int inp_key_state(int key);
-
-/*
-	Function: inp_key_name
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-const char *inp_key_name(int k);
-
-/*
-	Function: inp_key_code
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int inp_key_code(const char *key_name);
-
-
-
-/*
-	Function: inp_clear_key_states
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void inp_clear_key_states(); 
-
-void inp_update();
-void inp_init();
-void inp_mouse_mode_absolute();
-void inp_mouse_mode_relative();
-
-#endif
diff --git a/src/engine/e_if_modc.h b/src/engine/e_if_modc.h
deleted file mode 100644
index 8839d5f1..00000000
--- a/src/engine/e_if_modc.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_IF_MODC_H
-#define ENGINE_IF_MODC_H
-
-/**********************************************************************************
-	Section: Client Hooks
-*********************************************************************************/
-/*
-	Function: modc_console_init
-		TODO
-*/
-void modc_console_init();
-
-/*
-	Function: modc_rcon_line
-		TODO
-*/
-void modc_rcon_line(const char *line);
-
-/*
-	Function: modc_save_config
-		TODO
-*/
-void modc_save_config();
-
-/*
-	Function: modc_init
-		Called when the client starts.
-	
-	Remarks:
-		The game should load resources that are used during the entire
-		time of the game. No map is loaded.
-*/
-void modc_init();
-
-/*
-	Function: modc_newsnapshot
-		Called when the client progressed to a new snapshot.
-	
-	Remarks:
-		The client can check for items in the snapshot and perform one time
-		events like playing sounds, spawning client side effects etc.
-*/
-void modc_newsnapshot();
-
-/*
-	Function: modc_entergame
-		Called when the client has successfully connect to a server and
-		loaded a map.
-	
-	Remarks:
-		The client can check for items in the map and load them.
-*/
-void modc_entergame();
-
-/*
-	Function: modc_shutdown
-		Called when the client closes down.
-*/
-void modc_shutdown();
-
-/*
-	Function: modc_render
-		Called every frame to let the game render it self.
-*/
-void modc_render();
-
-/*
-	Function: modc_statechange
-		Called every time client changes state.
-*/
-void modc_statechange(int new_state, int old_state);
-
-/* undocumented callbacks */
-/*
-	Function: modc_connected
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void modc_connected();
-
-/*
-	Function: modc_message
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void modc_message(int msg);
-
-/*
-	Function: modc_predict
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void modc_predict();
-
-/*
-	Function: modc_snap_input
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int modc_snap_input(int *data);
-
-/*
-	Function: modc_net_version
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-const char *modc_net_version();
-
-#endif
diff --git a/src/engine/e_if_mods.h b/src/engine/e_if_mods.h
deleted file mode 100644
index 08d0ec37..00000000
--- a/src/engine/e_if_mods.h
+++ /dev/null
@@ -1,168 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_IF_MOD_H
-#define ENGINE_IF_MOD_H
-
-/**********************************************************************************
-	Section: Server Hooks
-**********************************************************************************/
-/*
-	Function: mods_console_init
-		TODO
-*/
-void mods_console_init();
-
-/*
-	Function: mods_init
-		Called when the server is started.
-	
-	Remarks:
-		It's called after the map is loaded so all map items are available.
-*/
-void mods_init();
-
-/*
-	Function: mods_shutdown
-		Called when the server quits.
-	
-	Remarks:
-		Should be used to clean up all resources used.
-*/
-void mods_shutdown();
-
-/*
-	Function: mods_client_enter
-		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 mods_client_enter(int cid);
-
-/*
-	Function: mods_client_drop
-		Called when a client drops from the server.
-
-	Arguments:
-		cid - Client ID. Is 0 - MAX_CLIENTS
-*/
-void mods_client_drop(int cid);
-
-/*
-	Function: mods_client_direct_input
-		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 mods_client_direct_input(int cid, void *input);
-
-/*
-	Function: mods_client_predicted_input
-		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 mods_client_predicted_input(int cid, void *input);
-
-
-/*
-	Function: mods_tick
-		Called with a regular interval to progress the gameplay.
-		
-	Remarks:
-		The SERVER_TICK_SPEED tells the number of ticks per second.
-*/
-void mods_tick();
-
-/*
-	Function: mods_presnap
-		Called before the server starts to construct snapshots for the clients.
-*/
-void mods_presnap();
-
-/*
-	Function: mods_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 mods_snap(int cid);
-
-/*
-	Function: mods_postsnap
-		Called after the server is done sending the snapshots.
-*/
-void mods_postsnap();
-
-
-/*
-	Function: mods_connected
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void mods_connected(int client_id);
-
-
-/*
-	Function: mods_net_version
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-const char *mods_net_version();
-
-/*
-	Function: mods_version
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-const char *mods_version();
-
-/*
-	Function: mods_message
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void mods_message(int msg, int client_id);
-
-#endif
diff --git a/src/engine/e_if_msg.h b/src/engine/e_if_msg.h
deleted file mode 100644
index d03a64a6..00000000
--- a/src/engine/e_if_msg.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_IF_MSG_H
-#define ENGINE_IF_MSG_H
-
-/*
-	Section: Messaging
-*/
-
-void msg_pack_start_system(int msg, int flags);
-
-/*
-	Function: msg_pack_start
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void msg_pack_start(int msg, int flags);
-
-/*
-	Function: msg_pack_int
-		TODO	
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void msg_pack_int(int i);
-
-/*
-	Function: msg_pack_string
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void msg_pack_string(const char *p, int limit);
-
-/*
-	Function: msg_pack_raw
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void msg_pack_raw(const void *data, int size);
-
-/*
-	Function: msg_pack_end
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void msg_pack_end();
-
-typedef struct
-{
-	int msg;
-	int flags;
-	const unsigned char *data;
-	int size;
-} MSG_INFO;
-
-const MSG_INFO *msg_get_info();
-
-/* message unpacking */
-int msg_unpack_start(const void *data, int data_size, int *is_system);
-
-/*
-	Function: msg_unpack_int
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int msg_unpack_int();
-
-/*
-	Function: msg_unpack_string
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-const char *msg_unpack_string();
-
-/*
-	Function: msg_unpack_raw
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-const unsigned char *msg_unpack_raw(int size);
-
-/*
-	Function: msg_unpack_error
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int msg_unpack_error();
-
-
-#endif
diff --git a/src/engine/e_if_other.h b/src/engine/e_if_other.h
deleted file mode 100644
index 3fbbd81e..00000000
--- a/src/engine/e_if_other.h
+++ /dev/null
@@ -1,385 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_IF_OTHER_H
-#define ENGINE_IF_OTHER_H
-
-/*
-	Title: Engine Interface
-*/
-
-#include <base/system.h>
-#include "e_keys.h"
-
-enum 
-{
-	SERVER_TICK_SPEED=50,
-	MAX_CLIENTS=16,
-	
-	SNAP_CURRENT=0,
-	SNAP_PREV=1,
-
-	MASK_NONE=0,
-	MASK_SET,
-	MASK_ZERO,
-
-	SNDFLAG_LOOP=1,
-	SNDFLAG_POS=2,
-	SNDFLAG_ALL=3,
-	
-	MAX_NAME_LENGTH=32
-};
-
-enum
-{
-	SRVFLAG_PASSWORD = 0x1,
-};
-
-/*
-	Structure: SNAP_ITEM
-*/
-typedef struct
-{
-	int type;
-	int id;
-	int datasize;
-} SNAP_ITEM;
-
-/*
-	Structure: CLIENT_INFO
-*/
-typedef struct
-{
-	const char *name;
-	int latency;
-} CLIENT_INFO;
-
-typedef struct PERFORMACE_INFO_t
-{
-	const char *name;
-	struct PERFORMACE_INFO_t *parent;
-	struct PERFORMACE_INFO_t *first_child;
-	struct PERFORMACE_INFO_t *next_child;
-	int tick;
-	int64 start;
-	int64 total;
-	int64 biggest;
-	int64 last_delta;
-} PERFORMACE_INFO;
-
-void perf_init();
-void perf_next();
-void perf_start(PERFORMACE_INFO *info);
-void perf_end();
-void perf_dump(PERFORMACE_INFO *top);
-
-int gfx_init();
-void gfx_shutdown();
-void gfx_swap();
-
-int gfx_window_active();
-int gfx_window_open();
-
-void gfx_set_vsync(int val);
-
-int snd_init();
-int snd_shutdown();
-int snd_update();
-
-int map_load(const char *mapname);
-void map_unload();
-
-void map_set(void *m);
-
-/*
-#include "e_if_client.h"
-#include "e_if_server.h"
-#include "e_if_snd.h"
-#include "e_if_gfx.h"
-#include "e_if_inp.h"
-#include "e_if_msg.h"
-#include "e_if_mod.h"*/
-
-/*
-	Section: Map
-*/
-
-/*
-	Function: map_is_loaded
-		Checks if a map is loaded.
-		
-	Returns:
-		Returns 1 if the button is pressed, otherwise 0.
-*/
-int map_is_loaded();
-
-/*
-	Function: map_num_items
-		Checks the number of items in the loaded map.
-		
-	Returns:
-		Returns the number of items. 0 if no map is loaded.
-*/
-int map_num_items();
-
-/*
-	Function: map_find_item
-		Searches the map for an item.
-	
-	Arguments:
-		type - Item type.
-		id - Item ID.
-	
-	Returns:
-		Returns a pointer to the item if it exists, otherwise it returns NULL.
-*/
-void *map_find_item(int type, int id);
-
-/*
-	Function: map_get_item
-		Gets an item from the loaded map from index.
-	
-	Arguments:
-		index - Item index.
-		type - Pointer that recives the item type (can be NULL).
-		id - Pointer that recives the item id (can be NULL).
-	
-	Returns:
-		Returns a pointer to the item if it exists, otherwise it returns NULL.
-*/
-void *map_get_item(int index, int *type, int *id);
-
-/*
-	Function: map_get_type
-		Gets the index range of an item type.
-	
-	Arguments:
-		type - Item type to search for.
-		start - Pointer that recives the starting index.
-		num - Pointer that recives the number of items.
-	
-	Returns:
-		If the item type is not in the map, start and num will be set to 0.
-*/
-void map_get_type(int type, int *start, int *num);
-
-/*
-	Function: map_get_data
-		Fetches a pointer to a raw data chunk in the map.
-	
-	Arguments:
-		index - Index to the data to fetch.
-	
-	Returns:
-		A pointer to the raw data, otherwise 0.
-*/
-void *map_get_data(int index);
-
-/*
-	Function: map_get_data_swapped
-		TODO
-		
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void *map_get_data_swapped(int index);
-
-/*
-	Section: Network (Server)
-*/
-/*
-	Function: snap_new_item
-		Creates a new item that should be sent.
-	
-	Arguments:
-		type - Type of the item.
-		id - ID of the item.
-		size - Size of the item.
-	
-	Returns:
-		A pointer to the item data, otherwise 0.
-	
-	Remarks:
-		The item data should only consist pf 4 byte integers as
-		they are subject to byte swapping. This means that the size
-		argument should be dividable by 4.
-*/
-void *snap_new_item(int type, int id, int size);
-
-/*
-	Section:Section: Network (Client)
-*/
-/*
-	Function: snap_num_items
-		Check the number of items in a snapshot.
-	
-	Arguments:
-		snapid - Snapshot ID to the data to fetch.
-			* SNAP_PREV for previous snapshot.
-			* SNAP_CUR for current snapshot.
-	
-	Returns:
-		The number of items in the snapshot.
-*/
-int snap_num_items(int snapid);
-
-/*
-	Function: snap_get_item
-		Gets an item from a snapshot.
-	
-	Arguments:
-		snapid - Snapshot ID to the data to fetch.
-			* SNAP_PREV for previous snapshot.
-			* SNAP_CUR for current snapshot.
-		index - Index of the item.
-		item - Pointer that recives the item info.
-	
-	Returns:
-		Returns a pointer to the item if it exists, otherwise NULL.
-*/
-void *snap_get_item(int snapid, int index, SNAP_ITEM *item);
-
-/*
-	Function: snap_find_item
-		Searches a snapshot for an item.
-	
-	Arguments:
-		snapid - Snapshot ID to the data to fetch.
-			* SNAP_PREV for previous snapshot.
-			* SNAP_CUR for current snapshot.
-		type - Type of the item.
-		id - ID of the item.
-	
-	Returns:
-		Returns a pointer to the item if it exists, otherwise NULL.
-*/
-void *snap_find_item(int snapid, int type, int id);
-
-/*
-	Function: snap_invalidate_item
-		Marks an item as invalid byt setting type and id to 0xffffffff.
-	
-	Arguments:
-		snapid - Snapshot ID to the data to fetch.
-			* SNAP_PREV for previous snapshot.
-			* SNAP_CUR for current snapshot.
-		index - Index of the item.
-*/
-void snap_invalidate_item(int snapid, int index);
-
-/*
-	Function: snap_input
-		Sets the input data to send to the server.
-	
-	Arguments:
-		data - Pointer to the data.
-		size - Size of the data.
-
-	Remarks:
-		The data should only consist of 4 bytes integer as they are
-		subject to byte swapping.
-*/
-void snap_input(void *data, int size);
-
-/*
-	Function: snap_set_staticsize
-		Tells the engine how big a specific item always will be. This
-		helps the engine to compress snapshots better.
-	
-	Arguments:
-		type - Item type
-		size - Size of the data.
-		
-	Remarks:
-		Size must be in a multiple of 4.
-*/
-void snap_set_staticsize(int type, int size);
-
-/* message packing */
-enum
-{
-	MSGFLAG_VITAL=1,
-	MSGFLAG_FLUSH=2,
-	MSGFLAG_NORECORD=4,
-	MSGFLAG_RECORD=8,
-	MSGFLAG_NOSEND=16
-};
-
-/* message sending */
-/*
-	Function: server_send_msg
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int server_send_msg(int client_id); /* client_id == -1 == broadcast */
-
-/*
-	Function: client_send_msg
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int client_send_msg();
-/* undocumented graphics stuff */
-
-/* server snap id */
-/*
-	Function: snap_new_id
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int snap_new_id();
-
-/*
-	Function: snap_free_id
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void snap_free_id(int id);
-
-/* other */
-/*
-	Function: map_unload_data
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void map_unload_data(int index);
-
-#endif
diff --git a/src/engine/e_if_server.h b/src/engine/e_if_server.h
deleted file mode 100644
index 1acd184c..00000000
--- a/src/engine/e_if_server.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_IF_SERVER_H
-#define ENGINE_IF_SERVER_H
-
-/*
-	Section: Server Interface
-*/
-
-/* server */
-/*
-	Function: server_getclientinfo
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int server_getclientinfo(int client_id, CLIENT_INFO *info);
-
-/*
-	Function: server_clientname
-		TODO	
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-const char *server_clientname(int client_id);
-
-/* grabs the latest input for the client. not withholding anything */
-
-/*
-	Function: server_latestinput
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int *server_latestinput(int client_id, int *size);
-
-/*
-	Function: server_setclientname
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void server_setclientname(int client_id, const char *name);
-
-/*
-	Function: server_setclientscore
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void server_setclientscore(int client_id, int score);
-
-/*
-	Function: server_setbrowseinfo
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void server_setbrowseinfo(const char *game_type, int progression);
-
-/*
-	Function: server_kick
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-void server_kick(int client_id, const char *reason);
-
-/*
-	Function: server_tick
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int server_tick();
-
-/*
-	Function: server_tickspeed
-		TODO
-	
-	Arguments:
-		arg1 - desc
-	
-	Returns:
-
-	See Also:
-		<other_func>
-*/
-int server_tickspeed();
-
-int server_ban_add(NETADDR addr, int seconds);
-int server_ban_remove(NETADDR addr);
-#endif
diff --git a/src/engine/e_if_snd.h b/src/engine/e_if_snd.h
deleted file mode 100644
index 48376bad..00000000
--- a/src/engine/e_if_snd.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_IF_SND_H
-#define ENGINE_IF_SND_H
-
-/*
-	Section: Sound
-*/
-
-/*
-	Function: snd_set_channel
-		Sets the parameters for a sound channel.
-	
-	Arguments:
-		cid - Channel ID
-		vol - Volume for the channel. 0.0 to 1.0.
-		pan - Panning for the channel. -1.0 is all left. 0.0 is equal distribution. 1.0 is all right.
-*/
-void snd_set_channel(int cid, float vol, float pan);
-
-/*
-	Function: snd_load_wv
-		Loads a wavpack compressed sound.
-	
-	Arguments:
-		filename - Filename of the file to load
-	
-	Returns:
-		The id of the loaded sound. -1 on failure.
-*/
-int snd_load_wv(const char *filename);
-
-/*
-	Function: snd_play_at
-		Plays a sound at a specified postition.
-	
-	Arguments:
-		cid - Channel id of the channel to use.
-		sid - Sound id of the sound to play.
-		flags - TODO
-		x - TODO
-		y - TODO
-	
-	Returns:
-		An id to the voice. -1 on failure.
-
-	See Also:
-		<snd_play, snd_stop>
-*/
-int snd_play_at(int cid, int sid, int flags, float x, float y);
-
-/*
-	Function: snd_play
-		Plays a sound.
-	
-	Arguments:
-	Arguments:
-		cid - Channel id of the channel to use.
-		sid - Sound id of the sound to play.
-		flags - TODO
-	
-	Returns:
-		An id to the voice. -1 on failure.
-
-	See Also:
-		<snd_play_at, snd_stop>
-*/
-int snd_play(int cid, int sid, int flags);
-
-/*
-	Function: snd_stop
-		Stops a currenly playing sound.
-	
-	Arguments:
-		id - The ID of the voice to stop.
-	
-	See Also:
-		<snd_play, snd_play_at>
-*/
-void snd_stop(int id);
-
-/*
-	Function: snd_set_listener_pos
-		Sets the listener posititon.
-	
-	Arguments:
-		x - TODO
-		y - TODO
-*/
-void snd_set_listener_pos(float x, float y);
-
-#endif
diff --git a/src/engine/e_jobs.cpp b/src/engine/e_jobs.cpp
deleted file mode 100644
index e87b395a..00000000
--- a/src/engine/e_jobs.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <base/system.h>
-#include "e_jobs.h"
-
-void worker_thread(void *user)
-{
-	JOBPOOL *pool = (JOBPOOL *)user;
-	
-	while(1)
-	{
-		JOB *job = 0;
-		
-		/* fetch job from queue */
-		lock_wait(pool->lock);
-		if(pool->first_job)
-		{
-			job = pool->first_job;
-			pool->first_job = pool->first_job->next;
-			if(pool->first_job)
-				pool->first_job->prev = 0;
-			else
-				pool->last_job = 0;
-		}
-		lock_release(pool->lock);
-		
-		/* do the job if we have one */
-		if(job)
-		{
-			job->status = JOBSTATUS_RUNNING;
-			job->result = job->func(job->func_data);
-			job->status = JOBSTATUS_DONE;
-		}
-		else
-			thread_sleep(10);
-	}
-	
-}
-
-int jobs_initpool(JOBPOOL *pool, int num_threads)
-{
-	int i;
-	
-	/* empty the pool */
-	mem_zero(pool, sizeof(JOBPOOL));
-	pool->lock = lock_create();
-	
-	/* start threads */
-	for(i = 0; i < num_threads; i++)
-		thread_create(worker_thread, pool);
-	return 0;
-}
-
-int jobs_add(JOBPOOL *pool, JOB *job, JOBFUNC func, void *data)
-{
-	mem_zero(job, sizeof(JOB));
-	job->func = func;
-	job->func_data = data;
-	
-	lock_wait(pool->lock);
-	
-	/* add job to queue */
-	job->prev = pool->last_job;
-	if(pool->last_job)
-		pool->last_job->next = job;
-	pool->last_job = job;
-	if(!pool->first_job)
-		pool->first_job = job;
-	
-	lock_release(pool->lock);
-	return 0;
-}
-
-int jobs_status(JOB *job)
-{
-	return job->status;
-}
diff --git a/src/engine/e_jobs.h b/src/engine/e_jobs.h
deleted file mode 100644
index 2b04a1e4..00000000
--- a/src/engine/e_jobs.h
+++ /dev/null
@@ -1,33 +0,0 @@
-
-typedef int (*JOBFUNC)(void *data);
-
-typedef struct JOB
-{
-	struct JOBPOOL *pool;
-	struct JOB *prev;
-	struct JOB *next;
-	volatile int status;
-	volatile int result;
-	JOBFUNC func;
-	void *func_data;
-} JOB;
-
-typedef struct JOBPOOL
-{
-	LOCK lock;
-	JOB *first_job;
-	JOB *last_job;
-} JOBPOOL;
-
-enum
-{
-	JOBSTATUS_PENDING=0,
-	JOBSTATUS_RUNNING,
-	JOBSTATUS_DONE
-	/*JOBSTATUS_ABORTING,*/
-	/*JOBSTATUS_ABORTED,*/
-};
-
-int jobs_initpool(JOBPOOL *pool, int num_threads);
-int jobs_add(JOBPOOL *pool, JOB *job, JOBFUNC func, void *data);
-int jobs_status(JOB *job);
diff --git a/src/engine/e_linereader.cpp b/src/engine/e_linereader.cpp
deleted file mode 100644
index 57ba9a85..00000000
--- a/src/engine/e_linereader.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#include "e_linereader.h"
-
-void linereader_init(LINEREADER *lr, IOHANDLE io)
-{
-	lr->buffer_max_size = 4*1024;
-	lr->buffer_size = 0;
-	lr->buffer_pos = 0;
-	lr->io = io;
-}
-
-char *linereader_get(LINEREADER *lr)
-{
-	unsigned line_start = lr->buffer_pos;
-
-	while(1)
-	{
-		if(lr->buffer_pos >= lr->buffer_size)
-		{
-			/* fetch more */
-
-			/* move the remaining part to the front */
-			unsigned read;
-			unsigned left = lr->buffer_size - line_start;
-
-			if(line_start > lr->buffer_size)
-				left = 0;
-			if(left)
-				mem_move(lr->buffer, &lr->buffer[line_start], left);
-			lr->buffer_pos = left;
-
-			/* fill the buffer */
-			read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos);
-			lr->buffer_size = left + read;
-			line_start = 0;
-
-			if(!read)
-			{
-				if(left)
-				{
-					lr->buffer[left] = 0; /* return the last line */
-					lr->buffer_pos = left;
-					lr->buffer_size = left;
-					return lr->buffer;
-				}
-				else
-					return 0x0; /* we are done! */
-			}
-		}
-		else
-		{
-			if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r')
-			{
-				/* line found */
-				lr->buffer[lr->buffer_pos] = 0;
-				lr->buffer_pos++;
-				return &lr->buffer[line_start];
-			}
-			else
-				lr->buffer_pos++;
-		}
-	}
-}
diff --git a/src/engine/e_linereader.h b/src/engine/e_linereader.h
deleted file mode 100644
index ca84960a..00000000
--- a/src/engine/e_linereader.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <base/system.h>
-
-/* buffered stream for reading lines, should perhaps be something smaller */
-typedef struct
-{
-	char buffer[4*1024];
-	unsigned buffer_pos;
-	unsigned buffer_size;
-	unsigned buffer_max_size;
-	IOHANDLE io;
-} LINEREADER;
-
-void linereader_init(LINEREADER *lr, IOHANDLE io);
-char *linereader_get(LINEREADER *lr);
diff --git a/src/engine/e_map.cpp b/src/engine/e_map.cpp
deleted file mode 100644
index a2048310..00000000
--- a/src/engine/e_map.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <base/system.h>
-#include "e_datafile.h"
-
-static DATAFILE *map = 0;
-
-void *map_get_data(int index)
-{
-	return datafile_get_data(map, index);
-}
-
-void *map_get_data_swapped(int index)
-{
-	return datafile_get_data_swapped(map, index);
-}
-
-void map_unload_data(int index)
-{
-	datafile_unload_data(map, index);
-}
-
-void *map_get_item(int index, int *type, int *id)
-{
-	return datafile_get_item(map, index, type, id);
-}
-
-void map_get_type(int type, int *start, int *num)
-{
-	datafile_get_type(map, type, start, num);
-}
-
-void *map_find_item(int type, int id)
-{
-	return datafile_find_item(map, type, id);
-}
-
-int map_num_items()
-{
-	return datafile_num_items(map);
-}
-
-void map_unload()
-{
-	datafile_unload(map);
-	map = 0x0;
-}
-
-int map_is_loaded()
-{
-	return map != 0;
-}
-
-int map_load(const char *mapname)
-{
-	char buf[512];
-	str_format(buf, sizeof(buf), "maps/%s.map", mapname);
-	map = datafile_load(buf);
-	return map != 0;
-}
-
-void map_set(void *m)
-{
-	if(map)
-		map_unload();
-	map = (DATAFILE*)m;
-}
diff --git a/src/engine/e_memheap.cpp b/src/engine/e_memheap.cpp
deleted file mode 100644
index fe157e86..00000000
--- a/src/engine/e_memheap.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <base/system.h>
-
-struct CHUNK
-{
-	char *memory;
-	char *current;
-	char *end;
-	CHUNK *next;
-};
-
-struct HEAP
-{
-	CHUNK *current;
-};
-
-/* how large each chunk should be */
-static const int chunksize = 1024*64;
-
-/* allocates a new chunk to be used */
-static CHUNK *memheap_newchunk()
-{
-	CHUNK *chunk;
-	char *mem;
-	
-	/* allocate memory */
-	mem = (char*)mem_alloc(sizeof(CHUNK)+chunksize, 1);
-	if(!mem)
-		return 0x0;
-
-	/* the chunk structure is located in the begining of the chunk */
-	/* init it and return the chunk */
-	chunk = (CHUNK*)mem;
-	chunk->memory = (char*)(chunk+1);
-	chunk->current = chunk->memory;
-	chunk->end = chunk->memory + chunksize;
-	chunk->next = (CHUNK *)0x0;
-	return chunk;
-}
-
-/******************/
-static void *memheap_allocate_from_chunk(CHUNK *chunk, unsigned int size)
-{
-	char *mem;
-	
-	/* check if we need can fit the allocation */
-	if(chunk->current + size > chunk->end)
-		return (void*)0x0;
-
-	/* get memory and move the pointer forward */
-	mem = chunk->current;
-	chunk->current += size;
-	return mem;
-}
-
-/* creates a heap */
-HEAP *memheap_create()
-{
-	CHUNK *chunk;
-	HEAP *heap;
-	
-	/* allocate a chunk and allocate the heap structure on that chunk */
-	chunk = memheap_newchunk();
-	heap = (HEAP *)memheap_allocate_from_chunk(chunk, sizeof(HEAP));
-	heap->current = chunk;
-	return heap;
-}
-
-/* destroys the heap */
-void memheap_destroy(HEAP *heap)
-{
-	CHUNK *chunk = heap->current;
-	CHUNK *next;
-	
-	while(chunk)
-	{
-		next = chunk->next;
-		mem_free(chunk);
-		chunk = next;
-	}
-}
-
-/* */
-void *memheap_allocate(HEAP *heap, unsigned int size)
-{
-	char *mem;
-
-	/* try to allocate from current chunk */
-	mem = (char *)memheap_allocate_from_chunk(heap->current, size);
-	if(!mem)
-	{
-		/* allocate new chunk and add it to the heap */
-		CHUNK *chunk = memheap_newchunk();
-		chunk->next = heap->current;
-		heap->current = chunk;
-		
-		/* try to allocate again */
-		mem = (char *)memheap_allocate_from_chunk(heap->current, size);
-	}
-	
-	return mem;
-}
diff --git a/src/engine/e_memheap.h b/src/engine/e_memheap.h
deleted file mode 100644
index b4391ec7..00000000
--- a/src/engine/e_memheap.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-
-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.cpp b/src/engine/e_msg.cpp
deleted file mode 100644
index 999a0ff0..00000000
--- a/src/engine/e_msg.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include "e_common_interface.h"
-#include "e_packer.h"
-
-/* message packing */
-static CPacker msg_packer;
-static MSG_INFO pack_info;
-static int packer_failed = 0;
-
-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)
-{
-	msg_packer.Reset();
-	pack_info.msg = (msg<<1)|1;
-	pack_info.flags = flags;
-	packer_failed = 0;
-	
-	msg_pack_int(pack_info.msg);
-}
-
-void msg_pack_start(int msg, int flags)
-{
-	msg_packer.Reset();
-	pack_info.msg = msg<<1;
-	pack_info.flags = flags;
-	packer_failed = 0;
-	
-	msg_pack_int(pack_info.msg);
-}
-
-void msg_pack_end()
-{
-	if(msg_packer.Error())
-	{
-		packer_failed = 1;
-		pack_info.size = 0;
-		pack_info.data = (unsigned char *)"";
-	}
-	else
-	{
-		pack_info.size = msg_packer.Size();
-		pack_info.data = msg_packer.Data();
-	}
-}
-
-const MSG_INFO *msg_get_info()
-{
-	if(packer_failed)
-		return 0;
-	return &pack_info;
-}
-
-/* message unpacking */
-static CUnpacker msg_unpacker;
-int msg_unpack_start(const void *data, int data_size, int *system)
-{
-	int msg;
-	msg_unpacker.Reset((const unsigned char *)data, data_size);
-	msg = msg_unpack_int();
-	*system = msg&1;
-	return msg>>1;
-}
-
-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_protocol.h b/src/engine/e_protocol.h
deleted file mode 100644
index a1feb4cc..00000000
--- a/src/engine/e_protocol.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <base/system.h>
-
-/*
-	Connection diagram - How the initilization works.
-	
-	Client -> INFO -> Server
-		Contains version info, name, and some other info.
-		
-	Client <- MAP <- Server
-		Contains current map.
-	
-	Client -> READY -> Server
-		The client has loaded the map and is ready to go,
-		but the mod needs to send it's information aswell.
-		modc_connected is called on the client and
-		mods_connected is called on the server.
-		The client should call client_entergame when the
-		mod has done it's initilization.
-		
-	Client -> ENTERGAME -> Server
-		Tells the server to start sending snapshots.
-		client_entergame and server_client_enter is called.
-*/
-
-
-enum
-{
-	NETMSG_NULL=0,
-	
-	/* the first thing sent by the client
-	contains the version info for the client */
-	NETMSG_INFO=1,
-	
-	/* sent by server */
-	NETMSG_MAP_CHANGE,		/* sent when client should switch map */
-	NETMSG_MAP_DATA,		/* map transfer, contains a chunk of the map file */
-	NETMSG_SNAP,			/* normal snapshot, multiple parts */
-	NETMSG_SNAPEMPTY,		/* empty snapshot */
-	NETMSG_SNAPSINGLE,		/* ? */
-	NETMSG_SNAPSMALL,		/* */
-	NETMSG_INPUTTIMING,		/* reports how off the input was */
-	NETMSG_RCON_AUTH_STATUS,/* result of the authentication */
-	NETMSG_RCON_LINE,		/* line that should be printed to the remote console */
-
-	NETMSG_AUTH_CHALLANGE,	/* */
-	NETMSG_AUTH_RESULT,		/* */
-	
-	/* sent by client */
-	NETMSG_READY,			/* */
-	NETMSG_ENTERGAME,
-	NETMSG_INPUT,			/* contains the inputdata from the client */
-	NETMSG_RCON_CMD,		/* */ 
-	NETMSG_RCON_AUTH,		/* */
-	NETMSG_REQUEST_MAP_DATA,/* */
-
-	NETMSG_AUTH_START,		/* */
-	NETMSG_AUTH_RESPONSE,	/* */
-	
-	/* sent by both */
-	NETMSG_PING,
-	NETMSG_PING_REPLY,
-	NETMSG_ERROR
-};
-
-
-/* this should be revised */
-enum
-{
-	MAX_CLANNAME_LENGTH=32,
-	MAX_INPUT_SIZE=128,
-	MAX_SNAPSHOT_PACKSIZE=900
-};
diff --git a/src/engine/e_server_interface.h b/src/engine/e_server_interface.h
deleted file mode 100644
index c325b9a1..00000000
--- a/src/engine/e_server_interface.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_SERVER_INTERFACE_H
-#define ENGINE_SERVER_INTERFACE_H
-
-#include "e_if_other.h"
-#include "e_if_server.h"
-#include "e_if_msg.h"
-#include "e_if_mods.h"
-
-#include "e_console.h" /* TODO: clean this up*/
-
-#endif
diff --git a/src/engine/e_snapshot.cpp b/src/engine/e_snapshot.cpp
deleted file mode 100644
index 65487665..00000000
--- a/src/engine/e_snapshot.cpp
+++ /dev/null
@@ -1,571 +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;
-}
-
-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/editor.h b/src/engine/editor.h
new file mode 100644
index 00000000..32a5cc93
--- /dev/null
+++ b/src/engine/editor.h
@@ -0,0 +1,16 @@
+#ifndef ENGINE_EDITOR_H
+#define ENGINE_EDITOR_H
+#include "kernel.h"
+
+class IEditor : public IInterface
+{
+	MACRO_INTERFACE("editor", 0)
+public:
+
+	virtual ~IEditor() {}
+	virtual void Init() = 0;
+	virtual void UpdateAndRender() = 0;
+};
+
+extern IEditor *CreateEditor();
+#endif
diff --git a/src/engine/graphics.h b/src/engine/graphics.h
new file mode 100644
index 00000000..4d3c0bc0
--- /dev/null
+++ b/src/engine/graphics.h
@@ -0,0 +1,151 @@
+#ifndef ENGINE_GRAPHICS_H
+#define ENGINE_GRAPHICS_H
+
+#include "kernel.h"
+
+class CImageInfo
+{
+public:
+	enum
+	{
+		FORMAT_AUTO=-1,
+		FORMAT_RGB=0,
+		FORMAT_RGBA=1,
+		FORMAT_ALPHA=2,
+	};
+
+	/* Variable: width
+		Contains the width of the image */
+	int m_Width;
+	
+	/* Variable: height
+		Contains the height of the image */
+	int m_Height;
+	
+	/* Variable: format
+		Contains the format of the image. See <Image Formats> for more information. */
+	int m_Format;
+
+	/* Variable: data
+		Pointer to the image data. */
+	void *m_pData;
+};
+
+/*
+	Structure: CVideoMode
+*/
+class CVideoMode
+{
+public:
+	int m_Width, m_Height;
+	int m_Red, m_Green, m_Blue;
+};
+
+class IGraphics : public IInterface
+{
+	MACRO_INTERFACE("graphics", 0)
+protected:
+	int m_ScreenWidth;
+	int m_ScreenHeight;
+public:
+	/* Constants: Texture Loading Flags
+		TEXLOAD_NORESAMPLE - Prevents the texture from any resampling
+	*/
+	enum
+	{
+		TEXLOAD_NORESAMPLE=1,
+	};
+
+	int ScreenWidth() const { return m_ScreenWidth; }
+	int ScreenHeight() const { return m_ScreenHeight; }
+	float ScreenAspect() const { return (float)ScreenWidth()/(float)ScreenHeight(); }
+	
+	virtual void Clear(float r, float g, float b) = 0;
+	
+	virtual void ClipEnable(int x, int y, int w, int h) = 0;
+	virtual void ClipDisable() = 0;
+	
+	virtual void MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY) = 0;
+	virtual void GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY) = 0;
+	
+	// TODO: These should perhaps not be virtuals
+	virtual void BlendNone() = 0;
+	virtual void BlendNormal() = 0;
+	virtual void BlendAdditive() = 0;
+	virtual int MemoryUsage() const = 0;
+	
+	virtual int LoadPNG(CImageInfo *pImg, const char *pFilename) =0;
+	virtual int UnloadTexture(int Index) = 0;
+	virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) = 0;
+	virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags) = 0;
+	virtual void TextureSet(int TextureID) = 0;
+	
+	struct CLineItem
+	{
+		float m_X0, m_Y0, m_X1, m_Y1;
+		CLineItem() {}
+		CLineItem(float x0, float y0, float x1, float y1) : m_X0(x0), m_Y0(y0), m_X1(x1), m_Y1(y1) {}
+	};
+	virtual void LinesBegin() = 0;
+	virtual void LinesEnd() = 0;
+	virtual void LinesDraw(const CLineItem *pArray, int Num) = 0;
+	
+	virtual void QuadsBegin() = 0;
+	virtual void QuadsEnd() = 0;
+	virtual void QuadsSetRotation(float Angle) = 0;
+	virtual void QuadsSetSubset(float TopLeftY, float TopLeftV, float BottomRightU, float BottomRightV) = 0;
+	virtual void QuadsSetSubsetFree(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) = 0;
+	
+	struct CQuadItem
+	{
+		float m_X, m_Y, m_Width, m_Height;
+		CQuadItem() {}
+		CQuadItem(float x, float y, float w, float h) : m_X(x), m_Y(y), m_Width(w), m_Height(h) {}
+	};
+	virtual void QuadsDraw(CQuadItem *pArray, int Num) = 0;
+	virtual void QuadsDrawTL(const CQuadItem *pArray, int Num) = 0;
+	
+	struct CFreeformItem
+	{
+		float m_X0, m_Y0, m_X1, m_Y1, m_X2, m_Y2, m_X3, m_Y3;
+		CFreeformItem() {}
+		CFreeformItem(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3)
+			: m_X0(x0), m_Y0(y0), m_X1(x1), m_Y1(y1), m_X2(x2), m_Y2(y2), m_X3(x3), m_Y3(y3) {}
+	};
+	virtual void QuadsDrawFreeform(const CFreeformItem *pArray, int Num) = 0;
+	virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText) = 0;
+	
+	struct CColorVertex
+	{
+		int m_Index;
+		float m_R, m_G, m_B, m_A;
+		CColorVertex() {}
+		CColorVertex(int i, float r, float g, float b, float a) : m_Index(i), m_R(r), m_G(g), m_B(b), m_A(a) {}
+	};
+	virtual void SetColorVertex(const CColorVertex *pArray, int Num) = 0;
+	virtual void SetColor(float r, float g, float b, float a) = 0;
+	
+	virtual void TakeScreenshot() = 0;
+	virtual int GetVideoModes(CVideoMode *pModes, int MaxModes) = 0;
+
+	virtual void Swap() = 0;
+};
+
+class IEngineGraphics : public IGraphics
+{
+	MACRO_INTERFACE("enginegraphics", 0)
+public:
+	virtual bool Init() = 0;
+	virtual void Shutdown() = 0;
+	
+	virtual void Minimize() = 0;
+	virtual void Maximize() = 0;
+	
+	virtual int WindowActive() = 0;
+	virtual int WindowOpen() = 0;
+	
+};
+
+extern IEngineGraphics *CreateEngineGraphics();
+
+#endif
diff --git a/src/engine/input.h b/src/engine/input.h
new file mode 100644
index 00000000..168614c8
--- /dev/null
+++ b/src/engine/input.h
@@ -0,0 +1,89 @@
+#ifndef ENGINE_INPUT_H
+#define ENGINE_INPUT_H
+
+#include "kernel.h"
+
+extern const char g_aaKeyStrings[512][16];
+
+class IInput : public IInterface
+{
+	MACRO_INTERFACE("input", 0)
+public:
+	class CEvent
+	{
+	public:
+		int m_Flags;
+		int m_Unicode;
+		int m_Key;
+	};
+
+protected:
+	enum
+	{
+		INPUT_BUFFER_SIZE=32
+	};
+
+	// quick access to events
+	int m_NumEvents;
+	IInput::CEvent m_aInputEvents[INPUT_BUFFER_SIZE];
+
+	//quick access to input
+	struct
+	{
+		unsigned char m_Presses;
+		unsigned char m_Releases;
+	} m_aInputCount[2][1024];
+
+	unsigned char m_aInputState[2][1024];
+	int m_InputCurrent;
+
+	int KeyWasPressed(int Key) { return m_aInputState[m_InputCurrent^1][Key]; }
+
+public:	
+	enum
+	{
+		FLAG_PRESS=1,
+		FLAG_RELEASE=2,
+		FLAG_REPEAT=4
+	};
+
+	// events
+	int NumEvents() const { return m_NumEvents; }
+	void ClearEvents() { m_NumEvents = 0; }
+	CEvent GetEvent(int Index) const
+	{ 
+		if(Index < 0 || Index >= m_NumEvents)
+		{
+			IInput::CEvent e = {0,0};
+			return e;
+		}
+		return m_aInputEvents[Index];
+	}
+	
+	// keys
+	int KeyPressed(int Key) { return m_aInputState[m_InputCurrent][Key]; }
+	int KeyReleases(int Key) { return m_aInputCount[m_InputCurrent][Key].m_Releases; }
+	int KeyPresses(int Key) { return m_aInputCount[m_InputCurrent][Key].m_Presses; }
+	int KeyDown(int Key) { return KeyPressed(Key)&&!KeyWasPressed(Key); }
+	const char *KeyName(int Key) { return (Key >= 0 && Key < 512) ? g_aaKeyStrings[Key] : g_aaKeyStrings[0]; }
+	
+	//
+	virtual void MouseModeRelative() = 0;
+	virtual void MouseModeAbsolute() = 0;
+	virtual int MouseDoubleClick() = 0;
+	
+	virtual void MouseRelative(int *x, int *y) = 0;
+};
+
+
+class IEngineInput : public IInput
+{
+	MACRO_INTERFACE("engineinput", 0)
+public:
+	virtual void Init() = 0;
+	virtual void Update() = 0;
+};
+
+extern IEngineInput *CreateEngineInput();
+
+#endif
diff --git a/src/engine/kernel.h b/src/engine/kernel.h
new file mode 100644
index 00000000..6a72690f
--- /dev/null
+++ b/src/engine/kernel.h
@@ -0,0 +1,66 @@
+#ifndef ENGINE_KERNEL_H
+#define ENGINE_KERNEL_H
+
+#include <base/system.h>
+
+class IKernel;
+class IInterface;
+
+class IInterface
+{
+	// friend with the kernel implementation
+	friend class CKernel;
+	IKernel *m_pKernel;
+protected:
+	IKernel *Kernel() { return m_pKernel; }
+public:
+	IInterface() : m_pKernel(0) {}
+	virtual ~IInterface() {}
+	
+	//virtual unsigned InterfaceID() = 0;
+	//virtual const char *InterfaceName() = 0;
+};
+
+#define MACRO_INTERFACE(Name, ver) \
+	public: \
+		static const char *InterfaceName() { return Name; } \
+	private:
+	
+		//virtual unsigned InterfaceID() { return INTERFACE_ID; }
+		//virtual const char *InterfaceName() { return name; }
+
+
+// This kernel thingie makes the structure very flat and basiclly singletons.
+// I'm not sure if this is a good idea but it works for now.
+class IKernel
+{
+	// hide the implementation
+	virtual bool RegisterInterfaceImpl(const char *InterfaceName, IInterface *pInterface) = 0;
+	virtual bool ReregisterInterfaceImpl(const char *InterfaceName, IInterface *pInterface) = 0;
+	virtual IInterface *RequestInterfaceImpl(const char *InterfaceName) = 0;
+public:
+	static IKernel *Create();
+	virtual ~IKernel() {}
+
+	// templated access to handle pointer convertions and interface names
+	template<class TINTERFACE>
+	bool RegisterInterface(TINTERFACE *pInterface)
+	{
+		return RegisterInterfaceImpl(TINTERFACE::InterfaceName(), pInterface);
+	}
+	template<class TINTERFACE>
+	bool ReregisterInterface(TINTERFACE *pInterface)
+	{
+		return ReregisterInterfaceImpl(TINTERFACE::InterfaceName(), pInterface);
+	}
+	
+	// Usage example:
+	//		IMyInterface *pMyHandle = Kernel()->RequestInterface<IMyInterface>()
+	template<class TINTERFACE>
+	TINTERFACE *RequestInterface()
+	{
+		return reinterpret_cast<TINTERFACE *>(RequestInterfaceImpl(TINTERFACE::InterfaceName()));
+	}
+};
+
+#endif
diff --git a/src/engine/e_keys.h b/src/engine/keys.h
index cb4ca371..cb4ca371 100644
--- a/src/engine/e_keys.h
+++ b/src/engine/keys.h
diff --git a/src/engine/map.h b/src/engine/map.h
new file mode 100644
index 00000000..2c285f26
--- /dev/null
+++ b/src/engine/map.h
@@ -0,0 +1,32 @@
+#ifndef ENGINE_MAP_H
+#define ENGINE_MAP_H
+
+#include "kernel.h"
+
+class IMap : public IInterface
+{
+	MACRO_INTERFACE("map", 0)
+public:
+	virtual void *GetData(int Index) = 0;
+	virtual void *GetDataSwapped(int Index) = 0;
+	virtual void UnloadData(int Index) = 0;
+	virtual void *GetItem(int Index, int *Type, int *pId) = 0;
+	virtual void GetType(int Type, int *pStart, int *pNum) = 0;
+	virtual void *FindItem(int Type, int Id) = 0;
+	virtual int NumItems() = 0;
+};
+
+
+class IEngineMap : public IMap
+{
+	MACRO_INTERFACE("enginemap", 0)
+public:
+	virtual bool Load(const char *pMapName) = 0;
+	virtual bool IsLoaded() = 0;
+	virtual void Unload() = 0;
+	virtual unsigned Crc() = 0;
+};
+
+extern IEngineMap *CreateEngineMap();
+
+#endif
diff --git a/src/engine/masterserver.h b/src/engine/masterserver.h
new file mode 100644
index 00000000..9d16e85f
--- /dev/null
+++ b/src/engine/masterserver.h
@@ -0,0 +1,40 @@
+#ifndef ENGINE_MASTERSERVER_H
+#define ENGINE_MASTERSERVER_H
+
+#include "kernel.h"
+
+class IMasterServer : public IInterface
+{
+	MACRO_INTERFACE("masterserver", 0)
+public:
+
+	enum
+	{
+		MAX_MASTERSERVERS=4
+	};
+
+	virtual void Init(class CEngine *pEngine) = 0;
+	virtual void SetDefault() = 0;
+	virtual int Load() = 0;
+	virtual int Save() = 0;
+
+	virtual int RefreshAddresses() = 0;
+	virtual void Update() = 0;
+	virtual int IsRefreshing() = 0;
+	virtual void DumpServers() = 0;
+	virtual NETADDR GetAddr(int Index) = 0;
+	virtual const char *GetName(int Index) = 0;
+};
+
+class IEngineMasterServer : public IMasterServer
+{
+	MACRO_INTERFACE("enginemasterserver", 0)
+public:
+};
+
+extern IEngineMasterServer *CreateEngineMasterServer();
+
+#endif
+
+
+
diff --git a/src/engine/message.h b/src/engine/message.h
index 6de407ed..3ffc488f 100644
--- a/src/engine/message.h
+++ b/src/engine/message.h
@@ -1,8 +1,16 @@
+#ifndef ENGINE_MESSAGE_H
+#define ENGINE_MESSAGE_H
 
+#include <engine/shared/packer.h>
 
-class CMessage
+class CMsgPacker : public CPacker
 {
 public:
-	virtual bool Pack(void *pData, unsigned MaxDataSize);
-	virtual bool Unpack(const void *pData, unsigned DataSize);
+	CMsgPacker(int Type)
+	{
+		Reset();
+		AddInt(Type);
+	}
 };
+
+#endif
diff --git a/src/engine/server.h b/src/engine/server.h
new file mode 100644
index 00000000..52e6ec6a
--- /dev/null
+++ b/src/engine/server.h
@@ -0,0 +1,81 @@
+#ifndef ENGINE_SERVER_H
+#define ENGINE_SERVER_H
+#include "kernel.h"
+#include "message.h"
+
+class IServer : public IInterface
+{
+	MACRO_INTERFACE("server", 0)
+protected:
+	int m_CurrentGameTick;
+	int m_TickSpeed;
+
+public:
+	/*
+		Structure: CClientInfo
+	*/
+	struct CClientInfo
+	{
+		const char *m_pName;
+		int m_Latency;
+	};
+	
+	int Tick() const { return m_CurrentGameTick; }
+	int TickSpeed() const { return m_TickSpeed; }
+
+	virtual const char *ClientName(int ClientID) = 0;
+	virtual bool ClientIngame(int ClientID) = 0;
+	virtual int GetClientInfo(int ClientID, CClientInfo *pInfo) = 0;
+	virtual void GetClientIP(int ClientID, char *pIPString, int Size) = 0;
+	virtual int *LatestInput(int ClientID, int *pSize) = 0;
+	
+	virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientID) = 0;
+
+	template<class T>
+	int SendPackMsg(T *pMsg, int Flags, int ClientID)
+	{
+		CMsgPacker Packer(pMsg->MsgID());
+		if(pMsg->Pack(&Packer))
+			return -1;
+		return SendMsg(&Packer, Flags, ClientID);
+	}
+	
+	virtual void SetBrowseInfo(char const *pGameType, int Progression) = 0;
+	virtual void SetClientName(int ClientID, char const *pName) = 0;
+	virtual void SetClientScore(int ClientID, int Score) = 0;
+	
+	virtual int SnapNewID() = 0;
+	virtual void SnapFreeID(int ID) = 0;
+	virtual void *SnapNewItem(int Type, int Id, int Size) = 0;
+
+	virtual void SnapSetStaticsize(int ItemType, int Size) = 0;
+};
+
+class IGameServer : public IInterface
+{
+	MACRO_INTERFACE("gameserver", 0)
+protected:
+public:
+	virtual void OnInit() = 0;
+	virtual void OnConsoleInit() = 0;
+	virtual void OnShutdown() = 0;
+	
+	virtual void OnTick() = 0;
+	virtual void OnPreSnap() = 0;
+	virtual void OnSnap(int ClientID) = 0;
+	virtual void OnPostSnap() = 0;
+	
+	virtual void OnMessage(int MsgId, CUnpacker *pUnpacker, int ClientID) = 0;
+
+	virtual void OnClientConnected(int ClientID) = 0;
+	virtual void OnClientEnter(int ClientID) = 0;
+	virtual void OnClientDrop(int ClientID) = 0;
+	virtual void OnClientDirectInput(int ClientID, void *pInput) = 0;
+	virtual void OnClientPredictedInput(int ClientID, void *pInput) = 0;
+	
+	virtual const char *Version() = 0;
+	virtual const char *NetVersion() = 0;
+};
+
+extern IGameServer *CreateGameServer();
+#endif
diff --git a/src/engine/server/es_register.cpp b/src/engine/server/es_register.cpp
deleted file mode 100644
index 0eb5dba5..00000000
--- a/src/engine/server/es_register.cpp
+++ /dev/null
@@ -1,271 +0,0 @@
-#include <string.h>
-#include <base/system.h>
-#include <engine/e_network.h>
-#include <engine/e_config.h>
-#include <engine/e_engine.h>
-
-#include <mastersrv/mastersrv.h>
-
-extern CNetServer *m_pNetServer;
-
-enum
-{
-	REGISTERSTATE_START=0,
-	REGISTERSTATE_UPDATE_ADDRS,
-	REGISTERSTATE_QUERY_COUNT,
-	REGISTERSTATE_HEARTBEAT,
-	REGISTERSTATE_REGISTERED,
-	REGISTERSTATE_ERROR
-};
-
-static int register_state = REGISTERSTATE_START;
-static int64 register_state_start = 0;
-static int register_first = 1;
-static int register_count = 0;
-
-static void register_new_state(int state)
-{
-	register_state = state;
-	register_state_start = time_get();
-}
-
-static void register_send_fwcheckresponse(NETADDR *pAddr)
-{
-	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;
-	CNetChunk Packet;
-	
-	mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT));
-	
-	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;
-	m_pNetServer->Send(&Packet);
-}
-
-static void register_send_count_request(NETADDR Addr)
-{
-	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
-{
-	NETADDR addr;
-	int count;
-	int valid;
-	int64 last_send;
-} MASTERSERVER_INFO;
-
-static MASTERSERVER_INFO masterserver_info[MAX_MASTERSERVERS] = {{{0}}};
-static int register_registered_server = -1;
-
-void register_update()
-{
-	int64 now = time_get();
-	int64 freq = time_freq();
-	
-	if(!config.sv_register)
-		return;
-	
-	mastersrv_update();
-	
-	if(register_state == REGISTERSTATE_START)
-	{
-		register_count = 0;
-		register_first = 1;
-		register_new_state(REGISTERSTATE_UPDATE_ADDRS);
-		mastersrv_refresh_addresses();
-		dbg_msg("register", "refreshing ip addresses");
-	}
-	else if(register_state == REGISTERSTATE_UPDATE_ADDRS)
-	{
-		register_registered_server = -1;
-		
-		if(!mastersrv_refreshing())
-		{
-			int i;
-			for(i = 0; i < MAX_MASTERSERVERS; i++)
-			{
-				NETADDR addr = mastersrv_get(i);
-				masterserver_info[i].addr = addr;
-				masterserver_info[i].count = 0;
-				
-				if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3])
-					masterserver_info[i].valid = 0;
-				else
-				{
-					masterserver_info[i].valid = 1;
-					masterserver_info[i].count = -1;
-					masterserver_info[i].last_send = 0;
-				}
-			}
-			
-			dbg_msg("register", "fetching server counts");
-			register_new_state(REGISTERSTATE_QUERY_COUNT);
-		}
-	}
-	else if(register_state == REGISTERSTATE_QUERY_COUNT)
-	{
-		int i;
-		int left = 0;
-		for(i = 0; i < MAX_MASTERSERVERS; i++)
-		{
-			if(!masterserver_info[i].valid)
-				continue;
-				
-			if(masterserver_info[i].count == -1)
-			{
-				left++;
-				if(masterserver_info[i].last_send+freq < now)
-				{
-					masterserver_info[i].last_send = now;
-					register_send_count_request(masterserver_info[i].addr);
-				}
-			}
-		}
-		
-		/* check if we are done or timed out */
-		if(left == 0 || now > register_state_start+freq*3)
-		{
-			/* choose server */
-			int best = -1;
-			int i;
-			for(i = 0; i < MAX_MASTERSERVERS; i++)
-			{
-				if(!masterserver_info[i].valid || masterserver_info[i].count == -1)
-					continue;
-					
-				if(best == -1 || masterserver_info[i].count < masterserver_info[best].count)
-					best = i;
-			}
-
-			/* server chosen */
-			register_registered_server = best;
-			if(register_registered_server == -1)
-			{
-				dbg_msg("register", "WARNING: No master servers. Retrying in 60 seconds");
-				register_new_state(REGISTERSTATE_ERROR);
-			}
-			else
-			{			
-				dbg_msg("register", "choosen '%s' as master, sending heartbeats", mastersrv_name(register_registered_server));
-				masterserver_info[register_registered_server].last_send = 0;
-				register_new_state(REGISTERSTATE_HEARTBEAT);
-			}
-		}
-	}
-	else if(register_state == REGISTERSTATE_HEARTBEAT)
-	{
-		/* check if we should send heartbeat */
-		if(now > masterserver_info[register_registered_server].last_send+freq*15)
-		{
-			masterserver_info[register_registered_server].last_send = now;
-			register_send_heartbeat(masterserver_info[register_registered_server].addr);
-		}
-		
-		if(now > register_state_start+freq*60)
-		{
-			dbg_msg("register", "WARNING: Master server is not responding, switching master");
-			register_new_state(REGISTERSTATE_START);
-		}
-	}
-	else if(register_state == REGISTERSTATE_REGISTERED)
-	{
-		if(register_first)
-			dbg_msg("register", "server registered");
-			
-		register_first = 0;
-		
-		/* check if we should send new heartbeat again */
-		if(now > register_state_start+freq)
-		{
-			if(register_count == 120) /* redo the whole process after 60 minutes to balance out the master servers */
-				register_new_state(REGISTERSTATE_START);
-			else
-			{
-				register_count++;
-				register_new_state(REGISTERSTATE_HEARTBEAT);
-			}
-		}
-	}
-	else if(register_state == REGISTERSTATE_ERROR)
-	{
-		/* check for restart */
-		if(now > register_state_start+freq*60)
-			register_new_state(REGISTERSTATE_START);
-	}
-}
-
-static void register_got_count(CNetChunk *pChunk)
-{
-	unsigned char *pData = (unsigned char *)pChunk->m_pData;
-	int Count = (pData[sizeof(SERVERBROWSE_COUNT)]<<8) | pData[sizeof(SERVERBROWSE_COUNT)+1];
-
-	for(int i = 0; i < MAX_MASTERSERVERS; i++)
-	{
-		if(net_addr_comp(&masterserver_info[i].addr, &pChunk->m_Address) == 0)
-		{
-			masterserver_info[i].count = Count;
-			break;
-		}
-	}
-}
-
-int register_process_packet(CNetChunk *pPacket)
-{
-	if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) &&
-		memcmp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
-	{
-		register_send_fwcheckresponse(&pPacket->m_Address);
-		return 1;
-	}
-	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(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(pPacket->m_DataSize == sizeof(SERVERBROWSE_COUNT)+2 &&
-		memcmp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0)
-	{
-		register_got_count(pPacket);
-		return 1;
-	}
-
-	return 0;
-}
diff --git a/src/engine/server/es_server.cpp b/src/engine/server/es_server.cpp
deleted file mode 100644
index cc0e6e0f..00000000
--- a/src/engine/server/es_server.cpp
+++ /dev/null
@@ -1,1969 +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
-
-
-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;
-}
-
diff --git a/src/engine/server/register.cpp b/src/engine/server/register.cpp
new file mode 100644
index 00000000..959b9288
--- /dev/null
+++ b/src/engine/server/register.cpp
@@ -0,0 +1,264 @@
+#include <base/system.h>
+#include <engine/shared/network.h>
+#include <engine/shared/config.h>
+#include <engine/shared/engine.h>
+#include <engine/masterserver.h>
+
+#include <mastersrv/mastersrv.h>
+
+#include "register.h"
+
+CRegister::CRegister()
+{
+	m_pNetServer = 0;
+	m_pMasterServer = 0;
+
+	m_RegisterState = REGISTERSTATE_START;
+	m_RegisterStateStart = 0;
+	m_RegisterFirst = 1;
+	m_RegisterCount = 0;
+
+	mem_zero(m_aMasterserverInfo, sizeof(m_aMasterserverInfo));
+	m_RegisterRegisteredServer = -1;
+}
+
+void CRegister::RegisterNewState(int State)
+{
+	m_RegisterState = State;
+	m_RegisterStateStart = time_get();
+}
+
+void CRegister::RegisterSendFwcheckresponse(NETADDR *pAddr)
+{
+	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);
+}
+	
+void CRegister::RegisterSendHeartbeat(NETADDR Addr)
+{
+	static unsigned char aData[sizeof(SERVERBROWSE_HEARTBEAT) + 2];
+	unsigned short Port = g_Config.m_SvPort;
+	CNetChunk Packet;
+	
+	mem_copy(aData, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT));
+	
+	Packet.m_ClientID = -1;
+	Packet.m_Address = Addr;
+	Packet.m_Flags = NETSENDFLAG_CONNLESS;
+	Packet.m_DataSize = sizeof(SERVERBROWSE_HEARTBEAT) + 2;
+	Packet.m_pData = &aData;
+
+	// supply the set port that the master can use if it has problems
+	if(g_Config.m_SvExternalPort)
+		Port = g_Config.m_SvExternalPort;
+	aData[sizeof(SERVERBROWSE_HEARTBEAT)] = Port >> 8;
+	aData[sizeof(SERVERBROWSE_HEARTBEAT)+1] = Port&0xff;
+	m_pNetServer->Send(&Packet);
+}
+
+void CRegister::RegisterSendCountRequest(NETADDR Addr)
+{
+	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);
+}
+
+void CRegister::RegisterGotCount(CNetChunk *pChunk)
+{
+	unsigned char *pData = (unsigned char *)pChunk->m_pData;
+	int Count = (pData[sizeof(SERVERBROWSE_COUNT)]<<8) | pData[sizeof(SERVERBROWSE_COUNT)+1];
+
+	for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
+	{
+		if(net_addr_comp(&m_aMasterserverInfo[i].m_Addr, &pChunk->m_Address) == 0)
+		{
+			m_aMasterserverInfo[i].m_Count = Count;
+			break;
+		}
+	}
+}
+
+void CRegister::Init(CNetServer *pNetServer, IEngineMasterServer *pMasterServer)
+{
+	m_pNetServer = pNetServer;
+	m_pMasterServer = pMasterServer;
+}
+
+void CRegister::RegisterUpdate()
+{
+	int64 Now = time_get();
+	int64 Freq = time_freq();
+
+	if(!g_Config.m_SvRegister)
+		return;
+
+	m_pMasterServer->Update();
+
+	if(m_RegisterState == REGISTERSTATE_START)
+	{
+		m_RegisterCount = 0;
+		m_RegisterFirst = 1;
+		RegisterNewState(REGISTERSTATE_UPDATE_ADDRS);
+		m_pMasterServer->RefreshAddresses();
+		dbg_msg("register", "refreshing ip addresses");
+	}
+	else if(m_RegisterState == REGISTERSTATE_UPDATE_ADDRS)
+	{
+		m_RegisterRegisteredServer = -1;
+	
+		if(!m_pMasterServer->IsRefreshing())
+		{
+			int i;
+			for(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
+			{
+				NETADDR addr = m_pMasterServer->GetAddr(i);
+				m_aMasterserverInfo[i].m_Addr = addr;
+				m_aMasterserverInfo[i].m_Count = 0;
+			
+				if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3])
+					m_aMasterserverInfo[i].m_Valid = 0;
+				else
+				{
+					m_aMasterserverInfo[i].m_Valid = 1;
+					m_aMasterserverInfo[i].m_Count = -1;
+					m_aMasterserverInfo[i].m_LastSend = 0;
+				}
+			}
+			
+			dbg_msg("register", "fetching server counts");
+			RegisterNewState(REGISTERSTATE_QUERY_COUNT);
+		}
+	}
+	else if(m_RegisterState == REGISTERSTATE_QUERY_COUNT)
+	{
+		int Left = 0;
+		for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
+		{
+			if(!m_aMasterserverInfo[i].m_Valid)
+				continue;
+				
+			if(m_aMasterserverInfo[i].m_Count == -1)
+			{
+				Left++;
+				if(m_aMasterserverInfo[i].m_LastSend+Freq < Now)
+				{
+					m_aMasterserverInfo[i].m_LastSend = Now;
+					RegisterSendCountRequest(m_aMasterserverInfo[i].m_Addr);
+				}
+			}
+		}
+		
+		// check if we are done or timed out
+		if(Left == 0 || Now > m_RegisterStateStart+Freq*3)
+		{
+			// choose server
+			int Best = -1;
+			int i;
+			for(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
+			{
+				if(!m_aMasterserverInfo[i].m_Valid || m_aMasterserverInfo[i].m_Count == -1)
+					continue;
+					
+				if(Best == -1 || m_aMasterserverInfo[i].m_Count < m_aMasterserverInfo[Best].m_Count)
+					Best = i;
+			}
+
+			// server chosen
+			m_RegisterRegisteredServer = Best;
+			if(m_RegisterRegisteredServer == -1)
+			{
+				dbg_msg("register", "WARNING: No master servers. Retrying in 60 seconds");
+				RegisterNewState(REGISTERSTATE_ERROR);
+			}
+			else
+			{			
+				dbg_msg("register", "choosen '%s' as master, sending heartbeats", m_pMasterServer->GetName(m_RegisterRegisteredServer));
+				m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend = 0;
+				RegisterNewState(REGISTERSTATE_HEARTBEAT);
+			}
+		}
+	}
+	else if(m_RegisterState == REGISTERSTATE_HEARTBEAT)
+	{
+		// check if we should send heartbeat
+		if(Now > m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend+Freq*15)
+		{
+			m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend = Now;
+			RegisterSendHeartbeat(m_aMasterserverInfo[m_RegisterRegisteredServer].m_Addr);
+		}
+		
+		if(Now > m_RegisterStateStart+Freq*60)
+		{
+			dbg_msg("register", "WARNING: Master server is not responding, switching master");
+			RegisterNewState(REGISTERSTATE_START);
+		}
+	}
+	else if(m_RegisterState == REGISTERSTATE_REGISTERED)
+	{
+		if(m_RegisterFirst)
+			dbg_msg("register", "server registered");
+			
+		m_RegisterFirst = 0;
+		
+		// check if we should send new heartbeat again
+		if(Now > m_RegisterStateStart+Freq)
+		{
+			if(m_RegisterCount == 120) // redo the whole process after 60 minutes to balance out the master servers
+				RegisterNewState(REGISTERSTATE_START);
+			else
+			{
+				m_RegisterCount++;
+				RegisterNewState(REGISTERSTATE_HEARTBEAT);
+			}
+		}
+	}
+	else if(m_RegisterState == REGISTERSTATE_ERROR)
+	{
+		// check for restart
+		if(Now > m_RegisterStateStart+Freq*60)
+			RegisterNewState(REGISTERSTATE_START);
+	}
+}
+
+int CRegister::RegisterProcessPacket(CNetChunk *pPacket)
+{
+	if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) &&
+		mem_comp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
+	{
+		RegisterSendFwcheckresponse(&pPacket->m_Address);
+		return 1;
+	}
+	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWOK) &&
+		mem_comp(pPacket->m_pData, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0)
+	{
+		if(m_RegisterFirst)
+			dbg_msg("register", "no firewall/nat problems detected");
+		RegisterNewState(REGISTERSTATE_REGISTERED);
+		return 1;
+	}
+	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWERROR) &&
+		mem_comp(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.", g_Config.m_SvPort);
+		RegisterNewState(REGISTERSTATE_ERROR);
+		return 1;
+	}
+	else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_COUNT)+2 &&
+		mem_comp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0)
+	{
+		RegisterGotCount(pPacket);
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/src/engine/server/register.h b/src/engine/server/register.h
new file mode 100644
index 00000000..a800ec1e
--- /dev/null
+++ b/src/engine/server/register.h
@@ -0,0 +1,48 @@
+#ifndef ENGINE_SERVER_REGISTER_H
+#define ENGINE_SERVER_REGISTER_H
+

+class CRegister

+{

+	enum

+	{

+		REGISTERSTATE_START=0,

+		REGISTERSTATE_UPDATE_ADDRS,

+		REGISTERSTATE_QUERY_COUNT,

+		REGISTERSTATE_HEARTBEAT,

+		REGISTERSTATE_REGISTERED,

+		REGISTERSTATE_ERROR

+	};

+

+	struct CMasterserverInfo

+	{

+		NETADDR m_Addr;

+		int m_Count;

+		int m_Valid;

+		int64 m_LastSend;

+	};

+

+	class CNetServer *m_pNetServer;

+	class IEngineMasterServer *m_pMasterServer;

+

+	int m_RegisterState;

+	int64 m_RegisterStateStart;

+	int m_RegisterFirst;

+	int m_RegisterCount;

+

+	class CMasterserverInfo m_aMasterserverInfo[IMasterServer::MAX_MASTERSERVERS];

+	int m_RegisterRegisteredServer;

+

+	void RegisterNewState(int State);

+	void RegisterSendFwcheckresponse(NETADDR *pAddr);

+	void RegisterSendHeartbeat(NETADDR Addr);

+	void RegisterSendCountRequest(NETADDR Addr);

+	void RegisterGotCount(class CNetChunk *pChunk);

+

+public:

+	CRegister();

+	void Init(class CNetServer *pNetServer, class IEngineMasterServer *pMasterServer);

+	void RegisterUpdate();

+	int RegisterProcessPacket(class CNetChunk *pPacket);

+};

+

+#endif

diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp
new file mode 100644
index 00000000..45cec1e4
--- /dev/null
+++ b/src/engine/server/server.cpp
@@ -0,0 +1,1400 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+
+#include <base/system.h>
+
+#include <engine/shared/config.h>
+#include <engine/shared/engine.h>
+
+#include <engine/shared/protocol.h>
+#include <engine/shared/snapshot.h>
+
+#include <engine/shared/compression.h>
+
+#include <engine/shared/network.h>
+#include <engine/shared/config.h>
+#include <engine/shared/packer.h>
+#include <engine/shared/datafile.h>
+#include <engine/shared/demorec.h>
+
+#include <engine/server.h>
+#include <engine/map.h>
+#include <engine/console.h>
+#include <engine/storage.h>
+#include <engine/masterserver.h>
+#include <engine/config.h>
+
+#include <mastersrv/mastersrv.h>
+
+#include "register.h"
+#include "server.h"
+
+#if defined(CONF_FAMILY_WINDOWS) 
+	#define _WIN32_WINNT 0x0500
+	#define NOGDI
+	#include <windows.h>
+#endif
+
+static const char *StrLtrim(const char *pStr)
+{
+	while(*pStr && *pStr <= 32)
+		pStr++;
+	return pStr;
+}
+
+static void StrRtrim(char *pStr)
+{
+	int i = str_length(pStr);
+	while(i >= 0)
+	{
+		if(pStr[i] > 32)
+			break;
+		pStr[i] = 0;
+		i--;
+	}
+}
+
+
+static int StrAllnum(const char *pStr)
+{
+	while(*pStr)
+	{
+		if(!(*pStr >= '0' && *pStr <= '9'))
+			return 0;
+		pStr++;
+	}
+	return 1;
+}
+
+CSnapIDPool::CSnapIDPool()
+{
+	Reset();
+}
+
+void CSnapIDPool::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 CSnapIDPool::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 CSnapIDPool::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 CSnapIDPool::TimeoutIDs()
+{
+	// process timed ids
+	while(m_FirstTimed != -1)
+		RemoveFirstTimeout();
+}
+
+void CSnapIDPool::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;
+	}
+}
+	
+void CServer::CClient::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;
+}
+
+CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta)
+{
+	m_TickSpeed = SERVER_TICK_SPEED;
+	
+	m_pGameServer = 0;
+	
+	m_CurrentGameTick = 0;
+	m_RunServer = 1;
+
+	mem_zero(m_aBrowseinfoGametype, sizeof(m_aBrowseinfoGametype));
+	m_BrowseinfoProgression = -1;
+
+	m_pCurrentMapData = 0;
+	m_CurrentMapSize = 0;
+
+	Init();
+}
+
+
+int CServer::TrySetClientName(int ClientID, const char *pName)
+{
+	char aTrimmedName[64];
+
+	// trim the name
+	str_copy(aTrimmedName, StrLtrim(pName), sizeof(aTrimmedName));
+	StrRtrim(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(int i = 0; i < MAX_CLIENTS; i++)
+		if(i != ClientID && m_aClients[i].m_State >= CClient::STATE_READY)
+		{
+			if(str_comp(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 CServer::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 CServer::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 CServer::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 CServer::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 CServer::Tick()
+{
+	return m_CurrentGameTick;
+}*/
+
+int64 CServer::TickStartTime(int Tick)
+{
+	return m_GameStartTime + (time_freq()*Tick)/SERVER_TICK_SPEED;
+}
+
+/*int CServer::TickSpeed()
+{
+	return SERVER_TICK_SPEED;
+}*/
+
+int CServer::Init()
+{
+	for(int 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_CurrentGameTick = 0;
+
+	return 0;
+}
+
+int CServer::GetClientInfo(int ClientID, CClientInfo *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->m_pName = m_aClients[ClientID].m_aName;
+		pInfo->m_Latency = m_aClients[ClientID].m_Latency;
+		return 1;
+	}
+	return 0;
+}
+
+void CServer::GetClientIP(int ClientID, char *pIPString, int Size)
+{
+	if(ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_INGAME)
+	{
+		NETADDR Addr = m_NetServer.ClientAddr(ClientID);
+		str_format(pIPString, Size, "%d.%d.%d.%d", Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
+	}
+}
+	
+
+int *CServer::LatestInput(int ClientId, int *size)
+{
+	if(ClientId < 0 || ClientId >= MAX_CLIENTS || m_aClients[ClientId].m_State < CServer::CClient::STATE_READY)
+		return 0;
+	return m_aClients[ClientId].m_LatestInput.m_aData;
+}
+
+const char *CServer::ClientName(int ClientId)
+{
+	if(ClientId < 0 || ClientId >= MAX_CLIENTS || m_aClients[ClientId].m_State < CServer::CClient::STATE_READY)
+		return "(invalid client)";
+	return m_aClients[ClientId].m_aName;
+}	
+
+bool CServer::ClientIngame(int ClientID)
+{
+	return ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CServer::CClient::STATE_INGAME;
+}
+
+int CServer::SendMsg(CMsgPacker *pMsg, int Flags, int ClientId)
+{
+	return SendMsgEx(pMsg, Flags, ClientId, false);
+}
+
+int CServer::SendMsgEx(CMsgPacker *pMsg, int Flags, int ClientID, bool System)
+{
+	CNetChunk Packet;
+	if(!pMsg)
+		return -1;
+		
+	mem_zero(&Packet, sizeof(CNetChunk));
+	
+	Packet.m_ClientID = ClientID;
+	Packet.m_pData = pMsg->Data();
+	Packet.m_DataSize = pMsg->Size();
+	
+	// HACK: modify the message id in the packet and store the system flag
+	*((unsigned char*)Packet.m_pData) <<= 1;
+	if(System)
+		*((unsigned char*)Packet.m_pData) |= 1;
+
+	if(Flags&MSGFLAG_VITAL)
+		Packet.m_Flags |= NETSENDFLAG_VITAL;
+	if(Flags&MSGFLAG_FLUSH)
+		Packet.m_Flags |= NETSENDFLAG_FLUSH;
+	
+	// write message to demo recorder
+	if(!(Flags&MSGFLAG_NORECORD))
+		m_DemoRecorder.RecordMessage(pMsg->Data(), pMsg->Size());
+
+	if(!(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 CServer::DoSnapshot()
+{
+	GameServer()->OnPreSnap();
+	
+	// create snapshot for demo recording
+	if(m_DemoRecorder.IsRecording())
+	{
+		char aData[CSnapshot::MAX_SIZE];
+		int SnapshotSize;
+
+		// build snap and possibly add some messages
+		m_SnapshotBuilder.Init();
+		GameServer()->OnSnap(-1);
+		SnapshotSize = m_SnapshotBuilder.Finish(aData);
+		
+		// write snapshot
+		m_DemoRecorder.RecordSnapshot(Tick(), aData, SnapshotSize);
+	}
+
+	// create snapshots for all clients
+	for(int 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 aData[CSnapshot::MAX_SIZE];
+			char aDeltaData[CSnapshot::MAX_SIZE];
+			char aCompData[CSnapshot::MAX_SIZE];
+			int SnapshotSize;
+			int Crc;
+			static CSnapshot EmptySnap;
+			CSnapshot *pDeltashot = &EmptySnap;
+			int DeltashotSize;
+			int DeltaTick = -1;
+			int DeltaSize;
+
+			m_SnapshotBuilder.Init();
+
+			GameServer()->OnSnap(i);
+
+			// finish snapshot
+			SnapshotSize = m_SnapshotBuilder.Finish(aData);
+			Crc = ((CSnapshot*)aData)->Crc();
+
+			// remove old snapshos
+			// keep 3 seconds worth of snapshots
+			m_aClients[i].m_Snapshots.PurgeUntil(m_CurrentGameTick-SERVER_TICK_SPEED*3);
+			
+			// save it the snapshot
+			m_aClients[i].m_Snapshots.Add(m_CurrentGameTick, time_get(), SnapshotSize, aData, 0);
+			
+			// find snapshot that we can preform delta against
+			EmptySnap.Clear();
+			
+			{
+				DeltashotSize = m_aClients[i].m_Snapshots.Get(m_aClients[i].m_LastAckedSnapshot, 0, &pDeltashot, 0);
+				if(DeltashotSize >= 0)
+					DeltaTick = 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
+			DeltaSize = m_SnapshotDelta.CreateDelta(pDeltashot, (CSnapshot*)aData, aDeltaData);
+			
+			if(DeltaSize)
+			{
+				// compress it
+				int SnapshotSize;
+				const int MaxSize = MAX_SNAPSHOT_PACKSIZE;
+				int NumPackets;
+
+				SnapshotSize = CVariableInt::Compress(aDeltaData, DeltaSize, aCompData);
+				NumPackets = (SnapshotSize+MaxSize-1)/MaxSize;
+				
+				for(int n = 0, Left = SnapshotSize; Left; n++)
+				{
+					int Chunk = Left < MaxSize ? Left : MaxSize;
+					Left -= Chunk;
+
+					if(NumPackets == 1)
+					{
+						CMsgPacker Msg(NETMSG_SNAPSINGLE);
+						Msg.AddInt(m_CurrentGameTick);
+						Msg.AddInt(m_CurrentGameTick-DeltaTick);
+						Msg.AddInt(Crc);
+						Msg.AddInt(Chunk);
+						Msg.AddRaw(&aCompData[n*MaxSize], Chunk);
+						SendMsgEx(&Msg, MSGFLAG_FLUSH, i, true);
+					}
+					else
+					{
+						CMsgPacker Msg(NETMSG_SNAP);
+						Msg.AddInt(m_CurrentGameTick);
+						Msg.AddInt(m_CurrentGameTick-DeltaTick);
+						Msg.AddInt(NumPackets);
+						Msg.AddInt(n);							
+						Msg.AddInt(Crc);
+						Msg.AddInt(Chunk);
+						Msg.AddRaw(&aCompData[n*MaxSize], Chunk);
+						SendMsgEx(&Msg, MSGFLAG_FLUSH, i, true);
+					}
+				}
+			}
+			else
+			{
+				CMsgPacker Msg(NETMSG_SNAPEMPTY);
+				Msg.AddInt(m_CurrentGameTick);
+				Msg.AddInt(m_CurrentGameTick-DeltaTick);
+				SendMsgEx(&Msg, MSGFLAG_FLUSH, i, true);
+			}
+		}
+	}
+
+	GameServer()->OnPostSnap();
+}
+
+
+int CServer::NewClientCallback(int ClientId, void *pUser)
+{
+	CServer *pThis = (CServer *)pUser;
+	pThis->m_aClients[ClientId].m_State = CClient::STATE_AUTH;
+	pThis->m_aClients[ClientId].m_aName[0] = 0;
+	pThis->m_aClients[ClientId].m_aClan[0] = 0;
+	pThis->m_aClients[ClientId].m_Authed = 0;
+	pThis->m_aClients[ClientId].Reset();
+	return 0;
+}
+
+int CServer::DelClientCallback(int ClientId, void *pUser)
+{
+	CServer *pThis = (CServer *)pUser;
+	
+	// notify the mod about the drop
+	if(pThis->m_aClients[ClientId].m_State >= CClient::STATE_READY)
+		pThis->GameServer()->OnClientDrop(ClientId);
+	
+	pThis->m_aClients[ClientId].m_State = CClient::STATE_EMPTY;
+	pThis->m_aClients[ClientId].m_aName[0] = 0;
+	pThis->m_aClients[ClientId].m_aClan[0] = 0;
+	pThis->m_aClients[ClientId].m_Authed = 0;
+	pThis->m_aClients[ClientId].m_Snapshots.PurgeAll();
+	return 0;
+}
+
+void CServer::SendMap(int ClientId)
+{
+	CMsgPacker Msg(NETMSG_MAP_CHANGE);
+	Msg.AddString(g_Config.m_SvMap, 0);
+	Msg.AddInt(m_CurrentMapCrc);
+	SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientId, true);
+}
+
+void CServer::SendRconLine(int ClientId, const char *pLine)
+{
+	CMsgPacker Msg(NETMSG_RCON_LINE);
+	Msg.AddString(pLine, 512);
+	SendMsgEx(&Msg, MSGFLAG_VITAL, ClientId, true);
+}
+
+void CServer::SendRconLineAuthed(const char *pLine, void *pUser)
+{
+	CServer *pThis = (CServer *)pUser;
+	static volatile int ReentryGuard = 0;
+	int i;
+	
+	if(ReentryGuard) return;
+	ReentryGuard++;
+	
+	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);
+	}
+	
+	ReentryGuard--;
+}
+
+void CServer::ProcessClientPacket(CNetChunk *pPacket)
+{
+	int ClientId = pPacket->m_ClientID;
+	NETADDR Addr;
+	CUnpacker Unpacker;
+	Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize);
+	
+	// unpack msgid and system flag
+	int Msg = Unpacker.GetInt();
+	int Sys = Msg&1;
+	Msg >>= 1;
+	
+	if(Unpacker.Error())
+		return;
+	
+	if(m_aClients[ClientId].m_State == CClient::STATE_AUTH)
+	{
+		if(Sys && Msg == NETMSG_INFO)
+		{
+			char aVersion[64];
+			const char *pPassword;
+			str_copy(aVersion, Unpacker.GetString(), 64);
+			if(str_comp(aVersion, GameServer()->NetVersion()) != 0)
+			{
+				// OH FUCK! wrong version, drop him
+				char aReason[256];
+				str_format(aReason, sizeof(aReason), "wrong version. server is running '%s' and client '%s'.", GameServer()->NetVersion(), aVersion);
+				m_NetServer.Drop(ClientId, aReason);
+				return;
+			}
+			
+			str_copy(m_aClients[ClientId].m_aName, Unpacker.GetString(), MAX_NAME_LENGTH);
+			str_copy(m_aClients[ClientId].m_aClan, Unpacker.GetString(), MAX_CLANNAME_LENGTH);
+			pPassword = Unpacker.GetString();
+			
+			if(g_Config.m_Password[0] != 0 && str_comp(g_Config.m_Password, pPassword) != 0)
+			{
+				// wrong password
+				m_NetServer.Drop(ClientId, "wrong password");
+				return;
+			}
+			
+			m_aClients[ClientId].m_State = CClient::STATE_CONNECTING;
+			SendMap(ClientId);
+		}
+	}
+	else
+	{
+		if(Sys)
+		{
+			// system message
+			if(Msg == NETMSG_REQUEST_MAP_DATA)
+			{
+				int Chunk = Unpacker.GetInt();
+				int ChunkSize = 1024-128;
+				int Offset = Chunk * ChunkSize;
+				int Last = 0;
+				
+				// drop faulty map data requests
+				if(Chunk < 0 || Offset > m_CurrentMapSize)
+					return;
+				
+				if(Offset+ChunkSize >= m_CurrentMapSize)
+				{
+					ChunkSize = m_CurrentMapSize-Offset;
+					if(ChunkSize < 0)
+						ChunkSize = 0;
+					Last = 1;
+				}
+				
+				CMsgPacker Msg(NETMSG_MAP_DATA);
+				Msg.AddInt(Last);
+				Msg.AddInt(m_CurrentMapSize);
+				Msg.AddInt(ChunkSize);
+				Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize);
+				SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientId, true);
+				
+				if(g_Config.m_Debug)
+					dbg_msg("server", "sending chunk %d with size %d", Chunk, ChunkSize);
+			}
+			else if(Msg == NETMSG_READY)
+			{
+				if(m_aClients[ClientId].m_State == CClient::STATE_CONNECTING)
+				{
+					Addr = m_NetServer.ClientAddr(ClientId);
+					
+					dbg_msg("server", "player is ready. ClientId=%x ip=%d.%d.%d.%d",
+						ClientId, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
+					m_aClients[ClientId].m_State = CClient::STATE_READY;
+					GameServer()->OnClientConnected(ClientId);
+				}
+			}
+			else if(Msg == NETMSG_ENTERGAME)
+			{
+				if(m_aClients[ClientId].m_State == CClient::STATE_READY)
+				{
+					Addr = m_NetServer.ClientAddr(ClientId);
+					
+					dbg_msg("server", "player has entered the game. ClientId=%x ip=%d.%d.%d.%d",
+						ClientId, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
+					m_aClients[ClientId].m_State = CClient::STATE_INGAME;
+					GameServer()->OnClientEnter(ClientId);
+				}
+			}
+			else if(Msg == NETMSG_INPUT)
+			{
+				CClient::CInput *pInput;
+				int64 TagTime;
+				
+				m_aClients[ClientId].m_LastAckedSnapshot = Unpacker.GetInt();
+				int IntendedTick = Unpacker.GetInt();
+				int Size = Unpacker.GetInt();
+				
+				// check for errors
+				if(Unpacker.Error() || Size/4 > MAX_INPUT_SIZE)
+					return;
+
+				if(m_aClients[ClientId].m_LastAckedSnapshot > 0)
+					m_aClients[ClientId].m_SnapRate = CClient::SNAPRATE_FULL;
+					
+				if(m_aClients[ClientId].m_Snapshots.Get(m_aClients[ClientId].m_LastAckedSnapshot, &TagTime, 0, 0) >= 0)
+					m_aClients[ClientId].m_Latency = (int)(((time_get()-TagTime)*1000)/time_freq());
+
+				// add message to report the input timing
+				// skip packets that are old
+				if(IntendedTick > m_aClients[ClientId].m_LastInputTick)
+				{
+					int TimeLeft = ((TickStartTime(IntendedTick)-time_get())*1000) / time_freq();
+					
+					CMsgPacker Msg(NETMSG_INPUTTIMING);
+					Msg.AddInt(IntendedTick);
+					Msg.AddInt(TimeLeft);
+					SendMsgEx(&Msg, 0, ClientId, true);
+				}
+
+				m_aClients[ClientId].m_LastInputTick = IntendedTick;
+
+				pInput = &m_aClients[ClientId].m_aInputs[m_aClients[ClientId].m_CurrentInput];
+				
+				if(IntendedTick <= Tick())
+					IntendedTick = Tick()+1;
+
+				pInput->m_GameTick = IntendedTick;
+				
+				for(int i = 0; i < Size/4; i++)
+					pInput->m_aData[i] = Unpacker.GetInt();
+				
+				mem_copy(m_aClients[ClientId].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int));
+				
+				m_aClients[ClientId].m_CurrentInput++;
+				m_aClients[ClientId].m_CurrentInput %= 200;
+			
+				// call the mod with the fresh input data
+				if(m_aClients[ClientId].m_State == CClient::STATE_INGAME)
+					GameServer()->OnClientDirectInput(ClientId, m_aClients[ClientId].m_LatestInput.m_aData);
+			}
+			else if(Msg == NETMSG_RCON_CMD)
+			{
+				const char *pCmd = Unpacker.GetString();
+				
+				if(Unpacker.Error() == 0 && m_aClients[ClientId].m_Authed)
+				{
+					dbg_msg("server", "ClientId=%d rcon='%s'", ClientId, pCmd);
+					Console()->ExecuteLine(pCmd);
+				}
+			}
+			else if(Msg == NETMSG_RCON_AUTH)
+			{
+				const char *pPw;
+				Unpacker.GetString(); // login name, not used
+				pPw = Unpacker.GetString();
+				
+				if(Unpacker.Error() == 0)
+				{
+					if(g_Config.m_SvRconPassword[0] == 0)
+					{
+						SendRconLine(ClientId, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
+					}
+					else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0)
+					{
+						CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
+						Msg.AddInt(1);
+						SendMsgEx(&Msg, MSGFLAG_VITAL, ClientId, true);
+						
+						m_aClients[ClientId].m_Authed = 1;
+						SendRconLine(ClientId, "Authentication successful. Remote console access granted.");
+						dbg_msg("server", "ClientId=%d authed", ClientId);
+					}
+					else
+					{
+						SendRconLine(ClientId, "Wrong password.");
+					}
+				}
+			}
+			else if(Msg == NETMSG_PING)
+			{
+				CMsgPacker Msg(NETMSG_PING_REPLY);
+				SendMsgEx(&Msg, 0, ClientId, true);
+			}
+			else
+			{
+				char aHex[] = "0123456789ABCDEF";
+				char aBuf[512];
+
+				for(int b = 0; b < pPacket->m_DataSize && b < 32; b++)
+				{
+					aBuf[b*3] = aHex[((const unsigned char *)pPacket->m_pData)[b]>>4];
+					aBuf[b*3+1] = aHex[((const unsigned char *)pPacket->m_pData)[b]&0xf];
+					aBuf[b*3+2] = ' ';
+					aBuf[b*3+3] = 0;
+				}
+
+				dbg_msg("server", "strange message ClientId=%d msg=%d data_size=%d", ClientId, Msg, pPacket->m_DataSize);
+				dbg_msg("server", "%s", aBuf);
+				
+			}
+		}
+		else
+		{
+			// game message
+			if(m_aClients[ClientId].m_State >= CClient::STATE_READY)
+				GameServer()->OnMessage(Msg, &Unpacker, ClientId);
+		}
+	}
+}
+	
+void CServer::SendServerInfo(NETADDR *pAddr, int Token)
+{
+	CNetChunk Packet;
+	CPacker p;
+	char aBuf[128];
+
+	// count the players
+	int PlayerCount = 0;
+	for(int i = 0; i < MAX_CLIENTS; i++)
+	{
+		if(m_aClients[i].m_State != CClient::STATE_EMPTY)
+			PlayerCount++;
+	}
+	
+	p.Reset();
+
+	if(Token >= 0)
+	{
+		// new token based format
+		p.AddRaw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO));
+		str_format(aBuf, sizeof(aBuf), "%d", Token);
+		p.AddString(aBuf, 6);
+	}
+	else
+	{
+		// old format
+		p.AddRaw(SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO));
+	}
+	
+	p.AddString(GameServer()->Version(), 32);
+	p.AddString(g_Config.m_SvName, 64);
+	p.AddString(g_Config.m_SvMap, 32);
+
+	// gametype
+	p.AddString(m_aBrowseinfoGametype, 16);
+
+	// flags
+	int i = 0;
+	if(g_Config.m_Password[0])   // password set
+		i |= SERVER_FLAG_PASSWORD;
+	str_format(aBuf, sizeof(aBuf), "%d", i);
+	p.AddString(aBuf, 2);
+
+	// progression
+	str_format(aBuf, sizeof(aBuf), "%d", m_BrowseinfoProgression);
+	p.AddString(aBuf, 4);
+	
+	str_format(aBuf, sizeof(aBuf), "%d", PlayerCount); p.AddString(aBuf, 3);  // num players
+	str_format(aBuf, sizeof(aBuf), "%d", m_NetServer.MaxClients()); p.AddString(aBuf, 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(aBuf, sizeof(aBuf), "%d", m_aClients[i].m_Score); p.AddString(aBuf, 6);  // player score
+		}
+	}
+	
+	
+	Packet.m_ClientID = -1;
+	Packet.m_Address = *pAddr;
+	Packet.m_Flags = NETSENDFLAG_CONNLESS;
+	Packet.m_DataSize = p.Size();
+	Packet.m_pData = p.Data();
+	m_NetServer.Send(&Packet);
+}
+
+void CServer::UpdateServerInfo()
+{
+	for(int i = 0; i < MAX_CLIENTS; ++i)
+	{
+		if(m_aClients[i].m_State != CClient::STATE_EMPTY)
+		{
+			NETADDR Addr = m_NetServer.ClientAddr(i);
+			SendServerInfo(&Addr, -1); 	// SERVERBROWSE_OLD_INFO
+		}
+	}
+}
+
+int CServer::BanAdd(NETADDR Addr, int Seconds)
+{
+	return m_NetServer.BanAdd(Addr, Seconds);	
+}
+
+int CServer::BanRemove(NETADDR Addr)
+{
+	return m_NetServer.BanRemove(Addr);
+}
+	
+
+void CServer::PumpNetwork()
+{
+	CNetChunk Packet;
+
+	m_NetServer.Update();
+	
+	// process packets
+	while(m_NetServer.Recv(&Packet))
+	{
+		if(Packet.m_ClientID == -1)
+		{
+			// stateless
+			if(!m_Register.RegisterProcessPacket(&Packet))
+			{
+				if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 &&
+					mem_comp(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) &&
+					mem_comp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0)
+				{
+					SendServerInfo(&Packet.m_Address, -1);
+				}
+			}
+		}
+		else
+			ProcessClientPacket(&Packet);
+	}
+}
+
+int CServer::LoadMap(const char *pMapName)
+{
+	//DATAFILE *df;
+	char aBuf[512];
+	str_format(aBuf, sizeof(aBuf), "maps/%s.map", pMapName);
+	
+	/*df = datafile_load(buf);
+	if(!df)
+		return 0;*/
+		
+	if(!m_pMap->Load(aBuf))
+		return 0;
+	
+	// stop recording when we change map
+	m_DemoRecorder.Stop();
+	
+	// reinit snapshot ids
+	m_IDPool.TimeoutIDs();
+	
+	// get the crc of the map
+	m_CurrentMapCrc = m_pMap->Crc();
+	dbg_msg("server", "%s crc is %08x", aBuf, m_CurrentMapCrc);
+		
+	str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap));
+	//map_set(df);
+	
+	// load compelate map into memory for download
+	{
+		IOHANDLE File = Storage()->OpenFile(aBuf, 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;
+}
+
+void CServer::InitEngine(const char *pAppname)
+{
+	m_Engine.Init(pAppname);
+}
+
+void CServer::InitRegister(CNetServer *pNetServer, IEngineMasterServer *pMasterServer)
+{
+	m_Register.Init(pNetServer, pMasterServer);
+}
+
+int CServer::Run()
+{
+	m_pGameServer = Kernel()->RequestInterface<IGameServer>();
+	m_pMap = Kernel()->RequestInterface<IEngineMap>();
+	m_pStorage = Kernel()->RequestInterface<IStorage>();
+
+	//snap_init_id();
+	net_init();
+	
+	//
+	Console()->RegisterPrintCallback(SendRconLineAuthed, this);
+
+	// load map
+	if(!LoadMap(g_Config.m_SvMap))
+	{
+		dbg_msg("server", "failed to load map. mapname='%s'", g_Config.m_SvMap);
+		return -1;
+	}
+	
+	// start server
+	// TODO: IPv6 support
+	NETADDR BindAddr;
+	if(g_Config.m_SvBindaddr[0] && net_host_lookup(g_Config.m_SvBindaddr, &BindAddr, NETTYPE_IPV4) == 0)
+	{
+		// sweet!
+		BindAddr.port = g_Config.m_SvPort;
+	}
+	else
+	{
+		mem_zero(&BindAddr, sizeof(BindAddr));
+		BindAddr.port = g_Config.m_SvPort;
+	}
+	
+	
+	if(!m_NetServer.Open(BindAddr, g_Config.m_SvMaxClients, 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'", g_Config.m_SvName);
+	
+	GameServer()->OnInit();
+	dbg_msg("server", "version %s", GameServer()->NetVersion());
+
+	// start game
+	{
+		int64 ReportTime = time_get();
+		int ReportInterval = 3;
+	
+		m_Lastheartbeat = 0;
+		m_GameStartTime = time_get();
+	
+		if(g_Config.m_Debug)
+			dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024);
+
+		while(m_RunServer)
+		{
+			int64 t = time_get();
+			int NewTicks = 0;
+			
+			// load new map TODO: don't poll this
+			if(str_comp(g_Config.m_SvMap, m_aCurrentMap) != 0 || g_Config.m_SvMapReload)
+			{
+				g_Config.m_SvMapReload = 0;
+				
+				// load map
+				if(LoadMap(g_Config.m_SvMap))
+				{
+					// new map loaded
+					GameServer()->OnShutdown();
+					
+					for(int 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_CurrentGameTick = 0;
+					Kernel()->ReregisterInterface(GameServer());
+					GameServer()->OnInit();
+				}
+				else
+				{
+					dbg_msg("server", "failed to load map. mapname='%s'", g_Config.m_SvMap);
+					str_copy(g_Config.m_SvMap, m_aCurrentMap, sizeof(g_Config.m_SvMap));
+				}
+			}
+			
+			while(t > TickStartTime(m_CurrentGameTick+1))
+			{
+				m_CurrentGameTick++;
+				NewTicks++;
+				
+				// apply new input
+				for(int c = 0; c < MAX_CLIENTS; c++)
+				{
+					if(m_aClients[c].m_State == CClient::STATE_EMPTY)
+						continue;
+					for(int i = 0; i < 200; i++)
+					{
+						if(m_aClients[c].m_aInputs[i].m_GameTick == Tick())
+						{
+							if(m_aClients[c].m_State == CClient::STATE_INGAME)
+								GameServer()->OnClientPredictedInput(c, m_aClients[c].m_aInputs[i].m_aData);
+							break;
+						}
+					}
+				}
+
+				GameServer()->OnTick();
+			}
+			
+			// snap game
+			if(NewTicks)
+			{
+				if(g_Config.m_SvHighBandwidth || (m_CurrentGameTick%2) == 0)
+					DoSnapshot();
+			}
+			
+			// master server stuff
+			m_Register.RegisterUpdate();
+	
+			PumpNetwork();
+	
+			if(ReportTime < time_get())
+			{
+				if(g_Config.m_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);
+		}
+	}
+	// disconnect all clients on shutdown
+	for(int i = 0; i < MAX_CLIENTS; ++i)
+	{
+		if(m_aClients[i].m_State != CClient::STATE_EMPTY)
+			m_NetServer.Drop(i, "server shutdown");
+	}
+
+	GameServer()->OnShutdown();
+	m_pMap->Unload();
+
+	if(m_pCurrentMapData)
+		mem_free(m_pCurrentMapData);
+	return 0;
+}
+
+void CServer::ConKick(IConsole::IResult *pResult, void *pUser)
+{
+	((CServer *)pUser)->Kick(pResult->GetInteger(0), "kicked by console");
+}
+
+void CServer::ConBan(IConsole::IResult *pResult, void *pUser)
+{
+	NETADDR Addr;
+	char aAddrStr[128];
+	const char *pStr = pResult->GetString(0);
+	int Minutes = 30;
+	
+	if(pResult->NumArguments() > 1)
+		Minutes = pResult->GetInteger(1);
+	
+	if(net_addr_from_str(&Addr, pStr) == 0)
+		((CServer *)pUser)->BanAdd(Addr, Minutes*60);
+	else if(StrAllnum(pStr))
+	{
+		int ClientId = str_toint(pStr);
+
+		if(ClientId < 0 || ClientId >= MAX_CLIENTS || ((CServer *)pUser)->m_aClients[ClientId].m_State == CClient::STATE_EMPTY)
+		{
+			dbg_msg("server", "invalid client id");
+			return;
+		}
+
+		NETADDR Addr = ((CServer *)pUser)->m_NetServer.ClientAddr(ClientId);
+		((CServer *)pUser)->BanAdd(Addr, Minutes*60);
+	}
+	
+	Addr.port = 0;
+	net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
+	
+	if(Minutes)
+		dbg_msg("server", "banned %s for %d minutes", aAddrStr, Minutes);
+	else
+		dbg_msg("server", "banned %s for life", aAddrStr);
+}
+
+void CServer::ConUnban(IConsole::IResult *pResult, void *pUser)
+{
+	NETADDR Addr;
+	const char *pStr = pResult->GetString(0);
+	
+	if(net_addr_from_str(&Addr, pStr) == 0)
+		((CServer *)pUser)->BanRemove(Addr);
+	else
+		dbg_msg("server", "invalid network address");
+}
+
+void CServer::ConBans(IConsole::IResult *pResult, void *pUser)
+{
+	unsigned Now = time_timestamp();
+	char aBuf[1024];
+	CServer* pServer = (CServer *)pUser;
+	
+	int Num = pServer->m_NetServer.BanNum();
+	for(int i = 0; i < Num; i++)
+	{
+		CNetServer::CBanInfo Info;
+		pServer->m_NetServer.BanGet(i, &Info);
+		NETADDR Addr = Info.m_Addr;
+		
+		if(Info.m_Expires == -1)
+		{
+			str_format(aBuf, sizeof(aBuf), "#%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;
+			str_format(aBuf, sizeof(aBuf), "#%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);
+		}
+		pServer->Console()->Print(aBuf);
+		dbg_msg("server", "%s", aBuf);
+	}
+	str_format(aBuf, sizeof(aBuf), "%d ban(s)", Num);
+	pServer->Console()->Print(aBuf);
+	dbg_msg("server", "%s", aBuf);
+}
+
+void CServer::ConStatus(IConsole::IResult *pResult, void *pUser)
+{
+	int i;
+	NETADDR Addr;
+	char aBuf[1024];
+	CServer* pServer = (CServer *)pUser;
+
+	for(i = 0; i < MAX_CLIENTS; i++)
+	{
+		if(pServer->m_aClients[i].m_State == CClient::STATE_INGAME)
+		{
+			Addr = pServer->m_NetServer.ClientAddr(i);
+			str_format(aBuf, sizeof(aBuf), "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,
+				pServer->m_aClients[i].m_aName, pServer->m_aClients[i].m_Score);
+			pServer->Console()->Print(aBuf);
+			dbg_msg("server", "%s", aBuf);
+		}
+	}
+}
+
+void CServer::ConShutdown(IConsole::IResult *pResult, void *pUser)
+{
+	((CServer *)pUser)->m_RunServer = 0;
+}
+
+void CServer::ConRecord(IConsole::IResult *pResult, void *pUser)
+{
+	char aFilename[512];
+	str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pResult->GetString(0));
+	((CServer *)pUser)->m_DemoRecorder.Start(((CServer *)pUser)->Storage(), aFilename, ((CServer *)pUser)->GameServer()->NetVersion(), ((CServer *)pUser)->m_aCurrentMap, ((CServer *)pUser)->m_CurrentMapCrc, "server");
+}
+
+void CServer::ConStopRecord(IConsole::IResult *pResult, void *pUser)
+{
+	((CServer *)pUser)->m_DemoRecorder.Stop();
+}
+
+void CServer::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
+{
+	pfnCallback(pResult, pCallbackUserData);
+	if(pResult->NumArguments())
+		((CServer *)pUserData)->UpdateServerInfo();
+}
+
+void CServer::RegisterCommands()
+{
+	m_pConsole = Kernel()->RequestInterface<IConsole>();
+	
+	Console()->Register("kick", "i", CFGFLAG_SERVER, ConKick, this, "");
+	Console()->Register("ban", "s?i", CFGFLAG_SERVER, ConBan, this, "");
+	Console()->Register("unban", "s", CFGFLAG_SERVER, ConUnban, this, "");
+	Console()->Register("bans", "", CFGFLAG_SERVER, ConBans, this, "");
+	Console()->Register("status", "", CFGFLAG_SERVER, ConStatus, this, "");
+	Console()->Register("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, "");
+
+	Console()->Register("record", "s", CFGFLAG_SERVER, ConRecord, this, "");
+	Console()->Register("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, "");
+
+	Console()->Chain("sv_name", ConchainSpecialInfoupdate, this);
+	Console()->Chain("password", ConchainSpecialInfoupdate, this);
+}	
+
+
+int CServer::SnapNewID()
+{
+	return m_IDPool.NewID();
+}
+
+void CServer::SnapFreeID(int ID)
+{
+	m_IDPool.FreeID(ID);
+}
+
+
+void *CServer::SnapNewItem(int Type, int Id, int Size)
+{
+	dbg_assert(Type >= 0 && Type <=0xffff, "incorrect type");
+	dbg_assert(Id >= 0 && Id <=0xffff, "incorrect id");
+	return m_SnapshotBuilder.NewItem(Type, Id, Size);		
+}
+
+void CServer::SnapSetStaticsize(int ItemType, int Size)
+{
+	m_SnapshotDelta.SetStaticsize(ItemType, Size);
+}
+
+static CServer *CreateServer() { return new CServer(); }
+
+int main(int argc, const char **argv) // ignore_convention
+{
+#if defined(CONF_FAMILY_WINDOWS)
+	for(int i = 1; i < argc; i++) // ignore_convention
+	{
+		if(str_comp("-s", argv[i]) == 0 || str_comp("--silent", argv[i]) == 0) // ignore_convention
+		{
+			ShowWindow(GetConsoleWindow(), SW_HIDE);
+			break;
+		}
+	}
+#endif
+
+	// init the engine
+	dbg_msg("server", "starting...");
+	CServer *pServer = CreateServer();
+	pServer->InitEngine("Teeworlds");
+	
+	IKernel *pKernel = IKernel::Create();
+
+	// create the components
+	IEngineMap *pEngineMap = CreateEngineMap();
+	IGameServer *pGameServer = CreateGameServer();
+	IConsole *pConsole = CreateConsole();
+	IEngineMasterServer *pEngineMasterServer = CreateEngineMasterServer();
+	IStorage *pStorage = CreateStorage("Teeworlds", argv[0]); // ignore_convention
+	IConfig *pConfig = CreateConfig();
+	
+	pServer->InitRegister(&pServer->m_NetServer, pEngineMasterServer);
+
+	{
+		bool RegisterFail = false;
+
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(pServer); // register as both
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMap*>(pEngineMap)); // register as both
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMap*>(pEngineMap));
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(pGameServer);
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(pConsole);
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(pStorage);
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(pConfig);
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMasterServer*>(pEngineMasterServer)); // register as both
+		RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMasterServer*>(pEngineMasterServer));
+		
+		if(RegisterFail)
+			return -1;
+	}
+	
+	pConfig->Init();
+	pEngineMasterServer->Init(pServer->Engine());
+	pEngineMasterServer->Load();
+		
+	// register all console commands
+	pServer->RegisterCommands();
+	pGameServer->OnConsoleInit();
+	
+	// execute autoexec file
+	pConsole->ExecuteFile("autoexec.cfg");
+
+	// parse the command line arguments
+	if(argc > 1) // ignore_convention
+		pConsole->ParseArguments(argc-1, &argv[1]); // ignore_convention
+	
+	// run the server
+	pServer->Run();
+	
+	// free
+	delete pServer;
+	delete pKernel;
+	delete pEngineMap;
+	delete pGameServer;
+	delete pConsole;
+	delete pEngineMasterServer;
+	delete pStorage;
+	delete pConfig;
+	return 0;
+}
+
diff --git a/src/engine/server/server.h b/src/engine/server/server.h
new file mode 100644
index 00000000..6904085a
--- /dev/null
+++ b/src/engine/server/server.h
@@ -0,0 +1,195 @@
+#ifndef ENGINE_SERVER_SERVER_H
+#define ENGINE_SERVER_SERVER_H
+
+#include <engine/server.h>
+
+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();
+	
+	void Reset();
+	void RemoveFirstTimeout();
+	int NewID();
+	void TimeoutIDs();
+	void FreeID(int Id);
+};
+
+class CServer : public IServer
+{
+	class IGameServer *m_pGameServer;
+	class IConsole *m_pConsole;
+	class IStorage *m_pStorage;
+public:
+	class IGameServer *GameServer() { return m_pGameServer; }
+	class IConsole *Console() { return m_pConsole; }
+	class IStorage *Storage() { return m_pStorage; }
+	class CEngine *Engine() { return &m_Engine; }
+
+	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();
+	};
+	
+	CClient m_aClients[MAX_CLIENTS];
+
+	CSnapshotDelta m_SnapshotDelta;
+	CSnapshotBuilder m_SnapshotBuilder;
+	CSnapIDPool m_IDPool;
+	CNetServer m_NetServer;
+	
+	IEngineMap *m_pMap;
+
+	int64 m_GameStartTime;
+	//int m_CurrentGameTick;
+	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;	
+	
+	CDemoRecorder m_DemoRecorder;
+	CEngine m_Engine;
+	CRegister m_Register;
+	
+	CServer();
+	
+	int TrySetClientName(int ClientID, const char *pName);
+
+	virtual void SetClientName(int ClientID, const char *pName);
+	virtual void SetClientScore(int ClientID, int Score);
+	virtual void SetBrowseInfo(const char *pGameType, int Progression);
+
+	void Kick(int ClientID, const char *pReason);
+
+	//int Tick()
+	int64 TickStartTime(int Tick);
+	//int TickSpeed()
+
+	int Init();
+
+	int GetClientInfo(int ClientID, CClientInfo *pInfo);
+	void GetClientIP(int ClientID, char *pIPString, int Size);
+	const char *ClientName(int ClientId);
+	bool ClientIngame(int ClientID);
+
+	int *LatestInput(int ClientId, int *size);
+
+	virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientId);
+	int SendMsgEx(CMsgPacker *pMsg, int Flags, int ClientID, bool System);
+
+	void DoSnapshot();
+
+	static int NewClientCallback(int ClientId, void *pUser);
+	static int DelClientCallback(int ClientId, void *pUser);
+
+	void SendMap(int ClientId);
+	void SendRconLine(int ClientId, const char *pLine);
+	static void SendRconLineAuthed(const char *pLine, void *pUser);
+	
+	void ProcessClientPacket(CNetChunk *pPacket);
+		
+	void SendServerInfo(NETADDR *pAddr, int Token);
+	void UpdateServerInfo();
+
+	int BanAdd(NETADDR Addr, int Seconds);
+	int BanRemove(NETADDR Addr);
+		
+
+	void PumpNetwork();
+
+	int LoadMap(const char *pMapName);
+
+	void InitEngine(const char *pAppname);
+	void InitRegister(CNetServer *pNetServer, IEngineMasterServer *pMasterServer);
+	int Run();
+
+	static void ConKick(IConsole::IResult *pResult, void *pUser);
+	static void ConBan(IConsole::IResult *pResult, void *pUser);
+	static void ConUnban(IConsole::IResult *pResult, void *pUser);
+	static void ConBans(IConsole::IResult *pResult, void *pUser);
+ 	static void ConStatus(IConsole::IResult *pResult, void *pUser);
+	static void ConShutdown(IConsole::IResult *pResult, void *pUser);
+	static void ConRecord(IConsole::IResult *pResult, void *pUser);
+	static void ConStopRecord(IConsole::IResult *pResult, void *pUser);
+	static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
+
+	void RegisterCommands();
+	
+	
+	virtual int SnapNewID();
+	virtual void SnapFreeID(int ID);
+	virtual void *SnapNewItem(int Type, int Id, int Size);
+	void SnapSetStaticsize(int ItemType, int Size);
+};
+
+#endif
diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h
new file mode 100644
index 00000000..43732f13
--- /dev/null
+++ b/src/engine/serverbrowser.h
@@ -0,0 +1,93 @@
+#ifndef ENGINE_SERVERBROWSER_H
+#define ENGINE_SERVERBROWSER_H
+
+#include "kernel.h"
+
+/*
+	Structure: CServerInfo
+*/
+class CServerInfo
+{
+public:
+	/*
+		Structure: CInfoPlayer
+	*/
+	class CPlayer
+	{
+	public:
+		char m_aName[48];
+		int m_Score;
+	} ;
+
+	int m_SortedIndex;
+	int m_ServerIndex;
+	
+	NETADDR m_NetAddr;
+	
+	int m_QuickSearchHit;
+	
+	int m_Progression;
+	int m_MaxPlayers;
+	int m_NumPlayers;
+	int m_Flags;
+	int m_Favorite;
+	int m_Latency; // in ms
+	char m_aGameType[16];
+	char m_aName[64];
+	char m_aMap[32];
+	char m_aVersion[32];
+	char m_aAddress[24];
+	CPlayer m_aPlayers[16];
+};
+
+class IServerBrowser : public IInterface
+{
+	MACRO_INTERFACE("serverbrowser", 0)
+public:
+
+	/* Constants: Server Browser Sorting
+		SORT_NAME - Sort by name.
+		SORT_PING - Sort by ping.
+		SORT_MAP - Sort by map
+		SORT_GAMETYPE - Sort by game type. DM, TDM etc.
+		SORT_PROGRESSION - Sort by progression.
+		SORT_NUMPLAYERS - Sort after how many players there are on the server.
+	*/
+	enum{
+		SORT_NAME = 0,
+		SORT_PING,
+		SORT_MAP,
+		SORT_GAMETYPE,
+		SORT_PROGRESSION,
+		SORT_NUMPLAYERS,
+		
+		QUICK_SERVERNAME=1,
+		QUICK_PLAYERNAME=2,
+		QUICK_MAPNAME=4,
+		
+		TYPE_INTERNET = 0,
+		TYPE_LAN = 1,
+		TYPE_FAVORITES = 2,
+
+		// TODO: clean this up
+		SET_MASTER_ADD=1,
+		SET_FAV_ADD,
+		SET_TOKEN,
+		SET_OLD_INTERNET,
+		SET_OLD_LAN
+	};
+
+	virtual void Refresh(int Type) = 0;
+	virtual bool IsRefreshingMasters() const = 0;
+	
+	virtual int NumServers() const = 0;
+	
+	virtual int NumSortedServers() const = 0;
+	virtual const CServerInfo *SortedGet(int Index) const = 0;
+	
+	virtual bool IsFavorite(const NETADDR &Addr) const = 0;
+	virtual void AddFavorite(const NETADDR &Addr) = 0;
+	virtual void RemoveFavorite(const NETADDR &Addr) = 0;
+};
+
+#endif
diff --git a/src/engine/shared/compression.cpp b/src/engine/shared/compression.cpp
new file mode 100644
index 00000000..63e44699
--- /dev/null
+++ b/src/engine/shared/compression.cpp
@@ -0,0 +1,88 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+#include <base/system.h>
+
+#include "compression.h"
+
+// Format: ESDDDDDD EDDDDDDD EDD...  Extended, Data, Sign
+unsigned char *CVariableInt::Pack(unsigned char *pDst, int i) 
+{ 
+	*pDst = (i>>25)&0x40; // set sign bit if i<0
+	i = i^(i>>31); // if(i<0) i = ~i
+
+	*pDst |= i&0x3F; // pack 6bit into dst
+	i >>= 6; // discard 6 bits
+	if(i)
+	{
+		*pDst |= 0x80; // set extend bit
+		while(1)
+		{
+			pDst++;
+			*pDst = i&(0x7F); // pack 7bit
+			i >>= 7; // discard 7 bits
+			*pDst |= (i!=0)<<7; // set extend bit (may branch)
+			if(!i)
+				break;
+		}
+	}
+
+	pDst++;
+	return pDst; 
+} 
+ 
+const unsigned char *CVariableInt::Unpack(const unsigned char *pSrc, int *pInOut)
+{ 
+	int Sign = (*pSrc>>6)&1; 
+	*pInOut = *pSrc&0x3F; 
+
+	do
+	{ 
+		if(!(*pSrc&0x80)) break;
+		pSrc++;
+		*pInOut |= (*pSrc&(0x7F))<<(6);
+
+		if(!(*pSrc&0x80)) break;
+		pSrc++;
+		*pInOut |= (*pSrc&(0x7F))<<(6+7);
+
+		if(!(*pSrc&0x80)) break;
+		pSrc++;
+		*pInOut |= (*pSrc&(0x7F))<<(6+7+7);
+
+		if(!(*pSrc&0x80)) break;
+		pSrc++;
+		*pInOut |= (*pSrc&(0x7F))<<(6+7+7+7);
+	} while(0);
+
+	pSrc++;
+	*pInOut ^= -Sign; // if(sign) *i = ~(*i)
+	return pSrc; 
+} 
+
+
+long CVariableInt::Decompress(const void *pSrc_, int Size, void *pDst_)
+{
+	const unsigned char *pSrc = (unsigned char *)pSrc_;
+	const unsigned char *pEnd = pSrc + Size;
+	int *pDst = (int *)pDst_;
+	while(pSrc < pEnd)
+	{
+		pSrc = CVariableInt::Unpack(pSrc, pDst);
+		pDst++;
+	}
+	return (long)((unsigned char *)pDst-(unsigned char *)pDst_);
+}
+
+long CVariableInt::Compress(const void *pSrc_, int Size, void *pDst_)
+{
+	int *pSrc = (int *)pSrc_;
+	unsigned char *pDst = (unsigned char *)pDst_;
+	Size /= 4;
+	while(Size)
+	{
+		pDst = CVariableInt::Pack(pDst, *pSrc);
+		Size--;
+		pSrc++;
+	}
+	return (long)(pDst-(unsigned char *)pDst_);
+}
+
diff --git a/src/engine/shared/compression.h b/src/engine/shared/compression.h
new file mode 100644
index 00000000..9bd9e61a
--- /dev/null
+++ b/src/engine/shared/compression.h
@@ -0,0 +1,12 @@
+#ifndef ENGINE_SHARED_COMPRESSION_H
+#define ENGINE_SHARED_COMPRESSION_H
+// variable int packing
+class CVariableInt
+{
+public:
+	static unsigned char *Pack(unsigned char *pDst, int i);
+	static const unsigned char *Unpack(const unsigned char *pSrc, int *pInOut);
+	static long Compress(const void *pSrc, int Size, void *pDst);
+	static long Decompress(const void *pSrc, int Size, void *pDst);
+};
+#endif
diff --git a/src/engine/shared/config.cpp b/src/engine/shared/config.cpp
new file mode 100644
index 00000000..ca12e8b7
--- /dev/null
+++ b/src/engine/shared/config.cpp
@@ -0,0 +1,111 @@
+#include <engine/config.h>
+#include <engine/storage.h>
+#include <engine/shared/config.h>
+
+CConfiguration g_Config;
+
+class CConfig : public IConfig
+{
+	IStorage *m_pStorage;
+	IOHANDLE m_ConfigFile;
+	
+	struct CCallback
+	{
+		SAVECALLBACKFUNC m_pfnFunc;
+		void *m_pUserData;
+	};
+	
+	enum
+	{
+		MAX_CALLBACKS = 16
+	};
+	
+	CCallback m_aCallbacks[MAX_CALLBACKS];
+	int m_NumCallbacks;
+	
+	void EscapeParam(char *pDst, const char *pSrc, int size)
+	{
+		for(int i = 0; *pSrc && i < size - 1; ++i)
+		{
+			if(*pSrc == '"' || *pSrc == '\\') // escape \ and "
+				*pDst++ = '\\';
+			*pDst++ = *pSrc++;
+		}
+		*pDst = 0;
+	}
+
+public:
+
+	CConfig()
+	{
+		m_ConfigFile = 0;
+		m_NumCallbacks = 0;
+	}
+	
+	virtual void Init()
+	{
+		m_pStorage = Kernel()->RequestInterface<IStorage>();
+		Reset();
+	}
+	
+	virtual void Reset()
+	{
+		#define MACRO_CONFIG_INT(Name,ScriptName,def,min,max,flags,desc) g_Config.m_##Name = def;
+		#define MACRO_CONFIG_STR(Name,ScriptName,len,def,flags,desc) str_copy(g_Config.m_##Name, def, len);
+
+		#include "config_variables.h" 
+
+		#undef MACRO_CONFIG_INT 
+		#undef MACRO_CONFIG_STR 		
+	}
+	
+	virtual void Save()
+	{
+		if(!m_pStorage)
+			return;
+		m_ConfigFile = m_pStorage->OpenFile("settings.cfg", IOFLAG_WRITE);
+		
+		if(!m_ConfigFile)
+			return;
+		
+		char aLineBuf[1024*2];
+		char aEscapeBuf[1024*2];
+
+		#define MACRO_CONFIG_INT(Name,ScriptName,def,min,max,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(aLineBuf, sizeof(aLineBuf), "%s %i", #ScriptName, g_Config.m_##Name); WriteLine(aLineBuf); }
+		#define MACRO_CONFIG_STR(Name,ScriptName,len,def,flags,desc) if((flags)&CFGFLAG_SAVE){ EscapeParam(aEscapeBuf, g_Config.m_##Name, sizeof(aEscapeBuf)); str_format(aLineBuf, sizeof(aLineBuf), "%s \"%s\"", #ScriptName, aEscapeBuf); WriteLine(aLineBuf); }
+
+		#include "config_variables.h" 
+
+		#undef MACRO_CONFIG_INT 
+		#undef MACRO_CONFIG_STR 				
+		
+		for(int i = 0; i < m_NumCallbacks; i++)
+			m_aCallbacks[i].m_pfnFunc(this, m_aCallbacks[i].m_pUserData);
+		
+		io_close(m_ConfigFile);
+		m_ConfigFile = 0;
+	}
+	
+	virtual void RegisterCallback(SAVECALLBACKFUNC pfnFunc, void *pUserData)
+	{
+		dbg_assert(m_NumCallbacks < MAX_CALLBACKS, "too many config callbacks");
+		m_aCallbacks[m_NumCallbacks].m_pfnFunc = pfnFunc;
+		m_aCallbacks[m_NumCallbacks].m_pUserData = pUserData;
+		m_NumCallbacks++;
+	}
+	
+	virtual void WriteLine(const char *pLine)
+	{
+		if(!m_ConfigFile)
+			return;
+#if defined(CONF_FAMILY_WINDOWS)
+		static const char Newline[] = "\r\n";
+#else
+		static const char Newline[] = "\n";
+#endif
+		io_write(m_ConfigFile, pLine, str_length(pLine));
+		io_write(m_ConfigFile, Newline, sizeof(Newline)-1);			
+	}
+};
+
+IConfig *CreateConfig() { return new CConfig; }
diff --git a/src/engine/shared/config.h b/src/engine/shared/config.h
new file mode 100644
index 00000000..10a54004
--- /dev/null
+++ b/src/engine/shared/config.h
@@ -0,0 +1,22 @@
+#ifndef ENGINE_SHARED_E_CONFIG_H
+#define ENGINE_SHARED_E_CONFIG_H
+
+struct CConfiguration
+{ 
+    #define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Save,Desc) int m_##Name;
+    #define MACRO_CONFIG_STR(Name,ScriptName,Len,Def,Save,Desc) char m_##Name[Len]; // Flawfinder: ignore
+    #include "config_variables.h" 
+    #undef MACRO_CONFIG_INT 
+    #undef MACRO_CONFIG_STR 
+};
+
+extern CConfiguration g_Config;
+
+enum
+{
+	CFGFLAG_SAVE=1,
+	CFGFLAG_CLIENT=2,
+	CFGFLAG_SERVER=4
+};
+
+#endif
diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h
new file mode 100644
index 00000000..e5541911
--- /dev/null
+++ b/src/engine/shared/config_variables.h
@@ -0,0 +1,80 @@
+#ifndef ENGINE_SHARED_E_CONFIG_VARIABLES_H
+#define ENGINE_SHARED_E_CONFIG_VARIABLES_H
+#undef ENGINE_SHARED_E_CONFIG_VARIABLES_H // this file will be included several times
+
+// TODO: remove this
+#include "././game/variables.h"
+
+
+MACRO_CONFIG_STR(PlayerName, player_name, 24, "nameless tee", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Name of the player")
+MACRO_CONFIG_STR(ClanName, clan_name, 32, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "(not used)")
+MACRO_CONFIG_STR(Password, password, 32, "", CFGFLAG_CLIENT|CFGFLAG_SERVER, "Password to the server")
+MACRO_CONFIG_STR(Logfile, logfile, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filename to log all output to")
+
+MACRO_CONFIG_INT(ClCpuThrottle, cl_cpu_throttle, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
+MACRO_CONFIG_INT(ClEditor, cl_editor, 0, 0, 1, CFGFLAG_CLIENT, "")
+
+MACRO_CONFIG_INT(ClEventthread, cl_eventthread, 0, 0, 1, CFGFLAG_CLIENT, "Enables the usage of a thread to pump the events")
+
+MACRO_CONFIG_INT(InpGrab, inp_grab, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use forceful input grabbing method")
+
+MACRO_CONFIG_STR(BrFilterString, br_filter_string, 25, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Server browser filtering string")
+
+MACRO_CONFIG_INT(BrFilterFull, br_filter_full, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out full server in browser")
+MACRO_CONFIG_INT(BrFilterEmpty, br_filter_empty, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out empty server in browser")
+MACRO_CONFIG_INT(BrFilterPw, br_filter_pw, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out password protected servers in browser")
+MACRO_CONFIG_INT(BrFilterPing, br_filter_ping, 999, 0, 999, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Ping to filter by in the server browser")
+MACRO_CONFIG_STR(BrFilterGametype, br_filter_gametype, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Game types to filter")
+MACRO_CONFIG_INT(BrFilterPure, br_filter_pure, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard servers in browser")
+MACRO_CONFIG_INT(BrFilterPureMap, br_filter_pure_map, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard maps in browser")
+MACRO_CONFIG_INT(BrFilterCompatversion, br_filter_compatversion, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-compatible servers in browser")
+
+MACRO_CONFIG_INT(BrSort, br_sort, 0, 0, 256, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
+MACRO_CONFIG_INT(BrSortOrder, br_sort_order, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
+MACRO_CONFIG_INT(BrMaxRequests, br_max_requests, 10, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Number of requests to use when refreshing server browser")
+
+MACRO_CONFIG_INT(SndBufferSize, snd_buffer_size, 512, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound buffer size")
+MACRO_CONFIG_INT(SndRate, snd_rate, 48000, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound mixing rate")
+MACRO_CONFIG_INT(SndEnable, snd_enable, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound enable")
+MACRO_CONFIG_INT(SndVolume, snd_volume, 100, 0, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound volume")
+MACRO_CONFIG_INT(SndDevice, snd_device, -1, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "(deprecated) Sound device to use")
+
+MACRO_CONFIG_INT(SndNonactiveMute, snd_nonactive_mute, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
+
+MACRO_CONFIG_INT(GfxScreenWidth, gfx_screen_width, 800, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution width")
+MACRO_CONFIG_INT(GfxScreenHeight, gfx_screen_height, 600, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution height")
+MACRO_CONFIG_INT(GfxFullscreen, gfx_fullscreen, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Fullscreen")
+MACRO_CONFIG_INT(GfxAlphabits, gfx_alphabits, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Alpha bits for framebuffer  (fullscreen only)")
+MACRO_CONFIG_INT(GfxColorDepth, gfx_color_depth, 24, 16, 24, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Colors bits for framebuffer (fullscreen only)")
+MACRO_CONFIG_INT(GfxClear, gfx_clear, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Clear screen before rendering")
+MACRO_CONFIG_INT(GfxVsync, gfx_vsync, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Vertical sync")
+MACRO_CONFIG_INT(GfxDisplayAllModes, gfx_display_all_modes, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
+MACRO_CONFIG_INT(GfxTextureCompression, gfx_texture_compression, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use texture compression")
+MACRO_CONFIG_INT(GfxHighDetail, gfx_high_detail, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "High detail")
+MACRO_CONFIG_INT(GfxTextureQuality, gfx_texture_quality, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
+MACRO_CONFIG_INT(GfxFsaaSamples, gfx_fsaa_samples, 0, 0, 16, CFGFLAG_SAVE|CFGFLAG_CLIENT, "FSAA Samples")
+MACRO_CONFIG_INT(GfxRefreshRate, gfx_refresh_rate, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen refresh rate")
+MACRO_CONFIG_INT(GfxFinish, gfx_finish, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
+
+MACRO_CONFIG_INT(InpMousesens, inp_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity")
+
+MACRO_CONFIG_STR(SvName, sv_name, 128, "unnamed server", CFGFLAG_SERVER, "Server name")
+MACRO_CONFIG_STR(SvBindaddr, sv_bindaddr, 128, "", CFGFLAG_SERVER, "Address to bind the server to")
+MACRO_CONFIG_INT(SvPort, sv_port, 8303, 0, 0, CFGFLAG_SERVER, "Port to use for the server")
+MACRO_CONFIG_INT(SvExternalPort, sv_external_port, 0, 0, 0, CFGFLAG_SERVER, "External port to report to the master servers")
+MACRO_CONFIG_STR(SvMap, sv_map, 128, "dm1", CFGFLAG_SERVER, "Map to use on the server")
+MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients that are allowed on a server")
+MACRO_CONFIG_INT(SvHighBandwidth, sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only")
+MACRO_CONFIG_INT(SvRegister, sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing")
+MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password")
+MACRO_CONFIG_INT(SvMapReload, sv_map_reload, 0, 0, 1, CFGFLAG_SERVER, "Reload the current map")
+
+MACRO_CONFIG_INT(Debug, debug, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Debug mode")
+MACRO_CONFIG_INT(DbgStress, dbg_stress, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress systems")
+MACRO_CONFIG_INT(DbgStressNetwork, dbg_stress_network, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress network")
+MACRO_CONFIG_INT(DbgPref, dbg_pref, 0, 0, 1, CFGFLAG_SERVER, "Performance outputs")
+MACRO_CONFIG_INT(DbgGraphs, dbg_graphs, 0, 0, 1, CFGFLAG_CLIENT, "Performance graphs")
+MACRO_CONFIG_INT(DbgHitch, dbg_hitch, 0, 0, 0, CFGFLAG_SERVER, "Hitch warnings")
+MACRO_CONFIG_STR(DbgStressServer, dbg_stress_server, 32, "localhost", CFGFLAG_CLIENT, "Server to stress")
+MACRO_CONFIG_INT(DbgResizable, dbg_resizable, 0, 0, 0, CFGFLAG_CLIENT, "Enables window resizing")
+#endif
diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp
new file mode 100644
index 00000000..c545b7db
--- /dev/null
+++ b/src/engine/shared/console.cpp
@@ -0,0 +1,489 @@
+#include <base/system.h>
+#include <engine/shared/protocol.h>
+#include <engine/storage.h>
+#include "console.h"
+#include "config.h"
+#include "engine.h"
+#include "linereader.h"
+
+const char *CConsole::CResult::GetString(unsigned Index)
+{
+	if (Index < 0 || Index >= m_NumArgs)
+		return "";
+	return m_apArgs[Index];
+}
+
+int CConsole::CResult::GetInteger(unsigned Index)
+{
+	if (Index < 0 || Index >= m_NumArgs)
+		return 0;
+	return str_toint(m_apArgs[Index]);
+}
+
+float CConsole::CResult::GetFloat(unsigned Index)
+{
+	if (Index < 0 || Index >= m_NumArgs)
+		return 0.0f;
+	return str_tofloat(m_apArgs[Index]);
+}
+
+// the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces
+static char *SkipBlanks(char *pStr)
+{
+	while(*pStr && (*pStr == ' ' || *pStr == '\t' || *pStr == '\n'))
+		pStr++;
+	return pStr;
+}
+
+static char *SkipToBlank(char *pStr)
+{
+	while(*pStr && (*pStr != ' ' && *pStr != '\t' && *pStr != '\n'))
+		pStr++;
+	return pStr;
+}
+
+
+int CConsole::ParseStart(CResult *pResult, const char *pString, int Length)
+{
+	char *pStr;
+	int Len = sizeof(pResult->m_aStringStorage);
+	if(Length < Len)
+		Len = Length;
+		
+	str_copy(pResult->m_aStringStorage, pString, Length);
+	pStr = pResult->m_aStringStorage;
+	
+	// get command
+	pStr = SkipBlanks(pStr);
+	pResult->m_pCommand = pStr;
+	pStr = SkipToBlank(pStr);
+	
+	if(*pStr)
+	{
+		pStr[0] = 0;
+		pStr++;
+	}
+	
+	pResult->m_pArgsStart = pStr;
+	return 0;
+}
+
+int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
+{
+	char Command;
+	char *pStr;
+	int Optional = 0;
+	int Error = 0;
+	
+	pStr = pResult->m_pArgsStart;
+
+	while(1)	
+	{
+		// fetch command
+		Command = *pFormat;
+		pFormat++;
+		
+		if(!Command)
+			break;
+		
+		if(Command == '?')
+			Optional = 1;
+		else
+		{
+			pStr = SkipBlanks(pStr);
+		
+			if(!(*pStr)) // error, non optional command needs value
+			{
+				if(!Optional)
+					Error = 1;
+				break;
+			}
+			
+			// add token
+			if(*pStr == '"')
+			{
+				char *pDst;
+				pStr++;
+				pResult->AddArgument(pStr);
+				
+				pDst = pStr; // we might have to process escape data
+				while(1)
+				{
+					if(pStr[0] == '"')
+						break;
+					else if(pStr[0] == '\\')
+					{
+						if(pStr[1] == '\\')
+							pStr++; // skip due to escape
+						else if(pStr[1] == '"')
+							pStr++; // skip due to escape
+					}
+					else if(pStr[0] == 0)
+						return 1; // return error
+						
+					*pDst = *pStr;
+					pDst++;
+					pStr++;
+				}
+				
+				// write null termination
+				*pDst = 0;
+
+				
+				pStr++;
+			}
+			else
+			{
+				pResult->AddArgument(pStr);
+				
+				if(Command == 'r') // rest of the string
+					break;
+				else if(Command == 'i') // validate int
+					pStr = SkipToBlank(pStr);
+				else if(Command == 'f') // validate float
+					pStr = SkipToBlank(pStr);
+				else if(Command == 's') // validate string
+					pStr = SkipToBlank(pStr);
+
+				if(pStr[0] != 0) // check for end of string
+				{
+					pStr[0] = 0;
+					pStr++;
+				}
+			}
+		}
+	}
+
+	return Error;
+}
+
+void CConsole::RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData)
+{
+	m_pfnPrintCallback = pfnPrintCallback;
+	m_pPrintCallbackUserdata = pUserData;
+}
+
+void CConsole::Print(const char *pStr)
+{
+	dbg_msg("console" ,"%s", pStr);
+	if (m_pfnPrintCallback)
+		m_pfnPrintCallback(pStr, m_pPrintCallbackUserdata);
+}
+
+void CConsole::ExecuteLineStroked(int Stroke, const char *pStr)
+{
+	CResult Result;
+	
+	char aStrokeStr[2] = {'0', 0};
+	if(Stroke)
+		aStrokeStr[0] = '1';
+
+	while(pStr && *pStr)
+	{
+		const char *pEnd = pStr;
+		const char *pNextPart = 0;
+		int InString = 0;
+		
+		while(*pEnd)
+		{
+			if(*pEnd == '"')
+				InString ^= 1;
+			else if(*pEnd == '\\') // escape sequences
+			{
+				if(pEnd[1] == '"')
+					pEnd++;
+			}
+			else if(!InString)
+			{
+				if(*pEnd == ';')  // command separator
+				{
+					pNextPart = pEnd+1;
+					break;
+				}
+				else if(*pEnd == '#')  // comment, no need to do anything more
+					break;
+			}
+			
+			pEnd++;
+		}
+		
+		if(ParseStart(&Result, pStr, (pEnd-pStr) + 1) != 0)
+			return;
+
+		CCommand *pCommand = FindCommand(Result.m_pCommand);
+
+		if(pCommand)
+		{
+			int IsStrokeCommand = 0;
+			if(Result.m_pCommand[0] == '+')
+			{
+				// insert the stroke direction token
+				Result.AddArgument(aStrokeStr);
+				IsStrokeCommand = 1;
+			}
+			
+			if(Stroke || IsStrokeCommand)
+			{
+				if(ParseArgs(&Result, pCommand->m_pParams))
+				{
+					char aBuf[256];
+					str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
+					Print(aBuf);
+				}
+				else
+					pCommand->m_pfnCallback(&Result, pCommand->m_pUserData);
+			}
+		}
+		else
+		{
+			char aBuf[256];
+			str_format(aBuf, sizeof(aBuf), "No such command: %s.", Result.m_pCommand);
+			Print(aBuf);
+		}
+		
+		pStr = pNextPart;
+	}
+}
+
+void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser)
+{
+	CCommand *pCommand;
+	for(pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
+	{
+		if(pCommand->m_Flags&FlagMask)
+		{
+			if(str_find_nocase(pCommand->m_pName, pStr))
+				pfnCallback(pCommand->m_pName, pUser);
+		}
+	}	
+}
+
+// TODO: this should regard the commands flag
+CConsole::CCommand *CConsole::FindCommand(const char *pName)
+{
+	CCommand *pCommand;
+	for (pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
+	{
+		if(str_comp_nocase(pCommand->m_pName, pName) == 0)
+			return pCommand;
+	}	
+	
+	return 0x0;
+}
+
+void CConsole::ExecuteLine(const char *pStr)
+{
+	CConsole::ExecuteLineStroked(1, pStr);
+}
+
+
+void CConsole::ExecuteFile(const char *pFilename)
+{
+	// make sure that this isn't being executed already
+	for(CExecFile *pCur = m_pFirstExec; pCur; pCur = pCur->m_pPrev)
+		if(str_comp(pFilename, pCur->m_pFilename) == 0)
+			return;
+
+	if(!m_pStorage)
+		m_pStorage = Kernel()->RequestInterface<IStorage>();
+	if(!m_pStorage)
+		return;
+		
+	// push this one to the stack
+	CExecFile ThisFile;
+	CExecFile *pPrev = m_pFirstExec;
+	ThisFile.m_pFilename = pFilename;
+	ThisFile.m_pPrev = m_pFirstExec;
+	m_pFirstExec = &ThisFile;
+
+	// exec the file
+	IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ);
+	
+	if(File)
+	{
+		char *pLine;
+		CLineReader lr;
+		
+		dbg_msg("console", "executing '%s'", pFilename);
+		lr.Init(File);
+
+		while((pLine = lr.Get()))
+			ExecuteLine(pLine);
+
+		io_close(File);
+	}
+	else
+		dbg_msg("console", "failed to open '%s'", pFilename);
+	
+	m_pFirstExec = pPrev;
+}
+
+void CConsole::Con_Echo(IResult *pResult, void *pUserData)
+{
+	((CConsole*)pUserData)->Print(pResult->GetString(0));
+}
+
+void CConsole::Con_Exec(IResult *pResult, void *pUserData)
+{
+	((CConsole*)pUserData)->ExecuteFile(pResult->GetString(0));
+}
+
+struct CIntVariableData
+{
+	IConsole *m_pConsole;
+	int *m_pVariable;
+	int m_Min;
+	int m_Max;
+};
+
+struct CStrVariableData
+{
+	IConsole *m_pConsole;
+	char *m_pStr;
+	int m_MaxSize;
+};
+
+static void IntVariableCommand(IConsole::IResult *pResult, void *pUserData)
+{
+	CIntVariableData *pData = (CIntVariableData *)pUserData;
+
+	if(pResult->NumArguments())
+	{
+		int Val = pResult->GetInteger(0);
+		
+		// do clamping
+		if(pData->m_Min != pData->m_Max)
+		{
+			if (Val < pData->m_Min)
+				Val = pData->m_Min;
+			if (pData->m_Max != 0 && Val > pData->m_Max)
+				Val = pData->m_Max;
+		}
+
+		*(pData->m_pVariable) = Val;
+	}
+	else
+	{
+		char aBuf[1024];
+		str_format(aBuf, sizeof(aBuf), "Value: %d", *(pData->m_pVariable));
+		pData->m_pConsole->Print(aBuf);
+	}
+}
+
+static void StrVariableCommand(IConsole::IResult *pResult, void *pUserData)
+{
+	CStrVariableData *pData = (CStrVariableData *)pUserData;
+
+	if(pResult->NumArguments())
+		str_copy(pData->m_pStr, pResult->GetString(0), pData->m_MaxSize);
+	else
+	{
+		char aBuf[1024];
+		str_format(aBuf, sizeof(aBuf), "Value: %s", pData->m_pStr);
+		pData->m_pConsole->Print(aBuf);
+	}
+}
+
+CConsole::CConsole()
+{
+	m_pFirstCommand = 0;
+	m_pFirstExec = 0;
+	m_pPrintCallbackUserdata = 0;
+	m_pfnPrintCallback = 0;
+	
+	m_pStorage = 0;
+	
+	// register some basic commands
+	Register("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Echo, this, "Echo the text");
+	Register("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Exec, this, "Execute the specified file");
+	
+	// TODO: this should disappear
+	#define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Flags,Desc) \
+	{ \
+		static CIntVariableData Data = { this, &g_Config.m_##Name, Min, Max }; \
+		Register(#ScriptName, "?i", Flags, IntVariableCommand, &Data, Desc); \
+	}
+	
+	#define MACRO_CONFIG_STR(Name,ScriptName,Len,Def,Flags,Desc) \
+	{ \
+		static CStrVariableData Data = { this, g_Config.m_##Name, Len }; \
+		Register(#ScriptName, "?r", Flags, StrVariableCommand, &Data, Desc); \
+	}
+
+	#include "config_variables.h" 
+
+	#undef MACRO_CONFIG_INT 
+	#undef MACRO_CONFIG_STR 	
+}
+
+void CConsole::ParseArguments(int NumArgs, const char **ppArguments)
+{
+	for(int i = 0; i < NumArgs; i++)
+	{
+		// check for scripts to execute
+		if(ppArguments[i][0] == '-' && ppArguments[i][1] == 'f' && ppArguments[i][2] == 0 && NumArgs - i > 1)
+		{
+			ExecuteFile(ppArguments[i+1]);
+			i++;
+		}
+		else
+		{
+			// search arguments for overrides
+			ExecuteLine(ppArguments[i]);
+		}
+	}
+}
+
+void CConsole::Register(const char *pName, const char *pParams, 
+	int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp)
+{
+	CCommand *pCommand = (CCommand *)mem_alloc(sizeof(CCommand), sizeof(void*));
+	pCommand->m_pfnCallback = pfnFunc;
+	pCommand->m_pUserData = pUser;
+	pCommand->m_pHelp = pHelp;
+	pCommand->m_pName = pName;
+	pCommand->m_pParams = pParams;
+	pCommand->m_Flags = Flags;
+	
+	
+	pCommand->m_pNext = m_pFirstCommand;
+	m_pFirstCommand = pCommand;
+}
+
+void CConsole::Con_Chain(IResult *pResult, void *pUserData)
+{
+	CChain *pInfo = (CChain *)pUserData;
+	pInfo->m_pfnChainCallback(pResult, pInfo->m_pUserData, pInfo->m_pfnCallback, pInfo->m_pCallbackUserData);
+}
+
+void CConsole::Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser)
+{
+	CCommand *pCommand = FindCommand(pName);
+	
+	if(!pCommand)
+	{
+		dbg_msg("console", "failed to chain '%s'", pName);
+		return;
+	}
+	
+	CChain *pChainInfo = (CChain *)mem_alloc(sizeof(CChain), sizeof(void*));
+
+	// store info
+	pChainInfo->m_pfnChainCallback = pfnChainFunc;
+	pChainInfo->m_pUserData = pUser;
+	pChainInfo->m_pfnCallback = pCommand->m_pfnCallback;
+	pChainInfo->m_pCallbackUserData = pCommand->m_pUserData;
+	
+	// chain
+	pCommand->m_pfnCallback = Con_Chain;
+	pCommand->m_pUserData = pChainInfo;
+}
+
+
+IConsole::CCommandInfo *CConsole::GetCommandInfo(const char *pName)
+{
+	return FindCommand(pName);
+}
+
+
+extern IConsole *CreateConsole() { return new CConsole(); }
diff --git a/src/engine/shared/console.h b/src/engine/shared/console.h
new file mode 100644
index 00000000..93d23547
--- /dev/null
+++ b/src/engine/shared/console.h
@@ -0,0 +1,96 @@
+#ifndef ENGINE_SHARED_CONSOLE_H
+#define ENGINE_SHARED_CONSOLE_H
+
+#include <engine/console.h>
+
+class CConsole : public IConsole
+{
+	class CCommand : public CCommandInfo
+	{
+	public:
+		CCommand *m_pNext;
+		int m_Flags;
+		FCommandCallback m_pfnCallback;
+		void *m_pUserData;
+	};
+		
+
+	class CChain
+	{
+	public:
+		FChainCommandCallback m_pfnChainCallback;
+		FCommandCallback m_pfnCallback;
+		void *m_pCallbackUserData;
+		void *m_pUserData;
+	};	
+	
+	CCommand *m_pFirstCommand;
+
+	class CExecFile
+	{
+	public:
+		const char *m_pFilename;
+		struct CExecFile *m_pPrev;
+	};
+	
+	CExecFile *m_pFirstExec;
+	class IStorage *m_pStorage;
+
+	static void Con_Chain(IResult *pResult, void *pUserData);
+	static void Con_Echo(IResult *pResult, void *pUserData);
+	static void Con_Exec(IResult *pResult, void *pUserData);
+
+	void ExecuteFileRecurse(const char *pFilename);
+	void ExecuteLineStroked(int Stroke, const char *pStr);
+	
+	FPrintCallback m_pfnPrintCallback;
+	void *m_pPrintCallbackUserdata;
+
+	enum
+	{
+		CONSOLE_MAX_STR_LENGTH  = 1024,
+		MAX_PARTS = (CONSOLE_MAX_STR_LENGTH+1)/2
+	};
+	
+	class CResult : public IResult
+	{
+	public:
+		char m_aStringStorage[CONSOLE_MAX_STR_LENGTH+1];
+		char *m_pArgsStart;
+		
+		const char *m_pCommand;
+		const char *m_apArgs[MAX_PARTS];
+		
+		void AddArgument(const char *pArg)
+		{
+			m_apArgs[m_NumArgs++] = pArg;
+		}
+
+		virtual const char *GetString(unsigned Index);
+		virtual int GetInteger(unsigned Index);
+		virtual float GetFloat(unsigned Index);
+	};
+	
+	int ParseStart(CResult *pResult, const char *pString, int Length);
+	int ParseArgs(CResult *pResult, const char *pFormat);
+
+	CCommand *FindCommand(const char *pName);
+
+public:
+	CConsole();
+
+	virtual CCommandInfo *GetCommandInfo(const char *pName);
+	virtual void PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser) ;
+
+	virtual void ParseArguments(int NumArgs, const char **ppArguments);
+	virtual void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp);
+	virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser);
+	
+	virtual void ExecuteLine(const char *pStr);
+	virtual void ExecuteFile(const char *pFilename);
+
+	virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData);
+	virtual void Print(const char *pStr);
+};
+
+#endif
diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp
new file mode 100644
index 00000000..dcc32ef2
--- /dev/null
+++ b/src/engine/shared/datafile.cpp
@@ -0,0 +1,643 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+#include <base/system.h>
+#include <engine/storage.h>
+#include "datafile.h"
+#include "engine.h"
+#include <zlib.h>
+
+static const int DEBUG=0;
+
+struct CDatafileItemType
+{
+	int m_Type;
+	int m_Start;
+	int m_Num;
+} ;
+
+struct CDatafileItem
+{
+	int m_TypeAndId;
+	int m_Size;
+};
+
+struct CDatafileHeader
+{
+	char m_aId[4];
+	int m_Version;
+	int m_Size;
+	int m_Swaplen;
+	int m_NumItemTypes;
+	int m_NumItems;
+	int m_NumRawData;
+	int m_ItemSize;
+	int m_DataSize;
+};
+
+struct CDatafileData
+{
+	int m_NumItemTypes;
+	int m_NumItems;
+	int m_NumRawData;
+	int m_ItemSize;
+	int m_DataSize;
+	char m_aStart[4];
+};
+
+struct CDatafileInfo
+{
+	CDatafileItemType *m_pItemTypes;
+	int *m_pItemOffsets;
+	int *m_pDataOffsets;
+	int *m_pDataSizes;
+
+	char *m_pItemStart;
+	char *m_pDataStart;
+};
+
+struct CDatafile
+{
+	IOHANDLE m_File;
+	unsigned m_Crc;
+	CDatafileInfo m_Info;
+	CDatafileHeader m_Header;
+	int m_DataStartOffset;
+	char **m_ppDataPtrs;
+	char *m_pData;
+};
+
+bool CDataFileReader::Open(class IStorage *pStorage, const char *pFilename)
+{
+	dbg_msg("datafile", "loading. filename='%s'", pFilename);
+
+	IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ);
+	if(!File)
+	{
+		dbg_msg("datafile", "could not open '%s'", pFilename);
+		return false;
+	}	
+	
+	
+	// take the CRC of the file and store it
+	unsigned Crc = 0;
+	{
+		enum
+		{
+			BUFFER_SIZE = 64*1024
+		};
+		
+		unsigned char aBuffer[BUFFER_SIZE];
+		
+		while(1)
+		{
+			unsigned Bytes = io_read(File, aBuffer, BUFFER_SIZE);
+			if(Bytes <= 0)
+				break;
+			Crc = crc32(Crc, aBuffer, Bytes); // ignore_convention
+		}
+		
+		io_seek(File, 0, IOSEEK_START);
+	}
+	
+	
+	// TODO: change this header
+	CDatafileHeader Header;
+	io_read(File, &Header, sizeof(Header));
+	if(Header.m_aId[0] != 'A' || Header.m_aId[1] != 'T' || Header.m_aId[2] != 'A' || Header.m_aId[3] != 'D')
+	{
+		if(Header.m_aId[0] != 'D' || Header.m_aId[1] != 'A' || Header.m_aId[2] != 'T' || Header.m_aId[3] != 'A')
+		{
+			dbg_msg("datafile", "wrong signature. %x %x %x %x", Header.m_aId[0], Header.m_aId[1], Header.m_aId[2], Header.m_aId[3]);
+			return 0;
+		}
+	}
+
+#if defined(CONF_ARCH_ENDIAN_BIG)
+	swap_endian(&Header, sizeof(int), sizeof(Header)/sizeof(int));	
+#endif
+	if(Header.m_Version != 3 && Header.m_Version != 4)
+	{
+		dbg_msg("datafile", "wrong version. version=%x", Header.m_Version);
+		return 0;
+	}
+	
+	// read in the rest except the data
+	unsigned Size = 0;
+	Size += Header.m_NumItemTypes*sizeof(CDatafileItemType);
+	Size += (Header.m_NumItems+Header.m_NumRawData)*sizeof(int);
+	if(Header.m_Version == 4)
+		Size += Header.m_NumRawData*sizeof(int); // v4 has uncompressed data sizes aswell
+	Size += Header.m_ItemSize;
+	
+	unsigned AllocSize = Size;
+	AllocSize += sizeof(CDatafile); // add space for info structure
+	AllocSize += Header.m_NumRawData*sizeof(void*); // add space for data pointers
+
+	m_pDataFile = (CDatafile*)mem_alloc(AllocSize, 1);
+	m_pDataFile->m_Header = Header;
+	m_pDataFile->m_DataStartOffset = sizeof(CDatafileHeader) + Size;
+	m_pDataFile->m_ppDataPtrs = (char**)(m_pDataFile+1);
+	m_pDataFile->m_pData = (char *)(m_pDataFile+1)+Header.m_NumRawData*sizeof(char *);
+	m_pDataFile->m_File = File;
+	m_pDataFile->m_Crc = Crc;
+	
+	// clear the data pointers
+	mem_zero(m_pDataFile->m_ppDataPtrs, Header.m_NumRawData*sizeof(void*));
+	
+	// read types, offsets, sizes and item data
+	unsigned ReadSize = io_read(File, m_pDataFile->m_pData, Size);
+	if(ReadSize != Size)
+	{
+		mem_free(m_pDataFile);
+		m_pDataFile = 0;
+		dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", Size, ReadSize);
+		return false;
+	}
+
+#if defined(CONF_ARCH_ENDIAN_BIG)
+	swap_endian(m_pDataFile->m_pData, sizeof(int), Header.Swaplen / sizeof(int));
+#endif
+
+	//if(DEBUG)
+	{
+		dbg_msg("datafile", "allocsize=%d", AllocSize);
+		dbg_msg("datafile", "readsize=%d", ReadSize);
+		dbg_msg("datafile", "swaplen=%d", Header.m_Swaplen);
+		dbg_msg("datafile", "item_size=%d", m_pDataFile->m_Header.m_ItemSize);
+	}
+	
+	m_pDataFile->m_Info.m_pItemTypes = (CDatafileItemType *)m_pDataFile->m_pData;
+	m_pDataFile->m_Info.m_pItemOffsets = (int *)&m_pDataFile->m_Info.m_pItemTypes[m_pDataFile->m_Header.m_NumItemTypes];
+	m_pDataFile->m_Info.m_pDataOffsets = (int *)&m_pDataFile->m_Info.m_pItemOffsets[m_pDataFile->m_Header.m_NumItems];
+	m_pDataFile->m_Info.m_pDataSizes = (int *)&m_pDataFile->m_Info.m_pDataOffsets[m_pDataFile->m_Header.m_NumRawData];
+	
+	if(Header.m_Version == 4)
+		m_pDataFile->m_Info.m_pItemStart = (char *)&m_pDataFile->m_Info.m_pDataSizes[m_pDataFile->m_Header.m_NumRawData];
+	else
+		m_pDataFile->m_Info.m_pItemStart = (char *)&m_pDataFile->m_Info.m_pDataOffsets[m_pDataFile->m_Header.m_NumRawData];
+	m_pDataFile->m_Info.m_pDataStart = m_pDataFile->m_Info.m_pItemStart + m_pDataFile->m_Header.m_ItemSize;
+
+	dbg_msg("datafile", "loading done. datafile='%s'", pFilename);
+
+	if(DEBUG)
+	{
+		/*
+		for(int i = 0; i < m_pDataFile->data.num_raw_data; i++)
+		{
+			void *p = datafile_get_data(df, i);
+			dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&m_pDataFile->data)), size);
+		}
+			
+		for(int i = 0; i < datafile_num_items(df); i++)
+		{
+			int type, id;
+			void *data = datafile_get_item(df, i, &type, &id);
+			dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, m_pDataFile->info.item_offsets[i]);
+			int *idata = (int*)data;
+			for(int k = 0; k < 3; k++)
+				dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]);
+		}
+
+		for(int i = 0; i < m_pDataFile->data.num_m_aItemTypes; i++)
+		{
+			dbg_msg("map", "\t%d: type=%x start=%d num=%d", i,
+				m_pDataFile->info.m_aItemTypes[i].type,
+				m_pDataFile->info.m_aItemTypes[i].start,
+				m_pDataFile->info.m_aItemTypes[i].num);
+			for(int k = 0; k < m_pDataFile->info.m_aItemTypes[i].num; k++)
+			{
+				int type, id;
+				datafile_get_item(df, m_pDataFile->info.m_aItemTypes[i].start+k, &type, &id);
+				if(type != m_pDataFile->info.m_aItemTypes[i].type)
+					dbg_msg("map", "\tERROR");
+			}
+		}
+		*/
+	}
+		
+	return true;
+}
+
+int CDataFileReader::NumData()
+{
+	if(!m_pDataFile) { return 0; }
+	return m_pDataFile->m_Header.m_NumRawData;
+}
+
+// always returns the size in the file
+int CDataFileReader::GetDataSize(int Index)
+{
+	if(!m_pDataFile) { return 0; }
+	
+	if(Index == m_pDataFile->m_Header.m_NumRawData-1)
+		return m_pDataFile->m_Header.m_DataSize-m_pDataFile->m_Info.m_pDataOffsets[Index];
+	return  m_pDataFile->m_Info.m_pDataOffsets[Index+1]-m_pDataFile->m_Info.m_pDataOffsets[Index];
+}
+
+void *CDataFileReader::GetDataImpl(int Index, int Swap)
+{
+	if(!m_pDataFile) { return 0; }
+	
+	// load it if needed
+	if(!m_pDataFile->m_ppDataPtrs[Index])
+	{
+		// fetch the data size
+		int DataSize = GetDataSize(Index);
+		int SwapSize = DataSize;
+		
+		if(m_pDataFile->m_Header.m_Version == 4)
+		{
+			// v4 has compressed data
+			void *pTemp = (char *)mem_alloc(DataSize, 1);
+			unsigned long UncompressedSize = m_pDataFile->m_Info.m_pDataSizes[Index];
+			unsigned long s;
+
+			dbg_msg("datafile", "loading data index=%d size=%d uncompressed=%d", Index, DataSize, UncompressedSize);
+			m_pDataFile->m_ppDataPtrs[Index] = (char *)mem_alloc(UncompressedSize, 1);
+			
+			// read the compressed data
+			io_seek(m_pDataFile->m_File, m_pDataFile->m_DataStartOffset+m_pDataFile->m_Info.m_pDataOffsets[Index], IOSEEK_START);
+			io_read(m_pDataFile->m_File, pTemp, DataSize);
+
+			// decompress the data, TODO: check for errors
+			s = UncompressedSize;
+			uncompress((Bytef*)m_pDataFile->m_ppDataPtrs[Index], &s, (Bytef*)pTemp, DataSize); // ignore_convention
+			SwapSize = s;
+
+			// clean up the temporary buffers
+			mem_free(pTemp);
+		}
+		else
+		{
+			// load the data
+			dbg_msg("datafile", "loading data index=%d size=%d", Index, DataSize);
+			m_pDataFile->m_ppDataPtrs[Index] = (char *)mem_alloc(DataSize, 1);
+			io_seek(m_pDataFile->m_File, m_pDataFile->m_DataStartOffset+m_pDataFile->m_Info.m_pDataOffsets[Index], IOSEEK_START);
+			io_read(m_pDataFile->m_File, m_pDataFile->m_ppDataPtrs[Index], DataSize);
+		}
+
+#if defined(CONF_ARCH_ENDIAN_BIG)
+		if(Swap && SwapSize)
+			swap_endian(m_pDataFile->m_ppDataPtrs[Index], sizeof(int), SwapSize/sizeof(int));
+#endif
+	}
+	
+	return m_pDataFile->m_ppDataPtrs[Index];
+}
+
+void *CDataFileReader::GetData(int Index)
+{
+	return GetDataImpl(Index, 0);
+}
+
+void *CDataFileReader::GetDataSwapped(int Index)
+{
+	return GetDataImpl(Index, 1);
+}
+
+void CDataFileReader::UnloadData(int Index)
+{
+	if(Index < 0)
+		return;
+		
+	//
+	mem_free(m_pDataFile->m_ppDataPtrs[Index]);
+	m_pDataFile->m_ppDataPtrs[Index] = 0x0;
+}
+
+int CDataFileReader::GetItemSize(int Index)
+{
+	if(!m_pDataFile) { return 0; }
+	if(Index == m_pDataFile->m_Header.m_NumItems-1)
+		return m_pDataFile->m_Header.m_ItemSize-m_pDataFile->m_Info.m_pItemOffsets[Index];
+	return  m_pDataFile->m_Info.m_pItemOffsets[Index+1]-m_pDataFile->m_Info.m_pItemOffsets[Index];
+}
+
+void *CDataFileReader::GetItem(int Index, int *pType, int *pId)
+{
+	if(!m_pDataFile) { if(pType) *pType = 0; if(pId) *pId = 0; return 0; }
+	
+	CDatafileItem *i = (CDatafileItem *)(m_pDataFile->m_Info.m_pItemStart+m_pDataFile->m_Info.m_pItemOffsets[Index]);
+	if(pType)
+		*pType = (i->m_TypeAndId>>16)&0xffff; // remove sign extention
+	if(pId)
+		*pId = i->m_TypeAndId&0xffff;
+	return (void *)(i+1);
+}
+
+void CDataFileReader::GetType(int Type, int *pStart, int *pNum)
+{
+	*pStart = 0;
+	*pNum = 0;
+
+	if(!m_pDataFile)
+		return;
+	
+	for(int i = 0; i < m_pDataFile->m_Header.m_NumItemTypes; i++)
+	{
+		if(m_pDataFile->m_Info.m_pItemTypes[i].m_Type == Type)
+		{
+			*pStart = m_pDataFile->m_Info.m_pItemTypes[i].m_Start;
+			*pNum = m_pDataFile->m_Info.m_pItemTypes[i].m_Num;
+			return;
+		}
+	}
+}
+
+void *CDataFileReader::FindItem(int Type, int Id)
+{
+	if(!m_pDataFile) return 0;
+	
+	int Start, Num;
+	GetType(Type, &Start, &Num);
+	for(int i = 0; i < Num; i++)
+	{
+		int ItemId;
+		void *pItem = GetItem(Start+i,0, &ItemId);
+		if(Id == ItemId)
+			return pItem;
+	}
+	return 0;
+}
+
+int CDataFileReader::NumItems()
+{
+	if(!m_pDataFile) return 0;
+	return m_pDataFile->m_Header.m_NumItems;
+}
+
+bool CDataFileReader::Close()
+{
+	if(!m_pDataFile)
+		return true;
+	
+	// free the data that is loaded
+	int i;
+	for(i = 0; i < m_pDataFile->m_Header.m_NumRawData; i++)
+		mem_free(m_pDataFile->m_ppDataPtrs[i]);
+	
+	io_close(m_pDataFile->m_File);
+	mem_free(m_pDataFile);
+	m_pDataFile = 0;
+	return true;
+}
+
+unsigned CDataFileReader::Crc()
+{
+	if(!m_pDataFile) return -1;
+	return m_pDataFile->m_Crc;
+}
+
+bool CDataFileWriter::Open(class IStorage *pStorage, const char *pFilename)
+{
+	int i;
+	//DATAFILE_OUT *df = (DATAFILE_OUT*)mem_alloc(sizeof(DATAFILE_OUT), 1);
+	m_File = pStorage->OpenFile(pFilename, IOFLAG_WRITE);
+	if(!m_File)
+		return false;
+	
+	m_NumItems = 0;
+	m_NumDatas = 0;
+	m_NumItemTypes = 0;
+	mem_zero(&m_aItemTypes, sizeof(m_aItemTypes));
+
+	for(i = 0; i < 0xffff; i++)
+	{
+		m_aItemTypes[i].m_First = -1;
+		m_aItemTypes[i].m_Last = -1;
+	}
+	
+	return true;
+}
+
+int CDataFileWriter::AddItem(int Type, int Id, int Size, void *pData)
+{
+	m_aItems[m_NumItems].m_Type = Type;
+	m_aItems[m_NumItems].m_Id = Id;
+	m_aItems[m_NumItems].m_Size = Size;
+	
+	/*
+	dbg_msg("datafile", "added item type=%d id=%d size=%d", type, id, size);
+	int i;
+	for(i = 0; i < size/4; i++)
+		dbg_msg("datafile", "\t%d: %08x %d", i, ((int*)data)[i], ((int*)data)[i]);
+	*/
+	
+	// copy data
+	m_aItems[m_NumItems].m_pData = mem_alloc(Size, 1);
+	mem_copy(m_aItems[m_NumItems].m_pData, pData, Size);
+
+	if(!m_aItemTypes[Type].m_Num) // count item types
+		m_NumItemTypes++;
+
+	// link
+	m_aItems[m_NumItems].m_Prev = m_aItemTypes[Type].m_Last;
+	m_aItems[m_NumItems].m_Next = -1;
+	
+	if(m_aItemTypes[Type].m_Last != -1)
+		m_aItems[m_aItemTypes[Type].m_Last].m_Next = m_NumItems;
+	m_aItemTypes[Type].m_Last = m_NumItems;
+	
+	if(m_aItemTypes[Type].m_First == -1)
+		m_aItemTypes[Type].m_First = m_NumItems;
+	
+	m_aItemTypes[Type].m_Num++;
+		
+	m_NumItems++;
+	return m_NumItems-1;
+}
+
+int CDataFileWriter::AddData(int Size, void *pData)
+{
+	CDataInfo *pInfo = &m_aDatas[m_NumDatas];
+	unsigned long s = compressBound(Size);
+	void *pCompData = mem_alloc(s, 1); // temporary buffer that we use duing compression
+
+	int Result = compress((Bytef*)pCompData, &s, (Bytef*)pData, Size); // ignore_convention
+	if(Result != Z_OK)
+	{
+		dbg_msg("datafile", "compression error %d", Result);
+		dbg_assert(0, "zlib error");
+	}
+		
+	pInfo->m_UncompressedSize = Size;
+	pInfo->m_CompressedSize = (int)s;
+	pInfo->m_pCompressedData = mem_alloc(pInfo->m_CompressedSize, 1);
+	mem_copy(pInfo->m_pCompressedData, pCompData, pInfo->m_CompressedSize);
+	mem_free(pCompData);
+
+	m_NumDatas++;
+	return m_NumDatas-1;
+}
+
+int CDataFileWriter::AddDataSwapped(int Size, void *pData)
+{
+#if defined(CONF_ARCH_ENDIAN_BIG)
+	void *pSwapped = mem_alloc(Size, 1); // temporary buffer that we use duing compression
+	int Index;
+	mem_copy(pSwapped, pData, Size);
+	swap_endian(&pSwapped, sizeof(int), Size/sizeof(int));
+	Index = AddData(Size, Swapped);
+	mem_free(pSwapped);
+	return Index;
+#else
+	return AddData(Size, pData);
+#endif
+}
+
+
+int CDataFileWriter::Finish()
+{
+	int ItemSize = 0;
+	int TypesSize, HeaderSize, OffsetSize, FileSize, SwapSize;
+	int DataSize = 0;
+	CDatafileHeader Header;
+
+	// we should now write this file!
+	if(DEBUG)
+		dbg_msg("datafile", "writing");
+
+	// calculate sizes
+	for(int i = 0; i < m_NumItems; i++)
+	{
+		if(DEBUG)
+			dbg_msg("datafile", "item=%d size=%d (%d)", i, m_aItems[i].m_Size, m_aItems[i].m_Size+sizeof(CDatafileItem));
+		ItemSize += m_aItems[i].m_Size + sizeof(CDatafileItem);
+	}
+	
+	
+	for(int i = 0; i < m_NumDatas; i++)
+		DataSize += m_aDatas[i].m_CompressedSize;
+	
+	// calculate the complete size
+	TypesSize = m_NumItemTypes*sizeof(CDatafileItemType);
+	HeaderSize = sizeof(CDatafileHeader);
+	OffsetSize = m_NumItems*sizeof(int) + m_NumDatas*sizeof(int);
+	FileSize = HeaderSize + TypesSize + OffsetSize + ItemSize + DataSize;
+	SwapSize = FileSize - DataSize;
+	
+	(void)SwapSize;
+	
+	if(DEBUG)
+		dbg_msg("datafile", "num_m_aItemTypes=%d TypesSize=%d m_aItemsize=%d DataSize=%d", m_NumItemTypes, TypesSize, ItemSize, DataSize);
+	
+	// construct Header
+	{
+		Header.m_aId[0] = 'D';
+		Header.m_aId[1] = 'A';
+		Header.m_aId[2] = 'T';
+		Header.m_aId[3] = 'A';
+		Header.m_Version = 4;
+		Header.m_Size = FileSize - 16;
+		Header.m_Swaplen = SwapSize - 16;
+		Header.m_NumItemTypes = m_NumItemTypes;
+		Header.m_NumItems = m_NumItems;
+		Header.m_NumRawData = m_NumDatas;
+		Header.m_ItemSize = ItemSize;
+		Header.m_DataSize = DataSize;
+		
+		// TODO: apply swapping
+		// write Header
+		if(DEBUG)
+			dbg_msg("datafile", "HeaderSize=%d", sizeof(Header));
+		io_write(m_File, &Header, sizeof(Header));
+	}
+	
+	// write types
+	for(int i = 0, Count = 0; i < 0xffff; i++)
+	{
+		if(m_aItemTypes[i].m_Num)
+		{
+			// write info
+			CDatafileItemType Info;
+			Info.m_Type = i;
+			Info.m_Start = Count;
+			Info.m_Num = m_aItemTypes[i].m_Num;
+			if(DEBUG)
+				dbg_msg("datafile", "writing type=%x start=%d num=%d", Info.m_Type, Info.m_Start, Info.m_Num);
+			io_write(m_File, &Info, sizeof(Info));
+			Count += m_aItemTypes[i].m_Num;
+		}
+	}
+	
+	// write item offsets
+	for(int i = 0, Offset = 0; i < 0xffff; i++)
+	{
+		if(m_aItemTypes[i].m_Num)
+		{
+			// write all m_aItems in of this type
+			int k = m_aItemTypes[i].m_First;
+			while(k != -1)
+			{
+				if(DEBUG)
+					dbg_msg("datafile", "writing item offset num=%d offset=%d", k, Offset);
+				io_write(m_File, &Offset, sizeof(Offset));
+				Offset += m_aItems[k].m_Size + sizeof(CDatafileItem);
+				
+				// next
+				k = m_aItems[k].m_Next;
+			}
+		}
+	}
+	
+	// write data offsets
+	for(int i = 0, Offset = 0; i < m_NumDatas; i++)
+	{
+		if(DEBUG)
+			dbg_msg("datafile", "writing data offset num=%d offset=%d", i, Offset);
+		io_write(m_File, &Offset, sizeof(Offset));
+		Offset += m_aDatas[i].m_CompressedSize;
+	}
+
+	// write data uncompressed sizes
+	for(int i = 0; i < m_NumDatas; i++)
+	{
+		/*
+		if(DEBUG)
+			dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset);
+		*/
+		io_write(m_File, &m_aDatas[i].m_UncompressedSize, sizeof(int));
+	}
+	
+	// write m_aItems
+	for(int i = 0; i < 0xffff; i++)
+	{
+		if(m_aItemTypes[i].m_Num)
+		{
+			// write all m_aItems in of this type
+			int k = m_aItemTypes[i].m_First;
+			while(k != -1)
+			{
+				CDatafileItem Item;
+				Item.m_TypeAndId = (i<<16)|m_aItems[k].m_Id;
+				Item.m_Size = m_aItems[k].m_Size;
+				if(DEBUG)
+					dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, m_aItems[k].m_Id, m_aItems[k].m_Size);
+				
+				io_write(m_File, &Item, sizeof(Item));
+				io_write(m_File, m_aItems[k].m_pData, m_aItems[k].m_Size);
+				
+				// next
+				k = m_aItems[k].m_Next;
+			}
+		}
+	}
+	
+	// write data
+	for(int i = 0; i < m_NumDatas; i++)
+	{
+		if(DEBUG)
+			dbg_msg("datafile", "writing data id=%d size=%d", i, m_aDatas[i].m_CompressedSize);
+		io_write(m_File, m_aDatas[i].m_pCompressedData, m_aDatas[i].m_CompressedSize);
+	}
+
+	// free data
+	for(int i = 0; i < m_NumItems; i++)
+		mem_free(m_aItems[i].m_pData);
+
+	
+	io_close(m_File);
+	
+	if(DEBUG)
+		dbg_msg("datafile", "done");
+	return 0;
+}
diff --git a/src/engine/shared/datafile.h b/src/engine/shared/datafile.h
new file mode 100644
index 00000000..eddce611
--- /dev/null
+++ b/src/engine/shared/datafile.h
@@ -0,0 +1,77 @@
+#ifndef ENGINE_SHARED_DATAFILE_H
+#define ENGINE_SHARED_DATAFILE_H
+
+// raw datafile access
+class CDataFileReader
+{
+	class CDatafile *m_pDataFile;
+	void *GetDataImpl(int Index, int Swap);
+public:
+	CDataFileReader() : m_pDataFile(0) {}
+	~CDataFileReader() { Close(); }
+	
+	bool IsOpen() const { return m_pDataFile != 0; }
+	
+	bool Open(class IStorage *pStorage, const char *pFilename);
+	bool Close();
+	
+	void *GetData(int Index);
+	void *GetDataSwapped(int Index); // makes sure that the data is 32bit LE ints when saved
+	int GetDataSize(int Index);
+	void UnloadData(int Index);
+	void *GetItem(int Index, int *pType, int *pId);
+	int GetItemSize(int Index);
+	void GetType(int Type, int *pStart, int *pNum);
+	void *FindItem(int Type, int Id);
+	int NumItems();
+	int NumData();
+	void Unload();
+	
+	unsigned Crc();
+};
+
+// write access
+class CDataFileWriter
+{
+	struct CDataInfo
+	{
+		int m_UncompressedSize;
+		int m_CompressedSize;
+		void *m_pCompressedData;
+	} ;
+
+	struct CItemInfo
+	{
+		int m_Type;
+		int m_Id;
+		int m_Size;
+		int m_Next;
+		int m_Prev;
+		void *m_pData;
+	};
+
+	struct CItemTypeInfo
+	{
+		int m_Num;
+		int m_First;
+		int m_Last;
+	};
+	
+	IOHANDLE m_File;
+	int m_NumItems;
+	int m_NumDatas;
+	int m_NumItemTypes;
+	CItemTypeInfo m_aItemTypes[0xffff];
+	CItemInfo m_aItems[1024];
+	CDataInfo m_aDatas[1024];	
+	
+public:
+	bool Open(class IStorage *pStorage, const char *Filename);
+	int AddData(int Size, void *pData);
+	int AddDataSwapped(int Size, void *pData);
+	int AddItem(int Type, int Id, int Size, void *pData);
+	int Finish();
+};
+
+
+#endif
diff --git a/src/engine/shared/demorec.cpp b/src/engine/shared/demorec.cpp
new file mode 100644
index 00000000..48b06e9a
--- /dev/null
+++ b/src/engine/shared/demorec.cpp
@@ -0,0 +1,623 @@
+#include <base/system.h>
+#include <engine/shared/protocol.h>
+#include <engine/storage.h>
+#include "demorec.h"
+#include "memheap.h"
+#include "snapshot.h"
+#include "compression.h"
+#include "network.h"
+#include "engine.h"
+
+static const unsigned char gs_aHeaderMarker[8] = {'T', 'W', 'D', 'E', 'M', 'O', 0, 1};
+
+CDemoRecorder::CDemoRecorder(class CSnapshotDelta *pSnapshotDelta)
+{
+	m_File = 0;
+	m_LastTickMarker = -1;
+	m_pSnapshotDelta = pSnapshotDelta;
+}
+
+//static IOHANDLE m_File = 0;
+
+// Record
+int CDemoRecorder::Start(class IStorage *pStorage, const char *pFilename, const char *pNetVersion, const char *pMap, int Crc, const char *pType)
+{
+	CDemoHeader Header;
+	if(m_File)
+		return -1;
+
+	m_File = pStorage->OpenFile(pFilename, IOFLAG_WRITE);
+	
+	if(!m_File)
+	{
+		dbg_msg("demorec/record", "Unable to open '%s' for recording", pFilename);
+		return -1;
+	}
+	
+	// write header
+	mem_zero(&Header, sizeof(Header));
+	mem_copy(Header.m_aMarker, gs_aHeaderMarker, sizeof(Header.m_aMarker));
+	str_copy(Header.m_aNetversion, pNetVersion, sizeof(Header.m_aNetversion));
+	str_copy(Header.m_aMap, pMap, sizeof(Header.m_aMap));
+	str_copy(Header.m_aType, pType, sizeof(Header.m_aType));
+	Header.m_aCrc[0] = (Crc>>24)&0xff;
+	Header.m_aCrc[1] = (Crc>>16)&0xff;
+	Header.m_aCrc[2] = (Crc>>8)&0xff;
+	Header.m_aCrc[3] = (Crc)&0xff;
+	io_write(m_File, &Header, sizeof(Header));
+	
+	m_LastKeyFrame = -1;
+	m_LastTickMarker = -1;
+	
+	dbg_msg("demorec/record", "Recording to '%s'", pFilename);
+	return 0;
+}
+
+/*
+	Tickmarker
+		7   = Always set
+		6   = Keyframe flag
+		0-5 = Delta tick
+	
+	Normal
+		7   = Not set
+		5-6 = Type
+		0-4 = Size
+*/
+
+enum
+{
+	CHUNKTYPEFLAG_TICKMARKER = 0x80,
+	CHUNKTICKFLAG_KEYFRAME = 0x40, // only when tickmarker is set
+	
+	CHUNKMASK_TICK = 0x3f,
+	CHUNKMASK_TYPE = 0x60,
+	CHUNKMASK_SIZE = 0x1f,
+	
+	CHUNKTYPE_SNAPSHOT = 1,
+	CHUNKTYPE_MESSAGE = 2,
+	CHUNKTYPE_DELTA = 3,
+
+	CHUNKFLAG_BIGSIZE = 0x10
+};
+
+void CDemoRecorder::WriteTickMarker(int Tick, int Keyframe)
+{
+	if(m_LastTickMarker == -1 || Tick-m_LastTickMarker > 63 || Keyframe)
+	{
+		unsigned char aChunk[5];
+		aChunk[0] = CHUNKTYPEFLAG_TICKMARKER;
+		aChunk[1] = (Tick>>24)&0xff;
+		aChunk[2] = (Tick>>16)&0xff;
+		aChunk[3] = (Tick>>8)&0xff;
+		aChunk[4] = (Tick)&0xff;
+
+		if(Keyframe)
+			aChunk[0] |= CHUNKTICKFLAG_KEYFRAME;
+		
+		io_write(m_File, aChunk, sizeof(aChunk));
+	}
+	else
+	{
+		unsigned char aChunk[1];
+		aChunk[0] = CHUNKTYPEFLAG_TICKMARKER | (Tick-m_LastTickMarker);
+		io_write(m_File, aChunk, sizeof(aChunk));
+	}	
+
+	m_LastTickMarker = Tick;
+}
+
+void CDemoRecorder::Write(int Type, const void *pData, int Size)
+{
+	char aBuffer[64*1024];
+	char aBuffer2[64*1024];
+	unsigned char aChunk[3];
+	
+	if(!m_File)
+		return;
+
+	/* pad the data with 0 so we get an alignment of 4,
+	else the compression won't work and miss some bytes */
+	mem_copy(aBuffer2, pData, Size);
+	while(Size&3)
+		aBuffer2[Size++] = 0;
+	Size = CVariableInt::Compress(aBuffer2, Size, aBuffer); // buffer2 -> buffer
+	Size = CNetBase::Compress(aBuffer, Size, aBuffer2, sizeof(aBuffer2)); // buffer -> buffer2
+	
+	
+	aChunk[0] = ((Type&0x3)<<5);
+	if(Size < 30)
+	{
+		aChunk[0] |= Size;
+		io_write(m_File, aChunk, 1);
+	}
+	else
+	{
+		if(Size < 256)
+		{
+			aChunk[0] |= 30;
+			aChunk[1] = Size&0xff;
+			io_write(m_File, aChunk, 2);
+		}
+		else
+		{
+			aChunk[0] |= 31;
+			aChunk[1] = Size&0xff;
+			aChunk[2] = Size>>8;
+			io_write(m_File, aChunk, 3);
+		}
+	}
+	
+	io_write(m_File, aBuffer2, Size);
+}
+
+void CDemoRecorder::RecordSnapshot(int Tick, const void *pData, int Size)
+{
+	if(m_LastKeyFrame == -1 || (Tick-m_LastKeyFrame) > SERVER_TICK_SPEED*5)
+	{
+		// write full tickmarker
+		WriteTickMarker(Tick, 1);
+		
+		// write snapshot
+		Write(CHUNKTYPE_SNAPSHOT, pData, Size);
+			
+		m_LastKeyFrame = Tick;
+		mem_copy(m_aLastSnapshotData, pData, Size);
+	}
+	else
+	{
+		// create delta, prepend tick
+		char aDeltaData[CSnapshot::MAX_SIZE+sizeof(int)];
+		int DeltaSize;
+
+		// write tickmarker
+		WriteTickMarker(Tick, 0);
+		
+		DeltaSize = m_pSnapshotDelta->CreateDelta((CSnapshot*)m_aLastSnapshotData, (CSnapshot*)pData, &aDeltaData);
+		if(DeltaSize)
+		{
+			// record delta
+			Write(CHUNKTYPE_DELTA, aDeltaData, DeltaSize);
+			mem_copy(m_aLastSnapshotData, pData, Size);
+		}
+	}
+}
+
+void CDemoRecorder::RecordMessage(const void *pData, int Size)
+{
+	Write(CHUNKTYPE_MESSAGE, pData, Size);
+}
+
+int CDemoRecorder::Stop()
+{
+	if(!m_File)
+		return -1;
+		
+	dbg_msg("demorec/record", "Stopped recording");
+	io_close(m_File);
+	m_File = 0;
+	return 0;
+}
+
+
+
+CDemoPlayer::CDemoPlayer(class CSnapshotDelta *pSnapshotDelta)
+{
+	m_File = 0;
+	m_pKeyFrames = 0;
+
+	m_pSnapshotDelta = pSnapshotDelta;
+	m_LastSnapshotDataSize = -1;
+}
+
+void CDemoPlayer::SetListner(IListner *pListner)
+{
+	m_pListner = pListner;
+}
+
+
+int CDemoPlayer::ReadChunkHeader(int *pType, int *pSize, int *pTick)
+{
+	unsigned char Chunk = 0;
+	
+	*pSize = 0;
+	*pType = 0;
+	
+	if(io_read(m_File, &Chunk, sizeof(Chunk)) != sizeof(Chunk))
+		return -1;
+		
+	if(Chunk&CHUNKTYPEFLAG_TICKMARKER)
+	{
+		// decode tick marker
+		int Tickdelta = Chunk&(CHUNKMASK_TICK);
+		*pType = Chunk&(CHUNKTYPEFLAG_TICKMARKER|CHUNKTICKFLAG_KEYFRAME);
+		
+		if(Tickdelta == 0)
+		{
+			unsigned char aTickdata[4];
+			if(io_read(m_File, aTickdata, sizeof(aTickdata)) != sizeof(aTickdata))
+				return -1;
+			*pTick = (aTickdata[0]<<24) | (aTickdata[1]<<16) | (aTickdata[2]<<8) | aTickdata[3];
+		}
+		else
+		{
+			*pTick += Tickdelta;
+		}
+		
+	}
+	else
+	{
+		// decode normal chunk
+		*pType = (Chunk&CHUNKMASK_TYPE)>>5;
+		*pSize = Chunk&CHUNKMASK_SIZE;
+		
+		if(*pSize == 30)
+		{
+			unsigned char aSizedata[1];
+			if(io_read(m_File, aSizedata, sizeof(aSizedata)) != sizeof(aSizedata))
+				return -1;
+			*pSize = aSizedata[0];
+			
+		}
+		else if(*pSize == 31)
+		{
+			unsigned char aSizedata[2];
+			if(io_read(m_File, aSizedata, sizeof(aSizedata)) != sizeof(aSizedata))
+				return -1;
+			*pSize = (aSizedata[1]<<8) | aSizedata[0];
+		}
+	}
+	
+	return 0;
+}
+
+void CDemoPlayer::ScanFile()
+{
+	long StartPos;
+	CHeap Heap;
+	CKeyFrameSearch *pFirstKey = 0;
+	CKeyFrameSearch *pCurrentKey = 0;
+	//DEMOREC_CHUNK chunk;
+	int ChunkSize, ChunkType, ChunkTick = 0;
+	int i;
+
+	StartPos = io_tell(m_File);
+	m_Info.m_SeekablePoints = 0;
+
+	while(1)
+	{
+		long CurrentPos = io_tell(m_File);
+		
+		if(ReadChunkHeader(&ChunkType, &ChunkSize, &ChunkTick))
+			break;
+			
+		// read the chunk
+		if(ChunkType&CHUNKTYPEFLAG_TICKMARKER)
+		{
+			if(ChunkType&CHUNKTICKFLAG_KEYFRAME)
+			{
+				CKeyFrameSearch *pKey;
+				
+				// save the position
+				pKey = (CKeyFrameSearch *)Heap.Allocate(sizeof(CKeyFrameSearch));
+				pKey->m_Frame.m_Filepos = CurrentPos;
+				pKey->m_Frame.m_Tick = ChunkTick;
+				pKey->m_pNext = 0;
+				if(pCurrentKey)
+					pCurrentKey->m_pNext = pKey;
+				if(!pFirstKey)
+					pFirstKey = pKey;
+				pCurrentKey = pKey;
+				m_Info.m_SeekablePoints++;
+			}
+			
+			if(m_Info.m_Info.m_FirstTick == -1)
+				m_Info.m_Info.m_FirstTick = ChunkTick;
+			m_Info.m_Info.m_LastTick = ChunkTick;
+		}
+		else if(ChunkSize)
+			io_skip(m_File, ChunkSize);
+			
+	}
+
+	// copy all the frames to an array instead for fast access
+	m_pKeyFrames = (CKeyFrame*)mem_alloc(m_Info.m_SeekablePoints*sizeof(CKeyFrame), 1);
+	for(pCurrentKey = pFirstKey, i = 0; pCurrentKey; pCurrentKey = pCurrentKey->m_pNext, i++)
+		m_pKeyFrames[i] = pCurrentKey->m_Frame;
+		
+	// destroy the temporary heap and seek back to the start
+	io_seek(m_File, StartPos, IOSEEK_START);
+}
+
+void CDemoPlayer::DoTick()
+{
+	static char aCompresseddata[CSnapshot::MAX_SIZE];
+	static char aDecompressed[CSnapshot::MAX_SIZE];
+	static char aData[CSnapshot::MAX_SIZE];
+	int ChunkType, ChunkTick, ChunkSize;
+	int DataSize;
+	int GotSnapshot = 0;
+
+	// update ticks
+	m_Info.m_PreviousTick = m_Info.m_Info.m_CurrentTick;
+	m_Info.m_Info.m_CurrentTick = m_Info.m_NextTick;
+	ChunkTick = m_Info.m_Info.m_CurrentTick;
+
+	while(1)
+	{
+		if(ReadChunkHeader(&ChunkType, &ChunkSize, &ChunkTick))
+		{
+			// stop on error or eof
+			dbg_msg("demorec", "end of file");
+			Pause();
+			break;
+		}
+		
+		// read the chunk
+		if(ChunkSize)
+		{
+			if(io_read(m_File, aCompresseddata, ChunkSize) != (unsigned)ChunkSize)
+			{
+				// stop on error or eof
+				dbg_msg("demorec", "error reading chunk");
+				Stop();
+				break;
+			}
+			
+			DataSize = CNetBase::Decompress(aCompresseddata, ChunkSize, aDecompressed, sizeof(aDecompressed));
+			if(DataSize < 0)
+			{
+				// stop on error or eof
+				dbg_msg("demorec", "error during network decompression");
+				Stop();
+				break;
+			}
+			
+			DataSize = CVariableInt::Decompress(aDecompressed, DataSize, aData);
+
+			if(DataSize < 0)
+			{
+				dbg_msg("demorec", "error during intpack decompression");
+				Stop();
+				break;
+			}
+		}
+			
+		if(ChunkType == CHUNKTYPE_DELTA)
+		{
+			// process delta snapshot
+			static char aNewsnap[CSnapshot::MAX_SIZE];
+			
+			GotSnapshot = 1;
+			
+			DataSize = m_pSnapshotDelta->UnpackDelta((CSnapshot*)m_aLastSnapshotData, (CSnapshot*)aNewsnap, aData, DataSize);
+			
+			if(DataSize >= 0)
+			{
+				if(m_pListner)
+					m_pListner->OnDemoPlayerSnapshot(aNewsnap, DataSize);
+
+				m_LastSnapshotDataSize = DataSize;
+				mem_copy(m_aLastSnapshotData, aNewsnap, DataSize);
+			}
+			else
+				dbg_msg("demorec", "error duing unpacking of delta, err=%d", DataSize);
+		}
+		else if(ChunkType == CHUNKTYPE_SNAPSHOT)
+		{
+			// process full snapshot
+			GotSnapshot = 1;
+			
+			m_LastSnapshotDataSize = DataSize;
+			mem_copy(m_aLastSnapshotData, aData, DataSize);
+			if(m_pListner)
+				m_pListner->OnDemoPlayerSnapshot(aData, DataSize);
+		}
+		else
+		{
+			// if there were no snapshots in this tick, replay the last one
+			if(!GotSnapshot && m_pListner && m_LastSnapshotDataSize != -1)
+			{
+				GotSnapshot = 1;
+				m_pListner->OnDemoPlayerSnapshot(m_aLastSnapshotData, m_LastSnapshotDataSize);
+			}
+			
+			// check the remaining types
+			if(ChunkType&CHUNKTYPEFLAG_TICKMARKER)
+			{
+				m_Info.m_NextTick = ChunkTick;
+				break;
+			}
+			else if(ChunkType == CHUNKTYPE_MESSAGE)
+			{
+				if(m_pListner)
+					m_pListner->OnDemoPlayerMessage(aData, DataSize);
+			}
+		}
+	}
+}
+
+void CDemoPlayer::Pause()
+{
+	m_Info.m_Info.m_Paused = 1;
+}
+
+void CDemoPlayer::Unpause()
+{
+	if(m_Info.m_Info.m_Paused)
+	{
+		/*m_Info.start_tick = m_Info.current_tick;
+		m_Info.start_time = time_get();*/
+		m_Info.m_Info.m_Paused = 0;
+	}
+}
+
+int CDemoPlayer::Load(class IStorage *pStorage, const char *pFilename)
+{
+	m_File = pStorage->OpenFile(pFilename, IOFLAG_READ);
+	if(!m_File)
+	{
+		dbg_msg("demorec/playback", "could not open '%s'", pFilename);
+		return -1;
+	}
+	
+	// clear the playback info
+	mem_zero(&m_Info, sizeof(m_Info));
+	m_Info.m_Info.m_FirstTick = -1;
+	m_Info.m_Info.m_LastTick = -1;
+	//m_Info.start_tick = -1;
+	m_Info.m_NextTick = -1;
+	m_Info.m_Info.m_CurrentTick = -1;
+	m_Info.m_PreviousTick = -1;
+	m_Info.m_Info.m_Speed = 1;
+	
+	m_LastSnapshotDataSize = -1;
+
+	// read the header
+	io_read(m_File, &m_Info.m_Header, sizeof(m_Info.m_Header));
+	if(mem_comp(m_Info.m_Header.m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) != 0)
+	{
+		dbg_msg("demorec/playback", "'%s' is not a demo file", pFilename);
+		io_close(m_File);
+		m_File = 0;
+		return -1;
+	}
+	
+	// scan the file for interessting points
+	ScanFile();
+	
+	// ready for playback
+	return 0;
+}
+
+int CDemoPlayer::NextFrame()
+{
+	DoTick();
+	return IsPlaying();
+}
+
+int CDemoPlayer::Play()
+{
+	// fill in previous and next tick
+	while(m_Info.m_PreviousTick == -1 && IsPlaying())
+		DoTick();
+		
+	// set start info
+	/*m_Info.start_tick = m_Info.previous_tick;
+	m_Info.start_time = time_get();*/
+	m_Info.m_CurrentTime = m_Info.m_PreviousTick*time_freq()/SERVER_TICK_SPEED;
+	m_Info.m_LastUpdate = time_get();
+	return 0;
+}
+
+int CDemoPlayer::SetPos(float Percent)
+{
+	int Keyframe;
+	int WantedTick;
+	if(!m_File)
+		return -1;
+	
+	// -5 because we have to have a current tick and previous tick when we do the playback
+	WantedTick = m_Info.m_Info.m_FirstTick + (int)((m_Info.m_Info.m_LastTick-m_Info.m_Info.m_FirstTick)*Percent) - 5;
+	
+	Keyframe = (int)(m_Info.m_SeekablePoints*Percent);
+
+	if(Keyframe < 0 || Keyframe >= m_Info.m_SeekablePoints)
+		return -1;
+	
+	// get correct key frame
+	if(m_pKeyFrames[Keyframe].m_Tick < WantedTick)
+		while(Keyframe < m_Info.m_SeekablePoints-1 && m_pKeyFrames[Keyframe].m_Tick < WantedTick)
+			Keyframe++;
+
+	while(Keyframe && m_pKeyFrames[Keyframe].m_Tick > WantedTick)
+		Keyframe--;
+	
+	// seek to the correct keyframe
+	io_seek(m_File, m_pKeyFrames[Keyframe].m_Filepos, IOSEEK_START);
+
+	//m_Info.start_tick = -1;
+	m_Info.m_NextTick = -1;
+	m_Info.m_Info.m_CurrentTick = -1;
+	m_Info.m_PreviousTick = -1;
+
+	// playback everything until we hit our tick
+	while(m_Info.m_PreviousTick < WantedTick)
+		DoTick();
+	
+	Play();
+	
+	return 0;
+}
+
+void CDemoPlayer::SetSpeed(float Speed)
+{
+	m_Info.m_Info.m_Speed = Speed;
+}
+
+int CDemoPlayer::Update()
+{
+	int64 Now = time_get();
+	int64 Deltatime = Now-m_Info.m_LastUpdate;
+	m_Info.m_LastUpdate = Now;
+	
+	if(!IsPlaying())
+		return 0;
+	
+	if(m_Info.m_Info.m_Paused)
+	{
+		
+	}
+	else
+	{
+		int64 Freq = time_freq();
+		m_Info.m_CurrentTime += (int64)(Deltatime*(double)m_Info.m_Info.m_Speed);
+		
+		while(1)
+		{
+			int64 CurtickStart = (m_Info.m_Info.m_CurrentTick)*Freq/SERVER_TICK_SPEED;
+
+			// break if we are ready
+			if(CurtickStart > m_Info.m_CurrentTime)
+				break;
+			
+			// do one more tick
+			DoTick();
+			
+			if(m_Info.m_Info.m_Paused)
+				return 0;
+		}
+
+		// update intratick
+		{	
+			int64 CurtickStart = (m_Info.m_Info.m_CurrentTick)*Freq/SERVER_TICK_SPEED;
+			int64 PrevtickStart = (m_Info.m_PreviousTick)*Freq/SERVER_TICK_SPEED;
+			m_Info.m_IntraTick = (m_Info.m_CurrentTime - PrevtickStart) / (float)(CurtickStart-PrevtickStart);
+			m_Info.m_TickTime = (m_Info.m_CurrentTime - PrevtickStart) / (float)Freq;
+		}
+		
+		if(m_Info.m_Info.m_CurrentTick == m_Info.m_PreviousTick ||
+			m_Info.m_Info.m_CurrentTick == m_Info.m_NextTick)
+		{
+			dbg_msg("demorec/playback", "tick error prev=%d cur=%d next=%d",
+				m_Info.m_PreviousTick, m_Info.m_Info.m_CurrentTick, m_Info.m_NextTick);
+		}
+	}
+	
+	return 0;
+}
+
+int CDemoPlayer::Stop()
+{
+	if(!m_File)
+		return -1;
+		
+	dbg_msg("demorec/playback", "Stopped playback");
+	io_close(m_File);
+	m_File = 0;
+	mem_free(m_pKeyFrames);
+	m_pKeyFrames = 0;
+	return 0;
+}
+
+
diff --git a/src/engine/shared/demorec.h b/src/engine/shared/demorec.h
new file mode 100644
index 00000000..0936c30c
--- /dev/null
+++ b/src/engine/shared/demorec.h
@@ -0,0 +1,117 @@
+#ifndef ENGINE_SHARED_DEMOREC_H
+#define ENGINE_SHARED_DEMOREC_H
+
+#include <engine/demo.h>
+#include "snapshot.h"
+
+struct CDemoHeader
+{
+	char m_aMarker[8];
+	char m_aNetversion[64];
+	char m_aMap[64];
+	unsigned char m_aCrc[4];
+	char m_aType[8];
+};
+
+class CDemoRecorder
+{
+	IOHANDLE m_File;
+	int m_LastTickMarker;
+	int m_LastKeyFrame;
+	unsigned char m_aLastSnapshotData[CSnapshot::MAX_SIZE];
+	class CSnapshotDelta *m_pSnapshotDelta;
+	
+	void WriteTickMarker(int Tick, int Keyframe);
+	void Write(int Type, const void *pData, int Size);
+public:
+	CDemoRecorder(class CSnapshotDelta *pSnapshotDelta);
+	
+	int Start(class IStorage *pStorage, const char *pFilename, const char *pNetversion, const char *pMap, int MapCrc, const char *pType);
+	int Stop();
+
+	void RecordSnapshot(int Tick, const void *pData, int Size);
+	void RecordMessage(const void *pData, int Size);
+
+	bool IsRecording() const { return m_File != 0; }
+};
+
+class CDemoPlayer : public IDemoPlayer
+{
+public:
+	class IListner
+	{
+	public:
+		virtual void OnDemoPlayerSnapshot(void *pData, int Size) = 0;
+		virtual void OnDemoPlayerMessage(void *pData, int Size) = 0;
+	};
+	
+	struct CPlaybackInfo
+	{
+		CDemoHeader m_Header;
+		
+		IDemoPlayer::CInfo m_Info;
+
+		int64 m_LastUpdate;
+		int64 m_CurrentTime;
+		
+		int m_SeekablePoints;
+		
+		int m_NextTick;
+		int m_PreviousTick;
+		
+		float m_IntraTick;
+		float m_TickTime;
+	};
+
+private:
+	IListner *m_pListner;
+
+
+	// Playback
+	struct CKeyFrame
+	{
+		long m_Filepos;
+		int m_Tick;
+	};
+		
+	struct CKeyFrameSearch
+	{
+		CKeyFrame m_Frame;
+		CKeyFrameSearch *m_pNext;
+	};	
+
+	IOHANDLE m_File;
+	CKeyFrame *m_pKeyFrames;
+
+	CPlaybackInfo m_Info;
+	unsigned char m_aLastSnapshotData[CSnapshot::MAX_SIZE];
+	int m_LastSnapshotDataSize;
+	class CSnapshotDelta *m_pSnapshotDelta;
+
+	int ReadChunkHeader(int *pType, int *pSize, int *pTick);
+	void DoTick();
+	void ScanFile();
+	int NextFrame();
+
+public:
+	
+	CDemoPlayer(class CSnapshotDelta *m_pSnapshotDelta);
+	
+	void SetListner(IListner *pListner);
+		
+	int Load(class IStorage *pStorage, const char *pFilename);
+	int Play();
+	void Pause();
+	void Unpause();
+	int Stop();	
+	void SetSpeed(float Speed);
+	int SetPos(float Precent);
+	const CInfo *BaseInfo() const { return &m_Info.m_Info; }
+	
+	int Update();
+	
+	const CPlaybackInfo *Info() const { return &m_Info; }
+	int IsPlaying() const { return m_File != 0; }
+};
+
+#endif
diff --git a/src/engine/shared/engine.cpp b/src/engine/shared/engine.cpp
new file mode 100644
index 00000000..5cd50cf0
--- /dev/null
+++ b/src/engine/shared/engine.cpp
@@ -0,0 +1,76 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+
+#include <base/system.h>
+
+#include <engine/shared/config.h>
+#include <engine/shared/engine.h>
+#include <engine/shared/network.h>
+#include <engine/console.h>
+#include "linereader.h"
+
+// compiled-in data-dir path
+#define DATA_DIR "data"
+
+//static int engine_find_datadir(char *argv0);
+/*
+static void con_dbg_dumpmem(IConsole::IResult *result, void *user_data)
+{
+	mem_debug_dump();
+}
+
+static void con_dbg_lognetwork(IConsole::IResult *result, void *user_data)
+{
+	CNetBase::OpenLog("network_sent.dat", "network_recv.dat");
+}*/
+
+/*
+static char application_save_path[512] = {0};
+static char datadir[512] = {0};
+
+const char *engine_savepath(const char *filename, char *buffer, int max)
+{
+	str_format(buffer, max, "%s/%s", application_save_path, filename);
+	return buffer;
+}*/
+
+void CEngine::Init(const char *pAppname)
+{
+	dbg_logger_stdout();
+	dbg_logger_debugger();
+	
+	//
+	dbg_msg("engine", "running on %s-%s-%s", CONF_FAMILY_STRING, CONF_PLATFORM_STRING, CONF_ARCH_STRING);
+#ifdef CONF_ARCH_ENDIAN_LITTLE
+	dbg_msg("engine", "arch is little endian");
+#elif defined(CONF_ARCH_ENDIAN_BIG)
+	dbg_msg("engine", "arch is big endian");
+#else
+	dbg_msg("engine", "unknown endian");
+#endif
+
+	// init the network
+	net_init();
+	CNetBase::Init();
+	
+	m_HostLookupPool.Init(1);
+
+	//MACRO_REGISTER_COMMAND("dbg_dumpmem", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_dumpmem, 0x0, "Dump the memory");
+	//MACRO_REGISTER_COMMAND("dbg_lognetwork", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_lognetwork, 0x0, "Log the network");
+	
+	// reset the config
+	//config_reset();
+}
+
+
+static int HostLookupThread(void *pUser)
+{
+	CHostLookup *pLookup = (CHostLookup *)pUser;
+	net_host_lookup(pLookup->m_aHostname, &pLookup->m_Addr, NETTYPE_IPV4);
+	return 0;
+}
+
+void CEngine::HostLookup(CHostLookup *pLookup, const char *pHostname)
+{
+	str_copy(pLookup->m_aHostname, pHostname, sizeof(pLookup->m_aHostname));
+	m_HostLookupPool.Add(&pLookup->m_Job, HostLookupThread, pLookup);
+}
diff --git a/src/engine/shared/engine.h b/src/engine/shared/engine.h
new file mode 100644
index 00000000..ad266ae4
--- /dev/null
+++ b/src/engine/shared/engine.h
@@ -0,0 +1,23 @@
+#ifndef ENGINE_SHARED_E_ENGINE_H
+#define ENGINE_SHARED_E_ENGINE_H
+
+#include "jobs.h"
+
+class CHostLookup
+{
+public:
+	CJob m_Job;
+	char m_aHostname[128];
+	NETADDR m_Addr;
+};
+
+class CEngine
+{
+	class CJobPool m_HostLookupPool;
+
+public:
+	void Init(const char *pAppname);
+	void HostLookup(CHostLookup *pLookup, const char *pHostname);
+};
+
+#endif
diff --git a/src/engine/shared/huffman.cpp b/src/engine/shared/huffman.cpp
new file mode 100644
index 00000000..8b0c1cd0
--- /dev/null
+++ b/src/engine/shared/huffman.cpp
@@ -0,0 +1,276 @@
+#include <base/system.h>
+#include "huffman.h"
+
+struct CHuffmanConstructNode
+{
+	unsigned short m_NodeId;
+ 	int m_Frequency;
+};
+
+void CHuffman::Setbits_r(CNode *pNode, int Bits, int Depth)
+{
+	if(pNode->m_aLeafs[1] != 0xffff)
+		Setbits_r(&m_aNodes[pNode->m_aLeafs[1]], Bits|(1<<Depth), Depth+1);
+	if(pNode->m_aLeafs[0] != 0xffff)
+		Setbits_r(&m_aNodes[pNode->m_aLeafs[0]], Bits, Depth+1);
+		
+	if(pNode->m_NumBits)
+	{
+		pNode->m_Bits = Bits;
+		pNode->m_NumBits = Depth;
+	}
+}
+
+// TODO: this should be something faster, but it's enough for now
+static void BubbleSort(CHuffmanConstructNode **ppList, int Size)
+{
+	int Changed = 1;
+	CHuffmanConstructNode *pTemp;
+	
+	while(Changed)
+	{
+		Changed = 0;
+		for(int i = 0; i < Size-1; i++)
+		{
+			if(ppList[i]->m_Frequency < ppList[i+1]->m_Frequency)
+			{
+				pTemp = ppList[i];
+				ppList[i] = ppList[i+1];
+				ppList[i+1] = pTemp;
+				Changed = 1;
+			}
+		}
+	}
+}
+
+void CHuffman::ConstructTree(const unsigned *pFrequencies)
+{
+	CHuffmanConstructNode aNodesLeftStorage[HUFFMAN_MAX_SYMBOLS];
+	CHuffmanConstructNode *apNodesLeft[HUFFMAN_MAX_SYMBOLS];
+	int NumNodesLeft = HUFFMAN_MAX_SYMBOLS;
+
+	// add the symbols
+	for(int i = 0; i < HUFFMAN_MAX_SYMBOLS; i++)
+	{
+		m_aNodes[i].m_NumBits = -1;
+		m_aNodes[i].m_Symbol = i;
+		m_aNodes[i].m_aLeafs[0] = -1;
+		m_aNodes[i].m_aLeafs[1] = -1;
+
+		if(i == HUFFMAN_EOF_SYMBOL)
+			aNodesLeftStorage[i].m_Frequency = 1;
+		else
+			aNodesLeftStorage[i].m_Frequency = pFrequencies[i];
+		aNodesLeftStorage[i].m_NodeId = i;
+		apNodesLeft[i] = &aNodesLeftStorage[i];
+
+	}
+	
+	m_NumNodes = HUFFMAN_MAX_SYMBOLS;
+	
+	// construct the table
+	while(NumNodesLeft > 1)
+	{
+		// we can't rely on stdlib's qsort for this, it can generate different results on different implementations
+		BubbleSort(apNodesLeft, NumNodesLeft);
+		
+		m_aNodes[m_NumNodes].m_NumBits = 0;
+		m_aNodes[m_NumNodes].m_aLeafs[0] = apNodesLeft[NumNodesLeft-1]->m_NodeId;
+		m_aNodes[m_NumNodes].m_aLeafs[1] = apNodesLeft[NumNodesLeft-2]->m_NodeId;
+		apNodesLeft[NumNodesLeft-2]->m_NodeId = m_NumNodes;
+		apNodesLeft[NumNodesLeft-2]->m_Frequency = apNodesLeft[NumNodesLeft-1]->m_Frequency + apNodesLeft[NumNodesLeft-2]->m_Frequency;
+
+		m_NumNodes++;
+		NumNodesLeft--;
+	}
+
+	// set start node
+	m_pStartNode = &m_aNodes[m_NumNodes-1];
+	
+	// build symbol bits
+	Setbits_r(m_pStartNode, 0, 0);
+}
+
+void CHuffman::Init(const unsigned *pFrequencies)
+{
+	int i;
+
+	// make sure to cleanout every thing
+	mem_zero(this, sizeof(*this));
+
+	// construct the tree
+	ConstructTree(pFrequencies);
+
+	// build decode LUT
+	for(i = 0; i < HUFFMAN_LUTSIZE; i++)
+	{
+		unsigned Bits = i;
+		int k;
+		CNode *pNode = m_pStartNode;
+		for(k = 0; k < HUFFMAN_LUTBITS; k++)
+		{
+			pNode = &m_aNodes[pNode->m_aLeafs[Bits&1]];
+			Bits >>= 1;
+
+			if(!pNode)
+				break;
+
+			if(pNode->m_NumBits)
+			{
+				m_apDecodeLut[i] = pNode;
+				break;
+			}
+		}
+
+		if(k == HUFFMAN_LUTBITS)
+			m_apDecodeLut[i] = pNode;
+	}
+
+}
+
+//***************************************************************
+int CHuffman::Compress(const void *pInput, int InputSize, void *pOutput, int OutputSize)
+{
+	// this macro loads a symbol for a byte into bits and bitcount
+#define HUFFMAN_MACRO_LOADSYMBOL(Sym) \
+	Bits |= m_aNodes[Sym].m_Bits << Bitcount; \
+	Bitcount += m_aNodes[Sym].m_NumBits;
+
+	// this macro writes the symbol stored in bits and bitcount to the dst pointer
+#define HUFFMAN_MACRO_WRITE() \
+	while(Bitcount >= 8) \
+	{ \
+		*pDst++ = (unsigned char)(Bits&0xff); \
+		if(pDst == pDstEnd) \
+			return -1; \
+		Bits >>= 8; \
+		Bitcount -= 8; \
+	}
+
+	// setup buffer pointers
+	const unsigned char *pSrc = (const unsigned char *)pInput;
+	const unsigned char *pSrcEnd = pSrc + InputSize;
+	unsigned char *pDst = (unsigned char *)pOutput;
+	unsigned char *pDstEnd = pDst + OutputSize;
+
+	// symbol variables
+	unsigned Bits = 0;
+	unsigned Bitcount = 0;
+
+	// make sure that we have data that we want to compress
+	if(InputSize)
+	{
+		// {A} load the first symbol
+		int Symbol = *pSrc++;
+
+		while(pSrc != pSrcEnd)
+		{
+			// {B} load the symbol
+			HUFFMAN_MACRO_LOADSYMBOL(Symbol)
+
+			// {C} fetch next symbol, this is done here because it will reduce dependency in the code
+			Symbol = *pSrc++;
+
+			// {B} write the symbol loaded at
+			HUFFMAN_MACRO_WRITE()
+		}
+
+		// write the last symbol loaded from {C} or {A} in the case of only 1 byte input buffer
+		HUFFMAN_MACRO_LOADSYMBOL(Symbol)
+		HUFFMAN_MACRO_WRITE()
+	}
+
+	// write EOF symbol
+	HUFFMAN_MACRO_LOADSYMBOL(HUFFMAN_EOF_SYMBOL)
+	HUFFMAN_MACRO_WRITE()
+
+	// write out the last bits
+	*pDst++ = Bits;
+
+	// return the size of the output
+	return (int)(pDst - (const unsigned char *)pOutput);
+
+	// remove macros
+#undef HUFFMAN_MACRO_LOADSYMBOL
+#undef HUFFMAN_MACRO_WRITE
+}
+
+//***************************************************************
+int CHuffman::Decompress(const void *pInput, int InputSize, void *pOutput, int OutputSize)
+{
+	// setup buffer pointers
+	unsigned char *pDst = (unsigned char *)pOutput;
+	unsigned char *pSrc = (unsigned char *)pInput;
+	unsigned char *pDstEnd = pDst + OutputSize;
+	unsigned char *pSrcEnd = pSrc + InputSize;
+
+	unsigned Bits = 0;
+	unsigned Bitcount = 0;
+
+	CNode *pEof = &m_aNodes[HUFFMAN_EOF_SYMBOL];
+	CNode *pNode = 0;
+
+	while(1)
+	{
+		// {A} try to load a node now, this will reduce dependency at location {D}
+		pNode = 0;
+		if(Bitcount >= HUFFMAN_LUTBITS)
+			pNode = m_apDecodeLut[Bits&HUFFMAN_LUTMASK];
+
+		// {B} fill with new bits
+		while(Bitcount < 24 && pSrc != pSrcEnd)
+		{
+			Bits |= (*pSrc++) << Bitcount;
+			Bitcount += 8;
+		}
+
+		// {C} load symbol now if we didn't that earlier at location {A}
+		if(!pNode)
+			pNode = m_apDecodeLut[Bits&HUFFMAN_LUTMASK];
+
+		// {D} check if we hit a symbol already
+		if(pNode->m_NumBits)
+		{
+			// remove the bits for that symbol
+			Bits >>= pNode->m_NumBits;
+			Bitcount -= pNode->m_NumBits;
+		}
+		else
+		{
+			// remove the bits that the lut checked up for us
+			Bits >>= HUFFMAN_LUTBITS;
+			Bitcount -= HUFFMAN_LUTBITS;
+
+			// walk the tree bit by bit
+			while(1)
+			{
+				// traverse tree
+				pNode = &m_aNodes[pNode->m_aLeafs[Bits&1]];
+
+				// remove bit
+				Bitcount--;
+				Bits >>= 1;
+
+				// check if we hit a symbol
+				if(pNode->m_NumBits)
+					break;
+
+				// no more bits, decoding error
+				if(Bitcount == 0)
+					return -1;
+			}
+		}
+
+		// check for eof
+		if(pNode == pEof)
+			break;
+
+		// output character
+		if(pDst == pDstEnd)
+			return -1;
+		*pDst++ = pNode->m_Symbol;
+	}
+
+	// return the size of the decompressed buffer
+	return (int)(pDst - (const unsigned char *)pOutput);
+}
diff --git a/src/engine/shared/huffman.h b/src/engine/shared/huffman.h
new file mode 100644
index 00000000..5aa56c8f
--- /dev/null
+++ b/src/engine/shared/huffman.h
@@ -0,0 +1,89 @@
+#ifndef ENGINE_SHARED_HUFFMAN_H
+#define ENGINE_SHARED_HUFFMAN_H
+
+
+
+class CHuffman
+{
+	enum
+	{
+		HUFFMAN_EOF_SYMBOL = 256,
+
+		HUFFMAN_MAX_SYMBOLS=HUFFMAN_EOF_SYMBOL+1,
+		HUFFMAN_MAX_NODES=HUFFMAN_MAX_SYMBOLS*2-1,
+		
+		HUFFMAN_LUTBITS = 10,
+		HUFFMAN_LUTSIZE = (1<<HUFFMAN_LUTBITS),
+		HUFFMAN_LUTMASK = (HUFFMAN_LUTSIZE-1)
+	};
+
+	struct CNode
+	{
+		// symbol
+		unsigned m_Bits;
+		unsigned m_NumBits;
+
+		// don't use pointers for this. shorts are smaller so we can fit more data into the cache
+		unsigned short m_aLeafs[2];
+
+		// what the symbol represents
+		unsigned char m_Symbol;
+	};
+
+	CNode m_aNodes[HUFFMAN_MAX_NODES];
+	CNode *m_apDecodeLut[HUFFMAN_LUTSIZE];
+	CNode *m_pStartNode;
+	int m_NumNodes;
+	
+	void Setbits_r(CNode *pNode, int Bits, int Depth);
+	void ConstructTree(const unsigned *pFrequencies);
+	
+public:
+	/*
+		Function: huffman_init
+			Inits the compressor/decompressor.
+
+		Parameters:
+			huff - Pointer to the state to init
+			frequencies - A pointer to an array of 256 entries of the frequencies of the bytes
+
+		Remarks:
+			- Does no allocation what so ever.
+			- You don't have to call any cleanup functions when you are done with it
+	*/
+	void Init(const unsigned *pFrequencies);
+
+	/*
+		Function: huffman_compress
+			Compresses a buffer and outputs a compressed buffer.
+
+		Parameters:
+			huff - Pointer to the huffman state
+			input - Buffer to compress
+			input_size - Size of the buffer to compress
+			output - Buffer to put the compressed data into
+			output_size - Size of the output buffer
+
+		Returns:
+			Returns the size of the compressed data. Negative value on failure.
+	*/
+	int Compress(const void *pInput, int InputSize, void *pOutput, int OutputSize);
+
+	/*
+		Function: huffman_decompress
+			Decompresses a buffer
+
+		Parameters:
+			huff - Pointer to the huffman state
+			input - Buffer to decompress
+			input_size - Size of the buffer to decompress
+			output - Buffer to put the uncompressed data into
+			output_size - Size of the output buffer
+
+		Returns:
+			Returns the size of the uncompressed data. Negative value on failure.
+	*/
+	int Decompress(const void *pInput, int InputSize, void *pOutput, int OutputSize);
+	
+};
+#endif // __HUFFMAN_HEADER__
diff --git a/src/engine/shared/jobs.cpp b/src/engine/shared/jobs.cpp
new file mode 100644
index 00000000..83d7290b
--- /dev/null
+++ b/src/engine/shared/jobs.cpp
@@ -0,0 +1,74 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+#include <base/system.h>
+#include "jobs.h"
+
+CJobPool::CJobPool()
+{
+	// empty the pool
+	m_Lock = lock_create();
+	m_pFirstJob = 0;
+	m_pLastJob = 0;
+}
+
+void CJobPool::WorkerThread(void *pUser)
+{
+	CJobPool *pPool = (CJobPool *)pUser;
+	
+	while(1)
+	{
+		CJob *pJob = 0;
+		
+		// fetch job from queue
+		lock_wait(pPool->m_Lock);
+		if(pPool->m_pFirstJob)
+		{
+			pJob = pPool->m_pFirstJob;
+			pPool->m_pFirstJob = pPool->m_pFirstJob->m_pNext;
+			if(pPool->m_pFirstJob)
+				pPool->m_pFirstJob->m_pPrev = 0;
+			else
+				pPool->m_pLastJob = 0;
+		}
+		lock_release(pPool->m_Lock);
+		
+		// do the job if we have one
+		if(pJob)
+		{
+			pJob->m_Status = CJob::STATE_RUNNING;
+			pJob->m_Result = pJob->m_pfnFunc(pJob->m_pFuncData);
+			pJob->m_Status = CJob::STATE_DONE;
+		}
+		else
+			thread_sleep(10);
+	}
+	
+}
+
+int CJobPool::Init(int NumThreads)
+{
+	// start threads
+	for(int i = 0; i < NumThreads; i++)
+		thread_create(WorkerThread, this);
+	return 0;
+}
+
+int CJobPool::Add(CJob *pJob, JOBFUNC pfnFunc, void *pData)
+{
+	mem_zero(pJob, sizeof(CJob));
+	pJob->m_pfnFunc = pfnFunc;
+	pJob->m_pFuncData = pData;
+	
+	lock_wait(m_Lock);
+	
+	// add job to queue
+	pJob->m_pPrev = m_pLastJob;
+	if(m_pLastJob)
+		m_pLastJob->m_pNext = pJob;
+	m_pLastJob = pJob;
+	if(!m_pFirstJob)
+		m_pFirstJob = pJob;
+	
+	lock_release(m_Lock);
+	return 0;
+}
+
diff --git a/src/engine/shared/jobs.h b/src/engine/shared/jobs.h
new file mode 100644
index 00000000..d04815b0
--- /dev/null
+++ b/src/engine/shared/jobs.h
@@ -0,0 +1,51 @@
+#ifndef ENGINE_SHARED_JOBS_H
+#define ENGINE_SHARED_JOBS_H
+typedef int (*JOBFUNC)(void *pData);
+
+class CJobPool;
+
+class CJob
+{
+	friend class CJobPool;
+	
+	CJobPool *m_pPool;
+	CJob *m_pPrev;
+	CJob *m_pNext;
+	
+	volatile int m_Status;
+	volatile int m_Result;
+	
+	JOBFUNC m_pfnFunc;
+	void *m_pFuncData;
+public:
+	CJob()
+	{
+		m_Status = STATE_DONE;
+		m_pFuncData = 0;
+	}
+	
+	enum
+	{
+		STATE_PENDING=0,
+		STATE_RUNNING,
+		STATE_DONE
+	};
+	
+	int Status() const { return m_Status; }
+};
+
+class CJobPool
+{
+	LOCK m_Lock;
+	CJob *m_pFirstJob;
+	CJob *m_pLastJob;
+	
+	static void WorkerThread(void *pUser);
+	
+public:
+	CJobPool();
+	
+	int Init(int NumThreads);
+	int Add(CJob *pJob, JOBFUNC pfnFunc, void *pData);
+};
+#endif
diff --git a/src/engine/shared/kernel.cpp b/src/engine/shared/kernel.cpp
new file mode 100644
index 00000000..9f6850ba
--- /dev/null
+++ b/src/engine/shared/kernel.cpp
@@ -0,0 +1,93 @@
+#include <base/system.h>
+#include <engine/kernel.h>
+
+class CKernel : public IKernel
+{
+	enum
+	{
+		MAX_INTERFACES=32,
+	};
+	
+	class CInterfaceInfo
+	{
+	public:
+		CInterfaceInfo()
+		{
+			m_aName[0] = 0;
+			m_pInterface = 0x0;
+		}
+		
+		char m_aName[64];
+		IInterface *m_pInterface;
+	};
+
+	CInterfaceInfo m_aInterfaces[MAX_INTERFACES];
+	int m_NumInterfaces;
+	
+	CInterfaceInfo *FindInterfaceInfo(const char *pName)
+	{
+		for(int i = 0; i < m_NumInterfaces; i++)
+		{
+			if(str_comp(pName, m_aInterfaces[i].m_aName) == 0)
+				return &m_aInterfaces[i];
+		}
+		return 0x0;
+	}
+	
+public:
+
+	CKernel()
+	{
+		m_NumInterfaces = 0;
+	}
+
+
+	virtual bool RegisterInterfaceImpl(const char *pName, IInterface *pInterface)
+	{
+		// TODO: More error checks here
+		if(m_NumInterfaces == MAX_INTERFACES)
+		{
+			dbg_msg("kernel", "ERROR: couldn't register interface '%s'. maximum of interfaces reached", pName);
+			return false;
+		}
+			
+		if(FindInterfaceInfo(pName) != 0)
+		{
+			dbg_msg("kernel", "ERROR: couldn't register interface '%s'. interface already exists");
+			return false;
+		}
+		
+		pInterface->m_pKernel = this;
+		m_aInterfaces[m_NumInterfaces].m_pInterface = pInterface;
+		str_copy(m_aInterfaces[m_NumInterfaces].m_aName, pName, sizeof(m_aInterfaces[m_NumInterfaces].m_aName));
+		m_NumInterfaces++;
+		
+		return true;
+	}
+
+	virtual bool ReregisterInterfaceImpl(const char *pName, IInterface *pInterface)
+	{
+		if(FindInterfaceInfo(pName) == 0)
+		{
+			dbg_msg("kernel", "ERROR: couldn't reregister interface '%s'. interface doesn't exist");
+			return false;
+		}
+		
+		pInterface->m_pKernel = this;
+		
+		return true;
+	}
+	
+	virtual IInterface *RequestInterfaceImpl(const char *pName)
+	{
+		CInterfaceInfo *pInfo = FindInterfaceInfo(pName);
+		if(!pInfo)
+		{
+			dbg_msg("kernel", "failed to find interface with the name '%s'", pName);
+			return 0;
+		}
+		return pInfo->m_pInterface;
+	}
+};
+
+IKernel *IKernel::Create() { return new CKernel; }
diff --git a/src/engine/shared/linereader.cpp b/src/engine/shared/linereader.cpp
new file mode 100644
index 00000000..b3de233b
--- /dev/null
+++ b/src/engine/shared/linereader.cpp
@@ -0,0 +1,62 @@
+#include "linereader.h"
+
+void CLineReader::Init(IOHANDLE io)
+{
+	m_BufferMaxSize = 4*1024;
+	m_BufferSize = 0;
+	m_BufferPos = 0;
+	m_IO = io;
+}
+
+char *CLineReader::Get()
+{
+	unsigned LineStart = m_BufferPos;
+
+	while(1)
+	{
+		if(m_BufferPos >= m_BufferSize)
+		{
+			// fetch more
+
+			// move the remaining part to the front
+			unsigned Read;
+			unsigned Left = m_BufferSize - LineStart;
+
+			if(LineStart > m_BufferSize)
+				Left = 0;
+			if(Left)
+				mem_move(m_aBuffer, &m_aBuffer[LineStart], Left);
+			m_BufferPos = Left;
+
+			// fill the buffer
+			Read = io_read(m_IO, &m_aBuffer[m_BufferPos], m_BufferMaxSize-m_BufferPos);
+			m_BufferSize = Left + Read;
+			LineStart = 0;
+
+			if(!Read)
+			{
+				if(Left)
+				{
+					m_aBuffer[Left] = 0; // return the last line
+					m_BufferPos = Left;
+					m_BufferSize = Left;
+					return m_aBuffer;
+				}
+				else
+					return 0x0; // we are done!
+			}
+		}
+		else
+		{
+			if(m_aBuffer[m_BufferPos] == '\n' || m_aBuffer[m_BufferPos] == '\r')
+			{
+				// line found
+				m_aBuffer[m_BufferPos] = 0;
+				m_BufferPos++;
+				return &m_aBuffer[LineStart];
+			}
+			else
+				m_BufferPos++;
+		}
+	}
+}
diff --git a/src/engine/shared/linereader.h b/src/engine/shared/linereader.h
new file mode 100644
index 00000000..f28d42f6
--- /dev/null
+++ b/src/engine/shared/linereader.h
@@ -0,0 +1,17 @@
+#ifndef ENGINE_SHARED_LINEREADER_H
+#define ENGINE_SHARED_LINEREADER_H
+#include <base/system.h>
+
+// buffered stream for reading lines, should perhaps be something smaller
+class CLineReader
+{
+	char m_aBuffer[4*1024];
+	unsigned m_BufferPos;
+	unsigned m_BufferSize;
+	unsigned m_BufferMaxSize;
+	IOHANDLE m_IO;
+public:
+	void Init(IOHANDLE IoHandle);
+	char *Get();
+};
+#endif
diff --git a/src/engine/shared/map.cpp b/src/engine/shared/map.cpp
new file mode 100644
index 00000000..505d18e9
--- /dev/null
+++ b/src/engine/shared/map.cpp
@@ -0,0 +1,45 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+#include <base/system.h>
+#include <engine/map.h>
+#include <engine/storage.h>
+#include "datafile.h"
+
+class CMap : public IEngineMap
+{
+	CDataFileReader m_DataFile;
+public:
+	CMap() {}
+	
+	virtual void *GetData(int Index) { return m_DataFile.GetData(Index); }
+	virtual void *GetDataSwapped(int Index) { return m_DataFile.GetDataSwapped(Index); }
+	virtual void UnloadData(int Index) { m_DataFile.UnloadData(Index); }
+	virtual void *GetItem(int Index, int *pType, int *pId) { return m_DataFile.GetItem(Index, pType, pId); }
+	virtual void GetType(int Type, int *pStart, int *pNum) { m_DataFile.GetType(Type, pStart, pNum); }
+	virtual void *FindItem(int Type, int Id) { return m_DataFile.FindItem(Type, Id); }
+	virtual int NumItems() { return m_DataFile.NumItems(); }
+	
+	virtual void Unload()
+	{
+		m_DataFile.Close();
+	}
+
+	virtual bool Load(const char *pMapName)
+	{
+		IStorage *pStorage = Kernel()->RequestInterface<IStorage>();
+		if(!pStorage)
+			return false;
+		return m_DataFile.Open(pStorage, pMapName);
+	}
+	
+	virtual bool IsLoaded()
+	{
+		return m_DataFile.IsOpen();
+	}
+	
+	virtual unsigned Crc()
+	{
+		return m_DataFile.Crc();
+	}
+};
+
+extern IEngineMap *CreateEngineMap() { return new CMap; }
diff --git a/src/engine/shared/masterserver.cpp b/src/engine/shared/masterserver.cpp
new file mode 100644
index 00000000..beade5bf
--- /dev/null
+++ b/src/engine/shared/masterserver.cpp
@@ -0,0 +1,187 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+#include <stdio.h>
+
+#include <base/system.h>
+#include <engine/masterserver.h>
+#include <engine/storage.h>
+#include "engine.h"
+#include "linereader.h"
+
+class CMasterServer : public IEngineMasterServer
+{
+public:
+	// master server functions
+	struct CMasterInfo
+	{
+		char m_aHostname[128];
+		NETADDR m_Addr;
+		
+		CHostLookup m_Lookup;
+	} ;
+
+	CMasterInfo m_aMasterServers[MAX_MASTERSERVERS];
+	int m_NeedsUpdate;
+	CEngine *m_pEngine;
+	
+	CMasterServer()
+	{
+		SetDefault();
+		m_NeedsUpdate = -1;
+		m_pEngine = 0;
+	}
+
+	virtual int RefreshAddresses()
+	{
+		int i;
+		
+		if(m_NeedsUpdate != -1)
+			return 0;
+		
+		dbg_msg("engine/mastersrv", "refreshing master server addresses");
+
+		// add lookup jobs
+		for(i = 0; i < MAX_MASTERSERVERS; i++)	
+			m_pEngine->HostLookup(&m_aMasterServers[i].m_Lookup, m_aMasterServers[i].m_aHostname);
+		
+		m_NeedsUpdate = 1;
+		return 0;
+	}
+
+	virtual void Update()
+	{
+		// check if we need to update
+		if(m_NeedsUpdate != 1)
+			return;
+		m_NeedsUpdate = 0;
+		
+		for(int i = 0; i < MAX_MASTERSERVERS; i++)
+		{
+			if(m_aMasterServers[i].m_Lookup.m_Job.Status() != CJob::STATE_DONE)
+				m_NeedsUpdate = 1;
+			else
+			{
+				m_aMasterServers[i].m_Addr = m_aMasterServers[i].m_Lookup.m_Addr;
+				m_aMasterServers[i].m_Addr.port = 8300;
+			}
+		}
+		
+		if(!m_NeedsUpdate)
+		{
+			dbg_msg("engine/mastersrv", "saving addresses");
+			Save();
+		}
+	}
+
+	virtual int IsRefreshing()
+	{
+		return m_NeedsUpdate;
+	}
+
+	virtual NETADDR GetAddr(int Index) 
+	{
+		return m_aMasterServers[Index].m_Addr;
+	}
+
+	virtual const char *GetName(int Index) 
+	{
+		return m_aMasterServers[Index].m_aHostname;
+	}
+
+	virtual void DumpServers()
+	{
+		for(int i = 0; i < MAX_MASTERSERVERS; i++)
+		{
+			dbg_msg("mastersrv", "#%d = %d.%d.%d.%d", i,
+				m_aMasterServers[i].m_Addr.ip[0], m_aMasterServers[i].m_Addr.ip[1],
+				m_aMasterServers[i].m_Addr.ip[2], m_aMasterServers[i].m_Addr.ip[3]);
+		}
+	}
+
+	virtual void Init(class CEngine *pEngine)
+	{
+		m_pEngine = pEngine;
+	}
+
+	virtual void SetDefault()
+	{
+		mem_zero(m_aMasterServers, sizeof(m_aMasterServers));
+		for(int i = 0; i < MAX_MASTERSERVERS; i++)
+			str_format(m_aMasterServers[i].m_aHostname, sizeof(m_aMasterServers[i].m_aHostname), "master%d.teeworlds.com", i+1);
+	}
+
+	virtual int Load()
+	{
+		CLineReader LineReader;
+		IOHANDLE File;
+		int Count = 0;
+		IStorage *pStorage = Kernel()->RequestInterface<IStorage>();
+		if(!pStorage)
+			return -1;
+		
+		// try to open file
+		File = pStorage->OpenFile("masters.cfg", IOFLAG_READ);
+		if(!File)
+			return -1;
+		
+		LineReader.Init(File);
+		while(1)
+		{
+			CMasterInfo Info = {{0}};
+			int aIp[4];
+			const char *pLine = LineReader.Get();
+			if(!pLine)
+				break;
+
+			// parse line	
+			if(sscanf(pLine, "%s %d.%d.%d.%d", Info.m_aHostname, &aIp[0], &aIp[1], &aIp[2], &aIp[3]) == 5)
+			{
+				Info.m_Addr.ip[0] = (unsigned char)aIp[0];
+				Info.m_Addr.ip[1] = (unsigned char)aIp[1];
+				Info.m_Addr.ip[2] = (unsigned char)aIp[2];
+				Info.m_Addr.ip[3] = (unsigned char)aIp[3];
+				Info.m_Addr.port = 8300;
+				if(Count != MAX_MASTERSERVERS)
+				{
+					m_aMasterServers[Count] = Info;
+					Count++;
+				}
+				//else
+				//	dbg_msg("engine/mastersrv", "warning: skipped master server '%s' due to limit of %d", pLine, MAX_MASTERSERVERS);
+			}
+			//else
+			//	dbg_msg("engine/mastersrv", "warning: couldn't parse master server '%s'", pLine);
+		}
+		
+		io_close(File);
+		return 0;
+	}
+
+	virtual int Save()
+	{
+		IOHANDLE File;
+
+		IStorage *pStorage = Kernel()->RequestInterface<IStorage>();
+		if(!pStorage)
+			return -1;
+			
+		// try to open file
+		File = pStorage->OpenFile("masters.cfg", IOFLAG_WRITE);
+		if(!File)
+			return -1;
+
+		for(int i = 0; i < MAX_MASTERSERVERS; i++)
+		{
+			char aBuf[1024];
+			str_format(aBuf, sizeof(aBuf), "%s %d.%d.%d.%d\n", m_aMasterServers[i].m_aHostname,
+				m_aMasterServers[i].m_Addr.ip[0], m_aMasterServers[i].m_Addr.ip[1],
+				m_aMasterServers[i].m_Addr.ip[2], m_aMasterServers[i].m_Addr.ip[3]);
+				
+			io_write(File, aBuf, str_length(aBuf));
+		}
+		
+		io_close(File);
+		return 0;
+	}
+};
+
+IEngineMasterServer *CreateEngineMasterServer() { return new CMasterServer; }
diff --git a/src/engine/shared/memheap.cpp b/src/engine/shared/memheap.cpp
new file mode 100644
index 00000000..6661962b
--- /dev/null
+++ b/src/engine/shared/memheap.cpp
@@ -0,0 +1,96 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+#include <base/system.h>
+#include "memheap.h"
+
+static const int CHUNK_SIZE = 1024*64;
+
+// allocates a new chunk to be used
+void CHeap::NewChunk()
+{
+	CChunk *pChunk;
+	char *pMem;
+	
+	// allocate memory
+	pMem = (char*)mem_alloc(sizeof(CChunk)+CHUNK_SIZE, 1);
+	if(!pMem)
+		return;
+
+	// the chunk structure is located in the begining of the chunk
+	// init it and return the chunk
+	pChunk = (CChunk*)pMem;
+	pChunk->m_pMemory = (char*)(pChunk+1);
+	pChunk->m_pCurrent = pChunk->m_pMemory;
+	pChunk->m_pEnd = pChunk->m_pMemory + CHUNK_SIZE;
+	pChunk->m_pNext = (CChunk *)0x0;
+
+	pChunk->m_pNext = m_pCurrent;
+	m_pCurrent = pChunk;	
+}
+
+//****************
+void *CHeap::AllocateFromChunk(unsigned int Size)
+{
+	char *pMem;
+	
+	// check if we need can fit the allocation
+	if(m_pCurrent->m_pCurrent + Size > m_pCurrent->m_pEnd)
+		return (void*)0x0;
+
+	// get memory and move the pointer forward
+	pMem = m_pCurrent->m_pCurrent;
+	m_pCurrent->m_pCurrent += Size;
+	return pMem;
+}
+
+// creates a heap
+CHeap::CHeap()
+{
+	m_pCurrent = 0x0;
+	Reset();
+}
+
+CHeap::~CHeap()
+{
+	Clear();
+}
+
+void CHeap::Reset()
+{
+	Clear();
+	NewChunk();
+}
+
+// destroys the heap
+void CHeap::Clear()
+{
+	CChunk *pChunk = m_pCurrent;
+	CChunk *pNext;
+	
+	while(pChunk)
+	{
+		pNext = pChunk->m_pNext;
+		mem_free(pChunk);
+		pChunk = pNext;
+	}
+	
+	m_pCurrent = 0x0;
+}
+
+//
+void *CHeap::Allocate(unsigned Size)
+{
+	char *pMem;
+
+	// try to allocate from current chunk
+	pMem = (char *)AllocateFromChunk(Size);
+	if(!pMem)
+	{
+		// allocate new chunk and add it to the heap
+		NewChunk();
+		
+		// try to allocate again
+		pMem = (char *)AllocateFromChunk(Size);
+	}
+	
+	return pMem;
+}
diff --git a/src/engine/shared/memheap.h b/src/engine/shared/memheap.h
new file mode 100644
index 00000000..706395f2
--- /dev/null
+++ b/src/engine/shared/memheap.h
@@ -0,0 +1,32 @@
+#ifndef ENGINE_SHARED_MEMHEAP_H
+#define ENGINE_SHARED_MEMHEAP_H
+class CHeap
+{
+	struct CChunk
+	{
+		char *m_pMemory;
+		char *m_pCurrent;
+		char *m_pEnd;
+		CChunk *m_pNext;
+	};
+	
+	enum
+	{
+		// how large each chunk should be
+		CHUNK_SIZE = 1025*64,
+	};
+	
+	CChunk *m_pCurrent;
+	
+	
+	void Clear();
+	void NewChunk();
+	void *AllocateFromChunk(unsigned int Size);
+	
+public:
+	CHeap();
+	~CHeap();
+	void Reset();
+	void *Allocate(unsigned Size);
+};
+#endif
diff --git a/src/engine/shared/message.h b/src/engine/shared/message.h
new file mode 100644
index 00000000..4e67a8e1
--- /dev/null
+++ b/src/engine/shared/message.h
@@ -0,0 +1,9 @@
+#ifndef ENGINE_SHARED_MESSAGE_H
+#define ENGINE_SHARED_MESSAGE_H
+class CMessage
+{
+public:
+	virtual bool Pack(void *pData, unsigned MaxDataSize);
+	virtual bool Unpack(const void *pData, unsigned DataSize);
+};
+#endif
diff --git a/src/engine/e_network.cpp b/src/engine/shared/network.cpp
index ac753e50..0305ffff 100644
--- a/src/engine/e_network.cpp
+++ b/src/engine/shared/network.cpp
@@ -1,12 +1,11 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+// 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"
+#include "config.h"
+#include "engine.h"
+#include "network.h"
+#include "huffman.h"
 
 void CNetRecvUnpacker::Clear()
 {
@@ -22,7 +21,7 @@ void CNetRecvUnpacker::Start(const NETADDR *pAddr, CNetConnection *pConnection,
 	m_Valid = true;
 }
 
-/* TODO: rename this function */
+// TODO: rename this function
 int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk)
 {
 	CNetChunkHeader Header;
@@ -32,21 +31,21 @@ int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk)
 	{
 		unsigned char *pData = m_Data.m_aChunkData;
 		
-		/* check for old data to unpack */
+		// 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 */
+		// 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 */
+		// unpack the header
 		pData = Header.Unpack(pData);
 		m_CurrentChunk++;
 		
@@ -56,29 +55,29 @@ int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk)
 			return 0;
 		}
 		
-		/* handle sequence stuff */
+		// 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 */
+				// in sequence
 				m_pConnection->m_Ack = (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE;
 			}
 			else
 			{
-				/* old packet that we already got */
+				// 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)
+				// out of sequence, request resend
+				if(g_Config.m_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 */
+				continue; // take the next chunk in the packet
 			}
 		}
 		
-		/* fill in the info */
+		// fill in the info
 		pChunk->m_ClientID = m_ClientID;
 		pChunk->m_Address = m_Addr;
 		pChunk->m_Flags = 0;
@@ -88,7 +87,7 @@ int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk)
 	}
 }
 
-/* packs the data tight and sends it */
+// 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];
@@ -108,20 +107,20 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
 	int CompressedSize = -1;
 	int FinalSize = -1;
 
-	/* log the data */
+	// log the data
 	if(ms_DataLogSent)
 	{
-		int type = 1;
-		io_write(ms_DataLogSent, &type, sizeof(type));
+		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);
+	// compress
+	CompressedSize = ms_Huffman.Compress(pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4);
 
-	/* check if the compression was enabled, successful and good enough	*/
+	// check if the compression was enabled, successful and good enough
 	if(CompressedSize > 0 && CompressedSize < pPacket->m_DataSize)
 	{
 		FinalSize = CompressedSize;
@@ -129,13 +128,13 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
 	}
 	else
 	{
-		/* use uncompressed data */
+		// 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 */
+	// set header and send the packet if all things are good
 	if(FinalSize >= 0)
 	{
 		FinalSize += NET_PACKETHEADERSIZE;
@@ -144,11 +143,11 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
 		aBuffer[2] = pPacket->m_NumChunks;
 		net_udp_send(Socket, pAddr, aBuffer, FinalSize);
 
-		/* log raw socket data */
+		// log raw socket data
 		if(ms_DataLogSent)
 		{
-			int type = 0;
-			io_write(ms_DataLogSent, &type, sizeof(type));
+			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);
@@ -156,27 +155,27 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
 	}
 }
 
-/* TODO: rename this function */
+// TODO: rename this function
 int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket)
 {
-	/* check the size */
+	// check the size
 	if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE)
 	{
 		dbg_msg("", "packet too small, %d", Size);
 		return -1;
 	}
 
-	/* log the data */
+	// log the data
 	if(ms_DataLogRecv)
 	{
-		int type = 0;
-		io_write(ms_DataLogRecv, &type, sizeof(type));
+		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 */
+	// read the packet
 	pPacket->m_Flags = pBuffer[0]>>4;
 	pPacket->m_Ack = ((pBuffer[0]&0xf)<<8) | pBuffer[1];
 	pPacket->m_NumChunks = pBuffer[2];
@@ -199,30 +198,30 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct
 	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));
+			pPacket->m_DataSize = ms_Huffman.Decompress(&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 */	
+	// check for errors
 	if(pPacket->m_DataSize < 0)
 	{
-		if(config.debug)
+		if(g_Config.m_Debug)
 			dbg_msg("network", "error during packet decoding");
 		return -1;
 	}
 
-	/* log the data */
+	// log the data
 	if(ms_DataLogRecv)
 	{
-		int type = 1;
-		io_write(ms_DataLogRecv, &type, sizeof(type));
+		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 success
 	return 0;
 }
 
@@ -237,7 +236,7 @@ void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int Con
 	Construct.m_aChunkData[0] = ControlMsg;
 	mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize);
 	
-	/* send the control message */
+	// send the control message
 	CNetBase::SendPacket(Socket, pAddr, &Construct);
 }
 
@@ -291,11 +290,12 @@ int CNetBase::IsSeqInBackroom(int Seq, int Ack)
 
 IOHANDLE CNetBase::ms_DataLogSent = 0;
 IOHANDLE CNetBase::ms_DataLogRecv = 0;
-HUFFMAN_STATE CNetBase::ms_HuffmanState;
+CHuffman CNetBase::ms_Huffman;
 
 
 void CNetBase::OpenLog(const char *pSentLog, const char *pRecvLog)
 {
+	/*
 	if(pSentLog)
 	{
 		ms_DataLogSent = engine_openfile(pSentLog, IOFLAG_WRITE);
@@ -312,17 +312,17 @@ void CNetBase::OpenLog(const char *pSentLog, const char *pRecvLog)
 			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);
+	return ms_Huffman.Compress(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);
+	return ms_Huffman.Decompress(pData, DataSize, pOutput, OutputSize);
 }
 
 
@@ -343,5 +343,5 @@ static const unsigned gs_aFreqTable[256+1] = {
 
 void CNetBase::Init()
 {
-	huffman_init(&ms_HuffmanState, gs_aFreqTable);
+	ms_Huffman.Init(gs_aFreqTable);
 }
diff --git a/src/engine/e_network.h b/src/engine/shared/network.h
index 19eca8da..11a1b70d 100644
--- a/src/engine/e_network.h
+++ b/src/engine/shared/network.h
@@ -1,9 +1,8 @@
-#ifndef ENGINE_NETWORK_H
-#define ENGINE_NETWORK_H
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#ifndef ENGINE_SHARED_NETWORK_H
+#define ENGINE_SHARED_NETWORK_H
 
-#include "e_ringbuffer.h"
-#include "e_huffman.h"
+#include "ringbuffer.h"
+#include "huffman.h"
 
 /*
 
@@ -80,15 +79,15 @@ enum
 };
 
 
-typedef int (*NETFUNC_DELCLIENT)(int cid, void *user);
-typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user);
+typedef int (*NETFUNC_DELCLIENT)(int ClientID, void *pUser);
+typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser);
 
 struct CNetChunk
 {
-	/* -1 means that it's a stateless packet */
-	/* 0 on the client means the server */
+	// -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 */
+	NETADDR m_Address; // only used when client_id == -1
 	int m_Flags;
 	int m_DataSize;
 	const void *m_pData;
@@ -162,7 +161,7 @@ private:
 	void SetError(const char *pString);
 	void AckChunks(int Ack);
 	
-	void QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence);
+	int 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();
@@ -176,7 +175,7 @@ public:
 	int Flush();	
 
 	int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr);
-	void QueueChunk(int Flags, int DataSize, const void *pData);
+	int QueueChunk(int Flags, int DataSize, const void *pData);
 
 	const char *ErrorString();
 	void SignalResend();
@@ -210,7 +209,7 @@ public:
 	int FetchChunk(CNetChunk *pChunk);	
 };
 
-/* server side */
+// server side
 class CNetServer
 {
 public:
@@ -232,11 +231,11 @@ private:
 	public:
 		CBanInfo m_Info;
 		
-		/* hash list */
+		// hash list
 		CBan *m_pHashNext;
 		CBan *m_pHashPrev;
 		
-		/* used or free list */
+		// used or free list
 		CBan *m_pNext;
 		CBan *m_pPrev;
 	};
@@ -257,13 +256,13 @@ private:
 	
 	CNetRecvUnpacker m_RecvUnpacker;
 	
-	void BanRemoveByObject(CBan *ban);
+	void BanRemoveByObject(CBan *pBan);
 	
 public:
 	int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
 
 	//
-	bool Open(NETADDR bindaddr, int MaxClients, int Flags);
+	bool Open(NETADDR BindAddr, int MaxClients, int Flags);
 	int Close();
 	
 	//
@@ -277,8 +276,8 @@ public:
 	// banning
 	int BanAdd(NETADDR Addr, int Seconds);
 	int BanRemove(NETADDR Addr);
-	int BanNum(); /* caution, slow */
-	int BanGet(int Index, CBanInfo *pInfo); /* caution, slow */
+	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(); }
@@ -288,7 +287,7 @@ public:
 
 
 
-/* client side */
+// client side
 class CNetClient
 {
 	NETADDR m_ServerAddr;
@@ -327,7 +326,7 @@ class CNetBase
 {
 	static IOHANDLE ms_DataLogSent;
 	static IOHANDLE ms_DataLogRecv;
-	static HUFFMAN_STATE ms_HuffmanState;
+	static CHuffman ms_Huffman;
 public:
 	static void OpenLog(const char *pSentlog, const char *pRecvlog);
 	static void Init();
@@ -339,7 +338,7 @@ public:
 	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 */
+	// 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);	
 };
 
diff --git a/src/engine/e_network_client.cpp b/src/engine/shared/network_client.cpp
index ce243b32..f7859c0a 100644
--- a/src/engine/e_network_client.cpp
+++ b/src/engine/shared/network_client.cpp
@@ -1,5 +1,5 @@
 #include <base/system.h>
-#include "e_network.h"
+#include "network.h"
 
 bool CNetClient::Open(NETADDR BindAddr, int Flags)
 {
@@ -14,7 +14,7 @@ bool CNetClient::Open(NETADDR BindAddr, int Flags)
 
 int CNetClient::Close()
 {
-	/* TODO: implement me */
+	// TODO: implement me
 	return 0;
 }
 
@@ -50,15 +50,15 @@ int CNetClient::Recv(CNetChunk *pChunk)
 {
 	while(1)
 	{
-		/* check for a chunk */
+		// check for a chunk
 		if(m_RecvUnpacker.FetchChunk(pChunk))
 			return 1;
 		
-		/* TODO: empty the recvinfo */
+		// 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 */
+		// no more packets for now
 		if(Bytes <= 0)
 			break;
 
@@ -93,7 +93,7 @@ int CNetClient::Send(CNetChunk *pChunk)
 	
 	if(pChunk->m_Flags&NETSENDFLAG_CONNLESS)
 	{
-		/* send connectionless packet */
+		// send connectionless packet
 		CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize);
 	}
 	else
diff --git a/src/engine/e_network_conn.cpp b/src/engine/shared/network_conn.cpp
index a54c9ce3..4ed157eb 100644
--- a/src/engine/e_network_conn.cpp
+++ b/src/engine/shared/network_conn.cpp
@@ -1,7 +1,6 @@
 #include <base/system.h>
-#include <string.h>
-#include "e_config.h"
-#include "e_network.h"
+#include "config.h"
+#include "network.h"
 
 void CNetConnection::ResetStats()
 {
@@ -71,27 +70,27 @@ int CNetConnection::Flush()
 	if(!NumChunks && !m_Construct.m_Flags)
 		return 0;
 
-	/* send of the packets */	
+	// send of the packets
 	m_Construct.m_Ack = m_Ack;
 	CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct);
 	
-	/* update send times */
+	// update send times
 	m_LastSendTime = time_get();
 	
-	/* clear construct so we can start building a new package */
+	// 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)
+int 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 */
+	// 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 */
+	// pack all the data
 	CNetChunkHeader Header;
 	Header.m_Flags = Flags;
 	Header.m_Size = DataSize;
@@ -101,15 +100,15 @@ void CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, in
 	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 */
+	// set packet flags aswell
 	
 	if(Flags&NET_CHUNKFLAG_VITAL && !(Flags&NET_CHUNKFLAG_RESEND))
 	{
-		/* save packet if we need to resend */
+		// save packet if we need to resend
 		CNetChunkResend *pResend = m_Buffer.Allocate(sizeof(CNetChunkResend)+DataSize);
 		if(pResend)
 		{
@@ -123,22 +122,25 @@ void CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, in
 		}
 		else
 		{
-			/* out of buffer */
+			// out of buffer
 			Disconnect("too weak connection (out of buffer)");
+			return -1;
 		}
 	}
+
+	return 0;
 }
 
-void CNetConnection::QueueChunk(int Flags, int DataSize, const void *pData)
+int 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);
+	return QueueChunkEx(Flags, DataSize, pData, m_Sequence);
 }
 
 void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSize)
 {
-	/* send the control message */
+	// send the control message
 	m_LastSendTime = time_get();
 	CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize);
 }
@@ -151,7 +153,7 @@ void CNetConnection::ResendChunk(CNetChunkResend *pResend)
 
 void CNetConnection::Resend()
 {
-	for(CNetChunkResend *pResend = m_Buffer.First(); pResend; m_Buffer.Next(pResend))
+	for(CNetChunkResend *pResend = m_Buffer.First(); pResend; pResend = m_Buffer.Next(pResend))
 		ResendChunk(pResend);
 }
 
@@ -160,7 +162,7 @@ int CNetConnection::Connect(NETADDR *pAddr)
 	if(State() != NET_CONNSTATE_OFFLINE)
 		return -1;
 	
-	/* init connection */
+	// init connection
 	Reset();
 	m_PeerAddr = *pAddr;
 	mem_zero(m_ErrorString, sizeof(m_ErrorString));
@@ -177,7 +179,7 @@ void CNetConnection::Disconnect(const char *pReason)
 	if(m_RemoteClosed == 0)
 	{
 		if(pReason)
-			SendControl(NET_CTRLMSG_CLOSE, pReason, strlen(pReason)+1);
+			SendControl(NET_CTRLMSG_CLOSE, pReason, str_length(pReason)+1);
 		else
 			SendControl(NET_CTRLMSG_CLOSE, 0, 0);
 
@@ -191,41 +193,44 @@ void CNetConnection::Disconnect(const char *pReason)
 
 int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr)
 {
-	int64 now = time_get();
-	m_LastRecvTime = now;
+	int64 Now = time_get();
+	m_LastRecvTime = Now;
 	
-	/* check if resend is requested */
+	// 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)
+			if(net_addr_comp(&m_PeerAddr, pAddr) == 0)
 			{
-				/* 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);
+				m_State = NET_CONNSTATE_ERROR;
+				m_RemoteClosed = 1;
 				
-				/* set the error string */
-				SetError(Str);
+				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(g_Config.m_Debug)
+					dbg_msg("conn", "closed reason='%s'", ErrorString());
 			}
-			else
-				SetError("no reason given");
-				
-			if(config.debug)
-				dbg_msg("conn", "closed reason='%s'", ErrorString());
 			return 0;			
 		}
 		else
@@ -234,32 +239,32 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr)
 			{
 				if(CtrlMsg == NET_CTRLMSG_CONNECT)
 				{
-					/* send response and init connection */
+					// send response and init connection
 					Reset();
 					m_State = NET_CONNSTATE_PENDING;
 					m_PeerAddr = *pAddr;
-					m_LastSendTime = now;
-					m_LastRecvTime = now;
-					m_LastUpdateTime = now;
+					m_LastSendTime = Now;
+					m_LastRecvTime = Now;
+					m_LastUpdateTime = Now;
 					SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0);
-					if(config.debug)
+					if(g_Config.m_Debug)
 						dbg_msg("connection", "got connection, sending connect+accept");			
 				}
 			}
 			else if(State() == NET_CONNSTATE_CONNECT)
 			{
-				/* connection made */
+				// connection made
 				if(CtrlMsg == NET_CTRLMSG_CONNECTACCEPT)
 				{
 					SendControl(NET_CTRLMSG_ACCEPT, 0, 0);
 					m_State = NET_CONNSTATE_ONLINE;
-					if(config.debug)
+					if(g_Config.m_Debug)
 						dbg_msg("connection", "got connect+accept, sending accept. connection online");
 				}
 			}
 			else if(State() == NET_CONNSTATE_ONLINE)
 			{
-				/* connection made */
+				// connection made
 				/*
 				if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT)
 				{
@@ -273,7 +278,7 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr)
 		if(State() == NET_CONNSTATE_PENDING)
 		{
 			m_State = NET_CONNSTATE_ONLINE;
-			if(config.debug)
+			if(g_Config.m_Debug)
 				dbg_msg("connection", "connecting online");
 		}
 	}
@@ -288,46 +293,46 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr)
 
 int CNetConnection::Update()
 {
-	int64 now = time_get();
+	int64 Now = time_get();
 
 	if(State() == NET_CONNSTATE_OFFLINE || State() == NET_CONNSTATE_ERROR)
 		return 0;
 	
-	/* check for timeout */
+	// check for timeout
 	if(State() != NET_CONNSTATE_OFFLINE &&
 		State() != NET_CONNSTATE_CONNECT &&
-		(now-m_LastRecvTime) > time_freq()*10)
+		(Now-m_LastRecvTime) > time_freq()*10)
 	{
 		m_State = NET_CONNSTATE_ERROR;
 		SetError("timeout");
 	}
 
-	/* fix resends */
+	// 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)
+		// 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())
+			// 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 */
+	// 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 */
+		if(time_get()-m_LastSendTime > time_freq()/2) // flush connection after 500ms if needed
 		{
 			int NumFlushedChunks = Flush();
-			if(NumFlushedChunks && config.debug)
+			if(NumFlushedChunks && g_Config.m_Debug)
 				dbg_msg("connection", "flushed connection due to timeout. %d chunks.", NumFlushedChunks);
 		}
 			
@@ -336,12 +341,12 @@ int CNetConnection::Update()
 	}
 	else if(State() == NET_CONNSTATE_CONNECT)
 	{
-		if(time_get()-m_LastSendTime > time_freq()/2) /* send a new connect every 500ms */
+		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 */
+		if(time_get()-m_LastSendTime > time_freq()/2) // send a new connect/accept every 500ms
 			SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0);
 	}
 	
diff --git a/src/engine/e_network_server.cpp b/src/engine/shared/network_server.cpp
index 995290ef..32b08bf6 100644
--- a/src/engine/e_network_server.cpp
+++ b/src/engine/shared/network_server.cpp
@@ -1,28 +1,28 @@
 #include <base/system.h>
-#include "e_network.h"
+#include "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_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_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_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; }
+#define MACRO_LIST_FIND(Start, Next, Expression) \
+	{ while(Start && !(Expression)) Start = Start->Next; }
 
 bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int Flags)
 {
@@ -44,7 +44,7 @@ bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int Flags)
 	for(int i = 0; i < NET_MAX_CLIENTS; i++)
 		m_aSlots[i].m_Connection.Init(m_Socket);
 	
-	/* setup all pointers for bans */
+	// setup all pointers for bans
 	for(int i = 1; i < NET_SERVER_MAXBANS-1; i++)
 	{
 		m_BanPool[i].m_pNext = &m_BanPool[i+1];
@@ -68,13 +68,13 @@ int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT p
 
 int CNetServer::Close()
 {
-	/* TODO: implement me */
+	// TODO: implement me
 	return 0;
 }
 
 int CNetServer::Drop(int ClientID, const char *pReason)
 {
-	/* TODO: insert lots of checks here */
+	// 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\"",
@@ -114,18 +114,18 @@ int CNetServer::BanNum()
 
 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;
+	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_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];
+	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);
 	
@@ -140,22 +140,22 @@ int CNetServer::BanRemove(NETADDR Addr)
 
 int CNetServer::BanAdd(NETADDR Addr, int Seconds)
 {
-	int iphash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff;
+	int IpHash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff;
 	int Stamp = -1;
 	CBan *pBan;
 	
-	/* remove the port */
+	// remove the port
 	Addr.port = 0;
 	
 	if(Seconds)
 		Stamp = time_timestamp() + Seconds;
 		
-	/* search to see if it already exists */
-	pBan = m_aBans[iphash];
+	// 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 */
+		// adjust the ban
 		pBan->m_Info.m_Expires = Stamp;
 		return 0;
 	}
@@ -163,18 +163,18 @@ int CNetServer::BanAdd(NETADDR Addr, int Seconds)
 	if(!m_BanPool_FirstFree)
 		return -1;
 
-	/* fetch and clear the new ban */
+	// 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 */
+	// 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);
+	// add it to the ban hash
+	MACRO_LIST_LINK_FIRST(pBan, m_aBans[IpHash], m_pHashPrev, m_pHashNext);
 	
-	/* insert it into the used list */
+	// insert it into the used list
 	{
 		if(m_BanPool_FirstUsed)
 		{
@@ -185,7 +185,7 @@ int CNetServer::BanAdd(NETADDR Addr, int Seconds)
 				pInsertAfter = pInsertAfter->m_pPrev;
 			else
 			{
-				/* add to last */
+				// add to last
 				pInsertAfter = m_BanPool_FirstUsed;
 				while(pInsertAfter->m_pNext)
 					pInsertAfter = pInsertAfter->m_pNext;
@@ -206,7 +206,7 @@ int CNetServer::BanAdd(NETADDR Addr, int Seconds)
 		}
 	}
 
-	/* drop banned clients */	
+	// drop banned clients
 	{
 		char Buf[128];
 		NETADDR BanAddr;
@@ -238,7 +238,7 @@ int CNetServer::Update()
 			Drop(i, m_aSlots[i].m_Connection.ErrorString());
 	}
 	
-	/* remove expired bans */
+	// remove expired bans
 	while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires < Now)
 	{
 		CBan *pBan = m_BanPool_FirstUsed;
@@ -253,20 +253,20 @@ int CNetServer::Update()
 */
 int CNetServer::Recv(CNetChunk *pChunk)
 {
-	unsigned now = time_timestamp();
+	unsigned Now = time_timestamp();
 	
 	while(1)
 	{
 		NETADDR Addr;
 			
-		/* check for a chunk */
+		// check for a chunk
 		if(m_RecvUnpacker.FetchChunk(pChunk))
 			return 1;
 		
-		/* TODO: empty the recvinfo */
+		// TODO: empty the recvinfo
 		int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE);
 
-		/* no more packets for now */
+		// no more packets for now
 		if(Bytes <= 0)
 			break;
 		
@@ -274,25 +274,25 @@ int CNetServer::Recv(CNetChunk *pChunk)
 		{
 			CBan *pBan = 0;
 			NETADDR BanAddr = Addr;
-			int iphash = (BanAddr.ip[0]+BanAddr.ip[1]+BanAddr.ip[2]+BanAddr.ip[3])&0xff;
+			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)
+			// 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 */
+			// 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;
+					int Mins = ((pBan->m_Info.m_Expires - Now)+59)/60;
 					if(Mins == 1)
 						str_format(BanStr, sizeof(BanStr), "banned for %d minute", Mins);
 					else
@@ -315,24 +315,24 @@ int CNetServer::Recv(CNetChunk *pChunk)
 			}
 			else
 			{			
-				/* TODO: check size here */
+				// 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 */
+					// 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 */
+							Found = 1; // silent ignore.. we got this client already
 							break;
 						}
 					}
 					
-					/* client that wants to connect */
+					// client that wants to connect
 					if(!Found)
 					{
 						for(int i = 0; i < MaxClients(); i++)
@@ -356,7 +356,7 @@ int CNetServer::Recv(CNetChunk *pChunk)
 				}
 				else
 				{
-					/* normal packet, find matching slot */
+					// normal packet, find matching slot
 					for(int i = 0; i < MaxClients(); i++)
 					{
 						NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress();
@@ -386,7 +386,7 @@ int CNetServer::Send(CNetChunk *pChunk)
 	
 	if(pChunk->m_Flags&NETSENDFLAG_CONNLESS)
 	{
-		/* send connectionless packet */
+		// send connectionless packet
 		CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize);
 	}
 	else
@@ -398,10 +398,15 @@ int CNetServer::Send(CNetChunk *pChunk)
 		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();
+		if(m_aSlots[pChunk->m_ClientID].m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData) == 0)
+		{
+			if(pChunk->m_Flags&NETSENDFLAG_FLUSH)
+				m_aSlots[pChunk->m_ClientID].m_Connection.Flush();
+		}
+		else
+		{
+			Drop(pChunk->m_ClientID, "error sending data");
+		}
 	}
 	return 0;
 }
diff --git a/src/engine/e_packer.cpp b/src/engine/shared/packer.cpp
index 0d8aeab3..3e1d8dd6 100644
--- a/src/engine/e_packer.cpp
+++ b/src/engine/shared/packer.cpp
@@ -1,11 +1,10 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#include <stdlib.h> /* rand() */
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
 #include <base/system.h>
 
-#include "e_packer.h"
-#include "e_compression.h"
-#include "e_engine.h"
-#include "e_config.h"
+#include "packer.h"
+#include "compression.h"
+#include "engine.h"
+#include "config.h"
 
 void CPacker::Reset()
 {
@@ -19,14 +18,14 @@ void CPacker::AddInt(int i)
 	if(m_Error)
 		return;
 		
-	/* make sure that we have space enough */
+	// 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);
+		m_pCurrent = CVariableInt::Pack(m_pCurrent, i);
 }
 
 void CPacker::AddString(const char *pStr, int Limit)
@@ -34,7 +33,7 @@ void CPacker::AddString(const char *pStr, int Limit)
 	if(m_Error)
 		return;
 	
-	/* */
+	//
 	if(Limit > 0)
 	{
 		while(*pStr && Limit != 0)
@@ -66,7 +65,7 @@ void CPacker::AddString(const char *pStr, int Limit)
 	}
 }
 
-void CPacker::AddRaw(const unsigned char *pData, int Size)
+void CPacker::AddRaw(const void *pData, int Size)
 {
 	if(m_Error)
 		return;
@@ -77,18 +76,19 @@ void CPacker::AddRaw(const unsigned char *pData, int Size)
 		return;
 	}
 	
+	const unsigned char *pSrc = (const unsigned char *)pData;
 	while(Size)
 	{
-		*m_pCurrent++ = *pData++;
+		*m_pCurrent++ = *pSrc++;
 		Size--;
 	}
 }
 
 
-void CUnpacker::Reset(const unsigned char *pData, int Size)
+void CUnpacker::Reset(const void *pData, int Size)
 {
 	m_Error = 0;
-	m_pStart = pData;
+	m_pStart = (const unsigned char *)pData;
 	m_pEnd = m_pStart + Size;
 	m_pCurrent = m_pStart;
 }
@@ -105,7 +105,7 @@ int CUnpacker::GetInt()
 	}
 	
 	int i;
-	m_pCurrent = vint_unpack(m_pCurrent, &i);
+	m_pCurrent = CVariableInt::Unpack(m_pCurrent, &i);
 	if(m_pCurrent > m_pEnd)
 	{
 		m_Error = 1;
@@ -120,7 +120,7 @@ const char *CUnpacker::GetString()
 		return "";
 		
 	char *pPtr = (char *)m_pCurrent;
-	while(*m_pCurrent) /* skip the string */
+	while(*m_pCurrent) // skip the string
 	{
 		m_pCurrent++;
 		if(m_pCurrent == m_pEnd)
@@ -131,7 +131,7 @@ const char *CUnpacker::GetString()
 	}
 	m_pCurrent++;
 	
-	/* sanitize all strings */
+	// sanitize all strings
 	str_sanitize(pPtr);
 	return pPtr;
 }
@@ -142,14 +142,14 @@ const unsigned char *CUnpacker::GetRaw(int Size)
 	if(m_Error)
 		return 0;
 	
-	/* check for nasty sizes */
+	// check for nasty sizes
 	if(Size < 0 || m_pCurrent+Size > m_pEnd)
 	{
 		m_Error = 1;
 		return 0;
 	}
 
-	/* "unpack" the data */	
+	// "unpack" the data
 	m_pCurrent += Size;
 	return pPtr;
 }
diff --git a/src/engine/e_packer.h b/src/engine/shared/packer.h
index 5dc80e7a..7a98501a 100644
--- a/src/engine/e_packer.h
+++ b/src/engine/shared/packer.h
@@ -1,4 +1,7 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#ifndef ENGINE_SHARED_PACKER_H
+#define ENGINE_SHARED_PACKER_H
+
+
 
 class CPacker
 {
@@ -15,7 +18,7 @@ public:
 	void Reset();
 	void AddInt(int i);
 	void AddString(const char *pStr, int Limit);
-	void AddRaw(const unsigned char *pData, int Size);
+	void AddRaw(const void *pData, int Size);
 	
 	int Size() const { return (int)(m_pCurrent-m_aBuffer); }
 	const unsigned char *Data() const { return m_aBuffer; }
@@ -29,9 +32,11 @@ class CUnpacker
 	const unsigned char *m_pEnd;
 	int m_Error;
 public:
-	void Reset(const unsigned char *pData, int Size);
+	void Reset(const void *pData, int Size);
 	int GetInt();
 	const char *GetString();
 	const unsigned char *GetRaw(int Size);
 	bool Error() const { return m_Error; }
 };
+
+#endif
diff --git a/src/engine/shared/protocol.h b/src/engine/shared/protocol.h
new file mode 100644
index 00000000..d09cff5a
--- /dev/null
+++ b/src/engine/shared/protocol.h
@@ -0,0 +1,91 @@
+#ifndef ENGINE_SHARED_PROTOCOL_H
+#define ENGINE_SHARED_PROTOCOL_H
+
+#include <base/system.h>
+
+/*
+	Connection diagram - How the initilization works.
+	
+	Client -> INFO -> Server
+		Contains version info, name, and some other info.
+		
+	Client <- MAP <- Server
+		Contains current map.
+	
+	Client -> READY -> Server
+		The client has loaded the map and is ready to go,
+		but the mod needs to send it's information aswell.
+		modc_connected is called on the client and
+		mods_connected is called on the server.
+		The client should call client_entergame when the
+		mod has done it's initilization.
+		
+	Client -> ENTERGAME -> Server
+		Tells the server to start sending snapshots.
+		client_entergame and server_client_enter is called.
+*/
+
+
+enum
+{
+	NETMSG_NULL=0,
+	
+	// the first thing sent by the client
+	// contains the version info for the client
+	NETMSG_INFO=1,
+	
+	// sent by server
+	NETMSG_MAP_CHANGE,		// sent when client should switch map
+	NETMSG_MAP_DATA,		// map transfer, contains a chunk of the map file
+	NETMSG_SNAP,			// normal snapshot, multiple parts
+	NETMSG_SNAPEMPTY,		// empty snapshot
+	NETMSG_SNAPSINGLE,		// ?
+	NETMSG_SNAPSMALL,		//
+	NETMSG_INPUTTIMING,		// reports how off the input was
+	NETMSG_RCON_AUTH_STATUS,// result of the authentication
+	NETMSG_RCON_LINE,		// line that should be printed to the remote console
+
+	NETMSG_AUTH_CHALLANGE,	//
+	NETMSG_AUTH_RESULT,		//
+	
+	// sent by client
+	NETMSG_READY,			//
+	NETMSG_ENTERGAME,
+	NETMSG_INPUT,			// contains the inputdata from the client
+	NETMSG_RCON_CMD,		// 
+	NETMSG_RCON_AUTH,		//
+	NETMSG_REQUEST_MAP_DATA,//
+
+	NETMSG_AUTH_START,		//
+	NETMSG_AUTH_RESPONSE,	//
+	
+	// sent by both
+	NETMSG_PING,
+	NETMSG_PING_REPLY,
+	NETMSG_ERROR
+};
+
+// this should be revised
+enum
+{
+	SERVER_TICK_SPEED=50,
+	SERVER_FLAG_PASSWORD = 0x1,
+
+	MAX_CLIENTS=16,
+
+	MAX_INPUT_SIZE=128,
+	MAX_SNAPSHOT_PACKSIZE=900,
+
+	MAX_CLANNAME_LENGTH=32,
+	MAX_NAME_LENGTH=24,
+	
+
+	// message packing
+	MSGFLAG_VITAL=1,
+	MSGFLAG_FLUSH=2,
+	MSGFLAG_NORECORD=4,
+	MSGFLAG_RECORD=8,
+	MSGFLAG_NOSEND=16
+};
+
+#endif
diff --git a/src/engine/e_ringbuffer.cpp b/src/engine/shared/ringbuffer.cpp
index eb8a8af4..45a845ee 100644
--- a/src/engine/e_ringbuffer.cpp
+++ b/src/engine/shared/ringbuffer.cpp
@@ -1,6 +1,6 @@
 #include <base/system.h>
 
-#include "e_ringbuffer.h"
+#include "ringbuffer.h"
 	
 CRingBufferBase::CItem *CRingBufferBase::NextBlock(CItem *pItem)
 {
@@ -18,15 +18,15 @@ CRingBufferBase::CItem *CRingBufferBase::PrevBlock(CItem *pItem)
 
 CRingBufferBase::CItem *CRingBufferBase::MergeBack(CItem *pItem)
 {
-	/* make sure that this block and previous block is free */
+	// 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 */
+	// merge the blocks
 	pItem->m_pPrev->m_Size += pItem->m_Size;
 	pItem->m_pPrev->m_pNext = pItem->m_pNext;
 	
-	/* fixup pointers */
+	// fixup pointers
 	if(pItem->m_pNext)
 		pItem->m_pNext->m_pPrev = pItem->m_pPrev;
 	else
@@ -38,7 +38,7 @@ CRingBufferBase::CItem *CRingBufferBase::MergeBack(CItem *pItem)
 	if(pItem == m_pConsume)
 		m_pConsume = pItem->m_pPrev;
 	
-	/* return the current block */
+	// return the current block
 	return pItem->m_pPrev;
 }
 
@@ -61,20 +61,20 @@ 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 */
+	// check if we even can fit this block
 	if(WantedSize > m_Size)
 		return 0;
 
 	while(1)	
 	{
-		/* check for space */
+		// 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 */
+				// wrap around to try to find a block
 				if(m_pFirst->m_Free && m_pFirst->m_Size >= WantedSize)
 					pBlock = m_pFirst;
 			}
@@ -84,7 +84,7 @@ void *CRingBufferBase::Allocate(int Size)
 			break;
 		else
 		{
-			/* we have no block, check our policy and see what todo */
+			// we have no block, check our policy and see what todo
 			if(m_Flags&FLAG_RECYCLE)
 			{
 				if(!PopFirst())
@@ -95,10 +95,10 @@ void *CRingBufferBase::Allocate(int Size)
 		}
 	}
 	
-	/* okey, we have our block */
+	// okey, we have our block
 	
-	/* split the block if needed */
-	if(pBlock->m_Size > WantedSize)
+	// split the block if needed
+	if(pBlock->m_Size > WantedSize+sizeof(CItem))
 	{
 		CItem *pNewItem = (CItem *)((char *)pBlock + WantedSize);
 		pNewItem->m_pPrev = pBlock;
@@ -116,10 +116,10 @@ void *CRingBufferBase::Allocate(int Size)
 	}
 	
 	
-	/* set next block */
+	// set next block
 	m_pProduce = NextBlock(pBlock);
 	
-	/* set as used and return the item pointer */
+	// set as used and return the item pointer
 	pBlock->m_Free = 0;
 	return (void *)(pBlock+1);
 }
@@ -129,13 +129,13 @@ int CRingBufferBase::PopFirst()
 	if(m_pConsume->m_Free)
 		return 0;
 	
-	/* set the free flag */
+	// set the free flag
 	m_pConsume->m_Free = 1;
 	
-	/* previous block is also free, merge them */
+	// previous block is also free, merge them
 	m_pConsume = MergeBack(m_pConsume);
 	
-	/* advance the consume pointer */
+	// advance the consume pointer
 	m_pConsume = NextBlock(m_pConsume);
 	while(m_pConsume->m_Free && m_pConsume != m_pProduce)
 	{
@@ -143,8 +143,8 @@ int CRingBufferBase::PopFirst()
 		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 */
+	// 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;
 }
@@ -187,8 +187,6 @@ void *CRingBufferBase::First()
 
 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/shared/ringbuffer.h
index b9f7219c..aa02b8d9 100644
--- a/src/engine/e_ringbuffer.h
+++ b/src/engine/shared/ringbuffer.h
@@ -1,5 +1,5 @@
-#ifndef _RINGBUFFER_H
-#define _RINGBUFFER_H
+#ifndef ENGINE_SHARED_RINGBUFFER_H
+#define ENGINE_SHARED_RINGBUFFER_H
 
 typedef struct RINGBUFFER RINGBUFFER;
 
@@ -40,7 +40,7 @@ protected:
 public:
 	enum
 	{
-		/* Will start to destroy items to try to fit the next one */
+		// Will start to destroy items to try to fit the next one
 		FLAG_RECYCLE=1
 	};
 };
diff --git a/src/engine/shared/snapshot.cpp b/src/engine/shared/snapshot.cpp
new file mode 100644
index 00000000..d566d3a3
--- /dev/null
+++ b/src/engine/shared/snapshot.cpp
@@ -0,0 +1,537 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+#include "snapshot.h"
+#include "engine.h"
+#include "compression.h"
+
+// CSnapshot
+
+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;
+}
+
+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]);
+	}
+}
+
+
+// CSnapshotDelta
+
+struct CItemList
+{
+	int m_Num;
+	int m_aKeys[64];
+	int m_aIndex[64];
+};
+
+enum
+{
+	HASHLIST_SIZE = 256,
+};
+
+static void GenerateHash(CItemList *pHashlist, CSnapshot *pSnapshot)
+{
+	for(int i = 0; i < HASHLIST_SIZE; i++)
+		pHashlist[i].m_Num = 0;
+		
+	for(int i = 0; i < pSnapshot->NumItems(); i++)
+	{
+		int Key = pSnapshot->GetItem(i)->Key();
+		int HashID = ((Key>>8)&0xf0) | (Key&0xf);
+		if(pHashlist[HashID].m_Num != 64)
+		{
+			pHashlist[HashID].m_aIndex[pHashlist[HashID].m_Num] = i;
+			pHashlist[HashID].m_aKeys[pHashlist[HashID].m_Num] = Key;
+			pHashlist[HashID].m_Num++;
+		}
+	}
+}
+
+static int GetItemIndexHashed(int Key, const CItemList *pHashlist)
+{
+		int HashID = ((Key>>8)&0xf0) | (Key&0xf);
+		for(int i = 0; i < pHashlist[HashID].m_Num; i++)
+		{
+			if(pHashlist[HashID].m_aKeys[i] == Key)
+				return pHashlist[HashID].m_aIndex[i];
+	}
+	
+	return -1;
+}
+
+static int DiffItem(int *pPast, int *pCurrent, int *pOut, int Size)
+{
+	int Needed = 0;
+	while(Size)
+	{
+		*pOut = *pCurrent-*pPast;
+		Needed |= *pOut;
+		pOut++;
+		pPast++;
+		pCurrent++;
+		Size--;
+	}
+	
+	return Needed;
+}
+
+void CSnapshotDelta::UndiffItem(int *pPast, int *pDiff, int *pOut, int Size)
+{
+	while(Size)
+	{
+		*pOut = *pPast+*pDiff;
+		
+		if(*pDiff == 0)
+			m_aSnapshotDataRate[m_SnapshotCurrent] += 1;
+		else
+		{
+			unsigned char aBuf[16];
+			unsigned char *pEnd = CVariableInt::Pack(aBuf,  *pDiff);
+			m_aSnapshotDataRate[m_SnapshotCurrent] += (int)(pEnd - (unsigned char*)aBuf) * 8;
+		}
+		
+		pOut++;
+		pPast++;
+		pDiff++;
+		Size--;
+	}
+}
+
+CSnapshotDelta::CSnapshotDelta()
+{
+	mem_zero(m_aItemSizes, sizeof(m_aItemSizes));
+	mem_zero(m_aSnapshotDataRate, sizeof(m_aSnapshotDataRate));
+	mem_zero(m_aSnapshotDataUpdates, sizeof(m_aSnapshotDataUpdates));
+	m_SnapshotCurrent = 0;
+	mem_zero(&m_Empty, sizeof(m_Empty));
+}
+
+void CSnapshotDelta::SetStaticsize(int ItemType, int Size)
+{
+	m_aItemSizes[ItemType] = Size;
+}
+
+CSnapshotDelta::CData *CSnapshotDelta::EmptyDelta()
+{
+	return &m_Empty;
+}
+
+// TODO: OPT: this should be made much faster
+int CSnapshotDelta::CreateDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pDstData)
+{
+	CData *pDelta = (CData *)pDstData;
+	int *pData = (int *)pDelta->m_pData;
+	int i, ItemSize, PastIndex;
+	CSnapshotItem *pFromItem;
+	CSnapshotItem *pCurItem;
+	CSnapshotItem *pPastItem;
+	int Count = 0;
+	int SizeCount = 0;
+	
+	pDelta->m_NumDeletedItems = 0;
+	pDelta->m_NumUpdateItems = 0;
+	pDelta->m_NumTempItems = 0;
+	
+	CItemList Hashlist[HASHLIST_SIZE];
+	GenerateHash(Hashlist, pTo);
+
+	// pack deleted stuff
+	for(i = 0; i < pFrom->NumItems(); i++)
+	{
+		pFromItem = pFrom->GetItem(i);
+		if(GetItemIndexHashed(pFromItem->Key(), Hashlist) == -1)
+		{
+			// deleted
+			pDelta->m_NumDeletedItems++;
+			*pData = pFromItem->Key();
+			pData++;
+		}
+	}
+	
+	GenerateHash(Hashlist, pFrom);
+	int aPastIndecies[1024];
+
+	// fetch previous indices
+	// we do this as a separate pass because it helps the cache
+	for(i = 0; i < pTo->NumItems(); i++)
+	{
+		pCurItem = pTo->GetItem(i);  // O(1) .. O(n)
+		aPastIndecies[i] = GetItemIndexHashed(pCurItem->Key(), Hashlist); // O(n) .. O(n^n)
+	}
+		
+	for(i = 0; i < pTo->NumItems(); i++)
+	{
+		// do delta
+		ItemSize = pTo->GetItemSize(i); // O(1) .. O(n)
+		pCurItem = pTo->GetItem(i);  // O(1) .. O(n)
+		PastIndex = aPastIndecies[i];
+		
+		if(PastIndex != -1)
+		{
+			int *pItemDataDst = pData+3;
+	
+			pPastItem = pFrom->GetItem(PastIndex);
+			
+			if(m_aItemSizes[pCurItem->Type()])
+				pItemDataDst = pData+2;
+			
+			if(DiffItem((int*)pPastItem->Data(), (int*)pCurItem->Data(), pItemDataDst, ItemSize/4))
+			{
+				
+				*pData++ = pCurItem->Type();
+				*pData++ = pCurItem->ID();
+				if(!m_aItemSizes[pCurItem->Type()])
+					*pData++ = ItemSize/4;
+				pData += ItemSize/4;
+				pDelta->m_NumUpdateItems++;
+			}
+		}
+		else
+		{
+			*pData++ = pCurItem->Type();
+			*pData++ = pCurItem->ID();
+			if(!m_aItemSizes[pCurItem->Type()])
+				*pData++ = ItemSize/4;
+			
+			mem_copy(pData, pCurItem->Data(), ItemSize);
+			SizeCount += ItemSize;
+			pData += ItemSize/4;
+			pDelta->m_NumUpdateItems++;
+			Count++;
+		}
+	}
+	
+	if(0)
+	{
+		dbg_msg("snapshot", "%d %d %d",
+			pDelta->m_NumDeletedItems,
+			pDelta->m_NumUpdateItems,
+			pDelta->m_NumTempItems);
+	}
+
+	/*
+	// TODO: pack temp stuff
+	
+	// finish
+	//mem_copy(pDelta->offsets, deleted, pDelta->num_deleted_items*sizeof(int));
+	//mem_copy(&(pDelta->offsets[pDelta->num_deleted_items]), update, pDelta->num_update_items*sizeof(int));
+	//mem_copy(&(pDelta->offsets[pDelta->num_deleted_items+pDelta->num_update_items]), temp, pDelta->num_temp_items*sizeof(int));
+	//mem_copy(pDelta->data_start(), data, data_size);
+	//pDelta->data_size = data_size;
+	* */
+	
+	if(!pDelta->m_NumDeletedItems && !pDelta->m_NumUpdateItems && !pDelta->m_NumTempItems)
+		return 0;
+	
+	return (int)((char*)pData-(char*)pDstData);
+}
+
+static int RangeCheck(void *pEnd, void *pPtr, int Size)
+{
+	if((const char *)pPtr + Size > (const char *)pEnd)
+		return -1;
+	return 0;
+}
+
+int CSnapshotDelta::UnpackDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pSrcData, int DataSize)
+{
+	CSnapshotBuilder Builder;
+	CData *pDelta = (CData *)pSrcData;
+	int *pData = (int *)pDelta->m_pData;
+	int *pEnd = (int *)(((char *)pSrcData + DataSize));
+	
+	CSnapshotItem *pFromItem;
+	int Keep, ItemSize;
+	int *pDeleted;
+	int Id, Type, Key;
+	int FromIndex;
+	int *pNewData;
+			
+	Builder.Init();
+	
+	// unpack deleted stuff
+	pDeleted = pData;
+	pData += pDelta->m_NumDeletedItems;
+	if(pData > pEnd)
+		return -1;
+
+	// copy all non deleted stuff
+	for(int i = 0; i < pFrom->NumItems(); i++)
+	{
+		// dbg_assert(0, "fail!");
+		pFromItem = pFrom->GetItem(i);
+		ItemSize = pFrom->GetItemSize(i); 
+		Keep = 1;
+		for(int d = 0; d < pDelta->m_NumDeletedItems; d++)
+		{
+			if(pDeleted[d] == pFromItem->Key())
+			{
+				Keep = 0;
+				break;
+			}
+		}
+		
+		if(Keep)
+		{
+			// keep it
+			mem_copy(
+				Builder.NewItem(pFromItem->Type(), pFromItem->ID(), ItemSize),
+				pFromItem->Data(), ItemSize);
+		}
+	}
+		
+	// unpack updated stuff
+	for(int i = 0; i < pDelta->m_NumUpdateItems; i++)
+	{
+		if(pData+2 > pEnd)
+			return -1;
+		
+		Type = *pData++;
+		Id = *pData++;
+		if(m_aItemSizes[Type])
+			ItemSize = m_aItemSizes[Type];
+		else
+		{
+			if(pData+1 > pEnd)
+				return -2;
+			ItemSize = (*pData++) * 4;
+		}
+		m_SnapshotCurrent = Type;
+		
+		if(RangeCheck(pEnd, pData, ItemSize) || ItemSize < 0) return -3;
+		
+		Key = (Type<<16)|Id;
+		
+		// create the item if needed
+		pNewData = Builder.GetItemData(Key);
+		if(!pNewData)
+			pNewData = (int *)Builder.NewItem(Key>>16, Key&0xffff, ItemSize);
+
+		//if(range_check(pEnd, pNewData, ItemSize)) return -4;
+			
+		FromIndex = pFrom->GetItemIndex(Key);
+		if(FromIndex != -1)
+		{
+			// we got an update so we need pTo apply the diff
+			UndiffItem((int *)pFrom->GetItem(FromIndex)->Data(), pData, pNewData, ItemSize/4);
+			m_aSnapshotDataUpdates[m_SnapshotCurrent]++;
+		}
+		else // no previous, just copy the pData
+		{
+			mem_copy(pNewData, pData, ItemSize);
+			m_aSnapshotDataRate[m_SnapshotCurrent] += ItemSize*8;
+			m_aSnapshotDataUpdates[m_SnapshotCurrent]++;
+		}
+			
+		pData += ItemSize/4;
+	}
+	
+	// finish up
+	return Builder.Finish(pTo);
+}
+
+
+// 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 *SpnapData)
+{
+	// flattern and make the snapshot
+	CSnapshot *pSnap = (CSnapshot *)SpnapData;
+	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/shared/snapshot.h
index 60dc254c..ec27d004 100644
--- a/src/engine/e_snapshot.h
+++ b/src/engine/shared/snapshot.h
@@ -1,12 +1,9 @@
-/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-#ifndef ENGINE_SNAPSHOT_H
-#define ENGINE_SNAPSHOT_H
+#ifndef ENGINE_SHARED_SNAPSHOT_H
+#define ENGINE_SHARED_SNAPSHOT_H
 
 #include <base/system.h>
 
-/* CSnapshot */
-
-
+// CSnapshot
 
 class CSnapshotItem
 {
@@ -19,48 +16,69 @@ public:
 	int Key() { return m_TypeAndID; }
 };
 
-class CSnapshotDelta
-{
-public:
-	int m_NumDeletedItems;
-	int m_NumUpdateItems;
-	int m_NumTempItems; /* needed? */
-	int m_pData[1];
-};
 
-// TODO: hide a lot of these members
 class CSnapshot
 {
+	friend class CSnapshotBuilder;
+	int m_DataSize;
+	int m_NumItems;
+
+	int *Offsets() const { return (int *)(this+1); }
+	char *DataStart() const { return (char*)(Offsets()+m_NumItems); }
+
 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); }
+	void Clear() { m_DataSize = 0; m_NumItems = 0; }
+	int NumItems() const { return 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);
+
+// CSnapshotDelta
+
+class CSnapshotDelta
+{
+public:
+	class CData
+	{
+	public:
+		int m_NumDeletedItems;
+		int m_NumUpdateItems;
+		int m_NumTempItems; // needed?
+		int m_pData[1];
+	};
+
+private:
+	// TODO: strange arbitrary number
+	short m_aItemSizes[64];
+	int m_aSnapshotDataRate[0xffff];
+	int m_aSnapshotDataUpdates[0xffff];
+	int m_SnapshotCurrent;
+	CData m_Empty;
+
+	void UndiffItem(int *pPast, int *pDiff, int *pOut, int Size);
+
+public:
+	CSnapshotDelta();
+	int GetDataRate(int Index) { return m_aSnapshotDataRate[Index]; }
+	int GetDataUpdates(int Index) { return m_aSnapshotDataUpdates[Index]; }
+	void SetStaticsize(int ItemType, int Size);
+	CData *EmptyDelta();
+	int CreateDelta(class CSnapshot *pFrom, class CSnapshot *pTo, void *pData);
+	int UnpackDelta(class CSnapshot *pFrom, class CSnapshot *pTo, void *pData, int DataSize);
 };
 
-/* CSnapshotStorage */
 
+// CSnapshotStorage
 
 class CSnapshotStorage
 {
@@ -115,4 +133,4 @@ public:
 };
 
 
-#endif /* ENGINE_SNAPSHOT_H */
+#endif // ENGINE_SNAPSHOT_H
diff --git a/src/engine/shared/storage.cpp b/src/engine/shared/storage.cpp
new file mode 100644
index 00000000..491795ad
--- /dev/null
+++ b/src/engine/shared/storage.cpp
@@ -0,0 +1,196 @@
+// copyright (c) 2007 magnus auvinen, see licence.txt for more info
+#include <base/system.h>
+#include <engine/storage.h>
+#include "engine.h"
+
+// compiled-in data-dir path
+#define DATA_DIR "data"
+
+class CStorage : public IStorage
+{
+public:
+	char m_aApplicationSavePath[512];
+	char m_aDatadir[512];
+	
+	CStorage()
+	{
+		m_aApplicationSavePath[0] = 0;
+		m_aDatadir[0] = 0;
+	}
+	
+	int Init(const char *pApplicationName, const char *pArgv0)
+	{
+		char aPath[1024] = {0};
+		fs_storage_path(pApplicationName, m_aApplicationSavePath, sizeof(m_aApplicationSavePath));
+		if(fs_makedir(m_aApplicationSavePath) == 0)
+		{		
+			str_format(aPath, sizeof(aPath), "%s/screenshots", m_aApplicationSavePath);
+			fs_makedir(aPath);
+
+			str_format(aPath, sizeof(aPath), "%s/maps", m_aApplicationSavePath);
+			fs_makedir(aPath);
+
+			str_format(aPath, sizeof(aPath), "%s/downloadedmaps", m_aApplicationSavePath);
+			fs_makedir(aPath);
+
+			str_format(aPath, sizeof(aPath), "%s/demos", m_aApplicationSavePath);
+			fs_makedir(aPath);
+		}
+		
+		return FindDatadir(pArgv0);
+	}
+		
+	int FindDatadir(const char *pArgv0)
+	{
+		// 1) use provided data-dir override
+		if(m_aDatadir[0])
+		{
+			if(fs_is_dir(m_aDatadir))
+				return 0;
+			else
+			{
+				dbg_msg("engine/datadir", "specified data-dir '%s' does not exist", m_aDatadir);
+				return -1;
+			}
+		}
+		
+		// 2) use data-dir in PWD if present
+		if(fs_is_dir("data/mapres"))
+		{
+			str_copy(m_aDatadir, "data", sizeof(m_aDatadir));
+			return 0;
+		}
+		
+		// 3) use compiled-in data-dir if present
+		if (fs_is_dir(DATA_DIR "/mapres"))
+		{
+			str_copy(m_aDatadir, DATA_DIR, sizeof(m_aDatadir));
+			return 0;
+		}
+		
+		// 4) check for usable path in argv[0]
+		{
+			unsigned int Pos = ~0U;
+			for(unsigned i = 0; pArgv0[i]; i++)
+				if(pArgv0[i] == '/')
+					Pos = i;
+			
+			if (Pos < sizeof(m_aDatadir))
+			{
+				char aBaseDir[sizeof(m_aDatadir)];
+				str_copy(aBaseDir, pArgv0, Pos);
+				aBaseDir[Pos] = '\0';
+				str_format(m_aDatadir, sizeof(m_aDatadir), "%s/data", aBaseDir);
+				
+				if (fs_is_dir(m_aDatadir))
+					return 0;
+			}
+		}
+		
+	#if defined(CONF_FAMILY_UNIX)
+		// 5) check for all default locations
+		{
+			const char *aDirs[] = {
+				"/usr/share/teeworlds/data",
+				"/usr/share/games/teeworlds/data",
+				"/usr/local/share/teeworlds/data",
+				"/usr/local/share/games/teeworlds/data",
+				"/opt/teeworlds/data"
+			};
+			const int DirsCount = sizeof(aDirs) / sizeof(aDirs[0]);
+			
+			int i;
+			for (i = 0; i < DirsCount; i++)
+			{
+				if (fs_is_dir(aDirs[i]))
+				{
+					str_copy(m_aDatadir, aDirs[i], sizeof(m_aDatadir));
+					return 0;
+				}
+			}
+		}
+	#endif
+		
+		// no data-dir found
+		dbg_msg("engine/datadir", "warning no data directory found");
+		return -1;
+	}
+
+	virtual void ListDirectory(int Types, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser)
+	{
+		char aBuffer[1024];
+		
+		// list current directory
+		if(Types&TYPE_CURRENT)
+		{
+			fs_listdir(pPath, pfnCallback, pUser);
+		}
+		
+		// list users directory
+		if(Types&TYPE_SAVE)
+		{
+			str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_aApplicationSavePath, pPath);
+			fs_listdir(aBuffer, pfnCallback, pUser);
+		}
+		
+		// list datadir directory
+		if(Types&TYPE_DATA)
+		{
+			str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_aDatadir, pPath);
+			fs_listdir(aBuffer, pfnCallback, pUser);
+		}		
+	}
+	
+	virtual IOHANDLE OpenFile(const char *pFilename, int Flags, char *pBuffer = 0, int BufferSize = 0)
+	{
+		char aBuffer[1024];
+		if(!pBuffer)
+		{
+			pBuffer = aBuffer;
+			BufferSize = sizeof(aBuffer);
+		}
+		
+		if(Flags&IOFLAG_WRITE)
+		{
+			str_format(pBuffer, BufferSize, "%s/%s", m_aApplicationSavePath, pFilename);
+			return io_open(pBuffer, Flags);
+		}
+		else
+		{
+			IOHANDLE Handle = 0;
+			
+			// check current directory
+			Handle = io_open(pFilename, Flags);
+			if(Handle)
+				return Handle;
+				
+			// check user directory
+			str_format(pBuffer, BufferSize, "%s/%s", m_aApplicationSavePath, pFilename);
+			Handle = io_open(pBuffer, Flags);
+			if(Handle)
+				return Handle;
+				
+			// check normal data directory
+			str_format(pBuffer, BufferSize, "%s/%s", m_aDatadir, pFilename);
+			Handle = io_open(pBuffer, Flags);
+			if(Handle)
+				return Handle;
+		}
+		
+		pBuffer[0] = 0;
+		return 0;		
+	}
+
+	static IStorage *Create(const char *pApplicationName, const char *pArgv0)
+	{
+		CStorage *p = new CStorage();
+		if(p->Init(pApplicationName, pArgv0))
+		{
+			delete p;
+			p = 0;
+		}
+		return p;
+	}
+};
+
+IStorage *CreateStorage(const char *pApplicationName, const char *pArgv0) { return CStorage::Create(pApplicationName, pArgv0); }
diff --git a/src/engine/sound.h b/src/engine/sound.h
new file mode 100644
index 00000000..bbbc4288
--- /dev/null
+++ b/src/engine/sound.h
@@ -0,0 +1,40 @@
+#ifndef ENGINE_SOUND_H
+#define ENGINE_SOUND_H
+
+#include "kernel.h"
+
+class ISound : public IInterface
+{
+	MACRO_INTERFACE("sound", 0)
+public:
+	enum
+	{
+		FLAG_LOOP=1,
+		FLAG_POS=2,
+		FLAG_ALL=3
+	};
+
+	virtual int LoadWV(const char *pFilename) = 0;
+	
+	virtual void SetChannel(int ChannelId, float Volume, float Panning) = 0;
+	virtual void SetListenerPos(float x, float y) = 0;
+	
+	virtual int PlayAt(int ChannelId, int SoundId, int Flags, float x, float y) = 0;
+	virtual int Play(int ChannelId, int SoundId, int Flags) = 0;
+	virtual void Stop(int VoiceId) = 0;
+	virtual void StopAll() = 0;
+};
+
+
+class IEngineSound : public ISound
+{
+	MACRO_INTERFACE("enginesound", 0)
+public:
+	virtual int Init() = 0;
+	virtual int Update() = 0;
+	virtual int Shutdown() = 0;
+};
+
+extern IEngineSound *CreateEngineSound();
+
+#endif
diff --git a/src/engine/storage.h b/src/engine/storage.h
new file mode 100644
index 00000000..4f875d40
--- /dev/null
+++ b/src/engine/storage.h
@@ -0,0 +1,25 @@
+#ifndef ENGINE_STORAGE_H
+#define ENGINE_STORAGE_H
+
+#include "kernel.h"
+
+class IStorage : public IInterface
+{
+	MACRO_INTERFACE("storage", 0)
+public:
+	enum
+	{
+		TYPE_SAVE = 1,
+		TYPE_CURRENT = 2,
+		TYPE_DATA = 4,
+		TYPE_ALL = ~0
+	};
+	
+	virtual void ListDirectory(int Types, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) = 0;
+	virtual IOHANDLE OpenFile(const char *pFilename, int Flags, char *pBuffer = 0, int BufferSize = 0) = 0;
+};
+
+extern IStorage *CreateStorage(const char *pApplicationName, const char *pArgv0);
+
+
+#endif
diff --git a/src/engine/textrender.h b/src/engine/textrender.h
new file mode 100644
index 00000000..7c7e036b
--- /dev/null
+++ b/src/engine/textrender.h
@@ -0,0 +1,60 @@
+#ifndef ENGINE_TEXTRENDER_H
+#define ENGINE_TEXTRENDER_H
+#include "kernel.h"
+
+enum
+{
+	TEXTFLAG_RENDER=1,
+	TEXTFLAG_ALLOW_NEWLINE=2,
+	TEXTFLAG_STOP_AT_END=4
+};
+
+class CFont;
+
+class CTextCursor
+{
+public:
+	int m_Flags;
+	int m_LineCount;
+	int m_CharCount;
+	
+	float m_StartX;
+	float m_StartY;
+	float m_LineWidth;
+	float m_X, m_Y;
+	
+	struct CFont *m_pFont;
+	float m_FontSize;
+};
+
+class ITextRender : public IInterface
+{
+	MACRO_INTERFACE("textrender", 0)
+public:
+	virtual void SetCursor(CTextCursor *pCursor, float x, float y, float FontSize, int Flags) = 0;
+	
+	virtual CFont *LoadFont(const char *pFilename) = 0;
+	virtual void DestroyFont(CFont *pFont) = 0;
+	
+	virtual void SetDefaultFont(struct CFont *pFont) = 0;
+
+	//
+	virtual void TextEx(CTextCursor *pCursor, const char *pText, int Length) = 0;
+	
+	// old foolish interface
+	virtual void TextColor(float r, float g, float b, float a) = 0;
+	virtual void Text(void *pFontSetV, float x, float y, float Size, const char *pText, int MaxWidth) = 0;
+	virtual float TextWidth(void *pFontSetV, float Size, const char *pText, int Length) = 0;
+	virtual float TextLineCount(void *pFontSetV, float Size, const char *pText, int LineWidth) = 0;
+};
+
+class IEngineTextRender : public ITextRender
+{
+	MACRO_INTERFACE("enginetextrender", 0)
+public:
+	virtual void Init() = 0;
+};
+
+extern IEngineTextRender *CreateEngineTextRender();
+
+#endif